feat(time-tracking): finish PMS-42 story (entries, timesheets, timers, rounding, work types) #26

Closed
David wants to merge 0 commits from feat/time-tracking-story-pms-42 into main
Owner

Implements YouTrack story PMS-42 (time tracking). Targets main directly.

Sub-tasks

  • PMS-43 scaffold: src/modules/time_tracking/{mod.rs, models.rs, service.rs, routes.rs} wired via merge, replaces the /time-entries and /timesheets stubs.
  • PMS-44 GET / POST /api/v1/time-entries (F8): filter by user / ticket / project / date range, paginated.
  • PMS-45 GET / PUT / DELETE /api/v1/time-entries/:id with auto-recomputed duration + total.
  • PMS-46 GET /api/v1/timesheets: aggregates by (user_id, week) using Postgres DATE_TRUNC('week', ...).
  • PMS-47 POST /api/v1/timesheets/{user_id}/{week_start}/submit: state transition over the week's entries.
  • PMS-48 active timers (/timers/active, /timers/start, /timers/{id}/stop). UNIQUE(user_id) enforced; stop creates the corresponding time_entries row in one transaction.
  • PMS-49 time rounding rules CRUD.
  • PMS-50 work types CRUD (admin writes, any-user reads).

The bottom seven sub-tasks ship in one commit because they share service.rs / routes.rs and splitting per-task would have been arbitrary diff churn; commit body lists every PMS- id with State Done so YouTrack closes them all when this PR merges.

Test plan

  • cargo check --bin mokosh-server clean (verified locally).
  • Smoke: POST /api/v1/time-entries {"user_id": "...", "date": "2026-05-17", "start_time": "09:00", "end_time": "10:30", "work_type_id": "...", "company_id": "..."} returns 90 minutes.
  • Smoke: GET /api/v1/timesheets?week=2026-05-12 rolls up the entry.
  • Smoke: POST /api/v1/timers/start {...} and POST /api/v1/timers/{id}/stop produce a time_entries row covering the elapsed window.

Closes #PMS-42

Implements YouTrack story PMS-42 (time tracking). Targets `main` directly. ## Sub-tasks - PMS-43 scaffold: `src/modules/time_tracking/{mod.rs, models.rs, service.rs, routes.rs}` wired via `merge`, replaces the `/time-entries` and `/timesheets` stubs. - PMS-44 GET / POST `/api/v1/time-entries` (F8): filter by user / ticket / project / date range, paginated. - PMS-45 GET / PUT / DELETE `/api/v1/time-entries/:id` with auto-recomputed duration + total. - PMS-46 GET `/api/v1/timesheets`: aggregates by `(user_id, week)` using Postgres `DATE_TRUNC('week', ...)`. - PMS-47 POST `/api/v1/timesheets/{user_id}/{week_start}/submit`: state transition over the week's entries. - PMS-48 active timers (`/timers/active`, `/timers/start`, `/timers/{id}/stop`). UNIQUE(user_id) enforced; stop creates the corresponding `time_entries` row in one transaction. - PMS-49 time rounding rules CRUD. - PMS-50 work types CRUD (admin writes, any-user reads). The bottom seven sub-tasks ship in one commit because they share `service.rs` / `routes.rs` and splitting per-task would have been arbitrary diff churn; commit body lists every PMS- id with `State Done` so YouTrack closes them all when this PR merges. ## Test plan - [ ] `cargo check --bin mokosh-server` clean (verified locally). - [ ] Smoke: `POST /api/v1/time-entries {"user_id": "...", "date": "2026-05-17", "start_time": "09:00", "end_time": "10:30", "work_type_id": "...", "company_id": "..."}` returns 90 minutes. - [ ] Smoke: `GET /api/v1/timesheets?week=2026-05-12` rolls up the entry. - [ ] Smoke: `POST /api/v1/timers/start {...}` and `POST /api/v1/timers/{id}/stop` produce a time_entries row covering the elapsed window. Closes #PMS-42
`src/modules/time_tracking/{mod.rs, models.rs, service.rs, routes.rs}` with full DTO coverage (work types, time entries, timesheet summaries, active timers, rounding rules). `time_tracking_routes` is wired via `merge` and replaces the `/time-entries` and `/timesheets` stubs. Router is empty in this commit; subsequent commits add endpoints.

#PMS-43 State Done
feat(time-tracking): implement work types, time entries, timesheets, timers, rounding rules
All checks were successful
Create release / Create release from merged PR (pull_request) Has been skipped
adaa594b2f
All six remaining sub-tasks in the PMS-42 story shipped in one batch because they share the same `service.rs` and `routes.rs` files; splitting per-task would have been arbitrary diff churn.

- PMS-44 GET / POST `/api/v1/time-entries` with filter (user / ticket / project / date range) + pagination. Compute `duration_minutes` from start/end or accept it directly. Non-admins can only log time for themselves.
- PMS-45 GET / PUT / DELETE `/api/v1/time-entries/:id`. Recompute `duration_minutes` and `total_amount` (rate * minutes / 60) on start/end/rate changes.
- PMS-46 GET `/api/v1/timesheets` aggregates `time_entries` by `(user_id, DATE_TRUNC('week', date))`. Filter by user / week.
- PMS-47 POST `/api/v1/timesheets/{user_id}/{week_start}/submit` flips every non-approved entry in the (user_id, week) window to `approval_status = pending` (the conventional "submitted, awaiting approval" state). Non-admins can only submit their own.
- PMS-48 active timers: GET `/timers/active`, POST `/timers/start`, POST `/timers/{id}/stop`. UNIQUE(user_id) on `active_timers` is honoured; start rejects if a timer is already running. Stop atomically removes the timer row and creates a `time_entries` row with the elapsed window; falls back to the tenant's first active work type / inferred company when the timer didn't carry one.
- PMS-49 time rounding rules CRUD (`/time-rounding-rules`). Setting `is_default = true` demotes the prior default in the same transaction. `rounding_method` validated against `up | down | nearest`.
- PMS-50 work types CRUD (`/work-types`). Admin-only writes; reads available to any authenticated user.

#PMS-44 State Done
#PMS-45 State Done
#PMS-46 State Done
#PMS-47 State Done
#PMS-48 State Done
#PMS-49 State Done
#PMS-50 State Done
vas2000-work closed this pull request 2026-05-21 02:42:55 +02:00
Some checks are pending
Create release / Create release from merged PR (pull_request) Has been skipped
Check / * (pull_request)
Required
E2E / * (pull_request)
Required

Pull request closed

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!26
No description provided.