docs: what importing a BNPL account changes, and the Zip data decisions
ci / lint-test (push) Successful in 41s
ci / lint-test (push) Successful in 41s
24 Zip statements landed (Dec 2020 - Jun 2026), all balance-asserting at $0.00. Zip is a credit account, so three existing row types had to be re-read. Data changes applied via transaction_overrides; this records why. A ZIPPAY debit leaving the bank duplicates the Zip statement's purchases only where a Zip statement covers that period. 2 of 8 do, and are now transfers ($125.97). The other six stay spend: for those periods the bank debit is the only record the purchase happened, so moving them would have deleted $703.35 of real spend. 'Account Credit' is cashback, confirmed by the owner and consistent with Zip labelling its other money-in types explicitly (Payment = repayment, 'Refund <merchant>' = merchant refund). 34 rows, $740, round amounts, no matching bank debit. Gemini split the identical description 23 'other' (subtracts from spend) / 11 'transfers' (excluded), so one thing did two opposite things. Now all 34 shopping+credit, netting against spend the way refunds already do. Income was rejected for the reason the Up item sales note gives. Prezzee gift cards ($1,580) stay spend — stored value only nets out if every downstream purchase is logged, and for 2020 it is not. Same reasoning as the ATM-withdrawal rule. 31 uncategorised debits mapped by merchant with merchant_normalized set too, since the raw descriptions carry card numbers and order UUIDs. All 137 Zip rows are now categorised; $11,135.11 net spend, $10,085.11 transfers. Flags statement 200 (Sept 2021): extracted without billing_end_date or balances, so it is the one statement uq_statement_identity cannot cover (the index is partial on billing_end_date IS NOT NULL) and a re-import would duplicate it.
This commit is contained in:
@@ -685,6 +685,44 @@ in a review table. Consents expire annually, so expect it.
|
||||
|
||||
Undo an import: `DELETE FROM transactions WHERE source = '<source>'`.
|
||||
|
||||
### Zip, and the three things a BNPL account needs (2026-08-15)
|
||||
|
||||
24 statements, Dec 2020 → Jun 2026, all balance-asserting at $0.00. Zip is a
|
||||
credit account, so importing it changes how three existing row types must read.
|
||||
|
||||
**1. A repayment leaving the bank is a duplicate ONLY where a Zip statement
|
||||
covers the purchases.** Zip's own `Payment` rows are already `transfers`. On the
|
||||
bank side, `ZIPPAY*` debits are the same money — but only 2 of 8 fall inside a
|
||||
Zip statement period. Those two are now `transfers`; **the other six must stay as
|
||||
spend**, because for those periods the bank debit is the only record the purchase
|
||||
ever happened. Moving all eight would have deleted $703.35 of real spend. Same
|
||||
statement-coverage rule as the CSV importer, and the same trap.
|
||||
|
||||
**2. `Account Credit` is cashback, and Zip labels its three money-in types
|
||||
differently.** `Payment` = repayment from the bank. `Refund <merchant>` = a
|
||||
genuine merchant refund. `Account Credit` = Zip cashback — 34 rows, $740, round
|
||||
amounts ($20 ×20, $30 ×10, $10 ×4), with no matching debit from any bank account.
|
||||
|
||||
Gemini filed the identical description two ways — 23 as `other` (which *subtracts*
|
||||
from spend) and 11 as `transfers` (excluded entirely) — so one thing was doing two
|
||||
opposite things. All 34 are now `shopping` + `credit`, **netting against spend**:
|
||||
cashback lowers what a purchase actually cost, which is how `NET_SPEND_ROWS` /
|
||||
`SPEND_SIGNED` already treat a refund. Filing it as `income` was rejected for the
|
||||
reason the Up item sales note gives — it flatters `net = income − spent` and mixes
|
||||
non-earnings into the salary line. Ties to the **Cashback tracking (ING-6)** epic;
|
||||
a dedicated `cashback` category would need the CHECK constraint, the TS mirror,
|
||||
and a decision in every analytics fragment, so it was not done here.
|
||||
|
||||
**3. A gift card stays spend.** Prezzee is $1,580 of the history. Stored value is
|
||||
tempting to call `transfers`, but that only works if every downstream purchase is
|
||||
logged — it is not, for 2020. Identical reasoning to the ATM-withdrawal rule.
|
||||
|
||||
All 137 Zip rows are categorised; net spend is $11,135.11 against $10,085.11 of
|
||||
`transfers`. **Statement 200 (Sept 2021) came through incomplete** — no
|
||||
`billing_end_date`, no balances, 2 rows — so it is the one statement not covered
|
||||
by `uq_statement_identity` (the index is partial on `billing_end_date IS NOT
|
||||
NULL`) and a re-import would duplicate it. Worth re-processing paperless doc 296.
|
||||
|
||||
### Trusting extracted statement data
|
||||
|
||||
**Balance assertions are the check that works.** `getStatements` computes
|
||||
|
||||
Reference in New Issue
Block a user