Repository Public
1// UQO Éval — Métré : méthode du coût (valeur indiquée) à partir d'une révision d'estimation. Module PUR.2/**3 * Coût à neuf utilisé = coût de la révision × (1 + ajustement de base %) [reproduction ↔ remplacement]4 * Coût à neuf total = coût à neuf utilisé + améliorations du site admissibles5 * D_physique = coût à neuf total × min(âge effectif / vie économique, 90 %) (âge-vie) ou × % saisi6 * D_fonctionnelle = Σ curables (coût de correction) + Σ incurables (perte de valeur)7 * — une déficience « éliminée par la conception de remplacement » n'est PAS déduite8 * quand la base est le coût de REMPLACEMENT (sinon double déduction)9 * D_externe = montant saisi (≥ 0)10 * Améliorations dépréciées = max(0, coût à neuf total − D_phys − D_fonct − D_ext)11 * Valeur indiquée = terrain + améliorations dépréciées12 */13import type { ValuationInput, ValuationResult } from "./types";14import { fromCents, mulCents, pctOf, sumCents, toCents } from "./money";1516export const VALUATION_VERSION = "metre-valuation-1.0";17export const MAX_PHYSICAL_PCT = 90;1819export function computeValuation(v: ValuationInput): ValuationResult {20 const warnings: string[] = [];21 const costNewC = toCents(Math.max(0, v.costNew));22 const basisAdjC = pctOf(costNewC, v.basisAdjustmentPct || 0);23 if (v.basis === "reproduction" && v.basisAdjustmentPct) warnings.push("ajustement de base appliqué sur un coût de reproduction : vérifier qu'il ne s'agit pas d'une obsolescence fonctionnelle déjà comptée");24 const usedC = costNewC + basisAdjC;25 const siteC = toCents(Math.max(0, v.siteImprovements || 0));26 const totalNewC = usedC + siteC;2728 let physicalPct: number;29 if (v.physical.method === "percent") {30 physicalPct = Math.min(MAX_PHYSICAL_PCT, Math.max(0, v.physical.percent ?? 0));31 } else {32 const life = Math.max(1, v.physical.economicLife || 1);33 const age = Math.max(0, v.physical.effectiveAge ?? 0);34 if (v.physical.effectiveAge == null) warnings.push("âge effectif non saisi : dépréciation physique nulle");35 physicalPct = Math.min(MAX_PHYSICAL_PCT, (100 * age) / life);36 }37 const physC = mulCents(totalNewC, physicalPct / 100);3839 const skipped: ValuationResult["functionalSkipped"] = [];40 let curC = 0, incC = 0;41 for (const f of v.functional) {42 const amt = toCents(Math.max(0, f.amount || 0));43 if (v.basis === "replacement" && f.eliminatedByReplacement) { skipped.push({ id: f.id, label: f.label, amount: fromCents(amt) }); continue; }44 if (f.curable) curC += amt; else incC += amt;45 }46 if (skipped.length) warnings.push(`${skipped.length} déficience(s) non déduite(s) : déjà éliminée(s) par la conception de remplacement`);47 const extC = toCents(Math.max(0, v.external.amount || 0));48 let depC = sumCents([physC, curC, incC, extC]);49 if (depC > totalNewC) { warnings.push("dépréciation totale plafonnée au coût à neuf"); depC = totalNewC; }50 const depImpC = totalNewC - depC;51 const landC = toCents(Math.max(0, v.land.value || 0));52 if (!(v.land.value > 0)) warnings.push("valeur du terrain nulle ou absente");53 const valueC = landC + depImpC;54 return {55 costNewUsed: fromCents(usedC), basisAdjustment: fromCents(basisAdjC), siteImprovements: fromCents(siteC), totalCostNew: fromCents(totalNewC),56 physicalPct: Math.round(physicalPct * 100) / 100, physical: fromCents(physC), functionalCurable: fromCents(curC), functionalIncurable: fromCents(incC), functionalSkipped: skipped,57 external: fromCents(extC), totalDepreciation: fromCents(depC), depreciatedImprovements: fromCents(depImpC), landValue: fromCents(landC), indicatedValue: fromCents(valueC),58 steps: [59 { label: v.basis === "replacement" ? "Coût de remplacement à neuf (utilité équivalente)" : "Coût de reproduction à neuf (réplique)", amount: fromCents(usedC) },60 { label: "+ Améliorations du site admissibles", amount: fromCents(siteC) },61 { label: "= Coût à neuf total", amount: fromCents(totalNewC) },62 { label: `− Détérioration physique (${Math.round(physicalPct * 100) / 100} %)`, amount: -fromCents(physC) },63 { label: "− Obsolescence fonctionnelle curable", amount: -fromCents(curC) },64 { label: "− Obsolescence fonctionnelle incurable", amount: -fromCents(incC) },65 { label: "− Obsolescence externe / économique", amount: -fromCents(extC) },66 { label: "= Améliorations dépréciées", amount: fromCents(depImpC) },67 { label: "+ Valeur du terrain", amount: fromCents(landC) },68 { label: "= Valeur indiquée par la méthode du coût", amount: fromCents(valueC) },69 ],70 warnings,71 };72}73