Initial commit: Voiced invoice desktop app

Offline-first invoicing app for Test Vendor, built by Bongbetic.

- Tauri v2 (Rust) + React/TypeScript + IBM Carbon Design System UI
- SQLite (rusqlite) storage with a persistent invoice-number series
- Guided first-run setup, pre-filled vendor/bank details
- Fixed-amount or rate x quantity line items (per sec/min/hour/session/unit)
- GST as CGST+SGST or IGST, Indian amount-in-words
- Optional signature, with a computer-generated note as fallback
- Print-ready PDF export via @react-pdf/renderer
- Windows cross-compile via cargo-xwin (NSIS installer)

Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
This commit is contained in:
2026-10-03 23:07:51 +05:30
co-authored by CommandCodeBot
commit 875a12b2e6
121 changed files with 13660 additions and 0 deletions
+54
View File
@@ -0,0 +1,54 @@
import { round2 } from "./format";
import type { InvoiceItem, TaxType } from "./types";
/** Amount for a single line: a fixed total, or rate × quantity. */
export function computeLineAmount(item: InvoiceItem): number {
if (item.mode === "rate") {
return round2((item.rate || 0) * (item.quantity || 0));
}
return round2(item.amount || 0);
}
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 = round2(items.reduce((sum, item) => sum + computeLineAmount(item), 0));
const discountValue = round2(Math.min(Math.max(discount || 0, 0), subtotal));
const taxable = round2(subtotal - discountValue);
let cgst = 0;
let sgst = 0;
let igst = 0;
if (taxType === "cgst_sgst") {
cgst = round2((taxable * (taxRate / 2)) / 100);
sgst = round2((taxable * (taxRate / 2)) / 100);
} else if (taxType === "igst") {
igst = round2((taxable * taxRate) / 100);
}
const taxTotal = round2(cgst + sgst + igst);
return {
subtotal,
discount: discountValue,
taxable,
cgst,
sgst,
igst,
taxTotal,
total: round2(taxable + taxTotal),
};
}