feat(orders): show where an Uber trip went, in the list
ci / lint-test (push) Failing after 41s

Five rows all reading "Order - Uber Trip" are indistinguishable — the list gives
you a date and an amount and nothing to tell one ride from another (user,
2026-07-27). Where the trip went is exactly what separates them, and it was
already stored on expense_metadata.route since this morning; nothing in the list
read it.

getTransactions now joins the receipt (both directions — transaction_id OR
matched_transaction_id, since a card-settled order points at the statement line
instead) and the description cell renders "Terminal 2, Melbourne Airport (MEL)
→ 19 Lady Penrhyn Dr" in the same italic sub-line notes use.

Two deliberate limits:

- **A note the user wrote always wins.** This only fills an empty sub-line; it
  never occupies the notes field, which is theirs.
- **Deliveries are excluded.** Their merchant already identifies them, so the
  restaurant's street address would be clutter on every food order. Gated on
  platform = 'uber'.

The summary keeps the first two comma-segments of each address — a truncation,
not a guess about geography. Uber puts the venue or street first, which is the
identifying part; the full stops with their times stay in the title attribute.
This commit is contained in:
2026-07-27 11:43:55 +10:00
parent 6161ddc9de
commit 3bb67f370d
3 changed files with 107 additions and 3 deletions
+23 -1
View File
@@ -1,5 +1,11 @@
import { queryRaw } from "./db";
export interface RoutePointRow {
label: string;
time: string | null;
address: string;
}
export interface TagRow {
id: number;
name: string;
@@ -28,6 +34,9 @@ export interface TransactionRow {
// How it was paid (migration 0016). NULL = unknown, treated as reconcilable.
// 'cash' and 'credits' are excluded from reconciliation — see needsCardMatch().
payment_method: string | null;
/** Uber pick-up/drop-off, when this row came from an order receipt. */
order_route: RoutePointRow[] | null;
order_platform: "doordash" | "ubereats" | "uber" | null;
// override fields
category_override: string | null;
merchant_override: string | null;
@@ -244,10 +253,23 @@ export async function getTransactions(ownerId: number, filters: TransactionFilte
tr.name as trip_name,
tr.color as trip_color,
txn_tags.tags,
txn_splits.splits
txn_splits.splits,
order_ctx.route as order_route,
order_ctx.platform as order_platform
FROM transactions t
LEFT JOIN transaction_overrides o ON o.transaction_id = t.id
LEFT JOIN statements s ON s.id = t.statement_id
-- Order provenance, for the sub-line under the description. Five rows all
-- reading "Order - Uber Trip" are indistinguishable; where they went is the
-- only thing that tells them apart, and it was already stored.
-- Both directions, because a card-settled order has no transaction of its
-- own and points at the statement line instead (I5).
LEFT JOIN LATERAL (
SELECT em.route, em.platform
FROM expense_metadata em
WHERE em.transaction_id = t.id OR em.matched_transaction_id = t.id
LIMIT 1
) order_ctx ON true
LEFT JOIN participants p ON p.id = COALESCE(t.owner_id, s.owner_id)
LEFT JOIN transactions src ON src.reconciled_with_id = t.id AND src.statement_id IS NULL
LEFT JOIN trips tr ON tr.id = o.trip_id