Redesign invoice PDFs, add correction-invoice downloads, return reasons, and per-product tax/bundle support
Invoice + Stornorechnung/Gutschrift PDFs get a modern header-band layout, a "bereits beglichen" badge for immediately-paid orders, labelled bank details, and a per-tax-rate summary breakdown. Correction invoices can now be re-downloaded from the account (regenerated deterministically, not stored as files, same approach as the original invoice). Return requests capture a reason. Products can define bundles (bundleItems) and a per-product VAT rate override, both snapshotted onto order items.
This commit is contained in:
+14
-4
@@ -426,6 +426,8 @@ export type CustomerOrderDetail = CustomerOrder & {
|
||||
id: number;
|
||||
invoiceNumber: string | null;
|
||||
invoiceIssuedAt: string | null;
|
||||
correctionInvoiceNumber: string | null;
|
||||
correctionInvoiceIssuedAt: string | null;
|
||||
customerFirstName: string;
|
||||
customerLastName: string;
|
||||
customerEmail: string;
|
||||
@@ -442,7 +444,8 @@ export type CustomerOrderDetail = CustomerOrder & {
|
||||
paymentMethodTitle: string;
|
||||
discountCode: string | null;
|
||||
discountAmount: number;
|
||||
items: { productName: string; quantity: number; unitPrice: number }[];
|
||||
returnReason: string | null;
|
||||
items: { productName: string; quantity: number; unitPrice: number; taxRatePercent: number; bundleContents: string | null }[];
|
||||
};
|
||||
|
||||
// Access control (Orders.ts) already scopes a customer's own JWT to only
|
||||
@@ -460,8 +463,7 @@ export async function getCustomerOrderDetail(token: string, customerId: number,
|
||||
cache: "no-store",
|
||||
});
|
||||
if (!res.ok) return null;
|
||||
const data: { docs?: (Omit<CustomerOrderDetail, "itemCount"> & { items: { productName: string; quantity: number; unitPrice: number }[] })[] } =
|
||||
await res.json();
|
||||
const data: { docs?: Omit<CustomerOrderDetail, "itemCount">[] } = await res.json();
|
||||
const doc = data.docs?.[0];
|
||||
if (!doc) return null;
|
||||
return { ...doc, itemCount: doc.items.length };
|
||||
@@ -475,12 +477,20 @@ export async function requestOrderStatusChange(
|
||||
token: string,
|
||||
orderId: number,
|
||||
action: "cancel" | "request-return",
|
||||
returnReason?: string,
|
||||
): Promise<{ ok: true } | { ok: false; reason: string }> {
|
||||
const status = action === "cancel" ? "cancelled" : "return_requested";
|
||||
// Orders.ts's beforeChange hook only allows a customer-authenticated
|
||||
// update to touch `status` (plus `returnReason`, but only together with
|
||||
// this exact transition — see that hook's own comment) — omitting the
|
||||
// key entirely for `cancel` rather than sending `returnReason: undefined`
|
||||
// keeps that request shaped exactly like before this field existed.
|
||||
const body: { status: string; returnReason?: string } = { status };
|
||||
if (action === "request-return" && returnReason) body.returnReason = returnReason;
|
||||
const res = await fetch(`${PAYLOAD_URL}/api/orders/${orderId}`, {
|
||||
method: "PATCH",
|
||||
headers: { Authorization: `JWT ${token}`, "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ status }),
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
if (!res.ok) {
|
||||
const data = await res.json().catch(() => null);
|
||||
|
||||
Reference in New Issue
Block a user