ntro.capabilities.gl is the external accounting system integration layer: one stable Python surface (GLProvider, resource clients, typed models) regardless of which underlying GL the customer connects.
It is distinct from:
| Layer | Role |
|---|---|
ntro.accounting | Fund-ops domain models (trial balance shape, journal proposals, validation) inside Ntropii. |
ntro.subledger | Tenant ledgers.* rows and lifecycle before/at posting. |
ntro.capabilities.gl | Read/write the customer’s GL (bills, invoices, TB reports, …) through the unified provider. |
BillProposal, etc.) strip Ntropii-only metadata before crossing into gl — see ntro.subledger.proposals and the unified-adapter docs in source.
Install
[gl] extra pulls the unified-GL SDK. Worker images that post to Xero / QuickBooks / Sage should include this extra — same as runbooks that call gl.for_entity.
Workflow authoring typically still pins ntro[workflow] for Temporal + capabilities; add [gl] when the runbook posts to a connected ledger.
Resolve a provider
Providers are selected fromtenant.config.gl:
provider— the registered GL adapter (e.g. the bundled unified adapter, or a future direct provider).options— provider-specific connection (consumer id, service id, etc.), populated after the tenant completes GL connect in Ntropii Workspace.
tenant.config.gl.provider is missing or unknown, resolution raises ProviderNotRegisteredError or ConnectionError_ — handle these in activities (many runbooks short-circuit posting when no GL is configured).
Example: post bills from approved expenses
Fromexpense_processor.post_expenses_to_gl — proposals built from subledger rows, then provider.bills.create with an idempotency external id:
external_id; providers must honour deduplication and expose find_by_external_id so Temporal retries stay safe.
Errors you should handle
| Exception | Typical meaning |
|---|---|
ConnectionError_ | GL not linked / credentials missing. |
ValidationFailedError | Provider rejected payload (posted journal, invalid account, …). |
IdempotencyConflictError | Same external_id, different body — logic bug or race. |
TransientProviderError | Retryable — often re-raised so Temporal retries the activity. |
Models
Types such asJournalEntry, Bill, LedgerAccount, … are re-exported from ntro.capabilities.gl.models. Prefer importing from gl.models in runbook code so upgrades stay centralized.
Reports — typed read-only views of GL state
A report is a typed, point-in-time view of the customer’s GL state —TrialBalanceReport, future BalanceSheetReport, ProfitAndLossReport. They live at ntro.capabilities.gl.reports (sibling to models).
Reports differ from subledgers: no per-row HITL lifecycle, no posting back, no transactional shape. They’re inputs to a workflow run, ephemeral after the run closes. The same typed shape is hydrated regardless of source.
Pattern B — the canonical flow for reports
<100ms, vs ~30s if the workflow re-runs ai.extract itself (the original parse_starting_tb shape pre-N-70).
Two source paths, one shape
| Source | When | How |
|---|---|---|
| Cached extraction (Pattern B) | Greenfield — customer’s doc-uploaded TB; no GL bound or no opening balance seeded yet | TrialBalanceReport.from_extracted_payload(cached_payload, period) |
| Live API (future) | Once customer’s GL is bound and opening balances posted | await provider.trial_balance.get(period=…) |
from_extracted_payload adapts the document-ingest payload dict; the TrialBalanceClient.get(...) Protocol returns the same TrialBalanceReport from the underlying provider. Workflow code is invariant across sources.
The <capability>.reports.<Type> convention
This pattern generalises across capabilities. Each “external ledger” capability (gl, banking, …) gets a sibling reports module:
| Module | Types it owns |
|---|---|
ntro.capabilities.gl.reports | TrialBalanceReport, future BalanceSheetReport, ProfitAndLossReport |
ntro.capabilities.banking.reports (forthcoming, N-72) | BankStatementReport |
from_extracted_payload(...) constructor shape across all of them.
Related
Accounting
Domain journals and proposals before external posting.
Subledgers
Typed rows and
propose_for_gl metadata.Data plane
Resolving tenant DB connections inside activities.