Files
Voiced/docs/voiced-invoice-v1.md
xavierk 2ffbf0f66c Add the ERPNext settings UI, History and detail controls, and voiced.invoice.v1 export (Phase F3)
- Settings gets an Integrations tab: connection fields with a write-only API secret, a CA PEM option, Test connection with versions and warnings, live mapping lists loaded after a test, naming mode, toggles and its own Save.
- Pushing for a GST-registered vendor is refused until a connection test result is stored. The UI shows the reason and disables Send.
- History shows an ERPNext status column from one bulk call, a row Send and Open in ERPNext, and a bulk Send selected with a draft-or-submit confirm step and per-row results that never abort the batch. The detail view gets an ERPNext panel and per-payment send. All of it is hidden or disabled when the integration is not configured.
- Optional auto-push after issue runs in the background and never blocks or fails the issue.
- Generic export: a deterministic voiced.invoice.v1 JSON (and CSV line items) for selected invoices, documented in docs/voiced-invoice-v1.md with a golden test. Money is integer paise plus a decimal string.

The new screens have not been run in a webview yet. The ERPNext Payment Entry deduction fields still need a live check.
2026-10-04 20:57:31 +05:30

3.6 KiB

voiced.invoice.v1

A neutral export of issued invoices for software Voiced has no direct integration with. Choose invoices in the History list (checkboxes) or open one, then use "Export selected (voiced.invoice.v1 JSON)" or "(line items CSV)". The code is src/lib/invoiceExportV1.ts; src/lib/__golden__/invoice-v1.golden.json is a full example.

Rules

  • Money is exact. Every amount appears twice: <name>_paise (an integer) and <name> (a decimal string with two places, e.g. "7310.10"). Nothing is a float. Rates (percent) are decimal strings such as "9" or "6.25".
  • Deterministic. The same invoices give the same bytes: fixed key order, no timestamps, input order kept.
  • JSON file is an array with one object per invoice; each object carries "schema": "voiced.invoice.v1".
  • Cancelled invoices are included with "cancelled": true (their figures are kept; filter them out of sums).
  • An unregistered supplier's plain "Invoice" has document_type: "invoice", all tax heads zero and no supplier GSTIN.

Invoice object

Key Meaning
schema, number, issue_date, due_date YYYY-MM-DD dates; due_date is null when unset
document_type tax_invoice or invoice
status, cancelled, cancelled_at, cancel_reason status is issued or cancelled
currency, po_number always INR
supplier name, address, gstin (null if none), pan, gst_registration, state {code, name}; frozen at issue
client name, gstin, gst_category, address {text, line1, line2, city, pincode, state}, place_of_supply {code, name}
items[] line, description, hsn_sac, quantity (decimal string), unit, rate, taxable_value (each with _paise)
tax cgst, sgst, utgst, igst, each {rate, amount_paise, amount}; heads that do not apply are zero
totals subtotal, discount, taxable_value, tax, total (each with _paise), amount_in_words
reverse_charge, notes
payments[] date, amount (cash), tds, mode, reference (amounts with _paise)

Notes on the fields:

  • Items. taxable_value of a line is the line amount before the invoice-level discount. totals.taxable_value is subtotal - discount, the base the tax is charged on. A fixed-amount line has quantity "1" and unit null.
  • UTGST. When the supplier is in a union territory without a legislature (state codes 04, 26, 31, 35, 38) the second head is reported under utgst and sgst is zero.
  • Client details. The name, GSTIN, printed address and place of supply are frozen on the invoice. The structured address parts and gst_category come from the client's saved record at export time (null or derived from the GSTIN when the invoice has no saved client).
  • Supplier snapshot. Only name, address, GSTIN, PAN, registration and state are exported; logo paths, e-mail and phone are not.

Line-items CSV

One row per line item, UTF-8 with a byte order mark and CRLF line ends; text cells that a spreadsheet would read as a formula get a leading apostrophe. Columns: invoice_number, issue_date, due_date, document_type, status, cancelled, currency, supplier_name, supplier_gstin, client_name, client_gstin, client_gst_category, place_of_supply_code, place_of_supply, reverse_charge, line, description, hsn_sac, quantity, unit, rate, taxable_value, invoice_discount, invoice_cgst, invoice_sgst, invoice_utgst, invoice_igst, invoice_total. The invoice_* amounts are filled on the first row of each invoice only, so summing a column never counts an invoice twice.