Add issue/cancel lifecycle, GST derivation and integer-paise totals
issue_invoice allocates the number, freezes vendor and bank details and stores server-computed totals in one transaction. Issued invoices are cancelled, not deleted. Tax heads derive from supplier state and place of supply (a disagreeing choice is rejected); unregistered suppliers issue a plain Invoice. Adds GSTIN checksum validation, canonical India Compliance state list, server-side drafts, series validation, relative and magic-byte-checked asset paths, and GST settings. PDF re-exports use the frozen vendor snapshot.
This commit is contained in:
+56
-13
@@ -19,7 +19,7 @@ export interface Settings {
|
||||
currency: string;
|
||||
onboarded: boolean;
|
||||
theme: string;
|
||||
gstRegistration: string;
|
||||
gstRegistration: GstRegistration;
|
||||
defaultHsnSac: string;
|
||||
signatoryName: string;
|
||||
signatoryDesignation: string;
|
||||
@@ -65,9 +65,41 @@ export interface InvoiceItem {
|
||||
quantity: number;
|
||||
amount: number;
|
||||
sortOrder: number;
|
||||
/** Optional so older callers that build items by hand keep compiling; stored as "" when absent. */
|
||||
hsnSac?: string;
|
||||
}
|
||||
|
||||
export type GstRegistration = "unregistered" | "regular";
|
||||
|
||||
/** What the webview sends to issue an invoice; totals, tax and words are derived in Rust. */
|
||||
export interface InvoiceInput {
|
||||
invoiceDate: string;
|
||||
dueDate: string;
|
||||
clientId: number | null;
|
||||
clientName: string;
|
||||
clientAddress: string;
|
||||
clientGstin: string;
|
||||
poNumber: string;
|
||||
placeOfSupplyStateCode: string;
|
||||
discount: number;
|
||||
taxType: TaxType;
|
||||
taxRate: number;
|
||||
reverseCharge: boolean;
|
||||
bankAccountId: number | null;
|
||||
signaturePath: string | null;
|
||||
notes: string;
|
||||
saveClient: boolean;
|
||||
draftId: number | null;
|
||||
items: InvoiceItem[];
|
||||
}
|
||||
|
||||
export type InvoiceStatus = "draft" | "issued" | "cancelled";
|
||||
|
||||
/** An invoice as stored: all amounts are derived on the Rust side. */
|
||||
export interface Invoice {
|
||||
id: number;
|
||||
number: string;
|
||||
seriesId: number | null;
|
||||
invoiceDate: string;
|
||||
dueDate: string;
|
||||
clientId: number | null;
|
||||
@@ -89,18 +121,25 @@ export interface InvoiceInput {
|
||||
bankSnapshot: string;
|
||||
signaturePath: string | null;
|
||||
notes: string;
|
||||
saveClient: boolean;
|
||||
status: InvoiceStatus;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
docType: "invoice" | "tax_invoice";
|
||||
reverseCharge: boolean;
|
||||
/** JSON of the supplier details at issue time; empty when unknown. */
|
||||
vendorSnapshot: string;
|
||||
snapshotOrigin: string;
|
||||
cancelledAt: string | null;
|
||||
cancelReason: string;
|
||||
archivedPdfSha256: string | null;
|
||||
items: InvoiceItem[];
|
||||
}
|
||||
|
||||
export interface Invoice extends Omit<InvoiceInput, "saveClient"> {
|
||||
export interface DraftSummary {
|
||||
id: number;
|
||||
number: string;
|
||||
seriesId: number | null;
|
||||
status: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
items: InvoiceItem[];
|
||||
clientName: string | null;
|
||||
total: number | null;
|
||||
}
|
||||
|
||||
export interface InvoiceSummary {
|
||||
@@ -110,7 +149,7 @@ export interface InvoiceSummary {
|
||||
dueDate: string;
|
||||
clientName: string;
|
||||
total: number;
|
||||
status: string;
|
||||
status: InvoiceStatus;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
@@ -122,10 +161,12 @@ export const EMPTY_ITEM: InvoiceItem = {
|
||||
quantity: 1,
|
||||
amount: 0,
|
||||
sortOrder: 0,
|
||||
hsnSac: "",
|
||||
};
|
||||
|
||||
/** GST state codes, same list as STATES in src-tauri/src/gst.rs (from India Compliance). */
|
||||
export const INDIAN_STATES: { code: string; name: string }[] = [
|
||||
{ code: "01", name: "Jammu & Kashmir" },
|
||||
{ code: "01", name: "Jammu and Kashmir" },
|
||||
{ code: "02", name: "Himachal Pradesh" },
|
||||
{ code: "03", name: "Punjab" },
|
||||
{ code: "04", name: "Chandigarh" },
|
||||
@@ -149,18 +190,20 @@ export const INDIAN_STATES: { code: string; name: string }[] = [
|
||||
{ code: "22", name: "Chhattisgarh" },
|
||||
{ code: "23", name: "Madhya Pradesh" },
|
||||
{ code: "24", name: "Gujarat" },
|
||||
{ code: "26", name: "Dadra & Nagar Haveli and Daman & Diu" },
|
||||
{ code: "26", name: "Dadra and Nagar Haveli and Daman and Diu" },
|
||||
{ code: "27", name: "Maharashtra" },
|
||||
{ code: "29", name: "Karnataka" },
|
||||
{ code: "30", name: "Goa" },
|
||||
{ code: "31", name: "Lakshadweep" },
|
||||
{ code: "31", name: "Lakshadweep Islands" },
|
||||
{ code: "32", name: "Kerala" },
|
||||
{ code: "33", name: "Tamil Nadu" },
|
||||
{ code: "34", name: "Puducherry" },
|
||||
{ code: "35", name: "Andaman & Nicobar Islands" },
|
||||
{ code: "35", name: "Andaman and Nicobar Islands" },
|
||||
{ code: "36", name: "Telangana" },
|
||||
{ code: "37", name: "Andhra Pradesh" },
|
||||
{ code: "38", name: "Ladakh" },
|
||||
{ code: "96", name: "Other Countries" },
|
||||
{ code: "97", name: "Other Territory" },
|
||||
];
|
||||
|
||||
export const UNIT_LABELS: Record<LineUnit, string> = {
|
||||
|
||||
Reference in New Issue
Block a user