From e0e097d31a5b8fee4b6252f3df01735b37729b6a Mon Sep 17 00:00:00 2001 From: Paul Trowbridge Date: Wed, 16 Sep 2026 23:25:25 -0400 Subject: [PATCH] Whole units for the measures, five places for price sales_usd and qty run to eight digits here, where two decimals are noise. Price is the opposite case: it sits around 0.27, so two places barely show a move and four still round away part of one. derive() hardcoded two decimals, so the editable rows would have disagreed with the lines above them. It now takes the measure's own precision. The percentage row keeps one place, being its own scale. Co-Authored-By: Claude Opus 5 (1M context) --- ui/src/components/OperationPanel.jsx | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/ui/src/components/OperationPanel.jsx b/ui/src/components/OperationPanel.jsx index b36b337..70f0478 100644 --- a/ui/src/components/OperationPanel.jsx +++ b/ui/src/components/OperationPanel.jsx @@ -189,7 +189,10 @@ const FIELDS = [ ] // given the active edit for a measure, what do the three rows read? -function derive(current, edit) { +// dp is the measure's own precision, so the editable rows read the same way as +// the lines above them -- whole dollars against whole dollars, five places +// against five. The percentage is its own scale and stays at one. +function derive(current, edit, dp = 2) { const blank = { new: '', change: '', pct: '' } if (!edit || edit.raw === '' || edit.raw == null) return blank const n = parseFloat(edit.raw) @@ -203,8 +206,8 @@ function derive(current, edit) { const change = next - current const pct = current === 0 ? null : (change / Math.abs(current)) * 100 const out = { - new: fmtNum(next), - change: fmtNum(change), + new: fmtNum(next, dp), + change: fmtNum(change, dp), pct: pct == null ? '—' : fmtNum(pct, 1), } out[edit.field] = edit.raw // keep what you typed exactly as typed @@ -285,13 +288,16 @@ function ScaleLedger({ currentTotals, scaleInputs, setScaleInputs, scalePlug, se // measure columns, in ledger order const measures = [ - valueCol && { key: 'value', label: valueCol, current: total.value, dp: 2 }, - unitsCol && { key: 'units', label: unitsCol, current: total.units, dp: 2 }, - (valueCol && unitsCol) && { key: 'price', label: 'price', current: curPrice, dp: 4, hint: 'value / units' }, + // Whole units for the measures -- the figures run to eight digits, where two + // decimals are noise. Price is the opposite: it sits around 0.27, so it needs + // the places to show a move at all. + valueCol && { key: 'value', label: valueCol, current: total.value, dp: 0 }, + unitsCol && { key: 'units', label: unitsCol, current: total.units, dp: 0 }, + (valueCol && unitsCol) && { key: 'price', label: 'price', current: curPrice, dp: 5, hint: 'value / units' }, ].filter(Boolean) const derived = Object.fromEntries( - measures.map(m => [m.key, derive(basisOf(m.key) ?? 0, scaleInputs[m.key])]) + measures.map(m => [m.key, derive(basisOf(m.key) ?? 0, scaleInputs[m.key], m.dp)]) ) // an edit is dollars-only when value carries a number and neither units nor