/** * Selection helper for the "recent orders" / "recent drafts" lists. * * A single order can accumulate several invoice rows over its lifetime * (regenerations bump the `version`, cancel-and-reissue cancels the old row * and issues a new one). Crucially, a CANCELLED invoice can carry a HIGHER * `version` than the current active one, so picking "highest version wins" * would surface a stale cancelled invoice and hide the live one — the order * would render as if it had no invoice ("Generate" button) even though a * valid issued invoice exists. * * The correct representative for the UI is the latest NON-cancelled invoice; * only when every row is cancelled do we fall back to the latest cancelled * one (so the order can still show its "cancelled" state). */ export interface RepresentativeInvoiceRow { orderId: string; version: number; cancelledAt: Date | null; } /** * Build a map of orderId -> representative invoice. * * @param invoices Invoice rows for the relevant orders. MUST already be sorted * by `version` descending (then `createdAt` descending), matching the * Prisma query order, so the first non-cancelled row encountered per order * is the highest-version active invoice. */ export function buildRepresentativeInvoiceMap( invoices: T[], ): Map { const byOrder = new Map(); for (const inv of invoices) { const existing = byOrder.get(inv.orderId); if (!existing) { byOrder.set(inv.orderId, inv); continue; } // Upgrade from a cancelled placeholder to the first active invoice seen. if (existing.cancelledAt && !inv.cancelledAt) { byOrder.set(inv.orderId, inv); } } return byOrder; }