@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
|
@@ -11,6 +11,11 @@ import type {
|
|
|
11
11
|
StaffTimeEntry,
|
|
12
12
|
StaffTimeProject,
|
|
13
13
|
StaffTimeProjectMember,
|
|
14
|
+
StaffTimeReport,
|
|
15
|
+
StaffTimeTag,
|
|
16
|
+
StaffTimeTask,
|
|
17
|
+
StaffTimeTaskComment,
|
|
18
|
+
StaffTimeTaskStatus,
|
|
14
19
|
} from '../data/entities'
|
|
15
20
|
|
|
16
21
|
function buildCrudEvents<TEntity>(entity: string): CrudEventsConfig<TEntity> {
|
|
@@ -38,9 +43,10 @@ export const staffTeamMemberJobHistoryCrudEvents = buildCrudEvents<StaffTeamMemb
|
|
|
38
43
|
// Timesheets
|
|
39
44
|
/**
|
|
40
45
|
* Command ids the time-entries CRUD route registers. Exported so the route and the
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
46
|
+
* custom write routes that drive the same commands share one string instead of
|
|
47
|
+
* re-typing it (#4970). The CRUD cache resource tag no longer follows these ids —
|
|
48
|
+
* the route declares an `events` config, which the factory prefers; see
|
|
49
|
+
* `lib/timesheets/timeEntryCacheInvalidation.ts`.
|
|
44
50
|
*/
|
|
45
51
|
export const staffTimeEntryCommandIds = {
|
|
46
52
|
create: 'staff.timesheets.time_entries.create',
|
|
@@ -51,3 +57,8 @@ export const staffTimeEntryCommandIds = {
|
|
|
51
57
|
export const staffTimeEntryCrudEvents = buildCrudEvents<StaffTimeEntry>('timesheets.time_entry')
|
|
52
58
|
export const staffTimeProjectCrudEvents = buildCrudEvents<StaffTimeProject>('timesheets.time_project')
|
|
53
59
|
export const staffTimeProjectMemberCrudEvents = buildCrudEvents<StaffTimeProjectMember>('timesheets.time_project_member')
|
|
60
|
+
export const staffTimeTaskCrudEvents = buildCrudEvents<StaffTimeTask>('timesheets.time_task')
|
|
61
|
+
export const staffTimeTaskStatusCrudEvents = buildCrudEvents<StaffTimeTaskStatus>('timesheets.time_task_status')
|
|
62
|
+
export const staffTimeTaskCommentCrudEvents = buildCrudEvents<StaffTimeTaskComment>('timesheets.time_task_comment')
|
|
63
|
+
export const staffTimeTagCrudEvents = buildCrudEvents<StaffTimeTag>('timesheets.time_tag')
|
|
64
|
+
export const staffTimeReportCrudEvents = buildCrudEvents<StaffTimeReport>('timesheets.time_report')
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import type { EntityManager } from '@mikro-orm/postgresql'
|
|
2
|
+
import { authorizeFeatures } from '@open-mercato/shared/security/featurePolicy'
|
|
3
|
+
import { findWithDecryption, findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
|
|
4
|
+
import { StaffTeamMember, StaffTimeProjectMember } from '../../data/entities'
|
|
5
|
+
import { DEFAULT_ASSIGNMENT_GRACE_DAYS, MAX_ASSIGNMENT_GRACE_DAYS } from './settings'
|
|
6
|
+
|
|
7
|
+
export const MANAGE_PROJECTS_FEATURE = 'staff.timesheets.projects.manage'
|
|
8
|
+
|
|
9
|
+
export type ProjectAccessContext = {
|
|
10
|
+
em: EntityManager
|
|
11
|
+
userId?: string | null
|
|
12
|
+
tenantId?: string | null
|
|
13
|
+
organizationId?: string | null
|
|
14
|
+
userFeatures?: readonly string[]
|
|
15
|
+
/**
|
|
16
|
+
* The manage-all decision, resolved by the caller through `resolveFeatureAccess`.
|
|
17
|
+
* Preferred over `userFeatures`: a grant array cannot distinguish "denied" from
|
|
18
|
+
* "could not ask", and the difference is a silently downgraded manager.
|
|
19
|
+
*/
|
|
20
|
+
canManageAll?: boolean
|
|
21
|
+
/**
|
|
22
|
+
* Resolved `access.assignmentGraceDays` setting. Passed in rather than read
|
|
23
|
+
* here so the resolver stays independent of `ModuleConfigService`; callers
|
|
24
|
+
* that already read the tenant settings avoid a second lookup.
|
|
25
|
+
*/
|
|
26
|
+
assignmentGraceDays?: number | null
|
|
27
|
+
/** Injectable clock so the assignment window is testable; defaults to now. */
|
|
28
|
+
now?: Date
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export type ProjectAccess = {
|
|
32
|
+
canManageAll: boolean
|
|
33
|
+
projectIds: string[]
|
|
34
|
+
staffMemberId: string | null
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const DENIED: ProjectAccess = { canManageAll: false, projectIds: [], staffMemberId: null }
|
|
38
|
+
|
|
39
|
+
const MS_PER_DAY = 86_400_000
|
|
40
|
+
const ISO_DATE_PREFIX = /^(\d{4})-(\d{2})-(\d{2})/
|
|
41
|
+
|
|
42
|
+
export function normalizeAssignmentGraceDays(value: unknown): number {
|
|
43
|
+
if (typeof value !== 'number' || !Number.isInteger(value)) return DEFAULT_ASSIGNMENT_GRACE_DAYS
|
|
44
|
+
if (value < 0 || value > MAX_ASSIGNMENT_GRACE_DAYS) return DEFAULT_ASSIGNMENT_GRACE_DAYS
|
|
45
|
+
return value
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* `assigned_start_date` / `assigned_end_date` are `date` columns, so both bounds
|
|
50
|
+
* are compared as whole days rather than timestamps. Date instances are read
|
|
51
|
+
* through their local calendar fields because the postgres driver materialises a
|
|
52
|
+
* `date` as local midnight, and "today" likewise means the server's local day.
|
|
53
|
+
*/
|
|
54
|
+
function toDayIndex(value: unknown): number | null {
|
|
55
|
+
if (value == null) return null
|
|
56
|
+
if (value instanceof Date) {
|
|
57
|
+
if (Number.isNaN(value.getTime())) return null
|
|
58
|
+
return Date.UTC(value.getFullYear(), value.getMonth(), value.getDate()) / MS_PER_DAY
|
|
59
|
+
}
|
|
60
|
+
if (typeof value === 'string') {
|
|
61
|
+
const match = ISO_DATE_PREFIX.exec(value.trim())
|
|
62
|
+
if (!match) return null
|
|
63
|
+
return Date.UTC(Number(match[1]), Number(match[2]) - 1, Number(match[3])) / MS_PER_DAY
|
|
64
|
+
}
|
|
65
|
+
return null
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* A membership only grants access inside its assignment window: from
|
|
70
|
+
* `assigned_start_date` until `assigned_end_date` plus the tenant's grace days.
|
|
71
|
+
* A null end date is open-ended and never expires.
|
|
72
|
+
*/
|
|
73
|
+
export function isWithinAssignmentWindow(
|
|
74
|
+
membership: Pick<StaffTimeProjectMember, 'assignedStartDate' | 'assignedEndDate'>,
|
|
75
|
+
todayIndex: number,
|
|
76
|
+
graceDays: number,
|
|
77
|
+
): boolean {
|
|
78
|
+
if (membership.assignedStartDate != null) {
|
|
79
|
+
const startIndex = toDayIndex(membership.assignedStartDate)
|
|
80
|
+
if (startIndex === null || todayIndex < startIndex) return false
|
|
81
|
+
}
|
|
82
|
+
if (membership.assignedEndDate == null) return true
|
|
83
|
+
const endIndex = toDayIndex(membership.assignedEndDate)
|
|
84
|
+
if (endIndex === null) return false
|
|
85
|
+
return todayIndex <= endIndex + graceDays
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export async function resolveProjectAccess(ctx: ProjectAccessContext): Promise<ProjectAccess> {
|
|
89
|
+
const tenantId = ctx.tenantId ?? null
|
|
90
|
+
const organizationId = ctx.organizationId ?? null
|
|
91
|
+
const userId = ctx.userId ?? null
|
|
92
|
+
const grantedFeatures = ctx.userFeatures ?? []
|
|
93
|
+
|
|
94
|
+
// The decision is the caller's to make, through `resolveFeatureAccess`, which
|
|
95
|
+
// asks the RBAC service once and fails closed loudly. Deriving it here from a
|
|
96
|
+
// grant array is what let a failed lookup pass as "no permission": the array
|
|
97
|
+
// came back empty, `canManageAll` went false, and a manager silently dropped to
|
|
98
|
+
// their own memberships while the KPI endpoint — asking the service directly —
|
|
99
|
+
// kept reporting the full portfolio.
|
|
100
|
+
//
|
|
101
|
+
// The array path remains for callers that have not been migrated, but it cannot
|
|
102
|
+
// distinguish "denied" from "could not ask", so it is deprecated.
|
|
103
|
+
const canManageAll =
|
|
104
|
+
ctx.canManageAll ??
|
|
105
|
+
authorizeFeatures([MANAGE_PROJECTS_FEATURE], {
|
|
106
|
+
grantedFeatures,
|
|
107
|
+
scopeAllowed: Boolean(tenantId) && Boolean(organizationId),
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
if (!tenantId || !organizationId || !userId) {
|
|
111
|
+
return canManageAll ? { ...DENIED, canManageAll } : { ...DENIED }
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const scope = { tenantId, organizationId }
|
|
115
|
+
const staffMember = await findOneWithDecryption(
|
|
116
|
+
ctx.em,
|
|
117
|
+
StaffTeamMember,
|
|
118
|
+
{ userId, tenantId, organizationId, deletedAt: null },
|
|
119
|
+
{},
|
|
120
|
+
scope,
|
|
121
|
+
)
|
|
122
|
+
const staffMemberId = staffMember?.id ?? null
|
|
123
|
+
|
|
124
|
+
if (canManageAll) return { canManageAll: true, projectIds: [], staffMemberId }
|
|
125
|
+
if (!staffMemberId) return { ...DENIED }
|
|
126
|
+
|
|
127
|
+
const memberships = await findWithDecryption(
|
|
128
|
+
ctx.em,
|
|
129
|
+
StaffTimeProjectMember,
|
|
130
|
+
{ staffMemberId, tenantId, organizationId, status: 'active', deletedAt: null },
|
|
131
|
+
{},
|
|
132
|
+
scope,
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
const graceDays = normalizeAssignmentGraceDays(ctx.assignmentGraceDays)
|
|
136
|
+
const todayIndex = toDayIndex(ctx.now ?? new Date())
|
|
137
|
+
|
|
138
|
+
const projectIds: string[] = []
|
|
139
|
+
const seen = new Set<string>()
|
|
140
|
+
for (const membership of memberships) {
|
|
141
|
+
const projectId = membership.timeProjectId
|
|
142
|
+
if (!projectId || seen.has(projectId)) continue
|
|
143
|
+
if (todayIndex === null || !isWithinAssignmentWindow(membership, todayIndex, graceDays)) continue
|
|
144
|
+
seen.add(projectId)
|
|
145
|
+
projectIds.push(projectId)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return { canManageAll: false, projectIds, staffMemberId }
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function assertProjectAccess(access: ProjectAccess, projectId?: string | null): boolean {
|
|
152
|
+
if (access.canManageAll) return true
|
|
153
|
+
if (!projectId) return false
|
|
154
|
+
return access.projectIds.includes(projectId)
|
|
155
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EP-34 — the billability resolver chain.
|
|
3
|
+
*
|
|
4
|
+
* The rule `commands/timesheets-entries.ts` documented in prose — an explicit
|
|
5
|
+
* value wins, then the project's own default, then the tenant setting — is now
|
|
6
|
+
* the built-in resolver `staff.time_tracking.billability.project_then_tenant`.
|
|
7
|
+
* Contributed resolvers are asked first, in descending priority, and the first
|
|
8
|
+
* non-null answer wins; a resolver that returns `null` abstains and the chain
|
|
9
|
+
* carries on, so an unopinionated contribution cannot change an entry.
|
|
10
|
+
*
|
|
11
|
+
* As everywhere else in this group, a contribution is consulted only when the
|
|
12
|
+
* caller supplies a complete tenant + organization scope.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { extensionPoints } from '@open-mercato/core/modules/staff/extension-points'
|
|
16
|
+
import { createStrategyRegistry, BUILT_IN_STRATEGY_PRIORITY } from './registries/registry'
|
|
17
|
+
import { tryStrategy } from './registries/invoke'
|
|
18
|
+
import { hasResolverScope, type ScopedResolverContext } from './registries/scope'
|
|
19
|
+
import type { TimeTrackingSettings } from './settings'
|
|
20
|
+
|
|
21
|
+
export type BillabilityContext = ScopedResolverContext & {
|
|
22
|
+
requested?: boolean | null
|
|
23
|
+
project?: { id?: string | null; billableByDefault?: boolean | null } | null
|
|
24
|
+
task?: { id?: string | null } | null
|
|
25
|
+
staffMemberId?: string | null
|
|
26
|
+
date?: string | null
|
|
27
|
+
settings: TimeTrackingSettings
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export type BillabilityResolver = {
|
|
31
|
+
id: string
|
|
32
|
+
priority?: number
|
|
33
|
+
resolve(ctx: BillabilityContext): boolean | null
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const BILLABILITY_REGISTRY_ID = extensionPoints.hosts.timeBillabilityRegistry.spotId
|
|
37
|
+
|
|
38
|
+
export const BUILT_IN_BILLABILITY_RESOLVER_ID = 'staff.time_tracking.billability.project_then_tenant'
|
|
39
|
+
|
|
40
|
+
const registry = createStrategyRegistry<BillabilityResolver>(BILLABILITY_REGISTRY_ID)
|
|
41
|
+
|
|
42
|
+
export function registerBillabilityResolver(resolver: BillabilityResolver): () => void {
|
|
43
|
+
return registry.register(resolver)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function listBillabilityResolvers(): BillabilityResolver[] {
|
|
47
|
+
return registry.list()
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function getBillabilityResolver(id: string | null | undefined): BillabilityResolver | null {
|
|
51
|
+
return registry.get(id)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const builtInBillabilityResolver: BillabilityResolver = registry.registerBuiltIn({
|
|
55
|
+
id: BUILT_IN_BILLABILITY_RESOLVER_ID,
|
|
56
|
+
priority: BUILT_IN_STRATEGY_PRIORITY,
|
|
57
|
+
resolve: (ctx) => ctx.requested ?? ctx.project?.billableByDefault ?? ctx.settings.defaults.billable,
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
export function resolveBillability(ctx: BillabilityContext): boolean {
|
|
61
|
+
const scoped = hasResolverScope(ctx)
|
|
62
|
+
for (const resolver of registry.list()) {
|
|
63
|
+
if (!scoped && resolver.id !== BUILT_IN_BILLABILITY_RESOLVER_ID) continue
|
|
64
|
+
const answer = tryStrategy(BILLABILITY_REGISTRY_ID, resolver.id, () => resolver.resolve(ctx))
|
|
65
|
+
if (typeof answer === 'boolean') return answer
|
|
66
|
+
}
|
|
67
|
+
return ctx.settings.defaults.billable
|
|
68
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EP-40 — the capacity / target provider.
|
|
3
|
+
*
|
|
4
|
+
* The timesheet's "target" is one flat number today: `targets.dailyHours` from
|
|
5
|
+
* the tenant settings, applied to every working day of every person. The
|
|
6
|
+
* built-in `staff.time_tracking.capacity.flat_daily_hours` reproduces exactly
|
|
7
|
+
* that, including its two edge cases — a `null` setting means "no target", and a
|
|
8
|
+
* day the caller did not mark as working contributes nothing.
|
|
9
|
+
*
|
|
10
|
+
* A contributed provider answers per day instead, which is what a contract-hours
|
|
11
|
+
* or leave-aware capacity model needs. It is consulted only when the caller
|
|
12
|
+
* supplies a complete tenant + organization scope.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { extensionPoints } from '@open-mercato/core/modules/staff/extension-points'
|
|
16
|
+
import { createStrategyRegistry, BUILT_IN_STRATEGY_PRIORITY } from './registries/registry'
|
|
17
|
+
import { selectScopedStrategy, type ScopedResolverContext } from './registries/scope'
|
|
18
|
+
import { runStrategy } from './registries/invoke'
|
|
19
|
+
|
|
20
|
+
export type CapacityDateRange = {
|
|
21
|
+
/** `yyyy-mm-dd`, inclusive. */
|
|
22
|
+
from: string
|
|
23
|
+
/** `yyyy-mm-dd`, inclusive. */
|
|
24
|
+
to: string
|
|
25
|
+
/** The days the caller counts as working; the built-in targets only these. */
|
|
26
|
+
workingDays: readonly string[]
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export type CapacityContext = ScopedResolverContext & {
|
|
30
|
+
/** The tenant's `targets.dailyHours`; `null` means the tenant set no target. */
|
|
31
|
+
dailyHours: number | null
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export type CapacityResult = {
|
|
35
|
+
/** Target minutes per `yyyy-mm-dd`, for the working days that carry one. */
|
|
36
|
+
targetMinutesByDate: Record<string, number>
|
|
37
|
+
/** Sum of `targetMinutesByDate`, or `null` when the tenant set no target. */
|
|
38
|
+
totalTargetMinutes: number | null
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export type CapacityProvider = {
|
|
42
|
+
id: string
|
|
43
|
+
priority?: number
|
|
44
|
+
resolve(
|
|
45
|
+
staffMemberId: string | null,
|
|
46
|
+
dateRange: CapacityDateRange,
|
|
47
|
+
ctx: CapacityContext,
|
|
48
|
+
): CapacityResult
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const CAPACITY_PROVIDER_REGISTRY_ID = extensionPoints.hosts.capacityProviderRegistry.spotId
|
|
52
|
+
|
|
53
|
+
export const BUILT_IN_CAPACITY_PROVIDER_ID = 'staff.time_tracking.capacity.flat_daily_hours'
|
|
54
|
+
|
|
55
|
+
const registry = createStrategyRegistry<CapacityProvider>(CAPACITY_PROVIDER_REGISTRY_ID)
|
|
56
|
+
|
|
57
|
+
export function registerCapacityProvider(provider: CapacityProvider): () => void {
|
|
58
|
+
return registry.register(provider)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function listCapacityProviders(): CapacityProvider[] {
|
|
62
|
+
return registry.list()
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function getCapacityProvider(id: string | null | undefined): CapacityProvider | null {
|
|
66
|
+
return registry.get(id)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const builtInCapacityProvider: CapacityProvider = registry.registerBuiltIn({
|
|
70
|
+
id: BUILT_IN_CAPACITY_PROVIDER_ID,
|
|
71
|
+
priority: BUILT_IN_STRATEGY_PRIORITY,
|
|
72
|
+
resolve: (_staffMemberId, dateRange, ctx) => {
|
|
73
|
+
const dailyHours = ctx.dailyHours
|
|
74
|
+
if (dailyHours === null || !Number.isFinite(dailyHours)) {
|
|
75
|
+
return { targetMinutesByDate: {}, totalTargetMinutes: null }
|
|
76
|
+
}
|
|
77
|
+
const dailyMinutes = Math.round(dailyHours * 60)
|
|
78
|
+
const targetMinutesByDate: Record<string, number> = {}
|
|
79
|
+
for (const date of dateRange.workingDays ?? []) targetMinutesByDate[date] = dailyMinutes
|
|
80
|
+
return {
|
|
81
|
+
targetMinutesByDate,
|
|
82
|
+
totalTargetMinutes: dailyMinutes * (dateRange.workingDays?.length ?? 0),
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
export function resolveCapacityProvider(ctx?: ScopedResolverContext | null): CapacityProvider {
|
|
88
|
+
return selectScopedStrategy(registry.list(), builtInCapacityProvider, ctx)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function isCapacityResult(value: unknown): value is CapacityResult {
|
|
92
|
+
if (!value || typeof value !== 'object') return false
|
|
93
|
+
const candidate = value as { targetMinutesByDate?: unknown; totalTargetMinutes?: unknown }
|
|
94
|
+
if (!candidate.targetMinutesByDate || typeof candidate.targetMinutesByDate !== 'object') return false
|
|
95
|
+
const total = candidate.totalTargetMinutes
|
|
96
|
+
if (total !== null && (typeof total !== 'number' || !Number.isFinite(total))) return false
|
|
97
|
+
return Object.values(candidate.targetMinutesByDate as Record<string, unknown>).every(
|
|
98
|
+
(minutes) => typeof minutes === 'number' && Number.isFinite(minutes),
|
|
99
|
+
)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* The targets the timesheet renders and compares logged minutes against. A provider
|
|
104
|
+
* that throws, or that answers with something the caller cannot subtract from, falls
|
|
105
|
+
* back to the flat daily target the module shipped — a timesheet showing the wrong
|
|
106
|
+
* target reads as the person being behind, which is worse than showing the default.
|
|
107
|
+
*/
|
|
108
|
+
export function resolveTimesheetCapacity(
|
|
109
|
+
staffMemberId: string | null,
|
|
110
|
+
dateRange: CapacityDateRange,
|
|
111
|
+
ctx: CapacityContext,
|
|
112
|
+
): CapacityResult {
|
|
113
|
+
const provider = resolveCapacityProvider(ctx)
|
|
114
|
+
const builtIn = () => builtInCapacityProvider.resolve(staffMemberId, dateRange, ctx)
|
|
115
|
+
if (provider === builtInCapacityProvider) return builtIn()
|
|
116
|
+
|
|
117
|
+
const answer = runStrategy(
|
|
118
|
+
CAPACITY_PROVIDER_REGISTRY_ID,
|
|
119
|
+
provider.id,
|
|
120
|
+
() => provider.resolve(staffMemberId, dateRange, ctx) as unknown,
|
|
121
|
+
() => null,
|
|
122
|
+
)
|
|
123
|
+
return isCapacityResult(answer) ? answer : builtIn()
|
|
124
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Shared building blocks for the `propsSchema` every replaceable time-tracking
|
|
5
|
+
* component publishes (EP-31).
|
|
6
|
+
*
|
|
7
|
+
* The schema is the contract a replacement must satisfy: `useRegisteredComponent`
|
|
8
|
+
* parses the resolved props against it in development and falls back to the
|
|
9
|
+
* original component when they do not match, so a schema that accepts anything
|
|
10
|
+
* would let a broken replacement render instead of being caught. Scalars are
|
|
11
|
+
* therefore described exactly; callbacks and React nodes, which zod cannot
|
|
12
|
+
* describe structurally, are narrowed to the one runtime check that matters.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
export const callbackProp = <TCallback>() =>
|
|
16
|
+
z.custom<TCallback>((value) => typeof value === 'function', {
|
|
17
|
+
message: '[internal] expected a function prop',
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
export const optionalCallbackProp = <TCallback>() =>
|
|
21
|
+
z.custom<TCallback | undefined>(
|
|
22
|
+
(value) => value === undefined || typeof value === 'function',
|
|
23
|
+
{ message: '[internal] expected a function prop or undefined' },
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
export const opaqueProp = <TValue>() => z.custom<TValue>(() => true)
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EP-33 — the rate resolver chain.
|
|
3
|
+
*
|
|
4
|
+
* `applicableRate` is still the single place an hourly rate is chosen, and
|
|
5
|
+
* `entryAmount` still the single place an amount is produced (D-7). What changed
|
|
6
|
+
* is that the override → project-rate chain now lives in the built-in resolver
|
|
7
|
+
* `staff.time_tracking.rate.entry_override_then_project`, registered at module
|
|
8
|
+
* load, instead of being spelled inline. Contributed resolvers are consulted in
|
|
9
|
+
* descending priority ahead of it, but only when the caller supplies a complete
|
|
10
|
+
* tenant + organization scope; with no contribution — or with none of them
|
|
11
|
+
* answering — the built-in decides, so the number is the one this file always
|
|
12
|
+
* produced.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { extensionPoints } from '@open-mercato/core/modules/staff/extension-points'
|
|
16
|
+
import { createStrategyRegistry, BUILT_IN_STRATEGY_PRIORITY } from './registries/registry'
|
|
17
|
+
import { tryStrategy } from './registries/invoke'
|
|
18
|
+
import { hasResolverScope, type ScopedResolverContext } from './registries/scope'
|
|
19
|
+
|
|
20
|
+
export type CostEntry = {
|
|
21
|
+
isBillable: boolean
|
|
22
|
+
roundedMinutes: number
|
|
23
|
+
rateOverrideAmount?: number | null
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export type CostProject = {
|
|
27
|
+
hourlyRate?: number | null
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Everything a rate resolver may reason about. Only `entry` and `project` are
|
|
32
|
+
* populated by the module's own call sites; the remaining fields exist so a
|
|
33
|
+
* contributed resolver can key off seniority, task type, customer contract or an
|
|
34
|
+
* effective date without the call site having to invent its own context shape.
|
|
35
|
+
*/
|
|
36
|
+
export type TimeRateContext = ScopedResolverContext & {
|
|
37
|
+
entry?: Pick<CostEntry, 'rateOverrideAmount'> | null
|
|
38
|
+
project?: CostProject | null
|
|
39
|
+
task?: { id?: string | null; timeProjectId?: string | null } | null
|
|
40
|
+
staffMember?: { id?: string | null } | null
|
|
41
|
+
role?: { id?: string | null; name?: string | null } | null
|
|
42
|
+
customer?: { id?: string | null } | null
|
|
43
|
+
date?: string | null
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export type TimeRateResolver = {
|
|
47
|
+
id: string
|
|
48
|
+
priority?: number
|
|
49
|
+
resolve(ctx: TimeRateContext): number | null
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export const TIME_RATE_REGISTRY_ID = extensionPoints.hosts.timeRateRegistry.spotId
|
|
53
|
+
|
|
54
|
+
export const BUILT_IN_TIME_RATE_RESOLVER_ID = 'staff.time_tracking.rate.entry_override_then_project'
|
|
55
|
+
|
|
56
|
+
const registry = createStrategyRegistry<TimeRateResolver>(TIME_RATE_REGISTRY_ID)
|
|
57
|
+
|
|
58
|
+
export function registerTimeRateResolver(resolver: TimeRateResolver): () => void {
|
|
59
|
+
return registry.register(resolver)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function listTimeRateResolvers(): TimeRateResolver[] {
|
|
63
|
+
return registry.list()
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function getTimeRateResolver(id: string | null | undefined): TimeRateResolver | null {
|
|
67
|
+
return registry.get(id)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function round2(value: number): number {
|
|
71
|
+
if (!Number.isFinite(value)) return 0
|
|
72
|
+
const sign = value < 0 ? -1 : 1
|
|
73
|
+
const magnitude = Math.abs(value)
|
|
74
|
+
const shifted = Number(`${magnitude}e2`)
|
|
75
|
+
if (!Number.isFinite(shifted)) return sign * (Math.round(magnitude * 100) / 100)
|
|
76
|
+
const rounded = Math.round(shifted)
|
|
77
|
+
const restored = Number(`${rounded}e-2`)
|
|
78
|
+
if (!Number.isFinite(restored)) return sign * (rounded / 100)
|
|
79
|
+
return sign * restored
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function toCents(amount: number): number {
|
|
83
|
+
return Math.round(round2(amount) * 100)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function builtInRate(ctx: TimeRateContext): number | null {
|
|
87
|
+
const override = ctx.entry?.rateOverrideAmount
|
|
88
|
+
if (override !== null && override !== undefined && Number.isFinite(override)) return override
|
|
89
|
+
const projectRate = ctx.project?.hourlyRate
|
|
90
|
+
if (projectRate !== null && projectRate !== undefined && Number.isFinite(projectRate)) return projectRate
|
|
91
|
+
return null
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const builtInRateResolver: TimeRateResolver = registry.registerBuiltIn({
|
|
95
|
+
id: BUILT_IN_TIME_RATE_RESOLVER_ID,
|
|
96
|
+
priority: BUILT_IN_STRATEGY_PRIORITY,
|
|
97
|
+
resolve: builtInRate,
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Walks the registry in priority order and takes the first non-null answer.
|
|
102
|
+
* Contributed resolvers are skipped entirely when the context carries no
|
|
103
|
+
* complete scope, which leaves the built-in as the only candidate.
|
|
104
|
+
*/
|
|
105
|
+
export function resolveTimeRate(ctx: TimeRateContext): number | null {
|
|
106
|
+
const scoped = hasResolverScope(ctx)
|
|
107
|
+
for (const resolver of registry.list()) {
|
|
108
|
+
if (!scoped && resolver.id !== BUILT_IN_TIME_RATE_RESOLVER_ID) continue
|
|
109
|
+
// A chain asks the next candidate anyway, so a thrower is skipped rather than
|
|
110
|
+
// replaced — and the built-in is always the last candidate.
|
|
111
|
+
const rate = tryStrategy(TIME_RATE_REGISTRY_ID, resolver.id, () => resolver.resolve(ctx))
|
|
112
|
+
if (rate !== null && rate !== undefined && Number.isFinite(rate)) return rate
|
|
113
|
+
}
|
|
114
|
+
return null
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function applicableRate(
|
|
118
|
+
entry: Pick<CostEntry, 'rateOverrideAmount'> | null | undefined,
|
|
119
|
+
project: CostProject | null | undefined,
|
|
120
|
+
ctx?: Omit<TimeRateContext, 'entry' | 'project'> | null,
|
|
121
|
+
): number | null {
|
|
122
|
+
return resolveTimeRate({ ...(ctx ?? {}), entry: entry ?? null, project: project ?? null })
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function entryAmount(
|
|
126
|
+
entry: CostEntry,
|
|
127
|
+
project: CostProject | null | undefined,
|
|
128
|
+
ctx?: Omit<TimeRateContext, 'entry' | 'project'> | null,
|
|
129
|
+
): number | null {
|
|
130
|
+
if (!entry?.isBillable) return null
|
|
131
|
+
const rate = applicableRate(entry, project, ctx)
|
|
132
|
+
if (rate === null) return null
|
|
133
|
+
const minutes = Number.isFinite(entry.roundedMinutes) ? entry.roundedMinutes : 0
|
|
134
|
+
return round2((minutes / 60) * rate)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function sumAmounts(amounts: readonly (number | null | undefined)[]): number {
|
|
138
|
+
let cents = 0
|
|
139
|
+
for (const amount of amounts) {
|
|
140
|
+
if (amount === null || amount === undefined || !Number.isFinite(amount)) continue
|
|
141
|
+
cents += toCents(amount)
|
|
142
|
+
}
|
|
143
|
+
return round2(cents / 100)
|
|
144
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
export const MAX_DURATION_MINUTES = 1440
|
|
2
|
+
|
|
3
|
+
export type DurationParseFailureReason = 'empty' | 'unparseable'
|
|
4
|
+
|
|
5
|
+
export type ParseDurationResult =
|
|
6
|
+
| { ok: true; minutes: number }
|
|
7
|
+
| { ok: false; reason: DurationParseFailureReason }
|
|
8
|
+
|
|
9
|
+
export type DurationFormatStyle = 'hm' | 'clock' | 'decimal'
|
|
10
|
+
|
|
11
|
+
const CLOCK_PATTERN = /^(\d{1,3}):([0-5]\d)$/
|
|
12
|
+
const UNIT_PATTERN = /^(?:(\d+(?:[.,]\d+)?)h)?(?:(\d+(?:[.,]\d+)?)m(?:in)?)?$/
|
|
13
|
+
const BARE_NUMBER_PATTERN = /^\d+(?:[.,]\d+)?$/
|
|
14
|
+
|
|
15
|
+
function toNumber(value: string): number {
|
|
16
|
+
return Number.parseFloat(value.replace(',', '.'))
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function clampMinutes(minutes: number): number {
|
|
20
|
+
if (!Number.isFinite(minutes)) return 0
|
|
21
|
+
if (minutes < 0) return 0
|
|
22
|
+
if (minutes > MAX_DURATION_MINUTES) return MAX_DURATION_MINUTES
|
|
23
|
+
return Math.round(minutes)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function parseDuration(input: string): ParseDurationResult {
|
|
27
|
+
if (typeof input !== 'string') return { ok: false, reason: 'unparseable' }
|
|
28
|
+
const trimmed = input.trim()
|
|
29
|
+
if (!trimmed) return { ok: false, reason: 'empty' }
|
|
30
|
+
|
|
31
|
+
const compact = trimmed.replace(/\s+/g, '').toLowerCase()
|
|
32
|
+
if (compact.startsWith('-')) return { ok: false, reason: 'unparseable' }
|
|
33
|
+
|
|
34
|
+
const clock = CLOCK_PATTERN.exec(compact)
|
|
35
|
+
if (clock) {
|
|
36
|
+
const hours = Number.parseInt(clock[1], 10)
|
|
37
|
+
const minutes = Number.parseInt(clock[2], 10)
|
|
38
|
+
return { ok: true, minutes: clampMinutes(hours * 60 + minutes) }
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const units = UNIT_PATTERN.exec(compact)
|
|
42
|
+
if (units && (units[1] !== undefined || units[2] !== undefined)) {
|
|
43
|
+
const hours = units[1] !== undefined ? toNumber(units[1]) : 0
|
|
44
|
+
const minutes = units[2] !== undefined ? toNumber(units[2]) : 0
|
|
45
|
+
if (!Number.isFinite(hours) || !Number.isFinite(minutes)) return { ok: false, reason: 'unparseable' }
|
|
46
|
+
return { ok: true, minutes: clampMinutes(hours * 60 + minutes) }
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (BARE_NUMBER_PATTERN.test(compact)) {
|
|
50
|
+
const hours = toNumber(compact)
|
|
51
|
+
if (!Number.isFinite(hours)) return { ok: false, reason: 'unparseable' }
|
|
52
|
+
return { ok: true, minutes: clampMinutes(hours * 60) }
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return { ok: false, reason: 'unparseable' }
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function formatDuration(minutes: number, style: DurationFormatStyle): string {
|
|
59
|
+
const safe = Number.isFinite(minutes) && minutes > 0 ? Math.round(minutes) : 0
|
|
60
|
+
const hours = Math.floor(safe / 60)
|
|
61
|
+
const rest = safe % 60
|
|
62
|
+
|
|
63
|
+
if (style === 'clock') return `${hours}:${String(rest).padStart(2, '0')}`
|
|
64
|
+
if (style === 'decimal') return (safe / 60).toFixed(2)
|
|
65
|
+
if (hours === 0) return `${rest}m`
|
|
66
|
+
if (rest === 0) return `${hours}h`
|
|
67
|
+
return `${hours}h ${rest}m`
|
|
68
|
+
}
|