// outils-imm1033.js — calculateurs du cours IMM1033 (Méthodes du coût) window.UQO_TOOLS = [ { id: "formule", title: "Équation générale de la méthode du coût", ref: { label: "Aide-mémoire · formule fondamentale", url: "/aide-memoire/" }, desc: "V = VT + (CN − Dtotale). Le terrain s’évalue séparément et ne se déprécie pas ; la dépréciation totale ventile physique, fonctionnelle et externe (améliorations seulement).", formula: "V = VT + (CN − Dphys − Dfonc − Dext)", inputs: [ { key: "vt", label: "Valeur du terrain (vacant, à son PHMU)", default: 292000, unit: "$", step: 1000 }, { key: "cn", label: "Coût neuf des améliorations", default: 1800000, unit: "$", step: 5000 }, { key: "dphys", label: "Dépréciation physique (curable + CT + LT)", default: 889000, unit: "$", step: 1000 }, { key: "dfonc", label: "Dépréciation fonctionnelle", default: 66000, unit: "$", step: 1000 }, { key: "dext", label: "Dépréciation externe (améliorations seulement)", default: 84500, unit: "$", step: 500 }, { key: "arr", label: "Arrondi de la conclusion", default: 10000, unit: "$", step: 5000 }, ], compute(v) { const d = v.dphys + v.dfonc + v.dext; const amel = v.cn - d; const val = v.vt + amel; return [ { label: "Dépréciation totale", value: d }, { label: "Taux global de dépréciation (D ÷ CN)", value: d / v.cn * 100, fmt: "pct1", cls: d >= v.cn ? "warn" : "" }, { label: "Valeur contributive des améliorations (CN − D)", value: amel, cls: amel < 0 ? "warn" : "good" }, { label: "Part terrain / valeur totale", value: v.vt / val * 100, fmt: "pct1" }, { label: "Valeur par la méthode du coût", value: val, cls: "main" }, { label: "Conclusion arrondie", value: Math.round(val / v.arr) * v.arr }, { label: d >= v.cn ? "⚠ La dépréciation totale ne peut excéder le coût neuf" : "Vérification : Dtotale < CN", value: d < v.cn ? "conforme" : "à revoir", fmt: "text", cls: d >= v.cn ? "warn" : "good" }, ]; }, }, { id: "terrain", title: "Terrain : comparaison directe (grille de 4 comparables)", ref: { label: "Séances 3–4 · évaluation du terrain", url: "/seance/04/" }, desc: "Ajustement de marché (temps) appliqué en premier, puis ajustements de propriété additifs. On ajuste le comparable vers le sujet. Seuils de vraisemblance : net ≤ 15 %, brut ≤ 25 % ; les comparables les moins ajustés pèsent davantage.", formula: "Pajusté = P × (1 + atemps) × (1 + aloc + asup + azon + atopo)", inputs: [ { key: "sup", label: "Superficie du terrain sujet", default: 8500, unit: "pi²", step: 100 }, { key: "g", label: "Taux de variation du marché", default: 6, unit: "%/an", step: 0.5 }, { group: "Comparable 1" }, { key: "p1", label: "Prix", default: 259000, unit: "$", step: 1000 }, { key: "s1", label: "Superficie", default: 8000, unit: "pi²", step: 100 }, { key: "m1", label: "Mois écoulés", default: 6, unit: "mois", step: 1 }, { key: "a1", label: "Ajust. localisation + zonage + topo", default: -3, unit: "%", step: 0.5 }, { group: "Comparable 2" }, { key: "p2", label: "Prix", default: 285000, unit: "$", step: 1000 }, { key: "s2", label: "Superficie", default: 9800, unit: "pi²", step: 100 }, { key: "m2", label: "Mois écoulés", default: 10, unit: "mois", step: 1 }, { key: "a2", label: "Ajust. localisation + zonage + topo", default: 2, unit: "%", step: 0.5 }, { group: "Comparable 3" }, { key: "p3", label: "Prix", default: 241000, unit: "$", step: 1000 }, { key: "s3", label: "Superficie", default: 7200, unit: "pi²", step: 100 }, { key: "m3", label: "Mois écoulés", default: 3, unit: "mois", step: 1 }, { key: "a3", label: "Ajust. localisation + zonage + topo", default: 5, unit: "%", step: 0.5 }, { group: "Comparable 4" }, { key: "p4", label: "Prix", default: 272000, unit: "$", step: 1000 }, { key: "s4", label: "Superficie", default: 8900, unit: "pi²", step: 100 }, { key: "m4", label: "Mois écoulés", default: 14, unit: "mois", step: 1 }, { key: "a4", label: "Ajust. localisation + zonage + topo", default: -8, unit: "%", step: 0.5 }, ], compute(v) { const rows = []; let wsum = 0, vsum = 0; const unit = []; for (let i = 1; i <= 4; i++) { const p = v["p" + i], s = v["s" + i]; const at = v.g / 100 / 12 * v["m" + i]; const pt = p * (1 + at); const upi2 = pt / s; // ajustement de superficie : la valeur unitaire décroît avec la taille (élasticité indicative −0,15) const aSup = -0.15 * (v.sup - s) / s; const aTot = v["a" + i] / 100 + aSup; const uAdj = upi2 * (1 + aTot); const net = at + aTot, brut = Math.abs(at) + Math.abs(v["a" + i] / 100) + Math.abs(aSup); const fiable = Math.abs(net) <= 0.15 && brut <= 0.25; const w = (fiable ? 1 : 0.3) / Math.max(brut, 0.02); wsum += w; vsum += w * uAdj; unit.push(uAdj); rows.push([`C${i}`, p, upi2, net * 100, brut * 100, uAdj]); } const u = vsum / wsum; const val = u * v.sup; return [ { table: { head: ["Comp.", "Prix", "$/pi² (temps)", "Net", "Brut", "$/pi² ajusté"], rows, fmt: [null, "money", "money2", "pct1", "pct1", "money2"] } }, { label: "Fourchette des $/pi² ajustés", value: `${new Intl.NumberFormat("fr-CA", { minimumFractionDigits: 2 }).format(Math.min(...unit))} – ${new Intl.NumberFormat("fr-CA", { minimumFractionDigits: 2 }).format(Math.max(...unit))} $/pi²`, fmt: "text" }, { label: "Valeur unitaire réconciliée (pondérée par la fiabilité)", value: u, fmt: "money2" }, { label: `Valeur du terrain sujet (${new Intl.NumberFormat("fr-CA").format(v.sup)} pi²)`, value: val, cls: "main" }, { label: "Arrondie au 1 000 $", value: Math.round(val / 1000) * 1000 }, ]; }, }, { id: "extraction", title: "Terrain : extraction, affectation et résidu foncier", ref: { label: "Séance 3 · méthodes du terrain", url: "/seance/03/" }, desc: "Trois méthodes d’appoint quand les ventes de terrains vacants manquent. L’extraction retranche la valeur dépréciée des améliorations d’une vente bâtie ; l’affectation applique un ratio de marché ; le résidu foncier capitalise le revenu résiduel du terrain.", formula: "VT = Prix − (CN − D) · VT = Vtotale × ratio · VT = (RNE − Vbât × τbât) ÷ τterrain", inputs: [ { group: "Extraction" }, { key: "prix", label: "Prix de vente de la propriété bâtie", default: 1200000, unit: "$", step: 5000 }, { key: "cn", label: "Coût neuf des améliorations", default: 1050000, unit: "$", step: 5000 }, { key: "dep", label: "Dépréciation totale des améliorations", value: 0, default: 30, unit: "%", step: 1 }, { group: "Affectation" }, { key: "ratio", label: "Ratio terrain / valeur totale observé", default: 35, unit: "%", step: 1 }, { group: "Résidu foncier" }, { key: "rne", label: "RNE stabilisé de la propriété", default: 96000, unit: "$", step: 1000 }, { key: "vb", label: "Valeur du bâtiment neuf au PHMU", default: 900000, unit: "$", step: 5000 }, { key: "tb", label: "Taux de rendement du bâtiment (τbât)", default: 8.5, unit: "%", step: 0.25 }, { key: "tt", label: "Taux de rendement du terrain (τterrain)", default: 6, unit: "%", step: 0.25 }, ], compute(v) { const amel = v.cn * (1 - v.dep / 100); const ext = v.prix - amel; const aff = v.prix * v.ratio / 100; const revBat = v.vb * v.tb / 100; const revTer = v.rne - revBat; const res = revTer / (v.tt / 100); return [ { label: "Valeur dépréciée des améliorations", value: amel }, { label: "Terrain par extraction", value: ext, cls: "main" }, { label: "Ratio terrain implicite (extraction)", value: ext / v.prix * 100, fmt: "pct1" }, { label: "Terrain par affectation", value: aff }, { label: "Revenu attribuable au bâtiment", value: revBat }, { label: "Revenu résiduel du terrain", value: revTer, cls: revTer <= 0 ? "warn" : "" }, { label: "Terrain par résidu foncier", value: res, cls: revTer <= 0 ? "warn" : "" }, { label: "Écart extraction ↔ résidu", value: Math.abs(ext - res) / ext * 100, fmt: "pct1", cls: Math.abs(ext - res) / ext > 0.15 ? "warn" : "good" }, ]; }, }, { id: "harper", title: "Règle de profondeur 4-3-2-1 (Harper) et variante de Davis", ref: { label: "Définition · séance 4", url: "/seance/04/" }, desc: "Répartit la valeur d’un terrain selon la profondeur : le premier quart (côté rue) porte 40 % de la valeur, puis 30 %, 20 % et 10 %. Utile pour ajuster un lot dont la profondeur s’écarte du standard du secteur.", formula: "Harper : 40-30-20-10 · Davis : 50-25-15-10 · valeur d’un lot = valeur standard × Σ des quarts couverts", inputs: [ { key: "vstd", label: "Valeur du lot standard (pleine profondeur)", default: 180000, unit: "$", step: 1000 }, { key: "pstd", label: "Profondeur standard du secteur", default: 120, unit: "pi", step: 5 }, { key: "p", label: "Profondeur du lot sujet", default: 90, unit: "pi", step: 5 }, { key: "regle", label: "Règle", type: "select", default: "harper", options: [{ value: "harper", label: "Harper 4-3-2-1" }, { value: "davis", label: "Davis 50-25-15-10" }] }, ], compute(v) { const parts = v.regle === "davis" ? [0.5, 0.25, 0.15, 0.10] : [0.4, 0.3, 0.2, 0.1]; const q = v.pstd / 4; let ratio = 0; const rows = []; for (let i = 0; i < 4; i++) { const couvert = Math.max(0, Math.min(q, v.p - i * q)) / q; ratio += couvert * parts[i]; rows.push([`Quart ${i + 1} (${Math.round(i * q)}–${Math.round((i + 1) * q)} pi)`, parts[i] * 100, couvert * 100, couvert * parts[i] * 100]); } // au-delà de la profondeur standard : surplus valorisé à 5 % par quart (repère prudent) const extra = v.p > v.pstd ? Math.min(2, (v.p - v.pstd) / q) * 0.05 : 0; ratio += extra; return [ { table: { head: ["Tranche", "Poids", "Couverte", "Contribution"], rows, fmt: [null, "pct1", "pct1", "pct1"] } }, ...(extra ? [{ label: "Surplus de profondeur au-delà du standard (5 % par quart, max 2)", value: extra * 100, fmt: "pct1" }] : []), { label: "Ratio de valeur du lot sujet", value: ratio * 100, fmt: "pct1" }, { label: "Valeur du lot sujet", value: v.vstd * ratio, cls: "main" }, { label: "Valeur au pied linéaire de profondeur (sujet)", value: v.vstd * ratio / v.p, fmt: "money2" }, ]; }, }, { id: "coutneuf", title: "Coût neuf : directs, indirects, profit et indexation", ref: { label: "Séances 5–8 · coût de construction", url: "/seance/08/" }, desc: "Le coût neuf total additionne les coûts directs, les coûts indirects (en % des directs) et le profit entrepreneurial (sur directs + indirects). L’indexation ramène un coût connu à la date d’évaluation par le ratio des indices (IPCB).", formula: "CN = Cdirects + Cindirects + Profit · Profit = (directs + indirects) × taux · Ccible = Cconnu × Icible / Iconnu", inputs: [ { key: "sup", label: "Superficie brute", default: 15000, unit: "pi²", step: 100 }, { key: "cu", label: "Coût direct unitaire (guide, base)", default: 160, unit: "$/pi²", step: 5 }, { key: "mloc", label: "Multiplicateur régional (Gatineau ≈ 0,95, base Toronto 1,00)", default: 0.95, unit: "×", step: 0.01 }, { key: "mq", label: "Multiplicateur de qualité / hauteur / périmètre (combiné)", default: 1.05, unit: "×", step: 0.01 }, { key: "ind", label: "Coûts indirects", default: 19, unit: "% des directs", step: 1 }, { key: "profit", label: "Profit entrepreneurial", default: 12, unit: "% de (directs + indirects)", step: 1 }, { group: "Indexation (IPCB, base 2023 = 100)" }, { key: "ibase", label: "Indice à la date du guide", default: 105.5, unit: "", step: 0.1 }, { key: "icible", label: "Indice à la date d’évaluation", default: 108.7, unit: "", step: 0.1 }, ], compute(v) { const directsBase = v.sup * v.cu * v.mloc * v.mq; const idx = v.icible / v.ibase; const directs = directsBase * idx; const indirects = directs * v.ind / 100; const profit = (directs + indirects) * v.profit / 100; const cn = directs + indirects + profit; return [ { label: "Coûts directs (base guide × multiplicateurs)", value: directsBase }, { label: "Facteur d’indexation Icible / Ibase", value: idx, fmt: "num4" }, { label: "Coûts directs indexés", value: directs }, { label: `Coûts indirects (${v.ind} %)`, value: indirects }, { label: `Profit entrepreneurial (${v.profit} % sur directs + indirects)`, value: profit }, { label: "Coût neuf total", value: cn, cls: "main" }, { label: "Coût neuf au pi²", value: cn / v.sup, fmt: "money2" }, { label: "Majoration totale au-dessus des directs", value: (cn / directs - 1) * 100, fmt: "pct1", cls: (cn / directs - 1) < 0.28 || (cn / directs - 1) > 0.45 ? "warn" : "good" }, ]; }, }, { id: "intercalaires", title: "Intérêts intercalaires et taxes de vente", ref: { label: "Séance 8 · coûts indirects", url: "/seance/08/" }, desc: "Le financement de chantier se calcule sur les décaissements progressifs (facteur ½) ; les TPS/TVQ (14,975 %) s’ajoutent au coût seulement si elles ne sont pas récupérables (occupant, locatif résidentiel) — jamais pour un immeuble commercial à baux taxables.", formula: "I = Cdirects × i × T × ½ · Taxes = coût × 14,975 % (si non récupérables)", inputs: [ { key: "cd", label: "Coûts directs", default: 1350000, unit: "$", step: 5000 }, { key: "i", label: "Taux du prêt de construction (préférentiel + marge)", default: 6.45, unit: "%", step: 0.05 }, { key: "t", label: "Durée du chantier", default: 14, unit: "mois", step: 1 }, { key: "statut", label: "Statut fiscal", type: "select", default: "loc", options: [{ value: "occ", label: "Propriétaire-occupant (remboursements partiels)" }, { value: "loc", label: "Locatif résidentiel neuf (TPS remboursée à 100 %, TVQ partielle)" }, { value: "com", label: "Commercial — CTI/RTI (taxes non incluses)" }] }, ], compute(v) { const I = v.cd * v.i / 100 * (v.t / 12) * 0.5; const tps = v.cd * 0.05, tvq = v.cd * 0.09975; let taxes; if (v.statut === "com") taxes = 0; else if (v.statut === "loc") taxes = tvq * 0.64; // remboursement TVQ partiel indicatif (36 %) else taxes = tps * 0.64 + tvq * 0.5; return [ { label: "Intérêts intercalaires", value: I, cls: "main" }, { label: "En % des coûts directs", value: I / v.cd * 100, fmt: "pct" }, { label: "TPS brute (5 %)", value: tps }, { label: "TVQ brute (9,975 %)", value: tvq }, { label: "Taxes nettes à inclure au coût neuf (selon le statut)", value: taxes, cls: taxes ? "" : "good" }, { label: "Repère : indirects usuels 15–25 % des directs", value: `${new Intl.NumberFormat("fr-CA").format(Math.round(v.cd * 0.15))} – ${new Intl.NumberFormat("fr-CA").format(Math.round(v.cd * 0.25))} $`, fmt: "text" }, ]; }, }, { id: "ventilation", title: "Ventilation de la dépréciation physique (curable, CT, LT)", ref: { label: "Séance 10 · dépréciation physique", url: "/seance/10/" }, desc: "Ordre impératif : la curable (entretien différé) sort d’abord de la base ; les composantes à courte durée de vie se déprécient chacune selon leur âge/vie ; le résidu long terme (coût neuf − curable − coûts neufs CT) se déprécie au ratio âge effectif / vie économique du bâtiment.", formula: "DCT,j = (Cneuf,j − curablej) × âgej/viej · BaseLT = CN − Dcur − Σ Cneuf,j · DLT = BaseLT × âge/vie éco.", inputs: [ { key: "cn", label: "Coût neuf total du bâtiment", default: 1800000, unit: "$", step: 5000 }, { key: "cur", label: "Entretien différé (curable)", default: 45000, unit: "$", step: 1000 }, { group: "Composante CT 1 — toiture" }, { key: "c1", label: "Coût neuf", default: 120000, unit: "$", step: 1000 }, { key: "a1", label: "Âge effectif", default: 12, unit: "ans", step: 1 }, { key: "v1", label: "Durée de vie", default: 20, unit: "ans", step: 1 }, { group: "Composante CT 2 — CVAC" }, { key: "c2", label: "Coût neuf", default: 180000, unit: "$", step: 1000 }, { key: "a2", label: "Âge effectif", default: 15, unit: "ans", step: 1 }, { key: "v2", label: "Durée de vie", default: 22, unit: "ans", step: 1 }, { group: "Composante CT 3 — finitions / planchers" }, { key: "c3", label: "Coût neuf", default: 260000, unit: "$", step: 1000 }, { key: "a3", label: "Âge effectif", default: 9, unit: "ans", step: 1 }, { key: "v3", label: "Durée de vie", default: 15, unit: "ans", step: 1 }, { group: "Long terme" }, { key: "age", label: "Âge effectif global du bâtiment", default: 27, unit: "ans", step: 1 }, { key: "vie", label: "Vie économique du bâtiment", default: 50, unit: "ans", step: 1 }, ], compute(v) { const rows = []; let sumCT = 0, sumCneufCT = 0; for (let j = 1; j <= 3; j++) { const c = v["c" + j], a = v["a" + j], vie = v["v" + j]; const ratio = Math.min(1, a / vie); const d = c * ratio; sumCT += d; sumCneufCT += c; rows.push([`CT ${j}`, c, ratio * 100, d]); } const baseLT = v.cn - v.cur - sumCneufCT; const ratioLT = Math.min(1, v.age / v.vie); const dLT = baseLT * ratioLT; const dphys = v.cur + sumCT + dLT; return [ { table: { head: ["Composante", "Coût neuf", "Âge/vie", "Dépréciation"], rows, fmt: [null, "money", "pct1", "money"] } }, { label: "Dépréciation curable (entretien différé)", value: v.cur }, { label: "Σ dépréciation incurable court terme", value: sumCT }, { label: "Base long terme (CN − curable − Σ coûts neufs CT)", value: baseLT, cls: baseLT <= 0 ? "warn" : "" }, { label: `Dépréciation incurable long terme (${v.age}/${v.vie})`, value: dLT }, { label: "Dépréciation physique totale", value: dphys, cls: "main" }, { label: "En % du coût neuf", value: dphys / v.cn * 100, fmt: "pct1" }, { label: "Vie économique restante du bâtiment", value: Math.max(0, v.vie - v.age), fmt: "int" }, { label: "Comparaison : âge/vie simple (sans ventilation)", value: v.cn * ratioLT }, ]; }, }, { id: "fonctionnelle", title: "Dépréciation fonctionnelle : ajout, substitution, superadéquation, conception", ref: { label: "Séance 11 · dépréciation fonctionnelle", url: "/seance/11/" }, desc: "Quatre gabarits de calcul avec garde-fous anti-double-comptage : la pénalité d’un ajout est l’excès de coût dans l’existant ; la superadéquation retranche la physique déjà comptée ; la conception capitalise la perte de loyer moins la portion terrain.", formula: "Ajout : D = coût dans l’existant − coût au neuf · Substitution : D = existant − neuf + démolition − récupération · Conception : D = perte de loyer ÷ TGA − portion terrain", inputs: [ { group: "Déficience par ajout" }, { key: "aje", label: "Coût d’installation dans l’existant", default: 38000, unit: "$", step: 500 }, { key: "ajn", label: "Coût du même élément dans une construction neuve", default: 25000, unit: "$", step: 500 }, { group: "Déficience par substitution" }, { key: "sex", label: "Coût neuf de l’élément existant non encore déprécié", default: 13000, unit: "$", step: 500 }, { key: "sdem", label: "Démolition de l’existant", default: 4000, unit: "$", step: 500 }, { key: "srec", label: "Valeur de récupération", default: 500, unit: "$", step: 100 }, { key: "ssur", label: "Surcoût d’installation dans l’existant", default: 6000, unit: "$", step: 500 }, { group: "Superadéquation" }, { key: "supc", label: "Excès de coût neuf de l’élément superadéquat", default: 80000, unit: "$", step: 1000 }, { key: "supd", label: "Dépréciation physique déjà comptée sur cet excès", default: 27000, unit: "$", step: 1000 }, { key: "supx", label: "Surcoûts d’exploitation annuels", default: 0, unit: "$/an", step: 100 }, { group: "Obsolescence de conception" }, { key: "perte", label: "Perte de loyer annuelle", default: 18000, unit: "$/an", step: 500 }, { key: "tga", label: "TGA", default: 7, unit: "%", step: 0.25 }, { key: "rt", label: "Portion attribuable au terrain", default: 20, unit: "%", step: 1 }, ], compute(v) { const ajout = Math.max(0, v.aje - v.ajn); const subst = v.ssur + v.sdem - v.srec + v.sex; const supr = v.supc - v.supd + v.supx / (v.tga / 100); const concTot = v.perte / (v.tga / 100); const conc = concTot * (1 - v.rt / 100); return [ { label: "Curable — déficience par ajout (excès de coût)", value: ajout }, { label: "Curable — déficience par substitution", value: subst }, { label: "Incurable — superadéquation (nette de la physique déjà comptée)", value: supr }, { label: "Perte capitalisée (conception), avant allocation", value: concTot }, { label: `Incurable — conception (améliorations, ${100 - v.rt} %)`, value: conc }, { label: "Dépréciation fonctionnelle totale", value: ajout + subst + supr + conc, cls: "main" }, ]; }, }, { id: "externe", title: "Dépréciation externe : capitalisation, ventes appariées, allocation", ref: { label: "Séance 12 · dépréciation économique", url: "/seance/12/" }, desc: "L’externe est toujours incurable et frappe terrain et améliorations : la dépréciation totale (perte de revenu ÷ TGA ou écart de ventes appariées) est allouée aux améliorations selon le ratio de valeur avant dépréciation. La portion terrain vit déjà dans VT.", formula: "Dext,totale = perte de RNE ÷ TGA · Dext,amél. = Dtotale × Vamél. / Vtotale", inputs: [ { key: "perte", label: "Perte de revenu net annuelle attribuable au facteur externe", default: 12000, unit: "$/an", step: 500 }, { key: "tga", label: "TGA", default: 7, unit: "%", step: 0.25 }, { key: "paires", label: "Écart moyen des ventes appariées (affecté vs non affecté)", default: 10, unit: "%", step: 0.5 }, { key: "vprop", label: "Valeur de la propriété non affectée (pour la méthode des paires)", default: 1720000, unit: "$", step: 5000 }, { key: "vt", label: "Valeur du terrain (avant dépréciation)", default: 292000, unit: "$", step: 1000 }, { key: "vam", label: "Valeur des améliorations (avant dépréciation externe)", default: 845000, unit: "$", step: 1000 }, ], compute(v) { const dcap = v.perte / (v.tga / 100); const dpaires = v.vprop * v.paires / 100; const ratioAm = v.vam / (v.vam + v.vt); return [ { label: "Dépréciation externe totale — capitalisation de la perte", value: dcap }, { label: "Dépréciation externe totale — ventes appariées", value: dpaires }, { label: "Ratio améliorations / valeur totale (avant dépréciation)", value: ratioAm * 100, fmt: "pct1" }, { label: "Externe allouée aux améliorations (capitalisation)", value: dcap * ratioAm, cls: "main" }, { label: "Externe allouée aux améliorations (paires)", value: dpaires * ratioAm }, { label: "Portion terrain (déjà reflétée dans VT, ne pas déduire)", value: dcap * (1 - ratioAm) }, { label: "Écart entre les deux mesures", value: Math.abs(dcap - dpaires) / Math.max(dcap, dpaires) * 100, fmt: "pct1", cls: Math.abs(dcap - dpaires) / Math.max(dcap, dpaires) > 0.2 ? "warn" : "good" }, ]; }, }, { id: "equipements", title: "Équipements et améliorations locatives", ref: { label: "Séance 13 · applications spécialisées", url: "/seance/13/" }, desc: "Équipement : âge/vie sur le coût installé (remplacement neuf + installation). Améliorations locatives : la durée de vie utile est le minimum de la vie physique et de la durée de bail restante — le bail limite presque toujours.", formula: "Véquip. = (Crempl. + Cinstall.) × (1 − âge/vie) · Dloc. = âge ÷ min(vie physique, bail restant)", inputs: [ { group: "Équipement" }, { key: "cr", label: "Coût de remplacement neuf", default: 250000, unit: "$", step: 1000 }, { key: "ci", label: "Coût d’installation", default: 35000, unit: "$", step: 1000 }, { key: "ae", label: "Âge effectif", default: 8, unit: "ans", step: 1 }, { key: "ve", label: "Vie économique", default: 20, unit: "ans", step: 1 }, { group: "Améliorations locatives" }, { key: "cal", label: "Coût neuf des améliorations locatives", default: 180000, unit: "$", step: 1000 }, { key: "aal", label: "Âge", default: 3, unit: "ans", step: 1 }, { key: "vph", label: "Vie physique", default: 15, unit: "ans", step: 1 }, { key: "bail", label: "Durée de bail restante (options probables incluses)", default: 4, unit: "ans", step: 1 }, ], compute(v) { const base = v.cr + v.ci; const ratio = Math.min(1, v.ae / v.ve); const veq = base * (1 - ratio); const vieUtile = Math.min(v.vph, v.aal + v.bail); const ratioAl = Math.min(1, v.aal / vieUtile); return [ { label: "Coût installé (base)", value: base }, { label: "Dépréciation âge/vie", value: ratio * 100, fmt: "pct1" }, { label: "Valeur dépréciée de l’équipement", value: veq, cls: "main" }, { label: "Vie utile retenue des améliorations locatives (min. physique, âge + bail)", value: vieUtile, fmt: "int" }, { label: "Taux de dépréciation des améliorations locatives", value: ratioAl * 100, fmt: "pct1" }, { label: "Valeur dépréciée des améliorations locatives", value: v.cal * (1 - ratioAl) }, { label: "Dépréciation annuelle des améliorations locatives", value: v.cal / vieUtile }, ]; }, }, { id: "unites", title: "Conversions d’unités et coûts unitaires", ref: { label: "Aide-mémoire · conversion des unités", url: "/aide-memoire/" }, desc: "Les guides de coûts publient en $/pi² ou $/m² ; ne mélangez jamais les deux dans un même bordereau.", formula: "1 m² ≈ 10,764 pi² · Coût ($/m²) = Coût ($/pi²) × 10,764", inputs: [ { key: "m2", label: "Superficie", default: 1394, unit: "m²", step: 1 }, { key: "cpi2", label: "Coût unitaire", default: 210, unit: "$/pi²", step: 5 }, ], compute(v) { const pi2 = v.m2 * 10.7639; return [ { label: "Superficie en pi²", value: pi2, fmt: "int" }, { label: "Coût unitaire en $/m²", value: v.cpi2 * 10.7639, fmt: "money2", cls: "main" }, { label: "Coût total", value: pi2 * v.cpi2 }, ]; }, }, ];