fix(projects): reject inverted task start/due dates (PMS-398) #304

Merged
vas2000-work merged 1 commit from fix/PMS-398-task-date-ordering into main 2026-06-18 03:01:43 +02:00
Owner

Enforces start_date <= due_date on task create and update (PMS-398), closing the gap that let an inverted negative-duration range persist. Adds a shared task_dates_ok predicate (equality allowed for single-day tasks; None on either side is unbounded) mirroring the PMS-343 appointment fix and PMS-306 contract check. Both request DTOs gain a #[validate(schema(...))] cross-field validator that re-keys the error onto due_date via cross_field_error so the SPA shows it inline (PMS-364), and update_task additionally combines requested dates with the stored row before the UPDATE so a one-sided partial PUT cannot invert the range against stored values. Covered by 6 unit tests; fmt/clippy/tests all green in the dev container.

#PMS-398

Enforces `start_date <= due_date` on task create and update (PMS-398), closing the gap that let an inverted negative-duration range persist. Adds a shared `task_dates_ok` predicate (equality allowed for single-day tasks; `None` on either side is unbounded) mirroring the PMS-343 appointment fix and PMS-306 contract check. Both request DTOs gain a `#[validate(schema(...))]` cross-field validator that re-keys the error onto `due_date` via `cross_field_error` so the SPA shows it inline (PMS-364), and `update_task` additionally combines requested dates with the stored row before the UPDATE so a one-sided partial PUT cannot invert the range against stored values. Covered by 6 unit tests; fmt/clippy/tests all green in the dev container. #PMS-398
fix(projects): reject inverted task start/due dates (PMS-398)
All checks were successful
E2E / Playwright against staging (pull_request) Successful in 34s
Check / fmt + clippy + compile + unit/doc tests (pull_request) Successful in 1m4s
Integration / integration tests (pull_request) Successful in 3m48s
Create release / Create release from merged PR (pull_request) Has been skipped
7828b275c2
Tasks carried optional start_date/due_date on CreateTaskRequest and UpdateTaskRequest with no ordering check, so an inverted (negative-duration) range could be persisted. This adds a shared task_dates_ok(start, due) predicate (true when either side is None or start <= due, so a single-day task where start == due is valid and only start > due is rejected), mirroring appointment_range_ok (PMS-343) and the contract date range check (PMS-306).

Both request DTOs now carry a #[validate(schema(function = ...))] cross-field validator that returns cross_field_error("task_dates_inverted", "due_date", ...) so the SPA renders the message inline against due_date rather than as a generic banner (PMS-364).

The update_task service path additionally combines each requested date with the stored row from the to_jsonb before snapshot (request wins, else stored) and rejects an inverted effective range with AppError::BadRequest before the UPDATE runs, so a one-sided partial PUT cannot invert the range against the stored values.

Unit tests cover task_dates_ok for both-None, only-start, only-due, equal, valid-ordered, and inverted cases.

#PMS-398

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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!304
No description provided.