# ============================================================================= # Projet : AIR — Accounting Intermediate Representation # Auteur : Simon-Pierre Boucher # Contact : contact@spboucher.ai # Fichier : tax.py # Description : Tax pass — applies ALSL tax policies deterministically (no rates in code). # ============================================================================= """Tax pass. For each taxable event, evaluates the ALSL tax policies matching the event's jurisdiction and computes each tax component on its base with the policy set's rounding mode. Every computed tax amount becomes a provenance node derived from the event subtotal. NO tax rate exists in this file — rates live in versioned ALSL policy sets with mandatory source citations (see docs/research/canada-gst-qst.md). """ from __future__ import annotations from aic.diagnostics import Diagnostic, Severity from aic.passes.base import Pass from aic.unit import CompilationUnit, TaxLineState from alsl.evaluator import applicable_tax_policies from core.events import EventType TAXABLE_TYPES = (EventType.SALE, EventType.PURCHASE, EventType.REFUND) class TaxPass(Pass): name = "tax" def run(self, unit: CompilationUnit) -> list[Diagnostic]: diags: list[Diagnostic] = [] rounding = unit.policies.rounding.mode for event in unit.document.events: if event.type not in TAXABLE_TYPES: continue if event.tax is None or event.tax.exempt: continue state = unit.event_state(event.id) if state.subtotal is None or state.subtotal_node is None: continue # validation already failed this event policies = applicable_tax_policies(unit.policies, event, state.subtotal) if not policies: diags.append(Diagnostic( code="AIR-E400", severity=Severity.ERROR, message=( f"no tax policy in set '{unit.policies.name}' matches " f"jurisdiction '{event.tax.jurisdiction}'" ), location=f"event {event.id}, field tax.jurisdiction", suggestion="add an ALSL tax policy for this jurisdiction " "or mark the event tax.exempt: true", origin_pass=self.name, )) continue wanted = set(event.tax.codes) # optional explicit filter produced: set[str] = set() for policy in policies: for comp in policy.components: if wanted and comp.code not in wanted: continue if comp.base != "subtotal": diags.append(Diagnostic( code="AIR-E401", severity=Severity.ERROR, message=f"unsupported tax base '{comp.base}' in policy " f"'{policy.name}' (ALSL v0.1 supports 'subtotal')", location=f"policy {policy.name}, component {comp.code}", suggestion="use base: subtotal", origin_pass=self.name, )) continue raw = state.subtotal.multiply(comp.rate) amount = raw.quantized(rounding) node = unit.provenance.define( kind="tax", operation=f"tax:{comp.code}@{comp.rate}~{rounding.value}", amount=amount, inputs=(state.subtotal_node,), source_ref=policy.source, ) state.taxes.append(TaxLineState( code=comp.code, amount=amount, node_id=node.id, payable_role=comp.payable_role, receivable_role=comp.receivable_role, recoverable=comp.recoverable_on_purchase, policy=policy.name, )) produced.add(comp.code) for missing in wanted - produced: diags.append(Diagnostic( code="AIR-W400", severity=Severity.WARNING, message=f"event requested tax code '{missing}' but no policy " "produced it", location=f"event {event.id}, field tax.codes", suggestion="check the policy set covers this code for the jurisdiction", origin_pass=self.name, )) return diags