Generate invoice PDFs attached to order confirmation, and send emails on order status changes

Invoice PDFs (§14 UStG line items, tenant-configurable VAT rate) are now
generated at checkout and attached to the confirmation email, plus
available on demand from the order-detail page. Payload-side, orders now
also email the customer on shipped/cancelled/return_requested/returned,
with Stornorechnung/Gutschrift correction PDFs attached for the latter two
so the original invoice's immutable number stays honest.
This commit is contained in:
Marco
2026-07-22 09:49:56 +00:00
parent fa02d95dff
commit 5232b14cdf
15 changed files with 1077 additions and 24 deletions
+39 -1
View File
@@ -604,7 +604,13 @@ export async function getLegalPage(type: LegalPageType, options?: { draft?: bool
};
}
export type EmailTemplateType = "order-confirmation" | "password-reset";
export type EmailTemplateType =
| "order-confirmation"
| "password-reset"
| "order-shipped"
| "order-cancelled"
| "order-return-requested"
| "order-returned";
type PayloadEmailTemplate = {
type: EmailTemplateType;
@@ -636,3 +642,35 @@ export async function getEmailTemplate(
const data: { docs?: PayloadEmailTemplate[] } = await res.json();
return data.docs?.[0] ?? null;
}
export type InvoiceSettings = {
sellerName: string;
sellerStreet: string;
sellerZip: string;
sellerCity: string;
sellerCountry: string;
sellerEmail: string;
vatId: string;
taxRatePercent: number;
bankDetails: string | null;
};
// Server-only in practice (only ever called from app/lib/invoiceData.ts),
// but kept in this file rather than a "use server"-only module since every
// other Payload fetcher lives here too — no client component imports it.
// invoice-settings' read access is admin-only plus this same service
// secret (see InvoiceSettings.ts) — it holds bank details, not something
// to leave publicly readable like Products/ShippingSettings.
export async function getInvoiceSettings(): Promise<InvoiceSettings | null> {
const params = new URLSearchParams({ "where[tenant.slug][equals]": TENANT_SLUG, limit: "1" });
const res = await fetch(`${PAYLOAD_URL}/api/invoice-settings?${params}`, {
headers: { "x-order-service-secret": process.env.ORDER_SERVICE_SECRET || "" },
cache: "no-store",
});
if (!res.ok) {
console.error(`getInvoiceSettings: Payload returned ${res.status} ${res.statusText}`);
return null;
}
const data: { docs?: InvoiceSettings[] } = await res.json();
return data.docs?.[0] ?? null;
}