SPB Git forge

spb/uqo-chat

Public
14commits 1branches 0releases
1.4 MBsize
maindefault branch
17 days agolast push
Python 64.6% TypeScript 33.7% CSS 0.8%
2.8 KB · 61 lines python
Raw Blame History
1"""Template: loan amortisation schedule."""23from __future__ import annotations45from typing import Any67from app.tools.coerce import num, pct8910def _f(p: dict[str, Any], key: str, default: float) -> float:11    """Tolerant numeric param; keys ending in `_pct` / starting with `taux` are ratios."""12    raw = p.get(key)13    v = pct(raw, None) if (key.endswith("_pct") or key.startswith("taux")) else num(raw, None)14    return float(default) if v is None else v151617def build(p: dict[str, Any]) -> dict[str, Any]:18    principal = _f(p, "capital", 350000)19    rate = _f(p, "taux_annuel", 0.055)20    years = int(_f(p, "amortissement_ans", 25))21    ppy = int(_f(p, "versements_par_an", 12))22    n_show = min(years * ppy, 360)23    inputs = [24        {"cell": "B4", "label": "Capital emprunté ($)", "value": principal, "format": "currency",25         "name": "Capital"},26        {"cell": "B5", "label": "Taux nominal annuel", "value": rate, "format": "percent",27         "name": "Taux"},28        {"cell": "B6", "label": "Amortissement (ans)", "value": years, "format": "integer"},29        {"cell": "B7", "label": "Versements par an", "value": ppy, "format": "integer"},30        {"cell": "B8", "label": "Taux périodique", "value": "=B5/B7", "format": "percent"},31        {"cell": "B9", "label": "Nombre de versements", "value": "=B6*B7", "format": "integer"},32        {"cell": "B10", "label": "Versement périodique ($)", "value": "=-PMT(B8,B9,B4)",33         "format": "currency", "name": "Versement"},34    ]35    rows = []36    for k in range(1, n_show + 1):37        r = 13 + k38        prev = "B4" if k == 1 else f"F{r - 1}"39        rows.append([k, f"={prev}", "=$B$10", f"=B{r}*$B$8", f"=C{r}-D{r}", f"=B{r}-E{r}"])40    table = {41        "anchor": "A13",42        "columns": [{"header": "Période", "type": "integer"}, {"header": "Solde début ($)", "type": "currency"},43                    {"header": "Versement ($)", "type": "currency"}, {"header": "Intérêts ($)", "type": "currency"},44                    {"header": "Capital ($)", "type": "currency"}, {"header": "Solde fin ($)", "type": "currency"}],45        "rows": rows,46    }47    return {48        "filename": p.get("filename", "tableau_amortissement.xlsx"),49        "style": "uqo",50        "sheets": [{51            "name": "Amortissement",52            "title": "Tableau d'amortissement d'un prêt hypothécaire",53            "inputs": inputs,54            "tables": [table],55            "charts": [{"type": "line", "title": "Solde du prêt", "categories_range": f"A14:A{13 + n_show}",56                        "values_range": f"F14:F{13 + n_show}", "anchor": "H13"}],57            "notes": ["Versement = Capital × FRC = Capital × i / (1 − (1+i)^-n).",58                      "Le taux périodique est le taux nominal divisé par le nombre de versements par an (capitalisation simple)."],59        }],60    }61