fix(accounting): resolve the return buyCrypto via the chargeback relation#4259
fix(accounting): resolve the return buyCrypto via the chargeback relation#4259Danswar wants to merge 1 commit into
Conversation
…tion A BUY_CRYPTO_RETURN bank_tx is linked from buy_crypto.chargebackBankTx; tx.buyCrypto is the inbound-payment inverse and is null on returns. The owed-CHF anchor therefore threw 'without buyCrypto.amountInChf' on every return since the cutover (prod: bank_tx 206858/206864 → buy_crypto 128422/128912, whose amountInChf is present), wedging the bank_tx watermark head-of-line. Load buyCryptoChargeback in both consumer queries and prefer it in buyCryptoOwedChf (fallback to buyCrypto unchanged). The existing return specs set tx.buyCrypto directly and so encoded the wrong relation; the new spec mirrors the prod linkage (buyCrypto null, buyCryptoChargeback set) and fails on the previous code.
TaprootFreak
left a comment
There was a problem hiding this comment.
Approve — verified against the code and against prod (read-only)
Correct, well-targeted fix. The wedge and the fix both check out.
Root cause confirmed. A return bank_tx is linked from buy_crypto.chargebackBankTx, so tx.buyCrypto (the inbound-payment inverse that buyCryptoOwedChf read) is null on returns → the old code threw. Preferring buyCryptoChargeback resolves the right anchor.
Verified against PRD data:
| bank_tx | type | as inbound | as chargeback | → tx.buyCrypto |
amountInChf |
|---|---|---|---|---|---|
| 206858 | BuyCryptoReturn | 0 | 1 | null | 9226.11 (bc 128422) |
| 206864 | BuyCryptoReturn | 0 | 1 | null | 9.25 (bc 128912) |
Exactly the shape the fix handles. The new spec's 9226.11 mirrors buy_crypto 128422 to the cent; totalFeeAmountChf is NULL on both → ?? 0 → owed = 9226.11.
Adversarial checks, all clean:
buyCryptoChargebackrelation exists on the entity; theusergetter already tried both — consistent.buyCryptoOwedChfhas a single caller,buyCryptoReturnLegs(§4.2a, return-only) — the??preference can't misroute a non-return.- No unguarded third path: both queries that feed booking — the §4.12 content-change scan and the
processForwardbackfill — now load the relation. The head-of-linebreakinprocessForwardis what made 206858 a hard wedge (nothing after it books); this closes it. - All three usages inside
buyCryptoOwedChfswitched consistently.
Two non-blocking notes:
- The throw message still reads
without buyCrypto.amountInChfwhile the check now uses the chargeback relation — fine to keep for log-grep stability, just slightly stale. - The spec sets
totalFeeAmountChf: 0; prod hasNULL. Functionally identical via?? 0, butnullwould mirror prod exactly.
Expected after deploy: the two returns book, the bank_tx watermark advances past 206858, and the gate-blocked content-change retries clear. Same class as the cutover relation bug (#4253).
|
Superseded by #4261 (merged to #4261 folds in the two notes from my review here:
Verified equivalent at the fix site (both queries load the Note for tracking: the wedge is still live on PRD — #4261 is on |
|
For coordination (process note, no blame): this overlapped with independent work on the same bug — #4259 and #4261 fixed the identical wrong-relation read in parallel. Worth a quick heads-up in the channel next time a ledger fix is in flight, so two branches don't chase the same wedge. Nothing to action here; #4261 is the one that shipped. |
Since the cutover every
BUY_CRYPTO_RETURNbooking fails with "bank_tx X BUY_CRYPTO_RETURN without buyCrypto.amountInChf" — and because the consumer is head-of-line-blocking, the bank_tx watermark is wedged: nothing afterbank_tx 206858gets booked.The data is intact: the returns' buy_cryptos (128422 / 128912) carry their
amountInChf. The bug is the relation — a return bank_tx is linked frombuy_crypto.chargebackBankTx, sotx.buyCrypto(the inbound-payment inverse, whichbuyCryptoOwedChfreads) is null on returns. The entity itself models this (bankTx.buyCryptoChargeback, and itsusergetter already tries both).Fix: load
buyCryptoChargebackin both consumer queries and prefer it inbuyCryptoOwedChf(fallback tobuyCryptounchanged, message unchanged).The existing return specs set
tx.buyCryptodirectly and so encoded the wrong relation; the new spec mirrors the prod linkage (buyCrypto null, buyCryptoChargeback set) — verified red on the previous code, green with the fix (71/71).Expected after deploy: the two prod returns book, the bank_tx watermark advances, and the
content-change scan gate-blockedretries on buy_crypto 128422 / buy_fiat 68748 clear.