@openlup/platform 0.17.0 → 0.18.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/CHANGELOG.md +96 -0
- package/README.md +284 -6
- package/dist/catalog/activeSkuSelection.d.ts +2 -0
- package/dist/catalog/activeSkuSelection.js +20 -0
- package/dist/catalog/adminBindings.d.ts +72 -0
- package/dist/catalog/adminBindings.js +1 -0
- package/dist/catalog/adminBundleHandler.d.ts +26 -0
- package/dist/catalog/adminBundleHandler.js +111 -0
- package/dist/catalog/adminBundleReadHandler.d.ts +20 -0
- package/dist/catalog/adminBundleReadHandler.js +185 -0
- package/dist/catalog/adminBundleWritePort.d.ts +85 -0
- package/dist/catalog/adminBundleWritePort.js +1 -0
- package/dist/catalog/bundleAvailability.d.ts +63 -0
- package/dist/catalog/bundleAvailability.js +78 -0
- package/dist/catalog/bundleAvailabilityService.d.ts +65 -0
- package/dist/catalog/bundleAvailabilityService.js +89 -0
- package/dist/catalog/bundleBindings.d.ts +33 -0
- package/dist/catalog/bundleBindings.js +1 -0
- package/dist/catalog/bundleCatalogQueries.d.ts +28 -0
- package/dist/catalog/bundleCatalogQueries.js +84 -0
- package/dist/catalog/bundleCatalogReadPort.d.ts +166 -0
- package/dist/catalog/bundleCatalogReadPort.js +36 -0
- package/dist/catalog/bundleCatalogStore.d.ts +3 -0
- package/dist/catalog/bundleCatalogStore.js +152 -0
- package/dist/catalog/bundleRuleCodes.d.ts +41 -0
- package/dist/catalog/bundleRuleCodes.js +110 -0
- package/dist/catalog/bundleRules.d.ts +69 -0
- package/dist/catalog/bundleRules.js +102 -0
- package/dist/catalog/catalogDraftStore.d.ts +4 -0
- package/dist/catalog/catalogDraftStore.js +63 -0
- package/dist/catalog/catalogSkuEnvelope.d.ts +7 -0
- package/dist/catalog/catalogSkuEnvelope.js +142 -0
- package/dist/catalog/catalogSkuEnvelopeMapper.d.ts +38 -0
- package/dist/catalog/catalogSkuEnvelopeMapper.js +88 -0
- package/dist/catalog/contracts/bundleAdmin.d.ts +143 -0
- package/dist/catalog/contracts/bundleAdmin.js +1 -0
- package/dist/catalog/contracts/bundleAvailability.d.ts +61 -0
- package/dist/catalog/contracts/bundleAvailability.js +1 -0
- package/dist/catalog/contracts/bundleRead.d.ts +223 -0
- package/dist/catalog/contracts/bundleRead.js +1 -0
- package/dist/catalog/contracts/draft.d.ts +79 -0
- package/dist/catalog/contracts/draft.js +47 -0
- package/dist/catalog/contracts/foundation.d.ts +78 -0
- package/dist/catalog/contracts/foundation.js +53 -0
- package/dist/catalog/contracts/index.d.ts +14 -0
- package/dist/catalog/contracts/index.js +6 -0
- package/dist/catalog/contracts/lifecycle.d.ts +77 -0
- package/dist/catalog/contracts/lifecycle.js +1 -0
- package/dist/catalog/contracts/productMediaContracts.d.ts +77 -0
- package/dist/catalog/contracts/productMediaContracts.js +149 -0
- package/dist/catalog/contracts/productTypes.d.ts +54 -0
- package/dist/catalog/contracts/productTypes.js +16 -0
- package/dist/catalog/contracts/resolveProductMedia.d.ts +3 -0
- package/dist/catalog/contracts/resolveProductMedia.js +78 -0
- package/dist/catalog/contracts/sellable.d.ts +23 -0
- package/dist/catalog/contracts/sellable.js +1 -0
- package/dist/catalog/draftHandler.d.ts +25 -0
- package/dist/catalog/draftHandler.js +118 -0
- package/dist/catalog/draftPorts.d.ts +18 -0
- package/dist/catalog/draftPorts.js +8 -0
- package/dist/catalog/draftSchemas.d.ts +423 -0
- package/dist/catalog/draftSchemas.js +89 -0
- package/dist/catalog/draftValidation.d.ts +21 -0
- package/dist/catalog/draftValidation.js +125 -0
- package/dist/catalog/foundationSchemas.d.ts +98 -0
- package/dist/catalog/foundationSchemas.js +110 -0
- package/dist/catalog/index.d.ts +43 -0
- package/dist/catalog/index.js +25 -0
- package/dist/catalog/mediaSourcePack.d.ts +51 -0
- package/dist/catalog/mediaSourcePack.js +247 -0
- package/dist/catalog/productTypeSchemas.d.ts +46 -0
- package/dist/catalog/productTypeSchemas.js +72 -0
- package/dist/catalog/publicationPorts.d.ts +42 -0
- package/dist/catalog/publicationPorts.js +1 -0
- package/dist/catalog/readPorts.d.ts +51 -0
- package/dist/catalog/readPorts.js +1 -0
- package/dist/catalog/referenceCatalogHandler.d.ts +7 -0
- package/dist/catalog/referenceCatalogHandler.js +27 -0
- package/dist/catalog/resolvedSkuProjection.d.ts +88 -0
- package/dist/catalog/resolvedSkuProjection.js +60 -0
- package/dist/catalog/sellableBundleHandler.d.ts +46 -0
- package/dist/catalog/sellableBundleHandler.js +84 -0
- package/dist/catalog/sellableSchemas.d.ts +46 -0
- package/dist/catalog/sellableSchemas.js +27 -0
- package/dist/catalog/sql.d.ts +46 -0
- package/dist/catalog/sql.js +13 -0
- package/dist/catalog/validation.d.ts +2 -0
- package/dist/catalog/validation.js +3 -0
- package/dist/communications/contracts/index.d.ts +1 -0
- package/dist/communications/contracts/marketingSync.d.ts +194 -0
- package/dist/communications/contracts/marketingSync.js +1 -0
- package/dist/communications/index.d.ts +1 -0
- package/dist/communications/klaviyo/consentPush.d.ts +51 -0
- package/dist/communications/klaviyo/consentPush.js +19 -0
- package/dist/communications/klaviyo/index.d.ts +110 -0
- package/dist/communications/klaviyo/index.js +860 -0
- package/dist/communications/newsletterIntegrationContracts.d.ts +3 -3
- package/dist/communications/resend/index.d.ts +40 -0
- package/dist/communications/resend/index.js +310 -0
- package/dist/composition/catalogContribution.d.ts +18 -0
- package/dist/composition/catalogContribution.js +21 -0
- package/dist/composition/index.d.ts +1 -0
- package/dist/composition/index.js +1 -0
- package/dist/identity/customerContracts.d.ts +2 -2
- package/dist/inventory/adminInventory.d.ts +13 -0
- package/dist/inventory/adminInventory.js +214 -0
- package/dist/inventory/contracts/forecast.d.ts +86 -0
- package/dist/inventory/contracts/forecast.js +1 -0
- package/dist/inventory/contracts/index.d.ts +4 -0
- package/dist/inventory/contracts/index.js +1 -0
- package/dist/inventory/contracts/inventory.d.ts +157 -0
- package/dist/inventory/contracts/inventory.js +1 -0
- package/dist/inventory/contracts/newPurchaseStock.d.ts +20 -0
- package/dist/inventory/contracts/newPurchaseStock.js +1 -0
- package/dist/inventory/contracts/reservation.d.ts +18 -0
- package/dist/inventory/contracts/reservation.js +1 -0
- package/dist/inventory/forecast.d.ts +24 -0
- package/dist/inventory/forecast.js +200 -0
- package/dist/inventory/index.d.ts +13 -0
- package/dist/inventory/index.js +10 -0
- package/dist/inventory/inventoryHandlers.d.ts +53 -0
- package/dist/inventory/inventoryHandlers.js +151 -0
- package/dist/inventory/newPurchaseStockAvailability.d.ts +47 -0
- package/dist/inventory/newPurchaseStockAvailability.js +75 -0
- package/dist/inventory/ports.d.ts +21 -0
- package/dist/inventory/ports.js +16 -0
- package/dist/inventory/renewalReservation.d.ts +27 -0
- package/dist/inventory/renewalReservation.js +61 -0
- package/dist/inventory/reservationPolicy.d.ts +6 -0
- package/dist/inventory/reservationPolicy.js +19 -0
- package/dist/inventory/schemas.d.ts +137 -0
- package/dist/inventory/schemas.js +112 -0
- package/dist/inventory/sql.d.ts +14 -0
- package/dist/inventory/sql.js +5 -0
- package/dist/inventory/subscriptionForecastCalculation.d.ts +4 -0
- package/dist/inventory/subscriptionForecastCalculation.js +133 -0
- package/dist/pricing/acquisitionFinancialPolicy.d.ts +26 -0
- package/dist/pricing/acquisitionFinancialPolicy.js +1 -0
- package/dist/pricing/acquisitionOfferPricing.d.ts +27 -0
- package/dist/pricing/acquisitionOfferPricing.js +25 -0
- package/dist/pricing/acquisitionOfferPricingHandler.d.ts +20 -0
- package/dist/pricing/acquisitionOfferPricingHandler.js +105 -0
- package/dist/pricing/acquisitionOfferPricingSchemas.d.ts +31 -0
- package/dist/pricing/acquisitionOfferPricingSchemas.js +33 -0
- package/dist/pricing/acquisitionQuoteBinding.d.ts +12 -0
- package/dist/pricing/acquisitionQuoteBinding.js +12 -0
- package/dist/pricing/acquisitionReadiness.d.ts +31 -0
- package/dist/pricing/acquisitionReadiness.js +117 -0
- package/dist/pricing/acquisitionTerms.d.ts +19 -0
- package/dist/pricing/acquisitionTerms.js +126 -0
- package/dist/pricing/acquisitionTermsSchemas.d.ts +59 -0
- package/dist/pricing/acquisitionTermsSchemas.js +42 -0
- package/dist/pricing/catalogFactsProvenance.d.ts +33 -0
- package/dist/pricing/catalogFactsProvenance.js +64 -0
- package/dist/pricing/catalogFactsSchemas.d.ts +45 -0
- package/dist/pricing/catalogFactsSchemas.js +58 -0
- package/dist/pricing/commerceOfferPricing.d.ts +25 -0
- package/dist/pricing/commerceOfferPricing.js +139 -0
- package/dist/pricing/commerceOfferPricingCache.d.ts +10 -0
- package/dist/pricing/commerceOfferPricingCache.js +44 -0
- package/dist/pricing/commerceOfferPricingHandler.d.ts +21 -0
- package/dist/pricing/commerceOfferPricingHandler.js +78 -0
- package/dist/pricing/commercePriceAuthority.d.ts +26 -0
- package/dist/pricing/commercePriceAuthority.js +79 -0
- package/dist/pricing/commercePricingPolicyErrors.d.ts +40 -0
- package/dist/pricing/commercePricingPolicyErrors.js +67 -0
- package/dist/pricing/commerceQuoteBatchHandler.d.ts +37 -0
- package/dist/pricing/commerceQuoteBatchHandler.js +83 -0
- package/dist/pricing/commerceQuoteCatalogReadPort.d.ts +27 -0
- package/dist/pricing/commerceQuoteCatalogReadPort.js +8 -0
- package/dist/pricing/commerceQuoteHandler.d.ts +31 -0
- package/dist/pricing/commerceQuoteHandler.js +110 -0
- package/dist/pricing/commerceQuoteSnapshotExpect.d.ts +9 -0
- package/dist/pricing/commerceQuoteSnapshotExpect.js +22 -0
- package/dist/pricing/contracts/acquisitionTerms.d.ts +32 -0
- package/dist/pricing/contracts/acquisitionTerms.js +2 -0
- package/dist/pricing/contracts/authority.d.ts +27 -0
- package/dist/pricing/contracts/authority.js +1 -0
- package/dist/pricing/contracts/evidence.d.ts +33 -0
- package/dist/pricing/contracts/evidence.js +1 -0
- package/dist/pricing/contracts/index.d.ts +17 -0
- package/dist/pricing/contracts/index.js +7 -0
- package/dist/pricing/contracts/money.d.ts +27 -0
- package/dist/pricing/contracts/money.js +1 -0
- package/dist/pricing/contracts/offer.d.ts +52 -0
- package/dist/pricing/contracts/offer.js +4 -0
- package/dist/pricing/contracts/offerPolicy.d.ts +16 -0
- package/dist/pricing/contracts/offerPolicy.js +5 -0
- package/dist/pricing/contracts/policy.d.ts +22 -0
- package/dist/pricing/contracts/policy.js +3 -0
- package/dist/pricing/contracts/provenance.d.ts +24 -0
- package/dist/pricing/contracts/provenance.js +2 -0
- package/dist/pricing/contracts/quote.d.ts +94 -0
- package/dist/pricing/contracts/quote.js +1 -0
- package/dist/pricing/dbBackedCommerceQuoteHelpers.d.ts +78 -0
- package/dist/pricing/dbBackedCommerceQuoteHelpers.js +146 -0
- package/dist/pricing/dbBackedCommerceQuotePort.d.ts +66 -0
- package/dist/pricing/dbBackedCommerceQuotePort.js +206 -0
- package/dist/pricing/dbBackedCommerceQuoteSnapshotVerifier.d.ts +4 -0
- package/dist/pricing/dbBackedCommerceQuoteSnapshotVerifier.js +25 -0
- package/dist/pricing/index.d.ts +51 -0
- package/dist/pricing/index.js +36 -0
- package/dist/pricing/offerPolicyReadiness.d.ts +21 -0
- package/dist/pricing/offerPolicyReadiness.js +79 -0
- package/dist/pricing/offerPolicySchemas.d.ts +25 -0
- package/dist/pricing/offerPolicySchemas.js +15 -0
- package/dist/pricing/offerPricingSchemas.d.ts +315 -0
- package/dist/pricing/offerPricingSchemas.js +65 -0
- package/dist/pricing/ports.d.ts +13 -0
- package/dist/pricing/ports.js +9 -0
- package/dist/pricing/pricingPolicyToken.d.ts +39 -0
- package/dist/pricing/pricingPolicyToken.js +84 -0
- package/dist/pricing/quoteAcquisitionTerms.d.ts +2 -0
- package/dist/pricing/quoteAcquisitionTerms.js +4 -0
- package/dist/pricing/quoteCodeRejectionDetails.d.ts +20 -0
- package/dist/pricing/quoteCodeRejectionDetails.js +22 -0
- package/dist/pricing/quotePorts.d.ts +30 -0
- package/dist/pricing/quotePorts.js +20 -0
- package/dist/pricing/quotePrimitives.d.ts +13 -0
- package/dist/pricing/quotePrimitives.js +18 -0
- package/dist/pricing/quotePromotionEvidence.d.ts +39 -0
- package/dist/pricing/quotePromotionEvidence.js +73 -0
- package/dist/pricing/quoteSchemas.d.ts +1176 -0
- package/dist/pricing/quoteSchemas.js +196 -0
- package/dist/pricing/subscriptionPricePolicy.d.ts +52 -0
- package/dist/pricing/subscriptionPricePolicy.js +96 -0
- package/dist/pricing/taxProfileGuard.d.ts +2 -0
- package/dist/pricing/taxProfileGuard.js +24 -0
- package/dist/pricing/validation.d.ts +2 -0
- package/dist/pricing/validation.js +3 -0
- package/dist/promotions/adminBindings.d.ts +72 -0
- package/dist/promotions/adminBindings.js +1 -0
- package/dist/promotions/adminPromotionCodesContracts.d.ts +498 -0
- package/dist/promotions/adminPromotionCodesContracts.js +211 -0
- package/dist/promotions/adminPromotionCodesDataPort.d.ts +47 -0
- package/dist/promotions/adminPromotionCodesDataPort.js +1 -0
- package/dist/promotions/adminPromotionCodesHandler.d.ts +14 -0
- package/dist/promotions/adminPromotionCodesHandler.js +194 -0
- package/dist/promotions/adminPromotionsContracts.d.ts +235 -0
- package/dist/promotions/adminPromotionsContracts.js +99 -0
- package/dist/promotions/adminPromotionsDataPort.d.ts +42 -0
- package/dist/promotions/adminPromotionsDataPort.js +1 -0
- package/dist/promotions/adminPromotionsHandler.d.ts +16 -0
- package/dist/promotions/adminPromotionsHandler.js +47 -0
- package/dist/promotions/contracts/index.d.ts +387 -0
- package/dist/promotions/contracts/index.js +1 -0
- package/dist/promotions/index.d.ts +22 -0
- package/dist/promotions/index.js +21 -0
- package/dist/promotions/promoDataPort.d.ts +47 -0
- package/dist/promotions/promoDataPort.js +14 -0
- package/dist/promotions/promotionAcceptanceTelemetry.d.ts +29 -0
- package/dist/promotions/promotionAcceptanceTelemetry.js +45 -0
- package/dist/promotions/promotionBenefitDefinition.d.ts +37 -0
- package/dist/promotions/promotionBenefitDefinition.js +33 -0
- package/dist/promotions/promotionClaimSweepPort.d.ts +14 -0
- package/dist/promotions/promotionClaimSweepPort.js +8 -0
- package/dist/promotions/promotionClaims.d.ts +5 -0
- package/dist/promotions/promotionClaims.js +57 -0
- package/dist/promotions/promotionCodePreview.d.ts +11 -0
- package/dist/promotions/promotionCodePreview.js +154 -0
- package/dist/promotions/promotionCodeQuotePort.d.ts +25 -0
- package/dist/promotions/promotionCodeQuotePort.js +1 -0
- package/dist/promotions/promotionQuoteAcceptance.d.ts +65 -0
- package/dist/promotions/promotionQuoteAcceptance.js +202 -0
- package/dist/promotions/promotionQuoteBinding.d.ts +71 -0
- package/dist/promotions/promotionQuoteBinding.js +85 -0
- package/dist/promotions/quoteAutomaticPromotions.d.ts +24 -0
- package/dist/promotions/quoteAutomaticPromotions.js +36 -0
- package/dist/promotions/quotePromoDiscounts.d.ts +32 -0
- package/dist/promotions/quotePromoDiscounts.js +140 -0
- package/dist/promotions/quotePromotionCodeAdjustments.d.ts +27 -0
- package/dist/promotions/quotePromotionCodeAdjustments.js +223 -0
- package/dist/promotions/quotePromotionV2.d.ts +29 -0
- package/dist/promotions/quotePromotionV2.js +174 -0
- package/dist/promotions/sql.d.ts +14 -0
- package/dist/promotions/sql.js +5 -0
- package/dist/promotions/validation.d.ts +2 -0
- package/dist/promotions/validation.js +3 -0
- package/dist/shipping/addressCanonLookupHandler.d.ts +7 -0
- package/dist/shipping/addressCanonLookupHandler.js +105 -0
- package/dist/shipping/addressCanonical.d.ts +2 -0
- package/dist/shipping/addressCanonical.js +26 -0
- package/dist/shipping/addressPorts.d.ts +39 -0
- package/dist/shipping/addressPorts.js +1 -0
- package/dist/shipping/addressSchemas.d.ts +30 -0
- package/dist/shipping/addressSchemas.js +1 -0
- package/dist/shipping/adminShippingRateHandler.d.ts +4 -0
- package/dist/shipping/adminShippingRateHandler.js +64 -0
- package/dist/shipping/contracts/address.d.ts +144 -0
- package/dist/shipping/contracts/address.js +1 -0
- package/dist/shipping/contracts/delivery.d.ts +110 -0
- package/dist/shipping/contracts/delivery.js +1 -0
- package/dist/shipping/contracts/index.d.ts +3 -0
- package/dist/shipping/contracts/index.js +1 -0
- package/dist/shipping/contracts/shippingRate.d.ts +11 -0
- package/dist/shipping/contracts/shippingRate.js +1 -0
- package/dist/shipping/customerAddressesHandler.d.ts +5 -0
- package/dist/shipping/customerAddressesHandler.js +80 -0
- package/dist/shipping/customerSelfServiceAddressModels.d.ts +27 -0
- package/dist/shipping/customerSelfServiceAddressModels.js +101 -0
- package/dist/shipping/deliverySelectionHandler.d.ts +10 -0
- package/dist/shipping/deliverySelectionHandler.js +77 -0
- package/dist/shipping/deliverySelectionPort.d.ts +8 -0
- package/dist/shipping/deliverySelectionPort.js +1 -0
- package/dist/shipping/deliverySelectionSummary.d.ts +2 -0
- package/dist/shipping/deliverySelectionSummary.js +53 -0
- package/dist/shipping/handlerSchemas.d.ts +21 -0
- package/dist/shipping/handlerSchemas.js +1 -0
- package/dist/shipping/index.d.ts +17 -0
- package/dist/shipping/index.js +8 -0
- package/dist/shipping/pickupPointSearchPort.d.ts +4 -0
- package/dist/shipping/pickupPointSearchPort.js +1 -0
- package/dist/shipping/readDeliverySelectionEvidence.d.ts +2 -0
- package/dist/shipping/readDeliverySelectionEvidence.js +44 -0
- package/dist/shipping/shippingRatePorts.d.ts +52 -0
- package/dist/shipping/shippingRatePorts.js +1 -0
- package/package.json +62 -2
- package/release-gates.json +218 -10
- package/sql/20260807140000_read_parity_catalog.sql +94 -0
- package/sql/20260810120000_storefront_catalog_attributes.sql +51 -0
- package/sql/20260812140000_sellable_bundle_catalog.sql +124 -0
- package/sql/20260812210000_bundle_operator_write_boundary.sql +567 -0
- package/sql/20260814000000_fulfillment_stock_evidence.sql +239 -0
- package/sql/20260815120000_automatic_subscription_renewal.sql +1083 -0
- package/sql/20260817180000_promotion_claim_lifecycle.sql +591 -0
- package/sql/20260817180100_promotion_claim_lifecycle_validation.sql +23 -0
- package/sql/20260827220000_catalog_document_revision_foundation.sql +226 -0
- package/sql/20260827220001_catalog_document_revision_foundation_validate.sql +10 -0
- package/sql/20260908100000_catalog_draft_foundation.sql +217 -0
- package/src/catalog/activeSkuSelection.ts +20 -0
- package/src/catalog/adminBindings.ts +59 -0
- package/src/catalog/adminBundleHandler.ts +165 -0
- package/src/catalog/adminBundleReadHandler.ts +261 -0
- package/src/catalog/adminBundleWritePort.ts +102 -0
- package/src/catalog/bundleAvailability.ts +129 -0
- package/src/catalog/bundleAvailabilityService.ts +171 -0
- package/src/catalog/bundleBindings.ts +29 -0
- package/src/catalog/bundleCatalogQueries.ts +98 -0
- package/src/catalog/bundleCatalogReadPort.ts +177 -0
- package/src/catalog/bundleCatalogStore.ts +269 -0
- package/src/catalog/bundleRuleCodes.ts +134 -0
- package/src/catalog/bundleRules.ts +153 -0
- package/src/catalog/catalogDraftStore.ts +71 -0
- package/src/catalog/catalogSkuEnvelope.ts +214 -0
- package/src/catalog/catalogSkuEnvelopeMapper.ts +160 -0
- package/src/catalog/contracts/bundleAdmin.ts +20 -0
- package/src/catalog/contracts/bundleAvailability.ts +5 -0
- package/src/catalog/contracts/bundleRead.ts +13 -0
- package/src/catalog/contracts/draft.ts +69 -0
- package/src/catalog/contracts/foundation.ts +104 -0
- package/src/catalog/contracts/index.ts +14 -0
- package/src/catalog/contracts/lifecycle.ts +74 -0
- package/src/catalog/contracts/productMediaContracts.ts +170 -0
- package/src/catalog/contracts/productTypes.ts +35 -0
- package/src/catalog/contracts/resolveProductMedia.ts +85 -0
- package/src/catalog/contracts/sellable.ts +12 -0
- package/src/catalog/draftHandler.ts +105 -0
- package/src/catalog/draftPorts.ts +18 -0
- package/src/catalog/draftSchemas.ts +88 -0
- package/src/catalog/draftValidation.ts +100 -0
- package/src/catalog/foundationSchemas.ts +123 -0
- package/src/catalog/index.ts +43 -0
- package/src/catalog/mediaSourcePack.ts +219 -0
- package/src/catalog/productTypeSchemas.ts +70 -0
- package/src/catalog/publicationPorts.ts +44 -0
- package/src/catalog/readPorts.ts +61 -0
- package/src/catalog/referenceCatalogHandler.ts +39 -0
- package/src/catalog/resolvedSkuProjection.ts +76 -0
- package/src/catalog/sellableBundleHandler.ts +133 -0
- package/src/catalog/sellableSchemas.ts +33 -0
- package/src/catalog/sql.ts +14 -0
- package/src/catalog/validation.ts +3 -0
- package/src/communications/contracts/index.ts +1 -0
- package/src/communications/contracts/marketingSync.ts +109 -0
- package/src/communications/index.ts +1 -0
- package/src/communications/klaviyo/consentPush.ts +19 -0
- package/src/communications/klaviyo/index.ts +649 -0
- package/src/communications/resend/index.ts +350 -0
- package/src/composition/catalogContribution.ts +32 -0
- package/src/composition/index.ts +1 -0
- package/src/inventory/adminInventory.ts +251 -0
- package/src/inventory/contracts/forecast.ts +96 -0
- package/src/inventory/contracts/index.ts +18 -0
- package/src/inventory/contracts/inventory.ts +14 -0
- package/src/inventory/contracts/newPurchaseStock.ts +23 -0
- package/src/inventory/contracts/reservation.ts +20 -0
- package/src/inventory/forecast.ts +239 -0
- package/src/inventory/index.ts +21 -0
- package/src/inventory/inventoryHandlers.ts +215 -0
- package/src/inventory/newPurchaseStockAvailability.ts +120 -0
- package/src/inventory/ports.ts +46 -0
- package/src/inventory/renewalReservation.ts +88 -0
- package/src/inventory/reservationPolicy.ts +27 -0
- package/src/inventory/schemas.ts +128 -0
- package/src/inventory/sql.ts +5 -0
- package/src/inventory/subscriptionForecastCalculation.ts +157 -0
- package/src/pricing/acquisitionFinancialPolicy.ts +26 -0
- package/src/pricing/acquisitionOfferPricing.ts +38 -0
- package/src/pricing/acquisitionOfferPricingHandler.ts +157 -0
- package/src/pricing/acquisitionOfferPricingSchemas.ts +35 -0
- package/src/pricing/acquisitionQuoteBinding.ts +12 -0
- package/src/pricing/acquisitionReadiness.ts +148 -0
- package/src/pricing/acquisitionTerms.ts +125 -0
- package/src/pricing/acquisitionTermsSchemas.ts +52 -0
- package/src/pricing/catalogFactsProvenance.ts +102 -0
- package/src/pricing/catalogFactsSchemas.ts +69 -0
- package/src/pricing/commerceOfferPricing.ts +224 -0
- package/src/pricing/commerceOfferPricingCache.ts +53 -0
- package/src/pricing/commerceOfferPricingHandler.ts +137 -0
- package/src/pricing/commercePriceAuthority.ts +104 -0
- package/src/pricing/commercePricingPolicyErrors.ts +88 -0
- package/src/pricing/commerceQuoteBatchHandler.ts +129 -0
- package/src/pricing/commerceQuoteCatalogReadPort.ts +42 -0
- package/src/pricing/commerceQuoteHandler.ts +184 -0
- package/src/pricing/commerceQuoteSnapshotExpect.ts +48 -0
- package/src/pricing/contracts/acquisitionTerms.ts +26 -0
- package/src/pricing/contracts/authority.ts +44 -0
- package/src/pricing/contracts/evidence.ts +25 -0
- package/src/pricing/contracts/index.ts +17 -0
- package/src/pricing/contracts/money.ts +32 -0
- package/src/pricing/contracts/offer.ts +42 -0
- package/src/pricing/contracts/offerPolicy.ts +17 -0
- package/src/pricing/contracts/policy.ts +26 -0
- package/src/pricing/contracts/provenance.ts +19 -0
- package/src/pricing/contracts/quote.ts +106 -0
- package/src/pricing/dbBackedCommerceQuoteHelpers.ts +256 -0
- package/src/pricing/dbBackedCommerceQuotePort.ts +260 -0
- package/src/pricing/dbBackedCommerceQuoteSnapshotVerifier.ts +49 -0
- package/src/pricing/index.ts +52 -0
- package/src/pricing/offerPolicyReadiness.ts +110 -0
- package/src/pricing/offerPolicySchemas.ts +23 -0
- package/src/pricing/offerPricingSchemas.ts +92 -0
- package/src/pricing/ports.ts +17 -0
- package/src/pricing/pricingPolicyToken.ts +119 -0
- package/src/pricing/quoteAcquisitionTerms.ts +10 -0
- package/src/pricing/quoteCodeRejectionDetails.ts +23 -0
- package/src/pricing/quotePorts.ts +58 -0
- package/src/pricing/quotePrimitives.ts +21 -0
- package/src/pricing/quotePromotionEvidence.ts +82 -0
- package/src/pricing/quoteSchemas.ts +230 -0
- package/src/pricing/subscriptionPricePolicy.ts +113 -0
- package/src/pricing/taxProfileGuard.ts +33 -0
- package/src/pricing/validation.ts +3 -0
- package/src/promotions/adminBindings.ts +53 -0
- package/src/promotions/adminPromotionCodesContracts.ts +244 -0
- package/src/promotions/adminPromotionCodesDataPort.ts +56 -0
- package/src/promotions/adminPromotionCodesHandler.ts +254 -0
- package/src/promotions/adminPromotionsContracts.ts +121 -0
- package/src/promotions/adminPromotionsDataPort.ts +48 -0
- package/src/promotions/adminPromotionsHandler.ts +72 -0
- package/src/promotions/contracts/index.ts +387 -0
- package/src/promotions/index.ts +22 -0
- package/src/promotions/promoDataPort.ts +65 -0
- package/src/promotions/promotionAcceptanceTelemetry.ts +73 -0
- package/src/promotions/promotionBenefitDefinition.ts +48 -0
- package/src/promotions/promotionClaimSweepPort.ts +23 -0
- package/src/promotions/promotionClaims.ts +70 -0
- package/src/promotions/promotionCodePreview.ts +183 -0
- package/src/promotions/promotionCodeQuotePort.ts +35 -0
- package/src/promotions/promotionQuoteAcceptance.ts +284 -0
- package/src/promotions/promotionQuoteBinding.ts +158 -0
- package/src/promotions/quoteAutomaticPromotions.ts +58 -0
- package/src/promotions/quotePromoDiscounts.ts +185 -0
- package/src/promotions/quotePromotionCodeAdjustments.ts +277 -0
- package/src/promotions/quotePromotionV2.ts +223 -0
- package/src/promotions/sql.ts +5 -0
- package/src/promotions/validation.ts +3 -0
- package/src/shipping/addressCanonLookupHandler.ts +114 -0
- package/src/shipping/addressCanonical.ts +31 -0
- package/src/shipping/addressPorts.ts +48 -0
- package/src/shipping/addressSchemas.ts +28 -0
- package/src/shipping/adminShippingRateHandler.ts +72 -0
- package/src/shipping/contracts/address.ts +71 -0
- package/src/shipping/contracts/delivery.ts +114 -0
- package/src/shipping/contracts/index.ts +18 -0
- package/src/shipping/contracts/shippingRate.ts +7 -0
- package/src/shipping/customerAddressesHandler.ts +100 -0
- package/src/shipping/customerSelfServiceAddressModels.ts +124 -0
- package/src/shipping/deliverySelectionHandler.ts +94 -0
- package/src/shipping/deliverySelectionPort.ts +10 -0
- package/src/shipping/deliverySelectionSummary.ts +56 -0
- package/src/shipping/handlerSchemas.ts +19 -0
- package/src/shipping/index.ts +23 -0
- package/src/shipping/pickupPointSearchPort.ts +8 -0
- package/src/shipping/readDeliverySelectionEvidence.ts +51 -0
- package/src/shipping/shippingRatePorts.ts +36 -0
|
@@ -0,0 +1,567 @@
|
|
|
1
|
+
-- Public platform bundle write boundary: the routines that are the only way a bundle
|
|
2
|
+
-- changes, and the ledger that makes a repeated attempt safe.
|
|
3
|
+
--
|
|
4
|
+
-- WHAT THIS FORWARD SHIPS. One relation and seven routines. The earlier forward
|
|
5
|
+
-- (20260812140000) created three relations and left them inert: a bundle could be
|
|
6
|
+
-- described but not written. This one supplies the writer. Every operation an operator
|
|
7
|
+
-- can perform on a bundle -- create it, edit it, clone it, replace its bill of materials,
|
|
8
|
+
-- price it, remove it from sale, put it back, publish it, unpublish it -- goes through
|
|
9
|
+
-- exactly one of these routines and through no other path.
|
|
10
|
+
--
|
|
11
|
+
-- WHY THE LEDGER EXISTS AND WHAT IT IS NOT. `catalog_bundle_write_events` records what
|
|
12
|
+
-- was done, by which principal, and under which idempotency key. It is not an audit
|
|
13
|
+
-- system: it makes no claim about authorisation and enforces no retention. It exists
|
|
14
|
+
-- because a write that can be retried must be able to recognise its own replay, and
|
|
15
|
+
-- because an operator asking "who last repriced this" has no other place to look on a
|
|
16
|
+
-- self-hosted node. The unique index is partial, so an unkeyed write is always allowed
|
|
17
|
+
-- and a keyed one can happen exactly once.
|
|
18
|
+
--
|
|
19
|
+
-- WHY REPLAY SHORT-CIRCUITS INSTEAD OF FAILING. A caller that lost its answer to a
|
|
20
|
+
-- network fault must be able to ask again and learn the outcome, not be told it broke a
|
|
21
|
+
-- rule. Each routine therefore returns the same shape it would have returned, with
|
|
22
|
+
-- `idempotent` set, and writes nothing. A conflicting write under a reused key is not a
|
|
23
|
+
-- case this rewards: the key names the attempt, not the payload.
|
|
24
|
+
--
|
|
25
|
+
-- WHY A DRY RUN VALIDATES AND THEN RETURNS. `p_mode = 'dry_run'` runs every precondition
|
|
26
|
+
-- and stops before the first statement that changes a row. An operator, or an agent
|
|
27
|
+
-- acting for one, can therefore learn whether a composition or a price would be accepted
|
|
28
|
+
-- without leaving a trace of having asked. Validation and commitment share one code path,
|
|
29
|
+
-- so the answer cannot drift from the act.
|
|
30
|
+
--
|
|
31
|
+
-- WHY THE PRICE MAY NOT EXCEED THE SUM OF THE PARTS. A bundle sold above its components
|
|
32
|
+
-- is a surcharge wearing a discount's name. The comparison is one line here and one line
|
|
33
|
+
-- in the pricing engine that allocates the accepted target back onto the components; both
|
|
34
|
+
-- refuse the same input for the same reason, and relaxing the rule stays a change in two
|
|
35
|
+
-- known places rather than a search.
|
|
36
|
+
--
|
|
37
|
+
-- WHY COMPOSITION IS REPLACED WHOLE. Every rule the bill of materials must satisfy is a
|
|
38
|
+
-- property of the set -- it must not be empty, it must not name a unit twice, and it must
|
|
39
|
+
-- contain something that is not an optional extra. An entry-level patch would let the set
|
|
40
|
+
-- pass through states no request described, so the routine deletes and re-inserts inside
|
|
41
|
+
-- its own transaction and checks the set it was handed, once.
|
|
42
|
+
--
|
|
43
|
+
-- WHAT THIS CHAIN CANNOT SAY, STATED PLAINLY. The managed deployment distinguishes a
|
|
44
|
+
-- human operator from a machine actor through a registry of administrators, and refuses
|
|
45
|
+
-- publication to the latter inside the database. This chain has no such registry: it has
|
|
46
|
+
-- one authenticated principal shape and no notion of an operator at all. The two
|
|
47
|
+
-- publish-state routines therefore take the principal, record it and enforce the state
|
|
48
|
+
-- preconditions, but they cannot re-derive what kind of principal it is, so the human-only
|
|
49
|
+
-- rule is enforced by the caller alone here. That is a real difference in defence depth
|
|
50
|
+
-- and it is named rather than papered over; closing it needs an operator-identity slice,
|
|
51
|
+
-- which is a decision about identity and not about bundles.
|
|
52
|
+
--
|
|
53
|
+
-- NO GRANTS AND NO ROW LEVEL SECURITY, DELIBERATELY. These are operator-owned objects. A
|
|
54
|
+
-- routine created here carries EXECUTE for its owner and for PUBLIC by default, so the
|
|
55
|
+
-- forward revokes PUBLIC explicitly and grants nothing back: only the owning role, which
|
|
56
|
+
-- is the adopter's own server identity, can call them. The actor bootstrap gives `anon`
|
|
57
|
+
-- and `authenticated` schema usage and one routine, never these, so both browser
|
|
58
|
+
-- principals are already denied without a policy to say so.
|
|
59
|
+
|
|
60
|
+
CREATE TABLE public.catalog_bundle_write_events (
|
|
61
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
62
|
+
bundle_code text NOT NULL,
|
|
63
|
+
action text NOT NULL,
|
|
64
|
+
principal_id uuid,
|
|
65
|
+
idempotency_key text,
|
|
66
|
+
before_state jsonb,
|
|
67
|
+
after_state jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
68
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
69
|
+
CONSTRAINT catalog_bundle_write_events_action_check CHECK (action IN (
|
|
70
|
+
'bundle_draft_upsert', 'bundle_composition_set', 'bundle_target_price_set',
|
|
71
|
+
'bundle_archive', 'bundle_restore', 'bundle_activate', 'bundle_deactivate'
|
|
72
|
+
)),
|
|
73
|
+
CONSTRAINT catalog_bundle_write_events_key_check
|
|
74
|
+
CHECK (idempotency_key IS NULL OR btrim(idempotency_key) <> '')
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
-- Partial and unique: an unkeyed write is always allowed, a keyed one lands once.
|
|
78
|
+
CREATE UNIQUE INDEX uq_catalog_bundle_write_events_key
|
|
79
|
+
ON public.catalog_bundle_write_events (idempotency_key)
|
|
80
|
+
WHERE idempotency_key IS NOT NULL;
|
|
81
|
+
|
|
82
|
+
CREATE INDEX idx_catalog_bundle_write_events_bundle_code
|
|
83
|
+
ON public.catalog_bundle_write_events (bundle_code, created_at DESC);
|
|
84
|
+
|
|
85
|
+
CREATE FUNCTION public.admin_upsert_bundle_draft(
|
|
86
|
+
p_actor_id uuid,
|
|
87
|
+
p_code text,
|
|
88
|
+
p_title text DEFAULT NULL,
|
|
89
|
+
p_fulfillment_mode text DEFAULT NULL,
|
|
90
|
+
p_composition_constraint jsonb DEFAULT NULL,
|
|
91
|
+
p_metadata jsonb DEFAULT NULL,
|
|
92
|
+
p_clone_from_code text DEFAULT NULL,
|
|
93
|
+
p_mode text DEFAULT 'commit',
|
|
94
|
+
p_idempotency_key text DEFAULT NULL,
|
|
95
|
+
p_request_id text DEFAULT NULL,
|
|
96
|
+
p_source text DEFAULT 'operator'
|
|
97
|
+
)
|
|
98
|
+
RETURNS jsonb
|
|
99
|
+
LANGUAGE plpgsql
|
|
100
|
+
SET search_path = pg_catalog, public
|
|
101
|
+
AS $$
|
|
102
|
+
DECLARE
|
|
103
|
+
v_bundle_id uuid;
|
|
104
|
+
v_source public.catalog_bundles%ROWTYPE;
|
|
105
|
+
BEGIN
|
|
106
|
+
IF p_actor_id IS NULL THEN RAISE EXCEPTION 'actor_required' USING ERRCODE = '42501'; END IF;
|
|
107
|
+
|
|
108
|
+
IF p_idempotency_key IS NOT NULL AND EXISTS (
|
|
109
|
+
SELECT 1 FROM public.catalog_bundle_write_events WHERE idempotency_key = p_idempotency_key
|
|
110
|
+
) THEN
|
|
111
|
+
RETURN jsonb_build_object('code', p_code, 'idempotent', true, 'dryRun', false);
|
|
112
|
+
END IF;
|
|
113
|
+
|
|
114
|
+
SELECT id INTO v_bundle_id FROM public.catalog_bundles WHERE code = p_code;
|
|
115
|
+
|
|
116
|
+
IF p_clone_from_code IS NOT NULL THEN
|
|
117
|
+
IF v_bundle_id IS NOT NULL THEN RAISE EXCEPTION 'code_taken' USING ERRCODE = 'P0001'; END IF;
|
|
118
|
+
SELECT * INTO v_source FROM public.catalog_bundles WHERE code = p_clone_from_code;
|
|
119
|
+
IF NOT FOUND THEN RAISE EXCEPTION 'clone_source_not_found' USING ERRCODE = 'P0002'; END IF;
|
|
120
|
+
ELSIF v_bundle_id IS NULL AND (p_title IS NULL OR btrim(p_title) = '') THEN
|
|
121
|
+
RAISE EXCEPTION 'bundle_title_required' USING ERRCODE = 'P0001';
|
|
122
|
+
END IF;
|
|
123
|
+
|
|
124
|
+
IF p_mode = 'dry_run' THEN
|
|
125
|
+
RETURN jsonb_build_object('code', p_code, 'idempotent', false, 'dryRun', true);
|
|
126
|
+
END IF;
|
|
127
|
+
|
|
128
|
+
IF p_clone_from_code IS NOT NULL THEN
|
|
129
|
+
INSERT INTO public.catalog_bundles
|
|
130
|
+
(code, title, status, fulfillment_mode, composition_constraint, metadata)
|
|
131
|
+
VALUES (p_code, COALESCE(p_title, v_source.title), 'draft', v_source.fulfillment_mode,
|
|
132
|
+
v_source.composition_constraint, v_source.metadata)
|
|
133
|
+
RETURNING id INTO v_bundle_id;
|
|
134
|
+
|
|
135
|
+
INSERT INTO public.catalog_bundle_components
|
|
136
|
+
(bundle_id, catalog_sku_id, quantity, is_addon, sort_order)
|
|
137
|
+
SELECT v_bundle_id, component.catalog_sku_id, component.quantity,
|
|
138
|
+
component.is_addon, component.sort_order
|
|
139
|
+
FROM public.catalog_bundle_components AS component
|
|
140
|
+
WHERE component.bundle_id = v_source.id;
|
|
141
|
+
ELSE
|
|
142
|
+
INSERT INTO public.catalog_bundles
|
|
143
|
+
(code, title, status, fulfillment_mode, composition_constraint, metadata)
|
|
144
|
+
VALUES (p_code, COALESCE(p_title, p_code), 'draft', COALESCE(p_fulfillment_mode, 'virtual'),
|
|
145
|
+
COALESCE(p_composition_constraint, '{}'::jsonb), COALESCE(p_metadata, '{}'::jsonb))
|
|
146
|
+
ON CONFLICT (code) DO UPDATE
|
|
147
|
+
SET title = COALESCE(p_title, public.catalog_bundles.title),
|
|
148
|
+
fulfillment_mode = COALESCE(p_fulfillment_mode, public.catalog_bundles.fulfillment_mode),
|
|
149
|
+
composition_constraint =
|
|
150
|
+
COALESCE(p_composition_constraint, public.catalog_bundles.composition_constraint),
|
|
151
|
+
metadata = COALESCE(p_metadata, public.catalog_bundles.metadata),
|
|
152
|
+
updated_at = now()
|
|
153
|
+
RETURNING id INTO v_bundle_id;
|
|
154
|
+
END IF;
|
|
155
|
+
|
|
156
|
+
INSERT INTO public.catalog_bundle_write_events
|
|
157
|
+
(bundle_code, action, principal_id, idempotency_key, after_state)
|
|
158
|
+
VALUES (p_code, 'bundle_draft_upsert', p_actor_id, p_idempotency_key,
|
|
159
|
+
jsonb_build_object('title', p_title, 'clonedFrom', p_clone_from_code,
|
|
160
|
+
'requestId', p_request_id, 'source', p_source));
|
|
161
|
+
|
|
162
|
+
RETURN jsonb_build_object('code', p_code, 'bundleId', v_bundle_id::text,
|
|
163
|
+
'idempotent', false, 'dryRun', false);
|
|
164
|
+
END;
|
|
165
|
+
$$;
|
|
166
|
+
|
|
167
|
+
CREATE FUNCTION public.admin_set_bundle_composition(
|
|
168
|
+
p_actor_id uuid,
|
|
169
|
+
p_code text,
|
|
170
|
+
p_components jsonb,
|
|
171
|
+
p_mode text DEFAULT 'commit',
|
|
172
|
+
p_idempotency_key text DEFAULT NULL,
|
|
173
|
+
p_request_id text DEFAULT NULL,
|
|
174
|
+
p_source text DEFAULT 'operator'
|
|
175
|
+
)
|
|
176
|
+
RETURNS jsonb
|
|
177
|
+
LANGUAGE plpgsql
|
|
178
|
+
SET search_path = pg_catalog, public
|
|
179
|
+
AS $$
|
|
180
|
+
DECLARE
|
|
181
|
+
v_bundle_id uuid;
|
|
182
|
+
v_count integer;
|
|
183
|
+
v_distinct integer;
|
|
184
|
+
v_core integer;
|
|
185
|
+
v_resolved integer;
|
|
186
|
+
v_unsellable integer;
|
|
187
|
+
v_currency text;
|
|
188
|
+
v_mismatched integer;
|
|
189
|
+
BEGIN
|
|
190
|
+
IF p_actor_id IS NULL THEN RAISE EXCEPTION 'actor_required' USING ERRCODE = '42501'; END IF;
|
|
191
|
+
|
|
192
|
+
IF p_idempotency_key IS NOT NULL AND EXISTS (
|
|
193
|
+
SELECT 1 FROM public.catalog_bundle_write_events WHERE idempotency_key = p_idempotency_key
|
|
194
|
+
) THEN
|
|
195
|
+
RETURN jsonb_build_object('code', p_code, 'idempotent', true, 'dryRun', false);
|
|
196
|
+
END IF;
|
|
197
|
+
|
|
198
|
+
SELECT id INTO v_bundle_id FROM public.catalog_bundles WHERE code = p_code;
|
|
199
|
+
IF NOT FOUND THEN RAISE EXCEPTION 'bundle_not_found' USING ERRCODE = 'P0002'; END IF;
|
|
200
|
+
|
|
201
|
+
-- The supplied set is read out of the argument each time it is needed. A temp
|
|
202
|
+
-- table is the obvious shortcut and the wrong one: ON COMMIT DROP lives until the
|
|
203
|
+
-- transaction ends, so a second composition write in the same transaction, or a
|
|
204
|
+
-- retry after a caught exception, collides with what the first call left behind.
|
|
205
|
+
SELECT count(*),
|
|
206
|
+
count(DISTINCT entry.sku),
|
|
207
|
+
count(*) FILTER (WHERE NOT COALESCE(entry.is_addon, false)),
|
|
208
|
+
count(unit.id),
|
|
209
|
+
count(*) FILTER (
|
|
210
|
+
WHERE unit.id IS NOT NULL
|
|
211
|
+
AND (unit.status <> 'active' OR unit.sellable_standalone IS NOT TRUE))
|
|
212
|
+
INTO v_count, v_distinct, v_core, v_resolved, v_unsellable
|
|
213
|
+
FROM jsonb_to_recordset(COALESCE(p_components, '[]'::jsonb))
|
|
214
|
+
AS entry(sku text, quantity integer, is_addon boolean, sort_order integer)
|
|
215
|
+
LEFT JOIN public.catalog_skus AS unit ON unit.sku = entry.sku;
|
|
216
|
+
|
|
217
|
+
IF v_count = 0 THEN RAISE EXCEPTION 'min_components' USING ERRCODE = 'P0001'; END IF;
|
|
218
|
+
IF v_distinct <> v_count THEN RAISE EXCEPTION 'duplicate_component' USING ERRCODE = 'P0001'; END IF;
|
|
219
|
+
IF v_core = 0 THEN RAISE EXCEPTION 'addon_only_composition' USING ERRCODE = 'P0001'; END IF;
|
|
220
|
+
IF v_resolved <> v_count THEN RAISE EXCEPTION 'component_not_found' USING ERRCODE = 'P0002'; END IF;
|
|
221
|
+
IF v_unsellable > 0 THEN RAISE EXCEPTION 'component_not_sellable' USING ERRCODE = 'P0001'; END IF;
|
|
222
|
+
|
|
223
|
+
SELECT list.currency INTO v_currency
|
|
224
|
+
FROM public.catalog_bundle_prices AS price
|
|
225
|
+
JOIN public.price_lists AS list ON list.id = price.price_list_id
|
|
226
|
+
WHERE price.bundle_id = v_bundle_id AND price.active
|
|
227
|
+
ORDER BY price.valid_from DESC
|
|
228
|
+
LIMIT 1;
|
|
229
|
+
|
|
230
|
+
IF v_currency IS NOT NULL THEN
|
|
231
|
+
SELECT count(*) INTO v_mismatched
|
|
232
|
+
FROM jsonb_to_recordset(COALESCE(p_components, '[]'::jsonb))
|
|
233
|
+
AS entry(sku text, quantity integer, is_addon boolean, sort_order integer)
|
|
234
|
+
JOIN public.catalog_skus AS unit ON unit.sku = entry.sku
|
|
235
|
+
WHERE NOT EXISTS (
|
|
236
|
+
SELECT 1
|
|
237
|
+
FROM public.price_entries AS unit_price
|
|
238
|
+
JOIN public.price_lists AS unit_list ON unit_list.id = unit_price.price_list_id
|
|
239
|
+
WHERE unit_price.variant_id = unit.id
|
|
240
|
+
AND unit_price.active
|
|
241
|
+
AND unit_list.currency = v_currency);
|
|
242
|
+
IF v_mismatched > 0 THEN
|
|
243
|
+
RAISE EXCEPTION 'component_currency_mismatch' USING ERRCODE = 'P0001';
|
|
244
|
+
END IF;
|
|
245
|
+
END IF;
|
|
246
|
+
|
|
247
|
+
IF p_mode = 'dry_run' THEN
|
|
248
|
+
RETURN jsonb_build_object('code', p_code, 'componentCount', v_count,
|
|
249
|
+
'idempotent', false, 'dryRun', true);
|
|
250
|
+
END IF;
|
|
251
|
+
|
|
252
|
+
DELETE FROM public.catalog_bundle_components WHERE bundle_id = v_bundle_id;
|
|
253
|
+
INSERT INTO public.catalog_bundle_components
|
|
254
|
+
(bundle_id, catalog_sku_id, quantity, is_addon, sort_order)
|
|
255
|
+
SELECT v_bundle_id, unit.id, entry.quantity,
|
|
256
|
+
COALESCE(entry.is_addon, false), COALESCE(entry.sort_order, 0)
|
|
257
|
+
FROM jsonb_to_recordset(COALESCE(p_components, '[]'::jsonb))
|
|
258
|
+
AS entry(sku text, quantity integer, is_addon boolean, sort_order integer)
|
|
259
|
+
JOIN public.catalog_skus AS unit ON unit.sku = entry.sku;
|
|
260
|
+
|
|
261
|
+
UPDATE public.catalog_bundles SET updated_at = now() WHERE id = v_bundle_id;
|
|
262
|
+
|
|
263
|
+
INSERT INTO public.catalog_bundle_write_events
|
|
264
|
+
(bundle_code, action, principal_id, idempotency_key, after_state)
|
|
265
|
+
VALUES (p_code, 'bundle_composition_set', p_actor_id, p_idempotency_key,
|
|
266
|
+
jsonb_build_object('componentCount', v_count, 'requestId', p_request_id,
|
|
267
|
+
'source', p_source));
|
|
268
|
+
|
|
269
|
+
RETURN jsonb_build_object('code', p_code, 'componentCount', v_count,
|
|
270
|
+
'idempotent', false, 'dryRun', false);
|
|
271
|
+
END;
|
|
272
|
+
$$;
|
|
273
|
+
|
|
274
|
+
CREATE FUNCTION public.admin_set_bundle_target_price(
|
|
275
|
+
p_actor_id uuid,
|
|
276
|
+
p_code text,
|
|
277
|
+
p_price_mode text,
|
|
278
|
+
p_target_price_minor integer,
|
|
279
|
+
p_currency text,
|
|
280
|
+
p_amount_kind text DEFAULT 'gross',
|
|
281
|
+
p_mode text DEFAULT 'commit',
|
|
282
|
+
p_idempotency_key text DEFAULT NULL,
|
|
283
|
+
p_request_id text DEFAULT NULL,
|
|
284
|
+
p_source text DEFAULT 'operator'
|
|
285
|
+
)
|
|
286
|
+
RETURNS jsonb
|
|
287
|
+
LANGUAGE plpgsql
|
|
288
|
+
SET search_path = pg_catalog, public
|
|
289
|
+
AS $$
|
|
290
|
+
DECLARE
|
|
291
|
+
v_bundle_id uuid;
|
|
292
|
+
v_list_id uuid;
|
|
293
|
+
v_reference_total bigint;
|
|
294
|
+
v_unit_total bigint;
|
|
295
|
+
v_now timestamptz := clock_timestamp();
|
|
296
|
+
BEGIN
|
|
297
|
+
IF p_actor_id IS NULL THEN RAISE EXCEPTION 'actor_required' USING ERRCODE = '42501'; END IF;
|
|
298
|
+
|
|
299
|
+
IF p_idempotency_key IS NOT NULL AND EXISTS (
|
|
300
|
+
SELECT 1 FROM public.catalog_bundle_write_events WHERE idempotency_key = p_idempotency_key
|
|
301
|
+
) THEN
|
|
302
|
+
RETURN jsonb_build_object('code', p_code, 'idempotent', true, 'dryRun', false);
|
|
303
|
+
END IF;
|
|
304
|
+
|
|
305
|
+
SELECT id INTO v_bundle_id FROM public.catalog_bundles WHERE code = p_code;
|
|
306
|
+
IF NOT FOUND THEN RAISE EXCEPTION 'bundle_not_found' USING ERRCODE = 'P0002'; END IF;
|
|
307
|
+
|
|
308
|
+
SELECT id INTO v_list_id
|
|
309
|
+
FROM public.price_lists
|
|
310
|
+
WHERE currency = p_currency AND status = 'active'
|
|
311
|
+
ORDER BY created_at DESC
|
|
312
|
+
LIMIT 1;
|
|
313
|
+
IF v_list_id IS NULL THEN RAISE EXCEPTION 'no_active_price_list' USING ERRCODE = 'P0002'; END IF;
|
|
314
|
+
|
|
315
|
+
SELECT COALESCE(sum(component.quantity::bigint * unit_price.unit_price_minor), 0),
|
|
316
|
+
COALESCE(sum(component.quantity::bigint), 0)
|
|
317
|
+
INTO v_reference_total, v_unit_total
|
|
318
|
+
FROM public.catalog_bundle_components AS component
|
|
319
|
+
JOIN public.price_entries AS unit_price
|
|
320
|
+
ON unit_price.variant_id = component.catalog_sku_id
|
|
321
|
+
AND unit_price.price_list_id = v_list_id
|
|
322
|
+
AND unit_price.active
|
|
323
|
+
WHERE component.bundle_id = v_bundle_id;
|
|
324
|
+
|
|
325
|
+
IF v_reference_total = 0 THEN RAISE EXCEPTION 'min_components' USING ERRCODE = 'P0001'; END IF;
|
|
326
|
+
IF p_target_price_minor > v_reference_total THEN
|
|
327
|
+
RAISE EXCEPTION 'target_above_component_sum' USING ERRCODE = 'P0001';
|
|
328
|
+
END IF;
|
|
329
|
+
IF p_target_price_minor < v_unit_total THEN
|
|
330
|
+
RAISE EXCEPTION 'target_below_floor' USING ERRCODE = 'P0001';
|
|
331
|
+
END IF;
|
|
332
|
+
|
|
333
|
+
IF p_mode = 'dry_run' THEN
|
|
334
|
+
RETURN jsonb_build_object('code', p_code, 'idempotent', false, 'dryRun', true);
|
|
335
|
+
END IF;
|
|
336
|
+
|
|
337
|
+
UPDATE public.catalog_bundle_prices
|
|
338
|
+
SET active = false, valid_to = v_now
|
|
339
|
+
WHERE bundle_id = v_bundle_id AND price_list_id = v_list_id AND mode = p_price_mode
|
|
340
|
+
AND active AND valid_from < v_now;
|
|
341
|
+
|
|
342
|
+
INSERT INTO public.catalog_bundle_prices
|
|
343
|
+
(bundle_id, price_list_id, mode, target_price_minor, amount_kind, active, valid_from)
|
|
344
|
+
VALUES (v_bundle_id, v_list_id, p_price_mode, p_target_price_minor,
|
|
345
|
+
COALESCE(p_amount_kind, 'gross'), true, v_now);
|
|
346
|
+
|
|
347
|
+
INSERT INTO public.catalog_bundle_write_events
|
|
348
|
+
(bundle_code, action, principal_id, idempotency_key, after_state)
|
|
349
|
+
VALUES (p_code, 'bundle_target_price_set', p_actor_id, p_idempotency_key,
|
|
350
|
+
jsonb_build_object('mode', p_price_mode, 'targetPriceMinor', p_target_price_minor,
|
|
351
|
+
'currency', p_currency, 'referenceTotalMinor', v_reference_total,
|
|
352
|
+
'requestId', p_request_id, 'source', p_source));
|
|
353
|
+
|
|
354
|
+
RETURN jsonb_build_object('code', p_code, 'idempotent', false, 'dryRun', false);
|
|
355
|
+
END;
|
|
356
|
+
$$;
|
|
357
|
+
|
|
358
|
+
CREATE FUNCTION public.admin_archive_bundle(
|
|
359
|
+
p_actor_id uuid,
|
|
360
|
+
p_code text,
|
|
361
|
+
p_mode text DEFAULT 'commit',
|
|
362
|
+
p_idempotency_key text DEFAULT NULL,
|
|
363
|
+
p_request_id text DEFAULT NULL,
|
|
364
|
+
p_source text DEFAULT 'operator'
|
|
365
|
+
)
|
|
366
|
+
RETURNS jsonb
|
|
367
|
+
LANGUAGE plpgsql
|
|
368
|
+
SET search_path = pg_catalog, public
|
|
369
|
+
AS $$
|
|
370
|
+
DECLARE
|
|
371
|
+
v_status text;
|
|
372
|
+
BEGIN
|
|
373
|
+
IF p_actor_id IS NULL THEN RAISE EXCEPTION 'actor_required' USING ERRCODE = '42501'; END IF;
|
|
374
|
+
|
|
375
|
+
IF p_idempotency_key IS NOT NULL AND EXISTS (
|
|
376
|
+
SELECT 1 FROM public.catalog_bundle_write_events WHERE idempotency_key = p_idempotency_key
|
|
377
|
+
) THEN
|
|
378
|
+
RETURN jsonb_build_object('code', p_code, 'idempotent', true, 'dryRun', false);
|
|
379
|
+
END IF;
|
|
380
|
+
|
|
381
|
+
SELECT status INTO v_status FROM public.catalog_bundles WHERE code = p_code;
|
|
382
|
+
IF NOT FOUND THEN RAISE EXCEPTION 'bundle_not_found' USING ERRCODE = 'P0002'; END IF;
|
|
383
|
+
|
|
384
|
+
IF p_mode = 'dry_run' THEN
|
|
385
|
+
RETURN jsonb_build_object('code', p_code, 'idempotent', false, 'dryRun', true);
|
|
386
|
+
END IF;
|
|
387
|
+
|
|
388
|
+
UPDATE public.catalog_bundles SET status = 'archived', updated_at = now() WHERE code = p_code;
|
|
389
|
+
|
|
390
|
+
INSERT INTO public.catalog_bundle_write_events
|
|
391
|
+
(bundle_code, action, principal_id, idempotency_key, before_state, after_state)
|
|
392
|
+
VALUES (p_code, 'bundle_archive', p_actor_id, p_idempotency_key,
|
|
393
|
+
jsonb_build_object('status', v_status),
|
|
394
|
+
jsonb_build_object('status', 'archived', 'requestId', p_request_id, 'source', p_source));
|
|
395
|
+
|
|
396
|
+
RETURN jsonb_build_object('code', p_code, 'idempotent', false, 'dryRun', false);
|
|
397
|
+
END;
|
|
398
|
+
$$;
|
|
399
|
+
|
|
400
|
+
CREATE FUNCTION public.admin_restore_bundle(
|
|
401
|
+
p_actor_id uuid,
|
|
402
|
+
p_code text,
|
|
403
|
+
p_mode text DEFAULT 'commit',
|
|
404
|
+
p_idempotency_key text DEFAULT NULL,
|
|
405
|
+
p_request_id text DEFAULT NULL,
|
|
406
|
+
p_source text DEFAULT 'operator'
|
|
407
|
+
)
|
|
408
|
+
RETURNS jsonb
|
|
409
|
+
LANGUAGE plpgsql
|
|
410
|
+
SET search_path = pg_catalog, public
|
|
411
|
+
AS $$
|
|
412
|
+
DECLARE
|
|
413
|
+
v_status text;
|
|
414
|
+
BEGIN
|
|
415
|
+
IF p_actor_id IS NULL THEN RAISE EXCEPTION 'actor_required' USING ERRCODE = '42501'; END IF;
|
|
416
|
+
|
|
417
|
+
IF p_idempotency_key IS NOT NULL AND EXISTS (
|
|
418
|
+
SELECT 1 FROM public.catalog_bundle_write_events WHERE idempotency_key = p_idempotency_key
|
|
419
|
+
) THEN
|
|
420
|
+
RETURN jsonb_build_object('code', p_code, 'idempotent', true, 'dryRun', false);
|
|
421
|
+
END IF;
|
|
422
|
+
|
|
423
|
+
SELECT status INTO v_status FROM public.catalog_bundles WHERE code = p_code;
|
|
424
|
+
IF NOT FOUND THEN RAISE EXCEPTION 'bundle_not_found' USING ERRCODE = 'P0002'; END IF;
|
|
425
|
+
IF v_status <> 'archived' THEN
|
|
426
|
+
RAISE EXCEPTION 'restore_requires_archived' USING ERRCODE = 'P0001';
|
|
427
|
+
END IF;
|
|
428
|
+
|
|
429
|
+
IF p_mode = 'dry_run' THEN
|
|
430
|
+
RETURN jsonb_build_object('code', p_code, 'idempotent', false, 'dryRun', true);
|
|
431
|
+
END IF;
|
|
432
|
+
|
|
433
|
+
UPDATE public.catalog_bundles SET status = 'draft', updated_at = now() WHERE code = p_code;
|
|
434
|
+
|
|
435
|
+
INSERT INTO public.catalog_bundle_write_events
|
|
436
|
+
(bundle_code, action, principal_id, idempotency_key, before_state, after_state)
|
|
437
|
+
VALUES (p_code, 'bundle_restore', p_actor_id, p_idempotency_key,
|
|
438
|
+
jsonb_build_object('status', v_status),
|
|
439
|
+
jsonb_build_object('status', 'draft', 'requestId', p_request_id, 'source', p_source));
|
|
440
|
+
|
|
441
|
+
RETURN jsonb_build_object('code', p_code, 'idempotent', false, 'dryRun', false);
|
|
442
|
+
END;
|
|
443
|
+
$$;
|
|
444
|
+
|
|
445
|
+
CREATE FUNCTION public.admin_activate_bundle(
|
|
446
|
+
p_actor_id uuid,
|
|
447
|
+
p_code text,
|
|
448
|
+
p_mode text DEFAULT 'commit',
|
|
449
|
+
p_idempotency_key text DEFAULT NULL,
|
|
450
|
+
p_request_id text DEFAULT NULL,
|
|
451
|
+
p_source text DEFAULT 'operator'
|
|
452
|
+
)
|
|
453
|
+
RETURNS jsonb
|
|
454
|
+
LANGUAGE plpgsql
|
|
455
|
+
SET search_path = pg_catalog, public
|
|
456
|
+
AS $$
|
|
457
|
+
DECLARE
|
|
458
|
+
v_bundle public.catalog_bundles%ROWTYPE;
|
|
459
|
+
v_components integer;
|
|
460
|
+
v_prices integer;
|
|
461
|
+
BEGIN
|
|
462
|
+
IF p_actor_id IS NULL THEN RAISE EXCEPTION 'actor_required' USING ERRCODE = '42501'; END IF;
|
|
463
|
+
|
|
464
|
+
IF p_idempotency_key IS NOT NULL AND EXISTS (
|
|
465
|
+
SELECT 1 FROM public.catalog_bundle_write_events WHERE idempotency_key = p_idempotency_key
|
|
466
|
+
) THEN
|
|
467
|
+
RETURN jsonb_build_object('code', p_code, 'idempotent', true, 'dryRun', false);
|
|
468
|
+
END IF;
|
|
469
|
+
|
|
470
|
+
SELECT * INTO v_bundle FROM public.catalog_bundles WHERE code = p_code;
|
|
471
|
+
IF NOT FOUND THEN RAISE EXCEPTION 'bundle_not_found' USING ERRCODE = 'P0002'; END IF;
|
|
472
|
+
|
|
473
|
+
IF v_bundle.fulfillment_mode <> 'virtual' THEN
|
|
474
|
+
RAISE EXCEPTION 'fulfillment_mode_unsupported' USING ERRCODE = 'P0001';
|
|
475
|
+
END IF;
|
|
476
|
+
|
|
477
|
+
SELECT count(*) INTO v_components
|
|
478
|
+
FROM public.catalog_bundle_components WHERE bundle_id = v_bundle.id;
|
|
479
|
+
IF v_components = 0 THEN
|
|
480
|
+
RAISE EXCEPTION 'composition_required_to_sell' USING ERRCODE = 'P0001';
|
|
481
|
+
END IF;
|
|
482
|
+
|
|
483
|
+
SELECT count(*) INTO v_prices
|
|
484
|
+
FROM public.catalog_bundle_prices WHERE bundle_id = v_bundle.id AND active;
|
|
485
|
+
IF v_prices = 0 THEN
|
|
486
|
+
RAISE EXCEPTION 'price_required_to_sell' USING ERRCODE = 'P0001';
|
|
487
|
+
END IF;
|
|
488
|
+
|
|
489
|
+
IF p_mode = 'dry_run' THEN
|
|
490
|
+
RETURN jsonb_build_object('code', p_code, 'idempotent', false, 'dryRun', true);
|
|
491
|
+
END IF;
|
|
492
|
+
|
|
493
|
+
UPDATE public.catalog_bundles SET status = 'active', updated_at = now() WHERE id = v_bundle.id;
|
|
494
|
+
|
|
495
|
+
INSERT INTO public.catalog_bundle_write_events
|
|
496
|
+
(bundle_code, action, principal_id, idempotency_key, before_state, after_state)
|
|
497
|
+
VALUES (p_code, 'bundle_activate', p_actor_id, p_idempotency_key,
|
|
498
|
+
jsonb_build_object('status', v_bundle.status),
|
|
499
|
+
jsonb_build_object('status', 'active', 'requestId', p_request_id, 'source', p_source));
|
|
500
|
+
|
|
501
|
+
RETURN jsonb_build_object('code', p_code, 'idempotent', false, 'dryRun', false);
|
|
502
|
+
END;
|
|
503
|
+
$$;
|
|
504
|
+
|
|
505
|
+
CREATE FUNCTION public.admin_deactivate_bundle(
|
|
506
|
+
p_actor_id uuid,
|
|
507
|
+
p_code text,
|
|
508
|
+
p_mode text DEFAULT 'commit',
|
|
509
|
+
p_idempotency_key text DEFAULT NULL,
|
|
510
|
+
p_request_id text DEFAULT NULL,
|
|
511
|
+
p_source text DEFAULT 'operator'
|
|
512
|
+
)
|
|
513
|
+
RETURNS jsonb
|
|
514
|
+
LANGUAGE plpgsql
|
|
515
|
+
SET search_path = pg_catalog, public
|
|
516
|
+
AS $$
|
|
517
|
+
DECLARE
|
|
518
|
+
v_status text;
|
|
519
|
+
BEGIN
|
|
520
|
+
IF p_actor_id IS NULL THEN RAISE EXCEPTION 'actor_required' USING ERRCODE = '42501'; END IF;
|
|
521
|
+
|
|
522
|
+
IF p_idempotency_key IS NOT NULL AND EXISTS (
|
|
523
|
+
SELECT 1 FROM public.catalog_bundle_write_events WHERE idempotency_key = p_idempotency_key
|
|
524
|
+
) THEN
|
|
525
|
+
RETURN jsonb_build_object('code', p_code, 'idempotent', true, 'dryRun', false);
|
|
526
|
+
END IF;
|
|
527
|
+
|
|
528
|
+
SELECT status INTO v_status FROM public.catalog_bundles WHERE code = p_code;
|
|
529
|
+
IF NOT FOUND THEN RAISE EXCEPTION 'bundle_not_found' USING ERRCODE = 'P0002'; END IF;
|
|
530
|
+
IF v_status <> 'active' THEN
|
|
531
|
+
RAISE EXCEPTION 'deactivate_requires_active' USING ERRCODE = 'P0001';
|
|
532
|
+
END IF;
|
|
533
|
+
|
|
534
|
+
IF p_mode = 'dry_run' THEN
|
|
535
|
+
RETURN jsonb_build_object('code', p_code, 'idempotent', false, 'dryRun', true);
|
|
536
|
+
END IF;
|
|
537
|
+
|
|
538
|
+
UPDATE public.catalog_bundles SET status = 'draft', updated_at = now() WHERE code = p_code;
|
|
539
|
+
|
|
540
|
+
INSERT INTO public.catalog_bundle_write_events
|
|
541
|
+
(bundle_code, action, principal_id, idempotency_key, before_state, after_state)
|
|
542
|
+
VALUES (p_code, 'bundle_deactivate', p_actor_id, p_idempotency_key,
|
|
543
|
+
jsonb_build_object('status', v_status),
|
|
544
|
+
jsonb_build_object('status', 'draft', 'requestId', p_request_id, 'source', p_source));
|
|
545
|
+
|
|
546
|
+
RETURN jsonb_build_object('code', p_code, 'idempotent', false, 'dryRun', false);
|
|
547
|
+
END;
|
|
548
|
+
$$;
|
|
549
|
+
|
|
550
|
+
REVOKE ALL ON FUNCTION public.admin_upsert_bundle_draft(
|
|
551
|
+
uuid, text, text, text, jsonb, jsonb, text, text, text, text, text
|
|
552
|
+
) FROM PUBLIC;
|
|
553
|
+
REVOKE ALL ON FUNCTION public.admin_set_bundle_composition(
|
|
554
|
+
uuid, text, jsonb, text, text, text, text
|
|
555
|
+
) FROM PUBLIC;
|
|
556
|
+
REVOKE ALL ON FUNCTION public.admin_set_bundle_target_price(
|
|
557
|
+
uuid, text, text, integer, text, text, text, text, text, text
|
|
558
|
+
) FROM PUBLIC;
|
|
559
|
+
REVOKE ALL ON FUNCTION public.admin_archive_bundle(uuid, text, text, text, text, text) FROM PUBLIC;
|
|
560
|
+
REVOKE ALL ON FUNCTION public.admin_restore_bundle(uuid, text, text, text, text, text) FROM PUBLIC;
|
|
561
|
+
REVOKE ALL ON FUNCTION public.admin_activate_bundle(uuid, text, text, text, text, text) FROM PUBLIC;
|
|
562
|
+
REVOKE ALL ON FUNCTION public.admin_deactivate_bundle(uuid, text, text, text, text, text) FROM PUBLIC;
|
|
563
|
+
|
|
564
|
+
COMMENT ON TABLE public.catalog_bundle_write_events IS
|
|
565
|
+
'Append-only record of every accepted bundle write: what changed, which principal asked, and under which idempotency key. Replay recognition, not an authorisation record.';
|
|
566
|
+
COMMENT ON COLUMN public.catalog_bundle_write_events.idempotency_key IS
|
|
567
|
+
'Names one attempt, not one payload. Unique when present, so a retried call returns the original outcome instead of writing twice.';
|