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) <noreply@anthropic.com>
This commit is contained in:
Paul Trowbridge 2026-09-16 23:25:25 -04:00
parent 8726543e34
commit e0e097d31a

View File

@ -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