French_Legal_Chatbot / Math /IFI /calc_IFI.py
Cassius1Morbant's picture
Upload 17 files
90355ac verified
# calc_IFI.py
import json
import sys
import logging
from pathlib import Path
from typing import Dict, Any
log = logging.getLogger(__name__)
def load_json(file_path: str) -> Dict[str, Any]:
"""Load JSON with helpful error messages."""
try:
with open(file_path, 'r', encoding='utf-8') as f:
return json.load(f)
except FileNotFoundError:
log.error(f"IFI JSON not found: {file_path}")
raise
except json.JSONDecodeError as e:
log.error(f"IFI JSON corrupt ({file_path}): {e}")
raise
def calculate_ifi(
gross_assets: float,
debts: float = 0.0,
primary_residence_value: float = 0.0,
json_file: str | None = None,
) -> dict:
"""
IFI 2025 – progressive brackets + 30 % primary-residence abatement.
"""
# 1. Resolve JSON path
if json_file is None:
json_file = getattr(sys.modules[__name__], "_JSON_PATH", None)
if json_file is None:
# Defensive fallback – same folder as this script
json_file = str(Path(__file__).parent / "ifi_taux.json")
log.debug(f"IFI JSON fallback → {json_file}")
log.info(f"IFI calc: gross={gross_assets:,.0f}€, debt={debts:,.0f}€, primary={primary_residence_value:,.0f}€")
# 2. Load rates
# ------------------------------------------------------------------
data = load_json(json_file)
if not data:
return {
"tax": 0.0,
"net_taxable": 0.0,
"abattement": 0.0,
"abattement_applied": False,
"note": "Impossible de charger les taux IFI."
}
# 3. Net assets + primary-residence abatement
net_assets = gross_assets - debts
abatement = 0.0
abatement_applied = primary_residence_value > 0
if abatement_applied:
abatement = primary_residence_value * data["abattement_primary_residence"]
net_assets -= abatement
net_assets = max(0.0, net_assets)
result = {
"tax": 0.0,
"net_taxable": round(net_assets, 2),
"abattement": round(abatement, 2),
"abattement_applied": abatement_applied,
"note": ""
}
# ------------------------------------------------------------------
# 4. Below threshold → no IFI
# ------------------------------------------------------------------
if net_assets <= data["threshold"]:
result["note"] = (
f"Patrimoine net {net_assets:,.0f} € < seuil {data['threshold']:,.0f} € → pas d'IFI. "
f"{'Abattement 30 % appliqué.' if abatement_applied else 'Aucun abattement.'}"
)
return result
# ------------------------------------------------------------------
# 5. Progressive tax
# ------------------------------------------------------------------
tax = 0.0
remaining = net_assets
prev_max = 0.0
for bracket in data["brackets"]:
current_max = bracket["max"] if bracket["max"] is not None else float("inf")
taxable = min(remaining, current_max - prev_max)
if taxable > 0:
tax += taxable * bracket["rate"]
remaining -= taxable
prev_max = current_max
if remaining <= 0:
break
result["tax"] = round(tax, 2)
result["note"] = (
f"IFI calculé sur {net_assets:,.0f} € net. "
f"{'Abattement 30 % appliqué.' if abatement_applied else 'Aucun abattement.'} "
f"Taux progressifs 2025."
)
log.info(f"IFI result → tax={result['tax']:,}€")
return result