@open-mercato/core 0.6.4-develop.4217.1.c9aa050183 → 0.6.4-develop.4239.1.4a264a5828
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/business_rules/backend/logs/[id]/page.js +24 -5
- package/dist/modules/business_rules/backend/logs/[id]/page.js.map +2 -2
- package/dist/modules/catalog/api/offers/route.js +15 -5
- package/dist/modules/catalog/api/offers/route.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/backend/currencies/[id]/page.js +19 -2
- package/dist/modules/currencies/backend/currencies/[id]/page.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/customer_accounts/backend/customer_accounts/roles/[id]/page.js +27 -7
- package/dist/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.js.map +2 -2
- package/dist/modules/customer_accounts/backend/customer_accounts/users/[id]/page.js +27 -7
- package/dist/modules/customer_accounts/backend/customer_accounts/users/[id]/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/people/[id]/page.js +29 -8
- package/dist/modules/customers/backend/customers/people/[id]/page.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/progress/acl.js +8 -4
- package/dist/modules/progress/acl.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/dist/modules/workflows/backend/events/[id]/page.js +24 -6
- package/dist/modules/workflows/backend/events/[id]/page.js.map +2 -2
- package/dist/modules/workflows/backend/instances/[id]/page.js +27 -5
- package/dist/modules/workflows/backend/instances/[id]/page.js.map +2 -2
- package/dist/modules/workflows/backend/tasks/[id]/page.js +25 -6
- package/dist/modules/workflows/backend/tasks/[id]/page.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/business_rules/backend/logs/[id]/page.tsx +32 -7
- package/src/modules/catalog/api/offers/route.ts +20 -5
- 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/backend/currencies/[id]/page.tsx +21 -2
- package/src/modules/currencies/commands/currencies.ts +27 -14
- package/src/modules/currencies/i18n/de.json +1 -0
- package/src/modules/currencies/i18n/en.json +1 -0
- package/src/modules/currencies/i18n/es.json +1 -0
- package/src/modules/currencies/i18n/pl.json +1 -0
- 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/customer_accounts/backend/customer_accounts/roles/[id]/page.tsx +34 -11
- package/src/modules/customer_accounts/backend/customer_accounts/users/[id]/page.tsx +34 -11
- package/src/modules/customers/backend/customers/people/[id]/page.tsx +35 -11
- 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/progress/acl.ts +4 -0
- 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
- package/src/modules/workflows/backend/events/[id]/page.tsx +32 -10
- package/src/modules/workflows/backend/instances/[id]/page.tsx +33 -9
- package/src/modules/workflows/backend/tasks/[id]/page.tsx +33 -10
- package/src/modules/workflows/i18n/de.json +1 -0
- package/src/modules/workflows/i18n/en.json +1 -0
- package/src/modules/workflows/i18n/es.json +1 -0
- package/src/modules/workflows/i18n/pl.json +1 -0
|
@@ -4,6 +4,7 @@ import { randomUUID } from "crypto";
|
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import { registerCommand } from "@open-mercato/shared/lib/commands";
|
|
6
6
|
import type { CommandHandler } from "@open-mercato/shared/lib/commands";
|
|
7
|
+
import { withAtomicFlush } from "@open-mercato/shared/lib/commands/flush";
|
|
7
8
|
import {
|
|
8
9
|
buildChanges,
|
|
9
10
|
emitCrudSideEffects,
|
|
@@ -4561,7 +4562,6 @@ const createQuoteCommand: CommandHandler<
|
|
|
4561
4562
|
createdAt: new Date(),
|
|
4562
4563
|
updatedAt: new Date(),
|
|
4563
4564
|
});
|
|
4564
|
-
em.persist(quote);
|
|
4565
4565
|
|
|
4566
4566
|
const lineInputs = (parsed.lines ?? []).map((line, index) =>
|
|
4567
4567
|
quoteLineCreateSchema.parse({
|
|
@@ -4631,32 +4631,48 @@ const createQuoteCommand: CommandHandler<
|
|
|
4631
4631
|
context: calculationContext,
|
|
4632
4632
|
});
|
|
4633
4633
|
|
|
4634
|
-
await replaceQuoteLines(em, quote, calculation, normalizedLineInputs);
|
|
4635
|
-
await replaceQuoteAdjustments(em, quote, calculation, adjustmentInputs);
|
|
4636
|
-
applyQuoteTotals(quote, calculation.totals, calculation.lines.length);
|
|
4637
4634
|
let eventBus: EventBus | null = null;
|
|
4638
4635
|
try {
|
|
4639
4636
|
eventBus = ctx.container.resolve("eventBus") as EventBus;
|
|
4640
4637
|
} catch {
|
|
4641
4638
|
eventBus = null;
|
|
4642
4639
|
}
|
|
4643
|
-
|
|
4644
|
-
|
|
4645
|
-
|
|
4646
|
-
|
|
4647
|
-
|
|
4648
|
-
|
|
4649
|
-
|
|
4650
|
-
|
|
4651
|
-
|
|
4652
|
-
|
|
4653
|
-
|
|
4654
|
-
|
|
4655
|
-
|
|
4656
|
-
|
|
4657
|
-
|
|
4658
|
-
|
|
4659
|
-
|
|
4640
|
+
// Persist the quote header, lines, adjustments, totals and tags atomically.
|
|
4641
|
+
// replace*/setRecordCustomFields flush mid-build, so without a transaction a
|
|
4642
|
+
// later failure (e.g. tag sync) would leave a half-built quote committed (#2336).
|
|
4643
|
+
await withAtomicFlush(
|
|
4644
|
+
em,
|
|
4645
|
+
[
|
|
4646
|
+
async () => {
|
|
4647
|
+
em.persist(quote);
|
|
4648
|
+
await replaceQuoteLines(em, quote, calculation, normalizedLineInputs);
|
|
4649
|
+
await replaceQuoteAdjustments(
|
|
4650
|
+
em,
|
|
4651
|
+
quote,
|
|
4652
|
+
calculation,
|
|
4653
|
+
adjustmentInputs,
|
|
4654
|
+
);
|
|
4655
|
+
applyQuoteTotals(quote, calculation.totals, calculation.lines.length);
|
|
4656
|
+
await emitTotalsCalculated(eventBus, {
|
|
4657
|
+
documentKind: "quote",
|
|
4658
|
+
documentId: quote.id,
|
|
4659
|
+
organizationId: quote.organizationId,
|
|
4660
|
+
tenantId: quote.tenantId,
|
|
4661
|
+
customerId: quote.customerEntityId ?? null,
|
|
4662
|
+
totals: calculation.totals,
|
|
4663
|
+
lineCount: calculation.lines.length,
|
|
4664
|
+
});
|
|
4665
|
+
await syncSalesDocumentTags(em, {
|
|
4666
|
+
documentId: quote.id,
|
|
4667
|
+
kind: "quote",
|
|
4668
|
+
organizationId: quote.organizationId,
|
|
4669
|
+
tenantId: quote.tenantId,
|
|
4670
|
+
tagIds: parsed.tags,
|
|
4671
|
+
});
|
|
4672
|
+
},
|
|
4673
|
+
],
|
|
4674
|
+
{ transaction: true },
|
|
4675
|
+
);
|
|
4660
4676
|
|
|
4661
4677
|
// Create notification for users with sales.quotes.manage feature
|
|
4662
4678
|
try {
|
|
@@ -4814,22 +4830,31 @@ const deleteQuoteCommand: CommandHandler<
|
|
|
4814
4830
|
em.find(SalesQuoteAdjustment, { quote: quote.id }),
|
|
4815
4831
|
em.find(SalesQuoteLine, { quote: quote.id }),
|
|
4816
4832
|
]);
|
|
4817
|
-
|
|
4818
|
-
|
|
4819
|
-
|
|
4820
|
-
|
|
4821
|
-
|
|
4822
|
-
|
|
4823
|
-
|
|
4824
|
-
|
|
4825
|
-
|
|
4826
|
-
|
|
4827
|
-
|
|
4828
|
-
|
|
4829
|
-
|
|
4830
|
-
|
|
4831
|
-
|
|
4832
|
-
|
|
4833
|
+
// Delete the quote and its cascade atomically so a mid-cascade failure
|
|
4834
|
+
// cannot leave a partially-deleted quote committed (#2336).
|
|
4835
|
+
await withAtomicFlush(
|
|
4836
|
+
em,
|
|
4837
|
+
[
|
|
4838
|
+
async () => {
|
|
4839
|
+
await em.nativeDelete(SalesDocumentAddress, {
|
|
4840
|
+
documentId: quote.id,
|
|
4841
|
+
documentKind: "quote",
|
|
4842
|
+
});
|
|
4843
|
+
await em.nativeDelete(SalesNote, {
|
|
4844
|
+
contextType: "quote",
|
|
4845
|
+
contextId: quote.id,
|
|
4846
|
+
});
|
|
4847
|
+
await em.nativeDelete(SalesDocumentTagAssignment, {
|
|
4848
|
+
documentId: quote.id,
|
|
4849
|
+
documentKind: "quote",
|
|
4850
|
+
});
|
|
4851
|
+
await em.nativeDelete(SalesQuoteAdjustment, { quote: quote.id });
|
|
4852
|
+
await em.nativeDelete(SalesQuoteLine, { quote: quote.id });
|
|
4853
|
+
em.remove(quote);
|
|
4854
|
+
},
|
|
4855
|
+
],
|
|
4856
|
+
{ transaction: true },
|
|
4857
|
+
);
|
|
4833
4858
|
const dataEngine = ctx.container.resolve<DataEngine>("dataEngine");
|
|
4834
4859
|
await Promise.all([
|
|
4835
4860
|
queueDeletionSideEffects(dataEngine, quote, E.sales.sales_quote),
|
|
@@ -4923,107 +4948,125 @@ const updateQuoteCommand: CommandHandler<
|
|
|
4923
4948
|
parsed.paymentMethodSnapshot !== undefined ||
|
|
4924
4949
|
parsed.paymentMethodCode !== undefined ||
|
|
4925
4950
|
parsed.currencyCode !== undefined;
|
|
4926
|
-
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
|
|
4951
|
+
// Apply the scalar update, optional sent-token reset, and totals recalc
|
|
4952
|
+
// atomically. applyDocumentUpdate/replace*/setRecordCustomFields flush
|
|
4953
|
+
// mid-build, so without a transaction a later failure would leave a
|
|
4954
|
+
// half-updated quote committed (#2336).
|
|
4955
|
+
await withAtomicFlush(
|
|
4930
4956
|
em,
|
|
4931
|
-
|
|
4932
|
-
|
|
4933
|
-
|
|
4934
|
-
|
|
4935
|
-
|
|
4936
|
-
|
|
4937
|
-
|
|
4938
|
-
|
|
4939
|
-
|
|
4940
|
-
|
|
4941
|
-
|
|
4942
|
-
|
|
4943
|
-
|
|
4944
|
-
|
|
4945
|
-
|
|
4946
|
-
|
|
4947
|
-
|
|
4948
|
-
|
|
4949
|
-
|
|
4950
|
-
|
|
4951
|
-
|
|
4952
|
-
|
|
4953
|
-
|
|
4954
|
-
|
|
4955
|
-
|
|
4956
|
-
|
|
4957
|
-
|
|
4958
|
-
|
|
4959
|
-
|
|
4960
|
-
|
|
4961
|
-
|
|
4962
|
-
|
|
4963
|
-
|
|
4964
|
-
|
|
4965
|
-
|
|
4966
|
-
|
|
4967
|
-
|
|
4968
|
-
|
|
4969
|
-
|
|
4970
|
-
|
|
4971
|
-
|
|
4972
|
-
|
|
4973
|
-
|
|
4974
|
-
|
|
4975
|
-
|
|
4976
|
-
|
|
4977
|
-
|
|
4978
|
-
|
|
4979
|
-
|
|
4980
|
-
|
|
4981
|
-
|
|
4982
|
-
|
|
4983
|
-
|
|
4984
|
-
|
|
4985
|
-
|
|
4986
|
-
|
|
4987
|
-
|
|
4957
|
+
[
|
|
4958
|
+
async () => {
|
|
4959
|
+
await applyDocumentUpdate({
|
|
4960
|
+
kind: "quote",
|
|
4961
|
+
entity: quote,
|
|
4962
|
+
input: parsed,
|
|
4963
|
+
em,
|
|
4964
|
+
});
|
|
4965
|
+
if (shouldInvalidateSentToken) {
|
|
4966
|
+
quote.status = "draft";
|
|
4967
|
+
quote.statusEntryId = await resolveStatusEntryIdByValue(em, {
|
|
4968
|
+
tenantId: quote.tenantId,
|
|
4969
|
+
organizationId: quote.organizationId,
|
|
4970
|
+
value: "draft",
|
|
4971
|
+
});
|
|
4972
|
+
}
|
|
4973
|
+
if (shouldRecalculateTotals) {
|
|
4974
|
+
const [existingLines, adjustments] = await Promise.all([
|
|
4975
|
+
em.find(
|
|
4976
|
+
SalesQuoteLine,
|
|
4977
|
+
{ quote },
|
|
4978
|
+
{ orderBy: { lineNumber: "asc" } },
|
|
4979
|
+
),
|
|
4980
|
+
em.find(
|
|
4981
|
+
SalesQuoteAdjustment,
|
|
4982
|
+
{ quote },
|
|
4983
|
+
{ orderBy: { position: "asc" } },
|
|
4984
|
+
),
|
|
4985
|
+
]);
|
|
4986
|
+
const lineSnapshots = existingLines.map(mapQuoteLineEntityToSnapshot);
|
|
4987
|
+
const calcLines = lineSnapshots.map((line, index) =>
|
|
4988
|
+
createLineSnapshotFromInput(
|
|
4989
|
+
{
|
|
4990
|
+
...line,
|
|
4991
|
+
organizationId: quote.organizationId,
|
|
4992
|
+
tenantId: quote.tenantId,
|
|
4993
|
+
quoteId: quote.id,
|
|
4994
|
+
lineNumber: line.lineNumber ?? index + 1,
|
|
4995
|
+
statusEntryId: (line as any).statusEntryId ?? null,
|
|
4996
|
+
catalogSnapshot: (line as any).catalogSnapshot ?? null,
|
|
4997
|
+
promotionSnapshot: (line as any).promotionSnapshot ?? null,
|
|
4998
|
+
},
|
|
4999
|
+
line.lineNumber ?? index + 1,
|
|
5000
|
+
),
|
|
5001
|
+
);
|
|
5002
|
+
const adjustmentDrafts = adjustments.map(mapQuoteAdjustmentToDraft);
|
|
5003
|
+
const salesCalculationService =
|
|
5004
|
+
ctx.container.resolve<SalesCalculationService>(
|
|
5005
|
+
"salesCalculationService",
|
|
5006
|
+
);
|
|
5007
|
+
const calculationContext = buildCalculationContext({
|
|
5008
|
+
tenantId: quote.tenantId,
|
|
5009
|
+
organizationId: quote.organizationId,
|
|
5010
|
+
currencyCode: quote.currencyCode,
|
|
5011
|
+
shippingSnapshot: quote.shippingMethodSnapshot,
|
|
5012
|
+
paymentSnapshot: quote.paymentMethodSnapshot,
|
|
5013
|
+
shippingMethodId: quote.shippingMethodId ?? null,
|
|
5014
|
+
paymentMethodId: quote.paymentMethodId ?? null,
|
|
5015
|
+
shippingMethodCode: quote.shippingMethodCode ?? null,
|
|
5016
|
+
paymentMethodCode: quote.paymentMethodCode ?? null,
|
|
5017
|
+
});
|
|
5018
|
+
const calculation =
|
|
5019
|
+
await salesCalculationService.calculateDocumentTotals({
|
|
5020
|
+
documentKind: "quote",
|
|
5021
|
+
lines: calcLines,
|
|
5022
|
+
adjustments: adjustmentDrafts,
|
|
5023
|
+
context: calculationContext,
|
|
5024
|
+
});
|
|
5025
|
+
const adjustmentInputs = adjustmentDrafts.map((adj, index) => ({
|
|
5026
|
+
organizationId: quote.organizationId,
|
|
5027
|
+
tenantId: quote.tenantId,
|
|
5028
|
+
quoteId: quote.id,
|
|
5029
|
+
scope: adj.scope ?? "order",
|
|
5030
|
+
kind: adj.kind ?? "custom",
|
|
5031
|
+
code: adj.code ?? undefined,
|
|
5032
|
+
label: adj.label ?? undefined,
|
|
5033
|
+
calculatorKey: adj.calculatorKey ?? undefined,
|
|
5034
|
+
promotionId: adj.promotionId ?? undefined,
|
|
5035
|
+
rate: adj.rate ?? undefined,
|
|
5036
|
+
amountNet: adj.amountNet ?? undefined,
|
|
5037
|
+
amountGross: adj.amountGross ?? undefined,
|
|
5038
|
+
currencyCode: adj.currencyCode ?? quote.currencyCode,
|
|
5039
|
+
metadata: adj.metadata ?? undefined,
|
|
5040
|
+
position: adj.position ?? index,
|
|
5041
|
+
}));
|
|
5042
|
+
await replaceQuoteAdjustments(
|
|
5043
|
+
em,
|
|
5044
|
+
quote,
|
|
5045
|
+
calculation,
|
|
5046
|
+
adjustmentInputs,
|
|
5047
|
+
);
|
|
5048
|
+
applyQuoteTotals(quote, calculation.totals, calculation.lines.length);
|
|
5049
|
+
let eventBus: EventBus | null = null;
|
|
5050
|
+
try {
|
|
5051
|
+
eventBus = ctx.container.resolve("eventBus") as EventBus;
|
|
5052
|
+
} catch {
|
|
5053
|
+
eventBus = null;
|
|
5054
|
+
}
|
|
5055
|
+
await emitTotalsCalculated(eventBus, {
|
|
5056
|
+
documentKind: "quote",
|
|
5057
|
+
documentId: quote.id,
|
|
5058
|
+
organizationId: quote.organizationId,
|
|
5059
|
+
tenantId: quote.tenantId,
|
|
5060
|
+
customerId: quote.customerEntityId ?? null,
|
|
5061
|
+
totals: calculation.totals,
|
|
5062
|
+
lineCount: calculation.lines.length,
|
|
5063
|
+
});
|
|
5064
|
+
}
|
|
5065
|
+
quote.updatedAt = new Date();
|
|
4988
5066
|
},
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
|
|
4992
|
-
tenantId: quote.tenantId,
|
|
4993
|
-
quoteId: quote.id,
|
|
4994
|
-
scope: adj.scope ?? "order",
|
|
4995
|
-
kind: adj.kind ?? "custom",
|
|
4996
|
-
code: adj.code ?? undefined,
|
|
4997
|
-
label: adj.label ?? undefined,
|
|
4998
|
-
calculatorKey: adj.calculatorKey ?? undefined,
|
|
4999
|
-
promotionId: adj.promotionId ?? undefined,
|
|
5000
|
-
rate: adj.rate ?? undefined,
|
|
5001
|
-
amountNet: adj.amountNet ?? undefined,
|
|
5002
|
-
amountGross: adj.amountGross ?? undefined,
|
|
5003
|
-
currencyCode: adj.currencyCode ?? quote.currencyCode,
|
|
5004
|
-
metadata: adj.metadata ?? undefined,
|
|
5005
|
-
position: adj.position ?? index,
|
|
5006
|
-
}));
|
|
5007
|
-
await replaceQuoteAdjustments(em, quote, calculation, adjustmentInputs);
|
|
5008
|
-
applyQuoteTotals(quote, calculation.totals, calculation.lines.length);
|
|
5009
|
-
let eventBus: EventBus | null = null;
|
|
5010
|
-
try {
|
|
5011
|
-
eventBus = ctx.container.resolve("eventBus") as EventBus;
|
|
5012
|
-
} catch {
|
|
5013
|
-
eventBus = null;
|
|
5014
|
-
}
|
|
5015
|
-
await emitTotalsCalculated(eventBus, {
|
|
5016
|
-
documentKind: "quote",
|
|
5017
|
-
documentId: quote.id,
|
|
5018
|
-
organizationId: quote.organizationId,
|
|
5019
|
-
tenantId: quote.tenantId,
|
|
5020
|
-
customerId: quote.customerEntityId ?? null,
|
|
5021
|
-
totals: calculation.totals,
|
|
5022
|
-
lineCount: calculation.lines.length,
|
|
5023
|
-
});
|
|
5024
|
-
}
|
|
5025
|
-
quote.updatedAt = new Date();
|
|
5026
|
-
await em.flush();
|
|
5067
|
+
],
|
|
5068
|
+
{ transaction: true },
|
|
5069
|
+
);
|
|
5027
5070
|
const resourceKind =
|
|
5028
5071
|
deriveResourceFromCommandId(updateQuoteCommand.id) ?? "sales.quote";
|
|
5029
5072
|
await invalidateCrudCache(
|
|
@@ -5126,106 +5169,124 @@ const updateOrderCommand: CommandHandler<
|
|
|
5126
5169
|
parsed.paymentMethodSnapshot !== undefined ||
|
|
5127
5170
|
parsed.paymentMethodCode !== undefined ||
|
|
5128
5171
|
parsed.currencyCode !== undefined;
|
|
5129
|
-
|
|
5130
|
-
|
|
5131
|
-
|
|
5132
|
-
|
|
5172
|
+
// Apply the scalar update, totals recalc, and status-change note atomically.
|
|
5173
|
+
// applyDocumentUpdate/replace*/setRecordCustomFields flush mid-build, so
|
|
5174
|
+
// without a transaction a later failure would leave a half-updated order
|
|
5175
|
+
// committed (#2336).
|
|
5176
|
+
await withAtomicFlush(
|
|
5133
5177
|
em,
|
|
5134
|
-
|
|
5135
|
-
|
|
5136
|
-
|
|
5137
|
-
|
|
5138
|
-
|
|
5139
|
-
|
|
5140
|
-
|
|
5141
|
-
|
|
5142
|
-
|
|
5143
|
-
|
|
5144
|
-
|
|
5145
|
-
|
|
5146
|
-
|
|
5147
|
-
|
|
5148
|
-
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
|
|
5152
|
-
|
|
5153
|
-
|
|
5154
|
-
|
|
5155
|
-
|
|
5156
|
-
|
|
5157
|
-
|
|
5158
|
-
|
|
5159
|
-
|
|
5160
|
-
|
|
5161
|
-
|
|
5162
|
-
|
|
5163
|
-
|
|
5164
|
-
|
|
5165
|
-
|
|
5166
|
-
|
|
5167
|
-
|
|
5168
|
-
|
|
5169
|
-
|
|
5170
|
-
|
|
5171
|
-
|
|
5172
|
-
|
|
5173
|
-
|
|
5174
|
-
|
|
5175
|
-
|
|
5176
|
-
|
|
5177
|
-
|
|
5178
|
-
|
|
5179
|
-
|
|
5180
|
-
|
|
5181
|
-
|
|
5182
|
-
|
|
5183
|
-
|
|
5178
|
+
[
|
|
5179
|
+
async () => {
|
|
5180
|
+
await applyDocumentUpdate({
|
|
5181
|
+
kind: "order",
|
|
5182
|
+
entity: order,
|
|
5183
|
+
input: parsed,
|
|
5184
|
+
em,
|
|
5185
|
+
});
|
|
5186
|
+
if (shouldRecalculateTotals) {
|
|
5187
|
+
const [existingLines, adjustments] = await Promise.all([
|
|
5188
|
+
em.find(
|
|
5189
|
+
SalesOrderLine,
|
|
5190
|
+
{ order },
|
|
5191
|
+
{ orderBy: { lineNumber: "asc" } },
|
|
5192
|
+
),
|
|
5193
|
+
em.find(
|
|
5194
|
+
SalesOrderAdjustment,
|
|
5195
|
+
{ order },
|
|
5196
|
+
{ orderBy: { position: "asc" } },
|
|
5197
|
+
),
|
|
5198
|
+
]);
|
|
5199
|
+
const lineSnapshots = existingLines.map(mapOrderLineEntityToSnapshot);
|
|
5200
|
+
const calcLines = lineSnapshots.map((line, index) =>
|
|
5201
|
+
createLineSnapshotFromInput(
|
|
5202
|
+
{
|
|
5203
|
+
...line,
|
|
5204
|
+
organizationId: order.organizationId,
|
|
5205
|
+
tenantId: order.tenantId,
|
|
5206
|
+
orderId: order.id,
|
|
5207
|
+
lineNumber: line.lineNumber ?? index + 1,
|
|
5208
|
+
statusEntryId: (line as any).statusEntryId ?? null,
|
|
5209
|
+
catalogSnapshot: (line as any).catalogSnapshot ?? null,
|
|
5210
|
+
promotionSnapshot: (line as any).promotionSnapshot ?? null,
|
|
5211
|
+
},
|
|
5212
|
+
line.lineNumber ?? index + 1,
|
|
5213
|
+
),
|
|
5214
|
+
);
|
|
5215
|
+
const adjustmentDrafts = adjustments.map(mapOrderAdjustmentToDraft);
|
|
5216
|
+
const salesCalculationService =
|
|
5217
|
+
ctx.container.resolve<SalesCalculationService>(
|
|
5218
|
+
"salesCalculationService",
|
|
5219
|
+
);
|
|
5220
|
+
const calculationContext = buildCalculationContext({
|
|
5221
|
+
tenantId: order.tenantId,
|
|
5222
|
+
organizationId: order.organizationId,
|
|
5223
|
+
currencyCode: order.currencyCode,
|
|
5224
|
+
shippingSnapshot: order.shippingMethodSnapshot,
|
|
5225
|
+
paymentSnapshot: order.paymentMethodSnapshot,
|
|
5226
|
+
shippingMethodId: order.shippingMethodId ?? null,
|
|
5227
|
+
paymentMethodId: order.paymentMethodId ?? null,
|
|
5228
|
+
shippingMethodCode: order.shippingMethodCode ?? null,
|
|
5229
|
+
paymentMethodCode: order.paymentMethodCode ?? null,
|
|
5230
|
+
});
|
|
5231
|
+
const calculation =
|
|
5232
|
+
await salesCalculationService.calculateDocumentTotals({
|
|
5233
|
+
documentKind: "order",
|
|
5234
|
+
lines: calcLines,
|
|
5235
|
+
adjustments: adjustmentDrafts,
|
|
5236
|
+
context: calculationContext,
|
|
5237
|
+
existingTotals: resolveExistingPaymentTotals(order),
|
|
5238
|
+
});
|
|
5239
|
+
const adjustmentInputs = adjustmentDrafts.map((adj, index) => ({
|
|
5240
|
+
organizationId: order.organizationId,
|
|
5241
|
+
tenantId: order.tenantId,
|
|
5242
|
+
orderId: order.id,
|
|
5243
|
+
scope: adj.scope ?? "order",
|
|
5244
|
+
kind: adj.kind ?? "custom",
|
|
5245
|
+
code: adj.code ?? undefined,
|
|
5246
|
+
label: adj.label ?? undefined,
|
|
5247
|
+
calculatorKey: adj.calculatorKey ?? undefined,
|
|
5248
|
+
promotionId: adj.promotionId ?? undefined,
|
|
5249
|
+
rate: adj.rate ?? undefined,
|
|
5250
|
+
amountNet: adj.amountNet ?? undefined,
|
|
5251
|
+
amountGross: adj.amountGross ?? undefined,
|
|
5252
|
+
currencyCode: adj.currencyCode ?? order.currencyCode,
|
|
5253
|
+
metadata: adj.metadata ?? undefined,
|
|
5254
|
+
position: adj.position ?? index,
|
|
5255
|
+
}));
|
|
5256
|
+
await replaceOrderAdjustments(
|
|
5257
|
+
em,
|
|
5258
|
+
order,
|
|
5259
|
+
calculation,
|
|
5260
|
+
adjustmentInputs,
|
|
5261
|
+
);
|
|
5262
|
+
applyOrderTotals(order, calculation.totals, calculation.lines.length);
|
|
5263
|
+
let eventBus: EventBus | null = null;
|
|
5264
|
+
try {
|
|
5265
|
+
eventBus = ctx.container.resolve("eventBus") as EventBus;
|
|
5266
|
+
} catch {
|
|
5267
|
+
eventBus = null;
|
|
5268
|
+
}
|
|
5269
|
+
await emitTotalsCalculated(eventBus, {
|
|
5270
|
+
documentKind: "order",
|
|
5271
|
+
documentId: order.id,
|
|
5272
|
+
organizationId: order.organizationId,
|
|
5273
|
+
tenantId: order.tenantId,
|
|
5274
|
+
customerId: order.customerEntityId ?? null,
|
|
5275
|
+
totals: calculation.totals,
|
|
5276
|
+
lineCount: calculation.lines.length,
|
|
5277
|
+
});
|
|
5278
|
+
}
|
|
5279
|
+
statusChangeNote = await appendOrderStatusChangeNote({
|
|
5280
|
+
em,
|
|
5281
|
+
order,
|
|
5282
|
+
previousStatus,
|
|
5283
|
+
auth: ctx.auth ?? null,
|
|
5284
|
+
});
|
|
5285
|
+
order.updatedAt = new Date();
|
|
5184
5286
|
},
|
|
5185
|
-
|
|
5186
|
-
|
|
5187
|
-
|
|
5188
|
-
tenantId: order.tenantId,
|
|
5189
|
-
orderId: order.id,
|
|
5190
|
-
scope: adj.scope ?? "order",
|
|
5191
|
-
kind: adj.kind ?? "custom",
|
|
5192
|
-
code: adj.code ?? undefined,
|
|
5193
|
-
label: adj.label ?? undefined,
|
|
5194
|
-
calculatorKey: adj.calculatorKey ?? undefined,
|
|
5195
|
-
promotionId: adj.promotionId ?? undefined,
|
|
5196
|
-
rate: adj.rate ?? undefined,
|
|
5197
|
-
amountNet: adj.amountNet ?? undefined,
|
|
5198
|
-
amountGross: adj.amountGross ?? undefined,
|
|
5199
|
-
currencyCode: adj.currencyCode ?? order.currencyCode,
|
|
5200
|
-
metadata: adj.metadata ?? undefined,
|
|
5201
|
-
position: adj.position ?? index,
|
|
5202
|
-
}));
|
|
5203
|
-
await replaceOrderAdjustments(em, order, calculation, adjustmentInputs);
|
|
5204
|
-
applyOrderTotals(order, calculation.totals, calculation.lines.length);
|
|
5205
|
-
let eventBus: EventBus | null = null;
|
|
5206
|
-
try {
|
|
5207
|
-
eventBus = ctx.container.resolve("eventBus") as EventBus;
|
|
5208
|
-
} catch {
|
|
5209
|
-
eventBus = null;
|
|
5210
|
-
}
|
|
5211
|
-
await emitTotalsCalculated(eventBus, {
|
|
5212
|
-
documentKind: "order",
|
|
5213
|
-
documentId: order.id,
|
|
5214
|
-
organizationId: order.organizationId,
|
|
5215
|
-
tenantId: order.tenantId,
|
|
5216
|
-
customerId: order.customerEntityId ?? null,
|
|
5217
|
-
totals: calculation.totals,
|
|
5218
|
-
lineCount: calculation.lines.length,
|
|
5219
|
-
});
|
|
5220
|
-
}
|
|
5221
|
-
statusChangeNote = await appendOrderStatusChangeNote({
|
|
5222
|
-
em,
|
|
5223
|
-
order,
|
|
5224
|
-
previousStatus,
|
|
5225
|
-
auth: ctx.auth ?? null,
|
|
5226
|
-
});
|
|
5227
|
-
order.updatedAt = new Date();
|
|
5228
|
-
await em.flush();
|
|
5287
|
+
],
|
|
5288
|
+
{ transaction: true },
|
|
5289
|
+
);
|
|
5229
5290
|
if (statusChangeNote) {
|
|
5230
5291
|
const dataEngine = ctx.container.resolve("dataEngine");
|
|
5231
5292
|
await emitCrudSideEffects({
|
|
@@ -5481,8 +5542,6 @@ const createOrderCommand: CommandHandler<
|
|
|
5481
5542
|
createdAt: new Date(),
|
|
5482
5543
|
updatedAt: new Date(),
|
|
5483
5544
|
});
|
|
5484
|
-
em.persist(order);
|
|
5485
|
-
|
|
5486
5545
|
const lineInputs = (parsed.lines ?? []).map((line, index) =>
|
|
5487
5546
|
orderLineCreateSchema.parse({
|
|
5488
5547
|
...line,
|
|
@@ -5552,32 +5611,48 @@ const createOrderCommand: CommandHandler<
|
|
|
5552
5611
|
existingTotals: resolveExistingPaymentTotals(order),
|
|
5553
5612
|
});
|
|
5554
5613
|
|
|
5555
|
-
await replaceOrderLines(em, order, calculation, normalizedLineInputs);
|
|
5556
|
-
await replaceOrderAdjustments(em, order, calculation, adjustmentInputs);
|
|
5557
|
-
applyOrderTotals(order, calculation.totals, calculation.lines.length);
|
|
5558
5614
|
let eventBus: EventBus | null = null;
|
|
5559
5615
|
try {
|
|
5560
5616
|
eventBus = ctx.container.resolve("eventBus") as EventBus;
|
|
5561
5617
|
} catch {
|
|
5562
5618
|
eventBus = null;
|
|
5563
5619
|
}
|
|
5564
|
-
|
|
5565
|
-
|
|
5566
|
-
|
|
5567
|
-
|
|
5568
|
-
|
|
5569
|
-
|
|
5570
|
-
|
|
5571
|
-
|
|
5572
|
-
|
|
5573
|
-
|
|
5574
|
-
|
|
5575
|
-
|
|
5576
|
-
|
|
5577
|
-
|
|
5578
|
-
|
|
5579
|
-
|
|
5580
|
-
|
|
5620
|
+
// Persist the order header, lines, adjustments, totals and tags atomically.
|
|
5621
|
+
// replace*/setRecordCustomFields flush mid-build, so without a transaction a
|
|
5622
|
+
// later failure (e.g. tag sync) would leave a half-built order committed (#2336).
|
|
5623
|
+
await withAtomicFlush(
|
|
5624
|
+
em,
|
|
5625
|
+
[
|
|
5626
|
+
async () => {
|
|
5627
|
+
em.persist(order);
|
|
5628
|
+
await replaceOrderLines(em, order, calculation, normalizedLineInputs);
|
|
5629
|
+
await replaceOrderAdjustments(
|
|
5630
|
+
em,
|
|
5631
|
+
order,
|
|
5632
|
+
calculation,
|
|
5633
|
+
adjustmentInputs,
|
|
5634
|
+
);
|
|
5635
|
+
applyOrderTotals(order, calculation.totals, calculation.lines.length);
|
|
5636
|
+
await emitTotalsCalculated(eventBus, {
|
|
5637
|
+
documentKind: "order",
|
|
5638
|
+
documentId: order.id,
|
|
5639
|
+
organizationId: order.organizationId,
|
|
5640
|
+
tenantId: order.tenantId,
|
|
5641
|
+
customerId: order.customerEntityId ?? null,
|
|
5642
|
+
totals: calculation.totals,
|
|
5643
|
+
lineCount: calculation.lines.length,
|
|
5644
|
+
});
|
|
5645
|
+
await syncSalesDocumentTags(em, {
|
|
5646
|
+
documentId: order.id,
|
|
5647
|
+
kind: "order",
|
|
5648
|
+
organizationId: order.organizationId,
|
|
5649
|
+
tenantId: order.tenantId,
|
|
5650
|
+
tagIds: parsed.tags,
|
|
5651
|
+
});
|
|
5652
|
+
},
|
|
5653
|
+
],
|
|
5654
|
+
{ transaction: true },
|
|
5655
|
+
);
|
|
5581
5656
|
|
|
5582
5657
|
// Create notification for users with sales.orders.manage feature
|
|
5583
5658
|
try {
|
|
@@ -5751,30 +5826,39 @@ const deleteOrderCommand: CommandHandler<
|
|
|
5751
5826
|
em.find(SalesOrderAdjustment, { order: order.id }),
|
|
5752
5827
|
em.find(SalesOrderLine, { order: order.id }),
|
|
5753
5828
|
]);
|
|
5754
|
-
|
|
5755
|
-
|
|
5756
|
-
|
|
5757
|
-
|
|
5758
|
-
|
|
5759
|
-
|
|
5760
|
-
|
|
5761
|
-
|
|
5762
|
-
|
|
5763
|
-
|
|
5764
|
-
|
|
5765
|
-
|
|
5766
|
-
|
|
5767
|
-
|
|
5768
|
-
|
|
5769
|
-
|
|
5770
|
-
|
|
5771
|
-
|
|
5772
|
-
|
|
5773
|
-
|
|
5774
|
-
|
|
5775
|
-
|
|
5776
|
-
|
|
5777
|
-
|
|
5829
|
+
// Delete the order and its cascade atomically so a mid-cascade failure
|
|
5830
|
+
// cannot leave a partially-deleted order committed (#2336).
|
|
5831
|
+
await withAtomicFlush(
|
|
5832
|
+
em,
|
|
5833
|
+
[
|
|
5834
|
+
async () => {
|
|
5835
|
+
if (shipmentIds.length) {
|
|
5836
|
+
await em.nativeDelete(SalesShipmentItem, {
|
|
5837
|
+
shipment: { $in: shipmentIds },
|
|
5838
|
+
});
|
|
5839
|
+
await em.nativeDelete(SalesShipment, { id: { $in: shipmentIds } });
|
|
5840
|
+
}
|
|
5841
|
+
await em.nativeDelete(SalesPaymentAllocation, { order: order.id });
|
|
5842
|
+
await em.nativeDelete(SalesPayment, { order: order.id });
|
|
5843
|
+
await em.nativeDelete(SalesDocumentAddress, {
|
|
5844
|
+
documentId: order.id,
|
|
5845
|
+
documentKind: "order",
|
|
5846
|
+
});
|
|
5847
|
+
await em.nativeDelete(SalesNote, {
|
|
5848
|
+
contextType: "order",
|
|
5849
|
+
contextId: order.id,
|
|
5850
|
+
});
|
|
5851
|
+
await em.nativeDelete(SalesDocumentTagAssignment, {
|
|
5852
|
+
documentId: order.id,
|
|
5853
|
+
documentKind: "order",
|
|
5854
|
+
});
|
|
5855
|
+
await em.nativeDelete(SalesOrderAdjustment, { order: order.id });
|
|
5856
|
+
await em.nativeDelete(SalesOrderLine, { order: order.id });
|
|
5857
|
+
em.remove(order);
|
|
5858
|
+
},
|
|
5859
|
+
],
|
|
5860
|
+
{ transaction: true },
|
|
5861
|
+
);
|
|
5778
5862
|
const dataEngine = ctx.container.resolve<DataEngine>("dataEngine");
|
|
5779
5863
|
await Promise.all([
|
|
5780
5864
|
queueDeletionSideEffects(dataEngine, order, E.sales.sales_order),
|
|
@@ -6581,30 +6665,39 @@ const orderLineUpsertCommand: CommandHandler<
|
|
|
6581
6665
|
context: calculationContext,
|
|
6582
6666
|
existingTotals: resolveExistingPaymentTotals(order),
|
|
6583
6667
|
});
|
|
6584
|
-
await applyOrderLineResults({
|
|
6585
|
-
em,
|
|
6586
|
-
order,
|
|
6587
|
-
calculation,
|
|
6588
|
-
sourceLines: sourceInputs,
|
|
6589
|
-
existingLines,
|
|
6590
|
-
});
|
|
6591
|
-
applyOrderTotals(order, calculation.totals, calculation.lines.length);
|
|
6592
6668
|
let eventBus: EventBus | null = null;
|
|
6593
6669
|
try {
|
|
6594
6670
|
eventBus = ctx.container.resolve("eventBus") as EventBus;
|
|
6595
6671
|
} catch {
|
|
6596
6672
|
eventBus = null;
|
|
6597
6673
|
}
|
|
6598
|
-
|
|
6599
|
-
|
|
6600
|
-
|
|
6601
|
-
|
|
6602
|
-
|
|
6603
|
-
|
|
6604
|
-
|
|
6605
|
-
|
|
6606
|
-
|
|
6607
|
-
|
|
6674
|
+
// Persist the line changes and recalculated totals atomically so a
|
|
6675
|
+
// mid-build failure cannot leave a half-updated order committed (#2336).
|
|
6676
|
+
await withAtomicFlush(
|
|
6677
|
+
em,
|
|
6678
|
+
[
|
|
6679
|
+
async () => {
|
|
6680
|
+
await applyOrderLineResults({
|
|
6681
|
+
em,
|
|
6682
|
+
order,
|
|
6683
|
+
calculation,
|
|
6684
|
+
sourceLines: sourceInputs,
|
|
6685
|
+
existingLines,
|
|
6686
|
+
});
|
|
6687
|
+
applyOrderTotals(order, calculation.totals, calculation.lines.length);
|
|
6688
|
+
await emitTotalsCalculated(eventBus, {
|
|
6689
|
+
documentKind: "order",
|
|
6690
|
+
documentId: order.id,
|
|
6691
|
+
organizationId: order.organizationId,
|
|
6692
|
+
tenantId: order.tenantId,
|
|
6693
|
+
customerId: order.customerEntityId ?? null,
|
|
6694
|
+
totals: calculation.totals,
|
|
6695
|
+
lineCount: calculation.lines.length,
|
|
6696
|
+
});
|
|
6697
|
+
},
|
|
6698
|
+
],
|
|
6699
|
+
{ transaction: true },
|
|
6700
|
+
);
|
|
6608
6701
|
return { orderId: order.id, lineId };
|
|
6609
6702
|
},
|
|
6610
6703
|
captureAfter: async (_input, result, ctx) => {
|
|
@@ -6749,30 +6842,39 @@ const orderLineDeleteCommand: CommandHandler<
|
|
|
6749
6842
|
context: calculationContext,
|
|
6750
6843
|
existingTotals: resolveExistingPaymentTotals(order),
|
|
6751
6844
|
});
|
|
6752
|
-
await applyOrderLineResults({
|
|
6753
|
-
em,
|
|
6754
|
-
order,
|
|
6755
|
-
calculation,
|
|
6756
|
-
sourceLines: sourceInputs,
|
|
6757
|
-
existingLines,
|
|
6758
|
-
});
|
|
6759
|
-
applyOrderTotals(order, calculation.totals, calculation.lines.length);
|
|
6760
6845
|
let eventBus: EventBus | null = null;
|
|
6761
6846
|
try {
|
|
6762
6847
|
eventBus = ctx.container.resolve("eventBus") as EventBus;
|
|
6763
6848
|
} catch {
|
|
6764
6849
|
eventBus = null;
|
|
6765
6850
|
}
|
|
6766
|
-
|
|
6767
|
-
|
|
6768
|
-
|
|
6769
|
-
|
|
6770
|
-
|
|
6771
|
-
|
|
6772
|
-
|
|
6773
|
-
|
|
6774
|
-
|
|
6775
|
-
|
|
6851
|
+
// Persist the line removal and recalculated totals atomically so a
|
|
6852
|
+
// mid-build failure cannot leave a half-updated order committed (#2336).
|
|
6853
|
+
await withAtomicFlush(
|
|
6854
|
+
em,
|
|
6855
|
+
[
|
|
6856
|
+
async () => {
|
|
6857
|
+
await applyOrderLineResults({
|
|
6858
|
+
em,
|
|
6859
|
+
order,
|
|
6860
|
+
calculation,
|
|
6861
|
+
sourceLines: sourceInputs,
|
|
6862
|
+
existingLines,
|
|
6863
|
+
});
|
|
6864
|
+
applyOrderTotals(order, calculation.totals, calculation.lines.length);
|
|
6865
|
+
await emitTotalsCalculated(eventBus, {
|
|
6866
|
+
documentKind: "order",
|
|
6867
|
+
documentId: order.id,
|
|
6868
|
+
organizationId: order.organizationId,
|
|
6869
|
+
tenantId: order.tenantId,
|
|
6870
|
+
customerId: order.customerEntityId ?? null,
|
|
6871
|
+
totals: calculation.totals,
|
|
6872
|
+
lineCount: calculation.lines.length,
|
|
6873
|
+
});
|
|
6874
|
+
},
|
|
6875
|
+
],
|
|
6876
|
+
{ transaction: true },
|
|
6877
|
+
);
|
|
6776
6878
|
return { orderId: order.id, lineId: parsed.id };
|
|
6777
6879
|
},
|
|
6778
6880
|
captureAfter: async (_input, result, ctx) => {
|
|
@@ -7050,30 +7152,39 @@ const quoteLineUpsertCommand: CommandHandler<
|
|
|
7050
7152
|
adjustments: adjustmentDrafts,
|
|
7051
7153
|
context: calculationContext,
|
|
7052
7154
|
});
|
|
7053
|
-
await applyQuoteLineResults({
|
|
7054
|
-
em,
|
|
7055
|
-
quote,
|
|
7056
|
-
calculation,
|
|
7057
|
-
sourceLines: sourceInputs,
|
|
7058
|
-
existingLines,
|
|
7059
|
-
});
|
|
7060
|
-
applyQuoteTotals(quote, calculation.totals, calculation.lines.length);
|
|
7061
7155
|
let eventBus: EventBus | null = null;
|
|
7062
7156
|
try {
|
|
7063
7157
|
eventBus = ctx.container.resolve("eventBus") as EventBus;
|
|
7064
7158
|
} catch {
|
|
7065
7159
|
eventBus = null;
|
|
7066
7160
|
}
|
|
7067
|
-
|
|
7068
|
-
|
|
7069
|
-
|
|
7070
|
-
|
|
7071
|
-
|
|
7072
|
-
|
|
7073
|
-
|
|
7074
|
-
|
|
7075
|
-
|
|
7076
|
-
|
|
7161
|
+
// Persist the line changes and recalculated totals atomically so a
|
|
7162
|
+
// mid-build failure cannot leave a half-updated quote committed (#2336).
|
|
7163
|
+
await withAtomicFlush(
|
|
7164
|
+
em,
|
|
7165
|
+
[
|
|
7166
|
+
async () => {
|
|
7167
|
+
await applyQuoteLineResults({
|
|
7168
|
+
em,
|
|
7169
|
+
quote,
|
|
7170
|
+
calculation,
|
|
7171
|
+
sourceLines: sourceInputs,
|
|
7172
|
+
existingLines,
|
|
7173
|
+
});
|
|
7174
|
+
applyQuoteTotals(quote, calculation.totals, calculation.lines.length);
|
|
7175
|
+
await emitTotalsCalculated(eventBus, {
|
|
7176
|
+
documentKind: "quote",
|
|
7177
|
+
documentId: quote.id,
|
|
7178
|
+
organizationId: quote.organizationId,
|
|
7179
|
+
tenantId: quote.tenantId,
|
|
7180
|
+
customerId: quote.customerEntityId ?? null,
|
|
7181
|
+
totals: calculation.totals,
|
|
7182
|
+
lineCount: calculation.lines.length,
|
|
7183
|
+
});
|
|
7184
|
+
},
|
|
7185
|
+
],
|
|
7186
|
+
{ transaction: true },
|
|
7187
|
+
);
|
|
7077
7188
|
return { quoteId: quote.id, lineId };
|
|
7078
7189
|
},
|
|
7079
7190
|
captureAfter: async (_input, result, ctx) => {
|
|
@@ -7194,30 +7305,39 @@ const quoteLineDeleteCommand: CommandHandler<
|
|
|
7194
7305
|
adjustments: adjustmentDrafts,
|
|
7195
7306
|
context: calculationContext,
|
|
7196
7307
|
});
|
|
7197
|
-
await applyQuoteLineResults({
|
|
7198
|
-
em,
|
|
7199
|
-
quote,
|
|
7200
|
-
calculation,
|
|
7201
|
-
sourceLines: sourceInputs,
|
|
7202
|
-
existingLines,
|
|
7203
|
-
});
|
|
7204
|
-
applyQuoteTotals(quote, calculation.totals, calculation.lines.length);
|
|
7205
7308
|
let eventBus: EventBus | null = null;
|
|
7206
7309
|
try {
|
|
7207
7310
|
eventBus = ctx.container.resolve("eventBus") as EventBus;
|
|
7208
7311
|
} catch {
|
|
7209
7312
|
eventBus = null;
|
|
7210
7313
|
}
|
|
7211
|
-
|
|
7212
|
-
|
|
7213
|
-
|
|
7214
|
-
|
|
7215
|
-
|
|
7216
|
-
|
|
7217
|
-
|
|
7218
|
-
|
|
7219
|
-
|
|
7220
|
-
|
|
7314
|
+
// Persist the line removal and recalculated totals atomically so a
|
|
7315
|
+
// mid-build failure cannot leave a half-updated quote committed (#2336).
|
|
7316
|
+
await withAtomicFlush(
|
|
7317
|
+
em,
|
|
7318
|
+
[
|
|
7319
|
+
async () => {
|
|
7320
|
+
await applyQuoteLineResults({
|
|
7321
|
+
em,
|
|
7322
|
+
quote,
|
|
7323
|
+
calculation,
|
|
7324
|
+
sourceLines: sourceInputs,
|
|
7325
|
+
existingLines,
|
|
7326
|
+
});
|
|
7327
|
+
applyQuoteTotals(quote, calculation.totals, calculation.lines.length);
|
|
7328
|
+
await emitTotalsCalculated(eventBus, {
|
|
7329
|
+
documentKind: "quote",
|
|
7330
|
+
documentId: quote.id,
|
|
7331
|
+
organizationId: quote.organizationId,
|
|
7332
|
+
tenantId: quote.tenantId,
|
|
7333
|
+
customerId: quote.customerEntityId ?? null,
|
|
7334
|
+
totals: calculation.totals,
|
|
7335
|
+
lineCount: calculation.lines.length,
|
|
7336
|
+
});
|
|
7337
|
+
},
|
|
7338
|
+
],
|
|
7339
|
+
{ transaction: true },
|
|
7340
|
+
);
|
|
7221
7341
|
return { quoteId: quote.id, lineId: parsed.id };
|
|
7222
7342
|
},
|
|
7223
7343
|
captureAfter: async (_input, result, ctx) => {
|
|
@@ -7483,25 +7603,34 @@ const orderAdjustmentUpsertCommand: CommandHandler<
|
|
|
7483
7603
|
customFields: (adj as any).customFields ?? undefined,
|
|
7484
7604
|
position: adj.position ?? index,
|
|
7485
7605
|
}));
|
|
7486
|
-
await replaceOrderAdjustments(em, order, calculation, adjustmentInputs);
|
|
7487
|
-
applyOrderTotals(order, calculation.totals, calculation.lines.length);
|
|
7488
|
-
order.updatedAt = new Date();
|
|
7489
7606
|
let eventBus: EventBus | null = null;
|
|
7490
7607
|
try {
|
|
7491
7608
|
eventBus = ctx.container.resolve("eventBus") as EventBus;
|
|
7492
7609
|
} catch {
|
|
7493
7610
|
eventBus = null;
|
|
7494
7611
|
}
|
|
7495
|
-
|
|
7496
|
-
|
|
7497
|
-
|
|
7498
|
-
|
|
7499
|
-
|
|
7500
|
-
|
|
7501
|
-
|
|
7502
|
-
|
|
7503
|
-
|
|
7504
|
-
|
|
7612
|
+
// Persist the adjustment change and recalculated totals atomically so a
|
|
7613
|
+
// mid-build failure cannot leave a half-updated order committed (#2336).
|
|
7614
|
+
await withAtomicFlush(
|
|
7615
|
+
em,
|
|
7616
|
+
[
|
|
7617
|
+
async () => {
|
|
7618
|
+
await replaceOrderAdjustments(em, order, calculation, adjustmentInputs);
|
|
7619
|
+
applyOrderTotals(order, calculation.totals, calculation.lines.length);
|
|
7620
|
+
order.updatedAt = new Date();
|
|
7621
|
+
await emitTotalsCalculated(eventBus, {
|
|
7622
|
+
documentKind: "order",
|
|
7623
|
+
documentId: order.id,
|
|
7624
|
+
organizationId: order.organizationId,
|
|
7625
|
+
tenantId: order.tenantId,
|
|
7626
|
+
customerId: order.customerEntityId ?? null,
|
|
7627
|
+
totals: calculation.totals,
|
|
7628
|
+
lineCount: calculation.lines.length,
|
|
7629
|
+
});
|
|
7630
|
+
},
|
|
7631
|
+
],
|
|
7632
|
+
{ transaction: true },
|
|
7633
|
+
);
|
|
7505
7634
|
return { orderId: order.id, adjustmentId };
|
|
7506
7635
|
},
|
|
7507
7636
|
captureAfter: async (_input, result, ctx) => {
|
|
@@ -7638,25 +7767,34 @@ const orderAdjustmentDeleteCommand: CommandHandler<
|
|
|
7638
7767
|
metadata: adj.metadata ?? undefined,
|
|
7639
7768
|
position: adj.position ?? index,
|
|
7640
7769
|
}));
|
|
7641
|
-
await replaceOrderAdjustments(em, order, calculation, adjustmentInputs);
|
|
7642
|
-
applyOrderTotals(order, calculation.totals, calculation.lines.length);
|
|
7643
|
-
order.updatedAt = new Date();
|
|
7644
7770
|
let eventBus: EventBus | null = null;
|
|
7645
7771
|
try {
|
|
7646
7772
|
eventBus = ctx.container.resolve("eventBus") as EventBus;
|
|
7647
7773
|
} catch {
|
|
7648
7774
|
eventBus = null;
|
|
7649
7775
|
}
|
|
7650
|
-
|
|
7651
|
-
|
|
7652
|
-
|
|
7653
|
-
|
|
7654
|
-
|
|
7655
|
-
|
|
7656
|
-
|
|
7657
|
-
|
|
7658
|
-
|
|
7659
|
-
|
|
7776
|
+
// Persist the adjustment removal and recalculated totals atomically so a
|
|
7777
|
+
// mid-build failure cannot leave a half-updated order committed (#2336).
|
|
7778
|
+
await withAtomicFlush(
|
|
7779
|
+
em,
|
|
7780
|
+
[
|
|
7781
|
+
async () => {
|
|
7782
|
+
await replaceOrderAdjustments(em, order, calculation, adjustmentInputs);
|
|
7783
|
+
applyOrderTotals(order, calculation.totals, calculation.lines.length);
|
|
7784
|
+
order.updatedAt = new Date();
|
|
7785
|
+
await emitTotalsCalculated(eventBus, {
|
|
7786
|
+
documentKind: "order",
|
|
7787
|
+
documentId: order.id,
|
|
7788
|
+
organizationId: order.organizationId,
|
|
7789
|
+
tenantId: order.tenantId,
|
|
7790
|
+
customerId: order.customerEntityId ?? null,
|
|
7791
|
+
totals: calculation.totals,
|
|
7792
|
+
lineCount: calculation.lines.length,
|
|
7793
|
+
});
|
|
7794
|
+
},
|
|
7795
|
+
],
|
|
7796
|
+
{ transaction: true },
|
|
7797
|
+
);
|
|
7660
7798
|
return { orderId: order.id, adjustmentId: parsed.id };
|
|
7661
7799
|
},
|
|
7662
7800
|
captureAfter: async (_input, result, ctx) => {
|
|
@@ -7920,25 +8058,34 @@ const quoteAdjustmentUpsertCommand: CommandHandler<
|
|
|
7920
8058
|
customFields: (adj as any).customFields ?? undefined,
|
|
7921
8059
|
position: adj.position ?? index,
|
|
7922
8060
|
}));
|
|
7923
|
-
await replaceQuoteAdjustments(em, quote, calculation, adjustmentInputs);
|
|
7924
|
-
applyQuoteTotals(quote, calculation.totals, calculation.lines.length);
|
|
7925
|
-
quote.updatedAt = new Date();
|
|
7926
8061
|
let eventBus: EventBus | null = null;
|
|
7927
8062
|
try {
|
|
7928
8063
|
eventBus = ctx.container.resolve("eventBus") as EventBus;
|
|
7929
8064
|
} catch {
|
|
7930
8065
|
eventBus = null;
|
|
7931
8066
|
}
|
|
7932
|
-
|
|
7933
|
-
|
|
7934
|
-
|
|
7935
|
-
|
|
7936
|
-
|
|
7937
|
-
|
|
7938
|
-
|
|
7939
|
-
|
|
7940
|
-
|
|
7941
|
-
|
|
8067
|
+
// Persist the adjustment change and recalculated totals atomically so a
|
|
8068
|
+
// mid-build failure cannot leave a half-updated quote committed (#2336).
|
|
8069
|
+
await withAtomicFlush(
|
|
8070
|
+
em,
|
|
8071
|
+
[
|
|
8072
|
+
async () => {
|
|
8073
|
+
await replaceQuoteAdjustments(em, quote, calculation, adjustmentInputs);
|
|
8074
|
+
applyQuoteTotals(quote, calculation.totals, calculation.lines.length);
|
|
8075
|
+
quote.updatedAt = new Date();
|
|
8076
|
+
await emitTotalsCalculated(eventBus, {
|
|
8077
|
+
documentKind: "quote",
|
|
8078
|
+
documentId: quote.id,
|
|
8079
|
+
organizationId: quote.organizationId,
|
|
8080
|
+
tenantId: quote.tenantId,
|
|
8081
|
+
customerId: quote.customerEntityId ?? null,
|
|
8082
|
+
totals: calculation.totals,
|
|
8083
|
+
lineCount: calculation.lines.length,
|
|
8084
|
+
});
|
|
8085
|
+
},
|
|
8086
|
+
],
|
|
8087
|
+
{ transaction: true },
|
|
8088
|
+
);
|
|
7942
8089
|
return { quoteId: quote.id, adjustmentId };
|
|
7943
8090
|
},
|
|
7944
8091
|
captureAfter: async (_input, result, ctx) => {
|
|
@@ -8074,25 +8221,34 @@ const quoteAdjustmentDeleteCommand: CommandHandler<
|
|
|
8074
8221
|
metadata: adj.metadata ?? undefined,
|
|
8075
8222
|
position: adj.position ?? index,
|
|
8076
8223
|
}));
|
|
8077
|
-
await replaceQuoteAdjustments(em, quote, calculation, adjustmentInputs);
|
|
8078
|
-
applyQuoteTotals(quote, calculation.totals, calculation.lines.length);
|
|
8079
|
-
quote.updatedAt = new Date();
|
|
8080
8224
|
let eventBus: EventBus | null = null;
|
|
8081
8225
|
try {
|
|
8082
8226
|
eventBus = ctx.container.resolve("eventBus") as EventBus;
|
|
8083
8227
|
} catch {
|
|
8084
8228
|
eventBus = null;
|
|
8085
8229
|
}
|
|
8086
|
-
|
|
8087
|
-
|
|
8088
|
-
|
|
8089
|
-
|
|
8090
|
-
|
|
8091
|
-
|
|
8092
|
-
|
|
8093
|
-
|
|
8094
|
-
|
|
8095
|
-
|
|
8230
|
+
// Persist the adjustment removal and recalculated totals atomically so a
|
|
8231
|
+
// mid-build failure cannot leave a half-updated quote committed (#2336).
|
|
8232
|
+
await withAtomicFlush(
|
|
8233
|
+
em,
|
|
8234
|
+
[
|
|
8235
|
+
async () => {
|
|
8236
|
+
await replaceQuoteAdjustments(em, quote, calculation, adjustmentInputs);
|
|
8237
|
+
applyQuoteTotals(quote, calculation.totals, calculation.lines.length);
|
|
8238
|
+
quote.updatedAt = new Date();
|
|
8239
|
+
await emitTotalsCalculated(eventBus, {
|
|
8240
|
+
documentKind: "quote",
|
|
8241
|
+
documentId: quote.id,
|
|
8242
|
+
organizationId: quote.organizationId,
|
|
8243
|
+
tenantId: quote.tenantId,
|
|
8244
|
+
customerId: quote.customerEntityId ?? null,
|
|
8245
|
+
totals: calculation.totals,
|
|
8246
|
+
lineCount: calculation.lines.length,
|
|
8247
|
+
});
|
|
8248
|
+
},
|
|
8249
|
+
],
|
|
8250
|
+
{ transaction: true },
|
|
8251
|
+
);
|
|
8096
8252
|
return { quoteId: quote.id, adjustmentId: parsed.id };
|
|
8097
8253
|
},
|
|
8098
8254
|
captureAfter: async (_input, result, ctx) => {
|
|
@@ -8218,48 +8374,66 @@ const createInvoiceCommand: CommandHandler<
|
|
|
8218
8374
|
createdAt: new Date(),
|
|
8219
8375
|
updatedAt: new Date(),
|
|
8220
8376
|
});
|
|
8221
|
-
em.persist(invoice);
|
|
8222
|
-
|
|
8223
|
-
if (parsed.lines?.length) {
|
|
8224
|
-
for (let i = 0; i < parsed.lines.length; i++) {
|
|
8225
|
-
const line = parsed.lines[i];
|
|
8226
|
-
em.persist(
|
|
8227
|
-
em.create(SalesInvoiceLine, {
|
|
8228
|
-
id: randomUUID(),
|
|
8229
|
-
invoice,
|
|
8230
|
-
orderLineId: line.orderLineId ?? null,
|
|
8231
|
-
organizationId: parsed.organizationId,
|
|
8232
|
-
tenantId: parsed.tenantId,
|
|
8233
|
-
lineNumber: line.lineNumber ?? i + 1,
|
|
8234
|
-
kind: line.kind ?? "product",
|
|
8235
|
-
name: line.name ?? null,
|
|
8236
|
-
sku: line.sku ?? null,
|
|
8237
|
-
description: line.description ?? null,
|
|
8238
|
-
quantity: toNumericString(line.quantity ?? 0),
|
|
8239
|
-
quantityUnit: line.quantityUnit ?? null,
|
|
8240
|
-
normalizedQuantity: toNumericString(line.normalizedQuantity ?? 0),
|
|
8241
|
-
normalizedUnit: line.normalizedUnit ?? null,
|
|
8242
|
-
uomSnapshot: line.uomSnapshot ?? null,
|
|
8243
|
-
currencyCode: line.currencyCode ?? parsed.currencyCode,
|
|
8244
|
-
unitPriceNet: toNumericString(line.unitPriceNet ?? 0),
|
|
8245
|
-
unitPriceGross: toNumericString(line.unitPriceGross ?? 0),
|
|
8246
|
-
discountAmount: toNumericString(line.discountAmount ?? 0),
|
|
8247
|
-
discountPercent: toNumericString(line.discountPercent ?? 0),
|
|
8248
|
-
taxRate: toNumericString(line.taxRate ?? 0),
|
|
8249
|
-
taxAmount: toNumericString(line.taxAmount ?? 0),
|
|
8250
|
-
totalNetAmount: toNumericString(line.totalNetAmount ?? 0),
|
|
8251
|
-
totalGrossAmount: toNumericString(line.totalGrossAmount ?? 0),
|
|
8252
|
-
metadata: line.metadata ?? null,
|
|
8253
|
-
}),
|
|
8254
|
-
);
|
|
8255
|
-
}
|
|
8256
|
-
}
|
|
8257
8377
|
|
|
8258
|
-
|
|
8259
|
-
|
|
8260
|
-
|
|
8378
|
+
// Header + lines + custom-field writes must commit atomically.
|
|
8379
|
+
// setRecordCustomFields flushes mid-build, so without a transaction a
|
|
8380
|
+
// partial write could persist the header without its lines/custom fields.
|
|
8381
|
+
await withAtomicFlush(
|
|
8382
|
+
em,
|
|
8383
|
+
[
|
|
8384
|
+
async () => {
|
|
8385
|
+
em.persist(invoice);
|
|
8386
|
+
|
|
8387
|
+
if (parsed.lines?.length) {
|
|
8388
|
+
for (let i = 0; i < parsed.lines.length; i++) {
|
|
8389
|
+
const line = parsed.lines[i];
|
|
8390
|
+
em.persist(
|
|
8391
|
+
em.create(SalesInvoiceLine, {
|
|
8392
|
+
id: randomUUID(),
|
|
8393
|
+
invoice,
|
|
8394
|
+
orderLineId: line.orderLineId ?? null,
|
|
8395
|
+
organizationId: parsed.organizationId,
|
|
8396
|
+
tenantId: parsed.tenantId,
|
|
8397
|
+
lineNumber: line.lineNumber ?? i + 1,
|
|
8398
|
+
kind: line.kind ?? "product",
|
|
8399
|
+
name: line.name ?? null,
|
|
8400
|
+
sku: line.sku ?? null,
|
|
8401
|
+
description: line.description ?? null,
|
|
8402
|
+
quantity: toNumericString(line.quantity ?? 0),
|
|
8403
|
+
quantityUnit: line.quantityUnit ?? null,
|
|
8404
|
+
normalizedQuantity: toNumericString(line.normalizedQuantity ?? 0),
|
|
8405
|
+
normalizedUnit: line.normalizedUnit ?? null,
|
|
8406
|
+
uomSnapshot: line.uomSnapshot ?? null,
|
|
8407
|
+
currencyCode: line.currencyCode ?? parsed.currencyCode,
|
|
8408
|
+
unitPriceNet: toNumericString(line.unitPriceNet ?? 0),
|
|
8409
|
+
unitPriceGross: toNumericString(line.unitPriceGross ?? 0),
|
|
8410
|
+
discountAmount: toNumericString(line.discountAmount ?? 0),
|
|
8411
|
+
discountPercent: toNumericString(line.discountPercent ?? 0),
|
|
8412
|
+
taxRate: toNumericString(line.taxRate ?? 0),
|
|
8413
|
+
taxAmount: toNumericString(line.taxAmount ?? 0),
|
|
8414
|
+
totalNetAmount: toNumericString(line.totalNetAmount ?? 0),
|
|
8415
|
+
totalGrossAmount: toNumericString(line.totalGrossAmount ?? 0),
|
|
8416
|
+
metadata: line.metadata ?? null,
|
|
8417
|
+
}),
|
|
8418
|
+
);
|
|
8419
|
+
}
|
|
8420
|
+
}
|
|
8261
8421
|
|
|
8262
|
-
|
|
8422
|
+
if (parsed.customFieldSetId) {
|
|
8423
|
+
await setRecordCustomFields(em, {
|
|
8424
|
+
entityId: E.sales.sales_invoice,
|
|
8425
|
+
recordId: invoiceId,
|
|
8426
|
+
organizationId: parsed.organizationId,
|
|
8427
|
+
tenantId: parsed.tenantId,
|
|
8428
|
+
values: normalizeCustomFieldValues(
|
|
8429
|
+
((parsed as Record<string, unknown>).customFields as Record<string, unknown>) ?? {},
|
|
8430
|
+
),
|
|
8431
|
+
});
|
|
8432
|
+
}
|
|
8433
|
+
},
|
|
8434
|
+
],
|
|
8435
|
+
{ transaction: true },
|
|
8436
|
+
);
|
|
8263
8437
|
|
|
8264
8438
|
const dataEngine = ctx.container.resolve("dataEngine") as DataEngine;
|
|
8265
8439
|
await emitCrudSideEffects({
|
|
@@ -8696,45 +8870,63 @@ const createCreditMemoCommand: CommandHandler<
|
|
|
8696
8870
|
createdAt: new Date(),
|
|
8697
8871
|
updatedAt: new Date(),
|
|
8698
8872
|
});
|
|
8699
|
-
em.persist(creditMemo);
|
|
8700
|
-
|
|
8701
|
-
if (parsed.lines?.length) {
|
|
8702
|
-
for (let i = 0; i < parsed.lines.length; i++) {
|
|
8703
|
-
const line = parsed.lines[i];
|
|
8704
|
-
em.persist(
|
|
8705
|
-
em.create(SalesCreditMemoLine, {
|
|
8706
|
-
id: randomUUID(),
|
|
8707
|
-
creditMemo,
|
|
8708
|
-
orderLineId: line.orderLineId ?? null,
|
|
8709
|
-
organizationId: parsed.organizationId,
|
|
8710
|
-
tenantId: parsed.tenantId,
|
|
8711
|
-
lineNumber: line.lineNumber ?? i + 1,
|
|
8712
|
-
name: line.name ?? null,
|
|
8713
|
-
sku: line.sku ?? null,
|
|
8714
|
-
description: line.description ?? null,
|
|
8715
|
-
quantity: toNumericString(line.quantity ?? 0),
|
|
8716
|
-
quantityUnit: line.quantityUnit ?? null,
|
|
8717
|
-
normalizedQuantity: toNumericString(line.normalizedQuantity ?? 0),
|
|
8718
|
-
normalizedUnit: line.normalizedUnit ?? null,
|
|
8719
|
-
uomSnapshot: line.uomSnapshot ?? null,
|
|
8720
|
-
currencyCode: line.currencyCode ?? parsed.currencyCode,
|
|
8721
|
-
unitPriceNet: toNumericString(line.unitPriceNet ?? 0),
|
|
8722
|
-
unitPriceGross: toNumericString(line.unitPriceGross ?? 0),
|
|
8723
|
-
taxRate: toNumericString(line.taxRate ?? 0),
|
|
8724
|
-
taxAmount: toNumericString(line.taxAmount ?? 0),
|
|
8725
|
-
totalNetAmount: toNumericString(line.totalNetAmount ?? 0),
|
|
8726
|
-
totalGrossAmount: toNumericString(line.totalGrossAmount ?? 0),
|
|
8727
|
-
metadata: line.metadata ?? null,
|
|
8728
|
-
}),
|
|
8729
|
-
);
|
|
8730
|
-
}
|
|
8731
|
-
}
|
|
8732
8873
|
|
|
8733
|
-
|
|
8734
|
-
|
|
8735
|
-
|
|
8874
|
+
// Header + lines + custom-field writes must commit atomically.
|
|
8875
|
+
// setRecordCustomFields flushes mid-build, so without a transaction a
|
|
8876
|
+
// partial write could persist the header without its lines/custom fields.
|
|
8877
|
+
await withAtomicFlush(
|
|
8878
|
+
em,
|
|
8879
|
+
[
|
|
8880
|
+
async () => {
|
|
8881
|
+
em.persist(creditMemo);
|
|
8882
|
+
|
|
8883
|
+
if (parsed.lines?.length) {
|
|
8884
|
+
for (let i = 0; i < parsed.lines.length; i++) {
|
|
8885
|
+
const line = parsed.lines[i];
|
|
8886
|
+
em.persist(
|
|
8887
|
+
em.create(SalesCreditMemoLine, {
|
|
8888
|
+
id: randomUUID(),
|
|
8889
|
+
creditMemo,
|
|
8890
|
+
orderLineId: line.orderLineId ?? null,
|
|
8891
|
+
organizationId: parsed.organizationId,
|
|
8892
|
+
tenantId: parsed.tenantId,
|
|
8893
|
+
lineNumber: line.lineNumber ?? i + 1,
|
|
8894
|
+
name: line.name ?? null,
|
|
8895
|
+
sku: line.sku ?? null,
|
|
8896
|
+
description: line.description ?? null,
|
|
8897
|
+
quantity: toNumericString(line.quantity ?? 0),
|
|
8898
|
+
quantityUnit: line.quantityUnit ?? null,
|
|
8899
|
+
normalizedQuantity: toNumericString(line.normalizedQuantity ?? 0),
|
|
8900
|
+
normalizedUnit: line.normalizedUnit ?? null,
|
|
8901
|
+
uomSnapshot: line.uomSnapshot ?? null,
|
|
8902
|
+
currencyCode: line.currencyCode ?? parsed.currencyCode,
|
|
8903
|
+
unitPriceNet: toNumericString(line.unitPriceNet ?? 0),
|
|
8904
|
+
unitPriceGross: toNumericString(line.unitPriceGross ?? 0),
|
|
8905
|
+
taxRate: toNumericString(line.taxRate ?? 0),
|
|
8906
|
+
taxAmount: toNumericString(line.taxAmount ?? 0),
|
|
8907
|
+
totalNetAmount: toNumericString(line.totalNetAmount ?? 0),
|
|
8908
|
+
totalGrossAmount: toNumericString(line.totalGrossAmount ?? 0),
|
|
8909
|
+
metadata: line.metadata ?? null,
|
|
8910
|
+
}),
|
|
8911
|
+
);
|
|
8912
|
+
}
|
|
8913
|
+
}
|
|
8736
8914
|
|
|
8737
|
-
|
|
8915
|
+
if (parsed.customFieldSetId) {
|
|
8916
|
+
await setRecordCustomFields(em, {
|
|
8917
|
+
entityId: E.sales.sales_credit_memo,
|
|
8918
|
+
recordId: creditMemoId,
|
|
8919
|
+
organizationId: parsed.organizationId,
|
|
8920
|
+
tenantId: parsed.tenantId,
|
|
8921
|
+
values: normalizeCustomFieldValues(
|
|
8922
|
+
((parsed as Record<string, unknown>).customFields as Record<string, unknown>) ?? {},
|
|
8923
|
+
),
|
|
8924
|
+
});
|
|
8925
|
+
}
|
|
8926
|
+
},
|
|
8927
|
+
],
|
|
8928
|
+
{ transaction: true },
|
|
8929
|
+
);
|
|
8738
8930
|
|
|
8739
8931
|
const dataEngine = ctx.container.resolve("dataEngine") as DataEngine;
|
|
8740
8932
|
await emitCrudSideEffects({
|