@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,115 @@
|
|
|
1
|
+
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
2
|
+
|
|
3
|
+
const logger = createLogger('staff').child({ component: 'time-tracking/featureAccess' })
|
|
4
|
+
|
|
5
|
+
type RbacServiceLike = {
|
|
6
|
+
getGrantedFeatures?: (
|
|
7
|
+
userId: string,
|
|
8
|
+
scope: { tenantId: string | null; organizationId: string | null },
|
|
9
|
+
) => Promise<string[]>
|
|
10
|
+
userHasAllFeatures?: (
|
|
11
|
+
userId: string,
|
|
12
|
+
required: string[],
|
|
13
|
+
scope: { tenantId: string | null; organizationId: string | null },
|
|
14
|
+
) => Promise<boolean>
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
type ContainerLike = { resolve: (name: string) => unknown }
|
|
18
|
+
|
|
19
|
+
export type FeatureAccess = {
|
|
20
|
+
/** The decision. Fail-closed: `false` whenever it could not be established. */
|
|
21
|
+
allowed: boolean
|
|
22
|
+
/**
|
|
23
|
+
* The caller's grants, for the plumbing that genuinely needs a list (mutation
|
|
24
|
+
* guards, enrichers). Empty when unresolved — check `resolved` before reading
|
|
25
|
+
* anything into an empty array.
|
|
26
|
+
*/
|
|
27
|
+
grantedFeatures: string[]
|
|
28
|
+
/**
|
|
29
|
+
* `false` when RBAC could not be consulted — the service was unresolvable, it
|
|
30
|
+
* exposes no way to answer, or the call threw. An unresolved lookup is not the
|
|
31
|
+
* same as "no permission", and a caller that treats it as one silently
|
|
32
|
+
* downgrades somebody's access.
|
|
33
|
+
*
|
|
34
|
+
* `true` therefore means the answer is definite: RBAC produced the grant list,
|
|
35
|
+
* or there was no one to ask about (no user). An empty `grantedFeatures` is
|
|
36
|
+
* only ever a real answer under `resolved`.
|
|
37
|
+
*/
|
|
38
|
+
resolved: boolean
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const DENIED: FeatureAccess = { allowed: false, grantedFeatures: [], resolved: false }
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* One question, one authority, one failure direction.
|
|
45
|
+
*
|
|
46
|
+
* The module previously carried twelve hand-rolled copies of this lookup, each
|
|
47
|
+
* choosing its own semantics: nine returned `[]` on failure (fail-closed, but
|
|
48
|
+
* silently — a manager was demoted to their own project memberships and nothing
|
|
49
|
+
* said why), six returned `null` and were then read as `granted === null || …`
|
|
50
|
+
* (fail-open — which is how an RBAC outage handed hourly rates to plain report
|
|
51
|
+
* viewers), and the rest were somewhere in between. The same
|
|
52
|
+
* `staff.timesheets.rates.view` flag ended up failing closed on two surfaces and
|
|
53
|
+
* open on three.
|
|
54
|
+
*
|
|
55
|
+
* The decision comes from `userHasAllFeatures` rather than from matching the
|
|
56
|
+
* grant array locally. Both paths agree in a healthy runtime — `getGrantedFeatures`
|
|
57
|
+
* expands a super-admin's `'*'` into per-module wildcards that `authorizeFeatures`
|
|
58
|
+
* understands — but only the service call carries `isSuperAdmin` explicitly, and
|
|
59
|
+
* asking one question in one way is what stops two surfaces disagreeing about the
|
|
60
|
+
* same person.
|
|
61
|
+
*
|
|
62
|
+
* The array is returned alongside it because guards and enrichers legitimately
|
|
63
|
+
* need a list. It is deliberately *not* the thing to authorize on.
|
|
64
|
+
*
|
|
65
|
+
* A failed lookup logs. That is the point: the previous silence meant an RBAC
|
|
66
|
+
* outage presented to users as missing data and to operators as nothing at all.
|
|
67
|
+
*/
|
|
68
|
+
export async function resolveFeatureAccess(
|
|
69
|
+
container: ContainerLike,
|
|
70
|
+
userId: string | null,
|
|
71
|
+
required: readonly string[],
|
|
72
|
+
scope: { tenantId: string | null; organizationId: string | null },
|
|
73
|
+
): Promise<FeatureAccess> {
|
|
74
|
+
if (!userId) return { ...DENIED, resolved: true }
|
|
75
|
+
|
|
76
|
+
let rbac: RbacServiceLike | undefined
|
|
77
|
+
try {
|
|
78
|
+
rbac = container.resolve('rbacService') as RbacServiceLike | undefined
|
|
79
|
+
} catch (err) {
|
|
80
|
+
logger.warn('rbacService could not be resolved; denying', { err, userId, required })
|
|
81
|
+
return { ...DENIED }
|
|
82
|
+
}
|
|
83
|
+
if (!rbac?.userHasAllFeatures) {
|
|
84
|
+
logger.warn('rbacService cannot answer feature checks; denying', { userId, required })
|
|
85
|
+
return { ...DENIED }
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
let allowed = false
|
|
89
|
+
try {
|
|
90
|
+
allowed = (await rbac.userHasAllFeatures(userId, [...required], scope)) === true
|
|
91
|
+
} catch (err) {
|
|
92
|
+
logger.warn('feature check failed; denying', { err, userId, required })
|
|
93
|
+
return { ...DENIED }
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// The grant list is a convenience, not the decision, so a failure to read it
|
|
97
|
+
// downgrades the plumbing that wants a list without touching `allowed`.
|
|
98
|
+
// A service that cannot produce a list at all is the same kind of failure as
|
|
99
|
+
// one whose call throws: the empty array is a fallback, not RBAC's answer, and
|
|
100
|
+
// saying otherwise is how an empty list gets read as authoritative.
|
|
101
|
+
let grantedFeatures: string[] = []
|
|
102
|
+
let resolved = false
|
|
103
|
+
if (!rbac.getGrantedFeatures) {
|
|
104
|
+
logger.warn('rbacService cannot list grants; feature decision still stands', { userId, required })
|
|
105
|
+
} else {
|
|
106
|
+
try {
|
|
107
|
+
grantedFeatures = (await rbac.getGrantedFeatures(userId, scope)) ?? []
|
|
108
|
+
resolved = true
|
|
109
|
+
} catch (err) {
|
|
110
|
+
logger.warn('grant list could not be read; feature decision still stands', { err, userId })
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return { allowed, grantedFeatures, resolved }
|
|
115
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
export const MINUTES_PER_DAY = 1440
|
|
2
|
+
|
|
3
|
+
export type IntervalInput = {
|
|
4
|
+
start?: string | null
|
|
5
|
+
end?: string | null
|
|
6
|
+
durationMinutes?: number | null
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export type ComputedIntervalField = 'start' | 'end' | 'duration' | null
|
|
10
|
+
|
|
11
|
+
export type DerivedInterval = {
|
|
12
|
+
start: string | null
|
|
13
|
+
end: string | null
|
|
14
|
+
durationMinutes: number | null
|
|
15
|
+
computed: ComputedIntervalField
|
|
16
|
+
crossesMidnight: boolean
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const CLOCK_PATTERN = /^(\d{1,2}):(\d{2})$/
|
|
20
|
+
|
|
21
|
+
export function parseClock(value: string | null | undefined): number | null {
|
|
22
|
+
if (typeof value !== 'string') return null
|
|
23
|
+
const match = CLOCK_PATTERN.exec(value.trim())
|
|
24
|
+
if (!match) return null
|
|
25
|
+
const hours = Number.parseInt(match[1], 10)
|
|
26
|
+
const minutes = Number.parseInt(match[2], 10)
|
|
27
|
+
if (hours > 23 || minutes > 59) return null
|
|
28
|
+
return hours * 60 + minutes
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function formatClock(minutesOfDay: number): string {
|
|
32
|
+
const normalized = ((Math.round(minutesOfDay) % MINUTES_PER_DAY) + MINUTES_PER_DAY) % MINUTES_PER_DAY
|
|
33
|
+
const hours = Math.floor(normalized / 60)
|
|
34
|
+
const minutes = normalized % 60
|
|
35
|
+
return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function normalizeDuration(value: number | null | undefined): number | null {
|
|
39
|
+
if (value === null || value === undefined) return null
|
|
40
|
+
if (!Number.isFinite(value) || value < 0) return null
|
|
41
|
+
return Math.round(value)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function deriveInterval(input: IntervalInput): DerivedInterval {
|
|
45
|
+
const start = parseClock(input.start)
|
|
46
|
+
const end = parseClock(input.end)
|
|
47
|
+
const duration = normalizeDuration(input.durationMinutes)
|
|
48
|
+
|
|
49
|
+
if (start !== null && end !== null) {
|
|
50
|
+
const crossesMidnight = end < start
|
|
51
|
+
const computedDuration = crossesMidnight ? MINUTES_PER_DAY - start + end : end - start
|
|
52
|
+
return {
|
|
53
|
+
start: formatClock(start),
|
|
54
|
+
end: formatClock(end),
|
|
55
|
+
durationMinutes: computedDuration,
|
|
56
|
+
computed: duration === computedDuration ? null : 'duration',
|
|
57
|
+
crossesMidnight,
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (start !== null && duration !== null) {
|
|
62
|
+
const absoluteEnd = start + duration
|
|
63
|
+
return {
|
|
64
|
+
start: formatClock(start),
|
|
65
|
+
end: formatClock(absoluteEnd),
|
|
66
|
+
durationMinutes: duration,
|
|
67
|
+
computed: 'end',
|
|
68
|
+
crossesMidnight: absoluteEnd >= MINUTES_PER_DAY,
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (end !== null && duration !== null) {
|
|
73
|
+
const absoluteStart = end - duration
|
|
74
|
+
return {
|
|
75
|
+
start: formatClock(absoluteStart),
|
|
76
|
+
end: formatClock(end),
|
|
77
|
+
durationMinutes: duration,
|
|
78
|
+
computed: 'start',
|
|
79
|
+
crossesMidnight: absoluteStart < 0,
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
start: start === null ? null : formatClock(start),
|
|
85
|
+
end: end === null ? null : formatClock(end),
|
|
86
|
+
durationMinutes: duration,
|
|
87
|
+
computed: null,
|
|
88
|
+
crossesMidnight: false,
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rewrites existing project codes to the short form and re-derives every task
|
|
3
|
+
* reference to match.
|
|
4
|
+
*
|
|
5
|
+
* `staff_time_tasks.reference` is denormalised (`<project code>-<sequence>`) and
|
|
6
|
+
* frozen at creation, because it is quoted where the task row is no longer
|
|
7
|
+
* joined. That freeze is what makes this a migration rather than a settings
|
|
8
|
+
* change: shortening a project's code does not retro-number its tasks, so
|
|
9
|
+
* without this the tenant ends up with `EHK` projects whose tasks still say
|
|
10
|
+
* `ERGO-HESTIA-KORPO-4`.
|
|
11
|
+
*
|
|
12
|
+
* What this deliberately preserves:
|
|
13
|
+
*
|
|
14
|
+
* * **Sequence numbers.** `APOLLO-14` becomes `APO-14`, never `APO-1`. The
|
|
15
|
+
* number is the task's identity to the people who use it; only the prefix
|
|
16
|
+
* changes.
|
|
17
|
+
* * **Hand-picked codes.** A code that is already at or under the target length
|
|
18
|
+
* was chosen, not derived, so it is left exactly as it is.
|
|
19
|
+
*
|
|
20
|
+
* What it cannot preserve: a reference somebody wrote down outside the system.
|
|
21
|
+
* Reports do not quote task references — report lines are labelled by task title
|
|
22
|
+
* and `staff_time_report_entries` freezes minutes and money, never the reference
|
|
23
|
+
* — so nothing already exported changes meaning. Anything a person memorised
|
|
24
|
+
* does.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import type { EntityManager } from '@mikro-orm/postgresql'
|
|
28
|
+
import { StaffTimeProject, StaffTimeTask } from '../../data/entities'
|
|
29
|
+
import { PROJECT_CODE_TARGET_LENGTH, deriveProjectCodeBase } from './projectCode'
|
|
30
|
+
import { formatTaskReference } from '../timesheets-tasks/taskReference'
|
|
31
|
+
|
|
32
|
+
export type MigrateProjectCodesScope = { tenantId: string; organizationId: string }
|
|
33
|
+
|
|
34
|
+
export type ProjectCodeChange = {
|
|
35
|
+
projectId: string
|
|
36
|
+
projectName: string
|
|
37
|
+
fromCode: string
|
|
38
|
+
toCode: string
|
|
39
|
+
taskCount: number
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export type MigrateProjectCodesResult = {
|
|
43
|
+
changes: ProjectCodeChange[]
|
|
44
|
+
/** Projects left alone because their code was already short. */
|
|
45
|
+
skipped: number
|
|
46
|
+
tasksRenumbered: number
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Plans the rename without writing anything. Exported so the CLI can offer a dry
|
|
51
|
+
* run: a rewrite of every code in a tenant is not something to discover the
|
|
52
|
+
* shape of by executing it.
|
|
53
|
+
*/
|
|
54
|
+
export function planProjectCodeMigration(
|
|
55
|
+
projects: readonly Pick<StaffTimeProject, 'id' | 'name' | 'code'>[],
|
|
56
|
+
taskCountByProjectId: ReadonlyMap<string, number>,
|
|
57
|
+
): { changes: ProjectCodeChange[]; skipped: number } {
|
|
58
|
+
// Codes that survive untouched are reserved first, so a project keeping `HBH`
|
|
59
|
+
// cannot have it taken from under it by a project being shortened to `HBH`.
|
|
60
|
+
const taken = new Set<string>()
|
|
61
|
+
const shortening: typeof projects[number][] = []
|
|
62
|
+
let skipped = 0
|
|
63
|
+
|
|
64
|
+
for (const project of projects) {
|
|
65
|
+
const code = (project.code ?? '').trim()
|
|
66
|
+
if (code.length > 0 && code.length <= PROJECT_CODE_TARGET_LENGTH) {
|
|
67
|
+
taken.add(code.toUpperCase())
|
|
68
|
+
skipped += 1
|
|
69
|
+
continue
|
|
70
|
+
}
|
|
71
|
+
shortening.push(project)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Oldest first, so a re-run is stable and the same project keeps the same code
|
|
75
|
+
// rather than the order of a query deciding who gets the unsuffixed one.
|
|
76
|
+
const ordered = [...shortening].sort((left, right) => left.id.localeCompare(right.id))
|
|
77
|
+
|
|
78
|
+
const changes: ProjectCodeChange[] = []
|
|
79
|
+
for (const project of ordered) {
|
|
80
|
+
const base = deriveProjectCodeBase(project.name ?? '')
|
|
81
|
+
let candidate = base
|
|
82
|
+
for (let counter = 2; taken.has(candidate.toUpperCase()); counter += 1) {
|
|
83
|
+
candidate = `${base}${counter}`
|
|
84
|
+
}
|
|
85
|
+
taken.add(candidate.toUpperCase())
|
|
86
|
+
changes.push({
|
|
87
|
+
projectId: project.id,
|
|
88
|
+
projectName: project.name ?? '',
|
|
89
|
+
fromCode: project.code ?? '',
|
|
90
|
+
toCode: candidate,
|
|
91
|
+
taskCount: taskCountByProjectId.get(project.id) ?? 0,
|
|
92
|
+
})
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return { changes, skipped }
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export async function migrateProjectCodes(
|
|
99
|
+
em: EntityManager,
|
|
100
|
+
scope: MigrateProjectCodesScope,
|
|
101
|
+
options: { dryRun?: boolean } = {},
|
|
102
|
+
): Promise<MigrateProjectCodesResult> {
|
|
103
|
+
const projects = await em.find(StaffTimeProject, {
|
|
104
|
+
tenantId: scope.tenantId,
|
|
105
|
+
organizationId: scope.organizationId,
|
|
106
|
+
deletedAt: null,
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
const tasks = await em.find(StaffTimeTask, {
|
|
110
|
+
tenantId: scope.tenantId,
|
|
111
|
+
organizationId: scope.organizationId,
|
|
112
|
+
deletedAt: null,
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
const tasksByProjectId = new Map<string, StaffTimeTask[]>()
|
|
116
|
+
for (const task of tasks) {
|
|
117
|
+
const bucket = tasksByProjectId.get(task.timeProjectId) ?? []
|
|
118
|
+
bucket.push(task)
|
|
119
|
+
tasksByProjectId.set(task.timeProjectId, bucket)
|
|
120
|
+
}
|
|
121
|
+
const counts = new Map([...tasksByProjectId].map(([id, list]) => [id, list.length]))
|
|
122
|
+
|
|
123
|
+
const { changes, skipped } = planProjectCodeMigration(projects, counts)
|
|
124
|
+
if (options.dryRun) {
|
|
125
|
+
return {
|
|
126
|
+
changes,
|
|
127
|
+
skipped,
|
|
128
|
+
tasksRenumbered: changes.reduce((total, change) => total + change.taskCount, 0),
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const projectById = new Map(projects.map((project) => [project.id, project]))
|
|
133
|
+
const now = new Date()
|
|
134
|
+
let tasksRenumbered = 0
|
|
135
|
+
|
|
136
|
+
// One transaction: a half-applied rename leaves tasks quoting a prefix their
|
|
137
|
+
// project no longer carries, which is the exact inconsistency this exists to
|
|
138
|
+
// remove.
|
|
139
|
+
await em.transactional(async (tem) => {
|
|
140
|
+
for (const change of changes) {
|
|
141
|
+
const project = projectById.get(change.projectId)
|
|
142
|
+
if (!project) continue
|
|
143
|
+
const managed = tem.getReference(StaffTimeProject, project.id)
|
|
144
|
+
managed.code = change.toCode
|
|
145
|
+
managed.updatedAt = now
|
|
146
|
+
tem.persist(managed)
|
|
147
|
+
|
|
148
|
+
for (const task of tasksByProjectId.get(change.projectId) ?? []) {
|
|
149
|
+
const managedTask = tem.getReference(StaffTimeTask, task.id)
|
|
150
|
+
// The sequence number is the task's identity to the people who use it;
|
|
151
|
+
// only the prefix moves.
|
|
152
|
+
managedTask.reference = formatTaskReference(change.toCode, task.sequenceNumber)
|
|
153
|
+
managedTask.updatedAt = now
|
|
154
|
+
tem.persist(managedTask)
|
|
155
|
+
tasksRenumbered += 1
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
return { changes, skipped, tasksRenumbered }
|
|
161
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { resolveFeatureAccess } from './featureAccess'
|
|
2
|
+
|
|
3
|
+
export const RATES_FEATURE = 'staff.timesheets.rates.view'
|
|
4
|
+
|
|
5
|
+
type ContainerLike = { resolve: (name: string) => unknown }
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Whether the caller may see rates, costs and amounts.
|
|
9
|
+
*
|
|
10
|
+
* One decision, made once, in one direction — because the same feature was
|
|
11
|
+
* previously answered by five hand-rolled gates with two opposite failure
|
|
12
|
+
* defaults. Three report surfaces (preview, export, sheet) read
|
|
13
|
+
* `grantedFeatures === null || authorizeFeatures(...)`, so an unreadable grant
|
|
14
|
+
* set *opened* the money fields; `my-work` and the entries list returned `false`
|
|
15
|
+
* in the same situation. None of the three report routes declares
|
|
16
|
+
* `staff.timesheets.rates.view` in its metadata — they require only
|
|
17
|
+
* `reports.view` — so nothing else stood between a plain report viewer and the
|
|
18
|
+
* customer's hourly rate when RBAC was unavailable.
|
|
19
|
+
*
|
|
20
|
+
* Failing closed is the only defensible default here: hiding a rate from someone
|
|
21
|
+
* entitled to it is a support ticket, showing it to someone who is not is a
|
|
22
|
+
* disclosure.
|
|
23
|
+
*
|
|
24
|
+
* The lookup itself belongs to `resolveFeatureAccess`, the module's single RBAC
|
|
25
|
+
* authority: it asks `userHasAllFeatures` (the call that carries `isSuperAdmin`
|
|
26
|
+
* internally), denies on every failure path, and logs the failure rather than
|
|
27
|
+
* swallowing it. This function stays as the *name* of the question, so a route
|
|
28
|
+
* reads `canSeeMoney` instead of a bare feature string — but it re-derives
|
|
29
|
+
* nothing and cannot drift from the rest of the module.
|
|
30
|
+
*/
|
|
31
|
+
export async function resolveMoneyVisibility(
|
|
32
|
+
container: ContainerLike,
|
|
33
|
+
userId: string | null,
|
|
34
|
+
scope: { tenantId: string | null; organizationId: string | null },
|
|
35
|
+
): Promise<boolean> {
|
|
36
|
+
return (await resolveFeatureAccess(container, userId, [RATES_FEATURE], scope)).allowed
|
|
37
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EP-38 — the overlap policy provider.
|
|
3
|
+
*
|
|
4
|
+
* `findOverlaps` is unchanged: it still answers the arithmetic question "which
|
|
5
|
+
* of these spans intersect the candidate". What is new is the policy layer on
|
|
6
|
+
* top of it, which answers the product question "and what should happen".
|
|
7
|
+
*
|
|
8
|
+
* The built-in `staff.time_tracking.overlap.warn_when_enabled` reproduces
|
|
9
|
+
* today's behaviour exactly, including the setting's off state: overlapping time
|
|
10
|
+
* is legitimate in consulting work, so the answer is `warn` when the
|
|
11
|
+
* `warnings.overlap` tenant setting is on and at least one span intersects, and
|
|
12
|
+
* `allow` otherwise. It never returns `block`.
|
|
13
|
+
*
|
|
14
|
+
* Contributed policies can only ESCALATE — the combined answer is the most
|
|
15
|
+
* severe of every policy's, `block` > `warn` > `allow` — so a payroll-compliance
|
|
16
|
+
* module can harden the rule but nothing can silently suppress a warning the
|
|
17
|
+
* tenant asked for.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { MINUTES_PER_DAY, deriveInterval, parseClock } from './interval'
|
|
21
|
+
import { extensionPoints } from '@open-mercato/core/modules/staff/extension-points'
|
|
22
|
+
import { createStrategyRegistry, BUILT_IN_STRATEGY_PRIORITY } from './registries/registry'
|
|
23
|
+
import { tryStrategy } from './registries/invoke'
|
|
24
|
+
import { hasResolverScope, type ScopedResolverContext } from './registries/scope'
|
|
25
|
+
|
|
26
|
+
export type OverlapSpan = {
|
|
27
|
+
id?: string | null
|
|
28
|
+
date: string
|
|
29
|
+
start?: string | null
|
|
30
|
+
end?: string | null
|
|
31
|
+
durationMinutes?: number | null
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export type Overlap = {
|
|
35
|
+
id: string | null
|
|
36
|
+
entry: OverlapSpan
|
|
37
|
+
overlapMinutes: number
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export type FindOverlapsOptions = {
|
|
41
|
+
excludeId?: string
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const DATE_ONLY_PATTERN = /^(\d{4})-(\d{2})-(\d{2})$/
|
|
45
|
+
|
|
46
|
+
const MS_PER_DAY = 86400000
|
|
47
|
+
|
|
48
|
+
function toEpochDay(date: string | null | undefined): number | null {
|
|
49
|
+
if (typeof date !== 'string') return null
|
|
50
|
+
const match = DATE_ONLY_PATTERN.exec(date.trim())
|
|
51
|
+
if (!match) return null
|
|
52
|
+
const utc = Date.UTC(Number(match[1]), Number(match[2]) - 1, Number(match[3]))
|
|
53
|
+
if (!Number.isFinite(utc)) return null
|
|
54
|
+
return Math.round(utc / MS_PER_DAY)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function toAbsoluteSpan(span: OverlapSpan): { start: number; end: number } | null {
|
|
58
|
+
const epochDay = toEpochDay(span?.date)
|
|
59
|
+
if (epochDay === null) return null
|
|
60
|
+
const derived = deriveInterval({
|
|
61
|
+
start: span.start,
|
|
62
|
+
end: span.end,
|
|
63
|
+
durationMinutes: span.durationMinutes,
|
|
64
|
+
})
|
|
65
|
+
const startOfDay = parseClock(derived.start)
|
|
66
|
+
if (startOfDay === null) return null
|
|
67
|
+
const duration = derived.durationMinutes
|
|
68
|
+
if (duration === null || duration <= 0) return null
|
|
69
|
+
const start = epochDay * MINUTES_PER_DAY + startOfDay
|
|
70
|
+
return { start, end: start + duration }
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function findOverlaps(
|
|
74
|
+
candidate: OverlapSpan,
|
|
75
|
+
existing: readonly OverlapSpan[],
|
|
76
|
+
opts?: FindOverlapsOptions,
|
|
77
|
+
): Overlap[] {
|
|
78
|
+
const candidateSpan = toAbsoluteSpan(candidate)
|
|
79
|
+
if (!candidateSpan) return []
|
|
80
|
+
|
|
81
|
+
const excludeId = opts?.excludeId
|
|
82
|
+
const overlaps: Overlap[] = []
|
|
83
|
+
|
|
84
|
+
for (const other of existing ?? []) {
|
|
85
|
+
if (!other) continue
|
|
86
|
+
if (excludeId !== undefined && other.id === excludeId) continue
|
|
87
|
+
const otherSpan = toAbsoluteSpan(other)
|
|
88
|
+
if (!otherSpan) continue
|
|
89
|
+
if (candidateSpan.start >= otherSpan.end || otherSpan.start >= candidateSpan.end) continue
|
|
90
|
+
overlaps.push({
|
|
91
|
+
id: other.id ?? null,
|
|
92
|
+
entry: other,
|
|
93
|
+
overlapMinutes:
|
|
94
|
+
Math.min(candidateSpan.end, otherSpan.end) - Math.max(candidateSpan.start, otherSpan.start),
|
|
95
|
+
})
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return overlaps
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export type OverlapDecision = 'allow' | 'warn' | 'block'
|
|
102
|
+
|
|
103
|
+
export type OverlapPolicyContext = ScopedResolverContext & {
|
|
104
|
+
candidate: OverlapSpan
|
|
105
|
+
/** The tenant's `warnings.overlap` setting; the built-in reads only this. */
|
|
106
|
+
warningsEnabled: boolean
|
|
107
|
+
staffMemberId?: string | null
|
|
108
|
+
timeProjectId?: string | null
|
|
109
|
+
excludeId?: string | null
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export type OverlapPolicy = {
|
|
113
|
+
id: string
|
|
114
|
+
priority?: number
|
|
115
|
+
evaluate(spans: readonly Overlap[], ctx: OverlapPolicyContext): OverlapDecision
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export const OVERLAP_POLICY_REGISTRY_ID = extensionPoints.hosts.overlapPolicyRegistry.spotId
|
|
119
|
+
|
|
120
|
+
export const BUILT_IN_OVERLAP_POLICY_ID = 'staff.time_tracking.overlap.warn_when_enabled'
|
|
121
|
+
|
|
122
|
+
const overlapRegistry = createStrategyRegistry<OverlapPolicy>(OVERLAP_POLICY_REGISTRY_ID)
|
|
123
|
+
|
|
124
|
+
export function registerOverlapPolicy(policy: OverlapPolicy): () => void {
|
|
125
|
+
return overlapRegistry.register(policy)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function listOverlapPolicies(): OverlapPolicy[] {
|
|
129
|
+
return overlapRegistry.list()
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function getOverlapPolicy(id: string | null | undefined): OverlapPolicy | null {
|
|
133
|
+
return overlapRegistry.get(id)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
overlapRegistry.registerBuiltIn({
|
|
137
|
+
id: BUILT_IN_OVERLAP_POLICY_ID,
|
|
138
|
+
priority: BUILT_IN_STRATEGY_PRIORITY,
|
|
139
|
+
evaluate: (spans, ctx) => (ctx.warningsEnabled && spans.length > 0 ? 'warn' : 'allow'),
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
const DECISION_SEVERITY: Record<OverlapDecision, number> = { allow: 0, warn: 1, block: 2 }
|
|
143
|
+
|
|
144
|
+
export function evaluateOverlapPolicies(
|
|
145
|
+
spans: readonly Overlap[],
|
|
146
|
+
ctx: OverlapPolicyContext,
|
|
147
|
+
): OverlapDecision {
|
|
148
|
+
const scoped = hasResolverScope(ctx)
|
|
149
|
+
let decision: OverlapDecision = 'allow'
|
|
150
|
+
for (const policy of overlapRegistry.list()) {
|
|
151
|
+
if (!scoped && policy.id !== BUILT_IN_OVERLAP_POLICY_ID) continue
|
|
152
|
+
// A policy can only ESCALATE, so a thrower is skipped: it cannot express a
|
|
153
|
+
// warning, and letting it abort the evaluation would suppress the warnings the
|
|
154
|
+
// remaining policies — including the built-in — do express.
|
|
155
|
+
const answer = tryStrategy(OVERLAP_POLICY_REGISTRY_ID, policy.id, () => policy.evaluate(spans, ctx))
|
|
156
|
+
if (answer && DECISION_SEVERITY[answer] > DECISION_SEVERITY[decision]) decision = answer
|
|
157
|
+
}
|
|
158
|
+
return decision
|
|
159
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { EntityManager } from '@mikro-orm/postgresql'
|
|
2
|
+
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
3
|
+
|
|
4
|
+
const logger = createLogger('staff').child({ component: 'time-tracking/portal-recipients' })
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* EP-50. The customer-user side of the portal contract, expressed as table and
|
|
8
|
+
* column **names** rather than imports.
|
|
9
|
+
*
|
|
10
|
+
* Staff is slated for extraction into `@open-mercato/staff`, so it may not take a
|
|
11
|
+
* static dependency on `customer_accounts`; `requires` in `index.ts` stays
|
|
12
|
+
* `['planner', 'resources']`. A Kysely query against the string
|
|
13
|
+
* `'customer_users'` reads the mapping without an import edge, exactly the way
|
|
14
|
+
* `warranty_claims/commands/claims.ts` does — and if the table is absent because
|
|
15
|
+
* the module is not installed, the query throws and this returns an empty list,
|
|
16
|
+
* which every caller treats as "no portal audience".
|
|
17
|
+
*
|
|
18
|
+
* `customer_users.customer_entity_id` is the FK into `customers:customer_entity`,
|
|
19
|
+
* which is the same id `staff_time_reports.customer_id` holds (EP-44 declares that
|
|
20
|
+
* link). That equality is the whole ownership model: a portal user reaches a
|
|
21
|
+
* report when, and only when, their own `customer_entity_id` matches the report's
|
|
22
|
+
* `customer_id` inside the same tenant and organization.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
type CustomerUsersDb = {
|
|
26
|
+
customer_users: {
|
|
27
|
+
id: string
|
|
28
|
+
tenant_id: string | null
|
|
29
|
+
organization_id: string | null
|
|
30
|
+
customer_entity_id: string | null
|
|
31
|
+
is_active: boolean
|
|
32
|
+
deleted_at: Date | null
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type PortalRecipientScope = {
|
|
37
|
+
tenantId: string
|
|
38
|
+
organizationId: string
|
|
39
|
+
customerId: string
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** A broadcast fan-out, not a mailing list — a customer with more portal users than this gets a truncated audience rather than an unbounded query. */
|
|
43
|
+
export const MAX_PORTAL_RECIPIENTS = 100
|
|
44
|
+
|
|
45
|
+
function isNonEmpty(value: unknown): value is string {
|
|
46
|
+
return typeof value === 'string' && value.trim().length > 0
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export async function resolvePortalRecipientUserIds(
|
|
50
|
+
em: EntityManager,
|
|
51
|
+
scope: PortalRecipientScope,
|
|
52
|
+
): Promise<string[]> {
|
|
53
|
+
if (!isNonEmpty(scope.tenantId) || !isNonEmpty(scope.organizationId) || !isNonEmpty(scope.customerId)) {
|
|
54
|
+
return []
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
const db = em.getKysely<CustomerUsersDb>()
|
|
58
|
+
const rows = await db
|
|
59
|
+
.selectFrom('customer_users')
|
|
60
|
+
.select('id')
|
|
61
|
+
.where('tenant_id', '=', scope.tenantId)
|
|
62
|
+
.where('organization_id', '=', scope.organizationId)
|
|
63
|
+
.where('customer_entity_id', '=', scope.customerId)
|
|
64
|
+
.where('is_active', '=', true)
|
|
65
|
+
.where('deleted_at', 'is', null)
|
|
66
|
+
.limit(MAX_PORTAL_RECIPIENTS)
|
|
67
|
+
.execute()
|
|
68
|
+
return rows.map((row) => row.id).filter(isNonEmpty)
|
|
69
|
+
} catch (err) {
|
|
70
|
+
logger.warn('Could not resolve portal recipients for a time-tracking customer', {
|
|
71
|
+
customerId: scope.customerId,
|
|
72
|
+
err,
|
|
73
|
+
})
|
|
74
|
+
return []
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EP-50. The customer-portal contract for time reports, in one place so the list
|
|
3
|
+
* route, the detail route and the tests cannot drift from each other.
|
|
4
|
+
*
|
|
5
|
+
* `portal.*` is the customer RBAC namespace (`CustomerRbacService`), disjoint from
|
|
6
|
+
* the staff `staff.timesheets.*` namespace. It is granted through
|
|
7
|
+
* `setup.defaultCustomerRoleFeatures`, not `acl.ts` — `acl.ts` declares staff
|
|
8
|
+
* features only.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export const PORTAL_TIME_REPORTS_VIEW_FEATURE = 'portal.time_reports.view'
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The four predicates every portal read of `staff_time_reports` must carry, as a
|
|
15
|
+
* SQL fragment with its parameters in order. Written once because a portal report
|
|
16
|
+
* read that forgets one of them is a cross-customer disclosure, and three call
|
|
17
|
+
* sites re-typing the same WHERE clause is how one of them eventually forgets.
|
|
18
|
+
*/
|
|
19
|
+
export function portalOwnedReportClause(scope: {
|
|
20
|
+
tenantId: string
|
|
21
|
+
organizationId: string
|
|
22
|
+
customerId: string
|
|
23
|
+
}): { sql: string; params: string[] } {
|
|
24
|
+
return {
|
|
25
|
+
sql: `tenant_id = ? AND organization_id = ? AND customer_id = ? AND status = 'closed' AND deleted_at IS NULL`,
|
|
26
|
+
params: [scope.tenantId, scope.organizationId, scope.customerId],
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const PORTAL_TIME_REPORT_BEFORE_SPOT_ID = 'portal:staff.time_report:before'
|
|
31
|
+
export const PORTAL_TIME_REPORT_AFTER_SPOT_ID = 'portal:staff.time_report:after'
|