@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,248 @@
|
|
|
1
|
+
import { Category } from '../entities/category.entity.js';
|
|
2
|
+
/**
|
|
3
|
+
* The category read model `catalog` publishes (feature 075, Phase P).
|
|
4
|
+
*
|
|
5
|
+
* Fourteen inbound sites read the `Category` entity: the inventory threshold
|
|
6
|
+
* resolver, `price_lists` walking the ancestor chain, `product_feeds` mapping
|
|
7
|
+
* the tree onto an external taxonomy, `seo` rendering a category page.
|
|
8
|
+
*
|
|
9
|
+
* `ancestorsOf` is the one method that is not a transcription of an existing
|
|
10
|
+
* call. `price_lists` walks the parent pointer with a `findOne` per level —
|
|
11
|
+
* a loop whose depth is data, in a module that does not own the table. Moving
|
|
12
|
+
* it here bounds it: the write path forbids a cycle, but a corrupt
|
|
13
|
+
* `parent_id` should degrade into a short answer rather than hang a request.
|
|
14
|
+
*/
|
|
15
|
+
const MAX_ANCESTOR_DEPTH = 64;
|
|
16
|
+
export class CatalogCategoryReadService {
|
|
17
|
+
emFactory;
|
|
18
|
+
constructor(emFactory) {
|
|
19
|
+
this.emFactory = emFactory;
|
|
20
|
+
}
|
|
21
|
+
async findById(id, options) {
|
|
22
|
+
const category = await this.emFactory().findOne(Category, {
|
|
23
|
+
id,
|
|
24
|
+
...liveFilter(options),
|
|
25
|
+
});
|
|
26
|
+
return category ? toCatalogCategoryRecord(category) : null;
|
|
27
|
+
}
|
|
28
|
+
async findByIds(ids, options) {
|
|
29
|
+
if (ids.length === 0)
|
|
30
|
+
return [];
|
|
31
|
+
const categories = await this.emFactory().find(Category, {
|
|
32
|
+
id: { $in: [...ids] },
|
|
33
|
+
...liveFilter(options),
|
|
34
|
+
});
|
|
35
|
+
return categories.map(toCatalogCategoryRecord);
|
|
36
|
+
}
|
|
37
|
+
async findBySlug(slug) {
|
|
38
|
+
const category = await this.emFactory().findOne(Category, { slug });
|
|
39
|
+
return category ? toCatalogCategoryRecord(category) : null;
|
|
40
|
+
}
|
|
41
|
+
async countByIds(ids) {
|
|
42
|
+
if (ids.length === 0)
|
|
43
|
+
return 0;
|
|
44
|
+
return this.emFactory().count(Category, { id: { $in: [...ids] } });
|
|
45
|
+
}
|
|
46
|
+
async listAll(options) {
|
|
47
|
+
const categories = await this.emFactory().find(Category, liveFilter(options), { orderBy: { sortOrder: 'asc', slug: 'asc' } });
|
|
48
|
+
return categories.map(toCatalogCategoryRecord);
|
|
49
|
+
}
|
|
50
|
+
async listWithInventoryThresholds() {
|
|
51
|
+
const categories = await this.emFactory().find(Category, {
|
|
52
|
+
$or: [
|
|
53
|
+
{ inventoryThresholdHigh: { $ne: null } },
|
|
54
|
+
{ inventoryThresholdMedium: { $ne: null } },
|
|
55
|
+
{ inventoryThresholdLow: { $ne: null } },
|
|
56
|
+
],
|
|
57
|
+
});
|
|
58
|
+
return categories.map(toCatalogCategoryRecord);
|
|
59
|
+
}
|
|
60
|
+
async ancestorsOf(categoryId) {
|
|
61
|
+
const em = this.emFactory();
|
|
62
|
+
const chain = [];
|
|
63
|
+
const seen = new Set();
|
|
64
|
+
let cursorId = categoryId;
|
|
65
|
+
while (cursorId !== null && chain.length < MAX_ANCESTOR_DEPTH && !seen.has(cursorId)) {
|
|
66
|
+
seen.add(cursorId);
|
|
67
|
+
const cursor = await em.findOne(Category, { id: cursorId });
|
|
68
|
+
if (!cursor)
|
|
69
|
+
break;
|
|
70
|
+
chain.push(toCatalogCategoryRecord(cursor));
|
|
71
|
+
cursorId = cursor.parentCategoryId ?? null;
|
|
72
|
+
}
|
|
73
|
+
return chain;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Feature 075 / D-87 — the `product_categories` bridge, which has no entity
|
|
77
|
+
* class and was therefore joined in raw SQL by whoever needed it. `search`
|
|
78
|
+
* ran this statement three times from inside its indexer.
|
|
79
|
+
*
|
|
80
|
+
* `em.execute` rather than `getConnection().execute`: the two are the same
|
|
81
|
+
* outside a transaction and only the first joins one when the caller has it
|
|
82
|
+
* open (issue #200).
|
|
83
|
+
*/
|
|
84
|
+
async listAssignmentsForProducts(productIds, options) {
|
|
85
|
+
if (productIds.length === 0)
|
|
86
|
+
return [];
|
|
87
|
+
const placeholders = productIds.map(() => '?').join(',');
|
|
88
|
+
const activeFilter = options?.activeOnly === true ? ' and c.is_active = true' : '';
|
|
89
|
+
const rows = await this.emFactory().execute(`select pc.product_id, pc.category_id, c.slug
|
|
90
|
+
from product_categories pc
|
|
91
|
+
join categories c on c.id = pc.category_id
|
|
92
|
+
where pc.product_id in (${placeholders})${activeFilter}`, [...productIds]);
|
|
93
|
+
return rows.map((row) => ({
|
|
94
|
+
productId: row.product_id,
|
|
95
|
+
categoryId: row.category_id,
|
|
96
|
+
slug: row.slug,
|
|
97
|
+
}));
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* One recursive query rather than a query per level: a subtree re-projection
|
|
101
|
+
* runs on every category write, and the depth of the tree is data.
|
|
102
|
+
*
|
|
103
|
+
* No `is_active` / `deleted_at` filter anywhere in the walk — see the port's
|
|
104
|
+
* contract. The caller re-projects *because* a category changed, and a
|
|
105
|
+
* deactivation is the change that matters most.
|
|
106
|
+
*/
|
|
107
|
+
async listProductIdsInSubtree(categoryId) {
|
|
108
|
+
const rows = await this.emFactory().execute(`with recursive subtree as (
|
|
109
|
+
select id from categories where id = ?
|
|
110
|
+
union all
|
|
111
|
+
select c.id from categories c join subtree s on c.parent_category_id = s.id
|
|
112
|
+
)
|
|
113
|
+
select distinct pc.product_id
|
|
114
|
+
from product_categories pc
|
|
115
|
+
join subtree s on s.id = pc.category_id`, [categoryId]);
|
|
116
|
+
return rows.map((row) => row.product_id);
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* The selection walk — see the port's contract for why it is not a batched
|
|
120
|
+
* {@link listProductIdsInSubtree}. It skips soft-deleted categories and it
|
|
121
|
+
* tolerates a cycle; the structural read above does neither, deliberately.
|
|
122
|
+
*
|
|
123
|
+
* It was `CatalogQueryService.expandCategoryProductIds` until
|
|
124
|
+
* `specs/110-instance-repository/` T118c, reached by both composition roots
|
|
125
|
+
* over the unpublished `catalogQueryPort` name and by nothing else. It is a
|
|
126
|
+
* category read, so it belongs to the category read model; there is one copy.
|
|
127
|
+
*/
|
|
128
|
+
async expandCategoryProductIds(categoryIds) {
|
|
129
|
+
const result = new Map();
|
|
130
|
+
if (categoryIds.length === 0)
|
|
131
|
+
return result;
|
|
132
|
+
const em = this.emFactory();
|
|
133
|
+
const requested = [...new Set(categoryIds)];
|
|
134
|
+
// Descendant ids per requested root (BFS, one query per level for the
|
|
135
|
+
// whole batch — the tree is shallow and this keeps the round trips flat).
|
|
136
|
+
const descendants = new Map();
|
|
137
|
+
for (const id of requested)
|
|
138
|
+
descendants.set(id, [id]);
|
|
139
|
+
// Visited set per root. Re-parenting is guarded against cycles
|
|
140
|
+
// (`category-admin.service.ts`), but that guard itself is written to
|
|
141
|
+
// tolerate *pre-existing* cycles in the data, so a corrupted
|
|
142
|
+
// `parent_category_id` chain is reachable here. Without this the loop
|
|
143
|
+
// never terminates: it would re-query the same level forever and grow
|
|
144
|
+
// `descendants` without bound.
|
|
145
|
+
const seen = new Map(requested.map((id) => [id, new Set([id])]));
|
|
146
|
+
let frontier = new Map(requested.map((id) => [id, [id]]));
|
|
147
|
+
while (frontier.size > 0) {
|
|
148
|
+
const parentIds = [...new Set([...frontier.values()].flat())];
|
|
149
|
+
const children = await em.find(Category, {
|
|
150
|
+
parentCategoryId: { $in: parentIds },
|
|
151
|
+
deletedAt: null,
|
|
152
|
+
});
|
|
153
|
+
if (children.length === 0)
|
|
154
|
+
break;
|
|
155
|
+
const childrenByParent = new Map();
|
|
156
|
+
for (const child of children) {
|
|
157
|
+
const parent = String(child.parentCategoryId);
|
|
158
|
+
childrenByParent.set(parent, [...(childrenByParent.get(parent) ?? []), child.id]);
|
|
159
|
+
}
|
|
160
|
+
const next = new Map();
|
|
161
|
+
for (const [root, level] of frontier) {
|
|
162
|
+
const visited = seen.get(root) ?? new Set();
|
|
163
|
+
const nextLevel = level
|
|
164
|
+
.flatMap((id) => childrenByParent.get(id) ?? [])
|
|
165
|
+
.filter((id) => !visited.has(id));
|
|
166
|
+
if (nextLevel.length === 0)
|
|
167
|
+
continue;
|
|
168
|
+
for (const id of nextLevel)
|
|
169
|
+
visited.add(id);
|
|
170
|
+
seen.set(root, visited);
|
|
171
|
+
descendants.set(root, [...(descendants.get(root) ?? []), ...nextLevel]);
|
|
172
|
+
next.set(root, nextLevel);
|
|
173
|
+
}
|
|
174
|
+
frontier = next;
|
|
175
|
+
}
|
|
176
|
+
const allIds = [...new Set([...descendants.values()].flat())];
|
|
177
|
+
const rows = await em.execute(`select category_id, product_id from product_categories
|
|
178
|
+
where category_id in (${allIds.map(() => '?').join(',')})`, allIds);
|
|
179
|
+
const productsByCategory = new Map();
|
|
180
|
+
for (const row of rows) {
|
|
181
|
+
productsByCategory.set(row.category_id, [
|
|
182
|
+
...(productsByCategory.get(row.category_id) ?? []),
|
|
183
|
+
row.product_id,
|
|
184
|
+
]);
|
|
185
|
+
}
|
|
186
|
+
for (const id of requested) {
|
|
187
|
+
const set = new Set();
|
|
188
|
+
for (const categoryId of descendants.get(id) ?? []) {
|
|
189
|
+
for (const productId of productsByCategory.get(categoryId) ?? [])
|
|
190
|
+
set.add(productId);
|
|
191
|
+
}
|
|
192
|
+
result.set(id, set);
|
|
193
|
+
}
|
|
194
|
+
return result;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* The one category, not its subtree — see the port's contract for why the two
|
|
198
|
+
* are separate methods. `pim_ergonode` wrote this statement itself before the
|
|
199
|
+
* port had it, to detach the products of a category the source tree dropped.
|
|
200
|
+
*/
|
|
201
|
+
async listProductIdsInCategory(categoryId) {
|
|
202
|
+
const rows = await this.emFactory().execute('select distinct pc.product_id from product_categories pc where pc.category_id = ?', [categoryId]);
|
|
203
|
+
return rows.map((row) => row.product_id);
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* One grouped query rather than a count per id: the caller renders a source
|
|
207
|
+
* hierarchy of a few hundred bound categories on one screen, so a count per
|
|
208
|
+
* row would be a few hundred round trips every time it loads.
|
|
209
|
+
*/
|
|
210
|
+
async countLiveProductsByCategory(categoryIds) {
|
|
211
|
+
if (categoryIds.length === 0)
|
|
212
|
+
return [];
|
|
213
|
+
const placeholders = categoryIds.map(() => '?').join(',');
|
|
214
|
+
const rows = await this.emFactory().execute(`select pc.category_id, count(*)::int as product_count
|
|
215
|
+
from product_categories pc
|
|
216
|
+
join products p on p.id = pc.product_id and p.deleted_at is null
|
|
217
|
+
where pc.category_id in (${placeholders})
|
|
218
|
+
group by pc.category_id`, [...categoryIds]);
|
|
219
|
+
return rows.map((row) => ({
|
|
220
|
+
categoryId: row.category_id,
|
|
221
|
+
productCount: Number(row.product_count),
|
|
222
|
+
}));
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
function liveFilter(options) {
|
|
226
|
+
return options?.liveOnly ? { deletedAt: null } : {};
|
|
227
|
+
}
|
|
228
|
+
export function toCatalogCategoryRecord(category) {
|
|
229
|
+
return {
|
|
230
|
+
id: category.id,
|
|
231
|
+
parentCategoryId: category.parentCategoryId ?? null,
|
|
232
|
+
name: category.name,
|
|
233
|
+
slug: category.slug,
|
|
234
|
+
sortOrder: category.sortOrder,
|
|
235
|
+
metaTitleOverride: category.metaTitleOverride ?? null,
|
|
236
|
+
metaDescriptionOverride: category.metaDescriptionOverride ?? null,
|
|
237
|
+
customFieldValues: category.customFieldValues ?? {},
|
|
238
|
+
isActive: category.isActive,
|
|
239
|
+
inventoryThresholdHigh: category.inventoryThresholdHigh ?? null,
|
|
240
|
+
inventoryThresholdMedium: category.inventoryThresholdMedium ?? null,
|
|
241
|
+
inventoryThresholdLow: category.inventoryThresholdLow ?? null,
|
|
242
|
+
mainImageAssetId: category.mainImageAssetId ?? null,
|
|
243
|
+
createdAt: category.createdAt,
|
|
244
|
+
updatedAt: category.updatedAt,
|
|
245
|
+
deletedAt: category.deletedAt ?? null,
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
//# sourceMappingURL=catalog-category-read.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalog-category-read.service.js","sourceRoot":"","sources":["../../../src/backend/services/catalog-category-read.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { EntityManager } from '@mikro-orm/postgresql';
|
|
2
|
+
import type { CatalogBulkPriceLine, OrgLinePricePort, OrganizationDetailsPort, ProductAvailability, ProductDetail, ProductSummary } from '@endora-commerce/contracts';
|
|
3
|
+
import type { SalesChannelMembershipPort } from '@endora-commerce/platform/kernel';
|
|
4
|
+
/**
|
|
5
|
+
* Feature 062 (research §R7) — pricing/availability decoration for the
|
|
6
|
+
* external catalog namespace (`/api/v1/external/catalog/*`).
|
|
7
|
+
*
|
|
8
|
+
* Thin decoration ONLY: query logic stays in `CatalogQueryService`, price
|
|
9
|
+
* resolution stays in `PricingService` (the exact resolver cart pricing
|
|
10
|
+
* uses — SC-001 parity by construction). This service:
|
|
11
|
+
*
|
|
12
|
+
* - for BOUND keys, replaces the summary/detail `price` with the bound
|
|
13
|
+
* Organization's effective unit price at quantity 1, sets the
|
|
14
|
+
* `priceUnavailable` marker, and (detail only) derives the org's
|
|
15
|
+
* quantity-bracket `priceTiers` ladder by probing the resolver at each
|
|
16
|
+
* bracket start quantity;
|
|
17
|
+
* - for ANY key, attaches the channel-public `availability` indication via
|
|
18
|
+
* the injected inventory port;
|
|
19
|
+
* - answers the bound-only bulk `(sku, quantity)` pricing request with
|
|
20
|
+
* per-line results (misses are data, not errors).
|
|
21
|
+
*/
|
|
22
|
+
/** The request's resolved sales channel — only `id` + `defaultCurrency` are read. */
|
|
23
|
+
export interface ExternalPricingChannel {
|
|
24
|
+
id: string;
|
|
25
|
+
defaultCurrency: string;
|
|
26
|
+
}
|
|
27
|
+
export type ResolveAvailabilityPort = (productIds: string[], salesChannelId: string) => Promise<Map<string, ProductAvailability>>;
|
|
28
|
+
export interface CatalogOrgPriceDecoratorDeps {
|
|
29
|
+
emFactory: () => EntityManager;
|
|
30
|
+
/**
|
|
31
|
+
* Feature 075 — the published line-pricing port, where
|
|
32
|
+
* `price_lists`' `PricingServiceContract` used to be. This decorator calls
|
|
33
|
+
* two of its methods. `OrgLinePricePort` — `LinePricePort` plus the bracket
|
|
34
|
+
* ladder — was published in this cut, because `PricingServiceContract` is
|
|
35
|
+
* deliberately not in `@endora-commerce/contracts` (it is the overlay decoration's
|
|
36
|
+
* contract gate) and was `listBracketMinQuantities`' only declaration.
|
|
37
|
+
*/
|
|
38
|
+
pricingService: OrgLinePricePort;
|
|
39
|
+
/**
|
|
40
|
+
* Feature 075 — the buying organisation, read through `organizations`' port
|
|
41
|
+
* instead of `em.findOne(Organization, …)` against its table. The engine
|
|
42
|
+
* reads two fields off it (the id, and the customer group a rule keys on),
|
|
43
|
+
* and `OrganizationRecord` carries both.
|
|
44
|
+
*/
|
|
45
|
+
organizations: OrganizationDetailsPort;
|
|
46
|
+
/** Optional inventory port — when absent, `availability` is omitted. */
|
|
47
|
+
resolveAvailability?: ResolveAvailabilityPort;
|
|
48
|
+
/**
|
|
49
|
+
* Issue #185 — the kernel's channel-membership accessor. The published
|
|
50
|
+
* assortment check below asked `sales_channel_products` directly, which is
|
|
51
|
+
* the bridge Constitution XII reserves to this service and a cross-module
|
|
52
|
+
* read no import specifier could reveal.
|
|
53
|
+
*/
|
|
54
|
+
channelMembership: SalesChannelMembershipPort;
|
|
55
|
+
}
|
|
56
|
+
export declare class CatalogOrgPriceDecorator {
|
|
57
|
+
#private;
|
|
58
|
+
private readonly deps;
|
|
59
|
+
constructor(deps: CatalogOrgPriceDecoratorDeps);
|
|
60
|
+
/**
|
|
61
|
+
* Decorates list summaries in place-order: availability for every caller,
|
|
62
|
+
* org-effective qty-1 price + `priceUnavailable` for bound callers.
|
|
63
|
+
*/
|
|
64
|
+
decorateSummaries(summaries: ProductSummary[], context: {
|
|
65
|
+
organizationId: string | null;
|
|
66
|
+
salesChannel: ExternalPricingChannel;
|
|
67
|
+
}): Promise<ProductSummary[]>;
|
|
68
|
+
/**
|
|
69
|
+
* Detail decoration = summary decoration + (bound only) the org's resolved
|
|
70
|
+
* bracket ladder.
|
|
71
|
+
*/
|
|
72
|
+
decorateDetail(detail: ProductDetail, context: {
|
|
73
|
+
organizationId: string | null;
|
|
74
|
+
salesChannel: ExternalPricingChannel;
|
|
75
|
+
}): Promise<ProductDetail>;
|
|
76
|
+
/**
|
|
77
|
+
* Bound-only bulk pricing: per-line, order-preserving. A SKU outside the
|
|
78
|
+
* bound channel's published assortment (or unknown / inactive / deleted)
|
|
79
|
+
* is indistinguishable from non-existence (`sku_not_in_assortment`); an
|
|
80
|
+
* in-assortment SKU with no resolvable bracket is `price_unavailable`.
|
|
81
|
+
*/
|
|
82
|
+
resolveBulkPrices(lines: Array<{
|
|
83
|
+
sku: string;
|
|
84
|
+
quantity: number;
|
|
85
|
+
}>, context: {
|
|
86
|
+
organizationId: string;
|
|
87
|
+
salesChannel: ExternalPricingChannel;
|
|
88
|
+
}): Promise<CatalogBulkPriceLine[]>;
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=catalog-org-price-decorator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalog-org-price-decorator.d.ts","sourceRoot":"","sources":["../../../src/backend/services/catalog-org-price-decorator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EACV,oBAAoB,EACpB,gBAAgB,EAChB,uBAAuB,EAEvB,mBAAmB,EACnB,aAAa,EAEb,cAAc,EACf,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,kCAAkC,CAAC;AAInF;;;;;;;;;;;;;;;;;GAiBG;AAEH,qFAAqF;AACrF,MAAM,WAAW,sBAAsB;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,MAAM,uBAAuB,GAAG,CACpC,UAAU,EAAE,MAAM,EAAE,EACpB,cAAc,EAAE,MAAM,KACnB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC,CAAC;AAE/C,MAAM,WAAW,4BAA4B;IAC3C,SAAS,EAAE,MAAM,aAAa,CAAC;IAC/B;;;;;;;OAOG;IACH,cAAc,EAAE,gBAAgB,CAAC;IACjC;;;;;OAKG;IACH,aAAa,EAAE,uBAAuB,CAAC;IACvC,wEAAwE;IACxE,mBAAmB,CAAC,EAAE,uBAAuB,CAAC;IAC9C;;;;;OAKG;IACH,iBAAiB,EAAE,0BAA0B,CAAC;CAC/C;AAED,qBAAa,wBAAwB;;IACvB,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,4BAA4B;IAE/D;;;OAGG;IACG,iBAAiB,CACrB,SAAS,EAAE,cAAc,EAAE,EAC3B,OAAO,EAAE;QAAE,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,YAAY,EAAE,sBAAsB,CAAA;KAAE,GAC/E,OAAO,CAAC,cAAc,EAAE,CAAC;IA4C5B;;;OAGG;IACG,cAAc,CAClB,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE;QAAE,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,YAAY,EAAE,sBAAsB,CAAA;KAAE,GAC/E,OAAO,CAAC,aAAa,CAAC;IAazB;;;;;OAKG;IACG,iBAAiB,CACrB,KAAK,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,EAC/C,OAAO,EAAE;QAAE,cAAc,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,sBAAsB,CAAA;KAAE,GACxE,OAAO,CAAC,oBAAoB,EAAE,CAAC;CA8InC"}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { Product } from '../entities/product.entity.js';
|
|
2
|
+
export class CatalogOrgPriceDecorator {
|
|
3
|
+
deps;
|
|
4
|
+
constructor(deps) {
|
|
5
|
+
this.deps = deps;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Decorates list summaries in place-order: availability for every caller,
|
|
9
|
+
* org-effective qty-1 price + `priceUnavailable` for bound callers.
|
|
10
|
+
*/
|
|
11
|
+
async decorateSummaries(summaries, context) {
|
|
12
|
+
if (summaries.length === 0)
|
|
13
|
+
return summaries;
|
|
14
|
+
const availabilityByProduct = await this.#resolveAvailability(summaries.map((s) => s.id), context.salesChannel.id);
|
|
15
|
+
if (context.organizationId === null) {
|
|
16
|
+
return summaries.map((summary) => this.#withAvailability(summary, availabilityByProduct));
|
|
17
|
+
}
|
|
18
|
+
const em = this.deps.emFactory();
|
|
19
|
+
const organization = await this.deps.organizations.findById(context.organizationId);
|
|
20
|
+
const products = await em.find(Product, { id: { $in: summaries.map((s) => s.id) } });
|
|
21
|
+
const productById = new Map(products.map((p) => [p.id, p]));
|
|
22
|
+
const out = [];
|
|
23
|
+
for (const summary of summaries) {
|
|
24
|
+
const decorated = this.#withAvailability(summary, availabilityByProduct);
|
|
25
|
+
const product = productById.get(summary.id);
|
|
26
|
+
if (!product) {
|
|
27
|
+
out.push({ ...decorated, price: null, priceUnavailable: true });
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
const resolved = await this.deps.pricingService.resolveLinePrice({
|
|
31
|
+
product,
|
|
32
|
+
context: {
|
|
33
|
+
quantity: 1,
|
|
34
|
+
organization,
|
|
35
|
+
salesChannel: context.salesChannel,
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
out.push(resolved
|
|
39
|
+
? {
|
|
40
|
+
...decorated,
|
|
41
|
+
price: { amount: Number(resolved.amount), currency: resolved.currency },
|
|
42
|
+
}
|
|
43
|
+
: { ...decorated, price: null, priceUnavailable: true });
|
|
44
|
+
}
|
|
45
|
+
return out;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Detail decoration = summary decoration + (bound only) the org's resolved
|
|
49
|
+
* bracket ladder.
|
|
50
|
+
*/
|
|
51
|
+
async decorateDetail(detail, context) {
|
|
52
|
+
const [decorated] = await this.decorateSummaries([detail], context);
|
|
53
|
+
const merged = { ...detail, ...decorated };
|
|
54
|
+
if (context.organizationId === null)
|
|
55
|
+
return merged;
|
|
56
|
+
const em = this.deps.emFactory();
|
|
57
|
+
const organization = await this.deps.organizations.findById(context.organizationId);
|
|
58
|
+
const product = await em.findOne(Product, { id: detail.id });
|
|
59
|
+
if (!product)
|
|
60
|
+
return merged;
|
|
61
|
+
const priceTiers = await this.#resolvePriceTiers(product, organization, context.salesChannel);
|
|
62
|
+
return { ...merged, priceTiers };
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Bound-only bulk pricing: per-line, order-preserving. A SKU outside the
|
|
66
|
+
* bound channel's published assortment (or unknown / inactive / deleted)
|
|
67
|
+
* is indistinguishable from non-existence (`sku_not_in_assortment`); an
|
|
68
|
+
* in-assortment SKU with no resolvable bracket is `price_unavailable`.
|
|
69
|
+
*/
|
|
70
|
+
async resolveBulkPrices(lines, context) {
|
|
71
|
+
const em = this.deps.emFactory();
|
|
72
|
+
const organization = await this.deps.organizations.findById(context.organizationId);
|
|
73
|
+
const skus = Array.from(new Set(lines.map((l) => l.sku)));
|
|
74
|
+
const products = await em.find(Product, {
|
|
75
|
+
sku: { $in: skus },
|
|
76
|
+
deletedAt: null,
|
|
77
|
+
status: 'active',
|
|
78
|
+
});
|
|
79
|
+
const productBySku = new Map(products.map((p) => [p.sku, p]));
|
|
80
|
+
const assortment = await this.#filterByChannel(products.map((p) => p.id), context.salesChannel.id);
|
|
81
|
+
const out = [];
|
|
82
|
+
for (const line of lines) {
|
|
83
|
+
const product = productBySku.get(line.sku);
|
|
84
|
+
if (!product || !assortment.has(product.id)) {
|
|
85
|
+
out.push({
|
|
86
|
+
sku: line.sku,
|
|
87
|
+
quantity: line.quantity,
|
|
88
|
+
price: null,
|
|
89
|
+
reason: 'sku_not_in_assortment',
|
|
90
|
+
});
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
const resolved = await this.deps.pricingService.resolveLinePrice({
|
|
94
|
+
product,
|
|
95
|
+
context: {
|
|
96
|
+
quantity: line.quantity,
|
|
97
|
+
organization,
|
|
98
|
+
salesChannel: context.salesChannel,
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
if (!resolved) {
|
|
102
|
+
out.push({
|
|
103
|
+
sku: line.sku,
|
|
104
|
+
quantity: line.quantity,
|
|
105
|
+
price: null,
|
|
106
|
+
reason: 'price_unavailable',
|
|
107
|
+
});
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
out.push({
|
|
111
|
+
sku: line.sku,
|
|
112
|
+
quantity: line.quantity,
|
|
113
|
+
price: {
|
|
114
|
+
amount: resolved.amount,
|
|
115
|
+
currency: resolved.currency,
|
|
116
|
+
isSale: resolved.isSale,
|
|
117
|
+
bracketStartQuantity: resolved.bracketStartQuantity,
|
|
118
|
+
priceListId: resolved.priceListId,
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
return out;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* The org's effective ladder: probe the resolver at every bracket start
|
|
126
|
+
* quantity known for the (product, currency) across active lists, then
|
|
127
|
+
* drop consecutive rungs that resolve identically (a non-matching list's
|
|
128
|
+
* boundary changes nothing for this org — no cross-org leak).
|
|
129
|
+
*/
|
|
130
|
+
async #resolvePriceTiers(product, organization, salesChannel) {
|
|
131
|
+
const currencyCode = salesChannel.defaultCurrency.toUpperCase();
|
|
132
|
+
const minQuantities = await this.deps.pricingService.listBracketMinQuantities(product.id, currencyCode);
|
|
133
|
+
const probeQuantities = Array.from(new Set([1, ...minQuantities])).sort((a, b) => a - b);
|
|
134
|
+
const tiers = [];
|
|
135
|
+
for (const quantity of probeQuantities) {
|
|
136
|
+
const resolved = await this.deps.pricingService.resolveLinePrice({
|
|
137
|
+
product,
|
|
138
|
+
context: { quantity, organization, salesChannel },
|
|
139
|
+
});
|
|
140
|
+
if (!resolved)
|
|
141
|
+
continue;
|
|
142
|
+
const tier = {
|
|
143
|
+
minQuantity: quantity,
|
|
144
|
+
amount: Number(resolved.amount),
|
|
145
|
+
currency: resolved.currency,
|
|
146
|
+
isSale: resolved.isSale,
|
|
147
|
+
};
|
|
148
|
+
const previous = tiers[tiers.length - 1];
|
|
149
|
+
if (previous && previous.amount === tier.amount && previous.isSale === tier.isSale) {
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
tiers.push(tier);
|
|
153
|
+
}
|
|
154
|
+
return tiers;
|
|
155
|
+
}
|
|
156
|
+
async #resolveAvailability(productIds, salesChannelId) {
|
|
157
|
+
// D-61 — no `catch` here. `inventory` being absent is *decided* by the
|
|
158
|
+
// contribution, in front of the gate, and arrives as the empty map this
|
|
159
|
+
// method already returns when nothing is wired; the manifest declares that
|
|
160
|
+
// degrade as `degrades-without`. What a `catch` would add is the ability to
|
|
161
|
+
// turn a genuine inventory failure into a silently unpriced band.
|
|
162
|
+
if (!this.deps.resolveAvailability)
|
|
163
|
+
return new Map();
|
|
164
|
+
return this.deps.resolveAvailability(productIds, salesChannelId);
|
|
165
|
+
}
|
|
166
|
+
#withAvailability(summary, availabilityByProduct) {
|
|
167
|
+
const availability = availabilityByProduct.get(summary.id);
|
|
168
|
+
return availability ? { ...summary, availability } : summary;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Published-assortment check — the same question
|
|
172
|
+
* `CatalogQueryService.filterByChannel` asks, through the same accessor since
|
|
173
|
+
* issue #185. This was a hand-written `select product_id from
|
|
174
|
+
* sales_channel_products`, which is precisely the read Principle XII reserves
|
|
175
|
+
* to the channel-membership service; it fails closed to the empty set either
|
|
176
|
+
* way.
|
|
177
|
+
*/
|
|
178
|
+
async #filterByChannel(productIds, salesChannelId) {
|
|
179
|
+
if (productIds.length === 0)
|
|
180
|
+
return new Set();
|
|
181
|
+
return new Set(await this.deps.channelMembership.filterEntityIdsInChannel(salesChannelId, 'product', productIds));
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
//# sourceMappingURL=catalog-org-price-decorator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalog-org-price-decorator.js","sourceRoot":"","sources":["../../../src/backend/services/catalog-org-price-decorator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type { EntityManager } from '@mikro-orm/postgresql';
|
|
2
|
+
import type { CatalogProductFilter, CatalogProductFilterPort, CatalogProductRecord, CatalogSellableProductQuery } from '@endora-commerce/contracts';
|
|
3
|
+
/**
|
|
4
|
+
* The sellable-product filter `catalog` publishes (feature 075).
|
|
5
|
+
*
|
|
6
|
+
* `product_feeds` compiles an operator's selection rule into a predicate and
|
|
7
|
+
* walks the catalogue with it. Until this port existed it did that by holding
|
|
8
|
+
* `Product` and handing MikroORM a `where` object of its own construction —
|
|
9
|
+
* the one demand Phase P declined to guess at, because a query object is the
|
|
10
|
+
* ORM rather than a contract.
|
|
11
|
+
*
|
|
12
|
+
* Three things move across the boundary with the translation, and each of them
|
|
13
|
+
* is why the port is worth more than the import it replaces:
|
|
14
|
+
*
|
|
15
|
+
* 1. **The floor is applied here.** Feature 067's FR-026 says an operator's
|
|
16
|
+
* rule may only ever narrow "active, visible to the feed's reader, not
|
|
17
|
+
* archived, not soft-deleted, in the feed's channel". On the caller's side
|
|
18
|
+
* that was a conjunction a future edit could spread into one object and
|
|
19
|
+
* lose; here it is one `$and` clause the caller cannot reach. Who "the
|
|
20
|
+
* feed's reader" is has one answer and it is not this file's to invent —
|
|
21
|
+
* see {@link ANONYMOUS_AUDIENCE_CLAUSE}.
|
|
22
|
+
* 2. **The cursor is applied here.** A keyset walk and the floor both
|
|
23
|
+
* constrain `id`, and so does a category criterion. Composing all three
|
|
24
|
+
* was the caller's problem and is now nobody's.
|
|
25
|
+
* 3. **Rows leave as records.** The caller reads six fields off each row and
|
|
26
|
+
* yields ids; it never needed a managed entity, and holding one made the
|
|
27
|
+
* identity map grow with the catalogue rather than with the page.
|
|
28
|
+
*
|
|
29
|
+
* ## What the translation may and may not do
|
|
30
|
+
*
|
|
31
|
+
* The filter grammar is deliberately narrower than MikroORM: six columns, the
|
|
32
|
+
* JSONB attribute bag, twelve operators, `and` / `or`, and the two constants.
|
|
33
|
+
* There is no node this function could translate into a join, a relation walk
|
|
34
|
+
* or a raw fragment, which is what stops the port becoming the general query
|
|
35
|
+
* surface publishing the DSL would have made it.
|
|
36
|
+
*
|
|
37
|
+
* `contains` and `startsWith` build their own patterns, so the caller never
|
|
38
|
+
* writes `LIKE` syntax and the escaping rule has exactly one home — this file.
|
|
39
|
+
* See {@link escapeLikeLiteral} for what that rule is and why the value has to
|
|
40
|
+
* go through it.
|
|
41
|
+
*/
|
|
42
|
+
/**
|
|
43
|
+
* The one place a `contains` / `startsWith` value becomes part of a pattern.
|
|
44
|
+
*
|
|
45
|
+
* Both operators name a **literal**: `contains "50%"` means the three
|
|
46
|
+
* characters `5`, `0`, `%`. `LIKE` reads `%` as "anything", `_` as "any one
|
|
47
|
+
* character" and — in PostgreSQL, absent an explicit `ESCAPE` clause — `\` as
|
|
48
|
+
* the escape character, so a value carrying any of the three used to mean
|
|
49
|
+
* something other than what the operator promises. Issue #181: an unescaped
|
|
50
|
+
* `contains "50%"` selected every product with "50" followed by anything.
|
|
51
|
+
*
|
|
52
|
+
* `\` is the escape character used here rather than an explicit `ESCAPE`
|
|
53
|
+
* clause, because there is nowhere to put one: MikroORM renders `$ilike` as
|
|
54
|
+
* `column ilike ?` with the pattern bound as a parameter, and reaching for a
|
|
55
|
+
* raw fragment to append `ESCAPE` would put SQL text back into a translation
|
|
56
|
+
* whose whole point is that it has no node for one. PostgreSQL's default escape
|
|
57
|
+
* character already *is* `\`, so escaping with it needs no clause — and it has
|
|
58
|
+
* to be escaped first in its own right, or a value ending in a backslash would
|
|
59
|
+
* escape the wildcard this function's callers append.
|
|
60
|
+
*
|
|
61
|
+
* Note the failure was not only over-matching: PostgreSQL degrades an escape
|
|
62
|
+
* sequence it does not recognise to the plain character, so the unescaped
|
|
63
|
+
* pattern for `C\D` also *missed* the row that actually holds the backslash.
|
|
64
|
+
*/
|
|
65
|
+
export declare function escapeLikeLiteral(value: string): string;
|
|
66
|
+
export declare class CatalogProductFilterService implements CatalogProductFilterPort {
|
|
67
|
+
private readonly emFactory;
|
|
68
|
+
constructor(emFactory: () => EntityManager);
|
|
69
|
+
listSellable(query: CatalogSellableProductQuery): Promise<CatalogProductRecord[]>;
|
|
70
|
+
countSellable(query: Omit<CatalogSellableProductQuery, 'afterId' | 'limit'>): Promise<number>;
|
|
71
|
+
/**
|
|
72
|
+
* Floor AND filter AND cursor, as an explicit `$and`.
|
|
73
|
+
*
|
|
74
|
+
* Never an object spread: all three constrain `id`, and spreading them keeps
|
|
75
|
+
* only the last one — which for a category criterion would drop the channel
|
|
76
|
+
* scoping altogether.
|
|
77
|
+
*/
|
|
78
|
+
private where;
|
|
79
|
+
}
|
|
80
|
+
/** `null` when the node constrains nothing, so the caller can omit the clause. */
|
|
81
|
+
export declare function translateFilter(filter: CatalogProductFilter): Record<string, unknown> | null;
|
|
82
|
+
//# sourceMappingURL=catalog-product-filter.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalog-product-filter.service.d.ts","sourceRoot":"","sources":["../../../src/backend/services/catalog-product-filter.service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAe,MAAM,uBAAuB,CAAC;AACxE,OAAO,KAAK,EACV,oBAAoB,EAEpB,wBAAwB,EACxB,oBAAoB,EACpB,2BAA2B,EAC5B,MAAM,4BAA4B,CAAC;AAIpC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAEH;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEvD;AAkBD,qBAAa,2BAA4B,YAAW,wBAAwB;IAC9D,OAAO,CAAC,QAAQ,CAAC,SAAS;gBAAT,SAAS,EAAE,MAAM,aAAa;IAErD,YAAY,CAAC,KAAK,EAAE,2BAA2B,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC;IAejF,aAAa,CACjB,KAAK,EAAE,IAAI,CAAC,2BAA2B,EAAE,SAAS,GAAG,OAAO,CAAC,GAC5D,OAAO,CAAC,MAAM,CAAC;IAQlB;;;;;;OAMG;IACH,OAAO,CAAC,KAAK;CAOd;AA2FD,kFAAkF;AAClF,wBAAgB,eAAe,CAAC,MAAM,EAAE,oBAAoB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAS5F"}
|