Remplace les mentions textuelles par des URLs directes dans les docstrings de cca_pool.py, cca_registry.py et le commentaire CCA_CATEGORIES dans store.py. - laws-lois.justice.gc.ca : LIR art. 13, 20 ; Règl. art. 1100 - canada.ca : Guide T2 (T4012) pour l'Annexe 8 / Schedule 8 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
180 lines
7.5 KiB
Python
180 lines
7.5 KiB
Python
"""
|
||
Module fiscal — Mécanique du pool de catégorie (DPA dégressive).
|
||
|
||
L'actif fiscal est un FLUX, pas un objet. La DPA se calcule sur le solde
|
||
global de la catégorie (FNACC), pas pièce par pièce.
|
||
|
||
Mécanique annuelle (déterministe) :
|
||
|
||
FNACC_ouverture
|
||
+ ajouts_annee
|
||
- dispositions_annee (produit de disposition, souvent 0 $)
|
||
= solde_avant_ajustement
|
||
- ajustement (coeff * ajouts_nets) ; demi-année => coeff 0.5
|
||
= base_calcul
|
||
* taux (0.55) = DPA_annee (bornée >= 0)
|
||
FNACC_cloture = solde_avant_ajustement - DPA_annee
|
||
|
||
Perte finale : si l'inventaire PHYSIQUE de la catégorie est vide (plus aucun
|
||
actif) mais que la FNACC reste positive, ce solde devient une perte finale
|
||
déductible à 100 % (DPA = solde, FNACC clôture = 0).
|
||
|
||
Récupération d'amortissement : si la FNACC devient négative (dispositions >
|
||
solde), ce négatif est un revenu (récupération) ; la FNACC clôture est
|
||
ramenée à 0 et le montant est signalé.
|
||
|
||
Le coefficient d'ajustement de l'année d'acquisition est PARAMÉTRABLE
|
||
(demi-année standard = 0.5 ; incitatif accéléré éventuel saisi par l'usager
|
||
sur avis de son CPA). Aucun taux d'accélération n'est câblé en dur.
|
||
|
||
Références législatives :
|
||
- LIR 20(1)(a) — Déduction pour amortissement du revenu d'entreprise ou de bien
|
||
- LIR 13(21) — Définition de la FNACC (fraction non amortie du coût en capital)
|
||
- LIR 13(1) — Récupération d'amortissement (inclusion dans le revenu)
|
||
- LIR 20(16) — Perte finale (déduction du revenu)
|
||
- Règl. 1100(1) — Méthode dégressive ; taux par catégorie (Règl. Annexe II)
|
||
- Règl. 1100(2) — Règle de la demi-année : ajustement sur ajouts nets de l'année
|
||
d'acquisition (coefficient 0,5 par défaut)
|
||
|
||
Textes officiels (Justice Canada) :
|
||
https://laws-lois.justice.gc.ca/fra/lois/I-3.3/section-13.html
|
||
https://laws-lois.justice.gc.ca/fra/lois/I-3.3/section-20.html
|
||
https://laws-lois.justice.gc.ca/fra/reglements/C.R.C.,_ch._945/section-1100.html
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
from dataclasses import dataclass, field
|
||
from decimal import Decimal, ROUND_HALF_UP
|
||
|
||
TWOPLACES = Decimal("0.01")
|
||
|
||
|
||
def _q(x: Decimal) -> Decimal:
|
||
return x.quantize(TWOPLACES, rounding=ROUND_HALF_UP)
|
||
|
||
|
||
@dataclass
|
||
class YearResult:
|
||
year: int
|
||
opening_ucc: Decimal # FNACC d'ouverture
|
||
additions: Decimal # ajouts de l'année
|
||
dispositions: Decimal # produit des dispositions de l'année
|
||
adjustment_coeff: Decimal # coeff sur ajouts nets (0.5 = demi-année)
|
||
adjustment: Decimal # montant retiré de la base de calcul
|
||
cca_rate: Decimal # 0.55
|
||
base_for_cca: Decimal # base après ajustement
|
||
cca_claimed: Decimal # DPA de l'année
|
||
closing_ucc: Decimal # FNACC de clôture
|
||
recapture: Decimal # récupération (revenu) si > 0
|
||
terminal_loss: Decimal # perte finale (déductible) si > 0
|
||
note: str = ""
|
||
|
||
def to_dict(self) -> dict:
|
||
return {
|
||
"year": self.year,
|
||
"opening_ucc": str(self.opening_ucc),
|
||
"additions": str(self.additions),
|
||
"dispositions": str(self.dispositions),
|
||
"adjustment_coeff": str(self.adjustment_coeff),
|
||
"adjustment": str(self.adjustment),
|
||
"cca_rate": str(self.cca_rate),
|
||
"base_for_cca": str(self.base_for_cca),
|
||
"cca_claimed": str(self.cca_claimed),
|
||
"closing_ucc": str(self.closing_ucc),
|
||
"recapture": str(self.recapture),
|
||
"terminal_loss": str(self.terminal_loss),
|
||
"note": self.note,
|
||
}
|
||
|
||
|
||
@dataclass
|
||
class YearInput:
|
||
year: int
|
||
additions: Decimal = Decimal("0")
|
||
dispositions: Decimal = Decimal("0")
|
||
adjustment_coeff: Decimal = Decimal("0.5") # demi-année par défaut
|
||
pool_physically_empty: bool = False # plus aucun actif physique
|
||
claim_full_cca: bool = True # demander la DPA max cette année
|
||
|
||
|
||
def compute_pool_year(opening_ucc: Decimal, yi: YearInput,
|
||
cca_rate: Decimal = Decimal("0.55")) -> YearResult:
|
||
"""Calcule une année du pool de façon déterministe."""
|
||
opening = _q(opening_ucc)
|
||
additions = _q(yi.additions)
|
||
dispositions = _q(yi.dispositions)
|
||
|
||
solde_avant = opening + additions - dispositions
|
||
|
||
recapture = Decimal("0")
|
||
terminal_loss = Decimal("0")
|
||
note = ""
|
||
|
||
# LIR 13(1) — récupération : le produit de disposition excède la FNACC ;
|
||
# le solde négatif est inclus dans le revenu imposable de l'année.
|
||
if solde_avant < 0:
|
||
recapture = -solde_avant
|
||
result = YearResult(
|
||
year=yi.year, opening_ucc=opening, additions=additions,
|
||
dispositions=dispositions, adjustment_coeff=yi.adjustment_coeff,
|
||
adjustment=Decimal("0.00"), cca_rate=cca_rate,
|
||
base_for_cca=Decimal("0.00"), cca_claimed=Decimal("0.00"),
|
||
closing_ucc=Decimal("0.00"), recapture=_q(recapture),
|
||
terminal_loss=Decimal("0.00"),
|
||
note="Solde négatif : récupération d'amortissement (revenu imposable). FNACC ramenée à 0.",
|
||
)
|
||
return result
|
||
|
||
# LIR 20(16) — perte finale : catégorie vide physiquement avec FNACC positive ;
|
||
# le solde résiduel est déductible en totalité (pas de plafond de taux).
|
||
if yi.pool_physically_empty and solde_avant > 0:
|
||
terminal_loss = solde_avant
|
||
return YearResult(
|
||
year=yi.year, opening_ucc=opening, additions=additions,
|
||
dispositions=dispositions, adjustment_coeff=yi.adjustment_coeff,
|
||
adjustment=Decimal("0.00"), cca_rate=cca_rate,
|
||
base_for_cca=Decimal("0.00"), cca_claimed=Decimal("0.00"),
|
||
closing_ucc=Decimal("0.00"), recapture=Decimal("0.00"),
|
||
terminal_loss=_q(terminal_loss),
|
||
note="Catégorie vidée physiquement avec solde positif : perte finale déductible à 100 %.",
|
||
)
|
||
|
||
# Règl. 1100(2) — règle de la demi-année : l'ajustement (coeff × ajouts nets)
|
||
# réduit la base de calcul pour l'année d'acquisition (coeff 0,5 par défaut).
|
||
net_additions = additions - dispositions
|
||
if net_additions < 0:
|
||
net_additions = Decimal("0") # pas d'ajustement négatif
|
||
adjustment = _q(yi.adjustment_coeff * net_additions)
|
||
|
||
base = solde_avant - adjustment
|
||
if base < 0:
|
||
base = Decimal("0")
|
||
|
||
cca = _q(base * cca_rate) if yi.claim_full_cca else Decimal("0.00")
|
||
# La DPA ne peut excéder le solde disponible.
|
||
if cca > solde_avant:
|
||
cca = solde_avant
|
||
|
||
closing = _q(solde_avant - cca)
|
||
|
||
return YearResult(
|
||
year=yi.year, opening_ucc=opening, additions=additions,
|
||
dispositions=dispositions, adjustment_coeff=yi.adjustment_coeff,
|
||
adjustment=adjustment, cca_rate=cca_rate, base_for_cca=_q(base),
|
||
cca_claimed=cca, closing_ucc=closing, recapture=Decimal("0.00"),
|
||
terminal_loss=Decimal("0.00"),
|
||
note=note or "Calcul standard : DPA dégressive sur le solde global.",
|
||
)
|
||
|
||
|
||
def compute_pool_timeline(years: list[YearInput],
|
||
opening_ucc: Decimal = Decimal("0"),
|
||
cca_rate: Decimal = Decimal("0.55")) -> list[YearResult]:
|
||
"""Enchaîne plusieurs années : la clôture d'une année ouvre la suivante."""
|
||
results: list[YearResult] = []
|
||
ucc = opening_ucc
|
||
for yi in sorted(years, key=lambda y: y.year):
|
||
r = compute_pool_year(ucc, yi, cca_rate)
|
||
results.append(r)
|
||
ucc = r.closing_ucc
|
||
return results
|