Aller au contenu

omnibase/ledger Référence de l’API

Étage
Structure préparée · Omnibase
Version
main
Vérifié en réel
Son README ne le dit pas
Licence
MIT
composer require omnibase/ledger:dev-main

Lue dans les fichiers PHP du paquet au commit af442f79ca5e : ses espaces de noms, classes, interfaces et énumérations, leurs constantes, propriétés et méthodes publiques avec leur PHPDoc, et les options que prennent ses fabriques. Rien n’en est exécuté.

Base\Ledger

  • LedgerBundle class

    Double-entry accounting on top of omnibase: books (one per legal entity), a French chart of accounts, journals, parties and their lettering, bank feeds through glitchr/omnibank (statement files or bank APIs), the reconciliation of bank lines against open items with ranked suggestions, and the reports a French accountant expects - trial balance, general ledger, open items and the FEC.

    3 methods

Base\Ledger\Admin\Widget

  • BankWidgetType class

    The banks at a glance, on the back office's dashboard: each account with its balance in the books (its 512) and at the bank, when it last synced, its consent ("Renouveler" when the bank asks), what waits to be reconciled; then the latest lines across accounts. Without any account yet: the buttons to connect a bank (the gateways configured in Omnibank) or to import a statement.

    4 methods

  • BooksWidgetType class

    Each book's current fiscal year at a glance: income (class 7) and expenses (class 6) to date and the result they make, what tenants still owe (open debits on 411), what is still to pay (open credits on 401), and the cash in the bank accounts (512). Each tile opens its report - the compte de résultat for the first three - and the bilan to date is a link away.

    5 methods

Base\Ledger\Bank

  • Connections class

    A book's bank connections and Omnibank: the gateway behind each, its Connection read from and written back to the sealed state, the bank's pages to go through to give consent (start(), complete()), and the bank accounts it gives access to, each with its ledger account (5121, 5122...) and bank journal (BQ, BQ2...).

    12 methods

  • Importer class

    Omnibank's transactions into a bank account's lines, each once: a transaction already imported (same id on the same account) is updated while it waits for reconciliation - banks correct pending lines - and left alone once reconciled or ignored.

    2 methods

  • StatementUpload class

    A statement file (CAMT.053, OFX, CSV) typed into a bank account through Omnibank's "files" gateway: its lines imported (each once, however many overlapping statements are uploaded) and the statement's closing balance kept. The file itself is not stored.

    2 methods · 1 constant

  • Sync class

    Brings a bank account's new lines in through its connection's gateway: since the last sync, minus a few days (banks book late; lines already there are matched on their id), with the bank's balance when the gateway gives one. A consent the bank wants renewed is recorded on the connection - the back office then shows "Renouveler" - and the sync stops there.

    3 methods

Base\Ledger\Command

  • BankSyncCommand class

    Brings the bank accounts' new lines in (all books, or one) and reconciles what is sure. Accounts fed by statement files are skipped: they are uploaded. A connection whose consent needs renewing is reported and skipped. Suggested cron: a few times a day.

    1 method

  • ChartSeedCommand class

    Gives a book the accounts and journals of a chart (config/charts/), keeping what it has.

    1 method

  • FecExportCommand class

    Writes a fiscal year's FEC, <SIREN>FEC<closing date>.txt, into a directory (the current one by default).

    1 method

Base\Ledger\Controller

  • WebhookController class

    Where a bank aggregator tells us something changed (Powens, Bridge): public, checked by the gateway against its signature (notify()). The connection it is about gets its consent updated, then its accounts synced and reconciled.

    2 methods

Base\Ledger\Controller\Admin

  • AbstractLedgerPageController class

    The ledger's back-office pages that are not a CRUD (reconciliation, statement upload, bank connection, reports): rendered in the admin's layout with its menus, as forge's refund page is.

    1 method

  • BankController class

    Banks in the back office: connecting one (the bank's or aggregator's consent page, then back here), renewing a consent, syncing an account, and uploading a statement file - after which what is sure is reconciled.

    6 methods

  • LedgerAdminTrait trait

    The ledger screens' permissions (LedgerVoter), for every ledger CRUD: reading takes LEDGER_VIEW, changing LEDGER_MANAGE - ledger.admin_role (ROLE_ADMIN) by default, rather than the admin's blanket ROLE_SUPERADMIN.

    2 methods

  • ReconcileController class

    A bank account's reconciliation: the lines still to place with what they may settle, best first - accept a suggestion, pick the open items (or a rule, or a party) by hand, ignore the line -, and the lines placed lately, which can be undone. Every change is a POST with its token.

    6 methods

  • ReportController class

    A book's reports - trial balance, general ledger, open items, income statement and balance sheet (on screen, printed or as CSV), the FEC to download - and its buttons: seeding the chart, validating or reversing an entry.

    10 methods

Base\Ledger\Controller\Admin\Crud

  • AccountCrudController class

    The chart of accounts, by number. An account with lines is not deleted.

    7 methods

  • BankAccountCrudController class

    The bank accounts, each with its ledger account (512x) and bank journal: reconcile its lines, upload a statement, sync it from the bank.

    5 methods

  • BankConnectionCrudController class

    The accesses to banks: opened from the "connect" page (the bank's own consent page), renewed when the bank asks. Their state - tokens - is sealed and never shown.

    6 methods

  • BankLineCrudController class

    The bank statements' lines, as imported, filtered by where they stand; they are reconciled from the reconciliation page.

    6 methods · 1 constant

  • BookCrudController class

    The books - one per legal entity - with their reports: trial balance, open items, the FEC, and seeding the chart.

    7 methods

  • EntryCrudController class

    The entries, with their lines. They are posted by the application and the reconciliation (Posting), not typed here: a draft's date, label and piece can be corrected, then validated; a validated entry is read-only - it is reversed, never edited.

    7 methods

  • FiscalYearCrudController class

    A book's fiscal years: entry numbers run per year; a closed year takes no entry. Without any, calendar years.

    5 methods

  • JournalCrudController class

    The journals: BQ (bank), VT (rent notices), AC (purchases), OD, AN (opening balances).

    6 methods

  • PartyCrudController class

    Tenants, suppliers, associates: their auxiliary account (411U3), the IBAN their transfers come from, the application's reference. Usually created by the application (Parties::partyFor()); one typed here gets its auxiliary account when saved.

    7 methods

  • ReconciliationRuleCrudController class

    What recurring bank lines are posted against when nothing open matches: bank fees to 627, the loan instalment split between 164 and 6611, Stripe payouts to 5112. The highest priority matching rule wins.

    5 methods

Base\Ledger\DependencyInjection

Base\Ledger\Entity

  • Account class

    An account of a book's chart, by its PCG number: 512 (bank), 706 (rents), or an auxiliary account under a collective one (411U3 for a tenant, under 411).

    14 methods

  • BankAccount class

    One account at the bank, as its connection's gateway sees it ($externalId), tied to the book's ledger account for it (a 512) and the journal its movements are posted in (BQ).

    22 methods

  • BankConnection class

    A book's access to a bank through one of Omnibank's gateways ("files", "qonto", "powens", "bridge"): the gateway's Connection state (tokens, the provider's user) sealed with sodium's secretbox (Bank\Connections reads and writes it, never in clear), the consent the bank gave and until when, and the last error a sync met.

    19 methods

  • BankLine class

    A transaction on a bank account's statement, as the bank booked it - negative when money leaves. Imported once (its $externalId is unique per bank account), then reconciled: posted in the bank journal ($entry) and lettered with what it pays, or ignored.

    31 methods

  • Book class

    One legal entity's accounts (a company, an SCI): its chart, journals, parties, entries and bank accounts all hang off it. Entries dated on or before $lockedUntil can no longer be posted, changed or reversed - the period is closed, declared, or handed to the accountant.

    14 methods

  • Entry class

    A journal entry: balanced lines (debits = credits) on one date, in one journal. $source says where it comes from ("bank:42", "notice:2026-10-U3") and is unique per book, so posting the same thing twice gives the same entry back. Validated, it gets its number (sequential per book and fiscal year) and becomes immutable: a mistake is undone by a reversal entry ($reversalOf), never by an edit.

    21 methods

  • EntryLine class

    One line of an entry: an amount on the debit or the credit side of an account, maybe for a party. Lettering ties lines of one account and party whose debits and credits cancel out (a rent notice and its payment): they share a $letter and are no longer open items. Only the lettering fields change once the entry is validated.

    15 methods

  • FiscalYear class

    An accounting period of a book, usually twelve months. Entry numbers run per fiscal year; once closed, nothing is posted in it any more. A book without fiscal years works on calendar years.

    15 methods

  • Journal class

    A book's journal: BQ (bank), VT (sales: rent notices), AC (purchases), OD (miscellaneous), AN (opening balances).

    9 methods · 5 constants

  • Party class

    Someone the book owes or is owed by - a tenant, a supplier, an associate - with their own auxiliary account (411U3 under 411). $reference is the application's handle on them (a lease, a supplier's code), unique per book; $iban is how their transfers are recognised on the bank statement.

    17 methods

  • ReconciliationRule class

    What to post a recurring bank line against when nothing open matches it: bank fees to 627, the loan instalment split between 164 (capital) and 6611 (interest), a Stripe payout to the 5112 clearing account. A rule matches on the label ($pattern: words that must all appear, or a /regular expression/), the counterparty's IBAN, and the direction; the highest priority wins.

    24 methods

Base\Ledger\Enum

  • AccountType enum

    What an account is in the balance sheet or the income statement - which side its balance normally sits on (debit for assets and expenses, credit for the others) and whether it is carried forward at year end.

    3 methods · 5 cases

  • BankLineStatus enum

    Where a bank line stands in the reconciliation.

    1 method · 4 cases

  • ConsentState enum

    A bank connection's consent as the ledger keeps it: Omnibank's ConsentStatus, stored by value so the entity does not depend on the package being installed.

    1 method · 4 cases

  • PartyKind enum

    Who a party is to the book: the collective account its own account sits under.

    1 method · 4 cases

  • RuleSign enum

    Which bank lines a reconciliation rule looks at: money coming in, going out, or both.

    1 method · 3 cases

Base\Ledger\Event

  • BankLineReconciled class

    A bank line posted and lettered with what it settles (Reconciler::apply): what the application owes or is owed can follow - a rent call paid, an invoice settled - from the open items it cleared, without posting again.

    1 method · 3 properties

Base\Ledger\EventListener

  • ValidatedEntryGuard class

    A validated entry is part of the books for good: whatever tries to change it, or one of its lines, or to delete either, or to add a line to it, is refused at flush time - the back office, a fixture, a careless service.

    1 method

Base\Ledger\Exception

  • BankException class

    A bank access that failed: Omnibank missing, a gateway or an account not found, the provider refusing.

  • LedgerException class

    What the ledger refused to do; nothing was written.

  • LetteringException class

    Lines that cannot be lettered together: they do not cancel out, or not on one account and party.

  • LockedEntryException class

    A change to a validated entry or its lines (other than their lettering): reverse it instead.

  • LockedPeriodException class

    A date on or before the book's lockedUntil, or in a closed fiscal year.

  • PostingException class

    An entry Posting refuses: unbalanced, a negative or two-sided line, an unknown account or journal.

  • ReconciliationException class

    A bank line that cannot be reconciled (or undone) that way.

Base\Ledger\Export

  • FecWriter class

    The Fichier des Écritures Comptables (article A47 A-1 du LPF) of a fiscal year: one row per line of every validated entry, the 18 columns in their order, tab-separated, CRLF, dates as YYYYMMDD, amounts with a decimal comma and no thousands separator. A line on a party's auxiliary account (411U3) is filed under its general account (411) with the auxiliary account and the party's name in CompAuxNum and CompAuxLib.

    6 methods · 1 constant

Base\Ledger\Model

  • EntryDraft class

    An entry to post, as the application describes it - account numbers and a journal code rather than entities:

    5 methods · 6 properties

  • Text class

    How bank labels, names and references are compared: upper-case, without accents nor punctuation.

    2 methods

Base\Ledger\Reconciliation

  • Matcher class

    What a bank line may settle, ranked. In order of trust:

    3 methods · 17 constants

  • Reconciler class

    Turns a bank line into the books: apply() posts it in its bank account's journal - the bank account (512x) against what the suggestion says: the open items' accounts, the rule's account(s), or the party's - and letters it with the open items it settles. Its entry's source is "bank:<line id>", so applying it twice posts it once.

    7 methods

  • Suggestion class

    What a bank line may be (Matcher), or what someone in the back office says it is: the open items it pays - lettered with it when they add up to its amount -, else the rule it falls under, else the party it comes from (a payment on account). $score runs from 0 to 1; Reconciler applies a suggestion on its own only when it is the single one at or above ledger.reconciliation.auto_threshold.

    5 methods · 5 properties

Base\Ledger\Report

  • BalanceSheet class

    The bilan at a date: every account's balance from the book's first entry to that day, the assets (actif) on one side, what finances them (passif) on the other - and, when asked, the same day a year earlier beside it.

    4 methods · 4 constants

  • GeneralLedger class

    The grand livre: every line of every account over a period, with the running balance and the account's totals.

    3 methods

  • IncomeStatement class

    The compte de résultat over a period: income (class 7) and expenses (class 6) in the headings an SCI's accounts are read under, their totals and the result (bénéfice when positive, perte when negative) - and, when asked, the same period a year earlier beside it (N-1).

    4 methods · 2 constants

  • OpenItems class

    What is still owed, party by party: the unlettered lines of the accounts under a prefix (411 - tenants - by default; 401 for suppliers), their balance and how old the oldest is.

    3 methods

  • TrialBalance class

    The balance générale: per account, the debit and credit totals over a period and the balance they leave, with totals per class and overall (debits = credits, or the books are wrong).

    4 methods

Base\Ledger\Repository

Base\Ledger\Security

  • BookAccessInterface interface

    Who else may reach a book than ledger.admin_role: an application's service implementing this (autoconfigured, tag ledger.book_access) is asked by LedgerVoter - an associate viewing their company's book, an accountant managing their clients'. Any service saying yes is enough.

    2 methods

  • StateCipher class

    Seals what a bank connection keeps (the provider's tokens, its user) with sodium's secretbox (XSalsa20-Poly1305), under a key derived from `ledger.secret` (the kernel secret by default): "v1:" + base64(nonce + ciphertext). A sealed state that was tampered with, or sealed under another secret, does not open.

    3 methods

Base\Ledger\Security\Voter

  • LedgerVoter class

    LEDGER_VIEW and LEDGER_MANAGE, on a Book or on anything of one (an entry, an account, a bank line: whatever has getBook()):

    2 methods · 2 constants

Base\Ledger\Service

  • Chart class

    Gives a book its chart of accounts and journals from a file of config/charts/ (sci: the PCG accounts a French real-estate SCI uses). It adds what is missing and leaves alone what the book already has, so it can run again after the chart grows.

    3 methods

  • Lettering class

    Lettering: lines of one account and one party whose debits and credits cancel out - a rent notice and the transfer paying it - share a letter (AAA, AAB...) and stop being open items. Partial payments stay open until the rest arrives.

    6 methods

  • Parties class

    The book's parties as the application knows them: partyFor() finds one by the application's reference (a lease, a supplier code) or creates it, with its auxiliary account under its kind's collective account - 411 + the reference for a tenant (411U3), 401 for a supplier, 455 for an associate, 467 otherwise.

    3 methods

  • Posting class

    The one way entries get into a book. post() checks what double-entry bookkeeping requires - balanced, one-sided non-negative lines, accounts and a journal the book has, a date outside the locked period - and refuses anything else with a PostingException, before writing a thing.

    6 methods

Base\Ledger\Twig

  • LedgerExtension class

    {{ 123456|ledger_money }} → 1 234,56 € (minor units, the locale's way); {{ 123456|ledger_amount }} → 1 234,56 (no currency: a column of a report); {{ iban|ledger_iban }} → FR76 •••• 4321.

    3 methods

Base\Ledger

LedgerBundle class

class LedgerBundle extends AbstractBaseBundle LedgerBundle.php:17

Double-entry accounting on top of omnibase: books (one per legal entity), a French chart of accounts, journals, parties and their lettering, bank feeds through glitchr/omnibank (statement files or bank APIs), the reconciliation of bank lines against open items with ranked suggestions, and the reports a French accountant expects - trial balance, general ledger, open items and the FEC.

LedgerBundle::__construct()

public function __construct()

LedgerBundle::getPath()

public function getPath(): string

Modern bundle layout: the class lives in src/, the bundle root is the package root - so TwigBundle picks up ./templates as @Ledger and Doctrine's auto_mapping ./src/Entity.

LedgerBundle::build()

public function build(ContainerBuilder $container): void

Base\Ledger\Admin\Widget

BankWidgetType class

final class BankWidgetType implements DashboardWidgetTypeInterface BankWidgetType.php:28

The banks at a glance, on the back office's dashboard: each account with its balance in the books (its 512) and at the bank, when it last synced, its consent ("Renouveler" when the bank asks), what waits to be reconciled; then the latest lines across accounts. Without any account yet: the buttons to connect a bank (the gateways configured in Omnibank) or to import a statement.

MenuItem::block('ledger_bank', 'Banque', 'fa-solid fa-building-columns')

BankWidgetType::__construct()

public function __construct(readonly BankAccountRepository $bankAccounts, readonly BankLineRepository $bankLines, readonly EntryLineRepository $entryLines, readonly BookRepository $books, readonly Connections $connections, readonly AuthorizationCheckerInterface $authorization)

BankWidgetType::getName()

public static function getName(): string

BankWidgetType::getTemplate()

public function getTemplate(): string

BankWidgetType::getTemplateVars()

public function getTemplateVars(MenuItem $widget): array

BooksWidgetType class

final class BooksWidgetType implements DashboardWidgetTypeInterface BooksWidgetType.php:25

Each book's current fiscal year at a glance: income (class 7) and expenses (class 6) to date and the result they make, what tenants still owe (open debits on 411), what is still to pay (open credits on 401), and the cash in the bank accounts (512). Each tile opens its report - the compte de résultat for the first three - and the bilan to date is a link away.

MenuItem::block('ledger_books', 'Comptabilité', 'fa-solid fa-book')

BooksWidgetType::__construct()

public function __construct(readonly BookRepository $books, readonly FiscalYearRepository $fiscalYears, readonly EntryLineRepository $lines, readonly IncomeStatement $incomeStatement, readonly AuthorizationCheckerInterface $authorization)

BooksWidgetType::getName()

public static function getName(): string

BooksWidgetType::getTemplate()

public function getTemplate(): string

BooksWidgetType::getTemplateVars()

public function getTemplateVars(MenuItem $widget): array

BooksWidgetType::figures()

public function figures(Book $book, \DateTimeImmutable $today): array

Base\Ledger\Bank

Connections class

class Connections Connections.php:31

A book's bank connections and Omnibank: the gateway behind each, its Connection read from and written back to the sealed state, the bank's pages to go through to give consent (start(), complete()), and the bank accounts it gives access to, each with its ledger account (5121, 5122...) and bank journal (BQ, BQ2...).

Connections::__construct()

public function __construct(readonly EntityManagerInterface $entityManager, readonly StateCipher $cipher, readonly AccountRepository $accounts, readonly JournalRepository $journals, readonly BankAccountRepository $bankAccounts, readonly BankConnectionRepository $connections, readonly ?Registry $registry = null)

Connections::isAvailable()

public function isAvailable(): bool

Connections::gateways()

public function gateways(): array
Returns

the gateways the application configured (omnibank.gateways)

Connections::gateway()

public function gateway(string $name): GatewayInterface

Connections::open()

public function open(BankConnection $connection): Connection

The connection's Omnibank Connection, unsealed.

Connections::store()

public function store(BankConnection $connection, Connection $omnibank): void

Seals $omnibank into the connection, with its consent. Flushed by the caller.

Connections::start()

public function start(Book $book, string $gateway, string|callable $returnUrl, ?string $name = null): array

Opens a new connection of $book through $gateway: the connection, saved, and the page the user gives consent on (null when there is none, as for statement files - its accounts are then discovered).

$returnUrl may be a callable given the saved connection: the return route usually names it.

Connections::renew()

public function renew(BankConnection $connection, string $returnUrl): ?string

Through the bank's page again: an expired consent, a new password. Null when nothing is needed.

Connections::complete()

public function complete(BankConnection $connection, string $returnUrl, array $query = []): ?string

The user came back from the bank's page: what the provider put in the return URL's query is handed to the gateway (state "return") and connect() asked again. Null when nothing more is needed - the accounts are then discovered -, else the next page to go through.

Connections::discover()

public function discover(BankConnection $connection): int

Asks the bank for the connection's accounts and adds those the book does not have yet; returns how many.

Connections::attach()

public function attach(BankConnection $connection, BankAccountModel|string $account, ?string $name = null, ?string $iban = null, ?string $currency = null): BankAccount

A bank account of the connection, with its ledger account and journal: the book's first bank account posts on 5121 in BQ, the second on 5122 in BQ2... Persisted, not flushed.

Connections::findByProviderId()

public function findByProviderId(string $gateway, ?string $providerId): ?BankConnection

The connection a provider's notification is about: the one of that gateway whose state holds the provider's connection id, or the only one of that gateway.

Importer class

class Importer Importer.php:17

Omnibank's transactions into a bank account's lines, each once: a transaction already imported (same id on the same account) is updated while it waits for reconciliation - banks correct pending lines - and left alone once reconciled or ignored.

Importer::__construct()

public function __construct(readonly EntityManagerInterface $entityManager, readonly BankLineRepository $lines)

Importer::import()

public function import(BankAccount $account, iterable $transactions): int
Returns

how many new lines

StatementUpload class

class StatementUpload StatementUpload.php:19

A statement file (CAMT.053, OFX, CSV) typed into a bank account through Omnibank's "files" gateway: its lines imported (each once, however many overlapping statements are uploaded) and the statement's closing balance kept. The file itself is not stored.

const GATEWAY = 'files'

StatementUpload::__construct()

public function __construct(readonly EntityManagerInterface $entityManager, readonly Connections $connections, readonly Importer $importer)

StatementUpload::upload()

public function upload(BankAccount $account, string $name, string $content): int
Returns

how many new lines

Sync class

class Sync Sync.php:22

Brings a bank account's new lines in through its connection's gateway: since the last sync, minus a few days (banks book late; lines already there are matched on their id), with the bank's balance when the gateway gives one. A consent the bank wants renewed is recorded on the connection - the back office then shows "Renouveler" - and the sync stops there.

Sync::__construct()

public function __construct(readonly EntityManagerInterface $entityManager, readonly Connections $connections, readonly Importer $importer, readonly int $overlapDays = 5)

Sync::sync()

public function sync(BankAccount $account, ?\DateTimeImmutable $until = null): int
Returns

how many new lines

Sync::syncConnection()

public function syncConnection(BankConnection $connection): int

Every account of a connection (a webhook said something changed); returns how many new lines.

Base\Ledger\Command

BankSyncCommand class

class BankSyncCommand extends Command BankSyncCommand.php:25

Brings the bank accounts' new lines in (all books, or one) and reconciles what is sure. Accounts fed by statement files are skipped: they are uploaded. A connection whose consent needs renewing is reported and skipped. Suggested cron: a few times a day.

BankSyncCommand::__construct()

public function __construct(readonly BookRepository $books, readonly BankAccountRepository $bankAccounts, readonly Sync $sync, readonly Reconciler $reconciler)

ChartSeedCommand class

class ChartSeedCommand extends Command ChartSeedCommand.php:16

Gives a book the accounts and journals of a chart (config/charts/), keeping what it has.

ChartSeedCommand::__construct()

public function __construct(readonly BookRepository $books, readonly Chart $chart)

FecExportCommand class

class FecExportCommand extends Command FecExportCommand.php:17

Writes a fiscal year's FEC, <SIREN>FEC<closing date>.txt, into a directory (the current one by default).

FecExportCommand::__construct()

public function __construct(readonly BookRepository $books, readonly FiscalYearRepository $fiscalYears, readonly FecWriter $writer)

Base\Ledger\Controller

WebhookController class

class WebhookController extends AbstractController WebhookController.php:24

Where a bank aggregator tells us something changed (Powens, Bridge): public, checked by the gateway against its signature (notify()). The connection it is about gets its consent updated, then its accounts synced and reconciled.

WebhookController::__construct()

public function __construct(readonly Connections $connections, readonly Sync $sync, readonly Reconciler $reconciler, readonly EntityManagerInterface $entityManager)

WebhookController::__invoke()

public function __invoke(Request $request, string $gateway): Response

Base\Ledger\Controller\Admin

AbstractLedgerPageController class

abstract class AbstractLedgerPageController extends AbstractController AbstractLedgerPageController.php:21

The ledger's back-office pages that are not a CRUD (reconciliation, statement upload, bank connection, reports): rendered in the admin's layout with its menus, as forge's refund page is.

AbstractLedgerPageController::setLedgerServices()

public function setLedgerServices(EntityManagerInterface $entityManager, AdminContext $adminContext, MenuBuilder $menuBuilder): void

BankController class

class BankController extends AbstractLedgerPageController BankController.php:26

Banks in the back office: connecting one (the bank's or aggregator's consent page, then back here), renewing a consent, syncing an account, and uploading a statement file - after which what is sure is reconciled.

BankController::__construct()

public function __construct(readonly Connections $connections, readonly StatementUpload $statements, readonly Sync $sync, readonly Reconciler $reconciler)

BankController::upload()

public function upload(Request $request, int $bankAccount): Response

BankController::connect()

public function connect(Request $request, string $gateway): Response

Connecting a bank: for statement files, the account's name and IBAN (nothing to consent to); for a bank API or an aggregator, off to its consent page, back on ledger_admin_bank_return.

BankController::return()

public function return(Request $request, int $connection): RedirectResponse

Back from the bank's consent page: the gateway finishes the connection, the accounts are discovered.

BankController::renew()

public function renew(Request $request, int $connection): RedirectResponse

BankController::sync()

public function sync(Request $request, int $bankAccount): RedirectResponse

LedgerAdminTrait trait

trait LedgerAdminTrait LedgerAdminTrait.php:20

The ledger screens' permissions (LedgerVoter), for every ledger CRUD: reading takes LEDGER_VIEW, changing LEDGER_MANAGE - ledger.admin_role (ROLE_ADMIN) by default, rather than the admin's blanket ROLE_SUPERADMIN.

Asked about a record, the voter looks at its book.

LedgerAdminTrait::configureCrud()

public function configureCrud(Crud $crud): Crud

LedgerAdminTrait::configureActions()

public function configureActions(Actions $actions): Actions

ReconcileController class

class ReconcileController extends AbstractLedgerPageController ReconcileController.php:31

A bank account's reconciliation: the lines still to place with what they may settle, best first - accept a suggestion, pick the open items (or a rule, or a party) by hand, ignore the line -, and the lines placed lately, which can be undone. Every change is a POST with its token.

ReconcileController::__construct()

public function __construct(readonly Matcher $matcher, readonly Reconciler $reconciler, readonly BankLineRepository $bankLines, readonly ReconciliationRuleRepository $rules, readonly PartyRepository $parties)

ReconcileController::index()

public function index(int $bankAccount): Response

ReconcileController::apply()

public function apply(Request $request, int $line): RedirectResponse

A suggestion accepted, or the open items / rule / party picked by hand.

ReconcileController::ignore()

public function ignore(Request $request, int $line): RedirectResponse

ReconcileController::undo()

public function undo(Request $request, int $line): RedirectResponse

ReconcileController::auto()

public function auto(Request $request, int $bankAccount): RedirectResponse

ReportController class

class ReportController extends AbstractLedgerPageController ReportController.php:31

A book's reports - trial balance, general ledger, open items, income statement and balance sheet (on screen, printed or as CSV), the FEC to download - and its buttons: seeding the chart, validating or reversing an entry.

ReportController::__construct()

public function __construct(readonly FiscalYearRepository $fiscalYears)

ReportController::trialBalance()

public function trialBalance(Request $request, int $book, TrialBalance $trialBalance): Response

ReportController::generalLedger()

public function generalLedger(Request $request, int $book, GeneralLedger $generalLedger): Response

ReportController::openItems()

public function openItems(Request $request, int $book, OpenItems $openItems): Response

ReportController::incomeStatement()

public function incomeStatement(Request $request, int $book, IncomeStatement $incomeStatement): Response

?n1=0 leaves out the N-1 column; ?format=csv downloads it.

ReportController::balanceSheet()

public function balanceSheet(Request $request, int $book, BalanceSheet $balanceSheet): Response

?at= (Y-m-d), else the fiscal year's end - or today, while it runs; ?n1=0 and ?format=csv as for the income statement.

ReportController::fec()

public function fec(int $book, int $year, FecWriter $writer): Response

ReportController::seed()

public function seed(Request $request, int $book, Chart $chart): RedirectResponse

ReportController::validate()

public function validate(Request $request, int $entry, Posting $posting): RedirectResponse

ReportController::reverse()

public function reverse(Request $request, int $entry, Posting $posting): RedirectResponse

Base\Ledger\Controller\Admin\Crud

AccountCrudController class

class AccountCrudController extends AbstractCrudController AccountCrudController.php:19

The chart of accounts, by number. An account with lines is not deleted.

AccountCrudController::getEntityFqcn()

public static function getEntityFqcn(): string

AccountCrudController::getPreferredIcon()

public static function getPreferredIcon(): ?string

AccountCrudController::configureCrud()

public function configureCrud(\Base\Admin\Config\Crud $crud): \Base\Admin\Config\Crud

AccountCrudController::configureFilters()

public function configureFilters(Filters $filters): Filters

AccountCrudController::configureFields()

public function configureFields(string $pageName): iterable

AccountCrudController::createEntity()

public function createEntity(string $entityFqcn): object

AccountCrudController::isDeletable()

public function isDeletable(object $entity): bool

BankAccountCrudController class

class BankAccountCrudController extends AbstractCrudController BankAccountCrudController.php:22

The bank accounts, each with its ledger account (512x) and bank journal: reconcile its lines, upload a statement, sync it from the bank.

BankAccountCrudController::configureCrud()

public function configureCrud(Crud $crud): Crud

BankAccountCrudController::getEntityFqcn()

public static function getEntityFqcn(): string

BankAccountCrudController::getPreferredIcon()

public static function getPreferredIcon(): ?string

BankAccountCrudController::configureActions()

public function configureActions(Actions $actions): Actions

BankAccountCrudController::configureFields()

public function configureFields(string $pageName): iterable

BankConnectionCrudController class

class BankConnectionCrudController extends AbstractCrudController BankConnectionCrudController.php:23

The accesses to banks: opened from the "connect" page (the bank's own consent page), renewed when the bank asks. Their state - tokens - is sealed and never shown.

BankConnectionCrudController::configureCrud()

public function configureCrud(Crud $crud): Crud

BankConnectionCrudController::getEntityFqcn()

public static function getEntityFqcn(): string

BankConnectionCrudController::getPreferredIcon()

public static function getPreferredIcon(): ?string

BankConnectionCrudController::configureActions()

public function configureActions(Actions $actions): Actions

BankConnectionCrudController::configureRecordNote()

public function configureRecordNote(object $entity): ?string

BankConnectionCrudController::configureFields()

public function configureFields(string $pageName): iterable

BankLineCrudController class

class BankLineCrudController extends AbstractCrudController BankLineCrudController.php:20

The bank statements' lines, as imported, filtered by where they stand; they are reconciled from the reconciliation page.

const STATUS_LABELS = ['unmatched' => 'À rapprocher', 'suggested' => 'Suggestion', 'reconciled' => 'Rapprochée', 'ignored' => 'Ignorée']

BankLineCrudController::getEntityFqcn()

public static function getEntityFqcn(): string

BankLineCrudController::getPreferredIcon()

public static function getPreferredIcon(): ?string

BankLineCrudController::configureCrud()

public function configureCrud(Crud $crud): Crud

BankLineCrudController::configureFilters()

public function configureFilters(Filters $filters): Filters

BankLineCrudController::configureActions()

public function configureActions(Actions $actions): Actions

BankLineCrudController::configureFields()

public function configureFields(string $pageName): iterable

BookCrudController class

class BookCrudController extends AbstractCrudController BookCrudController.php:18

The books - one per legal entity - with their reports: trial balance, open items, the FEC, and seeding the chart.

BookCrudController::configureCrud()

public function configureCrud(Crud $crud): Crud

BookCrudController::getEntityFqcn()

public static function getEntityFqcn(): string

BookCrudController::getPreferredIcon()

public static function getPreferredIcon(): ?string

BookCrudController::configureActions()

public function configureActions(Actions $actions): Actions

BookCrudController::configureFields()

public function configureFields(string $pageName): iterable

BookCrudController::createEntity()

public function createEntity(string $entityFqcn): object

BookCrudController::isDeletable()

public function isDeletable(object $entity): bool

A book with entries is part of the accounts: it stays.

EntryCrudController class

class EntryCrudController extends AbstractCrudController EntryCrudController.php:25

The entries, with their lines. They are posted by the application and the reconciliation (Posting), not typed here: a draft's date, label and piece can be corrected, then validated; a validated entry is read-only - it is reversed, never edited.

EntryCrudController::getEntityFqcn()

public static function getEntityFqcn(): string

EntryCrudController::getPreferredIcon()

public static function getPreferredIcon(): ?string

EntryCrudController::configureCrud()

public function configureCrud(Crud $crud): Crud

EntryCrudController::configureActions()

public function configureActions(Actions $actions): Actions

EntryCrudController::updateEntity()

public function updateEntity(EntityManagerInterface $entityManager, object $entity): void

The form is closed on a validated entry; a direct request is refused before the flush guard would.

EntryCrudController::isDeletable()

public function isDeletable(object $entity): bool

EntryCrudController::configureFields()

public function configureFields(string $pageName): iterable

FiscalYearCrudController class

class FiscalYearCrudController extends AbstractCrudController FiscalYearCrudController.php:15

A book's fiscal years: entry numbers run per year; a closed year takes no entry. Without any, calendar years.

FiscalYearCrudController::configureCrud()

public function configureCrud(Crud $crud): Crud

FiscalYearCrudController::getEntityFqcn()

public static function getEntityFqcn(): string

FiscalYearCrudController::getPreferredIcon()

public static function getPreferredIcon(): ?string

FiscalYearCrudController::configureFields()

public function configureFields(string $pageName): iterable

FiscalYearCrudController::createEntity()

public function createEntity(string $entityFqcn): object

JournalCrudController class

class JournalCrudController extends AbstractCrudController JournalCrudController.php:15

The journals: BQ (bank), VT (rent notices), AC (purchases), OD, AN (opening balances).

JournalCrudController::configureCrud()

public function configureCrud(Crud $crud): Crud

JournalCrudController::getEntityFqcn()

public static function getEntityFqcn(): string

JournalCrudController::getPreferredIcon()

public static function getPreferredIcon(): ?string

JournalCrudController::configureFields()

public function configureFields(string $pageName): iterable

JournalCrudController::createEntity()

public function createEntity(string $entityFqcn): object

JournalCrudController::isDeletable()

public function isDeletable(object $entity): bool

PartyCrudController class

class PartyCrudController extends AbstractCrudController PartyCrudController.php:24

Tenants, suppliers, associates: their auxiliary account (411U3), the IBAN their transfers come from, the application's reference. Usually created by the application (Parties::partyFor()); one typed here gets its auxiliary account when saved.

PartyCrudController::configureCrud()

public function configureCrud(Crud $crud): Crud

PartyCrudController::getEntityFqcn()

public static function getEntityFqcn(): string

PartyCrudController::getPreferredIcon()

public static function getPreferredIcon(): ?string

PartyCrudController::configureFields()

public function configureFields(string $pageName): iterable

PartyCrudController::createEntity()

public function createEntity(string $entityFqcn): object

PartyCrudController::persistEntity()

public function persistEntity(EntityManagerInterface $entityManager, object $entity): void

PartyCrudController::isDeletable()

public function isDeletable(object $entity): bool

ReconciliationRuleCrudController class

class ReconciliationRuleCrudController extends AbstractCrudController ReconciliationRuleCrudController.php:25

What recurring bank lines are posted against when nothing open matches: bank fees to 627, the loan instalment split between 164 and 6611, Stripe payouts to 5112. The highest priority matching rule wins.

ReconciliationRuleCrudController::configureCrud()

public function configureCrud(Crud $crud): Crud

ReconciliationRuleCrudController::getEntityFqcn()

public static function getEntityFqcn(): string

ReconciliationRuleCrudController::getPreferredIcon()

public static function getPreferredIcon(): ?string

ReconciliationRuleCrudController::configureFields()

public function configureFields(string $pageName): iterable

ReconciliationRuleCrudController::createEntity()

public function createEntity(string $entityFqcn): object

Base\Ledger\DependencyInjection

LedgerConfiguration class

class LedgerConfiguration implements ConfigurationInterface LedgerConfiguration.php:8

LedgerConfiguration::getConfigTreeBuilder()

public function getConfigTreeBuilder(): TreeBuilder

LedgerExtension class

class LedgerExtension extends AbstractBaseExtension LedgerExtension.php:11

LedgerExtension::getConfiguration()

public function getConfiguration(array $config, ContainerBuilder $container): LedgerConfiguration

LedgerExtension::load()

public function load(array $configs, ContainerBuilder $container): void

Base\Ledger\Entity

Account class

class Account Account.php:16

An account of a book's chart, by its PCG number: 512 (bank), 706 (rents), or an auxiliary account under a collective one (411U3 for a tenant, under 411).

Account::__construct()

public function __construct(Book $book, string $number, string $label, ?AccountType $type = null)

Account::normalize()

public static function normalize(string $number): string

Account numbers are kept upper-case, without spaces: "411 u3" is 411U3.

Account::__toString()

public function __toString(): string

Account::getId()

public function getId(): ?int

Account::getBook()

public function getBook(): Book

Account::setBook()

public function setBook(Book $book): self

An account is given its book and its number when it is created: the back office's "new" form writes them, its "edit" form shows them.

Account::getNumber()

public function getNumber(): string

Account::setNumber()

public function setNumber(?string $number): self

Account::getLabel()

public function getLabel(): string

Account::setLabel()

public function setLabel(?string $label): self

Account::getType()

public function getType(): AccountType

Account::setType()

public function setType(AccountType $type): self

Account::getGeneralNumber()

public function getGeneralNumber(): string

The general account this one sits under in the FEC: its leading digits (411 for 411U3).

Account::isUnder()

public function isUnder(string $prefix): bool

BankAccount class

class BankAccount BankAccount.php:13

One account at the bank, as its connection's gateway sees it ($externalId), tied to the book's ledger account for it (a 512) and the journal its movements are posted in (BQ).

BankAccount::__construct()

public function __construct(BankConnection $connection, string $externalId, string $name, Account $account, Journal $journal, ?string $iban = null, ?string $currency = null)

BankAccount::__toString()

public function __toString(): string

BankAccount::mask()

public static function mask(?string $iban): string

FR76 •••• 4321: what a screen shows of an IBAN.

BankAccount::getId()

public function getId(): ?int

BankAccount::getConnection()

public function getConnection(): BankConnection

BankAccount::getBook()

public function getBook(): Book

BankAccount::getExternalId()

public function getExternalId(): string

BankAccount::getIban()

public function getIban(): ?string

BankAccount::setIban()

public function setIban(?string $iban): self

BankAccount::getMaskedIban()

public function getMaskedIban(): string

BankAccount::getName()

public function getName(): string

BankAccount::setName()

public function setName(string $name): self

BankAccount::getCurrency()

public function getCurrency(): string

BankAccount::getAccount()

public function getAccount(): Account

BankAccount::setAccount()

public function setAccount(Account $account): self

BankAccount::getJournal()

public function getJournal(): Journal

BankAccount::setJournal()

public function setJournal(Journal $journal): self

BankAccount::getLastSyncedAt()

public function getLastSyncedAt(): ?\DateTimeImmutable

BankAccount::setLastSyncedAt()

public function setLastSyncedAt(?\DateTimeImmutable $lastSyncedAt): self

BankAccount::getBankBalance()

public function getBankBalance(): ?int

BankAccount::getBankBalanceAt()

public function getBankBalanceAt(): ?\DateTimeImmutable

BankAccount::setBankBalance()

public function setBankBalance(?int $balance, ?\DateTimeImmutable $at = null): self

BankConnection class

class BankConnection BankConnection.php:18

A book's access to a bank through one of Omnibank's gateways ("files", "qonto", "powens", "bridge"): the gateway's Connection state (tokens, the provider's user) sealed with sodium's secretbox (Bank\Connections reads and writes it, never in clear), the consent the bank gave and until when, and the last error a sync met.

BankConnection::__construct()

public function __construct(Book $book, string $gateway, ?string $name = null)

BankConnection::__toString()

public function __toString(): string

BankConnection::getId()

public function getId(): ?int

BankConnection::getBook()

public function getBook(): Book

BankConnection::getGateway()

public function getGateway(): string

BankConnection::getName()

public function getName(): ?string

BankConnection::setName()

public function setName(?string $name): self

BankConnection::getState()

public function getState(): ?string

BankConnection::setState()

public function setState(?string $state): self

The sealed state, as Bank\Connections::store() writes it.

BankConnection::getConsentStatus()

public function getConsentStatus(): ConsentState

BankConnection::setConsentStatus()

public function setConsentStatus(ConsentState $consentStatus): self

BankConnection::getExpiresAt()

public function getExpiresAt(): ?\DateTimeImmutable

BankConnection::setExpiresAt()

public function setExpiresAt(?\DateTimeImmutable $expiresAt): self

BankConnection::getLastError()

public function getLastError(): ?string

BankConnection::setLastError()

public function setLastError(?string $lastError): self

BankConnection::getCreatedAt()

public function getCreatedAt(): \DateTimeImmutable

BankConnection::getAccounts()

public function getAccounts(): Collection

BankConnection::addAccount()

public function addAccount(BankAccount $account): self

BankConnection::needsRenewal()

public function needsRenewal(?\DateTimeImmutable $now = null): bool

Whether the bank wants its consent given again (expired, or said so).

BankLine class

class BankLine BankLine.php:15

A transaction on a bank account's statement, as the bank booked it - negative when money leaves. Imported once (its $externalId is unique per bank account), then reconciled: posted in the bank journal ($entry) and lettered with what it pays, or ignored.

BankLine::__construct()

public function __construct(BankAccount $bankAccount, string $externalId, \DateTimeImmutable $bookedOn, int $amount, string $label, ?string $currency = null)

BankLine::__toString()

public function __toString(): string

BankLine::getId()

public function getId(): ?int

BankLine::getBankAccount()

public function getBankAccount(): BankAccount

BankLine::getBook()

public function getBook(): Book

BankLine::getExternalId()

public function getExternalId(): string

BankLine::getBookedOn()

public function getBookedOn(): \DateTimeImmutable

BankLine::setBookedOn()

public function setBookedOn(\DateTimeImmutable $bookedOn): self

BankLine::getValueOn()

public function getValueOn(): ?\DateTimeImmutable

BankLine::setValueOn()

public function setValueOn(?\DateTimeImmutable $valueOn): self

BankLine::getAmount()

public function getAmount(): int

BankLine::setAmount()

public function setAmount(int $amount): self

BankLine::getCurrency()

public function getCurrency(): string

BankLine::getLabel()

public function getLabel(): string

BankLine::setLabel()

public function setLabel(string $label): self

BankLine::getCounterpartyName()

public function getCounterpartyName(): ?string

BankLine::setCounterpartyName()

public function setCounterpartyName(?string $name): self

BankLine::getCounterpartyIban()

public function getCounterpartyIban(): ?string

BankLine::setCounterpartyIban()

public function setCounterpartyIban(?string $iban): self

BankLine::getReference()

public function getReference(): ?string

BankLine::setReference()

public function setReference(?string $reference): self

BankLine::getRaw()

public function getRaw(): array

BankLine::setRaw()

public function setRaw(array $raw): self

BankLine::getStatus()

public function getStatus(): BankLineStatus

BankLine::setStatus()

public function setStatus(BankLineStatus $status): self

BankLine::getEntry()

public function getEntry(): ?Entry

BankLine::setEntry()

public function setEntry(?Entry $entry): self

BankLine::getReconciliations()

public function getReconciliations(): int

BankLine::countReconciliation()

public function countReconciliation(): self

BankLine::getCreatedAt()

public function getCreatedAt(): \DateTimeImmutable

BankLine::getSource()

public function getSource(): string

The entry source of its next reconciliation: "bank:<id>", then "bank:<id>/2" after an undo.

Book class

class Book Book.php:15

One legal entity's accounts (a company, an SCI): its chart, journals, parties, entries and bank accounts all hang off it. Entries dated on or before $lockedUntil can no longer be posted, changed or reversed - the period is closed, declared, or handed to the accountant.

Book::__construct()

public function __construct(string $name, ?string $siren = null, string $currency = 'EUR')

Book::__toString()

public function __toString(): string

Book::getId()

public function getId(): ?int

Book::getName()

public function getName(): string

Book::setName()

public function setName(?string $name): self

Book::getSiren()

public function getSiren(): ?string

Book::setSiren()

public function setSiren(?string $siren): self

Book::getCurrency()

public function getCurrency(): string

Book::setCurrency()

public function setCurrency(?string $currency): self

Book::getLockedUntil()

public function getLockedUntil(): ?\DateTimeImmutable

Book::setLockedUntil()

public function setLockedUntil(?\DateTimeImmutable $lockedUntil): self

Book::getCreatedAt()

public function getCreatedAt(): \DateTimeImmutable

Book::isLocked()

public function isLocked(\DateTimeInterface $date): bool

Whether $date falls in the locked period (on or before lockedUntil).

Book::getBook()

public function getBook(): self

Entry class

class Entry Entry.php:18

A journal entry: balanced lines (debits = credits) on one date, in one journal. $source says where it comes from ("bank:42", "notice:2026-10-U3") and is unique per book, so posting the same thing twice gives the same entry back. Validated, it gets its number (sequential per book and fiscal year) and becomes immutable: a mistake is undone by a reversal entry ($reversalOf), never by an edit.

Entry::__construct()

public function __construct(Book $book, Journal $journal, \DateTimeImmutable $date, string $label, string $source, ?string $piece = null)

Entry::__toString()

public function __toString(): string

Entry::getId()

public function getId(): ?int

Entry::getBook()

public function getBook(): Book

Entry::getJournal()

public function getJournal(): Journal

Entry::getDate()

public function getDate(): \DateTimeImmutable

Entry::getLabel()

public function getLabel(): string

Entry::getPiece()

public function getPiece(): ?string

Entry::getSource()

public function getSource(): string

Entry::getNumber()

public function getNumber(): ?string

Entry::getValidatedAt()

public function getValidatedAt(): ?\DateTimeImmutable

Entry::isValidated()

public function isValidated(): bool

Entry::getReversalOf()

public function getReversalOf(): ?Entry

Entry::setReversalOf()

public function setReversalOf(?Entry $reversalOf): self

Entry::getCreatedAt()

public function getCreatedAt(): \DateTimeImmutable

Entry::validate()

public function validate(string $number, ?\DateTimeImmutable $at = null): self

Numbered and frozen - by Posting, which picks the number.

Entry::getLines()

public function getLines(): Collection

Entry::addLine()

public function addLine(EntryLine $line): self

Entry::getTotalDebit()

public function getTotalDebit(): int

Entry::getTotalCredit()

public function getTotalCredit(): int

Entry::isBalanced()

public function isBalanced(): bool

EntryLine class

class EntryLine EntryLine.php:15

One line of an entry: an amount on the debit or the credit side of an account, maybe for a party. Lettering ties lines of one account and party whose debits and credits cancel out (a rent notice and its payment): they share a $letter and are no longer open items. Only the lettering fields change once the entry is validated.

EntryLine::__construct()

public function __construct(Entry $entry, Account $account, int $debit, int $credit, ?Party $party = null, ?string $label = null)

EntryLine::__toString()

public function __toString(): string

EntryLine::getId()

public function getId(): ?int

EntryLine::getEntry()

public function getEntry(): Entry

EntryLine::getBook()

public function getBook(): Book

EntryLine::getAccount()

public function getAccount(): Account

EntryLine::getParty()

public function getParty(): ?Party

EntryLine::getDebit()

public function getDebit(): int

EntryLine::getCredit()

public function getCredit(): int

EntryLine::getLabel()

public function getLabel(): ?string

EntryLine::getLetter()

public function getLetter(): ?string

EntryLine::getLetteredOn()

public function getLetteredOn(): ?\DateTimeImmutable

EntryLine::isLettered()

public function isLettered(): bool

EntryLine::getNet()

public function getNet(): int

Debit minus credit: positive when the line is owed to the book (a rent notice on 411).

EntryLine::setLetter()

public function setLetter(?string $letter, ?\DateTimeImmutable $on = null): self

By Lettering only: it checks the lines cancel out.

FiscalYear class

class FiscalYear FiscalYear.php:16

An accounting period of a book, usually twelve months. Entry numbers run per fiscal year; once closed, nothing is posted in it any more. A book without fiscal years works on calendar years.

FiscalYear::__construct()

public function __construct(Book $book, \DateTimeImmutable $start, \DateTimeImmutable $end)

FiscalYear::calendar()

public static function calendar(Book $book, int $year): self

The calendar year $year of a book, unsaved: what a book without fiscal years works on.

FiscalYear::__toString()

public function __toString(): string

FiscalYear::getId()

public function getId(): ?int

FiscalYear::getBook()

public function getBook(): Book

FiscalYear::setBook()

public function setBook(Book $book): self

FiscalYear::getStart()

public function getStart(): \DateTimeImmutable

FiscalYear::setStart()

public function setStart(\DateTimeInterface $start): self

FiscalYear::getEnd()

public function getEnd(): \DateTimeImmutable

FiscalYear::setEnd()

public function setEnd(\DateTimeInterface $end): self

FiscalYear::validatePeriod()

public function validatePeriod(ExecutionContextInterface $context): void

What the constructor refuses, said to a form instead of thrown.

FiscalYear::getClosedAt()

public function getClosedAt(): ?\DateTimeImmutable

FiscalYear::setClosedAt()

public function setClosedAt(?\DateTimeImmutable $closedAt): self

FiscalYear::isClosed()

public function isClosed(): bool

FiscalYear::contains()

public function contains(\DateTimeInterface $date): bool

Journal class

class Journal Journal.php:11

A book's journal: BQ (bank), VT (sales: rent notices), AC (purchases), OD (miscellaneous), AN (opening balances).

const BANK = 'BQ'
const SALES = 'VT'
const PURCHASES = 'AC'
const MISCELLANEOUS = 'OD'
const OPENING = 'AN'

Journal::__construct()

public function __construct(Book $book, string $code, string $label)

Journal::__toString()

public function __toString(): string

Journal::getId()

public function getId(): ?int

Journal::getBook()

public function getBook(): Book

Journal::setBook()

public function setBook(Book $book): self

A journal is given its book and its code when it is created: the back office's "new" form writes them, its "edit" form shows them.

Journal::getCode()

public function getCode(): string

Journal::setCode()

public function setCode(?string $code): self

Journal::getLabel()

public function getLabel(): string

Journal::setLabel()

public function setLabel(?string $label): self

Party class

class Party Party.php:17

Someone the book owes or is owed by - a tenant, a supplier, an associate - with their own auxiliary account (411U3 under 411). $reference is the application's handle on them (a lease, a supplier's code), unique per book; $iban is how their transfers are recognised on the bank statement.

Party::__construct()

public function __construct(Book $book, PartyKind $kind, string $name, string $accountNumber, ?string $reference = null, ?string $iban = null)

Party::normalizeIban()

public static function normalizeIban(?string $iban): ?string

Party::__toString()

public function __toString(): string

Party::getId()

public function getId(): ?int

Party::getBook()

public function getBook(): Book

Party::setBook()

public function setBook(Book $book): self

Party::getKind()

public function getKind(): PartyKind

Party::setKind()

public function setKind(PartyKind $kind): self

Party::getName()

public function getName(): string

Party::setName()

public function setName(?string $name): self

Party::getIban()

public function getIban(): ?string

Party::setIban()

public function setIban(?string $iban): self

Party::getReference()

public function getReference(): ?string

Party::setReference()

public function setReference(?string $reference): self

Party::getAccountNumber()

public function getAccountNumber(): string

Party::setAccountNumber()

public function setAccountNumber(?string $accountNumber): self

Left empty (a form gives null), the back office numbers it from the party's kind and reference when it is saved.

Party::getCreatedAt()

public function getCreatedAt(): \DateTimeImmutable

ReconciliationRule class

class ReconciliationRule ReconciliationRule.php:24

What to post a recurring bank line against when nothing open matches it: bank fees to 627, the loan instalment split between 164 (capital) and 6611 (interest), a Stripe payout to the 5112 clearing account. A rule matches on the label ($pattern: words that must all appear, or a /regular expression/), the counterparty's IBAN, and the direction; the highest priority wins.

$split, when set, spreads the amount over several accounts: [{"account": "164", "amount": 85000}, {"account": "6611", "rest": true}] - "amount" a fixed part in minor units, "percent" a share, "rest" what remains (the rule's own account takes the rest when no part says so).

ReconciliationRule::__construct()

public function __construct(Book $book, string $name, ?string $pattern, Account $account, RuleSign $sign = RuleSign::ANY)

ReconciliationRule::__toString()

public function __toString(): string

ReconciliationRule::getId()

public function getId(): ?int

ReconciliationRule::getBook()

public function getBook(): Book

ReconciliationRule::setBook()

public function setBook(Book $book): self

ReconciliationRule::getName()

public function getName(): string

ReconciliationRule::setName()

public function setName(?string $name): self

ReconciliationRule::getPattern()

public function getPattern(): ?string

ReconciliationRule::setPattern()

public function setPattern(?string $pattern): self

ReconciliationRule::getIban()

public function getIban(): ?string

ReconciliationRule::setIban()

public function setIban(?string $iban): self

ReconciliationRule::getSign()

public function getSign(): RuleSign

ReconciliationRule::setSign()

public function setSign(RuleSign $sign): self

ReconciliationRule::getAccount()

public function getAccount(): Account

ReconciliationRule::setAccount()

public function setAccount(Account $account): self

ReconciliationRule::getParty()

public function getParty(): ?Party

ReconciliationRule::setParty()

public function setParty(?Party $party): self

ReconciliationRule::getPriority()

public function getPriority(): int

ReconciliationRule::setPriority()

public function setPriority(int $priority): self

ReconciliationRule::getSplit()

public function getSplit(): ?array

ReconciliationRule::setSplit()

public function setSplit(?array $split): self

ReconciliationRule::isEnabled()

public function isEnabled(): bool

ReconciliationRule::setEnabled()

public function setEnabled(bool $enabled): self

ReconciliationRule::matches()

public function matches(string $label, ?string $iban, int $amount): bool

Whether a bank line of this label, counterparty IBAN and signed amount is one of this rule's.

Base\Ledger\Enum

AccountType enum

enum AccountType AccountType.php:10

What an account is in the balance sheet or the income statement - which side its balance normally sits on (debit for assets and expenses, credit for the others) and whether it is carried forward at year end.

case ASSET = 'asset'
case LIABILITY = 'liability'
case EQUITY = 'equity'
case INCOME = 'income'
case EXPENSE = 'expense'

AccountType::isDebitNormal()

public function isDebitNormal(): bool

AccountType::isResult()

public function isResult(): bool

Income and expenses: closed into the result at year end, not carried forward.

AccountType::guess()

public static function guess(string $number): self

The PCG's default for an account number: its class, and for class 4 the usual side.

BankLineStatus enum

enum BankLineStatus BankLineStatus.php:6

Where a bank line stands in the reconciliation.

case UNMATCHED = 'unmatched'

Nothing found for it yet.

case SUGGESTED = 'suggested'

Suggestions exist, none sure enough to apply on its own: someone decides.

case RECONCILED = 'reconciled'

Posted in the bank journal (its entry) and lettered with what it pays.

case IGNORED = 'ignored'

Deliberately left out of the accounts (an internal transfer already posted, a test).

BankLineStatus::isPending()

public function isPending(): bool

ConsentState enum

enum ConsentState ConsentState.php:10

A bank connection's consent as the ledger keeps it: Omnibank's ConsentStatus, stored by value so the entity does not depend on the package being installed.

case ACTIVE = 'active'
case NEEDS_RENEWAL = 'needs_renewal'
case REVOKED = 'revoked'
case NONE = 'none'

ConsentState::of()

public static function of(\UnitEnum|string|null $status): self

From Omnibank's ConsentStatus (a backed or pure enum), by its value or its name.

PartyKind enum

enum PartyKind PartyKind.php:6

Who a party is to the book: the collective account its own account sits under.

case TENANT = 'tenant'
case SUPPLIER = 'supplier'
case ASSOCIATE = 'associate'
case OTHER = 'other'

PartyKind::collective()

public function collective(): string

The PCG collective account a party of this kind gets an auxiliary account under.

RuleSign enum

enum RuleSign RuleSign.php:6

Which bank lines a reconciliation rule looks at: money coming in, going out, or both.

case ANY = 'any'
case IN = 'in'
case OUT = 'out'

RuleSign::accepts()

public function accepts(int $amount): bool

Base\Ledger\Event

BankLineReconciled class

final class BankLineReconciled BankLineReconciled.php:14

A bank line posted and lettered with what it settles (Reconciler::apply): what the application owes or is owed can follow - a rent call paid, an invoice settled - from the open items it cleared, without posting again.

public readonly BankLine $line
public readonly Entry $entry
public readonly array $openItems

BankLineReconciled::__construct()

public function __construct(public readonly BankLine $line, public readonly Entry $entry, public readonly array $openItems)

Base\Ledger\EventListener

ValidatedEntryGuard class

class ValidatedEntryGuard ValidatedEntryGuard.php:21

A validated entry is part of the books for good: whatever tries to change it, or one of its lines, or to delete either, or to add a line to it, is refused at flush time - the back office, a fixture, a careless service.

The lettering fields of its lines are the only exception: lettering is done after the fact. A mistake is corrected by a reversal entry (Posting::reverse()).

ValidatedEntryGuard::onFlush()

public function onFlush(OnFlushEventArgs $args): void

Base\Ledger\Exception

BankException class

class BankException extends LedgerException BankException.php:6

A bank access that failed: Omnibank missing, a gateway or an account not found, the provider refusing.

LedgerException class

class LedgerException extends RuntimeException LedgerException.php:6

What the ledger refused to do; nothing was written.

LetteringException class

class LetteringException extends LedgerException LetteringException.php:6

Lines that cannot be lettered together: they do not cancel out, or not on one account and party.

LockedEntryException class

class LockedEntryException extends LedgerException LockedEntryException.php:6

A change to a validated entry or its lines (other than their lettering): reverse it instead.

LockedPeriodException class

class LockedPeriodException extends PostingException LockedPeriodException.php:6

A date on or before the book's lockedUntil, or in a closed fiscal year.

PostingException class

class PostingException extends LedgerException PostingException.php:6

An entry Posting refuses: unbalanced, a negative or two-sided line, an unknown account or journal.

ReconciliationException class

class ReconciliationException extends LedgerException ReconciliationException.php:6

A bank line that cannot be reconciled (or undone) that way.

Base\Ledger\Export

FecWriter class

class FecWriter FecWriter.php:21

The Fichier des Écritures Comptables (article A47 A-1 du LPF) of a fiscal year: one row per line of every validated entry, the 18 columns in their order, tab-separated, CRLF, dates as YYYYMMDD, amounts with a decimal comma and no thousands separator. A line on a party's auxiliary account (411U3) is filed under its general account (411) with the auxiliary account and the party's name in CompAuxNum and CompAuxLib.

The file is named <SIREN>FEC<closing date YYYYMMDD>.txt (filename()).

const COLUMNS = ['JournalCode', 'JournalLib', 'EcritureNum', 'EcritureDate', 'CompteNum', 'CompteLib', 'CompAuxNum', 'CompAuxLib', 'PieceRef', 'PieceDate', 'EcritureLib', 'D…

FecWriter::__construct()

public function __construct(readonly EntryRepository $entries, readonly AccountRepository $accounts)

FecWriter::write()

public function write(Book $book, FiscalYear $year): string

FecWriter::filename()

public static function filename(Book $book, FiscalYear $year): string

FecWriter::render()

public function render(Book $book, iterable $entries): string

FecWriter::amount()

public static function amount(int $minor): string

123456 -> "1234,56"

FecWriter::clean()

public static function clean(?string $value): string

No tab, pipe nor line break inside a field: they separate fields and rows.

Base\Ledger\Model

EntryDraft class

final class EntryDraft EntryDraft.php:19

An entry to post, as the application describes it - account numbers and a journal code rather than entities:

$posting->post((new EntryDraft($book, 'VT', $date, 'Loyer octobre U3', 'notice:2026-10-U3', 'AVIS-2026-10-U3')) ->line('411U3', 85000, 0, $tenant) ->line('706', 0, 85000));

$source identifies what the entry records, once: posting the same source again returns the entry already there.

public readonly Book $book
public readonly string $journal
public readonly \DateTimeImmutable $date
public readonly string $label
public readonly string $source
public readonly ?string $piece

EntryDraft::__construct()

public function __construct(public readonly Book $book, public readonly string $journal, public readonly \DateTimeImmutable $date, public readonly string $label, public readonly string $source, public readonly ?string $piece = null)

EntryDraft::line()

public function line(string $account, int $debit, int $credit, ?Party $party = null, ?string $label = null): self

A line: amounts in minor units, one of them zero.

EntryDraft::getLines()

public function getLines(): array

EntryDraft::getTotalDebit()

public function getTotalDebit(): int

EntryDraft::getTotalCredit()

public function getTotalCredit(): int

Text class

final class Text Text.php:6

How bank labels, names and references are compared: upper-case, without accents nor punctuation.

Text::fold()

public static function fold(?string $text): string

"Loyer d'Octobre - Dupont" -> "LOYER D OCTOBRE DUPONT"

Text::squash()

public static function squash(?string $text): string

"AVIS-2026-10/U3" -> "AVIS202610U3": references survive the bank's own spacing and punctuation.

Base\Ledger\Reconciliation

Matcher class

class Matcher Matcher.php:37

What a bank line may settle, ranked. In order of trust:

1. a reference of an open item found in the line's label or reference (the piece of a rent notice, "AVIS-2026-10-U3"; a Stripe payout's id) - 0.99 when the amounts agree; 2. the counterparty's IBAN, a party's, with an open item of exactly the amount (0.95), several adding up to it (0.92-0.94), or none (0.5: a payment on account); 3. a party's name in the label, with an open item of exactly the amount (0.9), several adding up to it (0.85-0.89); 4. nobody recognised, but open items of one party adding up to the amount (0.7), or one open item of exactly the amount (0.75); 5. a reconciliation rule (0.9), with the open items of its account when they add up (0.92: a Stripe payout against the 5112 clearing account).

The same amount owed by two parties, nothing else to tell them apart, scores 0.5 each; two parties recognised by the same IBAN or name, 0.6. Under the auto threshold (0.9), someone decides.

const REFERENCE = 0.99
const REFERENCE_PARTIAL = 0.6
const IBAN_EXACT = 0.95
const IBAN_ALL = 0.9399999999999999
const IBAN_OLDEST = 0.93
const IBAN_GROUP = 0.92
const IBAN_ONLY = 0.5
const NAME_EXACT = 0.9
const NAME_ALL = 0.89
const NAME_OLDEST = 0.88
const NAME_GROUP = 0.85
const AMOUNT_ONLY = 0.75
const GROUP_ONLY = 0.7
const RULE_ITEMS = 0.92
const RULE = 0.9
const AMBIGUOUS_PARTY = 0.6
const AMBIGUOUS = 0.5

Matcher::__construct()

public function __construct(readonly EntryLineRepository $lines, readonly PartyRepository $parties, readonly ReconciliationRuleRepository $rules, readonly int $maxGroupItems = 12)

Matcher::openItems()

public function openItems(Book $book): array

What a bank line can settle in $book: the open lines of the third-party accounts (class 4) and of the clearing accounts (511).

Matcher::suggest()

public function suggest(BankLine $line, ?array $openItems = null): array
$openItems

the book's open items, when the caller has them (autoReconcile)

Returns

best first

Reconciler class

class Reconciler Reconciler.php:36

Turns a bank line into the books: apply() posts it in its bank account's journal - the bank account (512x) against what the suggestion says: the open items' accounts, the rule's account(s), or the party's - and letters it with the open items it settles. Its entry's source is "bank:<line id>", so applying it twice posts it once.

Stripe and the like go through the 5112 clearing account: the payment (5112 / 411) and its fee (6278 / 5112) are posted by the application; the payout on the statement (512 / 5112) settles the 5112 open items, by its reference or by a rule on 5112.

Reconciler::__construct()

public function __construct(readonly EntityManagerInterface $entityManager, readonly Posting $posting, readonly Lettering $lettering, readonly Matcher $matcher, readonly BankLineRepository $bankLines, readonly float $threshold = 0.9, readonly ?EventDispatcherInterface $dispatcher = null)

Reconciler::getThreshold()

public function getThreshold(): float

Reconciler::apply()

public function apply(BankLine $line, Suggestion $suggestion): Entry
Throws

ReconciliationException|\LedgerException

Reconciler::autoReconcile()

public function autoReconcile(BankAccount $account): int

Matches the account's pending lines: a line with exactly one suggestion at or above the threshold is applied; the others are left SUGGESTED (or UNMATCHED, nothing found) for someone to decide. Returns how many were applied.

Reconciler::ignore()

public function ignore(BankLine $line): void

Leaves a line out of the books (an internal transfer posted otherwise, a test).

Reconciler::undo()

public function undo(BankLine $line): void

Back to unmatched: an ignored line simply, a reconciled one by reversing its entry (dated as it was) and unlettering what it settled - refused when that date is in the locked period.

Reconciler::split()

public function split(ReconciliationRule $rule, int $amount): array

A rule's parts of $amount: [account number, minor units, label] - fixed amounts first, then percentages, the rest to the part saying "rest" or to the rule's own account.

Suggestion class

final readonly class Suggestion Suggestion.php:17

What a bank line may be (Matcher), or what someone in the back office says it is: the open items it pays - lettered with it when they add up to its amount -, else the rule it falls under, else the party it comes from (a payment on account). $score runs from 0 to 1; Reconciler applies a suggestion on its own only when it is the single one at or above ledger.reconciliation.auto_threshold.

public float $score
public string $reason
public array $openItems
public ?ReconciliationRule $rule
public ?Party $party

Suggestion::__construct()

public function __construct(public float $score, public string $reason, public array $openItems = [], public ?ReconciliationRule $rule = null, public ?Party $party = null)

Suggestion::total()

public function total(): int

What the open items add up to, debit minus credit: the bank amount they settle exactly.

Suggestion::settles()

public function settles(int $amount): bool

Whether it settles $amount exactly: its open items are then lettered with the bank entry.

Suggestion::key()

public function key(): string

Identity of what it proposes, whatever its score: two ways to the same items are one suggestion.

Suggestion::withScore()

public function withScore(float $score, ?string $reason = null): self

Base\Ledger\Report

BalanceSheet class

class BalanceSheet BalanceSheet.php:30

The bilan at a date: every account's balance from the book's first entry to that day, the assets (actif) on one side, what finances them (passif) on the other - and, when asked, the same day a year earlier beside it.

  • Class 1 is always passif: capital and reserves (10-14), loans (16), tenants' deposits (165); what it holds on the wrong side shows negative.
  • Class 2 is always actif, net: depreciation (28) shows negative under the fixed asset group.
  • The other balance sheet accounts go by their balance: a tenant who owes (411 debit) is a receivable, one who overpaid (411 credit) a debt; the bank (51, 53) is cash, overdrawn a debt; suppliers (40) and associates' current accounts (455) are debts, or receivables when debit.
  • The income and expense accounts (by type) are not closed in the books: their balance is the result, in the capitaux propres - the current fiscal year's, and before it whatever earlier years left unallocated (no closing entry posted to 120/129 and the report à nouveau).

Actif and passif then add up to the same total, or the books are wrong. Draft entries count, as in the trial balance.

const ASSETS = ['fixed_assets' => 'Immobilisations nettes', 'receivables' => 'Créances locataires et clients', 'other_assets' => 'Autres créances', 'cash' => 'Disponibilités']

key => heading, in the order of the bilan

const LIABILITIES = ['equity' => 'Capitaux propres', 'loans' => 'Emprunts', 'deposits' => 'Dépôts de garantie reçus', 'suppliers' => 'Dettes fournisseurs', 'associates' => 'Comp…

key => heading, in the order of the bilan

const RESULT = 'Résultat de l\'exercice'
const EARLIER_RESULTS = 'Résultats antérieurs non affectés'

BalanceSheet::__construct()

public function __construct(readonly EntryLineRepository $lines, readonly FiscalYearRepository $fiscalYears)

BalanceSheet::compute()

public function compute(Book $book, \DateTimeImmutable $at, bool $withPrevious = false): array

Amounts are in minor units, positive on their normal side. The result lines have no account number. previous is null throughout unless $withPrevious.

BalanceSheet::fromTotals()

public static function fromTotals(array $totals, ?array $yearTotals = null, ?array $previous = null, ?array $previousYear = null): array
$totals

every entry up to the date

$yearTotals

the fiscal year's entries up to it: the result they make is the year's, the rest of the result earlier years'; null, all of it is the year's

$previous

the same a year earlier (N-1)

BalanceSheet::place()

public static function place(string $number, int $balance): array
Returns

where a balance sheet account's balance (debit minus credit) goes

GeneralLedger class

class GeneralLedger GeneralLedger.php:10

The grand livre: every line of every account over a period, with the running balance and the account's totals.

GeneralLedger::__construct()

public function __construct(readonly EntryLineRepository $lines)

GeneralLedger::compute()

public function compute(Book $book, ?\DateTimeImmutable $from = null, ?\DateTimeImmutable $to = null, ?string $accountPrefix = null): array

GeneralLedger::fromLines()

public static function fromLines(iterable $lines): array
$lines

by account then date

IncomeStatement class

class IncomeStatement IncomeStatement.php:23

The compte de résultat over a period: income (class 7) and expenses (class 6) in the headings an SCI's accounts are read under, their totals and the result (bénéfice when positive, perte when negative) - and, when asked, the same period a year earlier beside it (N-1).

An account goes to the group of the longest prefix it starts with on its side (7083 to the rents, 708 to the recovered charges), and to "Autres produits" / "Autres charges" when none matches. Which side is the account's type (income, expense), as for the trial balance's result.

Draft entries count, as in the trial balance: the statement is what the books say today, before the accountant validates them.

const INCOME = ['rents' => ['Loyers', ['706', '7083']], 'recovered_charges' => ['Charges récupérées', ['708']], 'other_income' => ['Autres produits', ['7088']]]

key => [heading, account prefixes]

const EXPENSES = ['rental_charges' => ['Charges locatives et de copropriété', ['614', '6061']], 'repairs' => ['Entretien et réparations', ['615', '6063']], 'insurance' => ['A…

key => [heading, account prefixes]

IncomeStatement::__construct()

public function __construct(readonly EntryLineRepository $lines)

IncomeStatement::compute()

public function compute(Book $book, \DateTimeImmutable $from, \DateTimeImmutable $to, bool $withPrevious = false): array

Amounts are in minor units, income and expenses both positive when normal (a refund larger than the charge makes it negative); previous is null throughout unless $withPrevious.

IncomeStatement::fromTotals()

public static function fromTotals(array $totals, ?array $previous = null): array
$previous

the N-1 period's

IncomeStatement::yearBefore()

public static function yearBefore(\DateTimeImmutable $date): \DateTimeImmutable

The same day a year earlier, a month's last day staying its last day (28/02/2029 → 29/02/2028, 29/02/2028 → 28/02/2027): the N-1 period of a twelve-month fiscal year is the fiscal year before.

OpenItems class

class OpenItems OpenItems.php:15

What is still owed, party by party: the unlettered lines of the accounts under a prefix (411 - tenants - by default; 401 for suppliers), their balance and how old the oldest is.

OpenItems::__construct()

public function __construct(readonly Lettering $lettering)

OpenItems::compute()

public function compute(Book $book, ?Party $party = null, ?string $accountPrefix = '411', ?\DateTimeImmutable $at = null): array

OpenItems::fromLines()

public static function fromLines(iterable $lines, ?\DateTimeImmutable $at = null): array

TrialBalance class

class TrialBalance TrialBalance.php:14

The balance générale: per account, the debit and credit totals over a period and the balance they leave, with totals per class and overall (debits = credits, or the books are wrong).

TrialBalance::__construct()

public function __construct(readonly EntryLineRepository $lines)

TrialBalance::compute()

public function compute(Book $book, ?\DateTimeImmutable $from = null, ?\DateTimeImmutable $to = null): array

TrialBalance::fromTotals()

public static function fromTotals(array $totals): array

TrialBalance::typeOf()

public static function typeOf(array $row): AccountType

A totals row's account type: the enum, its value, or the PCG's guess from the number.

Base\Ledger\Repository

AccountRepository class

class AccountRepository extends ServiceEntityRepository AccountRepository.php:11

AccountRepository::__construct()

public function __construct(ManagerRegistry $registry)

AccountRepository::findOneByNumber()

public function findOneByNumber(Book $book, string $number): ?Account

AccountRepository::chart()

public function chart(Book $book): array
Returns

the book's chart, by number

AccountRepository::findParent()

public function findParent(Book $book, string $number): ?Account

The account $number belongs under: the book's account with the longest number $number starts with (401 for 4011, 41 for 4199).

BankAccountRepository class

class BankAccountRepository extends ServiceEntityRepository BankAccountRepository.php:11

BankAccountRepository::__construct()

public function __construct(ManagerRegistry $registry)

BankAccountRepository::forBook()

public function forBook(?Book $book = null): array
Returns

every bank account, or a book's

BankAccountRepository::countForBook()

public function countForBook(Book $book): int

BankConnectionRepository class

class BankConnectionRepository extends ServiceEntityRepository BankConnectionRepository.php:11

BankConnectionRepository::__construct()

public function __construct(ManagerRegistry $registry)

BankConnectionRepository::forBook()

public function forBook(?Book $book = null): array

BankLineRepository class

class BankLineRepository extends ServiceEntityRepository BankLineRepository.php:12

BankLineRepository::__construct()

public function __construct(ManagerRegistry $registry)

BankLineRepository::findOneByExternalId()

public function findOneByExternalId(BankAccount $account, string $externalId): ?BankLine

BankLineRepository::pending()

public function pending(BankAccount $account): array
Returns

the lines still to reconcile (unmatched or suggested), oldest first

BankLineRepository::latest()

public function latest(?BankAccount $account = null, int $limit = 50, ?BankLineStatus $status = null): array
Returns

a bank account's lines, newest first

BankLineRepository::countByStatus()

public function countByStatus(BankAccount $account): array
Returns

status value => count, for one bank account

BookRepository class

class BookRepository extends ServiceEntityRepository BookRepository.php:10

BookRepository::__construct()

public function __construct(ManagerRegistry $registry)

BookRepository::resolve()

public function resolve(string $handle): ?Book

A book by its id, SIREN or exact name: what the commands accept.

EntryLineRepository class

class EntryLineRepository extends ServiceEntityRepository EntryLineRepository.php:14

EntryLineRepository::__construct()

public function __construct(ManagerRegistry $registry)

EntryLineRepository::openItems()

public function openItems(Book $book, ?Party $party = null, ?string $accountPrefix = null): array

The unlettered lines - what is still owed, either way - of the accounts under $accountPrefix (all when null), maybe one party's, oldest first.

EntryLineRepository::findByLetter()

public function findByLetter(Book $book, string $letter): array

EntryLineRepository::lastLetter()

public function lastLetter(Book $book): ?string

The last letter given in the book: letters run AAA, AAB... ZZZ, AAAA.

EntryLineRepository::totalsByAccount()

public function totalsByAccount(Book $book, ?\DateTimeImmutable $from = null, ?\DateTimeImmutable $to = null, bool $validatedOnly = false): array

Debit and credit totals per account over [$from, $to] (either open).

EntryLineRepository::forLedger()

public function forLedger(Book $book, ?\DateTimeImmutable $from = null, ?\DateTimeImmutable $to = null, ?string $accountPrefix = null): array

The lines of the accounts under $accountPrefix over [$from, $to], by account then date: the general ledger's rows.

EntryLineRepository::sumUnder()

public function sumUnder(Book $book, string $accountPrefix, ?\DateTimeImmutable $from = null, ?\DateTimeImmutable $to = null, bool $openOnly = false): array

Debit and credit totals of the accounts under $accountPrefix, all lines or only the unlettered ones.

EntryLineRepository::balance()

public function balance(Account $account, ?\DateTimeImmutable $to = null): int

An account's balance (debit minus credit), all dates or up to $to.

EntryRepository class

class EntryRepository extends ServiceEntityRepository EntryRepository.php:11

EntryRepository::__construct()

public function __construct(ManagerRegistry $registry)

EntryRepository::findOneBySource()

public function findOneBySource(Book $book, string $source): ?Entry

EntryRepository::lastNumber()

public function lastNumber(Book $book, string $prefix): ?string

The highest number given with this prefix ("2026-"): numbers are zero-padded, so the greatest string is the last.

EntryRepository::findReversal()

public function findReversal(Entry $entry): ?Entry

The entry reversing $entry, if one does.

EntryRepository::forPeriod()

public function forPeriod(Book $book, \DateTimeImmutable $from, \DateTimeImmutable $to, bool $validatedOnly = true): array

The book's entries dated in [$from, $to], with their lines, in the order the FEC lists them (date, then number).

FiscalYearRepository class

class FiscalYearRepository extends ServiceEntityRepository FiscalYearRepository.php:11

FiscalYearRepository::__construct()

public function __construct(ManagerRegistry $registry)

FiscalYearRepository::containing()

public function containing(Book $book, \DateTimeInterface $date): ?FiscalYear

The fiscal year of $book that $date falls in, if the book has one.

FiscalYearRepository::forYear()

public function forYear(Book $book, int $year): FiscalYear

The fiscal year closing in $year, else the calendar year $year (unsaved).

FiscalYearRepository::current()

public function current(Book $book, ?\DateTimeImmutable $date = null): FiscalYear

The fiscal year $date falls in, or its calendar year.

JournalRepository class

class JournalRepository extends ServiceEntityRepository JournalRepository.php:11

JournalRepository::__construct()

public function __construct(ManagerRegistry $registry)

JournalRepository::findOneByCode()

public function findOneByCode(Book $book, string $code): ?Journal

PartyRepository class

class PartyRepository extends ServiceEntityRepository PartyRepository.php:11

PartyRepository::__construct()

public function __construct(ManagerRegistry $registry)

PartyRepository::findOneByReference()

public function findOneByReference(Book $book, string $reference): ?Party

PartyRepository::findByIban()

public function findByIban(Book $book, ?string $iban): array
Returns

the book's parties paying from (or paid to) this IBAN

PartyRepository::accountNumberTaken()

public function accountNumberTaken(Book $book, string $number): bool

ReconciliationRuleRepository class

class ReconciliationRuleRepository extends ServiceEntityRepository ReconciliationRuleRepository.php:11

ReconciliationRuleRepository::__construct()

public function __construct(ManagerRegistry $registry)

ReconciliationRuleRepository::forBook()

public function forBook(Book $book): array
Returns

a book's enabled rules, highest priority first

Base\Ledger\Security

BookAccessInterface interface

interface BookAccessInterface BookAccessInterface.php:15

Who else may reach a book than ledger.admin_role: an application's service implementing this (autoconfigured, tag ledger.book_access) is asked by LedgerVoter - an associate viewing their company's book, an accountant managing their clients'. Any service saying yes is enough.

BookAccessInterface::canView()

public function canView(UserInterface $user, Book $book): bool

BookAccessInterface::canManage()

public function canManage(UserInterface $user, Book $book): bool

StateCipher class

class StateCipher StateCipher.php:15

Seals what a bank connection keeps (the provider's tokens, its user) with sodium's secretbox (XSalsa20-Poly1305), under a key derived from ledger.secret (the kernel secret by default): "v1:" + base64(nonce + ciphertext). A sealed state that was tampered with, or sealed under another secret, does not open.

StateCipher::__construct()

public function __construct(string $secret)

StateCipher::seal()

public function seal(array $data): string

StateCipher::open()

public function open(string $sealed): array
Throws

BankException when it was not sealed by this secret, or was changed since

Base\Ledger\Security\Voter

LedgerVoter class

final class LedgerVoter extends Voter LedgerVoter.php:26

LEDGER_VIEW and LEDGER_MANAGE, on a Book or on anything of one (an entry, an account, a bank line: whatever has getBook()):

  • ledger.admin_role (ROLE_ADMIN by default) views and manages every book;
  • the application's BookAccessInterface services may grant a user a book (managing implies viewing).

Without a subject - the back office's ledger screens as a whole - only the admin role is granted.

const VIEW = 'LEDGER_VIEW'
const MANAGE = 'LEDGER_MANAGE'

LedgerVoter::__construct()

public function __construct(readonly AccessDecisionManagerInterface $accessDecisionManager, readonly iterable $access = [], readonly string $adminRole = 'ROLE_ADMIN')

LedgerVoter::bookOf()

public static function bookOf(mixed $subject): ?Book

Base\Ledger\Service

Chart class

class Chart Chart.php:20

Gives a book its chart of accounts and journals from a file of config/charts/ (sci: the PCG accounts a French real-estate SCI uses). It adds what is missing and leaves alone what the book already has, so it can run again after the chart grows.

Chart::__construct()

public function __construct(readonly EntityManagerInterface $entityManager, readonly AccountRepository $accounts, readonly JournalRepository $journals, readonly string $defaultChart = 'sci', readonly ?string $chartsDir = null)

Chart::seed()

public function seed(Book $book, ?string $chart = null): void

Chart::definition()

public function definition(string $chart): array

Lettering class

class Lettering Lettering.php:18

Lettering: lines of one account and one party whose debits and credits cancel out - a rent notice and the transfer paying it - share a letter (AAA, AAB...) and stop being open items. Partial payments stay open until the rest arrives.

Lettering::__construct()

public function __construct(readonly EntityManagerInterface $entityManager, readonly EntryLineRepository $lines)

Lettering::letter()

public function letter(iterable $lines, ?\DateTimeImmutable $on = null): string

Letters $lines together and returns their letter.

Throws

LetteringException unless they balance, on one account and one party, none lettered yet

Lettering::unletter()

public function unletter(string $letter, Book $book): int

Takes a letter off the lines carrying it in $book; returns how many.

Lettering::openItems()

public function openItems(Book $book, ?Party $party = null, ?string $accountPrefix = '411'): array

The unlettered lines of the accounts under $accountPrefix (411: what tenants still owe, and their payments not matched yet), maybe one party's, oldest first.

Lettering::nextLetter()

public function nextLetter(Book $book): string

The letter after the book's last one: AAA, AAB... AAZ, ABA... ZZZ, AAAA.

Lettering::increment()

public static function increment(string $letter): string

Parties class

class Parties Parties.php:20

The book's parties as the application knows them: partyFor() finds one by the application's reference (a lease, a supplier code) or creates it, with its auxiliary account under its kind's collective account - 411 + the reference for a tenant (411U3), 401 for a supplier, 455 for an associate, 467 otherwise.

Parties::__construct()

public function __construct(readonly EntityManagerInterface $entityManager, readonly PartyRepository $parties, readonly AccountRepository $accounts)

Parties::partyFor()

public function partyFor(Book $book, PartyKind|string $kind, string $reference, string $name, ?string $iban = null): Party

Parties::accountNumber()

public function accountNumber(Book $book, PartyKind $kind, string $reference): string

411 + the reference, upper-case and alphanumeric (411U3), made unique in the book.

Posting class

class Posting Posting.php:31

The one way entries get into a book. post() checks what double-entry bookkeeping requires - balanced, one-sided non-negative lines, accounts and a journal the book has, a date outside the locked period - and refuses anything else with a PostingException, before writing a thing.

The same (book, source) twice gives back the entry already posted.

Validated, an entry is numbered "<fiscal year>-<000001>", in sequence per book and fiscal year, and frozen (ValidatedEntryGuard): it is corrected by reverse(), which posts its mirror image.

Posting::__construct()

public function __construct(readonly EntityManagerInterface $entityManager, readonly AccountRepository $accounts, readonly JournalRepository $journals, readonly EntryRepository $entries, readonly FiscalYearRepository $fiscalYears, readonly bool $autoAccounts = false, readonly ?LockFactory $locks = null)

Posting::post()

public function post(EntryDraft $draft, bool $validate = true): Entry
Throws

PostingException the draft is not a valid entry of its book

Throws

LockedPeriodException its date is in the locked period or a closed fiscal year

Posting::reverse()

public function reverse(Entry $entry, string $source, ?\DateTimeImmutable $date = null): Entry

Cancels a validated entry by its mirror image (debits and credits swapped), dated $date or the entry's own date, and returns it. The same $source twice gives back the reversal already posted.

Posting::validate()

public function validate(Entry $entry): Entry

Numbers and freezes a draft entry.

Posting::assertOpen()

public function assertOpen(Book $book, \DateTimeInterface $date): void

Throws when nothing may be posted on $date in $book.

Posting::nextNumber()

public function nextNumber(Book $book, \DateTimeInterface $date): string

The number the next validated entry of $book dated $date gets.

Base\Ledger\Twig

LedgerExtension class

final class LedgerExtension LedgerExtension.php:13

{{ 123456|ledger_money }} → 1 234,56 € (minor units, the locale's way); {{ 123456|ledger_amount }} → 1 234,56 (no currency: a column of a report); {{ iban|ledger_iban }} → FR76 •••• 4321.

LedgerExtension::money()

public function money(int|string|null $minor, string $currency = 'EUR', ?string $locale = null): string

LedgerExtension::amount()

public function amount(int|string|null $minor): string

LedgerExtension::iban()

public function iban(?string $iban): string