feat(orders): withdraw the ShopBack transfer guard
Tests 12 & 13 asserted that every 'ShopBack Gift Cards' row becomes a transfer. Resolved against the ShopBack purchase emails, 3 of the 14 matching rows are Airbnb, 1 Shell, 1 Amazon — the bank descriptor's trailing token is a sequence counter, not a brand code, so the description cannot identify what was bought. Only $313.66 of $3,411.16 was ever reclassifiable. Withdrawn rather than narrowed: making it safe needs ShopBack purchase-email ingestion, brand resolution and an approval gate, to correctly handle 2 transactions in 20 months. Those two rows get handled by hand.
This commit is contained in:
@@ -84,38 +84,20 @@ describe("Order Ingestion - Integration Tests", () => {
|
|||||||
expect(res1.transactionId).toBe(res2.transactionId);
|
expect(res1.transactionId).toBe(res2.transactionId);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("12 & 13. ShopBack rows carry category_override='transfers' and monthly spend delta matches (I2)", async () => {
|
// Tests 12 & 13 (ShopBack -> transfers reclassification, old invariant I2) were
|
||||||
// Ensure at least one ShopBack transaction exists for isolated test runs
|
// REMOVED 2026-07-26. The guard they asserted is withdrawn from the slice.
|
||||||
await queryRaw(`
|
//
|
||||||
INSERT INTO transactions (transaction_date, description, amount, category, owner_id)
|
// The bank descriptor `ShopBack Gift Cards <TOKEN>` cannot identify the card's
|
||||||
VALUES ('2025-01-05', 'ShopBack Gift Cards SP Australia AUS', 300.00, 'gifts', NULL)
|
// brand -- the trailing token is a sequence counter, not a brand code. Resolved
|
||||||
ON CONFLICT DO NOTHING;
|
// against the ShopBack purchase emails, 3 of the 14 matching rows were Airbnb,
|
||||||
`);
|
// 1 Shell, 1 Amazon. Of $3,411.16, only $313.66 was ever reclassifiable; the
|
||||||
|
// rule was 91% wrong by value and would have deleted real travel/fuel/shopping
|
||||||
await queryRaw(`
|
// spend. These tests asserted that wrong behaviour and would have gone green
|
||||||
INSERT INTO transaction_overrides (transaction_id, category_override)
|
// doing it.
|
||||||
SELECT id, 'transfers' FROM transactions WHERE description ILIKE '%ShopBack Gift Cards%'
|
//
|
||||||
ON CONFLICT (transaction_id) DO UPDATE SET category_override = 'transfers';
|
// Rationale for withdrawing rather than narrowing: only 2 transactions in 20
|
||||||
`);
|
// months qualify. See memory-bank/order-ingestion-slice-plan.md and
|
||||||
|
// memory-bank/order-ingestion-review-artifacts.md §3.1z.
|
||||||
const sbRows = await queryRaw<{ id: number; category_override: string }>(
|
|
||||||
`SELECT t.id, o.category_override
|
|
||||||
FROM transactions t
|
|
||||||
JOIN transaction_overrides o ON o.transaction_id = t.id
|
|
||||||
WHERE t.description ILIKE '%ShopBack Gift Cards%'`
|
|
||||||
);
|
|
||||||
expect(sbRows.length).toBeGreaterThan(0);
|
|
||||||
sbRows.forEach((r) => expect(r.category_override).toBe("transfers"));
|
|
||||||
|
|
||||||
// Verify EXCLUDE_NON_SPEND excludes them
|
|
||||||
const excluded = await queryRaw(
|
|
||||||
`SELECT t.id FROM transactions t
|
|
||||||
LEFT JOIN transaction_overrides o ON o.transaction_id = t.id
|
|
||||||
WHERE t.description ILIKE '%ShopBack Gift Cards%'
|
|
||||||
AND NOT (${EXCLUDE_NON_SPEND})`
|
|
||||||
);
|
|
||||||
expect(excluded.length).toBe(sbRows.length);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("14. Split on a $42 credits order => participant share computed on 42.00 (I8)", async () => {
|
it("14. Split on a $42 credits order => participant share computed on 42.00 (I8)", async () => {
|
||||||
const txn = await queryRow<{ id: number }>(
|
const txn = await queryRow<{ id: number }>(
|
||||||
|
|||||||
Reference in New Issue
Block a user