@open-mercato/core 0.6.6-develop.6089.1.a7ed560528 → 0.6.6-develop.6121.1.5797a901b3
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 +1 -1
- package/dist/generated/entities/catalog_product/index.js +52 -0
- package/dist/generated/entities/catalog_product/index.js.map +2 -2
- package/dist/generated/entities/catalog_product_variant/index.js +4 -0
- package/dist/generated/entities/catalog_product_variant/index.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +28 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/modules/catalog/api/products/route.js +103 -33
- package/dist/modules/catalog/api/products/route.js.map +2 -2
- package/dist/modules/catalog/api/variants/route.js +4 -0
- package/dist/modules/catalog/api/variants/route.js.map +2 -2
- package/dist/modules/catalog/backend/catalog/products/[id]/page.js +46 -2
- package/dist/modules/catalog/backend/catalog/products/[id]/page.js.map +2 -2
- package/dist/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.js +4 -0
- package/dist/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.js.map +2 -2
- package/dist/modules/catalog/backend/catalog/products/[productId]/variants/create/page.js +2 -0
- package/dist/modules/catalog/backend/catalog/products/[productId]/variants/create/page.js.map +2 -2
- package/dist/modules/catalog/backend/catalog/products/create/page.js +42 -2
- package/dist/modules/catalog/backend/catalog/products/create/page.js.map +2 -2
- package/dist/modules/catalog/commands/products.js +251 -14
- package/dist/modules/catalog/commands/products.js.map +2 -2
- package/dist/modules/catalog/commands/variants.js +72 -1
- package/dist/modules/catalog/commands/variants.js.map +2 -2
- package/dist/modules/catalog/components/products/ProductComplianceSection.js +500 -0
- package/dist/modules/catalog/components/products/ProductComplianceSection.js.map +7 -0
- package/dist/modules/catalog/components/products/VariantBuilder.js +39 -1
- package/dist/modules/catalog/components/products/VariantBuilder.js.map +2 -2
- package/dist/modules/catalog/components/products/productForm.js +164 -2
- package/dist/modules/catalog/components/products/productForm.js.map +2 -2
- package/dist/modules/catalog/components/products/variantForm.js +2 -0
- package/dist/modules/catalog/components/products/variantForm.js.map +2 -2
- package/dist/modules/catalog/data/entities.js +93 -0
- package/dist/modules/catalog/data/entities.js.map +2 -2
- package/dist/modules/catalog/data/types.js +28 -0
- package/dist/modules/catalog/data/types.js.map +2 -2
- package/dist/modules/catalog/data/validators.js +100 -5
- package/dist/modules/catalog/data/validators.js.map +2 -2
- package/dist/modules/catalog/lib/gtin.js +61 -0
- package/dist/modules/catalog/lib/gtin.js.map +7 -0
- package/dist/modules/catalog/migrations/Migration20260611090000.js +17 -0
- package/dist/modules/catalog/migrations/Migration20260611090000.js.map +7 -0
- package/dist/modules/catalog/translations.js +1 -1
- package/dist/modules/catalog/translations.js.map +2 -2
- package/dist/modules/configs/api/cache/route.js +60 -10
- package/dist/modules/configs/api/cache/route.js.map +3 -3
- package/dist/modules/configs/components/CachePanel.js +52 -29
- package/dist/modules/configs/components/CachePanel.js.map +2 -2
- package/dist/modules/currencies/api/currencies/options/route.js +18 -4
- package/dist/modules/currencies/api/currencies/options/route.js.map +2 -2
- package/dist/modules/currencies/services/rateFetchingService.js +35 -18
- package/dist/modules/currencies/services/rateFetchingService.js.map +2 -2
- package/dist/modules/customer_accounts/backend/customer_accounts/users/[id]/page.js +16 -16
- package/dist/modules/customer_accounts/backend/customer_accounts/users/[id]/page.js.map +2 -2
- package/dist/modules/customer_accounts/backend/customer_accounts/users/page.js +1 -1
- package/dist/modules/customer_accounts/backend/customer_accounts/users/page.js.map +2 -2
- package/dist/modules/customers/backend/calendar/page.js +10 -0
- package/dist/modules/customers/backend/calendar/page.js.map +7 -0
- package/dist/modules/customers/backend/calendar/page.meta.js +25 -0
- package/dist/modules/customers/backend/calendar/page.meta.js.map +7 -0
- package/dist/modules/customers/cli.js +1 -0
- package/dist/modules/customers/cli.js.map +2 -2
- package/dist/modules/customers/components/calendar/AgendaList.js +182 -0
- package/dist/modules/customers/components/calendar/AgendaList.js.map +7 -0
- package/dist/modules/customers/components/calendar/CalendarEventEditor.js +331 -0
- package/dist/modules/customers/components/calendar/CalendarEventEditor.js.map +7 -0
- package/dist/modules/customers/components/calendar/CalendarFooter.js +48 -0
- package/dist/modules/customers/components/calendar/CalendarFooter.js.map +7 -0
- package/dist/modules/customers/components/calendar/CalendarHeader.js +36 -0
- package/dist/modules/customers/components/calendar/CalendarHeader.js.map +7 -0
- package/dist/modules/customers/components/calendar/CalendarScreen.js +516 -0
- package/dist/modules/customers/components/calendar/CalendarScreen.js.map +7 -0
- package/dist/modules/customers/components/calendar/CalendarSettingsModal.js +163 -0
- package/dist/modules/customers/components/calendar/CalendarSettingsModal.js.map +7 -0
- package/dist/modules/customers/components/calendar/CalendarTabs.js +60 -0
- package/dist/modules/customers/components/calendar/CalendarTabs.js.map +7 -0
- package/dist/modules/customers/components/calendar/CalendarToolbar.js +262 -0
- package/dist/modules/customers/components/calendar/CalendarToolbar.js.map +7 -0
- package/dist/modules/customers/components/calendar/EventBlock.js +149 -0
- package/dist/modules/customers/components/calendar/EventBlock.js.map +7 -0
- package/dist/modules/customers/components/calendar/EventPeekPopover.js +72 -0
- package/dist/modules/customers/components/calendar/EventPeekPopover.js.map +7 -0
- package/dist/modules/customers/components/calendar/MonthGrid.js +214 -0
- package/dist/modules/customers/components/calendar/MonthGrid.js.map +7 -0
- package/dist/modules/customers/components/calendar/ShortcutsDialog.js +44 -0
- package/dist/modules/customers/components/calendar/ShortcutsDialog.js.map +7 -0
- package/dist/modules/customers/components/calendar/TimeGrid.js +410 -0
- package/dist/modules/customers/components/calendar/TimeGrid.js.map +7 -0
- package/dist/modules/customers/components/calendar/UpcomingCards.js +175 -0
- package/dist/modules/customers/components/calendar/UpcomingCards.js.map +7 -0
- package/dist/modules/customers/components/calendar/editor/CategoryField.js +75 -0
- package/dist/modules/customers/components/calendar/editor/CategoryField.js.map +7 -0
- package/dist/modules/customers/components/calendar/editor/LocationField.js +30 -0
- package/dist/modules/customers/components/calendar/editor/LocationField.js.map +7 -0
- package/dist/modules/customers/components/calendar/editor/PeopleField.js +135 -0
- package/dist/modules/customers/components/calendar/editor/PeopleField.js.map +7 -0
- package/dist/modules/customers/components/calendar/editor/RelatedToField.js +218 -0
- package/dist/modules/customers/components/calendar/editor/RelatedToField.js.map +7 -0
- package/dist/modules/customers/components/calendar/editor/RepeatField.js +119 -0
- package/dist/modules/customers/components/calendar/editor/RepeatField.js.map +7 -0
- package/dist/modules/customers/components/calendar/editor/ScheduleSection.js +83 -0
- package/dist/modules/customers/components/calendar/editor/ScheduleSection.js.map +7 -0
- package/dist/modules/customers/components/calendar/editor/SegmentGroup.js +44 -0
- package/dist/modules/customers/components/calendar/editor/SegmentGroup.js.map +7 -0
- package/dist/modules/customers/components/calendar/editor/hooks.js +132 -0
- package/dist/modules/customers/components/calendar/editor/hooks.js.map +7 -0
- package/dist/modules/customers/components/calendar/editor/inputs.js +160 -0
- package/dist/modules/customers/components/calendar/editor/inputs.js.map +7 -0
- package/dist/modules/customers/components/calendar/editor/lookups.js +107 -0
- package/dist/modules/customers/components/calendar/editor/lookups.js.map +7 -0
- package/dist/modules/customers/components/calendar/types.js +30 -0
- package/dist/modules/customers/components/calendar/types.js.map +7 -0
- package/dist/modules/customers/components/calendar/useCalendarItems.js +131 -0
- package/dist/modules/customers/components/calendar/useCalendarItems.js.map +7 -0
- package/dist/modules/customers/components/calendar/useCalendarPreferences.js +41 -0
- package/dist/modules/customers/components/calendar/useCalendarPreferences.js.map +7 -0
- package/dist/modules/customers/lib/calendar/categories.js +28 -0
- package/dist/modules/customers/lib/calendar/categories.js.map +7 -0
- package/dist/modules/customers/lib/calendar/conflicts.js +72 -0
- package/dist/modules/customers/lib/calendar/conflicts.js.map +7 -0
- package/dist/modules/customers/lib/calendar/editorPayload.js +271 -0
- package/dist/modules/customers/lib/calendar/editorPayload.js.map +7 -0
- package/dist/modules/customers/lib/calendar/grid.js +48 -0
- package/dist/modules/customers/lib/calendar/grid.js.map +7 -0
- package/dist/modules/customers/lib/calendar/layout.js +39 -0
- package/dist/modules/customers/lib/calendar/layout.js.map +7 -0
- package/dist/modules/customers/lib/calendar/mapItem.js +79 -0
- package/dist/modules/customers/lib/calendar/mapItem.js.map +7 -0
- package/dist/modules/customers/lib/calendar/preferences.js +71 -0
- package/dist/modules/customers/lib/calendar/preferences.js.map +7 -0
- package/dist/modules/customers/lib/calendar/range.js +46 -0
- package/dist/modules/customers/lib/calendar/range.js.map +7 -0
- package/dist/modules/customers/lib/calendar/recurrence.js +113 -0
- package/dist/modules/customers/lib/calendar/recurrence.js.map +7 -0
- package/dist/modules/dashboards/services/widgetDataService.js +5 -5
- package/dist/modules/dashboards/services/widgetDataService.js.map +2 -2
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.js +61 -1
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.js.map +2 -2
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/route.js +33 -1
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/route.js.map +2 -2
- package/dist/modules/dictionaries/api/[dictionaryId]/route.js +61 -1
- package/dist/modules/dictionaries/api/[dictionaryId]/route.js.map +2 -2
- package/dist/modules/dictionaries/api/route.js +33 -1
- package/dist/modules/dictionaries/api/route.js.map +2 -2
- package/dist/modules/feature_toggles/lib/queries.js +36 -52
- package/dist/modules/feature_toggles/lib/queries.js.map +2 -2
- package/dist/modules/messages/commands/actions.js +81 -9
- package/dist/modules/messages/commands/actions.js.map +2 -2
- package/dist/modules/perspectives/api/[tableId]/[perspectiveId]/route.js +32 -1
- package/dist/modules/perspectives/api/[tableId]/[perspectiveId]/route.js.map +2 -2
- package/dist/modules/perspectives/api/[tableId]/roles/[roleId]/route.js +32 -1
- package/dist/modules/perspectives/api/[tableId]/roles/[roleId]/route.js.map +2 -2
- package/dist/modules/perspectives/api/[tableId]/route.js +73 -2
- package/dist/modules/perspectives/api/[tableId]/route.js.map +2 -2
- package/dist/modules/perspectives/services/perspectiveService.js +5 -3
- package/dist/modules/perspectives/services/perspectiveService.js.map +2 -2
- package/dist/modules/planner/api/access.js +9 -0
- package/dist/modules/planner/api/access.js.map +2 -2
- package/dist/modules/planner/api/availability-date-specific.js +22 -1
- package/dist/modules/planner/api/availability-date-specific.js.map +2 -2
- package/dist/modules/planner/api/availability-weekly.js +22 -1
- package/dist/modules/planner/api/availability-weekly.js.map +2 -2
- package/dist/modules/resources/api/resources/tags/assign/route.js +39 -0
- package/dist/modules/resources/api/resources/tags/assign/route.js.map +2 -2
- package/dist/modules/resources/api/resources/tags/unassign/route.js +39 -0
- package/dist/modules/resources/api/resources/tags/unassign/route.js.map +2 -2
- package/dist/modules/sales/api/settings/document-numbers/route.js +33 -1
- package/dist/modules/sales/api/settings/document-numbers/route.js.map +2 -2
- package/dist/modules/sales/api/settings/order-editing/route.js +35 -3
- package/dist/modules/sales/api/settings/order-editing/route.js.map +2 -2
- package/dist/modules/staff/api/leave-requests/accept/route.js +41 -0
- package/dist/modules/staff/api/leave-requests/accept/route.js.map +2 -2
- package/dist/modules/staff/api/leave-requests/reject/route.js +41 -0
- package/dist/modules/staff/api/leave-requests/reject/route.js.map +2 -2
- package/dist/modules/staff/api/team-members/self/route.js +40 -0
- package/dist/modules/staff/api/team-members/self/route.js.map +2 -2
- package/dist/modules/staff/api/team-members/tags/assign/route.js +41 -0
- package/dist/modules/staff/api/team-members/tags/assign/route.js.map +2 -2
- package/dist/modules/staff/api/team-members/tags/unassign/route.js +41 -0
- package/dist/modules/staff/api/team-members/tags/unassign/route.js.map +2 -2
- package/dist/modules/translations/api/[entityType]/[entityId]/route.js +61 -1
- package/dist/modules/translations/api/[entityType]/[entityId]/route.js.map +2 -2
- package/dist/modules/translations/api/context.js +7 -0
- package/dist/modules/translations/api/context.js.map +2 -2
- package/dist/modules/translations/api/put/locales.js +33 -1
- package/dist/modules/translations/api/put/locales.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/visual-editor/page.js +6 -7
- package/dist/modules/workflows/backend/definitions/visual-editor/page.js.map +2 -2
- package/dist/modules/workflows/backend/instances/[id]/page.js.map +1 -1
- package/dist/modules/workflows/components/EdgeEditDialog.js.map +1 -1
- package/dist/modules/workflows/components/EdgeEditDialogCrudForm.js.map +1 -1
- package/dist/modules/workflows/components/NodeEditDialog.js.map +1 -1
- package/dist/modules/workflows/components/NodeEditDialogCrudForm.js.map +1 -1
- package/dist/modules/workflows/components/WorkflowGraph.js.map +1 -1
- package/dist/modules/workflows/components/WorkflowGraphImpl.js +17 -9
- package/dist/modules/workflows/components/WorkflowGraphImpl.js.map +2 -2
- package/dist/modules/workflows/components/mobile/MobileVisualEditor.js.map +1 -1
- package/dist/modules/workflows/frontend/checkout-demo/deriveCurrentStep.js +8 -0
- package/dist/modules/workflows/frontend/checkout-demo/deriveCurrentStep.js.map +7 -0
- package/dist/modules/workflows/frontend/checkout-demo/page.js +2 -20
- package/dist/modules/workflows/frontend/checkout-demo/page.js.map +2 -2
- package/dist/modules/workflows/lib/graph-utils.js +8 -0
- package/dist/modules/workflows/lib/graph-utils.js.map +2 -2
- package/generated/entities/catalog_product/index.ts +26 -0
- package/generated/entities/catalog_product_variant/index.ts +2 -0
- package/generated/entity-fields-registry.ts +28 -0
- package/package.json +7 -7
- package/src/modules/catalog/api/products/route.ts +123 -39
- package/src/modules/catalog/api/variants/route.ts +4 -0
- package/src/modules/catalog/backend/catalog/products/[id]/page.tsx +43 -0
- package/src/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.tsx +14 -0
- package/src/modules/catalog/backend/catalog/products/[productId]/variants/create/page.tsx +2 -0
- package/src/modules/catalog/backend/catalog/products/create/page.tsx +41 -0
- package/src/modules/catalog/commands/products.ts +307 -2
- package/src/modules/catalog/commands/variants.ts +90 -1
- package/src/modules/catalog/components/products/ProductComplianceSection.tsx +497 -0
- package/src/modules/catalog/components/products/VariantBuilder.tsx +43 -0
- package/src/modules/catalog/components/products/productForm.ts +262 -0
- package/src/modules/catalog/components/products/variantForm.ts +4 -0
- package/src/modules/catalog/data/entities.ts +97 -0
- package/src/modules/catalog/data/types.ts +36 -0
- package/src/modules/catalog/data/validators.ts +159 -3
- package/src/modules/catalog/i18n/de.json +64 -0
- package/src/modules/catalog/i18n/en.json +64 -0
- package/src/modules/catalog/i18n/es.json +64 -0
- package/src/modules/catalog/i18n/pl.json +64 -0
- package/src/modules/catalog/lib/gtin.ts +66 -0
- package/src/modules/catalog/migrations/.snapshot-open-mercato.json +457 -0
- package/src/modules/catalog/migrations/Migration20260611090000.ts +17 -0
- package/src/modules/catalog/translations.ts +1 -1
- package/src/modules/configs/api/cache/route.ts +72 -10
- package/src/modules/configs/components/CachePanel.tsx +59 -29
- package/src/modules/currencies/api/currencies/options/route.ts +22 -4
- package/src/modules/currencies/services/rateFetchingService.ts +63 -26
- package/src/modules/customer_accounts/backend/customer_accounts/users/[id]/page.tsx +18 -18
- package/src/modules/customer_accounts/backend/customer_accounts/users/page.tsx +1 -1
- package/src/modules/customer_accounts/i18n/de.json +1 -1
- package/src/modules/customer_accounts/i18n/en.json +1 -1
- package/src/modules/customer_accounts/i18n/es.json +1 -1
- package/src/modules/customer_accounts/i18n/pl.json +1 -1
- package/src/modules/customers/backend/calendar/page.meta.ts +23 -0
- package/src/modules/customers/backend/calendar/page.tsx +12 -0
- package/src/modules/customers/cli.ts +1 -0
- package/src/modules/customers/components/calendar/AgendaList.tsx +219 -0
- package/src/modules/customers/components/calendar/CalendarEventEditor.tsx +386 -0
- package/src/modules/customers/components/calendar/CalendarFooter.tsx +61 -0
- package/src/modules/customers/components/calendar/CalendarHeader.tsx +40 -0
- package/src/modules/customers/components/calendar/CalendarScreen.tsx +590 -0
- package/src/modules/customers/components/calendar/CalendarSettingsModal.tsx +207 -0
- package/src/modules/customers/components/calendar/CalendarTabs.tsx +63 -0
- package/src/modules/customers/components/calendar/CalendarToolbar.tsx +295 -0
- package/src/modules/customers/components/calendar/EventBlock.tsx +205 -0
- package/src/modules/customers/components/calendar/EventPeekPopover.tsx +100 -0
- package/src/modules/customers/components/calendar/MonthGrid.tsx +236 -0
- package/src/modules/customers/components/calendar/ShortcutsDialog.tsx +57 -0
- package/src/modules/customers/components/calendar/TimeGrid.tsx +510 -0
- package/src/modules/customers/components/calendar/UpcomingCards.tsx +220 -0
- package/src/modules/customers/components/calendar/editor/CategoryField.tsx +79 -0
- package/src/modules/customers/components/calendar/editor/LocationField.tsx +37 -0
- package/src/modules/customers/components/calendar/editor/PeopleField.tsx +148 -0
- package/src/modules/customers/components/calendar/editor/RelatedToField.tsx +242 -0
- package/src/modules/customers/components/calendar/editor/RepeatField.tsx +132 -0
- package/src/modules/customers/components/calendar/editor/ScheduleSection.tsx +110 -0
- package/src/modules/customers/components/calendar/editor/SegmentGroup.tsx +50 -0
- package/src/modules/customers/components/calendar/editor/hooks.ts +162 -0
- package/src/modules/customers/components/calendar/editor/inputs.tsx +158 -0
- package/src/modules/customers/components/calendar/editor/lookups.ts +160 -0
- package/src/modules/customers/components/calendar/types.ts +163 -0
- package/src/modules/customers/components/calendar/useCalendarItems.ts +162 -0
- package/src/modules/customers/components/calendar/useCalendarPreferences.ts +52 -0
- package/src/modules/customers/i18n/de.json +195 -0
- package/src/modules/customers/i18n/en.json +195 -0
- package/src/modules/customers/i18n/es.json +195 -0
- package/src/modules/customers/i18n/pl.json +195 -0
- package/src/modules/customers/lib/calendar/categories.ts +26 -0
- package/src/modules/customers/lib/calendar/conflicts.ts +117 -0
- package/src/modules/customers/lib/calendar/editorPayload.ts +388 -0
- package/src/modules/customers/lib/calendar/grid.ts +50 -0
- package/src/modules/customers/lib/calendar/layout.ts +47 -0
- package/src/modules/customers/lib/calendar/mapItem.ts +94 -0
- package/src/modules/customers/lib/calendar/preferences.ts +95 -0
- package/src/modules/customers/lib/calendar/range.ts +45 -0
- package/src/modules/customers/lib/calendar/recurrence.ts +137 -0
- package/src/modules/dashboards/services/widgetDataService.ts +8 -6
- package/src/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.ts +61 -1
- package/src/modules/dictionaries/api/[dictionaryId]/entries/route.ts +33 -1
- package/src/modules/dictionaries/api/[dictionaryId]/route.ts +65 -1
- package/src/modules/dictionaries/api/route.ts +36 -1
- package/src/modules/feature_toggles/lib/queries.ts +41 -60
- package/src/modules/messages/commands/actions.ts +100 -7
- package/src/modules/perspectives/api/[tableId]/[perspectiveId]/route.ts +34 -1
- package/src/modules/perspectives/api/[tableId]/roles/[roleId]/route.ts +34 -1
- package/src/modules/perspectives/api/[tableId]/route.ts +107 -32
- package/src/modules/perspectives/services/perspectiveService.ts +9 -5
- package/src/modules/planner/api/access.ts +9 -0
- package/src/modules/planner/api/availability-date-specific.ts +22 -1
- package/src/modules/planner/api/availability-weekly.ts +22 -1
- package/src/modules/resources/api/resources/tags/assign/route.ts +42 -0
- package/src/modules/resources/api/resources/tags/unassign/route.ts +42 -0
- package/src/modules/sales/api/settings/document-numbers/route.ts +35 -1
- package/src/modules/sales/api/settings/order-editing/route.ts +37 -3
- package/src/modules/staff/api/leave-requests/accept/route.ts +45 -0
- package/src/modules/staff/api/leave-requests/reject/route.ts +45 -0
- package/src/modules/staff/api/team-members/self/route.ts +43 -0
- package/src/modules/staff/api/team-members/tags/assign/route.ts +45 -0
- package/src/modules/staff/api/team-members/tags/unassign/route.ts +45 -0
- package/src/modules/translations/api/[entityType]/[entityId]/route.ts +65 -1
- package/src/modules/translations/api/context.ts +7 -0
- package/src/modules/translations/api/put/locales.ts +35 -1
- package/src/modules/workflows/backend/definitions/visual-editor/page.tsx +11 -9
- package/src/modules/workflows/backend/instances/[id]/page.tsx +1 -1
- package/src/modules/workflows/components/EdgeEditDialog.tsx +1 -1
- package/src/modules/workflows/components/EdgeEditDialogCrudForm.tsx +1 -1
- package/src/modules/workflows/components/NodeEditDialog.tsx +1 -1
- package/src/modules/workflows/components/NodeEditDialogCrudForm.tsx +1 -1
- package/src/modules/workflows/components/WorkflowGraph.tsx +2 -2
- package/src/modules/workflows/components/WorkflowGraphImpl.tsx +27 -13
- package/src/modules/workflows/components/mobile/MobileVisualEditor.tsx +3 -3
- package/src/modules/workflows/frontend/checkout-demo/deriveCurrentStep.ts +12 -0
- package/src/modules/workflows/frontend/checkout-demo/page.tsx +3 -23
- package/src/modules/workflows/lib/graph-utils.ts +21 -1
package/src/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.tsx
CHANGED
|
@@ -345,6 +345,18 @@ export default function EditVariantPage({ params }: { params?: { productId?: str
|
|
|
345
345
|
name: typeof record.name === 'string' ? record.name : '',
|
|
346
346
|
sku: typeof record.sku === 'string' ? record.sku : '',
|
|
347
347
|
barcode: typeof record.barcode === 'string' ? record.barcode : '',
|
|
348
|
+
gtinType:
|
|
349
|
+
typeof record.gtin_type === 'string'
|
|
350
|
+
? record.gtin_type
|
|
351
|
+
: typeof record.gtinType === 'string'
|
|
352
|
+
? record.gtinType
|
|
353
|
+
: null,
|
|
354
|
+
hsCode:
|
|
355
|
+
typeof record.hs_code === 'string'
|
|
356
|
+
? record.hs_code
|
|
357
|
+
: typeof record.hsCode === 'string'
|
|
358
|
+
? record.hsCode
|
|
359
|
+
: '',
|
|
348
360
|
isDefault: record.is_default === true || record.isDefault === true,
|
|
349
361
|
isActive: record.is_active !== false && record.isActive !== false,
|
|
350
362
|
optionValues: normalizedOptionValues,
|
|
@@ -600,6 +612,8 @@ export default function EditVariantPage({ params }: { params?: { productId?: str
|
|
|
600
612
|
name,
|
|
601
613
|
sku: values.sku?.trim() || undefined,
|
|
602
614
|
barcode: values.barcode?.trim() || undefined,
|
|
615
|
+
gtinType: values.gtinType ?? null,
|
|
616
|
+
hsCode: values.hsCode?.trim() || null,
|
|
603
617
|
isDefault: Boolean(values.isDefault),
|
|
604
618
|
isActive: values.isActive !== false,
|
|
605
619
|
optionValues: Object.keys(values.optionValues ?? {}).length ? values.optionValues : undefined,
|
|
@@ -367,6 +367,8 @@ export default function CreateVariantPage({ params }: { params?: { productId?: s
|
|
|
367
367
|
name,
|
|
368
368
|
sku: values.sku?.trim() || undefined,
|
|
369
369
|
barcode: values.barcode?.trim() || undefined,
|
|
370
|
+
gtinType: values.gtinType ?? null,
|
|
371
|
+
hsCode: values.hsCode?.trim() || null,
|
|
370
372
|
isDefault: Boolean(values.isDefault),
|
|
371
373
|
isActive: values.isActive !== false,
|
|
372
374
|
optionValues: Object.keys(values.optionValues ?? {}).length ? values.optionValues : undefined,
|
|
@@ -77,6 +77,7 @@ import {
|
|
|
77
77
|
updateDimensionValue,
|
|
78
78
|
updateWeightValue,
|
|
79
79
|
isConfigurableProductType,
|
|
80
|
+
buildComplianceProductPayload,
|
|
80
81
|
} from "@open-mercato/core/modules/catalog/components/products/productForm";
|
|
81
82
|
import { CATALOG_PRODUCT_TYPES } from "@open-mercato/core/modules/catalog/data/types";
|
|
82
83
|
import {
|
|
@@ -84,6 +85,7 @@ import {
|
|
|
84
85
|
slugifyAttachmentFileName,
|
|
85
86
|
} from "@open-mercato/core/modules/attachments/lib/imageUrls";
|
|
86
87
|
import { ProductUomSection } from "@open-mercato/core/modules/catalog/components/products/ProductUomSection";
|
|
88
|
+
import { ProductComplianceSection } from "@open-mercato/core/modules/catalog/components/products/ProductComplianceSection";
|
|
87
89
|
import { canonicalizeUnitCode } from "@open-mercato/core/modules/catalog/lib/unitCodes";
|
|
88
90
|
import {
|
|
89
91
|
UNIT_PRICE_REFERENCE_UNITS,
|
|
@@ -150,6 +152,33 @@ const STEP_FIELD_MATCHERS: Record<
|
|
|
150
152
|
matchField("unitPriceBaseQuantity"),
|
|
151
153
|
matchPrefix("unitConversions"),
|
|
152
154
|
],
|
|
155
|
+
compliance: [
|
|
156
|
+
matchField("countryOfOriginCode"),
|
|
157
|
+
matchField("pkwiuCode"),
|
|
158
|
+
matchField("cnCode"),
|
|
159
|
+
matchField("hsCode"),
|
|
160
|
+
matchField("taxClassificationCode"),
|
|
161
|
+
matchField("gtuCodes"),
|
|
162
|
+
matchField("ageMin"),
|
|
163
|
+
matchField("isExciseGood"),
|
|
164
|
+
matchField("exciseCategory"),
|
|
165
|
+
matchField("requiresPrescription"),
|
|
166
|
+
matchPrefix("hazmat"),
|
|
167
|
+
matchField("unNumber"),
|
|
168
|
+
matchField("containsLithiumBattery"),
|
|
169
|
+
matchField("launchAt"),
|
|
170
|
+
matchField("endOfLifeAt"),
|
|
171
|
+
matchField("availableFrom"),
|
|
172
|
+
matchField("availableUntil"),
|
|
173
|
+
matchField("minOrderQty"),
|
|
174
|
+
matchField("maxOrderQty"),
|
|
175
|
+
matchField("orderQtyIncrement"),
|
|
176
|
+
matchField("requiresShipping"),
|
|
177
|
+
matchField("isQuoteOnly"),
|
|
178
|
+
matchField("seoTitle"),
|
|
179
|
+
matchField("seoDescription"),
|
|
180
|
+
matchField("canonicalUrl"),
|
|
181
|
+
],
|
|
153
182
|
variants: [
|
|
154
183
|
matchField("hasVariants"),
|
|
155
184
|
matchPrefix("options"),
|
|
@@ -542,6 +571,7 @@ export default function CreateCatalogProductPage() {
|
|
|
542
571
|
unitPriceBaseQuantity: unitPriceEnabled
|
|
543
572
|
? unitPriceBaseQuantity
|
|
544
573
|
: undefined,
|
|
574
|
+
...buildComplianceProductPayload(formValues),
|
|
545
575
|
};
|
|
546
576
|
if (optionSchemaDefinition) {
|
|
547
577
|
productPayload.optionSchema = optionSchemaDefinition;
|
|
@@ -1312,6 +1342,8 @@ function ProductBuilder({
|
|
|
1312
1342
|
t("catalog.products.create.steps.organize", "Organize")}
|
|
1313
1343
|
{step === "uom" &&
|
|
1314
1344
|
t("catalog.products.uom.title", "Units of measure")}
|
|
1345
|
+
{step === "compliance" &&
|
|
1346
|
+
t("catalog.products.compliance.title", "Compliance & commerce")}
|
|
1315
1347
|
{step === "variants" &&
|
|
1316
1348
|
t("catalog.products.create.steps.variants", "Variants")}
|
|
1317
1349
|
{(stepErrors[step]?.length ?? 0) > 0 ? (
|
|
@@ -1434,6 +1466,15 @@ function ProductBuilder({
|
|
|
1434
1466
|
/>
|
|
1435
1467
|
) : null}
|
|
1436
1468
|
|
|
1469
|
+
{currentStepKey === "compliance" ? (
|
|
1470
|
+
<ProductComplianceSection
|
|
1471
|
+
values={values as ProductFormValues}
|
|
1472
|
+
setValue={setValue}
|
|
1473
|
+
errors={errors}
|
|
1474
|
+
embedded
|
|
1475
|
+
/>
|
|
1476
|
+
) : null}
|
|
1477
|
+
|
|
1437
1478
|
{currentStepKey === "variants" ? (
|
|
1438
1479
|
<div className="space-y-6">
|
|
1439
1480
|
<label className="flex items-center gap-2 text-sm font-medium">
|
|
@@ -52,6 +52,8 @@ import {
|
|
|
52
52
|
type ProductUpdateInput,
|
|
53
53
|
} from "../data/validators";
|
|
54
54
|
import type {
|
|
55
|
+
CatalogExciseCategory,
|
|
56
|
+
CatalogHazmatPackingGroup,
|
|
55
57
|
CatalogProductOptionSchema,
|
|
56
58
|
CatalogProductType,
|
|
57
59
|
} from "../data/types";
|
|
@@ -105,6 +107,32 @@ type ProductSnapshot = {
|
|
|
105
107
|
weightValue: string | null;
|
|
106
108
|
weightUnit: string | null;
|
|
107
109
|
dimensions: Record<string, unknown> | null;
|
|
110
|
+
countryOfOriginCode: string | null;
|
|
111
|
+
pkwiuCode: string | null;
|
|
112
|
+
cnCode: string | null;
|
|
113
|
+
hsCode: string | null;
|
|
114
|
+
taxClassificationCode: string | null;
|
|
115
|
+
gtuCodes: string[] | null;
|
|
116
|
+
ageMin: number | null;
|
|
117
|
+
isExciseGood: boolean;
|
|
118
|
+
exciseCategory: CatalogExciseCategory | null;
|
|
119
|
+
requiresPrescription: boolean;
|
|
120
|
+
hazmatClass: string | null;
|
|
121
|
+
unNumber: string | null;
|
|
122
|
+
hazmatPackingGroup: CatalogHazmatPackingGroup | null;
|
|
123
|
+
containsLithiumBattery: boolean;
|
|
124
|
+
launchAt: string | null;
|
|
125
|
+
endOfLifeAt: string | null;
|
|
126
|
+
availableFrom: string | null;
|
|
127
|
+
availableUntil: string | null;
|
|
128
|
+
minOrderQty: number | null;
|
|
129
|
+
maxOrderQty: number | null;
|
|
130
|
+
orderQtyIncrement: number | null;
|
|
131
|
+
requiresShipping: boolean;
|
|
132
|
+
isQuoteOnly: boolean;
|
|
133
|
+
seoTitle: string | null;
|
|
134
|
+
seoDescription: string | null;
|
|
135
|
+
canonicalUrl: string | null;
|
|
108
136
|
optionSchemaId: string | null;
|
|
109
137
|
customFieldsetCode: string | null;
|
|
110
138
|
metadata: Record<string, unknown> | null;
|
|
@@ -1154,6 +1182,36 @@ async function loadProductSnapshot(
|
|
|
1154
1182
|
weightValue,
|
|
1155
1183
|
weightUnit,
|
|
1156
1184
|
dimensions,
|
|
1185
|
+
countryOfOriginCode: record.countryOfOriginCode ?? null,
|
|
1186
|
+
pkwiuCode: record.pkwiuCode ?? null,
|
|
1187
|
+
cnCode: record.cnCode ?? null,
|
|
1188
|
+
hsCode: record.hsCode ?? null,
|
|
1189
|
+
taxClassificationCode: record.taxClassificationCode ?? null,
|
|
1190
|
+
gtuCodes: record.gtuCodes?.length ? [...record.gtuCodes] : null,
|
|
1191
|
+
ageMin: record.ageMin ?? null,
|
|
1192
|
+
isExciseGood: record.isExciseGood ?? false,
|
|
1193
|
+
exciseCategory: record.exciseCategory ?? null,
|
|
1194
|
+
requiresPrescription: record.requiresPrescription ?? false,
|
|
1195
|
+
hazmatClass: record.hazmatClass ?? null,
|
|
1196
|
+
unNumber: record.unNumber ?? null,
|
|
1197
|
+
hazmatPackingGroup: record.hazmatPackingGroup ?? null,
|
|
1198
|
+
containsLithiumBattery: record.containsLithiumBattery ?? false,
|
|
1199
|
+
launchAt: record.launchAt ? record.launchAt.toISOString() : null,
|
|
1200
|
+
endOfLifeAt: record.endOfLifeAt ? record.endOfLifeAt.toISOString() : null,
|
|
1201
|
+
availableFrom: record.availableFrom
|
|
1202
|
+
? record.availableFrom.toISOString()
|
|
1203
|
+
: null,
|
|
1204
|
+
availableUntil: record.availableUntil
|
|
1205
|
+
? record.availableUntil.toISOString()
|
|
1206
|
+
: null,
|
|
1207
|
+
minOrderQty: record.minOrderQty ?? null,
|
|
1208
|
+
maxOrderQty: record.maxOrderQty ?? null,
|
|
1209
|
+
orderQtyIncrement: record.orderQtyIncrement ?? null,
|
|
1210
|
+
requiresShipping: record.requiresShipping ?? true,
|
|
1211
|
+
isQuoteOnly: record.isQuoteOnly ?? false,
|
|
1212
|
+
seoTitle: record.seoTitle ?? null,
|
|
1213
|
+
seoDescription: record.seoDescription ?? null,
|
|
1214
|
+
canonicalUrl: record.canonicalUrl ?? null,
|
|
1157
1215
|
customFieldsetCode: record.customFieldsetCode ?? null,
|
|
1158
1216
|
metadata,
|
|
1159
1217
|
isConfigurable: record.isConfigurable,
|
|
@@ -1198,6 +1256,36 @@ function productSeedFromSnapshot(
|
|
|
1198
1256
|
weightValue: snapshot.weightValue ?? null,
|
|
1199
1257
|
weightUnit: snapshot.weightUnit ?? null,
|
|
1200
1258
|
dimensions: snapshot.dimensions ? cloneJson(snapshot.dimensions) : null,
|
|
1259
|
+
countryOfOriginCode: snapshot.countryOfOriginCode ?? null,
|
|
1260
|
+
pkwiuCode: snapshot.pkwiuCode ?? null,
|
|
1261
|
+
cnCode: snapshot.cnCode ?? null,
|
|
1262
|
+
hsCode: snapshot.hsCode ?? null,
|
|
1263
|
+
taxClassificationCode: snapshot.taxClassificationCode ?? null,
|
|
1264
|
+
gtuCodes: snapshot.gtuCodes ? [...snapshot.gtuCodes] : null,
|
|
1265
|
+
ageMin: snapshot.ageMin ?? null,
|
|
1266
|
+
isExciseGood: snapshot.isExciseGood ?? false,
|
|
1267
|
+
exciseCategory: snapshot.exciseCategory ?? null,
|
|
1268
|
+
requiresPrescription: snapshot.requiresPrescription ?? false,
|
|
1269
|
+
hazmatClass: snapshot.hazmatClass ?? null,
|
|
1270
|
+
unNumber: snapshot.unNumber ?? null,
|
|
1271
|
+
hazmatPackingGroup: snapshot.hazmatPackingGroup ?? null,
|
|
1272
|
+
containsLithiumBattery: snapshot.containsLithiumBattery ?? false,
|
|
1273
|
+
launchAt: snapshot.launchAt ? new Date(snapshot.launchAt) : null,
|
|
1274
|
+
endOfLifeAt: snapshot.endOfLifeAt ? new Date(snapshot.endOfLifeAt) : null,
|
|
1275
|
+
availableFrom: snapshot.availableFrom
|
|
1276
|
+
? new Date(snapshot.availableFrom)
|
|
1277
|
+
: null,
|
|
1278
|
+
availableUntil: snapshot.availableUntil
|
|
1279
|
+
? new Date(snapshot.availableUntil)
|
|
1280
|
+
: null,
|
|
1281
|
+
minOrderQty: snapshot.minOrderQty ?? null,
|
|
1282
|
+
maxOrderQty: snapshot.maxOrderQty ?? null,
|
|
1283
|
+
orderQtyIncrement: snapshot.orderQtyIncrement ?? null,
|
|
1284
|
+
requiresShipping: snapshot.requiresShipping ?? true,
|
|
1285
|
+
isQuoteOnly: snapshot.isQuoteOnly ?? false,
|
|
1286
|
+
seoTitle: snapshot.seoTitle ?? null,
|
|
1287
|
+
seoDescription: snapshot.seoDescription ?? null,
|
|
1288
|
+
canonicalUrl: snapshot.canonicalUrl ?? null,
|
|
1201
1289
|
metadata: snapshot.metadata ? cloneJson(snapshot.metadata) : null,
|
|
1202
1290
|
customFieldsetCode: snapshot.customFieldsetCode ?? null,
|
|
1203
1291
|
isConfigurable: snapshot.isConfigurable,
|
|
@@ -1239,6 +1327,38 @@ function applyProductSnapshot(
|
|
|
1239
1327
|
record.dimensions = snapshot.dimensions
|
|
1240
1328
|
? cloneJson(snapshot.dimensions)
|
|
1241
1329
|
: null;
|
|
1330
|
+
record.countryOfOriginCode = snapshot.countryOfOriginCode ?? null;
|
|
1331
|
+
record.pkwiuCode = snapshot.pkwiuCode ?? null;
|
|
1332
|
+
record.cnCode = snapshot.cnCode ?? null;
|
|
1333
|
+
record.hsCode = snapshot.hsCode ?? null;
|
|
1334
|
+
record.taxClassificationCode = snapshot.taxClassificationCode ?? null;
|
|
1335
|
+
record.gtuCodes = snapshot.gtuCodes ? [...snapshot.gtuCodes] : null;
|
|
1336
|
+
record.ageMin = snapshot.ageMin ?? null;
|
|
1337
|
+
record.isExciseGood = snapshot.isExciseGood ?? false;
|
|
1338
|
+
record.exciseCategory = snapshot.exciseCategory ?? null;
|
|
1339
|
+
record.requiresPrescription = snapshot.requiresPrescription ?? false;
|
|
1340
|
+
record.hazmatClass = snapshot.hazmatClass ?? null;
|
|
1341
|
+
record.unNumber = snapshot.unNumber ?? null;
|
|
1342
|
+
record.hazmatPackingGroup = snapshot.hazmatPackingGroup ?? null;
|
|
1343
|
+
record.containsLithiumBattery = snapshot.containsLithiumBattery ?? false;
|
|
1344
|
+
record.launchAt = snapshot.launchAt ? new Date(snapshot.launchAt) : null;
|
|
1345
|
+
record.endOfLifeAt = snapshot.endOfLifeAt
|
|
1346
|
+
? new Date(snapshot.endOfLifeAt)
|
|
1347
|
+
: null;
|
|
1348
|
+
record.availableFrom = snapshot.availableFrom
|
|
1349
|
+
? new Date(snapshot.availableFrom)
|
|
1350
|
+
: null;
|
|
1351
|
+
record.availableUntil = snapshot.availableUntil
|
|
1352
|
+
? new Date(snapshot.availableUntil)
|
|
1353
|
+
: null;
|
|
1354
|
+
record.minOrderQty = snapshot.minOrderQty ?? null;
|
|
1355
|
+
record.maxOrderQty = snapshot.maxOrderQty ?? null;
|
|
1356
|
+
record.orderQtyIncrement = snapshot.orderQtyIncrement ?? null;
|
|
1357
|
+
record.requiresShipping = snapshot.requiresShipping ?? true;
|
|
1358
|
+
record.isQuoteOnly = snapshot.isQuoteOnly ?? false;
|
|
1359
|
+
record.seoTitle = snapshot.seoTitle ?? null;
|
|
1360
|
+
record.seoDescription = snapshot.seoDescription ?? null;
|
|
1361
|
+
record.canonicalUrl = snapshot.canonicalUrl ?? null;
|
|
1242
1362
|
record.metadata = snapshot.metadata ? cloneJson(snapshot.metadata) : null;
|
|
1243
1363
|
record.customFieldsetCode = snapshot.customFieldsetCode ?? null;
|
|
1244
1364
|
record.optionSchemaTemplate = snapshot.optionSchemaId
|
|
@@ -1332,6 +1452,32 @@ const createProductCommand: CommandHandler<
|
|
|
1332
1452
|
weightValue,
|
|
1333
1453
|
weightUnit,
|
|
1334
1454
|
dimensions,
|
|
1455
|
+
countryOfOriginCode: parsed.countryOfOriginCode ?? null,
|
|
1456
|
+
pkwiuCode: parsed.pkwiuCode ?? null,
|
|
1457
|
+
cnCode: parsed.cnCode ?? null,
|
|
1458
|
+
hsCode: parsed.hsCode ?? null,
|
|
1459
|
+
taxClassificationCode: parsed.taxClassificationCode ?? null,
|
|
1460
|
+
gtuCodes: parsed.gtuCodes ?? null,
|
|
1461
|
+
ageMin: parsed.ageMin ?? null,
|
|
1462
|
+
isExciseGood: parsed.isExciseGood ?? false,
|
|
1463
|
+
exciseCategory: parsed.exciseCategory ?? null,
|
|
1464
|
+
requiresPrescription: parsed.requiresPrescription ?? false,
|
|
1465
|
+
hazmatClass: parsed.hazmatClass ?? null,
|
|
1466
|
+
unNumber: parsed.unNumber ?? null,
|
|
1467
|
+
hazmatPackingGroup: parsed.hazmatPackingGroup ?? null,
|
|
1468
|
+
containsLithiumBattery: parsed.containsLithiumBattery ?? false,
|
|
1469
|
+
launchAt: parsed.launchAt ?? null,
|
|
1470
|
+
endOfLifeAt: parsed.endOfLifeAt ?? null,
|
|
1471
|
+
availableFrom: parsed.availableFrom ?? null,
|
|
1472
|
+
availableUntil: parsed.availableUntil ?? null,
|
|
1473
|
+
minOrderQty: parsed.minOrderQty ?? null,
|
|
1474
|
+
maxOrderQty: parsed.maxOrderQty ?? null,
|
|
1475
|
+
orderQtyIncrement: parsed.orderQtyIncrement ?? null,
|
|
1476
|
+
requiresShipping: parsed.requiresShipping ?? true,
|
|
1477
|
+
isQuoteOnly: parsed.isQuoteOnly ?? false,
|
|
1478
|
+
seoTitle: parsed.seoTitle ?? null,
|
|
1479
|
+
seoDescription: parsed.seoDescription ?? null,
|
|
1480
|
+
canonicalUrl: parsed.canonicalUrl ?? null,
|
|
1335
1481
|
metadata,
|
|
1336
1482
|
customFieldsetCode: parsed.customFieldsetCode ?? null,
|
|
1337
1483
|
isConfigurable: parsed.isConfigurable ?? false,
|
|
@@ -1551,6 +1697,75 @@ const updateProductCommand: CommandHandler<
|
|
|
1551
1697
|
ensureTenantScope(ctx, tenantId);
|
|
1552
1698
|
ensureOrganizationScope(ctx, organizationId);
|
|
1553
1699
|
ensureSameScope(record, organizationId, tenantId);
|
|
1700
|
+
// Re-validate range constraints against the merged record state: a partial
|
|
1701
|
+
// update may carry only one half of a (min, max) or (from, until) pair
|
|
1702
|
+
// while the other half is stored — payload-level zod cannot see it.
|
|
1703
|
+
const mergedRangeViolation = (() => {
|
|
1704
|
+
const minOrderQty =
|
|
1705
|
+
parsed.minOrderQty !== undefined
|
|
1706
|
+
? (parsed.minOrderQty ?? null)
|
|
1707
|
+
: (record.minOrderQty ?? null);
|
|
1708
|
+
const maxOrderQty =
|
|
1709
|
+
parsed.maxOrderQty !== undefined
|
|
1710
|
+
? (parsed.maxOrderQty ?? null)
|
|
1711
|
+
: (record.maxOrderQty ?? null);
|
|
1712
|
+
if (minOrderQty != null && maxOrderQty != null && maxOrderQty < minOrderQty) {
|
|
1713
|
+
return {
|
|
1714
|
+
field: "maxOrderQty",
|
|
1715
|
+
key: "catalog.products.validation.orderQtyRange",
|
|
1716
|
+
fallback: "Maximum order quantity cannot be lower than the minimum.",
|
|
1717
|
+
};
|
|
1718
|
+
}
|
|
1719
|
+
const launchAt =
|
|
1720
|
+
parsed.launchAt !== undefined
|
|
1721
|
+
? (parsed.launchAt ?? null)
|
|
1722
|
+
: (record.launchAt ?? null);
|
|
1723
|
+
const endOfLifeAt =
|
|
1724
|
+
parsed.endOfLifeAt !== undefined
|
|
1725
|
+
? (parsed.endOfLifeAt ?? null)
|
|
1726
|
+
: (record.endOfLifeAt ?? null);
|
|
1727
|
+
if (launchAt && endOfLifeAt && endOfLifeAt < launchAt) {
|
|
1728
|
+
return {
|
|
1729
|
+
field: "endOfLifeAt",
|
|
1730
|
+
key: "catalog.products.validation.lifecycleDateRange",
|
|
1731
|
+
fallback: "End of life cannot be before the launch date.",
|
|
1732
|
+
};
|
|
1733
|
+
}
|
|
1734
|
+
const availableFrom =
|
|
1735
|
+
parsed.availableFrom !== undefined
|
|
1736
|
+
? (parsed.availableFrom ?? null)
|
|
1737
|
+
: (record.availableFrom ?? null);
|
|
1738
|
+
const availableUntil =
|
|
1739
|
+
parsed.availableUntil !== undefined
|
|
1740
|
+
? (parsed.availableUntil ?? null)
|
|
1741
|
+
: (record.availableUntil ?? null);
|
|
1742
|
+
if (availableFrom && availableUntil && availableUntil < availableFrom) {
|
|
1743
|
+
return {
|
|
1744
|
+
field: "availableUntil",
|
|
1745
|
+
key: "catalog.products.validation.availabilityDateRange",
|
|
1746
|
+
fallback: "Available until cannot be before available from.",
|
|
1747
|
+
};
|
|
1748
|
+
}
|
|
1749
|
+
return null;
|
|
1750
|
+
})();
|
|
1751
|
+
if (mergedRangeViolation) {
|
|
1752
|
+
const message = translate(
|
|
1753
|
+
mergedRangeViolation.key,
|
|
1754
|
+
mergedRangeViolation.fallback,
|
|
1755
|
+
);
|
|
1756
|
+
throw new CrudHttpError(400, {
|
|
1757
|
+
error: message,
|
|
1758
|
+
fieldErrors: { [mergedRangeViolation.field]: message },
|
|
1759
|
+
details: [
|
|
1760
|
+
{
|
|
1761
|
+
path: [mergedRangeViolation.field],
|
|
1762
|
+
message,
|
|
1763
|
+
code: "invalid",
|
|
1764
|
+
origin: "validation",
|
|
1765
|
+
},
|
|
1766
|
+
],
|
|
1767
|
+
});
|
|
1768
|
+
}
|
|
1554
1769
|
const dataEngine = ctx.container.resolve("dataEngine") as DataEngine;
|
|
1555
1770
|
const lookupEm = em.fork();
|
|
1556
1771
|
const taxRateProvided =
|
|
@@ -1717,6 +1932,55 @@ const updateProductCommand: CommandHandler<
|
|
|
1717
1932
|
if (parsed.isConfigurable !== undefined)
|
|
1718
1933
|
record.isConfigurable = parsed.isConfigurable;
|
|
1719
1934
|
if (parsed.isActive !== undefined) record.isActive = parsed.isActive;
|
|
1935
|
+
if (parsed.countryOfOriginCode !== undefined)
|
|
1936
|
+
record.countryOfOriginCode = parsed.countryOfOriginCode ?? null;
|
|
1937
|
+
if (parsed.pkwiuCode !== undefined)
|
|
1938
|
+
record.pkwiuCode = parsed.pkwiuCode ?? null;
|
|
1939
|
+
if (parsed.cnCode !== undefined) record.cnCode = parsed.cnCode ?? null;
|
|
1940
|
+
if (parsed.hsCode !== undefined) record.hsCode = parsed.hsCode ?? null;
|
|
1941
|
+
if (parsed.taxClassificationCode !== undefined)
|
|
1942
|
+
record.taxClassificationCode = parsed.taxClassificationCode ?? null;
|
|
1943
|
+
if (parsed.gtuCodes !== undefined)
|
|
1944
|
+
record.gtuCodes = parsed.gtuCodes ?? null;
|
|
1945
|
+
if (parsed.ageMin !== undefined) record.ageMin = parsed.ageMin ?? null;
|
|
1946
|
+
if (parsed.isExciseGood !== undefined)
|
|
1947
|
+
record.isExciseGood = parsed.isExciseGood;
|
|
1948
|
+
if (parsed.exciseCategory !== undefined)
|
|
1949
|
+
record.exciseCategory = parsed.exciseCategory ?? null;
|
|
1950
|
+
if (parsed.requiresPrescription !== undefined)
|
|
1951
|
+
record.requiresPrescription = parsed.requiresPrescription;
|
|
1952
|
+
if (parsed.hazmatClass !== undefined)
|
|
1953
|
+
record.hazmatClass = parsed.hazmatClass ?? null;
|
|
1954
|
+
if (parsed.unNumber !== undefined)
|
|
1955
|
+
record.unNumber = parsed.unNumber ?? null;
|
|
1956
|
+
if (parsed.hazmatPackingGroup !== undefined)
|
|
1957
|
+
record.hazmatPackingGroup = parsed.hazmatPackingGroup ?? null;
|
|
1958
|
+
if (parsed.containsLithiumBattery !== undefined)
|
|
1959
|
+
record.containsLithiumBattery = parsed.containsLithiumBattery;
|
|
1960
|
+
if (parsed.launchAt !== undefined)
|
|
1961
|
+
record.launchAt = parsed.launchAt ?? null;
|
|
1962
|
+
if (parsed.endOfLifeAt !== undefined)
|
|
1963
|
+
record.endOfLifeAt = parsed.endOfLifeAt ?? null;
|
|
1964
|
+
if (parsed.availableFrom !== undefined)
|
|
1965
|
+
record.availableFrom = parsed.availableFrom ?? null;
|
|
1966
|
+
if (parsed.availableUntil !== undefined)
|
|
1967
|
+
record.availableUntil = parsed.availableUntil ?? null;
|
|
1968
|
+
if (parsed.minOrderQty !== undefined)
|
|
1969
|
+
record.minOrderQty = parsed.minOrderQty ?? null;
|
|
1970
|
+
if (parsed.maxOrderQty !== undefined)
|
|
1971
|
+
record.maxOrderQty = parsed.maxOrderQty ?? null;
|
|
1972
|
+
if (parsed.orderQtyIncrement !== undefined)
|
|
1973
|
+
record.orderQtyIncrement = parsed.orderQtyIncrement ?? null;
|
|
1974
|
+
if (parsed.requiresShipping !== undefined)
|
|
1975
|
+
record.requiresShipping = parsed.requiresShipping;
|
|
1976
|
+
if (parsed.isQuoteOnly !== undefined)
|
|
1977
|
+
record.isQuoteOnly = parsed.isQuoteOnly;
|
|
1978
|
+
if (parsed.seoTitle !== undefined)
|
|
1979
|
+
record.seoTitle = parsed.seoTitle ?? null;
|
|
1980
|
+
if (parsed.seoDescription !== undefined)
|
|
1981
|
+
record.seoDescription = parsed.seoDescription ?? null;
|
|
1982
|
+
if (parsed.canonicalUrl !== undefined)
|
|
1983
|
+
record.canonicalUrl = parsed.canonicalUrl ?? null;
|
|
1720
1984
|
try {
|
|
1721
1985
|
await withAtomicFlush(
|
|
1722
1986
|
em,
|
|
@@ -1757,6 +2021,19 @@ const updateProductCommand: CommandHandler<
|
|
|
1757
2021
|
const after = snapshots.after as ProductSnapshot | undefined;
|
|
1758
2022
|
if (!before || !after) return null;
|
|
1759
2023
|
const { translate } = await resolveTranslations();
|
|
2024
|
+
// buildChanges compares with strict !== — array fields need a serialized
|
|
2025
|
+
// comparison or every update would emit a phantom gtuCodes diff.
|
|
2026
|
+
const beforeGtuCodes = (before.gtuCodes ?? []).join(",");
|
|
2027
|
+
const afterGtuCodes = (after.gtuCodes ?? []).join(",");
|
|
2028
|
+
const gtuCodesChange =
|
|
2029
|
+
beforeGtuCodes !== afterGtuCodes
|
|
2030
|
+
? {
|
|
2031
|
+
gtuCodes: {
|
|
2032
|
+
from: before.gtuCodes ?? null,
|
|
2033
|
+
to: after.gtuCodes ?? null,
|
|
2034
|
+
},
|
|
2035
|
+
}
|
|
2036
|
+
: null;
|
|
1760
2037
|
return {
|
|
1761
2038
|
actionLabel: translate(
|
|
1762
2039
|
"catalog.audit.products.update",
|
|
@@ -1766,7 +2043,8 @@ const updateProductCommand: CommandHandler<
|
|
|
1766
2043
|
resourceId: before.id,
|
|
1767
2044
|
tenantId: before.tenantId,
|
|
1768
2045
|
organizationId: before.organizationId,
|
|
1769
|
-
changes:
|
|
2046
|
+
changes: {
|
|
2047
|
+
...buildChanges(before, after, [
|
|
1770
2048
|
"title",
|
|
1771
2049
|
"sku",
|
|
1772
2050
|
"productType",
|
|
@@ -1779,7 +2057,34 @@ const updateProductCommand: CommandHandler<
|
|
|
1779
2057
|
"unitPriceReferenceUnit",
|
|
1780
2058
|
"unitPriceBaseQuantity",
|
|
1781
2059
|
"isActive",
|
|
1782
|
-
|
|
2060
|
+
"countryOfOriginCode",
|
|
2061
|
+
"pkwiuCode",
|
|
2062
|
+
"cnCode",
|
|
2063
|
+
"hsCode",
|
|
2064
|
+
"taxClassificationCode",
|
|
2065
|
+
"ageMin",
|
|
2066
|
+
"isExciseGood",
|
|
2067
|
+
"exciseCategory",
|
|
2068
|
+
"requiresPrescription",
|
|
2069
|
+
"hazmatClass",
|
|
2070
|
+
"unNumber",
|
|
2071
|
+
"hazmatPackingGroup",
|
|
2072
|
+
"containsLithiumBattery",
|
|
2073
|
+
"launchAt",
|
|
2074
|
+
"endOfLifeAt",
|
|
2075
|
+
"availableFrom",
|
|
2076
|
+
"availableUntil",
|
|
2077
|
+
"minOrderQty",
|
|
2078
|
+
"maxOrderQty",
|
|
2079
|
+
"orderQtyIncrement",
|
|
2080
|
+
"requiresShipping",
|
|
2081
|
+
"isQuoteOnly",
|
|
2082
|
+
"seoTitle",
|
|
2083
|
+
"seoDescription",
|
|
2084
|
+
"canonicalUrl",
|
|
2085
|
+
]),
|
|
2086
|
+
...gtuCodesChange,
|
|
2087
|
+
},
|
|
1783
2088
|
snapshotBefore: before,
|
|
1784
2089
|
snapshotAfter: after,
|
|
1785
2090
|
payload: {
|
|
@@ -25,6 +25,8 @@ import {
|
|
|
25
25
|
type VariantCreateInput,
|
|
26
26
|
type VariantUpdateInput,
|
|
27
27
|
} from '../data/validators'
|
|
28
|
+
import { isValidGtin, normalizeGtinValue } from '../lib/gtin'
|
|
29
|
+
import type { CatalogGtinType } from '../data/types'
|
|
28
30
|
import {
|
|
29
31
|
cloneJson,
|
|
30
32
|
commandActorScope,
|
|
@@ -59,6 +61,8 @@ type VariantSnapshot = {
|
|
|
59
61
|
name: string | null
|
|
60
62
|
sku: string | null
|
|
61
63
|
barcode: string | null
|
|
64
|
+
gtinType: CatalogGtinType | null
|
|
65
|
+
hsCode: string | null
|
|
62
66
|
statusEntryId: string | null
|
|
63
67
|
isDefault: boolean
|
|
64
68
|
isActive: boolean
|
|
@@ -86,6 +90,8 @@ const VARIANT_CHANGE_KEYS = [
|
|
|
86
90
|
'name',
|
|
87
91
|
'sku',
|
|
88
92
|
'barcode',
|
|
93
|
+
'gtinType',
|
|
94
|
+
'hsCode',
|
|
89
95
|
'statusEntryId',
|
|
90
96
|
'isDefault',
|
|
91
97
|
'isActive',
|
|
@@ -127,6 +133,8 @@ async function loadVariantSnapshot(
|
|
|
127
133
|
name: record.name ?? null,
|
|
128
134
|
sku: record.sku ?? null,
|
|
129
135
|
barcode: record.barcode ?? null,
|
|
136
|
+
gtinType: record.gtinType ?? null,
|
|
137
|
+
hsCode: record.hsCode ?? null,
|
|
130
138
|
statusEntryId: record.statusEntryId ?? null,
|
|
131
139
|
isDefault: record.isDefault,
|
|
132
140
|
isActive: record.isActive,
|
|
@@ -154,6 +162,8 @@ function variantSeedFromSnapshot(snapshot: VariantSnapshot): Record<string, unkn
|
|
|
154
162
|
name: snapshot.name ?? null,
|
|
155
163
|
sku: snapshot.sku ?? null,
|
|
156
164
|
barcode: snapshot.barcode ?? null,
|
|
165
|
+
gtinType: snapshot.gtinType ?? null,
|
|
166
|
+
hsCode: snapshot.hsCode ?? null,
|
|
157
167
|
statusEntryId: snapshot.statusEntryId ?? null,
|
|
158
168
|
isDefault: snapshot.isDefault,
|
|
159
169
|
isActive: snapshot.isActive,
|
|
@@ -176,6 +186,8 @@ function applyVariantSnapshot(record: CatalogProductVariant, snapshot: VariantSn
|
|
|
176
186
|
record.name = snapshot.name ?? null
|
|
177
187
|
record.sku = snapshot.sku ?? null
|
|
178
188
|
record.barcode = snapshot.barcode ?? null
|
|
189
|
+
record.gtinType = snapshot.gtinType ?? null
|
|
190
|
+
record.hsCode = snapshot.hsCode ?? null
|
|
179
191
|
record.statusEntryId = snapshot.statusEntryId ?? null
|
|
180
192
|
record.isDefault = snapshot.isDefault
|
|
181
193
|
record.isActive = snapshot.isActive
|
|
@@ -592,6 +604,12 @@ const createVariantCommand: CommandHandler<VariantCreateInput, { variantId: stri
|
|
|
592
604
|
const resolvedOptionValues =
|
|
593
605
|
parsed.optionValues ?? (metadataSplit.hadOptionValues ? metadataSplit.optionValues : null)
|
|
594
606
|
|
|
607
|
+
const gtinType = parsed.gtinType ?? null
|
|
608
|
+
const barcode =
|
|
609
|
+
gtinType && parsed.barcode
|
|
610
|
+
? normalizeGtinValue(gtinType, parsed.barcode)
|
|
611
|
+
: (parsed.barcode ?? null)
|
|
612
|
+
|
|
595
613
|
const now = new Date()
|
|
596
614
|
const record = em.create(CatalogProductVariant, {
|
|
597
615
|
organizationId: product.organizationId,
|
|
@@ -599,7 +617,9 @@ const createVariantCommand: CommandHandler<VariantCreateInput, { variantId: stri
|
|
|
599
617
|
product,
|
|
600
618
|
name: parsed.name ?? null,
|
|
601
619
|
sku: parsed.sku ?? null,
|
|
602
|
-
barcode
|
|
620
|
+
barcode,
|
|
621
|
+
gtinType,
|
|
622
|
+
hsCode: parsed.hsCode ?? null,
|
|
603
623
|
statusEntryId: parsed.statusEntryId ?? null,
|
|
604
624
|
isDefault: parsed.isDefault ?? false,
|
|
605
625
|
isActive: parsed.isActive ?? true,
|
|
@@ -812,6 +832,27 @@ const updateVariantCommand: CommandHandler<VariantUpdateInput, { variantId: stri
|
|
|
812
832
|
? await resolveVariantTaxRate(em, product, parsed.taxRateId ?? null, parsed.taxRate)
|
|
813
833
|
: null
|
|
814
834
|
|
|
835
|
+
// Re-validate GTIN against the merged record state: a partial update may
|
|
836
|
+
// carry only one of (gtinType, barcode) while the other half is stored.
|
|
837
|
+
let normalizedGtinBarcode: string | null = null
|
|
838
|
+
if (parsed.gtinType !== undefined || parsed.barcode !== undefined) {
|
|
839
|
+
const effectiveGtinType =
|
|
840
|
+
parsed.gtinType !== undefined ? (parsed.gtinType ?? null) : (record.gtinType ?? null)
|
|
841
|
+
if (effectiveGtinType) {
|
|
842
|
+
const effectiveBarcode =
|
|
843
|
+
parsed.barcode !== undefined ? (parsed.barcode ?? null) : (record.barcode ?? null)
|
|
844
|
+
if (!effectiveBarcode || !effectiveBarcode.trim().length) {
|
|
845
|
+
await throwGtinBarcodeRequiredError()
|
|
846
|
+
} else {
|
|
847
|
+
const normalized = normalizeGtinValue(effectiveGtinType, effectiveBarcode)
|
|
848
|
+
if (!isValidGtin(effectiveGtinType, normalized)) {
|
|
849
|
+
await throwInvalidGtinError()
|
|
850
|
+
}
|
|
851
|
+
normalizedGtinBarcode = normalized
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
|
|
815
856
|
let previousDefaultVariantId: string | null = null
|
|
816
857
|
try {
|
|
817
858
|
await withAtomicFlush(
|
|
@@ -821,6 +862,9 @@ const updateVariantCommand: CommandHandler<VariantUpdateInput, { variantId: stri
|
|
|
821
862
|
if (parsed.name !== undefined) record.name = parsed.name ?? null
|
|
822
863
|
if (parsed.sku !== undefined) record.sku = parsed.sku ?? null
|
|
823
864
|
if (parsed.barcode !== undefined) record.barcode = parsed.barcode ?? null
|
|
865
|
+
if (parsed.gtinType !== undefined) record.gtinType = parsed.gtinType ?? null
|
|
866
|
+
if (parsed.hsCode !== undefined) record.hsCode = parsed.hsCode ?? null
|
|
867
|
+
if (normalizedGtinBarcode !== null) record.barcode = normalizedGtinBarcode
|
|
824
868
|
if (parsed.statusEntryId !== undefined) record.statusEntryId = parsed.statusEntryId ?? null
|
|
825
869
|
if (parsed.isDefault !== undefined) record.isDefault = parsed.isDefault
|
|
826
870
|
if (parsed.isActive !== undefined) record.isActive = parsed.isActive
|
|
@@ -1159,6 +1203,45 @@ async function throwDuplicateVariantSkuError(): Promise<never> {
|
|
|
1159
1203
|
})
|
|
1160
1204
|
}
|
|
1161
1205
|
|
|
1206
|
+
async function throwDuplicateVariantGtinError(): Promise<never> {
|
|
1207
|
+
const { translate } = await resolveTranslations()
|
|
1208
|
+
const message = translate(
|
|
1209
|
+
'catalog.variants.errors.gtinExists',
|
|
1210
|
+
'Another variant already uses this identifier.',
|
|
1211
|
+
)
|
|
1212
|
+
throw new CrudHttpError(400, {
|
|
1213
|
+
error: message,
|
|
1214
|
+
fieldErrors: { barcode: message },
|
|
1215
|
+
details: [{ path: ['barcode'], message, code: 'duplicate', origin: 'validation' }],
|
|
1216
|
+
})
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
async function throwGtinBarcodeRequiredError(): Promise<never> {
|
|
1220
|
+
const { translate } = await resolveTranslations()
|
|
1221
|
+
const message = translate(
|
|
1222
|
+
'catalog.variants.validation.gtinBarcodeRequired',
|
|
1223
|
+
'A barcode value is required when an identifier type is set.',
|
|
1224
|
+
)
|
|
1225
|
+
throw new CrudHttpError(400, {
|
|
1226
|
+
error: message,
|
|
1227
|
+
fieldErrors: { barcode: message },
|
|
1228
|
+
details: [{ path: ['barcode'], message, code: 'invalid', origin: 'validation' }],
|
|
1229
|
+
})
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
async function throwInvalidGtinError(): Promise<never> {
|
|
1233
|
+
const { translate } = await resolveTranslations()
|
|
1234
|
+
const message = translate(
|
|
1235
|
+
'catalog.variants.validation.gtinChecksum',
|
|
1236
|
+
'The barcode does not match the selected identifier type.',
|
|
1237
|
+
)
|
|
1238
|
+
throw new CrudHttpError(400, {
|
|
1239
|
+
error: message,
|
|
1240
|
+
fieldErrors: { barcode: message },
|
|
1241
|
+
details: [{ path: ['barcode'], message, code: 'invalid', origin: 'validation' }],
|
|
1242
|
+
})
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1162
1245
|
async function rethrowVariantUniqueConstraint(error: unknown): Promise<never> {
|
|
1163
1246
|
if (error instanceof UniqueConstraintViolationException) {
|
|
1164
1247
|
const constraint = getErrorConstraint(error)
|
|
@@ -1169,6 +1252,12 @@ async function rethrowVariantUniqueConstraint(error: unknown): Promise<never> {
|
|
|
1169
1252
|
) {
|
|
1170
1253
|
await throwDuplicateVariantSkuError()
|
|
1171
1254
|
}
|
|
1255
|
+
if (
|
|
1256
|
+
constraint === 'catalog_product_variants_gtin_scope_unique' ||
|
|
1257
|
+
message.includes('catalog_product_variants_gtin_scope_unique')
|
|
1258
|
+
) {
|
|
1259
|
+
await throwDuplicateVariantGtinError()
|
|
1260
|
+
}
|
|
1172
1261
|
}
|
|
1173
1262
|
throw error
|
|
1174
1263
|
}
|