Problem
app/schemas/invoicejob.schema.js declares injbAmount as
z.coerce.number(). zod's .number() rejects NaN by default but
allows Infinity and -Infinity through.
JSON has no literal for Infinity, but z.coerce.number() happily
turns the string "Infinity" into the float, and the underlying
Sequelize DOUBLE column accepts the value and stores it as inf.
Any downstream consumer doing arithmetic on the column (invoice
totals, aging buckets, CSV exports) then gets contaminated.
This mirrors the cpayAmount bug fixed in #172, on the invoice-line
side. Unlike the payment field, injbAmount legitimately accepts
zero (reference / courtesy lines) and negatives (credits, discounts),
so the appropriate refinement is .finite() only — not a non-zero
gate.
Fix
Acceptance
Proudly Made in Nebraska. Go Big Red! 🌽 https://xkcd.com/2347/
Problem
app/schemas/invoicejob.schema.jsdeclaresinjbAmountasz.coerce.number(). zod's.number()rejectsNaNby default butallows
Infinityand-Infinitythrough.JSON has no literal for Infinity, but
z.coerce.number()happilyturns the string
"Infinity"into the float, and the underlyingSequelize
DOUBLEcolumn accepts the value and stores it asinf.Any downstream consumer doing arithmetic on the column (invoice
totals, aging buckets, CSV exports) then gets contaminated.
This mirrors the cpayAmount bug fixed in #172, on the invoice-line
side. Unlike the payment field,
injbAmountlegitimately acceptszero (reference / courtesy lines) and negatives (credits, discounts),
so the appropriate refinement is
.finite()only — not a non-zerogate.
Fix
.finite()toinjbAmountvia a sharedinjbAmountFieldvalidator (matches
cpayAmountFieldpattern from feat(customerpayment): reject zero + non-finite cpayAmount at the schema layer #172).injbAmount: 'Infinity'→ 400 (string coerced)injbAmount: '-Infinity'→ 400injbAmount: 0→ not 400 (still valid)injbAmount: -50→ not 400 (still valid)Acceptance
Infinity/-Infinity(post coerce)Proudly Made in Nebraska. Go Big Red! 🌽 https://xkcd.com/2347/