import type { InvoiceItem, TaxType } from "./types"; // The preview must show exactly what Rust stores (src-tauri/src/gst.rs), so money is // computed in integer paise with round-half-up, the same way. const toPaise = (rupees: number) => Math.round((Number.isFinite(rupees) ? rupees : 0) * 100); function linePaise(item: InvoiceItem): number { if (item.mode === "rate") { return Math.round(toPaise(item.rate || 0) * (item.quantity || 0)); } return toPaise(item.amount || 0); } /** Round-half-up division for non-negative integers. */ const divRound = (num: number, den: number) => Math.floor((num + den / 2) / den); /** Amount for a single line: a fixed total, or rate × quantity. */ export function computeLineAmount(item: InvoiceItem): number { return linePaise(item) / 100; } export interface Totals { subtotal: number; discount: number; taxable: number; cgst: number; sgst: number; igst: number; taxTotal: number; total: number; } export function computeTotals( items: InvoiceItem[], discount: number, taxType: TaxType, taxRate: number, ): Totals { const subtotal = items.reduce((sum, item) => sum + linePaise(item), 0); const discountPaise = Math.min(Math.max(toPaise(discount), 0), Math.max(subtotal, 0)); const taxable = subtotal - discountPaise; const rateBp = Math.round((Number.isFinite(taxRate) ? taxRate : 0) * 100); let cgst = 0; let sgst = 0; let igst = 0; if (taxType === "cgst_sgst") { cgst = divRound(taxable * rateBp, 20_000); sgst = cgst; } else if (taxType === "igst") { igst = divRound(taxable * rateBp, 10_000); } const taxTotal = cgst + sgst + igst; return { subtotal: subtotal / 100, discount: discountPaise / 100, taxable: taxable / 100, cgst: cgst / 100, sgst: sgst / 100, igst: igst / 100, taxTotal: taxTotal / 100, total: (taxable + taxTotal) / 100, }; }