Add units of measure and fractional quantities to rate lines
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
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
/** Pure helpers behind the ERPNext screens (Settings, History, invoice detail, auto-push). No UI, no Tauri. */
|
||||
import { UNITS } from "./units";
|
||||
import type {
|
||||
ErpnextConfig,
|
||||
ErpnextConfigInput,
|
||||
@@ -259,14 +260,8 @@ export function versionsLine(test: ErpnextConnectionTest): string {
|
||||
|
||||
// ---- settings form ----
|
||||
|
||||
export const UOM_UNITS = ["second", "minute", "hour", "session", "unit"] as const;
|
||||
export const DEFAULT_UOM_MAP: Record<string, string> = {
|
||||
second: "Second",
|
||||
minute: "Minute",
|
||||
hour: "Hour",
|
||||
session: "Nos",
|
||||
unit: "Nos",
|
||||
};
|
||||
export const UOM_UNITS: readonly string[] = UNITS.map((u) => u.id);
|
||||
export const DEFAULT_UOM_MAP: Record<string, string> = Object.fromEntries(UNITS.map((u) => [u.id, u.uom]));
|
||||
|
||||
export function withDefaultUoms(map: Record<string, string> | undefined): Record<string, string> {
|
||||
const out: Record<string, string> = { ...DEFAULT_UOM_MAP };
|
||||
|
||||
Reference in New Issue
Block a user