SPB Git

spb/air Public MIT

AIR — The Language of Accounting.

Python 100%
2.8 KB · 74 lines python
Raw Blame History
1# =============================================================================2# Projet : AIR — Accounting Intermediate Representation3# Auteur : Simon-Pierre Boucher4# Contact : contact@spboucher.ai5# Fichier : backend.py6# Description : Native backend — posts into AIR's own ledger (standalone mode, no ERP needed).7# =============================================================================8"""Native backend: AIR as a self-sufficient accounting system.910When no third-party system (QuickBooks, Xero, ...) is used, compiled journals11post into AIR's own append-only, hash-chained ledger, from which all12statements (general ledger, trial balance, income statement, balance sheet)13are generated in multiple formats.14"""15from __future__ import annotations1617from pathlib import Path1819from backends.base import (20    Backend,21    BackendCapabilities,22    PostingReceipt,23    ReversalReceipt,24    TargetPayload,25)26from core.journal import CompiledJournal27from kernel.ledger import Ledger282930class NativeLedgerBackend(Backend):31    def __init__(self, ledger_path: str | Path | None = None):32        self.ledger = Ledger(path=Path(ledger_path) if ledger_path else None)3334    def capabilities(self) -> BackendCapabilities:35        return BackendCapabilities(36            name="native",37            posts_remotely=False,38            native_reversal=True,39            multi_currency=True,40            idempotency="native",41            notes="AIR's own append-only hash-chained ledger; full reporting built in",42        )4344    def compile(self, journal: CompiledJournal) -> TargetPayload:45        # The native target consumes the journal as-is: no lowering needed.46        return TargetPayload(backend="native", format="journal", body=journal)4748    def post(self, payload: TargetPayload, idempotency_key: str) -> PostingReceipt:49        journal = payload.body50        assert isinstance(journal, CompiledJournal)51        appended = self.ledger.post_journal(journal, idempotency_key)52        return PostingReceipt(53            backend="native",54            reference=str(self.ledger.path or "<memory>"),55            idempotency_key=idempotency_key,56            details={"entries_appended": str(appended)},57        )5859    def reverse(self, receipt: PostingReceipt) -> ReversalReceipt:60        # Reverse every entry posted under the receipt's idempotency key.61        reversed_ids = []62        for entry in list(self.ledger.entries):63            if entry.id.startswith("rev_"):64                continue65            contra = self.ledger.reverse_entry(66                entry.id, f"rev:{receipt.idempotency_key}:{entry.id}"67            )68            reversed_ids.append(contra.id)69        return ReversalReceipt(70            backend="native",71            reference=",".join(reversed_ids),72            reversed_reference=receipt.idempotency_key,73        )74