feat(invoice): informal German tone + show payment method and status

- i18n.de: switch Sie/Ihren to du/dein for salutation, thank-you line,
  customer-VAT label and payment-terms paragraph. Closing line was
  already informal.
- i18n: add paymentMethodLabel/paymentStatusLabel + per-status labels
  (paid/unpaid/partial/refunded) for both DE and EN, plus
  derivePaymentStatus helper that condenses Shopify's
  displayFinancialStatus (PAID, PARTIALLY_PAID, REFUNDED, …) into a
  4-value enum.
- loadOrderForInvoice: query Order.paymentGatewayNames and propagate it
  on the raw view-model.
- composeInvoice + types: expose paymentStatus + paymentGatewayNames on
  InvoiceViewModel (filtered/trimmed). loadDraftOrderForOffer keeps
  paymentGatewayNames empty (drafts have no gateway yet).
- InvoiceDocument: render two new meta rows on real invoices —
  'Zahlart / Payment method' (joined, prettified gateway names) and
  'Zahlstatus / Payment status' (translated label). Storno + offer kinds
  intentionally omit them.
- scripts/render-sample.ts: extend smoke checks to assert the informal
  DE wording, the new payment-method/status rows and the
  paymentStatus/paymentGatewayNames composer outputs.
This commit is contained in:
Gerhard Scheikl
2026-05-15 11:26:26 +02:00
parent dde53319e5
commit 55a0dd03f2
7 changed files with 133 additions and 7 deletions
+54 -4
View File
@@ -52,6 +52,44 @@ export interface InvoiceStrings {
webLabel: string;
phoneLabel: string;
paidStamp: string;
paymentMethodLabel: string;
paymentStatusLabel: string;
paymentStatusPaid: string;
paymentStatusUnpaid: string;
paymentStatusPartial: string;
paymentStatusRefunded: string;
}
/** Status displayed for the order's payment, derived from Shopify's
* `displayFinancialStatus`. */
export type PaymentStatus = "paid" | "unpaid" | "partial" | "refunded";
export function paymentStatusLabel(
status: PaymentStatus,
strings: InvoiceStrings,
): string {
switch (status) {
case "paid":
return strings.paymentStatusPaid;
case "partial":
return strings.paymentStatusPartial;
case "refunded":
return strings.paymentStatusRefunded;
default:
return strings.paymentStatusUnpaid;
}
}
/** Maps Shopify's `displayFinancialStatus` to our condensed enum. Values not
* signalling actual receipt of money map to "unpaid". */
export function derivePaymentStatus(
displayFinancialStatus: string | null | undefined,
): PaymentStatus {
const v = (displayFinancialStatus || "").toUpperCase();
if (v === "PAID") return "paid";
if (v === "PARTIALLY_PAID") return "partial";
if (v === "REFUNDED" || v === "PARTIALLY_REFUNDED") return "refunded";
return "unpaid";
}
const de: InvoiceStrings = {
@@ -65,7 +103,7 @@ const de: InvoiceStrings = {
invoiceNumber: "Rechnungs-Nr.",
invoiceDate: "Rechnungsdatum",
deliveryDate: "Lieferdatum",
customerVatId: "Ihre USt-Id.",
customerVatId: "Deine USt-Id.",
position: "Pos.",
description: "Beschreibung",
quantity: "Menge",
@@ -74,12 +112,12 @@ const de: InvoiceStrings = {
netTotal: "Gesamtbetrag netto",
vatLine: (r) => `zzgl. Umsatzsteuer ${r}`,
grossTotal: "Gesamtbetrag brutto",
salutationGeneric: "Sehr geehrte Damen und Herren,",
salutationGeneric: "Hallo,",
thankYouLine:
"vielen Dank für Ihren Auftrag. Wir erlauben uns, Ihnen folgende Leistungen in Rechnung zu stellen:",
"vielen Dank für deine Bestellung. Wir berechnen dir folgende Leistungen:",
closing: "Danke für deinen Einkauf",
paymentTerms: (days, due) =>
`Bitte überweisen Sie den Rechnungsbetrag innerhalb von ${days} Tagen, spätestens bis zum ${due}, auf das unten angegebene Konto. Bei Fragen zur Rechnung stehen wir Ihnen gerne zur Verfügung.`,
`Bitte überweise den Rechnungsbetrag innerhalb von ${days} Tagen, spätestens bis zum ${due}, auf das unten angegebene Konto. Bei Fragen zur Rechnung sind wir gerne für dich da.`,
paymentTermsImmediate:
"Der Rechnungsbetrag ist sofort nach Erhalt zur Zahlung fällig.",
giroCodeCaption: "GiroCode",
@@ -109,6 +147,12 @@ const de: InvoiceStrings = {
webLabel: "Web",
phoneLabel: "Tel.",
paidStamp: "BEZAHLT",
paymentMethodLabel: "Zahlart",
paymentStatusLabel: "Zahlstatus",
paymentStatusPaid: "Bezahlt",
paymentStatusUnpaid: "Offen",
paymentStatusPartial: "Teilweise bezahlt",
paymentStatusRefunded: "Erstattet",
};
const en: InvoiceStrings = {
@@ -165,6 +209,12 @@ const en: InvoiceStrings = {
webLabel: "Web",
phoneLabel: "Tel.",
paidStamp: "PAID",
paymentMethodLabel: "Payment method",
paymentStatusLabel: "Payment status",
paymentStatusPaid: "Paid",
paymentStatusUnpaid: "Outstanding",
paymentStatusPartial: "Partially paid",
paymentStatusRefunded: "Refunded",
};
// Locale → invoice language. We only render in German (`de`) when the