spb/air Public MIT
AIR — The Language of Accounting.
Python 100%
1# =============================================================================2# Projet : AIR — Accounting Intermediate Representation3# Auteur : Simon-Pierre Boucher4# Contact : contact@spboucher.ai5# Fichier : journal.py6# Description : Compiled output types — accounts, journal lines/entries, compiled journal.7# =============================================================================8"""Journal types: what the AIC compiler PRODUCES (never what the LLM writes).910Every journal line carries a provenance node id, so any posted figure can be11traced back through tax/FX derivations to the source economic event and its12document.13"""14from __future__ import annotations1516import enum17from dataclasses import dataclass, field18from datetime import date19from decimal import Decimal2021from core.money import Money22from core.provenance import ProvenanceGraph232425class AccountType(str, enum.Enum):26 ASSET = "asset"27 LIABILITY = "liability"28 EQUITY = "equity"29 REVENUE = "revenue"30 EXPENSE = "expense"3132 @property33 def normal_side(self) -> "Side":34 if self in (AccountType.ASSET, AccountType.EXPENSE):35 return Side.DEBIT36 return Side.CREDIT373839class Side(str, enum.Enum):40 DEBIT = "debit"41 CREDIT = "credit"424344@dataclass(frozen=True, slots=True)45class Account:46 code: str47 name: str48 type: AccountType495051@dataclass(frozen=True, slots=True)52class JournalLine:53 account: Account54 side: Side55 amount: Money # always >= 0; direction is carried by `side`56 memo: str = ""57 provenance_id: str | None = None5859 def signed(self) -> Decimal:60 """Debit-positive signed amount (for balance math)."""61 return self.amount.amount if self.side is Side.DEBIT else -self.amount.amount626364@dataclass(frozen=True, slots=True)65class JournalEntry:66 id: str67 date: date68 description: str69 lines: tuple[JournalLine, ...]70 source_event_id: str71 policy_set: str = ""72 policy_version: str = ""73 reverses: str | None = None # id of the entry this one reverses, if any747576@dataclass77class CompiledJournal:78 """The result of one AIC compilation: entries + full provenance."""7980 entries: list[JournalEntry] = field(default_factory=list)81 provenance: ProvenanceGraph = field(default_factory=ProvenanceGraph)82 policy_set: str = ""83 policy_version: str = ""8485 def accounts(self) -> dict[str, Account]:86 out: dict[str, Account] = {}87 for entry in self.entries:88 for line in entry.lines:89 out[line.account.code] = line.account90 return out91