@open-mercato/core 0.7.1-develop.7178.1.ab93fbaf4e → 0.7.1-develop.7180.1.9717fbbb43
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +2 -2
- package/dist/generated/entities/staff_time_entry/index.js +14 -0
- package/dist/generated/entities/staff_time_entry/index.js.map +2 -2
- package/dist/generated/entities/staff_time_entry_tag/index.js +15 -0
- package/dist/generated/entities/staff_time_entry_tag/index.js.map +7 -0
- package/dist/generated/entities/staff_time_project/index.js +16 -0
- package/dist/generated/entities/staff_time_project/index.js.map +2 -2
- package/dist/generated/entities/staff_time_report/index.js +57 -0
- package/dist/generated/entities/staff_time_report/index.js.map +7 -0
- package/dist/generated/entities/staff_time_report_entry/index.js +27 -0
- package/dist/generated/entities/staff_time_report_entry/index.js.map +7 -0
- package/dist/generated/entities/staff_time_report_event/index.js +21 -0
- package/dist/generated/entities/staff_time_report_event/index.js.map +7 -0
- package/dist/generated/entities/staff_time_report_project/index.js +15 -0
- package/dist/generated/entities/staff_time_report_project/index.js.map +7 -0
- package/dist/generated/entities/staff_time_tag/index.js +21 -0
- package/dist/generated/entities/staff_time_tag/index.js.map +7 -0
- package/dist/generated/entities/staff_time_task/index.js +37 -0
- package/dist/generated/entities/staff_time_task/index.js.map +7 -0
- package/dist/generated/entities/staff_time_task_comment/index.js +21 -0
- package/dist/generated/entities/staff_time_task_comment/index.js.map +7 -0
- package/dist/generated/entities/staff_time_task_status/index.js +29 -0
- package/dist/generated/entities/staff_time_task_status/index.js.map +7 -0
- package/dist/generated/entities/staff_time_task_tag/index.js +15 -0
- package/dist/generated/entities/staff_time_task_tag/index.js.map +7 -0
- package/dist/generated/entities.ids.generated.js +11 -1
- package/dist/generated/entities.ids.generated.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +149 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/helpers/integration/timesheetFixtures.js +7 -0
- package/dist/helpers/integration/timesheetFixtures.js.map +2 -2
- package/dist/modules/auth/cli.js +16 -0
- package/dist/modules/auth/cli.js.map +2 -2
- package/dist/modules/auth/lib/backendChrome.js +1 -0
- package/dist/modules/auth/lib/backendChrome.js.map +2 -2
- package/dist/modules/customer_accounts/lib/customerRoleAcls.js +44 -0
- package/dist/modules/customer_accounts/lib/customerRoleAcls.js.map +7 -0
- package/dist/modules/customer_accounts/setup.js +1 -28
- package/dist/modules/customer_accounts/setup.js.map +2 -2
- package/dist/modules/data_sync/api/options.js +2 -0
- package/dist/modules/data_sync/api/options.js.map +2 -2
- package/dist/modules/data_sync/backend/data-sync/page.js +39 -17
- package/dist/modules/data_sync/backend/data-sync/page.js.map +2 -2
- package/dist/modules/data_sync/lib/start-controls.js +42 -0
- package/dist/modules/data_sync/lib/start-controls.js.map +7 -0
- package/dist/modules/staff/acl.js +9 -1
- package/dist/modules/staff/acl.js.map +2 -2
- package/dist/modules/staff/ai-agents.js +142 -0
- package/dist/modules/staff/ai-agents.js.map +7 -0
- package/dist/modules/staff/ai-tools/time-tracking-pack.js +348 -0
- package/dist/modules/staff/ai-tools/time-tracking-pack.js.map +7 -0
- package/dist/modules/staff/ai-tools/types.js +54 -0
- package/dist/modules/staff/ai-tools/types.js.map +7 -0
- package/dist/modules/staff/ai-tools.js +8 -0
- package/dist/modules/staff/ai-tools.js.map +7 -0
- package/dist/modules/staff/analytics.js +84 -1
- package/dist/modules/staff/analytics.js.map +2 -2
- package/dist/modules/staff/api/guards.js +37 -3
- package/dist/modules/staff/api/guards.js.map +2 -2
- package/dist/modules/staff/api/leave-requests/accept/route.js +1 -3
- package/dist/modules/staff/api/leave-requests/accept/route.js.map +2 -2
- package/dist/modules/staff/api/leave-requests/reject/route.js +1 -3
- package/dist/modules/staff/api/leave-requests/reject/route.js.map +2 -2
- package/dist/modules/staff/api/portal/time-reports/[id]/route.js +131 -0
- package/dist/modules/staff/api/portal/time-reports/[id]/route.js.map +7 -0
- package/dist/modules/staff/api/portal/time-reports/route.js +179 -0
- package/dist/modules/staff/api/portal/time-reports/route.js.map +7 -0
- package/dist/modules/staff/api/team-members/self/route.js +1 -3
- package/dist/modules/staff/api/team-members/self/route.js.map +2 -2
- package/dist/modules/staff/api/team-members/tags/assign/route.js +1 -3
- package/dist/modules/staff/api/team-members/tags/assign/route.js.map +2 -2
- package/dist/modules/staff/api/team-members/tags/unassign/route.js +1 -3
- package/dist/modules/staff/api/team-members/tags/unassign/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/_shared/withTimesheetInterceptors.js +174 -0
- package/dist/modules/staff/api/timesheets/_shared/withTimesheetInterceptors.js.map +7 -0
- package/dist/modules/staff/api/timesheets/access-requests/route.js +226 -0
- package/dist/modules/staff/api/timesheets/access-requests/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/my-projects/[projectId]/route.js +15 -8
- package/dist/modules/staff/api/timesheets/my-projects/[projectId]/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/my-projects/route.js +13 -1
- package/dist/modules/staff/api/timesheets/my-projects/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/my-work/myWorkAggregate.js +42 -0
- package/dist/modules/staff/api/timesheets/my-work/myWorkAggregate.js.map +7 -0
- package/dist/modules/staff/api/timesheets/my-work/route.js +383 -0
- package/dist/modules/staff/api/timesheets/my-work/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/projects/kpis/route.js +14 -2
- package/dist/modules/staff/api/timesheets/projects/kpis/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/reports/[id]/close/route.js +168 -0
- package/dist/modules/staff/api/timesheets/reports/[id]/close/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/reports/[id]/export/route.js +246 -0
- package/dist/modules/staff/api/timesheets/reports/[id]/export/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/reports/[id]/sheet/route.js +183 -0
- package/dist/modules/staff/api/timesheets/reports/[id]/sheet/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/reports/[id]/unlock/route.js +164 -0
- package/dist/modules/staff/api/timesheets/reports/[id]/unlock/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/reports/preview/route.js +360 -0
- package/dist/modules/staff/api/timesheets/reports/preview/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/reports/route.js +316 -0
- package/dist/modules/staff/api/timesheets/reports/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/reports/shared.js +63 -0
- package/dist/modules/staff/api/timesheets/reports/shared.js.map +7 -0
- package/dist/modules/staff/api/timesheets/settings/reapply-rounding/route.js +164 -0
- package/dist/modules/staff/api/timesheets/settings/reapply-rounding/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/settings/rounding-impact/route.js +168 -0
- package/dist/modules/staff/api/timesheets/settings/rounding-impact/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/settings/route.js +199 -0
- package/dist/modules/staff/api/timesheets/settings/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/tags/access.js +76 -0
- package/dist/modules/staff/api/timesheets/tags/access.js.map +7 -0
- package/dist/modules/staff/api/timesheets/tags/entry-assignments/route.js +235 -0
- package/dist/modules/staff/api/timesheets/tags/entry-assignments/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/tags/route.js +160 -0
- package/dist/modules/staff/api/timesheets/tags/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/tags/task-assignments/route.js +233 -0
- package/dist/modules/staff/api/timesheets/tags/task-assignments/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/task-statuses/route.js +188 -0
- package/dist/modules/staff/api/timesheets/task-statuses/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/tasks/[id]/comments/route.js +399 -0
- package/dist/modules/staff/api/timesheets/tasks/[id]/comments/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/tasks/[id]/status/route.js +210 -0
- package/dist/modules/staff/api/timesheets/tasks/[id]/status/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/tasks/route.js +333 -0
- package/dist/modules/staff/api/timesheets/tasks/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/time-entries/[id]/duplicate/route.js +228 -0
- package/dist/modules/staff/api/timesheets/time-entries/[id]/duplicate/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/time-entries/[id]/segments/[segmentId]/route.js +29 -10
- package/dist/modules/staff/api/timesheets/time-entries/[id]/segments/[segmentId]/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/time-entries/[id]/segments/route.js +31 -18
- package/dist/modules/staff/api/timesheets/time-entries/[id]/segments/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.js +21 -7
- package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-stop/route.js +27 -7
- package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-stop/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/time-entries/bulk/route.js +215 -32
- package/dist/modules/staff/api/timesheets/time-entries/bulk/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/time-entries/copy-day/route.js +378 -0
- package/dist/modules/staff/api/timesheets/time-entries/copy-day/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/time-entries/overlaps/route.js +340 -0
- package/dist/modules/staff/api/timesheets/time-entries/overlaps/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/time-entries/route.js +238 -26
- package/dist/modules/staff/api/timesheets/time-entries/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/time-entries/start-timer/route.js +23 -8
- package/dist/modules/staff/api/timesheets/time-entries/start-timer/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/time-projects/[id]/change-currency/route.js +209 -0
- package/dist/modules/staff/api/timesheets/time-projects/[id]/change-currency/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/time-projects/[id]/employees/route.js +59 -2
- package/dist/modules/staff/api/timesheets/time-projects/[id]/employees/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/time-projects/route.js +264 -22
- package/dist/modules/staff/api/timesheets/time-projects/route.js.map +2 -2
- package/dist/modules/staff/backend/staff/time-tracking/board/page.js +110 -0
- package/dist/modules/staff/backend/staff/time-tracking/board/page.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/board/page.meta.js +18 -0
- package/dist/modules/staff/backend/staff/time-tracking/board/page.meta.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/entries/page.js +981 -0
- package/dist/modules/staff/backend/staff/time-tracking/entries/page.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/entries/page.meta.js +18 -0
- package/dist/modules/staff/backend/staff/time-tracking/entries/page.meta.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/page.js +583 -0
- package/dist/modules/staff/backend/staff/time-tracking/page.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/page.meta.js +15 -0
- package/dist/modules/staff/backend/staff/time-tracking/page.meta.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/board/page.js +65 -0
- package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/board/page.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/board/page.meta.js +16 -0
- package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/board/page.meta.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/edit/page.js +229 -0
- package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/edit/page.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/edit/page.meta.js +16 -0
- package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/edit/page.meta.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/page.js +1014 -0
- package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/page.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/page.meta.js +16 -0
- package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/page.meta.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/projects/create/page.js +97 -0
- package/dist/modules/staff/backend/staff/time-tracking/projects/create/page.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/projects/create/page.meta.js +16 -0
- package/dist/modules/staff/backend/staff/time-tracking/projects/create/page.meta.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/projects/page.js +1040 -0
- package/dist/modules/staff/backend/staff/time-tracking/projects/page.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/projects/page.meta.js +19 -0
- package/dist/modules/staff/backend/staff/time-tracking/projects/page.meta.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/projects/projectFormConfig.js +353 -0
- package/dist/modules/staff/backend/staff/time-tracking/projects/projectFormConfig.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/reports/[id]/page.js +457 -0
- package/dist/modules/staff/backend/staff/time-tracking/reports/[id]/page.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/reports/[id]/page.meta.js +16 -0
- package/dist/modules/staff/backend/staff/time-tracking/reports/[id]/page.meta.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/reports/create/page.js +697 -0
- package/dist/modules/staff/backend/staff/time-tracking/reports/create/page.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/reports/create/page.meta.js +16 -0
- package/dist/modules/staff/backend/staff/time-tracking/reports/create/page.meta.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/reports/page.js +174 -0
- package/dist/modules/staff/backend/staff/time-tracking/reports/page.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/reports/page.meta.js +18 -0
- package/dist/modules/staff/backend/staff/time-tracking/reports/page.meta.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/settings/page.js +546 -0
- package/dist/modules/staff/backend/staff/time-tracking/settings/page.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/settings/page.meta.js +18 -0
- package/dist/modules/staff/backend/staff/time-tracking/settings/page.meta.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/timesheet/GridView.js +710 -0
- package/dist/modules/staff/backend/staff/time-tracking/timesheet/GridView.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/timesheet/page.js +646 -0
- package/dist/modules/staff/backend/staff/time-tracking/timesheet/page.js.map +7 -0
- package/dist/modules/staff/backend/staff/time-tracking/timesheet/page.meta.js +18 -0
- package/dist/modules/staff/backend/staff/time-tracking/timesheet/page.meta.js.map +7 -0
- package/dist/modules/staff/backend/staff/timesheets/page.js +4 -793
- package/dist/modules/staff/backend/staff/timesheets/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/timesheets/page.meta.js +2 -7
- package/dist/modules/staff/backend/staff/timesheets/page.meta.js.map +2 -2
- package/dist/modules/staff/backend/staff/timesheets/projects/[id]/edit/page.js +5 -136
- package/dist/modules/staff/backend/staff/timesheets/projects/[id]/edit/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/timesheets/projects/[id]/edit/page.meta.js +1 -7
- package/dist/modules/staff/backend/staff/timesheets/projects/[id]/edit/page.meta.js.map +2 -2
- package/dist/modules/staff/backend/staff/timesheets/projects/[id]/page.js +5 -445
- package/dist/modules/staff/backend/staff/timesheets/projects/[id]/page.js.map +3 -3
- package/dist/modules/staff/backend/staff/timesheets/projects/[id]/page.meta.js +1 -7
- package/dist/modules/staff/backend/staff/timesheets/projects/[id]/page.meta.js.map +2 -2
- package/dist/modules/staff/backend/staff/timesheets/projects/create/page.js +4 -76
- package/dist/modules/staff/backend/staff/timesheets/projects/create/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/timesheets/projects/create/page.meta.js +1 -7
- package/dist/modules/staff/backend/staff/timesheets/projects/create/page.meta.js.map +2 -2
- package/dist/modules/staff/backend/staff/timesheets/projects/page.js +4 -613
- package/dist/modules/staff/backend/staff/timesheets/projects/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/timesheets/projects/page.meta.js +2 -17
- package/dist/modules/staff/backend/staff/timesheets/projects/page.meta.js.map +2 -2
- package/dist/modules/staff/ce.js +40 -0
- package/dist/modules/staff/ce.js.map +2 -2
- package/dist/modules/staff/cli.js +166 -1
- package/dist/modules/staff/cli.js.map +2 -2
- package/dist/modules/staff/commands/index.js +5 -0
- package/dist/modules/staff/commands/index.js.map +2 -2
- package/dist/modules/staff/commands/timesheets-entries.js +569 -21
- package/dist/modules/staff/commands/timesheets-entries.js.map +3 -3
- package/dist/modules/staff/commands/timesheets-projects.js +450 -7
- package/dist/modules/staff/commands/timesheets-projects.js.map +2 -2
- package/dist/modules/staff/commands/timesheets-reports.js +1036 -0
- package/dist/modules/staff/commands/timesheets-reports.js.map +7 -0
- package/dist/modules/staff/commands/timesheets-tags.js +1015 -0
- package/dist/modules/staff/commands/timesheets-tags.js.map +7 -0
- package/dist/modules/staff/commands/timesheets-task-comments.js +438 -0
- package/dist/modules/staff/commands/timesheets-task-comments.js.map +7 -0
- package/dist/modules/staff/commands/timesheets-task-statuses.js +723 -0
- package/dist/modules/staff/commands/timesheets-task-statuses.js.map +7 -0
- package/dist/modules/staff/commands/timesheets-tasks.js +834 -0
- package/dist/modules/staff/commands/timesheets-tasks.js.map +7 -0
- package/dist/modules/staff/data/enrichers.js +426 -12
- package/dist/modules/staff/data/enrichers.js.map +2 -2
- package/dist/modules/staff/data/entities.js +552 -3
- package/dist/modules/staff/data/entities.js.map +2 -2
- package/dist/modules/staff/data/extensions.js +41 -0
- package/dist/modules/staff/data/extensions.js.map +7 -0
- package/dist/modules/staff/data/validators.js +254 -9
- package/dist/modules/staff/data/validators.js.map +2 -2
- package/dist/modules/staff/di.js +42 -1
- package/dist/modules/staff/di.js.map +2 -2
- package/dist/modules/staff/events.js +84 -6
- package/dist/modules/staff/events.js.map +2 -2
- package/dist/modules/staff/events.payloads.js +158 -0
- package/dist/modules/staff/events.payloads.js.map +7 -0
- package/dist/modules/staff/extension-points.js +261 -0
- package/dist/modules/staff/extension-points.js.map +7 -0
- package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/[id]/page.js +138 -0
- package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/[id]/page.js.map +7 -0
- package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/[id]/page.meta.js +12 -0
- package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/[id]/page.meta.js.map +7 -0
- package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/page.js +133 -0
- package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/page.js.map +7 -0
- package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/page.meta.js +19 -0
- package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/page.meta.js.map +7 -0
- package/dist/modules/staff/lib/crud.js +11 -1
- package/dist/modules/staff/lib/crud.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking/access.js +92 -0
- package/dist/modules/staff/lib/time-tracking/access.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/billability.js +39 -0
- package/dist/modules/staff/lib/time-tracking/billability.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/capacity.js +68 -0
- package/dist/modules/staff/lib/time-tracking/capacity.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/componentContracts.js +15 -0
- package/dist/modules/staff/lib/time-tracking/componentContracts.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/cost.js +82 -0
- package/dist/modules/staff/lib/time-tracking/cost.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/duration.js +55 -0
- package/dist/modules/staff/lib/time-tracking/duration.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/featureAccess.js +41 -0
- package/dist/modules/staff/lib/time-tracking/featureAccess.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/interval.js +72 -0
- package/dist/modules/staff/lib/time-tracking/interval.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/migrateProjectCodes.js +88 -0
- package/dist/modules/staff/lib/time-tracking/migrateProjectCodes.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/moneyVisibility.js +10 -0
- package/dist/modules/staff/lib/time-tracking/moneyVisibility.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/overlap.js +87 -0
- package/dist/modules/staff/lib/time-tracking/overlap.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/portalRecipients.js +27 -0
- package/dist/modules/staff/lib/time-tracking/portalRecipients.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/portalReports.js +16 -0
- package/dist/modules/staff/lib/time-tracking/portalReports.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/projectCode.js +135 -0
- package/dist/modules/staff/lib/time-tracking/projectCode.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/reapplyRounding.js +187 -0
- package/dist/modules/staff/lib/time-tracking/reapplyRounding.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/recalculationRunner.js +80 -0
- package/dist/modules/staff/lib/time-tracking/recalculationRunner.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/recalculations.js +56 -0
- package/dist/modules/staff/lib/time-tracking/recalculations.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/registries/invoke.js +63 -0
- package/dist/modules/staff/lib/time-tracking/registries/invoke.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/registries/registry.js +62 -0
- package/dist/modules/staff/lib/time-tracking/registries/registry.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/registries/scope.js +21 -0
- package/dist/modules/staff/lib/time-tracking/registries/scope.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/rollup.js +34 -0
- package/dist/modules/staff/lib/time-tracking/rollup.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/rounding.js +66 -0
- package/dist/modules/staff/lib/time-tracking/rounding.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/roundingImpact.js +39 -0
- package/dist/modules/staff/lib/time-tracking/roundingImpact.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/settingKeys.js +214 -0
- package/dist/modules/staff/lib/time-tracking/settingKeys.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/settings.js +87 -0
- package/dist/modules/staff/lib/time-tracking/settings.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/sqlInClause.js +9 -0
- package/dist/modules/staff/lib/time-tracking/sqlInClause.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking/timeEntrySources.js +48 -0
- package/dist/modules/staff/lib/time-tracking/timeEntrySources.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/BoardFilterChips.js +283 -0
- package/dist/modules/staff/lib/time-tracking-ui/BoardFilterChips.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/CustomerPicker.js +324 -0
- package/dist/modules/staff/lib/time-tracking-ui/CustomerPicker.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/DurationInput.js +121 -0
- package/dist/modules/staff/lib/time-tracking-ui/DurationInput.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/EntryDetailsFields.js +188 -0
- package/dist/modules/staff/lib/time-tracking-ui/EntryDetailsFields.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/EntryFilterBar.js +89 -0
- package/dist/modules/staff/lib/time-tracking-ui/EntryFilterBar.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/KanbanBoard.js +855 -0
- package/dist/modules/staff/lib/time-tracking-ui/KanbanBoard.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/KanbanCard.js +360 -0
- package/dist/modules/staff/lib/time-tracking-ui/KanbanCard.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/KanbanColumn.js +259 -0
- package/dist/modules/staff/lib/time-tracking-ui/KanbanColumn.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/NewTaskDialog.js +238 -0
- package/dist/modules/staff/lib/time-tracking-ui/NewTaskDialog.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/NoProjectAccess.js +130 -0
- package/dist/modules/staff/lib/time-tracking-ui/NoProjectAccess.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/PeriodSelector.js +98 -0
- package/dist/modules/staff/lib/time-tracking-ui/PeriodSelector.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/PhasePlaceholder.js +27 -0
- package/dist/modules/staff/lib/time-tracking-ui/PhasePlaceholder.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/ProjectCodeField.js +166 -0
- package/dist/modules/staff/lib/time-tracking-ui/ProjectCodeField.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/ProjectCurrencyField.js +295 -0
- package/dist/modules/staff/lib/time-tracking-ui/ProjectCurrencyField.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/ProjectFormSections.js +212 -0
- package/dist/modules/staff/lib/time-tracking-ui/ProjectFormSections.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/ProjectTeamDrawer.js +729 -0
- package/dist/modules/staff/lib/time-tracking-ui/ProjectTeamDrawer.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/ReportSheet.js +217 -0
- package/dist/modules/staff/lib/time-tracking-ui/ReportSheet.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/TagPicker.js +158 -0
- package/dist/modules/staff/lib/time-tracking-ui/TagPicker.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/TaskBoardScreen.js +224 -0
- package/dist/modules/staff/lib/time-tracking-ui/TaskBoardScreen.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/TaskCommentThread.js +93 -0
- package/dist/modules/staff/lib/time-tracking-ui/TaskCommentThread.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/TaskDrawer.js +1196 -0
- package/dist/modules/staff/lib/time-tracking-ui/TaskDrawer.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/TaskListView.js +176 -0
- package/dist/modules/staff/lib/time-tracking-ui/TaskListView.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/TaskPicker.js +307 -0
- package/dist/modules/staff/lib/time-tracking-ui/TaskPicker.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/TaskQuickLog.js +284 -0
- package/dist/modules/staff/lib/time-tracking-ui/TaskQuickLog.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/TimeEntriesSummaryFooter.js +73 -0
- package/dist/modules/staff/lib/time-tracking-ui/TimeEntriesSummaryFooter.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/TimeEntryDialog.js +1499 -0
- package/dist/modules/staff/lib/time-tracking-ui/TimeEntryDialog.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/TimeEntryDurationCell.js +95 -0
- package/dist/modules/staff/lib/time-tracking-ui/TimeEntryDurationCell.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/TimesheetCalendar.js +188 -0
- package/dist/modules/staff/lib/time-tracking-ui/TimesheetCalendar.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/TimesheetPeriodFooter.js +61 -0
- package/dist/modules/staff/lib/time-tracking-ui/TimesheetPeriodFooter.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/TimesheetViewSwitch.js +45 -0
- package/dist/modules/staff/lib/time-tracking-ui/TimesheetViewSwitch.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/boardDirectory.js +176 -0
- package/dist/modules/staff/lib/time-tracking-ui/boardDirectory.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/boardFilters.js +82 -0
- package/dist/modules/staff/lib/time-tracking-ui/boardFilters.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/boardSummary.js +5 -0
- package/dist/modules/staff/lib/time-tracking-ui/boardSummary.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/kanbanBoardData.js +152 -0
- package/dist/modules/staff/lib/time-tracking-ui/kanbanBoardData.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/projectTeamAssignment.js +92 -0
- package/dist/modules/staff/lib/time-tracking-ui/projectTeamAssignment.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/taskDrawerData.js +111 -0
- package/dist/modules/staff/lib/time-tracking-ui/taskDrawerData.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/timeEntryDialogState.js +243 -0
- package/dist/modules/staff/lib/time-tracking-ui/timeEntryDialogState.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/timeEntryListData.js +151 -0
- package/dist/modules/staff/lib/time-tracking-ui/timeEntryListData.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/timeTrackingSettingsForm.js +105 -0
- package/dist/modules/staff/lib/time-tracking-ui/timeTrackingSettingsForm.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/timesheetData.js +132 -0
- package/dist/modules/staff/lib/time-tracking-ui/timesheetData.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/timesheetPeriod.js +205 -0
- package/dist/modules/staff/lib/time-tracking-ui/timesheetPeriod.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/timesheetTargets.js +53 -0
- package/dist/modules/staff/lib/time-tracking-ui/timesheetTargets.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/useBoardFilters.js +77 -0
- package/dist/modules/staff/lib/time-tracking-ui/useBoardFilters.js.map +7 -0
- package/dist/modules/staff/lib/time-tracking-ui/useTimesheetPreferences.js +151 -0
- package/dist/modules/staff/lib/time-tracking-ui/useTimesheetPreferences.js.map +7 -0
- package/dist/modules/staff/lib/timeTrackingSeeds.js +809 -0
- package/dist/modules/staff/lib/timeTrackingSeeds.js.map +7 -0
- package/dist/modules/staff/lib/timesheets/timeEntryCacheInvalidation.js +2 -3
- package/dist/modules/staff/lib/timesheets/timeEntryCacheInvalidation.js.map +2 -2
- package/dist/modules/staff/lib/timesheets/timeEntryDecoration.js +152 -0
- package/dist/modules/staff/lib/timesheets/timeEntryDecoration.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-projects/budgetBurn.js +36 -0
- package/dist/modules/staff/lib/timesheets-projects/budgetBurn.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-projects/budgetThreshold.js +66 -0
- package/dist/modules/staff/lib/timesheets-projects/budgetThreshold.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-projects/budgetThresholdState.js +78 -0
- package/dist/modules/staff/lib/timesheets-projects/budgetThresholdState.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-projects/computeProjectFinancials.js +81 -0
- package/dist/modules/staff/lib/timesheets-projects/computeProjectFinancials.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-projects/reportSelection.js +44 -0
- package/dist/modules/staff/lib/timesheets-projects/reportSelection.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-projects-ui/AssignMembersDialog.js +169 -0
- package/dist/modules/staff/lib/timesheets-projects-ui/AssignMembersDialog.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-projects-ui/ProjectBudgetCell.js +22 -0
- package/dist/modules/staff/lib/timesheets-projects-ui/ProjectBudgetCell.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-projects-ui/ProjectCard.js +51 -1
- package/dist/modules/staff/lib/timesheets-projects-ui/ProjectCard.js.map +2 -2
- package/dist/modules/staff/lib/timesheets-projects-ui/ProjectsBulkActionsBar.js +70 -0
- package/dist/modules/staff/lib/timesheets-projects-ui/ProjectsBulkActionsBar.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-reports/buildReportSheet.js +63 -0
- package/dist/modules/staff/lib/timesheets-reports/buildReportSheet.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-reports/loadReportData.js +170 -0
- package/dist/modules/staff/lib/timesheets-reports/loadReportData.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-reports/pdf.js +205 -0
- package/dist/modules/staff/lib/timesheets-reports/pdf.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-reports/reportApprovalPolicies.js +68 -0
- package/dist/modules/staff/lib/timesheets-reports/reportApprovalPolicies.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-reports/reportConfigData.js +135 -0
- package/dist/modules/staff/lib/timesheets-reports/reportConfigData.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-reports/reportConfigState.js +48 -0
- package/dist/modules/staff/lib/timesheets-reports/reportConfigState.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-reports/reportExport.js +262 -0
- package/dist/modules/staff/lib/timesheets-reports/reportExport.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-reports/reportExportFormats.js +34 -0
- package/dist/modules/staff/lib/timesheets-reports/reportExportFormats.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-reports/reportGroupings.js +79 -0
- package/dist/modules/staff/lib/timesheets-reports/reportGroupings.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-reports/reportLabels.js +11 -0
- package/dist/modules/staff/lib/timesheets-reports/reportLabels.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-reports/reportReference.js +83 -0
- package/dist/modules/staff/lib/timesheets-reports/reportReference.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-reports/reportRows.js +42 -0
- package/dist/modules/staff/lib/timesheets-reports/reportRows.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-reports/reportSheetData.js +107 -0
- package/dist/modules/staff/lib/timesheets-reports/reportSheetData.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-reports/reportTotals.js +289 -0
- package/dist/modules/staff/lib/timesheets-reports/reportTotals.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-reports/xlsx.js +145 -0
- package/dist/modules/staff/lib/timesheets-reports/xlsx.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-tasks/computeTaskRollups.js +116 -0
- package/dist/modules/staff/lib/timesheets-tasks/computeTaskRollups.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-tasks/defaultStatusTemplate.js +50 -0
- package/dist/modules/staff/lib/timesheets-tasks/defaultStatusTemplate.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-tasks/seedProjectStatuses.js +27 -0
- package/dist/modules/staff/lib/timesheets-tasks/seedProjectStatuses.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-tasks/statusPositions.js +65 -0
- package/dist/modules/staff/lib/timesheets-tasks/statusPositions.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-tasks/statusSlug.js +31 -0
- package/dist/modules/staff/lib/timesheets-tasks/statusSlug.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-tasks/taskHierarchy.js +31 -0
- package/dist/modules/staff/lib/timesheets-tasks/taskHierarchy.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-tasks/taskHoursTotals.js +17 -0
- package/dist/modules/staff/lib/timesheets-tasks/taskHoursTotals.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-tasks/taskReference.js +51 -0
- package/dist/modules/staff/lib/timesheets-tasks/taskReference.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-ui/CreateProjectDialog.js +11 -4
- package/dist/modules/staff/lib/timesheets-ui/CreateProjectDialog.js.map +2 -2
- package/dist/modules/staff/lib/timesheets-ui/ListView.js +369 -111
- package/dist/modules/staff/lib/timesheets-ui/ListView.js.map +2 -2
- package/dist/modules/staff/lib/timesheets-ui/TimerBar.js +47 -2
- package/dist/modules/staff/lib/timesheets-ui/TimerBar.js.map +2 -2
- package/dist/modules/staff/migrations/Migration20260813073131_staff.js +79 -0
- package/dist/modules/staff/migrations/Migration20260813073131_staff.js.map +7 -0
- package/dist/modules/staff/migrations/Migration20260824143357_staff.js +15 -0
- package/dist/modules/staff/migrations/Migration20260824143357_staff.js.map +7 -0
- package/dist/modules/staff/notifications.client.js +16 -0
- package/dist/modules/staff/notifications.client.js.map +7 -0
- package/dist/modules/staff/notifications.handlers.js +80 -0
- package/dist/modules/staff/notifications.handlers.js.map +7 -0
- package/dist/modules/staff/notifications.js +168 -0
- package/dist/modules/staff/notifications.js.map +2 -2
- package/dist/modules/staff/search.js +159 -1
- package/dist/modules/staff/search.js.map +2 -2
- package/dist/modules/staff/setup.js +20 -1
- package/dist/modules/staff/setup.js.map +2 -2
- package/dist/modules/staff/subscribers/time-project-access-requested-notification.js +68 -0
- package/dist/modules/staff/subscribers/time-project-access-requested-notification.js.map +7 -0
- package/dist/modules/staff/subscribers/time-project-budget-threshold-notification.js +149 -0
- package/dist/modules/staff/subscribers/time-project-budget-threshold-notification.js.map +7 -0
- package/dist/modules/staff/subscribers/time-report-approved-notification.js +78 -0
- package/dist/modules/staff/subscribers/time-report-approved-notification.js.map +7 -0
- package/dist/modules/staff/subscribers/time-report-portal-broadcast.js +78 -0
- package/dist/modules/staff/subscribers/time-report-portal-broadcast.js.map +7 -0
- package/dist/modules/staff/translations.js +4 -1
- package/dist/modules/staff/translations.js.map +2 -2
- package/dist/modules/staff/widgets/components.js +7 -0
- package/dist/modules/staff/widgets/components.js.map +7 -0
- package/dist/modules/staff/widgets/injection/timer-sidebar-indicator/widget.js +5 -4
- package/dist/modules/staff/widgets/injection/timer-sidebar-indicator/widget.js.map +2 -2
- package/dist/modules/staff/widgets/notifications/TimeProjectAccessRequestedRenderer.js +135 -0
- package/dist/modules/staff/widgets/notifications/TimeProjectAccessRequestedRenderer.js.map +7 -0
- package/dist/modules/staff/widgets/notifications/index.js +5 -0
- package/dist/modules/staff/widgets/notifications/index.js.map +7 -0
- package/dist/modules/staff/workers/timesheets-reapply-rounding.js +43 -0
- package/dist/modules/staff/workers/timesheets-reapply-rounding.js.map +7 -0
- package/generated/entities/staff_time_entry/index.ts +7 -0
- package/generated/entities/staff_time_entry_tag/index.ts +6 -0
- package/generated/entities/staff_time_project/index.ts +8 -0
- package/generated/entities/staff_time_report/index.ts +27 -0
- package/generated/entities/staff_time_report_entry/index.ts +12 -0
- package/generated/entities/staff_time_report_event/index.ts +9 -0
- package/generated/entities/staff_time_report_project/index.ts +6 -0
- package/generated/entities/staff_time_tag/index.ts +9 -0
- package/generated/entities/staff_time_task/index.ts +17 -0
- package/generated/entities/staff_time_task_comment/index.ts +9 -0
- package/generated/entities/staff_time_task_status/index.ts +13 -0
- package/generated/entities/staff_time_task_tag/index.ts +6 -0
- package/generated/entities.ids.generated.ts +11 -1
- package/generated/entity-fields-registry.ts +149 -0
- package/package.json +7 -7
- package/src/helpers/integration/timesheetFixtures.ts +31 -1
- package/src/modules/auth/README.md +1 -1
- package/src/modules/auth/cli.ts +35 -1
- package/src/modules/auth/i18n/de.json +7 -0
- package/src/modules/auth/i18n/en.json +7 -0
- package/src/modules/auth/i18n/es.json +7 -0
- package/src/modules/auth/i18n/ko.json +7 -0
- package/src/modules/auth/i18n/pl.json +7 -0
- package/src/modules/auth/lib/backendChrome.tsx +1 -0
- package/src/modules/customer_accounts/lib/customerRoleAcls.ts +79 -0
- package/src/modules/customer_accounts/setup.ts +1 -45
- package/src/modules/data_sync/AGENTS.md +34 -0
- package/src/modules/data_sync/api/options.ts +2 -0
- package/src/modules/data_sync/backend/data-sync/page.tsx +82 -33
- package/src/modules/data_sync/i18n/de.json +1 -0
- package/src/modules/data_sync/i18n/en.json +1 -0
- package/src/modules/data_sync/i18n/es.json +1 -0
- package/src/modules/data_sync/i18n/ko.json +1 -0
- package/src/modules/data_sync/i18n/pl.json +1 -0
- package/src/modules/data_sync/lib/adapter.ts +45 -0
- package/src/modules/data_sync/lib/start-controls.ts +89 -0
- package/src/modules/staff/AGENTS.md +769 -1
- package/src/modules/staff/acl.ts +9 -0
- package/src/modules/staff/ai-agents.ts +189 -0
- package/src/modules/staff/ai-tools/time-tracking-pack.ts +494 -0
- package/src/modules/staff/ai-tools/types.ts +82 -0
- package/src/modules/staff/ai-tools.ts +13 -0
- package/src/modules/staff/analytics.ts +113 -0
- package/src/modules/staff/api/guards.ts +85 -3
- package/src/modules/staff/api/leave-requests/accept/route.ts +0 -2
- package/src/modules/staff/api/leave-requests/reject/route.ts +0 -2
- package/src/modules/staff/api/portal/time-reports/[id]/route.ts +182 -0
- package/src/modules/staff/api/portal/time-reports/route.ts +270 -0
- package/src/modules/staff/api/team-members/self/route.ts +0 -2
- package/src/modules/staff/api/team-members/tags/assign/route.ts +0 -2
- package/src/modules/staff/api/team-members/tags/unassign/route.ts +0 -2
- package/src/modules/staff/api/timesheets/_shared/withTimesheetInterceptors.ts +292 -0
- package/src/modules/staff/api/timesheets/access-requests/route.ts +299 -0
- package/src/modules/staff/api/timesheets/my-projects/[projectId]/route.ts +15 -7
- package/src/modules/staff/api/timesheets/my-projects/route.ts +14 -1
- package/src/modules/staff/api/timesheets/my-work/myWorkAggregate.ts +90 -0
- package/src/modules/staff/api/timesheets/my-work/route.ts +475 -0
- package/src/modules/staff/api/timesheets/projects/kpis/route.ts +15 -2
- package/src/modules/staff/api/timesheets/reports/[id]/close/route.ts +203 -0
- package/src/modules/staff/api/timesheets/reports/[id]/export/route.ts +315 -0
- package/src/modules/staff/api/timesheets/reports/[id]/sheet/route.ts +220 -0
- package/src/modules/staff/api/timesheets/reports/[id]/unlock/route.ts +208 -0
- package/src/modules/staff/api/timesheets/reports/preview/route.ts +449 -0
- package/src/modules/staff/api/timesheets/reports/route.ts +386 -0
- package/src/modules/staff/api/timesheets/reports/shared.ts +121 -0
- package/src/modules/staff/api/timesheets/settings/reapply-rounding/route.ts +205 -0
- package/src/modules/staff/api/timesheets/settings/rounding-impact/route.ts +226 -0
- package/src/modules/staff/api/timesheets/settings/route.ts +242 -0
- package/src/modules/staff/api/timesheets/tags/access.ts +134 -0
- package/src/modules/staff/api/timesheets/tags/entry-assignments/route.ts +290 -0
- package/src/modules/staff/api/timesheets/tags/route.ts +177 -0
- package/src/modules/staff/api/timesheets/tags/task-assignments/route.ts +294 -0
- package/src/modules/staff/api/timesheets/task-statuses/route.ts +232 -0
- package/src/modules/staff/api/timesheets/tasks/[id]/comments/route.ts +532 -0
- package/src/modules/staff/api/timesheets/tasks/[id]/status/route.ts +271 -0
- package/src/modules/staff/api/timesheets/tasks/route.ts +443 -0
- package/src/modules/staff/api/timesheets/time-entries/[id]/duplicate/route.ts +306 -0
- package/src/modules/staff/api/timesheets/time-entries/[id]/segments/[segmentId]/route.ts +32 -10
- package/src/modules/staff/api/timesheets/time-entries/[id]/segments/route.ts +32 -17
- package/src/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.ts +27 -6
- package/src/modules/staff/api/timesheets/time-entries/[id]/timer-stop/route.ts +36 -6
- package/src/modules/staff/api/timesheets/time-entries/bulk/route.ts +331 -34
- package/src/modules/staff/api/timesheets/time-entries/copy-day/route.ts +533 -0
- package/src/modules/staff/api/timesheets/time-entries/overlaps/route.ts +457 -0
- package/src/modules/staff/api/timesheets/time-entries/route.ts +378 -24
- package/src/modules/staff/api/timesheets/time-entries/start-timer/route.ts +31 -9
- package/src/modules/staff/api/timesheets/time-projects/[id]/change-currency/route.ts +246 -0
- package/src/modules/staff/api/timesheets/time-projects/[id]/employees/route.ts +62 -2
- package/src/modules/staff/api/timesheets/time-projects/route.ts +397 -22
- package/src/modules/staff/backend/staff/time-tracking/board/page.meta.ts +14 -0
- package/src/modules/staff/backend/staff/time-tracking/board/page.tsx +151 -0
- package/src/modules/staff/backend/staff/time-tracking/entries/page.meta.ts +14 -0
- package/src/modules/staff/backend/staff/time-tracking/entries/page.tsx +1229 -0
- package/src/modules/staff/backend/staff/time-tracking/page.meta.ts +11 -0
- package/src/modules/staff/backend/staff/time-tracking/page.tsx +823 -0
- package/src/modules/staff/backend/staff/time-tracking/projects/[id]/board/page.meta.ts +12 -0
- package/src/modules/staff/backend/staff/time-tracking/projects/[id]/board/page.tsx +81 -0
- package/src/modules/staff/backend/staff/time-tracking/projects/[id]/edit/page.meta.ts +12 -0
- package/src/modules/staff/backend/staff/time-tracking/projects/[id]/edit/page.tsx +275 -0
- package/src/modules/staff/backend/staff/time-tracking/projects/[id]/page.meta.ts +12 -0
- package/src/modules/staff/backend/staff/time-tracking/projects/[id]/page.tsx +1420 -0
- package/src/modules/staff/backend/staff/time-tracking/projects/create/page.meta.ts +12 -0
- package/src/modules/staff/backend/staff/time-tracking/projects/create/page.tsx +109 -0
- package/src/modules/staff/backend/staff/time-tracking/projects/page.meta.ts +15 -0
- package/src/modules/staff/backend/staff/time-tracking/projects/page.tsx +1254 -0
- package/src/modules/staff/backend/staff/time-tracking/projects/projectFormConfig.ts +466 -0
- package/src/modules/staff/backend/staff/time-tracking/reports/[id]/page.meta.ts +12 -0
- package/src/modules/staff/backend/staff/time-tracking/reports/[id]/page.tsx +632 -0
- package/src/modules/staff/backend/staff/time-tracking/reports/create/page.meta.ts +12 -0
- package/src/modules/staff/backend/staff/time-tracking/reports/create/page.tsx +867 -0
- package/src/modules/staff/backend/staff/time-tracking/reports/page.meta.ts +14 -0
- package/src/modules/staff/backend/staff/time-tracking/reports/page.tsx +246 -0
- package/src/modules/staff/backend/staff/time-tracking/settings/page.meta.ts +14 -0
- package/src/modules/staff/backend/staff/time-tracking/settings/page.tsx +701 -0
- package/src/modules/staff/backend/staff/time-tracking/timesheet/GridView.tsx +953 -0
- package/src/modules/staff/backend/staff/time-tracking/timesheet/page.meta.ts +14 -0
- package/src/modules/staff/backend/staff/time-tracking/timesheet/page.tsx +800 -0
- package/src/modules/staff/backend/staff/timesheets/page.meta.ts +6 -6
- package/src/modules/staff/backend/staff/timesheets/page.tsx +12 -1010
- package/src/modules/staff/backend/staff/timesheets/projects/[id]/edit/page.meta.ts +0 -6
- package/src/modules/staff/backend/staff/timesheets/projects/[id]/edit/page.tsx +4 -158
- package/src/modules/staff/backend/staff/timesheets/projects/[id]/page.meta.ts +0 -6
- package/src/modules/staff/backend/staff/timesheets/projects/[id]/page.tsx +4 -609
- package/src/modules/staff/backend/staff/timesheets/projects/create/page.meta.ts +0 -6
- package/src/modules/staff/backend/staff/timesheets/projects/create/page.tsx +3 -89
- package/src/modules/staff/backend/staff/timesheets/projects/page.meta.ts +2 -19
- package/src/modules/staff/backend/staff/timesheets/projects/page.tsx +3 -778
- package/src/modules/staff/ce.ts +63 -0
- package/src/modules/staff/cli.ts +207 -1
- package/src/modules/staff/commands/index.ts +5 -0
- package/src/modules/staff/commands/timesheets-entries.ts +1006 -28
- package/src/modules/staff/commands/timesheets-projects.ts +593 -7
- package/src/modules/staff/commands/timesheets-reports.ts +1329 -0
- package/src/modules/staff/commands/timesheets-tags.ts +1315 -0
- package/src/modules/staff/commands/timesheets-task-comments.ts +583 -0
- package/src/modules/staff/commands/timesheets-task-statuses.ts +889 -0
- package/src/modules/staff/commands/timesheets-tasks.ts +1121 -0
- package/src/modules/staff/data/enrichers.ts +675 -10
- package/src/modules/staff/data/entities.ts +494 -2
- package/src/modules/staff/data/extensions.ts +68 -0
- package/src/modules/staff/data/validators.ts +348 -3
- package/src/modules/staff/di.ts +96 -0
- package/src/modules/staff/events.payloads.ts +268 -0
- package/src/modules/staff/events.ts +87 -5
- package/src/modules/staff/extension-points.ts +308 -0
- package/src/modules/staff/frontend/[orgSlug]/portal/time-reports/[id]/page.meta.ts +10 -0
- package/src/modules/staff/frontend/[orgSlug]/portal/time-reports/[id]/page.tsx +225 -0
- package/src/modules/staff/frontend/[orgSlug]/portal/time-reports/page.meta.ts +17 -0
- package/src/modules/staff/frontend/[orgSlug]/portal/time-reports/page.tsx +182 -0
- package/src/modules/staff/i18n/de.json +899 -0
- package/src/modules/staff/i18n/en.json +899 -0
- package/src/modules/staff/i18n/es.json +899 -0
- package/src/modules/staff/i18n/ko.json +899 -0
- package/src/modules/staff/i18n/pl.json +899 -0
- package/src/modules/staff/lib/crud.ts +14 -3
- package/src/modules/staff/lib/time-tracking/access.ts +155 -0
- package/src/modules/staff/lib/time-tracking/billability.ts +68 -0
- package/src/modules/staff/lib/time-tracking/capacity.ts +124 -0
- package/src/modules/staff/lib/time-tracking/componentContracts.ts +26 -0
- package/src/modules/staff/lib/time-tracking/cost.ts +144 -0
- package/src/modules/staff/lib/time-tracking/duration.ts +68 -0
- package/src/modules/staff/lib/time-tracking/featureAccess.ts +115 -0
- package/src/modules/staff/lib/time-tracking/interval.ts +90 -0
- package/src/modules/staff/lib/time-tracking/migrateProjectCodes.ts +161 -0
- package/src/modules/staff/lib/time-tracking/moneyVisibility.ts +37 -0
- package/src/modules/staff/lib/time-tracking/overlap.ts +159 -0
- package/src/modules/staff/lib/time-tracking/portalRecipients.ts +76 -0
- package/src/modules/staff/lib/time-tracking/portalReports.ts +31 -0
- package/src/modules/staff/lib/time-tracking/projectCode.ts +221 -0
- package/src/modules/staff/lib/time-tracking/reapplyRounding.ts +313 -0
- package/src/modules/staff/lib/time-tracking/recalculationRunner.ts +118 -0
- package/src/modules/staff/lib/time-tracking/recalculations.ts +135 -0
- package/src/modules/staff/lib/time-tracking/registries/invoke.ts +122 -0
- package/src/modules/staff/lib/time-tracking/registries/registry.ts +124 -0
- package/src/modules/staff/lib/time-tracking/registries/scope.ts +56 -0
- package/src/modules/staff/lib/time-tracking/rollup.ts +59 -0
- package/src/modules/staff/lib/time-tracking/rounding.ts +126 -0
- package/src/modules/staff/lib/time-tracking/roundingImpact.ts +75 -0
- package/src/modules/staff/lib/time-tracking/settingKeys.ts +274 -0
- package/src/modules/staff/lib/time-tracking/settings.ts +168 -0
- package/src/modules/staff/lib/time-tracking/sqlInClause.ts +22 -0
- package/src/modules/staff/lib/time-tracking/timeEntrySources.ts +85 -0
- package/src/modules/staff/lib/time-tracking-ui/BoardFilterChips.tsx +360 -0
- package/src/modules/staff/lib/time-tracking-ui/CustomerPicker.tsx +427 -0
- package/src/modules/staff/lib/time-tracking-ui/DurationInput.tsx +192 -0
- package/src/modules/staff/lib/time-tracking-ui/EntryDetailsFields.tsx +224 -0
- package/src/modules/staff/lib/time-tracking-ui/EntryFilterBar.tsx +144 -0
- package/src/modules/staff/lib/time-tracking-ui/KanbanBoard.tsx +1065 -0
- package/src/modules/staff/lib/time-tracking-ui/KanbanCard.tsx +435 -0
- package/src/modules/staff/lib/time-tracking-ui/KanbanColumn.tsx +316 -0
- package/src/modules/staff/lib/time-tracking-ui/NewTaskDialog.tsx +291 -0
- package/src/modules/staff/lib/time-tracking-ui/NoProjectAccess.tsx +162 -0
- package/src/modules/staff/lib/time-tracking-ui/PeriodSelector.tsx +149 -0
- package/src/modules/staff/lib/time-tracking-ui/PhasePlaceholder.tsx +31 -0
- package/src/modules/staff/lib/time-tracking-ui/ProjectCodeField.tsx +230 -0
- package/src/modules/staff/lib/time-tracking-ui/ProjectCurrencyField.tsx +432 -0
- package/src/modules/staff/lib/time-tracking-ui/ProjectFormSections.tsx +323 -0
- package/src/modules/staff/lib/time-tracking-ui/ProjectTeamDrawer.tsx +930 -0
- package/src/modules/staff/lib/time-tracking-ui/ReportSheet.tsx +357 -0
- package/src/modules/staff/lib/time-tracking-ui/TagPicker.tsx +195 -0
- package/src/modules/staff/lib/time-tracking-ui/TaskBoardScreen.tsx +243 -0
- package/src/modules/staff/lib/time-tracking-ui/TaskCommentThread.tsx +137 -0
- package/src/modules/staff/lib/time-tracking-ui/TaskDrawer.tsx +1405 -0
- package/src/modules/staff/lib/time-tracking-ui/TaskListView.tsx +260 -0
- package/src/modules/staff/lib/time-tracking-ui/TaskPicker.tsx +435 -0
- package/src/modules/staff/lib/time-tracking-ui/TaskQuickLog.tsx +296 -0
- package/src/modules/staff/lib/time-tracking-ui/TimeEntriesSummaryFooter.tsx +97 -0
- package/src/modules/staff/lib/time-tracking-ui/TimeEntryDialog.tsx +1929 -0
- package/src/modules/staff/lib/time-tracking-ui/TimeEntryDurationCell.tsx +126 -0
- package/src/modules/staff/lib/time-tracking-ui/TimesheetCalendar.tsx +265 -0
- package/src/modules/staff/lib/time-tracking-ui/TimesheetPeriodFooter.tsx +78 -0
- package/src/modules/staff/lib/time-tracking-ui/TimesheetViewSwitch.tsx +70 -0
- package/src/modules/staff/lib/time-tracking-ui/boardDirectory.ts +231 -0
- package/src/modules/staff/lib/time-tracking-ui/boardFilters.ts +128 -0
- package/src/modules/staff/lib/time-tracking-ui/boardSummary.ts +20 -0
- package/src/modules/staff/lib/time-tracking-ui/kanbanBoardData.ts +228 -0
- package/src/modules/staff/lib/time-tracking-ui/projectTeamAssignment.ts +143 -0
- package/src/modules/staff/lib/time-tracking-ui/taskDrawerData.ts +194 -0
- package/src/modules/staff/lib/time-tracking-ui/timeEntryDialogState.ts +400 -0
- package/src/modules/staff/lib/time-tracking-ui/timeEntryListData.ts +271 -0
- package/src/modules/staff/lib/time-tracking-ui/timeTrackingSettingsForm.ts +168 -0
- package/src/modules/staff/lib/time-tracking-ui/timesheetData.ts +225 -0
- package/src/modules/staff/lib/time-tracking-ui/timesheetPeriod.ts +278 -0
- package/src/modules/staff/lib/time-tracking-ui/timesheetTargets.ts +93 -0
- package/src/modules/staff/lib/time-tracking-ui/useBoardFilters.ts +107 -0
- package/src/modules/staff/lib/time-tracking-ui/useTimesheetPreferences.ts +245 -0
- package/src/modules/staff/lib/timeTrackingSeeds.ts +1037 -0
- package/src/modules/staff/lib/timesheets/timeEntryCacheInvalidation.ts +11 -10
- package/src/modules/staff/lib/timesheets/timeEntryDecoration.ts +256 -0
- package/src/modules/staff/lib/timesheets-projects/budgetBurn.ts +60 -0
- package/src/modules/staff/lib/timesheets-projects/budgetThreshold.ts +135 -0
- package/src/modules/staff/lib/timesheets-projects/budgetThresholdState.ts +137 -0
- package/src/modules/staff/lib/timesheets-projects/computeProjectFinancials.ts +142 -0
- package/src/modules/staff/lib/timesheets-projects/reportSelection.ts +82 -0
- package/src/modules/staff/lib/timesheets-projects-ui/AssignMembersDialog.tsx +237 -0
- package/src/modules/staff/lib/timesheets-projects-ui/ProjectBudgetCell.tsx +33 -0
- package/src/modules/staff/lib/timesheets-projects-ui/ProjectCard.tsx +56 -1
- package/src/modules/staff/lib/timesheets-projects-ui/ProjectsBulkActionsBar.tsx +90 -0
- package/src/modules/staff/lib/timesheets-reports/buildReportSheet.ts +125 -0
- package/src/modules/staff/lib/timesheets-reports/loadReportData.ts +251 -0
- package/src/modules/staff/lib/timesheets-reports/pdf.ts +263 -0
- package/src/modules/staff/lib/timesheets-reports/reportApprovalPolicies.ts +137 -0
- package/src/modules/staff/lib/timesheets-reports/reportConfigData.ts +247 -0
- package/src/modules/staff/lib/timesheets-reports/reportConfigState.ts +89 -0
- package/src/modules/staff/lib/timesheets-reports/reportExport.ts +373 -0
- package/src/modules/staff/lib/timesheets-reports/reportExportFormats.ts +71 -0
- package/src/modules/staff/lib/timesheets-reports/reportGroupings.ts +153 -0
- package/src/modules/staff/lib/timesheets-reports/reportLabels.ts +23 -0
- package/src/modules/staff/lib/timesheets-reports/reportReference.ts +146 -0
- package/src/modules/staff/lib/timesheets-reports/reportRows.ts +89 -0
- package/src/modules/staff/lib/timesheets-reports/reportSheetData.ts +196 -0
- package/src/modules/staff/lib/timesheets-reports/reportTotals.ts +544 -0
- package/src/modules/staff/lib/timesheets-reports/xlsx.ts +207 -0
- package/src/modules/staff/lib/timesheets-tasks/computeTaskRollups.ts +238 -0
- package/src/modules/staff/lib/timesheets-tasks/defaultStatusTemplate.ts +90 -0
- package/src/modules/staff/lib/timesheets-tasks/seedProjectStatuses.ts +43 -0
- package/src/modules/staff/lib/timesheets-tasks/statusPositions.ts +132 -0
- package/src/modules/staff/lib/timesheets-tasks/statusSlug.ts +43 -0
- package/src/modules/staff/lib/timesheets-tasks/taskHierarchy.ts +99 -0
- package/src/modules/staff/lib/timesheets-tasks/taskHoursTotals.ts +42 -0
- package/src/modules/staff/lib/timesheets-tasks/taskReference.ts +108 -0
- package/src/modules/staff/lib/timesheets-ui/CreateProjectDialog.tsx +18 -4
- package/src/modules/staff/lib/timesheets-ui/ListView.tsx +504 -149
- package/src/modules/staff/lib/timesheets-ui/TimerBar.tsx +50 -2
- package/src/modules/staff/migrations/.snapshot-open-mercato.json +3244 -228
- package/src/modules/staff/migrations/Migration20260813073131_staff.ts +94 -0
- package/src/modules/staff/migrations/Migration20260824143357_staff.ts +17 -0
- package/src/modules/staff/notifications.client.ts +16 -0
- package/src/modules/staff/notifications.handlers.ts +88 -0
- package/src/modules/staff/notifications.ts +163 -0
- package/src/modules/staff/search.ts +191 -1
- package/src/modules/staff/setup.ts +22 -0
- package/src/modules/staff/subscribers/time-project-access-requested-notification.ts +95 -0
- package/src/modules/staff/subscribers/time-project-budget-threshold-notification.ts +226 -0
- package/src/modules/staff/subscribers/time-report-approved-notification.ts +116 -0
- package/src/modules/staff/subscribers/time-report-portal-broadcast.ts +120 -0
- package/src/modules/staff/translations.ts +17 -0
- package/src/modules/staff/widgets/components.ts +34 -0
- package/src/modules/staff/widgets/injection/timer-sidebar-indicator/widget.tsx +6 -5
- package/src/modules/staff/widgets/notifications/TimeProjectAccessRequestedRenderer.tsx +138 -0
- package/src/modules/staff/widgets/notifications/index.ts +1 -0
- package/src/modules/staff/workers/timesheets-reapply-rounding.ts +59 -0
- package/dist/modules/staff/backend/staff/timesheets/projects/projectFormConfig.js +0 -123
- package/dist/modules/staff/backend/staff/timesheets/projects/projectFormConfig.js.map +0 -7
- package/src/modules/staff/backend/staff/timesheets/projects/projectFormConfig.ts +0 -138
|
@@ -2,11 +2,19 @@ import { z } from "zod";
|
|
|
2
2
|
import { makeCrudRoute } from "@open-mercato/shared/lib/crud/factory";
|
|
3
3
|
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
|
|
4
4
|
import { resolveCrudRecordId, parseScopedCommandInput } from "@open-mercato/shared/lib/api/scoped";
|
|
5
|
-
import {
|
|
5
|
+
import { createLogger } from "@open-mercato/shared/lib/logger";
|
|
6
|
+
import { escapeLikePattern } from "@open-mercato/shared/lib/db/escapeLikePattern";
|
|
7
|
+
import { sanitizeSearchTerm } from "../../helpers.js";
|
|
8
|
+
import { StaffTimeEntry, StaffTimeEntryTag, StaffTimeProject } from "../../../data/entities.js";
|
|
6
9
|
import { staffTimeEntryCreateSchema, staffTimeEntryUpdateSchema } from "../../../data/validators.js";
|
|
7
10
|
import { buildTimeEntryListFilters, isParseableDateFilter } from "../../../lib/timesheets/timeEntryListFilters.js";
|
|
8
|
-
import { staffTimeEntryCommandIds } from "../../../lib/crud.js";
|
|
11
|
+
import { staffTimeEntryCommandIds, staffTimeEntryCrudEvents } from "../../../lib/crud.js";
|
|
12
|
+
import { resolveFeatureAccess } from "../../../lib/time-tracking/featureAccess.js";
|
|
13
|
+
import { MANAGE_PROJECTS_FEATURE, resolveProjectAccess } from "../../../lib/time-tracking/access.js";
|
|
14
|
+
import { readTimeTrackingSettings } from "../../../lib/time-tracking/settings.js";
|
|
15
|
+
import { decorateTimeEntryRows } from "../../../lib/timesheets/timeEntryDecoration.js";
|
|
9
16
|
import { createStaffCrudOpenApi, createPagedListResponseSchema, defaultOkResponseSchema } from "../../openapi.js";
|
|
17
|
+
const logger = createLogger("staff").child({ component: "api/timesheets/time-entries" });
|
|
10
18
|
const F = {
|
|
11
19
|
id: "id",
|
|
12
20
|
tenant_id: "tenant_id",
|
|
@@ -14,18 +22,26 @@ const F = {
|
|
|
14
22
|
staff_member_id: "staff_member_id",
|
|
15
23
|
date: "date",
|
|
16
24
|
duration_minutes: "duration_minutes",
|
|
25
|
+
rounded_minutes: "rounded_minutes",
|
|
17
26
|
started_at: "started_at",
|
|
18
27
|
ended_at: "ended_at",
|
|
19
28
|
notes: "notes",
|
|
20
29
|
time_project_id: "time_project_id",
|
|
30
|
+
task_id: "task_id",
|
|
21
31
|
customer_id: "customer_id",
|
|
22
32
|
deal_id: "deal_id",
|
|
23
33
|
order_id: "order_id",
|
|
34
|
+
is_billable: "is_billable",
|
|
35
|
+
rate_override_amount: "rate_override_amount",
|
|
36
|
+
rate_currency_code: "rate_currency_code",
|
|
37
|
+
locked_report_id: "locked_report_id",
|
|
38
|
+
locked_at: "locked_at",
|
|
24
39
|
source: "source",
|
|
25
40
|
created_at: "created_at",
|
|
26
41
|
updated_at: "updated_at",
|
|
27
42
|
deleted_at: "deleted_at"
|
|
28
43
|
};
|
|
44
|
+
const RATES_FEATURE = "staff.timesheets.rates.view";
|
|
29
45
|
const routeMetadata = {
|
|
30
46
|
GET: { requireAuth: true, requireFeatures: ["staff.timesheets.view"] },
|
|
31
47
|
POST: { requireAuth: true, requireFeatures: ["staff.timesheets.manage_own"] },
|
|
@@ -42,11 +58,190 @@ const listSchema = z.object({
|
|
|
42
58
|
from: dateFilterSchema,
|
|
43
59
|
to: dateFilterSchema,
|
|
44
60
|
projectId: z.string().uuid().optional(),
|
|
61
|
+
/**
|
|
62
|
+
* One task id, or a comma-separated list of them. The list form is what the
|
|
63
|
+
* task drawer needs: a parent and its subtasks are one question ("what has
|
|
64
|
+
* been logged against this task?"), so answering it must not cost one request
|
|
65
|
+
* per child.
|
|
66
|
+
*/
|
|
67
|
+
taskId: z.string().optional(),
|
|
45
68
|
ids: z.string().optional(),
|
|
46
69
|
running: z.string().optional(),
|
|
70
|
+
/** `true` / `false` — omitted means "either". */
|
|
71
|
+
billable: z.string().optional(),
|
|
72
|
+
/** `true` = frozen into a closed report, `false` = still editable. */
|
|
73
|
+
locked: z.string().optional(),
|
|
74
|
+
/** Free text over the entry note. */
|
|
75
|
+
q: z.string().optional(),
|
|
76
|
+
/** Customer id — narrows to that customer's projects. */
|
|
77
|
+
customerId: z.string().uuid().optional(),
|
|
78
|
+
/** One tag id, or a comma-separated list; an entry matching any of them. */
|
|
79
|
+
tagIds: z.string().optional(),
|
|
47
80
|
sortField: z.string().optional(),
|
|
48
81
|
sortDir: z.enum(["asc", "desc"]).optional()
|
|
49
82
|
}).passthrough();
|
|
83
|
+
function parseBooleanFlagOrNull(value) {
|
|
84
|
+
if (typeof value !== "string") return null;
|
|
85
|
+
const normalized = value.trim().toLowerCase();
|
|
86
|
+
if (normalized.length === 0) return null;
|
|
87
|
+
if (["1", "true", "yes", "on"].includes(normalized)) return true;
|
|
88
|
+
if (["0", "false", "no", "off"].includes(normalized)) return false;
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
const IMPOSSIBLE_ID = "00000000-0000-0000-0000-000000000000";
|
|
92
|
+
const DENIED_ACCESS = { canManageAll: false, projectIds: [], staffMemberId: null };
|
|
93
|
+
const UUID_PATTERN = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
|
|
94
|
+
function splitTaskIdList(value) {
|
|
95
|
+
if (typeof value !== "string" || value.trim().length === 0) return null;
|
|
96
|
+
return value.split(",").map((entry) => entry.trim()).filter((entry) => UUID_PATTERN.test(entry));
|
|
97
|
+
}
|
|
98
|
+
const accessByRequest = /* @__PURE__ */ new WeakMap();
|
|
99
|
+
async function resolveAssignmentGraceDays(container, tenantId) {
|
|
100
|
+
try {
|
|
101
|
+
const configService = container.resolve("moduleConfigService");
|
|
102
|
+
const settings = await readTimeTrackingSettings(configService, { tenantId });
|
|
103
|
+
return settings.access.assignmentGraceDays;
|
|
104
|
+
} catch {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
async function loadProjectAccess(container, userId, tenantId, organizationId) {
|
|
109
|
+
if (!tenantId || !organizationId) return { ...DENIED_ACCESS };
|
|
110
|
+
const scope = { tenantId, organizationId };
|
|
111
|
+
try {
|
|
112
|
+
const access = await resolveFeatureAccess(container, userId, [MANAGE_PROJECTS_FEATURE], scope);
|
|
113
|
+
const em = container.resolve("em");
|
|
114
|
+
return await resolveProjectAccess({
|
|
115
|
+
em: em.fork(),
|
|
116
|
+
userId,
|
|
117
|
+
tenantId,
|
|
118
|
+
organizationId,
|
|
119
|
+
canManageAll: access.allowed,
|
|
120
|
+
userFeatures: access.grantedFeatures,
|
|
121
|
+
assignmentGraceDays: await resolveAssignmentGraceDays(container, tenantId)
|
|
122
|
+
});
|
|
123
|
+
} catch (err) {
|
|
124
|
+
logger.error("staff.timesheets.time-entries access resolution failed", { err });
|
|
125
|
+
return { ...DENIED_ACCESS };
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
function resolveCtxScope(ctx) {
|
|
129
|
+
return {
|
|
130
|
+
tenantId: ctx.auth?.tenantId ?? null,
|
|
131
|
+
organizationId: ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
async function resolveListProjectAccess(ctx) {
|
|
135
|
+
const { tenantId, organizationId } = resolveCtxScope(ctx);
|
|
136
|
+
const load = () => loadProjectAccess(ctx.container, ctx.auth?.sub ?? null, tenantId, organizationId);
|
|
137
|
+
const request = ctx.request;
|
|
138
|
+
if (!request) return load();
|
|
139
|
+
const cached = accessByRequest.get(request);
|
|
140
|
+
if (cached) return cached;
|
|
141
|
+
const pending = load();
|
|
142
|
+
accessByRequest.set(request, pending);
|
|
143
|
+
return pending;
|
|
144
|
+
}
|
|
145
|
+
async function buildScopedTimeEntryListFilters(query, ctx) {
|
|
146
|
+
const filters = buildTimeEntryListFilters(query);
|
|
147
|
+
const taskIds = splitTaskIdList(query.taskId);
|
|
148
|
+
if (taskIds !== null) {
|
|
149
|
+
filters[F.task_id] = { $in: taskIds.length > 0 ? taskIds : [IMPOSSIBLE_ID] };
|
|
150
|
+
}
|
|
151
|
+
const billable = parseBooleanFlagOrNull(query.billable);
|
|
152
|
+
if (billable !== null) filters[F.is_billable] = { $eq: billable };
|
|
153
|
+
const locked = parseBooleanFlagOrNull(query.locked);
|
|
154
|
+
if (locked !== null) {
|
|
155
|
+
filters[F.locked_report_id] = locked ? { $ne: null } : { $eq: null };
|
|
156
|
+
}
|
|
157
|
+
const term = sanitizeSearchTerm(query.q);
|
|
158
|
+
if (term) filters[F.notes] = { $ilike: `%${escapeLikePattern(term)}%` };
|
|
159
|
+
if (typeof query.customerId === "string" && query.customerId.trim().length > 0) {
|
|
160
|
+
const em = ctx.container.resolve("em");
|
|
161
|
+
const { tenantId, organizationId } = resolveCtxScope(ctx);
|
|
162
|
+
const projects = await em.fork().find(
|
|
163
|
+
StaffTimeProject,
|
|
164
|
+
{ customerId: query.customerId.trim(), tenantId, organizationId, deletedAt: null },
|
|
165
|
+
{ fields: ["id"] }
|
|
166
|
+
);
|
|
167
|
+
const ids = projects.map((project) => project.id);
|
|
168
|
+
filters[F.time_project_id] = { $in: ids.length > 0 ? ids : [IMPOSSIBLE_ID] };
|
|
169
|
+
}
|
|
170
|
+
const tagIds = splitTaskIdList(query.tagIds);
|
|
171
|
+
if (tagIds !== null) {
|
|
172
|
+
const em = ctx.container.resolve("em");
|
|
173
|
+
const { tenantId, organizationId } = resolveCtxScope(ctx);
|
|
174
|
+
const assignments = tagIds.length === 0 ? [] : await em.fork().find(
|
|
175
|
+
StaffTimeEntryTag,
|
|
176
|
+
{ tagId: { $in: tagIds }, tenantId, organizationId },
|
|
177
|
+
{ fields: ["timeEntryId"] }
|
|
178
|
+
);
|
|
179
|
+
const entryIds = Array.from(new Set(assignments.map((row) => row.timeEntryId)));
|
|
180
|
+
filters[F.id] = { $in: entryIds.length > 0 ? entryIds : [IMPOSSIBLE_ID] };
|
|
181
|
+
}
|
|
182
|
+
const access = await resolveListProjectAccess(ctx);
|
|
183
|
+
if (access.canManageAll) return filters;
|
|
184
|
+
const branches = [];
|
|
185
|
+
if (access.projectIds.length > 0) {
|
|
186
|
+
branches.push({ [F.time_project_id]: { $in: access.projectIds } });
|
|
187
|
+
}
|
|
188
|
+
if (access.staffMemberId) {
|
|
189
|
+
branches.push({ [F.time_project_id]: { $eq: null }, [F.staff_member_id]: access.staffMemberId });
|
|
190
|
+
}
|
|
191
|
+
if (branches.length === 0) {
|
|
192
|
+
filters[F.id] = { $in: [IMPOSSIBLE_ID] };
|
|
193
|
+
return filters;
|
|
194
|
+
}
|
|
195
|
+
filters.$or = branches;
|
|
196
|
+
return filters;
|
|
197
|
+
}
|
|
198
|
+
const timeEntryListFields = [
|
|
199
|
+
F.id,
|
|
200
|
+
F.organization_id,
|
|
201
|
+
F.tenant_id,
|
|
202
|
+
F.staff_member_id,
|
|
203
|
+
F.date,
|
|
204
|
+
F.duration_minutes,
|
|
205
|
+
F.rounded_minutes,
|
|
206
|
+
F.started_at,
|
|
207
|
+
F.ended_at,
|
|
208
|
+
F.notes,
|
|
209
|
+
F.time_project_id,
|
|
210
|
+
F.task_id,
|
|
211
|
+
F.customer_id,
|
|
212
|
+
F.deal_id,
|
|
213
|
+
F.order_id,
|
|
214
|
+
F.is_billable,
|
|
215
|
+
F.locked_report_id,
|
|
216
|
+
F.locked_at,
|
|
217
|
+
F.source,
|
|
218
|
+
F.created_at,
|
|
219
|
+
F.updated_at
|
|
220
|
+
];
|
|
221
|
+
async function callerHasRatesView(ctx) {
|
|
222
|
+
const userId = ctx.auth?.sub;
|
|
223
|
+
if (!userId) return false;
|
|
224
|
+
const { tenantId, organizationId } = resolveCtxScope(ctx);
|
|
225
|
+
try {
|
|
226
|
+
const rbac = ctx.container.resolve("rbacService");
|
|
227
|
+
if (!rbac?.userHasAllFeatures) return false;
|
|
228
|
+
return await rbac.userHasAllFeatures(userId, [RATES_FEATURE], { tenantId, organizationId });
|
|
229
|
+
} catch {
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
async function decorateTimeEntryList(payload, ctx) {
|
|
234
|
+
const items = payload?.items;
|
|
235
|
+
if (!Array.isArray(items) || items.length === 0) return;
|
|
236
|
+
const { tenantId, organizationId } = resolveCtxScope(ctx);
|
|
237
|
+
await decorateTimeEntryRows(items, {
|
|
238
|
+
em: ctx.container.resolve("em"),
|
|
239
|
+
tenantId,
|
|
240
|
+
organizationId,
|
|
241
|
+
canSeeRates: await callerHasRatesView(ctx),
|
|
242
|
+
onError: (err) => logger.error("staff.timesheets.time-entries response decoration failed", { err })
|
|
243
|
+
});
|
|
244
|
+
}
|
|
50
245
|
const crud = makeCrudRoute({
|
|
51
246
|
metadata: routeMetadata,
|
|
52
247
|
orm: {
|
|
@@ -56,35 +251,21 @@ const crud = makeCrudRoute({
|
|
|
56
251
|
tenantField: "tenantId",
|
|
57
252
|
softDeleteField: "deletedAt"
|
|
58
253
|
},
|
|
254
|
+
events: staffTimeEntryCrudEvents,
|
|
255
|
+
enrichers: { entityId: "staff:staff_time_entry" },
|
|
59
256
|
indexer: { entityType: "staff:staff_time_entry" },
|
|
60
257
|
list: {
|
|
61
258
|
schema: listSchema,
|
|
62
259
|
entityId: "staff:staff_time_entry",
|
|
63
|
-
fields: [
|
|
64
|
-
F.id,
|
|
65
|
-
F.organization_id,
|
|
66
|
-
F.tenant_id,
|
|
67
|
-
F.staff_member_id,
|
|
68
|
-
F.date,
|
|
69
|
-
F.duration_minutes,
|
|
70
|
-
F.started_at,
|
|
71
|
-
F.ended_at,
|
|
72
|
-
F.notes,
|
|
73
|
-
F.time_project_id,
|
|
74
|
-
F.customer_id,
|
|
75
|
-
F.deal_id,
|
|
76
|
-
F.order_id,
|
|
77
|
-
F.source,
|
|
78
|
-
F.created_at,
|
|
79
|
-
F.updated_at
|
|
80
|
-
],
|
|
260
|
+
fields: [...timeEntryListFields],
|
|
81
261
|
sortFieldMap: {
|
|
82
262
|
date: F.date,
|
|
83
263
|
createdAt: F.created_at,
|
|
84
264
|
updatedAt: F.updated_at,
|
|
85
|
-
durationMinutes: F.duration_minutes
|
|
265
|
+
durationMinutes: F.duration_minutes,
|
|
266
|
+
roundedMinutes: F.rounded_minutes
|
|
86
267
|
},
|
|
87
|
-
buildFilters:
|
|
268
|
+
buildFilters: buildScopedTimeEntryListFilters
|
|
88
269
|
},
|
|
89
270
|
actions: {
|
|
90
271
|
create: {
|
|
@@ -122,6 +303,12 @@ const GET = crud.GET;
|
|
|
122
303
|
const POST = crud.POST;
|
|
123
304
|
const PUT = crud.PUT;
|
|
124
305
|
const DELETE = crud.DELETE;
|
|
306
|
+
const timeEntryTagSchema = z.object({
|
|
307
|
+
id: z.string().uuid(),
|
|
308
|
+
slug: z.string(),
|
|
309
|
+
label: z.string(),
|
|
310
|
+
color: z.string().nullable()
|
|
311
|
+
});
|
|
125
312
|
const timeEntryListItemSchema = z.object({
|
|
126
313
|
id: z.string().uuid().nullable().optional(),
|
|
127
314
|
organization_id: z.string().uuid().nullable().optional(),
|
|
@@ -129,16 +316,36 @@ const timeEntryListItemSchema = z.object({
|
|
|
129
316
|
staff_member_id: z.string().uuid().nullable().optional(),
|
|
130
317
|
date: z.string().nullable().optional(),
|
|
131
318
|
duration_minutes: z.number().nullable().optional(),
|
|
319
|
+
rounded_minutes: z.number().nullable().optional(),
|
|
132
320
|
started_at: z.string().nullable().optional(),
|
|
133
321
|
ended_at: z.string().nullable().optional(),
|
|
134
322
|
notes: z.string().nullable().optional(),
|
|
135
323
|
time_project_id: z.string().uuid().nullable().optional(),
|
|
324
|
+
task_id: z.string().uuid().nullable().optional(),
|
|
136
325
|
customer_id: z.string().uuid().nullable().optional(),
|
|
137
326
|
deal_id: z.string().uuid().nullable().optional(),
|
|
138
327
|
order_id: z.string().uuid().nullable().optional(),
|
|
328
|
+
is_billable: z.boolean().nullable().optional(),
|
|
329
|
+
/** Present only for a caller holding `staff.timesheets.rates.view`. */
|
|
330
|
+
rate_override_amount: z.union([z.string(), z.number()]).nullable().optional(),
|
|
331
|
+
/** Present only for a caller holding `staff.timesheets.rates.view`. */
|
|
332
|
+
rate_currency_code: z.string().nullable().optional(),
|
|
333
|
+
locked_report_id: z.string().uuid().nullable().optional(),
|
|
334
|
+
locked_at: z.string().nullable().optional(),
|
|
139
335
|
source: z.string().nullable().optional(),
|
|
140
336
|
created_at: z.string().nullable().optional(),
|
|
141
|
-
updated_at: z.string().nullable().optional()
|
|
337
|
+
updated_at: z.string().nullable().optional(),
|
|
338
|
+
/** Alias of `notes`; both keys are returned (see UPGRADE_NOTES.md). */
|
|
339
|
+
description: z.string().nullable().optional(),
|
|
340
|
+
/** Raw minutes rounded by the tenant's rule at write time; the only input to cost (D-7). */
|
|
341
|
+
roundedMinutes: z.number().nullable().optional(),
|
|
342
|
+
isLocked: z.boolean().optional(),
|
|
343
|
+
lockedReportId: z.string().uuid().nullable().optional(),
|
|
344
|
+
tags: z.array(timeEntryTagSchema).optional(),
|
|
345
|
+
/** Present only for a caller holding `staff.timesheets.rates.view`; `null` when non-billable. */
|
|
346
|
+
cost: z.number().nullable().optional(),
|
|
347
|
+
/** Present only for a caller holding `staff.timesheets.rates.view`. */
|
|
348
|
+
currencyCode: z.string().nullable().optional()
|
|
142
349
|
});
|
|
143
350
|
const openApi = createStaffCrudOpenApi({
|
|
144
351
|
resourceName: "TimeEntry",
|
|
@@ -147,12 +354,12 @@ const openApi = createStaffCrudOpenApi({
|
|
|
147
354
|
listResponseSchema: createPagedListResponseSchema(timeEntryListItemSchema),
|
|
148
355
|
create: {
|
|
149
356
|
schema: staffTimeEntryCreateSchema,
|
|
150
|
-
description: "Creates a time entry for a staff member."
|
|
357
|
+
description: "Creates a time entry for a staff member. The note is accepted under either `notes` or `description` (`description` wins when both are sent). `roundedMinutes` is derived from the tenant rounding rule at write time and stored; `isBillable` falls back to the project default then the tenant default; `rateCurrencyCode` snapshots the project currency. `tagIds` is applied through the tag assignment command."
|
|
151
358
|
},
|
|
152
359
|
update: {
|
|
153
360
|
schema: staffTimeEntryUpdateSchema,
|
|
154
361
|
responseSchema: defaultOkResponseSchema,
|
|
155
|
-
description: "Updates a time entry by id."
|
|
362
|
+
description: "Updates a time entry by id. `durationMinutes`, `startedAt` and `endedAt` are reconciled so the stored set never contradicts itself: sending both clocks recomputes the duration from them (a duration sent alongside is advisory), sending one clock with a duration derives the other clock, and sending only a duration shifts `endedAt` while `startedAt` anchors. An `endedAt` earlier than its `startedAt` is stored on the next calendar day (a midnight crossing) and `date` still names the day the work started. Any change to the effective duration recomputes the stored rounded minutes from the tenant rounding rule; changing the project re-snapshots the currency. `tagIds` replaces the entry tag set through the tag assignment commands, which refuse an entry locked in a closed report."
|
|
156
363
|
},
|
|
157
364
|
del: {
|
|
158
365
|
schema: z.object({ id: z.string().uuid() }),
|
|
@@ -165,7 +372,12 @@ export {
|
|
|
165
372
|
GET,
|
|
166
373
|
POST,
|
|
167
374
|
PUT,
|
|
375
|
+
RATES_FEATURE,
|
|
376
|
+
buildScopedTimeEntryListFilters,
|
|
377
|
+
decorateTimeEntryList,
|
|
168
378
|
metadata,
|
|
169
|
-
openApi
|
|
379
|
+
openApi,
|
|
380
|
+
resolveListProjectAccess,
|
|
381
|
+
timeEntryListFields
|
|
170
382
|
};
|
|
171
383
|
//# sourceMappingURL=route.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/staff/api/timesheets/time-entries/route.ts"],
|
|
4
|
-
"sourcesContent": ["import { z } from 'zod'\nimport { makeCrudRoute } from '@open-mercato/shared/lib/crud/factory'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { resolveCrudRecordId, parseScopedCommandInput } from '@open-mercato/shared/lib/api/scoped'\nimport { StaffTimeEntry } from '../../../data/entities'\nimport { staffTimeEntryCreateSchema, staffTimeEntryUpdateSchema } from '../../../data/validators'\nimport { buildTimeEntryListFilters, isParseableDateFilter } from '../../../lib/timesheets/timeEntryListFilters'\nimport { staffTimeEntryCommandIds } from '../../../lib/crud'\nimport { createStaffCrudOpenApi, createPagedListResponseSchema, defaultOkResponseSchema } from '../../openapi'\n\nconst F = {\n id: 'id',\n tenant_id: 'tenant_id',\n organization_id: 'organization_id',\n staff_member_id: 'staff_member_id',\n date: 'date',\n duration_minutes: 'duration_minutes',\n started_at: 'started_at',\n ended_at: 'ended_at',\n notes: 'notes',\n time_project_id: 'time_project_id',\n customer_id: 'customer_id',\n deal_id: 'deal_id',\n order_id: 'order_id',\n source: 'source',\n created_at: 'created_at',\n updated_at: 'updated_at',\n deleted_at: 'deleted_at',\n} as const\n\nconst routeMetadata = {\n GET: { requireAuth: true, requireFeatures: ['staff.timesheets.view'] },\n POST: { requireAuth: true, requireFeatures: ['staff.timesheets.manage_own'] },\n PUT: { requireAuth: true, requireFeatures: ['staff.timesheets.manage_own'] },\n DELETE: { requireAuth: true, requireFeatures: ['staff.timesheets.manage_own'] },\n}\n\nexport const metadata = routeMetadata\n\nconst rawBodySchema = z.object({}).passthrough()\n\nconst dateFilterSchema = z\n .string()\n .refine(isParseableDateFilter, { message: 'Invalid date' })\n .optional()\n\nconst listSchema = z\n .object({\n page: z.coerce.number().min(1).default(1),\n pageSize: z.coerce.number().min(1).max(100).default(50),\n staffMemberId: z.string().uuid().optional(),\n from: dateFilterSchema,\n to: dateFilterSchema,\n projectId: z.string().uuid().optional(),\n ids: z.string().optional(),\n running: z.string().optional(),\n sortField: z.string().optional(),\n sortDir: z.enum(['asc', 'desc']).optional(),\n })\n .passthrough()\n\nconst crud = makeCrudRoute({\n metadata: routeMetadata,\n orm: {\n entity: StaffTimeEntry,\n idField: 'id',\n orgField: 'organizationId',\n tenantField: 'tenantId',\n softDeleteField: 'deletedAt',\n },\n indexer: { entityType: 'staff:staff_time_entry' },\n list: {\n schema: listSchema,\n entityId: 'staff:staff_time_entry',\n fields: [\n F.id,\n F.organization_id,\n F.tenant_id,\n F.staff_member_id,\n F.date,\n F.duration_minutes,\n F.started_at,\n F.ended_at,\n F.notes,\n F.time_project_id,\n F.customer_id,\n F.deal_id,\n F.order_id,\n F.source,\n F.created_at,\n F.updated_at,\n ],\n sortFieldMap: {\n date: F.date,\n createdAt: F.created_at,\n updatedAt: F.updated_at,\n durationMinutes: F.duration_minutes,\n },\n buildFilters: async (query) => buildTimeEntryListFilters(query),\n },\n actions: {\n create: {\n commandId: staffTimeEntryCommandIds.create,\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n return parseScopedCommandInput(staffTimeEntryCreateSchema, raw ?? {}, ctx, translate)\n },\n response: ({ result }) => ({ id: result?.timeEntryId ?? null }),\n status: 201,\n },\n update: {\n commandId: staffTimeEntryCommandIds.update,\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n return parseScopedCommandInput(staffTimeEntryUpdateSchema, raw ?? {}, ctx, translate)\n },\n response: () => ({ ok: true }),\n },\n delete: {\n commandId: staffTimeEntryCommandIds.delete,\n schema: rawBodySchema,\n mapInput: async ({ parsed, ctx }) => {\n const { translate } = await resolveTranslations()\n const id = resolveCrudRecordId(parsed, ctx, translate)\n return { id }\n },\n response: () => ({ ok: true }),\n },\n },\n})\n\nexport const GET = crud.GET\nexport const POST = crud.POST\nexport const PUT = crud.PUT\nexport const DELETE = crud.DELETE\n\nconst timeEntryListItemSchema = z.object({\n id: z.string().uuid().nullable().optional(),\n organization_id: z.string().uuid().nullable().optional(),\n tenant_id: z.string().uuid().nullable().optional(),\n staff_member_id: z.string().uuid().nullable().optional(),\n date: z.string().nullable().optional(),\n duration_minutes: z.number().nullable().optional(),\n started_at: z.string().nullable().optional(),\n ended_at: z.string().nullable().optional(),\n notes: z.string().nullable().optional(),\n time_project_id: z.string().uuid().nullable().optional(),\n customer_id: z.string().uuid().nullable().optional(),\n deal_id: z.string().uuid().nullable().optional(),\n order_id: z.string().uuid().nullable().optional(),\n source: z.string().nullable().optional(),\n created_at: z.string().nullable().optional(),\n updated_at: z.string().nullable().optional(),\n})\n\nexport const openApi = createStaffCrudOpenApi({\n resourceName: 'TimeEntry',\n pluralName: 'TimeEntries',\n querySchema: listSchema,\n listResponseSchema: createPagedListResponseSchema(timeEntryListItemSchema),\n create: {\n schema: staffTimeEntryCreateSchema,\n description: 'Creates a time entry for a staff member.',\n },\n update: {\n schema: staffTimeEntryUpdateSchema,\n responseSchema: defaultOkResponseSchema,\n description: 'Updates a time entry by id.',\n },\n del: {\n schema: z.object({ id: z.string().uuid() }),\n responseSchema: defaultOkResponseSchema,\n description: 'Deletes a time entry by id.',\n },\n})\n"],
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["/**\n * Time entries \u2014 the timesheet grid of screen 10 and the full entry form of\n * screen 8, served from one collection.\n *\n * Three things this route owns beyond plain CRUD:\n *\n * 1. **Project access is intersected into every query.** A caller without\n * `staff.timesheets.projects.manage` sees entries on the projects they are an\n * active member of, plus their own project-less entries \u2014 nobody else's. The\n * resolution is memoised per request and fails closed, mirroring the projects\n * and tasks routes.\n * 2. **`description` is returned beside `notes`.** The column is `notes`; every\n * consulting-suite screen calls it `description`. UPGRADE_NOTES.md promises\n * both keys on read for at least one minor release, so both are emitted here\n * and both are accepted on write (the command folds them, `description` wins).\n * 3. **Money is added, not hidden.** `cost`, `currencyCode` and the stored rate\n * override only exist in the payload for a caller holding\n * `staff.timesheets.rates.view`; everyone else gets a response with no money\n * keys at all, the same shape the project portfolio enricher uses. The base\n * projection selects no money column \u2014 see `timeEntryListFields` for why that\n * is the gate rather than an optimisation.\n *\n * `cost` is computed from `rounded_minutes` \u2014 never from `duration_minutes` \u2014 via\n * `entryAmount`, so a non-billable entry reads `null` rather than `0`: zero is a\n * price, and out-of-scope work does not have one (D-7).\n */\n\nimport { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { makeCrudRoute, type CrudCtx } from '@open-mercato/shared/lib/crud/factory'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { resolveCrudRecordId, parseScopedCommandInput } from '@open-mercato/shared/lib/api/scoped'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { escapeLikePattern } from '@open-mercato/shared/lib/db/escapeLikePattern'\nimport { sanitizeSearchTerm } from '../../helpers'\nimport type { ModuleConfigService } from '@open-mercato/core/modules/configs/lib/module-config-service'\nimport { StaffTimeEntry, StaffTimeEntryTag, StaffTimeProject } from '../../../data/entities'\nimport { staffTimeEntryCreateSchema, staffTimeEntryUpdateSchema } from '../../../data/validators'\nimport { buildTimeEntryListFilters, isParseableDateFilter } from '../../../lib/timesheets/timeEntryListFilters'\nimport { staffTimeEntryCommandIds, staffTimeEntryCrudEvents } from '../../../lib/crud'\nimport { resolveFeatureAccess } from '../../../lib/time-tracking/featureAccess'\nimport { MANAGE_PROJECTS_FEATURE, resolveProjectAccess, type ProjectAccess } from '../../../lib/time-tracking/access'\nimport { readTimeTrackingSettings } from '../../../lib/time-tracking/settings'\nimport { decorateTimeEntryRows } from '../../../lib/timesheets/timeEntryDecoration'\nimport { createStaffCrudOpenApi, createPagedListResponseSchema, defaultOkResponseSchema } from '../../openapi'\n\nconst logger = createLogger('staff').child({ component: 'api/timesheets/time-entries' })\n\nconst F = {\n id: 'id',\n tenant_id: 'tenant_id',\n organization_id: 'organization_id',\n staff_member_id: 'staff_member_id',\n date: 'date',\n duration_minutes: 'duration_minutes',\n rounded_minutes: 'rounded_minutes',\n started_at: 'started_at',\n ended_at: 'ended_at',\n notes: 'notes',\n time_project_id: 'time_project_id',\n task_id: 'task_id',\n customer_id: 'customer_id',\n deal_id: 'deal_id',\n order_id: 'order_id',\n is_billable: 'is_billable',\n rate_override_amount: 'rate_override_amount',\n rate_currency_code: 'rate_currency_code',\n locked_report_id: 'locked_report_id',\n locked_at: 'locked_at',\n source: 'source',\n created_at: 'created_at',\n updated_at: 'updated_at',\n deleted_at: 'deleted_at',\n} as const\n\nexport const RATES_FEATURE = 'staff.timesheets.rates.view'\n\nconst routeMetadata = {\n GET: { requireAuth: true, requireFeatures: ['staff.timesheets.view'] },\n POST: { requireAuth: true, requireFeatures: ['staff.timesheets.manage_own'] },\n PUT: { requireAuth: true, requireFeatures: ['staff.timesheets.manage_own'] },\n DELETE: { requireAuth: true, requireFeatures: ['staff.timesheets.manage_own'] },\n}\n\nexport const metadata = routeMetadata\n\nconst rawBodySchema = z.object({}).passthrough()\n\nconst dateFilterSchema = z\n .string()\n .refine(isParseableDateFilter, { message: 'Invalid date' })\n .optional()\n\nconst listSchema = z\n .object({\n page: z.coerce.number().min(1).default(1),\n pageSize: z.coerce.number().min(1).max(100).default(50),\n staffMemberId: z.string().uuid().optional(),\n from: dateFilterSchema,\n to: dateFilterSchema,\n projectId: z.string().uuid().optional(),\n /**\n * One task id, or a comma-separated list of them. The list form is what the\n * task drawer needs: a parent and its subtasks are one question (\"what has\n * been logged against this task?\"), so answering it must not cost one request\n * per child.\n */\n taskId: z.string().optional(),\n ids: z.string().optional(),\n running: z.string().optional(),\n /** `true` / `false` \u2014 omitted means \"either\". */\n billable: z.string().optional(),\n /** `true` = frozen into a closed report, `false` = still editable. */\n locked: z.string().optional(),\n /** Free text over the entry note. */\n q: z.string().optional(),\n /** Customer id \u2014 narrows to that customer's projects. */\n customerId: z.string().uuid().optional(),\n /** One tag id, or a comma-separated list; an entry matching any of them. */\n tagIds: z.string().optional(),\n sortField: z.string().optional(),\n sortDir: z.enum(['asc', 'desc']).optional(),\n })\n .passthrough()\n\ntype EntryListQuery = z.infer<typeof listSchema>\n\n/**\n * Three-state flag: `true`, `false`, or \"the caller did not ask\". A missing\n * filter must widen to everything rather than collapse to `false`, which is what\n * a plain boolean parse would do.\n */\nfunction parseBooleanFlagOrNull(value: unknown): boolean | null {\n if (typeof value !== 'string') return null\n const normalized = value.trim().toLowerCase()\n if (normalized.length === 0) return null\n if (['1', 'true', 'yes', 'on'].includes(normalized)) return true\n if (['0', 'false', 'no', 'off'].includes(normalized)) return false\n return null\n}\n\n/** Non-UUID sentinel used as the \"match nothing\" filter, mirroring the tasks route. */\nconst IMPOSSIBLE_ID = '00000000-0000-0000-0000-000000000000'\n\nconst DENIED_ACCESS: ProjectAccess = { canManageAll: false, projectIds: [], staffMemberId: null }\n\nconst UUID_PATTERN = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/\n\n/**\n * Splits `?taskId=a,b,c` into ids the database can be asked about.\n *\n * A token that is not a UUID is dropped rather than passed through: the column is\n * `uuid`, so a malformed id would fail the whole query instead of matching\n * nothing. `null` means the caller asked no task question at all; an empty array\n * means they asked one no row can answer.\n */\nfunction splitTaskIdList(value: unknown): string[] | null {\n if (typeof value !== 'string' || value.trim().length === 0) return null\n return value\n .split(',')\n .map((entry) => entry.trim())\n .filter((entry) => UUID_PATTERN.test(entry))\n}\n\ntype RbacServiceLike = {\n getGrantedFeatures?: (\n userId: string,\n options: { tenantId: string | null; organizationId: string | null },\n ) => Promise<string[]>\n userHasAllFeatures?: (\n userId: string,\n required: string[],\n scope: { tenantId: string | null; organizationId: string | null },\n ) => Promise<boolean>\n}\n\ntype ContainerLike = { resolve: (name: string) => unknown }\n\n/** One access decision per request, shared by every filter that needs it. */\nconst accessByRequest = new WeakMap<Request, Promise<ProjectAccess>>()\n\nasync function resolveAssignmentGraceDays(container: ContainerLike, tenantId: string): Promise<number | null> {\n try {\n const configService = container.resolve('moduleConfigService') as ModuleConfigService\n const settings = await readTimeTrackingSettings(configService, { tenantId })\n return settings.access.assignmentGraceDays\n } catch {\n // Fail safe to the documented default rather than widening the window.\n return null\n }\n}\n\nasync function loadProjectAccess(\n container: ContainerLike,\n userId: string | null,\n tenantId: string | null,\n organizationId: string | null,\n): Promise<ProjectAccess> {\n if (!tenantId || !organizationId) return { ...DENIED_ACCESS }\n const scope = { tenantId, organizationId }\n try {\n // One lookup, one authority, and a failure that says so. The previous\n // `catch \u2192 []` could not tell \"denied\" from \"could not ask\", so an RBAC\n // hiccup silently demoted a manager to their own memberships.\n const access = await resolveFeatureAccess(container, userId, [MANAGE_PROJECTS_FEATURE], scope)\n const em = container.resolve('em') as EntityManager\n return await resolveProjectAccess({\n em: em.fork(),\n userId,\n tenantId,\n organizationId,\n canManageAll: access.allowed,\n userFeatures: access.grantedFeatures,\n assignmentGraceDays: await resolveAssignmentGraceDays(container, tenantId),\n })\n } catch (err) {\n logger.error('staff.timesheets.time-entries access resolution failed', { err })\n return { ...DENIED_ACCESS }\n }\n}\n\nfunction resolveCtxScope(ctx: CrudCtx): { tenantId: string | null; organizationId: string | null } {\n return {\n tenantId: ctx.auth?.tenantId ?? null,\n organizationId: ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null,\n }\n}\n\nexport async function resolveListProjectAccess(ctx: CrudCtx): Promise<ProjectAccess> {\n const { tenantId, organizationId } = resolveCtxScope(ctx)\n const load = () => loadProjectAccess(ctx.container, ctx.auth?.sub ?? null, tenantId, organizationId)\n const request = ctx.request\n if (!request) return load()\n const cached = accessByRequest.get(request)\n if (cached) return cached\n const pending = load()\n accessByRequest.set(request, pending)\n return pending\n}\n\n/**\n * Every entry query narrows to what the caller may actually see: entries on the\n * projects they belong to, plus their OWN entries that name no project. A\n * project-less entry has no membership to check, so the only person it can belong\n * to is the member who logged it.\n *\n * Exported so the intersection is testable without standing up the CRUD factory.\n */\nexport async function buildScopedTimeEntryListFilters(\n query: EntryListQuery,\n ctx: CrudCtx,\n): Promise<Record<string, unknown>> {\n const filters = buildTimeEntryListFilters(query)\n\n // The task narrowing is applied before the access branches and never instead of\n // them: asking for a task the caller may not see must return nothing, not the\n // task's entries.\n const taskIds = splitTaskIdList(query.taskId)\n if (taskIds !== null) {\n filters[F.task_id] = { $in: taskIds.length > 0 ? taskIds : [IMPOSSIBLE_ID] }\n }\n\n // --- Optional narrowing the grid's filter overlay drives ---------------\n\n const billable = parseBooleanFlagOrNull(query.billable)\n if (billable !== null) filters[F.is_billable] = { $eq: billable }\n\n const locked = parseBooleanFlagOrNull(query.locked)\n if (locked !== null) {\n // \"Locked\" is the presence of the report that froze the entry, so it is asked\n // as a nullness question rather than a boolean column that does not exist.\n filters[F.locked_report_id] = locked ? { $ne: null } : { $eq: null }\n }\n\n const term = sanitizeSearchTerm(query.q)\n if (term) filters[F.notes] = { $ilike: `%${escapeLikePattern(term)}%` }\n\n // A customer is a property of the project, not of the entry \u2014 `customer_id` on\n // the entry is a denormalised convenience that older rows may not carry \u2014 so\n // the question is answered by resolving the customer's projects first.\n if (typeof query.customerId === 'string' && query.customerId.trim().length > 0) {\n const em = ctx.container.resolve('em') as EntityManager\n const { tenantId, organizationId } = resolveCtxScope(ctx)\n const projects = await em.fork().find(\n StaffTimeProject,\n { customerId: query.customerId.trim(), tenantId, organizationId, deletedAt: null },\n { fields: ['id'] },\n )\n const ids = projects.map((project) => project.id)\n filters[F.time_project_id] = { $in: ids.length > 0 ? ids : [IMPOSSIBLE_ID] }\n }\n\n const tagIds = splitTaskIdList(query.tagIds)\n if (tagIds !== null) {\n const em = ctx.container.resolve('em') as EntityManager\n const { tenantId, organizationId } = resolveCtxScope(ctx)\n const assignments = tagIds.length === 0\n ? []\n : await em.fork().find(\n StaffTimeEntryTag,\n { tagId: { $in: tagIds }, tenantId, organizationId },\n { fields: ['timeEntryId'] },\n )\n const entryIds = Array.from(new Set(assignments.map((row) => row.timeEntryId)))\n filters[F.id] = { $in: entryIds.length > 0 ? entryIds : [IMPOSSIBLE_ID] }\n }\n\n const access = await resolveListProjectAccess(ctx)\n if (access.canManageAll) return filters\n\n const branches: Record<string, unknown>[] = []\n if (access.projectIds.length > 0) {\n branches.push({ [F.time_project_id]: { $in: access.projectIds } })\n }\n if (access.staffMemberId) {\n branches.push({ [F.time_project_id]: { $eq: null }, [F.staff_member_id]: access.staffMemberId })\n }\n if (branches.length === 0) {\n // A caller with no membership and no staff record gets no row at all \u2014 not a\n // redacted one \u2014 so an entry's note and its project never leave the server.\n filters[F.id] = { $in: [IMPOSSIBLE_ID] }\n return filters\n }\n filters.$or = branches\n return filters\n}\n\n/**\n * The base projection, and the reason `rate_override_amount` / `rate_currency_code`\n * are NOT in it.\n *\n * The money gate is `staff.timesheets.rates.view`, and it is applied by the\n * `staff.timesheets-time-entries` response enricher, which is declared\n * `critical: false`. A non-critical enricher that throws or exceeds its timeout\n * leaves the items exactly as the route produced them (`enricher-runner.ts`), so\n * anything the projection selects survives an enricher failure. Selecting the two\n * money columns here and removing them there would therefore fail OPEN under DB\n * contention. They are read back inside the enricher instead, for entitled callers\n * only \u2014 see `lib/timesheets/timeEntryDecoration.ts`.\n */\nexport const timeEntryListFields = [\n F.id,\n F.organization_id,\n F.tenant_id,\n F.staff_member_id,\n F.date,\n F.duration_minutes,\n F.rounded_minutes,\n F.started_at,\n F.ended_at,\n F.notes,\n F.time_project_id,\n F.task_id,\n F.customer_id,\n F.deal_id,\n F.order_id,\n F.is_billable,\n F.locked_report_id,\n F.locked_at,\n F.source,\n F.created_at,\n F.updated_at,\n] as const\n\nexport type { TimeEntryTagSummary } from '../../../lib/timesheets/timeEntryDecoration'\n\nasync function callerHasRatesView(ctx: CrudCtx): Promise<boolean> {\n const userId = ctx.auth?.sub\n if (!userId) return false\n const { tenantId, organizationId } = resolveCtxScope(ctx)\n try {\n const rbac = ctx.container.resolve('rbacService') as RbacServiceLike | undefined\n if (!rbac?.userHasAllFeatures) return false\n return await rbac.userHasAllFeatures(userId, [RATES_FEATURE], { tenantId, organizationId })\n } catch {\n // Fail closed: an unreadable grant set never opens the money fields.\n return false\n }\n}\n\n/**\n * @deprecated The decoration is now the declared `staff.timesheets-time-entries`\n * response enricher (`data/enrichers.ts`), so third-party enrichers for\n * `staff:staff_time_entry` compose with it instead of running blind behind a\n * route-private hook. Kept as a thin, in-place wrapper because it is an exported\n * symbol of this route module; new callers should register an enricher.\n */\nexport async function decorateTimeEntryList(payload: unknown, ctx: CrudCtx): Promise<void> {\n const items = (payload as { items?: unknown })?.items\n if (!Array.isArray(items) || items.length === 0) return\n const { tenantId, organizationId } = resolveCtxScope(ctx)\n await decorateTimeEntryRows(items as Record<string, unknown>[], {\n em: ctx.container.resolve('em') as EntityManager,\n tenantId,\n organizationId,\n canSeeRates: await callerHasRatesView(ctx),\n onError: (err) => logger.error('staff.timesheets.time-entries response decoration failed', { err }),\n })\n}\n\nconst crud = makeCrudRoute({\n metadata: routeMetadata,\n orm: {\n entity: StaffTimeEntry,\n idField: 'id',\n orgField: 'organizationId',\n tenantField: 'tenantId',\n softDeleteField: 'deletedAt',\n },\n events: staffTimeEntryCrudEvents,\n enrichers: { entityId: 'staff:staff_time_entry' },\n indexer: { entityType: 'staff:staff_time_entry' },\n list: {\n schema: listSchema,\n entityId: 'staff:staff_time_entry',\n fields: [...timeEntryListFields],\n sortFieldMap: {\n date: F.date,\n createdAt: F.created_at,\n updatedAt: F.updated_at,\n durationMinutes: F.duration_minutes,\n roundedMinutes: F.rounded_minutes,\n },\n buildFilters: buildScopedTimeEntryListFilters,\n },\n actions: {\n create: {\n commandId: staffTimeEntryCommandIds.create,\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n return parseScopedCommandInput(staffTimeEntryCreateSchema, raw ?? {}, ctx, translate)\n },\n response: ({ result }) => ({ id: result?.timeEntryId ?? null }),\n status: 201,\n },\n update: {\n commandId: staffTimeEntryCommandIds.update,\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n return parseScopedCommandInput(staffTimeEntryUpdateSchema, raw ?? {}, ctx, translate)\n },\n response: () => ({ ok: true }),\n },\n delete: {\n commandId: staffTimeEntryCommandIds.delete,\n schema: rawBodySchema,\n mapInput: async ({ parsed, ctx }) => {\n const { translate } = await resolveTranslations()\n const id = resolveCrudRecordId(parsed, ctx, translate)\n return { id }\n },\n response: () => ({ ok: true }),\n },\n },\n})\n\nexport const GET = crud.GET\nexport const POST = crud.POST\nexport const PUT = crud.PUT\nexport const DELETE = crud.DELETE\n\nconst timeEntryTagSchema = z.object({\n id: z.string().uuid(),\n slug: z.string(),\n label: z.string(),\n color: z.string().nullable(),\n})\n\nconst timeEntryListItemSchema = z.object({\n id: z.string().uuid().nullable().optional(),\n organization_id: z.string().uuid().nullable().optional(),\n tenant_id: z.string().uuid().nullable().optional(),\n staff_member_id: z.string().uuid().nullable().optional(),\n date: z.string().nullable().optional(),\n duration_minutes: z.number().nullable().optional(),\n rounded_minutes: z.number().nullable().optional(),\n started_at: z.string().nullable().optional(),\n ended_at: z.string().nullable().optional(),\n notes: z.string().nullable().optional(),\n time_project_id: z.string().uuid().nullable().optional(),\n task_id: z.string().uuid().nullable().optional(),\n customer_id: z.string().uuid().nullable().optional(),\n deal_id: z.string().uuid().nullable().optional(),\n order_id: z.string().uuid().nullable().optional(),\n is_billable: z.boolean().nullable().optional(),\n /** Present only for a caller holding `staff.timesheets.rates.view`. */\n rate_override_amount: z.union([z.string(), z.number()]).nullable().optional(),\n /** Present only for a caller holding `staff.timesheets.rates.view`. */\n rate_currency_code: z.string().nullable().optional(),\n locked_report_id: z.string().uuid().nullable().optional(),\n locked_at: z.string().nullable().optional(),\n source: z.string().nullable().optional(),\n created_at: z.string().nullable().optional(),\n updated_at: z.string().nullable().optional(),\n /** Alias of `notes`; both keys are returned (see UPGRADE_NOTES.md). */\n description: z.string().nullable().optional(),\n /** Raw minutes rounded by the tenant's rule at write time; the only input to cost (D-7). */\n roundedMinutes: z.number().nullable().optional(),\n isLocked: z.boolean().optional(),\n lockedReportId: z.string().uuid().nullable().optional(),\n tags: z.array(timeEntryTagSchema).optional(),\n /** Present only for a caller holding `staff.timesheets.rates.view`; `null` when non-billable. */\n cost: z.number().nullable().optional(),\n /** Present only for a caller holding `staff.timesheets.rates.view`. */\n currencyCode: z.string().nullable().optional(),\n})\n\nexport const openApi = createStaffCrudOpenApi({\n resourceName: 'TimeEntry',\n pluralName: 'TimeEntries',\n querySchema: listSchema,\n listResponseSchema: createPagedListResponseSchema(timeEntryListItemSchema),\n create: {\n schema: staffTimeEntryCreateSchema,\n description:\n 'Creates a time entry for a staff member. The note is accepted under either `notes` or `description` (`description` wins when both are sent). `roundedMinutes` is derived from the tenant rounding rule at write time and stored; `isBillable` falls back to the project default then the tenant default; `rateCurrencyCode` snapshots the project currency. `tagIds` is applied through the tag assignment command.',\n },\n update: {\n schema: staffTimeEntryUpdateSchema,\n responseSchema: defaultOkResponseSchema,\n description:\n 'Updates a time entry by id. `durationMinutes`, `startedAt` and `endedAt` are reconciled so the stored set never contradicts itself: sending both clocks recomputes the duration from them (a duration sent alongside is advisory), sending one clock with a duration derives the other clock, and sending only a duration shifts `endedAt` while `startedAt` anchors. An `endedAt` earlier than its `startedAt` is stored on the next calendar day (a midnight crossing) and `date` still names the day the work started. Any change to the effective duration recomputes the stored rounded minutes from the tenant rounding rule; changing the project re-snapshots the currency. `tagIds` replaces the entry tag set through the tag assignment commands, which refuse an entry locked in a closed report.',\n },\n del: {\n schema: z.object({ id: z.string().uuid() }),\n responseSchema: defaultOkResponseSchema,\n description: 'Deletes a time entry by id.',\n },\n})\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -9,7 +9,10 @@ import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
|
|
|
9
9
|
import { parseScopedCommandInput } from "@open-mercato/shared/lib/api/scoped";
|
|
10
10
|
import { staffTimeEntryStartTimerSchema } from "../../../../data/validators.js";
|
|
11
11
|
import { createLogger } from "@open-mercato/shared/lib/logger";
|
|
12
|
+
import { readJsonSafe } from "@open-mercato/shared/lib/http/readJsonSafe";
|
|
12
13
|
import { getCommandInterceptorHttpRejection } from "@open-mercato/shared/lib/commands/errors";
|
|
14
|
+
import { STAFF_TIME_TRACKING_RESOURCE_KINDS } from "../../../guards.js";
|
|
15
|
+
import { runTimesheetInterceptors } from "../../_shared/withTimesheetInterceptors.js";
|
|
13
16
|
const logger = createLogger("staff");
|
|
14
17
|
const metadata = {
|
|
15
18
|
POST: { requireAuth: true, requireFeatures: ["staff.timesheets.manage_own"] }
|
|
@@ -28,19 +31,31 @@ async function buildContext(req) {
|
|
|
28
31
|
organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),
|
|
29
32
|
request: req
|
|
30
33
|
};
|
|
31
|
-
return {
|
|
34
|
+
return {
|
|
35
|
+
ctx,
|
|
36
|
+
translate,
|
|
37
|
+
tenantId: scope?.tenantId ?? auth.tenantId ?? null,
|
|
38
|
+
organizationId: scope?.selectedId ?? auth.orgId ?? null
|
|
39
|
+
};
|
|
32
40
|
}
|
|
33
41
|
async function POST(req) {
|
|
34
42
|
try {
|
|
35
|
-
const { ctx, translate } = await buildContext(req);
|
|
36
|
-
const
|
|
37
|
-
|
|
43
|
+
const { ctx, translate, tenantId, organizationId } = await buildContext(req);
|
|
44
|
+
const interceptors = await runTimesheetInterceptors({
|
|
45
|
+
request: req,
|
|
46
|
+
method: "POST",
|
|
47
|
+
scope: { container: ctx.container, userId: ctx.auth?.sub, tenantId, organizationId },
|
|
48
|
+
body: await readJsonSafe(req, {})
|
|
49
|
+
});
|
|
50
|
+
if (!interceptors.ok) return interceptors.response;
|
|
51
|
+
const { session } = interceptors;
|
|
52
|
+
const input = parseScopedCommandInput(staffTimeEntryStartTimerSchema, session.body, ctx, translate);
|
|
38
53
|
const commandBus = ctx.container.resolve("commandBus");
|
|
39
54
|
const { result, logEntry } = await commandBus.execute(
|
|
40
55
|
"staff.timesheets.time_entries.start_timer",
|
|
41
56
|
{ input, ctx }
|
|
42
57
|
);
|
|
43
|
-
const response =
|
|
58
|
+
const response = await session.respond(201, { ok: true, id: result?.timeEntryId ?? null });
|
|
44
59
|
if (logEntry?.undoToken && logEntry?.id && logEntry?.commandId) {
|
|
45
60
|
response.headers.set(
|
|
46
61
|
"x-om-operation",
|
|
@@ -49,7 +64,7 @@ async function POST(req) {
|
|
|
49
64
|
undoToken: logEntry.undoToken,
|
|
50
65
|
commandId: logEntry.commandId,
|
|
51
66
|
actionLabel: logEntry.actionLabel ?? null,
|
|
52
|
-
resourceKind: logEntry.resourceKind ??
|
|
67
|
+
resourceKind: logEntry.resourceKind ?? STAFF_TIME_TRACKING_RESOURCE_KINDS.timeEntry,
|
|
53
68
|
resourceId: logEntry.resourceId ?? result?.timeEntryId ?? null,
|
|
54
69
|
executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : void 0
|
|
55
70
|
})
|
|
@@ -78,7 +93,7 @@ const openApi = {
|
|
|
78
93
|
methods: {
|
|
79
94
|
POST: {
|
|
80
95
|
summary: "Start a timesheet timer",
|
|
81
|
-
description: "Atomically creates a timer-sourced time entry and starts it (sets startedAt and creates the initial work segment) in a single transaction, so a partial failure cannot leave an orphaned, unstarted entry.",
|
|
96
|
+
description: "Atomically creates a timer-sourced time entry and starts it (sets startedAt and creates the initial work segment) in a single transaction, so a partial failure cannot leave an orphaned, unstarted entry. An optional `taskId` is validated and stored by the same write, so starting a timer from a board card or the task drawer takes one request instead of a start followed by a link that can fail; when only `taskId` is sent, the project follows from the task.",
|
|
82
97
|
requestBody: {
|
|
83
98
|
contentType: "application/json",
|
|
84
99
|
schema: staffTimeEntryStartTimerSchema
|
|
@@ -99,7 +114,7 @@ const openApi = {
|
|
|
99
114
|
},
|
|
100
115
|
{
|
|
101
116
|
status: 422,
|
|
102
|
-
description: "Referenced time project not found
|
|
117
|
+
description: "Referenced time project or task not found, out of scope, or the task belongs to another project",
|
|
103
118
|
schema: z.object({ error: z.string() })
|
|
104
119
|
}
|
|
105
120
|
]
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../../src/modules/staff/api/timesheets/time-entries/start-timer/route.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { serializeOperationMetadata } from '@open-mercato/shared/lib/commands/operationMetadata'\nimport type { CommandRuntimeContext, CommandBus } from '@open-mercato/shared/lib/commands'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { parseScopedCommandInput } from '@open-mercato/shared/lib/api/scoped'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { staffTimeEntryStartTimerSchema, type StaffTimeEntryStartTimerInput } from '../../../../data/validators'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'\n\nconst logger = createLogger('staff')\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['staff.timesheets.manage_own'] },\n}\n\nasync function buildContext(\n req: Request\n): Promise<{
|
|
5
|
-
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAClB,SAAS,8BAA8B;AACvC,SAAS,0BAA0B;AACnC,SAAS,0CAA0C;AACnD,SAAS,kCAAkC;AAE3C,SAAS,eAAe,uBAAuB;AAC/C,SAAS,2BAA2B;AACpC,SAAS,+BAA+B;AAExC,SAAS,sCAA0E;AACnF,SAAS,oBAAoB;AAC7B,SAAS,0CAA0C;
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { serializeOperationMetadata } from '@open-mercato/shared/lib/commands/operationMetadata'\nimport type { CommandRuntimeContext, CommandBus } from '@open-mercato/shared/lib/commands'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { parseScopedCommandInput } from '@open-mercato/shared/lib/api/scoped'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { staffTimeEntryStartTimerSchema, type StaffTimeEntryStartTimerInput } from '../../../../data/validators'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'\nimport { STAFF_TIME_TRACKING_RESOURCE_KINDS } from '../../../guards'\nimport { runTimesheetInterceptors } from '../../_shared/withTimesheetInterceptors'\n\nconst logger = createLogger('staff')\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['staff.timesheets.manage_own'] },\n}\n\nasync function buildContext(\n req: Request\n): Promise<{\n ctx: CommandRuntimeContext\n translate: (key: string, fallback?: string) => string\n tenantId: string | null\n organizationId: string | null\n}> {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n if (!auth) throw new CrudHttpError(401, { error: translate('staff.errors.unauthorized', 'Unauthorized') })\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const ctx: CommandRuntimeContext = {\n container,\n auth,\n organizationScope: scope,\n selectedOrganizationId: scope?.selectedId ?? auth.orgId ?? null,\n organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: req,\n }\n return {\n ctx,\n translate,\n tenantId: scope?.tenantId ?? auth.tenantId ?? null,\n organizationId: scope?.selectedId ?? auth.orgId ?? null,\n }\n}\n\nexport async function POST(req: Request) {\n try {\n const { ctx, translate, tenantId, organizationId } = await buildContext(req)\n const interceptors = await runTimesheetInterceptors({\n request: req,\n method: 'POST',\n scope: { container: ctx.container, userId: ctx.auth?.sub, tenantId, organizationId },\n body: await readJsonSafe<Record<string, unknown>>(req, {}),\n })\n if (!interceptors.ok) return interceptors.response\n const { session } = interceptors\n\n const input = parseScopedCommandInput(staffTimeEntryStartTimerSchema, session.body, ctx, translate)\n const commandBus = (ctx.container.resolve('commandBus') as CommandBus)\n const { result, logEntry } = await commandBus.execute<StaffTimeEntryStartTimerInput, { timeEntryId: string }>(\n 'staff.timesheets.time_entries.start_timer',\n { input, ctx },\n )\n const response = await session.respond(201, { ok: true, id: result?.timeEntryId ?? null })\n if (logEntry?.undoToken && logEntry?.id && logEntry?.commandId) {\n response.headers.set(\n 'x-om-operation',\n serializeOperationMetadata({\n id: logEntry.id,\n undoToken: logEntry.undoToken,\n commandId: logEntry.commandId,\n actionLabel: logEntry.actionLabel ?? null,\n resourceKind: logEntry.resourceKind ?? STAFF_TIME_TRACKING_RESOURCE_KINDS.timeEntry,\n resourceId: logEntry.resourceId ?? result?.timeEntryId ?? null,\n executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : undefined,\n }),\n )\n }\n return response\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n const interceptorRejection = getCommandInterceptorHttpRejection(err)\n if (interceptorRejection) {\n return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })\n }\n const { translate } = await resolveTranslations()\n logger.error('staff.timesheets.time-entries.start-timer failed', { err })\n return NextResponse.json(\n { error: translate('staff.timesheets.errors.timerStart', 'Failed to start timer.') },\n { status: 400 },\n )\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Staff',\n summary: 'Start a timesheet timer',\n methods: {\n POST: {\n summary: 'Start a timesheet timer',\n description:\n 'Atomically creates a timer-sourced time entry and starts it (sets startedAt and creates the initial work segment) in a single transaction, so a partial failure cannot leave an orphaned, unstarted entry. An optional `taskId` is validated and stored by the same write, so starting a timer from a board card or the task drawer takes one request instead of a start followed by a link that can fail; when only `taskId` is sent, the project follows from the task.',\n requestBody: {\n contentType: 'application/json',\n schema: staffTimeEntryStartTimerSchema,\n },\n responses: [\n {\n status: 201,\n description: 'Timer started',\n schema: z.object({ ok: z.literal(true), id: z.string().uuid().nullable() }),\n },\n { status: 400, description: 'Invalid payload', schema: z.object({ error: z.string() }) },\n { status: 401, description: 'Unauthorized', schema: z.object({ error: z.string() }) },\n { status: 403, description: 'Forbidden', schema: z.object({ error: z.string() }) },\n {\n status: 409,\n description: 'Another timer is already running for this staff member',\n schema: z.object({ error: z.string() }),\n },\n {\n status: 422,\n description:\n 'Referenced time project or task not found, out of scope, or the task belongs to another project',\n schema: z.object({ error: z.string() }),\n },\n ],\n },\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|