Ledgers
Verified against source (14)
accounting/Ledger.csaccounting/LedgerItem.csConfig/Ledger.configConfig/LedgerItem.configConfig/LedgerTemplate.configConfig/LedgerTemplateItem.configProperties/Settings.settingsTables/Ledger.sqlInterfaces/IPaymentProvider.csqbo4.Payment.AuthorizeNet/PaymentProvider.csUsage/LedgerUsageHandler.csUsage/LedgerUsageOptions.csExtensions/AbstractExtensions.csExtensions/ServiceCollectionExtensions.cs
Ledgers use four classes, each with the standard routes: /api/{module}/{operation} runs synchronously, and /api/{module}/queue/{operation} queues the operation for an app server.
| Class | Module | Purpose |
|---|---|---|
LedgerObject |
ledger |
One ledger, bound to a parent through Object / ObjectID. Balance and BalanceAdjusted are computed; don’t set them. |
LedgerItemObject |
ledgeritem |
One line: AmountMoney, AmountDecimal, Units, UnitCount, Rate, IsDebit, IsTotaled, PostedDate, PaidDate, TransactionNumber, and an optional ChildLedgerID. |
LedgerTemplateObject |
ledgertemplate |
Implements ITemplate; adds RefreshOnInsert, CalculateMethod, ProjectMethod, InvoiceTemplateID, InvoiceMatrixID. |
LedgerTemplateItemObject |
ledgertemplateitem |
A template line: defaults, IsDebit, IsTotaled, CanExclude, CanRefresh, ManualInsert, MatrixID. |
Create a ledger
Section titled “Create a ledger”POST /api/ledger/save?Template=Case Fees&Object=Process&ObjectID=1001On insert, a database trigger adds a LedgerItem for each template item whose ManualInsert is 0 or 2, copying its name, type, sequence, debit and totaled flags, lock, units, and default amount. If the template has RefreshOnInsert, the ledger is then calculated before the call returns. The template’s Repeatable setting applies as described in Templates.
Calculate
Section titled “Calculate”GET /api/ledger/calculate?ID=55GET /api/ledger/calculate?ID=55&RecalculateItem=false- Project items (unless
RecalculateItem=false). Without a templateProjectMethod, QBO runs theProjectionListGoodThroughstatement (withGoodThrough, defaulting to now) and calculates each row: a child ledger’s adjusted balance, then the item’s matrix measures (applied by property name), then template defaults. - Total. Without a template
CalculateMethod, theCalculatestatement setsBalanceto the sum of debits minus credits over items with anAmountMoney, andBalanceAdjustedto the same sum over items whereIsTotaledis true.IsDebitandIsTotaledfall back to the template item when null on the line; a nullIsDebitcounts as a credit. - Roll up to any
LedgerItemwhoseChildLedgerIDis this ledger, setting itsAmountMoneyto this ledger’sBalanceAdjusted.
A custom CalculateMethod must return Balance and BalanceAdjusted in its first row.
Saving a LedgerItem re-totals its ledger without re-projecting. Inserting an item fails with an alert when the ledger’s IsExpandable is false; updating a locked item fails likewise.
To preview projected lines without saving them:
GET /api/ledgeritem/project?LedgerID=55&GoodThrough=2026-12-31Approve, post, and lock
Section titled “Approve, post, and lock”GET /api/ledger/approve?ID=55,56GET /api/ledger/post?ID=57| Operation | Effect |
|---|---|
/api/ledger/approve |
If the template has an InvoiceMatrixID or InvoiceTemplateID and the ledger has no invoice yet, generates the invoice (the matrix’s AttachmentTemplate measure, looked up against the parent, wins). Sets Completed, Status = Approved, IsLocked = true, then runs LockItems. |
/api/ledger/post |
Sets PostedDate, Status = Posted, IsLocked = true, then runs LockItems. |
/api/ledger/lockitems |
Locks every item on the listed ledgers. |
Both refuse, with an alert, when the ledger is locked (without the LedgerUpdateLocked permission) or already in the target status.
Line item operations
Section titled “Line item operations”| Operation | Effect |
|---|---|
/api/ledgeritem/calculate?ID=… |
Recalculates and saves the items. |
/api/ledgeritem/includeintotal?ID=… |
Sets IsTotaled on unlocked items. |
/api/ledgeritem/excludefromtotal?ID=… |
Clears IsTotaled on unlocked items whose template allows exclusion. |
/api/ledgeritem/toggleistotaled?ID=… |
Flips IsTotaled and re-totals the ledger and its parent line. |
/api/ledgeritem/approve?ID=…, /api/ledgeritem/reject?ID=… |
Sets status Approved (and totals the item) or Rejected (and excludes it); used for timesheet entries. |
/api/ledgeritem/recalculate?ID=… |
Re-totals the ledgers that own the items. |
Charge a stored payment method
Section titled “Charge a stored payment method”GET /api/ledger/authorizeandcapture?ID=55For each ledger, QBO takes BalanceAdjusted (or Balance). If it is greater than zero, QBO finds the parent record’s Active payment method and invokes that payment method’s AuthorizeAndCapture service with the LedgerID and PaymentMethodID; with no active payment method it throws Invalid PaymentMethod. If nothing is owed, the ledger is posted instead. A locked ledger throws unless the caller has LedgerUpdateLocked.
Gateways implement IPaymentProvider, which covers customers, addresses, stored (vaulted) payment methods, authorize, capture, void, refund, one-time payments, and subscriptions behind one interface. Card and bank details are vaulted with the gateway; QBO keeps the gateway’s identifiers. Authorize.Net is the shipped implementation. See Integrations.
Record and apply a payment
Section titled “Record and apply a payment”POST /api/ledger/applypayment?LedgerID=55&AmountMoney=750.00&TransactionNumber=TX-1042ApplyPayment requires AmountMoney. It:
- Creates a payment line on the ledger, labeled and typed
Paymentby default (settingsApplyPaymentDefaultLabelandApplyPaymentType), posted now. Other parameters, such asTransactionNumberorDescription, are set on that line. - Adds any funds left unapplied by earlier payments.
- Walks the ledger’s unpaid items (no
PaidDate,AmountMoney≥ 0). Each item the remaining funds cover in full gets a Payment Applied line in the payment’s child ledger, referencing the item. It stops at the first item it can’t cover; partial payments are off. - Records any remainder as an Unapplied Funds line (setting
ApplyPaymentUnappliedFunds) and in the payment’sAmountMoneyCountered.
It returns the saved payment LedgerItem, and throws if the ledger has no items to pay.
Record AI token costs
Section titled “Record AI token costs”Register AddLedgerUsageTracking (configuration section GenerativeAI:LedgerUsage) to write AI usage to ledgers. Then pass Chat_LedgerID to an AI operation that accepts chat options (aiimport, aireview, aiupdate, aiupdatexml):
GET /api/attachment/aireview?ID=7&Template={message template}&Agent={agent}&Chat_LedgerID=42For each call, QBO adds up to four LedgerItem rows, one per token type with a non-zero count: Input, CachedInput, Output, and Reasoning. Each row has Units set to the token type, UnitCount and AmountDecimal set to the token count, LedgerItemType set to the model, IsDebit and IsTotaled true, and a shared TransactionNumber (the conversation ID, or a new GUID) grouping the call.
| Option | Purpose |
|---|---|
InputRatesPerMToken, OutputRatesPerMToken |
Dollars per million tokens, keyed by model ID (exact or prefix match). |
CachedInputRatesPerMToken, ReasoningRatesPerMToken |
Optional; fall back to the input and output rates. |
DefaultInputRatePerMToken, DefaultOutputRatePerMToken, … |
Rates for models not listed. With no rate, AmountMoney is left empty and only tokens are recorded. |
LedgerTemplateItemID, LedgerTemplateItemIDs |
Template line for the rows: per token type, then per call (Chat_LedgerTemplateItemID), then the default. |
Rows are written as the user who made the AI call; calls without a trusted user, or without a ledger, are skipped.