feat(time-tracking): rounding, rate derivation, timesheet approval, tenant scoping #58
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/time-tracking-resolve-rounding"
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
Composes the existing tickets and time_tracking surfaces into a demoable Milestone 1 Service Desk slice: a technician opens a ticket, times work, and submits a timesheet a manager approves. Every Service Desk response is now priced and tenant-scoped, no 501s on the slice.
The starting point was further along than the dev-docs snapshot claimed: F3 (ticket DTO joins) and the F8 time_tracking module already existed. This PR fills the real gaps.
Changes
apply_rounding(floor tominimum_minutes, then round toincrement_minutes; exact midpoint rounds up) runs oncreate_time_entryandstop_timerusing the tenant default rule. Billing-critical order documented on the fn.resolve_billingprecedence: explicit request rate >work_type.default_rate> none.stop_timernow prices the entry instead of inserting NULL rate/total with a hardcodedis_billable = TRUE.POST /timesheets/:user/:week/{approve,reject}(manager+), week-levelapproval_statusrollup onTimesheetSummaryResponse, empty/already-approved week returns a zeroed summary instead of 404. No realsubmittedstate yet (documented as debt).create_time_entry/start_timervalidate work-type/ticket/company ownership;stop_timercompany inference is tenant-scoped; single-active-timer race maps toConflict.time_tracking/mod.rsmoved to the gated convention so the client carries a byte-identical copy (companion PR in mokosh-apps).Testing
tests/time_tracking.rs: two-actor happy path (technician times + submits, manager approves) + technician-cannot-approve guard, against real Postgres.apply_roundingandresolve_billing.--all-targets -D warnings) clean.scripts/demo-service-desk.nuadded as the 2026-06-05 rehearsal walkthrough.🤖 Generated with Claude Code
Composes the existing tickets and time_tracking surfaces into a demoable Milestone 1 Service Desk slice: a technician opens a ticket, times work, and submits a timesheet a manager approves, with every response priced and tenant-scoped. Rounding is now applied. A new `apply_rounding` pure fn floors a raw duration to the rule's `minimum_minutes` then rounds to `increment_minutes` (exact midpoint rounds up); it runs on both `create_time_entry` and `stop_timer` using the tenant default rule. The ordering is billing-critical and documented on the function so changing it after entries are billed is an explicit decision. Billable rate is derived from the work type. `resolve_billing` applies precedence explicit-request-rate > `work_type.default_rate` > none, and `stop_timer` now prices the entry from the work type instead of inserting a NULL rate/total with a hardcoded `is_billable = TRUE`. Adds a timesheet approval state machine: `POST /timesheets/:user/:week/{approve,reject}` (manager+ via RequireManager) transition `approval_status`, and `TimesheetSummaryResponse` gains a week-level `approval_status` rollup so the client can render a submitted week as "awaiting approval". There is no real `submitted` state (the schema enum is pending|approved|rejected); that is documented as debt on the DTO. Submit on an empty or already-approved week now returns a zeroed summary instead of 404, consistent with approve/reject. Closes write-side tenant leaks: FKs check existence not ownership, so `create_time_entry` and `start_timer` now verify the work type, ticket, and company belong to the caller's tenant; `stop_timer`'s company-inference query is tenant-scoped; and the single-active-timer TOCTOU race maps to a clean Conflict instead of a raw 500. Converts `time_tracking/mod.rs` to the gated shared-module convention so the WASM client can carry a byte-identical copy and compile only the model types (the matching client port lands in mokosh-apps). Tests: `tests/time_tracking.rs` drives the two-actor happy path (technician times and submits, manager approves) plus a technician-cannot-approve guard against a real Postgres; `apply_rounding` and `resolve_billing` get pure-fn unit tests. Adds `scripts/demo-service-desk.nu` as the rehearsal walkthrough and refreshes the stale F3/F8/time_tracking entries in the dev-docs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>