@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,510 @@
|
|
|
1
|
+
import { randomUUID } from 'crypto';
|
|
2
|
+
import { BULK_OPERATION_TYPES, } from '@endora-commerce/contracts';
|
|
3
|
+
import { effectiveState } from '@endora-commerce/platform/kernel';
|
|
4
|
+
import { rethrowIfModuleDisabled } from '@endora-commerce/platform/kernel';
|
|
5
|
+
import { applyUndo } from '@endora-commerce/platform/commands';
|
|
6
|
+
import { Product } from '../entities/product.entity.js';
|
|
7
|
+
import { BulkOperation, } from '../entities/bulk-operation.entity.js';
|
|
8
|
+
/**
|
|
9
|
+
* BulkOperationService — queued product bulk-edit.
|
|
10
|
+
*
|
|
11
|
+
* Owns the lifecycle of {@link BulkOperation} rows:
|
|
12
|
+
* - `create()` persists a `pending` row and enqueues it (via the injected
|
|
13
|
+
* `onEnqueued` producer) onto the durable BullMQ queue — it never
|
|
14
|
+
* inline-executes the job (Constitution Principle X).
|
|
15
|
+
* - `processById()` is the consumer entry point invoked by the BullMQ
|
|
16
|
+
* worker: it claims the row atomically (`pending → running`) and applies
|
|
17
|
+
* it through {@link CatalogBulkUpdateService} with live progress persisted
|
|
18
|
+
* on the row. The conditional claim makes it idempotent under BullMQ
|
|
19
|
+
* redelivery / retries and safe at N ≥ 2 worker instances.
|
|
20
|
+
* - `processPending()` drains every `pending` row in-process; it is kept for
|
|
21
|
+
* manual scripts / tests only — the deployed path is the queue worker.
|
|
22
|
+
* - On completion / failure the requester is notified twice: an
|
|
23
|
+
* in-app bell notification and an email.
|
|
24
|
+
* - `list()` / `get()` back the "Bulk actions" admin page.
|
|
25
|
+
*/
|
|
26
|
+
/** Per-product outcomes are capped on the row so a huge run can't bloat it. */
|
|
27
|
+
const MAX_PERSISTED_RESULTS = 1000;
|
|
28
|
+
/**
|
|
29
|
+
* Wraps the gated port in the presence decision, so absence is *decided* rather
|
|
30
|
+
* than caught (D-60; Constitution XVII).
|
|
31
|
+
*
|
|
32
|
+
* A function rather than an object literal at the composition site on purpose:
|
|
33
|
+
* `check-port-catches.ts` follows the port **through the value**, and an object
|
|
34
|
+
* literal is where that trail deliberately stops.
|
|
35
|
+
*/
|
|
36
|
+
export function presenceAwareBulkRecorder(adminNotifications) {
|
|
37
|
+
return {
|
|
38
|
+
async record(input) {
|
|
39
|
+
// First, and outside any `try`: a closed gate throws rather than
|
|
40
|
+
// answering, so asking after the call is asking too late.
|
|
41
|
+
if (!effectiveState.isPresent('admin_notifications'))
|
|
42
|
+
return 'not-present';
|
|
43
|
+
await adminNotifications.record(input);
|
|
44
|
+
return 'recorded';
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
export class BulkOperationService {
|
|
49
|
+
emFactory;
|
|
50
|
+
bulkUpdateService;
|
|
51
|
+
deps;
|
|
52
|
+
commandBus;
|
|
53
|
+
/** Single-flight guard so overlapping sweep ticks don't double-process. */
|
|
54
|
+
draining = false;
|
|
55
|
+
constructor(emFactory, bulkUpdateService, deps = {},
|
|
56
|
+
/**
|
|
57
|
+
* Feature 054 — when injected, product bulk edits capture per-product revert
|
|
58
|
+
* state and expose an audited, conflict-aware undo (US2). Optional so bus-less
|
|
59
|
+
* constructions (some tests) keep the pre-054 behavior.
|
|
60
|
+
*/
|
|
61
|
+
commandBus) {
|
|
62
|
+
this.emFactory = emFactory;
|
|
63
|
+
this.bulkUpdateService = bulkUpdateService;
|
|
64
|
+
this.deps = deps;
|
|
65
|
+
this.commandBus = commandBus;
|
|
66
|
+
}
|
|
67
|
+
async create(input) {
|
|
68
|
+
// command-coverage-ignore: catalog_bulk_operations row bookkeeping (queue
|
|
69
|
+
// status / progress counters); the actual product mutations are audited by
|
|
70
|
+
// updateProduct. The operation record itself is not a domain-audit target.
|
|
71
|
+
const em = this.emFactory();
|
|
72
|
+
const op = new BulkOperation();
|
|
73
|
+
op.type = input.type ?? 'product_bulk_update';
|
|
74
|
+
op.status = 'pending';
|
|
75
|
+
op.requestedByAdminUserId = input.requestedByAdminUserId;
|
|
76
|
+
op.total = input.payload.productIds.length;
|
|
77
|
+
op.payload = input.payload;
|
|
78
|
+
op.logs = [
|
|
79
|
+
logEntry('info', op.type === BULK_OPERATION_TYPES.SEARCH_REINDEX
|
|
80
|
+
? 'Operacja przeindeksowania wyszukiwarki utworzona i dodana do kolejki.'
|
|
81
|
+
: `Operacja utworzona i dodana do kolejki (${op.total} elementów).`),
|
|
82
|
+
];
|
|
83
|
+
await em.persistAndFlush(op);
|
|
84
|
+
// Producer side (Principle X): enqueue and return. Never block the HTTP
|
|
85
|
+
// response on actual processing. Enqueue errors are non-fatal — the row
|
|
86
|
+
// stays `pending` and the worker's boot reconciliation re-enqueues it.
|
|
87
|
+
try {
|
|
88
|
+
await this.deps.onEnqueued?.(op.id);
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
/* enqueue is best-effort; reconciliation is the safety net */
|
|
92
|
+
}
|
|
93
|
+
return serialize(op);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Consumer entry point invoked by the BullMQ worker for one queued
|
|
97
|
+
* operation. Atomically claims the row (`pending → running`); if the claim
|
|
98
|
+
* affects no row the operation was already taken or finished, so this is a
|
|
99
|
+
* no-op — making the handler idempotent under redelivery/retries and safe at
|
|
100
|
+
* N ≥ 2 worker instances.
|
|
101
|
+
*/
|
|
102
|
+
async processById(operationId) {
|
|
103
|
+
// command-coverage-ignore: catalog_bulk_operations row bookkeeping (queue
|
|
104
|
+
// status / progress counters); the actual product mutations are audited by
|
|
105
|
+
// updateProduct. The operation record itself is not a domain-audit target.
|
|
106
|
+
const em = this.emFactory();
|
|
107
|
+
const affected = await em.nativeUpdate(BulkOperation, { id: operationId, status: 'pending' }, { status: 'running', startedAt: new Date() });
|
|
108
|
+
if (affected === 0)
|
|
109
|
+
return;
|
|
110
|
+
await this.processOne(operationId);
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Ids of every operation still `pending`. Used by the worker at boot to
|
|
114
|
+
* re-enqueue rows that were created while no producer/queue was reachable
|
|
115
|
+
* (e.g. an enqueue failure, or a row predating this deployment).
|
|
116
|
+
*/
|
|
117
|
+
async findPendingIds() {
|
|
118
|
+
const em = this.emFactory();
|
|
119
|
+
const rows = await em.find(BulkOperation, { status: 'pending' }, { fields: ['id'], orderBy: { createdAt: 'asc' } });
|
|
120
|
+
return rows.map((r) => r.id);
|
|
121
|
+
}
|
|
122
|
+
async list(input = {}) {
|
|
123
|
+
const em = this.emFactory();
|
|
124
|
+
const where = input.status && input.status.length > 0 ? { status: { $in: input.status } } : {};
|
|
125
|
+
const limit = Math.min(Math.max(input.limit ?? 25, 1), 100);
|
|
126
|
+
const offset = Math.max(input.offset ?? 0, 0);
|
|
127
|
+
const [rows, total] = await em.findAndCount(BulkOperation, where, {
|
|
128
|
+
orderBy: { createdAt: 'desc' },
|
|
129
|
+
limit,
|
|
130
|
+
offset,
|
|
131
|
+
});
|
|
132
|
+
return { items: rows.map(serialize), total };
|
|
133
|
+
}
|
|
134
|
+
async get(id) {
|
|
135
|
+
const em = this.emFactory();
|
|
136
|
+
const op = await em.findOne(BulkOperation, { id });
|
|
137
|
+
return op ? serialize(op) : null;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Drain every `pending` operation. Safe to call repeatedly and
|
|
141
|
+
* concurrently — the single-flight guard ensures one drain at a time.
|
|
142
|
+
*/
|
|
143
|
+
async processPending() {
|
|
144
|
+
if (this.draining)
|
|
145
|
+
return { processed: 0 };
|
|
146
|
+
this.draining = true;
|
|
147
|
+
let processed = 0;
|
|
148
|
+
try {
|
|
149
|
+
for (;;) {
|
|
150
|
+
const next = await this.claimNextPending();
|
|
151
|
+
if (!next)
|
|
152
|
+
break;
|
|
153
|
+
await this.processOne(next);
|
|
154
|
+
processed += 1;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
finally {
|
|
158
|
+
this.draining = false;
|
|
159
|
+
}
|
|
160
|
+
return { processed };
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Atomically flip the oldest `pending` row to `running` and return its
|
|
164
|
+
* id. The conditional UPDATE means even if two drains race past the
|
|
165
|
+
* in-process guard (e.g. across worker restarts) only one wins a row.
|
|
166
|
+
*/
|
|
167
|
+
async claimNextPending() {
|
|
168
|
+
// command-coverage-ignore: catalog_bulk_operations row bookkeeping (queue
|
|
169
|
+
// status / progress counters); the actual product mutations are audited by
|
|
170
|
+
// updateProduct. The operation record itself is not a domain-audit target.
|
|
171
|
+
const em = this.emFactory();
|
|
172
|
+
const candidate = await em.findOne(BulkOperation, { status: 'pending' }, { orderBy: { createdAt: 'asc' } });
|
|
173
|
+
if (!candidate)
|
|
174
|
+
return null;
|
|
175
|
+
const affected = await em.nativeUpdate(BulkOperation, { id: candidate.id, status: 'pending' }, { status: 'running', startedAt: new Date() });
|
|
176
|
+
if (affected === 0) {
|
|
177
|
+
// Lost the race — try again on the next loop iteration.
|
|
178
|
+
return this.claimNextPending();
|
|
179
|
+
}
|
|
180
|
+
return candidate.id;
|
|
181
|
+
}
|
|
182
|
+
async processOne(id) {
|
|
183
|
+
// command-coverage-ignore: catalog_bulk_operations row bookkeeping (queue
|
|
184
|
+
// status / progress counters); the actual product mutations are audited by
|
|
185
|
+
// updateProduct. The operation record itself is not a domain-audit target.
|
|
186
|
+
const em = this.emFactory();
|
|
187
|
+
const op = await em.findOne(BulkOperation, { id });
|
|
188
|
+
if (!op)
|
|
189
|
+
return;
|
|
190
|
+
const logs = Array.isArray(op.logs) ? [...op.logs] : [];
|
|
191
|
+
logs.push(logEntry('info', `Rozpoczęto przetwarzanie operacji (${op.total} elementów).`));
|
|
192
|
+
if (op.type === BULK_OPERATION_TYPES.SEARCH_REINDEX) {
|
|
193
|
+
await this.processReindex(op, logs);
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
const req = {
|
|
197
|
+
productIds: op.payload.productIds,
|
|
198
|
+
fields: op.payload.fields,
|
|
199
|
+
};
|
|
200
|
+
// Feature 054 (US2) — capture per-product before-state for undo, but only
|
|
201
|
+
// when the edit touches revertible direct columns (never the category
|
|
202
|
+
// bridge). Read BEFORE the mutation so the snapshot is the true pre-state.
|
|
203
|
+
const revertKeys = this.commandBus ? revertibleFieldKeys(op.payload.fields) : null;
|
|
204
|
+
const beforeByProduct = revertKeys && revertKeys.length > 0
|
|
205
|
+
? await this.#snapshotProductFields(em, op.payload.productIds, revertKeys)
|
|
206
|
+
: null;
|
|
207
|
+
try {
|
|
208
|
+
const result = await this.bulkUpdateService.bulkUpdate(req, { actorAdminUserId: op.requestedByAdminUserId }, {
|
|
209
|
+
skipBatchLimit: true,
|
|
210
|
+
onProgress: async (p) => {
|
|
211
|
+
await em.nativeUpdate(BulkOperation, { id }, {
|
|
212
|
+
processed: p.processed,
|
|
213
|
+
succeeded: p.succeeded,
|
|
214
|
+
skipped: p.skipped,
|
|
215
|
+
failed: p.failed,
|
|
216
|
+
});
|
|
217
|
+
},
|
|
218
|
+
});
|
|
219
|
+
logs.push(logEntry('info', `Zakończono: ${result.summary.succeeded} z powodzeniem, ${result.summary.skipped} pominięto, ${result.summary.failed} z błędem (łącznie ${result.summary.total}).`));
|
|
220
|
+
if (result.summary.failed > 0) {
|
|
221
|
+
logs.push(logEntry('warn', `${result.summary.failed} elementów zakończyło się błędem — szczegóły w statusie pojedynczych elementów.`));
|
|
222
|
+
}
|
|
223
|
+
// Build revert state from the products that actually succeeded, reading
|
|
224
|
+
// their post-state so undo can detect later changes (research §R5).
|
|
225
|
+
let revertState = null;
|
|
226
|
+
if (revertKeys && revertKeys.length > 0 && beforeByProduct) {
|
|
227
|
+
const succeededIds = result.results
|
|
228
|
+
.filter((r) => r.status === 'succeeded')
|
|
229
|
+
.map((r) => r.productId)
|
|
230
|
+
.filter((pid) => beforeByProduct.has(pid));
|
|
231
|
+
const afterByProduct = await this.#snapshotProductFields(em, succeededIds, revertKeys);
|
|
232
|
+
revertState = succeededIds.map((pid) => ({
|
|
233
|
+
recordId: pid,
|
|
234
|
+
before: beforeByProduct.get(pid),
|
|
235
|
+
after: afterByProduct.get(pid) ?? {},
|
|
236
|
+
}));
|
|
237
|
+
}
|
|
238
|
+
await em.nativeUpdate(BulkOperation, { id }, {
|
|
239
|
+
status: 'completed',
|
|
240
|
+
processed: result.summary.total,
|
|
241
|
+
succeeded: result.summary.succeeded,
|
|
242
|
+
skipped: result.summary.skipped,
|
|
243
|
+
failed: result.summary.failed,
|
|
244
|
+
results: result.results.slice(0, MAX_PERSISTED_RESULTS),
|
|
245
|
+
logs,
|
|
246
|
+
finishedAt: new Date(),
|
|
247
|
+
...(revertState && revertState.length > 0
|
|
248
|
+
? { reversible: true, revertState }
|
|
249
|
+
: {}),
|
|
250
|
+
});
|
|
251
|
+
await this.notify(op, 'completed', result.summary);
|
|
252
|
+
}
|
|
253
|
+
catch (err) {
|
|
254
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
255
|
+
logs.push(logEntry('error', `Operacja nie powiodła się: ${message}`));
|
|
256
|
+
await em.nativeUpdate(BulkOperation, { id }, { status: 'failed', error: message, logs, finishedAt: new Date() });
|
|
257
|
+
await this.notify(op, 'failed', null, message);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Runs a `search_reindex` operation: a full Meilisearch reindex (the
|
|
262
|
+
* `search:reindex` CLI equivalent). Counts every reindexed document as
|
|
263
|
+
* both processed and succeeded; failure surfaces on the row + notifications.
|
|
264
|
+
*/
|
|
265
|
+
async processReindex(op, logs = []) {
|
|
266
|
+
// command-coverage-ignore: catalog_bulk_operations row bookkeeping (queue
|
|
267
|
+
// status / progress counters); the actual product mutations are audited by
|
|
268
|
+
// updateProduct. The operation record itself is not a domain-audit target.
|
|
269
|
+
const em = this.emFactory();
|
|
270
|
+
if (!this.deps.reindexRunner) {
|
|
271
|
+
const message = 'Search reindex handler is not configured.';
|
|
272
|
+
logs.push(logEntry('error', `Operacja nie powiodła się: ${message}`));
|
|
273
|
+
await em.nativeUpdate(BulkOperation, { id: op.id }, { status: 'failed', error: message, logs, finishedAt: new Date() });
|
|
274
|
+
await this.notify(op, 'failed', null, message);
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
try {
|
|
278
|
+
const { documentCount } = await this.deps.reindexRunner();
|
|
279
|
+
logs.push(logEntry('info', `Przeindeksowano ${documentCount} dokumentów we wszystkich indeksach.`));
|
|
280
|
+
await em.nativeUpdate(BulkOperation, { id: op.id }, {
|
|
281
|
+
status: 'completed',
|
|
282
|
+
total: documentCount,
|
|
283
|
+
processed: documentCount,
|
|
284
|
+
succeeded: documentCount,
|
|
285
|
+
skipped: 0,
|
|
286
|
+
failed: 0,
|
|
287
|
+
logs,
|
|
288
|
+
finishedAt: new Date(),
|
|
289
|
+
});
|
|
290
|
+
await this.notify(op, 'completed', {
|
|
291
|
+
succeeded: documentCount,
|
|
292
|
+
skipped: 0,
|
|
293
|
+
failed: 0,
|
|
294
|
+
total: documentCount,
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
catch (err) {
|
|
298
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
299
|
+
logs.push(logEntry('error', `Operacja nie powiodła się: ${message}`));
|
|
300
|
+
await em.nativeUpdate(BulkOperation, { id: op.id }, { status: 'failed', error: message, logs, finishedAt: new Date() });
|
|
301
|
+
await this.notify(op, 'failed', null, message);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
async notify(op, status, summary, error) {
|
|
305
|
+
const admin = await this.deps.adminUsers?.findById(op.requestedByAdminUserId);
|
|
306
|
+
const succeeded = summary?.succeeded ?? 0;
|
|
307
|
+
const total = summary?.total ?? op.total;
|
|
308
|
+
const isReindex = op.type === BULK_OPERATION_TYPES.SEARCH_REINDEX;
|
|
309
|
+
const title = isReindex
|
|
310
|
+
? status === 'completed'
|
|
311
|
+
? `Search reindex finished — ${total} documents indexed`
|
|
312
|
+
: 'Search reindex failed'
|
|
313
|
+
: status === 'completed'
|
|
314
|
+
? `Bulk edit finished — ${succeeded}/${total} updated`
|
|
315
|
+
: 'Bulk edit failed';
|
|
316
|
+
const body = isReindex
|
|
317
|
+
? status === 'completed'
|
|
318
|
+
? `The search index was rebuilt: ${total} documents indexed.`
|
|
319
|
+
: `The search reindex could not be completed: ${error ?? 'unknown error'}.`
|
|
320
|
+
: status === 'completed'
|
|
321
|
+
? `${total} products processed: ${succeeded} succeeded, ${summary?.skipped ?? 0} skipped, ${summary?.failed ?? 0} failed.`
|
|
322
|
+
: `The bulk edit of ${op.total} products could not be completed: ${error ?? 'unknown error'}.`;
|
|
323
|
+
try {
|
|
324
|
+
// The outcome is read rather than discarded: `not-present` is the
|
|
325
|
+
// operator having switched `admin_notifications` off, which is their
|
|
326
|
+
// choice and not a failure, and it used to be indistinguishable from a
|
|
327
|
+
// failed write inside this `catch` (D-60).
|
|
328
|
+
await this.deps.notificationService?.record({
|
|
329
|
+
audience: 'admin_user',
|
|
330
|
+
targetAdminUserId: op.requestedByAdminUserId,
|
|
331
|
+
kind: `catalog.bulk_operation.${status}`,
|
|
332
|
+
subjectType: 'bulk_operation',
|
|
333
|
+
subjectId: op.id,
|
|
334
|
+
title,
|
|
335
|
+
body,
|
|
336
|
+
linkPath: '/catalog/bulk-operations',
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
catch (err) {
|
|
340
|
+
// Narrow, and correct: the bell is written when the operation has already
|
|
341
|
+
// finished and its row carries the outcome, so a failed write must not
|
|
342
|
+
// fail a job whose work is done. Presence is decided by the recorder
|
|
343
|
+
// before the port is reached, so what is left here is a failed write.
|
|
344
|
+
rethrowIfModuleDisabled(err);
|
|
345
|
+
/* notification is best-effort */
|
|
346
|
+
}
|
|
347
|
+
if (this.deps.mailer && admin?.email) {
|
|
348
|
+
try {
|
|
349
|
+
const outcome = await this.deps.mailer.send({
|
|
350
|
+
messageId: `bulk-op-${op.id}-${status}`,
|
|
351
|
+
to: admin.email,
|
|
352
|
+
subject: title,
|
|
353
|
+
text: `${body}\n\nView the bulk actions page in the admin panel for details.`,
|
|
354
|
+
kind: `catalog.bulk_operation.${status}`,
|
|
355
|
+
meta: { bulkOperationId: op.id, status },
|
|
356
|
+
});
|
|
357
|
+
if (outcome.status !== 'sent') {
|
|
358
|
+
// The products are already written; this only tells the operator it
|
|
359
|
+
// finished. Named rather than silent, and durable in D-59's record.
|
|
360
|
+
console.warn('[catalog] the bulk-operation e-mail was not sent', {
|
|
361
|
+
bulkOperationId: op.id,
|
|
362
|
+
reason: outcome.reason,
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
catch {
|
|
367
|
+
/* email is best-effort */
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
// ---- Feature 054 (US2) — undo -----------------------------------------
|
|
372
|
+
/**
|
|
373
|
+
* Undo a reversible bulk edit: restore every affected product whose current
|
|
374
|
+
* state still matches the operation's captured after-state; refuse (report,
|
|
375
|
+
* never clobber) any product changed since. Runs as an audited `bulk.undo`
|
|
376
|
+
* Command and is idempotent-safe on re-invocation (FR-006/FR-007/FR-014).
|
|
377
|
+
*/
|
|
378
|
+
async undo(operationId) {
|
|
379
|
+
const em = this.emFactory();
|
|
380
|
+
const op = await em.findOne(BulkOperation, { id: operationId });
|
|
381
|
+
if (!op)
|
|
382
|
+
return { ok: false, code: 'NOT_FOUND' };
|
|
383
|
+
if (op.status !== 'completed' || !op.reversible || !op.revertState || op.revertState.length === 0) {
|
|
384
|
+
return { ok: false, code: 'NOT_REVERSIBLE' };
|
|
385
|
+
}
|
|
386
|
+
if (op.undoStatus === 'reverted')
|
|
387
|
+
return { ok: false, code: 'ALREADY_REVERTED' };
|
|
388
|
+
if (!this.commandBus)
|
|
389
|
+
return { ok: false, code: 'NOT_REVERSIBLE' };
|
|
390
|
+
const records = op.revertState.map((r) => ({
|
|
391
|
+
recordId: r.recordId,
|
|
392
|
+
before: r.before,
|
|
393
|
+
after: r.after,
|
|
394
|
+
}));
|
|
395
|
+
return this.commandBus.run(this.#undoCommand(operationId, records));
|
|
396
|
+
}
|
|
397
|
+
#undoCommand(operationId, records) {
|
|
398
|
+
return {
|
|
399
|
+
action: 'product.bulk_update.undo',
|
|
400
|
+
objectType: 'bulk_operation',
|
|
401
|
+
objectId: operationId,
|
|
402
|
+
run: async ({ em }) => {
|
|
403
|
+
const op = await em.findOne(BulkOperation, { id: operationId });
|
|
404
|
+
if (!op || op.undoStatus === 'reverted') {
|
|
405
|
+
return {
|
|
406
|
+
result: { ok: true, reverted: [], conflicts: [], undoStatus: 'reverted' },
|
|
407
|
+
skipAudit: true,
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
const res = await applyUndo(em, records, {
|
|
411
|
+
readCurrent: async (tem, rec) => {
|
|
412
|
+
const p = await tem.findOne(Product, { id: rec.recordId });
|
|
413
|
+
return p ? pickProductFields(p, Object.keys(rec.after)) : null;
|
|
414
|
+
},
|
|
415
|
+
restore: async (tem, rec) => {
|
|
416
|
+
const p = await tem.findOne(Product, { id: rec.recordId });
|
|
417
|
+
if (!p)
|
|
418
|
+
return;
|
|
419
|
+
tem.assign(p, rec.before);
|
|
420
|
+
tem.persist(p);
|
|
421
|
+
},
|
|
422
|
+
});
|
|
423
|
+
op.undoStatus = res.undoStatus === 'reverted' ? 'reverted' : 'partially_reverted';
|
|
424
|
+
op.undoneAt = new Date();
|
|
425
|
+
op.undoOperationId = randomUUID();
|
|
426
|
+
em.persist(op);
|
|
427
|
+
return {
|
|
428
|
+
result: { ok: true, ...res },
|
|
429
|
+
after: { reverted: res.reverted.length, conflicts: res.conflicts.length },
|
|
430
|
+
};
|
|
431
|
+
},
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
/** Read `productIds` and pick each product's values for `keys`. */
|
|
435
|
+
async #snapshotProductFields(em, productIds, keys) {
|
|
436
|
+
const out = new Map();
|
|
437
|
+
if (productIds.length === 0)
|
|
438
|
+
return out;
|
|
439
|
+
const products = await em.find(Product, { id: { $in: [...productIds] } });
|
|
440
|
+
for (const p of products) {
|
|
441
|
+
out.set(p.id, pickProductFields(p, keys));
|
|
442
|
+
}
|
|
443
|
+
return out;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
/**
|
|
447
|
+
* Field keys of a bulk edit that can be undone by restoring direct product
|
|
448
|
+
* columns. Returns `[]` (⇒ not reversible) when the edit is empty or touches a
|
|
449
|
+
* non-revertible field (the category bridge) — all-or-nothing, never partial.
|
|
450
|
+
*/
|
|
451
|
+
const NON_REVERTIBLE_BULK_FIELDS = new Set(['categoryIds']);
|
|
452
|
+
function revertibleFieldKeys(fields) {
|
|
453
|
+
const keys = Object.keys(fields);
|
|
454
|
+
if (keys.length === 0)
|
|
455
|
+
return [];
|
|
456
|
+
if (keys.some((k) => NON_REVERTIBLE_BULK_FIELDS.has(k)))
|
|
457
|
+
return [];
|
|
458
|
+
const set = new Set(keys);
|
|
459
|
+
// `status` changes also flip `archivedAt` in updateProduct — restore both.
|
|
460
|
+
if (set.has('status'))
|
|
461
|
+
set.add('archivedAt');
|
|
462
|
+
return [...set];
|
|
463
|
+
}
|
|
464
|
+
/** Pick a plain, JSON-cloned snapshot of a product's `keys` for revert state. */
|
|
465
|
+
function pickProductFields(product, keys) {
|
|
466
|
+
const src = product;
|
|
467
|
+
const out = {};
|
|
468
|
+
for (const key of keys) {
|
|
469
|
+
const v = src[key];
|
|
470
|
+
out[key] = v === undefined ? null : JSON.parse(JSON.stringify(v ?? null));
|
|
471
|
+
}
|
|
472
|
+
return out;
|
|
473
|
+
}
|
|
474
|
+
function logEntry(level, message) {
|
|
475
|
+
return { ts: new Date().toISOString(), level, message };
|
|
476
|
+
}
|
|
477
|
+
function touchedFieldsOf(payload) {
|
|
478
|
+
const fields = payload.fields ?? {};
|
|
479
|
+
const keys = Object.keys(fields).filter((k) => k !== 'attributeValues');
|
|
480
|
+
if (fields['attributeValues'] && typeof fields['attributeValues'] === 'object') {
|
|
481
|
+
for (const attrKey of Object.keys(fields['attributeValues'])) {
|
|
482
|
+
keys.push(`attribute:${attrKey}`);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
return keys;
|
|
486
|
+
}
|
|
487
|
+
function serialize(op) {
|
|
488
|
+
return {
|
|
489
|
+
id: op.id,
|
|
490
|
+
type: op.type,
|
|
491
|
+
status: op.status,
|
|
492
|
+
requestedByAdminUserId: op.requestedByAdminUserId,
|
|
493
|
+
total: op.total,
|
|
494
|
+
processed: op.processed,
|
|
495
|
+
succeeded: op.succeeded,
|
|
496
|
+
skipped: op.skipped,
|
|
497
|
+
failed: op.failed,
|
|
498
|
+
touchedFields: touchedFieldsOf(op.payload),
|
|
499
|
+
results: op.results ?? null,
|
|
500
|
+
logs: op.logs ?? null,
|
|
501
|
+
error: op.error ?? null,
|
|
502
|
+
createdAt: op.createdAt.toISOString(),
|
|
503
|
+
startedAt: op.startedAt ? op.startedAt.toISOString() : null,
|
|
504
|
+
finishedAt: op.finishedAt ? op.finishedAt.toISOString() : null,
|
|
505
|
+
reversible: op.reversible,
|
|
506
|
+
undoStatus: op.undoStatus,
|
|
507
|
+
undoneAt: op.undoneAt ? op.undoneAt.toISOString() : null,
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
//# sourceMappingURL=bulk-operation.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bulk-operation.service.js","sourceRoot":"","sources":["../../../src/backend/services/bulk-operation.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { EntityManager } from '@mikro-orm/postgresql';
|
|
2
|
+
import type { CommandBus } from '@endora-commerce/platform/commands';
|
|
3
|
+
/**
|
|
4
|
+
* BundleService — admin CRUD for bundle slots + their options, plus the
|
|
5
|
+
* pure-compute `validateConfiguration` used by the public storefront
|
|
6
|
+
* endpoint (feature 002 US5, T129).
|
|
7
|
+
*
|
|
8
|
+
* Same composite-nesting guard as GroupedService: option products MUST
|
|
9
|
+
* NOT be `type='grouped'` or `type='bundle'` (research R-8).
|
|
10
|
+
*/
|
|
11
|
+
export interface BundleSlotRow {
|
|
12
|
+
id: string;
|
|
13
|
+
parentProductId: string;
|
|
14
|
+
name: Record<string, string>;
|
|
15
|
+
minQuantity: number;
|
|
16
|
+
maxQuantity: number;
|
|
17
|
+
position: number;
|
|
18
|
+
options: BundleSlotOptionRow[];
|
|
19
|
+
}
|
|
20
|
+
export interface BundleSlotOptionRow {
|
|
21
|
+
id: string;
|
|
22
|
+
slotId: string;
|
|
23
|
+
optionProductId: string;
|
|
24
|
+
defaultQuantity: number;
|
|
25
|
+
position: number;
|
|
26
|
+
}
|
|
27
|
+
export interface ConfigurationSelection {
|
|
28
|
+
slotId: string;
|
|
29
|
+
optionId: string;
|
|
30
|
+
quantity: number;
|
|
31
|
+
}
|
|
32
|
+
export interface BundleValidationError {
|
|
33
|
+
code: 'MIN_NOT_MET' | 'MAX_EXCEEDED' | 'UNKNOWN_OPTION';
|
|
34
|
+
slotId?: string;
|
|
35
|
+
message: string;
|
|
36
|
+
}
|
|
37
|
+
export interface BundleValidationResult {
|
|
38
|
+
valid: boolean;
|
|
39
|
+
errors: BundleValidationError[];
|
|
40
|
+
resolvedSelections: Array<{
|
|
41
|
+
slotId: string;
|
|
42
|
+
optionId: string;
|
|
43
|
+
optionProductId: string;
|
|
44
|
+
quantity: number;
|
|
45
|
+
}>;
|
|
46
|
+
}
|
|
47
|
+
export declare class BundleService {
|
|
48
|
+
#private;
|
|
49
|
+
private readonly emFactory;
|
|
50
|
+
/** Feature 054 — audits bundle slot/option writes co-transactionally when provided. */
|
|
51
|
+
private readonly commandBus?;
|
|
52
|
+
constructor(emFactory: () => EntityManager,
|
|
53
|
+
/** Feature 054 — audits bundle slot/option writes co-transactionally when provided. */
|
|
54
|
+
commandBus?: CommandBus | undefined);
|
|
55
|
+
listSlots(parentProductId: string): Promise<BundleSlotRow[]>;
|
|
56
|
+
createSlot(parentProductId: string, input: {
|
|
57
|
+
name: Record<string, string>;
|
|
58
|
+
minQuantity?: number | undefined;
|
|
59
|
+
maxQuantity: number;
|
|
60
|
+
position?: number | undefined;
|
|
61
|
+
}): Promise<BundleSlotRow>;
|
|
62
|
+
updateSlot(parentProductId: string, slotId: string, input: {
|
|
63
|
+
name?: Record<string, string> | undefined;
|
|
64
|
+
minQuantity?: number | undefined;
|
|
65
|
+
maxQuantity?: number | undefined;
|
|
66
|
+
position?: number | undefined;
|
|
67
|
+
}): Promise<BundleSlotRow>;
|
|
68
|
+
deleteSlot(parentProductId: string, slotId: string): Promise<void>;
|
|
69
|
+
addOption(parentProductId: string, slotId: string, input: {
|
|
70
|
+
optionProductId: string;
|
|
71
|
+
defaultQuantity?: number | undefined;
|
|
72
|
+
position?: number | undefined;
|
|
73
|
+
}): Promise<BundleSlotOptionRow>;
|
|
74
|
+
removeOption(parentProductId: string, slotId: string, optionId: string): Promise<void>;
|
|
75
|
+
/**
|
|
76
|
+
* Pure-compute validation of a buyer's bundle configuration. Returns a
|
|
77
|
+
* structured result rather than throwing — the storefront wants the
|
|
78
|
+
* whole error list per-slot so it can highlight every offending row.
|
|
79
|
+
*
|
|
80
|
+
* Throws HttpError 400 only when the parent product is not a bundle
|
|
81
|
+
* (the call doesn't make sense in that case).
|
|
82
|
+
*/
|
|
83
|
+
validateConfiguration(parentProductId: string, selections: ConfigurationSelection[]): Promise<BundleValidationResult>;
|
|
84
|
+
private assertBundleParent;
|
|
85
|
+
private toOptionRow;
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=bundle.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bundle.service.d.ts","sourceRoot":"","sources":["../../../src/backend/services/bundle.service.ts"],"names":[],"mappings":"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"}
|