feat(email): split invoice email into paid/unpaid variants
Persist paymentStatus on the Invoice at generation time and use it at send
time to pick the default email body. Paid orders (paid/refunded/voided) now
get a body without the bank-transfer reference hint; outstanding orders keep
it. Custom merchant templates still override the defaults for both cases.
- Add Invoice.paymentStatus column + migration
- Split DEFAULT_EMAIL_BODY_{DE,EN} into _PAID/_UNPAID variants
- Persist paymentStatus in generateInvoice; select default in email.server
- Document behaviour on the settings page
- Add email-templates test (paid omits hint, unpaid keeps it)
This commit is contained in:
@@ -3,10 +3,12 @@ import type { Transporter } from "nodemailer";
|
||||
import type { ShopSettings } from "@prisma/client";
|
||||
|
||||
import db from "../../db.server";
|
||||
import { getStrings, pickLanguage } from "./i18n";
|
||||
import { pickLanguage } from "./i18n";
|
||||
import {
|
||||
DEFAULT_EMAIL_BODY_DE,
|
||||
DEFAULT_EMAIL_BODY_EN,
|
||||
DEFAULT_EMAIL_BODY_DE_PAID,
|
||||
DEFAULT_EMAIL_BODY_DE_UNPAID,
|
||||
DEFAULT_EMAIL_BODY_EN_PAID,
|
||||
DEFAULT_EMAIL_BODY_EN_UNPAID,
|
||||
DEFAULT_EMAIL_SUBJECT_DE,
|
||||
DEFAULT_EMAIL_SUBJECT_EN,
|
||||
} from "./emailTemplates";
|
||||
@@ -74,7 +76,6 @@ export async function sendInvoiceEmail(
|
||||
// rendered in, so the email matches the attachment. Caller can still
|
||||
// override via `customerLocale` if they really want a different language.
|
||||
const language = pickLanguage(args.customerLocale ?? invoice.language ?? settings.defaultLanguage);
|
||||
const t = getStrings(language);
|
||||
const customer = parseCustomer(invoice.customerJson);
|
||||
const totals = parseTotals(invoice.totalsJson);
|
||||
const vars = buildTemplateVars({
|
||||
@@ -90,9 +91,26 @@ export async function sendInvoiceEmail(
|
||||
(language === "en" ? DEFAULT_EMAIL_SUBJECT_EN : DEFAULT_EMAIL_SUBJECT_DE);
|
||||
const subject = renderTemplate(customSubject, vars, { html: false });
|
||||
|
||||
// Pick the default body variant for the invoice's payment state: orders that
|
||||
// are already settled (paid / refunded / voided) get a body WITHOUT the
|
||||
// bank-transfer reference hint; outstanding orders (unpaid / partial) keep
|
||||
// it. Legacy rows have an empty paymentStatus → treated as unpaid.
|
||||
// A merchant-supplied custom template always wins over the defaults.
|
||||
const isPaid =
|
||||
invoice.paymentStatus === "paid" ||
|
||||
invoice.paymentStatus === "refunded" ||
|
||||
invoice.paymentStatus === "voided";
|
||||
const defaultBody =
|
||||
language === "en"
|
||||
? isPaid
|
||||
? DEFAULT_EMAIL_BODY_EN_PAID
|
||||
: DEFAULT_EMAIL_BODY_EN_UNPAID
|
||||
: isPaid
|
||||
? DEFAULT_EMAIL_BODY_DE_PAID
|
||||
: DEFAULT_EMAIL_BODY_DE_UNPAID;
|
||||
const customBodyHtml =
|
||||
(language === "en" ? settings.emailBodyHtmlEn : settings.emailBodyHtmlDe) ||
|
||||
(language === "en" ? DEFAULT_EMAIL_BODY_EN : DEFAULT_EMAIL_BODY_DE);
|
||||
defaultBody;
|
||||
const body = renderHtmlBody(renderTemplate(customBodyHtml, vars));
|
||||
|
||||
// If the rendered body references the inline logo, attach it.
|
||||
|
||||
Reference in New Issue
Block a user