@openlup/platform 0.17.0 → 0.19.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 +143 -0
- package/README.md +299 -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 +21 -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 +173 -0
- package/dist/catalog/bundleCatalogReadPort.js +36 -0
- package/dist/catalog/bundleCatalogStore.d.ts +5 -0
- package/dist/catalog/bundleCatalogStore.js +156 -0
- package/dist/catalog/bundlePriceSelection.d.ts +13 -0
- package/dist/catalog/bundlePriceSelection.js +26 -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 +241 -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/effectiveBundleCatalogQueries.d.ts +8 -0
- package/dist/catalog/effectiveBundleCatalogQueries.js +79 -0
- package/dist/catalog/effectiveBundleCatalogStore.d.ts +4 -0
- package/dist/catalog/effectiveBundleCatalogStore.js +156 -0
- package/dist/catalog/foundationSchemas.d.ts +98 -0
- package/dist/catalog/foundationSchemas.js +110 -0
- package/dist/catalog/index.d.ts +44 -0
- package/dist/catalog/index.js +26 -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 +3 -0
- package/dist/composition/index.js +3 -0
- package/dist/composition/inventoryContribution.d.ts +16 -0
- package/dist/composition/inventoryContribution.js +17 -0
- package/dist/composition/shippingContribution.d.ts +16 -0
- package/dist/composition/shippingContribution.js +17 -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/bindings.d.ts +35 -0
- package/dist/inventory/bindings.js +45 -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 +14 -0
- package/dist/inventory/index.js +11 -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 +28 -0
- package/dist/pricing/commercePriceAuthority.js +140 -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 +35 -0
- package/dist/pricing/commerceQuoteHandler.js +122 -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 +74 -0
- package/dist/pricing/dbBackedCommerceQuotePort.js +223 -0
- package/dist/pricing/dbBackedCommerceQuoteSnapshotVerifier.d.ts +4 -0
- package/dist/pricing/dbBackedCommerceQuoteSnapshotVerifier.js +25 -0
- package/dist/pricing/index.d.ts +52 -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/quoteShippingPromotions.d.ts +20 -0
- package/dist/pricing/quoteShippingPromotions.js +1 -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/commerceQuoteReadPreparation.d.ts +3 -0
- package/dist/promotions/commerceQuoteReadPreparation.js +27 -0
- package/dist/promotions/contracts/index.d.ts +387 -0
- package/dist/promotions/contracts/index.js +1 -0
- package/dist/promotions/index.d.ts +25 -0
- package/dist/promotions/index.js +22 -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 +70 -0
- package/dist/promotions/promotionQuoteAcceptance.js +202 -0
- package/dist/promotions/promotionQuoteBinding.d.ts +73 -0
- package/dist/promotions/promotionQuoteBinding.js +87 -0
- package/dist/promotions/quoteAcquisitionBinding.d.ts +6 -0
- package/dist/promotions/quoteAcquisitionBinding.js +1 -0
- package/dist/promotions/quoteAutomaticPromotions.d.ts +26 -0
- package/dist/promotions/quoteAutomaticPromotions.js +60 -0
- package/dist/promotions/quotePromoDiscounts.d.ts +36 -0
- package/dist/promotions/quotePromoDiscounts.js +146 -0
- package/dist/promotions/quotePromotionCodeAdjustments.d.ts +27 -0
- package/dist/promotions/quotePromotionCodeAdjustments.js +223 -0
- package/dist/promotions/quotePromotionV2.d.ts +30 -0
- package/dist/promotions/quotePromotionV2.js +186 -0
- package/dist/promotions/quoteShippingPolicy.d.ts +23 -0
- package/dist/promotions/quoteShippingPolicy.js +47 -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/bindings.d.ts +57 -0
- package/dist/shipping/bindings.js +46 -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 +4 -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/contracts/threshold.d.ts +22 -0
- package/dist/shipping/contracts/threshold.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 +20 -0
- package/dist/shipping/index.js +10 -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/dist/shipping/thresholdPolicy.d.ts +42 -0
- package/dist/shipping/thresholdPolicy.js +60 -0
- package/package.json +62 -2
- package/release-gates.json +263 -53
- 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 +263 -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 +181 -0
- package/src/catalog/bundleCatalogStore.ts +274 -0
- package/src/catalog/bundlePriceSelection.ts +25 -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/effectiveBundleCatalogQueries.ts +80 -0
- package/src/catalog/effectiveBundleCatalogStore.ts +208 -0
- package/src/catalog/foundationSchemas.ts +123 -0
- package/src/catalog/index.ts +44 -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 +3 -0
- package/src/composition/inventoryContribution.ts +24 -0
- package/src/composition/shippingContribution.ts +24 -0
- package/src/inventory/adminInventory.ts +251 -0
- package/src/inventory/bindings.ts +61 -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 +22 -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 +169 -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 +199 -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 +280 -0
- package/src/pricing/dbBackedCommerceQuoteSnapshotVerifier.ts +49 -0
- package/src/pricing/index.ts +53 -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/quoteShippingPromotions.ts +21 -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/commerceQuoteReadPreparation.ts +29 -0
- package/src/promotions/contracts/index.ts +387 -0
- package/src/promotions/index.ts +25 -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 +291 -0
- package/src/promotions/promotionQuoteBinding.ts +163 -0
- package/src/promotions/quoteAcquisitionBinding.ts +7 -0
- package/src/promotions/quoteAutomaticPromotions.ts +84 -0
- package/src/promotions/quotePromoDiscounts.ts +194 -0
- package/src/promotions/quotePromotionCodeAdjustments.ts +277 -0
- package/src/promotions/quotePromotionV2.ts +239 -0
- package/src/promotions/quoteShippingPolicy.ts +61 -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/bindings.ts +74 -0
- package/src/shipping/contracts/address.ts +71 -0
- package/src/shipping/contracts/delivery.ts +114 -0
- package/src/shipping/contracts/index.ts +19 -0
- package/src/shipping/contracts/shippingRate.ts +7 -0
- package/src/shipping/contracts/threshold.ts +22 -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 +26 -0
- package/src/shipping/pickupPointSearchPort.ts +8 -0
- package/src/shipping/readDeliverySelectionEvidence.ts +51 -0
- package/src/shipping/shippingRatePorts.ts +36 -0
- package/src/shipping/thresholdPolicy.ts +65 -0
|
@@ -0,0 +1,591 @@
|
|
|
1
|
+
-- Public promotion definition and claim lifecycle.
|
|
2
|
+
--
|
|
3
|
+
-- CHANGE. This forward adds a neutral, content-addressed promotion definition
|
|
4
|
+
-- catalogue, a durable reserved/redeemed/released claim ledger, append-only
|
|
5
|
+
-- claim events, read routines for the existing pure quote evaluator, an
|
|
6
|
+
-- idempotent promoted-order wrapper with deterministic effective-line
|
|
7
|
+
-- allocation, order transition hooks and one bounded stale-draft sweep.
|
|
8
|
+
--
|
|
9
|
+
-- WHY. The public bundle already owns catalogue pricing, idempotent order drafts,
|
|
10
|
+
-- settlement truth and the pure promotion evaluator. It could not persist the
|
|
11
|
+
-- evaluator's neutral inputs or reserve the selected benefit, so its mounted
|
|
12
|
+
-- quote silently omitted promotions and its scheduled sweep was managed-only.
|
|
13
|
+
--
|
|
14
|
+
-- SAFETY. This is additive and copies no private promotion/code schema. A
|
|
15
|
+
-- definition is an opaque reviewed JSON contract, not editable presentation or
|
|
16
|
+
-- provider data. Product discounts are allocated only to the fresh item rows
|
|
17
|
+
-- created inside the same uncommitted draft transaction; legacy, renewal,
|
|
18
|
+
-- payment-truth and accounting functions are untouched. Positive net shipping,
|
|
19
|
+
-- line-targeted and zero-payable drafts fail before any write. Reservation
|
|
20
|
+
-- happens in that same transaction.
|
|
21
|
+
-- Every lifecycle mutation locks order before claim, matching settlement's lock
|
|
22
|
+
-- order. Sweep refuses any settlement intent or active renewal inventory before
|
|
23
|
+
-- terminally marking a stale draft. It preserves the order, receipt, claim and
|
|
24
|
+
-- outbox rows as audit evidence and fences later replay; no durable row is deleted.
|
|
25
|
+
-- Routines are invoker-rights, role-free and revoked from PUBLIC.
|
|
26
|
+
--
|
|
27
|
+
-- ROLLBACK. Disable the direct promotion adopter and scheduler, then drop the
|
|
28
|
+
-- capability triggers, routines and four capability-local tables. The receipt
|
|
29
|
+
-- audit columns and nullable item allocation evidence can remain. Existing orders, quote/catalog rows,
|
|
30
|
+
-- settlement intents and outbox facts remain valid.
|
|
31
|
+
|
|
32
|
+
CREATE TABLE public.commerce_promotion_definitions (
|
|
33
|
+
promotion_id text PRIMARY KEY,
|
|
34
|
+
revision integer NOT NULL DEFAULT 1,
|
|
35
|
+
definition jsonb NOT NULL,
|
|
36
|
+
definition_fingerprint text NOT NULL,
|
|
37
|
+
active boolean NOT NULL DEFAULT false,
|
|
38
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
39
|
+
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
40
|
+
CONSTRAINT commerce_promotion_definitions_id_check
|
|
41
|
+
CHECK (promotion_id ~ '^[A-Za-z0-9][A-Za-z0-9._:-]{0,119}$'),
|
|
42
|
+
CONSTRAINT commerce_promotion_definitions_revision_check CHECK (revision > 0),
|
|
43
|
+
CONSTRAINT commerce_promotion_definitions_payload_check CHECK (
|
|
44
|
+
jsonb_typeof(definition) = 'object'
|
|
45
|
+
AND definition->>'id' = promotion_id
|
|
46
|
+
AND definition->>'status' IN ('draft', 'active', 'paused', 'archived')
|
|
47
|
+
AND definition->>'trigger_type' IN ('coupon_code', 'automatic', 'referral')
|
|
48
|
+
AND definition->>'discount_type' IN ('percentage', 'fixed_amount', 'free_shipping')
|
|
49
|
+
AND definition->>'applies_to_kind' IN ('order_total', 'line_with_variant', 'line_with_category')
|
|
50
|
+
AND definition->>'stacking_rule' IN ('exclusive', 'stackable_with_any', 'stackable_with_loyalty')
|
|
51
|
+
AND jsonb_typeof(definition->'eligibility') = 'object'
|
|
52
|
+
AND jsonb_typeof(definition->'applies_to_payload') = 'object'
|
|
53
|
+
AND jsonb_typeof(definition->'region_availability') = 'array'
|
|
54
|
+
)
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
COMMENT ON TABLE public.commerce_promotion_definitions IS
|
|
58
|
+
'Provider-neutral inputs to the shared pure evaluator. Presentation, provider attempts and private promotion-code bindings are deliberately absent.';
|
|
59
|
+
|
|
60
|
+
CREATE FUNCTION public.commerce_promotion_definition_fingerprint()
|
|
61
|
+
RETURNS trigger LANGUAGE plpgsql SET search_path = pg_catalog AS $$
|
|
62
|
+
BEGIN
|
|
63
|
+
NEW.definition_fingerprint := encode(
|
|
64
|
+
sha256(convert_to(NEW.definition::text, 'UTF8')), 'hex'
|
|
65
|
+
);
|
|
66
|
+
RETURN NEW;
|
|
67
|
+
END;
|
|
68
|
+
$$;
|
|
69
|
+
|
|
70
|
+
CREATE TRIGGER commerce_promotion_definition_fingerprint_trigger
|
|
71
|
+
BEFORE INSERT OR UPDATE OF definition ON public.commerce_promotion_definitions
|
|
72
|
+
FOR EACH ROW EXECUTE FUNCTION public.commerce_promotion_definition_fingerprint();
|
|
73
|
+
|
|
74
|
+
CREATE TABLE public.commerce_order_adjustments (
|
|
75
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
76
|
+
order_id uuid NOT NULL REFERENCES public.commerce_orders(id) ON DELETE RESTRICT,
|
|
77
|
+
kind text NOT NULL,
|
|
78
|
+
scope text NOT NULL,
|
|
79
|
+
amount_minor bigint NOT NULL,
|
|
80
|
+
promotion_id text REFERENCES public.commerce_promotion_definitions(promotion_id) ON DELETE RESTRICT,
|
|
81
|
+
definition_fingerprint text,
|
|
82
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
83
|
+
CONSTRAINT commerce_order_adjustments_kind_check
|
|
84
|
+
CHECK (kind IN ('shipping_charge', 'promotion_discount')),
|
|
85
|
+
CONSTRAINT commerce_order_adjustments_scope_check
|
|
86
|
+
CHECK (scope IN ('order_total', 'line', 'shipping')),
|
|
87
|
+
CONSTRAINT commerce_order_adjustments_amount_check CHECK (amount_minor > 0),
|
|
88
|
+
CONSTRAINT commerce_order_adjustments_semantics_check CHECK (
|
|
89
|
+
(kind = 'shipping_charge' AND scope = 'shipping'
|
|
90
|
+
AND promotion_id IS NULL AND definition_fingerprint IS NULL)
|
|
91
|
+
OR
|
|
92
|
+
(kind = 'promotion_discount' AND promotion_id IS NOT NULL
|
|
93
|
+
AND definition_fingerprint ~ '^[0-9a-f]{64}$')
|
|
94
|
+
),
|
|
95
|
+
CONSTRAINT commerce_order_adjustments_promotion_key UNIQUE (order_id, promotion_id)
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
CREATE UNIQUE INDEX commerce_order_adjustments_shipping_charge_key
|
|
99
|
+
ON public.commerce_order_adjustments (order_id)
|
|
100
|
+
WHERE kind = 'shipping_charge';
|
|
101
|
+
|
|
102
|
+
COMMENT ON TABLE public.commerce_order_adjustments IS
|
|
103
|
+
'Immutable neutral money facts. Catalogue lines stay gross; charges add and promotion discounts subtract from the charged order total.';
|
|
104
|
+
|
|
105
|
+
ALTER TABLE public.commerce_order_items
|
|
106
|
+
ADD COLUMN source_line_ordinal integer,
|
|
107
|
+
ADD COLUMN catalog_unit_amount_minor bigint,
|
|
108
|
+
ADD COLUMN catalog_line_amount_minor bigint,
|
|
109
|
+
ADD COLUMN promotion_discount_allocated_minor bigint,
|
|
110
|
+
ADD COLUMN promotion_allocation_fingerprint text,
|
|
111
|
+
ADD CONSTRAINT commerce_order_items_promoted_allocation_check CHECK (
|
|
112
|
+
(source_line_ordinal IS NULL
|
|
113
|
+
AND catalog_unit_amount_minor IS NULL
|
|
114
|
+
AND catalog_line_amount_minor IS NULL
|
|
115
|
+
AND promotion_discount_allocated_minor IS NULL
|
|
116
|
+
AND promotion_allocation_fingerprint IS NULL)
|
|
117
|
+
OR
|
|
118
|
+
(source_line_ordinal > 0
|
|
119
|
+
AND catalog_unit_amount_minor >= 0
|
|
120
|
+
AND catalog_line_amount_minor = catalog_unit_amount_minor * quantity
|
|
121
|
+
AND promotion_discount_allocated_minor >= 0
|
|
122
|
+
AND promotion_discount_allocated_minor <= catalog_line_amount_minor
|
|
123
|
+
AND line_amount_minor + promotion_discount_allocated_minor = catalog_line_amount_minor
|
|
124
|
+
AND promotion_allocation_fingerprint ~ '^[0-9a-f]{64}$')
|
|
125
|
+
) NOT VALID;
|
|
126
|
+
|
|
127
|
+
COMMENT ON COLUMN public.commerce_order_items.catalog_line_amount_minor IS
|
|
128
|
+
'Catalogue subtotal retained only for promoted effective lines. Legacy and renewal rows remain structurally unchanged with all allocation columns NULL.';
|
|
129
|
+
|
|
130
|
+
CREATE TABLE public.commerce_promotion_claims (
|
|
131
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
132
|
+
promotion_id text NOT NULL REFERENCES public.commerce_promotion_definitions(promotion_id) ON DELETE RESTRICT,
|
|
133
|
+
definition_fingerprint text NOT NULL,
|
|
134
|
+
adjustment_id uuid NOT NULL UNIQUE REFERENCES public.commerce_order_adjustments(id) ON DELETE RESTRICT,
|
|
135
|
+
order_id uuid NOT NULL REFERENCES public.commerce_orders(id) ON DELETE RESTRICT,
|
|
136
|
+
client_id uuid REFERENCES public.clients(id) ON DELETE SET NULL,
|
|
137
|
+
status text NOT NULL DEFAULT 'reserved',
|
|
138
|
+
amount_off_minor bigint NOT NULL,
|
|
139
|
+
reserved_at timestamptz NOT NULL DEFAULT now(),
|
|
140
|
+
expires_at timestamptz NOT NULL,
|
|
141
|
+
redeemed_at timestamptz,
|
|
142
|
+
released_at timestamptz,
|
|
143
|
+
release_reason text,
|
|
144
|
+
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
145
|
+
CONSTRAINT commerce_promotion_claims_definition_order_key UNIQUE (promotion_id, order_id),
|
|
146
|
+
CONSTRAINT commerce_promotion_claims_fingerprint_check
|
|
147
|
+
CHECK (definition_fingerprint ~ '^[0-9a-f]{64}$'),
|
|
148
|
+
CONSTRAINT commerce_promotion_claims_status_check
|
|
149
|
+
CHECK (status IN ('reserved', 'redeemed', 'released')),
|
|
150
|
+
CONSTRAINT commerce_promotion_claims_amount_check CHECK (amount_off_minor > 0),
|
|
151
|
+
CONSTRAINT commerce_promotion_claims_expiry_check CHECK (expires_at > reserved_at),
|
|
152
|
+
CONSTRAINT commerce_promotion_claims_terminal_check CHECK (
|
|
153
|
+
(status = 'reserved' AND redeemed_at IS NULL AND released_at IS NULL AND release_reason IS NULL)
|
|
154
|
+
OR (status = 'redeemed' AND redeemed_at IS NOT NULL AND released_at IS NULL AND release_reason IS NULL)
|
|
155
|
+
OR (status = 'released' AND redeemed_at IS NULL AND released_at IS NOT NULL AND btrim(release_reason) <> '')
|
|
156
|
+
)
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
CREATE INDEX commerce_promotion_claims_sweep
|
|
160
|
+
ON public.commerce_promotion_claims (expires_at, id)
|
|
161
|
+
WHERE status = 'reserved';
|
|
162
|
+
|
|
163
|
+
CREATE TABLE public.commerce_promotion_claim_events (
|
|
164
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
165
|
+
claim_id uuid NOT NULL REFERENCES public.commerce_promotion_claims(id) ON DELETE RESTRICT,
|
|
166
|
+
event_key text NOT NULL,
|
|
167
|
+
transition text NOT NULL,
|
|
168
|
+
reason text,
|
|
169
|
+
occurred_at timestamptz NOT NULL DEFAULT now(),
|
|
170
|
+
CONSTRAINT commerce_promotion_claim_events_key UNIQUE (claim_id, event_key),
|
|
171
|
+
CONSTRAINT commerce_promotion_claim_events_transition_check
|
|
172
|
+
CHECK (transition IN ('reserved', 'redeemed', 'released')),
|
|
173
|
+
CONSTRAINT commerce_promotion_claim_events_key_check CHECK (btrim(event_key) <> '')
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
ALTER TABLE public.commerce_order_draft_receipts
|
|
177
|
+
ADD COLUMN invalidated_at timestamptz,
|
|
178
|
+
ADD COLUMN invalidation_reason text;
|
|
179
|
+
ALTER TABLE public.commerce_order_draft_receipts
|
|
180
|
+
ADD CONSTRAINT commerce_order_draft_receipts_invalidation_check CHECK (
|
|
181
|
+
(invalidated_at IS NULL AND invalidation_reason IS NULL)
|
|
182
|
+
OR (invalidated_at IS NOT NULL AND btrim(invalidation_reason) <> '')
|
|
183
|
+
) NOT VALID;
|
|
184
|
+
|
|
185
|
+
CREATE FUNCTION public.commerce_order_adjustments_append_only()
|
|
186
|
+
RETURNS trigger LANGUAGE plpgsql SET search_path = pg_catalog AS $$
|
|
187
|
+
BEGIN
|
|
188
|
+
RAISE EXCEPTION 'commerce_order_adjustments_append_only' USING ERRCODE = '55000';
|
|
189
|
+
END;
|
|
190
|
+
$$;
|
|
191
|
+
|
|
192
|
+
CREATE TRIGGER commerce_order_adjustments_append_only_trigger
|
|
193
|
+
BEFORE UPDATE OR DELETE ON public.commerce_order_adjustments
|
|
194
|
+
FOR EACH ROW EXECUTE FUNCTION public.commerce_order_adjustments_append_only();
|
|
195
|
+
|
|
196
|
+
CREATE FUNCTION public.commerce_promotion_claim_money_freeze()
|
|
197
|
+
RETURNS trigger
|
|
198
|
+
LANGUAGE plpgsql
|
|
199
|
+
SET search_path = public, pg_catalog
|
|
200
|
+
AS $$
|
|
201
|
+
BEGIN
|
|
202
|
+
IF EXISTS (
|
|
203
|
+
SELECT 1 FROM public.commerce_promotion_claims AS claim
|
|
204
|
+
WHERE claim.order_id = OLD.order_id
|
|
205
|
+
) THEN
|
|
206
|
+
RAISE EXCEPTION 'commerce_promotion_claim_money_frozen' USING ERRCODE = '55000';
|
|
207
|
+
END IF;
|
|
208
|
+
IF TG_OP = 'DELETE' THEN RETURN OLD; END IF;
|
|
209
|
+
RETURN NEW;
|
|
210
|
+
END;
|
|
211
|
+
$$;
|
|
212
|
+
|
|
213
|
+
CREATE TRIGGER commerce_promotion_claim_money_freeze_trigger
|
|
214
|
+
BEFORE UPDATE OR DELETE ON public.commerce_order_items
|
|
215
|
+
FOR EACH ROW EXECUTE FUNCTION public.commerce_promotion_claim_money_freeze();
|
|
216
|
+
|
|
217
|
+
CREATE FUNCTION public.commerce_promotion_active_definitions()
|
|
218
|
+
RETURNS jsonb LANGUAGE sql STABLE SET search_path = pg_catalog AS $$
|
|
219
|
+
SELECT COALESCE(jsonb_agg(definition_row.definition ORDER BY definition_row.promotion_id), '[]'::jsonb)
|
|
220
|
+
FROM public.commerce_promotion_definitions AS definition_row
|
|
221
|
+
WHERE definition_row.active AND definition_row.definition->>'status' = 'active';
|
|
222
|
+
$$;
|
|
223
|
+
|
|
224
|
+
CREATE FUNCTION public.commerce_promotion_paid_order_counts(p_client_id uuid)
|
|
225
|
+
RETURNS jsonb LANGUAGE sql STABLE SET search_path = pg_catalog AS $$
|
|
226
|
+
SELECT jsonb_build_object(
|
|
227
|
+
'oneTime', count(*) FILTER (WHERE order_row.subscription_cycle_id IS NULL),
|
|
228
|
+
'subscription', count(*) FILTER (WHERE order_row.subscription_cycle_id IS NOT NULL)
|
|
229
|
+
)
|
|
230
|
+
FROM public.commerce_orders AS order_row
|
|
231
|
+
WHERE order_row.client_id = p_client_id
|
|
232
|
+
AND order_row.status IN ('paid', 'fulfillment_pending', 'fulfilled');
|
|
233
|
+
$$;
|
|
234
|
+
|
|
235
|
+
CREATE FUNCTION public.commerce_promotion_redemption_counts(p_client_id uuid DEFAULT NULL)
|
|
236
|
+
RETURNS TABLE (promotion_id text, global_count bigint, per_customer_count bigint)
|
|
237
|
+
LANGUAGE sql STABLE SET search_path = pg_catalog AS $$
|
|
238
|
+
SELECT claim.promotion_id,
|
|
239
|
+
count(*) FILTER (WHERE claim.status = 'redeemed') AS global_count,
|
|
240
|
+
count(*) FILTER (WHERE claim.status = 'redeemed' AND p_client_id IS NOT NULL
|
|
241
|
+
AND claim.client_id = p_client_id) AS per_customer_count
|
|
242
|
+
FROM public.commerce_promotion_claims AS claim GROUP BY claim.promotion_id;
|
|
243
|
+
$$;
|
|
244
|
+
|
|
245
|
+
CREATE FUNCTION public.commerce_promotion_device_first_order_redeemed(p_visitor_id text)
|
|
246
|
+
RETURNS boolean LANGUAGE sql STABLE SET search_path = pg_catalog AS $$
|
|
247
|
+
-- The public order-draft response stores no visitor identity, so the direct
|
|
248
|
+
-- adapter refuses to manufacture device evidence. Account counts remain real.
|
|
249
|
+
SELECT false;
|
|
250
|
+
$$;
|
|
251
|
+
|
|
252
|
+
CREATE FUNCTION public.commerce_create_promoted_order_draft_with_outbox(
|
|
253
|
+
p_idempotency_key text,
|
|
254
|
+
p_quote_snapshot jsonb,
|
|
255
|
+
p_order_draft_snapshot jsonb,
|
|
256
|
+
p_client_id uuid DEFAULT NULL
|
|
257
|
+
)
|
|
258
|
+
RETURNS jsonb
|
|
259
|
+
LANGUAGE plpgsql
|
|
260
|
+
SET search_path = pg_catalog
|
|
261
|
+
AS $$
|
|
262
|
+
DECLARE
|
|
263
|
+
v_receipt public.commerce_order_draft_receipts%ROWTYPE;
|
|
264
|
+
v_response jsonb;
|
|
265
|
+
v_order_id uuid;
|
|
266
|
+
v_discount jsonb;
|
|
267
|
+
v_definition public.commerce_promotion_definitions%ROWTYPE;
|
|
268
|
+
v_adjustment public.commerce_order_adjustments%ROWTYPE;
|
|
269
|
+
v_claim public.commerce_promotion_claims%ROWTYPE;
|
|
270
|
+
v_global bigint;
|
|
271
|
+
v_customer bigint;
|
|
272
|
+
v_global_limit integer;
|
|
273
|
+
v_customer_limit integer;
|
|
274
|
+
v_lines bigint;
|
|
275
|
+
v_order_discount bigint;
|
|
276
|
+
v_shipping bigint;
|
|
277
|
+
v_shipping_discount bigint;
|
|
278
|
+
v_total bigint;
|
|
279
|
+
v_allocation_fingerprint text;
|
|
280
|
+
BEGIN
|
|
281
|
+
IF p_idempotency_key IS NULL OR char_length(btrim(p_idempotency_key)) NOT BETWEEN 8 AND 120 THEN
|
|
282
|
+
RAISE EXCEPTION 'commerce_order_draft_invalid_idempotency_key' USING ERRCODE = '22023';
|
|
283
|
+
END IF;
|
|
284
|
+
PERFORM pg_advisory_xact_lock(hashtextextended(p_idempotency_key, 0));
|
|
285
|
+
SELECT * INTO v_receipt FROM public.commerce_order_draft_receipts
|
|
286
|
+
WHERE idempotency_key = p_idempotency_key FOR UPDATE;
|
|
287
|
+
IF FOUND AND v_receipt.invalidated_at IS NOT NULL THEN
|
|
288
|
+
RAISE EXCEPTION 'commerce_order_draft_expired' USING ERRCODE = '40001';
|
|
289
|
+
END IF;
|
|
290
|
+
|
|
291
|
+
SELECT COALESCE(sum((line.value#>>'{lineSubtotalGross,amountMinor}')::bigint), 0)
|
|
292
|
+
INTO v_lines
|
|
293
|
+
FROM jsonb_array_elements(p_order_draft_snapshot->'lines') AS line(value);
|
|
294
|
+
SELECT
|
|
295
|
+
COALESCE(sum((discount.value->>'amountOffMinor')::bigint)
|
|
296
|
+
FILTER (WHERE discount.value->>'appliesTo' <> 'shipping'), 0),
|
|
297
|
+
COALESCE(sum((discount.value->>'amountOffMinor')::bigint)
|
|
298
|
+
FILTER (WHERE discount.value->>'appliesTo' = 'shipping'), 0)
|
|
299
|
+
INTO v_order_discount, v_shipping_discount
|
|
300
|
+
FROM jsonb_array_elements(COALESCE(p_order_draft_snapshot->'discounts', '[]'::jsonb))
|
|
301
|
+
AS discount(value);
|
|
302
|
+
v_shipping := COALESCE((p_order_draft_snapshot#>>'{totals,shippingGross,amountMinor}')::bigint, 0);
|
|
303
|
+
IF v_shipping_discount IS DISTINCT FROM
|
|
304
|
+
COALESCE((p_order_draft_snapshot#>>'{totals,shippingDiscountGross,amountMinor}')::bigint, 0)
|
|
305
|
+
OR v_order_discount IS DISTINCT FROM
|
|
306
|
+
(p_order_draft_snapshot#>>'{totals,discountTotalGross,amountMinor}')::bigint
|
|
307
|
+
OR v_lines IS DISTINCT FROM
|
|
308
|
+
(p_order_draft_snapshot#>>'{totals,subtotalGross,amountMinor}')::bigint THEN
|
|
309
|
+
RAISE EXCEPTION 'commerce_order_draft_money_components_mismatch' USING ERRCODE = '22023';
|
|
310
|
+
END IF;
|
|
311
|
+
IF EXISTS (
|
|
312
|
+
SELECT 1 FROM jsonb_array_elements(COALESCE(p_order_draft_snapshot->'discounts', '[]'::jsonb))
|
|
313
|
+
AS discount(value)
|
|
314
|
+
WHERE discount.value->>'appliesTo' = 'line'
|
|
315
|
+
) THEN
|
|
316
|
+
RAISE EXCEPTION 'commerce_promotion_line_allocation_unsupported' USING ERRCODE = '22023';
|
|
317
|
+
END IF;
|
|
318
|
+
IF v_shipping IS DISTINCT FROM v_shipping_discount THEN
|
|
319
|
+
RAISE EXCEPTION 'commerce_promoted_net_shipping_unsupported' USING ERRCODE = '22023';
|
|
320
|
+
END IF;
|
|
321
|
+
v_total := v_lines + v_shipping - v_order_discount - v_shipping_discount;
|
|
322
|
+
IF v_total IS DISTINCT FROM
|
|
323
|
+
(p_order_draft_snapshot#>>'{totals,totalGross,amountMinor}')::bigint THEN
|
|
324
|
+
RAISE EXCEPTION 'commerce_order_draft_money_equation_mismatch' USING ERRCODE = '22023';
|
|
325
|
+
END IF;
|
|
326
|
+
IF v_total <= 0 THEN
|
|
327
|
+
RAISE EXCEPTION 'commerce_zero_payable_order_unsupported' USING ERRCODE = '22023';
|
|
328
|
+
END IF;
|
|
329
|
+
|
|
330
|
+
v_response := public.commerce_create_order_draft_with_outbox(
|
|
331
|
+
p_idempotency_key, p_quote_snapshot, p_order_draft_snapshot, p_client_id
|
|
332
|
+
);
|
|
333
|
+
IF (v_response#>>'{orderDraft,replayed}')::boolean THEN RETURN v_response; END IF;
|
|
334
|
+
v_order_id := replace(v_response#>>'{orderDraft,orderId}', 'order_', '')::uuid;
|
|
335
|
+
v_allocation_fingerprint := encode(sha256(convert_to(
|
|
336
|
+
p_order_draft_snapshot::text || '|effective-line-allocation.v1', 'UTF8'
|
|
337
|
+
)), 'hex');
|
|
338
|
+
|
|
339
|
+
IF v_order_discount > 0 THEN
|
|
340
|
+
DELETE FROM public.commerce_order_items WHERE order_id = v_order_id;
|
|
341
|
+
WITH source_lines AS (
|
|
342
|
+
SELECT
|
|
343
|
+
line.ordinality::integer AS source_ordinal,
|
|
344
|
+
line.value->>'sku' AS sku,
|
|
345
|
+
(line.value->>'quantity')::integer AS quantity,
|
|
346
|
+
(line.value#>>'{unitPriceGross,amountMinor}')::bigint AS catalog_unit,
|
|
347
|
+
(line.value#>>'{lineSubtotalGross,amountMinor}')::bigint AS catalog_line
|
|
348
|
+
FROM jsonb_array_elements(p_order_draft_snapshot->'lines')
|
|
349
|
+
WITH ORDINALITY AS line(value, ordinality)
|
|
350
|
+
), bases AS (
|
|
351
|
+
SELECT source.*,
|
|
352
|
+
floor(v_total::numeric * source.catalog_line / v_lines)::bigint AS effective_base,
|
|
353
|
+
(v_total::numeric * source.catalog_line / v_lines)
|
|
354
|
+
- floor(v_total::numeric * source.catalog_line / v_lines) AS fraction
|
|
355
|
+
FROM source_lines AS source
|
|
356
|
+
), remainder AS (
|
|
357
|
+
SELECT v_total - sum(effective_base) AS cents FROM bases
|
|
358
|
+
), allocated_lines AS (
|
|
359
|
+
SELECT base.*,
|
|
360
|
+
base.effective_base + CASE WHEN row_number() OVER (
|
|
361
|
+
ORDER BY base.fraction DESC, base.source_ordinal
|
|
362
|
+
) <= remainder.cents THEN 1 ELSE 0 END AS effective_line
|
|
363
|
+
FROM bases AS base CROSS JOIN remainder
|
|
364
|
+
), split_lines AS (
|
|
365
|
+
SELECT allocated.*,
|
|
366
|
+
allocated.quantity - (allocated.effective_line % allocated.quantity)::integer AS split_quantity,
|
|
367
|
+
allocated.effective_line / allocated.quantity AS effective_unit
|
|
368
|
+
FROM allocated_lines AS allocated
|
|
369
|
+
UNION ALL
|
|
370
|
+
SELECT allocated.*,
|
|
371
|
+
(allocated.effective_line % allocated.quantity)::integer,
|
|
372
|
+
allocated.effective_line / allocated.quantity + 1
|
|
373
|
+
FROM allocated_lines AS allocated
|
|
374
|
+
WHERE allocated.effective_line % allocated.quantity > 0
|
|
375
|
+
), prepared AS (
|
|
376
|
+
SELECT split.*, sku_row.id AS sku_id,
|
|
377
|
+
row_number() OVER (
|
|
378
|
+
ORDER BY split.source_ordinal, split.effective_unit, split.split_quantity
|
|
379
|
+
)::integer AS final_ordinal
|
|
380
|
+
FROM split_lines AS split
|
|
381
|
+
JOIN public.catalog_skus AS sku_row ON sku_row.sku = split.sku
|
|
382
|
+
WHERE split.split_quantity > 0
|
|
383
|
+
)
|
|
384
|
+
INSERT INTO public.commerce_order_items (
|
|
385
|
+
order_id, sku_id, line_ordinal, quantity, unit_amount_minor, line_amount_minor,
|
|
386
|
+
source_line_ordinal, catalog_unit_amount_minor, catalog_line_amount_minor,
|
|
387
|
+
promotion_discount_allocated_minor, promotion_allocation_fingerprint
|
|
388
|
+
)
|
|
389
|
+
SELECT
|
|
390
|
+
v_order_id, prepared.sku_id, prepared.final_ordinal,
|
|
391
|
+
prepared.split_quantity, prepared.effective_unit,
|
|
392
|
+
prepared.effective_unit * prepared.split_quantity,
|
|
393
|
+
prepared.source_ordinal, prepared.catalog_unit,
|
|
394
|
+
prepared.catalog_unit * prepared.split_quantity,
|
|
395
|
+
(prepared.catalog_unit - prepared.effective_unit) * prepared.split_quantity,
|
|
396
|
+
v_allocation_fingerprint
|
|
397
|
+
FROM prepared ORDER BY prepared.final_ordinal;
|
|
398
|
+
END IF;
|
|
399
|
+
|
|
400
|
+
IF v_shipping > 0 THEN
|
|
401
|
+
INSERT INTO public.commerce_order_adjustments (
|
|
402
|
+
order_id, kind, scope, amount_minor
|
|
403
|
+
) VALUES (v_order_id, 'shipping_charge', 'shipping', v_shipping);
|
|
404
|
+
END IF;
|
|
405
|
+
|
|
406
|
+
FOR v_discount IN
|
|
407
|
+
SELECT value FROM jsonb_array_elements(COALESCE(p_order_draft_snapshot->'discounts', '[]'::jsonb))
|
|
408
|
+
WHERE value ? 'promotionId' AND COALESCE((value->>'amountOffMinor')::bigint, 0) > 0
|
|
409
|
+
ORDER BY value->>'promotionId'
|
|
410
|
+
LOOP
|
|
411
|
+
SELECT * INTO v_definition FROM public.commerce_promotion_definitions AS definition_row
|
|
412
|
+
WHERE definition_row.promotion_id = v_discount->>'promotionId' FOR UPDATE;
|
|
413
|
+
IF NOT FOUND OR NOT v_definition.active OR v_definition.definition->>'status' <> 'active' THEN
|
|
414
|
+
RAISE EXCEPTION 'commerce_promotion_definition_not_active' USING ERRCODE = '40001';
|
|
415
|
+
END IF;
|
|
416
|
+
v_global_limit := NULLIF(v_definition.definition->>'redemption_limit_global', '')::integer;
|
|
417
|
+
v_customer_limit := NULLIF(v_definition.definition->>'redemption_limit_per_customer', '')::integer;
|
|
418
|
+
SELECT count(*) FILTER (WHERE status IN ('reserved', 'redeemed')),
|
|
419
|
+
count(*) FILTER (WHERE status IN ('reserved', 'redeemed') AND client_id = p_client_id)
|
|
420
|
+
INTO v_global, v_customer FROM public.commerce_promotion_claims
|
|
421
|
+
WHERE promotion_id = v_definition.promotion_id;
|
|
422
|
+
IF v_global_limit IS NOT NULL AND v_global >= v_global_limit THEN
|
|
423
|
+
RAISE EXCEPTION 'commerce_promotion_global_limit_reached' USING ERRCODE = '40001';
|
|
424
|
+
END IF;
|
|
425
|
+
IF v_customer_limit IS NOT NULL AND p_client_id IS NULL THEN
|
|
426
|
+
RAISE EXCEPTION 'commerce_promotion_client_required' USING ERRCODE = '22023';
|
|
427
|
+
END IF;
|
|
428
|
+
IF v_customer_limit IS NOT NULL AND v_customer >= v_customer_limit THEN
|
|
429
|
+
RAISE EXCEPTION 'commerce_promotion_customer_limit_reached' USING ERRCODE = '40001';
|
|
430
|
+
END IF;
|
|
431
|
+
INSERT INTO public.commerce_order_adjustments (
|
|
432
|
+
order_id, kind, scope, amount_minor, promotion_id, definition_fingerprint
|
|
433
|
+
) VALUES (
|
|
434
|
+
v_order_id, 'promotion_discount', v_discount->>'appliesTo',
|
|
435
|
+
(v_discount->>'amountOffMinor')::bigint,
|
|
436
|
+
v_definition.promotion_id, v_definition.definition_fingerprint
|
|
437
|
+
) RETURNING * INTO v_adjustment;
|
|
438
|
+
INSERT INTO public.commerce_promotion_claims (
|
|
439
|
+
promotion_id, definition_fingerprint, adjustment_id,
|
|
440
|
+
order_id, client_id, amount_off_minor, expires_at
|
|
441
|
+
) VALUES (
|
|
442
|
+
v_definition.promotion_id, v_definition.definition_fingerprint, v_adjustment.id,
|
|
443
|
+
v_order_id, p_client_id, v_adjustment.amount_minor, now() + interval '24 hours'
|
|
444
|
+
) RETURNING * INTO v_claim;
|
|
445
|
+
INSERT INTO public.commerce_promotion_claim_events (claim_id, event_key, transition)
|
|
446
|
+
VALUES (v_claim.id, 'reserved:' || v_order_id::text, 'reserved');
|
|
447
|
+
END LOOP;
|
|
448
|
+
RETURN v_response;
|
|
449
|
+
END;
|
|
450
|
+
$$;
|
|
451
|
+
|
|
452
|
+
CREATE FUNCTION public.commerce_transition_order_promotion_claims()
|
|
453
|
+
RETURNS trigger LANGUAGE plpgsql SET search_path = pg_catalog AS $$
|
|
454
|
+
DECLARE
|
|
455
|
+
v_claim public.commerce_promotion_claims%ROWTYPE;
|
|
456
|
+
v_transition text;
|
|
457
|
+
BEGIN
|
|
458
|
+
IF NEW.status IS NOT DISTINCT FROM OLD.status THEN RETURN NEW; END IF;
|
|
459
|
+
IF NEW.status IN ('paid', 'fulfillment_pending', 'fulfilled') THEN v_transition := 'redeemed';
|
|
460
|
+
ELSIF NEW.status IN ('cancelled', 'refunded') THEN v_transition := 'released';
|
|
461
|
+
ELSE RETURN NEW;
|
|
462
|
+
END IF;
|
|
463
|
+
FOR v_claim IN SELECT * FROM public.commerce_promotion_claims
|
|
464
|
+
WHERE order_id = NEW.id ORDER BY promotion_id FOR UPDATE
|
|
465
|
+
LOOP
|
|
466
|
+
IF v_transition = 'redeemed' AND v_claim.status <> 'redeemed' THEN
|
|
467
|
+
UPDATE public.commerce_promotion_claims
|
|
468
|
+
SET status = 'redeemed', redeemed_at = COALESCE(NEW.paid_at, now()),
|
|
469
|
+
released_at = NULL, release_reason = NULL, updated_at = now() WHERE id = v_claim.id;
|
|
470
|
+
ELSIF v_transition = 'released' AND v_claim.status = 'reserved' THEN
|
|
471
|
+
UPDATE public.commerce_promotion_claims
|
|
472
|
+
SET status = 'released', redeemed_at = NULL, released_at = now(),
|
|
473
|
+
release_reason = 'order_' || NEW.status, updated_at = now() WHERE id = v_claim.id;
|
|
474
|
+
ELSE CONTINUE;
|
|
475
|
+
END IF;
|
|
476
|
+
INSERT INTO public.commerce_promotion_claim_events (claim_id, event_key, transition, reason)
|
|
477
|
+
VALUES (v_claim.id, v_transition || ':' || NEW.status, v_transition,
|
|
478
|
+
CASE WHEN v_transition = 'released' THEN 'order_' || NEW.status ELSE NULL END)
|
|
479
|
+
ON CONFLICT (claim_id, event_key) DO NOTHING;
|
|
480
|
+
END LOOP;
|
|
481
|
+
RETURN NEW;
|
|
482
|
+
END;
|
|
483
|
+
$$;
|
|
484
|
+
|
|
485
|
+
CREATE TRIGGER commerce_transition_order_promotion_claims_trigger
|
|
486
|
+
AFTER UPDATE OF status ON public.commerce_orders
|
|
487
|
+
FOR EACH ROW EXECUTE FUNCTION public.commerce_transition_order_promotion_claims();
|
|
488
|
+
|
|
489
|
+
CREATE FUNCTION public.commerce_order_money_summary(p_order_id uuid)
|
|
490
|
+
RETURNS jsonb
|
|
491
|
+
LANGUAGE sql
|
|
492
|
+
STABLE
|
|
493
|
+
SET search_path = pg_catalog
|
|
494
|
+
AS $$
|
|
495
|
+
SELECT jsonb_build_object(
|
|
496
|
+
'subtotalMinor', COALESCE((SELECT sum(item.line_amount_minor)
|
|
497
|
+
FROM public.commerce_order_items AS item WHERE item.order_id = p_order_id), 0),
|
|
498
|
+
'discountMinor', COALESCE((SELECT sum(adjustment.amount_minor)
|
|
499
|
+
FROM public.commerce_order_adjustments AS adjustment
|
|
500
|
+
WHERE adjustment.order_id = p_order_id AND adjustment.kind = 'promotion_discount'
|
|
501
|
+
AND adjustment.scope <> 'shipping'), 0),
|
|
502
|
+
'shippingMinor', COALESCE((SELECT sum(adjustment.amount_minor)
|
|
503
|
+
FROM public.commerce_order_adjustments AS adjustment
|
|
504
|
+
WHERE adjustment.order_id = p_order_id AND adjustment.kind = 'shipping_charge'), 0),
|
|
505
|
+
'shippingDiscountMinor', COALESCE((SELECT sum(adjustment.amount_minor)
|
|
506
|
+
FROM public.commerce_order_adjustments AS adjustment
|
|
507
|
+
WHERE adjustment.order_id = p_order_id AND adjustment.kind = 'promotion_discount'
|
|
508
|
+
AND adjustment.scope = 'shipping'), 0)
|
|
509
|
+
);
|
|
510
|
+
$$;
|
|
511
|
+
|
|
512
|
+
CREATE FUNCTION public.commerce_sweep_stale_promotion_claims(
|
|
513
|
+
p_now timestamptz DEFAULT now(), p_limit integer DEFAULT 50,
|
|
514
|
+
p_claim_lease_minutes integer DEFAULT 15, p_grace_minutes integer DEFAULT 5
|
|
515
|
+
)
|
|
516
|
+
RETURNS jsonb LANGUAGE plpgsql SET search_path = pg_catalog AS $$
|
|
517
|
+
DECLARE
|
|
518
|
+
v_order public.commerce_orders%ROWTYPE;
|
|
519
|
+
v_checked integer := 0;
|
|
520
|
+
v_cancelled integer := 0;
|
|
521
|
+
v_skipped integer := 0;
|
|
522
|
+
BEGIN
|
|
523
|
+
IF p_now IS NULL OR p_limit < 1 OR p_limit > 200
|
|
524
|
+
OR p_claim_lease_minutes < 1 OR p_grace_minutes < 0 THEN
|
|
525
|
+
RAISE EXCEPTION 'promotion_claim_sweep_invalid_input' USING ERRCODE = '22023';
|
|
526
|
+
END IF;
|
|
527
|
+
FOR v_order IN
|
|
528
|
+
SELECT order_row.* FROM public.commerce_orders AS order_row
|
|
529
|
+
WHERE order_row.status = 'pending_payment'
|
|
530
|
+
AND order_row.created_at <= p_now - make_interval(mins => p_claim_lease_minutes)
|
|
531
|
+
AND EXISTS (SELECT 1 FROM public.commerce_promotion_claims AS claim
|
|
532
|
+
WHERE claim.order_id = order_row.id AND claim.status = 'reserved'
|
|
533
|
+
AND claim.expires_at <= p_now - make_interval(mins => p_grace_minutes))
|
|
534
|
+
ORDER BY order_row.created_at, order_row.id LIMIT p_limit
|
|
535
|
+
FOR UPDATE OF order_row SKIP LOCKED
|
|
536
|
+
LOOP
|
|
537
|
+
v_checked := v_checked + 1;
|
|
538
|
+
PERFORM 1 FROM public.commerce_promotion_claims WHERE order_id = v_order.id
|
|
539
|
+
AND status = 'reserved' ORDER BY promotion_id FOR UPDATE;
|
|
540
|
+
IF EXISTS (SELECT 1 FROM public.commerce_settlement_intents WHERE order_id = v_order.id)
|
|
541
|
+
OR EXISTS (SELECT 1 FROM public.subscription_cycles AS cycle
|
|
542
|
+
JOIN public.fulfillment_inventory_reservations AS reservation
|
|
543
|
+
ON reservation.renewal_operation_id = cycle.renewal_operation_id
|
|
544
|
+
WHERE cycle.id = v_order.subscription_cycle_id
|
|
545
|
+
AND reservation.status IN ('held', 'committed', 'consumed')) THEN
|
|
546
|
+
v_skipped := v_skipped + 1;
|
|
547
|
+
CONTINUE;
|
|
548
|
+
END IF;
|
|
549
|
+
UPDATE public.commerce_orders SET status = 'cancelled', cancelled_at = p_now, updated_at = p_now
|
|
550
|
+
WHERE id = v_order.id AND status = 'pending_payment';
|
|
551
|
+
IF NOT FOUND THEN v_skipped := v_skipped + 1; CONTINUE; END IF;
|
|
552
|
+
UPDATE public.commerce_order_draft_receipts
|
|
553
|
+
SET invalidated_at = p_now, invalidation_reason = 'stale_promotion_claim'
|
|
554
|
+
WHERE order_id = v_order.id AND invalidated_at IS NULL;
|
|
555
|
+
UPDATE public.outbox_events
|
|
556
|
+
SET processed_at = COALESCE(processed_at, p_now),
|
|
557
|
+
metadata = metadata || jsonb_build_object('terminalReason', 'stale_promotion_claim')
|
|
558
|
+
WHERE aggregate_id = v_order.id AND event_type = 'commerce.order_draft.created';
|
|
559
|
+
v_cancelled := v_cancelled + 1;
|
|
560
|
+
END LOOP;
|
|
561
|
+
RETURN jsonb_build_object('checked', v_checked, 'cancelled', v_cancelled, 'skipped', v_skipped);
|
|
562
|
+
END;
|
|
563
|
+
$$;
|
|
564
|
+
|
|
565
|
+
CREATE FUNCTION public.commerce_promotion_claim_readback(p_order_id uuid)
|
|
566
|
+
RETURNS jsonb LANGUAGE sql STABLE SET search_path = pg_catalog AS $$
|
|
567
|
+
SELECT COALESCE(jsonb_agg(jsonb_build_object(
|
|
568
|
+
'claimId', claim.id, 'promotionId', claim.promotion_id, 'status', claim.status,
|
|
569
|
+
'amountOffMinor', claim.amount_off_minor, 'definitionFingerprint', claim.definition_fingerprint,
|
|
570
|
+
'reservedAt', claim.reserved_at, 'expiresAt', claim.expires_at,
|
|
571
|
+
'redeemedAt', claim.redeemed_at, 'releasedAt', claim.released_at,
|
|
572
|
+
'releaseReason', claim.release_reason
|
|
573
|
+
) ORDER BY claim.promotion_id), '[]'::jsonb)
|
|
574
|
+
FROM public.commerce_promotion_claims AS claim WHERE claim.order_id = p_order_id;
|
|
575
|
+
$$;
|
|
576
|
+
|
|
577
|
+
REVOKE ALL ON TABLE public.commerce_promotion_definitions FROM PUBLIC;
|
|
578
|
+
REVOKE ALL ON TABLE public.commerce_order_adjustments FROM PUBLIC;
|
|
579
|
+
REVOKE ALL ON TABLE public.commerce_promotion_claims FROM PUBLIC;
|
|
580
|
+
REVOKE ALL ON TABLE public.commerce_promotion_claim_events FROM PUBLIC;
|
|
581
|
+
REVOKE ALL ON FUNCTION public.commerce_promotion_active_definitions() FROM PUBLIC;
|
|
582
|
+
REVOKE ALL ON FUNCTION public.commerce_promotion_paid_order_counts(uuid) FROM PUBLIC;
|
|
583
|
+
REVOKE ALL ON FUNCTION public.commerce_promotion_redemption_counts(uuid) FROM PUBLIC;
|
|
584
|
+
REVOKE ALL ON FUNCTION public.commerce_promotion_device_first_order_redeemed(text) FROM PUBLIC;
|
|
585
|
+
REVOKE ALL ON FUNCTION public.commerce_create_promoted_order_draft_with_outbox(text, jsonb, jsonb, uuid) FROM PUBLIC;
|
|
586
|
+
REVOKE ALL ON FUNCTION public.commerce_order_adjustments_append_only() FROM PUBLIC;
|
|
587
|
+
REVOKE ALL ON FUNCTION public.commerce_promotion_claim_money_freeze() FROM PUBLIC;
|
|
588
|
+
REVOKE ALL ON FUNCTION public.commerce_transition_order_promotion_claims() FROM PUBLIC;
|
|
589
|
+
REVOKE ALL ON FUNCTION public.commerce_order_money_summary(uuid) FROM PUBLIC;
|
|
590
|
+
REVOKE ALL ON FUNCTION public.commerce_sweep_stale_promotion_claims(timestamptz, integer, integer, integer) FROM PUBLIC;
|
|
591
|
+
REVOKE ALL ON FUNCTION public.commerce_promotion_claim_readback(uuid) FROM PUBLIC;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
-- Validate promotion lifecycle constraints without coupling validation to DDL.
|
|
2
|
+
--
|
|
3
|
+
-- CHANGE. This forward validates the promoted order-item allocation constraint
|
|
4
|
+
-- and the draft-receipt invalidation constraint added as NOT VALID by the
|
|
5
|
+
-- preceding promotion-claim lifecycle forward.
|
|
6
|
+
--
|
|
7
|
+
-- WHY. Existing rows are structurally compatible, but validating in the same
|
|
8
|
+
-- transaction as ADD CONSTRAINT would retain the stronger DDL lock for the
|
|
9
|
+
-- table scan. A separate forward lets PostgreSQL validate under its lower-lock
|
|
10
|
+
-- validation path.
|
|
11
|
+
--
|
|
12
|
+
-- SAFETY. No data or routine body changes. New writes were already checked from
|
|
13
|
+
-- the moment the constraints were added; this pass only certifies historical
|
|
14
|
+
-- rows. Deployment order is fixed by the platform migration manifest.
|
|
15
|
+
--
|
|
16
|
+
-- ROLLBACK. No rollback is required. If validation finds legacy drift the
|
|
17
|
+
-- transaction aborts without changing data or constraint validity.
|
|
18
|
+
|
|
19
|
+
ALTER TABLE public.commerce_order_items
|
|
20
|
+
VALIDATE CONSTRAINT commerce_order_items_promoted_allocation_check;
|
|
21
|
+
|
|
22
|
+
ALTER TABLE public.commerce_order_draft_receipts
|
|
23
|
+
VALIDATE CONSTRAINT commerce_order_draft_receipts_invalidation_check;
|