Phase7b: Manual Invoice
Some checks failed
Build and Push / build (push) Failing after 59s

This commit is contained in:
2026-05-26 23:04:09 +02:00
parent 667617296b
commit ed915ec539
26 changed files with 2365 additions and 65 deletions

View File

@@ -923,8 +923,8 @@ export async function sendInvoiceIssuedEmail(params: {
currency: string; // "CHF" — passed for future-proofing
dueAt: string; // ISO date
lineCount: number;
periodStart: string; // ISO date
periodEnd: string; // ISO date
periodStart: string | null; // ISO date; null for custom invoices
periodEnd: string | null; // ISO date; null for custom invoices
locale: "de" | "en" | "fr" | "it";
}): Promise<void> {
// All four locales — the email is sent in the invoice's locale,
@@ -960,7 +960,13 @@ export async function sendInvoiceIssuedEmail(params: {
const safeCompany = escapeHtml(params.companyName);
const safeNumber = escapeHtml(params.invoiceNumber);
const totalFmt = `${params.currency} ${params.totalChf.toFixed(2)}`;
const periodFmt = `${params.periodStart.slice(0, 10)}${params.periodEnd.slice(0, 10)}`;
// Phase 8: period is null for custom invoices. When missing, the
// template skips the "Service period:" line entirely; otherwise
// it renders the date range as before.
const periodFmt =
params.periodStart && params.periodEnd
? `${params.periodStart.slice(0, 10)}${params.periodEnd.slice(0, 10)}`
: null;
const dueFmt = params.dueAt.slice(0, 10);
// Both bodies built in the invoice's locale.
@@ -977,7 +983,9 @@ export async function sendInvoiceIssuedEmail(params: {
introByLocale[L],
"",
`${l.number}: ${params.invoiceNumber}`,
`${l.period}: ${periodFmt}`,
// Phase 8: omit the period line entirely for custom
// invoices (which have no billing period).
...(periodFmt ? [`${l.period}: ${periodFmt}`] : []),
`${l.total}: ${totalFmt}`,
`${l.due}: ${dueFmt}`,
`${l.lines}: ${params.lineCount}`,
@@ -995,7 +1003,7 @@ export async function sendInvoiceIssuedEmail(params: {
<p>${escapeHtml(introByLocale[L])}</p>
<table style="width:100%; border-collapse:collapse; margin:16px 0; font-size:14px;">
<tr><td style="color:#888; padding:6px 0; width:120px;">${l.number}</td><td><strong>${safeNumber}</strong></td></tr>
<tr><td style="color:#888; padding:6px 0;">${l.period}</td><td>${escapeHtml(periodFmt)}</td></tr>
${periodFmt ? `<tr><td style="color:#888; padding:6px 0;">${l.period}</td><td>${escapeHtml(periodFmt)}</td></tr>` : ""}
<tr><td style="color:#888; padding:6px 0;">${l.total}</td><td style="color:#10B981; font-weight:600;">${escapeHtml(totalFmt)}</td></tr>
<tr><td style="color:#888; padding:6px 0;">${l.due}</td><td>${escapeHtml(dueFmt)}</td></tr>
<tr><td style="color:#888; padding:6px 0;">${l.lines}</td><td>${params.lineCount}</td></tr>