diff --git a/README.md b/README.md index 23ca5bc..8fe4e75 100644 --- a/README.md +++ b/README.md @@ -1,84 +1,181 @@ # Voiced -An offline-first desktop invoicing app for **Test Vendor**, built by **Bongbetic**. +An offline-first GST invoicing desktop app for **Test Vendor**, built by **Bongbetic**. -Voiced turns a few details into a clean, GST-ready PDF invoice. It keeps a running -invoice-number series in a local database, remembers your vendor and bank details, and -exports print-ready PDFs — no account, no network required. +Voiced turns a few details into a clean, print-ready PDF invoice. It keeps a running invoice-number series in a +local database, remembers your vendor, client and bank details, and works without an account or a network +connection. The only optional network feature is the ERPNext push, which you configure yourself. + +Targets: Void Linux (xbps, WebKitGTK 2.50 or newer) and Windows 10/11 (NSIS installer, WebView2). ## Features -- **Automatic invoice numbering** — a persistent series (e.g. `INV/2026-001`) that keeps - counting up, with a one-click "start a new series" that resets the counter. -- **Guided first-run setup** — your name, address, contact, PAN, GSTIN, logo, signature and - bank details, pre-filled from your existing invoice and editable at any time. -- **Two line-item modes** — a fixed amount, or a rate × quantity where the unit can be per - second / minute / hour / session / unit (e.g. 10 sessions × ₹2,500). -- **GST support** — toggle taxes on/off and split as **CGST + SGST** (within state) or - **IGST** (inter-state), with live totals and Indian amount-in-words. -- **Signatures** — attach a PNG/JPEG per invoice, or omit it and the invoice notes that it is - computer-generated and needs no signature. -- **PDF export** — a print-ready A4 invoice styled to match the original `format.pdf`. -- **Saved clients**, invoice history with re-export, and light/dark (Carbon g10/g100) themes. -- **Input validation** for PAN, IFSC, GSTIN, email and phone. +- **Invoices.** Automatic numbering in a persistent series (for example `INV/2026-001`) with a one-click new series, + drafts, issue and cancel. An issued invoice is immutable: it stores a snapshot of the vendor, client, GST figures + and layout. Line items are a fixed amount or rate x quantity (per second, minute, hour, session or unit), with + item presets. +- **GST.** All money and tax maths is done in integer paise. CGST+SGST, IGST or UTGST follow from the supplier's + state and the place of supply, and a mismatch is rejected. An unregistered vendor gets a plain "Invoice" with no + tax. GSTIN, PAN, IFSC, email and phone are validated; amounts are written in Indian words. +- **Templates.** 14 templates: Voiced Standard (Classic) and 13 designs measured from Canva originals (Linea, + Monogram, Serenity, Crimson Grid, Tangerine Ledger, Slate Band, Teal Swoosh, Purple Pop, Citrus Split, Monolith, + Cobalt Stripe, Marble, Highlighter). Your logo fills the template's logo slot; a few templates offer an opt-in + logo position. Specs are in `docs/templates/`. +- **Page setup.** A4 or Letter, and margin presets (template, narrow 36 pt, normal 54 pt). +- **Export modes.** *Searchable* PDF (text layer kept) or *flattened* (pages rasterised at 300 DPI and rebuilt as + an image-only PDF). Issued PDFs are archived by content hash, and "Original as issued" re-exports the exact + archived bytes. +- **Fonts.** 11 bundled font families (see "Fonts and licences"). The three commercial template faces (Now, Gotham, + Open Sauce One) can be overridden with your own licensed files under Settings, Fonts. +- **Clients and presets.** Saved clients with structured addresses, state and GST category; item presets. +- **Payments.** Record payments against an issued invoice, including TDS deducted by the client. Outstanding + balance is cash plus TDS against the invoice total. +- **History.** Filterable invoice list, detail view with payments, and CSV or JSON export of the list. +- **Backup and restore.** Manual and automatic daily backups of the database, assets, PDF archive and imported fonts. +- **Export for other software.** `voiced.invoice.v1` JSON and line-item CSV for selected invoices + (see `docs/voiced-invoice-v1.md`). +- **ERPNext integration.** Push an invoice (as draft or submitted) with its PDF attached, and record payments, to an + ERPNext v15 site. +- Light and dark (Carbon g10/g100) themes. ## Tech stack -| Layer | Choice | -| --------- | ------------------------------------------------------------------- | -| Shell | [Tauri v2](https://v2.tauri.app/) (Rust core, WebView2 on Windows) | -| UI | React + TypeScript + [IBM Carbon Design System](https://carbondesignsystem.com/) (`@carbon/react`) | -| Fonts | IBM Plex, self-hosted (works offline) | -| Storage | SQLite via `rusqlite` (bundled) | -| PDF | `@react-pdf/renderer` with bundled IBM Plex | +| Layer | Choice | +| ------- | ------------------------------------------------------------------------------------------- | +| Shell | [Tauri v2](https://v2.tauri.app/) (Rust core, WebKitGTK on Linux, WebView2 on Windows) | +| UI | React 19 + TypeScript + [IBM Carbon](https://carbondesignsystem.com/) (`@carbon/react`) | +| Storage | SQLite via `rusqlite` (bundled), numbered migrations | +| PDF | `@react-pdf/renderer` 4.9.0 in a Web Worker, pdf.js for preview and flattening | +| ERPNext | `reqwest` with rustls (ring provider); all HTTP runs in Rust | ## Prerequisites -- **Node.js 20+** and npm -- **Rust** (stable) via [rustup](https://rustup.rs/) -- Linux dev only: WebKitGTK 4.1, libsoup3, GTK3, librsvg, `pkg-config`, `clang`/`llvm`/`lld` +- **Node.js 22** and npm +- **Rust** stable, via [rustup](https://rustup.rs/) +- Linux only: WebKitGTK 4.1, libsoup3, GTK3, librsvg, `pkg-config`, `clang`/`llvm`/`lld` ## Development ```bash npm install -npm run app:dev # launches the desktop app with hot reload +npm run app:dev # tauri dev, served from localhost:1420 (does not exercise the tauri:// protocol) ``` -Other scripts: +| Command | What it does | +| --- | --- | +| `npm run build` | `tsc --noEmit` and `vite build` (the type-check gate) | +| `npm test` | `vitest run`, about 7 minutes; run on a quiet machine because load starves the sweep tests | +| `npm run fonts:verify` | Checks the bundled fonts against `public/fonts/manifest.json` (`fonts:manifest` regenerates it) | +| `npm run logo:measure` | Regenerates `src/pdf/templates/slots.generated.ts` from the Canva PDFs; must be idempotent | +| `npm run thumbnails` | Regenerates `public/templates/*.png` | +| `npm run templates:compare` | Canva vs Voiced side-by-side PNGs under `/tmp` | +| `cargo test --manifest-path src-tauri/Cargo.toml` | Rust unit tests | +| `npm run app:build` | Production build for the current platform (custom-protocol) | -```bash -npm run build # type-check + build the web frontend -npm run app:build # package the app for the current platform -``` +The release procedure, including the manual first-run checklist, is in `docs/RELEASE.md`. -## Building for Windows (cross-compile from Linux/macOS) +## Where data lives -Tauri only supports the MSVC target. Cross-compiling uses [`cargo-xwin`](https://github.com/rust-cross/cargo-xwin) -and produces an **NSIS installer** plus a portable `.exe`. A `.msi` still requires a real -Windows host. +The Tauri identifier is `com.bongbetic.voiced`. Voiced uses two directories, which differ only on Windows: + +| What | Linux | Windows | +| --- | --- | --- | +| `app_data_dir`: database `voiced.db`, `assets/` (logos, signatures), `backups/` (`auto/`, `pre-restore-*`, pre-migration copies) | `~/.local/share/com.bongbetic.voiced/` | `%APPDATA%\com.bongbetic.voiced\` | +| `app_local_data_dir`: `archive/.pdf` (issued PDFs), `fonts/` (fonts you import) | `~/.local/share/com.bongbetic.voiced/` | `%LOCALAPPDATA%\com.bongbetic.voiced\` | + +On Linux `XDG_DATA_HOME` moves both. On Windows the archive and imported fonts stay out of the roaming profile so a +growing archive and licensed font files are not copied between machines. The window size and position are kept by +the window-state plugin in the app config directory (`~/.config/com.bongbetic.voiced/` on Linux). + +## Backup and restore + +Settings, Data tab. + +- **Backup now** writes a zip: a consistent snapshot of `voiced.db`, `assets/`, `archive/`, `fonts/` and a + `manifest.json` with a sha256 for every file. +- **Automatic backup.** Once per calendar day, while the app is open, Voiced writes + `backups/auto/voiced-auto-YYYYMMDD.zip` and keeps the newest 14. +- **Restore** validates the zip (manifest, hashes, database integrity check) and stages it. Nothing live changes until + the next start: use the "Restart" button or close and reopen the app. On that start the current database, assets, + archive and fonts are moved to `backups/pre-restore-/` (a safety copy that is never pruned), then the + restored files are moved in. If anything fails the app rolls back and starts on the old data. A backup from an + older schema is migrated after restoring. +- **Backups contain secrets.** The ERPNext API secret is stored in the SQLite database, so every backup (manual, + automatic and pre-restore) holds it in plain form, together with your clients, invoices and bank details. Keep + backup files on storage you trust, do not email or upload them casually, and rotate the ERPNext API key if a + backup is lost. + +## Fonts and licences + +Eleven families are bundled as static TTF files in `public/fonts//`, each with its `OFL.txt`: DM Sans, +IBM Plex Mono, IBM Plex Sans, Inter, Jost, Lustria, Montserrat, Noto Sans Devanagari, Noto Sans Kannada, Open Sans +and Poppins. All are under the SIL Open Font License 1.1. `public/fonts/manifest.json` lists every file with its +hash and metrics, and `npm run fonts:verify` checks it. PDFs embed subsets, so the app never depends on system +fonts. + +The Void package also installs these fonts system-wide under `/usr/share/fonts/voiced//` and each OFL text +as `/usr/share/licenses/voiced/-OFL.txt`. + +**Now, Gotham and Open Sauce One** are not bundled. Voiced draws Now with Jost, Gotham with Montserrat and Open +Sauce One with Poppins (Marble, Citrus Split and Purple Pop use these faces). If you hold a licence +for the real fonts, import the files under Settings, Fonts (TTF, OTF or WOFF, one file per weight; WOFF2, variable +and font-collection files are refused). Importing is your confirmation that your licence covers this use. The files +are stored on this computer only, are included in backups, and issued invoices keep the fonts they were issued with. + +## ERPNext integration + +Settings, Integrations. Verified live against ERPNext v15.121.6 (Frappe 15.121), with and without India Compliance +15.32. v14 and v16 have not been tested. + +1. **Integration user.** In ERPNext create a dedicated user (type System User) with the roles **Accounts User** and + **Sales User**. Open the user, Settings tab, API Access, Generate Keys, and copy the secret once. Accounts User + covers invoices, payments and attachments; Sales User covers customers and addresses (Accounts User alone gets + HTTP 403 on Customer). Do not use Administrator or System Manager keys. If you change roles, run + `bench clear-cache`. +2. **Rounding.** In ERPNext System Settings set Rounding Method to **Commercial Rounding**. ERPNext defaults to + Banker's Rounding while Voiced rounds half-paise up, so some totals would differ by 1 paise. Voiced detects a + mismatch after creating the draft, keeps it as a draft, never submits it, and names this fix. +3. **Connect.** Enter the site address, API key and secret and run "Test connection". Use `https://` for real + servers; plain `http://` is accepted only for `localhost`, `127.0.0.1`, `*.localhost` and `*.test`. The secret is + never shown again, and it is not sent to a different server than the one it was saved for. +4. **Company, accounts and units.** Pick the company, the output tax accounts (CGST, SGST, IGST), the bank account for + payments and the unit-of-measure mapping from the loaded option lists. +5. **Naming.** *Mirror* (default) names the ERPNext document with the Voiced number, which needs a site with API v2 + naming (Frappe 15.73 or newer). *Series* lets an ERPNext naming series name the document and puts the Voiced + number in the remarks. +6. **India Compliance.** Detected by the connection test. It requires a 6 or 8 digit HSN/SAC code on each row + (checked at submit), limits document names to 16 characters (mirror mode checks this before posting), and Voiced + always sends `is_reverse_charge` as 0 with a warning. + +Not covered: overseas and SEZ customers, multi-currency, reverse charge booked on RCM accounts, e-invoice and +e-waybill, TLS-terminating proxies beyond plain HTTPS, and Windows. The live test setup is in +`scripts/erpnext-e2e/README.md`. + +## Building for Windows (NSIS, cross-compiled from Linux) + +Tauri supports only the MSVC target. [`cargo-xwin`](https://github.com/rust-cross/cargo-xwin) cross-compiles it +and Tauri produces an **NSIS installer** (a `.msi` needs a real Windows host). ```bash # one-time setup rustup target add x86_64-pc-windows-msvc cargo install --locked cargo-xwin -# plus NSIS (makensis), LLVM/LLD and clang on the host +# plus makensis (NSIS), LLVM/LLD and clang on the host -npm run app:build:windows +npm run app:build:windows # embeds the small WebView2 bootstrapper (needs internet if WebView2 is missing) +npm run app:build:windows:offline # embeds the full WebView2 runtime (over 100 MB larger), for offline machines ``` -Output lands in `src-tauri/target/x86_64-pc-windows-msvc/release/bundle/nsis/` and the -portable binary in the sibling `release/` folder. +Output: `src-tauri/target/x86_64-pc-windows-msvc/release/bundle/nsis/Voiced__x64-setup.exe`, and the portable +binary in the sibling `release/` folder. The installer is a per-user install (`installMode: currentUser`). The +frontend, fonts and pdf.js are compiled into the executable. Size budget: about 25 MB for the standard installer. +`cargo tree -i aws-lc-sys --target x86_64-pc-windows-msvc` must print nothing (the TLS stack is rustls with the ring +provider only, which cross-compiles cleanly). -### Notes for Linux hosts without an `nsis` package +Hosts without an `nsis` package (for example Void Linux): -`makensis` is required for the installer, and Tauri also fetches a small NSIS plugin. On -distributions that don't package NSIS (e.g. Void Linux): - -1. Provide `makensis` (e.g. extract it from your distribution's or an upstream package) and - put it on `PATH`. -2. Point `makensis` at a full NSIS data directory (3.11 recommended, for `Win/RestartManager.nsh`); - it looks in the directory compiled into the binary unless `NSISDIR` is set. +1. Provide `makensis` and put it on `PATH`. +2. Point it at a full NSIS data directory (3.11 recommended, for `Win/RestartManager.nsh`) with `NSISDIR`. 3. Pre-seed Tauri's plugin cache so it does not have to download it: ``` @@ -87,13 +184,10 @@ distributions that don't package NSIS (e.g. Void Linux): # from https://github.com/tauri-apps/nsis-tauri-utils/releases ``` -With those in place, `npm run app:build:windows` produces the NSIS `-setup.exe` and the -portable `voiced.exe`. +The installer has not been run on a real Windows machine. ## Installing on Void Linux -Two ways to get a native `.xbps` package. - ### Prebuilt binary package (no xbps-src needed) ```bash @@ -102,52 +196,91 @@ echo "repository=$(pwd)/build/void/repo" | sudo tee /etc/xbps.d/99-voiced-local. sudo xbps-install -Sy voiced ``` -Launch **Voiced** from the application menu, or run `voiced`. Remove it with -`sudo xbps-remove voiced`. +The script runs `npx tauri build --no-bundle --features custom-protocol` (using your existing `node_modules`), stages +the install tree and runs `xbps-create` and `xbps-rindex`. Launch **Voiced** from the application menu or run +`voiced`; remove it with `sudo xbps-remove voiced`. -The package installs `/usr/bin/voiced`, a desktop entry and hicolor icons, and depends on -`libwebkit2gtk41`. - -**Fonts on Void.** The package also installs the bundled TTF fonts under -`/usr/share/fonts/voiced//` and each family's OFL licence under -`/usr/share/licenses/voiced/`. The `voiced/` subdirectory avoids file conflicts with Void's own -`font-ibm-plex-ttf` and `noto-fonts-ttf`. The `INSTALL` and `REMOVE` scripts in `packaging/void/` -run `fc-cache -f /usr/share/fonts/voiced` after install and removal. `xbps-create` packs them from -the destdir root (checked with xbps 0.60.7). +The package installs `/usr/bin/voiced`, a desktop entry, hicolor icons, the bundled fonts under +`/usr/share/fonts/voiced//`, the OFL texts under `/usr/share/licenses/voiced/`, and `INSTALL` and `REMOVE` +action scripts that run `fc-cache -f /usr/share/fonts/voiced` after install and removal. The `voiced/` font +subdirectory avoids file conflicts with Void's `font-ibm-plex-ttf` and `noto-fonts-ttf`. It depends on +`libwebkit2gtk41>=2.50`, `fontconfig`, `xdg-utils`, `hicolor-icon-theme` and `desktop-file-utils`. ### Building through xbps-src -Copy `packaging/void/template` into a `void-packages` checkout and build it the standard way: - ```bash mkdir -p void-packages/srcpkgs/voiced cp packaging/void/template packaging/void/INSTALL packaging/void/REMOVE void-packages/srcpkgs/voiced/ cd void-packages && ./xbps-src pkg voiced ``` -See the comments at the top of the template about the private source archive and checksums. - -## Where data lives - -| What | Location | -| ------------------- | ------------------------------------------------------------- | -| Database | `/voiced.db` — on Windows `%APPDATA%\com.bongbetic.voiced\` | -| Logos & signatures | `/assets/` | +`INSTALL` and `REMOVE` must sit next to the template; xbps-src packs them. The source is a tagged archive of the +private repository (see the comments at the top of the template; `checksum=SKIP` until a tarball is published). +xbps-src builds in a network-less chroot, so vendor the dependencies first: run `cargo vendor` in `src-tauri/` (with a +`.cargo/config.toml` pointing at `vendor/`) and populate the npm cache, and include the results in the source +archive. The template runs `npm ci` (not `--omit=optional`: that removes +the native rollup and esbuild binaries the Vite build needs, and `@napi-rs/canvas` is type-checked by `tsc`). +No OpenSSL is needed (rustls), and the +dependencies of the ERPNext and backup code (`reqwest`, `zip`) are pure Rust. ## Project layout ``` -src/ React + Carbon frontend - views/ Onboarding · NewInvoice · InvoiceHistory · SeriesSettings · AppSettings - components/ AppShell · InvoicePreview · ImagePicker - pdf/ InvoiceDocument (react-pdf template) - lib/ api · types · invoice · numberToWords · validators · format · pdf -src-tauri/ Rust backend (Tauri commands, SQLite schema, asset store) -public/brand/ Bongbetic + vendor brand assets -public/fonts/ IBM Plex (woff/woff2) -packaging/void/ Void Linux packaging (xbps-src template + build script + desktop entry) +src/ React + Carbon frontend + views/ Onboarding, NewInvoice, InvoiceHistory, InvoiceDetail, Clients, AppSettings, SeriesSettings + components/ AppShell, TemplatePicker, PageSetupControls, PdfPreview, LogoBranding, + FontOverrideTable, DataBackupPanel, ErpnextSettingsPanel, RecordPaymentModal, ... + hooks/ Export, PDF preview, user fonts, shortcuts + lib/ Typed Tauri wrapper (api.ts), money, GST helpers, exportFlow, historyExport, + invoiceExportV1, ERPNext UI helpers + pdf/ PDF engine + model/ buildRenderModel (invoice -> render model) + engine/ geometry (A4/Letter, margins), logo slots, layout fingerprint + templates/ / with tokens.ts, plan.ts, Layout.tsx; registry.ts, catalog.ts, contract.ts + blocks/ decor/ Shared layout blocks and decor + fonts/ Font registration, roles, glyph priming + render/ renderCore, Web Worker, latest-wins client + flatten.ts assemble.ts Rasterise at 300 DPI and rebuild an image-only PDF +src-tauri/src/ Rust backend + db.rs Schema as numbered migrations (copies the DB aside before migrating) + gst.rs Integer-paise money and GST maths + logo.rs Logo trimming, quantising, measuring + commands/ Tauri commands: invoice, series, clients, payments, presets, settings, assets, + archive, fonts, backup, files, raw (binary IPC), erpnext, logo + integrations/erpnext/ ERPNext client, mapping, discovery, push + selftest.rs Unattended real-webview self-test +public/fonts/ Bundled TTF families, OFL texts, manifest.json (plus legacy IBM Plex web fonts) +public/templates/ Template thumbnails +docs/ Template specs, voiced.invoice.v1, release checklist +scripts/ Font, logo, template, thumbnail and ERPNext end-to-end tooling +packaging/void/ xbps-src template, build-xbps.sh, desktop entry, INSTALL and REMOVE scripts ``` +## Tests and gates + +Before a release (details in `docs/RELEASE.md`): `npm run build`, `npm test`, `npm run fonts:verify`, +`cargo test --manifest-path src-tauri/Cargo.toml`, and the real-webview self-test on a debug or installed build: + +```bash +npm run tauri -- build --debug --no-bundle --features custom-protocol +VOICED_SELFTEST_OUT=/tmp/voiced-e2e/report.json XDG_DATA_HOME=/tmp/voiced-e2e/data \ + XDG_CONFIG_HOME=/tmp/voiced-e2e/config timeout 180 src-tauri/target/debug/voiced +``` + +The app is inert unless `VOICED_SELFTEST_OUT` is set; `ok: true` is expected in the report. The same checks run from +Settings, Diagnostics. + +## Known limitations + +- Windows and WebView2 have never been run on a real machine; the NSIS installer is cross-compiled only. +- The 13 Canva-derived templates are re-creations of Canva designs. Canva's content licence may restrict this, so + ship them only in this vendor's build until it is confirmed. +- The SAC code for the vendor's services and the signature rule still need confirmation from a chartered accountant. +- Now, Gotham and Open Sauce One are replaced by free look-alikes unless you import licensed files. +- ERPNext: only v15 is verified; HTTP is allowed only for local hosts; see "Not covered" above. +- Not implemented (deferred): copies and watermarks, PDF/A, custom margins, user background textures, SVG logo + import, CESS, composition and SEZ invoices. + ## Ownership © Bongbetic. All rights reserved. This is a private application. diff --git a/docs/RELEASE.md b/docs/RELEASE.md new file mode 100644 index 0000000..7baad48 --- /dev/null +++ b/docs/RELEASE.md @@ -0,0 +1,91 @@ +# Release checklist + +Run from the repository root on a quiet machine, on a branch cut from `main`. + +## 1. Version + +Keep these in step: `package.json` and the root entry of `package-lock.json`, `src-tauri/Cargo.toml` and +`src-tauri/Cargo.lock` (`cargo update --offline -p voiced`), `src-tauri/tauri.conf.json`, and `version=` plus +`revision=` in `packaging/void/template`. The PDF Creator string follows `package.json` (`src/lib/pdf.tsx`); layout +fingerprints do not include it, so goldens stay unchanged. + +## 2. Gates + +```bash +npm run build +npm test # about 7 minutes +npm run fonts:verify +cargo test --manifest-path src-tauri/Cargo.toml +cargo tree -i aws-lc-sys --manifest-path src-tauri/Cargo.toml # must not match +cargo tree -i aws-lc-sys --manifest-path src-tauri/Cargo.toml --target x86_64-pc-windows-msvc # must not match +``` + +Real-webview self-test on the debug build: + +```bash +npm run tauri -- build --debug --no-bundle --features custom-protocol +VOICED_SELFTEST_OUT=/tmp/voiced-e2e/report.json XDG_DATA_HOME=/tmp/voiced-e2e/data \ + XDG_CONFIG_HOME=/tmp/voiced-e2e/config timeout 180 src-tauri/target/debug/voiced +``` + +`/tmp/voiced-e2e/report.json` must contain `"ok": true`. Repeat the same check on each installed build with +Settings, Diagnostics. + +## 3. Void package + +```bash +./packaging/void/build-xbps.sh +tar --zstd -tf build/void/repo/voiced-_1.x86_64.xbps | less +``` + +Expect `./usr/bin/voiced`, the desktop file, hicolor icons, `usr/share/fonts/voiced//*.ttf`, +`usr/share/licenses/voiced/*-OFL.txt`, `./INSTALL` and `./REMOVE`. Then install it into a clean container: + +```bash +podman run --rm -v "$PWD/build/void/repo:/repo:Z" ghcr.io/void-linux/void-glibc-full sh -c ' + xbps-install -Syu xbps && xbps-install -y -R /repo voiced && + fc-list | grep -iE "montserrat|poppins|lustria|dm sans|inter|jost|open sans|ibm plex" && + ls /usr/share/licenses/voiced && ldd /usr/bin/voiced | grep "not found"; xbps-remove -y voiced' +``` + +`ldd` must print no `not found` line, and `xbps-remove` must finish without errors. Optionally build with +`xbps-src` in a `void-packages` checkout (see the README). + +## 4. Windows installer + +```bash +npm run app:build:windows # standard installer +npm run app:build:windows:offline # only for machines without internet +``` + +Needs `cargo-xwin`, `makensis` and the NSIS plugin cache (README, "Building for Windows"). Check the size of +`src-tauri/target/x86_64-pc-windows-msvc/release/bundle/nsis/*-setup.exe` against the budget of about 25 MB. Optional: +sign it with `osslsigncode`. Never commit build outputs (`build/`, `dist/`, `src-tauri/target/`). + +## 5. Manual first-run checklist (on a real machine, installed build, not `app:dev`) + +Linux and Windows. Windows has never been run on real hardware, so treat it as untested until this passes. + +- **Onboarding and Branding.** Complete first-run setup; upload a wide and a square logo, check the dark-theme + wordmark and the signature. +- **Template picker.** Open all 14 templates; switch A4 and Letter and the margin presets; check the logo slots. +- **Export.** Issue an invoice; export searchable and flattened; change settings and re-export; "Original as issued" + must be byte-identical to the archived PDF. On Windows, check file names with `/`, `:` and reserved names. +- **Fonts.** Settings, Fonts: import a TTF for Now, Gotham or Open Sauce One (if a licensed file is at hand), confirm + a template that uses it changes, remove it again. +- **Clients.** Create a client with structured address and GST category; use it on an invoice. +- **Settings tabs.** Business, Branding, Bank and payments, Invoice defaults, Numbering, Templates and export, Fonts, + Data, Integrations: open each and save a change. +- **History, detail and payments.** Filter the list; open an invoice; record a payment with TDS; cancel one; export + CSV, JSON and `voiced.invoice.v1`. +- **Data.** Backup now, change something, restore the backup, restart, confirm the data returned and that + `backups/pre-restore-*` exists. Confirm the archive and fonts directories (Windows: under `%LOCALAPPDATA%`). +- **Integrations.** Test connection and push an invoice to a test ERPNext (README, "ERPNext integration"). +- **Single instance.** Launch a second copy: the first window must come to the front. + +## 6. Canva licence + +The 13 Canva-derived templates are re-creations of Canva designs. Canva's content licence may restrict that. Until it +is confirmed in writing, distribute builds containing those templates only to this vendor. If the answer is no, +remove the templates from `src/pdf/templates/registry.ts` and `catalog.ts` (Classic stays) and rebuild. Also still +open: a chartered accountant's sign-off on the SAC code and the signature rule. diff --git a/package-lock.json b/package-lock.json index 2bc7f35..e39dc72 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "voiced", - "version": "0.1.0", + "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "voiced", - "version": "0.1.0", + "version": "1.0.0", "dependencies": { "@carbon/icons-react": "^11.89.0", "@carbon/react": "^1.117.0", diff --git a/package.json b/package.json index a60b0b1..7470add 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "voiced", "private": true, - "version": "0.1.0", + "version": "1.0.0", "description": "Voiced — offline invoicing desktop app by Bongbetic", "type": "module", "author": "Bongbetic", diff --git a/packaging/void/template b/packaging/void/template index b0cbeaa..a1131a7 100644 --- a/packaging/void/template +++ b/packaging/void/template @@ -21,11 +21,14 @@ # not install them. Copy them next to the template (see the commands above). # The stock x11-fonts trigger enabled by font_dirs does not run fc-cache # unless mkfontdir and mkfontscale are installed, hence these scripts. -# * If the build machine is offline, pre-populate the npm cache and Cargo -# registry in the masterdir before building. +# * xbps-src builds without network access: vendor the crates (`cargo vendor` +# in src-tauri/ plus a .cargo/config.toml pointing at vendor/) and pre-populate +# the npm cache in the source archive or masterdir before building. +# * No openssl-devel: the ERPNext client uses rustls (ring); reqwest and zip are +# pure Rust and need no extra system libraries. # pkgname=voiced -version=0.1.0 +version=1.0.0 revision=1 archs="x86_64*" hostmakedepends="rust nodejs pkg-config" @@ -43,7 +46,10 @@ compression="zstd" font_dirs="/usr/share/fonts/voiced" do_build() { - npm install --include=dev --no-audit --no-fund + # Not --omit=optional: that also drops rollup's and esbuild's native binaries (they are + # optional dependencies), so vite fails; and tsc in `npm run build` type-checks the + # @napi-rs/canvas test helpers, so that package must be installed too. + npm ci --no-audit --no-fund npm run build cd src-tauri # `custom-protocol` embeds the frontend for production; without it the app diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 4459920..6ea10a5 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -4647,7 +4647,7 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "voiced" -version = "0.1.0" +version = "1.0.0" dependencies = [ "base64 0.22.1", "chrono", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index f705b59..395085f 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "voiced" -version = "0.1.0" +version = "1.0.0" description = "Voiced — offline invoicing desktop app by Bongbetic" authors = ["Bongbetic"] edition = "2021" diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 315ee23..99665f8 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "Voiced", - "version": "0.1.0", + "version": "1.0.0", "identifier": "com.bongbetic.voiced", "build": { "beforeDevCommand": "npm run dev",