Files
Voiced/src-tauri/src/models.rs
T
xavierk 5597fb791d 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.
2026-10-04 04:15:58 +05:30

221 lines
5.8 KiB
Rust

use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Settings {
pub vendor_name: String,
pub vendor_address: String,
pub vendor_email: String,
pub vendor_phone: String,
pub vendor_pan: String,
pub vendor_gstin: String,
pub vendor_state_code: String,
pub logo_path: Option<String>,
pub signature_path: Option<String>,
pub default_bank_id: Option<i64>,
pub default_tax_rate: f64,
pub default_tax_type: String,
pub payment_terms_days: i64,
pub currency: String,
pub onboarded: bool,
pub theme: String,
pub gst_registration: String,
pub default_hsn_sac: String,
pub signatory_name: String,
pub signatory_designation: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BankAccount {
pub id: Option<i64>,
#[serde(default)]
pub label: String,
#[serde(default)]
pub bank_name: String,
#[serde(default)]
pub account_name: String,
#[serde(default)]
pub account_no: String,
#[serde(default)]
pub branch: String,
#[serde(default)]
pub ifsc: String,
#[serde(default)]
pub is_default: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct InvoiceSeries {
pub id: i64,
pub prefix: String,
pub padding: i64,
pub next_number: i64,
pub is_active: bool,
pub created_at: String,
/// The number the next invoice created under this series would receive.
pub next_invoice_number: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Client {
pub id: Option<i64>,
pub name: String,
#[serde(default)]
pub address: String,
#[serde(default)]
pub gstin: String,
#[serde(default)]
pub state_code: String,
#[serde(default)]
pub po_number: String,
#[serde(default)]
pub created_at: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct InvoiceItem {
#[serde(default)]
pub id: Option<i64>,
#[serde(default)]
pub description: String,
/// "fixed" = a single amount; "rate" = rate x quantity.
#[serde(default = "default_mode")]
pub mode: String,
#[serde(default)]
pub rate: f64,
/// second | minute | hour | session | unit
#[serde(default = "default_unit")]
pub unit: String,
#[serde(default)]
pub quantity: f64,
#[serde(default)]
pub amount: f64,
#[serde(default)]
pub sort_order: i64,
#[serde(default)]
pub hsn_sac: String,
}
fn default_mode() -> String {
"fixed".to_string()
}
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 {
pub invoice_date: String,
#[serde(default)]
pub due_date: String,
#[serde(default)]
pub client_id: Option<i64>,
#[serde(default)]
pub client_name: String,
#[serde(default)]
pub client_address: String,
#[serde(default)]
pub client_gstin: String,
#[serde(default)]
pub po_number: String,
#[serde(default)]
pub place_of_supply_state_code: String,
#[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 reverse_charge: bool,
#[serde(default)]
pub bank_account_id: Option<i64>,
#[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>,
}
fn default_tax_type() -> String {
"none".to_string()
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Invoice {
pub id: i64,
pub number: String,
pub series_id: Option<i64>,
pub invoice_date: String,
pub due_date: String,
pub client_id: Option<i64>,
pub client_name: String,
pub client_address: String,
pub client_gstin: String,
pub po_number: String,
pub place_of_supply_state_code: String,
pub subtotal: f64,
pub discount: f64,
pub tax_type: String,
pub tax_rate: f64,
pub cgst_amount: f64,
pub sgst_amount: f64,
pub igst_amount: f64,
pub total: f64,
pub amount_in_words: String,
pub bank_account_id: Option<i64>,
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>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct InvoiceSummary {
pub id: i64,
pub number: String,
pub invoice_date: String,
pub due_date: String,
pub client_name: String,
pub total: f64,
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>,
}