@endora-commerce/mod-catalog 0.100.0
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/LICENSE +21 -0
- package/README.md +63 -0
- package/dist/admin/components/PackagingUnitsEditor.d.ts +12 -0
- package/dist/admin/components/PackagingUnitsEditor.d.ts.map +1 -0
- package/dist/admin/components/PackagingUnitsEditor.js +99 -0
- package/dist/admin/components/PackagingUnitsEditor.js.map +1 -0
- package/dist/admin/components/ProductAttributesTab.d.ts +13 -0
- package/dist/admin/components/ProductAttributesTab.d.ts.map +1 -0
- package/dist/admin/components/ProductAttributesTab.js +140 -0
- package/dist/admin/components/ProductAttributesTab.js.map +1 -0
- package/dist/admin/components/ProductInventoryTab.d.ts +14 -0
- package/dist/admin/components/ProductInventoryTab.d.ts.map +1 -0
- package/dist/admin/components/ProductInventoryTab.js +284 -0
- package/dist/admin/components/ProductInventoryTab.js.map +1 -0
- package/dist/admin/components/ProductScopeEditor.d.ts +94 -0
- package/dist/admin/components/ProductScopeEditor.d.ts.map +1 -0
- package/dist/admin/components/ProductScopeEditor.js +395 -0
- package/dist/admin/components/ProductScopeEditor.js.map +1 -0
- package/dist/admin/components/ProductsBulkEditDialog.d.ts +11 -0
- package/dist/admin/components/ProductsBulkEditDialog.d.ts.map +1 -0
- package/dist/admin/components/ProductsBulkEditDialog.js +304 -0
- package/dist/admin/components/ProductsBulkEditDialog.js.map +1 -0
- package/dist/admin/index.d.ts +45 -0
- package/dist/admin/index.d.ts.map +1 -0
- package/dist/admin/index.js +184 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/admin/lib/resolve-product-selection.d.ts +21 -0
- package/dist/admin/lib/resolve-product-selection.d.ts.map +1 -0
- package/dist/admin/lib/resolve-product-selection.js +25 -0
- package/dist/admin/lib/resolve-product-selection.js.map +1 -0
- package/dist/admin/pages/AttachmentTypesPage.d.ts +18 -0
- package/dist/admin/pages/AttachmentTypesPage.d.ts.map +1 -0
- package/dist/admin/pages/AttachmentTypesPage.js +92 -0
- package/dist/admin/pages/AttachmentTypesPage.js.map +1 -0
- package/dist/admin/pages/AttributeSetsPage.d.ts +21 -0
- package/dist/admin/pages/AttributeSetsPage.d.ts.map +1 -0
- package/dist/admin/pages/AttributeSetsPage.js +158 -0
- package/dist/admin/pages/AttributeSetsPage.js.map +1 -0
- package/dist/admin/pages/AttributesManager.d.ts +9 -0
- package/dist/admin/pages/AttributesManager.d.ts.map +1 -0
- package/dist/admin/pages/AttributesManager.js +305 -0
- package/dist/admin/pages/AttributesManager.js.map +1 -0
- package/dist/admin/pages/BulkOperationDetailPage.d.ts +9 -0
- package/dist/admin/pages/BulkOperationDetailPage.d.ts.map +1 -0
- package/dist/admin/pages/BulkOperationDetailPage.js +187 -0
- package/dist/admin/pages/BulkOperationDetailPage.js.map +1 -0
- package/dist/admin/pages/BulkOperationsPage.d.ts +9 -0
- package/dist/admin/pages/BulkOperationsPage.d.ts.map +1 -0
- package/dist/admin/pages/BulkOperationsPage.js +98 -0
- package/dist/admin/pages/BulkOperationsPage.js.map +1 -0
- package/dist/admin/pages/CategoriesTree.d.ts +9 -0
- package/dist/admin/pages/CategoriesTree.d.ts.map +1 -0
- package/dist/admin/pages/CategoriesTree.js +176 -0
- package/dist/admin/pages/CategoriesTree.js.map +1 -0
- package/dist/admin/pages/ProductEditor.d.ts +13 -0
- package/dist/admin/pages/ProductEditor.d.ts.map +1 -0
- package/dist/admin/pages/ProductEditor.js +1050 -0
- package/dist/admin/pages/ProductEditor.js.map +1 -0
- package/dist/admin/pages/ProductsList.d.ts +9 -0
- package/dist/admin/pages/ProductsList.d.ts.map +1 -0
- package/dist/admin/pages/ProductsList.js +399 -0
- package/dist/admin/pages/ProductsList.js.map +1 -0
- package/dist/backend/commands/attribute-commands.d.ts +111 -0
- package/dist/backend/commands/attribute-commands.d.ts.map +1 -0
- package/dist/backend/commands/attribute-commands.js +482 -0
- package/dist/backend/commands/attribute-commands.js.map +1 -0
- package/dist/backend/demo/reset.d.ts +23 -0
- package/dist/backend/demo/reset.d.ts.map +1 -0
- package/dist/backend/demo/reset.js +41 -0
- package/dist/backend/demo/reset.js.map +1 -0
- package/dist/backend/demo/rows.d.ts +103 -0
- package/dist/backend/demo/rows.d.ts.map +1 -0
- package/dist/backend/demo/rows.js +162 -0
- package/dist/backend/demo/rows.js.map +1 -0
- package/dist/backend/demo/seed.d.ts +35 -0
- package/dist/backend/demo/seed.d.ts.map +1 -0
- package/dist/backend/demo/seed.js +177 -0
- package/dist/backend/demo/seed.js.map +1 -0
- package/dist/backend/entities/attachment-type.entity.d.ts +16 -0
- package/dist/backend/entities/attachment-type.entity.d.ts.map +1 -0
- package/dist/backend/entities/attachment-type.entity.js +57 -0
- package/dist/backend/entities/attachment-type.entity.js.map +1 -0
- package/dist/backend/entities/attribute-set-attribute.entity.d.ts +26 -0
- package/dist/backend/entities/attribute-set-attribute.entity.d.ts.map +1 -0
- package/dist/backend/entities/attribute-set-attribute.entity.js +55 -0
- package/dist/backend/entities/attribute-set-attribute.entity.js.map +1 -0
- package/dist/backend/entities/attribute-set.entity.d.ts +22 -0
- package/dist/backend/entities/attribute-set.entity.d.ts.map +1 -0
- package/dist/backend/entities/attribute-set.entity.js +67 -0
- package/dist/backend/entities/attribute-set.entity.js.map +1 -0
- package/dist/backend/entities/bulk-operation.entity.d.ts +78 -0
- package/dist/backend/entities/bulk-operation.entity.d.ts.map +1 -0
- package/dist/backend/entities/bulk-operation.entity.js +138 -0
- package/dist/backend/entities/bulk-operation.entity.js.map +1 -0
- package/dist/backend/entities/bundle-slot-option.entity.d.ts +23 -0
- package/dist/backend/entities/bundle-slot-option.entity.d.ts.map +1 -0
- package/dist/backend/entities/bundle-slot-option.entity.js +68 -0
- package/dist/backend/entities/bundle-slot-option.entity.js.map +1 -0
- package/dist/backend/entities/bundle-slot.entity.d.ts +25 -0
- package/dist/backend/entities/bundle-slot.entity.d.ts.map +1 -0
- package/dist/backend/entities/bundle-slot.entity.js +74 -0
- package/dist/backend/entities/bundle-slot.entity.js.map +1 -0
- package/dist/backend/entities/category.entity.d.ts +26 -0
- package/dist/backend/entities/category.entity.d.ts.map +1 -0
- package/dist/backend/entities/category.entity.js +119 -0
- package/dist/backend/entities/category.entity.js.map +1 -0
- package/dist/backend/entities/gallery-item-label.entity.d.ts +22 -0
- package/dist/backend/entities/gallery-item-label.entity.d.ts.map +1 -0
- package/dist/backend/entities/gallery-item-label.entity.js +50 -0
- package/dist/backend/entities/gallery-item-label.entity.js.map +1 -0
- package/dist/backend/entities/gallery-item.entity.d.ts +17 -0
- package/dist/backend/entities/gallery-item.entity.d.ts.map +1 -0
- package/dist/backend/entities/gallery-item.entity.js +58 -0
- package/dist/backend/entities/gallery-item.entity.js.map +1 -0
- package/dist/backend/entities/grouped-item.entity.d.ts +23 -0
- package/dist/backend/entities/grouped-item.entity.d.ts.map +1 -0
- package/dist/backend/entities/grouped-item.entity.js +68 -0
- package/dist/backend/entities/grouped-item.entity.js.map +1 -0
- package/dist/backend/entities/product-attachment.entity.d.ts +23 -0
- package/dist/backend/entities/product-attachment.entity.d.ts.map +1 -0
- package/dist/backend/entities/product-attachment.entity.js +77 -0
- package/dist/backend/entities/product-attachment.entity.js.map +1 -0
- package/dist/backend/entities/product-attribute.entity.d.ts +72 -0
- package/dist/backend/entities/product-attribute.entity.d.ts.map +1 -0
- package/dist/backend/entities/product-attribute.entity.js +161 -0
- package/dist/backend/entities/product-attribute.entity.js.map +1 -0
- package/dist/backend/entities/product-editor-preference.entity.d.ts +28 -0
- package/dist/backend/entities/product-editor-preference.entity.d.ts.map +1 -0
- package/dist/backend/entities/product-editor-preference.entity.js +63 -0
- package/dist/backend/entities/product-editor-preference.entity.js.map +1 -0
- package/dist/backend/entities/product-link.entity.d.ts +25 -0
- package/dist/backend/entities/product-link.entity.d.ts.map +1 -0
- package/dist/backend/entities/product-link.entity.js +70 -0
- package/dist/backend/entities/product-link.entity.js.map +1 -0
- package/dist/backend/entities/product-packaging-unit.entity.d.ts +26 -0
- package/dist/backend/entities/product-packaging-unit.entity.d.ts.map +1 -0
- package/dist/backend/entities/product-packaging-unit.entity.js +76 -0
- package/dist/backend/entities/product-packaging-unit.entity.js.map +1 -0
- package/dist/backend/entities/product-value-override.entity.d.ts +43 -0
- package/dist/backend/entities/product-value-override.entity.d.ts.map +1 -0
- package/dist/backend/entities/product-value-override.entity.js +90 -0
- package/dist/backend/entities/product-value-override.entity.js.map +1 -0
- package/dist/backend/entities/product-variant.entity.d.ts +18 -0
- package/dist/backend/entities/product-variant.entity.d.ts.map +1 -0
- package/dist/backend/entities/product-variant.entity.js +68 -0
- package/dist/backend/entities/product-variant.entity.js.map +1 -0
- package/dist/backend/entities/product.entity.d.ts +108 -0
- package/dist/backend/entities/product.entity.d.ts.map +1 -0
- package/dist/backend/entities/product.entity.js +230 -0
- package/dist/backend/entities/product.entity.js.map +1 -0
- package/dist/backend/index.d.ts +262 -0
- package/dist/backend/index.d.ts.map +1 -0
- package/dist/backend/index.js +642 -0
- package/dist/backend/index.js.map +1 -0
- package/dist/backend/plugin.d.ts +216 -0
- package/dist/backend/plugin.d.ts.map +1 -0
- package/dist/backend/plugin.js +223 -0
- package/dist/backend/plugin.js.map +1 -0
- package/dist/backend/prompt-tools.d.ts +47 -0
- package/dist/backend/prompt-tools.d.ts.map +1 -0
- package/dist/backend/prompt-tools.js +284 -0
- package/dist/backend/prompt-tools.js.map +1 -0
- package/dist/backend/routes.admin.d.ts +105 -0
- package/dist/backend/routes.admin.d.ts.map +1 -0
- package/dist/backend/routes.admin.js +1162 -0
- package/dist/backend/routes.admin.js.map +1 -0
- package/dist/backend/routes.api-key.d.ts +33 -0
- package/dist/backend/routes.api-key.d.ts.map +1 -0
- package/dist/backend/routes.api-key.js +43 -0
- package/dist/backend/routes.api-key.js.map +1 -0
- package/dist/backend/routes.external.d.ts +32 -0
- package/dist/backend/routes.external.d.ts.map +1 -0
- package/dist/backend/routes.external.js +115 -0
- package/dist/backend/routes.external.js.map +1 -0
- package/dist/backend/routes.public.d.ts +46 -0
- package/dist/backend/routes.public.d.ts.map +1 -0
- package/dist/backend/routes.public.js +318 -0
- package/dist/backend/routes.public.js.map +1 -0
- package/dist/backend/services/asset-references.d.ts +4 -0
- package/dist/backend/services/asset-references.d.ts.map +1 -0
- package/dist/backend/services/asset-references.js +104 -0
- package/dist/backend/services/asset-references.js.map +1 -0
- package/dist/backend/services/attachment.service.d.ts +45 -0
- package/dist/backend/services/attachment.service.d.ts.map +1 -0
- package/dist/backend/services/attachment.service.js +268 -0
- package/dist/backend/services/attachment.service.js.map +1 -0
- package/dist/backend/services/attribute-option-validator.d.ts +47 -0
- package/dist/backend/services/attribute-option-validator.d.ts.map +1 -0
- package/dist/backend/services/attribute-option-validator.js +69 -0
- package/dist/backend/services/attribute-option-validator.js.map +1 -0
- package/dist/backend/services/attribute-set-validations.d.ts +65 -0
- package/dist/backend/services/attribute-set-validations.d.ts.map +1 -0
- package/dist/backend/services/attribute-set-validations.js +89 -0
- package/dist/backend/services/attribute-set-validations.js.map +1 -0
- package/dist/backend/services/attribute-set.service.d.ts +50 -0
- package/dist/backend/services/attribute-set.service.d.ts.map +1 -0
- package/dist/backend/services/attribute-set.service.js +401 -0
- package/dist/backend/services/attribute-set.service.js.map +1 -0
- package/dist/backend/services/attribute-type-mapping.d.ts +66 -0
- package/dist/backend/services/attribute-type-mapping.d.ts.map +1 -0
- package/dist/backend/services/attribute-type-mapping.js +121 -0
- package/dist/backend/services/attribute-type-mapping.js.map +1 -0
- package/dist/backend/services/attribute-value-key.service.d.ts +48 -0
- package/dist/backend/services/attribute-value-key.service.d.ts.map +1 -0
- package/dist/backend/services/attribute-value-key.service.js +118 -0
- package/dist/backend/services/attribute-value-key.service.js.map +1 -0
- package/dist/backend/services/audit-references.d.ts +13 -0
- package/dist/backend/services/audit-references.d.ts.map +1 -0
- package/dist/backend/services/audit-references.js +51 -0
- package/dist/backend/services/audit-references.js.map +1 -0
- package/dist/backend/services/bulk-operation-queue.d.ts +26 -0
- package/dist/backend/services/bulk-operation-queue.d.ts.map +1 -0
- package/dist/backend/services/bulk-operation-queue.js +34 -0
- package/dist/backend/services/bulk-operation-queue.js.map +1 -0
- package/dist/backend/services/bulk-operation.service.d.ts +195 -0
- package/dist/backend/services/bulk-operation.service.d.ts.map +1 -0
- package/dist/backend/services/bulk-operation.service.js +510 -0
- package/dist/backend/services/bulk-operation.service.js.map +1 -0
- package/dist/backend/services/bundle.service.d.ts +87 -0
- package/dist/backend/services/bundle.service.d.ts.map +1 -0
- package/dist/backend/services/bundle.service.js +286 -0
- package/dist/backend/services/bundle.service.js.map +1 -0
- package/dist/backend/services/catalog-admin.service.d.ts +398 -0
- package/dist/backend/services/catalog-admin.service.d.ts.map +1 -0
- package/dist/backend/services/catalog-admin.service.js +1516 -0
- package/dist/backend/services/catalog-admin.service.js.map +1 -0
- package/dist/backend/services/catalog-attribute-read.service.d.ts +78 -0
- package/dist/backend/services/catalog-attribute-read.service.d.ts.map +1 -0
- package/dist/backend/services/catalog-attribute-read.service.js +216 -0
- package/dist/backend/services/catalog-attribute-read.service.js.map +1 -0
- package/dist/backend/services/catalog-bulk-import.service.d.ts +30 -0
- package/dist/backend/services/catalog-bulk-import.service.d.ts.map +1 -0
- package/dist/backend/services/catalog-bulk-import.service.js +136 -0
- package/dist/backend/services/catalog-bulk-import.service.js.map +1 -0
- package/dist/backend/services/catalog-bulk-update.service.d.ts +91 -0
- package/dist/backend/services/catalog-bulk-update.service.d.ts.map +1 -0
- package/dist/backend/services/catalog-bulk-update.service.js +330 -0
- package/dist/backend/services/catalog-bulk-update.service.js.map +1 -0
- package/dist/backend/services/catalog-category-read.service.d.ts +66 -0
- package/dist/backend/services/catalog-category-read.service.d.ts.map +1 -0
- package/dist/backend/services/catalog-category-read.service.js +248 -0
- package/dist/backend/services/catalog-category-read.service.js.map +1 -0
- package/dist/backend/services/catalog-org-price-decorator.d.ts +90 -0
- package/dist/backend/services/catalog-org-price-decorator.d.ts.map +1 -0
- package/dist/backend/services/catalog-org-price-decorator.js +184 -0
- package/dist/backend/services/catalog-org-price-decorator.js.map +1 -0
- package/dist/backend/services/catalog-product-filter.service.d.ts +82 -0
- package/dist/backend/services/catalog-product-filter.service.d.ts.map +1 -0
- package/dist/backend/services/catalog-product-filter.service.js +275 -0
- package/dist/backend/services/catalog-product-filter.service.js.map +1 -0
- package/dist/backend/services/catalog-product-read.service.d.ts +46 -0
- package/dist/backend/services/catalog-product-read.service.d.ts.map +1 -0
- package/dist/backend/services/catalog-product-read.service.js +197 -0
- package/dist/backend/services/catalog-product-read.service.js.map +1 -0
- package/dist/backend/services/catalog-query.service.d.ts +444 -0
- package/dist/backend/services/catalog-query.service.d.ts.map +1 -0
- package/dist/backend/services/catalog-query.service.js +1633 -0
- package/dist/backend/services/catalog-query.service.js.map +1 -0
- package/dist/backend/services/catalog-quick-search.service.d.ts +78 -0
- package/dist/backend/services/catalog-quick-search.service.d.ts.map +1 -0
- package/dist/backend/services/catalog-quick-search.service.js +159 -0
- package/dist/backend/services/catalog-quick-search.service.js.map +1 -0
- package/dist/backend/services/catalog-write-ports.d.ts +25 -0
- package/dist/backend/services/catalog-write-ports.d.ts.map +1 -0
- package/dist/backend/services/catalog-write-ports.js +63 -0
- package/dist/backend/services/catalog-write-ports.js.map +1 -0
- package/dist/backend/services/category-admin.service.d.ts +102 -0
- package/dist/backend/services/category-admin.service.d.ts.map +1 -0
- package/dist/backend/services/category-admin.service.js +271 -0
- package/dist/backend/services/category-admin.service.js.map +1 -0
- package/dist/backend/services/gallery.service.d.ts +85 -0
- package/dist/backend/services/gallery.service.d.ts.map +1 -0
- package/dist/backend/services/gallery.service.js +326 -0
- package/dist/backend/services/gallery.service.js.map +1 -0
- package/dist/backend/services/grouped.service.d.ts +39 -0
- package/dist/backend/services/grouped.service.d.ts.map +1 -0
- package/dist/backend/services/grouped.service.js +136 -0
- package/dist/backend/services/grouped.service.js.map +1 -0
- package/dist/backend/services/label-resolver.d.ts +16 -0
- package/dist/backend/services/label-resolver.d.ts.map +1 -0
- package/dist/backend/services/label-resolver.js +43 -0
- package/dist/backend/services/label-resolver.js.map +1 -0
- package/dist/backend/services/packaging-unit.service.d.ts +28 -0
- package/dist/backend/services/packaging-unit.service.d.ts.map +1 -0
- package/dist/backend/services/packaging-unit.service.js +197 -0
- package/dist/backend/services/packaging-unit.service.js.map +1 -0
- package/dist/backend/services/primary-asset-url.d.ts +4 -0
- package/dist/backend/services/primary-asset-url.d.ts.map +1 -0
- package/dist/backend/services/primary-asset-url.js +55 -0
- package/dist/backend/services/primary-asset-url.js.map +1 -0
- package/dist/backend/services/product-editor-preferences.service.d.ts +20 -0
- package/dist/backend/services/product-editor-preferences.service.d.ts.map +1 -0
- package/dist/backend/services/product-editor-preferences.service.js +44 -0
- package/dist/backend/services/product-editor-preferences.service.js.map +1 -0
- package/dist/backend/services/product-link.service.d.ts +169 -0
- package/dist/backend/services/product-link.service.d.ts.map +1 -0
- package/dist/backend/services/product-link.service.js +297 -0
- package/dist/backend/services/product-link.service.js.map +1 -0
- package/dist/backend/services/product-overrides.service.d.ts +77 -0
- package/dist/backend/services/product-overrides.service.d.ts.map +1 -0
- package/dist/backend/services/product-overrides.service.js +204 -0
- package/dist/backend/services/product-overrides.service.js.map +1 -0
- package/dist/backend/services/product-scope-context.service.d.ts +36 -0
- package/dist/backend/services/product-scope-context.service.d.ts.map +1 -0
- package/dist/backend/services/product-scope-context.service.js +106 -0
- package/dist/backend/services/product-scope-context.service.js.map +1 -0
- package/dist/backend/services/product-type-validations.d.ts +42 -0
- package/dist/backend/services/product-type-validations.d.ts.map +1 -0
- package/dist/backend/services/product-type-validations.js +55 -0
- package/dist/backend/services/product-type-validations.js.map +1 -0
- package/dist/backend/services/product-value-resolver.service.d.ts +59 -0
- package/dist/backend/services/product-value-resolver.service.d.ts.map +1 -0
- package/dist/backend/services/product-value-resolver.service.js +111 -0
- package/dist/backend/services/product-value-resolver.service.js.map +1 -0
- package/dist/backend/services/system-attribute-scopes.d.ts +18 -0
- package/dist/backend/services/system-attribute-scopes.d.ts.map +1 -0
- package/dist/backend/services/system-attribute-scopes.js +18 -0
- package/dist/backend/services/system-attribute-scopes.js.map +1 -0
- package/dist/backend/services/viewer-organization.d.ts +25 -0
- package/dist/backend/services/viewer-organization.d.ts.map +1 -0
- package/dist/backend/services/viewer-organization.js +31 -0
- package/dist/backend/services/viewer-organization.js.map +1 -0
- package/dist/manifest.d.ts +204 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +686 -0
- package/dist/manifest.js.map +1 -0
- package/dist/migrations/20260429T064146_catalog_attribute_sets_init.d.ts +33 -0
- package/dist/migrations/20260429T064146_catalog_attribute_sets_init.d.ts.map +1 -0
- package/dist/migrations/20260429T064146_catalog_attribute_sets_init.js +118 -0
- package/dist/migrations/20260429T064146_catalog_attribute_sets_init.js.map +1 -0
- package/dist/migrations/20260429T070004_catalog_product_attribute_extensions.d.ts +24 -0
- package/dist/migrations/20260429T070004_catalog_product_attribute_extensions.d.ts.map +1 -0
- package/dist/migrations/20260429T070004_catalog_product_attribute_extensions.js +31 -0
- package/dist/migrations/20260429T070004_catalog_product_attribute_extensions.js.map +1 -0
- package/dist/migrations/20260429T102322_catalog_product_type_and_virtual_fields.d.ts +32 -0
- package/dist/migrations/20260429T102322_catalog_product_type_and_virtual_fields.d.ts.map +1 -0
- package/dist/migrations/20260429T102322_catalog_product_type_and_virtual_fields.js +58 -0
- package/dist/migrations/20260429T102322_catalog_product_type_and_virtual_fields.js.map +1 -0
- package/dist/migrations/20260429T111839_catalog_gallery_items_and_labels.d.ts +22 -0
- package/dist/migrations/20260429T111839_catalog_gallery_items_and_labels.d.ts.map +1 -0
- package/dist/migrations/20260429T111839_catalog_gallery_items_and_labels.js +66 -0
- package/dist/migrations/20260429T111839_catalog_gallery_items_and_labels.js.map +1 -0
- package/dist/migrations/20260429T112543_catalog_product_attachments.d.ts +18 -0
- package/dist/migrations/20260429T112543_catalog_product_attachments.d.ts.map +1 -0
- package/dist/migrations/20260429T112543_catalog_product_attachments.js +73 -0
- package/dist/migrations/20260429T112543_catalog_product_attachments.js.map +1 -0
- package/dist/migrations/20260429T123726_catalog_product_links.d.ts +21 -0
- package/dist/migrations/20260429T123726_catalog_product_links.d.ts.map +1 -0
- package/dist/migrations/20260429T123726_catalog_product_links.js +48 -0
- package/dist/migrations/20260429T123726_catalog_product_links.js.map +1 -0
- package/dist/migrations/20260429T130803_catalog_grouped_and_bundle.d.ts +23 -0
- package/dist/migrations/20260429T130803_catalog_grouped_and_bundle.d.ts.map +1 -0
- package/dist/migrations/20260429T130803_catalog_grouped_and_bundle.js +94 -0
- package/dist/migrations/20260429T130803_catalog_grouped_and_bundle.js.map +1 -0
- package/dist/migrations/20260501T185835_catalog_product_attribute_is_comparable.d.ts +24 -0
- package/dist/migrations/20260501T185835_catalog_product_attribute_is_comparable.d.ts.map +1 -0
- package/dist/migrations/20260501T185835_catalog_product_attribute_is_comparable.js +31 -0
- package/dist/migrations/20260501T185835_catalog_product_attribute_is_comparable.js.map +1 -0
- package/dist/migrations/20260505T060113_catalog_attribute_options_and_flags.d.ts +31 -0
- package/dist/migrations/20260505T060113_catalog_attribute_options_and_flags.d.ts.map +1 -0
- package/dist/migrations/20260505T060113_catalog_attribute_options_and_flags.js +133 -0
- package/dist/migrations/20260505T060113_catalog_attribute_options_and_flags.js.map +1 -0
- package/dist/migrations/20260515T082629_catalog_attribute_mass_editable.d.ts +16 -0
- package/dist/migrations/20260515T082629_catalog_attribute_mass_editable.d.ts.map +1 -0
- package/dist/migrations/20260515T082629_catalog_attribute_mass_editable.js +23 -0
- package/dist/migrations/20260515T082629_catalog_attribute_mass_editable.js.map +1 -0
- package/dist/migrations/20260526T124736_catalog_product_status_inactive.d.ts +10 -0
- package/dist/migrations/20260526T124736_catalog_product_status_inactive.d.ts.map +1 -0
- package/dist/migrations/20260526T124736_catalog_product_status_inactive.js +22 -0
- package/dist/migrations/20260526T124736_catalog_product_status_inactive.js.map +1 -0
- package/dist/migrations/20260611T140346_catalog_product_value_overrides_init.d.ts +38 -0
- package/dist/migrations/20260611T140346_catalog_product_value_overrides_init.d.ts.map +1 -0
- package/dist/migrations/20260611T140346_catalog_product_value_overrides_init.js +98 -0
- package/dist/migrations/20260611T140346_catalog_product_value_overrides_init.js.map +1 -0
- package/dist/migrations/20260611T140400_catalog_attribute_quick_searchable.d.ts +13 -0
- package/dist/migrations/20260611T140400_catalog_attribute_quick_searchable.d.ts.map +1 -0
- package/dist/migrations/20260611T140400_catalog_attribute_quick_searchable.js +17 -0
- package/dist/migrations/20260611T140400_catalog_attribute_quick_searchable.js.map +1 -0
- package/dist/migrations/20260611T140407_catalog_bulk_operations.d.ts +16 -0
- package/dist/migrations/20260611T140407_catalog_bulk_operations.d.ts.map +1 -0
- package/dist/migrations/20260611T140407_catalog_bulk_operations.js +42 -0
- package/dist/migrations/20260611T140407_catalog_bulk_operations.js.map +1 -0
- package/dist/migrations/20260611T140408_catalog_bulk_operation_logs.d.ts +14 -0
- package/dist/migrations/20260611T140408_catalog_bulk_operation_logs.d.ts.map +1 -0
- package/dist/migrations/20260611T140408_catalog_bulk_operation_logs.js +18 -0
- package/dist/migrations/20260611T140408_catalog_bulk_operation_logs.js.map +1 -0
- package/dist/migrations/20260611T140412_catalog_product_packaging_units.d.ts +11 -0
- package/dist/migrations/20260611T140412_catalog_product_packaging_units.d.ts.map +1 -0
- package/dist/migrations/20260611T140412_catalog_product_packaging_units.js +29 -0
- package/dist/migrations/20260611T140412_catalog_product_packaging_units.js.map +1 -0
- package/dist/migrations/20260718T060659_catalog_bulk_operation_revert_state.d.ts +23 -0
- package/dist/migrations/20260718T060659_catalog_bulk_operation_revert_state.d.ts.map +1 -0
- package/dist/migrations/20260718T060659_catalog_bulk_operation_revert_state.js +35 -0
- package/dist/migrations/20260718T060659_catalog_bulk_operation_revert_state.js.map +1 -0
- package/dist/migrations/20260718T200343_catalog_category_custom_field_values.d.ts +12 -0
- package/dist/migrations/20260718T200343_catalog_category_custom_field_values.d.ts.map +1 -0
- package/dist/migrations/20260718T200343_catalog_category_custom_field_values.js +16 -0
- package/dist/migrations/20260718T200343_catalog_category_custom_field_values.js.map +1 -0
- package/dist/migrations/20260723T230401_catalog_attributes_on_custom_fields.d.ts +32 -0
- package/dist/migrations/20260723T230401_catalog_attributes_on_custom_fields.d.ts.map +1 -0
- package/dist/migrations/20260723T230401_catalog_attributes_on_custom_fields.js +286 -0
- package/dist/migrations/20260723T230401_catalog_attributes_on_custom_fields.js.map +1 -0
- package/dist/migrations/20260804T152604_catalog_widen_product_sku.d.ts +36 -0
- package/dist/migrations/20260804T152604_catalog_widen_product_sku.d.ts.map +1 -0
- package/dist/migrations/20260804T152604_catalog_widen_product_sku.js +63 -0
- package/dist/migrations/20260804T152604_catalog_widen_product_sku.js.map +1 -0
- package/dist/migrations/20260804T160244_catalog_category_activation.d.ts +18 -0
- package/dist/migrations/20260804T160244_catalog_category_activation.d.ts.map +1 -0
- package/dist/migrations/20260804T160244_catalog_category_activation.js +22 -0
- package/dist/migrations/20260804T160244_catalog_category_activation.js.map +1 -0
- package/dist/migrations/20260912T094557_catalog_sales_channel_products.d.ts +27 -0
- package/dist/migrations/20260912T094557_catalog_sales_channel_products.d.ts.map +1 -0
- package/dist/migrations/20260912T094557_catalog_sales_channel_products.js +44 -0
- package/dist/migrations/20260912T094557_catalog_sales_channel_products.js.map +1 -0
- package/dist/migrations/20260912T094623_catalog_sales_channel_categories.d.ts +27 -0
- package/dist/migrations/20260912T094623_catalog_sales_channel_categories.d.ts.map +1 -0
- package/dist/migrations/20260912T094623_catalog_sales_channel_categories.js +44 -0
- package/dist/migrations/20260912T094623_catalog_sales_channel_categories.js.map +1 -0
- package/dist/migrations/20260925T125527_catalog_inventory_columns.d.ts +36 -0
- package/dist/migrations/20260925T125527_catalog_inventory_columns.d.ts.map +1 -0
- package/dist/migrations/20260925T125527_catalog_inventory_columns.js +87 -0
- package/dist/migrations/20260925T125527_catalog_inventory_columns.js.map +1 -0
- package/dist/migrations/index.d.ts +55 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/migrations/index.js +80 -0
- package/dist/migrations/index.js.map +1 -0
- package/dist/ports/index.d.ts +96 -0
- package/dist/ports/index.d.ts.map +1 -0
- package/dist/ports/index.js +2 -0
- package/dist/ports/index.js.map +1 -0
- package/docs/catalog/attachments.md +69 -0
- package/docs/catalog/attribute-sets.md +63 -0
- package/docs/catalog/attributes.md +244 -0
- package/docs/catalog/composite-products.md +115 -0
- package/docs/catalog/gallery-and-labels.md +73 -0
- package/docs/catalog/packaging-units.md +67 -0
- package/docs/catalog/per-channel-per-language-overrides.md +234 -0
- package/docs/catalog/product-links.md +79 -0
- package/docs/catalog.md +207 -0
- package/i18n/en.json +649 -0
- package/i18n/pl.json +649 -0
- package/package.json +109 -0
- package/tailwind.css +14 -0
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import { ERROR_CODES, listingPriceMoney, } from '@endora-commerce/contracts';
|
|
2
|
+
import { HttpError } from '@endora-commerce/platform/http';
|
|
3
|
+
import { Product } from '../entities/product.entity.js';
|
|
4
|
+
import { ProductLink } from '../entities/product-link.entity.js';
|
|
5
|
+
import { resolvePrimaryAssetUrls } from './primary-asset-url.js';
|
|
6
|
+
import { viewerOrganizationFor } from './viewer-organization.js';
|
|
7
|
+
function pickLang(blob, preferred) {
|
|
8
|
+
if (preferred && blob[preferred])
|
|
9
|
+
return blob[preferred];
|
|
10
|
+
const fallback = blob['en-US'] ?? blob['en'] ?? Object.values(blob)[0] ?? '';
|
|
11
|
+
return fallback;
|
|
12
|
+
}
|
|
13
|
+
export class ProductLinkService {
|
|
14
|
+
emFactory;
|
|
15
|
+
commandBus;
|
|
16
|
+
listingPrices;
|
|
17
|
+
assets;
|
|
18
|
+
channelMembership;
|
|
19
|
+
organizationDetails;
|
|
20
|
+
constructor(emFactory,
|
|
21
|
+
/** Feature 054 — audits product-link writes co-transactionally when provided. */
|
|
22
|
+
commandBus,
|
|
23
|
+
/**
|
|
24
|
+
* Issue #132 — the pricing engine, through the `pricingService` port. A
|
|
25
|
+
* related-product tile is a listing: it prices through the same chain as the
|
|
26
|
+
* catalogue grid, not off the catalogue's legacy default-price attribute.
|
|
27
|
+
*/
|
|
28
|
+
listingPrices,
|
|
29
|
+
/**
|
|
30
|
+
* Feature 075 — `assets_library`'s read port. The two `join assets` clauses
|
|
31
|
+
* this service used to write are gone; the tile's image is resolved by the
|
|
32
|
+
* shared `resolvePrimaryAssetUrls` helper, which asks the owner for the
|
|
33
|
+
* asset row and keeps the bridge query on this module's own tables.
|
|
34
|
+
*/
|
|
35
|
+
assets,
|
|
36
|
+
/**
|
|
37
|
+
* Issue #185 — the kernel's channel-membership accessor. The storefront
|
|
38
|
+
* link read below asked `sales_channel_products` in raw SQL, which crosses
|
|
39
|
+
* the boundary while naming no import specifier and is the read
|
|
40
|
+
* Constitution XII reserves to this service.
|
|
41
|
+
*
|
|
42
|
+
* Optional only in the signature, like the two above; a link read that
|
|
43
|
+
* reaches it unwired fails loudly rather than quietly answering the
|
|
44
|
+
* cross-channel set.
|
|
45
|
+
*/
|
|
46
|
+
channelMembership,
|
|
47
|
+
/**
|
|
48
|
+
* `organizations`' read model — the customer group a group-targeted price
|
|
49
|
+
* list is selected by, which lives on the organisation row. Optional only
|
|
50
|
+
* in the signature, and never reached by an anonymous strip.
|
|
51
|
+
*/
|
|
52
|
+
organizationDetails) {
|
|
53
|
+
this.emFactory = emFactory;
|
|
54
|
+
this.commandBus = commandBus;
|
|
55
|
+
this.listingPrices = listingPrices;
|
|
56
|
+
this.assets = assets;
|
|
57
|
+
this.channelMembership = channelMembership;
|
|
58
|
+
this.organizationDetails = organizationDetails;
|
|
59
|
+
}
|
|
60
|
+
#requireOrganizationDetails() {
|
|
61
|
+
if (!this.organizationDetails) {
|
|
62
|
+
throw new Error('ProductLinkService: the organization read port is not wired — a signed-in buyer cannot be priced.');
|
|
63
|
+
}
|
|
64
|
+
return this.organizationDetails;
|
|
65
|
+
}
|
|
66
|
+
#requireChannelMembership() {
|
|
67
|
+
if (!this.channelMembership) {
|
|
68
|
+
throw new Error('ProductLinkService: the channel-membership port is not wired — link tiles cannot be ' +
|
|
69
|
+
'scoped to a channel without leaking the cross-channel set.');
|
|
70
|
+
}
|
|
71
|
+
return this.channelMembership;
|
|
72
|
+
}
|
|
73
|
+
#requireAssets() {
|
|
74
|
+
if (!this.assets) {
|
|
75
|
+
throw new Error('ProductLinkService: the asset read port is not wired — link tiles cannot show an image.');
|
|
76
|
+
}
|
|
77
|
+
return this.assets;
|
|
78
|
+
}
|
|
79
|
+
#requireListingPrices() {
|
|
80
|
+
if (!this.listingPrices) {
|
|
81
|
+
throw new Error('ProductLinkService: the pricing port is not wired — link tiles cannot be priced.');
|
|
82
|
+
}
|
|
83
|
+
return this.listingPrices;
|
|
84
|
+
}
|
|
85
|
+
/** Feature 054 — run a product-link write through the Command Bus. */
|
|
86
|
+
async #audited(action, objectId, write) {
|
|
87
|
+
if (this.commandBus) {
|
|
88
|
+
return this.commandBus.run({ action, objectType: 'product_link', objectId, run: ({ em }) => write(em) });
|
|
89
|
+
}
|
|
90
|
+
const em = this.emFactory();
|
|
91
|
+
const w = await write(em);
|
|
92
|
+
await em.flush();
|
|
93
|
+
return w.result;
|
|
94
|
+
}
|
|
95
|
+
async listForAdmin(sourceProductId, kind) {
|
|
96
|
+
const em = this.emFactory();
|
|
97
|
+
const where = { sourceProductId };
|
|
98
|
+
if (kind)
|
|
99
|
+
where['kind'] = kind;
|
|
100
|
+
const rows = await em.find(ProductLink, where, {
|
|
101
|
+
orderBy: { position: 'asc', id: 'asc' },
|
|
102
|
+
});
|
|
103
|
+
return rows.map((r) => ({
|
|
104
|
+
id: r.id,
|
|
105
|
+
sourceProductId: r.sourceProductId,
|
|
106
|
+
targetProductId: r.targetProductId,
|
|
107
|
+
kind: r.kind,
|
|
108
|
+
position: r.position,
|
|
109
|
+
}));
|
|
110
|
+
}
|
|
111
|
+
async bulkCreate(sourceProductId, inputs) {
|
|
112
|
+
const em = this.emFactory();
|
|
113
|
+
// 1) Source must exist and not be archived/deleted (404 PRODUCT_NOT_FOUND).
|
|
114
|
+
const source = await em.findOne(Product, { id: sourceProductId });
|
|
115
|
+
if (!source || source.deletedAt) {
|
|
116
|
+
throw new HttpError(404, ERROR_CODES.PRODUCT_NOT_FOUND, `Product "${sourceProductId}" not found.`);
|
|
117
|
+
}
|
|
118
|
+
// 2) Self-link rejected at API layer (400). The DB CHECK is the
|
|
119
|
+
// belt-and-braces backstop, but we want the typed error code first.
|
|
120
|
+
for (const input of inputs) {
|
|
121
|
+
if (input.targetProductId === sourceProductId) {
|
|
122
|
+
throw new HttpError(400, ERROR_CODES.SELF_LINK_NOT_ALLOWED, 'A product cannot link to itself.');
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// 3) Targets must all exist (404 TARGET_PRODUCT_NOT_FOUND); single
|
|
126
|
+
// findIn keeps it to one query.
|
|
127
|
+
const targetIds = [...new Set(inputs.map((i) => i.targetProductId))];
|
|
128
|
+
const targets = await em.find(Product, { id: { $in: targetIds } });
|
|
129
|
+
const foundIds = new Set(targets.filter((t) => !t.deletedAt).map((t) => t.id));
|
|
130
|
+
for (const id of targetIds) {
|
|
131
|
+
if (!foundIds.has(id)) {
|
|
132
|
+
throw new HttpError(404, ERROR_CODES.TARGET_PRODUCT_NOT_FOUND, `Target product "${id}" not found.`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
// 4) Duplicate (source, target, kind) rejected up-front (409). The
|
|
136
|
+
// DB UNIQUE is the backstop; this lets us return the typed code
|
|
137
|
+
// without parsing PG error strings.
|
|
138
|
+
const existing = await em.find(ProductLink, {
|
|
139
|
+
sourceProductId,
|
|
140
|
+
targetProductId: { $in: targetIds },
|
|
141
|
+
});
|
|
142
|
+
const existingKey = new Set(existing.map((e) => `${e.targetProductId}|${e.kind}`));
|
|
143
|
+
for (const input of inputs) {
|
|
144
|
+
if (existingKey.has(`${input.targetProductId}|${input.kind}`)) {
|
|
145
|
+
throw new HttpError(409, ERROR_CODES.LINK_ALREADY_EXISTS, `Link source=${sourceProductId} target=${input.targetProductId} kind=${input.kind} already exists.`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
// 5) Default position: append to the end of the (source, kind) list.
|
|
149
|
+
const nextPositionByKind = new Map();
|
|
150
|
+
const tally = await em.find(ProductLink, { sourceProductId });
|
|
151
|
+
for (const row of tally) {
|
|
152
|
+
const cur = nextPositionByKind.get(row.kind) ?? -1;
|
|
153
|
+
if (row.position > cur)
|
|
154
|
+
nextPositionByKind.set(row.kind, row.position);
|
|
155
|
+
}
|
|
156
|
+
const created = await this.#audited('product_link.bulk_create', sourceProductId, async (cem) => {
|
|
157
|
+
const rows = [];
|
|
158
|
+
for (const input of inputs) {
|
|
159
|
+
const explicit = input.position;
|
|
160
|
+
const next = (nextPositionByKind.get(input.kind) ?? -1) + 1;
|
|
161
|
+
const position = explicit !== undefined ? explicit : next;
|
|
162
|
+
nextPositionByKind.set(input.kind, position);
|
|
163
|
+
rows.push(cem.create(ProductLink, {
|
|
164
|
+
sourceProductId,
|
|
165
|
+
targetProductId: input.targetProductId,
|
|
166
|
+
kind: input.kind,
|
|
167
|
+
position,
|
|
168
|
+
}));
|
|
169
|
+
}
|
|
170
|
+
await cem.flush();
|
|
171
|
+
return { result: rows, before: null, after: { sourceProductId, count: rows.length } };
|
|
172
|
+
});
|
|
173
|
+
return created.map((r) => ({
|
|
174
|
+
id: r.id,
|
|
175
|
+
sourceProductId: r.sourceProductId,
|
|
176
|
+
targetProductId: r.targetProductId,
|
|
177
|
+
kind: r.kind,
|
|
178
|
+
position: r.position,
|
|
179
|
+
}));
|
|
180
|
+
}
|
|
181
|
+
async removeLink(sourceProductId, linkId) {
|
|
182
|
+
await this.#audited('product_link.delete', linkId, async (em) => {
|
|
183
|
+
const link = await em.findOne(ProductLink, { id: linkId, sourceProductId });
|
|
184
|
+
if (!link) {
|
|
185
|
+
throw new HttpError(404, ERROR_CODES.PRODUCT_LINK_NOT_FOUND, `Link "${linkId}" not found on source ${sourceProductId}.`);
|
|
186
|
+
}
|
|
187
|
+
const before = { sourceProductId, targetProductId: link.targetProductId, kind: link.kind };
|
|
188
|
+
em.remove(link);
|
|
189
|
+
return { result: undefined, before, after: null };
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Storefront read — drops links whose target product is not active or
|
|
194
|
+
* not visible in the requested sales channel; localizes name to the
|
|
195
|
+
* caller's preferred language.
|
|
196
|
+
*/
|
|
197
|
+
async listForStorefront(sourceProductId, ctx, kind) {
|
|
198
|
+
const em = this.emFactory();
|
|
199
|
+
const where = { sourceProductId };
|
|
200
|
+
if (kind)
|
|
201
|
+
where['kind'] = kind;
|
|
202
|
+
const rows = await em.find(ProductLink, where, {
|
|
203
|
+
orderBy: { position: 'asc', id: 'asc' },
|
|
204
|
+
});
|
|
205
|
+
if (rows.length === 0)
|
|
206
|
+
return [];
|
|
207
|
+
const targetIds = rows.map((r) => r.targetProductId);
|
|
208
|
+
const targets = await em.find(Product, {
|
|
209
|
+
id: { $in: targetIds },
|
|
210
|
+
status: 'active',
|
|
211
|
+
});
|
|
212
|
+
const byId = new Map(targets.map((p) => [p.id, p]));
|
|
213
|
+
// Feature 053 (FR-006 / Principle XII) — sales-channel visibility. The
|
|
214
|
+
// channel is resolved once upstream (`getResolvedChannel()`) and handed in;
|
|
215
|
+
// the membership filter is ALWAYS applied, failing closed to an empty
|
|
216
|
+
// visible set rather than leaking the full cross-channel target set.
|
|
217
|
+
// Through the kernel's accessor since issue #185: this was a `select
|
|
218
|
+
// product_id from sales_channel_products` written here, which is the one
|
|
219
|
+
// read Principle XII names the membership service for.
|
|
220
|
+
const channel = ctx.resolvedChannel;
|
|
221
|
+
const visibleIds = targets.length > 0
|
|
222
|
+
? new Set(await this.#requireChannelMembership().filterEntityIdsInChannel(channel.id, 'product', targets.map((t) => t.id)))
|
|
223
|
+
: // No targets → nothing is visible (fail closed).
|
|
224
|
+
new Set();
|
|
225
|
+
// Primary asset urls: the gallery thumb chain, with the legacy
|
|
226
|
+
// `product_assets` fallback. Shared with `CatalogQueryService` so the chain
|
|
227
|
+
// and the `assets_library` port call have one home.
|
|
228
|
+
const assetUrlByProductId = await resolvePrimaryAssetUrls(em, this.#requireAssets(), targetIds);
|
|
229
|
+
// Sales-channel public flag controls price visibility (R-18), read off the
|
|
230
|
+
// resolved channel handed in by the route. A channel that withholds prices
|
|
231
|
+
// is not asked for them (issue #132).
|
|
232
|
+
const visibleTargets = rows
|
|
233
|
+
.filter((link) => visibleIds.has(link.targetProductId))
|
|
234
|
+
.map((link) => byId.get(link.targetProductId))
|
|
235
|
+
.filter((target) => target !== undefined);
|
|
236
|
+
const viewerOrganization = ctx.audience.organizationId === null
|
|
237
|
+
? null
|
|
238
|
+
: await viewerOrganizationFor(this.#requireOrganizationDetails(), ctx.audience);
|
|
239
|
+
const resolvedPrices = ctx.resolvedChannel.isPublic && visibleTargets.length > 0
|
|
240
|
+
? await this.#requireListingPrices().resolveListingPrices({
|
|
241
|
+
products: visibleTargets,
|
|
242
|
+
context: {
|
|
243
|
+
salesChannel: {
|
|
244
|
+
id: ctx.resolvedChannel.id,
|
|
245
|
+
defaultCurrency: ctx.resolvedChannel.defaultCurrency,
|
|
246
|
+
},
|
|
247
|
+
organization: viewerOrganization,
|
|
248
|
+
},
|
|
249
|
+
})
|
|
250
|
+
: new Map();
|
|
251
|
+
const out = [];
|
|
252
|
+
for (const link of rows) {
|
|
253
|
+
if (!visibleIds.has(link.targetProductId))
|
|
254
|
+
continue;
|
|
255
|
+
const target = byId.get(link.targetProductId);
|
|
256
|
+
if (!target)
|
|
257
|
+
continue;
|
|
258
|
+
const name = pickLang(target.name, ctx.preferredLanguage);
|
|
259
|
+
const resolved = resolvedPrices.get(target.id);
|
|
260
|
+
const price = resolved === undefined ? null : listingPriceMoney(resolved);
|
|
261
|
+
out.push({
|
|
262
|
+
id: link.id,
|
|
263
|
+
kind: link.kind,
|
|
264
|
+
position: link.position,
|
|
265
|
+
product: {
|
|
266
|
+
id: target.id,
|
|
267
|
+
sku: target.sku,
|
|
268
|
+
slug: target.slug,
|
|
269
|
+
name,
|
|
270
|
+
primaryAssetUrl: assetUrlByProductId.get(target.id) ?? null,
|
|
271
|
+
price,
|
|
272
|
+
},
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
return out;
|
|
276
|
+
}
|
|
277
|
+
async reorderForKind(sourceProductId, kind, linkIds) {
|
|
278
|
+
await this.#audited('product_link.reorder', sourceProductId, async (em) => {
|
|
279
|
+
// Fetch all links of this (source, kind) so we can validate the input
|
|
280
|
+
// covers exactly that set.
|
|
281
|
+
const rows = await em.find(ProductLink, { sourceProductId, kind });
|
|
282
|
+
const byId = new Map(rows.map((r) => [r.id, r]));
|
|
283
|
+
for (const id of linkIds) {
|
|
284
|
+
if (!byId.has(id)) {
|
|
285
|
+
throw new HttpError(400, ERROR_CODES.VALIDATION_FAILED, `Link "${id}" is not part of (${sourceProductId}, ${kind}).`);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
// Apply 0..N positions in array order.
|
|
289
|
+
for (let i = 0; i < linkIds.length; i++) {
|
|
290
|
+
const link = byId.get(linkIds[i]);
|
|
291
|
+
link.position = i;
|
|
292
|
+
}
|
|
293
|
+
return { result: undefined, before: null, after: { sourceProductId, kind, count: linkIds.length } };
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
//# sourceMappingURL=product-link.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"product-link.service.js","sourceRoot":"","sources":["../../../src/backend/services/product-link.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { EntityManager } from '@mikro-orm/postgresql';
|
|
2
|
+
import type { CommandBus } from '@endora-commerce/platform/commands';
|
|
3
|
+
import type { SalesChannelMembershipPort } from '@endora-commerce/platform/kernel';
|
|
4
|
+
import { ProductValueOverride } from '../entities/product-value-override.entity.js';
|
|
5
|
+
import type { CatalogAttributeReadService } from './catalog-attribute-read.service.js';
|
|
6
|
+
/**
|
|
7
|
+
* Feature 022 — channel-aware value-override CRUD.
|
|
8
|
+
*
|
|
9
|
+
* The bulk upsert+delete handler is the only mutator; it runs all
|
|
10
|
+
* operations in one MikroORM transaction so a single 422 rolls back
|
|
11
|
+
* every requested change (FR-030 atomicity).
|
|
12
|
+
*
|
|
13
|
+
* Validation rules (data-model.md §3.3 + §4):
|
|
14
|
+
* - `attribute_unknown` — key is neither a system attr nor a
|
|
15
|
+
* product_attributes.key row.
|
|
16
|
+
* - `attribute_not_channel_scoped` — caller wrote to a (channel, ...)
|
|
17
|
+
* slot for an attr that has
|
|
18
|
+
* channelScoped=false.
|
|
19
|
+
* - `attribute_missing_language` — caller wrote (channel, null) for
|
|
20
|
+
* an attr that has languageScoped=
|
|
21
|
+
* true (must address a specific
|
|
22
|
+
* language slot).
|
|
23
|
+
* - `channel_not_assigned_to_product` — channel is not in the
|
|
24
|
+
* product's membership set.
|
|
25
|
+
* - `language_not_in_channel` — language is not in the channel's
|
|
26
|
+
* SalesChannel.languages array.
|
|
27
|
+
* - `value_invalid` — value shape does not satisfy the
|
|
28
|
+
* attribute's valueType.
|
|
29
|
+
*/
|
|
30
|
+
export type OverrideUpsertInput = {
|
|
31
|
+
attributeKey: string;
|
|
32
|
+
channelId: string;
|
|
33
|
+
languageCode: string | null;
|
|
34
|
+
value: {
|
|
35
|
+
v: unknown;
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
export type OverrideDeleteInput = {
|
|
39
|
+
attributeKey: string;
|
|
40
|
+
channelId: string;
|
|
41
|
+
languageCode: string | null;
|
|
42
|
+
};
|
|
43
|
+
export type ApplyOverridesInput = {
|
|
44
|
+
upserts: readonly OverrideUpsertInput[];
|
|
45
|
+
deletes: readonly OverrideDeleteInput[];
|
|
46
|
+
};
|
|
47
|
+
export type ApplyOverridesResult = {
|
|
48
|
+
productId: string;
|
|
49
|
+
applied: {
|
|
50
|
+
upserted: number;
|
|
51
|
+
deleted: number;
|
|
52
|
+
};
|
|
53
|
+
overrides: ProductValueOverride[];
|
|
54
|
+
};
|
|
55
|
+
export declare class ProductOverridesService {
|
|
56
|
+
#private;
|
|
57
|
+
private readonly emFactory;
|
|
58
|
+
private readonly salesChannelMembership;
|
|
59
|
+
/** Feature 054 — audits the override apply co-transactionally when provided. */
|
|
60
|
+
private readonly commandBus?;
|
|
61
|
+
/** Feature 061 — composed attribute read model (scope + value-type lookups). */
|
|
62
|
+
private readonly attributeRead?;
|
|
63
|
+
constructor(emFactory: () => EntityManager, salesChannelMembership: SalesChannelMembershipPort,
|
|
64
|
+
/** Feature 054 — audits the override apply co-transactionally when provided. */
|
|
65
|
+
commandBus?: CommandBus | undefined,
|
|
66
|
+
/** Feature 061 — composed attribute read model (scope + value-type lookups). */
|
|
67
|
+
attributeRead?: CatalogAttributeReadService | undefined);
|
|
68
|
+
listForProduct(productId: string): Promise<ProductValueOverride[]>;
|
|
69
|
+
/**
|
|
70
|
+
* Validate + apply a bulk write. All operations run in one
|
|
71
|
+
* transaction. The first validation failure aborts everything; the
|
|
72
|
+
* thrown HttpError carries the offending input's index in `issues`
|
|
73
|
+
* so callers can highlight the bad row.
|
|
74
|
+
*/
|
|
75
|
+
applyBulk(productId: string, input: ApplyOverridesInput): Promise<ApplyOverridesResult>;
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=product-overrides.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"product-overrides.service.d.ts","sourceRoot":"","sources":["../../../src/backend/services/product-overrides.service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAG3D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oCAAoC,CAAC;AACrE,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,kCAAkC,CAAC;AAGnF,OAAO,EAAE,oBAAoB,EAAE,MAAM,8CAA8C,CAAC;AAEpF,OAAO,KAAK,EACV,2BAA2B,EAE5B,MAAM,qCAAqC,CAAC;AAE7C;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,MAAM,MAAM,mBAAmB,GAAG;IAChC,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,KAAK,EAAE;QAAE,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,SAAS,mBAAmB,EAAE,CAAC;IACxC,OAAO,EAAE,SAAS,mBAAmB,EAAE,CAAC;CACzC,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C,SAAS,EAAE,oBAAoB,EAAE,CAAC;CACnC,CAAC;AAEF,qBAAa,uBAAuB;;IAEhC,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,sBAAsB;IACvC,gFAAgF;IAChF,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC;IAC5B,gFAAgF;IAChF,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC;gBALd,SAAS,EAAE,MAAM,aAAa,EAC9B,sBAAsB,EAAE,0BAA0B;IACnE,gFAAgF;IAC/D,UAAU,CAAC,EAAE,UAAU,YAAA;IACxC,gFAAgF;IAC/D,aAAa,CAAC,EAAE,2BAA2B,YAAA;IAiCxD,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC;IAKxE;;;;;OAKG;IACG,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,oBAAoB,CAAC;CA+J9F"}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { ERROR_CODES } from '@endora-commerce/contracts';
|
|
2
|
+
import { HttpError } from '@endora-commerce/platform/http';
|
|
3
|
+
import { SalesChannel } from '@endora-commerce/platform/kernel';
|
|
4
|
+
import { Product } from '../entities/product.entity.js';
|
|
5
|
+
import { ProductValueOverride } from '../entities/product-value-override.entity.js';
|
|
6
|
+
import { getAttributeScope, isSystemAttributeKey } from './system-attribute-scopes.js';
|
|
7
|
+
export class ProductOverridesService {
|
|
8
|
+
emFactory;
|
|
9
|
+
salesChannelMembership;
|
|
10
|
+
commandBus;
|
|
11
|
+
attributeRead;
|
|
12
|
+
constructor(emFactory, salesChannelMembership,
|
|
13
|
+
/** Feature 054 — audits the override apply co-transactionally when provided. */
|
|
14
|
+
commandBus,
|
|
15
|
+
/** Feature 061 — composed attribute read model (scope + value-type lookups). */
|
|
16
|
+
attributeRead) {
|
|
17
|
+
this.emFactory = emFactory;
|
|
18
|
+
this.salesChannelMembership = salesChannelMembership;
|
|
19
|
+
this.commandBus = commandBus;
|
|
20
|
+
this.attributeRead = attributeRead;
|
|
21
|
+
}
|
|
22
|
+
#requireAttributeRead() {
|
|
23
|
+
if (!this.attributeRead) {
|
|
24
|
+
throw new Error('ProductOverridesService: CatalogAttributeReadService is not wired — attribute reads are unavailable.');
|
|
25
|
+
}
|
|
26
|
+
return this.attributeRead;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Feature 054 — run the override mutations through the Command Bus (its
|
|
30
|
+
* transaction replaces the local `em.transactional`, and the audit is
|
|
31
|
+
* co-transactional). Bus-less path keeps the local transaction.
|
|
32
|
+
*/
|
|
33
|
+
async #audited(action, objectId, write) {
|
|
34
|
+
if (this.commandBus) {
|
|
35
|
+
return this.commandBus.run({ action, objectType: 'product', objectId, run: ({ em }) => write(em) });
|
|
36
|
+
}
|
|
37
|
+
const em = this.emFactory();
|
|
38
|
+
return em.transactional(async (tem) => (await write(tem)).result);
|
|
39
|
+
}
|
|
40
|
+
async listForProduct(productId) {
|
|
41
|
+
const em = this.emFactory();
|
|
42
|
+
return em.find(ProductValueOverride, { productId });
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Validate + apply a bulk write. All operations run in one
|
|
46
|
+
* transaction. The first validation failure aborts everything; the
|
|
47
|
+
* thrown HttpError carries the offending input's index in `issues`
|
|
48
|
+
* so callers can highlight the bad row.
|
|
49
|
+
*/
|
|
50
|
+
async applyBulk(productId, input) {
|
|
51
|
+
const em = this.emFactory();
|
|
52
|
+
const product = await em.findOne(Product, { id: productId });
|
|
53
|
+
if (!product) {
|
|
54
|
+
throw new HttpError(404, ERROR_CODES.PRODUCT_NOT_FOUND, 'Product not found.');
|
|
55
|
+
}
|
|
56
|
+
// Collect referenced channels + attribute rows in one fetch each.
|
|
57
|
+
const channelIds = Array.from(new Set([
|
|
58
|
+
...input.upserts.map((u) => u.channelId),
|
|
59
|
+
...input.deletes.map((d) => d.channelId),
|
|
60
|
+
]));
|
|
61
|
+
const channels = channelIds.length === 0
|
|
62
|
+
? []
|
|
63
|
+
: await em.find(SalesChannel, { id: { $in: channelIds } });
|
|
64
|
+
const channelById = new Map(channels.map((c) => [c.id, c]));
|
|
65
|
+
const attrKeys = Array.from(new Set([
|
|
66
|
+
...input.upserts.map((u) => u.attributeKey),
|
|
67
|
+
...input.deletes.map((d) => d.attributeKey),
|
|
68
|
+
])).filter((k) => !isSystemAttributeKey(k));
|
|
69
|
+
const wantedKeys = new Set(attrKeys);
|
|
70
|
+
const attrRows = attrKeys.length === 0
|
|
71
|
+
? []
|
|
72
|
+
: (await this.#requireAttributeRead().listAll()).filter((v) => wantedKeys.has(v.key));
|
|
73
|
+
const attrByKey = new Map(attrRows.map((r) => [r.key, r]));
|
|
74
|
+
const assignedChannels = await this.salesChannelMembership.listChannelsForEntity('product', productId);
|
|
75
|
+
const assignedChannelIds = new Set(assignedChannels.map((c) => c.id));
|
|
76
|
+
// Run validation first; if anything throws, we abort before mutating.
|
|
77
|
+
const validateEntry = (kind, idx, e) => {
|
|
78
|
+
const pathPrefix = `${kind === 'upsert' ? 'upserts' : 'deletes'}[${idx}]`;
|
|
79
|
+
const attrRow = attrByKey.get(e.attributeKey);
|
|
80
|
+
if (!isSystemAttributeKey(e.attributeKey) && !attrRow) {
|
|
81
|
+
throw new HttpError(422, ERROR_CODES.VALIDATION_FAILED, 'attribute_unknown', [{ path: `${pathPrefix}.attributeKey`, issue: e.attributeKey }]);
|
|
82
|
+
}
|
|
83
|
+
const scope = getAttributeScope(e.attributeKey, attrRow ?? null);
|
|
84
|
+
if (!scope.channelScoped) {
|
|
85
|
+
throw new HttpError(422, ERROR_CODES.VALIDATION_FAILED, 'attribute_not_channel_scoped', [{ path: `${pathPrefix}.attributeKey`, issue: e.attributeKey }]);
|
|
86
|
+
}
|
|
87
|
+
if (scope.languageScoped && e.languageCode === null) {
|
|
88
|
+
throw new HttpError(422, ERROR_CODES.VALIDATION_FAILED, 'attribute_missing_language', [{ path: `${pathPrefix}.languageCode`, issue: 'null' }]);
|
|
89
|
+
}
|
|
90
|
+
if (!assignedChannelIds.has(e.channelId)) {
|
|
91
|
+
throw new HttpError(422, ERROR_CODES.VALIDATION_FAILED, 'channel_not_assigned_to_product', [{ path: `${pathPrefix}.channelId`, issue: e.channelId }]);
|
|
92
|
+
}
|
|
93
|
+
const channel = channelById.get(e.channelId);
|
|
94
|
+
if (e.languageCode !== null) {
|
|
95
|
+
if (!channel || !channel.languages.includes(e.languageCode)) {
|
|
96
|
+
throw new HttpError(422, ERROR_CODES.VALIDATION_FAILED, 'language_not_in_channel', [{ path: `${pathPrefix}.languageCode`, issue: e.languageCode }]);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (kind === 'upsert') {
|
|
100
|
+
const u = e;
|
|
101
|
+
validateValueShape(u.value.v, attrRow?.valueType ?? 'string', `${pathPrefix}.value`);
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
input.upserts.forEach((u, i) => validateEntry('upsert', i, u));
|
|
105
|
+
input.deletes.forEach((d, i) => validateEntry('delete', i, d));
|
|
106
|
+
// Apply in one transaction (via the Command Bus — co-transactional audit).
|
|
107
|
+
const { upserted, deleted } = await this.#audited('product.overrides_apply', productId, async (tem) => {
|
|
108
|
+
let up = 0;
|
|
109
|
+
let del = 0;
|
|
110
|
+
for (const u of input.upserts) {
|
|
111
|
+
const where = {
|
|
112
|
+
productId,
|
|
113
|
+
attributeKey: u.attributeKey,
|
|
114
|
+
channelId: u.channelId,
|
|
115
|
+
languageCode: u.languageCode ?? null,
|
|
116
|
+
};
|
|
117
|
+
const existing = await tem.findOne(ProductValueOverride, where);
|
|
118
|
+
if (existing) {
|
|
119
|
+
existing.value = u.value;
|
|
120
|
+
// Touch flush; updatedAt hook handles the rest.
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
tem.persist(tem.create(ProductValueOverride, {
|
|
124
|
+
productId,
|
|
125
|
+
attributeKey: u.attributeKey,
|
|
126
|
+
channelId: u.channelId,
|
|
127
|
+
languageCode: u.languageCode,
|
|
128
|
+
value: u.value,
|
|
129
|
+
}));
|
|
130
|
+
}
|
|
131
|
+
up += 1;
|
|
132
|
+
}
|
|
133
|
+
for (const d of input.deletes) {
|
|
134
|
+
const existing = await tem.findOne(ProductValueOverride, {
|
|
135
|
+
productId,
|
|
136
|
+
attributeKey: d.attributeKey,
|
|
137
|
+
channelId: d.channelId,
|
|
138
|
+
languageCode: d.languageCode ?? null,
|
|
139
|
+
});
|
|
140
|
+
if (existing) {
|
|
141
|
+
tem.remove(existing);
|
|
142
|
+
del += 1;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return {
|
|
146
|
+
result: { upserted: up, deleted: del },
|
|
147
|
+
before: null,
|
|
148
|
+
after: { productId, upserted: up, deleted: del },
|
|
149
|
+
};
|
|
150
|
+
});
|
|
151
|
+
const all = await em.find(ProductValueOverride, { productId });
|
|
152
|
+
return { productId, applied: { upserted, deleted }, overrides: all };
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Minimal value-shape check. Allows null (clearing); otherwise enforces
|
|
157
|
+
* a primitive shape consistent with the attribute's valueType. Stricter
|
|
158
|
+
* validation (enum option membership, price decimals, date format) is
|
|
159
|
+
* deferred — the existing attribute-write path in catalog-admin.service
|
|
160
|
+
* does the strict pass when the value lands in the global baseline, and
|
|
161
|
+
* the override path can lean on that until a follow-up centralises the
|
|
162
|
+
* rule.
|
|
163
|
+
*/
|
|
164
|
+
function validateValueShape(v, valueType, path) {
|
|
165
|
+
if (v === null || v === undefined)
|
|
166
|
+
return;
|
|
167
|
+
const fail = (issue) => {
|
|
168
|
+
throw new HttpError(422, ERROR_CODES.VALIDATION_FAILED, 'value_invalid', [
|
|
169
|
+
{ path, issue },
|
|
170
|
+
]);
|
|
171
|
+
};
|
|
172
|
+
switch (valueType) {
|
|
173
|
+
case 'string':
|
|
174
|
+
case 'enum':
|
|
175
|
+
case 'select':
|
|
176
|
+
if (typeof v !== 'string')
|
|
177
|
+
return fail(`expected string, got ${typeof v}`);
|
|
178
|
+
return;
|
|
179
|
+
case 'number':
|
|
180
|
+
case 'price':
|
|
181
|
+
if (typeof v !== 'number' || !Number.isFinite(v)) {
|
|
182
|
+
return fail(`expected finite number, got ${typeof v}`);
|
|
183
|
+
}
|
|
184
|
+
return;
|
|
185
|
+
case 'boolean':
|
|
186
|
+
if (typeof v !== 'boolean')
|
|
187
|
+
return fail(`expected boolean, got ${typeof v}`);
|
|
188
|
+
return;
|
|
189
|
+
case 'date':
|
|
190
|
+
if (typeof v !== 'string')
|
|
191
|
+
return fail(`expected ISO-date string, got ${typeof v}`);
|
|
192
|
+
return;
|
|
193
|
+
case 'multiselect':
|
|
194
|
+
if (!Array.isArray(v) || v.some((x) => typeof x !== 'string')) {
|
|
195
|
+
return fail('expected string[]');
|
|
196
|
+
}
|
|
197
|
+
return;
|
|
198
|
+
default:
|
|
199
|
+
// Unknown value types pass through; the legacy storage path will
|
|
200
|
+
// reject anything pathological.
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
//# sourceMappingURL=product-overrides.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"product-overrides.service.js","sourceRoot":"","sources":["../../../src/backend/services/product-overrides.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { EntityManager } from '@mikro-orm/postgresql';
|
|
2
|
+
import type { LanguageReadPort, ProductScopeContextResponse } from '@endora-commerce/contracts';
|
|
3
|
+
import type { SalesChannelMembershipPort } from '@endora-commerce/platform/kernel';
|
|
4
|
+
import { ProductEditorPreference } from '../entities/product-editor-preference.entity.js';
|
|
5
|
+
import type { ProductEditorPreferencesService } from './product-editor-preferences.service.js';
|
|
6
|
+
/**
|
|
7
|
+
* Feature 022 — assembles the response of
|
|
8
|
+
* `GET /admin/products/:id/scope-context`.
|
|
9
|
+
*
|
|
10
|
+
* Combines:
|
|
11
|
+
* - the product's assigned channels (via SalesChannelMembershipPort),
|
|
12
|
+
* each carrying its language list (`SalesChannel.languages` JSONB),
|
|
13
|
+
* - the union of those languages,
|
|
14
|
+
* - the platform's primary admin language (LanguageService.getDefault()),
|
|
15
|
+
* - the editor's remembered context (ProductEditorPreference).
|
|
16
|
+
*
|
|
17
|
+
* If the product has zero assigned channels, `languagesUnion` falls
|
|
18
|
+
* back to every active platform language so the editor can still
|
|
19
|
+
* translate the global baseline.
|
|
20
|
+
*/
|
|
21
|
+
export declare class ProductScopeContextService {
|
|
22
|
+
private readonly emFactory;
|
|
23
|
+
private readonly salesChannelMembership;
|
|
24
|
+
private readonly languageService;
|
|
25
|
+
private readonly editorPreferences;
|
|
26
|
+
constructor(emFactory: () => EntityManager, salesChannelMembership: SalesChannelMembershipPort, languageService: LanguageReadPort, editorPreferences: ProductEditorPreferencesService);
|
|
27
|
+
getContext(productId: string, adminUserId: string): Promise<ProductScopeContextResponse>;
|
|
28
|
+
/**
|
|
29
|
+
* Validate that a remembered preference is still applicable for the
|
|
30
|
+
* given product. Used by the PUT editor-preference endpoint.
|
|
31
|
+
*/
|
|
32
|
+
assertChannelAssignedToProduct(productId: string, channelId: string): Promise<void>;
|
|
33
|
+
/** Re-export for tests + consumers needing direct entity access. */
|
|
34
|
+
preferenceEntityClass(): typeof ProductEditorPreference;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=product-scope-context.service.d.ts.map
|