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:
2026-10-04 04:15:58 +05:30
parent d6ec6b2a17
commit 5597fb791d
19 changed files with 2403 additions and 327 deletions
+26 -13
View File
@@ -96,6 +96,8 @@ pub struct InvoiceItem {
pub amount: f64,
#[serde(default)]
pub sort_order: i64,
#[serde(default)]
pub hsn_sac: String,
}
fn default_mode() -> String {
@@ -106,6 +108,8 @@ fn default_unit() -> String {
"unit".to_string()
}
/// What the webview may send when issuing an invoice. Money totals, tax amounts,
/// the amount in words and the vendor/bank snapshots are derived on the Rust side.
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct InvoiceInput {
@@ -125,34 +129,25 @@ pub struct InvoiceInput {
#[serde(default)]
pub place_of_supply_state_code: String,
#[serde(default)]
pub subtotal: f64,
#[serde(default)]
pub discount: f64,
/// The tax treatment the user chose; it must match the derived one.
#[serde(default = "default_tax_type")]
pub tax_type: String,
#[serde(default)]
pub tax_rate: f64,
#[serde(default)]
pub cgst_amount: f64,
#[serde(default)]
pub sgst_amount: f64,
#[serde(default)]
pub igst_amount: f64,
#[serde(default)]
pub total: f64,
#[serde(default)]
pub amount_in_words: String,
pub reverse_charge: bool,
#[serde(default)]
pub bank_account_id: Option<i64>,
#[serde(default)]
pub bank_snapshot: String,
#[serde(default)]
pub signature_path: Option<String>,
#[serde(default)]
pub notes: String,
#[serde(default)]
pub save_client: bool,
#[serde(default)]
pub draft_id: Option<i64>,
#[serde(default)]
pub items: Vec<InvoiceItem>,
}
@@ -187,9 +182,18 @@ pub struct Invoice {
pub bank_snapshot: String,
pub signature_path: Option<String>,
pub notes: String,
/// draft | issued | cancelled
pub status: String,
pub created_at: String,
pub updated_at: String,
pub doc_type: String,
pub reverse_charge: bool,
/// JSON of the supplier details as they were when the invoice was issued; empty if unknown.
pub vendor_snapshot: String,
pub snapshot_origin: String,
pub cancelled_at: Option<String>,
pub cancel_reason: String,
pub archived_pdf_sha256: Option<String>,
pub items: Vec<InvoiceItem>,
}
@@ -205,3 +209,12 @@ pub struct InvoiceSummary {
pub status: String,
pub created_at: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct DraftSummary {
pub id: i64,
pub updated_at: String,
pub client_name: Option<String>,
pub total: Option<f64>,
}