# ============================================================================= # Projet : AIR — Accounting Intermediate Representation # Auteur : Simon-Pierre Boucher # Contact : contact@spboucher.ai # Fichier : backend.py # Description : Native backend — posts into AIR's own ledger (standalone mode, no ERP needed). # ============================================================================= """Native backend: AIR as a self-sufficient accounting system. When no third-party system (QuickBooks, Xero, ...) is used, compiled journals post into AIR's own append-only, hash-chained ledger, from which all statements (general ledger, trial balance, income statement, balance sheet) are generated in multiple formats. """ from __future__ import annotations from pathlib import Path from backends.base import ( Backend, BackendCapabilities, PostingReceipt, ReversalReceipt, TargetPayload, ) from core.journal import CompiledJournal from kernel.ledger import Ledger class NativeLedgerBackend(Backend): def __init__(self, ledger_path: str | Path | None = None): self.ledger = Ledger(path=Path(ledger_path) if ledger_path else None) def capabilities(self) -> BackendCapabilities: return BackendCapabilities( name="native", posts_remotely=False, native_reversal=True, multi_currency=True, idempotency="native", notes="AIR's own append-only hash-chained ledger; full reporting built in", ) def compile(self, journal: CompiledJournal) -> TargetPayload: # The native target consumes the journal as-is: no lowering needed. return TargetPayload(backend="native", format="journal", body=journal) def post(self, payload: TargetPayload, idempotency_key: str) -> PostingReceipt: journal = payload.body assert isinstance(journal, CompiledJournal) appended = self.ledger.post_journal(journal, idempotency_key) return PostingReceipt( backend="native", reference=str(self.ledger.path or ""), idempotency_key=idempotency_key, details={"entries_appended": str(appended)}, ) def reverse(self, receipt: PostingReceipt) -> ReversalReceipt: # Reverse every entry posted under the receipt's idempotency key. reversed_ids = [] for entry in list(self.ledger.entries): if entry.id.startswith("rev_"): continue contra = self.ledger.reverse_entry( entry.id, f"rev:{receipt.idempotency_key}:{entry.id}" ) reversed_ids.append(contra.id) return ReversalReceipt( backend="native", reference=",".join(reversed_ids), reversed_reference=receipt.idempotency_key, )