Units of measure. A rate line can now be priced per second, minute, hour, day, week, month or session; per
unit, piece or set; per km or metre; per square foot or square metre; per kg; per litre; or per a custom unit you
type (up to 12 characters). The unit prints on the invoice beside the quantity ("4.5 hr", "12.75 km") and in the rate
("₹1,200.00 per hour"), on all 14 templates.
Time typed as hours and minutes. In a time unit, type 4:30 and it counts as 4.5 hours when the amount is
worked out, with the decimal shown under the box. 4:20 is kept as an exact 4.333... so the amount is not off by a
paisa. Other units take decimals such as 12.75.
Item presets can use any unit as well. The export format (voiced.invoice.v1) documents the new unit values.
Fixed
The window went blank after every export, so the app had to be killed. The "Saved to ..." notice held buttons
inside a notification, which made the interface crash and leave an empty window. It now shows the notice and the
Open file and Show in folder buttons, and you can go straight to New invoice. If anything in the interface ever
fails again, a message with a Reload button appears instead of a blank window.
A saved client's name was cut off (for example "Timber Media Pv" for "Timber Media Pvt Ltd"). The saved-client
box and the other invoice fields were too narrow; the form now uses the full width. Dates, GSTIN, PO number and
place of supply no longer clip their text, and the line type shows "Rate × quantity" in full.
Quantities with a long unit (for example "120 wooden crate") no longer squeeze the description column in the
templates, and the Classic template's quantity column grows to hold "12.75 km" on one line.
Known
Windows has not been run on a real machine yet; it is built and tested from Linux only.
ERPNext: a custom unit is sent as "Nos", which ERPNext only accepts for whole numbers, and "Month" is not a stock
ERPNext unit. Map the unit in Settings, Integrations. A time such as 4:20 hours (not exact to three decimals) is
still refused for sending, with a message saying so.
Merge with a merge commit (not squash or rebase): the v1.1.0 tag already points at the head of this branch and the release assets were built from it.
Local gates on this head: tsc, ESLint (0 errors), cargo fmt --check, jscpd 3.57%, Semgrep clean, Playwright smoke test, vitest 1729 passed, cargo test 253 passed, fonts:verify, real-webview self-test ok.
## New
- **Units of measure.** A rate line can now be priced per second, minute, hour, day, week, month or session; per
unit, piece or set; per km or metre; per square foot or square metre; per kg; per litre; or per a custom unit you
type (up to 12 characters). The unit prints on the invoice beside the quantity ("4.5 hr", "12.75 km") and in the rate
("₹1,200.00 per hour"), on all 14 templates.
- **Time typed as hours and minutes.** In a time unit, type `4:30` and it counts as 4.5 hours when the amount is
worked out, with the decimal shown under the box. `4:20` is kept as an exact 4.333... so the amount is not off by a
paisa. Other units take decimals such as `12.75`.
- Item presets can use any unit as well. The export format (`voiced.invoice.v1`) documents the new unit values.
## Fixed
- **The window went blank after every export, so the app had to be killed.** The "Saved to ..." notice held buttons
inside a notification, which made the interface crash and leave an empty window. It now shows the notice and the
Open file and Show in folder buttons, and you can go straight to New invoice. If anything in the interface ever
fails again, a message with a Reload button appears instead of a blank window.
- **A saved client's name was cut off** (for example "Timber Media Pv" for "Timber Media Pvt Ltd"). The saved-client
box and the other invoice fields were too narrow; the form now uses the full width. Dates, GSTIN, PO number and
place of supply no longer clip their text, and the line type shows "Rate × quantity" in full.
- Quantities with a long unit (for example "120 wooden crate") no longer squeeze the description column in the
templates, and the Classic template's quantity column grows to hold "12.75 km" on one line.
## Known
- Windows has not been run on a real machine yet; it is built and tested from Linux only.
- ERPNext: a custom unit is sent as "Nos", which ERPNext only accepts for whole numbers, and "Month" is not a stock
ERPNext unit. Map the unit in Settings, Integrations. A time such as 4:20 hours (not exact to three decimals) is
still refused for sending, with a message saying so.
Merge with a merge commit (not squash or rebase): the `v1.1.0` tag already points at the head of this branch and the release assets were built from it.
Local gates on this head: tsc, ESLint (0 errors), cargo fmt --check, jscpd 3.57%, Semgrep clean, Playwright smoke test, vitest 1729 passed, cargo test 253 passed, fonts:verify, real-webview self-test ok.
The saved-client box showed "Timber Media Pv" for "Timber Media Pvt Ltd": the form sat on a Carbon Grid nested in a
Column, whose tracks came out much narrower than the page grid, so the combo box was about 230 px wide at the default
window size and the date, GSTIN and PO fields were just as tight. The fields now sit on a plain 12-column CSS grid
(.voiced-fields), the client fields use the full width, place of supply gets its own row, and the line type select is
wide enough for "Rate x quantity".
Claude-Session: https://claude.ai/code/session_01PZypiWDfMkDTeEPeXjRhW5
Every successful export left the window blank and unresponsive, so the app had to be killed. The "Saved to ..." notice
(and the export-failed notice) held buttons inside a Carbon InlineNotification. Carbon throws "component should have no
interactive child nodes" in an effect, and an uncaught throw makes React unmount the whole tree. Found by driving the
real debug binary through WebKitWebDriver: the page body was empty and the error listener showed that throw.
- The buttons now sit beside the notice, and the failed-export notice uses ActionableNotification's own Retry action.
- ErrorBoundary around the app (reload screen instead of a blank window) and around each export notice.
- A lint test that fails if a Carbon notification wraps an interactive element.
- The real-webview self-test mounts the saved and failed notices and fails on an uncaught React error, and issues and
exports a second invoice after the first.
Claude-Session: https://claude.ai/code/session_01PZypiWDfMkDTeEPeXjRhW5
Quantities can now be time, count, distance, area, weight or volume, or a custom unit, instead of only
second/minute/hour/session/unit.
- Registry in src/lib/units.ts (mirrored in src-tauri/src/units.rs): grouped built-in units plus a custom unit of 1-12
characters. A rate line's unit is validated when an invoice or a preset is saved; migration M12 drops the old unit
allow-list from item_presets and keeps every row.
- Time is typed as h:mm: "4:30" hours counts as 4.5, "4:20" as 4.333... (kept exact, so rate x quantity rounds once, the
same in TypeScript and Rust). Other units take plain decimals up to 3 places. A bad quantity blocks issuing.
- The PDF quantity column shows the unit ("4.5 hr", "12.75 km"); the rate keeps "per hour" / "/hr". RenderItem carries
priceText, perText and rateShort, so no template parses the rate text with a regex over five hard-coded units.
- Quantity columns are sized from the widest word once a quantity with its unit is wider than 72 pt, so one long
custom unit cannot squeeze the description (the serenity and citrus-split tables overflowed in the new fixture).
Classic's quantity column is now content-sized.
- ERPNext: the default UOM map comes from the registry, with fractional-capable UOMs for time, distance, area, weight
and volume. A custom unit is sent as Nos. A quantity that is not exact to 3 decimals (4:20 hours) is still refused for
push, as before.
- Goldens and template thumbnails regenerated; the template harness has a "units" fixture with the longest texts.
Claude-Session: https://claude.ai/code/session_01PZypiWDfMkDTeEPeXjRhW5
Version bump, the README line about units, and two new manual checks in the release checklist (export then New invoice
through the real save dialog, and units).
Claude-Session: https://claude.ai/code/session_01PZypiWDfMkDTeEPeXjRhW5
The new CI runs cargo fmt --check. QuantityInput also reported its validity only when it changed, so removing a line
whose quantity box held bad text left that box counted as invalid and blocked issuing; it now reports valid when it
unmounts, and no longer writes a ref during render.
Claude-Session: https://claude.ai/code/session_01PZypiWDfMkDTeEPeXjRhW5
Closing: every commit in this PR (head 93a4ece, release/1.1.0) is already in main through #3 (Release 1.2.0: Reset all data), which was merged as e87fd8f. Nothing left to merge here.
Closing: every commit in this PR (head 93a4ece, release/1.1.0) is already in main through #3 (Release 1.2.0: Reset all data), which was merged as e87fd8f. Nothing left to merge here.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
New
unit, piece or set; per km or metre; per square foot or square metre; per kg; per litre; or per a custom unit you
type (up to 12 characters). The unit prints on the invoice beside the quantity ("4.5 hr", "12.75 km") and in the rate
("₹1,200.00 per hour"), on all 14 templates.
4:30and it counts as 4.5 hours when the amount isworked out, with the decimal shown under the box.
4:20is kept as an exact 4.333... so the amount is not off by apaisa. Other units take decimals such as
12.75.voiced.invoice.v1) documents the new unit values.Fixed
inside a notification, which made the interface crash and leave an empty window. It now shows the notice and the
Open file and Show in folder buttons, and you can go straight to New invoice. If anything in the interface ever
fails again, a message with a Reload button appears instead of a blank window.
box and the other invoice fields were too narrow; the form now uses the full width. Dates, GSTIN, PO number and
place of supply no longer clip their text, and the line type shows "Rate × quantity" in full.
templates, and the Classic template's quantity column grows to hold "12.75 km" on one line.
Known
ERPNext unit. Map the unit in Settings, Integrations. A time such as 4:20 hours (not exact to three decimals) is
still refused for sending, with a message saying so.
Merge with a merge commit (not squash or rebase): the
v1.1.0tag already points at the head of this branch and the release assets were built from it.Local gates on this head: tsc, ESLint (0 errors), cargo fmt --check, jscpd 3.57%, Semgrep clean, Playwright smoke test, vitest 1729 passed, cargo test 253 passed, fonts:verify, real-webview self-test ok.
Quantities can now be time, count, distance, area, weight or volume, or a custom unit, instead of only second/minute/hour/session/unit. - Registry in src/lib/units.ts (mirrored in src-tauri/src/units.rs): grouped built-in units plus a custom unit of 1-12 characters. A rate line's unit is validated when an invoice or a preset is saved; migration M12 drops the old unit allow-list from item_presets and keeps every row. - Time is typed as h:mm: "4:30" hours counts as 4.5, "4:20" as 4.333... (kept exact, so rate x quantity rounds once, the same in TypeScript and Rust). Other units take plain decimals up to 3 places. A bad quantity blocks issuing. - The PDF quantity column shows the unit ("4.5 hr", "12.75 km"); the rate keeps "per hour" / "/hr". RenderItem carries priceText, perText and rateShort, so no template parses the rate text with a regex over five hard-coded units. - Quantity columns are sized from the widest word once a quantity with its unit is wider than 72 pt, so one long custom unit cannot squeeze the description (the serenity and citrus-split tables overflowed in the new fixture). Classic's quantity column is now content-sized. - ERPNext: the default UOM map comes from the registry, with fractional-capable UOMs for time, distance, area, weight and volume. A custom unit is sent as Nos. A quantity that is not exact to 3 decimals (4:20 hours) is still refused for push, as before. - Goldens and template thumbnails regenerated; the template harness has a "units" fixture with the longest texts. Claude-Session: https://claude.ai/code/session_01PZypiWDfMkDTeEPeXjRhW5Preparing review...
Closing: every commit in this PR (head
93a4ece, release/1.1.0) is already in main through #3 (Release 1.2.0: Reset all data), which was merged ase87fd8f. Nothing left to merge here.Pull request closed