@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
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/staff/commands/timesheets-entries.ts"],
|
|
4
|
-
"sourcesContent": ["import type { CommandHandler } from '@open-mercato/shared/lib/commands'\nimport { registerCommand } from '@open-mercato/shared/lib/commands'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { buildChanges, emitCrudSideEffects, emitCrudUndoSideEffects } from '@open-mercato/shared/lib/commands/helpers'\nimport { makeCreateRedo } from '@open-mercato/shared/lib/commands/redo'\nimport type { CrudIndexerConfig } from '@open-mercato/shared/lib/crud/types'\nimport { StaffTimeEntry, StaffTimeEntrySegment, StaffTimeProject, type StaffTimeEntrySource } from '../data/entities'\nimport { emitStaffEvent } from '../events'\n\nconst timeEntryCrudIndexer: CrudIndexerConfig<StaffTimeEntry> = {\n entityType: 'staff:staff_time_entry',\n}\nimport {\n staffTimeEntryCreateSchema,\n staffTimeEntryStartTimerSchema,\n staffTimeEntryUpdateSchema,\n type StaffTimeEntryCreateInput,\n type StaffTimeEntryStartTimerInput,\n type StaffTimeEntryUpdateInput,\n} from '../data/validators'\nimport { staffTimeEntryCrudEvents } from '../lib/crud'\nimport {\n applyScopeToWhere,\n commandActorScope,\n commandInputScope,\n ensureOrganizationScope,\n ensureTenantScope,\n extractUndoPayload,\n scopeForDecryption,\n scopedStaffSnapshotWhere,\n staffSnapshotDecryptionScope,\n staffSnapshotScopeFromContext,\n staffSnapshotScopeFromSnapshot,\n type StaffSnapshotScope,\n} from './shared'\nimport { getStaffMemberByUserId } from '../lib/staffMemberResolver'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('staff')\n\ntype RbacServiceLike = {\n userHasAllFeatures: (\n userId: string,\n required: string[],\n scope: { tenantId: string | null; organizationId: string | null },\n ) => Promise<boolean>\n}\n\n/**\n * Returns true when the caller holds `staff.timesheets.manage_all`, honoring\n * wildcard ACL grants (`staff.*`, `*`) and the super-admin flag via the cached\n * rbacService. Returns false when no auth context (e.g. system/CLI ctx) so\n * write paths that lack a caller identity are NOT silently elevated.\n */\nasync function callerHasManageAll(ctx: {\n auth?: { sub?: string | null; tenantId?: string | null; orgId?: string | null } | null\n container: { resolve: (token: string) => unknown }\n}): Promise<boolean> {\n const userId = ctx.auth?.sub\n if (!userId) return false\n try {\n const rbac = ctx.container.resolve('rbacService') as RbacServiceLike | undefined\n if (!rbac?.userHasAllFeatures) return false\n return await rbac.userHasAllFeatures(\n userId,\n ['staff.timesheets.manage_all'],\n { tenantId: ctx.auth?.tenantId ?? null, organizationId: ctx.auth?.orgId ?? null },\n )\n } catch {\n return false\n }\n}\n\nasync function resolveCallerStaffMemberId(\n em: EntityManager,\n ctx: { auth?: { sub?: string | null; tenantId?: string | null; orgId?: string | null } | null },\n): Promise<string | null> {\n const userId = ctx.auth?.sub\n if (!userId) return null\n const member = await getStaffMemberByUserId(\n em,\n userId,\n ctx.auth?.tenantId ?? null,\n ctx.auth?.orgId ?? null,\n )\n return member?.id ?? null\n}\n\n/**\n * Verifies the referenced time project exists and is in-scope (same tenant + org,\n * not soft-deleted). Throws 422 if the ID is provided but unresolvable.\n * No-op when projectId is null/undefined (timeProjectId is optional on entries).\n */\nasync function assertTimeProjectInScope(\n em: EntityManager,\n projectId: string | null | undefined,\n tenantId: string,\n organizationId: string,\n): Promise<void> {\n if (!projectId) return\n const exists = await em.findOne(\n StaffTimeProject,\n { id: projectId, tenantId, organizationId, deletedAt: null },\n { fields: ['id'] },\n )\n if (!exists) {\n const { translate } = await resolveTranslations()\n throw new CrudHttpError(422, {\n error: translate('staff.timesheets.errors.projectNotFound', 'Time project not found or not accessible.'),\n fieldErrors: {\n timeProjectId: translate('staff.timesheets.errors.projectNotFound', 'Time project not found or not accessible.'),\n },\n })\n }\n}\n\ntype TimeEntrySnapshot = {\n id: string\n tenantId: string\n organizationId: string\n staffMemberId: string\n date: string\n durationMinutes: number\n startedAt: string | null\n endedAt: string | null\n notes: string | null\n timeProjectId: string | null\n customerId: string | null\n dealId: string | null\n orderId: string | null\n source: string\n deletedAt: string | null\n}\n\ntype TimeEntryUndoPayload = {\n before?: TimeEntrySnapshot | null\n after?: TimeEntrySnapshot | null\n}\n\nasync function loadTimeEntrySnapshot(em: EntityManager, id: string, scope?: StaffSnapshotScope | null): Promise<TimeEntrySnapshot | null> {\n const entry = await findOneWithDecryption(\n em,\n StaffTimeEntry,\n scopedStaffSnapshotWhere(id, scope),\n undefined,\n staffSnapshotDecryptionScope(scope),\n )\n if (!entry) return null\n return {\n id: entry.id,\n tenantId: entry.tenantId,\n organizationId: entry.organizationId,\n staffMemberId: entry.staffMemberId,\n date: entry.date instanceof Date ? entry.date.toISOString().split('T')[0] : String(entry.date),\n durationMinutes: entry.durationMinutes,\n startedAt: entry.startedAt ? entry.startedAt.toISOString() : null,\n endedAt: entry.endedAt ? entry.endedAt.toISOString() : null,\n notes: entry.notes ?? null,\n timeProjectId: entry.timeProjectId ?? null,\n customerId: entry.customerId ?? null,\n dealId: entry.dealId ?? null,\n orderId: entry.orderId ?? null,\n source: entry.source,\n deletedAt: entry.deletedAt ? entry.deletedAt.toISOString() : null,\n }\n}\n\nfunction timeEntrySeedFromSnapshot(snapshot: TimeEntrySnapshot): Record<string, unknown> {\n return {\n id: snapshot.id,\n tenantId: snapshot.tenantId,\n organizationId: snapshot.organizationId,\n staffMemberId: snapshot.staffMemberId,\n date: snapshot.date,\n durationMinutes: snapshot.durationMinutes,\n startedAt: snapshot.startedAt ? new Date(snapshot.startedAt) : null,\n endedAt: snapshot.endedAt ? new Date(snapshot.endedAt) : null,\n notes: snapshot.notes ?? null,\n timeProjectId: snapshot.timeProjectId ?? null,\n customerId: snapshot.customerId ?? null,\n dealId: snapshot.dealId ?? null,\n orderId: snapshot.orderId ?? null,\n source: (snapshot.source ?? 'manual') as StaffTimeEntrySource,\n createdAt: new Date(),\n updatedAt: new Date(),\n deletedAt: null,\n }\n}\n\nconst createTimeEntryCommand: CommandHandler<StaffTimeEntryCreateInput, { timeEntryId: string }> = {\n id: 'staff.timesheets.time_entries.create',\n async execute(rawInput, ctx) {\n const parsed = staffTimeEntryCreateSchema.parse(rawInput)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n commandInputScope(ctx, parsed.tenantId, parsed.organizationId)\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n\n // Ownership enforcement: callers without `staff.timesheets.manage_all`\n // can only create entries attributed to themselves. Silent override\n // (mirrors `bulk/route.ts` behavior) so the request body's staffMemberId\n // can't forge an entry under a colleague's identity.\n let effectiveStaffMemberId = parsed.staffMemberId\n if (!(await callerHasManageAll(ctx))) {\n const callerStaffMemberId = await resolveCallerStaffMemberId(em, ctx)\n if (!callerStaffMemberId) {\n const { translate } = await resolveTranslations()\n throw new CrudHttpError(403, {\n error: translate('staff.timesheets.errors.noStaffMember', 'No staff member linked to your account.'),\n })\n }\n effectiveStaffMemberId = callerStaffMemberId\n }\n\n // Validate referenced timeProjectId is in-scope before persisting.\n // Without this check a foreign or stale UUID would produce a dangling reference.\n await assertTimeProjectInScope(em, parsed.timeProjectId ?? null, parsed.tenantId, parsed.organizationId)\n\n const now = new Date()\n const entry = em.create(StaffTimeEntry, {\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n staffMemberId: effectiveStaffMemberId,\n date: parsed.date,\n durationMinutes: parsed.durationMinutes,\n startedAt: parsed.startedAt ?? null,\n endedAt: parsed.endedAt ?? null,\n notes: parsed.notes ?? null,\n timeProjectId: parsed.timeProjectId ?? null,\n customerId: parsed.customerId ?? null,\n dealId: parsed.dealId ?? null,\n orderId: parsed.orderId ?? null,\n source: parsed.source ?? 'manual',\n createdAt: now,\n updatedAt: now,\n deletedAt: null,\n })\n em.persist(entry)\n await em.flush()\n\n await emitCrudSideEffects({\n dataEngine: ctx.container.resolve('dataEngine'),\n action: 'created',\n entity: entry,\n identifiers: { id: entry.id, organizationId: entry.organizationId, tenantId: entry.tenantId },\n events: staffTimeEntryCrudEvents,\n indexer: timeEntryCrudIndexer,\n })\n\n return { timeEntryId: entry.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const snapshot = await loadTimeEntrySnapshot(em, result.timeEntryId, staffSnapshotScopeFromContext(ctx))\n if (!snapshot) return null\n return { snapshot }\n },\n buildLog: async ({ result, ctx }) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const snapshot = await loadTimeEntrySnapshot(em, result.timeEntryId, staffSnapshotScopeFromContext(ctx))\n if (!snapshot) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('staff.audit.timesheets.time_entries.create', 'Create time entry'),\n resourceKind: 'staff.timesheets.time_entry',\n resourceId: snapshot.id,\n tenantId: snapshot.tenantId,\n organizationId: snapshot.organizationId,\n snapshotAfter: snapshot,\n payload: {\n undo: {\n after: snapshot,\n } satisfies TimeEntryUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<TimeEntryUndoPayload>(logEntry)\n const after = payload?.after\n if (!after) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const entry = await em.findOne(StaffTimeEntry, scopedStaffSnapshotWhere(after.id, staffSnapshotScopeFromSnapshot(after)))\n if (entry) {\n entry.deletedAt = new Date()\n await em.flush()\n\n await emitCrudUndoSideEffects({\n dataEngine: ctx.container.resolve('dataEngine'),\n action: 'deleted',\n entity: entry,\n identifiers: { id: entry.id, organizationId: entry.organizationId, tenantId: entry.tenantId },\n events: staffTimeEntryCrudEvents,\n indexer: timeEntryCrudIndexer,\n })\n }\n },\n redo: makeCreateRedo<StaffTimeEntry, TimeEntrySnapshot, StaffTimeEntryCreateInput, { timeEntryId: string }>({\n entityClass: StaffTimeEntry,\n getSnapshotId: (snapshot) => snapshot.id,\n seedFromSnapshot: timeEntrySeedFromSnapshot,\n buildResult: (entity) => ({ timeEntryId: entity.id }),\n events: staffTimeEntryCrudEvents,\n indexer: timeEntryCrudIndexer,\n }),\n}\n\nconst startTimerCommand: CommandHandler<StaffTimeEntryStartTimerInput, { timeEntryId: string }> = {\n id: 'staff.timesheets.time_entries.start_timer',\n async execute(rawInput, ctx) {\n const parsed = staffTimeEntryStartTimerSchema.parse(rawInput)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n commandInputScope(ctx, parsed.tenantId, parsed.organizationId)\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n\n // Ownership enforcement mirrors createTimeEntryCommand: callers without\n // `staff.timesheets.manage_all` can only start their own timer, so the\n // request body's staffMemberId can't start a timer under a colleague's id.\n let effectiveStaffMemberId = parsed.staffMemberId\n if (!(await callerHasManageAll(ctx))) {\n const callerStaffMemberId = await resolveCallerStaffMemberId(em, ctx)\n if (!callerStaffMemberId) {\n const { translate } = await resolveTranslations()\n throw new CrudHttpError(403, {\n error: translate('staff.timesheets.errors.noStaffMember', 'No staff member linked to your account.'),\n })\n }\n effectiveStaffMemberId = callerStaffMemberId\n }\n\n await assertTimeProjectInScope(em, parsed.timeProjectId ?? null, parsed.tenantId, parsed.organizationId)\n\n const scopeCtx = { tenantId: parsed.tenantId, organizationId: parsed.organizationId }\n\n // Create the timer entry AND start it inside a single transaction so a\n // partial failure can never leave an orphaned, never-started timer entry\n // (issue #3311 \u2014 the legacy two-request create-then-start flow). The\n // single-active-timer invariant (#2855) is re-checked here so a second\n // surface cannot create a parallel running timer for the same staff member.\n const { entry, startedAt } = await em.transactional(async (trx) => {\n const otherRunningEntry = await findOneWithDecryption(\n trx,\n StaffTimeEntry,\n {\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n staffMemberId: effectiveStaffMemberId,\n startedAt: { $ne: null },\n endedAt: null,\n deletedAt: null,\n },\n {},\n scopeCtx,\n )\n if (otherRunningEntry) {\n const { translate } = await resolveTranslations()\n throw new CrudHttpError(409, {\n error: translate(\n 'staff.timesheets.errors.timerAlreadyRunning',\n 'Another timer is already running. Stop it before starting a new one.',\n ),\n })\n }\n\n const startedAt = new Date()\n const entry = trx.create(StaffTimeEntry, {\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n staffMemberId: effectiveStaffMemberId,\n date: parsed.date,\n durationMinutes: 0,\n startedAt,\n endedAt: null,\n notes: parsed.notes ?? null,\n timeProjectId: parsed.timeProjectId ?? null,\n customerId: null,\n dealId: null,\n orderId: null,\n source: 'timer',\n createdAt: startedAt,\n updatedAt: startedAt,\n deletedAt: null,\n })\n // Flush so the DB-generated id is populated before the work segment\n // references it; both writes commit together when the transaction closes.\n await trx.flush()\n\n const segmentData = {\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n timeEntryId: entry.id,\n startedAt,\n segmentType: 'work' as const,\n }\n trx.create(StaffTimeEntrySegment, segmentData as never)\n await trx.flush()\n\n return { entry, startedAt }\n })\n\n await emitCrudSideEffects({\n dataEngine: ctx.container.resolve('dataEngine'),\n action: 'created',\n entity: entry,\n identifiers: { id: entry.id, organizationId: entry.organizationId, tenantId: entry.tenantId },\n events: staffTimeEntryCrudEvents,\n indexer: timeEntryCrudIndexer,\n })\n\n void emitStaffEvent('staff.timesheets.time_entry.timer_started', {\n id: entry.id,\n staffMemberId: effectiveStaffMemberId,\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n startedAt: startedAt.toISOString(),\n }, { persistent: true }).catch((err) => {\n logger.error('staff.timesheets emit timer_started failed', { err })\n })\n\n return { timeEntryId: entry.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const snapshot = await loadTimeEntrySnapshot(em, result.timeEntryId, staffSnapshotScopeFromContext(ctx))\n if (!snapshot) return null\n return { snapshot }\n },\n buildLog: async ({ result, ctx }) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const snapshot = await loadTimeEntrySnapshot(em, result.timeEntryId, staffSnapshotScopeFromContext(ctx))\n if (!snapshot) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('staff.audit.timesheets.time_entries.startTimer', 'Start timer'),\n resourceKind: 'staff.timesheets.time_entry',\n resourceId: snapshot.id,\n tenantId: snapshot.tenantId,\n organizationId: snapshot.organizationId,\n snapshotAfter: snapshot,\n payload: {\n undo: {\n after: snapshot,\n } satisfies TimeEntryUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<TimeEntryUndoPayload>(logEntry)\n const after = payload?.after\n if (!after) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const entry = await em.findOne(StaffTimeEntry, scopedStaffSnapshotWhere(after.id, staffSnapshotScopeFromSnapshot(after)))\n if (entry) {\n entry.deletedAt = new Date()\n await em.flush()\n\n await emitCrudUndoSideEffects({\n dataEngine: ctx.container.resolve('dataEngine'),\n action: 'deleted',\n entity: entry,\n identifiers: { id: entry.id, organizationId: entry.organizationId, tenantId: entry.tenantId },\n events: staffTimeEntryCrudEvents,\n indexer: timeEntryCrudIndexer,\n })\n }\n },\n}\n\nconst updateTimeEntryCommand: CommandHandler<StaffTimeEntryUpdateInput, { timeEntryId: string }> = {\n id: 'staff.timesheets.time_entries.update',\n async prepare(rawInput, ctx) {\n const parsed = staffTimeEntryUpdateSchema.parse(rawInput)\n const em = (ctx.container.resolve('em') as EntityManager)\n const snapshot = await loadTimeEntrySnapshot(em, parsed.id, staffSnapshotScopeFromContext(ctx))\n if (!snapshot) return {}\n return { before: snapshot }\n },\n async execute(rawInput, ctx) {\n const parsed = staffTimeEntryUpdateSchema.parse(rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const scope = commandActorScope(ctx)\n const entry = await findOneWithDecryption(\n em,\n StaffTimeEntry,\n applyScopeToWhere<StaffTimeEntry>({ id: parsed.id, deletedAt: null }, scope),\n undefined,\n scopeForDecryption(scope),\n )\n if (!entry) throw new CrudHttpError(404, { error: 'Time entry not found.' })\n ensureTenantScope(ctx, entry.tenantId)\n ensureOrganizationScope(ctx, entry.organizationId)\n\n // Ownership enforcement: callers without `staff.timesheets.manage_all`\n // can only update entries they own.\n if (!(await callerHasManageAll(ctx))) {\n const callerStaffMemberId = await resolveCallerStaffMemberId(em, ctx)\n if (!callerStaffMemberId || entry.staffMemberId !== callerStaffMemberId) {\n const { translate } = await resolveTranslations()\n throw new CrudHttpError(403, {\n error: translate('staff.timesheets.errors.notOwner', 'You can only manage your own time entries.'),\n })\n }\n }\n\n // Validate referenced timeProjectId is in-scope when it's being changed to a non-null value.\n if (parsed.timeProjectId !== undefined && parsed.timeProjectId !== null) {\n await assertTimeProjectInScope(em, parsed.timeProjectId, entry.tenantId, entry.organizationId)\n }\n\n if (parsed.date !== undefined) entry.date = parsed.date\n if (parsed.durationMinutes !== undefined) entry.durationMinutes = parsed.durationMinutes\n if (parsed.timeProjectId !== undefined) entry.timeProjectId = parsed.timeProjectId ?? null\n if (parsed.customerId !== undefined) entry.customerId = parsed.customerId ?? null\n if (parsed.dealId !== undefined) entry.dealId = parsed.dealId ?? null\n if (parsed.orderId !== undefined) entry.orderId = parsed.orderId ?? null\n if (parsed.notes !== undefined) entry.notes = parsed.notes ?? null\n entry.updatedAt = new Date()\n await em.flush()\n\n await emitCrudSideEffects({\n dataEngine: ctx.container.resolve('dataEngine'),\n action: 'updated',\n entity: entry,\n identifiers: { id: entry.id, organizationId: entry.organizationId, tenantId: entry.tenantId },\n events: staffTimeEntryCrudEvents,\n indexer: timeEntryCrudIndexer,\n })\n\n return { timeEntryId: entry.id }\n },\n buildLog: async ({ snapshots, ctx }) => {\n const before = snapshots.before as TimeEntrySnapshot | undefined\n if (!before) return null\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const after = await loadTimeEntrySnapshot(em, before.id, staffSnapshotScopeFromSnapshot(before))\n if (!after) return null\n const changes = buildChanges(before as unknown as Record<string, unknown>, after as unknown as Record<string, unknown>, [\n 'date',\n 'durationMinutes',\n 'timeProjectId',\n 'customerId',\n 'dealId',\n 'orderId',\n 'notes',\n 'deletedAt',\n ])\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('staff.audit.timesheets.time_entries.update', 'Update time entry'),\n resourceKind: 'staff.timesheets.time_entry',\n resourceId: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n snapshotBefore: before,\n snapshotAfter: after,\n changes,\n payload: {\n undo: {\n before,\n after,\n } satisfies TimeEntryUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<TimeEntryUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const entry = await em.findOne(StaffTimeEntry, scopedStaffSnapshotWhere(before.id, staffSnapshotScopeFromSnapshot(before)))\n if (!entry) return\n entry.date = before.date as unknown as Date\n entry.durationMinutes = before.durationMinutes\n entry.timeProjectId = before.timeProjectId ?? null\n entry.customerId = before.customerId ?? null\n entry.dealId = before.dealId ?? null\n entry.orderId = before.orderId ?? null\n entry.notes = before.notes ?? null\n entry.deletedAt = before.deletedAt ? new Date(before.deletedAt) : null\n entry.updatedAt = new Date()\n await em.flush()\n\n await emitCrudUndoSideEffects({\n dataEngine: ctx.container.resolve('dataEngine'),\n action: 'updated',\n entity: entry,\n identifiers: { id: entry.id, organizationId: entry.organizationId, tenantId: entry.tenantId },\n events: staffTimeEntryCrudEvents,\n indexer: timeEntryCrudIndexer,\n })\n },\n}\n\nconst deleteTimeEntryCommand: CommandHandler<{ id?: string }, { timeEntryId: string }> = {\n id: 'staff.timesheets.time_entries.delete',\n async prepare(input, ctx) {\n const id = input?.id\n if (!id) throw new CrudHttpError(400, { error: 'Time entry id is required.' })\n const em = (ctx.container.resolve('em') as EntityManager)\n const snapshot = await loadTimeEntrySnapshot(em, id, staffSnapshotScopeFromContext(ctx))\n if (!snapshot) return {}\n return { before: snapshot }\n },\n async execute(input, ctx) {\n const id = input?.id\n if (!id) throw new CrudHttpError(400, { error: 'Time entry id is required.' })\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const scope = commandActorScope(ctx)\n const entry = await findOneWithDecryption(\n em,\n StaffTimeEntry,\n applyScopeToWhere<StaffTimeEntry>({ id, deletedAt: null }, scope),\n undefined,\n scopeForDecryption(scope),\n )\n if (!entry) throw new CrudHttpError(404, { error: 'Time entry not found.' })\n ensureTenantScope(ctx, entry.tenantId)\n ensureOrganizationScope(ctx, entry.organizationId)\n\n // Ownership enforcement: callers without `staff.timesheets.manage_all`\n // can only delete entries they own.\n if (!(await callerHasManageAll(ctx))) {\n const callerStaffMemberId = await resolveCallerStaffMemberId(em, ctx)\n if (!callerStaffMemberId || entry.staffMemberId !== callerStaffMemberId) {\n const { translate } = await resolveTranslations()\n throw new CrudHttpError(403, {\n error: translate('staff.timesheets.errors.notOwner', 'You can only manage your own time entries.'),\n })\n }\n }\n\n entry.deletedAt = new Date()\n entry.updatedAt = new Date()\n await em.flush()\n\n await emitCrudSideEffects({\n dataEngine: ctx.container.resolve('dataEngine'),\n action: 'deleted',\n entity: entry,\n identifiers: { id: entry.id, organizationId: entry.organizationId, tenantId: entry.tenantId },\n events: staffTimeEntryCrudEvents,\n indexer: timeEntryCrudIndexer,\n })\n\n return { timeEntryId: entry.id }\n },\n buildLog: async ({ snapshots }) => {\n const before = snapshots.before as TimeEntrySnapshot | undefined\n if (!before) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('staff.audit.timesheets.time_entries.delete', 'Delete time entry'),\n resourceKind: 'staff.timesheets.time_entry',\n resourceId: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n snapshotBefore: before,\n payload: {\n undo: {\n before,\n } satisfies TimeEntryUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<TimeEntryUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let entry = await em.findOne(StaffTimeEntry, scopedStaffSnapshotWhere(before.id, staffSnapshotScopeFromSnapshot(before)))\n if (!entry) {\n entry = em.create(StaffTimeEntry, {\n id: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n staffMemberId: before.staffMemberId,\n date: before.date as unknown as Date,\n durationMinutes: before.durationMinutes,\n startedAt: before.startedAt ? new Date(before.startedAt) : null,\n endedAt: before.endedAt ? new Date(before.endedAt) : null,\n notes: before.notes ?? null,\n timeProjectId: before.timeProjectId ?? null,\n customerId: before.customerId ?? null,\n dealId: before.dealId ?? null,\n orderId: before.orderId ?? null,\n source: (before.source ?? 'manual') as StaffTimeEntrySource,\n deletedAt: null,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n em.persist(entry)\n } else {\n entry.staffMemberId = before.staffMemberId\n entry.date = before.date as unknown as Date\n entry.durationMinutes = before.durationMinutes\n entry.startedAt = before.startedAt ? new Date(before.startedAt) : null\n entry.endedAt = before.endedAt ? new Date(before.endedAt) : null\n entry.notes = before.notes ?? null\n entry.timeProjectId = before.timeProjectId ?? null\n entry.customerId = before.customerId ?? null\n entry.dealId = before.dealId ?? null\n entry.orderId = before.orderId ?? null\n entry.source = (before.source ?? 'manual') as StaffTimeEntrySource\n entry.deletedAt = null\n entry.updatedAt = new Date()\n }\n await em.flush()\n\n await emitCrudUndoSideEffects({\n dataEngine: ctx.container.resolve('dataEngine'),\n action: 'created',\n entity: entry,\n identifiers: { id: entry.id, organizationId: entry.organizationId, tenantId: entry.tenantId },\n events: staffTimeEntryCrudEvents,\n indexer: timeEntryCrudIndexer,\n })\n },\n}\n\nregisterCommand(createTimeEntryCommand)\nregisterCommand(startTimerCommand)\nregisterCommand(updateTimeEntryCommand)\nregisterCommand(deleteTimeEntryCommand)\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["startedAt", "entry"]
|
|
4
|
+
"sourcesContent": ["import { z } from 'zod'\nimport type { CommandBus, CommandHandler, CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport { registerCommand } from '@open-mercato/shared/lib/commands'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { buildChanges, emitCrudSideEffects, emitCrudUndoSideEffects } from '@open-mercato/shared/lib/commands/helpers'\nimport { makeCreateRedo } from '@open-mercato/shared/lib/commands/redo'\nimport type { CrudIndexerConfig } from '@open-mercato/shared/lib/crud/types'\nimport type { ModuleConfigService } from '@open-mercato/core/modules/configs/lib/module-config-service'\nimport {\n StaffTimeEntry,\n StaffTimeEntrySegment,\n StaffTimeProject,\n StaffTimeTask,\n type StaffTimeEntrySource,\n} from '../data/entities'\nimport { emitStaffEvent } from '../events'\nimport { deriveInterval, formatClock, parseClock } from '../lib/time-tracking/interval'\nimport { roundMinutes } from '../lib/time-tracking/rounding'\nimport { resolveBillability } from '../lib/time-tracking/billability'\nimport type { ScopedResolverContext } from '../lib/time-tracking/registries/scope'\nimport {\n DEFAULT_TIME_TRACKING_SETTINGS,\n readTimeTrackingSettings,\n type TimeTrackingSettings,\n} from '../lib/time-tracking/settings'\nimport { resolveProjectAccess, type ProjectAccess } from '../lib/time-tracking/access'\n// Imported for the command ids AND for the registration side effect: the tag\n// commands must be in the registry before this file asks the bus to run one.\nimport { staffTimeTagCommandIds, TAG_TARGET_LOCKED_CODE } from './timesheets-tags'\n\nconst timeEntryCrudIndexer: CrudIndexerConfig<StaffTimeEntry> = {\n entityType: 'staff:staff_time_entry',\n}\nimport {\n staffTimeEntryCreateSchema,\n staffTimeEntryStartTimerSchema,\n staffTimeEntryUpdateSchema,\n type StaffTimeEntryCreateInput,\n type StaffTimeEntryStartTimerInput,\n type StaffTimeEntryUpdateInput,\n} from '../data/validators'\nimport { staffTimeEntryCrudEvents } from '../lib/crud'\nimport {\n applyScopeToWhere,\n commandActorScope,\n commandInputScope,\n ensureOrganizationScope,\n ensureTenantScope,\n extractUndoPayload,\n scopeForDecryption,\n scopedStaffSnapshotWhere,\n staffSnapshotDecryptionScope,\n staffSnapshotScopeFromContext,\n staffSnapshotScopeFromSnapshot,\n type StaffSnapshotScope,\n} from './shared'\nimport { getStaffMemberByUserId } from '../lib/staffMemberResolver'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('staff')\n\ntype ContainerLike = { resolve: (token: string) => unknown }\n\ntype Translate = (key: string, fallback?: string) => string\n\ntype RbacServiceLike = {\n userHasAllFeatures: (\n userId: string,\n required: string[],\n scope: { tenantId: string | null; organizationId: string | null },\n ) => Promise<boolean>\n getGrantedFeatures?: (\n userId: string,\n scope: { tenantId: string | null; organizationId: string | null },\n ) => Promise<string[]>\n}\n\n/**\n * Returns true when the caller holds `staff.timesheets.manage_all`, honoring\n * wildcard ACL grants (`staff.*`, `*`) and the super-admin flag via the cached\n * rbacService. Returns false when no auth context (e.g. system/CLI ctx) so\n * write paths that lack a caller identity are NOT silently elevated.\n */\nasync function callerHasManageAll(ctx: {\n auth?: { sub?: string | null; tenantId?: string | null; orgId?: string | null } | null\n container: { resolve: (token: string) => unknown }\n}): Promise<boolean> {\n const userId = ctx.auth?.sub\n if (!userId) return false\n try {\n const rbac = ctx.container.resolve('rbacService') as RbacServiceLike | undefined\n if (!rbac?.userHasAllFeatures) return false\n return await rbac.userHasAllFeatures(\n userId,\n ['staff.timesheets.manage_all'],\n { tenantId: ctx.auth?.tenantId ?? null, organizationId: ctx.auth?.orgId ?? null },\n )\n } catch {\n return false\n }\n}\n\nasync function resolveCallerStaffMemberId(\n em: EntityManager,\n ctx: { auth?: { sub?: string | null; tenantId?: string | null; orgId?: string | null } | null },\n): Promise<string | null> {\n const userId = ctx.auth?.sub\n if (!userId) return null\n const member = await getStaffMemberByUserId(\n em,\n userId,\n ctx.auth?.tenantId ?? null,\n ctx.auth?.orgId ?? null,\n )\n return member?.id ?? null\n}\n\n/**\n * Verifies the referenced time project exists and is in-scope (same tenant + org,\n * not soft-deleted) and hands the row back \u2014 the write path reads its\n * `billableByDefault` and `currencyCode` to seed the entry. Throws 422 if the ID\n * is provided but unresolvable. Returns null when projectId is null/undefined\n * (timeProjectId is optional on entries).\n */\nasync function requireTimeProjectInScope(\n em: EntityManager,\n projectId: string | null | undefined,\n tenantId: string,\n organizationId: string,\n): Promise<StaffTimeProject | null> {\n if (!projectId) return null\n const project = await em.findOne(StaffTimeProject, {\n id: projectId,\n tenantId,\n organizationId,\n deletedAt: null,\n })\n if (!project) {\n const { translate } = await resolveTranslations()\n throw new CrudHttpError(422, {\n error: translate('staff.timesheets.errors.projectNotFound', 'Time project not found or not accessible.'),\n fieldErrors: {\n timeProjectId: translate('staff.timesheets.errors.projectNotFound', 'Time project not found or not accessible.'),\n },\n })\n }\n return project\n}\n\n/**\n * Same contract as the project check, for `task_id`. A dangling task reference is\n * refused rather than stored: the task rollups on screens 6 and 7 aggregate entries\n * by `task_id`, so a stale or foreign UUID would silently move hours onto another\n * project's card.\n */\nasync function requireTaskInScope(\n em: EntityManager,\n taskId: string | null | undefined,\n tenantId: string,\n organizationId: string,\n): Promise<StaffTimeTask | null> {\n if (!taskId) return null\n const task = await em.findOne(StaffTimeTask, {\n id: taskId,\n tenantId,\n organizationId,\n deletedAt: null,\n })\n if (!task) {\n const { translate } = await resolveTranslations()\n const message = translate('staff.timesheets.errors.taskNotFound', 'Task not found or not accessible.')\n throw new CrudHttpError(422, { error: message, fieldErrors: { taskId: message } })\n }\n return task\n}\n\n/**\n * A task belongs to exactly one project, so an entry naming both must name the\n * same one. Letting them disagree would file the hours under a project whose\n * board never shows the task \u2014 the same rollup corruption the task-scope check\n * above prevents, one level up.\n */\nasync function assertTaskBelongsToProject(\n task: StaffTimeTask | null,\n projectId: string | null,\n): Promise<void> {\n if (timeEntryTaskMatchesProject(task, projectId)) return\n const { translate } = await resolveTranslations()\n const message = translate('staff.timesheets.errors.taskNotFound', 'Task not found or not accessible.')\n throw new CrudHttpError(422, { error: message, fieldErrors: { taskId: message } })\n}\n\n/**\n * The rule the assertion above enforces, as a predicate: a task belongs to\n * exactly one project, so an entry naming both must name the same one. Exported\n * because the grid bulk save validates a whole batch at once and answers with a\n * per-row error list rather than a thrown 422 \u2014 it reuses the rule and only\n * differs in how it reports a violation.\n */\nexport function timeEntryTaskMatchesProject(\n task: { timeProjectId?: string | null } | null | undefined,\n projectId: string | null | undefined,\n): boolean {\n if (!task || !projectId) return true\n return task.timeProjectId === projectId\n}\n\n/**\n * Screen 8 picks a task and lets the project follow from it (\"Projekt i klient\n * wynikaj\u0105 z zadania\"), so a write that names only a task still lands on the\n * project the task belongs to \u2014 and with it the billable and currency defaults.\n * One derivation for every write path, including the grid bulk save.\n */\nexport function resolveTimeEntryProjectId(\n requestedProjectId: string | null | undefined,\n task: { timeProjectId?: string | null } | null | undefined,\n): string | null {\n return requestedProjectId ?? task?.timeProjectId ?? null\n}\n\n/**\n * The billable default chain, in one place: an explicit value wins, then the\n * project's own default \u2014 a fixed-price engagement is non-billable however the\n * tenant normally works \u2014 and the tenant setting only decides what is left.\n *\n * EP-34 moved that chain into the billability registry's built-in resolver; this\n * stays the module's entry point and forwards the scope so a contributed\n * resolver can be consulted ahead of it.\n */\nexport function resolveTimeEntryBillable(params: {\n requested?: boolean | null\n project?: { id?: string | null; billableByDefault?: boolean | null } | null\n settings: TimeTrackingSettings\n scope?: ScopedResolverContext | null\n}): boolean {\n return resolveBillability({\n ...(params.scope ?? {}),\n requested: params.requested,\n project: params.project ?? null,\n settings: params.settings,\n })\n}\n\n/**\n * Rounding and the billable default are tenant settings. A settings read that\n * fails must not fail the write, so it degrades to the documented defaults \u2014\n * the same fail-safe the projects and tasks routes apply to the grace window.\n */\nasync function readEntrySettingsFrom(\n container: ContainerLike,\n tenantId: string,\n): Promise<TimeTrackingSettings> {\n try {\n const configService = container.resolve('moduleConfigService') as ModuleConfigService | undefined\n if (!configService?.getRecord) return DEFAULT_TIME_TRACKING_SETTINGS\n return await readTimeTrackingSettings(configService, { tenantId })\n } catch {\n return DEFAULT_TIME_TRACKING_SETTINGS\n }\n}\n\nasync function readEntrySettings(ctx: CommandRuntimeContext, tenantId: string): Promise<TimeTrackingSettings> {\n return readEntrySettingsFrom(ctx.container, tenantId)\n}\n\n/**\n * The same tenant-settings read, for write paths that run outside a command \u2014\n * the grid bulk save reads it ONCE per request (it is identical for every row)\n * and then seeds rounding and the billable default from that one snapshot.\n */\nexport async function resolveTimeEntrySettings(\n container: ContainerLike,\n tenantId: string,\n): Promise<TimeTrackingSettings> {\n return readEntrySettingsFrom(container, tenantId)\n}\n\n/**\n * The single rounding entry point for write paths that cannot run inside this\n * command file \u2014 the grid bulk save and timer-stop both compute a duration in\n * their own transaction, and D-7 makes `rounded_minutes` the ONLY input to cost,\n * so a duration written without its rounded twin bills nothing. They call this\n * rather than reaching for `roundMinutes` themselves, which keeps the tenant\n * settings read, its fail-safe, and the rule itself in one place.\n */\nexport async function resolveRoundedMinutes(\n container: ContainerLike,\n tenantId: string,\n durationMinutes: number,\n organizationId?: string | null,\n): Promise<number> {\n return roundedMinutesFor(durationMinutes, await readEntrySettingsFrom(container, tenantId), {\n tenantId,\n organizationId: organizationId ?? undefined,\n })\n}\n\nasync function resolveGrantedFeatures(\n ctx: CommandRuntimeContext,\n tenantId: string,\n organizationId: string,\n): Promise<string[]> {\n const userId = ctx.auth?.sub\n if (!userId) return []\n try {\n const rbac = ctx.container.resolve('rbacService') as RbacServiceLike | undefined\n if (!rbac?.getGrantedFeatures) return []\n return (await rbac.getGrantedFeatures(userId, { tenantId, organizationId })) ?? []\n } catch {\n // Fail closed: an unreadable grant set is no grant set, so the caller falls\n // back to their project memberships instead of being treated as a manager.\n return []\n }\n}\n\nasync function resolveEntryProjectAccess(\n em: EntityManager,\n ctx: CommandRuntimeContext,\n tenantId: string,\n organizationId: string,\n settings: TimeTrackingSettings,\n): Promise<ProjectAccess> {\n return resolveProjectAccess({\n em: em.fork(),\n userId: ctx.auth?.sub ?? null,\n tenantId,\n organizationId,\n userFeatures: await resolveGrantedFeatures(ctx, tenantId, organizationId),\n assignmentGraceDays: settings.access.assignmentGraceDays,\n })\n}\n\n/**\n * Every entry write intersects the caller's project access. An entry that names a\n * project is writable by a project manager or an assigned member; an entry with no\n * project belongs to nobody else, so only the member who logged it may touch it.\n * `staff.timesheets.manage_all` stays a superset of both, which is what it has\n * always meant on this route.\n */\nasync function assertEntryProjectAccess(params: {\n access: ProjectAccess\n hasManageAll: boolean\n projectId: string | null\n entryStaffMemberId: string\n}): Promise<void> {\n if (params.hasManageAll || params.access.canManageAll) return\n const { translate } = await resolveTranslations()\n if (params.projectId) {\n if (params.access.projectIds.includes(params.projectId)) return\n throw new CrudHttpError(403, {\n error: translate('staff.timesheets.errors.notAssigned', 'You are not assigned to this project.'),\n })\n }\n if (params.access.staffMemberId && params.access.staffMemberId === params.entryStaffMemberId) return\n throw new CrudHttpError(403, {\n error: translate('staff.timesheets.errors.notOwner', 'You can only manage your own time entries.'),\n })\n}\n\n/**\n * `notes` is the column; `description` is the name every consulting-suite screen\n * uses for it. Both keys are accepted on write and `description` wins when a\n * request carries both \u2014 the precedence UPGRADE_NOTES.md already published, and\n * the one that matches intent: a client sending the new key means it.\n * Returns `undefined` when neither key was supplied, so a PUT that omits both\n * leaves the stored note alone.\n *\n * Exported so the grid bulk save applies the SAME precedence \u2014 it accepts both\n * keys too, and a second interpretation of which one wins is exactly the kind of\n * divergence that makes one screen's edit disappear on another.\n */\nexport function resolveTimeEntryNotesInput(parsed: {\n notes?: string | null\n description?: string | null\n}): string | null | undefined {\n if (parsed.description !== undefined) return parsed.description ?? null\n if (parsed.notes !== undefined) return parsed.notes ?? null\n return undefined\n}\n\n/**\n * `rate_override_amount` is `numeric(14,4)`, which MikroORM surfaces as a string;\n * the validator accepts a JSON number. One conversion point keeps the two apart,\n * for the grid bulk save as much as for this file.\n */\nexport function toStoredTimeEntryRateOverride(amount: number | null | undefined): string | null {\n return amount === null || amount === undefined ? null : String(amount)\n}\n\n/**\n * D-7: `duration_minutes` keeps the RAW value the user entered \u2014 it is what the\n * timesheet and the project hour totals display \u2014 while `rounded_minutes` is\n * derived from the tenant's rounding rule at write time and is the only input to\n * cost. Storing the rounded value rather than deriving it on read is what stops a\n * later settings change from silently restating money that has already been\n * invoiced (risk R1).\n */\nexport function roundedMinutesFor(\n durationMinutes: number,\n settings: TimeTrackingSettings,\n scope?: ScopedResolverContext | null,\n): number {\n return roundMinutes(durationMinutes, settings.rounding, scope)\n}\n\nexport type TimeEntryInterval = {\n startedAt: Date | null\n endedAt: Date | null\n durationMinutes: number\n}\n\n/**\n * `started_at` / `ended_at` are absolute timestamps; `deriveInterval` reasons in\n * wall-clock minutes. Both directions read and write the SAME basis (UTC), so a\n * timestamp that needs no correction survives the round trip untouched.\n */\nfunction clockOfTimestamp(value: Date): string {\n return formatClock(value.getUTCHours() * 60 + value.getUTCMinutes())\n}\n\nfunction timestampOnDayOf(reference: Date, clock: string, dayOffset: number): Date {\n const minutesOfDay = parseClock(clock) ?? 0\n return new Date(\n Date.UTC(\n reference.getUTCFullYear(),\n reference.getUTCMonth(),\n reference.getUTCDate() + dayOffset,\n Math.floor(minutesOfDay / 60),\n minutesOfDay % 60,\n 0,\n 0,\n ),\n )\n}\n\n/**\n * D-8. An end whose clock is earlier than its start's is a midnight crossing, not\n * a validation error, so the end belongs on the NEXT calendar day while `date`\n * keeps naming the day the work started. A caller that already sent the end on\n * the following day (the entry dialog does) is left exactly as it sent it.\n */\nfunction alignEndToStart(startedAt: Date, endedAt: Date, endClock: string, crossesMidnight: boolean): Date {\n if (endedAt.getTime() >= startedAt.getTime()) return endedAt\n return timestampOnDayOf(startedAt, endClock, crossesMidnight ? 1 : 0)\n}\n\n/**\n * US-D3, on the update path: `duration_minutes`, `started_at` and `ended_at` can\n * each be written on their own, and the server must never store a set that\n * contradicts itself \u2014 a two-hour duration on a 09:00\u201310:00 span is a\n * defensible-invoice problem, not a cosmetic one (D-7 bills the rounded twin of\n * whatever duration lands here).\n *\n * The rule, in one line: **the clocks are the observation and the duration is\n * their summary, so the clocks win \u2014 unless the duration is the field the request\n * actually pinned, in which case the untouched clock moves to match it.**\n *\n * - both clocks in the request \u2192 the duration is recomputed from them, and a\n * duration sent alongside is advisory (this is what the entry dialog sends);\n * - one clock plus a duration \u2192 the OTHER clock is derived from that pair;\n * - one clock alone \u2192 the duration is recomputed against the stored counterpart;\n * - the duration alone \u2192 `started_at` anchors and `ended_at` shifts to match;\n * - none of the three \u2192 every one of them is left exactly as stored.\n *\n * Derivation only ever RESTATES a clock that already exists: with one side null\n * there is nothing for a duration to contradict, and manufacturing the missing\n * side would silently stop a running timer.\n *\n * Exported because the grid bulk save writes durations in its own transaction\n * (per-row, pessimistically locked) and cannot go through this command \u2014 it calls\n * the same reconciliation rather than repeating the arithmetic, which is what\n * keeps one rule for one invariant.\n */\nexport function reconcileTimeEntryInterval(params: {\n stored: TimeEntryInterval\n startedAt: Date | null | undefined\n endedAt: Date | null | undefined\n durationMinutes: number | undefined\n}): TimeEntryInterval {\n const startProvided = params.startedAt !== undefined\n const endProvided = params.endedAt !== undefined\n const durationProvided = params.durationMinutes !== undefined\n if (!startProvided && !endProvided && !durationProvided) return params.stored\n\n const startedAt = startProvided ? params.startedAt ?? null : params.stored.startedAt\n const endedAt = endProvided ? params.endedAt ?? null : params.stored.endedAt\n const durationMinutes = durationProvided\n ? (params.durationMinutes as number)\n : params.stored.durationMinutes\n if (!startedAt || !endedAt) return { startedAt, endedAt, durationMinutes }\n\n const startClock = clockOfTimestamp(startedAt)\n const endClock = clockOfTimestamp(endedAt)\n const durationAnchored = durationProvided && !(startProvided && endProvided)\n\n if (durationAnchored && !endProvided) {\n const derived = deriveInterval({ start: startClock, durationMinutes })\n return {\n startedAt,\n endedAt: timestampOnDayOf(startedAt, derived.end ?? endClock, derived.crossesMidnight ? 1 : 0),\n durationMinutes,\n }\n }\n\n if (durationAnchored && !startProvided) {\n const derived = deriveInterval({ end: endClock, durationMinutes })\n return {\n startedAt: timestampOnDayOf(endedAt, derived.start ?? startClock, derived.crossesMidnight ? -1 : 0),\n endedAt,\n durationMinutes,\n }\n }\n\n const derived = deriveInterval({ start: startClock, end: endClock, durationMinutes })\n return {\n startedAt,\n endedAt: alignEndToStart(startedAt, endedAt, endClock, derived.crossesMidnight),\n durationMinutes: derived.durationMinutes ?? durationMinutes,\n }\n}\n\nconst MS_PER_DAY = 86_400_000\n\nfunction toDateOrNull(value: Date | string | null | undefined): Date | null {\n if (value === null || value === undefined) return null\n const date = value instanceof Date ? value : new Date(value)\n return Number.isNaN(date.getTime()) ? null : date\n}\n\nfunction utcDayIndex(value: Date): number {\n return Math.round(\n Date.UTC(value.getUTCFullYear(), value.getUTCMonth(), value.getUTCDate()) / MS_PER_DAY,\n )\n}\n\n/**\n * T4.10(b): moving an entry to another day moves the work with it.\n *\n * `date` names the day the work STARTED (D-8), so an entry re-dated to Tuesday\n * whose clocks stayed on Monday is the same self-contradicting triple T4.9\n * eliminated one field over \u2014 a 09:00\u201310:00 span filed against a day it did not\n * happen on, and a defensible-invoice problem the moment anyone reads the times\n * back. \"Move this entry to Tuesday\" means the whole observation moves.\n *\n * The clocks are SHIFTED by whole days rather than recomposed from the new date,\n * which preserves two things a recomposition would destroy: the stored offset\n * between `date` and the timestamps (for a tenant east of UTC a 01:00 local start\n * legitimately sits on the previous UTC day), and a midnight crossing (an end\n * already on the following day stays on the following day).\n *\n * Two things it never does, both inherited from the reconciliation rule above:\n *\n * - a clock that does not exist is not manufactured, and\n * - a RUNNING timer \u2014 a start with no end \u2014 is left exactly where it is, because\n * re-anchoring its start would restate the elapsed time of work still in\n * progress, and nothing about re-dating the row says the timer began later.\n */\nexport function rebaseTimeEntryIntervalToDate(params: {\n stored: TimeEntryInterval\n fromDate: Date | string | null | undefined\n toDate: Date | string | null | undefined\n}): TimeEntryInterval {\n const fromDate = toDateOrNull(params.fromDate)\n const toDate = toDateOrNull(params.toDate)\n if (!fromDate || !toDate) return params.stored\n\n const { startedAt, endedAt } = params.stored\n if (!startedAt && !endedAt) return params.stored\n if (startedAt && !endedAt) return params.stored\n\n const shiftMs = (utcDayIndex(toDate) - utcDayIndex(fromDate)) * MS_PER_DAY\n if (shiftMs === 0) return params.stored\n return {\n startedAt: startedAt ? new Date(startedAt.getTime() + shiftMs) : null,\n endedAt: endedAt ? new Date(endedAt.getTime() + shiftMs) : null,\n durationMinutes: params.stored.durationMinutes,\n }\n}\n\n/**\n * Reuses the code the tag commands already refuse a frozen entry with, rather\n * than inventing a second one \u2014 a client that learned to handle `time_entry_locked`\n * from `\u2026/tags/entry-assignments` handles it identically everywhere else.\n */\nexport const TIME_ENTRY_LOCKED_CODE = TAG_TARGET_LOCKED_CODE\n\nexport type LockedTimeEntryRef = { id: string; lockedReportId?: string | null }\n\n/**\n * The 409 body names the report that froze the entries so screen 10 note 4 can\n * link straight to it, and lists the offending ids so a grid save can mark those\n * cells read-only instead of showing an opaque failure. `lockedReportId` is kept\n * beside the arrays for shape-compatibility with the tag commands' single-target\n * refusal.\n */\nexport function buildTimeEntryLockedError(\n lockedEntries: LockedTimeEntryRef[],\n translate: Translate,\n): CrudHttpError {\n const lockedEntryIds = lockedEntries.map((entry) => entry.id)\n const lockedReportIds = Array.from(\n new Set(\n lockedEntries\n .map((entry) => entry.lockedReportId)\n .filter((reportId): reportId is string => typeof reportId === 'string' && reportId.length > 0),\n ),\n )\n return new CrudHttpError(409, {\n code: TIME_ENTRY_LOCKED_CODE,\n error:\n lockedEntryIds.length > 1\n ? translate(\n 'staff.time_tracking.entries.errors.entriesLocked',\n 'Some of these time entries are locked in a closed report and cannot be changed. Unlock the report first.',\n )\n : translate(\n 'staff.time_tracking.entries.errors.entryLocked',\n 'This time entry is locked in a closed report and cannot be changed. Unlock the report first.',\n ),\n lockedReportId: lockedReportIds[0] ?? null,\n lockedEntryIds,\n lockedReportIds,\n })\n}\n\n/**\n * The lock gate (risk R3). It sits in the command layer rather than in each write\n * route because the routes are the part that keeps growing: duplicate, copy-day\n * and the grid bulk save all arrived after the lock did, and any one of them\n * could have forgotten a per-route check. Every command that resolves an entry\n * for mutation runs this immediately after the row is loaded, so a field added to\n * the update command later is frozen without anybody remembering to freeze it.\n *\n * No permission lifts it \u2014 not `staff.timesheets.manage_all`. An entry frozen in\n * a closed report has already been billed at those minutes (D-7), so the only way\n * back is the Phase-6 unlock command, which clears `locked_report_id` itself and\n * therefore never reaches this gate.\n */\nexport function assertTimeEntryUnlocked(\n entry: { id: string; lockedReportId?: string | null },\n translate: Translate,\n): void {\n if (!entry.lockedReportId) return\n throw buildTimeEntryLockedError([{ id: entry.id, lockedReportId: entry.lockedReportId }], translate)\n}\n\n/**\n * Tags are written through the tag commands, never through the junction table, so\n * an entry write inherits their guarantees unchanged: unknown ids are refused,\n * re-sending a tag is idempotent, an entry frozen in a closed report is refused\n * with 409 `time_entry_locked`, and each change lands in the audit log with its\n * own undo.\n *\n * `tagIds` is a SET, not an append. The assign command answers with the target's\n * full tag list, and that answer is what tells us which tags must then be\n * unassigned \u2014 so the desired state is reached without this file ever reading or\n * writing `staff_time_entry_tags` itself.\n */\nasync function syncEntryTags(\n ctx: CommandRuntimeContext,\n params: { timeEntryId: string; tenantId: string; organizationId: string; tagIds: string[] },\n): Promise<void> {\n const bus = ctx.container.resolve('commandBus') as CommandBus\n const desired = Array.from(new Set(params.tagIds))\n const scoped = {\n tenantId: params.tenantId,\n organizationId: params.organizationId,\n timeEntryId: params.timeEntryId,\n }\n\n const assigned = await bus.execute<unknown, { tagIds?: string[] }>(staffTimeTagCommandIds.assignEntry, {\n input: { ...scoped, tagIds: desired },\n ctx,\n })\n\n const current = assigned?.result?.tagIds ?? desired\n const stale = current.filter((tagId) => !desired.includes(tagId))\n if (stale.length === 0) return\n\n await bus.execute(staffTimeTagCommandIds.unassignEntry, {\n input: { ...scoped, tagIds: stale },\n ctx,\n })\n}\n\ntype TimeEntrySnapshot = {\n id: string\n tenantId: string\n organizationId: string\n staffMemberId: string\n date: string\n durationMinutes: number\n roundedMinutes: number | null\n startedAt: string | null\n endedAt: string | null\n notes: string | null\n timeProjectId: string | null\n taskId: string | null\n customerId: string | null\n dealId: string | null\n orderId: string | null\n isBillable: boolean\n rateOverrideAmount: string | null\n rateCurrencyCode: string | null\n source: string\n deletedAt: string | null\n}\n\ntype TimeEntryUndoPayload = {\n before?: TimeEntrySnapshot | null\n after?: TimeEntrySnapshot | null\n}\n\nasync function loadTimeEntrySnapshot(em: EntityManager, id: string, scope?: StaffSnapshotScope | null): Promise<TimeEntrySnapshot | null> {\n const entry = await findOneWithDecryption(\n em,\n StaffTimeEntry,\n scopedStaffSnapshotWhere(id, scope),\n undefined,\n staffSnapshotDecryptionScope(scope),\n )\n if (!entry) return null\n return {\n id: entry.id,\n tenantId: entry.tenantId,\n organizationId: entry.organizationId,\n staffMemberId: entry.staffMemberId,\n date: entry.date instanceof Date ? entry.date.toISOString().split('T')[0] : String(entry.date),\n durationMinutes: entry.durationMinutes,\n roundedMinutes: entry.roundedMinutes ?? null,\n startedAt: entry.startedAt ? entry.startedAt.toISOString() : null,\n endedAt: entry.endedAt ? entry.endedAt.toISOString() : null,\n notes: entry.notes ?? null,\n timeProjectId: entry.timeProjectId ?? null,\n taskId: entry.taskId ?? null,\n customerId: entry.customerId ?? null,\n dealId: entry.dealId ?? null,\n orderId: entry.orderId ?? null,\n isBillable: entry.isBillable,\n rateOverrideAmount: entry.rateOverrideAmount ?? null,\n rateCurrencyCode: entry.rateCurrencyCode ?? null,\n source: entry.source,\n deletedAt: entry.deletedAt ? entry.deletedAt.toISOString() : null,\n }\n}\n\nfunction timeEntrySeedFromSnapshot(snapshot: TimeEntrySnapshot): Record<string, unknown> {\n return {\n id: snapshot.id,\n tenantId: snapshot.tenantId,\n organizationId: snapshot.organizationId,\n staffMemberId: snapshot.staffMemberId,\n date: snapshot.date,\n durationMinutes: snapshot.durationMinutes,\n roundedMinutes: snapshot.roundedMinutes ?? null,\n startedAt: snapshot.startedAt ? new Date(snapshot.startedAt) : null,\n endedAt: snapshot.endedAt ? new Date(snapshot.endedAt) : null,\n notes: snapshot.notes ?? null,\n timeProjectId: snapshot.timeProjectId ?? null,\n taskId: snapshot.taskId ?? null,\n customerId: snapshot.customerId ?? null,\n dealId: snapshot.dealId ?? null,\n orderId: snapshot.orderId ?? null,\n isBillable: snapshot.isBillable ?? true,\n rateOverrideAmount: snapshot.rateOverrideAmount ?? null,\n rateCurrencyCode: snapshot.rateCurrencyCode ?? null,\n source: (snapshot.source ?? 'manual') as StaffTimeEntrySource,\n createdAt: new Date(),\n updatedAt: new Date(),\n deletedAt: null,\n }\n}\n\nconst createTimeEntryCommand: CommandHandler<StaffTimeEntryCreateInput, { timeEntryId: string }> = {\n id: 'staff.timesheets.time_entries.create',\n async execute(rawInput, ctx) {\n const parsed = staffTimeEntryCreateSchema.parse(rawInput)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n commandInputScope(ctx, parsed.tenantId, parsed.organizationId)\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n\n // Ownership enforcement: callers without `staff.timesheets.manage_all`\n // can only create entries attributed to themselves. Silent override\n // (mirrors `bulk/route.ts` behavior) so the request body's staffMemberId\n // can't forge an entry under a colleague's identity.\n let effectiveStaffMemberId = parsed.staffMemberId\n if (!(await callerHasManageAll(ctx))) {\n const callerStaffMemberId = await resolveCallerStaffMemberId(em, ctx)\n if (!callerStaffMemberId) {\n const { translate } = await resolveTranslations()\n throw new CrudHttpError(403, {\n error: translate('staff.timesheets.errors.noStaffMember', 'No staff member linked to your account.'),\n })\n }\n effectiveStaffMemberId = callerStaffMemberId\n }\n\n // Validate referenced task and timeProjectId are in-scope before persisting.\n // Without these checks a foreign or stale UUID would produce a dangling reference.\n const task = await requireTaskInScope(em, parsed.taskId ?? null, parsed.tenantId, parsed.organizationId)\n const requestedProjectId = resolveTimeEntryProjectId(parsed.timeProjectId, task)\n const project = await requireTimeProjectInScope(em, requestedProjectId, parsed.tenantId, parsed.organizationId)\n await assertTaskBelongsToProject(task, project?.id ?? null)\n\n const settings = await readEntrySettings(ctx, parsed.tenantId)\n await assertEntryProjectAccess({\n access: await resolveEntryProjectAccess(em, ctx, parsed.tenantId, parsed.organizationId, settings),\n hasManageAll: await callerHasManageAll(ctx),\n projectId: project?.id ?? null,\n entryStaffMemberId: effectiveStaffMemberId,\n })\n\n const now = new Date()\n const entry = em.create(StaffTimeEntry, {\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n staffMemberId: effectiveStaffMemberId,\n date: parsed.date,\n durationMinutes: parsed.durationMinutes,\n roundedMinutes: roundedMinutesFor(parsed.durationMinutes, settings, {\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n }),\n startedAt: parsed.startedAt ?? null,\n endedAt: parsed.endedAt ?? null,\n notes: resolveTimeEntryNotesInput(parsed) ?? null,\n timeProjectId: project?.id ?? null,\n taskId: task?.id ?? null,\n customerId: parsed.customerId ?? null,\n dealId: parsed.dealId ?? null,\n orderId: parsed.orderId ?? null,\n isBillable: resolveTimeEntryBillable({\n requested: parsed.isBillable,\n project,\n settings,\n scope: { tenantId: parsed.tenantId, organizationId: parsed.organizationId },\n }),\n rateOverrideAmount: toStoredTimeEntryRateOverride(parsed.rateOverrideAmount),\n // Snapshotted rather than joined at read time so a later project currency\n // change cannot re-denominate money that has already been reported (D-3).\n rateCurrencyCode: project?.currencyCode ?? null,\n source: parsed.source ?? 'manual',\n createdAt: now,\n updatedAt: now,\n deletedAt: null,\n })\n em.persist(entry)\n await em.flush()\n\n if (parsed.tagIds !== undefined) {\n await syncEntryTags(ctx, {\n timeEntryId: entry.id,\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n tagIds: parsed.tagIds,\n })\n }\n\n await emitCrudSideEffects({\n dataEngine: ctx.container.resolve('dataEngine'),\n action: 'created',\n entity: entry,\n identifiers: { id: entry.id, organizationId: entry.organizationId, tenantId: entry.tenantId },\n events: staffTimeEntryCrudEvents,\n indexer: timeEntryCrudIndexer,\n })\n\n return { timeEntryId: entry.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const snapshot = await loadTimeEntrySnapshot(em, result.timeEntryId, staffSnapshotScopeFromContext(ctx))\n if (!snapshot) return null\n return { snapshot }\n },\n buildLog: async ({ result, ctx }) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const snapshot = await loadTimeEntrySnapshot(em, result.timeEntryId, staffSnapshotScopeFromContext(ctx))\n if (!snapshot) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('staff.audit.timesheets.time_entries.create', 'Create time entry'),\n resourceKind: 'staff.timesheets.time_entry',\n resourceId: snapshot.id,\n tenantId: snapshot.tenantId,\n organizationId: snapshot.organizationId,\n snapshotAfter: snapshot,\n payload: {\n undo: {\n after: snapshot,\n } satisfies TimeEntryUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<TimeEntryUndoPayload>(logEntry)\n const after = payload?.after\n if (!after) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const entry = await em.findOne(StaffTimeEntry, scopedStaffSnapshotWhere(after.id, staffSnapshotScopeFromSnapshot(after)))\n if (entry) {\n entry.deletedAt = new Date()\n await em.flush()\n\n await emitCrudUndoSideEffects({\n dataEngine: ctx.container.resolve('dataEngine'),\n action: 'deleted',\n entity: entry,\n identifiers: { id: entry.id, organizationId: entry.organizationId, tenantId: entry.tenantId },\n events: staffTimeEntryCrudEvents,\n indexer: timeEntryCrudIndexer,\n })\n }\n },\n redo: makeCreateRedo<StaffTimeEntry, TimeEntrySnapshot, StaffTimeEntryCreateInput, { timeEntryId: string }>({\n entityClass: StaffTimeEntry,\n getSnapshotId: (snapshot) => snapshot.id,\n seedFromSnapshot: timeEntrySeedFromSnapshot,\n buildResult: (entity) => ({ timeEntryId: entity.id }),\n events: staffTimeEntryCrudEvents,\n indexer: timeEntryCrudIndexer,\n }),\n}\n\nconst startTimerCommand: CommandHandler<StaffTimeEntryStartTimerInput, { timeEntryId: string }> = {\n id: 'staff.timesheets.time_entries.start_timer',\n async execute(rawInput, ctx) {\n const parsed = staffTimeEntryStartTimerSchema.parse(rawInput)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n commandInputScope(ctx, parsed.tenantId, parsed.organizationId)\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n\n // Ownership enforcement mirrors createTimeEntryCommand: callers without\n // `staff.timesheets.manage_all` can only start their own timer, so the\n // request body's staffMemberId can't start a timer under a colleague's id.\n let effectiveStaffMemberId = parsed.staffMemberId\n if (!(await callerHasManageAll(ctx))) {\n const callerStaffMemberId = await resolveCallerStaffMemberId(em, ctx)\n if (!callerStaffMemberId) {\n const { translate } = await resolveTranslations()\n throw new CrudHttpError(403, {\n error: translate('staff.timesheets.errors.noStaffMember', 'No staff member linked to your account.'),\n })\n }\n effectiveStaffMemberId = callerStaffMemberId\n }\n\n // Starting a timer on a task is ONE write. The task is validated here, in the\n // same command, so the entry is created already carrying it \u2014 the alternative\n // (start, then link) can fail after the timer is running and strand it against\n // the project instead of the task.\n const task = await requireTaskInScope(em, parsed.taskId ?? null, parsed.tenantId, parsed.organizationId)\n // A card knows its task, not always its project, and a task belongs to exactly\n // one project \u2014 so the project follows from the task, exactly as it does when\n // an entry is created from screen 8.\n const requestedProjectId = resolveTimeEntryProjectId(parsed.timeProjectId, task)\n const project = await requireTimeProjectInScope(em, requestedProjectId, parsed.tenantId, parsed.organizationId)\n await assertTaskBelongsToProject(task, project?.id ?? null)\n\n // A timer entry starts at zero minutes, and D-7 makes `rounded_minutes` the\n // only input to cost \u2014 leaving it null here would price the entry at nothing\n // until something else happened to rewrite it. Seeded from the same rule as\n // every other create so timer-stop only ever restates it.\n const settings = await readEntrySettings(ctx, parsed.tenantId)\n\n const scopeCtx = { tenantId: parsed.tenantId, organizationId: parsed.organizationId }\n\n // Create the timer entry AND start it inside a single transaction so a\n // partial failure can never leave an orphaned, never-started timer entry\n // (issue #3311 \u2014 the legacy two-request create-then-start flow). The\n // single-active-timer invariant (#2855) is re-checked here so a second\n // surface cannot create a parallel running timer for the same staff member.\n const { entry, startedAt } = await em.transactional(async (trx) => {\n const otherRunningEntry = await findOneWithDecryption(\n trx,\n StaffTimeEntry,\n {\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n staffMemberId: effectiveStaffMemberId,\n startedAt: { $ne: null },\n endedAt: null,\n deletedAt: null,\n },\n {},\n scopeCtx,\n )\n if (otherRunningEntry) {\n const { translate } = await resolveTranslations()\n throw new CrudHttpError(409, {\n error: translate(\n 'staff.timesheets.errors.timerAlreadyRunning',\n 'Another timer is already running. Stop it before starting a new one.',\n ),\n })\n }\n\n const startedAt = new Date()\n const entry = trx.create(StaffTimeEntry, {\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n staffMemberId: effectiveStaffMemberId,\n date: parsed.date,\n durationMinutes: 0,\n roundedMinutes: roundedMinutesFor(0, settings, {\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n }),\n startedAt,\n endedAt: null,\n notes: parsed.notes ?? null,\n timeProjectId: project?.id ?? null,\n taskId: task?.id ?? null,\n customerId: null,\n dealId: null,\n orderId: null,\n source: 'timer',\n createdAt: startedAt,\n updatedAt: startedAt,\n deletedAt: null,\n })\n // Flush so the DB-generated id is populated before the work segment\n // references it; both writes commit together when the transaction closes.\n await trx.flush()\n\n const segmentData = {\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n timeEntryId: entry.id,\n startedAt,\n segmentType: 'work' as const,\n }\n trx.create(StaffTimeEntrySegment, segmentData as never)\n await trx.flush()\n\n return { entry, startedAt }\n })\n\n await emitCrudSideEffects({\n dataEngine: ctx.container.resolve('dataEngine'),\n action: 'created',\n entity: entry,\n identifiers: { id: entry.id, organizationId: entry.organizationId, tenantId: entry.tenantId },\n events: staffTimeEntryCrudEvents,\n indexer: timeEntryCrudIndexer,\n })\n\n void emitStaffEvent('staff.timesheets.time_entry.timer_started', {\n id: entry.id,\n staffMemberId: effectiveStaffMemberId,\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n startedAt: startedAt.toISOString(),\n }, { persistent: true }).catch((err) => {\n logger.error('staff.timesheets emit timer_started failed', { err })\n })\n\n return { timeEntryId: entry.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const snapshot = await loadTimeEntrySnapshot(em, result.timeEntryId, staffSnapshotScopeFromContext(ctx))\n if (!snapshot) return null\n return { snapshot }\n },\n buildLog: async ({ result, ctx }) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const snapshot = await loadTimeEntrySnapshot(em, result.timeEntryId, staffSnapshotScopeFromContext(ctx))\n if (!snapshot) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('staff.audit.timesheets.time_entries.startTimer', 'Start timer'),\n resourceKind: 'staff.timesheets.time_entry',\n resourceId: snapshot.id,\n tenantId: snapshot.tenantId,\n organizationId: snapshot.organizationId,\n snapshotAfter: snapshot,\n payload: {\n undo: {\n after: snapshot,\n } satisfies TimeEntryUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<TimeEntryUndoPayload>(logEntry)\n const after = payload?.after\n if (!after) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const entry = await em.findOne(StaffTimeEntry, scopedStaffSnapshotWhere(after.id, staffSnapshotScopeFromSnapshot(after)))\n if (entry) {\n entry.deletedAt = new Date()\n await em.flush()\n\n await emitCrudUndoSideEffects({\n dataEngine: ctx.container.resolve('dataEngine'),\n action: 'deleted',\n entity: entry,\n identifiers: { id: entry.id, organizationId: entry.organizationId, tenantId: entry.tenantId },\n events: staffTimeEntryCrudEvents,\n indexer: timeEntryCrudIndexer,\n })\n }\n },\n}\n\nconst updateTimeEntryCommand: CommandHandler<StaffTimeEntryUpdateInput, { timeEntryId: string }> = {\n id: 'staff.timesheets.time_entries.update',\n async prepare(rawInput, ctx) {\n const parsed = staffTimeEntryUpdateSchema.parse(rawInput)\n const em = (ctx.container.resolve('em') as EntityManager)\n const snapshot = await loadTimeEntrySnapshot(em, parsed.id, staffSnapshotScopeFromContext(ctx))\n if (!snapshot) return {}\n return { before: snapshot }\n },\n async execute(rawInput, ctx) {\n const parsed = staffTimeEntryUpdateSchema.parse(rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const scope = commandActorScope(ctx)\n const entry = await findOneWithDecryption(\n em,\n StaffTimeEntry,\n applyScopeToWhere<StaffTimeEntry>({ id: parsed.id, deletedAt: null }, scope),\n undefined,\n scopeForDecryption(scope),\n )\n if (!entry) throw new CrudHttpError(404, { error: 'Time entry not found.' })\n ensureTenantScope(ctx, entry.tenantId)\n ensureOrganizationScope(ctx, entry.organizationId)\n\n // Before ownership, before access, before a single field is read off the\n // request: a frozen entry is frozen for everyone, so nothing below can be\n // reached with a lock in place.\n assertTimeEntryUnlocked(entry, (await resolveTranslations()).translate)\n\n // Ownership enforcement: callers without `staff.timesheets.manage_all`\n // can only update entries they own.\n const hasManageAll = await callerHasManageAll(ctx)\n if (!hasManageAll) {\n const callerStaffMemberId = await resolveCallerStaffMemberId(em, ctx)\n if (!callerStaffMemberId || entry.staffMemberId !== callerStaffMemberId) {\n const { translate } = await resolveTranslations()\n throw new CrudHttpError(403, {\n error: translate('staff.timesheets.errors.notOwner', 'You can only manage your own time entries.'),\n })\n }\n }\n\n const task =\n parsed.taskId !== undefined\n ? await requireTaskInScope(em, parsed.taskId, entry.tenantId, entry.organizationId)\n : null\n\n // A task carries its project (screen 8), so re-pointing an entry at a task\n // moves the entry with it unless the request names a project explicitly.\n const nextProjectId =\n parsed.timeProjectId !== undefined\n ? parsed.timeProjectId ?? null\n : task?.timeProjectId ?? entry.timeProjectId ?? null\n const projectChanged = nextProjectId !== (entry.timeProjectId ?? null)\n const project = projectChanged\n ? await requireTimeProjectInScope(em, nextProjectId, entry.tenantId, entry.organizationId)\n : null\n await assertTaskBelongsToProject(task, nextProjectId)\n\n const settings = await readEntrySettings(ctx, entry.tenantId)\n const access = await resolveEntryProjectAccess(em, ctx, entry.tenantId, entry.organizationId, settings)\n // Both ends of a move are checked: a caller may not lift an entry out of a\n // project they cannot see, nor drop one into a project they cannot see.\n await assertEntryProjectAccess({\n access,\n hasManageAll,\n projectId: entry.timeProjectId ?? null,\n entryStaffMemberId: entry.staffMemberId,\n })\n if (projectChanged) {\n await assertEntryProjectAccess({\n access,\n hasManageAll,\n projectId: nextProjectId,\n entryStaffMemberId: entry.staffMemberId,\n })\n }\n\n const notes = resolveTimeEntryNotesInput(parsed)\n\n // A `date` move re-anchors the STORED clocks first, so what reconciliation\n // reasons about is the entry as it will exist on its new day. Order matters:\n // clocks the request sends explicitly still win over the rebased ones (the\n // entry dialog rebuilds all three from the new date and would otherwise be\n // shifted twice), while clocks it omits travel with the entry instead of\n // being stranded on the old day.\n const stored = rebaseTimeEntryIntervalToDate({\n stored: {\n startedAt: entry.startedAt ?? null,\n endedAt: entry.endedAt ?? null,\n durationMinutes: entry.durationMinutes,\n },\n fromDate: entry.date,\n toDate: parsed.date,\n })\n\n // The three time fields are settled together, before anything is assigned, so\n // no combination of them can be stored contradicting itself (US-D3, D-8).\n const interval = reconcileTimeEntryInterval({\n stored,\n startedAt: parsed.startedAt,\n endedAt: parsed.endedAt,\n durationMinutes: parsed.durationMinutes,\n })\n\n if (parsed.date !== undefined) entry.date = parsed.date\n if (interval.startedAt !== (entry.startedAt ?? null)) entry.startedAt = interval.startedAt\n if (interval.endedAt !== (entry.endedAt ?? null)) entry.endedAt = interval.endedAt\n if (parsed.durationMinutes !== undefined || interval.durationMinutes !== entry.durationMinutes) {\n entry.durationMinutes = interval.durationMinutes\n // Raw minutes moved \u2014 because the duration was written, or because the\n // clocks were edited and restated it \u2014 so the frozen rounded value is\n // recomputed from the tenant's current rule. D-7 makes it the only input to\n // cost, so a clock edit that changes the length of the work and leaves the\n // rounded twin behind would mis-bill it.\n entry.roundedMinutes = roundedMinutesFor(interval.durationMinutes, settings, {\n tenantId: entry.tenantId,\n organizationId: entry.organizationId,\n })\n }\n if (projectChanged) {\n entry.timeProjectId = nextProjectId\n entry.rateCurrencyCode = project?.currencyCode ?? null\n }\n if (parsed.taskId !== undefined) entry.taskId = task?.id ?? null\n if (parsed.customerId !== undefined) entry.customerId = parsed.customerId ?? null\n if (parsed.dealId !== undefined) entry.dealId = parsed.dealId ?? null\n if (parsed.orderId !== undefined) entry.orderId = parsed.orderId ?? null\n if (parsed.isBillable !== undefined) entry.isBillable = parsed.isBillable\n if (parsed.rateOverrideAmount !== undefined) {\n entry.rateOverrideAmount = toStoredTimeEntryRateOverride(parsed.rateOverrideAmount)\n }\n if (notes !== undefined) entry.notes = notes\n entry.updatedAt = new Date()\n await em.flush()\n\n if (parsed.tagIds !== undefined) {\n await syncEntryTags(ctx, {\n timeEntryId: entry.id,\n tenantId: entry.tenantId,\n organizationId: entry.organizationId,\n tagIds: parsed.tagIds,\n })\n }\n\n await emitCrudSideEffects({\n dataEngine: ctx.container.resolve('dataEngine'),\n action: 'updated',\n entity: entry,\n identifiers: { id: entry.id, organizationId: entry.organizationId, tenantId: entry.tenantId },\n events: staffTimeEntryCrudEvents,\n indexer: timeEntryCrudIndexer,\n })\n\n return { timeEntryId: entry.id }\n },\n buildLog: async ({ snapshots, ctx }) => {\n const before = snapshots.before as TimeEntrySnapshot | undefined\n if (!before) return null\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const after = await loadTimeEntrySnapshot(em, before.id, staffSnapshotScopeFromSnapshot(before))\n if (!after) return null\n const changes = buildChanges(before as unknown as Record<string, unknown>, after as unknown as Record<string, unknown>, [\n 'date',\n 'durationMinutes',\n 'roundedMinutes',\n // A time correction is exactly the edit somebody will later have to justify\n // on an invoice, so the clocks are named in the trail beside the minutes.\n 'startedAt',\n 'endedAt',\n 'timeProjectId',\n 'taskId',\n 'customerId',\n 'dealId',\n 'orderId',\n 'isBillable',\n 'rateOverrideAmount',\n 'rateCurrencyCode',\n 'notes',\n 'deletedAt',\n ])\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('staff.audit.timesheets.time_entries.update', 'Update time entry'),\n resourceKind: 'staff.timesheets.time_entry',\n resourceId: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n snapshotBefore: before,\n snapshotAfter: after,\n changes,\n payload: {\n undo: {\n before,\n after,\n } satisfies TimeEntryUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<TimeEntryUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const entry = await em.findOne(StaffTimeEntry, scopedStaffSnapshotWhere(before.id, staffSnapshotScopeFromSnapshot(before)))\n if (!entry) return\n entry.date = before.date as unknown as Date\n entry.durationMinutes = before.durationMinutes\n entry.roundedMinutes = before.roundedMinutes ?? null\n // Restored beside the minutes they belong to: now that an update can move the\n // clocks, an undo that left them where the edit put them would hand back an\n // entry whose duration and span disagree.\n entry.startedAt = before.startedAt ? new Date(before.startedAt) : null\n entry.endedAt = before.endedAt ? new Date(before.endedAt) : null\n entry.timeProjectId = before.timeProjectId ?? null\n entry.taskId = before.taskId ?? null\n entry.customerId = before.customerId ?? null\n entry.dealId = before.dealId ?? null\n entry.orderId = before.orderId ?? null\n entry.isBillable = before.isBillable ?? true\n entry.rateOverrideAmount = before.rateOverrideAmount ?? null\n entry.rateCurrencyCode = before.rateCurrencyCode ?? null\n entry.notes = before.notes ?? null\n entry.deletedAt = before.deletedAt ? new Date(before.deletedAt) : null\n entry.updatedAt = new Date()\n await em.flush()\n\n await emitCrudUndoSideEffects({\n dataEngine: ctx.container.resolve('dataEngine'),\n action: 'updated',\n entity: entry,\n identifiers: { id: entry.id, organizationId: entry.organizationId, tenantId: entry.tenantId },\n events: staffTimeEntryCrudEvents,\n indexer: timeEntryCrudIndexer,\n })\n },\n}\n\nconst deleteTimeEntryCommand: CommandHandler<{ id?: string }, { timeEntryId: string }> = {\n id: 'staff.timesheets.time_entries.delete',\n async prepare(input, ctx) {\n const id = input?.id\n if (!id) throw new CrudHttpError(400, { error: 'Time entry id is required.' })\n const em = (ctx.container.resolve('em') as EntityManager)\n const snapshot = await loadTimeEntrySnapshot(em, id, staffSnapshotScopeFromContext(ctx))\n if (!snapshot) return {}\n return { before: snapshot }\n },\n async execute(input, ctx) {\n const id = input?.id\n if (!id) throw new CrudHttpError(400, { error: 'Time entry id is required.' })\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const scope = commandActorScope(ctx)\n const entry = await findOneWithDecryption(\n em,\n StaffTimeEntry,\n applyScopeToWhere<StaffTimeEntry>({ id, deletedAt: null }, scope),\n undefined,\n scopeForDecryption(scope),\n )\n if (!entry) throw new CrudHttpError(404, { error: 'Time entry not found.' })\n ensureTenantScope(ctx, entry.tenantId)\n ensureOrganizationScope(ctx, entry.organizationId)\n\n // A closed report renders these minutes; deleting the row would leave the\n // report totalling hours that no longer exist.\n assertTimeEntryUnlocked(entry, (await resolveTranslations()).translate)\n\n // Ownership enforcement: callers without `staff.timesheets.manage_all`\n // can only delete entries they own.\n const hasManageAll = await callerHasManageAll(ctx)\n if (!hasManageAll) {\n const callerStaffMemberId = await resolveCallerStaffMemberId(em, ctx)\n if (!callerStaffMemberId || entry.staffMemberId !== callerStaffMemberId) {\n const { translate } = await resolveTranslations()\n throw new CrudHttpError(403, {\n error: translate('staff.timesheets.errors.notOwner', 'You can only manage your own time entries.'),\n })\n }\n }\n\n const settings = await readEntrySettings(ctx, entry.tenantId)\n await assertEntryProjectAccess({\n access: await resolveEntryProjectAccess(em, ctx, entry.tenantId, entry.organizationId, settings),\n hasManageAll,\n projectId: entry.timeProjectId ?? null,\n entryStaffMemberId: entry.staffMemberId,\n })\n\n entry.deletedAt = new Date()\n entry.updatedAt = new Date()\n await em.flush()\n\n await emitCrudSideEffects({\n dataEngine: ctx.container.resolve('dataEngine'),\n action: 'deleted',\n entity: entry,\n identifiers: { id: entry.id, organizationId: entry.organizationId, tenantId: entry.tenantId },\n events: staffTimeEntryCrudEvents,\n indexer: timeEntryCrudIndexer,\n })\n\n return { timeEntryId: entry.id }\n },\n buildLog: async ({ snapshots }) => {\n const before = snapshots.before as TimeEntrySnapshot | undefined\n if (!before) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('staff.audit.timesheets.time_entries.delete', 'Delete time entry'),\n resourceKind: 'staff.timesheets.time_entry',\n resourceId: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n snapshotBefore: before,\n payload: {\n undo: {\n before,\n } satisfies TimeEntryUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<TimeEntryUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let entry = await em.findOne(StaffTimeEntry, scopedStaffSnapshotWhere(before.id, staffSnapshotScopeFromSnapshot(before)))\n if (!entry) {\n entry = em.create(StaffTimeEntry, {\n id: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n staffMemberId: before.staffMemberId,\n date: before.date as unknown as Date,\n durationMinutes: before.durationMinutes,\n roundedMinutes: before.roundedMinutes ?? null,\n startedAt: before.startedAt ? new Date(before.startedAt) : null,\n endedAt: before.endedAt ? new Date(before.endedAt) : null,\n notes: before.notes ?? null,\n timeProjectId: before.timeProjectId ?? null,\n taskId: before.taskId ?? null,\n customerId: before.customerId ?? null,\n dealId: before.dealId ?? null,\n orderId: before.orderId ?? null,\n isBillable: before.isBillable ?? true,\n rateOverrideAmount: before.rateOverrideAmount ?? null,\n rateCurrencyCode: before.rateCurrencyCode ?? null,\n source: (before.source ?? 'manual') as StaffTimeEntrySource,\n deletedAt: null,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n em.persist(entry)\n } else {\n entry.staffMemberId = before.staffMemberId\n entry.date = before.date as unknown as Date\n entry.durationMinutes = before.durationMinutes\n entry.roundedMinutes = before.roundedMinutes ?? null\n entry.startedAt = before.startedAt ? new Date(before.startedAt) : null\n entry.endedAt = before.endedAt ? new Date(before.endedAt) : null\n entry.notes = before.notes ?? null\n entry.timeProjectId = before.timeProjectId ?? null\n entry.taskId = before.taskId ?? null\n entry.customerId = before.customerId ?? null\n entry.dealId = before.dealId ?? null\n entry.orderId = before.orderId ?? null\n entry.isBillable = before.isBillable ?? true\n entry.rateOverrideAmount = before.rateOverrideAmount ?? null\n entry.rateCurrencyCode = before.rateCurrencyCode ?? null\n entry.source = (before.source ?? 'manual') as StaffTimeEntrySource\n entry.deletedAt = null\n entry.updatedAt = new Date()\n }\n await em.flush()\n\n await emitCrudUndoSideEffects({\n dataEngine: ctx.container.resolve('dataEngine'),\n action: 'created',\n entity: entry,\n identifiers: { id: entry.id, organizationId: entry.organizationId, tenantId: entry.tenantId },\n events: staffTimeEntryCrudEvents,\n indexer: timeEntryCrudIndexer,\n })\n },\n}\n\nexport const staffTimeEntryDuplicateSchema = z.object({\n id: z.string().uuid(),\n // The copy usually lands on another day (US-D6 \"log the same thing again\"), so\n // the caller may move it; omitted, it keeps the source's day.\n date: z.coerce.date().optional(),\n // US-D6 ends \"\u2026I adjust the duration and the date\", so both adjustments travel\n // with the copy request rather than costing a second round trip that would\n // leave the unadjusted duration visible in between. Omitted, the copy keeps the\n // source's minutes. The rounded twin is always recomputed by the create command\n // from the tenant rule, so an overridden duration can never carry the source's\n // stale `rounded_minutes` into a new entry (D-7).\n durationMinutes: z.number().int().min(0).max(1440).optional(),\n // Tags are NOT read off the source here: this file writes tags only through the\n // tag commands and never touches `staff_time_entry_tags`, so the caller \u2014 which\n // already has the source's `tags[]` from the list response \u2014 passes them on.\n tagIds: z.array(z.string().uuid()).max(50).optional(),\n})\n\nexport type StaffTimeEntryDuplicateInput = z.infer<typeof staffTimeEntryDuplicateSchema>\n\n/**\n * US-D6. The fourth write path TC-TT-018 exercises. It resolves the SOURCE entry\n * and runs it past the lock gate before copying: screen 10 note 4 hides every row\n * action on a locked row, so an API that happily duplicated one would be a side\n * door back into a frozen report's numbers.\n *\n * The copy itself is delegated to the create command rather than re-implemented,\n * so it inherits ownership enforcement, project access, the billable/currency\n * defaults, rounding (D-7), tag syncing and the audit entry unchanged.\n */\nconst duplicateTimeEntryCommand: CommandHandler<StaffTimeEntryDuplicateInput, { timeEntryId: string }> = {\n id: 'staff.timesheets.time_entries.duplicate',\n async execute(rawInput, ctx) {\n const parsed = staffTimeEntryDuplicateSchema.parse(rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const scope = commandActorScope(ctx)\n const source = await findOneWithDecryption(\n em,\n StaffTimeEntry,\n applyScopeToWhere<StaffTimeEntry>({ id: parsed.id, deletedAt: null }, scope),\n undefined,\n scopeForDecryption(scope),\n )\n if (!source) throw new CrudHttpError(404, { error: 'Time entry not found.' })\n ensureTenantScope(ctx, source.tenantId)\n ensureOrganizationScope(ctx, source.organizationId)\n\n assertTimeEntryUnlocked(source, (await resolveTranslations()).translate)\n\n const bus = ctx.container.resolve('commandBus') as CommandBus\n const created = await bus.execute<unknown, { timeEntryId: string }>(createTimeEntryCommand.id, {\n input: {\n tenantId: source.tenantId,\n organizationId: source.organizationId,\n staffMemberId: source.staffMemberId,\n date: parsed.date ?? source.date,\n durationMinutes: parsed.durationMinutes ?? source.durationMinutes,\n timeProjectId: source.timeProjectId ?? null,\n taskId: source.taskId ?? null,\n customerId: source.customerId ?? null,\n dealId: source.dealId ?? null,\n orderId: source.orderId ?? null,\n isBillable: source.isBillable,\n rateOverrideAmount:\n source.rateOverrideAmount === null || source.rateOverrideAmount === undefined\n ? null\n : Number(source.rateOverrideAmount),\n description: source.notes ?? null,\n // A copy is typed, not timed: carrying `started_at`/`ended_at` over would\n // manufacture an overlap with the entry it was copied from (US-D7).\n source: 'manual' as StaffTimeEntrySource,\n ...(parsed.tagIds !== undefined ? { tagIds: parsed.tagIds } : {}),\n },\n ctx,\n })\n\n const timeEntryId = created?.result?.timeEntryId\n if (!timeEntryId) throw new CrudHttpError(400, { error: 'Failed to duplicate time entry.' })\n return { timeEntryId }\n },\n}\n\nexport const staffTimeEntryReapplyRoundingSchema = z.object({\n tenantId: z.string().uuid(),\n organizationId: z.string().uuid(),\n entryIds: z.array(z.string().uuid()).min(1).max(500),\n})\n\nexport type StaffTimeEntryReapplyRoundingInput = z.infer<typeof staffTimeEntryReapplyRoundingSchema>\n\nexport type StaffTimeEntryReapplyRoundingResult = {\n /** Entries whose `rounded_minutes` actually moved. */\n updatedCount: number\n /** Entries already at the current rule \u2014 read, compared, left alone. */\n unchangedCount: number\n /** Requested ids that were locked, deleted or out of scope, and therefore never loaded. */\n skippedCount: number\n}\n\n/**\n * Retroactive rounding (T7.3, screen 16 note 3) \u2014 restates `rounded_minutes` on a\n * batch of existing entries under the tenant's current rule.\n *\n * Three properties, in the order they matter:\n *\n * 1. **A locked entry is unreachable, not merely skipped.** `lockedReportId: null`\n * is part of the WHERE clause, so an entry frozen into a closed report is never\n * loaded, never compared and never assigned. The guarantee is structural: there\n * is no branch in this handler that could write one.\n * 2. **It is an explicit, batched administrative action**, driven by the\n * `reapply-rounding` ProgressJob worker, never a side effect of saving the\n * settings form. Rewriting billing history silently is exactly the failure the\n * spec's risk R1 is about.\n * 3. **It writes one derived column and nothing else.** No clock is re-anchored,\n * no duration is touched \u2014 `duration_minutes` remains the raw observation.\n *\n * Reachable only by a privileged actor: the queue worker (`systemActor`) or a\n * super-admin. An interactive caller cannot rewrite a tenant's history through the\n * command bus by guessing the id; the route that enqueues the job is the gate, and\n * it is gated on `staff.timesheets.settings.manage`.\n */\nconst reapplyRoundingCommand: CommandHandler<\n StaffTimeEntryReapplyRoundingInput,\n StaffTimeEntryReapplyRoundingResult\n> = {\n id: 'staff.timesheets.time_entries.reapply_rounding',\n async execute(input, ctx) {\n const parsed = staffTimeEntryReapplyRoundingSchema.parse(input ?? {})\n if (ctx.systemActor !== true && ctx.auth?.isSuperAdmin !== true) {\n const { translate } = await resolveTranslations()\n throw new CrudHttpError(403, { error: translate('staff.errors.forbidden', 'Forbidden') })\n }\n\n const scope = commandInputScope(ctx, parsed.tenantId, parsed.organizationId)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const settings = await readEntrySettings(ctx, parsed.tenantId)\n\n const entries = await em.find(\n StaffTimeEntry,\n applyScopeToWhere<StaffTimeEntry>(\n {\n id: { $in: parsed.entryIds },\n deletedAt: null,\n // Non-negotiable: a frozen entry has already been billed at the value it\n // carries, so it is excluded at the query, not at an `if`.\n lockedReportId: null,\n },\n scope,\n ),\n )\n\n const changed: StaffTimeEntry[] = []\n let unchangedCount = 0\n for (const entry of entries) {\n const next = roundedMinutesFor(entry.durationMinutes, settings, {\n tenantId: entry.tenantId,\n organizationId: entry.organizationId,\n })\n if (entry.roundedMinutes === next) {\n unchangedCount += 1\n continue\n }\n entry.roundedMinutes = next\n entry.updatedAt = new Date()\n changed.push(entry)\n }\n\n if (changed.length > 0) await em.flush()\n\n const dataEngine = ctx.container.resolve('dataEngine') as Parameters<\n typeof emitCrudSideEffects\n >[0]['dataEngine']\n for (const entry of changed) {\n await emitCrudSideEffects({\n dataEngine,\n action: 'updated',\n entity: entry,\n identifiers: { id: entry.id, organizationId: entry.organizationId, tenantId: entry.tenantId },\n events: staffTimeEntryCrudEvents,\n indexer: timeEntryCrudIndexer,\n })\n }\n\n return {\n updatedCount: changed.length,\n unchangedCount,\n skippedCount: Math.max(0, parsed.entryIds.length - entries.length),\n }\n },\n}\n\nregisterCommand(createTimeEntryCommand)\nregisterCommand(startTimerCommand)\nregisterCommand(updateTimeEntryCommand)\nregisterCommand(deleteTimeEntryCommand)\nregisterCommand(duplicateTimeEntryCommand)\nregisterCommand(reapplyRoundingCommand)\n\nexport const staffTimeEntryCommandIds = {\n create: createTimeEntryCommand.id,\n startTimer: startTimerCommand.id,\n update: updateTimeEntryCommand.id,\n delete: deleteTimeEntryCommand.id,\n duplicate: duplicateTimeEntryCommand.id,\n reapplyRounding: reapplyRoundingCommand.id,\n} as const\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["derived", "startedAt", "entry"]
|
|
7
7
|
}
|