Problem
app/schemas/billingtype.schema.js declares:
btHourlyRate: z.coerce.number().nonnegative()
.nonnegative() correctly blocks negative rates (a -$50/hr rate is operator error), but it does NOT block Infinity — Infinity >= 0 is true. The coerce path also turns the string "Infinity" into the float, so even JSON (no Infinity literal) can land inf in the underlying DOUBLE column.
An inf rate silently contaminates every downstream total: invoice line totals, time-entry rate math, billing reports.
Same class of bug as #171 (cpayAmount), #179 (injbAmount), #193 (polQty/polPrice).
Fix
Chain .finite() ahead of .nonnegative() in a shared btHourlyRateField validator. Zero remains valid (pro-bono / internal-only billing).
Proudly Made in Nebraska. Go Big Red! 🌽 https://xkcd.com/2347/
Problem
app/schemas/billingtype.schema.jsdeclares:.nonnegative()correctly blocks negative rates (a -$50/hr rate is operator error), but it does NOT blockInfinity—Infinity >= 0istrue. The coerce path also turns the string"Infinity"into the float, so even JSON (no Infinity literal) can landinfin the underlying DOUBLE column.An
infrate silently contaminates every downstream total: invoice line totals, time-entry rate math, billing reports.Same class of bug as #171 (cpayAmount), #179 (injbAmount), #193 (polQty/polPrice).
Fix
Chain
.finite()ahead of.nonnegative()in a sharedbtHourlyRateFieldvalidator. Zero remains valid (pro-bono / internal-only billing).Proudly Made in Nebraska. Go Big Red! 🌽 https://xkcd.com/2347/