fix(orders): the restaurant is the merchant, not the courier
ci / lint-test (push) Successful in 43s

Reverses a change made on request. The platform in the headline
fragmented the merchant: the same restaurant read as two, depending on
who carried the bag, and that is not a distinction anyone rating the food
cares about. It also already has a home — the expandable Order details
panel renders expense_metadata.platform next to its heading, which is
where the user asked for it.

The fragmentation was worse than cosmetic. merchantVerdict joined on an
exact merchant_normalized, and the platforms capitalise differently
("TEG Kebabs & Biryani" on Uber Eats, "TEG KEBABS & BIRYANI" on
DoorDash), so one restaurant kept two separate histories and a "never
again" recorded through one app never warned in the other — silently
defeating the point of the memory. Now case-folded; verified on real
data, where the DoorDash order sees 1 prior verdict against 0 before.

81 existing descriptions backfilled in one transaction, dry-run first and
dumped beforehand. The regex is anchored to the end so suburb parens
survive: "Order - Coles (Wyndham Vale) (Uber Eats)" becomes
"Order - Coles (Wyndham Vale)", not "Order - Coles".
This commit is contained in:
2026-07-28 17:51:36 +10:00
parent 50c5b7c430
commit 0595d49d5c
5 changed files with 53 additions and 20 deletions
@@ -177,7 +177,7 @@ describe("Order ingestion — invariants", () => {
expect(b.skipped).toBe("already_ingested");
expect(b.metadataId).toBe(a.metadataId);
const n = await queryRow<{ c: string }>(
`SELECT count(*)::text c FROM transactions WHERE description = 'Order - Mad Mex (DoorDash)'`
`SELECT count(*)::text c FROM transactions WHERE description = 'Order - Mad Mex'`
);
expect(Number(n!.c)).toBe(1);
});
@@ -389,15 +389,17 @@ describe("owner scoping", () => {
});
describe("how an ingested order presents in the app", () => {
it("names the platform in the description", async () => {
// "Order - Burger Corner" gives no way to know where to look for the
// detail, and the same restaurant can be on both platforms.
it("names the restaurant, not the courier", async () => {
// Reversed 2026-07-28. The platform was in the headline on request, but it
// fragmented the merchant — the same restaurant read differently depending
// on who delivered — and it already has a home: the Order details panel
// renders expense_metadata.platform next to its heading.
const p = parseOrderHTML(html("dd-01"), meta({ messageId: `desc-${Date.now()}` }));
const res = await processOrderIngestion(p);
const row = await queryRow<{ description: string }>(
`SELECT description FROM transactions WHERE id = $1`, [res.transactionId]
);
expect(row!.description).toMatch(/\(DoorDash\)$/);
expect(row!.description).toBe('Order - Mad Mex');
});
it("reads as 'Gift Card', not 'Manual', and stays out of the reconcile queue", async () => {