fixed issue in order cancellation

This commit is contained in:
Gerhard Scheikl
2026-05-31 11:12:20 +02:00
parent 15c62627be
commit 2837731815
3 changed files with 118 additions and 4 deletions
+8 -4
View File
@@ -3,6 +3,7 @@ import { Link, useLoaderData, useNavigation, useFetcher } from "react-router";
import { authenticate } from "../shopify.server";
import db from "../db.server";
import { buildRepresentativeInvoiceMap } from "../services/invoice/representativeInvoice";
interface RecentOrder {
id: string; // gid
@@ -102,10 +103,13 @@ export const loader = async ({ request }: LoaderFunctionArgs) => {
},
orderBy: [{ version: "desc" }, { createdAt: "desc" }],
});
const latestByOrder = new Map<string, (typeof invoices)[number]>();
for (const inv of invoices) {
if (!latestByOrder.has(inv.orderId)) latestByOrder.set(inv.orderId, inv);
}
// Pick the representative invoice per order. `invoices` is sorted by
// version desc, but a cancelled invoice can carry a HIGHER version than
// the current active one (cancel-and-reissue bumps versions), so a naive
// "first row wins" would surface a stale cancelled invoice and hide the
// live one. Prefer the latest non-cancelled invoice; only fall back to a
// cancelled row when no active invoice exists.
const latestByOrder = buildRepresentativeInvoiceMap(invoices);
orders = nodes.map((n) => {
const inv = latestByOrder.get(n.id);
@@ -0,0 +1,47 @@
/**
* 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<T extends RepresentativeInvoiceRow>(
invoices: T[],
): Map<string, T> {
const byOrder = new Map<string, T>();
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;
}