Skip to main content

Ledger (Accounting v2)

📄️Rebuild Journal Entry

Rebuild the entry's SOURCE document INLINE (single-entry action): the user clicked Rebuild on the entry detail page and expects the regenerated entry to be current the instant the request returns, not after the async drain catches up. The rebuild writes the same `updated` outbox row a real source mutation rides, then processes JUST that row synchronously through the shared outbox processor (build → persistSet → batch-assign → sync-select) — so posted-immutability (reversal + replacement) holds exactly as on the async path.

📄️Refresh Journal Entry

Refresh this entry's amounts from its SOURCE document IN PLACE — the light counterpart to Rebuild. Where Rebuild corrects a posted entry by writing a reversal + a replacement, Refresh re-derives the entry's lines and totals and writes them onto the SAME entry: its id, status and accounting-provider link are all kept, so a posted, already-synced entry whose local figure drifted is corrected WITHOUT creating a new entry and WITHOUT touching the linked Xero / QuickBooks document. After the rewrite the sync state is re-graded — a still-editable remote is offered an update, while a settled/locked or voided remote is left as-is (nothing is pushed).

📄️Resync Entry to Provider

Push this SINGLE entry to the connected accounting provider SYNCHRONOUSLY (InlineLedgerSyncRunner): the provider round-trip runs inline within the request, so the response already reflects the real Synced / Failed outcome — there is no interim 'queued' state to poll. This powers the entry detail page's 'Resync Now' button and its live 'talking to the provider' animation. The bulk equivalent — `POST /api/ledger/entries/bulk` with action `sync_now` — stays an async tracked job.

📄️Preview Bill Settlement With Vendor Credits

Preview the plan to settle a bill (a purchase-invoice entry) using the vendor credits already allocated against it — the read side of the 'sync & mark paid' flow. Reports whether the entry is a settleable bill, whether the bill itself is synced to the accounting provider, and the state of each applied vendor credit: `missing_transaction` (its accounting transaction has not been generated yet), `unsynced` (generated but not pushed to the provider), or `synced` (its credit note is pushed and its allocation applied). Use it to show exactly what will happen before committing.

📄️Settle Bill With Vendor Credits

Settle a bill (a purchase-invoice entry) using the vendor credits allocated against it so the connected accounting provider marks the bill PAID — the missing half of two-way sync when the allocations exist here but were never pushed. Dispatches a tracked background job that (1) syncs the bill so its remote document exists, (2) for each applied vendor credit generates its accounting transactions if missing, syncs its credit note, then pushes the allocation (Xero credit-note allocation / QuickBooks zero-dollar bill payment), and (3) re-reads the bill so its link re-grades to Synced, Locked (paid). Idempotent — re-running skips work already done, so a partial failure is safe to retry.

📄️Fetch Latest From Provider

The inbound mirror of 'Sync Now' (— two-way sync, on demand): instead of PUSHING local changes out, it re-reads this entry's remote document(s) FROM the accounting provider (Xero / QuickBooks) and re-grades the sync link RIGHT NOW. For each supported, still-linked connection it fetches the current remote truth via the same InboundChangeProcessor funnel the webhook + nightly sweep use — picking up drift (→ linked_conflict), a void/delete (→ remote_gone), a settled/paid lock (→ remote_locked), or a previously settled lock that has re-opened for edits — the paying payment / credit note was reversed at the provider, e.g. Xero PAID → AUTHORISED — which un-parks the link (→ linked_stale) so it is ready to resync — and refreshes `last_remote_seen_at`. A 404 at the provider degrades to a synthetic-deleted snapshot so the link still parks remote_gone rather than silently staying Synced.

📄️Create Opening Balance Equity Account

Create the provider-conventional 'Opening Balance Equity' account when the chart of accounts has none, so the opening-balance form's balancing-equity selector has something to point at. The created account is type `Equity` and is a never-synced internal plug. Its `code` is the first free preferred code (970, then 3000, then 9999, then numerically incremented past the last candidate), checked against ALL codes including archived ones since `code` is the unique key.

📄️Backfill Provider Cache

Populate the local provider document cache (the xero_/qbo_ mirror) for a connection by dispatching a tracked background job that downloads every remote document over the reconcile window. The reconcile screen, matching, and the period-comparison report all read off this local cache, so this is the on-demand, UI-triggered equivalent of the `ledger:cache:backfill` artisan command. Returns the tracked job log id so the frontend can follow progress via the tracked job log endpoints.

📄️Get Inventory Reconciliation Awaiting-Sync Items

Per earmarked inventory account, the SKU ledger activity not reflected in the provider balance, split by sync disposition: `pending_ledger`/`pending_count` WILL reach the provider once it syncs (self-resolving), while `excluded_ledger`/`excluded_count` will NEVER sync (dated before the books' sync start date, or internal-only) — a permanent mismatch a measure-from baseline sets aside. Batch-aggregate aware (a batched document counts once, via its batch). Requires connection_id.

📄️Get Inventory Reconciliation Opening Breakdown

The warehouse-first opening breakdown for reconciling inventory to the accounting provider. For each earmarked inventory control account, returns the provider general-ledger balance and its divergence from the SKU inventory value at the opening date, broken down into the individual warehouses that hold that account's stock. The provider balance and divergence are reported at the account level (a provider account has no warehouse dimension); the SKU side sums standard and Amazon FBA inventory per warehouse. Warehouses that currently hold no stock are still listed so an account's full warehouse footprint is visible. The opening date is the reconciliation baseline when one has been set, otherwise the configured inventory start date. Reads materialized/cached data only — no live provider call.

📄️Get Inventory Reconciliation Adjustment Preview

The proposed adjusting journal that would move the provider's inventory control account to match SKU as of a date — computed read-only so you see exactly what would post. `divergence = provider − SKU`; a positive gap credits the inventory control account (reducing it) and debits the counter, both for the same amount. When the date is inside a locked provider period `postable` is false: post the journal dated on or after `earliest_postable_date` instead. Accounts backed by Amazon warehouses (FBA/AWD) are Amazon-sourced — their stock comes from Amazon and cannot be adjusted in SKU (`sku_side_adjustable` is false), so they can only be reconciled provider-side; other accounts may instead correct SKU's opening for the warehouses in `warehouses`.

📄️Sync Provider GL Journals

Kick the background tracked job that pulls the provider's **GL journals feed** (Xero Journals) into the local cache — the metered half of the daily reconciliation. One paginated pull per run; resumes incrementally from the highest JournalNumber already cached. Once cached, **Rebuild Inventory Daily Series** reconstructs the daily provider balance from the cache with no further provider call. Returns the tracked job log id so the UI can follow it in the global progress tray.

📄️Upload Statement Screenshot

Upload a screenshot of bank statement lines and **queue** its OCR extraction (Azure Document Intelligence prebuilt-layout). Returns immediately with the import in the `extracting` state plus a top-level `tracked_job_log_id`; the parsed lines arrive once the background a background job completes (watch it via the tracked job log endpoints, then re-fetch the import via GET `.../bank-statements/{id}`). Deferring the OCR keeps an unreachable provider from hanging the upload request.

📄️List Line Match Suggestions

For each parsed line in an import, candidate SKU ledger entries it could reconcile against. Money-in lines (Received) match AR documents (Sales Order Invoice, Vendor Credit); money-out lines (Spent) match AP documents (Bill, Purchase Order Invoice, Sales Credit). Candidates are entries of the matching type whose total is within $0.01 of the line amount and whose effective date is within 60 days of the statement date, ranked by date proximity then amount closeness. `synced` flags entries already pushed to the provider.

📄️Push Matched Lines to Xero

Push the matched line(s)' SKU document(s) to the accounting provider (Xero) via the shared ledger sync pipeline (the same dedup-aware path the rest of the app posts through). Only lines whose `match_status` is `matched` are pushed; each matched entry that is already linked-current in Xero is skipped, others are enqueued as a Create/Update sync operation. Optional body `line_ids` scopes it to a selection (a single line, or a bulk pick); omit to push every matched line.

📄️Close Period

Close and lock the period. Only the earliest non-closed month can close (periods close strictly in order). Every failing blocking check refuses the close; every warning-level failure and incomplete manual item must be listed in acknowledged_keys. The close runs as a background job that re-validates all gates, snapshots the report pack, inserts the period lock (new entries dated on or before the period end are refused; corrections are redated to the first open day), and records the sign-off. Returns the background job id.

📄️Capture Provider Trial Balances

Capture the accounting provider's trial balance as of the period end (period key format YYYY-MM), for every connected accounting integration. This is the one metered provider pull behind the Trial Balance Reconciliation report and the trial_balance_reconciled close check — both read only the cached snapshot this produces. Synchronous: one trial-balance call per connection; connections whose provider exposes no trial-balance report are skipped, not failed. Re-running re-captures in place so provider-side edits are picked up.