@open-mercato/core 0.6.4-develop.4217.1.c9aa050183 → 0.6.4-develop.4236.1.9fa6806b34
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 +37 -0
- package/dist/generated/entities/staff_time_entry/index.js.map +7 -0
- package/dist/generated/entities/staff_time_entry_segment/index.js +23 -0
- package/dist/generated/entities/staff_time_entry_segment/index.js.map +7 -0
- package/dist/generated/entities/staff_time_project/index.js +35 -0
- package/dist/generated/entities/staff_time_project/index.js.map +7 -0
- package/dist/generated/entities/staff_time_project_member/index.js +29 -0
- package/dist/generated/entities/staff_time_project_member/index.js.map +7 -0
- package/dist/generated/entities.ids.generated.js +5 -1
- package/dist/generated/entities.ids.generated.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +64 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/helpers/integration/timesheetFixtures.js +50 -0
- package/dist/helpers/integration/timesheetFixtures.js.map +7 -0
- package/dist/modules/attachments/api/library/[id]/route.js +20 -16
- package/dist/modules/attachments/api/library/[id]/route.js.map +2 -2
- package/dist/modules/attachments/api/route.js +18 -14
- package/dist/modules/attachments/api/route.js.map +2 -2
- package/dist/modules/auth/api/roles/acl/route.js +10 -4
- package/dist/modules/auth/api/roles/acl/route.js.map +2 -2
- package/dist/modules/auth/api/sidebar/preferences/route.js +27 -20
- package/dist/modules/auth/api/sidebar/preferences/route.js.map +2 -2
- package/dist/modules/auth/api/users/acl/route.js +16 -11
- package/dist/modules/auth/api/users/acl/route.js.map +2 -2
- package/dist/modules/auth/commands/users.js +87 -71
- package/dist/modules/auth/commands/users.js.map +2 -2
- package/dist/modules/auth/services/sidebarPreferencesService.js +39 -30
- package/dist/modules/auth/services/sidebarPreferencesService.js.map +2 -2
- package/dist/modules/catalog/commands/categories.js +61 -12
- package/dist/modules/catalog/commands/categories.js.map +2 -2
- package/dist/modules/catalog/commands/products.js +79 -54
- package/dist/modules/catalog/commands/products.js.map +2 -2
- package/dist/modules/catalog/commands/variants.js +29 -16
- package/dist/modules/catalog/commands/variants.js.map +2 -2
- package/dist/modules/currencies/commands/currencies.js +15 -8
- package/dist/modules/currencies/commands/currencies.js.map +2 -2
- package/dist/modules/customer_accounts/api/admin/users.js +27 -26
- package/dist/modules/customer_accounts/api/admin/users.js.map +2 -2
- package/dist/modules/customer_accounts/api/password/reset-confirm.js +5 -5
- package/dist/modules/customer_accounts/api/password/reset-confirm.js.map +2 -2
- package/dist/modules/customer_accounts/api/portal/users/[id]/roles.js +11 -10
- package/dist/modules/customer_accounts/api/portal/users/[id]/roles.js.map +2 -2
- package/dist/modules/customers/commands/addresses.js +35 -21
- package/dist/modules/customers/commands/addresses.js.map +2 -2
- package/dist/modules/customers/commands/companies.js +163 -162
- package/dist/modules/customers/commands/companies.js.map +2 -2
- package/dist/modules/customers/commands/deals.js +3 -4
- package/dist/modules/customers/commands/deals.js.map +2 -2
- package/dist/modules/customers/commands/interactions.js +19 -22
- package/dist/modules/customers/commands/interactions.js.map +2 -2
- package/dist/modules/customers/commands/people.js +18 -15
- package/dist/modules/customers/commands/people.js.map +2 -2
- package/dist/modules/customers/commands/personCompanyLinks.js +105 -94
- package/dist/modules/customers/commands/personCompanyLinks.js.map +2 -2
- package/dist/modules/customers/commands/pipeline-stages.js +30 -23
- package/dist/modules/customers/commands/pipeline-stages.js.map +2 -2
- package/dist/modules/customers/commands/pipelines.js +27 -20
- package/dist/modules/customers/commands/pipelines.js.map +2 -2
- package/dist/modules/customers/commands/tags.js +13 -5
- package/dist/modules/customers/commands/tags.js.map +2 -2
- package/dist/modules/dashboards/api/users/widgets/route.js +0 -1
- package/dist/modules/dashboards/api/users/widgets/route.js.map +2 -2
- package/dist/modules/dashboards/api/widgets/data/route.js +29 -1
- package/dist/modules/dashboards/api/widgets/data/route.js.map +2 -2
- package/dist/modules/data_sync/lib/sync-engine.js +4 -4
- package/dist/modules/data_sync/lib/sync-engine.js.map +2 -2
- package/dist/modules/data_sync/lib/sync-run-service.js +51 -27
- package/dist/modules/data_sync/lib/sync-run-service.js.map +2 -2
- package/dist/modules/directory/commands/organizations.js +192 -158
- package/dist/modules/directory/commands/organizations.js.map +3 -3
- package/dist/modules/inbox_ops/api/emails/[id]/reprocess/route.js +22 -16
- package/dist/modules/inbox_ops/api/emails/[id]/reprocess/route.js.map +2 -2
- package/dist/modules/messages/commands/messages.js +77 -75
- package/dist/modules/messages/commands/messages.js.map +2 -2
- package/dist/modules/messages/commands/shared.js +132 -132
- package/dist/modules/messages/commands/shared.js.map +2 -2
- package/dist/modules/perspectives/api/[tableId]/route.js +37 -26
- package/dist/modules/perspectives/api/[tableId]/route.js.map +2 -2
- package/dist/modules/resources/commands/resources.js +125 -117
- package/dist/modules/resources/commands/resources.js.map +2 -2
- package/dist/modules/resources/commands/tags.js +7 -3
- package/dist/modules/resources/commands/tags.js.map +2 -2
- package/dist/modules/sales/api/quotes/send/route.js +12 -11
- package/dist/modules/sales/api/quotes/send/route.js.map +2 -2
- package/dist/modules/sales/commands/documents.js +629 -478
- package/dist/modules/sales/commands/documents.js.map +2 -2
- package/dist/modules/sales/commands/payments.js +146 -146
- package/dist/modules/sales/commands/payments.js.map +2 -2
- package/dist/modules/sales/commands/returns.js +68 -60
- package/dist/modules/sales/commands/returns.js.map +2 -2
- package/dist/modules/staff/acl.js +10 -1
- package/dist/modules/staff/acl.js.map +2 -2
- package/dist/modules/staff/analytics.js +33 -0
- package/dist/modules/staff/analytics.js.map +7 -0
- package/dist/modules/staff/api/guards.js +31 -0
- package/dist/modules/staff/api/guards.js.map +7 -0
- package/dist/modules/staff/api/interceptors.js +96 -0
- package/dist/modules/staff/api/interceptors.js.map +7 -0
- package/dist/modules/staff/api/timesheets/my-projects/[projectId]/route.js +170 -0
- package/dist/modules/staff/api/timesheets/my-projects/[projectId]/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/my-projects/route.js +103 -0
- package/dist/modules/staff/api/timesheets/my-projects/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/projects/kpis/route.js +147 -0
- package/dist/modules/staff/api/timesheets/projects/kpis/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/time-entries/[id]/segments/[segmentId]/route.js +171 -0
- package/dist/modules/staff/api/timesheets/time-entries/[id]/segments/[segmentId]/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/time-entries/[id]/segments/route.js +180 -0
- package/dist/modules/staff/api/timesheets/time-entries/[id]/segments/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.js +155 -0
- package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-stop/route.js +173 -0
- package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-stop/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/time-entries/bulk/route.js +260 -0
- package/dist/modules/staff/api/timesheets/time-entries/bulk/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/time-entries/route.js +188 -0
- package/dist/modules/staff/api/timesheets/time-entries/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/time-projects/[id]/employees/route.js +159 -0
- package/dist/modules/staff/api/timesheets/time-projects/[id]/employees/route.js.map +7 -0
- package/dist/modules/staff/api/timesheets/time-projects/route.js +230 -0
- package/dist/modules/staff/api/timesheets/time-projects/route.js.map +7 -0
- package/dist/modules/staff/backend/staff/timesheets/page.js +710 -0
- package/dist/modules/staff/backend/staff/timesheets/page.js.map +7 -0
- package/dist/modules/staff/backend/staff/timesheets/page.meta.js +22 -0
- package/dist/modules/staff/backend/staff/timesheets/page.meta.js.map +7 -0
- package/dist/modules/staff/backend/staff/timesheets/projects/[id]/edit/page.js +125 -0
- package/dist/modules/staff/backend/staff/timesheets/projects/[id]/edit/page.js.map +7 -0
- package/dist/modules/staff/backend/staff/timesheets/projects/[id]/edit/page.meta.js +16 -0
- package/dist/modules/staff/backend/staff/timesheets/projects/[id]/edit/page.meta.js.map +7 -0
- package/dist/modules/staff/backend/staff/timesheets/projects/[id]/page.js +418 -0
- package/dist/modules/staff/backend/staff/timesheets/projects/[id]/page.js.map +7 -0
- package/dist/modules/staff/backend/staff/timesheets/projects/[id]/page.meta.js +16 -0
- package/dist/modules/staff/backend/staff/timesheets/projects/[id]/page.meta.js.map +7 -0
- package/dist/modules/staff/backend/staff/timesheets/projects/create/page.js +79 -0
- package/dist/modules/staff/backend/staff/timesheets/projects/create/page.js.map +7 -0
- package/dist/modules/staff/backend/staff/timesheets/projects/create/page.meta.js +16 -0
- package/dist/modules/staff/backend/staff/timesheets/projects/create/page.meta.js.map +7 -0
- package/dist/modules/staff/backend/staff/timesheets/projects/page.js +602 -0
- package/dist/modules/staff/backend/staff/timesheets/projects/page.js.map +7 -0
- package/dist/modules/staff/backend/staff/timesheets/projects/page.meta.js +25 -0
- package/dist/modules/staff/backend/staff/timesheets/projects/page.meta.js.map +7 -0
- package/dist/modules/staff/backend/staff/timesheets/projects/projectFormConfig.js +123 -0
- package/dist/modules/staff/backend/staff/timesheets/projects/projectFormConfig.js.map +7 -0
- package/dist/modules/staff/cli.js +38 -1
- package/dist/modules/staff/cli.js.map +2 -2
- package/dist/modules/staff/commands/index.js +2 -0
- package/dist/modules/staff/commands/index.js.map +2 -2
- package/dist/modules/staff/commands/leave-requests.js +30 -28
- package/dist/modules/staff/commands/leave-requests.js.map +3 -3
- package/dist/modules/staff/commands/team-members.js +21 -20
- package/dist/modules/staff/commands/team-members.js.map +2 -2
- package/dist/modules/staff/commands/timesheets-entries.js +409 -0
- package/dist/modules/staff/commands/timesheets-entries.js.map +7 -0
- package/dist/modules/staff/commands/timesheets-projects.js +618 -0
- package/dist/modules/staff/commands/timesheets-projects.js.map +7 -0
- package/dist/modules/staff/data/enrichers.js +104 -0
- package/dist/modules/staff/data/enrichers.js.map +7 -0
- package/dist/modules/staff/data/entities.js +226 -1
- package/dist/modules/staff/data/entities.js.map +2 -2
- package/dist/modules/staff/data/validators.js +113 -1
- package/dist/modules/staff/data/validators.js.map +2 -2
- package/dist/modules/staff/events.js +13 -1
- package/dist/modules/staff/events.js.map +2 -2
- package/dist/modules/staff/lib/crud.js +7 -1
- package/dist/modules/staff/lib/crud.js.map +2 -2
- package/dist/modules/staff/lib/staffMemberResolver.js +15 -0
- package/dist/modules/staff/lib/staffMemberResolver.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-projects/computeProjectHoursTrend.js +60 -0
- package/dist/modules/staff/lib/timesheets-projects/computeProjectHoursTrend.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-projects/computeProjectsKpis.js +260 -0
- package/dist/modules/staff/lib/timesheets-projects/computeProjectsKpis.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-projects/dateBuckets.js +41 -0
- package/dist/modules/staff/lib/timesheets-projects/dateBuckets.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-projects/initials.js +10 -0
- package/dist/modules/staff/lib/timesheets-projects/initials.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-projects/kpiMath.js +12 -0
- package/dist/modules/staff/lib/timesheets-projects/kpiMath.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-projects/listProjectMembersPreview.js +55 -0
- package/dist/modules/staff/lib/timesheets-projects/listProjectMembersPreview.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-projects-ui/HoursSparkline.js +66 -0
- package/dist/modules/staff/lib/timesheets-projects-ui/HoursSparkline.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-projects-ui/ProjectCard.js +81 -0
- package/dist/modules/staff/lib/timesheets-projects-ui/ProjectCard.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-projects-ui/ProjectMembersAvatarStack.js +58 -0
- package/dist/modules/staff/lib/timesheets-projects-ui/ProjectMembersAvatarStack.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-projects-ui/ProjectsKpiStrip.js +152 -0
- package/dist/modules/staff/lib/timesheets-projects-ui/ProjectsKpiStrip.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-projects-ui/SavedViewTabs.js +37 -0
- package/dist/modules/staff/lib/timesheets-projects-ui/SavedViewTabs.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-projects-ui/ViewModeToggle.js +57 -0
- package/dist/modules/staff/lib/timesheets-projects-ui/ViewModeToggle.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-projects-ui/useProjectsViewMode.js +50 -0
- package/dist/modules/staff/lib/timesheets-projects-ui/useProjectsViewMode.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-ui/AddRowDropdown.js +163 -0
- package/dist/modules/staff/lib/timesheets-ui/AddRowDropdown.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-ui/CalendarPicker.js +209 -0
- package/dist/modules/staff/lib/timesheets-ui/CalendarPicker.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-ui/ColorPicker.js +52 -0
- package/dist/modules/staff/lib/timesheets-ui/ColorPicker.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-ui/CreateProjectDialog.js +77 -0
- package/dist/modules/staff/lib/timesheets-ui/CreateProjectDialog.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-ui/ListView.js +173 -0
- package/dist/modules/staff/lib/timesheets-ui/ListView.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-ui/ProjectColorDot.js +32 -0
- package/dist/modules/staff/lib/timesheets-ui/ProjectColorDot.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-ui/TimerBar.js +270 -0
- package/dist/modules/staff/lib/timesheets-ui/TimerBar.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-ui/ViewSwitcher.js +57 -0
- package/dist/modules/staff/lib/timesheets-ui/ViewSwitcher.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-ui/colors.js +43 -0
- package/dist/modules/staff/lib/timesheets-ui/colors.js.map +7 -0
- package/dist/modules/staff/migrations/Migration20260326135612.js +24 -0
- package/dist/modules/staff/migrations/Migration20260326135612.js.map +7 -0
- package/dist/modules/staff/migrations/Migration20260413102715.js +23 -0
- package/dist/modules/staff/migrations/Migration20260413102715.js.map +7 -0
- package/dist/modules/staff/migrations/Migration20260413111602.js +13 -0
- package/dist/modules/staff/migrations/Migration20260413111602.js.map +7 -0
- package/dist/modules/staff/migrations/Migration20260511112759.js +19 -0
- package/dist/modules/staff/migrations/Migration20260511112759.js.map +7 -0
- package/dist/modules/staff/search.js +35 -0
- package/dist/modules/staff/search.js.map +2 -2
- package/dist/modules/staff/setup.js +15 -1
- package/dist/modules/staff/setup.js.map +2 -2
- package/dist/modules/staff/widgets/dashboard/timesheets-hours-by-project/config.js +16 -0
- package/dist/modules/staff/widgets/dashboard/timesheets-hours-by-project/config.js.map +7 -0
- package/dist/modules/staff/widgets/dashboard/timesheets-hours-by-project/widget.client.js +126 -0
- package/dist/modules/staff/widgets/dashboard/timesheets-hours-by-project/widget.client.js.map +7 -0
- package/dist/modules/staff/widgets/dashboard/timesheets-hours-by-project/widget.js +26 -0
- package/dist/modules/staff/widgets/dashboard/timesheets-hours-by-project/widget.js.map +7 -0
- package/dist/modules/staff/widgets/dashboard/timesheets-time-reporting/config.js +15 -0
- package/dist/modules/staff/widgets/dashboard/timesheets-time-reporting/config.js.map +7 -0
- package/dist/modules/staff/widgets/dashboard/timesheets-time-reporting/widget.client.js +238 -0
- package/dist/modules/staff/widgets/dashboard/timesheets-time-reporting/widget.client.js.map +7 -0
- package/dist/modules/staff/widgets/dashboard/timesheets-time-reporting/widget.js +26 -0
- package/dist/modules/staff/widgets/dashboard/timesheets-time-reporting/widget.js.map +7 -0
- package/dist/modules/staff/widgets/injection/timer-sidebar-indicator/widget.js +145 -0
- package/dist/modules/staff/widgets/injection/timer-sidebar-indicator/widget.js.map +7 -0
- package/dist/modules/staff/widgets/injection-table.js +12 -0
- package/dist/modules/staff/widgets/injection-table.js.map +7 -0
- package/dist/modules/sync_excel/api/import/route.js +19 -17
- package/dist/modules/sync_excel/api/import/route.js.map +2 -2
- package/dist/modules/translations/commands/translations.js +22 -19
- package/dist/modules/translations/commands/translations.js.map +2 -2
- package/generated/entities/staff_time_entry/index.ts +17 -0
- package/generated/entities/staff_time_entry_segment/index.ts +10 -0
- package/generated/entities/staff_time_project/index.ts +16 -0
- package/generated/entities/staff_time_project_member/index.ts +13 -0
- package/generated/entities.ids.generated.ts +5 -1
- package/generated/entity-fields-registry.ts +64 -0
- package/package.json +7 -7
- package/src/helpers/integration/timesheetFixtures.ts +61 -0
- package/src/modules/attachments/api/library/[id]/route.ts +24 -17
- package/src/modules/attachments/api/route.ts +20 -14
- package/src/modules/auth/api/roles/acl/route.ts +11 -5
- package/src/modules/auth/api/sidebar/preferences/route.ts +33 -24
- package/src/modules/auth/api/users/acl/route.ts +17 -12
- package/src/modules/auth/commands/users.ts +96 -80
- package/src/modules/auth/services/sidebarPreferencesService.ts +40 -32
- package/src/modules/catalog/commands/categories.ts +61 -12
- package/src/modules/catalog/commands/products.ts +93 -60
- package/src/modules/catalog/commands/variants.ts +29 -16
- package/src/modules/currencies/commands/currencies.ts +27 -14
- package/src/modules/customer_accounts/api/admin/users.ts +31 -26
- package/src/modules/customer_accounts/api/password/reset-confirm.ts +5 -6
- package/src/modules/customer_accounts/api/portal/users/[id]/roles.ts +14 -13
- package/src/modules/customers/commands/addresses.ts +35 -23
- package/src/modules/customers/commands/companies.ts +166 -165
- package/src/modules/customers/commands/deals.ts +2 -4
- package/src/modules/customers/commands/interactions.ts +20 -26
- package/src/modules/customers/commands/people.ts +18 -15
- package/src/modules/customers/commands/personCompanyLinks.ts +109 -100
- package/src/modules/customers/commands/pipeline-stages.ts +31 -27
- package/src/modules/customers/commands/pipelines.ts +29 -23
- package/src/modules/customers/commands/tags.ts +13 -5
- package/src/modules/dashboards/api/users/widgets/route.ts +0 -1
- package/src/modules/dashboards/api/widgets/data/route.ts +36 -1
- package/src/modules/data_sync/lib/sync-engine.ts +4 -5
- package/src/modules/data_sync/lib/sync-run-service.ts +57 -28
- package/src/modules/directory/commands/organizations.ts +203 -166
- package/src/modules/inbox_ops/api/emails/[id]/reprocess/route.ts +26 -18
- package/src/modules/messages/commands/messages.ts +82 -80
- package/src/modules/messages/commands/shared.ts +138 -133
- package/src/modules/perspectives/api/[tableId]/route.ts +38 -27
- package/src/modules/resources/commands/resources.ts +127 -117
- package/src/modules/resources/commands/tags.ts +7 -3
- package/src/modules/sales/api/quotes/send/route.ts +17 -12
- package/src/modules/sales/commands/documents.ts +673 -481
- package/src/modules/sales/commands/payments.ts +158 -152
- package/src/modules/sales/commands/returns.ts +74 -63
- package/src/modules/staff/acl.ts +11 -0
- package/src/modules/staff/analytics.ts +30 -0
- package/src/modules/staff/api/guards.ts +59 -0
- package/src/modules/staff/api/interceptors.ts +122 -0
- package/src/modules/staff/api/timesheets/my-projects/[projectId]/route.ts +191 -0
- package/src/modules/staff/api/timesheets/my-projects/route.ts +115 -0
- package/src/modules/staff/api/timesheets/projects/kpis/route.ts +159 -0
- package/src/modules/staff/api/timesheets/time-entries/[id]/segments/[segmentId]/route.ts +187 -0
- package/src/modules/staff/api/timesheets/time-entries/[id]/segments/route.ts +191 -0
- package/src/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.ts +168 -0
- package/src/modules/staff/api/timesheets/time-entries/[id]/timer-stop/route.ts +191 -0
- package/src/modules/staff/api/timesheets/time-entries/bulk/route.ts +292 -0
- package/src/modules/staff/api/timesheets/time-entries/route.ts +193 -0
- package/src/modules/staff/api/timesheets/time-projects/[id]/employees/route.ts +167 -0
- package/src/modules/staff/api/timesheets/time-projects/route.ts +244 -0
- package/src/modules/staff/backend/staff/timesheets/page.meta.ts +20 -0
- package/src/modules/staff/backend/staff/timesheets/page.tsx +899 -0
- package/src/modules/staff/backend/staff/timesheets/projects/[id]/edit/page.meta.ts +12 -0
- package/src/modules/staff/backend/staff/timesheets/projects/[id]/edit/page.tsx +141 -0
- package/src/modules/staff/backend/staff/timesheets/projects/[id]/page.meta.ts +12 -0
- package/src/modules/staff/backend/staff/timesheets/projects/[id]/page.tsx +579 -0
- package/src/modules/staff/backend/staff/timesheets/projects/create/page.meta.ts +12 -0
- package/src/modules/staff/backend/staff/timesheets/projects/create/page.tsx +90 -0
- package/src/modules/staff/backend/staff/timesheets/projects/page.meta.ts +23 -0
- package/src/modules/staff/backend/staff/timesheets/projects/page.tsx +765 -0
- package/src/modules/staff/backend/staff/timesheets/projects/projectFormConfig.ts +138 -0
- package/src/modules/staff/cli.ts +40 -1
- package/src/modules/staff/commands/index.ts +2 -0
- package/src/modules/staff/commands/leave-requests.ts +37 -29
- package/src/modules/staff/commands/team-members.ts +25 -20
- package/src/modules/staff/commands/timesheets-entries.ts +504 -0
- package/src/modules/staff/commands/timesheets-projects.ts +699 -0
- package/src/modules/staff/data/enrichers.ts +134 -0
- package/src/modules/staff/data/entities.ts +198 -0
- package/src/modules/staff/data/validators.ts +129 -0
- package/src/modules/staff/events.ts +13 -0
- package/src/modules/staff/i18n/de.json +209 -1
- package/src/modules/staff/i18n/en.json +209 -1
- package/src/modules/staff/i18n/es.json +209 -1
- package/src/modules/staff/i18n/pl.json +209 -1
- package/src/modules/staff/lib/crud.ts +8 -0
- package/src/modules/staff/lib/staffMemberResolver.ts +22 -0
- package/src/modules/staff/lib/timesheets-projects/computeProjectHoursTrend.ts +89 -0
- package/src/modules/staff/lib/timesheets-projects/computeProjectsKpis.ts +311 -0
- package/src/modules/staff/lib/timesheets-projects/dateBuckets.ts +37 -0
- package/src/modules/staff/lib/timesheets-projects/initials.ts +6 -0
- package/src/modules/staff/lib/timesheets-projects/kpiMath.ts +8 -0
- package/src/modules/staff/lib/timesheets-projects/listProjectMembersPreview.ts +83 -0
- package/src/modules/staff/lib/timesheets-projects-ui/HoursSparkline.tsx +75 -0
- package/src/modules/staff/lib/timesheets-projects-ui/ProjectCard.tsx +110 -0
- package/src/modules/staff/lib/timesheets-projects-ui/ProjectMembersAvatarStack.tsx +73 -0
- package/src/modules/staff/lib/timesheets-projects-ui/ProjectsKpiStrip.tsx +185 -0
- package/src/modules/staff/lib/timesheets-projects-ui/SavedViewTabs.tsx +53 -0
- package/src/modules/staff/lib/timesheets-projects-ui/ViewModeToggle.tsx +63 -0
- package/src/modules/staff/lib/timesheets-projects-ui/useProjectsViewMode.ts +63 -0
- package/src/modules/staff/lib/timesheets-ui/AddRowDropdown.tsx +188 -0
- package/src/modules/staff/lib/timesheets-ui/CalendarPicker.tsx +229 -0
- package/src/modules/staff/lib/timesheets-ui/ColorPicker.tsx +65 -0
- package/src/modules/staff/lib/timesheets-ui/CreateProjectDialog.tsx +99 -0
- package/src/modules/staff/lib/timesheets-ui/ListView.tsx +230 -0
- package/src/modules/staff/lib/timesheets-ui/ProjectColorDot.tsx +40 -0
- package/src/modules/staff/lib/timesheets-ui/TimerBar.tsx +327 -0
- package/src/modules/staff/lib/timesheets-ui/ViewSwitcher.tsx +60 -0
- package/src/modules/staff/lib/timesheets-ui/colors.ts +58 -0
- package/src/modules/staff/migrations/.snapshot-open-mercato.json +1148 -0
- package/src/modules/staff/migrations/Migration20260326135612.ts +26 -0
- package/src/modules/staff/migrations/Migration20260413102715.ts +25 -0
- package/src/modules/staff/migrations/Migration20260413111602.ts +13 -0
- package/src/modules/staff/migrations/Migration20260511112759.ts +21 -0
- package/src/modules/staff/search.ts +35 -0
- package/src/modules/staff/setup.ts +15 -0
- package/src/modules/staff/widgets/dashboard/timesheets-hours-by-project/config.ts +17 -0
- package/src/modules/staff/widgets/dashboard/timesheets-hours-by-project/widget.client.tsx +158 -0
- package/src/modules/staff/widgets/dashboard/timesheets-hours-by-project/widget.ts +25 -0
- package/src/modules/staff/widgets/dashboard/timesheets-time-reporting/config.ts +15 -0
- package/src/modules/staff/widgets/dashboard/timesheets-time-reporting/widget.client.tsx +297 -0
- package/src/modules/staff/widgets/dashboard/timesheets-time-reporting/widget.ts +25 -0
- package/src/modules/staff/widgets/injection/timer-sidebar-indicator/widget.tsx +161 -0
- package/src/modules/staff/widgets/injection-table.ts +10 -0
- package/src/modules/sync_excel/api/import/route.ts +23 -18
- package/src/modules/translations/commands/translations.ts +49 -41
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
loadCustomFieldSnapshot,
|
|
27
27
|
buildCustomFieldResetMap,
|
|
28
28
|
} from "@open-mercato/shared/lib/commands/customFieldSnapshots";
|
|
29
|
+
import { withAtomicFlush } from "@open-mercato/shared/lib/commands/flush";
|
|
29
30
|
import { E } from "#generated/entities.ids.generated";
|
|
30
31
|
import { slugifyTagLabel } from "@open-mercato/shared/lib/utils";
|
|
31
32
|
import { parseObjectLike } from "@open-mercato/shared/lib/json/parseObjectLike";
|
|
@@ -950,15 +951,15 @@ type VariantCleanupSnapshot = {
|
|
|
950
951
|
custom: Record<string, unknown> | null;
|
|
951
952
|
};
|
|
952
953
|
|
|
953
|
-
async function
|
|
954
|
-
em: EntityManager
|
|
955
|
-
product: CatalogProduct
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
954
|
+
async function collectProductVariantCleanup(
|
|
955
|
+
em: EntityManager,
|
|
956
|
+
product: CatalogProduct,
|
|
957
|
+
): Promise<{
|
|
958
|
+
variants: CatalogProductVariant[];
|
|
959
|
+
cleanupEntries: VariantCleanupSnapshot[];
|
|
960
|
+
}> {
|
|
960
961
|
const variants = await em.find(CatalogProductVariant, { product });
|
|
961
|
-
if (!variants.length) return;
|
|
962
|
+
if (!variants.length) return { variants: [], cleanupEntries: [] };
|
|
962
963
|
const cleanupEntries: VariantCleanupSnapshot[] = await Promise.all(
|
|
963
964
|
variants.map(async (variant) => {
|
|
964
965
|
const custom = await loadCustomFieldSnapshot(em, {
|
|
@@ -975,6 +976,14 @@ async function deleteProductVariantsAndRelatedData(opts: {
|
|
|
975
976
|
};
|
|
976
977
|
}),
|
|
977
978
|
);
|
|
979
|
+
return { variants, cleanupEntries };
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
async function removeProductVariants(
|
|
983
|
+
em: EntityManager,
|
|
984
|
+
variants: CatalogProductVariant[],
|
|
985
|
+
): Promise<void> {
|
|
986
|
+
if (!variants.length) return;
|
|
978
987
|
const variantIds = variants.map((variant) => variant.id);
|
|
979
988
|
if (variantIds.length) {
|
|
980
989
|
await em.nativeDelete(CatalogProductPrice, {
|
|
@@ -984,7 +993,14 @@ async function deleteProductVariantsAndRelatedData(opts: {
|
|
|
984
993
|
for (const variant of variants) {
|
|
985
994
|
em.remove(variant);
|
|
986
995
|
}
|
|
987
|
-
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
async function emitProductVariantCleanupSideEffects(opts: {
|
|
999
|
+
dataEngine: DataEngine;
|
|
1000
|
+
ctx: CommandRuntimeContext;
|
|
1001
|
+
cleanupEntries: VariantCleanupSnapshot[];
|
|
1002
|
+
}): Promise<void> {
|
|
1003
|
+
const { dataEngine, ctx, cleanupEntries } = opts;
|
|
988
1004
|
for (const cleanup of cleanupEntries) {
|
|
989
1005
|
if (!cleanup.custom) continue;
|
|
990
1006
|
const resetValues = buildCustomFieldResetMap(cleanup.custom, undefined);
|
|
@@ -1307,15 +1323,16 @@ const createProductCommand: CommandHandler<
|
|
|
1307
1323
|
}
|
|
1308
1324
|
em.persist(record);
|
|
1309
1325
|
try {
|
|
1310
|
-
await
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1326
|
+
await withAtomicFlush(
|
|
1327
|
+
em,
|
|
1328
|
+
[
|
|
1329
|
+
() => em.flush(),
|
|
1330
|
+
() => syncOffers(em, record, parsed.offers),
|
|
1331
|
+
() => syncCategoryAssignments(em, record, parsed.categoryIds),
|
|
1332
|
+
() => syncProductTags(em, record, parsed.tags),
|
|
1333
|
+
],
|
|
1334
|
+
{ transaction: true },
|
|
1335
|
+
);
|
|
1319
1336
|
} catch (error) {
|
|
1320
1337
|
await rethrowProductUniqueConstraint(error);
|
|
1321
1338
|
}
|
|
@@ -1614,15 +1631,16 @@ const updateProductCommand: CommandHandler<
|
|
|
1614
1631
|
record.isConfigurable = parsed.isConfigurable;
|
|
1615
1632
|
if (parsed.isActive !== undefined) record.isActive = parsed.isActive;
|
|
1616
1633
|
try {
|
|
1617
|
-
await
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1634
|
+
await withAtomicFlush(
|
|
1635
|
+
em,
|
|
1636
|
+
[
|
|
1637
|
+
() => em.flush(),
|
|
1638
|
+
() => syncOffers(em, record, parsed.offers),
|
|
1639
|
+
() => syncCategoryAssignments(em, record, parsed.categoryIds),
|
|
1640
|
+
() => syncProductTags(em, record, parsed.tags),
|
|
1641
|
+
],
|
|
1642
|
+
{ transaction: true },
|
|
1643
|
+
);
|
|
1626
1644
|
} catch (error) {
|
|
1627
1645
|
await rethrowProductUniqueConstraint(error);
|
|
1628
1646
|
}
|
|
@@ -1732,16 +1750,16 @@ const updateProductCommand: CommandHandler<
|
|
|
1732
1750
|
ensureTenantScope(ctx, before.tenantId);
|
|
1733
1751
|
ensureOrganizationScope(ctx, before.organizationId);
|
|
1734
1752
|
applyProductSnapshot(em, record, before);
|
|
1735
|
-
await
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1753
|
+
await withAtomicFlush(
|
|
1754
|
+
em,
|
|
1755
|
+
[
|
|
1756
|
+
() => em.flush(),
|
|
1757
|
+
() => restoreOffersFromSnapshot(em, record, before.offers),
|
|
1758
|
+
() => syncCategoryAssignments(em, record, before.categoryIds),
|
|
1759
|
+
() => syncProductTags(em, record, before.tags),
|
|
1760
|
+
],
|
|
1761
|
+
{ transaction: true },
|
|
1762
|
+
);
|
|
1745
1763
|
const dataEngine = ctx.container.resolve("dataEngine") as DataEngine;
|
|
1746
1764
|
const resetValues = buildCustomFieldResetMap(
|
|
1747
1765
|
before.custom ?? undefined,
|
|
@@ -1803,23 +1821,38 @@ const deleteProductCommand: CommandHandler<
|
|
|
1803
1821
|
ensureTenantScope(ctx, record.tenantId);
|
|
1804
1822
|
ensureOrganizationScope(ctx, record.organizationId);
|
|
1805
1823
|
const dataEngine = ctx.container.resolve("dataEngine") as DataEngine;
|
|
1806
|
-
await
|
|
1824
|
+
const { variants, cleanupEntries } = await collectProductVariantCleanup(
|
|
1807
1825
|
em,
|
|
1808
|
-
|
|
1826
|
+
record,
|
|
1827
|
+
);
|
|
1828
|
+
await withAtomicFlush(
|
|
1829
|
+
em,
|
|
1830
|
+
[
|
|
1831
|
+
() => removeProductVariants(em, variants),
|
|
1832
|
+
async () => {
|
|
1833
|
+
await em.nativeDelete(CatalogProductPrice, { product: record.id });
|
|
1834
|
+
},
|
|
1835
|
+
async () => {
|
|
1836
|
+
const templateToRemove = await resolveOptionSchemaTemplateForRemoval(
|
|
1837
|
+
em,
|
|
1838
|
+
record,
|
|
1839
|
+
);
|
|
1840
|
+
if (templateToRemove) {
|
|
1841
|
+
record.optionSchemaTemplate = null;
|
|
1842
|
+
em.remove(templateToRemove);
|
|
1843
|
+
}
|
|
1844
|
+
},
|
|
1845
|
+
() => {
|
|
1846
|
+
em.remove(record);
|
|
1847
|
+
},
|
|
1848
|
+
],
|
|
1849
|
+
{ transaction: true },
|
|
1850
|
+
);
|
|
1851
|
+
await emitProductVariantCleanupSideEffects({
|
|
1809
1852
|
dataEngine,
|
|
1810
1853
|
ctx,
|
|
1854
|
+
cleanupEntries,
|
|
1811
1855
|
});
|
|
1812
|
-
await em.nativeDelete(CatalogProductPrice, { product: record.id });
|
|
1813
|
-
const templateToRemove = await resolveOptionSchemaTemplateForRemoval(
|
|
1814
|
-
em,
|
|
1815
|
-
record,
|
|
1816
|
-
);
|
|
1817
|
-
if (templateToRemove) {
|
|
1818
|
-
record.optionSchemaTemplate = null;
|
|
1819
|
-
em.remove(templateToRemove);
|
|
1820
|
-
}
|
|
1821
|
-
em.remove(record);
|
|
1822
|
-
await em.flush();
|
|
1823
1856
|
if (snapshot?.custom && Object.keys(snapshot.custom).length) {
|
|
1824
1857
|
const resetValues = buildCustomFieldResetMap(snapshot.custom, undefined);
|
|
1825
1858
|
if (Object.keys(resetValues).length) {
|
|
@@ -1908,16 +1941,16 @@ const deleteProductCommand: CommandHandler<
|
|
|
1908
1941
|
ensureTenantScope(ctx, before.tenantId);
|
|
1909
1942
|
ensureOrganizationScope(ctx, before.organizationId);
|
|
1910
1943
|
applyProductSnapshot(em, record, before);
|
|
1911
|
-
await
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1944
|
+
await withAtomicFlush(
|
|
1945
|
+
em,
|
|
1946
|
+
[
|
|
1947
|
+
() => em.flush(),
|
|
1948
|
+
() => restoreOffersFromSnapshot(em, record, before.offers),
|
|
1949
|
+
() => syncCategoryAssignments(em, record, before.categoryIds),
|
|
1950
|
+
() => syncProductTags(em, record, before.tags),
|
|
1951
|
+
],
|
|
1952
|
+
{ transaction: true },
|
|
1953
|
+
);
|
|
1921
1954
|
const dataEngine = ctx.container.resolve("dataEngine") as DataEngine;
|
|
1922
1955
|
if (before.custom && Object.keys(before.custom).length) {
|
|
1923
1956
|
await setCustomFieldsIfAny({
|
|
@@ -6,6 +6,7 @@ import { UniqueConstraintViolationException } from '@mikro-orm/core'
|
|
|
6
6
|
import { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'
|
|
7
7
|
import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
|
|
8
8
|
import { loadCustomFieldSnapshot, buildCustomFieldResetMap } from '@open-mercato/shared/lib/commands/customFieldSnapshots'
|
|
9
|
+
import { withAtomicFlush } from '@open-mercato/shared/lib/commands/flush'
|
|
9
10
|
import { E } from '#generated/entities.ids.generated'
|
|
10
11
|
import { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'
|
|
11
12
|
import {
|
|
@@ -588,21 +589,25 @@ const createVariantCommand: CommandHandler<VariantCreateInput, { variantId: stri
|
|
|
588
589
|
updatedAt: now,
|
|
589
590
|
})
|
|
590
591
|
em.persist(record)
|
|
592
|
+
let previousDefaultVariantId: string | null = null
|
|
591
593
|
try {
|
|
592
|
-
await
|
|
594
|
+
await withAtomicFlush(
|
|
595
|
+
em,
|
|
596
|
+
[
|
|
597
|
+
() => em.flush(),
|
|
598
|
+
async () => {
|
|
599
|
+
if (record.isDefault) {
|
|
600
|
+
previousDefaultVariantId = await enforceSingleDefaultVariant(em, record)
|
|
601
|
+
await em.flush()
|
|
602
|
+
}
|
|
603
|
+
},
|
|
604
|
+
() => aggregateVariantMediaToProduct(em, record),
|
|
605
|
+
],
|
|
606
|
+
{ transaction: true }
|
|
607
|
+
)
|
|
593
608
|
} catch (error) {
|
|
594
609
|
await rethrowVariantUniqueConstraint(error)
|
|
595
610
|
}
|
|
596
|
-
let previousDefaultVariantId: string | null = null
|
|
597
|
-
if (record.isDefault) {
|
|
598
|
-
previousDefaultVariantId = await enforceSingleDefaultVariant(em, record)
|
|
599
|
-
try {
|
|
600
|
-
await em.flush()
|
|
601
|
-
} catch (error) {
|
|
602
|
-
await rethrowVariantUniqueConstraint(error)
|
|
603
|
-
}
|
|
604
|
-
}
|
|
605
|
-
await aggregateVariantMediaToProduct(em, record)
|
|
606
611
|
await setCustomFieldsIfAny({
|
|
607
612
|
dataEngine: ctx.container.resolve('dataEngine'),
|
|
608
613
|
entityId: E.catalog.catalog_product_variant,
|
|
@@ -753,15 +758,23 @@ const updateVariantCommand: CommandHandler<VariantUpdateInput, { variantId: stri
|
|
|
753
758
|
}
|
|
754
759
|
|
|
755
760
|
let previousDefaultVariantId: string | null = null
|
|
756
|
-
if (parsed.isDefault === true) {
|
|
757
|
-
previousDefaultVariantId = await enforceSingleDefaultVariant(em, record)
|
|
758
|
-
}
|
|
759
761
|
try {
|
|
760
|
-
await
|
|
762
|
+
await withAtomicFlush(
|
|
763
|
+
em,
|
|
764
|
+
[
|
|
765
|
+
async () => {
|
|
766
|
+
if (parsed.isDefault === true) {
|
|
767
|
+
previousDefaultVariantId = await enforceSingleDefaultVariant(em, record)
|
|
768
|
+
}
|
|
769
|
+
},
|
|
770
|
+
() => em.flush(),
|
|
771
|
+
() => aggregateVariantMediaToProduct(em, record),
|
|
772
|
+
],
|
|
773
|
+
{ transaction: true }
|
|
774
|
+
)
|
|
761
775
|
} catch (error) {
|
|
762
776
|
await rethrowVariantUniqueConstraint(error)
|
|
763
777
|
}
|
|
764
|
-
await aggregateVariantMediaToProduct(em, record)
|
|
765
778
|
if (custom && Object.keys(custom).length) {
|
|
766
779
|
await setCustomFieldsIfAny({
|
|
767
780
|
dataEngine: ctx.container.resolve('dataEngine'),
|
|
@@ -2,6 +2,7 @@ import { registerCommand } from '@open-mercato/shared/lib/commands'
|
|
|
2
2
|
import type { CommandHandler } from '@open-mercato/shared/lib/commands'
|
|
3
3
|
import { buildChanges, requireId, emitCrudSideEffects } from '@open-mercato/shared/lib/commands/helpers'
|
|
4
4
|
import { extractUndoPayload, type UndoPayload } from '@open-mercato/shared/lib/commands/undo'
|
|
5
|
+
import { withAtomicFlush } from '@open-mercato/shared/lib/commands/flush'
|
|
5
6
|
import type { EntityManager } from '@mikro-orm/postgresql'
|
|
6
7
|
import { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'
|
|
7
8
|
import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
|
|
@@ -119,13 +120,19 @@ const createCurrencyCommand: CommandHandler<CurrencyCreateInput, { currencyId: s
|
|
|
119
120
|
updatedAt: now,
|
|
120
121
|
})
|
|
121
122
|
em.persist(record)
|
|
122
|
-
|
|
123
|
-
//
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
123
|
+
|
|
124
|
+
// Demote any existing base currency and insert the new record in one
|
|
125
|
+
// transaction; a partial commit would leave zero or two base currencies.
|
|
126
|
+
await withAtomicFlush(
|
|
127
|
+
em,
|
|
128
|
+
[
|
|
129
|
+
() =>
|
|
130
|
+
record.isBase
|
|
131
|
+
? enforceBaseCurrency(em, record.id, record.organizationId, record.tenantId)
|
|
132
|
+
: undefined,
|
|
133
|
+
],
|
|
134
|
+
{ transaction: true },
|
|
135
|
+
)
|
|
129
136
|
|
|
130
137
|
const de = ctx.container.resolve('dataEngine') as DataEngine
|
|
131
138
|
await emitCrudSideEffects({
|
|
@@ -227,13 +234,19 @@ const updateCurrencyCommand: CommandHandler<CurrencyUpdateInput, { currencyId: s
|
|
|
227
234
|
;(record as any)[key] = change.to
|
|
228
235
|
}
|
|
229
236
|
record.updatedAt = new Date()
|
|
230
|
-
|
|
231
|
-
//
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
+
|
|
238
|
+
// Demote any existing base currency and persist the scalar changes in one
|
|
239
|
+
// transaction; a partial commit would leave zero or two base currencies.
|
|
240
|
+
await withAtomicFlush(
|
|
241
|
+
em,
|
|
242
|
+
[
|
|
243
|
+
() =>
|
|
244
|
+
parsed.isBase === true && record.isBase
|
|
245
|
+
? enforceBaseCurrency(em, record.id, record.organizationId, record.tenantId)
|
|
246
|
+
: undefined,
|
|
247
|
+
],
|
|
248
|
+
{ transaction: true },
|
|
249
|
+
)
|
|
237
250
|
|
|
238
251
|
const de = ctx.container.resolve('dataEngine') as DataEngine
|
|
239
252
|
await emitCrudSideEffects({
|
|
@@ -225,35 +225,40 @@ export async function POST(req: Request) {
|
|
|
225
225
|
{ tenantId: auth.tenantId!, organizationId: auth.orgId! },
|
|
226
226
|
)
|
|
227
227
|
user.emailVerifiedAt = new Date()
|
|
228
|
-
em.persist(user)
|
|
229
|
-
await em.flush()
|
|
230
228
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
229
|
+
// Persist the user, its company association, and its role links in one
|
|
230
|
+
// transaction so a flush failure on the role loop cannot leave a roleless
|
|
231
|
+
// user committed (privilege gap).
|
|
232
|
+
await em.transactional(async (tx) => {
|
|
233
|
+
tx.persist(user)
|
|
234
|
+
await tx.flush()
|
|
234
235
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
em,
|
|
238
|
-
CustomerRole,
|
|
239
|
-
{
|
|
240
|
-
id: { $in: parsed.data.roleIds } as any,
|
|
241
|
-
tenantId: auth.tenantId,
|
|
242
|
-
deletedAt: null,
|
|
243
|
-
} as any,
|
|
244
|
-
undefined,
|
|
245
|
-
{ tenantId: auth.tenantId, organizationId: auth.orgId },
|
|
246
|
-
)
|
|
247
|
-
for (const role of validRoles) {
|
|
248
|
-
const userRole = em.create(CustomerUserRole, {
|
|
249
|
-
user,
|
|
250
|
-
role,
|
|
251
|
-
createdAt: new Date(),
|
|
252
|
-
} as any)
|
|
253
|
-
em.persist(userRole)
|
|
236
|
+
if (parsed.data.customerEntityId) {
|
|
237
|
+
await tx.nativeUpdate(CustomerUser, { id: user.id }, { customerEntityId: parsed.data.customerEntityId })
|
|
254
238
|
}
|
|
255
|
-
|
|
256
|
-
|
|
239
|
+
|
|
240
|
+
if (parsed.data.roleIds && parsed.data.roleIds.length > 0) {
|
|
241
|
+
const validRoles = await findWithDecryption(
|
|
242
|
+
tx,
|
|
243
|
+
CustomerRole,
|
|
244
|
+
{
|
|
245
|
+
id: { $in: parsed.data.roleIds } as any,
|
|
246
|
+
tenantId: auth.tenantId,
|
|
247
|
+
deletedAt: null,
|
|
248
|
+
} as any,
|
|
249
|
+
undefined,
|
|
250
|
+
{ tenantId: auth.tenantId, organizationId: auth.orgId },
|
|
251
|
+
)
|
|
252
|
+
for (const role of validRoles) {
|
|
253
|
+
const userRole = tx.create(CustomerUserRole, {
|
|
254
|
+
user,
|
|
255
|
+
role,
|
|
256
|
+
createdAt: new Date(),
|
|
257
|
+
} as any)
|
|
258
|
+
tx.persist(userRole)
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
})
|
|
257
262
|
|
|
258
263
|
void emitCustomerAccountsEvent('customer_accounts.user.created', {
|
|
259
264
|
id: user.id,
|
|
@@ -44,14 +44,13 @@ export async function POST(req: Request) {
|
|
|
44
44
|
await em.transactional(async (trx) => {
|
|
45
45
|
await customerUserService.updatePassword(user, parsed.data.password, trx)
|
|
46
46
|
await customerSessionService.revokeAllUserSessions(user.id, trx)
|
|
47
|
+
await trx.nativeUpdate(
|
|
48
|
+
CustomerUser,
|
|
49
|
+
{ id: user.id, emailVerifiedAt: null },
|
|
50
|
+
{ emailVerifiedAt: new Date() },
|
|
51
|
+
)
|
|
47
52
|
})
|
|
48
53
|
|
|
49
|
-
await em.nativeUpdate(
|
|
50
|
-
CustomerUser,
|
|
51
|
-
{ id: user.id, emailVerifiedAt: null },
|
|
52
|
-
{ emailVerifiedAt: new Date() },
|
|
53
|
-
)
|
|
54
|
-
|
|
55
54
|
void emitCustomerAccountsEvent('customer_accounts.password.changed', {
|
|
56
55
|
userId: user.id,
|
|
57
56
|
tenantId: user.tenantId,
|
|
@@ -75,19 +75,20 @@ export async function PUT(req: Request, { params }: { params: { id: string } })
|
|
|
75
75
|
validatedRoles.push(role)
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
//
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
78
|
+
// Replace the role set atomically: deleting the old roles and inserting the new
|
|
79
|
+
// ones in one transaction prevents a flush failure from leaving the user with
|
|
80
|
+
// zero roles (portal lockout / privilege loss) (#2337).
|
|
81
|
+
await em.transactional(async (tx) => {
|
|
82
|
+
await tx.nativeDelete(CustomerUserRole, { user: targetUser.id as any })
|
|
83
|
+
for (const role of validatedRoles) {
|
|
84
|
+
const userRole = tx.create(CustomerUserRole, {
|
|
85
|
+
user: targetUser,
|
|
86
|
+
role,
|
|
87
|
+
createdAt: new Date(),
|
|
88
|
+
} as any)
|
|
89
|
+
tx.persist(userRole)
|
|
90
|
+
}
|
|
91
|
+
})
|
|
91
92
|
|
|
92
93
|
await customerRbacService.invalidateUserCache(targetUser.id)
|
|
93
94
|
|
|
@@ -17,6 +17,7 @@ import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
|
|
|
17
17
|
import { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'
|
|
18
18
|
import type { CrudIndexerConfig, CrudEventsConfig } from '@open-mercato/shared/lib/crud/types'
|
|
19
19
|
import { E } from '#generated/entities.ids.generated'
|
|
20
|
+
import { withAtomicFlush } from '@open-mercato/shared/lib/commands/flush'
|
|
20
21
|
|
|
21
22
|
const addressCrudIndexer: CrudIndexerConfig<CustomerAddress> = {
|
|
22
23
|
entityType: E.customers.customer_address,
|
|
@@ -130,13 +131,15 @@ const createAddressCommand: CommandHandler<AddressCreateInput, { addressId: stri
|
|
|
130
131
|
createdAt: new Date(),
|
|
131
132
|
updatedAt: new Date(),
|
|
132
133
|
})
|
|
133
|
-
em
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
134
|
+
await withAtomicFlush(em, [
|
|
135
|
+
async () => {
|
|
136
|
+
em.persist(address)
|
|
137
|
+
await em.flush()
|
|
138
|
+
if (address.isPrimary) {
|
|
139
|
+
await enforcePrimaryAddress(em, entity.id, address.id)
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
], { transaction: true })
|
|
140
143
|
|
|
141
144
|
const de = (ctx.container.resolve('dataEngine') as DataEngine)
|
|
142
145
|
await emitCrudSideEffects({
|
|
@@ -225,12 +228,13 @@ const updateAddressCommand: CommandHandler<AddressUpdateInput, { addressId: stri
|
|
|
225
228
|
if (parsed.longitude !== undefined) address.longitude = parsed.longitude ?? null
|
|
226
229
|
if (parsed.isPrimary !== undefined) address.isPrimary = parsed.isPrimary
|
|
227
230
|
|
|
228
|
-
await em
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
231
|
+
await withAtomicFlush(em, [
|
|
232
|
+
async () => {
|
|
233
|
+
if (address.isPrimary) {
|
|
234
|
+
await enforcePrimaryAddress(em, typeof address.entity === 'string' ? address.entity : address.entity.id, address.id)
|
|
235
|
+
}
|
|
236
|
+
},
|
|
237
|
+
], { transaction: true })
|
|
234
238
|
|
|
235
239
|
const de = (ctx.container.resolve('dataEngine') as DataEngine)
|
|
236
240
|
await emitCrudSideEffects({
|
|
@@ -348,11 +352,15 @@ const updateAddressCommand: CommandHandler<AddressUpdateInput, { addressId: stri
|
|
|
348
352
|
address.longitude = before.longitude
|
|
349
353
|
address.isPrimary = before.isPrimary
|
|
350
354
|
}
|
|
351
|
-
await em
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
355
|
+
await withAtomicFlush(em, [
|
|
356
|
+
async () => {
|
|
357
|
+
em.persist(address)
|
|
358
|
+
await em.flush()
|
|
359
|
+
if (before.isPrimary) {
|
|
360
|
+
await enforcePrimaryAddress(em, before.entityId, before.id)
|
|
361
|
+
}
|
|
362
|
+
},
|
|
363
|
+
], { transaction: true })
|
|
356
364
|
|
|
357
365
|
const de = (ctx.container.resolve('dataEngine') as DataEngine)
|
|
358
366
|
await emitCrudUndoSideEffects({
|
|
@@ -471,11 +479,15 @@ const deleteAddressCommand: CommandHandler<{ body?: Record<string, unknown>; que
|
|
|
471
479
|
address.longitude = before.longitude
|
|
472
480
|
address.isPrimary = before.isPrimary
|
|
473
481
|
}
|
|
474
|
-
await em
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
482
|
+
await withAtomicFlush(em, [
|
|
483
|
+
async () => {
|
|
484
|
+
em.persist(address)
|
|
485
|
+
await em.flush()
|
|
486
|
+
if (before.isPrimary) {
|
|
487
|
+
await enforcePrimaryAddress(em, before.entityId, before.id)
|
|
488
|
+
}
|
|
489
|
+
},
|
|
490
|
+
], { transaction: true })
|
|
479
491
|
|
|
480
492
|
const de = (ctx.container.resolve('dataEngine') as DataEngine)
|
|
481
493
|
await emitCrudUndoSideEffects({
|