@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
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EP-51 — the recalculation registry.
|
|
3
|
+
*
|
|
4
|
+
* A recalculation is a tenant-wide restatement of a derived time-tracking value:
|
|
5
|
+
* the module ships one, the retro-rounding pass behind
|
|
6
|
+
* `POST /api/staff/timesheets/settings/reapply-rounding`, and a third party that
|
|
7
|
+
* plugs in its own rate resolver (EP-33) or billability chain (EP-34) needs the
|
|
8
|
+
* same thing — a way to re-apply the new rule to the rows that already exist,
|
|
9
|
+
* with the progress bar, the cancel button and the queue worker the module
|
|
10
|
+
* already has.
|
|
11
|
+
*
|
|
12
|
+
* The contract is deliberately narrow. A hook receives a scope and a reporter and
|
|
13
|
+
* answers a count summary; it does NOT own the `ProgressJob`, because several
|
|
14
|
+
* hooks share one job when the CLI runs them together, and a hook that started or
|
|
15
|
+
* completed the job would fight the next one.
|
|
16
|
+
*
|
|
17
|
+
* **`run` is a write over a tenant's history.** Two rules that are not optional:
|
|
18
|
+
* every query must carry `tenantId` (and honour `organizationIds` when it is not
|
|
19
|
+
* `null`), and a locked entry — one frozen into a closed report — must never be
|
|
20
|
+
* touched. The built-in excludes locked entries twice, in its own candidate query
|
|
21
|
+
* and again in the command it drives.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import type { AwilixContainer } from 'awilix'
|
|
25
|
+
import { extensionPoints } from '@open-mercato/core/modules/staff/extension-points'
|
|
26
|
+
import { createStrategyRegistry, BUILT_IN_STRATEGY_PRIORITY } from './registries/registry'
|
|
27
|
+
|
|
28
|
+
export const TIME_TRACKING_RECALCULATION_REGISTRY_ID =
|
|
29
|
+
extensionPoints.hosts.recalculationRegistry.spotId
|
|
30
|
+
|
|
31
|
+
export const BUILT_IN_RECALCULATION_ID = 'staff.time_tracking.recalculation.rounding'
|
|
32
|
+
|
|
33
|
+
export type TimeTrackingRecalculationScope = {
|
|
34
|
+
tenantId: string
|
|
35
|
+
/** Organizations the caller may act in; `null` means every organization of the tenant. */
|
|
36
|
+
organizationIds: string[] | null
|
|
37
|
+
userId?: string | null
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* The slice of the progress job a hook is allowed to touch. `advance` is additive
|
|
42
|
+
* so two hooks sharing one job cannot rewind each other's count.
|
|
43
|
+
*/
|
|
44
|
+
export type TimeTrackingRecalculationReporter = {
|
|
45
|
+
setTotal(total: number): Promise<void>
|
|
46
|
+
advance(processed: number): Promise<void>
|
|
47
|
+
isCancellationRequested(): Promise<boolean>
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export type TimeTrackingRecalculationContext = {
|
|
51
|
+
container: AwilixContainer
|
|
52
|
+
scope: TimeTrackingRecalculationScope
|
|
53
|
+
report: TimeTrackingRecalculationReporter
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export type TimeTrackingRecalculationSummary = {
|
|
57
|
+
totalCount: number
|
|
58
|
+
processedCount: number
|
|
59
|
+
updatedCount: number
|
|
60
|
+
unchangedCount: number
|
|
61
|
+
skippedCount: number
|
|
62
|
+
cancelled: boolean
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export type TimeTrackingRecalculation = {
|
|
66
|
+
id: string
|
|
67
|
+
labelKey: string
|
|
68
|
+
priority?: number
|
|
69
|
+
run(ctx: TimeTrackingRecalculationContext): Promise<TimeTrackingRecalculationSummary>
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const registry = createStrategyRegistry<TimeTrackingRecalculation>(
|
|
73
|
+
TIME_TRACKING_RECALCULATION_REGISTRY_ID,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
export function registerTimeTrackingRecalculation(
|
|
77
|
+
recalculation: TimeTrackingRecalculation,
|
|
78
|
+
): () => void {
|
|
79
|
+
if (typeof recalculation?.run !== 'function') {
|
|
80
|
+
throw new Error('[internal] a time-tracking recalculation must declare a run() function')
|
|
81
|
+
}
|
|
82
|
+
return registry.register(recalculation)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function listTimeTrackingRecalculations(): TimeTrackingRecalculation[] {
|
|
86
|
+
return registry.list()
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function timeTrackingRecalculationIds(): string[] {
|
|
90
|
+
return registry.ids()
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function getTimeTrackingRecalculation(
|
|
94
|
+
id: string | null | undefined,
|
|
95
|
+
): TimeTrackingRecalculation | null {
|
|
96
|
+
return registry.get(id)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function emptyRecalculationSummary(): TimeTrackingRecalculationSummary {
|
|
100
|
+
return {
|
|
101
|
+
totalCount: 0,
|
|
102
|
+
processedCount: 0,
|
|
103
|
+
updatedCount: 0,
|
|
104
|
+
unchangedCount: 0,
|
|
105
|
+
skippedCount: 0,
|
|
106
|
+
cancelled: false,
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Resolves the hooks a job should run.
|
|
112
|
+
*
|
|
113
|
+
* An empty or absent request resolves to the built-in alone — which is what keeps
|
|
114
|
+
* the settings route byte-identical: it enqueues no hook ids, so a contribution
|
|
115
|
+
* cannot attach itself to the retro-rounding button a tenant pressed. Running
|
|
116
|
+
* more than one is an explicit act, and the CLI is where it is made.
|
|
117
|
+
*
|
|
118
|
+
* An unknown id is an error rather than a silent skip: a backfill that quietly
|
|
119
|
+
* did nothing is worse than one that refused to start.
|
|
120
|
+
*/
|
|
121
|
+
export function resolveTimeTrackingRecalculations(
|
|
122
|
+
ids?: readonly string[] | null,
|
|
123
|
+
): TimeTrackingRecalculation[] {
|
|
124
|
+
if (!ids || ids.length === 0) {
|
|
125
|
+
const builtIn = registry.get(BUILT_IN_RECALCULATION_ID)
|
|
126
|
+
return builtIn ? [builtIn] : []
|
|
127
|
+
}
|
|
128
|
+
const resolved: TimeTrackingRecalculation[] = []
|
|
129
|
+
for (const id of ids) {
|
|
130
|
+
const entry = registry.get(id)
|
|
131
|
+
if (!entry) throw new Error(`[internal] unknown time-tracking recalculation id: ${id}`)
|
|
132
|
+
if (!resolved.some((candidate) => candidate.id === entry.id)) resolved.push(entry)
|
|
133
|
+
}
|
|
134
|
+
return resolved
|
|
135
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Calling a contributed strategy safely.
|
|
3
|
+
*
|
|
4
|
+
* Every registry in `EP-32…EP-41` hands third-party code a function the module then
|
|
5
|
+
* calls in the middle of a write path or a report render. None of those call sites
|
|
6
|
+
* originally wrapped the call, so one contributed `round`, `sort` or `serialize`
|
|
7
|
+
* that threw took the surrounding operation down — for the whole process, not just
|
|
8
|
+
* the tenant that installed the contribution, because the registries are plain
|
|
9
|
+
* module-scope maps shared by every request the process serves.
|
|
10
|
+
*
|
|
11
|
+
* The rule these helpers encode: **a broken contribution degrades to the module's
|
|
12
|
+
* own behaviour, loudly.** It never propagates, and it never silently wins either —
|
|
13
|
+
* every fallback logs the registry and the strategy id, because a strategy that
|
|
14
|
+
* quietly stopped applying is the failure mode that takes weeks to notice.
|
|
15
|
+
*
|
|
16
|
+
* Which helper to reach for:
|
|
17
|
+
*
|
|
18
|
+
* - `runStrategy` — single-winner registries, where there is exactly one right
|
|
19
|
+
* answer and the built-in is it when the contribution cannot produce one.
|
|
20
|
+
* - `tryStrategy` — chain registries, where the next candidate is asked anyway, so
|
|
21
|
+
* a thrower is skipped rather than replaced.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
25
|
+
|
|
26
|
+
const logger = createLogger('staff').child({ component: 'time-tracking/registries' })
|
|
27
|
+
|
|
28
|
+
export function runStrategy<TResult>(
|
|
29
|
+
registryId: string,
|
|
30
|
+
strategyId: string,
|
|
31
|
+
run: () => TResult,
|
|
32
|
+
fallback: () => TResult,
|
|
33
|
+
): TResult {
|
|
34
|
+
try {
|
|
35
|
+
return run()
|
|
36
|
+
} catch (err) {
|
|
37
|
+
logger.error('a time-tracking strategy threw; falling back to the built-in', {
|
|
38
|
+
registryId,
|
|
39
|
+
strategyId,
|
|
40
|
+
err,
|
|
41
|
+
})
|
|
42
|
+
return fallback()
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** `undefined` means "this candidate could not answer"; ask the next one. */
|
|
47
|
+
export function tryStrategy<TResult>(
|
|
48
|
+
registryId: string,
|
|
49
|
+
strategyId: string,
|
|
50
|
+
run: () => TResult,
|
|
51
|
+
): TResult | undefined {
|
|
52
|
+
try {
|
|
53
|
+
return run()
|
|
54
|
+
} catch (err) {
|
|
55
|
+
logger.error('a time-tracking strategy threw; skipping this candidate', {
|
|
56
|
+
registryId,
|
|
57
|
+
strategyId,
|
|
58
|
+
err,
|
|
59
|
+
})
|
|
60
|
+
return undefined
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** The largest value `staff_time_entries.rounded_minutes` — a PostgreSQL `integer` — can hold. */
|
|
65
|
+
export const MAX_STORED_MINUTES = 2_147_483_647
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Clamps a strategy's answer to something the column it lands in can hold.
|
|
69
|
+
*
|
|
70
|
+
* `staff_time_entries.rounded_minutes` is an `integer` and the sole input to every
|
|
71
|
+
* amount the suite computes, so a `NaN`, a fraction or a negative from a
|
|
72
|
+
* contributed strategy is not a cosmetic problem: the write either fails or stores
|
|
73
|
+
* garbage, and on read `entryAmount` substitutes `0`, which bills the entry at
|
|
74
|
+
* nothing without saying so. The shipped default `unitMinutes: 0` makes this the
|
|
75
|
+
* FIRST thing a plausible contribution hits — `Math.floor(raw / ctx.settings.unitMinutes)`
|
|
76
|
+
* is division by zero.
|
|
77
|
+
*
|
|
78
|
+
* Three answers are refused rather than stored, and each one logs, because the rule
|
|
79
|
+
* this file encodes is that a degraded contribution degrades *loudly*:
|
|
80
|
+
*
|
|
81
|
+
* - **Not a finite number** — nothing to clamp toward; take the built-in.
|
|
82
|
+
* - **Negative** — a duration that ran backwards is not a value to clamp to zero.
|
|
83
|
+
* Storing `0` is exactly the silent zero-bill the paragraph above describes, so a
|
|
84
|
+
* negative takes the built-in arm alongside `NaN`.
|
|
85
|
+
* - **Larger than the column** — the INSERT would fail with an out-of-range error
|
|
86
|
+
* and 500 the write, which is the propagation these helpers exist to prevent.
|
|
87
|
+
* Clamped down to the column's ceiling so the write still lands.
|
|
88
|
+
*/
|
|
89
|
+
export function clampToStoredMinutes(
|
|
90
|
+
registryId: string,
|
|
91
|
+
strategyId: string,
|
|
92
|
+
value: unknown,
|
|
93
|
+
fallback: () => number,
|
|
94
|
+
): number {
|
|
95
|
+
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
|
96
|
+
logger.error('a time-tracking strategy answered a non-finite duration; falling back to the built-in', {
|
|
97
|
+
registryId,
|
|
98
|
+
strategyId,
|
|
99
|
+
value: String(value),
|
|
100
|
+
})
|
|
101
|
+
return fallback()
|
|
102
|
+
}
|
|
103
|
+
if (value < 0) {
|
|
104
|
+
logger.error('a time-tracking strategy answered a negative duration; falling back to the built-in', {
|
|
105
|
+
registryId,
|
|
106
|
+
strategyId,
|
|
107
|
+
value,
|
|
108
|
+
})
|
|
109
|
+
return fallback()
|
|
110
|
+
}
|
|
111
|
+
const rounded = Math.round(value)
|
|
112
|
+
if (rounded > MAX_STORED_MINUTES) {
|
|
113
|
+
logger.error('a time-tracking strategy answered more minutes than the column can hold; clamping', {
|
|
114
|
+
registryId,
|
|
115
|
+
strategyId,
|
|
116
|
+
value,
|
|
117
|
+
max: MAX_STORED_MINUTES,
|
|
118
|
+
})
|
|
119
|
+
return MAX_STORED_MINUTES
|
|
120
|
+
}
|
|
121
|
+
return rounded
|
|
122
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one registry shape every time-tracking strategy registry is built from
|
|
3
|
+
* (EP-32…EP-41 of `.ai/specs/2026-08-24-time-tracking-umes-extension-points.md`).
|
|
4
|
+
*
|
|
5
|
+
* It follows the house provider idiom — `registerPaymentProvider` /
|
|
6
|
+
* `registerShippingProvider` in `modules/sales/lib/providers/registry.ts`: a
|
|
7
|
+
* module-level `Map`, a `register` that returns its own disposer, and plain
|
|
8
|
+
* lookup helpers. That makes every registry reachable from the browser bundle as
|
|
9
|
+
* well as the server, which matters because four of these strategies are
|
|
10
|
+
* consulted by client previews that cannot resolve DI.
|
|
11
|
+
*
|
|
12
|
+
* Ordering is the only thing added on top of the sales idiom, and it is what
|
|
13
|
+
* keeps "no contribution means no behaviour change" true: entries are served in
|
|
14
|
+
* descending `priority`, ties broken by registration order, and every built-in
|
|
15
|
+
* registers at `BUILT_IN_STRATEGY_PRIORITY` — far below the default of `0` — so
|
|
16
|
+
* a built-in is always the last candidate considered.
|
|
17
|
+
*
|
|
18
|
+
* **A built-in is not just a low-priority entry, it is a separate slot.** The
|
|
19
|
+
* fail-closed rule in `scope.ts` lands an unscoped call on "the built-in", and the
|
|
20
|
+
* whole point of that rule is that the built-in is the same pure code the module
|
|
21
|
+
* shipped before the registries existed. With one map keyed by id and last-writer-
|
|
22
|
+
* wins semantics, a contribution registering under a published built-in id BECAME
|
|
23
|
+
* the built-in: it ran on the unscoped path the gate exists to keep byte-identical,
|
|
24
|
+
* and its disposer then deleted the real built-in permanently, after which every
|
|
25
|
+
* resolver that requires one throws. So `registerBuiltIn` writes to a map
|
|
26
|
+
* `register` cannot reach, `register` refuses a built-in id outright, and no
|
|
27
|
+
* disposer can remove a built-in.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
export const BUILT_IN_STRATEGY_PRIORITY = -1000
|
|
31
|
+
|
|
32
|
+
export type RegisteredStrategy = {
|
|
33
|
+
id: string
|
|
34
|
+
priority?: number
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export type StrategyRegistry<TEntry extends RegisteredStrategy> = {
|
|
38
|
+
registryId: string
|
|
39
|
+
/**
|
|
40
|
+
* Registers the module's own default. Not part of the contribution surface —
|
|
41
|
+
* module-load code only. The returned entry is the identity every resolver
|
|
42
|
+
* should compare against rather than re-looking the id up.
|
|
43
|
+
*/
|
|
44
|
+
registerBuiltIn(entry: TEntry): TEntry
|
|
45
|
+
/** Contributes a strategy. Throws on a built-in id. Returns its own disposer. */
|
|
46
|
+
register(entry: TEntry): () => void
|
|
47
|
+
get(id: string | null | undefined): TEntry | null
|
|
48
|
+
has(id: string | null | undefined): boolean
|
|
49
|
+
isBuiltIn(id: string | null | undefined): boolean
|
|
50
|
+
list(): TEntry[]
|
|
51
|
+
ids(): string[]
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
type Slot<TEntry extends RegisteredStrategy> = {
|
|
55
|
+
entry: TEntry
|
|
56
|
+
sequence: number
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function orderSlots<TEntry extends RegisteredStrategy>(left: Slot<TEntry>, right: Slot<TEntry>): number {
|
|
60
|
+
const leftPriority = left.entry.priority ?? 0
|
|
61
|
+
const rightPriority = right.entry.priority ?? 0
|
|
62
|
+
if (leftPriority !== rightPriority) return rightPriority - leftPriority
|
|
63
|
+
return left.sequence - right.sequence
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function createStrategyRegistry<TEntry extends RegisteredStrategy>(
|
|
67
|
+
registryId: string,
|
|
68
|
+
): StrategyRegistry<TEntry> {
|
|
69
|
+
const builtInSlots = new Map<string, Slot<TEntry>>()
|
|
70
|
+
const contributedSlots = new Map<string, Slot<TEntry>>()
|
|
71
|
+
let sequence = 0
|
|
72
|
+
|
|
73
|
+
const readId = (entry: TEntry): string => {
|
|
74
|
+
const id = typeof entry?.id === 'string' ? entry.id.trim() : ''
|
|
75
|
+
if (!id) throw new Error(`[internal] ${registryId} requires a non-empty strategy id`)
|
|
76
|
+
return id
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const registerBuiltIn = (entry: TEntry): TEntry => {
|
|
80
|
+
const id = readId(entry)
|
|
81
|
+
if (builtInSlots.has(id)) {
|
|
82
|
+
throw new Error(`[internal] ${registryId} already has a built-in registered as ${id}`)
|
|
83
|
+
}
|
|
84
|
+
sequence += 1
|
|
85
|
+
const slot: Slot<TEntry> = { entry: { ...entry, id }, sequence }
|
|
86
|
+
builtInSlots.set(id, slot)
|
|
87
|
+
return slot.entry
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const register = (entry: TEntry): (() => void) => {
|
|
91
|
+
const id = readId(entry)
|
|
92
|
+
if (builtInSlots.has(id)) {
|
|
93
|
+
throw new Error(
|
|
94
|
+
`[internal] ${id} is a built-in ${registryId} strategy and cannot be replaced; register your own id`,
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
sequence += 1
|
|
98
|
+
const slot: Slot<TEntry> = { entry: { ...entry, id }, sequence }
|
|
99
|
+
contributedSlots.set(id, slot)
|
|
100
|
+
return () => {
|
|
101
|
+
if (contributedSlots.get(id) === slot) contributedSlots.delete(id)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const list = (): TEntry[] =>
|
|
106
|
+
[...contributedSlots.values(), ...builtInSlots.values()].sort(orderSlots).map((slot) => slot.entry)
|
|
107
|
+
|
|
108
|
+
const get = (id: string | null | undefined): TEntry | null => {
|
|
109
|
+
if (typeof id !== 'string') return null
|
|
110
|
+
const trimmed = id.trim()
|
|
111
|
+
return contributedSlots.get(trimmed)?.entry ?? builtInSlots.get(trimmed)?.entry ?? null
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
registryId,
|
|
116
|
+
registerBuiltIn,
|
|
117
|
+
register,
|
|
118
|
+
get,
|
|
119
|
+
has: (id) => get(id) !== null,
|
|
120
|
+
isBuiltIn: (id) => typeof id === 'string' && builtInSlots.has(id.trim()),
|
|
121
|
+
list,
|
|
122
|
+
ids: () => list().map((entry) => entry.id),
|
|
123
|
+
}
|
|
124
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The scope contract every time-tracking strategy resolver receives, and the
|
|
3
|
+
* fail-closed rule the spec's cross-cutting requirements attach to it: a
|
|
4
|
+
* contributed strategy runs only when the caller supplied BOTH a tenant and an
|
|
5
|
+
* organization, matching `resolveProjectAccess` in `../access.ts`.
|
|
6
|
+
*
|
|
7
|
+
* A built-in never needs the scope — it is the same pure arithmetic the module
|
|
8
|
+
* shipped before the registries existed — so failing closed lands on the
|
|
9
|
+
* built-in rather than on an error. That is what makes a client preview
|
|
10
|
+
* (`TimeEntryDialog`, `timeTrackingSettingsForm`, `ProjectCodeField`), which has
|
|
11
|
+
* no tenant id to hand, keep producing exactly the number it produced before.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export type TimeTrackingResolverScope = {
|
|
15
|
+
tenantId: string
|
|
16
|
+
organizationId: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type ScopedResolverContext = Partial<TimeTrackingResolverScope>
|
|
20
|
+
|
|
21
|
+
function isNonEmpty(value: unknown): value is string {
|
|
22
|
+
return typeof value === 'string' && value.trim().length > 0
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function readResolverScope(
|
|
26
|
+
ctx: ScopedResolverContext | null | undefined,
|
|
27
|
+
): TimeTrackingResolverScope | null {
|
|
28
|
+
if (!ctx) return null
|
|
29
|
+
if (!isNonEmpty(ctx.tenantId) || !isNonEmpty(ctx.organizationId)) return null
|
|
30
|
+
return { tenantId: ctx.tenantId, organizationId: ctx.organizationId }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function hasResolverScope(ctx: ScopedResolverContext | null | undefined): boolean {
|
|
34
|
+
return readResolverScope(ctx) !== null
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Picks the strategy a scoped call site should run: the highest-priority
|
|
39
|
+
* contribution when the scope is complete, the built-in otherwise. `candidates`
|
|
40
|
+
* arrives already ordered by the registry.
|
|
41
|
+
*
|
|
42
|
+
* The built-in is passed **by reference**, not looked up by id among the
|
|
43
|
+
* candidates. An id is a string anyone can register; identity is not. Resolving
|
|
44
|
+
* the fail-closed default by id meant a contribution that named itself after the
|
|
45
|
+
* built-in was served on the unscoped path the rule exists to keep byte-identical
|
|
46
|
+
* — `registry.ts` now refuses that registration, and this makes the second half of
|
|
47
|
+
* the same guarantee structural rather than conventional.
|
|
48
|
+
*/
|
|
49
|
+
export function selectScopedStrategy<TEntry extends { id: string }>(
|
|
50
|
+
candidates: readonly TEntry[],
|
|
51
|
+
builtIn: TEntry,
|
|
52
|
+
ctx: ScopedResolverContext | null | undefined,
|
|
53
|
+
): TEntry {
|
|
54
|
+
if (!hasResolverScope(ctx)) return builtIn
|
|
55
|
+
return candidates[0] ?? builtIn
|
|
56
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
export type RollupTask = {
|
|
2
|
+
id: string
|
|
3
|
+
parentTaskId?: string | null
|
|
4
|
+
deletedAt?: Date | string | null
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export type RollupEntry = {
|
|
8
|
+
durationMinutes?: number | null
|
|
9
|
+
deletedAt?: Date | string | null
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type EntriesByTaskId =
|
|
13
|
+
| ReadonlyMap<string, readonly RollupEntry[]>
|
|
14
|
+
| Record<string, readonly RollupEntry[] | undefined>
|
|
15
|
+
|
|
16
|
+
export type TaskRollup = {
|
|
17
|
+
ownMinutes: number
|
|
18
|
+
loggedMinutes: number
|
|
19
|
+
childCount: number
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function readEntries(source: EntriesByTaskId | null | undefined, taskId: string): readonly RollupEntry[] {
|
|
23
|
+
if (!source) return []
|
|
24
|
+
if (source instanceof Map) return source.get(taskId) ?? []
|
|
25
|
+
return (source as Record<string, readonly RollupEntry[] | undefined>)[taskId] ?? []
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function sumEntryMinutes(entries: readonly RollupEntry[]): number {
|
|
29
|
+
let total = 0
|
|
30
|
+
for (const entry of entries) {
|
|
31
|
+
if (!entry || entry.deletedAt) continue
|
|
32
|
+
const minutes = entry.durationMinutes
|
|
33
|
+
if (minutes === null || minutes === undefined || !Number.isFinite(minutes)) continue
|
|
34
|
+
total += minutes
|
|
35
|
+
}
|
|
36
|
+
return total
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function loggedMinutes(
|
|
40
|
+
task: RollupTask,
|
|
41
|
+
allTasks: readonly RollupTask[],
|
|
42
|
+
entriesByTaskId: EntriesByTaskId,
|
|
43
|
+
): TaskRollup {
|
|
44
|
+
const ownMinutes = sumEntryMinutes(readEntries(entriesByTaskId, task.id))
|
|
45
|
+
const children = (allTasks ?? []).filter(
|
|
46
|
+
(candidate) => candidate.parentTaskId === task.id && candidate.id !== task.id && !candidate.deletedAt,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
let childMinutes = 0
|
|
50
|
+
for (const child of children) {
|
|
51
|
+
childMinutes += sumEntryMinutes(readEntries(entriesByTaskId, child.id))
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
ownMinutes,
|
|
56
|
+
loggedMinutes: ownMinutes + childMinutes,
|
|
57
|
+
childCount: children.length,
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EP-32 — the rounding strategy registry.
|
|
3
|
+
*
|
|
4
|
+
* `roundMinutes` is still the single rounding entry point every call site uses,
|
|
5
|
+
* and with no contribution it runs the built-in `staff.time_tracking.rounding.unit`
|
|
6
|
+
* strategy, which is the same `up`/`nearest` × `0|5|10|15` arithmetic this file
|
|
7
|
+
* has always held. A contribution outranks it only when the call site supplies a
|
|
8
|
+
* complete tenant + organization scope (`registries/scope.ts`), so an unscoped
|
|
9
|
+
* client preview keeps producing the built-in's number.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { extensionPoints } from '@open-mercato/core/modules/staff/extension-points'
|
|
13
|
+
import { createStrategyRegistry, BUILT_IN_STRATEGY_PRIORITY } from './registries/registry'
|
|
14
|
+
import { selectScopedStrategy, type ScopedResolverContext } from './registries/scope'
|
|
15
|
+
import { clampToStoredMinutes, runStrategy } from './registries/invoke'
|
|
16
|
+
|
|
17
|
+
export type RoundingUnitMinutes = 0 | 5 | 10 | 15
|
|
18
|
+
|
|
19
|
+
export type RoundingDirection = 'up' | 'nearest'
|
|
20
|
+
|
|
21
|
+
export type RoundingSettings = {
|
|
22
|
+
unitMinutes: RoundingUnitMinutes
|
|
23
|
+
direction: RoundingDirection
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const DEFAULT_ROUNDING_SETTINGS: RoundingSettings = {
|
|
27
|
+
unitMinutes: 0,
|
|
28
|
+
direction: 'up',
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export type TimeRoundingContext = ScopedResolverContext & {
|
|
32
|
+
settings: RoundingSettings
|
|
33
|
+
/** Present on write paths; absent on the settings-screen and dialog previews. */
|
|
34
|
+
timeProjectId?: string | null
|
|
35
|
+
taskId?: string | null
|
|
36
|
+
staffMemberId?: string | null
|
|
37
|
+
date?: string | null
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export type TimeRoundingStrategy = {
|
|
41
|
+
id: string
|
|
42
|
+
labelKey: string
|
|
43
|
+
priority?: number
|
|
44
|
+
round(rawMinutes: number, ctx: TimeRoundingContext): number
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export const TIME_ROUNDING_REGISTRY_ID = extensionPoints.hosts.timeRoundingRegistry.spotId
|
|
48
|
+
|
|
49
|
+
export const BUILT_IN_TIME_ROUNDING_STRATEGY_ID = 'staff.time_tracking.rounding.unit'
|
|
50
|
+
|
|
51
|
+
const registry = createStrategyRegistry<TimeRoundingStrategy>(TIME_ROUNDING_REGISTRY_ID)
|
|
52
|
+
|
|
53
|
+
export function registerTimeRoundingStrategy(strategy: TimeRoundingStrategy): () => void {
|
|
54
|
+
return registry.register(strategy)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function listTimeRoundingStrategies(): TimeRoundingStrategy[] {
|
|
58
|
+
return registry.list()
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function getTimeRoundingStrategy(id: string | null | undefined): TimeRoundingStrategy | null {
|
|
62
|
+
return registry.get(id)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function roundToUnit(raw: number, settings: RoundingSettings): number {
|
|
66
|
+
if (!Number.isFinite(raw)) return 0
|
|
67
|
+
const unit = settings?.unitMinutes ?? 0
|
|
68
|
+
if (!unit) return Math.round(raw)
|
|
69
|
+
|
|
70
|
+
const sign = raw < 0 ? -1 : 1
|
|
71
|
+
const magnitude = Math.abs(raw) / unit
|
|
72
|
+
const units = settings.direction === 'nearest' ? Math.round(magnitude) : Math.ceil(magnitude)
|
|
73
|
+
return sign * units * unit
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const builtInRoundingStrategy: TimeRoundingStrategy = registry.registerBuiltIn({
|
|
77
|
+
id: BUILT_IN_TIME_ROUNDING_STRATEGY_ID,
|
|
78
|
+
labelKey: 'staff.time_tracking.rounding.strategy.unit',
|
|
79
|
+
priority: BUILT_IN_STRATEGY_PRIORITY,
|
|
80
|
+
round: (rawMinutes, ctx) => roundToUnit(rawMinutes, ctx.settings),
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
export function resolveTimeRoundingStrategy(
|
|
84
|
+
ctx?: ScopedResolverContext | null,
|
|
85
|
+
): TimeRoundingStrategy {
|
|
86
|
+
return selectScopedStrategy(registry.list(), builtInRoundingStrategy, ctx)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* The single rounding entry point, and the boundary a contributed strategy's answer
|
|
91
|
+
* has to survive.
|
|
92
|
+
*
|
|
93
|
+
* The result is written straight into `staff_time_entries.rounded_minutes` — an
|
|
94
|
+
* `integer` column, and the only input to every amount the suite computes — so an
|
|
95
|
+
* unusable answer is clamped back to the built-in's rather than stored, and one
|
|
96
|
+
* larger than the column can hold is capped at its ceiling rather than 500ing the
|
|
97
|
+
* write. The built-in is exempt from the clamp on purpose: it is the arithmetic the
|
|
98
|
+
* module shipped, it cannot produce a non-finite or fractional value, and re-shaping
|
|
99
|
+
* its output would be the behaviour change the registries exist to avoid.
|
|
100
|
+
*
|
|
101
|
+
* A strategy that *threw* has already been logged and replaced by `runStrategy`, so
|
|
102
|
+
* it returns the built-in directly instead of routing its `NaN` through the clamp,
|
|
103
|
+
* which would log the same failure a second time under the wrong description.
|
|
104
|
+
*/
|
|
105
|
+
export function roundMinutes(
|
|
106
|
+
raw: number,
|
|
107
|
+
settings: RoundingSettings,
|
|
108
|
+
ctx?: ScopedResolverContext | null,
|
|
109
|
+
): number {
|
|
110
|
+
const strategy = resolveTimeRoundingStrategy(ctx)
|
|
111
|
+
const builtIn = () => roundToUnit(raw, settings)
|
|
112
|
+
if (strategy === builtInRoundingStrategy) return builtIn()
|
|
113
|
+
|
|
114
|
+
let threw = false
|
|
115
|
+
const answer = runStrategy(
|
|
116
|
+
TIME_ROUNDING_REGISTRY_ID,
|
|
117
|
+
strategy.id,
|
|
118
|
+
() => strategy.round(raw, { ...(ctx ?? {}), settings }),
|
|
119
|
+
() => {
|
|
120
|
+
threw = true
|
|
121
|
+
return Number.NaN
|
|
122
|
+
},
|
|
123
|
+
)
|
|
124
|
+
if (threw) return builtIn()
|
|
125
|
+
return clampToStoredMinutes(TIME_ROUNDING_REGISTRY_ID, strategy.id, answer, builtIn)
|
|
126
|
+
}
|