139 lines
8.0 KiB
Markdown
139 lines
8.0 KiB
Markdown
# Accounts Receivable Aging — Reverse-Engineered Logic
|
||
|
||
This document is the source-of-truth specification for how the month-end **Amazon
|
||
Accounts Receivable Aging** workbook is calculated. It was reverse-engineered from the
|
||
actual January-2026 files and every figure below is verified to the penny by the engine
|
||
(`app/core`) and the reconciliation test (`tests/test_reconciliation_jan2026.py`).
|
||
|
||
> **Benchmark:** USA receivable = **11,110,433** = `Detail!D11`.
|
||
|
||
---
|
||
|
||
## 1. Source files
|
||
|
||
Finance exports the Amazon **Custom Unified Transaction** report in ~10-day chunks:
|
||
|
||
| File | Rows | Dates | Settlement id range |
|
||
|---|---:|---|---|
|
||
| `USA…01 to 10 January,2026.xlsx` | 778,356 | Jan 1–10 | 25218044401 → 25434014921 |
|
||
| `USA…11 to 20 January,2026.xlsx` | 823,483 | Jan 11–20 | 25231317951 → 25468048861 |
|
||
| `USA…21 to 31 January,2026.xlsx` | 777,392 | Jan 21–31 | 25342616501 → 25564376091 |
|
||
|
||
Each file has **two worksheets**: a user-made **pivot ("Sheet1") that must be ignored**,
|
||
and the single **raw data worksheet** the app parses. The app auto-detects the raw sheet
|
||
by locating the header row containing `settlement id` + `total` + `date/time`.
|
||
|
||
### Schema (30 columns, header row 8, data from row 9)
|
||
`A date/time · B settlement id · C type · D order id · E sku · F description · G quantity ·
|
||
H marketplace · I account type · J fulfillment · K order city · L order state · M order postal ·
|
||
N tax collection model · O product sales · P product sales tax · Q shipping credits ·
|
||
R shipping credits tax · S gift wrap credits · T giftwrap credits tax · U Regulatory Fee ·
|
||
V Tax On Regulatory Fee · W promotional rebates · X promotional rebates tax ·
|
||
Y marketplace withheld tax · Z selling fees · AA fba fees · AB other transaction fees ·
|
||
AC other · AD total`
|
||
|
||
- `AD total` is the **net per-row amount** (already nets O..AC) — the only amount the receivable needs.
|
||
- `date/time` is **text** ("Jan 15, 2026 5:45:40 PM PST"); `settlement id` and amounts are numeric.
|
||
- `account type` (col I) ∈ {`Standard Orders`, `Invoiced Orders`}; **blank/populated on Transfer rows
|
||
but Transfer rows are always excluded from the receivable**.
|
||
- **Transaction types:** Order, Refund, Adjustment, FBA Customer Return Fee, Order_Retrocharge,
|
||
Refund_Retrocharge, Service Fee, Service Fee - Reversal, FBA Inventory Fee, FBA Transaction fees,
|
||
Amazon Fees, Fee Adjustment, Shipping Services, SAFE-T reimbursement, A-to-z Guarantee Claim,
|
||
Chargeback Refund, Liquidations, Others, **Transfer**.
|
||
|
||
---
|
||
|
||
## 2. The sample workbook (`Accounts Receivable Aging (Jan-26) 1.xlsx`, 18 sheets)
|
||
|
||
- **`Summary`** — A/R Aging Summary. One row per vendor × bands **Current / 1-30 / 31-60 / 61-90 /
|
||
91-Over**. **All Amazon receivable sits in "Current"** (other bands = 0). `Amazon USA` row =
|
||
`=Detail!D11`. `TOTAL = SUM`, then `Allowance for Sales Returns` (manual) → `Net Receivable`.
|
||
- **`Detail`** — the engine (one column block per marketplace; USA uses 3 columns Invoiced / Standard /
|
||
Total). Roll-forward rows R4–R11 + FX rows R23–R24 (see §4).
|
||
- **`COA`** — chart-of-accounts / FX table (`B local · C rate · D=B×C · E currency`), rates manual.
|
||
- **14 marketplace tabs** (`USA (19-20)`, `USA (21-31)`, Canada, UK, …) — each holds only the
|
||
**unpaid (receivable) transactions** for that marketplace, same 30-col schema, Transfer rows lifted
|
||
to the top (feed Receipts), bottom subtotal cells:
|
||
`AD = SUMIFS($AD$8:$AD$last, $I$8:$I$last, "Standard Orders")` (and `"Invoiced Orders"`).
|
||
|
||
---
|
||
|
||
## 3. Settlement classification — which rows are unpaid (the crux)
|
||
|
||
Settlement ids are **monotonic** (higher = newer). Amazon closes a settlement when the next opens
|
||
and disburses it via a **`Transfer` row tagged with the *next/open* settlement id** that pays out the
|
||
*prior* settlement(s). **A settlement is receivable until the payout that clears it is *received* in
|
||
the bank by month-end.**
|
||
|
||
Full verified Jan-2026 chain:
|
||
|
||
| Settlement | Acct | Net orders (Σ total, non-transfer) | Payout Transfer | Received by Jan-31? | Status |
|
||
|---|---|---:|---|---|---|
|
||
| 25218044401 | Std | 55,026.85 | (Dec payout) | yes | paid |
|
||
| 25327934061 | Std | 4,471,986.74 | −15,323,853.80 (Jan 8) | yes | paid |
|
||
| 25434014921 | Std | 3,244,454.86 | −3,244,604.61 (Jan 16) | yes | paid |
|
||
| **25468048861** | Std | **9,556,111.11** | −9,555,841.69 (Jan 30) | **no (in transit)** | **receivable** |
|
||
| **25546871121** | Std | **1,486,342.10** | (February) | no | **receivable** |
|
||
| 25231317951 | Inv | 156,883.15 | −228,606.81 (Jan 20) | yes | paid |
|
||
| **25342616501** | Inv | **67,501.46** | (February) | no | **receivable** |
|
||
| **25564376091** | Inv | **353.25** | (February) | no | **receivable** |
|
||
|
||
**Algorithm (auto clearing-lag; `app/core/settlements.py`):**
|
||
1. Collect `Transfer` rows → disbursements `(date, settlement_id, amount, account_type)`.
|
||
2. A disbursement is **received** iff `date ≤ month_end − clearing_lag_days` (default **2**). The
|
||
Jan-30 payout → in-transit → settlement 25468048861 stays receivable. Users can override per
|
||
transfer in the Settlement Reconciliation page.
|
||
3. Per `(marketplace, account_type)`: `paid_boundary = max(settlement_id over received transfers)`;
|
||
**receivable settlements = settlement_id ≥ paid_boundary** (equivalently: a settlement is paid iff
|
||
its id < paid_boundary).
|
||
4. **Receivable = Σ `total` for non-Transfer rows** (account_type = Standard/Invoiced Orders) in
|
||
receivable settlements.
|
||
|
||
---
|
||
|
||
## 4. Receivable calculation (`Detail` roll-forward)
|
||
|
||
Per marketplace, per account type:
|
||
|
||
```
|
||
Additional sales after last payment = SUMIFS(total, account_type) over receivable settlements
|
||
Net Closing Balance (Reserve) = Opening + Sales + Receipts + Refunds + Expenses (≈ 0)
|
||
Receivable (local currency) = ROUND( reserve + additional_sales , 0 ) [marketplace level = Detail!D11]
|
||
Receivable (USD) = Receivable_local × FX_rate (USA rate = 1)
|
||
```
|
||
|
||
Jan-2026 USA:
|
||
- Standard additional = **11,042,453.21**, Invoiced additional = **67,854.71**, total = **11,110,307.92**.
|
||
- Reserve (Net Closing Balance): Standard **125.44**, Invoiced **0** → `ROUND(11,110,433.36)` = **11,110,433**.
|
||
|
||
**Reserve / opening.** The reserve nets to ~0 by design; it is a Finance-maintained reconciliation
|
||
carry (prior-month `Opening Balance` + last-settlement reconciliation). It cannot be reconstructed
|
||
exactly from the date-range files alone because received transfers pay out multiple settlements
|
||
including prior-month carryover. It is therefore an **optional per-(marketplace, account_type) input**
|
||
(default 0). Without it, the receivable base from the files alone is **11,110,308** (11,110,307.92
|
||
rounded); with the workbook's 125.44 reserve it is the exact **11,110,433**.
|
||
|
||
---
|
||
|
||
## 5. FX, aging, reconciliation
|
||
|
||
- **FX:** manual per-marketplace rate to USD (`COA` / `Detail!R23`). USA = 1. Editable & importable;
|
||
never fetched online; every rate is shown and auditable in the export.
|
||
- **Aging:** all Amazon receivable is **Current** (matches workbook). A day-based aging function
|
||
(`classify_aging`) is available for completeness.
|
||
- **Reconciliation:** identity `uploaded_total = receivable_orders + paid_orders + transfers_total`
|
||
(proves nothing was dropped); `final_receivable = Σ ROUND(reserve + additional_sales)×fx + manual
|
||
adj`. Status **Reconciled** when |identity| and |vs-expected| ≤ tolerance (default USD 0.01).
|
||
|
||
---
|
||
|
||
## 6. Notes / open items
|
||
|
||
- The **01-10 file was originally truncated** (56 MB, cut mid-data during copy); the re-shared 212 MB
|
||
copy is valid. The 01-10 data contains only **paid** settlements (< the receivable boundary) so it
|
||
contributes **0** to the receivable — but it is retained for reserve reconciliation and audit trail.
|
||
- Reserve/opening exact reproduction depends on the prior-month close (external to these files);
|
||
surfaced as a manual input with a reconciliation schedule for transparency.
|
||
- Scope is **all Amazon marketplaces** (generic engine); only USA files were supplied for Jan-2026,
|
||
so other regions activate when their Amazon files are uploaded.
|