feat(projects): budget-vs-actual + time rollup + tests (PMS-51) #126
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/pms-51-project-actuals-and-tests"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What
Completes the Projects and tasks story (PMS-51). All CRUD (projects, phases, per-tenant task statuses, tasks, dependencies with cycle detection) was already wired and returning real data - the story's "routes return 501" note was stale. This PR closes the three remaining story-level gaps: budget-vs-actual (AC1), the time-entry rollup (AC5), and integration tests (AC6). It also fixes a latent 500 in filtered project listing.
Closes PMS-51.
Budget vs actual, computed on read (AC1)
ProjectResponsegainsactual_hoursandactual_amount, each a correlatedSUMovertime_entrieswithapproval_status = 'approved'scoped to the project. Computed on read, so there is no denormalized counter to drift and no coupling to the approval path. Pair withbudget_hours/budget_amountfor budget-vs-actual.Time -> actual_hours rollup (AC5)
task_idadded toCreateTimeEntryRequest/UpdateTimeEntryRequest(thetime_entries.task_idcolumn already existed) and bound in create/update, so time can link to a task.TaskResponse.actual_hoursis derived from approved time linked to the task. Since it is now derived, the manualactual_hourssetter was removed fromUpdateTaskRequest/update_taskso the field has a single source of truth.Bug fix: filtered
list_projects500list_projectsshared one WHERE clause between the data query (filters numbered from$4, after limit/offset) and the count query (no limit/offset, so filters must start at$2), so a filtered list bound 3 params against a statement requiring 5 -> 500. Split into parallel data/count clauses with independent numbering - the same fix PMS-145 applied to the billing lists. The path had no test before, which is why it went unnoticed.Tests -
tests/projects.rs409, a self-dependency is400.actual_hours2.0 and the project shows 2.0 hours / $100 amount.401unauthenticated and never501.Verification
cargo test --test projects-> 3 passed;cargo test --lib-> 110 passed;cargo test --test time_trackingand--test billingstill pass (the time-tracking service was touched).cargo fmt --checkclean;cargo clippy --all-targets -- -D warningsclean.Known limitation
tasks.checklist(JSONB) is in the schema but not surfaced inTaskResponse/ the task requests; AC3's checklist is therefore not exercised. Left for a follow-up since no create/update path exposes it today.🤖 Generated with Claude Code