// Auteur : Simon-Pierre Boucher — contact@spboucher.ai /** * Pro forma interactif de l'investisseur — « avec les frais et tout le kit ». * Loyer moyen implicite qui justifie la valeur, cascade revenus → RNE, * financement hypothécaire canadien, cashflow par porte, DSCR, droits de * mutation et liquidités requises. Chaque hypothèse est ajustable en direct. */ "use client"; import { useState } from "react"; import { buildProforma, DEFAULT_PARAMS, tgaReference } from "@/lib/proforma"; import { useLang } from "./LangContext"; const fmt = (v: number, lang: string, frac = 0) => new Intl.NumberFormat(lang === "fr" ? "fr-CA" : "en-CA", { style: "currency", currency: "CAD", maximumFractionDigits: frac, }).format(v); function Slider({ label, value, min, max, step, unit, onChange, }: { label: string; value: number; min: number; max: number; step: number; unit: string; onChange: (v: number) => void; }) { return ( ); } function Row({ k, v, strong, neg }: { k: string; v: string; strong?: boolean; neg?: boolean }) { return (
{k} {v}
); } export default function Proforma({ valeur, doors, valeurRole, municipalite, }: { valeur: number; doors: number; valeurRole: number | null; municipalite: string | null; }) { const { lang } = useLang(); const fr = lang === "fr"; const [taux, setTaux] = useState(DEFAULT_PARAMS.tauxHypoPct); const [mise, setMise] = useState(DEFAULT_PARAMS.miseDeFondsPct); const [amort, setAmort] = useState(DEFAULT_PARAMS.amortAns); const [tga, setTga] = useState(tgaReference(doors)); const [appr, setAppr] = useState(DEFAULT_PARAMS.appreciationPct); const pf = buildProforma(valeur, doors, valeurRole, municipalite, { tauxHypoPct: taux, miseDeFondsPct: mise, amortAns: amort, tgaPct: tga, appreciationPct: appr, }); const DEP_FR: Record = { taxesMunicipales: fr ? "Taxes municipales (≈1,1 % du rôle)" : "Municipal taxes (≈1.1% of roll)", taxeScolaire: fr ? "Taxe scolaire (≈0,1 % du rôle)" : "School tax (≈0.1% of roll)", assurances: fr ? "Assurances" : "Insurance", entretien: fr ? "Entretien et réparations (5 % RB)" : "Maintenance & repairs (5% GI)", gestion: fr ? "Gestion (4 % RB)" : "Management (4% GI)", deneigementPelouse: fr ? "Déneigement et pelouse" : "Snow & lawn", energieCommuns: fr ? "Énergie des espaces communs" : "Common-area energy", reserveRemplacement: fr ? "Réserve de remplacement" : "Replacement reserve", }; return (
{fr ? "Approche revenu — interactif" : "Income approach — interactive"}

{fr ? "Pro forma de l'investisseur" : "Investor pro forma"}

{fr ? `Quelle structure de revenus la valeur estimée suppose-t-elle ? Au TGA choisi, ce ${doors <= 6 ? "plex" : "immeuble"} doit générer un loyer moyen de ` : `What income structure does the estimated value imply? At the chosen cap rate, this building must generate an average rent of `} {fmt(pf.loyerMoyenMensuel, lang)} {fr ? " par porte par mois. Ajustez les hypothèses — tout recalcule." : " per door per month. Adjust the assumptions — everything recomputes."}

{/* hypothèses ajustables */}
{/* état des résultats */}

{fr ? "État des résultats normalisé (annuel)" : "Normalized operating statement (annual)"}

{pf.depenses.map((d) => ( ))}
TGA {fr ? "implicite" : "implied"} : {pf.tgaImplicitePct.toFixed(2)} % MRB : × {pf.mrb.toFixed(1)} {fr ? "RNE/porte" : "NOI/door"} : {fmt(pf.rne / doors, lang)}
{/* financement + acquisition */}

{fr ? "Financement" : "Financing"}

= 1.2 ? "pill-conf-A" : pf.dscr >= 1.0 ? "pill-conf-C" : "pill-conf-D"}`}> DSCR {pf.dscr.toFixed(2)} = 0 ? "pill-conf-B" : "pill-conf-D"}`}> {fmt(pf.cashflowMensuelParPorte, lang)} / {fr ? "porte / mois" : "door / mo"} = 4 ? "pill-conf-A" : pf.cashOnCashPct >= 0 ? "pill-conf-C" : "pill-conf-D"}`}> {fr ? "Rend. liquidités" : "Cash-on-cash"} {pf.cashOnCashPct.toFixed(1)} %

{fr ? "Liquidités requises — tout le kit" : "Cash required — the whole kit"}

{fr ? "Total à prévoir" : "Total to plan"} {fmt(pf.liquiditesRequises, lang)}
{/* projection 5 ans + marges de sécurité */}

{fr ? `Création de richesse — 5 ans (appréciation ${pf.params.appreciationPct} %/an)` : `Wealth creation — 5 years (${pf.params.appreciationPct}%/yr appreciation)`}

= 1.8 ? "pill-conf-A" : pf.multipleLiquidites5Ans >= 1.2 ? "pill-conf-B" : "pill-conf-C"}`}> {fr ? "Multiple sur liquidités à 5 ans" : "5-yr equity multiple"} : × {pf.multipleLiquidites5Ans.toFixed(2)}

{fr ? "Marges de sécurité et sensibilité" : "Safety margins & sensitivity"}

{fr ? "Cashflow selon le taux" : "Cashflow by rate"}

{pf.sensibiliteTaux.map((sc) => ( = 0 ? "pill-conf-B" : "pill-conf-D"}`}> {sc.tauxPct.toFixed(2)} % → {fmt(sc.cashflowAnnuel, lang)}/{fr ? "an" : "yr"} · DSCR {sc.dscr.toFixed(2)} ))}

{fr ? "Pro forma indicatif à hypothèses normalisées (loyer implicite dérivé de la valeur estimée et du TGA — pas des baux réels). Taxes approximées depuis la valeur au rôle. Ne constitue pas un conseil financier." : "Indicative pro forma with normalized assumptions (implied rent derived from estimated value and cap rate — not actual leases). Taxes approximated from assessed value. Not financial advice."}

); }