diff --git a/src/__tests__/templates-branch-coverage.test.ts b/src/__tests__/templates-branch-coverage.test.ts
new file mode 100644
index 0000000..c1c7ed3
--- /dev/null
+++ b/src/__tests__/templates-branch-coverage.test.ts
@@ -0,0 +1,276 @@
+import { describe, it, expect } from "vitest";
+import { renderTemplate, TemplateData } from "../services/templates.js";
+
+describe("Receipt template optional branches", () => {
+ const baseReceipt = {
+ receiptNumber: "R-100",
+ date: "2026-03-01",
+ items: [{ description: "Item", amount: 10 }],
+ };
+
+ it("renders from.address when present", () => {
+ const html = renderTemplate("receipt", {
+ ...baseReceipt,
+ from: { name: "Shop", address: "123 Main St" },
+ } as TemplateData);
+ expect(html).toContain("123 Main St");
+ expect(html).toContain('class="center"');
+ });
+
+ it("omits from.address when absent", () => {
+ const html = renderTemplate("receipt", {
+ ...baseReceipt,
+ from: { name: "Shop" },
+ } as TemplateData);
+ // The "Thank you!" div has class="center", but no address div should exist
+ const addressDivCount = (html.match(/class="center"/g) || []).length;
+ // Only the "Thank you!" div should have class="center", not an address div
+ expect(addressDivCount).toBe(1);
+ });
+
+ it("renders to.name (Customer line) when present", () => {
+ const html = renderTemplate("receipt", {
+ ...baseReceipt,
+ from: { name: "Shop" },
+ to: { name: "John Doe" },
+ } as TemplateData);
+ expect(html).toContain("Customer: John Doe");
+ });
+
+ it("omits Customer line when to.name is absent", () => {
+ const html = renderTemplate("receipt", {
+ ...baseReceipt,
+ from: { name: "Shop" },
+ } as TemplateData);
+ expect(html).not.toContain("Customer:");
+ });
+
+ it("renders paymentMethod when present", () => {
+ const html = renderTemplate("receipt", {
+ ...baseReceipt,
+ from: { name: "Shop" },
+ paymentMethod: "Credit Card",
+ } as TemplateData);
+ expect(html).toContain("Paid via: Credit Card");
+ });
+
+ it("omits paymentMethod when absent", () => {
+ const html = renderTemplate("receipt", {
+ ...baseReceipt,
+ from: { name: "Shop" },
+ } as TemplateData);
+ expect(html).not.toContain("Paid via:");
+ });
+});
+
+describe("Receipt template fallback branches", () => {
+ it("renders without from (falls back to empty object)", () => {
+ const html = renderTemplate("receipt", {
+ receiptNumber: "R-200",
+ date: "2026-03-01",
+ items: [{ description: "Item", amount: 5 }],
+ } as TemplateData);
+ expect(html).toContain("R-200");
+ expect(html).toContain("€5.00");
+ });
+
+ it("renders without to (falls back to empty object)", () => {
+ const html = renderTemplate("receipt", {
+ receiptNumber: "R-201",
+ date: "2026-03-01",
+ from: { name: "Shop" },
+ items: [{ description: "Item", amount: 5 }],
+ } as TemplateData);
+ expect(html).not.toContain("Customer:");
+ });
+
+ it("renders with custom currency", () => {
+ const html = renderTemplate("receipt", {
+ receiptNumber: "R-202",
+ date: "2026-03-01",
+ from: { name: "Shop" },
+ currency: "$",
+ items: [{ description: "Item", amount: 7 }],
+ } as TemplateData);
+ expect(html).toContain("$7.00");
+ });
+
+ it("renders with empty items array", () => {
+ const html = renderTemplate("receipt", {
+ receiptNumber: "R-203",
+ date: "2026-03-01",
+ from: { name: "Shop" },
+ } as TemplateData);
+ expect(html).toContain("€0.00");
+ });
+});
+
+describe("Invoice template fallback branches", () => {
+ it("renders without from (falls back to empty object)", () => {
+ const html = renderTemplate("invoice", {
+ invoiceNumber: "INV-300",
+ date: "2026-03-01",
+ to: { name: "Buyer" },
+ items: [{ description: "Widget", quantity: 1, unitPrice: 10 }],
+ } as TemplateData);
+ expect(html).toContain("INV-300");
+ });
+
+ it("renders without to (falls back to empty object)", () => {
+ const html = renderTemplate("invoice", {
+ invoiceNumber: "INV-301",
+ date: "2026-03-01",
+ from: { name: "Seller" },
+ items: [{ description: "Widget", quantity: 1, unitPrice: 10 }],
+ } as TemplateData);
+ expect(html).toContain("INV-301");
+ });
+});
+
+describe("Invoice item default values", () => {
+ it("defaults quantity to 1 when missing", () => {
+ const html = renderTemplate("invoice", {
+ invoiceNumber: "INV-400",
+ date: "2026-03-01",
+ from: { name: "S" }, to: { name: "B" },
+ items: [{ description: "Widget", unitPrice: 10 }],
+ } as TemplateData);
+ expect(html).toContain("€10.00"); // 1 * 10
+ });
+
+ it("defaults unitPrice to 0 when missing", () => {
+ const html = renderTemplate("invoice", {
+ invoiceNumber: "INV-401",
+ date: "2026-03-01",
+ from: { name: "S" }, to: { name: "B" },
+ items: [{ description: "Free item", quantity: 2 }],
+ } as TemplateData);
+ expect(html).toContain("€0.00");
+ });
+
+ it("defaults items to empty array when missing", () => {
+ const html = renderTemplate("invoice", {
+ invoiceNumber: "INV-402",
+ date: "2026-03-01",
+ from: { name: "S" }, to: { name: "B" },
+ } as TemplateData);
+ expect(html).toContain("€0.00");
+ });
+});
+
+describe("Receipt item default values", () => {
+ it("defaults amount to 0 when missing/invalid", () => {
+ const html = renderTemplate("receipt", {
+ receiptNumber: "R-300",
+ date: "2026-03-01",
+ from: { name: "Shop" },
+ items: [{ description: "Bad item", amount: "not-a-number" }],
+ } as unknown as TemplateData);
+ expect(html).toContain("€0.00");
+ });
+});
+
+describe("Invoice template optional branches", () => {
+ const baseInvoice = {
+ invoiceNumber: "INV-100",
+ date: "2026-03-01",
+ from: { name: "Seller" },
+ to: { name: "Buyer" },
+ items: [{ description: "Widget", quantity: 1, unitPrice: 10 }],
+ };
+
+ it("renders dueDate when present", () => {
+ const html = renderTemplate("invoice", {
+ ...baseInvoice,
+ dueDate: "2026-04-01",
+ } as TemplateData);
+ expect(html).toContain("Due: 2026-04-01");
+ });
+
+ it("omits dueDate when absent", () => {
+ const html = renderTemplate("invoice", baseInvoice as TemplateData);
+ expect(html).not.toContain("Due:");
+ });
+
+ it("renders from.address when present", () => {
+ const html = renderTemplate("invoice", {
+ ...baseInvoice,
+ from: { name: "Seller", address: "456 Elm St\nSuite 2" },
+ } as TemplateData);
+ expect(html).toContain("456 Elm St
Suite 2");
+ });
+
+ it("omits from.address when absent", () => {
+ const html = renderTemplate("invoice", baseInvoice as TemplateData);
+ // from section should have name but no address paragraph
+ const fromSection = html.split('
456');
+ });
+
+ it("renders from.email when present", () => {
+ const html = renderTemplate("invoice", {
+ ...baseInvoice,
+ from: { name: "Seller", email: "seller@test.com" },
+ } as TemplateData);
+ expect(html).toContain("seller@test.com");
+ });
+
+ it("renders from.vatId when present", () => {
+ const html = renderTemplate("invoice", {
+ ...baseInvoice,
+ from: { name: "Seller", vatId: "ATU12345678" },
+ } as TemplateData);
+ expect(html).toContain("VAT: ATU12345678");
+ });
+
+ it("renders to.address when present", () => {
+ const html = renderTemplate("invoice", {
+ ...baseInvoice,
+ to: { name: "Buyer", address: "789 Oak Ave" },
+ } as TemplateData);
+ expect(html).toContain("789 Oak Ave");
+ });
+
+ it("renders to.email when present", () => {
+ const html = renderTemplate("invoice", {
+ ...baseInvoice,
+ to: { name: "Buyer", email: "buyer@test.com" },
+ } as TemplateData);
+ expect(html).toContain("buyer@test.com");
+ });
+
+ it("renders to.vatId when present", () => {
+ const html = renderTemplate("invoice", {
+ ...baseInvoice,
+ to: { name: "Buyer", vatId: "DE999999999" },
+ } as TemplateData);
+ expect(html).toContain("VAT: DE999999999");
+ });
+
+ it("renders paymentDetails when present", () => {
+ const html = renderTemplate("invoice", {
+ ...baseInvoice,
+ paymentDetails: "IBAN: AT123456\nBIC: BKAUATWW",
+ } as TemplateData);
+ expect(html).toContain("Payment Details");
+ expect(html).toContain("IBAN: AT123456
BIC: BKAUATWW");
+ });
+
+ it("omits paymentDetails when absent", () => {
+ const html = renderTemplate("invoice", baseInvoice as TemplateData);
+ expect(html).not.toContain("Payment Details");
+ });
+
+ it("renders notes when present", () => {
+ const html = renderTemplate("invoice", {
+ ...baseInvoice,
+ notes: "Thank you for your business",
+ } as TemplateData);
+ expect(html).toContain("Thank you for your business");
+ });
+
+ it("omits notes when absent", () => {
+ const html = renderTemplate("invoice", baseInvoice as TemplateData);
+ expect(html).not.toContain("Notes");
+ });
+});