@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
|
@@ -10,14 +10,16 @@ import type { EntityManager } from '@mikro-orm/postgresql'
|
|
|
10
10
|
import { LockMode } from '@mikro-orm/core'
|
|
11
11
|
import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
|
|
12
12
|
import { StaffTimeEntry, StaffTimeEntrySegment } from '../../../../../data/entities'
|
|
13
|
+
import { assertTimeEntryUnlocked, resolveRoundedMinutes } from '../../../../../commands/timesheets-entries'
|
|
13
14
|
import { getStaffMemberByUserId } from '../../../../../lib/staffMemberResolver'
|
|
14
15
|
import {
|
|
15
|
-
|
|
16
|
+
STAFF_TIME_TRACKING_RESOURCE_KINDS,
|
|
16
17
|
runStaffMutationGuardAfterSuccess,
|
|
17
18
|
runStaffMutationGuards,
|
|
18
19
|
} from '../../../../guards'
|
|
19
20
|
import { emitStaffEvent } from '../../../../../events'
|
|
20
21
|
import { invalidateStaffTimeEntryCache } from '../../../../../lib/timesheets/timeEntryCacheInvalidation'
|
|
22
|
+
import { runTimesheetInterceptors } from '../../../_shared/withTimesheetInterceptors'
|
|
21
23
|
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
22
24
|
|
|
23
25
|
const logger = createLogger('staff')
|
|
@@ -51,6 +53,14 @@ export async function POST(req: Request) {
|
|
|
51
53
|
throw new CrudHttpError(400, { error: translate('staff.errors.missingScope', 'Missing tenant or organization scope.') })
|
|
52
54
|
}
|
|
53
55
|
|
|
56
|
+
const interceptors = await runTimesheetInterceptors({
|
|
57
|
+
request: req,
|
|
58
|
+
method: 'POST',
|
|
59
|
+
scope: { container, userId: auth.sub, tenantId, organizationId },
|
|
60
|
+
})
|
|
61
|
+
if (!interceptors.ok) return interceptors.response
|
|
62
|
+
const { session } = interceptors
|
|
63
|
+
|
|
54
64
|
const em = (container.resolve('em') as EntityManager).fork()
|
|
55
65
|
const scopeCtx = { tenantId, organizationId }
|
|
56
66
|
|
|
@@ -75,19 +85,22 @@ export async function POST(req: Request) {
|
|
|
75
85
|
throw new CrudHttpError(403, { error: translate('staff.timesheets.errors.notOwner', 'You can only manage your own time entries.') })
|
|
76
86
|
}
|
|
77
87
|
|
|
88
|
+
// Stopping a timer rewrites `duration_minutes`, which is exactly what a
|
|
89
|
+
// closed report froze — same gate as the entries command.
|
|
90
|
+
assertTimeEntryUnlocked(entry, translate)
|
|
91
|
+
|
|
78
92
|
const guardResult = await runStaffMutationGuards(
|
|
79
93
|
container,
|
|
80
94
|
{
|
|
81
95
|
tenantId,
|
|
82
96
|
organizationId,
|
|
83
97
|
userId: auth.sub ?? '',
|
|
84
|
-
resourceKind:
|
|
98
|
+
resourceKind: STAFF_TIME_TRACKING_RESOURCE_KINDS.timeEntry,
|
|
85
99
|
resourceId: entry.id,
|
|
86
100
|
operation: 'update',
|
|
87
101
|
requestMethod: req.method,
|
|
88
102
|
requestHeaders: req.headers,
|
|
89
103
|
},
|
|
90
|
-
resolveUserFeatures(auth),
|
|
91
104
|
)
|
|
92
105
|
if (!guardResult.ok) {
|
|
93
106
|
return NextResponse.json(
|
|
@@ -111,6 +124,9 @@ export async function POST(req: Request) {
|
|
|
111
124
|
if (!lockedEntry) {
|
|
112
125
|
throw new CrudHttpError(404, { error: translate('staff.timesheets.errors.entryNotFound', 'Time entry not found.') })
|
|
113
126
|
}
|
|
127
|
+
// Re-checked under the row lock: a report close committed between the
|
|
128
|
+
// pre-flight read and this transaction would otherwise slip through.
|
|
129
|
+
assertTimeEntryUnlocked(lockedEntry, translate)
|
|
114
130
|
|
|
115
131
|
const segments = await findWithDecryption(
|
|
116
132
|
trx,
|
|
@@ -148,6 +164,16 @@ export async function POST(req: Request) {
|
|
|
148
164
|
|
|
149
165
|
const computedMinutes = Math.round(totalWorkMinutes / 60000)
|
|
150
166
|
lockedEntry.durationMinutes = computedMinutes
|
|
167
|
+
// This route writes a duration outside the entries command, and D-7 makes
|
|
168
|
+
// `rounded_minutes` the only input to cost — so it is restated here with the
|
|
169
|
+
// same tenant rule the command applies, not left stale from the zero-minute
|
|
170
|
+
// value the timer was created with.
|
|
171
|
+
lockedEntry.roundedMinutes = await resolveRoundedMinutes(
|
|
172
|
+
container,
|
|
173
|
+
tenantId,
|
|
174
|
+
computedMinutes,
|
|
175
|
+
organizationId,
|
|
176
|
+
)
|
|
151
177
|
|
|
152
178
|
await trx.flush()
|
|
153
179
|
return { now: stoppedAt, durationMinutes: computedMinutes }
|
|
@@ -176,7 +202,7 @@ export async function POST(req: Request) {
|
|
|
176
202
|
tenantId,
|
|
177
203
|
organizationId,
|
|
178
204
|
userId: auth.sub ?? '',
|
|
179
|
-
resourceKind:
|
|
205
|
+
resourceKind: STAFF_TIME_TRACKING_RESOURCE_KINDS.timeEntry,
|
|
180
206
|
resourceId: entry.id,
|
|
181
207
|
operation: 'update',
|
|
182
208
|
requestMethod: req.method,
|
|
@@ -184,7 +210,7 @@ export async function POST(req: Request) {
|
|
|
184
210
|
})
|
|
185
211
|
}
|
|
186
212
|
|
|
187
|
-
return
|
|
213
|
+
return session.respond(200, { ok: true, durationMinutes })
|
|
188
214
|
} catch (err) {
|
|
189
215
|
if (err instanceof CrudHttpError) {
|
|
190
216
|
return NextResponse.json(err.body, { status: err.status })
|
|
@@ -212,7 +238,11 @@ export const openApi: OpenApiRouteDoc = {
|
|
|
212
238
|
schema: z.object({ ok: z.literal(true), durationMinutes: z.number() }),
|
|
213
239
|
},
|
|
214
240
|
{ status: 404, description: 'Time entry not found', schema: z.object({ error: z.string() }) },
|
|
215
|
-
{
|
|
241
|
+
{
|
|
242
|
+
status: 409,
|
|
243
|
+
description: 'No active timer segment, or the entry is locked in a closed report (code time_entry_locked)',
|
|
244
|
+
schema: z.object({ error: z.string(), code: z.string().optional(), lockedReportId: z.string().uuid().nullable().optional() }),
|
|
245
|
+
},
|
|
216
246
|
{ status: 401, description: 'Unauthorized', schema: z.object({ error: z.string() }) },
|
|
217
247
|
],
|
|
218
248
|
},
|
|
@@ -11,19 +11,73 @@ import { emitCrudSideEffects, flushCrudSideEffects } from '@open-mercato/shared/
|
|
|
11
11
|
import type { DataEngine } from '@open-mercato/shared/lib/data/engine'
|
|
12
12
|
import type { EntityManager } from '@mikro-orm/postgresql'
|
|
13
13
|
import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
|
|
14
|
-
import { StaffTimeEntry, StaffTeamMember, StaffTimeProject } from '../../../../data/entities'
|
|
15
|
-
import {
|
|
14
|
+
import { StaffTimeEntry, StaffTeamMember, StaffTimeProject, StaffTimeTask } from '../../../../data/entities'
|
|
15
|
+
import {
|
|
16
|
+
staffTimeEntryBulkSaveSchema,
|
|
17
|
+
type StaffTimeEntryBulkSaveInput,
|
|
18
|
+
} from '../../../../data/validators'
|
|
19
|
+
import {
|
|
20
|
+
buildTimeEntryLockedError,
|
|
21
|
+
rebaseTimeEntryIntervalToDate,
|
|
22
|
+
reconcileTimeEntryInterval,
|
|
23
|
+
resolveTimeEntryBillable,
|
|
24
|
+
resolveTimeEntryNotesInput,
|
|
25
|
+
resolveTimeEntryProjectId,
|
|
26
|
+
resolveTimeEntrySettings,
|
|
27
|
+
roundedMinutesFor,
|
|
28
|
+
timeEntryTaskMatchesProject,
|
|
29
|
+
toStoredTimeEntryRateOverride,
|
|
30
|
+
type LockedTimeEntryRef,
|
|
31
|
+
} from '../../../../commands/timesheets-entries'
|
|
16
32
|
import { staffTimeEntryCrudEvents } from '../../../../lib/crud'
|
|
33
|
+
import { emitStaffEvent } from '../../../../events'
|
|
17
34
|
import { invalidateStaffTimeEntryCache } from '../../../../lib/timesheets/timeEntryCacheInvalidation'
|
|
18
35
|
import {
|
|
19
|
-
|
|
36
|
+
STAFF_TIME_TRACKING_RESOURCE_KINDS,
|
|
20
37
|
runStaffMutationGuardAfterSuccess,
|
|
21
38
|
runStaffMutationGuards,
|
|
22
39
|
} from '../../../guards'
|
|
40
|
+
import { runTimesheetInterceptors } from '../../_shared/withTimesheetInterceptors'
|
|
23
41
|
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
24
42
|
|
|
25
43
|
const logger = createLogger('staff')
|
|
26
44
|
|
|
45
|
+
/**
|
|
46
|
+
* A grid cell is addressed by the day the work started, never by the timestamp,
|
|
47
|
+
* so both sides of the comparison collapse to `YYYY-MM-DD` (D-8).
|
|
48
|
+
*/
|
|
49
|
+
function cellDateKey(value: Date | string | null | undefined): string {
|
|
50
|
+
if (!value) return ''
|
|
51
|
+
const date = value instanceof Date ? value : new Date(value)
|
|
52
|
+
return Number.isNaN(date.getTime()) ? String(value) : date.toISOString().slice(0, 10)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
type BulkRowError = { path: string; message: string; value?: unknown }
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* The batch answers with a per-row error list rather than a thrown 422: one grid
|
|
59
|
+
* save spans a week of cells, so the client needs to know WHICH row it must fix.
|
|
60
|
+
* The single-entry command raises the same refusals as `CrudHttpError` field
|
|
61
|
+
* errors — same rule, different reporting shape.
|
|
62
|
+
*/
|
|
63
|
+
function bulkValidationError(errors: BulkRowError[]): NextResponse {
|
|
64
|
+
return NextResponse.json({ ok: false, errors }, { status: 422 })
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* A row as the write loop needs it: the payload plus the two things that must be
|
|
69
|
+
* settled before the lock gate can address a cell — the task it names (validated
|
|
70
|
+
* in scope) and the project the entry actually lands on, which a task-only row
|
|
71
|
+
* inherits from its task exactly as the single-entry path does.
|
|
72
|
+
*/
|
|
73
|
+
type BulkEntryInput = StaffTimeEntryBulkSaveInput['entries'][number]
|
|
74
|
+
|
|
75
|
+
type ResolvedBulkRow = {
|
|
76
|
+
entry: BulkEntryInput
|
|
77
|
+
task: { id: string; timeProjectId: string } | null
|
|
78
|
+
timeProjectId: string
|
|
79
|
+
}
|
|
80
|
+
|
|
27
81
|
export const metadata = {
|
|
28
82
|
POST: { requireAuth: true, requireFeatures: ['staff.timesheets.manage_own'] },
|
|
29
83
|
}
|
|
@@ -42,8 +96,16 @@ export async function POST(req: Request) {
|
|
|
42
96
|
throw new CrudHttpError(400, { error: translate('staff.errors.missingScope', 'Missing tenant or organization scope.') })
|
|
43
97
|
}
|
|
44
98
|
|
|
45
|
-
const
|
|
46
|
-
|
|
99
|
+
const interceptors = await runTimesheetInterceptors({
|
|
100
|
+
request: req,
|
|
101
|
+
method: 'POST',
|
|
102
|
+
scope: { container, userId: auth.sub, tenantId, organizationId },
|
|
103
|
+
body: await readJsonSafe<Record<string, unknown>>(req, {}),
|
|
104
|
+
})
|
|
105
|
+
if (!interceptors.ok) return interceptors.response
|
|
106
|
+
const { session } = interceptors
|
|
107
|
+
|
|
108
|
+
const parsed = staffTimeEntryBulkSaveSchema.safeParse(session.body)
|
|
47
109
|
if (!parsed.success) {
|
|
48
110
|
const errors = parsed.error.issues.map((issue) => ({
|
|
49
111
|
path: issue.path.join('.'),
|
|
@@ -54,6 +116,27 @@ export async function POST(req: Request) {
|
|
|
54
116
|
|
|
55
117
|
const { entries } = parsed.data
|
|
56
118
|
|
|
119
|
+
// Tags are REFUSED here rather than accepted and dropped.
|
|
120
|
+
//
|
|
121
|
+
// The single-entry path assigns tags by dispatching the tag commands, which
|
|
122
|
+
// fork their own EntityManager so they can carry their own audit entry, undo
|
|
123
|
+
// and lock check. Inside this route's `em.transactional` that fork would not
|
|
124
|
+
// see the rows the transaction has not committed yet, and dispatching AFTER
|
|
125
|
+
// the commit would answer `{ ok: true, created: n }` for a batch whose tags
|
|
126
|
+
// silently failed — a response carrying only counts cannot say which cell
|
|
127
|
+
// lost them, which is the very silent-success class this route is being
|
|
128
|
+
// fixed for. So the row is rejected and the user is pointed at the entry
|
|
129
|
+
// dialog, which writes tags through the commands with all of their
|
|
130
|
+
// guarantees intact.
|
|
131
|
+
const taggedRows = entries.filter((entry) => entry.tagIds !== undefined)
|
|
132
|
+
if (taggedRows.length > 0) {
|
|
133
|
+
const message = translate(
|
|
134
|
+
'staff.timesheets.errors.bulkTagsUnsupported',
|
|
135
|
+
'Tags cannot be changed from the timesheet grid. Open the time entry to edit its tags.',
|
|
136
|
+
)
|
|
137
|
+
return bulkValidationError(taggedRows.map(() => ({ path: 'entries[].tagIds', message })))
|
|
138
|
+
}
|
|
139
|
+
|
|
57
140
|
const em = (container.resolve('em') as EntityManager).fork()
|
|
58
141
|
const scopeCtx = { tenantId, organizationId }
|
|
59
142
|
|
|
@@ -69,34 +152,94 @@ export async function POST(req: Request) {
|
|
|
69
152
|
}
|
|
70
153
|
const staffMemberId = staffMember.id
|
|
71
154
|
|
|
72
|
-
// Validate that all referenced
|
|
73
|
-
|
|
155
|
+
// Validate that all referenced taskIds exist and are in-scope, in one query
|
|
156
|
+
// for the whole batch. Same rule the single-entry path applies through
|
|
157
|
+
// `requireTaskInScope`: a dangling task reference is refused rather than
|
|
158
|
+
// stored, because the task rollups aggregate entries by `task_id` and a stale
|
|
159
|
+
// or foreign UUID would silently move hours onto another project's card.
|
|
160
|
+
const referencedTaskIds = [
|
|
74
161
|
...new Set(
|
|
75
162
|
entries
|
|
76
|
-
.map((
|
|
163
|
+
.map((entry) => entry.taskId)
|
|
77
164
|
.filter((id): id is string => typeof id === 'string' && id.length > 0),
|
|
78
165
|
),
|
|
79
166
|
]
|
|
167
|
+
const tasksById = new Map<string, { id: string; timeProjectId: string }>()
|
|
168
|
+
if (referencedTaskIds.length > 0) {
|
|
169
|
+
const validTasks = await em.find(StaffTimeTask, {
|
|
170
|
+
id: { $in: referencedTaskIds },
|
|
171
|
+
tenantId,
|
|
172
|
+
organizationId,
|
|
173
|
+
deletedAt: null,
|
|
174
|
+
}, { fields: ['id', 'timeProjectId'] })
|
|
175
|
+
for (const task of validTasks) tasksById.set(task.id, { id: task.id, timeProjectId: task.timeProjectId })
|
|
176
|
+
const invalidTaskIds = referencedTaskIds.filter((id) => !tasksById.has(id))
|
|
177
|
+
if (invalidTaskIds.length > 0) {
|
|
178
|
+
const message = translate('staff.timesheets.errors.taskNotFound', 'Task not found or not accessible.')
|
|
179
|
+
return bulkValidationError(
|
|
180
|
+
invalidTaskIds.map((id) => ({ path: 'entries[].taskId', message, value: id })),
|
|
181
|
+
)
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Every row is pinned to the project it will actually land on BEFORE the lock
|
|
186
|
+
// gate runs, because the gate addresses a cell by (project, date) — a task row
|
|
187
|
+
// that inherited its project must be checked against that inherited cell, not
|
|
188
|
+
// against a blank one.
|
|
189
|
+
const resolvedRows: ResolvedBulkRow[] = []
|
|
190
|
+
const missingProjectErrors: BulkRowError[] = []
|
|
191
|
+
const taskProjectMismatchErrors: BulkRowError[] = []
|
|
192
|
+
for (const entry of entries) {
|
|
193
|
+
const task = entry.taskId ? tasksById.get(entry.taskId) ?? null : null
|
|
194
|
+
const timeProjectId = resolveTimeEntryProjectId(entry.timeProjectId, task)
|
|
195
|
+
if (!timeProjectId) {
|
|
196
|
+
missingProjectErrors.push({
|
|
197
|
+
path: 'entries[].timeProjectId',
|
|
198
|
+
message: translate('staff.timesheets.errors.projectRequired', 'Time project id is required.'),
|
|
199
|
+
})
|
|
200
|
+
continue
|
|
201
|
+
}
|
|
202
|
+
if (!timeEntryTaskMatchesProject(task, timeProjectId)) {
|
|
203
|
+
taskProjectMismatchErrors.push({
|
|
204
|
+
path: 'entries[].taskId',
|
|
205
|
+
message: translate('staff.timesheets.errors.taskNotFound', 'Task not found or not accessible.'),
|
|
206
|
+
value: entry.taskId,
|
|
207
|
+
})
|
|
208
|
+
continue
|
|
209
|
+
}
|
|
210
|
+
resolvedRows.push({ entry, task, timeProjectId })
|
|
211
|
+
}
|
|
212
|
+
if (missingProjectErrors.length > 0 || taskProjectMismatchErrors.length > 0) {
|
|
213
|
+
return bulkValidationError([...missingProjectErrors, ...taskProjectMismatchErrors])
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Validate that all referenced timeProjectIds exist and are in-scope. The
|
|
217
|
+
// project rows are kept, not just their ids: a created entry snapshots the
|
|
218
|
+
// project's currency and inherits its billable default (D-3), so one query
|
|
219
|
+
// serves validation and both defaults.
|
|
220
|
+
const referencedProjectIds = [...new Set(resolvedRows.map((row) => row.timeProjectId))]
|
|
221
|
+
const projectsById = new Map<string, { currencyCode?: string | null; billableByDefault?: boolean | null }>()
|
|
80
222
|
if (referencedProjectIds.length > 0) {
|
|
81
223
|
const validProjects = await em.find(StaffTimeProject, {
|
|
82
224
|
id: { $in: referencedProjectIds },
|
|
83
225
|
tenantId,
|
|
84
226
|
organizationId,
|
|
85
227
|
deletedAt: null,
|
|
86
|
-
}, { fields: ['id'] })
|
|
87
|
-
const
|
|
88
|
-
|
|
228
|
+
}, { fields: ['id', 'currencyCode', 'billableByDefault'] })
|
|
229
|
+
for (const project of validProjects) {
|
|
230
|
+
projectsById.set(project.id, {
|
|
231
|
+
currencyCode: project.currencyCode ?? null,
|
|
232
|
+
billableByDefault: project.billableByDefault ?? null,
|
|
233
|
+
})
|
|
234
|
+
}
|
|
235
|
+
const invalidIds = referencedProjectIds.filter((id) => !projectsById.has(id))
|
|
89
236
|
if (invalidIds.length > 0) {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
value: id,
|
|
97
|
-
})),
|
|
98
|
-
},
|
|
99
|
-
{ status: 422 },
|
|
237
|
+
const message = translate(
|
|
238
|
+
'staff.timesheets.errors.projectNotFound',
|
|
239
|
+
'Time project not found or not accessible.',
|
|
240
|
+
)
|
|
241
|
+
return bulkValidationError(
|
|
242
|
+
invalidIds.map((id) => ({ path: 'entries[].timeProjectId', message, value: id })),
|
|
100
243
|
)
|
|
101
244
|
}
|
|
102
245
|
}
|
|
@@ -105,14 +248,14 @@ export async function POST(req: Request) {
|
|
|
105
248
|
tenantId,
|
|
106
249
|
organizationId,
|
|
107
250
|
userId: auth.sub ?? '',
|
|
108
|
-
resourceKind:
|
|
251
|
+
resourceKind: STAFF_TIME_TRACKING_RESOURCE_KINDS.timeEntry,
|
|
109
252
|
resourceId: staffMemberId,
|
|
110
253
|
operation: 'update' as const,
|
|
111
254
|
requestMethod: req.method,
|
|
112
255
|
requestHeaders: req.headers,
|
|
113
256
|
mutationPayload: parsed.data as unknown as Record<string, unknown>,
|
|
114
257
|
}
|
|
115
|
-
const guardResult = await runStaffMutationGuards(container, guardInput
|
|
258
|
+
const guardResult = await runStaffMutationGuards(container, guardInput)
|
|
116
259
|
if (!guardResult.ok) {
|
|
117
260
|
return NextResponse.json(
|
|
118
261
|
guardResult.errorBody ?? { error: 'Operation blocked by guard' },
|
|
@@ -120,8 +263,8 @@ export async function POST(req: Request) {
|
|
|
120
263
|
)
|
|
121
264
|
}
|
|
122
265
|
|
|
123
|
-
const existingIds =
|
|
124
|
-
.map((
|
|
266
|
+
const existingIds = resolvedRows
|
|
267
|
+
.map((row) => row.entry.id)
|
|
125
268
|
.filter((id): id is string => typeof id === 'string' && id.length > 0)
|
|
126
269
|
|
|
127
270
|
// Validate referenced entry IDs upfront: a stale or foreign UUID would
|
|
@@ -153,11 +296,80 @@ export async function POST(req: Request) {
|
|
|
153
296
|
}
|
|
154
297
|
}
|
|
155
298
|
|
|
299
|
+
// --- Lock gate (risk R3) -------------------------------------------------
|
|
300
|
+
//
|
|
301
|
+
// This is the endpoint the lock is most likely to leak through, because the
|
|
302
|
+
// grid addresses a cell by (project, date) and only carries an id when the
|
|
303
|
+
// client happened to have one loaded. So both shapes are checked:
|
|
304
|
+
//
|
|
305
|
+
// * a row naming an id that resolves to a frozen entry, and
|
|
306
|
+
// * an id-less row landing on a (project, date) cell a frozen entry already
|
|
307
|
+
// occupies — the grid renders one summed value per cell, so creating a
|
|
308
|
+
// second row there silently restates hours a closed report has billed.
|
|
309
|
+
//
|
|
310
|
+
// The batch fails WHOLESALE, before the transaction opens. A grid save is one
|
|
311
|
+
// user gesture spanning a week of cells and the response carries only counts,
|
|
312
|
+
// so applying the unlocked rows would leave the client unable to tell which
|
|
313
|
+
// cells landed — and the route already rejects the whole payload for an
|
|
314
|
+
// unknown project or a stale entry id, so a partial 409 would be the odd one
|
|
315
|
+
// out. The 409 names every offending id and the reports that froze them, so
|
|
316
|
+
// the grid can mark exactly those cells read-only and the user retries the
|
|
317
|
+
// rest after reloading (which is also what makes the lock badge appear).
|
|
318
|
+
//
|
|
319
|
+
// The cell is addressed by the RESOLVED project, so a task row that inherited
|
|
320
|
+
// its project is checked against the cell it will really occupy.
|
|
321
|
+
const idlessRows = resolvedRows.filter((row) => !row.entry.id)
|
|
322
|
+
const idlessCells = idlessRows.map((row) => `${cellDateKey(row.entry.date)}|${row.timeProjectId}`)
|
|
323
|
+
const lockConditions: Record<string, unknown>[] = []
|
|
324
|
+
if (existingIds.length > 0) lockConditions.push({ id: { $in: existingIds } })
|
|
325
|
+
if (idlessCells.length > 0) {
|
|
326
|
+
lockConditions.push({
|
|
327
|
+
date: { $in: idlessRows.map((row) => row.entry.date) },
|
|
328
|
+
timeProjectId: { $in: [...new Set(idlessRows.map((row) => row.timeProjectId))] },
|
|
329
|
+
})
|
|
330
|
+
}
|
|
331
|
+
if (lockConditions.length > 0) {
|
|
332
|
+
const existingIdSet = new Set(existingIds)
|
|
333
|
+
const idlessCellSet = new Set(idlessCells)
|
|
334
|
+
const lockedCandidates = await em.find(
|
|
335
|
+
StaffTimeEntry,
|
|
336
|
+
{
|
|
337
|
+
tenantId,
|
|
338
|
+
organizationId,
|
|
339
|
+
staffMemberId,
|
|
340
|
+
deletedAt: null,
|
|
341
|
+
lockedReportId: { $ne: null },
|
|
342
|
+
$or: lockConditions,
|
|
343
|
+
},
|
|
344
|
+
{ fields: ['id', 'lockedReportId', 'date', 'timeProjectId'] },
|
|
345
|
+
)
|
|
346
|
+
// The (date × project) condition above is a cross product, so it can match a
|
|
347
|
+
// locked entry on a cell the payload never touches — narrowed here.
|
|
348
|
+
const blocked: LockedTimeEntryRef[] = lockedCandidates.filter(
|
|
349
|
+
(row) =>
|
|
350
|
+
existingIdSet.has(row.id) ||
|
|
351
|
+
idlessCellSet.has(`${cellDateKey(row.date)}|${row.timeProjectId ?? ''}`),
|
|
352
|
+
)
|
|
353
|
+
if (blocked.length > 0) {
|
|
354
|
+
throw buildTimeEntryLockedError(
|
|
355
|
+
blocked.map((row) => ({ id: row.id, lockedReportId: row.lockedReportId })),
|
|
356
|
+
translate,
|
|
357
|
+
)
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
156
361
|
type PendingChange = {
|
|
157
362
|
action: 'created' | 'updated' | 'deleted'
|
|
158
363
|
entity: StaffTimeEntry
|
|
159
364
|
}
|
|
160
365
|
|
|
366
|
+
// D-7 makes `rounded_minutes` the only input to cost, and this route writes
|
|
367
|
+
// durations outside the entries command — so every row it touches is rounded
|
|
368
|
+
// with the same tenant rule the command uses. The settings read is hoisted out
|
|
369
|
+
// of the loop because it is tenant-scoped and identical for every row; the
|
|
370
|
+
// billable default a created row falls back to comes from the same snapshot.
|
|
371
|
+
const settings = await resolveTimeEntrySettings(container, tenantId)
|
|
372
|
+
|
|
161
373
|
const { counts, pendingChanges } = await em.transactional(async (trx) => {
|
|
162
374
|
let created = 0
|
|
163
375
|
let updated = 0
|
|
@@ -176,7 +388,12 @@ export async function POST(req: Request) {
|
|
|
176
388
|
|
|
177
389
|
const existingMap = new Map(existingEntries.map((entry) => [entry.id, entry]))
|
|
178
390
|
|
|
179
|
-
for (const entry of
|
|
391
|
+
for (const { entry, task, timeProjectId } of resolvedRows) {
|
|
392
|
+
const project = projectsById.get(timeProjectId) ?? null
|
|
393
|
+
// Both branches read the five fields the schema has always accepted
|
|
394
|
+
// through the SAME helpers the single-entry command uses, so a grid write
|
|
395
|
+
// and a dialog write cannot settle them differently (#silent-no-op).
|
|
396
|
+
const notes = resolveTimeEntryNotesInput(entry)
|
|
180
397
|
if (entry.id && existingMap.has(entry.id)) {
|
|
181
398
|
const existing = existingMap.get(entry.id)!
|
|
182
399
|
if (entry.durationMinutes === 0) {
|
|
@@ -184,10 +401,52 @@ export async function POST(req: Request) {
|
|
|
184
401
|
deleted++
|
|
185
402
|
changes.push({ action: 'deleted', entity: existing })
|
|
186
403
|
} else {
|
|
404
|
+
// T4.10(a). A grid cell carries a duration and nothing else, so writing
|
|
405
|
+
// it straight onto an entry that has clocks would store a two-hour
|
|
406
|
+
// duration against a 09:00–10:00 span — exactly the contradiction the
|
|
407
|
+
// single-entry path already refuses to store. The same reconciliation
|
|
408
|
+
// is called here rather than re-derived: only-a-duration means
|
|
409
|
+
// `started_at` anchors and `ended_at` shifts to match, a clock that
|
|
410
|
+
// does not exist is never manufactured (so a cell whose entry has no
|
|
411
|
+
// clocks keeps having none, and a running timer is not stopped), and a
|
|
412
|
+
// re-dated row takes its clocks with it.
|
|
413
|
+
const interval = reconcileTimeEntryInterval({
|
|
414
|
+
stored: rebaseTimeEntryIntervalToDate({
|
|
415
|
+
stored: {
|
|
416
|
+
startedAt: existing.startedAt ?? null,
|
|
417
|
+
endedAt: existing.endedAt ?? null,
|
|
418
|
+
durationMinutes: existing.durationMinutes,
|
|
419
|
+
},
|
|
420
|
+
fromDate: existing.date,
|
|
421
|
+
toDate: entry.date,
|
|
422
|
+
}),
|
|
423
|
+
startedAt: undefined,
|
|
424
|
+
endedAt: undefined,
|
|
425
|
+
durationMinutes: entry.durationMinutes,
|
|
426
|
+
})
|
|
427
|
+
const projectChanged = timeProjectId !== (existing.timeProjectId ?? null)
|
|
187
428
|
existing.date = entry.date
|
|
188
|
-
existing.timeProjectId =
|
|
189
|
-
|
|
190
|
-
|
|
429
|
+
existing.timeProjectId = timeProjectId
|
|
430
|
+
// Snapshotted, never joined at read time, so a later project currency
|
|
431
|
+
// change cannot re-denominate money already reported (D-3) — and only
|
|
432
|
+
// restated when the entry actually moves between projects, exactly as
|
|
433
|
+
// the single-entry update command does it.
|
|
434
|
+
if (projectChanged) existing.rateCurrencyCode = project?.currencyCode ?? null
|
|
435
|
+
existing.startedAt = interval.startedAt
|
|
436
|
+
existing.endedAt = interval.endedAt
|
|
437
|
+
existing.durationMinutes = interval.durationMinutes
|
|
438
|
+
// D-7 keeps `rounded_minutes` the only input to cost, so it is restated
|
|
439
|
+
// from the effective duration the reconciliation settled on.
|
|
440
|
+
existing.roundedMinutes = roundedMinutesFor(interval.durationMinutes, settings, {
|
|
441
|
+
tenantId,
|
|
442
|
+
organizationId,
|
|
443
|
+
})
|
|
444
|
+
if (entry.taskId !== undefined) existing.taskId = task?.id ?? null
|
|
445
|
+
if (entry.isBillable !== undefined) existing.isBillable = entry.isBillable
|
|
446
|
+
if (entry.rateOverrideAmount !== undefined) {
|
|
447
|
+
existing.rateOverrideAmount = toStoredTimeEntryRateOverride(entry.rateOverrideAmount)
|
|
448
|
+
}
|
|
449
|
+
if (notes !== undefined) existing.notes = notes
|
|
191
450
|
existing.updatedAt = new Date()
|
|
192
451
|
updated++
|
|
193
452
|
changes.push({ action: 'updated', entity: existing })
|
|
@@ -199,9 +458,22 @@ export async function POST(req: Request) {
|
|
|
199
458
|
organizationId,
|
|
200
459
|
staffMemberId,
|
|
201
460
|
date: entry.date,
|
|
202
|
-
timeProjectId
|
|
461
|
+
timeProjectId,
|
|
462
|
+
taskId: task?.id ?? null,
|
|
203
463
|
durationMinutes: entry.durationMinutes,
|
|
204
|
-
|
|
464
|
+
roundedMinutes: roundedMinutesFor(entry.durationMinutes, settings, {
|
|
465
|
+
tenantId,
|
|
466
|
+
organizationId,
|
|
467
|
+
}),
|
|
468
|
+
notes: notes ?? null,
|
|
469
|
+
isBillable: resolveTimeEntryBillable({
|
|
470
|
+
requested: entry.isBillable,
|
|
471
|
+
project,
|
|
472
|
+
settings,
|
|
473
|
+
scope: { tenantId, organizationId },
|
|
474
|
+
}),
|
|
475
|
+
rateOverrideAmount: toStoredTimeEntryRateOverride(entry.rateOverrideAmount),
|
|
476
|
+
rateCurrencyCode: project?.currencyCode ?? null,
|
|
205
477
|
source: 'manual',
|
|
206
478
|
createdAt: now,
|
|
207
479
|
updatedAt: now,
|
|
@@ -252,7 +524,7 @@ export async function POST(req: Request) {
|
|
|
252
524
|
tenantId,
|
|
253
525
|
organizationId,
|
|
254
526
|
userId: auth.sub ?? '',
|
|
255
|
-
resourceKind:
|
|
527
|
+
resourceKind: STAFF_TIME_TRACKING_RESOURCE_KINDS.timeEntry,
|
|
256
528
|
resourceId: staffMemberId,
|
|
257
529
|
operation: 'update',
|
|
258
530
|
requestMethod: req.method,
|
|
@@ -260,7 +532,20 @@ export async function POST(req: Request) {
|
|
|
260
532
|
})
|
|
261
533
|
}
|
|
262
534
|
|
|
263
|
-
|
|
535
|
+
// One batch-level event beside the per-row CRUD events already flushed above.
|
|
536
|
+
// It carries no `id`, so the entry-scoped subscribers that key off one record
|
|
537
|
+
// skip it and only the batch-aware ones (payroll exports, audit mirrors) react.
|
|
538
|
+
void emitStaffEvent('staff.timesheets.time_entry.bulk_updated', {
|
|
539
|
+
tenantId,
|
|
540
|
+
organizationId,
|
|
541
|
+
staffMemberId,
|
|
542
|
+
...counts,
|
|
543
|
+
entryIds: pendingChanges.map((change) => change.entity.id),
|
|
544
|
+
}, { persistent: true }).catch((err) => {
|
|
545
|
+
logger.error('staff.timesheets emit time_entry.bulk_updated failed', { err })
|
|
546
|
+
})
|
|
547
|
+
|
|
548
|
+
return session.respond(200, { ok: true, ...counts })
|
|
264
549
|
} catch (err) {
|
|
265
550
|
if (err instanceof CrudHttpError) {
|
|
266
551
|
return NextResponse.json(err.body, { status: err.status })
|
|
@@ -280,7 +565,8 @@ export const openApi: OpenApiRouteDoc = {
|
|
|
280
565
|
methods: {
|
|
281
566
|
POST: {
|
|
282
567
|
summary: 'Bulk save time entries',
|
|
283
|
-
description:
|
|
568
|
+
description:
|
|
569
|
+
'Creates, updates, or soft-deletes multiple time entries in a single request. Entries with durationMinutes=0 and an existing id are soft-deleted. A row may name a taskId and let timeProjectId follow from it; task, description/notes precedence, isBillable defaulting, the rate override and the project currency snapshot follow the single-entry path exactly. tagIds is rejected with 422 — tags are edited from the time entry itself so their audit trail and undo stay intact. The whole batch is rejected with 409 time_entry_locked when any row targets — by id, or by landing on its (project, date) cell — an entry frozen in a closed report.',
|
|
284
570
|
requestBody: {
|
|
285
571
|
contentType: 'application/json',
|
|
286
572
|
schema: staffTimeEntryBulkSaveSchema,
|
|
@@ -304,6 +590,17 @@ export const openApi: OpenApiRouteDoc = {
|
|
|
304
590
|
errors: z.array(z.object({ path: z.string(), message: z.string() })),
|
|
305
591
|
}),
|
|
306
592
|
},
|
|
593
|
+
{
|
|
594
|
+
status: 409,
|
|
595
|
+
description: 'One or more rows target a time entry locked in a closed report; nothing was saved',
|
|
596
|
+
schema: z.object({
|
|
597
|
+
code: z.literal('time_entry_locked'),
|
|
598
|
+
error: z.string(),
|
|
599
|
+
lockedReportId: z.string().uuid().nullable(),
|
|
600
|
+
lockedEntryIds: z.array(z.string().uuid()),
|
|
601
|
+
lockedReportIds: z.array(z.string().uuid()),
|
|
602
|
+
}),
|
|
603
|
+
},
|
|
307
604
|
{ status: 401, description: 'Unauthorized', schema: z.object({ error: z.string() }) },
|
|
308
605
|
{ status: 403, description: 'Forbidden', schema: z.object({ error: z.string() }) },
|
|
309
606
|
],
|