fix(validation): range/scale-guard money and hours numeric fields (PMS-383) #294

Merged
David merged 2 commits from fix/PMS-383-numeric-money-hours-validation into main 2026-06-17 12:45:49 +02:00
Owner

Six create/update endpoints bound a Decimal/f64 money or hours field straight to a DECIMAL/NUMERIC column with no request-layer guard, so an oversized value overflowed the column and surfaced as a raw 500 (numeric-field-overflow DATABASE_ERROR) instead of a clean 422, and excess scale was silently rounded.

Add a shared mokosh_types::validation module (sibling to the server crate's validate_money_amount / validate_budget_*, but living in mokosh-types so both mokosh-server and the mokosh-apps client share one definition) with validate_rate (DECIMAL(10,2)), validate_hours (DECIMAL(10,2)), validate_rate_per_mile (NUMERIC(8,4)), and validate_distance_miles (NUMERIC(8,2), strictly positive). Each rejects negatives (distance: non-positive), more decimal places than the column holds, and magnitudes that would overflow.

Apply via #[validate(custom(...))] on the affected fields: default_rate (work types), hourly_rate (time entries create/update), distance_miles + rate_per_mile (mileage create/update), and hourly_rate + estimated_hours (projects/tasks create/update). The ticket estimated_hours is Option<f64>, so it uses a range(min = 0.0, max = 99_999_999.99) bound mirroring the existing sla_multiplier pattern; that also rejects NaN/Infinity, which fail the comparison.

#PMS-383

Six create/update endpoints bound a Decimal/f64 money or hours field straight to a DECIMAL/NUMERIC column with no request-layer guard, so an oversized value overflowed the column and surfaced as a raw 500 (numeric-field-overflow DATABASE_ERROR) instead of a clean 422, and excess scale was silently rounded. Add a shared `mokosh_types::validation` module (sibling to the server crate's `validate_money_amount` / `validate_budget_*`, but living in `mokosh-types` so both `mokosh-server` and the `mokosh-apps` client share one definition) with `validate_rate` (DECIMAL(10,2)), `validate_hours` (DECIMAL(10,2)), `validate_rate_per_mile` (NUMERIC(8,4)), and `validate_distance_miles` (NUMERIC(8,2), strictly positive). Each rejects negatives (distance: non-positive), more decimal places than the column holds, and magnitudes that would overflow. Apply via `#[validate(custom(...))]` on the affected fields: `default_rate` (work types), `hourly_rate` (time entries create/update), `distance_miles` + `rate_per_mile` (mileage create/update), and `hourly_rate` + `estimated_hours` (projects/tasks create/update). The ticket `estimated_hours` is `Option<f64>`, so it uses a `range(min = 0.0, max = 99_999_999.99)` bound mirroring the existing `sla_multiplier` pattern; that also rejects `NaN`/`Infinity`, which fail the comparison. #PMS-383
fix(validation): range/scale-guard money and hours numeric fields (PMS-383)
All checks were successful
E2E / Playwright against staging (pull_request) Successful in 58s
Check / fmt + clippy + compile + unit/doc tests (pull_request) Successful in 2m35s
Integration / integration tests (pull_request) Successful in 7m40s
6eef45ec35
Six create/update endpoints bound a Decimal/f64 money or hours field straight to a DECIMAL/NUMERIC column with no request-layer guard, so an oversized value overflowed the column and surfaced as a raw 500 (numeric-field-overflow DATABASE_ERROR) instead of a clean 422, and excess scale was silently rounded.

Add a shared `mokosh_types::validation` module (sibling to the server crate's `validate_money_amount` / `validate_budget_*`, but living in `mokosh-types` so both `mokosh-server` and the `mokosh-apps` client share one definition) with `validate_rate` (DECIMAL(10,2)), `validate_hours` (DECIMAL(10,2)), `validate_rate_per_mile` (NUMERIC(8,4)), and `validate_distance_miles` (NUMERIC(8,2), strictly positive). Each rejects negatives (distance: non-positive), more decimal places than the column holds, and magnitudes that would overflow.

Apply via `#[validate(custom(...))]` on the affected fields: `default_rate` (work types), `hourly_rate` (time entries create/update), `distance_miles` + `rate_per_mile` (mileage create/update), and `hourly_rate` + `estimated_hours` (projects/tasks create/update). The ticket `estimated_hours` is `Option<f64>`, so it uses a `range(min = 0.0, max = 99_999_999.99)` bound mirroring the existing `sla_multiplier` pattern; that also rejects `NaN`/`Infinity`, which fail the comparison.

#PMS-383
Merge branch 'main' into fix/PMS-383-numeric-money-hours-validation
Some checks failed
Create release / Create release from merged PR (pull_request) Has been skipped
Integration / integration tests (pull_request) Failing after 48s
E2E / Playwright against staging (pull_request) Successful in 58s
Check / fmt + clippy + compile + unit/doc tests (pull_request) Successful in 3m44s
80a0333aa9
David merged commit a5b45bac9d into main 2026-06-17 12:45:49 +02:00
David deleted branch fix/PMS-383-numeric-money-hours-validation 2026-06-17 12:45:49 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/mokosh-server!294
No description provided.