ci / lint-test (push) Failing after 47s
`expense_metadata` has held the itemised receipt since ingestion started and nothing in the UI ever read it. A transaction that came from a DoorDash or Uber Eats receipt showed a merchant and an amount, with the item list and the delivery addresses sitting unread in the row behind it (user, 2026-07-27). Adds GET /api/transactions/[id]/order and an "Order details" section in the edit modal: line items with their options, pick-up/delivery stops with times and addresses, the card tail when one was involved, and the provider's own order reference. Two details that matter: - The lookup resolves from **both** sides — `transaction_id` OR `matched_transaction_id`. A card-settled order creates no transaction of its own (I5); the receipt points at the statement line instead. Matching only on transaction_id would have left the panel blank on exactly the card-paid orders, which are the ones whose detail is hardest to find elsewhere. - An empty item list says so in words rather than rendering nothing. Uber itemises groceries but not restaurant orders, and orders ingested before the Uber item parser existed have none either — a blank section reads as a bug when it is usually the receipt. Read-only. This is what a provider sent; editing it would make provenance mean nothing.
45 lines
1.9 KiB
TypeScript
45 lines
1.9 KiB
TypeScript
import { NextRequest, NextResponse } from "next/server";
|
|
import { queryRow } from "@/lib/db";
|
|
import { getCurrentUser } from "@/lib/auth";
|
|
import { canAccessTransactions } from "@/lib/queries";
|
|
|
|
/**
|
|
* Order provenance for one transaction.
|
|
*
|
|
* `expense_metadata` has held the itemised receipt since ingestion started and
|
|
* nothing in the UI ever read it — a transaction that came from a DoorDash or
|
|
* Uber Eats receipt showed a merchant and an amount, with the item list and the
|
|
* delivery addresses sitting unread in the row behind it (user, 2026-07-27).
|
|
*
|
|
* Read-only. The receipt is a record of what a provider sent; editing it here
|
|
* would make provenance mean nothing.
|
|
*/
|
|
export async function GET(
|
|
req: NextRequest,
|
|
{ params }: { params: Promise<{ id: string }> }
|
|
) {
|
|
const user = await getCurrentUser(req);
|
|
if (!user) return NextResponse.json({ error: "Unauthorized" }, { status: 403 });
|
|
const { id } = await params;
|
|
if (!(await canAccessTransactions(user.id, [Number(id)]))) {
|
|
return NextResponse.json({ error: "Forbidden" }, { status: 403 });
|
|
}
|
|
|
|
const row = await queryRow(
|
|
`SELECT platform, order_reference, line_items, route, subtotal, amount,
|
|
currency, card_last4, flags, source_email_subject, transaction_date
|
|
FROM expense_metadata
|
|
-- A card-settled order creates no transaction of its own (I5): the
|
|
-- statement line is the transaction, and the receipt points at it
|
|
-- through matched_transaction_id. Both directions have to resolve or the
|
|
-- detail is missing on exactly the orders that were paid by card.
|
|
WHERE transaction_id = $1 OR matched_transaction_id = $1
|
|
LIMIT 1`,
|
|
[Number(id)]
|
|
);
|
|
|
|
// Not an order — most transactions aren't. Null, not 404: the caller is
|
|
// asking "is there a receipt behind this?", and "no" is a normal answer.
|
|
return NextResponse.json(row ?? null);
|
|
}
|