SPB Git
11.6 KB · 168 lines python
1"""Tests du chapitre 45 (cas 1, maison unifamiliale à Gatineau — DONNÉES SYNTHÉTIQUES) : le dossier ne doit reprendre aucun chiffre2différent des chapitres de méthode (ch. 9, 11, 12, 16, 17, 18, 20, 21, 24, 25) ; identités du financement de l'acheteur ;3chiffres cités dans le texte et les solutions. Tolérances déclarées : montants cités au dollar ±1 $ ; taux ±1e-6 ; identités ±1e-6."""4import sys, pathlib, math5import numpy as np, pytest6sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[1] / "cases"))7from traite import finance as fin, cas18import ch45_dossier as ch9import ch17_ajustements as c1710import ch20_reconciliation as c2011import ch24_depreciation as c2412import ch12_mesurage as c1213import ch09_ratios as c09141516@pytest.fixture(scope="module")17def D():18    return ch.dossier()192021@pytest.fixture(scope="module")22def E(D):23    return ch.exercices(D)242526# ----------------------------------------------------------------------------- héritages : égalité stricte avec les chapitres de méthode27def test_selection_ch16(D):28    assert D["etapes16"] == {"candidats_total": 600, "apres_delai": 236, "apres_rayon": 40, "apres_type": 27, "apres_superficie": 16}29    assert list(D["comparables"]["id_vente"]) == ["G0574", "G0404", "G0468", "G0477", "G0575"]30    assert D["indication_brute16"] == pytest.approx(551_029, abs=1)313233def test_grille_ch17(D):34    b = c17.base(); rec = c17.reconcilier(b)35    assert np.allclose(D["adj17"]["prix_ajuste"].values, b["adj"]["prix_ajuste"].values)36    assert D["adj17"]["prix_ajuste"].round(0).tolist() == [640247, 541222, 572793, 568853, 584862]37    assert np.allclose(D["poids17"], rec["poids_inverse_brut"]) and np.round(D["poids17"], 3).tolist() == [0.179, 0.327, 0.202, 0.159, 0.133]38    assert D["V_comp"] == pytest.approx(rec["inverse_brut"]["valeur_ponderee"]) and D["V_comp"] == pytest.approx(575_565, abs=1)39    assert D["rec17"]["egal"]["valeur_ponderee"] == pytest.approx(581_595, abs=1) and D["rec17"]["egal"]["mediane"] == pytest.approx(572_793, abs=1)404142def test_hedonique_ch18(D):43    t = cas1.tout()44    assert D["V_hed"] == pytest.approx(t["prediction_hedonique"]) and D["V_hed"] == pytest.approx(637_953, abs=1)45    assert D["ip95"][0] == pytest.approx(539_682, abs=1) and D["ip95"][1] == pytest.approx(749_087, abs=1)46    assert D["ip80"][0] == pytest.approx(571_192, abs=1) and D["ip80"][1] == pytest.approx(707_764, abs=1)47    assert D["ic95_valeur"][0] == pytest.approx(625_586, abs=5) and D["ic95_valeur"][1] == pytest.approx(650_596, abs=5)48    assert D["pred18"]["facteur_duan"] == pytest.approx(1.00335, abs=1e-5)495051def test_cout_ch21_ch24_ch25(D):52    assert D["cn"]["total"] == pytest.approx(397_567.33, abs=0.01) and D["cn"]["direct"] == pytest.approx(298_826.17, abs=0.01)53    assert D["cn"]["indirects"] == pytest.approx(62_598.68, abs=0.01) and D["cn"]["incitatif"] == pytest.approx(36_142.49, abs=0.01)54    assert D["V_L"] == 277_000 and D["D_ext"] == pytest.approx(87_461.05, abs=0.01) and D["taux_ext"] == pytest.approx(0.22, abs=5e-5)55    assert D["D_fonc"] == pytest.approx(7_605.31, abs=0.01) and D["D_tot"] == pytest.approx(95_066.36, abs=0.01) and D["taux_tot"] == pytest.approx(0.2391, abs=5e-5)56    assert D["V_cout"] == pytest.approx(579_500.97, abs=0.01) and D["V_cout_arrondie"] == 580_00057    assert D["V_cout"] == pytest.approx(D["V_L"] + D["CN"] - D["D_ext"] - D["D_fonc"])58    assert D["V_ventilee"] == pytest.approx(445_872.14, abs=0.01)59    assert D["comp25"]["reconciliation"]["valeur_ponderee"] == pytest.approx(267_388, abs=1)60    assert D["comp25"]["prediction_hedonique"] == pytest.approx(253_412, abs=1)61    assert D["extr25"]["mediane"] == pytest.approx(276_954, abs=1) and D["extr25"]["n"] == 13 and D["extr25"]["retenue"] == 277_000626364def test_inspection_ch11_mesurage_ch12(D):65    dv = D["dv11"]66    assert dv["age_effectif_structure"] == pytest.approx(29, abs=1e-9) and dv["curable"] == pytest.approx(10_479.98, abs=0.01)67    assert dv["total"] == pytest.approx(221_089.88, abs=0.01) and dv["taux"] == pytest.approx(0.5561, abs=5e-5)68    s = D["sup12"]69    assert s["aire_sol_m2"] == pytest.approx(95.04, abs=1e-6) and s["aire_etages_m2"] == pytest.approx(190.08, abs=1e-6) and s["habitable_m2"] == pytest.approx(165.0, abs=0.01)70    assert s["nette_m2"] == pytest.approx(159.9, abs=0.05) and s["lot_m2"] == pytest.approx(620, abs=0.05) and s["garage_m2"] == pytest.approx(22.33, abs=0.01)71    assert len(D["fiche"]) == 20727374def test_role_taxes_ch09(D):75    assert D["role"] == 468_000 and D["valeur_reelle_2024"] == 573_00076    assert D["PM_fictif"] == pytest.approx(0.9244, abs=5e-5) and D["F_fictif"] == pytest.approx(1.0818, abs=5e-5)77    assert D["taxes"]["total"] == pytest.approx(4_076.01, abs=0.01)78    assert D["ratio_sujet_role"] == pytest.approx(0.8168, abs=5e-5)798081def test_reconciliation_ch20(D):82    r = c20.tout(); cb = r["combinaison"]83    assert D["conclusion"] == cb["conclusion"] == 605_000 and tuple(D["fourchette"]) == (575_000, 640_000)84    assert D["V_raisonnee"] == pytest.approx(604_229.92, abs=0.01)85    assert D["V_raisonnee"] == pytest.approx(0.40 * D["V_comp"] + 0.45 * D["V_hed"] + 0.15 * D["V_cout"])86    assert D["indications"]["cout"] == pytest.approx(D["V_cout"])87    assert D["ecarts_types20"]["comparaison"] == pytest.approx(18_385, abs=1) and D["ecarts_types20"]["hedonique"] == pytest.approx(11_462, abs=1)88    assert D["ecarts_types20"]["cout"] == pytest.approx(23_695, abs=1) and D["sd_conclusion"] == pytest.approx(10_450, abs=1)89    assert tuple(D["bande90"]) == (590_000, 625_000) and D["verite"]["V_true"] == pytest.approx(641_159, abs=1)90    assert D["residu_moyen_retenus"] == pytest.approx(-0.0853, abs=5e-5) and D["V_16"] == pytest.approx(604_689, abs=1)919293# ----------------------------------------------------------------------------- droits de mutation et financement (propres au ch. 45)94def test_mutation(D):95    assert D["base_mutation"] == 605_000  # 605 000 > 468 000 × 1,0296    attendu = 62_900 * 0.005 + (315_000 - 62_900) * 0.01 + (500_000 - 315_000) * 0.015 + (605_000 - 500_000) * 0.0397    assert D["droits_mutation"] == pytest.approx(attendu) and D["droits_mutation"] == pytest.approx(8_760.50, abs=0.01)98    assert D["droits_mutation_general"] == pytest.approx(7_185.50, abs=0.01)99100101def test_mise_de_fonds_et_prime():102    assert ch.mise_de_fonds_minimale(605_000) == pytest.approx(35_500) and ch.mise_de_fonds_minimale(500_000) == pytest.approx(25_000)103    assert ch.mise_de_fonds_minimale(1_000_000) == pytest.approx(75_000)104    with pytest.raises(ValueError):105        ch.mise_de_fonds_minimale(1_500_000)106    assert ch.taux_prime(0.80) == 0 and ch.taux_prime(0.8001) == 0.028 and ch.taux_prime(0.90) == 0.031 and ch.taux_prime(0.9413) == 0.04107    with pytest.raises(ValueError):108        ch.taux_prime(0.96)109110111def test_financement_minimal(D):112    f = D["financement"]["minimale"]; h = f["hypotheque"]113    assert f["mise"] == 35_500 and f["pret_base"] == 569_500 and f["rpv"] == pytest.approx(569_500 / 605_000)114    assert f["taux_prime"] == 0.04 and f["prime"] == pytest.approx(22_780) and f["pret"] == pytest.approx(592_280)115    assert f["taux"] == 0.0412 and f["amortissement_mois"] == 300116    # paiement : taux mensuel équivalent à une composition semestrielle (Loi sur l'intérêt, art. 6)117    r = (1 + 0.0412 / 2) ** (1 / 6) - 1118    assert h.taux_periodique == pytest.approx(r) and r == pytest.approx(fin.taux_mensuel_canadien(0.0412))119    assert f["paiement"] == pytest.approx(592_280 * r / (1 - (1 + r) ** -300)) and f["paiement"] == pytest.approx(3_154.16, abs=0.01)120    assert f["service_dette"] == pytest.approx(12 * f["paiement"]) and f["constante"] == pytest.approx(f["service_dette"] / f["pret"])121    # solde après 60 paiements = valeur actualisée des 240 paiements restants ; intérêts + capital de l'an 1 = 12 paiements122    assert f["solde_terme"] == pytest.approx(f["paiement"] * (1 - (1 + r) ** -240) / r) and f["solde_terme"] == pytest.approx(516_675.31, abs=0.01)123    assert f["interets_an1"] + f["capital_an1"] == pytest.approx(12 * f["paiement"], abs=1e-6)124    assert f["mqr"] == pytest.approx(0.0612) and f["paiement_mqr"] == pytest.approx(3_831.80, abs=0.01)125    assert f["revenu_min_abd"] == pytest.approx((12 * f["paiement_mqr"] + 4_076.01 + 2_400) / 0.39, abs=0.05)126    assert f["revenu_min_atd"] == pytest.approx((12 * f["paiement_mqr"] + 4_076.01 + 2_400 + 6_000) / 0.44, abs=0.05)127    assert f["contrainte_revenu"] == "ABD" and f["liquidites"] == pytest.approx(35_500 + 8_760.50 + 2_050.20, abs=0.01)128129130def test_financement_variantes(D):131    F = D["financement"]132    assert F["10 %"]["prime"] == pytest.approx(16_879.50) and F["10 %"]["paiement"] == pytest.approx(2_989.60, abs=0.01)133    assert F["20 %"]["assure"] is False and F["20 %"]["prime"] == 0 and F["20 %"]["taux"] == 0.0458 and F["20 %"]["mqr"] == pytest.approx(0.0658)134    assert F["20 %"]["paiement"] == pytest.approx(2_700.38, abs=0.01) and F["20 %"]["contrainte_revenu"] == "ATD"135    assert F["minimale 30 ans"]["paiement"] == pytest.approx(2_856.76, abs=0.01)136    # le paiement décroît avec la mise de fonds et avec l'amortissement137    assert F["minimale"]["paiement"] > F["10 %"]["paiement"] > F["20 %"]["paiement"] and F["minimale 30 ans"]["paiement"] < F["minimale"]["paiement"]138139140def test_renouvellement(D):141    r = D["renouvellement"].set_index("hausse_pb")142    assert r.loc[0, "paiement"] == pytest.approx(D["financement"]["minimale"]["paiement"], abs=1e-6)   # même taux → même paiement143    assert r.loc[200, "paiement"] == pytest.approx(3_714.57, abs=0.01) and r.loc[200, "hausse_paiement_pct"] == pytest.approx(0.1777, abs=5e-5)144    assert r.loc[300, "paiement"] == pytest.approx(4_010.94, abs=0.01)145146147def test_valeur_emprunt(D):148    v = D["valeur_emprunt"]149    assert v[575_000.0]["pret_max"] == pytest.approx(546_250) and v[575_000.0]["mise_requise"] == pytest.approx(58_750) and v[575_000.0]["mise_supplementaire"] == pytest.approx(23_250)150    assert v[640_000.0]["base"] == 605_000 and v[640_000.0]["mise_supplementaire"] == 0151152153# ----------------------------------------------------------------------------- exercices154def test_exercices(E, D):155    assert E["ex1"]["par_m2_hab"] == pytest.approx(3_666.67, abs=0.01) and E["ex1"]["par_pi2_hab"] == pytest.approx(340.64, abs=0.01)156    assert E["ex1"]["ecart_pct"] == pytest.approx(0.0979, abs=5e-5)157    assert E["ex2"][575_000.0]["droits"] == pytest.approx(7_860.50) and E["ex2"][640_000.0]["droits"] == pytest.approx(9_810.50)158    assert E["ex2"][640_000.0]["mise"] == pytest.approx(39_000) and E["ex2"][640_000.0]["liquidites"] == pytest.approx(50_974.10, abs=0.01)159    assert E["ex6"][240_000.0]["V_cout"] == pytest.approx(542_500.97, abs=0.01) and E["ex6"][240_000.0]["arrondie"] == 600_000160    assert E["ex6"][285_000.0]["arrondie"] == 605_000 and E["ex6"][240_000.0]["fourchette"] == (540_000, 640_000)161    assert E["ex3"]["20 %"]["economie_paiement_mensuel"] == pytest.approx(453.78, abs=0.01) and E["ex3"]["20 %"]["placement_5ans"] == pytest.approx(85_500 * 1.0358 ** 5)162    assert E["ex3"]["10 %"]["patrimoine_net_60_vs_min"] == pytest.approx(36_829.60, abs=0.01)163    assert E["ex4"][575_000.0]["pret"] == pytest.approx(546_250) and E["ex4"][575_000.0]["prime"] == pytest.approx(21_850) and E["ex4"][575_000.0]["paiement"] == pytest.approx(3_025.39, abs=0.01)164    assert E["ex4"][575_000.0]["revenu_min_abd"] == pytest.approx(129_693, abs=1) and E["ex4"][640_000.0]["pret"] == 569_500165    assert E["ex5"]["variante"]["abd5"] == pytest.approx(0.3414, abs=5e-5) and E["ex5"]["variante"]["revenu5"] == pytest.approx(134_506.57 * 1.025 ** 5, abs=0.5)166    assert E["ex7"]["esperance_p80"] == pytest.approx(603_200) and E["ex7"]["paiement_nonH"] == pytest.approx(3_109.30, abs=0.01)167    assert E["ex7"]["pret_nonH"] == pytest.approx((596_000 - 34_600) * 1.04)168
Files · bfb194a51b558636a70145917941ece87124f2fa
Menu

Theme