# ============================================================================= # Projet : AIR — Accounting Intermediate Representation # Auteur : Simon-Pierre Boucher # Contact : contact@spboucher.ai # Fichier : classification.py # Description : Classification pass — applies ALSL classification policies (e.g. capitalization). # ============================================================================= """Classification pass. Decides which account role receives the debit side of a Purchase (expense vs capitalized asset) by evaluating ALSL classification policies — e.g. "capitalize equipment purchases at or above the policy threshold". Thresholds live in ALSL, never here. """ from __future__ import annotations from aic.diagnostics import Diagnostic, Severity from aic.passes.base import Pass from aic.unit import CompilationUnit from alsl.evaluator import applicable_classifications from core.events import EventType class ClassificationPass(Pass): name = "classification" def run(self, unit: CompilationUnit) -> list[Diagnostic]: diags: list[Diagnostic] = [] for event in unit.document.events: if event.type is not EventType.PURCHASE: continue state = unit.event_state(event.id) if state.subtotal is None: continue # validation already failed this event hits = applicable_classifications(unit.policies, event, state.subtotal) if not hits: continue if len(hits) > 1: diags.append(Diagnostic( code="AIR-W300", severity=Severity.WARNING, message=( "multiple classification policies match: " + ", ".join(p.name for p in hits) + f"; applying '{hits[0].name}'" ), location=f"event {event.id}", suggestion="tighten the policies' when-clauses so at most one matches", origin_pass=self.name, )) policy = hits[0] state.classify_as = policy.classify_as state.classification_role = policy.account_role state.classification_policy = policy.name return diags