CIM Generator OpenAI v4 | 360 Biz Brokers
CIM Generator — New CIM
💾 Save
👁 Preview
⬇ Export PDF
Quick Start
📄
Cover Info
Name, price, key metrics
✍️
Executive Summary
AI can write this for you
💰
Financials
Revenue, SDE, EBITDA
📸
Upload Photos
Logos & business photos
🤖 AI Connection
The upload and AI writing tools require a working WordPress REST proxy endpoint. If your site returns 404, paste the current proxy URL below and click Save.
📥 Step 1 — Download & Send Intake Form to Seller
Download the 360 Biz Brokers Listing Intake Form. Send it to your seller to complete. Once returned, upload it in Step 2 below and AI will auto-populate all CIM fields.
English Version
Full listing intake • 4 pages • All CIM sections
⬇ Download (English)
Spanish Version / Versión en Español
Formulario completo • 4 páginas • Todas las secciones
⬇ Descargar (Español)
💡 Tip: Save as PDF from your browser's Print dialog (File → Print → Save as PDF) before emailing to the seller.
⚡ Step 2 — Upload Completed Intake & Auto-Fill CIM
Upload a listing intake form, seller questionnaire, or any business document. AI will read it and automatically populate all CIM fields.
📋
Drop your listing intake form here
Supports PDF, Word (.docx), JPG, PNG — seller questionnaires, intake sheets, listing agreements
Browse Files
📄
✨ Extract & Auto-Fill
✕
Choose CIM Template
CONFIDENTIAL
Business Name
Presented by 360 Biz Brokers
Gold & Black ★
CONFIDENTIAL
Business Name
Investment Overview
Navy Professional
CONFIDENTIAL
Business Name
Presented by Broker
Forest Green
CONFIDENTIAL
Business Name
Luxury Brief
Burgundy Luxury
Know the Listing Broker
Broker Biography
✨ AI:
Improve Wording
Make More Professional
Expand
Executive Summary
Executive Summary
✨ AI:
🪄 Generate from All Data
Improve
Rewrite for Buyers
Make Professional
Summarize
Expand
Business Overview
Business Description
History & Background
Legal Structure
Select... LLC S-Corp C-Corp Sole Proprietorship Partnership
Ownership Type
Select... Single Owner Partnership Family-Owned Absentee-Owned
Hours of Operation
Customer Demographics
Competitive Advantages
Improve Description
Rewrite for Buyers
Expand Advantages
Investment Highlights
Investment Highlights (one per line)
🪄 Generate from Data
Improve
Add More
Location & Market Overview
Location Description & Demographics
🌐 Research Location & Demographics
Improve
Expand
Nearby Demand Drivers / Attractions
Products & Services
Products & Services
Improve
Rewrite for Buyers
Expand
Revenue Mix / Top Revenue Sources
Operations & Staff
Operations Overview
Improve
Make Professional
Staff & Org Notes
Facilities & Lease
Facility & Lease Description
Improve
Make Professional
Financial Summary & Recasted Earnings
Recasted Earnings Notes & Add-Backs
Improve
Make Professional
📊 Financial Documents — AI Auto-Extract
Upload tax returns, P&Ls, or spreadsheets. AI will read the numbers and auto-populate the financial table above. You can upload up to 3 documents (one per year).
✨ Extract Financials from Documents
✕ Clear All
Supports PDF, Word, Excel/CSV, JPG/PNG · Tax returns, P&Ls, QuickBooks exports
Uploaded Documents Preview
These document thumbnails will appear in the printed CIM appendix as source document references.
Competition & Industry Analysis
Competitive Landscape
🌐 Research Competitors
Improve
Industry Analysis & Trends
🌐 Research Industry
Improve
Public Reviews Summary
🌐 Research Public Reviews
Growth Opportunities
Growth Opportunities (one per line)
🪄 Generate from Data
Improve
Add More
Reason for Sale & Transition Support
Reason for Sale
Transition Support Offered
Improve Reason
Improve Transition
Asset List (Included in Sale)
ITEM DESCRIPTION
QTY
FAIR MKT VALUE
+ Add Asset
Asset List Notes
Confidentiality Disclaimer
Disclaimer Text
Additional Appendix Notes
CIM Preview — use Print to save as PDF
🖨 Print / Save PDF
✕ Close
✨ AI Writing Assistant
Processing...
`);
printWin.document.close();
printWin.onload = () => {
setTimeout(() => {
printWin.focus();
printWin.print();
}, 700);
};
}
function buildCIM() {
const t=ST.tpl;
const tc={
1:{bg:'#111111',ac:'#C9A84C',tx:'white',sb:'rgba(255,255,255,0.5)',tf:"'Anton',sans-serif"},
2:{bg:'#1a2744',ac:'#4a90d9',tx:'white',sb:'rgba(255,255,255,0.5)',tf:"'Playfair Display',serif"},
3:{bg:'#1b3a2d',ac:'#5cb87a',tx:'white',sb:'rgba(255,255,255,0.5)',tf:"'Montserrat',sans-serif"},
4:{bg:'#2d0a1e',ac:'#b8436e',tx:'white',sb:'rgba(255,255,255,0.5)',tf:"'Playfair Display',serif"},
}[t];
const bn=g('f-biz-name')||'Business Name';
const bt=g('f-biz-type');
const ap=g('f-asking-price');
const rv=g('f-revenue');
const sd=g('f-sde');
const lc=g('f-location');
const yr=g('f-years');
const bk=g('f-brokerage')||'360 Biz Brokers';
const brn=g('f-broker-name');
function tp(text) {
if(!text) return '';
return text.split('\n').filter(l=>l.trim()).map(l=>`
${l.trim()}
`).join('');
}
function tb(text) {
if(!text) return '';
return `
${text.split('\n').filter(l=>l.trim()).map(l=>`▸ ${l.trim().replace(/^[-•*▸]\s*/,'')} `).join('')} `;
}
function sec(label, title, body) {
if(!body||!body.trim()) return '';
return `
${label?`
${label}
`:''}
${title}
${body}
`;
}
// Cover meta
const metaItems=[['Industry',bt],['Asking Price',ap],['Annual Revenue',rv],['SDE / Owner Benefit',sd],['Location',lc],['Years Established',yr?(yr+' Years'):'']].filter(([,v])=>v);
const coverMeta=metaItems.length?`
${metaItems.map(([k,v])=>`
`).join('')}
`:'';
const brokerLogoHtml=im('broker-logo')?`
`:`
${bk}
`;
const bizLogoHtml=im('biz-logo')?`
`:'';
const coverPhotoHtml=im('cover-photo')?`
`:'';
const badgeHtml=im('broker-badge')?`
`:'';
const brokerPhotoHtml=im('broker-photo')?`
`:`
👤
`;
// KPI grid
const kpis=[[ap,'Asking Price'],[rv,'Annual Revenue'],[sd,'SDE / Owner Benefit'],[yr?yr+' Yrs':'','Years Established']].filter(([v])=>v);
const kpiHtml=kpis.length?`
${kpis.map(([v,l])=>`
`).join('')}
`:'';
// Financial table
const finRows=[['Gross Revenue','f-rev'],['Cost of Goods Sold','f-cogs'],['Gross Profit','f-gp'],['Total Operating Expenses','f-opex'],['Net SDE / Owner Benefit','f-sde']];
const hasFinData=['f-rev1','f-rev2','f-rev3','f-sde1','f-sde2','f-sde3'].some(id=>g(id));
const finTable=hasFinData?`
Financial Metric Year 1 Year 2 Year 3 (Latest) ${finRows.map(([lbl,px],i)=>{const last=i===finRows.length-1;const r1=g(px+'1'),r2=g(px+'2'),r3=g(px+'3');return`${lbl} ${r1||'—'} ${r2||'—'} ${r3||'—'} `}).join('')}
`:'';
// Assets
const assetRows=Array.from(document.querySelectorAll('#asset-rows > div')).map(r=>{const inp=r.querySelectorAll('input');return inp.length>=3?[inp[0].value,inp[1].value,inp[2].value]:null;}).filter(r=>r&&r[0]);
const assetTable=assetRows.length?`
Asset Description Qty FMV ${assetRows.map(([d,q,f])=>`${d} ${q||'1'} ${f||'—'} `).join('')}
`:'';
// Gallery
const gphotos=['p2','p3','p4','p5','p6'].map(k=>im(k)).filter(Boolean);
const gallery=gphotos.length?`
${gphotos.slice(0,6).map(s=>`
`).join('')}
`:'';
// Financial body
const finBody=[
kpiHtml,finTable,
g('f-recast')?`
${tp(g('f-recast'))}
`:'',
[g('f-valuation')&&`
Valuation: ${g('f-valuation')}`,g('f-deal')&&`
Deal Structure: ${g('f-deal')}`].filter(Boolean).join('
'),
].filter(Boolean).join('');
// Broker bio
const brokerBio=(g('f-broker-bio')||g('f-broker-fullname'))?`
Your Listing Broker
Know Your Broker
${brokerPhotoHtml}
${g('f-broker-fullname')||brn}
${g('f-broker-title')||'Principal Broker'}
${g('f-broker-phone')?`
📞 ${g('f-broker-phone')}
`:''}
${g('f-broker-email')?`
✉️ ${g('f-broker-email')}
`:''}
${g('f-broker-website')?`
🌐 ${g('f-broker-website')}
`:''}
${g('f-broker-bio')?`
${g('f-broker-bio')}
`:''}
${badgeHtml}
`:'';
// Info rows helper
function infoRow(items) {
const filled=items.filter(([,v])=>v);
if(!filled.length) return '';
return `
${filled.map(([k,v])=>`${k}: ${v}`).join(' · ')}
`;
}
return `
${brokerLogoHtml}${bizLogoHtml}
Confidential Information Memorandum
${bn}
${bt?`
${bt}
`:''}
${coverMeta}
Presented exclusively by ${bk}${brn?' | '+brn:''}
Strictly Confidential
© ${new Date().getFullYear()} ${bk}
${coverPhotoHtml}
${brokerBio}
${sec('','Executive Summary',tp(g('f-exec')))}
${(g('f-biz-desc')||g('f-biz-history'))?sec('The Opportunity','Business Overview',[tp(g('f-biz-desc')),g('f-biz-history')?`
${tp(g('f-biz-history'))}
`:'',infoRow([['Legal',g('f-legal')],['Ownership',g('f-ownership')],['Hours',g('f-hours')]]),g('f-customers')?`
Customer Demographics: ${g('f-customers')}
`:'' ].filter(Boolean).join('')):''}
${g('f-highlights')?sec('Why Buy','Investment Highlights',tb(g('f-highlights'))):''}
${gallery}
${(g('f-loc-desc')||g('f-demand'))?sec('Market','Location & Market Overview',[lc?`
Area: ${lc}
`:'',tp(g('f-loc-desc')),g('f-demand')?`
Nearby Demand Drivers: ${tp(g('f-demand'))}
`:'' ].filter(Boolean).join('')):''}
${(g('f-products')||g('f-rev-mix'))?sec('Offerings','Products & Services',[tp(g('f-products')),g('f-rev-mix')?`
Revenue Mix: ${g('f-rev-mix')}
`:'' ].filter(Boolean).join('')):''}
${(g('f-ops')||g('f-employees'))?sec('How It Runs','Operations & Staff',[infoRow([['Team',g('f-employees')],['Owner Role',g('f-owner-role')],['Key Staff',g('f-mgmt')]]),tp(g('f-ops')),g('f-staff')?`
${tp(g('f-staff'))}
`:'' ].filter(Boolean).join('')):''}
${(g('f-facilities')||g('f-sqft')||g('f-rent'))?sec('The Space','Facilities & Lease',[infoRow([['Size',g('f-sqft')],['Monthly Rent',g('f-rent')],['Lease Exp.',g('f-lease-exp')]]),tp(g('f-facilities'))].filter(Boolean).join('')):''}
${finBody?sec('Performance','Financial Summary & Recasted Earnings',finBody):''}
${(g('f-competition')||g('f-industry')||g('f-reviews'))?sec('The Market','Competition & Industry Analysis',[g('f-competition')?`
Competitive Landscape ${tp(g('f-competition'))}`:'',g('f-industry')?`
Industry Analysis ${tp(g('f-industry'))}`:'',g('f-reviews')?`
Public Reputation ${tp(g('f-reviews'))}`:''].filter(Boolean).join('')):''}
${g('f-growth')?sec('The Upside','Growth Opportunities',tb(g('f-growth'))):''}
${(g('f-reason')||g('f-transition'))?sec('Next Steps','Reason for Sale & Transition',[g('f-reason')?`
Reason for Sale: ${tp(g('f-reason'))}
`:'',g('f-transition')?`
Transition Support: ${tp(g('f-transition'))}
`:'' ].filter(Boolean).join('')):''}
${(assetTable||g('f-asset-notes'))?sec('Included Assets','Asset List',[assetTable,g('f-asset-notes')?tp(g('f-asset-notes')):'' ].filter(Boolean).join('')):''}
${g('f-disclaimer')?`
Confidentiality Disclaimer
${tp(g('f-disclaimer'))}
${g('f-appendix')?`
${tp(g('f-appendix'))}
`:''}
`:''}
${bk} · Strictly Confidential
Generated ${new Date().toLocaleDateString('en-US',{month:'long',day:'numeric',year:'numeric'})}
`;
}
// ── INTAKE FORM DOWNLOAD ──
function downloadIntakeForm(lang) {
const isES = lang === 'es';
const L = {
title: isES ? 'Formulario de Listado — Cuestionario del Vendedor' : 'Business Listing Intake Form — Seller Questionnaire',
confidential: isES ? 'CONFIDENCIAL' : 'CONFIDENTIAL',
tagline: isES ? 'Complete este formulario y devuélvalo a su corredor. Toda información será mantenida estrictamente confidencial.' : 'Please complete this form and return it to your broker. All information will be kept strictly confidential.',
broker_label: isES ? 'Corredor Asignado' : 'Assigned Broker',
date_label: isES ? 'Fecha' : 'Date',
s1: isES ? 'SECCIÓN 1 — INFORMACIÓN GENERAL DEL NEGOCIO' : 'SECTION 1 — GENERAL BUSINESS INFORMATION',
s2: isES ? 'SECCIÓN 2 — FINANZAS' : 'SECTION 2 — FINANCIALS',
s3: isES ? 'SECCIÓN 3 — OPERACIONES Y PERSONAL' : 'SECTION 3 — OPERATIONS & STAFF',
s4: isES ? 'SECCIÓN 4 — INSTALACIONES Y ARRENDAMIENTO' : 'SECTION 4 — FACILITIES & LEASE',
s5: isES ? 'SECCIÓN 5 — PRODUCTOS Y SERVICIOS' : 'SECTION 5 — PRODUCTS & SERVICES',
s6: isES ? 'SECCIÓN 6 — ACTIVOS INCLUIDOS EN LA VENTA' : 'SECTION 6 — ASSETS INCLUDED IN SALE',
s7: isES ? 'SECCIÓN 7 — RAZÓN DE VENTA Y TRANSICIÓN' : 'SECTION 7 — REASON FOR SALE & TRANSITION',
s8: isES ? 'SECCIÓN 8 — INFORMACIÓN ADICIONAL' : 'SECTION 8 — ADDITIONAL INFORMATION',
disclaimer: isES
? 'Este formulario es estrictamente confidencial y está destinado únicamente para uso interno de 360 Biz Brokers. La información proporcionada se utilizará exclusivamente para la preparación del Memorando de Información Confidencial (CIM).'
: 'This form is strictly confidential and intended solely for internal use by 360 Biz Brokers. Information provided will be used exclusively for preparation of the Confidential Information Memorandum (CIM).',
fields: isES ? {
biz_name: 'Nombre Legal del Negocio', dba: 'Nombre Comercial (DBA)', biz_type: 'Tipo de Negocio / Industria',
founded: 'Año de Fundación', years: 'Años en Operación', address: 'Dirección Completa',
city_state: 'Ciudad, Estado, Código Postal', phone: 'Teléfono del Negocio', website: 'Sitio Web',
legal_structure: 'Estructura Legal (LLC, Corp, etc.)', ein: 'EIN / Número de Identificación Fiscal',
owner_name: 'Nombre del Propietario', owner_phone: 'Teléfono del Propietario', owner_email: 'Correo del Propietario',
asking_price: 'Precio Pedido', price_rationale: 'Justificación del Precio',
rev_yr1: 'Ingresos Brutos — Año 1 ($)', rev_yr2: 'Ingresos Brutos — Año 2 ($)', rev_yr3: 'Ingresos Brutos — Año 3 / Más Reciente ($)',
cogs_yr3: 'Costo de Ventas — Año 3 ($)', opex_yr3: 'Gastos Operativos Totales — Año 3 ($)',
owner_salary: 'Salario del Propietario Incluido en Gastos ($)', sde: 'SDE / Beneficio Neto del Propietario ($)',
addbacks: 'Gastos Adicionales (add-backs) y Gastos Personales', taxes_current: '¿Impuestos al Día?',
employees_ft: 'Empleados de Tiempo Completo', employees_pt: 'Empleados de Tiempo Parcial',
owner_hours: 'Horas del Propietario por Semana', owner_role: 'Rol del Propietario en el Negocio',
key_staff: 'Personal Clave y Sus Roles', staff_staying: '¿El Personal Clave Continuará?',
systems: 'Sistemas / Software / POS Utilizados', suppliers: 'Proveedores Principales',
sqft: 'Metros Cuadrados del Local', rent: 'Renta Mensual ($)', lease_exp: 'Vencimiento del Arrendamiento',
lease_options: 'Opciones de Renovación del Arrendamiento', parking: 'Estacionamiento Disponible',
landlord: 'Relación con el Arrendador', real_estate: '¿Se Incluye Bienes Raíces?',
products_desc: 'Descripción de Productos y/o Servicios', revenue_mix: 'Mezcla de Ingresos (% por línea de producto/servicio)',
customers: 'Perfil del Cliente Principal', contracts: 'Contratos o Clientes Recurrentes',
ffe_desc: 'Describa el FF&E Incluido (muebles, equipos, accesorios)',
inventory: 'Valor Estimado del Inventario ($)', vehicles: 'Vehículos Incluidos',
ip: 'Propiedad Intelectual (marcas, patentes, dominio web)',
reason: 'Razón para Vender', timeline: 'Línea de Tiempo Deseada para Cerrar',
training: 'Capacitación / Soporte de Transición Ofrecido', seller_financing: '¿Considera Financiamiento del Vendedor?',
competition: 'Principales Competidores', advantages: 'Ventajas Competitivas',
growth: 'Oportunidades de Crecimiento', risks: 'Riesgos o Desafíos a Revelar',
pending_legal: 'Litigios Pendientes o Problemas Legales', licenses: 'Licencias y Permisos Requeridos',
other: 'Información Adicional que el Comprador Debe Conocer'
} : {
biz_name: 'Legal Business Name', dba: 'Trade Name / DBA', biz_type: 'Business Type / Industry',
founded: 'Year Founded', years: 'Years in Operation', address: 'Full Business Address',
city_state: 'City, State, Zip Code', phone: 'Business Phone', website: 'Website',
legal_structure: 'Legal Structure (LLC, Corp, etc.)', ein: 'EIN / Federal Tax ID',
owner_name: 'Owner Full Name', owner_phone: 'Owner Phone', owner_email: 'Owner Email',
asking_price: 'Asking Price', price_rationale: 'Basis for Asking Price',
rev_yr1: 'Gross Revenue — Year 1 ($)', rev_yr2: 'Gross Revenue — Year 2 ($)', rev_yr3: 'Gross Revenue — Year 3 / Most Recent ($)',
cogs_yr3: 'Cost of Goods Sold — Year 3 ($)', opex_yr3: 'Total Operating Expenses — Year 3 ($)',
owner_salary: 'Owner Salary Included in Expenses ($)', sde: 'SDE / Net Owner Benefit ($)',
addbacks: 'Add-Back Items & Personal Expenses Run Through Business', taxes_current: 'Are Business Taxes Current?',
employees_ft: 'Full-Time Employees', employees_pt: 'Part-Time Employees',
owner_hours: 'Owner Hours Per Week', owner_role: 'Owner\'s Role in the Business',
key_staff: 'Key Staff Members & Their Roles', staff_staying: 'Will Key Staff Stay Post-Sale?',
systems: 'Systems / Software / POS Used', suppliers: 'Major Suppliers / Vendors',
sqft: 'Total Square Footage', rent: 'Monthly Rent ($)', lease_exp: 'Lease Expiration Date',
lease_options: 'Lease Renewal Options', parking: 'Parking Available',
landlord: 'Landlord Relationship', real_estate: 'Is Real Estate Included in Sale?',
products_desc: 'Description of Products and/or Services Offered', revenue_mix: 'Revenue Mix (% breakdown by product/service line)',
customers: 'Primary Customer Profile / Demographics', contracts: 'Recurring Contracts or Key Accounts',
ffe_desc: 'Describe FF&E Included (furniture, fixtures, equipment)',
inventory: 'Estimated Inventory Value at Time of Sale ($)', vehicles: 'Vehicles Included',
ip: 'Intellectual Property (trademarks, patents, website domain)',
reason: 'Reason for Selling', timeline: 'Desired Closing Timeline',
training: 'Training / Transition Support Offered', seller_financing: 'Open to Seller Financing?',
competition: 'Primary Competitors', advantages: 'Key Competitive Advantages',
growth: 'Untapped Growth Opportunities', risks: 'Known Risks or Challenges to Disclose',
pending_legal: 'Pending Litigation or Legal Issues', licenses: 'Licenses & Permits Required to Operate',
other: 'Any Other Information a Buyer Should Know'
}
};
const F = L.fields;
function row(label, lines=1, hint='') {
const h = lines === 1
? `
`
: `
`;
return `
${label}${hint?` — ${hint} `:''}
${h}
`;
}
function section(num, title, body) {
return `
`;
}
function twoCol(f1, f2, lines=1) {
return `
${row(f1,lines)}${row(f2,lines)}
`;
}
function threeCol(f1, f2, f3) {
return `
${row(f1)}${row(f2)}${row(f3)}
`;
}
const html = `
${L.title} — 360 Biz Brokers
360 Biz Brokers — Listing Intake Form Preview
🖨 Print / Save as PDF
360 BIZ BROKERS
360bizbrokers.com · (954) 325-2625 · val@360bizbrokers.com
${section(1, L.s1, `
${twoCol(F.biz_name, F.dba)}
${twoCol(F.biz_type, F.legal_structure)}
${threeCol(F.founded, F.years, F.ein)}
${row(F.address)}
${twoCol(F.city_state, F.phone)}
${twoCol(F.website, '')}
${threeCol(F.owner_name, F.owner_phone, F.owner_email)}
${twoCol(F.asking_price, F.price_rationale)}
`)}
${section(2, L.s2, `
${threeCol(F.rev_yr1, F.rev_yr2, F.rev_yr3)}
${threeCol(F.cogs_yr3, F.opex_yr3, F.owner_salary)}
${twoCol(F.sde, F.taxes_current)}
${row(F.addbacks, 3, isES?'Describa cada partida y monto':'Describe each item and amount')}
`)}
${section(3, L.s3, `
${threeCol(F.employees_ft, F.employees_pt, F.owner_hours)}
${row(F.owner_role, 2)}
${row(F.key_staff, 3, isES?'Nombre, cargo, salario, años en la empresa':'Name, title, salary, years with company')}
${twoCol(F.staff_staying, F.systems)}
${row(F.suppliers, 2)}
`)}
${section(4, L.s4, `
${threeCol(F.sqft, F.rent, F.lease_exp)}
${twoCol(F.lease_options, F.parking)}
${twoCol(F.landlord, F.real_estate)}
`)}
${section(5, L.s5, `
${row(F.products_desc, 4)}
${row(F.revenue_mix, 2, isES?'ej. 60% comidas, 25% catering':'e.g. 60% dine-in, 25% catering')}
${row(F.customers, 2)}
${row(F.contracts, 2)}
`)}
${section(6, L.s6, `
${row(F.ffe_desc, 4, isES?'Liste los equipos más importantes y su estado':'List major equipment items and condition')}
${threeCol(F.inventory, F.vehicles, F.ip)}
`)}
${section(7, L.s7, `
${row(F.reason, 3)}
${twoCol(F.timeline, F.seller_financing)}
${row(F.training, 3)}
`)}
${section(8, L.s8, `
${row(F.competition, 2)}
${row(F.advantages, 2)}
${row(F.growth, 3)}
${row(F.risks, 2)}
${twoCol(F.pending_legal, F.licenses)}
${row(F.other, 3)}
`)}
${isES?'Firma del Propietario':'Owner Signature'}
${isES?'Nombre impreso y fecha':'Printed name and date'}
${isES?'Firma del Corredor':'Broker Signature'}
${isES?'Nombre impreso y fecha':'Printed name and date'}
360 Biz Brokers — A DBA of Florida 360 Realty, LLC · ${isES?'Todos los derechos reservados':'All rights reserved'} © ${new Date().getFullYear()}
${L.confidential}
${L.disclaimer}
`;
// Open in new window and trigger print
const win = window.open('', '_blank');
win.document.write(html);
win.document.close();
// Small delay so fonts load before print dialog
win.onload = () => setTimeout(() => win.focus(), 400);
}
// ── INTAKE EXTRACTION ──
let intakeFileData = null;
let intakeFileType = null;
let intakeFileName = null;
function handleIntakeUpload(input) {
const file = input.files[0];
if (!file) return;
intakeFileName = file.name;
const sizeMB = (file.size / 1024 / 1024).toFixed(1);
// Show preview bar
document.getElementById('intake-preview-bar').style.display = 'flex';
document.getElementById('intake-filename').textContent = file.name;
document.getElementById('intake-filesize').textContent = sizeMB + ' MB · ' + (file.type || 'document');
// Detect file type
const ext = file.name.split('.').pop().toLowerCase();
const isImage = file.type.startsWith('image/');
const mimeMap = {
pdf: 'application/pdf',
docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
doc: 'application/msword',
txt: 'text/plain',
csv: 'text/csv'
};
intakeFileType = isImage ? file.type : (file.type || mimeMap[ext] || 'application/octet-stream');
const reader = new FileReader();
reader.onload = (e) => {
intakeFileData = e.target.result.split(',')[1];
};
reader.readAsDataURL(file);
// Style the drop zone
const dz = document.getElementById('intake-drop-zone');
dz.style.borderColor = 'var(--gold)';
dz.style.background = 'rgba(201,168,76,0.06)';
}
function clearIntake() {
intakeFileData = null; intakeFileType = null; intakeFileName = null;
document.getElementById('intake-file-input').value = '';
document.getElementById('intake-preview-bar').style.display = 'none';
document.getElementById('intake-status').style.display = 'none';
const dz = document.getElementById('intake-drop-zone');
dz.style.borderColor = 'var(--gold-light)';
dz.style.background = 'rgba(201,168,76,0.04)';
}
async function runIntakeExtraction() {
if (!intakeFileData) { toast('No file loaded','err'); return; }
const statusEl = document.getElementById('intake-status');
statusEl.style.display = 'block';
statusEl.innerHTML = '
AI is reading your intake document and extracting data… this may take 15–30 seconds. ';
const systemPrompt = `You are an expert business broker assistant. You will be given a listing intake form, seller questionnaire, or business document. Extract all available information and return it as a single valid JSON object with these exact keys (use empty string "" if not found):
{
"f-biz-name": "",
"f-biz-type": "",
"f-asking-price": "",
"f-revenue": "",
"f-sde": "",
"f-location": "",
"f-years": "",
"f-brokerage": "",
"f-broker-name": "",
"f-broker-fullname": "",
"f-broker-title": "",
"f-broker-phone": "",
"f-broker-email": "",
"f-broker-website": "",
"f-broker-bio": "",
"f-exec": "",
"f-biz-desc": "",
"f-biz-history": "",
"f-legal": "",
"f-ownership": "",
"f-hours": "",
"f-customers": "",
"f-advantages": "",
"f-address": "",
"f-neighborhood": "",
"f-loc-desc": "",
"f-demand": "",
"f-products": "",
"f-rev-mix": "",
"f-employees": "",
"f-owner-role": "",
"f-ops": "",
"f-staff": "",
"f-sqft": "",
"f-rent": "",
"f-lease-exp": "",
"f-facilities": "",
"f-rev1": "",
"f-rev2": "",
"f-rev3": "",
"f-cogs1": "",
"f-cogs2": "",
"f-cogs3": "",
"f-gp1": "",
"f-gp2": "",
"f-gp3": "",
"f-opex1": "",
"f-opex2": "",
"f-opex3": "",
"f-sde1": "",
"f-sde2": "",
"f-sde3": "",
"f-recast": "",
"f-valuation": "",
"f-deal": "",
"f-competition": "",
"f-industry": "",
"f-growth": "",
"f-reason": "",
"f-transition": "",
"f-asset-notes": "",
"f-highlights": "",
"f-appendix": ""
}
Rules:
- Return ONLY the raw JSON object. No markdown, no backticks, no explanation.
- For financial fields (f-rev1/2/3, f-sde1/2/3, etc.) use the most recently labeled years available.
- For f-highlights and f-growth, format as one item per line.
- For f-legal, use one of: LLC, S-Corp, C-Corp, Sole Proprietorship, Partnership — or leave blank.
- For f-ownership, use one of: Single Owner, Partnership, Family-Owned, Absentee-Owned — or leave blank.
- For f-mgmt, use one of: Yes all key staff staying, Most key staff staying, Varies, Unknown — or leave blank.
- Be comprehensive — extract every piece of information available in the document.`;
try {
// Determine media type and build message content
let msgContent;
if (intakeFileType && intakeFileType.startsWith('image/')) {
msgContent = [
{ type: 'image', source: { type: 'base64', media_type: intakeFileType, data: intakeFileData } },
{ type: 'text', text: 'Extract all business listing information from this intake form image and return as JSON.' }
];
} else {
msgContent = [
{ type: 'document', source: { type: 'base64', media_type: intakeFileType || 'application/octet-stream', filename: intakeFileName || 'uploaded-document', data: intakeFileData } },
{ type: 'text', text: 'Extract all business listing information from this uploaded document and return as JSON.' }
];
}
const payload = {
model: AI_MODEL,
max_tokens: 2000,
system: systemPrompt,
messages: [{ role: 'user', content: msgContent }]
};
const data = await callProxyAI(payload);
const rawText = parseProxyResponse(data);
const extracted = extractJsonObject(rawText);
// Count fields populated
let filled = 0;
Object.entries(extracted).forEach(([k, v]) => {
if (v && v.toString().trim()) {
const el = document.getElementById(k);
if (el) { el.value = v; filled++; }
}
});
updateTopbar();
updateProgress();
saveData();
statusEl.innerHTML = `
✅ Extraction complete — ${filled} fields populated from "${intakeFileName}". Review each section and use AI Assist to refine any content.
`;
toast(`✅ ${filled} fields auto-filled from intake!`);
// Highlight the drop zone
const dz = document.getElementById('intake-drop-zone');
dz.style.borderColor = '#16a34a';
dz.style.background = 'rgba(34,197,94,0.04)';
} catch(err) {
console.error('Intake extraction error:', err);
statusEl.innerHTML = `
❌ Extraction failed: ${err.message}
`;
}
}
// ── FINANCIAL DOCUMENT EXTRACTION ──
const finDocs = { 1: null, 2: null, 3: null };
const finDocTypes = { 1: null, 2: null, 3: null };
const finDocNames = { 1: null, 2: null, 3: null };
const FIN_ICONS = {
pdf: '📄', docx: '📝', doc: '📝',
xlsx: '📊', xls: '📊', csv: '📊',
jpg: '🖼️', jpeg: '🖼️', png: '🖼️', tiff: '🖼️', tif: '🖼️'
};
function handleFinDoc(input, yearNum) {
const file = input.files[0];
if (!file) return;
const ext = file.name.split('.').pop().toLowerCase();
finDocNames[yearNum] = file.name;
const zone = document.getElementById('fin-zone-' + yearNum);
const iconEl = document.getElementById('fin-icon-' + yearNum);
const labelEl = document.getElementById('fin-label-' + yearNum);
const nameEl = document.getElementById('fin-doc-name-' + yearNum);
zone.style.borderColor = 'var(--gold)';
zone.style.background = 'rgba(201,168,76,0.06)';
iconEl.textContent = FIN_ICONS[ext] || '📄';
labelEl.innerHTML = '
' + file.name.substring(0,22) + (file.name.length>22?'…':'') + ' ';
nameEl.textContent = (file.size/1024).toFixed(0) + ' KB';
nameEl.style.display = 'block';
const isWord = ext === 'docx' || ext === 'doc';
const isImg = file.type.startsWith('image/');
finDocTypes[yearNum] = isWord ? 'docx' : (ext === 'pdf' ? 'application/pdf' : (isImg ? file.type : (ext === 'csv' || ext === 'xlsx' || ext === 'xls' ? 'spreadsheet' : 'application/pdf')));
const reader = new FileReader();
if (isWord || ext === 'xlsx' || ext === 'xls' || ext === 'csv') {
reader.onload = (e) => {
const bytes = new Uint8Array(e.target.result);
let bin = '';
bytes.forEach(b => bin += String.fromCharCode(b));
finDocs[yearNum] = btoa(bin);
updateFinImgPreviews();
};
reader.readAsArrayBuffer(file);
} else {
reader.onload = (e) => {
finDocs[yearNum] = e.target.result.split(',')[1];
finDocTypes[yearNum] = isImg ? file.type : 'application/pdf';
updateFinImgPreviews();
};
reader.readAsDataURL(file);
}
}
function updateFinImgPreviews() {
const previewSection = document.getElementById('fin-img-previews');
const grid = document.getElementById('fin-img-grid');
const hasAny = Object.values(finDocNames).some(n => n);
if (!hasAny) { previewSection.style.display = 'none'; return; }
previewSection.style.display = 'block';
grid.innerHTML = '';
[1, 2, 3].forEach(yr => {
if (!finDocNames[yr]) return;
const ext = finDocNames[yr].split('.').pop().toLowerCase();
const isImg = ['jpg','jpeg','png','tiff','tif','gif','webp'].includes(ext);
const cell = document.createElement('div');
cell.style.cssText = 'border:1px solid var(--g200);border-radius:var(--r);overflow:hidden;background:var(--g50)';
if (isImg && finDocs[yr]) {
// Show actual image preview
cell.innerHTML = '
'
+ '
Year ' + yr + ' — ' + finDocNames[yr].substring(0,20) + '
';
} else {
// Show a styled doc tile
const icon = FIN_ICONS[ext] || '📄';
cell.innerHTML = '
'
+ '
' + icon + '
'
+ '
' + finDocNames[yr].substring(0,22) + (finDocNames[yr].length>22?'…':'') + '
'
+ '
'
+ '
Year ' + yr + (yr===3?' (Latest)':'') + '
';
}
grid.appendChild(cell);
});
}
function clearFinDocs() {
[1,2,3].forEach(yr => {
finDocs[yr]=null; finDocTypes[yr]=null; finDocNames[yr]=null;
const inp = document.getElementById('fin-file-'+yr);
if (inp) inp.value='';
const zone = document.getElementById('fin-zone-'+yr);
if (zone) { zone.style.borderColor='var(--g200)'; zone.style.background='var(--g50)'; }
const icon = document.getElementById('fin-icon-'+yr);
if (icon) icon.textContent='📂';
const lbl = document.getElementById('fin-label-'+yr);
if (lbl) lbl.innerHTML = 'Tax Return / P&L
Year ' + yr + (yr===3?' / Latest':'');
const nm = document.getElementById('fin-doc-name-'+yr);
if (nm) nm.style.display='none';
});
document.getElementById('fin-img-previews').style.display='none';
document.getElementById('fin-extract-status').style.display='none';
}
async function runFinExtraction() {
const hasAny = Object.values(finDocs).some(d => d);
if (!hasAny) { toast('Upload at least one financial document first','err'); return; }
const statusEl = document.getElementById('fin-extract-status');
statusEl.style.display='block';
statusEl.innerHTML='
AI is reading your financial documents and extracting numbers… this may take 20–40 seconds. ';
const finSysPrompt = `You are an expert business broker and CPA assistant. You will be given one or more financial documents (tax returns, P&Ls, income statements, spreadsheets). Extract the key financial figures for each year and return ONLY a valid JSON object with these exact keys. Use empty string "" if not found. Format all dollar amounts with $ and commas (e.g. "$485,000").
{
"f-rev1": "",
"f-rev2": "",
"f-rev3": "",
"f-cogs1": "",
"f-cogs2": "",
"f-cogs3": "",
"f-gp1": "",
"f-gp2": "",
"f-gp3": "",
"f-opex1": "",
"f-opex2": "",
"f-opex3": "",
"f-sde1": "",
"f-sde2": "",
"f-sde3": "",
"f-recast": "",
"f-valuation": "",
"year1_label": "",
"year2_label": "",
"year3_label": ""
}
Rules:
- year1_label/year2_label/year3_label should be the actual year (e.g. "2022", "2023", "2024").
- f-rev = Gross Revenue / Total Sales / Total Income
- f-cogs = Cost of Goods Sold / Cost of Sales / Direct Costs
- f-gp = Gross Profit (Revenue minus COGS)
- f-opex = Total Operating Expenses (excluding COGS)
- f-sde = Seller's Discretionary Earnings / Net Income + owner compensation + depreciation + amortization + other add-backs
- f-recast = Summarize any add-back items found (owner salary, personal expenses, depreciation, one-time items)
- For multiple documents assign year numbers based on the document's tax year or date
- Return ONLY raw JSON, no markdown, no explanation`;
try {
// Build message content with all uploaded docs
const msgContent = [];
const yearLabels = ['first','second','third'];
[1,2,3].forEach((yr, i) => {
if (!finDocs[yr]) return;
const ft = finDocTypes[yr] || 'application/pdf';
const isImg = ft.startsWith('image/');
if (isImg) {
msgContent.push({ type:'image', source:{ type:'base64', media_type:ft, data:finDocs[yr] } });
} else {
msgContent.push({ type:'document', source:{ type:'base64', media_type:ft || 'application/octet-stream', filename:finDocNames[yr] || ('financial-year-' + yr), data:finDocs[yr] } });
}
msgContent.push({ type:'text', text:'The ' + yearLabels[i] + ' document above corresponds to Year ' + yr + '.' });
});
msgContent.push({ type:'text', text:'Extract all financial figures from these documents and return as JSON.' });
const payload = {
model:AI_MODEL, max_tokens:1500,
system:finSysPrompt,
messages:[{ role:'user', content:msgContent }]
};
const data = await callProxyAI(payload);
const rawText = parseProxyResponse(data);
const extracted = extractJsonObject(rawText);
const finFields = ['f-rev1','f-rev2','f-rev3','f-cogs1','f-cogs2','f-cogs3','f-gp1','f-gp2','f-gp3','f-opex1','f-opex2','f-opex3','f-sde1','f-sde2','f-sde3','f-recast','f-valuation'];
let filled = 0;
finFields.forEach(k => {
if (extracted[k] && extracted[k].trim()) {
const el = document.getElementById(k);
if (el) { el.value = extracted[k]; filled++; }
}
});
// Update year column headers if we got labels
const headers = document.querySelectorAll('#panel-s-financials table thead th');
if (headers.length >= 4) {
if (extracted.year1_label) headers[1].textContent = extracted.year1_label;
if (extracted.year2_label) headers[2].textContent = extracted.year2_label;
if (extracted.year3_label) headers[3].textContent = extracted.year3_label + ' (Latest)';
}
updateProgress();
saveData();
statusEl.innerHTML='
✅ Extracted ' + filled + ' financial values from ' + Object.values(finDocNames).filter(Boolean).length + ' document(s). Review the table above and adjust any figures as needed.
';
toast('✅ ' + filled + ' financial fields populated!');
} catch(err) {
console.error(err);
statusEl.innerHTML='
❌ Extraction failed: ' + err.message + '
';
}
}
// Drag-and-drop support for intake zone
document.addEventListener('DOMContentLoaded', () => {
const dz = document.getElementById('intake-drop-zone');
if (!dz) return;
dz.addEventListener('dragover', e => { e.preventDefault(); dz.style.borderColor='var(--gold)'; dz.style.background='rgba(201,168,76,0.1)'; });
dz.addEventListener('dragleave', () => { dz.style.borderColor='var(--gold-light)'; dz.style.background='rgba(201,168,76,0.04)'; });
dz.addEventListener('drop', e => {
e.preventDefault();
const file = e.dataTransfer.files[0];
if (file) {
const inp = document.getElementById('intake-file-input');
// Create a DataTransfer to assign file
const dt = new DataTransfer(); dt.items.add(file); inp.files = dt.files;
handleIntakeUpload(inp);
}
});
});
// Spin animation for intake loader
const spinStyle = document.createElement('style');
spinStyle.textContent = '@keyframes spin{to{transform:rotate(360deg)}}';
document.head.appendChild(spinStyle);
// INIT
initAiProxySettings();
loadData();
updateProgress();
setInterval(saveData,120000);