(function(global) { 'use strict'; function fmt(n) { return '$' + Math.round(n).toLocaleString('en-US'); } const PRINT_LOGO = ``; function printInvoice() { const pricing = global.getPricingConfig(); var state = global.readFormState(); const q = global.SVSQuoteEngine.calculateQuote(state, pricing); const waived = document.getElementById('onboardingWaived')?.checked || false; const feeEl = document.getElementById('oneTimeFee'); const onboardingFee = waived ? 0 : (parseFloat(feeEl?.value) || Math.round(q.MRR / 2)); const waivedAmt = Math.round(q.MRR / 2); const quoteRef = document.getElementById('quoteRef')?.textContent || '—'; const quoteDate = document.getElementById('headerDate')?.textContent || '—'; const client = q.clientName || 'Client'; const repName = document.getElementById('repName')?.value || ''; const quoteNotes = document.getElementById('quoteNotes')?.value || ''; const termLabel = q.contractTerm === '12mo' ? '12-Month Contract — 3% off MRR' : q.contractTerm === '24mo' ? '24-Month Contract — 5% off MRR' : 'Month-to-Month'; // P2: Compute explicit validity date (30 days from now) const now = new Date(); const validUntil = new Date(now.getTime() + 30 * 24 * 60 * 60 * 1000); const validUntilStr = validUntil.toLocaleDateString('en-CA', { year: 'numeric', month: 'long', day: 'numeric' }); const rows = []; const row = (label, detail, amt, sub) => rows.push({ label, detail, amt, sub: !!sub }); if (q.users > 0) { const pkg = q.byol ? 'BYOL — Bring Your Own License' : 'M365 Premium Included (Identity, Email & Protection)'; row(`User Package — ${pkg}`, `${q.users} user${q.users !== 1 ? 's' : ''} × ${fmt(q.baseUserRate)}/mo`, fmt(q.userBase)); if (q.userExt > 0) row(`↳ Extended Hours (+${fmt(pricing.ADDON_EXT_HOURS)}/user)`, '', fmt(q.userExt), true); if (q.userPWM > 0) row(`↳ 1Password Business (+${fmt(pricing.ADDON_1PASSWORD)}/user)`, '', fmt(q.userPWM), true); if (q.userINKY > 0) row(`↳ INKY Pro Upgrade (+${fmt(pricing.ADDON_INKY)}/user)`, '', fmt(q.userINKY), true); if (q.userZT > 0) row(`↳ Zero Trust User (+${fmt(pricing.ADDON_ZERO_TRUST_USER)}/user)`, '', fmt(q.userZT), true); } if (q.endpoints > 0) { row('Endpoint Management', `${q.endpoints} endpoint${q.endpoints !== 1 ? 's' : ''} × ${fmt(pricing.RATE_ENDPOINT)}/mo`, fmt(q.endpointBase)); if (q.endpointUSB > 0) row(`↳ USB Blocking (+${fmt(pricing.ADDON_USB_BLOCKING)}/endpoint)`, '', fmt(q.endpointUSB), true); if (q.endpointBMB > 0) row(`↳ Bare Metal Backup (+${fmt(pricing.ADDON_BARE_METAL_BACKUP)}/endpoint)`, '', fmt(q.endpointBMB), true); } if (q.servers > 0) { row('Server Management', `${q.servers} server${q.servers !== 1 ? 's' : ''} × ${fmt(pricing.RATE_SERVER)}/mo`, fmt(q.serverBase)); } if (q.ztNetTotal > 0) { row('Zero Trust Networking — HaaS', '', fmt(q.ztNetTotal)); if (q.ztNetSeats > 0) row(`↳ ZT Seats (${q.ztSeats} × ${fmt(pricing.ZT_SEAT_RATE)}/mo)`, '', fmt(q.ztNetSeats), true); if (q.ztNetRouters > 0) row(`↳ HaaS Routers (${q.ztRouters} × ${fmt(pricing.ZT_ROUTER_RATE)}/mo)`, '', fmt(q.ztNetRouters), true); } if (q.voipTotal > 0) { const tier = { basic: 'Basic', standard: 'Standard', premium: 'Premium' }[q.voipTier] || 'Basic'; row(`VoIP / UCaaS — ${tier}`, `${q.voipSeats} seat${q.voipSeats !== 1 ? 's' : ''} × $${q.voipSeatRate}/mo`, fmt(q.voipSeatsAmt)); if (q.voipPhoneAmt > 0) row(`↳ Desk Phone HaaS (+${fmt(pricing.VOIP_PHONE_RATE)}/seat)`, '', fmt(q.voipPhoneAmt), true); if (q.voipFaxAmt > 0) row(`↳ Virtual Fax (+${fmt(pricing.VOIP_FAX_RATE)}/mo)`, '', fmt(q.voipFaxAmt), true); } if (q.adminWaived) { row('Site Admin Fee', `Tenant, network, documentation & vendor management (waived; normally ${fmt(q.adminFeeNet)}/mo)`, fmt(0)); } else { row('Site Admin Fee', 'Tenant, network, documentation & vendor management', fmt(q.adminFeeNet)); } row('↳ Base Site Admin', '', fmt(q.siteAdminBase), true); if (q.ztActive) row('↳ Zero Trust Supplement', '', fmt(pricing.ADMIN_FEE_ZT), true); if (q.addPWM && q.admin1PWM > 0) { row(`↳ 1Password Management (${Math.round(pricing.ADMIN_1PWM_PCT * 100)}%)`, '', fmt(q.admin1PWM), true); } const itemsHTML = rows.map(r => ` ${r.label} ${r.detail} ${r.amt}/mo `).join(''); const features = []; const feat = (name, active, detail) => features.push({ name, active, detail: detail || '' }); feat('Licensing Model', true, q.byol ? 'BYOL — Bring Your Own License' : 'M365 Premium Included'); feat('Extended Help Desk Hours', q.addExtHours, q.addExtHours ? `+${fmt(pricing.ADDON_EXT_HOURS)}/user/mo` : ''); feat('1Password Business', q.addPWM, q.addPWM ? `+${fmt(pricing.ADDON_1PASSWORD)}/user/mo` : ''); feat('INKY Pro Upgrade', q.addINKY, q.addINKY ? `+${fmt(pricing.ADDON_INKY)}/user/mo` : ''); feat('Zero Trust User Access', q.addZT, q.addZT ? `+${fmt(pricing.ADDON_ZERO_TRUST_USER)}/user/mo` : ''); feat('USB Device Blocking', q.addUSB, q.addUSB ? `+${fmt(pricing.ADDON_USB_BLOCKING)}/endpoint/mo` : ''); feat('Bare Metal Backup', q.addBMB, q.addBMB ? `+${fmt(pricing.ADDON_BARE_METAL_BACKUP)}/endpoint/mo` : ''); feat('Zero Trust Networking (HaaS)', q.ztNetTotal > 0, q.ztNetTotal > 0 ? `${q.ztSeats} seats, ${q.ztRouters} routers` : ''); feat('VoIP / UCaaS', q.voipTotal > 0, q.voipTotal > 0 ? `${({ basic: 'Basic', standard: 'Standard', premium: 'Premium' })[q.voipTier]} — ${q.voipSeats} seats` : ''); feat('Desk Phone HaaS', q.addVoipPhone, q.addVoipPhone ? `+${fmt(pricing.VOIP_PHONE_RATE)}/seat/mo` : ''); feat('Virtual Fax', q.addVoipFax, q.addVoipFax ? `+${fmt(pricing.VOIP_FAX_RATE)}/mo` : ''); // P5: Split into included vs excluded (CYA) const included = features.filter(f => f.active); const excluded = features.filter(f => !f.active); const includedHTML = included.map(f => `
${f.name} ${f.detail ? `${f.detail}` : ''}
`).join(''); const excludedHTML = excluded.length ? `
Services Not Included in This Quote
${excluded.map(f => `
${f.name}
`).join('')}
` : ''; let totals = ''; if (q.discountPct > 0) { totals += `Base MRR${fmt(q.MRR)}/mo`; totals += `Term Discount (${Math.round(q.discountPct * 100)}% off)−${fmt(q.discountAmt)}/mo`; } totals += `Monthly Recurring (MRR)${fmt(q.effectiveMrr)}/mo`; if (q.users > 0) { totals += `Per-User Effective Cost${fmt(q.effectiveMrr / q.users)}/user/mo`; totals += `${fmt(q.perUserServices)} user services + ${fmt(q.perUserSiteOvhd)} site overhead`; } if (q.hstEnabled) { totals += `Ontario HST (${Math.round(pricing.HST_RATE * 100)}%)+${fmt(q.hstAmt)}/mo`; totals += `Total Monthly${fmt(q.mrrWithHst)}/mo`; } if (waived && waivedAmt > 0) { totals += `Onboarding Fee COMPLIMENTARY — included with ${termLabel.split(' —')[0]}${fmt(waivedAmt)} saved`; } else if (q.contractTerm === '12mo' && onboardingFee > 0) { totals += `Onboarding Fee 50% OFF — 12-Month term${fmt(waivedAmt)} ${fmt(onboardingFee)}`; } else if (onboardingFee > 0) { totals += `Onboarding Fee (one-time)${fmt(onboardingFee)}`; } totals += `Annual Projection${fmt(q.effectiveAnnual)}/yr`; // P1: Notes section (only if notes exist) const notesHTML = quoteNotes.trim() ? `
Notes
${quoteNotes.trim().replace(/&/g,'&').replace(//g,'>').replace(/\n/g,'
')}
` : ''; // P4: Rep name in header const repHTML = repName.trim() ? `
Prepared by: ${repName.trim().replace(/&/g,'&').replace(//g,'>')}
` : ''; const html = ` SVS MSP Quote — ${client}
${PRINT_LOGO}
${quoteRef}
${quoteDate}
MSP Service Proposal
Prepared for
${client}
${termLabel}  ·  ${q.hstEnabled ? 'HST included in figures' : 'Prices in CAD, excl. HST'}
${repHTML}
Service Breakdown
${itemsHTML}
Your Service Configuration
${includedHTML}
${excludedHTML}
Quote Summary
${totals}
${notesHTML}