# ============================================================================= # Projet : AIR — Accounting Intermediate Representation # Auteur : Simon-Pierre Boucher # Contact : contact@spboucher.ai # Fichier : unit.py # Description : CompilationUnit — the mutable state threaded through AIC passes. # ============================================================================= """The compilation unit: AIR document in, journal entries out. Passes read the immutable AIR events and accumulate derived state here (subtotals, tax lines, FX conversions, classifications), all anchored in the provenance graph. The pass manager verifies the double-entry invariant on `entries` after every pass. """ from __future__ import annotations from dataclasses import dataclass, field from aic.diagnostics import Diagnostic from alsl.model import PolicySet from core.events import AirDocument, EconomicEvent from core.journal import JournalEntry from core.money import Money from core.provenance import ProvenanceGraph @dataclass class TaxLineState: """A computed tax amount for one event (transaction currency).""" code: str amount: Money node_id: str payable_role: str receivable_role: str recoverable: bool policy: str # name of the ALSL policy that produced it @dataclass class EventState: """Everything the passes derive for a single event.""" # transaction-currency amounts subtotal: Money | None = None subtotal_node: str | None = None taxes: list[TaxLineState] = field(default_factory=list) # classification (purchases): account role for the debit side classify_as: str | None = None # "asset" | "expense" classification_role: str | None = None classification_policy: str | None = None # functional-currency amounts, ready for posting (set by the FX pass) post_subtotal: Money | None = None post_subtotal_node: str | None = None post_taxes: list[TaxLineState] = field(default_factory=list) # settlement FX difference (payments): + = more functional units than booked fx_diff: Money | None = None fx_diff_node: str | None = None booked_amount: Money | None = None # functional value at booking rate @dataclass class CompilationUnit: document: AirDocument policies: PolicySet provenance: ProvenanceGraph = field(default_factory=ProvenanceGraph) state: dict[str, EventState] = field(default_factory=dict) entries: list[JournalEntry] = field(default_factory=list) diagnostics: list[Diagnostic] = field(default_factory=list) def event_state(self, event_id: str) -> EventState: return self.state.setdefault(event_id, EventState()) def find_event(self, event_id: str) -> EconomicEvent | None: for event in self.document.events: if event.id == event_id: return event return None