import { computeTotals } from "../../lib/invoice"; import { amountInWords } from "../../lib/numberToWords"; import type { BankAccount, Invoice, InvoiceItem, Settings, TaxType } from "../../lib/types"; import { buildRenderModel, type RenderModel } from "../model/build"; import { DEFAULT_PREFS, type RenderPrefsV1 } from "../model/prefs"; /** * Pure data for the Diagnostics self-test and the goldens test (no Node or DOM imports, so the * webview and Node build the very same models). Change a fixture and regenerate the goldens. */ export interface SelfTestFixture { id: string; label: string; model: RenderModel; prefs: RenderPrefsV1; invoiceNumber: string; /** The grand total as the template prints it, e.g. "₹8,260.00". */ totalText: string; } const VENDOR: Settings = { vendorName: "Selftest Studio", vendorAddress: "1 Test Lane, Test Town, Bangalore - 560005", vendorEmail: "selftest@example.com", vendorPhone: "9000000000", vendorPan: "AAAAA0000A", vendorGstin: "", vendorStateCode: "29", logoPath: null, signaturePath: null, defaultBankId: 1, defaultTaxRate: 18, defaultTaxType: "cgst_sgst", paymentTermsDays: 30, currency: "INR", onboarded: true, theme: "g10", gstRegistration: "unregistered", defaultHsnSac: "", signatoryName: "", signatoryDesignation: "", renderPrefs: "", }; const BANK: BankAccount = { id: 1, label: "Primary", bankName: "State Bank of India", accountName: "Selftest Studio", accountNo: "00000000000", branch: "000000", ifsc: "SBIN0000000", isDefault: true, }; const items = (n: number, hsn: boolean): InvoiceItem[] => Array.from({ length: n }, (_, i) => ({ id: i + 1, description: `Voice-over session ${i + 1} - narration for module ${i + 1}`, mode: i % 2 === 0 ? "rate" : "fixed", rate: 1200, unit: "hour", quantity: 1 + (i % 3), amount: 2500 + i * 10, sortOrder: i, ...(hsn ? { hsnSac: "998397" } : {}), })); function build(o: { id: string; label: string; number: string; rows: number; registered: boolean; prefs: Partial }): SelfTestFixture { const taxType: TaxType = o.registered ? "cgst_sgst" : "none"; const taxRate = o.registered ? 18 : 0; const rows = items(o.rows, o.registered); const totals = computeTotals(rows, 0, taxType, taxRate); const vendor: Settings = o.registered ? { ...VENDOR, vendorGstin: "29AAAAA0000A1Z5", gstRegistration: "regular", signatoryName: "Selftest Studio", signatoryDesignation: "Proprietor" } : VENDOR; const invoice: Invoice = { id: 1, number: o.number, seriesId: 1, invoiceDate: "2026-09-15", dueDate: "2026-10-15", clientId: null, clientName: "Acme Learning Pvt Ltd", clientAddress: "42 MG Road\nBangalore 560001", clientGstin: o.registered ? "29ABCDE1234F1Z5" : "", poNumber: "", placeOfSupplyStateCode: o.registered ? "29" : "", subtotal: totals.subtotal, discount: totals.discount, taxType, taxRate, cgstAmount: totals.cgst, sgstAmount: totals.sgst, igstAmount: totals.igst, total: totals.total, amountInWords: amountInWords(totals.total), bankAccountId: 1, bankSnapshot: "", signaturePath: null, notes: "", status: "issued", createdAt: "2026-09-15T00:00:00Z", updatedAt: "2026-09-15T00:00:00Z", docType: o.registered ? "tax_invoice" : "invoice", reverseCharge: false, vendorSnapshot: "", snapshotOrigin: "", cancelledAt: null, cancelReason: "", archivedPdfSha256: null, renderPrefs: "", items: rows, }; const model = buildRenderModel({ invoice, vendor, bank: BANK, logo: null, signatureDataUri: null, appVersion: "0.0.0-selftest" }); return { id: o.id, label: o.label, model, prefs: { ...DEFAULT_PREFS, ...o.prefs }, invoiceNumber: o.number, totalText: model.totals.totalText, }; } export const SELF_TEST_FIXTURE_IDS = ["classic-a4-tax-3", "classic-letter-unreg-60"] as const; /** Classic A4 tax invoice with 3 rows, and Classic Letter plain invoice with 60 rows (a multi-page table). */ export function selfTestFixtures(): SelfTestFixture[] { return [ build({ id: "classic-a4-tax-3", label: "Classic, A4, tax invoice, 3 rows", number: "ST/2026-001", rows: 3, registered: true, prefs: { pageSize: "A4" } }), build({ id: "classic-letter-unreg-60", label: "Classic, Letter, unregistered, 60 rows", number: "ST/2026-002", rows: 60, registered: false, prefs: { pageSize: "LETTER" } }), ]; }