"""Méthode du coût : coût neuf, dépréciation (physique, fonctionnelle, externe), extraction de marché. Convention centrale (sans double comptage) : V = V_L + C_N − D_phys − D_fonc − D_ext où C_N est le coût de REMPLACEMENT (ou de reproduction) neuf, incluant coûts directs, indirects et incitatif de l'entrepreneur, et où chaque dépréciation est mesurée sur la base de coût restante après les précédentes (ordre : physique curable → physique incurable → fonctionnelle → externe).""" from __future__ import annotations from dataclasses import dataclass, field from typing import Sequence # ----------------------------------------------------------------------------- coût neuf def cout_neuf(cout_direct: float, taux_indirects: float, taux_incitatif: float, base_incitatif: str = "direct+indirect") -> dict: """Coût neuf = direct + indirects (taux × direct) + incitatif de l'entrepreneur. base_incitatif : 'direct' ou 'direct+indirect' (convention à déclarer).""" indirects = cout_direct * taux_indirects base = cout_direct if base_incitatif == "direct" else cout_direct + indirects incitatif = base * taux_incitatif return {"direct": cout_direct, "indirects": indirects, "incitatif": incitatif, "total": cout_direct + indirects + incitatif} def indexer_cout(cout_base: float, indice_base: float, indice_courant: float) -> float: """Actualisation d'un coût historique par ratio d'indices : C_t = C_0 × I_t / I_0.""" return cout_base * indice_courant / indice_base def cout_par_superficie_ajuste(cout_unitaire_ref: float, superficie_ref: float, superficie: float, elasticite: float = -0.1) -> float: """Ajustement de taille : coût unitaire ∝ (S/S_ref)^élasticité (économies d'échelle si élasticité < 0).""" return cout_unitaire_ref * (superficie / superficie_ref) ** elasticite def cout_par_composantes(quantites: dict[str, float], prix_unitaires: dict[str, float]) -> dict: """Métré × prix unitaires → coût direct par composante et total.""" detail = {k: quantites[k] * prix_unitaires[k] for k in quantites} return {"detail": detail, "total": sum(detail.values())} # ----------------------------------------------------------------------------- dépréciation def depreciation_age_vie(cout_neuf_: float, age_effectif: float, vie_economique: float) -> float: """Méthode âge-vie (linéaire) : D = C_N × âge effectif / vie économique totale (plafonnée à C_N).""" if vie_economique <= 0: raise ValueError("vie économique > 0") return cout_neuf_ * min(age_effectif / vie_economique, 1.0) def depreciation_age_vie_modifiee(cout_neuf_: float, cout_curable: float, age_effectif: float, vie_economique: float) -> dict: """Âge-vie modifiée : on retire d'abord les éléments curables, puis on applique le ratio au reste.""" reste = cout_neuf_ - cout_curable incurable = depreciation_age_vie(reste, age_effectif, vie_economique) return {"curable": cout_curable, "incurable": incurable, "total": cout_curable + incurable} def vie_restante(vie_economique: float, age_effectif: float) -> float: return max(vie_economique - age_effectif, 0.0) @dataclass class Composante: nom: str cout: float age_effectif: float vie_utile: float courte_duree: bool = True def depreciation_ventilee(cout_neuf_total: float, curable: float, composantes_courte: Sequence[Composante], age_effectif_structure: float, vie_economique_structure: float) -> dict: """Méthode ventilée (breakdown) de la dépréciation physique : curable (coût de réparation) + incurable de courte durée (par composante, âge/vie utile) + incurable de longue durée (structure : (C_N − curable − coût des composantes courte durée) × âge/vie).""" d_courte = {c.nom: c.cout * min(c.age_effectif / c.vie_utile, 1.0) for c in composantes_courte} cout_courte = sum(c.cout for c in composantes_courte) base_longue = cout_neuf_total - curable - cout_courte d_longue = depreciation_age_vie(base_longue, age_effectif_structure, vie_economique_structure) total = curable + sum(d_courte.values()) + d_longue return {"curable": curable, "courte_duree": d_courte, "longue_duree": d_longue, "base_longue": base_longue, "total": total} def desuetude_fonctionnelle_curable(cout_correction: float, cout_si_neuf: float, cout_deja_compte: float = 0.0) -> float: """Désuétude fonctionnelle curable (carence ou surabondance) : D = coût de correction (installé) − coût de l'élément s'il avait été inclus à neuf + dépréciation déjà imputée à l'élément existant. Convention : on évite le double comptage en soustrayant ce qui a déjà été compté en dépréciation physique.""" return cout_correction - cout_si_neuf - cout_deja_compte def desuetude_incurable_par_loyer(perte_loyer_mensuelle: float, multiplicateur_loyer_brut: float) -> float: """Désuétude incurable mesurée par capitalisation de la perte de loyer : D = perte mensuelle × MRB mensuel.""" return perte_loyer_mensuelle * multiplicateur_loyer_brut def desuetude_incurable_par_rne(perte_rne_annuelle: float, taux_capitalisation: float, part_batiment: float = 1.0) -> float: """D = (perte de RNE / R) × part attribuable au bâtiment (le reste est imputé au terrain).""" return perte_rne_annuelle / taux_capitalisation * part_batiment def desuetude_externe_ventes_appariees(prix_sans: float, prix_avec: float, part_batiment: float) -> float: """Désuétude externe par ventes appariées : (P_sans − P_avec) × part du bâtiment.""" return (prix_sans - prix_avec) * part_batiment def extraction_depreciation_marche(prix_vente: float, valeur_terrain: float, cout_neuf_: float, age_effectif: float | None = None) -> dict: """Dépréciation totale extraite du marché : D = C_N − (P − V_L) ; taux = D/C_N ; taux annuel = taux/âge.""" contrib_bat = prix_vente - valeur_terrain d = cout_neuf_ - contrib_bat out = {"contribution_batiment": contrib_bat, "depreciation": d, "taux": d / cout_neuf_} if age_effectif: out["taux_annuel"] = out["taux"] / age_effectif return out # ----------------------------------------------------------------------------- indication def valeur_par_cout(valeur_terrain: float, cout_neuf_: float, dep_physique: float, dep_fonctionnelle: float = 0.0, dep_externe: float = 0.0, ameliorations_site: float = 0.0) -> dict: """V = V_L + (C_N − D_phys − D_fonc − D_ext) + améliorations du site (dépréciées séparément).""" contrib = cout_neuf_ - dep_physique - dep_fonctionnelle - dep_externe return {"contribution_batiment": contrib, "valeur": valeur_terrain + contrib + ameliorations_site, "depreciation_totale": dep_physique + dep_fonctionnelle + dep_externe, "taux_depreciation": (dep_physique + dep_fonctionnelle + dep_externe) / cout_neuf_ if cout_neuf_ else 0.0}