2a4a7fd983
Two related fixes around the order/invoice number: 1) The thank-you page and the customer-account order page were showing the bare Shopify order name (e.g. '#1034') as the payment reference, while the PDF (and its GiroCode QR) used the canonical invoice number (e.g. 'RE-1034'). Banks treat each unique reference as a separate payment, and several reject the '#' character outright \u2014 so customers who pasted the thank-you reference into their banking app ended up with a payment the shop couldn't reconcile. New shared helper resolveOrderRemittance() (services/invoice/ remittance.server.ts) returns the single source of truth for the reference: latest non-cancelled Invoice row for the order, falling back to '${prefix}${orderNumber}' when no PDF has been generated yet. Both /api/public/payment-info and /api/public/girocode.png now route through it, so the thank-you page, the customer-account page and the GiroCode QR are guaranteed to match the PDF byte-for-byte. 2) Drop the redundant '\u00b7 Bestellnummer: #1004' suffix from the PDF title when the invoice number's trailing digits already match the Shopify order name (default 'order_number' numbering mode). In that mode the two strings carry identical numeric content and the suffix only adds noise; sequential mode (RE-7 vs #1004) keeps the suffix. - New smoke assertion verifies the suppression triggers on invoiceNumber='RE-1004' + orderName='#1004' and that the invoice number itself is still shown. - Both endpoints now also query 'Order.number' (already covered by read_orders) so the fallback path can build the prefix+order-number string without requiring the Invoice row.
48 lines
1.8 KiB
TypeScript
48 lines
1.8 KiB
TypeScript
import db from "../../db.server";
|
|
import type { ShopSettings } from "@prisma/client";
|
|
|
|
/**
|
|
* Returns the canonical remittance reference for an order — i.e. the
|
|
* exact string that should appear:
|
|
* - on the printed invoice PDF (`invoice.number`),
|
|
* - in the GiroCode QR payload,
|
|
* - and in the customer-facing payment instructions on the
|
|
* thank-you / customer-account pages.
|
|
*
|
|
* Banking systems treat each unique reference string as a separate
|
|
* payment, so all three surfaces MUST use this single source of truth.
|
|
*
|
|
* Resolution order:
|
|
* 1. The latest non-cancelled `Invoice` row for the order — guaranteed
|
|
* to match what's printed on the PDF.
|
|
* 2. Predicted default-mode number (`${prefix}${orderNumber}`). Safe
|
|
* for the default `order_number` numbering mode and a sensible
|
|
* best-guess for `prefix_sequential` before the invoice has been
|
|
* generated (the customer just sees the order number with the
|
|
* shop's invoice prefix instead of the bare Shopify "#1004").
|
|
*/
|
|
export async function resolveOrderRemittance(args: {
|
|
shopDomain: string;
|
|
orderGid: string;
|
|
orderNumber: number | null | undefined;
|
|
settings: Pick<ShopSettings, "invoicePrefix">;
|
|
}): Promise<string> {
|
|
const invoice = await db.invoice.findFirst({
|
|
where: {
|
|
shopDomain: args.shopDomain,
|
|
orderId: args.orderGid,
|
|
kind: "invoice",
|
|
cancelledAt: null,
|
|
},
|
|
orderBy: [{ version: "desc" }, { createdAt: "desc" }],
|
|
select: { invoiceNumber: true },
|
|
});
|
|
if (invoice?.invoiceNumber) return invoice.invoiceNumber;
|
|
|
|
const prefix = args.settings.invoicePrefix || "";
|
|
if (args.orderNumber != null) return `${prefix}${args.orderNumber}`;
|
|
// Last-ditch: derive numeric tail from the GID.
|
|
const tail = args.orderGid.split("/").pop() ?? "";
|
|
return `${prefix}${tail}`;
|
|
}
|