Skip to content

Settlement Accounting

Sticky converts Amazon settlement reports or current Finances API transactions into a balanced, A2X-style journal preview. The first release is limited to eComHD's US marketplace and the Hemani Holdings chart of accounts.

Safety posture

  • QuickBooks posting and scheduled automatic posting use separate seller-level kill switches. The scheduled runner refuses to act unless both are enabled.
  • The generator remains preview-only. A separate nightly posting step handles only exception-free preview_ready batches.
  • Generated batches are either preview_ready or blocked.
  • Approved batches and every batch with a posting attempt are immutable to preview regeneration.
  • A batch cannot be ready unless its debits equal credits and the complete settlement source total equals Amazon's stated payout within one cent.
  • Approval stores a SHA-256 hash of the exact QBO request. Posting refuses if the source rows or approved payload change.
  • A posting run requires an exact payload approval hash and live QBO duplicate/readback verification for every journal. The scheduled path records sticky-auto-policy as the policy approver; exception batches are never eligible.

Data flow

  1. pull_settlement.py downloads Amazon settlement TSVs.
  2. Every report row is stored, including legitimate identical rows. The first copy keeps the legacy content hash; repeated copies receive deterministic duplicate ordinals and hashes.
  3. Header timestamps populate settlement_start, settlement_end, and deposit_date; report_total_amount preserves Amazon's payout control total.
  4. generate_settlement_journals.py applies the configurable rules in accounting.account_mapping_rules.
  5. Order principal is split by brand through accounting.brand_account_mappings. Catalog gaps can be resolved by explicit accounting.sku_brand_rules.
  6. Liquidation rows use an ASIN in Amazon's SKU field. Sticky resolves that ASIN back to the catalog SKU/title and posts liquidation principal to the applicable brand child account.
  7. accounting.non_posting_accounts blocks parent/control accounts at preview and posting time. 400 Sales - Amazon FBA US (QBO id 125) is explicitly non-posting.
  8. Settlements spanning a calendar month are split into monthly journal segments using Amazon Carried Balances. The final segment posts the payout to GNTY Clearing 6153.

Historical groups that are no longer available from the Reports API use a second lossless path:

  1. sync_financial_event_groups.py records Amazon's exact group id, boundary, transfer date, beginning balance, and payout control total.
  2. pull_financial_transactions_by_group.py calls Finances v2024-06-19 with a FINANCIAL_EVENT_GROUP_ID filter and stores every compressed response page with a SHA-256 digest. Transfer transactions are retained as evidence but excluded from the next group's settlement activity.
  3. normalize_financial_transaction_groups.py converts the nested transaction breakdowns to settlement-report categories. It creates a beginning-balance component only when that amount is mathematically required to reconcile the group. Explicit reserve debits and credits map to A2X's Current Reserve and Previous Reserve categories.
  4. validate_financial_transaction_groups.py requires contiguous pages, matching hashes, unique transaction ids, and an exact group control total.
  5. generate_financial_group_journals.py applies the same account, brand, carry, non-posting, and QBO safety rules as report-based previews.

Amazon is retiring the legacy v0 financial-event-detail operation on 2026-08-28. Sticky uses v0 only to enumerate financial event groups and uses the supported v2024-06-19 listTransactions operation for detail.

A2X handoff and recovered backlog

The cutoff is defined by source streams, not an arbitrary date:

  • Final primary A2X journal: A2XUS-01Mar-10Mar-441.
  • Final secondary A2X journal: A2XUS-01Mar-11Mar-379.
  • First missing primary Amazon group starts 2025-03-10T16:02:06Z.
  • First missing secondary Amazon group starts 2025-03-12T06:49:24Z.

The post-A2X register contains 147 closed USD groups totaling $813,367.65. Calendar-month splitting produced 177 journal entries. On 2026-08-03, all 177 were posted and read back successfully as QBO journal entries 10535 through 10711. Every document number is unique, every posting attempt is verified, and no line uses non-posting QBO account 125. Automatic posting remains disabled. That sentence describes the August 3 backlog close. Migration 0051 later enables the separate fail-closed scheduled policy for new, exception-free eComHD batches.

Source coverage for those 177 previews is:

  • 66 entries from 47 groups recovered wholly from Finances v2024-06-19.
  • 71 entries from 61 groups whose legacy report rows were provisional and were replaced as preview inputs by exact Finances transactions.
  • 40 entries from 39 lossless, exactly matched settlement reports.

Positive Amazon amounts become credits; negative amounts become debits. This produces the same accounting direction observed in Hemani Holdings' historical A2X journals for sales, fees, refunds, tax, reimbursements, reserves, storage, and settlement clearing.

Commands

Recover and validate the historical Finances source without contacting QBO:

/usr/bin/python3 scripts/sync_financial_event_groups.py \
  --seller ecomhd_us --start 2025-03-01 --end 2026-07-31
/usr/bin/python3 scripts/pull_financial_transactions_by_group.py \
  --seller ecomhd_us --unmatched-only
/usr/bin/python3 scripts/validate_financial_transaction_groups.py \
  --seller ecomhd_us --unmatched-only
/usr/bin/python3 scripts/normalize_financial_transaction_groups.py \
  --seller ecomhd_us --unmatched-only
/usr/bin/python3 scripts/generate_financial_group_journals.py \
  --seller ecomhd_us --unmatched-only --dry-run

Use --match-method posted_dates_and_closest_total instead of --unmatched-only for the 61 provisional legacy-report groups.

Generate and store the ten most recent previews:

/usr/bin/python3 scripts/generate_settlement_journals.py \
  --seller ecomhd_us --latest 10

Generate one preview without writing it:

/usr/bin/python3 scripts/generate_settlement_journals.py \
  --seller ecomhd_us --settlement-id 27217869441 --dry-run

Inspect readiness:

SELECT settlement_id, document_number, status, clearing_amount,
       reconciliation_difference, unmapped_source_amount
FROM accounting.journal_batches
ORDER BY transaction_date DESC;

Journal document numbers use STKUS-DDMon-DDMon-NNN. The five-character prefix keeps the complete value within QuickBooks Online's 21-character DocNumber limit.

Guarded QBO workflow

Show the exact journal request without contacting QBO:

/usr/bin/python3 scripts/post_settlement_journal.py --batch-id 47 --show

Run read-only live checks against the RPP HH connection. This verifies the source fingerprint, balance, active account ids/names, and absence of the DocNumber in QBO:

/usr/bin/python3 scripts/post_settlement_journal.py \
  --batch-id 47 --preflight \
  --confirm-document STKUS-28Jul-30Jul-441

Approval and posting are separate commands. Approval records the reviewer, time, and exact payload hash:

/usr/bin/python3 scripts/post_settlement_journal.py \
  --batch-id 47 --approve \
  --approved-by '<reviewer>' \
  --confirm-document STKUS-28Jul-30Jul-441

The later --post command requires the typed DocNumber again. It claims the batch in Postgres before any external side effect, checks QBO for duplicates a second time after the claim, sends one JournalEntry, and reads the entry back by DocNumber. Attempts are stored in accounting.journal_posting_attempts; approval, claim, POST, and readback events are append-only in accounting.journal_posting_events.

A network timeout or QBO 5xx leaves the batch in posting with an ambiguous attempt. It must be reconciled by DocNumber and is never retried blindly. A successful POST is never automatically deleted if readback fails.

Production status and 2025 audit control

The recovered backlog was fully posted on 2026-08-03. The final checks found 177 stored batches, 177 QBO STKUS journals, 177 unique document numbers, 177 verified attempts, and zero unposted batches.

The scheduled fail-closed policy is now live for new eComHD work. A production check on 2026-08-26 found 193 stored batches through 2026-08-23, all 193 posted, 193 verified posting attempts, and no non-verified attempt. Exception batches remain ineligible for automatic posting.

For Sticky's post-A2X portion of 2025:

  • 47 Amazon financial groups and 194,036 source transactions passed the raw page, hash, duplicate-id, and group-control validator with zero mismatches.
  • Amazon transaction activity from 2025-03-10 through 2025-12-31 totaled $584,469.82. The actual non-control lines in the 61 QBO Sticky journals for 2025 total exactly $584,469.82.
  • Amazon's signed payout controls for the 42 closed post-cutoff groups paid in 2025 totaled $563,965.04. The actual signed QBO clearing lines total exactly $563,965.04.
  • Unmapped source amount and reconciliation difference are both zero.

The complete calendar-year audit is a bridge: retain the existing A2X journals through the two documented March cutoffs, then use Sticky from the first missing primary and secondary groups onward. The A2X-side January-to-cutoff audit remains separate from the proven Sticky post-cutoff totals above.