SPB Git
20.8 KB · 281 lines python
1"""Tests du chapitre 23 — coûts directs et indirects : structure du devis du cas 1, financement intérimaire (traite.finance),2taxes de vente, incitatif, IPCB par région/type/division (données réelles), multilogement (cas 3) et exercices.3Tolérances : 0,01 $ sur les montants recalculés depuis le devis ; 0,5 $ (ou 1 $ sur les milliers arrondis) sur les montants cités ;41e-9 sur les identités ; valeurs IPCB exactes (lecture du CSV brut de Statistique Canada)."""5import sys, pathlib, math6import numpy as np, pytest7sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[1] / "cases"))8import ch21_cout_neuf as c219import ch23_structure_couts as cas10from traite import cout as ct, finance as fin111213@pytest.fixture(scope="module")14def data():15    devis, sujet = c21.charger()16    cn = c21.cout_neuf_sujet(devis)17    sup = c21.superficies(sujet, devis)18    return {"devis": devis, "sujet": sujet, "cn": cn, "sup": sup, "st": cas.structure(devis, cn), "ri": cas.ratios_indirects(devis, cn),19            "i": cas.taux_affiche_5ans(), "ipcb": cas.ipcb_long(), "ipcb21": c21.ipcb_montreal()}202122class TestStructure:23    def test_coherence_ch21(self, data):24        cn = data["cn"]25        assert cn["direct"] == pytest.approx(298826.17, abs=0.01) and cn["indirects"] == pytest.approx(62598.68, abs=0.01)26        assert cn["incitatif"] == pytest.approx(36142.49, abs=0.01) and cn["total"] == pytest.approx(397567.33, abs=0.01)27        assert cn["taux_indirects"] == pytest.approx(0.2095, abs=5e-5)2829    def test_structure_complete(self, data):30        st, cn = data["st"], data["cn"]31        assert len(st) == 38 and st["nature"].notna().all()32        assert st.loc[~st["indirect"], "part_bloc"].sum() == pytest.approx(1.0, abs=1e-9)33        assert st.loc[st["indirect"], "part_bloc"].sum() == pytest.approx(1.0, abs=1e-9)34        assert st["part_CN"].sum() == pytest.approx((cn["direct"] + cn["indirects"]) / cn["total"], abs=1e-9)35        assert st["part_CN"].sum() + cn["incitatif"] / cn["total"] == pytest.approx(1.0, abs=1e-9)36        sup_ = st[st["poste"] == "Supervision de chantier (salaires)"].iloc[0]37        assert sup_["part_bloc"] == pytest.approx(0.5704, abs=5e-4) and sup_["part_CN"] == pytest.approx(0.0898, abs=5e-4)3839    def test_ratios_indirects(self, data):40        ri = data["ri"]41        attendu = {"Supervision de chantier (salaires)": (35708.66, 11.95, 9.88, 8.98), "Plans et honoraires professionnels": (10119.90, 3.39, 2.80, 2.55),42                   "Financement intérimaire": (8529.63, 2.85, 2.36, 2.15), "Permis et études (arpentage, sol)": (4481.67, 1.50, 1.24, 1.13),43                   "Assurances chantier et cautionnement": (3758.82, 1.26, 1.04, 0.95)}44        for _, r in ri.iterrows():45            m, a, b, c = attendu[r["poste"]]46            assert r["montant"] == pytest.approx(m, abs=0.01)47            assert r["pct_D"] * 100 == pytest.approx(a, abs=0.005) and r["pct_DI"] * 100 == pytest.approx(b, abs=0.005) and r["pct_CN"] * 100 == pytest.approx(c, abs=0.005)48        assert ri["pct_D"].sum() == pytest.approx(0.2095, abs=5e-5) and ri["pct_DI"].sum() == pytest.approx(0.1732, abs=5e-5) and ri["pct_CN"].sum() == pytest.approx(0.1575, abs=5e-5)49        assert (ri["pct_D"].sum() - 0.1195) * 100 == pytest.approx(9.00, abs=0.01)5051    def test_parts_nature(self, data):52        pn = cas.parts_nature(data["st"])53        assert pn.sum() == pytest.approx(data["cn"]["direct"], abs=0.01)54        assert pn["mixte"] / data["cn"]["direct"] == pytest.approx(0.645, abs=5e-4)55        assert pn["matériaux"] / data["cn"]["direct"] == pytest.approx(0.217, abs=5e-4)56        assert pn["main-d'œuvre"] / data["cn"]["direct"] == pytest.approx(0.078, abs=5e-4)575859class TestFinancement:60    def test_taux_documentes(self, data):61        assert data["i"] == pytest.approx(0.0619, abs=1e-9)62        fa = cas.taux_fonds_avances()63        assert fa["non_assure_fixe_5ans_plus"] == pytest.approx(0.0458, abs=1e-9) and fa["non_assure_total"] == pytest.approx(0.0434, abs=1e-9)64        assert fa["assure_total"] == pytest.approx(0.0419, abs=1e-9) and fa["non_assure_variable"] == pytest.approx(0.0390, abs=1e-9)6566    def test_formules_interets(self):67        # continu : L i T / 2 ; mensuel sans capitalisation : L i T / 2 × (n + 1) / n ; capitalisation > simple ; mise de fonds d'abord68        L, i, T = 100000.0, 0.06, 1.069        c = fin.interets_intermediaires(L, i, T)["interets"]70        assert c == pytest.approx(3000.0, abs=1e-9)71        m = fin.interets_intermediaires(L, i, T, "mensuel")["interets"]72        assert m == pytest.approx(3000.0 * 13 / 12, abs=1e-9)73        k = fin.interets_intermediaires(L, i, T, "mensuel", capitaliser=True)["interets"]74        assert k > m75        e = fin.interets_intermediaires(0.75 * L, i, T, "continu", mise_de_fonds=0.25 * L, cout_total=L)76        assert e["t0"] == pytest.approx(0.25, abs=1e-12) and e["interets"] == pytest.approx(0.75 * L * i * 0.75 / 2, abs=1e-9)77        assert fin.taux_duree_implicites(3000.0, L) == pytest.approx(0.06, abs=1e-12)78        with pytest.raises(ValueError):79            fin.interets_intermediaires(L, i, 0.0)8081    def test_financement_cas1(self, data):82        fc = cas.financement_cas1(data["devis"], data["cn"], data["i"])83        assert fc["forfait"] == pytest.approx(8529.63, abs=0.01) and fc["base_hors_financement"] == pytest.approx(352895.22, abs=0.01)84        assert fc["interets_continu"] == pytest.approx(5461.05, abs=0.01) and fc["interets_mensuel"] == pytest.approx(6371.23, abs=0.01)85        assert fc["interets_capitalise"] == pytest.approx(6426.29, abs=0.01)86        assert fc["iT_implicite"] == pytest.approx(0.04834, abs=5e-6) and fc["duree_implicite_mois"] == pytest.approx(9.4, abs=0.05)87        assert fc["taux_implicite_6mois"] == pytest.approx(0.0967, abs=5e-5)88        cn6 = cas.cout_neuf_avec_financement(data["devis"], data["cn"], fc["interets_continu"])89        assert cn6["total"] == pytest.approx(394191.90, abs=0.01) and cn6["total"] - data["cn"]["total"] == pytest.approx(-3375.43, abs=0.01)90        # identité : remplacer le forfait par lui-même redonne le coût neuf du ch. 2191        assert cas.cout_neuf_avec_financement(data["devis"], data["cn"], fc["forfait"])["total"] == pytest.approx(data["cn"]["total"], rel=1e-12)9293    def test_grille(self, data):94        fc = cas.financement_cas1(data["devis"], data["cn"], data["i"])95        G = cas.grille_financement(fc["base_hors_financement"], [6, 9, 12, 18], [0.04, 0.05, 0.0619, 0.07, 0.09])96        assert G[0, 0] == pytest.approx(3529, abs=1) and G[0, 2] == pytest.approx(5461, abs=1) and G[3, 4] == pytest.approx(23820, abs=1)97        cn18 = cas.cout_neuf_avec_financement(data["devis"], data["cn"], G[3, 4])["total"]98        assert cn18 == pytest.approx(414387, abs=1)99        # linéarité en durée et en taux100        assert G[2, 0] == pytest.approx(2 * G[0, 0], rel=1e-12) and G[0, 4] == pytest.approx(G[0, 0] * 9 / 4, rel=1e-12)101102103class TestTaxes:104    def test_profils(self, data):105        tp = cas.taxes_profils(data["cn"]["total"])106        occ, prem, inv = (tp.iloc[k] for k in range(3))107        assert occ["prix_avant_taxes"] == pytest.approx(577567.33, abs=0.01)108        assert occ["tps"] == pytest.approx(28878.37, abs=0.01) and occ["tvq"] == pytest.approx(57612.34, abs=0.01)109        assert occ["remb_tps"] == 0.0 and occ["remb_tvq"] == 0.0 and occ["taxes_nettes"] == pytest.approx(86490.71, abs=0.01)110        assert occ["cout_ttc"] == pytest.approx(664058, abs=1) and occ["taux_effectif"] == pytest.approx(0.14975, abs=1e-9)111        assert prem["remb_tps"] == pytest.approx(prem["tps"], rel=1e-12) and prem["taxes_nettes"] == pytest.approx(57612.34, abs=0.01)112        assert prem["cout_ttc"] == pytest.approx(635180, abs=1)113        assert inv["taxes_nettes"] == 0.0 and inv["cout_ttc"] == pytest.approx(577567.33, abs=0.01)114        assert data["cn"]["total"] * (cas.TPS + cas.TVQ) == pytest.approx(59536, abs=1)115116    def test_remboursements(self):117        r = cas.remboursement_tps(340000.0)118        assert r["remboursement_general"] == pytest.approx(6120.0, abs=1e-9) and not r["interpolation_appliquee"]119        assert cas.remboursement_tps(350000.0)["remboursement_general"] == pytest.approx(6300.0, abs=1e-9)120        r4 = cas.remboursement_tps(400000.0)121        assert r4["remboursement_general"] == pytest.approx(3150.0, abs=1e-9) and r4["interpolation_appliquee"]122        assert cas.remboursement_tps(450000.0)["remboursement_general"] == 0.0 and cas.remboursement_tps(600000.0)["remboursement_general"] == 0.0123        assert cas.remboursement_tps(900000.0, premier_acheteur=True)["remboursement"] == pytest.approx(45000.0, abs=1e-9)124        assert cas.remboursement_tps(1250000.0, premier_acheteur=True)["remboursement"] == pytest.approx(0.5 * 62500.0, abs=1e-9)125        assert cas.remboursement_tps(1500000.0, premier_acheteur=True)["remboursement"] == 0.0126        assert cas.remboursement_tps(900000.0, premier_acheteur=True, travaux_apres_20mars2025=False)["remboursement"] == 0.0127        assert cas.remboursement_tvq(250000.0)["remboursement"] == pytest.approx(4987.5, abs=1e-9)128        assert cas.remboursement_tvq(150000.0)["remboursement"] == pytest.approx(0.5 * 0.09975 * 150000.0, abs=1e-9)129        assert cas.remboursement_tvq(300000.0)["remboursement"] == 0.0130131132class TestIncitatif:133    def test_marges(self):134        assert fin.marge_sur_prix(0.10) == pytest.approx(1 / 11, abs=1e-12)135        assert fin.marge_sur_cout(fin.marge_sur_prix(0.137)) == pytest.approx(0.137, abs=1e-12)136        with pytest.raises(ValueError):137            fin.marge_sur_cout(1.0)138139    def test_rendement_implicite(self, data):140        cn = data["cn"]141        rho = cas.incitatif_rendement_implicite(cn["incitatif"], 180000.0, cn["direct"] + cn["indirects"], 0.75)142        assert rho == pytest.approx(0.134, abs=5e-4)143        assert cas.incitatif_rendement_implicite(cn["incitatif"], 180000.0, cn["direct"] + cn["indirects"], 0.5) == pytest.approx(0.200, abs=5e-4)144145    def test_hausse_salariale(self, data):146        D = data["cn"]["direct"]147        assert cas.effet_hausse_salariale(D, 0.40, 0.08) == pytest.approx(9562, abs=1)148        assert cas.effet_hausse_salariale(D, 0.35, 0.08) == pytest.approx(8367, abs=1) and cas.effet_hausse_salariale(D, 0.50, 0.08) == pytest.approx(11953, abs=1)149        assert cas.effet_hausse_salariale(D, 0.40, 0.08) * (1 + data["cn"]["taux_indirects"]) * 1.10 == pytest.approx(12722, abs=1)150151152class TestIPCB:153    def test_couverture(self, data):154        df = data["ipcb"]155        q = df[(df["geo"] == "Québec, Québec") & (df["type"] == "Bâtiments résidentiels [621]")]156        assert q["periode"].iloc[0] == "2023-01" and len(q) == 15157        m = df[(df["geo"] == "Montréal, Québec") & (df["type"] == "Maison individuelle [621112]")]158        assert m["periode"].iloc[0] == "2017-01" and m["periode"].iloc[-1] == "2026-07"159160    def test_valeurs_tableau(self, data):161        df = data["ipcb"]; v = cas.val162        assert v(df, "Montréal, Québec", "Bâtiments résidentiels [621]", "2026-07") == 117.8 and v(df, "Montréal, Québec", "Bâtiments résidentiels [621]", "2025-07") == 108.5163        assert v(df, "Montréal, Québec", "Maison individuelle [621112]", "2026-07") == 116.5 and v(df, "Montréal, Québec", "Maison en rangée [621112]", "2026-07") == 116.2164        assert v(df, "Montréal, Québec", "Immeuble à hauteur restreinte (moins de cinq étages) [62111]", "2026-07") == 118.5165        assert v(df, "Montréal, Québec", "Tour d'habitations (cinq étages ou plus) [621111]", "2026-07") == 119.5166        assert v(df, "Montréal, Québec", "Immeubles d'appartements", "2026-07") == 118.2 and v(df, "Montréal, Québec", "Bâtiments non résidentiels [622]", "2026-07") == 114.4167        assert v(df, "Québec, Québec", "Bâtiments résidentiels [621]", "2026-07") == 124.8 and v(df, "Québec, Québec", "Bâtiments résidentiels [621]", "2023-01") == 98.8168        assert v(df, "Ottawa–Gatineau, partie ontarienne, Ontario/Québec", "Bâtiments résidentiels [621]", "2026-07") == 110.8169        assert v(df, "Composite de quinze régions métropolitaines de recensement", "Bâtiments résidentiels [621]", "2026-07") == 111.4170171    def test_variations(self, data):172        tt = cas.tableau_types_regions(data["ipcb"]).set_index(["geo", "type"])173        r = tt.loc[("Montréal, Québec", "Bâtiments résidentiels [621]")]174        assert r["var_aa"] == pytest.approx(0.0857, abs=5e-4) and r["var_2020"] == pytest.approx(0.620, abs=5e-4) and r["depuis_debut"] == pytest.approx(0.798, abs=5e-4)175        assert tt.loc[("Montréal, Québec", "Immeuble à hauteur restreinte (moins de cinq étages) [62111]"), "var_aa"] == pytest.approx(0.092, abs=5e-4)176        assert tt.loc[("Québec, Québec", "Bâtiments résidentiels [621]"), "var_aa"] == pytest.approx(0.092, abs=5e-4)177        assert tt.loc[("Québec, Québec", "Bâtiments résidentiels [621]"), "depuis_debut"] == pytest.approx(0.263, abs=5e-4)178        assert tt.loc[("Ottawa–Gatineau, partie ontarienne, Ontario/Québec", "Bâtiments résidentiels [621]"), "var_aa"] == pytest.approx(0.046, abs=5e-4)179        assert tt.loc[("Composite de quinze régions métropolitaines de recensement", "Bâtiments résidentiels [621]"), "var_aa"] == pytest.approx(0.027, abs=5e-4)180        va = cas.variations_annuelles(data["ipcb"])181        mi = va[(va["geo"] == "Montréal, Québec") & (va["type"] == "Maison individuelle [621112]")]182        assert mi.loc[mi["var_aa"].idxmax(), "periode"] == "2022-01" and mi["var_aa"].max() == pytest.approx(0.189, abs=5e-4)183        assert mi.loc[mi["var_aa"].idxmin(), "periode"] == "2024-01" and mi["var_aa"].min() == pytest.approx(0.009, abs=5e-4)184185    def test_divisions(self):186        df_div = cas.ipcb_long(("Montréal, Québec",), ("Maison individuelle [621112]",), None)187        dv = cas.divisions_maison(df_div).set_index("division")188        assert dv.loc["Bois, plastiques et composites", "var_2020_2022"] == pytest.approx(0.851, abs=5e-4) and dv.loc["Bois, plastiques et composites", "var_aa"] == pytest.approx(0.087, abs=5e-4)189        assert dv.loc["Béton", "var_2020_2022"] == pytest.approx(0.290, abs=5e-4) and dv.loc["Béton", "var_aa"] == pytest.approx(0.154, abs=5e-4)190        assert dv.loc["Agrégat des divisions", "var_2020_2022"] == pytest.approx(0.360, abs=5e-4) and dv.loc["Agrégat des divisions", "var_aa"] == pytest.approx(0.086, abs=5e-4)191        assert dv.loc["Électricité", "var_2020_2022"] == pytest.approx(0.048, abs=5e-4) and dv.loc["Terrassements", "var_aa"] == pytest.approx(0.017, abs=5e-4)192        assert dv.loc["Exigences générales", "var_aa"] == pytest.approx(0.062, abs=5e-4) and dv.loc["Maçonnerie", "var_aa"] == pytest.approx(0.116, abs=5e-4)193        assert len(dv) == 18 and dv.index[0] == "Bois, plastiques et composites"194195196class TestMultilogement:197    def test_cas3(self, data):198        ml = cas.multilogement_cas3(data["devis"], data["cn"], data["sup"], data["ipcb21"], data["i"])199        assert ml["n_logements"] == 12 and ml["s_locative"] == 825 and ml["s_brute"] == pytest.approx(970.6, abs=0.05)200        assert ml["ratio_ipcb"] == pytest.approx(118.5 / 116.5, rel=1e-12) and ml["cu_direct_cas1"] == pytest.approx(1572.77, abs=0.01)201        assert ml["cu_direct"] == pytest.approx(1599.77, abs=0.01) and ml["direct"] == pytest.approx(1552717.60, abs=0.01)202        assert ml["indirects_detail"]["Supervision de chantier (salaires)"] == pytest.approx(185544.21, abs=0.01)203        assert ml["financement"]["t0"] * 12 == pytest.approx(3.0, abs=1e-9) and ml["financement"]["interets"] == pytest.approx(31922.93, abs=0.01)204        assert ml["indirects"] == pytest.approx(312868.76, abs=0.01) and ml["taux_indirects"] == pytest.approx(0.2015, abs=5e-5)205        assert ml["incitatif"] == pytest.approx(186558.64, abs=0.01) and ml["total"] == pytest.approx(2052145.00, abs=0.01)206        assert ml["par_logement"] == pytest.approx(171012, abs=1) and ml["par_m2_locatif"] == pytest.approx(2487, abs=1) and ml["par_m2_brut"] == pytest.approx(2114, abs=1)207        assert ml["total"] == pytest.approx(ml["direct"] * (1 + ml["taux_indirects"]) * 1.10, rel=1e-12)208        ml18 = cas.multilogement_cas3(data["devis"], data["cn"], data["sup"], data["ipcb21"], 0.07, duree_ans=1.5)209        assert ml18["financement"]["interets"] == pytest.approx(54150.37, abs=0.01) and ml18["total"] == pytest.approx(2076595.18, abs=0.01)210        assert ml18["par_logement"] == pytest.approx(173050, abs=1)211212213class TestSensibilite:214    def test_tornade(self, data):215        t = {o["param"][:10]: o for o in cas.tornade(data["devis"], data["cn"], data["i"])}216        assert t["Durée du c"]["bas"] == pytest.approx(394192, abs=1) and t["Durée du c"]["haut"] == pytest.approx(406206, abs=1)217        assert t["Taux intér"]["bas"] == pytest.approx(395948, abs=1) and t["Taux intér"]["haut"] == pytest.approx(405653, abs=1)218        assert t["Indirects "]["bas"] == pytest.approx(381132, abs=1) and t["Indirects "]["haut"] == pytest.approx(414003, abs=1)219        assert t["Hausse sal"]["bas"] == pytest.approx(397567, abs=1) and t["Hausse sal"]["haut"] == pytest.approx(408086, abs=1)220        assert t["Taux d'inc"]["bas"] == pytest.approx(379496, abs=1) and t["Taux d'inc"]["haut"] == pytest.approx(415639, abs=1)221222223class TestExercices:224    """Chiffres des solutions (solutions/ch23.tex)."""225226    def test_ex1_classement(self):227        D = 12000 + 48000 + 16000 + 2600; I = 9500 + 2800 + 3200 + 7400228        assert D == 78600 and I == 22900229        assert I / D == pytest.approx(0.2913, abs=5e-5) and I / (D + I) == pytest.approx(0.2256, abs=5e-5)230        cn = ct.cout_neuf(D, I / D, 0.10)231        assert cn["total"] == pytest.approx(111650.0, abs=1e-6) and cn["incitatif"] == pytest.approx(10150.0, abs=1e-6)232233    def test_ex2_interets(self):234        c = fin.interets_intermediaires(300000.0, 0.06, 0.75)["interets"]235        m = fin.interets_intermediaires(300000.0, 0.06, 0.75, "mensuel")["interets"]236        k = fin.interets_intermediaires(300000.0, 0.06, 0.75, "mensuel", capitaliser=True)["interets"]237        assert c == pytest.approx(6750.0, abs=1e-9) and m == pytest.approx(7500.0, abs=1e-9) and k == pytest.approx(7600.88, abs=0.01)238        assert fin.taux_duree_implicites(6750.0, 300000.0) == pytest.approx(0.045, abs=1e-12)239240    def test_ex3_taxes(self):241        P = 520000.0242        assert P * cas.TPS == pytest.approx(26000.0) and P * cas.TVQ == pytest.approx(51870.0)243        assert cas.remboursement_tps(P)["remboursement_general"] == 0.0 and cas.remboursement_tvq(P)["remboursement"] == 0.0244        assert cas.remboursement_tps(P, premier_acheteur=True)["remboursement"] == pytest.approx(26000.0)245        assert P + 51870.0 == pytest.approx(571870.0) and P + 26000.0 + 51870.0 == pytest.approx(597870.0)246        assert cas.remboursement_tps(400000.0)["remboursement_general"] == pytest.approx(3150.0)247248    def test_ex4_incitatif(self):249        P, VL, DI = 640000.0, 170000.0, 410000.0250        E = P - VL - DI251        assert E == 60000.0252        mc = E / (VL + DI); mp = E / P253        assert mc == pytest.approx(0.10345, abs=5e-6) and mp == pytest.approx(0.09375, abs=1e-9)254        assert fin.marge_sur_prix(mc) == pytest.approx(mp, abs=1e-12)255        assert cas.incitatif_rendement_implicite(E, VL, DI, 10 / 12) == pytest.approx(0.192, abs=5e-4)256257    def test_ex5_salaires(self, data):258        D = data["cn"]["direct"]; cum = 1.08 * 1.055 - 1259        assert cum == pytest.approx(0.1394, abs=5e-5)260        dD = cas.effet_hausse_salariale(D, 0.40, cum)261        assert dD == pytest.approx(16663, abs=1) and dD / D == pytest.approx(0.0558, abs=5e-5)262        assert D * 0.0857 == pytest.approx(25609, abs=1)263        assert dD * (1 + data["cn"]["taux_indirects"]) * 1.10 == pytest.approx(22168, abs=1)264265    def test_ex6_mise_de_fonds(self, data):266        fc = cas.financement_cas1(data["devis"], data["cn"], data["i"]); C = fc["base_hors_financement"]267        e = fin.interets_intermediaires(0.75 * C, 0.0619, 1.0, "continu", mise_de_fonds=0.25 * C, cout_total=C)268        assert e["t0"] * 12 == pytest.approx(3.0, abs=1e-9) and e["interets"] == pytest.approx(6143.69, abs=0.01)269        assert fin.interets_intermediaires(C, 0.0619, 1.0)["interets"] == pytest.approx(10922.11, abs=0.01)270        assert fin.interets_intermediaires(0.75 * C, 0.0619, 1.0)["interets"] == pytest.approx(8191.58, abs=0.01)271        k = fin.interets_intermediaires(0.75 * C, 0.0619, 1.0, "mensuel", capitaliser=True, mise_de_fonds=0.25 * C, cout_total=C)272        assert k["n_mois"] == 9 and k["interets"] == pytest.approx(6921.07, abs=0.01)273        assert cas.cout_neuf_avec_financement(data["devis"], data["cn"], e["interets"])["total"] == pytest.approx(394942.80, abs=0.01)274275    def test_ex7_multilogement(self, data):276        ml18 = cas.multilogement_cas3(data["devis"], data["cn"], data["sup"], data["ipcb21"], 0.07, duree_ans=1.5)277        ml = cas.multilogement_cas3(data["devis"], data["cn"], data["sup"], data["ipcb21"], data["i"])278        assert ml18["total"] - ml["total"] == pytest.approx(24450.18, abs=0.01)279        assert ml18["par_m2_locatif"] == pytest.approx(2517, abs=1) and ml18["taux_indirects"] == pytest.approx(0.2158, abs=5e-5)280        assert ml18["financement"]["interets"] / ml18["indirects"] == pytest.approx(0.162, abs=5e-4)281
Files · d49e547b9b0535e034d80fed31849741adb255e2
Menu

Theme