@mohasinac/appkit 2.6.3 → 2.6.4
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/dist/_internal/server/features/account/data.js +2 -2
- package/dist/_internal/server/features/bundles/data.d.ts +26 -6
- package/dist/_internal/server/features/bundles/data.js +41 -6
- package/dist/_internal/server/features/bundles/index.d.ts +3 -2
- package/dist/_internal/server/features/bundles/index.js +4 -2
- package/dist/_internal/server/features/bundles/metadata.d.ts +20 -0
- package/dist/_internal/server/features/bundles/metadata.js +46 -0
- package/dist/_internal/server/features/bundles/og.d.ts +38 -0
- package/dist/_internal/server/features/bundles/og.js +122 -0
- package/dist/_internal/server/features/checkout/actions.js +192 -133
- package/dist/_internal/server/features/checkout/bundle-expansion.d.ts +57 -0
- package/dist/_internal/server/features/checkout/bundle-expansion.js +70 -0
- package/dist/_internal/server/features/checkout/prize-bundle-gates.d.ts +5 -15
- package/dist/_internal/server/features/checkout/prize-bundle-gates.js +5 -21
- package/dist/_internal/server/features/media/contextGuards.js +15 -1
- package/dist/_internal/server/features/payouts/actions.d.ts +19 -0
- package/dist/_internal/server/features/payouts/actions.js +38 -0
- package/dist/_internal/server/features/products/data.js +1 -2
- package/dist/_internal/server/features/raffle/actions.d.ts +11 -0
- package/dist/_internal/server/features/raffle/actions.js +31 -0
- package/dist/_internal/server/features/refunds/actions.d.ts +31 -0
- package/dist/_internal/server/features/refunds/actions.js +77 -0
- package/dist/_internal/server/jobs/core/adminAnalytics.d.ts +28 -0
- package/dist/_internal/server/jobs/core/adminAnalytics.js +98 -0
- package/dist/_internal/server/jobs/core/assignSpinPrize.d.ts +19 -0
- package/dist/_internal/server/jobs/core/assignSpinPrize.js +81 -0
- package/dist/_internal/server/jobs/core/auctionSettlement.d.ts +2 -0
- package/dist/_internal/server/jobs/core/auctionSettlement.js +70 -0
- package/dist/_internal/server/jobs/core/autoPayoutEligibility.d.ts +2 -0
- package/dist/_internal/server/jobs/core/autoPayoutEligibility.js +109 -0
- package/dist/_internal/server/jobs/core/bundleStockSync.d.ts +10 -0
- package/dist/_internal/server/jobs/core/bundleStockSync.js +71 -0
- package/dist/_internal/server/jobs/core/cartPrune.d.ts +2 -0
- package/dist/_internal/server/jobs/core/cartPrune.js +13 -0
- package/dist/_internal/server/jobs/core/cleanupRtdbEvents.d.ts +2 -0
- package/dist/_internal/server/jobs/core/cleanupRtdbEvents.js +45 -0
- package/dist/_internal/server/jobs/core/countersReconcile.d.ts +2 -0
- package/dist/_internal/server/jobs/core/countersReconcile.js +107 -0
- package/dist/_internal/server/jobs/core/couponExpiry.d.ts +2 -0
- package/dist/_internal/server/jobs/core/couponExpiry.js +13 -0
- package/dist/_internal/server/jobs/core/dailyDataCleanup.d.ts +2 -0
- package/dist/_internal/server/jobs/core/dailyDataCleanup.js +20 -0
- package/dist/_internal/server/jobs/core/index.d.ts +44 -0
- package/dist/_internal/server/jobs/core/index.js +47 -0
- package/dist/_internal/server/jobs/core/listingProcessor.d.ts +30 -0
- package/dist/_internal/server/jobs/core/listingProcessor.js +138 -0
- package/dist/_internal/server/jobs/core/mediaTmpCleanup.d.ts +14 -0
- package/dist/_internal/server/jobs/core/mediaTmpCleanup.js +68 -0
- package/dist/_internal/server/jobs/core/notificationPrune.d.ts +2 -0
- package/dist/_internal/server/jobs/core/notificationPrune.js +13 -0
- package/dist/_internal/server/jobs/core/offerExpiry.d.ts +2 -0
- package/dist/_internal/server/jobs/core/offerExpiry.js +50 -0
- package/dist/_internal/server/jobs/core/onBidPlaced.d.ts +15 -0
- package/dist/_internal/server/jobs/core/onBidPlaced.js +59 -0
- package/dist/_internal/server/jobs/core/onCategoryWrite.d.ts +14 -0
- package/dist/_internal/server/jobs/core/onCategoryWrite.js +134 -0
- package/dist/_internal/server/jobs/core/onOrderCreate.d.ts +18 -0
- package/dist/_internal/server/jobs/core/onOrderCreate.js +78 -0
- package/dist/_internal/server/jobs/core/onOrderStatusChange.d.ts +19 -0
- package/dist/_internal/server/jobs/core/onOrderStatusChange.js +139 -0
- package/dist/_internal/server/jobs/core/onProductStockChange.d.ts +23 -0
- package/dist/_internal/server/jobs/core/onProductStockChange.js +130 -0
- package/dist/_internal/server/jobs/core/onProductWrite.d.ts +12 -0
- package/dist/_internal/server/jobs/core/onProductWrite.js +94 -0
- package/dist/_internal/server/jobs/core/onReviewWrite.d.ts +7 -0
- package/dist/_internal/server/jobs/core/onReviewWrite.js +49 -0
- package/dist/_internal/server/jobs/core/onStoreWrite.d.ts +12 -0
- package/dist/_internal/server/jobs/core/onStoreWrite.js +7 -0
- package/dist/_internal/server/jobs/core/payoutBatch.d.ts +8 -0
- package/dist/_internal/server/jobs/core/payoutBatch.js +102 -0
- package/dist/_internal/server/jobs/core/pendingOrderTimeout.d.ts +2 -0
- package/dist/_internal/server/jobs/core/pendingOrderTimeout.js +27 -0
- package/dist/_internal/server/jobs/core/positionsReconcile.d.ts +7 -0
- package/dist/_internal/server/jobs/core/positionsReconcile.js +87 -0
- package/dist/_internal/server/jobs/core/prizeRevealClose.d.ts +2 -0
- package/dist/_internal/server/jobs/core/prizeRevealClose.js +22 -0
- package/dist/_internal/server/jobs/core/prizeRevealExpiry.d.ts +2 -0
- package/dist/_internal/server/jobs/core/prizeRevealExpiry.js +50 -0
- package/dist/_internal/server/jobs/core/prizeRevealOpen.d.ts +2 -0
- package/dist/_internal/server/jobs/core/prizeRevealOpen.js +56 -0
- package/dist/_internal/server/jobs/core/prizeRevealReminder.d.ts +2 -0
- package/dist/_internal/server/jobs/core/prizeRevealReminder.js +38 -0
- package/dist/_internal/server/jobs/core/productStatsSync.d.ts +8 -0
- package/dist/_internal/server/jobs/core/productStatsSync.js +36 -0
- package/dist/_internal/server/jobs/core/promotions.d.ts +12 -0
- package/dist/_internal/server/jobs/core/promotions.js +43 -0
- package/dist/_internal/server/jobs/core/storeAnalytics.d.ts +30 -0
- package/dist/_internal/server/jobs/core/storeAnalytics.js +109 -0
- package/dist/_internal/server/jobs/core/triggerEventRaffle.d.ts +19 -0
- package/dist/_internal/server/jobs/core/triggerEventRaffle.js +86 -0
- package/dist/_internal/server/jobs/core/weeklyPayoutEligibility.d.ts +6 -0
- package/dist/_internal/server/jobs/core/weeklyPayoutEligibility.js +85 -0
- package/dist/_internal/server/jobs/handlers/adminAnalytics.d.ts +2 -26
- package/dist/_internal/server/jobs/handlers/adminAnalytics.js +2 -98
- package/dist/_internal/server/jobs/handlers/assignSpinPrize.d.ts +1 -22
- package/dist/_internal/server/jobs/handlers/assignSpinPrize.js +2 -86
- package/dist/_internal/server/jobs/handlers/auctionSettlement.js +2 -70
- package/dist/_internal/server/jobs/handlers/autoPayoutEligibility.js +2 -110
- package/dist/_internal/server/jobs/handlers/bundleStockSync.d.ts +0 -16
- package/dist/_internal/server/jobs/handlers/bundleStockSync.js +2 -80
- package/dist/_internal/server/jobs/handlers/cartPrune.js +2 -13
- package/dist/_internal/server/jobs/handlers/cleanupRtdbEvents.js +2 -45
- package/dist/_internal/server/jobs/handlers/countersReconcile.js +2 -109
- package/dist/_internal/server/jobs/handlers/couponExpiry.js +2 -13
- package/dist/_internal/server/jobs/handlers/dailyDataCleanup.js +2 -20
- package/dist/_internal/server/jobs/handlers/listingProcessor.d.ts +3 -28
- package/dist/_internal/server/jobs/handlers/listingProcessor.js +3 -138
- package/dist/_internal/server/jobs/handlers/mediaTmpCleanup.d.ts +0 -12
- package/dist/_internal/server/jobs/handlers/mediaTmpCleanup.js +2 -69
- package/dist/_internal/server/jobs/handlers/notificationPrune.js +2 -13
- package/dist/_internal/server/jobs/handlers/offerExpiry.js +2 -50
- package/dist/_internal/server/jobs/handlers/onBidPlaced.d.ts +1 -10
- package/dist/_internal/server/jobs/handlers/onBidPlaced.js +2 -56
- package/dist/_internal/server/jobs/handlers/onCategoryWrite.d.ts +1 -8
- package/dist/_internal/server/jobs/handlers/onCategoryWrite.js +6 -134
- package/dist/_internal/server/jobs/handlers/onOrderCreate.d.ts +1 -12
- package/dist/_internal/server/jobs/handlers/onOrderCreate.js +2 -76
- package/dist/_internal/server/jobs/handlers/onOrderStatusChange.d.ts +1 -12
- package/dist/_internal/server/jobs/handlers/onOrderStatusChange.js +2 -139
- package/dist/_internal/server/jobs/handlers/onProductStockChange.d.ts +0 -13
- package/dist/_internal/server/jobs/handlers/onProductStockChange.js +7 -134
- package/dist/_internal/server/jobs/handlers/onProductWrite.d.ts +1 -6
- package/dist/_internal/server/jobs/handlers/onProductWrite.js +6 -106
- package/dist/_internal/server/jobs/handlers/onReviewWrite.js +2 -49
- package/dist/_internal/server/jobs/handlers/onStoreWrite.d.ts +1 -8
- package/dist/_internal/server/jobs/handlers/onStoreWrite.js +7 -8
- package/dist/_internal/server/jobs/handlers/payoutBatch.d.ts +0 -9
- package/dist/_internal/server/jobs/handlers/payoutBatch.js +2 -104
- package/dist/_internal/server/jobs/handlers/pendingOrderTimeout.d.ts +0 -6
- package/dist/_internal/server/jobs/handlers/pendingOrderTimeout.js +2 -33
- package/dist/_internal/server/jobs/handlers/positionsReconcile.d.ts +0 -5
- package/dist/_internal/server/jobs/handlers/positionsReconcile.js +2 -87
- package/dist/_internal/server/jobs/handlers/prizeRevealClose.d.ts +0 -7
- package/dist/_internal/server/jobs/handlers/prizeRevealClose.js +2 -29
- package/dist/_internal/server/jobs/handlers/prizeRevealExpiry.d.ts +0 -8
- package/dist/_internal/server/jobs/handlers/prizeRevealExpiry.js +2 -58
- package/dist/_internal/server/jobs/handlers/prizeRevealOpen.d.ts +0 -8
- package/dist/_internal/server/jobs/handlers/prizeRevealOpen.js +2 -65
- package/dist/_internal/server/jobs/handlers/prizeRevealReminder.d.ts +0 -7
- package/dist/_internal/server/jobs/handlers/prizeRevealReminder.js +2 -45
- package/dist/_internal/server/jobs/handlers/productStatsSync.d.ts +0 -6
- package/dist/_internal/server/jobs/handlers/productStatsSync.js +2 -36
- package/dist/_internal/server/jobs/handlers/promotions.d.ts +2 -10
- package/dist/_internal/server/jobs/handlers/promotions.js +2 -43
- package/dist/_internal/server/jobs/handlers/storeAnalytics.d.ts +2 -28
- package/dist/_internal/server/jobs/handlers/storeAnalytics.js +2 -109
- package/dist/_internal/server/jobs/handlers/triggerEventRaffle.d.ts +1 -28
- package/dist/_internal/server/jobs/handlers/triggerEventRaffle.js +2 -94
- package/dist/_internal/server/jobs/handlers/weeklyPayoutEligibility.d.ts +0 -6
- package/dist/_internal/server/jobs/handlers/weeklyPayoutEligibility.js +2 -87
- package/dist/_internal/server/jobs/index.d.ts +1 -0
- package/dist/_internal/server/jobs/index.js +1 -0
- package/dist/_internal/server/jobs/runtime/adapters/firebase.js +21 -0
- package/dist/_internal/shared/actions/action-registry.d.ts +84 -0
- package/dist/_internal/shared/actions/action-registry.js +160 -0
- package/dist/_internal/shared/checkout/rules/_defaults.d.ts +3 -0
- package/dist/_internal/shared/checkout/rules/_defaults.js +22 -0
- package/dist/_internal/shared/checkout/rules/_limits.d.ts +19 -0
- package/dist/_internal/shared/checkout/rules/_limits.js +19 -0
- package/dist/_internal/shared/checkout/rules/_registry.d.ts +44 -0
- package/dist/_internal/shared/checkout/rules/_registry.js +87 -0
- package/dist/_internal/shared/checkout/rules/auction.rule.d.ts +2 -0
- package/dist/_internal/shared/checkout/rules/auction.rule.js +10 -0
- package/dist/_internal/shared/checkout/rules/bundle.rule.d.ts +11 -0
- package/dist/_internal/shared/checkout/rules/bundle.rule.js +6 -0
- package/dist/_internal/shared/checkout/rules/classified.rule.d.ts +9 -0
- package/dist/_internal/shared/checkout/rules/classified.rule.js +9 -0
- package/dist/_internal/shared/checkout/rules/digital-code.rule.d.ts +8 -0
- package/dist/_internal/shared/checkout/rules/digital-code.rule.js +6 -0
- package/dist/_internal/shared/checkout/rules/index.d.ts +12 -0
- package/dist/_internal/shared/checkout/rules/index.js +12 -0
- package/dist/_internal/shared/checkout/rules/live.rule.d.ts +10 -0
- package/dist/_internal/shared/checkout/rules/live.rule.js +6 -0
- package/dist/_internal/shared/checkout/rules/offer.rule.d.ts +2 -0
- package/dist/_internal/shared/checkout/rules/offer.rule.js +9 -0
- package/dist/_internal/shared/checkout/rules/preorder.rule.d.ts +2 -0
- package/dist/_internal/shared/checkout/rules/preorder.rule.js +28 -0
- package/dist/_internal/shared/checkout/rules/prize-draw.rule.d.ts +2 -0
- package/dist/_internal/shared/checkout/rules/prize-draw.rule.js +65 -0
- package/dist/_internal/shared/checkout/rules/standard.rule.d.ts +2 -0
- package/dist/_internal/shared/checkout/rules/standard.rule.js +5 -0
- package/dist/_internal/shared/checkout/rules/types.d.ts +125 -0
- package/dist/_internal/shared/checkout/rules/types.js +13 -0
- package/dist/_internal/shared/features/cart/schema.d.ts +6 -6
- package/dist/_internal/shared/features/categories/bundle-copy.d.ts +123 -0
- package/dist/_internal/shared/features/categories/bundle-copy.js +134 -0
- package/dist/_internal/shared/features/categories/bundle-schemas.d.ts +318 -0
- package/dist/_internal/shared/features/categories/bundle-schemas.js +55 -0
- package/dist/_internal/shared/features/orders/refund-copy.d.ts +36 -0
- package/dist/_internal/shared/features/orders/refund-copy.js +40 -0
- package/dist/_internal/shared/features/orders/schema.d.ts +4 -4
- package/dist/_internal/shared/features/products/schema.d.ts +8 -8
- package/dist/_internal/shared/listing-types/_registry.d.ts +27 -0
- package/dist/_internal/shared/listing-types/_registry.js +8 -0
- package/dist/_internal/shared/listing-types/action-tracker.d.ts +41 -0
- package/dist/_internal/shared/listing-types/action-tracker.js +70 -0
- package/dist/_internal/shared/listing-types/capabilities.js +25 -0
- package/dist/_internal/shared/listing-types/cart-shipping.d.ts +37 -0
- package/dist/_internal/shared/listing-types/cart-shipping.js +46 -0
- package/dist/_internal/shared/listing-types/classified/config.d.ts +7 -0
- package/dist/_internal/shared/listing-types/classified/config.js +8 -0
- package/dist/_internal/shared/listing-types/classified/ctas.d.ts +1 -0
- package/dist/_internal/shared/listing-types/classified/ctas.js +3 -0
- package/dist/_internal/shared/listing-types/classified/og.d.ts +1 -0
- package/dist/_internal/shared/listing-types/classified/og.js +1 -0
- package/dist/_internal/shared/listing-types/classified/schema.d.ts +1 -0
- package/dist/_internal/shared/listing-types/classified/schema.js +1 -0
- package/dist/_internal/shared/listing-types/classified/seed-factory.d.ts +1 -0
- package/dist/_internal/shared/listing-types/classified/seed-factory.js +1 -0
- package/dist/_internal/shared/listing-types/digital-code/config.d.ts +7 -0
- package/dist/_internal/shared/listing-types/digital-code/config.js +8 -0
- package/dist/_internal/shared/listing-types/digital-code/ctas.d.ts +1 -0
- package/dist/_internal/shared/listing-types/digital-code/ctas.js +3 -0
- package/dist/_internal/shared/listing-types/digital-code/og.d.ts +1 -0
- package/dist/_internal/shared/listing-types/digital-code/og.js +1 -0
- package/dist/_internal/shared/listing-types/digital-code/schema.d.ts +1 -0
- package/dist/_internal/shared/listing-types/digital-code/schema.js +1 -0
- package/dist/_internal/shared/listing-types/digital-code/seed-factory.d.ts +1 -0
- package/dist/_internal/shared/listing-types/digital-code/seed-factory.js +1 -0
- package/dist/_internal/shared/listing-types/feature-flags.d.ts +34 -0
- package/dist/_internal/shared/listing-types/feature-flags.js +45 -0
- package/dist/_internal/shared/listing-types/live/config.d.ts +7 -0
- package/dist/_internal/shared/listing-types/live/config.js +8 -0
- package/dist/_internal/shared/listing-types/live/ctas.d.ts +1 -0
- package/dist/_internal/shared/listing-types/live/ctas.js +3 -0
- package/dist/_internal/shared/listing-types/live/og.d.ts +1 -0
- package/dist/_internal/shared/listing-types/live/og.js +1 -0
- package/dist/_internal/shared/listing-types/live/schema.d.ts +1 -0
- package/dist/_internal/shared/listing-types/live/schema.js +1 -0
- package/dist/_internal/shared/listing-types/live/seed-factory.d.ts +1 -0
- package/dist/_internal/shared/listing-types/live/seed-factory.js +1 -0
- package/dist/_internal/shared/media/limits.js +8 -0
- package/dist/client.d.ts +6 -1
- package/dist/client.js +14 -1
- package/dist/configs/next.js +7 -0
- package/dist/constants/api-endpoints.d.ts +6 -0
- package/dist/constants/api-endpoints.js +4 -0
- package/dist/core/hooks/useSyncManager.js +13 -1
- package/dist/core/unit-of-work.d.ts +1 -1
- package/dist/core/unit-of-work.js +2 -2
- package/dist/features/account/actions/address-actions.d.ts +1 -1
- package/dist/features/account/actions/address-actions.js +15 -7
- package/dist/features/account/schemas/firestore.d.ts +8 -43
- package/dist/features/account/schemas/firestore.js +8 -54
- package/dist/features/account/schemas/index.d.ts +6 -6
- package/dist/features/account/server.d.ts +1 -1
- package/dist/features/account/server.js +3 -1
- package/dist/features/addresses/index.d.ts +2 -0
- package/dist/features/addresses/index.js +2 -0
- package/dist/features/addresses/repository/addresses.repository.d.ts +29 -0
- package/dist/features/addresses/repository/addresses.repository.js +157 -0
- package/dist/features/addresses/schemas/firestore.d.ts +53 -0
- package/dist/features/addresses/schemas/firestore.js +68 -0
- package/dist/features/{bundles → addresses}/schemas/index.d.ts +0 -1
- package/dist/features/{bundles → addresses}/schemas/index.js +0 -1
- package/dist/features/addresses/server.d.ts +2 -0
- package/dist/features/addresses/server.js +2 -0
- package/dist/features/admin/components/AdminAllEventEntriesView.js +4 -3
- package/dist/features/admin/components/AdminBidsView.js +4 -3
- package/dist/features/admin/components/AdminBlogView.js +8 -3
- package/dist/features/admin/components/AdminBundleEditorView.d.ts +9 -0
- package/dist/features/admin/components/AdminBundleEditorView.js +209 -0
- package/dist/features/admin/components/AdminBundlesView.d.ts +9 -0
- package/dist/features/admin/components/AdminBundlesView.js +59 -0
- package/dist/features/admin/components/AdminCartsView.js +13 -3
- package/dist/features/admin/components/AdminContactView.js +4 -3
- package/dist/features/admin/components/AdminCouponsView.js +32 -13
- package/dist/features/admin/components/AdminNewsletterView.js +4 -3
- package/dist/features/admin/components/AdminOrdersView.js +15 -7
- package/dist/features/admin/components/AdminPayoutsView.js +4 -3
- package/dist/features/admin/components/AdminProductsView.js +6 -5
- package/dist/features/admin/components/AdminReviewsView.js +5 -4
- package/dist/features/admin/components/AdminStoresView.js +4 -3
- package/dist/features/admin/components/AdminUsersView.js +15 -5
- package/dist/features/admin/components/AdminWishlistsView.js +10 -1
- package/dist/features/admin/components/DataTable.d.ts +5 -1
- package/dist/features/admin/components/DataTable.js +24 -20
- package/dist/features/admin/components/index.d.ts +4 -0
- package/dist/features/admin/components/index.js +3 -0
- package/dist/features/admin/constants/filter-tabs.d.ts +401 -0
- package/dist/features/admin/constants/filter-tabs.js +200 -0
- package/dist/features/admin/hooks/useAdminListingData.d.ts +1 -0
- package/dist/features/admin/hooks/useAdminListingData.js +1 -0
- package/dist/features/admin/schemas/firestore.d.ts +15 -0
- package/dist/features/admin/schemas/firestore.js +18 -0
- package/dist/features/admin/types/product.types.d.ts +2 -2
- package/dist/features/auctions/components/AuctionCard.d.ts +4 -1
- package/dist/features/auctions/components/AuctionCard.js +5 -3
- package/dist/features/auctions/components/AuctionDetailPageView.js +1 -1
- package/dist/features/auctions/components/MarketplaceAuctionCard.d.ts +2 -2
- package/dist/features/auctions/components/PlaceBidFormClient.js +12 -2
- package/dist/features/auctions/schemas/index.d.ts +2 -2
- package/dist/features/auth/index.d.ts +1 -0
- package/dist/features/auth/index.js +1 -0
- package/dist/features/auth/role-predicates.d.ts +16 -0
- package/dist/features/auth/role-predicates.js +15 -0
- package/dist/features/blog/components/BlogFeaturedCard.d.ts +4 -1
- package/dist/features/blog/components/BlogFeaturedCard.js +5 -3
- package/dist/features/cart/actions/cart-actions.d.ts +1 -0
- package/dist/features/cart/actions/cart-actions.js +16 -0
- package/dist/features/cart/components/CartView.d.ts +19 -1
- package/dist/features/cart/components/CartView.js +2 -2
- package/dist/features/cart/components/ShippingPicker.d.ts +13 -0
- package/dist/features/cart/components/ShippingPicker.js +48 -0
- package/dist/features/cart/components/index.d.ts +3 -1
- package/dist/features/cart/components/index.js +1 -0
- package/dist/features/cart/hooks/useCartCount.js +7 -1
- package/dist/features/cart/repository/cart.repository.d.ts +1 -0
- package/dist/features/cart/repository/cart.repository.js +35 -0
- package/dist/features/cart/schemas/firestore.d.ts +27 -2
- package/dist/features/categories/components/BundleAddToCartCta.d.ts +16 -0
- package/dist/features/categories/components/BundleAddToCartCta.js +54 -0
- package/dist/features/categories/components/BundleBuyNowCta.d.ts +8 -0
- package/dist/features/categories/components/BundleBuyNowCta.js +37 -0
- package/dist/features/categories/components/BundleDetailView.d.ts +22 -0
- package/dist/features/categories/components/BundleDetailView.js +31 -0
- package/dist/features/categories/components/BundleDynamicRuleEditor.d.ts +18 -0
- package/dist/features/categories/components/BundleDynamicRuleEditor.js +60 -0
- package/dist/features/categories/components/BundleItemsPicker.d.ts +26 -0
- package/dist/features/categories/components/BundleItemsPicker.js +135 -0
- package/dist/features/categories/components/BundlesListView.d.ts +13 -0
- package/dist/features/categories/components/BundlesListView.js +27 -0
- package/dist/features/categories/components/index.d.ts +12 -0
- package/dist/features/categories/components/index.js +9 -0
- package/dist/features/checkout/actions/checkout-actions.js +10 -3
- package/dist/features/events/components/AdminEventEditorView.js +108 -2
- package/dist/features/events/components/AdminEventsView.js +16 -4
- package/dist/features/events/components/EventCard.d.ts +4 -1
- package/dist/features/events/components/EventCard.js +7 -3
- package/dist/features/events/components/EventRaffleWinnerView.d.ts +21 -0
- package/dist/features/events/components/EventRaffleWinnerView.js +18 -0
- package/dist/features/events/components/SpinWheelView.d.ts +27 -0
- package/dist/features/events/components/SpinWheelView.js +64 -0
- package/dist/features/events/components/index.d.ts +4 -0
- package/dist/features/events/components/index.js +2 -0
- package/dist/features/events/schemas/firestore.d.ts +27 -1
- package/dist/features/events/schemas/firestore.js +7 -0
- package/dist/features/events/types/index.d.ts +27 -1
- package/dist/features/homepage/components/FeaturedBundlesSection.d.ts +16 -0
- package/dist/features/homepage/components/FeaturedBundlesSection.js +24 -0
- package/dist/features/homepage/components/MarketplaceHomepageView.js +4 -1
- package/dist/features/homepage/components/WhatsAppCommunitySection.js +2 -2
- package/dist/features/homepage/lib/section-renderer.d.ts +2 -0
- package/dist/features/homepage/lib/section-renderer.js +5 -5
- package/dist/features/layout/NavbarLayout.d.ts +3 -1
- package/dist/features/layout/NavbarLayout.js +32 -3
- package/dist/features/layout/TitleBarLayout.d.ts +6 -3
- package/dist/features/layout/TitleBarLayout.js +22 -7
- package/dist/features/media/upload/MediaUploadField.d.ts +15 -1
- package/dist/features/media/upload/MediaUploadField.js +22 -1
- package/dist/features/orders/components/MarketplaceOrderCard.js +4 -2
- package/dist/features/orders/components/OrderSiblingPayments.d.ts +8 -0
- package/dist/features/orders/components/OrderSiblingPayments.js +16 -0
- package/dist/features/orders/components/RefundHistoryTable.d.ts +6 -0
- package/dist/features/orders/components/RefundHistoryTable.js +23 -0
- package/dist/features/orders/components/RefundRequestView.d.ts +8 -0
- package/dist/features/orders/components/RefundRequestView.js +42 -0
- package/dist/features/orders/components/index.d.ts +6 -0
- package/dist/features/orders/components/index.js +3 -0
- package/dist/features/orders/index.d.ts +1 -0
- package/dist/features/orders/index.js +2 -0
- package/dist/features/orders/repository/orders.repository.d.ts +20 -1
- package/dist/features/orders/repository/orders.repository.js +30 -1
- package/dist/features/orders/schemas/firestore.d.ts +63 -13
- package/dist/features/orders/schemas/firestore.js +5 -5
- package/dist/features/orders/schemas/index.d.ts +4 -4
- package/dist/features/orders/types/index.d.ts +12 -2
- package/dist/features/orders/utils/bundle-grouping.d.ts +48 -0
- package/dist/features/orders/utils/bundle-grouping.js +54 -0
- package/dist/features/orders/utils/order-splitter.d.ts +9 -10
- package/dist/features/orders/utils/order-splitter.js +24 -30
- package/dist/features/payments/repository/payout.repository.d.ts +17 -1
- package/dist/features/payments/repository/payout.repository.js +47 -0
- package/dist/features/payments/schemas/firestore.d.ts +26 -0
- package/dist/features/pre-orders/components/PreorderCard.d.ts +4 -1
- package/dist/features/pre-orders/components/PreorderCard.js +8 -6
- package/dist/features/products/actions/product-actions.d.ts +1 -1
- package/dist/features/products/actions/product-actions.js +1 -1
- package/dist/features/products/components/CompareOverlay.d.ts +2 -2
- package/dist/features/products/components/CompareOverlay.js +4 -4
- package/dist/features/products/components/InteractiveProductCard.js +7 -12
- package/dist/features/products/components/NonRefundableConsentModal.d.ts +1 -1
- package/dist/features/products/components/NonRefundableConsentModal.js +0 -10
- package/dist/features/products/components/ProductGrid.js +28 -12
- package/dist/features/products/components/ShowGroupSection.js +3 -3
- package/dist/features/products/components/SublistingCarouselSection.js +2 -2
- package/dist/features/products/components/index.d.ts +0 -4
- package/dist/features/products/components/index.js +5 -2
- package/dist/features/products/index.d.ts +1 -1
- package/dist/features/products/index.js +3 -1
- package/dist/features/products/repository/products.repository.js +4 -0
- package/dist/features/products/schemas/catalog-product.d.ts +70 -0
- package/dist/features/products/schemas/catalog-product.js +50 -0
- package/dist/features/products/schemas/firestore.d.ts +110 -2
- package/dist/features/products/schemas/firestore.js +30 -0
- package/dist/features/products/schemas/index.d.ts +8 -8
- package/dist/features/products/types/index.d.ts +1 -1
- package/dist/features/products/utils/listing-type.d.ts +9 -0
- package/dist/features/products/utils/listing-type.js +4 -0
- package/dist/features/promotions/actions/coupon-actions.d.ts +2 -2
- package/dist/features/promotions/actions/coupon-actions.js +1 -1
- package/dist/features/promotions/components/CouponCard.d.ts +28 -10
- package/dist/features/promotions/components/CouponCard.js +116 -14
- package/dist/features/promotions/hooks/useCouponValidate.d.ts +1 -1
- package/dist/features/promotions/repository/coupons.repository.d.ts +1 -1
- package/dist/features/reviews/schemas/index.d.ts +2 -2
- package/dist/features/search/schemas/index.d.ts +2 -2
- package/dist/features/search/types/index.d.ts +2 -2
- package/dist/features/seller/components/SellerAuctionsView.js +4 -5
- package/dist/features/seller/components/SellerBidsView.js +5 -13
- package/dist/features/seller/components/SellerCouponsView.js +31 -67
- package/dist/features/seller/components/SellerOffersView.js +4 -5
- package/dist/features/seller/components/SellerOrdersView.js +4 -5
- package/dist/features/seller/components/SellerPayoutsView.js +4 -5
- package/dist/features/seller/components/SellerProductsView.js +4 -8
- package/dist/features/seller/schemas/index.d.ts +2 -2
- package/dist/features/stores/actions/store-address-actions.d.ts +10 -6
- package/dist/features/stores/actions/store-address-actions.js +8 -7
- package/dist/features/stores/components/InteractiveStoreCard.js +3 -10
- package/dist/features/stores/components/StoreDetailLayoutView.js +20 -5
- package/dist/features/stores/schemas/firestore.d.ts +45 -38
- package/dist/features/stores/schemas/firestore.js +2 -49
- package/dist/features/stores/server.d.ts +0 -1
- package/dist/features/stores/server.js +2 -1
- package/dist/features/wishlist/hooks/useWishlistCount.d.ts +7 -9
- package/dist/features/wishlist/hooks/useWishlistCount.js +67 -86
- package/dist/features/wishlist/types/index.d.ts +1 -1
- package/dist/index.d.ts +50 -9
- package/dist/index.js +57 -11
- package/dist/next/routing/route-map.d.ts +6 -0
- package/dist/next/routing/route-map.js +4 -0
- package/dist/providers/db-firebase/admin.d.ts +6 -2
- package/dist/providers/db-firebase/admin.js +23 -0
- package/dist/repositories/index.d.ts +1 -2
- package/dist/repositories/index.js +5 -2
- package/dist/security/authorization.d.ts +2 -1
- package/dist/security/authorization.js +3 -2
- package/dist/seed/actions/demo-seed-actions.d.ts +1 -1
- package/dist/seed/bids-seed-data.d.ts +2 -2
- package/dist/seed/bids-seed-data.js +77 -2
- package/dist/seed/cart-seed-data.d.ts +10 -10
- package/dist/seed/cart-seed-data.js +15 -15
- package/dist/seed/events-seed-data.js +77 -0
- package/dist/seed/manifest.js +16 -8
- package/dist/seed/orders-seed-data.js +41 -2
- package/dist/seed/payouts-seed-data.js +18 -2
- package/dist/seed/stores-seed-data.js +55 -0
- package/dist/server-entry.d.ts +1 -0
- package/dist/server-entry.js +2 -0
- package/dist/server.d.ts +4 -0
- package/dist/server.js +8 -0
- package/dist/tailwind-utilities.css +1 -1
- package/dist/ui/components/FilterChipGroup.d.ts +39 -0
- package/dist/ui/components/FilterChipGroup.js +15 -0
- package/dist/ui/components/HorizontalScroller.js +32 -7
- package/dist/ui/components/HorizontalScroller.style.css +6 -0
- package/dist/ui/components/SiteLogo.js +1 -1
- package/dist/ui/index.d.ts +2 -0
- package/dist/ui/index.js +2 -0
- package/dist/utils/id-generators.d.ts +11 -0
- package/dist/utils/id-generators.js +16 -0
- package/package.json +1 -1
- package/dist/_internal/shared/features/bundles/config.d.ts +0 -6
- package/dist/_internal/shared/features/bundles/config.js +0 -6
- package/dist/_internal/shared/schema-versions.d.ts +0 -76
- package/dist/_internal/shared/schema-versions.js +0 -82
- package/dist/features/account/migrations.d.ts +0 -2
- package/dist/features/account/migrations.js +0 -10
- package/dist/features/admin/migrations.d.ts +0 -2
- package/dist/features/admin/migrations.js +0 -10
- package/dist/features/auctions/migrations.d.ts +0 -2
- package/dist/features/auctions/migrations.js +0 -10
- package/dist/features/auth/migrations.d.ts +0 -2
- package/dist/features/auth/migrations.js +0 -10
- package/dist/features/blog/migrations.d.ts +0 -2
- package/dist/features/blog/migrations.js +0 -10
- package/dist/features/brands/actions/brand-actions.d.ts +0 -2
- package/dist/features/brands/actions/brand-actions.js +0 -5
- package/dist/features/brands/index.d.ts +0 -3
- package/dist/features/brands/index.js +0 -3
- package/dist/features/brands/migrations.d.ts +0 -2
- package/dist/features/brands/migrations.js +0 -10
- package/dist/features/brands/repository/brands.repository.d.ts +0 -13
- package/dist/features/brands/repository/brands.repository.js +0 -60
- package/dist/features/brands/schemas/index.d.ts +0 -33
- package/dist/features/brands/schemas/index.js +0 -15
- package/dist/features/brands/server.d.ts +0 -7
- package/dist/features/brands/server.js +0 -7
- package/dist/features/bundles/components/AdminBundleEditorView.d.ts +0 -8
- package/dist/features/bundles/components/AdminBundleEditorView.js +0 -7
- package/dist/features/bundles/components/BundleDetailPageView.d.ts +0 -9
- package/dist/features/bundles/components/BundleDetailPageView.js +0 -45
- package/dist/features/bundles/components/BundleForm.d.ts +0 -12
- package/dist/features/bundles/components/BundleForm.js +0 -126
- package/dist/features/bundles/components/BundleItemsPicker.d.ts +0 -9
- package/dist/features/bundles/components/BundleItemsPicker.js +0 -77
- package/dist/features/bundles/components/BundlesByCategoryListing.d.ts +0 -6
- package/dist/features/bundles/components/BundlesByCategoryListing.js +0 -50
- package/dist/features/bundles/components/BundlesListingView.d.ts +0 -17
- package/dist/features/bundles/components/BundlesListingView.js +0 -50
- package/dist/features/bundles/components/FeaturedBundlesSection.d.ts +0 -5
- package/dist/features/bundles/components/FeaturedBundlesSection.js +0 -55
- package/dist/features/bundles/components/SellerBundleCreateView.d.ts +0 -8
- package/dist/features/bundles/components/SellerBundleCreateView.js +0 -7
- package/dist/features/bundles/components/SellerBundleEditView.d.ts +0 -9
- package/dist/features/bundles/components/SellerBundleEditView.js +0 -7
- package/dist/features/bundles/components/index.d.ts +0 -18
- package/dist/features/bundles/components/index.js +0 -9
- package/dist/features/bundles/constants/index.d.ts +0 -32
- package/dist/features/bundles/constants/index.js +0 -35
- package/dist/features/bundles/index.d.ts +0 -2
- package/dist/features/bundles/index.js +0 -2
- package/dist/features/bundles/migrations.d.ts +0 -2
- package/dist/features/bundles/migrations.js +0 -10
- package/dist/features/bundles/repository/bundles.repository.d.ts +0 -36
- package/dist/features/bundles/repository/bundles.repository.js +0 -148
- package/dist/features/bundles/repository/index.d.ts +0 -1
- package/dist/features/bundles/repository/index.js +0 -1
- package/dist/features/bundles/schemas/firestore.d.ts +0 -88
- package/dist/features/bundles/schemas/firestore.js +0 -29
- package/dist/features/bundles/schemas/zod.d.ts +0 -377
- package/dist/features/bundles/schemas/zod.js +0 -71
- package/dist/features/cart/migrations.d.ts +0 -2
- package/dist/features/cart/migrations.js +0 -10
- package/dist/features/categories/migrations.d.ts +0 -2
- package/dist/features/categories/migrations.js +0 -10
- package/dist/features/events/migrations.d.ts +0 -2
- package/dist/features/events/migrations.js +0 -10
- package/dist/features/faq/migrations.d.ts +0 -2
- package/dist/features/faq/migrations.js +0 -10
- package/dist/features/grouped/migrations.d.ts +0 -2
- package/dist/features/grouped/migrations.js +0 -10
- package/dist/features/history/migrations.d.ts +0 -2
- package/dist/features/history/migrations.js +0 -10
- package/dist/features/messages/migrations.d.ts +0 -2
- package/dist/features/messages/migrations.js +0 -10
- package/dist/features/orders/migrations.d.ts +0 -2
- package/dist/features/orders/migrations.js +0 -10
- package/dist/features/payments/migrations.d.ts +0 -2
- package/dist/features/payments/migrations.js +0 -10
- package/dist/features/products/migrations.d.ts +0 -2
- package/dist/features/products/migrations.js +0 -10
- package/dist/features/products/repository/sublisting-categories.repository.d.ts +0 -16
- package/dist/features/products/repository/sublisting-categories.repository.js +0 -126
- package/dist/features/products/schemas/sublisting-categories.d.ts +0 -45
- package/dist/features/products/schemas/sublisting-categories.js +0 -16
- package/dist/features/promotions/migrations.d.ts +0 -2
- package/dist/features/promotions/migrations.js +0 -10
- package/dist/features/reviews/migrations.d.ts +0 -2
- package/dist/features/reviews/migrations.js +0 -10
- package/dist/features/scams/migrations.d.ts +0 -2
- package/dist/features/scams/migrations.js +0 -10
- package/dist/features/seller/migrations.d.ts +0 -2
- package/dist/features/seller/migrations.js +0 -10
- package/dist/features/stores/migrations.d.ts +0 -2
- package/dist/features/stores/migrations.js +0 -10
- package/dist/features/sublisting/migrations.d.ts +0 -2
- package/dist/features/sublisting/migrations.js +0 -10
- package/dist/features/sublisting/schemas/firestore.d.ts +0 -32
- package/dist/features/sublisting/schemas/firestore.js +0 -19
- package/dist/features/support/migrations.d.ts +0 -2
- package/dist/features/support/migrations.js +0 -10
- package/dist/features/wishlist/migrations.d.ts +0 -2
- package/dist/features/wishlist/migrations.js +0 -10
- package/dist/seed/_bundle-constants.d.ts +0 -14
- package/dist/seed/_bundle-constants.js +0 -14
- package/dist/seed/anime-figures-seed-data.d.ts +0 -8
- package/dist/seed/anime-figures-seed-data.js +0 -1033
- package/dist/seed/beyblade-seed-data.d.ts +0 -7
- package/dist/seed/beyblade-seed-data.js +0 -1129
- package/dist/seed/brands-seed-data.d.ts +0 -7
- package/dist/seed/brands-seed-data.js +0 -410
- package/dist/seed/bundles-seed-data.d.ts +0 -13
- package/dist/seed/bundles-seed-data.js +0 -229
- package/dist/seed/cosplay-accessories-seed-data.d.ts +0 -8
- package/dist/seed/cosplay-accessories-seed-data.js +0 -647
- package/dist/seed/hot-wheels-seed-data.d.ts +0 -7
- package/dist/seed/hot-wheels-seed-data.js +0 -1612
- package/dist/seed/letitrip-official-seed-data.d.ts +0 -8
- package/dist/seed/letitrip-official-seed-data.js +0 -399
- package/dist/seed/pokemon-carousel-slides-seed-data.d.ts +0 -8
- package/dist/seed/pokemon-carousel-slides-seed-data.js +0 -322
- package/dist/seed/pokemon-categories-seed-data.d.ts +0 -24
- package/dist/seed/pokemon-categories-seed-data.js +0 -547
- package/dist/seed/pokemon-coupons-seed-data.d.ts +0 -6
- package/dist/seed/pokemon-coupons-seed-data.js +0 -465
- package/dist/seed/pokemon-homepage-sections-seed-data.d.ts +0 -7
- package/dist/seed/pokemon-homepage-sections-seed-data.js +0 -241
- package/dist/seed/pokemon-products-seed-data.d.ts +0 -9
- package/dist/seed/pokemon-products-seed-data.js +0 -1791
- package/dist/seed/pokemon-seed-bundle.d.ts +0 -47
- package/dist/seed/pokemon-seed-bundle.js +0 -71
- package/dist/seed/pokemon-stores-seed-data.d.ts +0 -6
- package/dist/seed/pokemon-stores-seed-data.js +0 -179
- package/dist/seed/pokemon-users-seed-data.d.ts +0 -6
- package/dist/seed/pokemon-users-seed-data.js +0 -521
- package/dist/seed/products-seed-data.d.ts +0 -6
- package/dist/seed/products-seed-data.js +0 -2530
- package/dist/seed/retro-gaming-seed-data.d.ts +0 -8
- package/dist/seed/retro-gaming-seed-data.js +0 -801
- package/dist/seed/server.d.ts +0 -2
- package/dist/seed/server.js +0 -7
- package/dist/seed/sublisting-categories-seed-data.d.ts +0 -7
- package/dist/seed/sublisting-categories-seed-data.js +0 -159
- package/dist/seed/transformers-seed-data.d.ts +0 -7
- package/dist/seed/transformers-seed-data.js +0 -530
|
@@ -14,15 +14,18 @@ import { sendOrderConfirmationEmail } from "../../../../features/contact/server"
|
|
|
14
14
|
import { splitCartIntoOrderGroups } from "../../../../features/orders/index";
|
|
15
15
|
import { resolveDate } from "../../../../utils";
|
|
16
16
|
import { getAdminDb, getAdminRealtimeDb, RTDB_PATHS, } from "../../../../providers/db-firebase";
|
|
17
|
-
import { PRODUCT_COLLECTION
|
|
17
|
+
import { PRODUCT_COLLECTION } from "../../../../features/products/schemas/firestore";
|
|
18
18
|
import { CART_COLLECTION } from "../../../../features/cart/schemas/index";
|
|
19
|
+
// S-SBUNI-RULES 2026-05-13 — bundle cart-line stock fan-out helpers.
|
|
20
|
+
import { getCartItemMemberIds, getExpandedDecrements, validateCartItemStock, } from "./bundle-expansion";
|
|
19
21
|
import { consentOtpRef, consentOtpRateLimitRef, CONSENT_OTP_MAX_BYPASS_CREDITS, } from "../../../../features/auth/server";
|
|
20
22
|
import { OrderStatusValues, PaymentStatusValues, PaymentMethodValues, } from "../../../../features/orders/schemas/index";
|
|
21
23
|
import { getDefaultCurrency } from "../../../../core/index";
|
|
22
24
|
import { verifyPaymentSignatureWithKeys, fetchRazorpayOrder, paiseToRupees, } from "../../../../providers/payment-razorpay/index";
|
|
23
25
|
import { CHECKOUT_DEFAULT_COMMISSIONS } from "../../../shared/features/checkout/config";
|
|
24
26
|
import { formatShippingAddress } from "./data";
|
|
25
|
-
import { enforceMaxPerUserForCart,
|
|
27
|
+
import { enforceMaxPerUserForCart, computePrizeRevealDeadline, } from "./prize-bundle-gates";
|
|
28
|
+
import { getListingRule, runSyncPreflight, } from "../../../shared/checkout/rules";
|
|
26
29
|
/**
|
|
27
30
|
* Fire-and-forget in-app notifications when an order is created.
|
|
28
31
|
*
|
|
@@ -89,7 +92,10 @@ export async function createCheckoutOrderAction(input) {
|
|
|
89
92
|
if (cartItems.length === 0) {
|
|
90
93
|
throw new ValidationError(ERROR_MESSAGES.CHECKOUT.CART_EMPTY);
|
|
91
94
|
}
|
|
92
|
-
const
|
|
95
|
+
const addressDoc = await unitOfWork.addresses.findById(addressId);
|
|
96
|
+
const address = addressDoc && addressDoc.ownerType === "user" && addressDoc.ownerId === uid
|
|
97
|
+
? addressDoc
|
|
98
|
+
: null;
|
|
93
99
|
if (!address) {
|
|
94
100
|
failedCheckoutRepository
|
|
95
101
|
.logCheckout(uid, "address_not_found", "Address not found", { addressId, paymentMethod })
|
|
@@ -104,14 +110,26 @@ export async function createCheckoutOrderAction(input) {
|
|
|
104
110
|
// product docs are re-read inside the transaction below for stock + prize
|
|
105
111
|
// pool checks; this is intentional (the tx is the source of truth for the
|
|
106
112
|
// counts we actually mutate).
|
|
107
|
-
|
|
113
|
+
//
|
|
114
|
+
// SB-UNI-5 2026-05-13 — for bundle cart-lines we pair the bundle item with
|
|
115
|
+
// its FIRST member product (so the maxPerUser cap check uses real product
|
|
116
|
+
// data instead of failing the bundle's category-id lookup). The full
|
|
117
|
+
// bundle-member stock fan-out happens in-tx below.
|
|
118
|
+
const preTxLookup = getExpandedDecrements(cartItems);
|
|
119
|
+
const preTxProductRefs = preTxLookup.productIds.map((pid) => db.collection(PRODUCT_COLLECTION).doc(pid));
|
|
108
120
|
const preTxProductSnaps = await Promise.all(preTxProductRefs.map((r) => r.get()));
|
|
121
|
+
const preTxProductById = new Map();
|
|
122
|
+
for (let i = 0; i < preTxLookup.productIds.length; i++) {
|
|
123
|
+
const snap = preTxProductSnaps[i];
|
|
124
|
+
if (snap.exists) {
|
|
125
|
+
preTxProductById.set(preTxLookup.productIds[i], snap.data());
|
|
126
|
+
}
|
|
127
|
+
}
|
|
109
128
|
const preTxPairs = cartItems
|
|
110
|
-
.map((item
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
return data ? { item, product: data } : null;
|
|
129
|
+
.map((item) => {
|
|
130
|
+
const [firstMember] = getCartItemMemberIds(item);
|
|
131
|
+
const product = preTxProductById.get(firstMember);
|
|
132
|
+
return product ? { item, product } : null;
|
|
115
133
|
})
|
|
116
134
|
.filter((pair) => pair !== null);
|
|
117
135
|
await enforceMaxPerUserForCart({ userId: uid, items: preTxPairs });
|
|
@@ -130,40 +148,63 @@ export async function createCheckoutOrderAction(input) {
|
|
|
130
148
|
throw Object.assign(new ApiError(403, "Order verification required. Please complete OTP verification before placing this order."), { _failReason: reason });
|
|
131
149
|
}
|
|
132
150
|
const emailOtpUsed = otpData.verifiedVia !== "sms";
|
|
133
|
-
|
|
134
|
-
|
|
151
|
+
// SB-UNI-5 2026-05-13 — bundle-aware stock fan-out. Build the unique
|
|
152
|
+
// product-id set across the whole cart (regular items + each bundle's
|
|
153
|
+
// members) and fetch each one ONCE. Validation walks per cart-line and
|
|
154
|
+
// checks every required member against the cart-cumulative decrement.
|
|
155
|
+
const expansion = getExpandedDecrements(cartItems);
|
|
156
|
+
const productRefById = new Map();
|
|
157
|
+
for (const pid of expansion.productIds) {
|
|
158
|
+
productRefById.set(pid, db.collection(PRODUCT_COLLECTION).doc(pid));
|
|
159
|
+
}
|
|
160
|
+
const productDocs = await Promise.all(expansion.productIds.map((pid) => tx.get(productRefById.get(pid))));
|
|
161
|
+
const productById = new Map();
|
|
162
|
+
for (let i = 0; i < expansion.productIds.length; i++) {
|
|
163
|
+
const doc = productDocs[i];
|
|
164
|
+
if (doc.exists) {
|
|
165
|
+
productById.set(expansion.productIds[i], doc.data());
|
|
166
|
+
}
|
|
167
|
+
}
|
|
135
168
|
const availableItems = [];
|
|
136
169
|
const unavailableItems = [];
|
|
137
|
-
for (
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
const productData = doc.exists ? doc.data() : null;
|
|
141
|
-
if (!productData ||
|
|
142
|
-
productData.status !== ProductStatusValues.PUBLISHED ||
|
|
143
|
-
productData.availableQuantity < item.quantity) {
|
|
170
|
+
for (const item of cartItems) {
|
|
171
|
+
const shortfall = validateCartItemStock(item, productById, expansion.decrements);
|
|
172
|
+
if (shortfall) {
|
|
144
173
|
unavailableItems.push({
|
|
145
|
-
productId:
|
|
174
|
+
productId: shortfall.productId,
|
|
146
175
|
productTitle: item.productTitle,
|
|
147
176
|
requestedQty: item.quantity,
|
|
148
|
-
availableQty:
|
|
177
|
+
availableQty: shortfall.availableQty,
|
|
149
178
|
});
|
|
179
|
+
continue;
|
|
150
180
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
181
|
+
// Bundle cart-lines surface the first member as the "product" so
|
|
182
|
+
// downstream listingType branching (prize-draw) stays sound — bundle
|
|
183
|
+
// pricing is always "standard" so the prize-draw cap isn't enforced.
|
|
184
|
+
const memberIds = getCartItemMemberIds(item);
|
|
185
|
+
const representative = productById.get(memberIds[0]);
|
|
186
|
+
// S-SBUNI-RULES 2026-05-13 — sync preflight (prize-pool cap, pre-order
|
|
187
|
+
// quota) via rule registry. Bundles skip — they bypass the cart path.
|
|
188
|
+
if (!item.bundleProductIds?.length) {
|
|
189
|
+
runSyncPreflight([{ item, product: representative }]);
|
|
190
|
+
}
|
|
191
|
+
availableItems.push({ item, product: representative });
|
|
192
|
+
}
|
|
193
|
+
// Apply per-product decrements ONCE per unique product (sums across the
|
|
194
|
+
// cart) so two cart lines sharing a product don't double-decrement.
|
|
195
|
+
if (availableItems.length > 0) {
|
|
196
|
+
for (const [pid, qtyDelta] of expansion.decrements) {
|
|
197
|
+
const product = productById.get(pid);
|
|
198
|
+
if (!product)
|
|
199
|
+
continue;
|
|
200
|
+
const lt = (product.listingType ?? "standard");
|
|
201
|
+
const rule = getListingRule(lt);
|
|
202
|
+
const update = {
|
|
203
|
+
availableQuantity: product.availableQuantity - qtyDelta,
|
|
159
204
|
updatedAt: new Date(),
|
|
205
|
+
...rule.stockDecrementExtras(product, qtyDelta),
|
|
160
206
|
};
|
|
161
|
-
|
|
162
|
-
productUpdate.prizeCurrentEntries =
|
|
163
|
-
(productData.prizeCurrentEntries ?? 0) + item.quantity;
|
|
164
|
-
}
|
|
165
|
-
tx.update(productRefs[i], productUpdate);
|
|
166
|
-
availableItems.push({ item, product: productData });
|
|
207
|
+
tx.update(productRefById.get(pid), update);
|
|
167
208
|
}
|
|
168
209
|
}
|
|
169
210
|
if (availableItems.length > 0) {
|
|
@@ -217,30 +258,27 @@ export async function createCheckoutOrderAction(input) {
|
|
|
217
258
|
const firstItem = group[0].item;
|
|
218
259
|
const groupTotal = group.reduce((sum, { item }) => sum + item.price * item.quantity, 0);
|
|
219
260
|
total += groupTotal;
|
|
220
|
-
//
|
|
221
|
-
// reveal status + deadline so the user-orders surface can render the
|
|
222
|
-
// reveals-remaining badge immediately after order creation.
|
|
223
|
-
const groupRevealDeadline = orderType === "prize-draw"
|
|
224
|
-
? computePrizeRevealDeadline(group[0].product)
|
|
225
|
-
: undefined;
|
|
261
|
+
// S-SBUNI-RULES 2026-05-13 — order-item decoration via rule registry.
|
|
226
262
|
const orderItems = group.map(({ item, product }) => {
|
|
227
|
-
const
|
|
228
|
-
|
|
263
|
+
const lt = (product.listingType ?? "standard");
|
|
264
|
+
const itemRule = getListingRule(lt);
|
|
265
|
+
// SB-UNI-5 2026-05-13 — forward bundle identifiers so the order-detail
|
|
266
|
+
// UI can collapse expanded lines back under a "Bundle: <name>" header.
|
|
267
|
+
const bundleFields = item.bundleCategorySlug && item.bundleProductIds
|
|
268
|
+
? {
|
|
269
|
+
bundleCategorySlug: item.bundleCategorySlug,
|
|
270
|
+
bundleProductIds: item.bundleProductIds,
|
|
271
|
+
}
|
|
272
|
+
: {};
|
|
273
|
+
const baseLine = {
|
|
229
274
|
productId: item.productId,
|
|
230
275
|
productTitle: item.productTitle,
|
|
231
276
|
quantity: item.quantity,
|
|
232
277
|
unitPrice: item.price,
|
|
233
278
|
totalPrice: item.price * item.quantity,
|
|
234
|
-
...
|
|
235
|
-
? {
|
|
236
|
-
listingType: "prize-draw",
|
|
237
|
-
prizeRevealStatus: product.prizeRevealStatus === "open" ? "open" : "pending",
|
|
238
|
-
prizeRevealDeadline: groupRevealDeadline?.toISOString(),
|
|
239
|
-
}
|
|
240
|
-
: product.listingType
|
|
241
|
-
? { listingType: product.listingType }
|
|
242
|
-
: {}),
|
|
279
|
+
...bundleFields,
|
|
243
280
|
};
|
|
281
|
+
return itemRule.decorateOrderItem(baseLine, product);
|
|
244
282
|
});
|
|
245
283
|
const totalQuantity = group.reduce((sum, { item }) => sum + item.quantity, 0);
|
|
246
284
|
let shippingFee = 0;
|
|
@@ -327,17 +365,15 @@ export async function createCheckoutOrderAction(input) {
|
|
|
327
365
|
.map(({ product }) => product.mainImage)
|
|
328
366
|
.filter((url) => typeof url === "string" && url.length > 0)),
|
|
329
367
|
];
|
|
330
|
-
//
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
const
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
}
|
|
340
|
-
: {};
|
|
368
|
+
// S-SBUNI-RULES 2026-05-13 — order-doc decoration via rule registry.
|
|
369
|
+
const lt0 = (group[0].product.listingType ?? "standard");
|
|
370
|
+
const groupRule = getListingRule(lt0);
|
|
371
|
+
const extraOrderFields = {
|
|
372
|
+
...groupRule.decorateOrderDoc(group[0].item, group[0].product),
|
|
373
|
+
...(orderType === "prize-draw"
|
|
374
|
+
? { prizeRevealDeadline: computePrizeRevealDeadline(group[0].product) }
|
|
375
|
+
: {}),
|
|
376
|
+
};
|
|
341
377
|
const order = await unitOfWork.orders.create({
|
|
342
378
|
productId: firstItem.productId,
|
|
343
379
|
productTitle: firstItem.productTitle,
|
|
@@ -365,7 +401,7 @@ export async function createCheckoutOrderAction(input) {
|
|
|
365
401
|
couponDiscount: couponDiscount > 0 ? couponDiscount : undefined,
|
|
366
402
|
appliedDiscounts: appliedDiscounts.length > 0 ? appliedDiscounts : undefined,
|
|
367
403
|
imageUrls: imageUrls.length > 0 ? imageUrls : undefined,
|
|
368
|
-
...
|
|
404
|
+
...extraOrderFields,
|
|
369
405
|
});
|
|
370
406
|
orderIds.push(order.id);
|
|
371
407
|
for (const code of groupCouponCodes) {
|
|
@@ -473,7 +509,10 @@ export async function verifyAndPlaceRazorpayOrderAction(input) {
|
|
|
473
509
|
if (!cart.items || cart.items.length === 0) {
|
|
474
510
|
throw new ValidationError(ERROR_MESSAGES.CHECKOUT.CART_EMPTY);
|
|
475
511
|
}
|
|
476
|
-
const
|
|
512
|
+
const addressDoc = await unitOfWork.addresses.findById(addressId);
|
|
513
|
+
const address = addressDoc && addressDoc.ownerType === "user" && addressDoc.ownerId === uid
|
|
514
|
+
? addressDoc
|
|
515
|
+
: null;
|
|
477
516
|
if (!address) {
|
|
478
517
|
throw new NotFoundError(ERROR_MESSAGES.CHECKOUT.ADDRESS_REQUIRED);
|
|
479
518
|
}
|
|
@@ -500,10 +539,22 @@ export async function verifyAndPlaceRazorpayOrderAction(input) {
|
|
|
500
539
|
throw new ApiError(403, "Order verification required. Please complete OTP verification and retry.");
|
|
501
540
|
}
|
|
502
541
|
}
|
|
503
|
-
|
|
504
|
-
|
|
542
|
+
// SB-UNI-5 2026-05-13 — bundle-aware product fetch + validation. Each
|
|
543
|
+
// cart line gets paired with a "representative" product (first member id
|
|
544
|
+
// for bundles, productId for regular items). The full bundle-member
|
|
545
|
+
// decrement runs against `expansionPaid.decrements` lower down.
|
|
546
|
+
const expansionPaid = getExpandedDecrements(cart.items);
|
|
547
|
+
const productByIdPaid = new Map();
|
|
548
|
+
for (const pid of expansionPaid.productIds) {
|
|
549
|
+
const product = await unitOfWork.products.findById(pid);
|
|
550
|
+
if (product)
|
|
551
|
+
productByIdPaid.set(pid, product);
|
|
552
|
+
}
|
|
553
|
+
const productChecks = cart.items.map((item) => {
|
|
554
|
+
const [firstMember] = getCartItemMemberIds(item);
|
|
555
|
+
const product = productByIdPaid.get(firstMember) ?? null;
|
|
505
556
|
return { item, product };
|
|
506
|
-
})
|
|
557
|
+
});
|
|
507
558
|
// SB6-C — maxPerUser cap check before we touch any state.
|
|
508
559
|
await enforceMaxPerUserForCart({
|
|
509
560
|
userId: uid,
|
|
@@ -511,39 +562,40 @@ export async function verifyAndPlaceRazorpayOrderAction(input) {
|
|
|
511
562
|
.filter((p) => p.product !== null && p.product !== undefined)
|
|
512
563
|
.map(({ item, product }) => ({ item, product })),
|
|
513
564
|
});
|
|
514
|
-
//
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
.logPayment(uid, "product_unavailable", `Product ${item.productId} not published`, {
|
|
527
|
-
gatewayOrderId: razorpay_order_id,
|
|
528
|
-
gatewayPaymentId: razorpay_payment_id,
|
|
529
|
-
addressId,
|
|
530
|
-
})
|
|
531
|
-
.catch(() => { });
|
|
532
|
-
throw new ValidationError(ERROR_MESSAGES.CHECKOUT.PRODUCT_UNAVAILABLE);
|
|
533
|
-
}
|
|
534
|
-
if (product.availableQuantity < item.quantity) {
|
|
565
|
+
// S-SBUNI-RULES 2026-05-13 — sync preflight via rule registry.
|
|
566
|
+
// Bundle items bypass the cart flow so skip the prize-pool cap.
|
|
567
|
+
const preflightPairs = productChecks.filter((p) => p.product !== null && p.product !== undefined && !p.item.bundleProductIds?.length);
|
|
568
|
+
runSyncPreflight(preflightPairs);
|
|
569
|
+
// SB-UNI-5 — validate every required member product across the cart with
|
|
570
|
+
// cumulative decrement awareness (two bundles sharing a member must NOT
|
|
571
|
+
// both succeed unless the product has enough stock for the sum).
|
|
572
|
+
for (const item of cart.items) {
|
|
573
|
+
const shortfall = validateCartItemStock(item, productByIdPaid, expansionPaid.decrements);
|
|
574
|
+
if (shortfall) {
|
|
575
|
+
const exists = productByIdPaid.has(shortfall.productId);
|
|
576
|
+
const reason = exists ? "stock_insufficient" : "product_unavailable";
|
|
535
577
|
failedCheckoutRepository
|
|
536
|
-
.logPayment(uid,
|
|
578
|
+
.logPayment(uid, reason, exists
|
|
579
|
+
? `Product ${shortfall.productId} has ${shortfall.availableQty} left, requested ${item.quantity}`
|
|
580
|
+
: `Product ${shortfall.productId} not published`, {
|
|
537
581
|
gatewayOrderId: razorpay_order_id,
|
|
538
582
|
gatewayPaymentId: razorpay_payment_id,
|
|
539
583
|
addressId,
|
|
540
584
|
})
|
|
541
585
|
.catch(() => { });
|
|
542
|
-
throw new ValidationError(
|
|
586
|
+
throw new ValidationError(exists
|
|
587
|
+
? ERROR_MESSAGES.CHECKOUT.INSUFFICIENT_STOCK
|
|
588
|
+
: ERROR_MESSAGES.CHECKOUT.PRODUCT_UNAVAILABLE);
|
|
543
589
|
}
|
|
544
590
|
}
|
|
545
591
|
{
|
|
546
|
-
|
|
592
|
+
// SB-UNI-5 2026-05-13 — bundle cart-lines use item.price (locked
|
|
593
|
+
// bundlePriceInPaise) instead of the representative member's product.price.
|
|
594
|
+
const cartSubtotalRs = productChecks.reduce((sum, { item, product }) => {
|
|
595
|
+
const isBundle = Boolean(item.bundleCategorySlug && item.bundleProductIds?.length);
|
|
596
|
+
const unit = isBundle ? item.price : product.price;
|
|
597
|
+
return sum + unit * item.quantity;
|
|
598
|
+
}, 0);
|
|
547
599
|
const expectedPlatformFee = Math.round(cartSubtotalRs * (razorpayFeePercent / 100) * 100) / 100;
|
|
548
600
|
const expectedPaymentAmountRs = cartSubtotalRs + expectedPlatformFee;
|
|
549
601
|
const rzpOrderRecord = await fetchRazorpayOrder(razorpay_order_id);
|
|
@@ -566,11 +618,17 @@ export async function verifyAndPlaceRazorpayOrderAction(input) {
|
|
|
566
618
|
const orderIds = [];
|
|
567
619
|
let total = 0;
|
|
568
620
|
const emailsToSend = [];
|
|
621
|
+
// SB-UNI-5 2026-05-13 — bundle cart-lines use item.price (locked bundle
|
|
622
|
+
// price); regular lines use product.price. The helper keeps the math
|
|
623
|
+
// local to this block.
|
|
624
|
+
const unitPriceFor = (item, product) => item.bundleCategorySlug && item.bundleProductIds?.length
|
|
625
|
+
? item.price
|
|
626
|
+
: product.price;
|
|
569
627
|
const cartSubtotal = orderGroups.reduce((s, { items: g }) => s +
|
|
570
|
-
g.reduce((gs, { item, product }) => gs + product
|
|
628
|
+
g.reduce((gs, { item, product }) => gs + unitPriceFor(item, product) * item.quantity, 0), 0);
|
|
571
629
|
for (const { items: group, orderType } of orderGroups) {
|
|
572
630
|
const firstItem = group[0].item;
|
|
573
|
-
const groupTotal = group.reduce((sum, { item, product }) => sum + product
|
|
631
|
+
const groupTotal = group.reduce((sum, { item, product }) => sum + unitPriceFor(item, product) * item.quantity, 0);
|
|
574
632
|
let shippingFee = 0;
|
|
575
633
|
let storeOwnerId;
|
|
576
634
|
const storeId = firstItem.storeId;
|
|
@@ -600,7 +658,7 @@ export async function verifyAndPlaceRazorpayOrderAction(input) {
|
|
|
600
658
|
if (coupon.applicableItemIds?.length) {
|
|
601
659
|
const eligibleTotal = group
|
|
602
660
|
.filter(({ item }) => coupon.applicableItemIds.includes(item.itemId))
|
|
603
|
-
.reduce((s, { item, product }) => s + product
|
|
661
|
+
.reduce((s, { item, product }) => s + unitPriceFor(item, product) * item.quantity, 0);
|
|
604
662
|
couponGroupDiscount =
|
|
605
663
|
eligibleTotal > 0
|
|
606
664
|
? Math.min(Math.round((eligibleTotal / groupTotal) * coupon.discountAmount * 100) / 100, eligibleTotal)
|
|
@@ -629,30 +687,30 @@ export async function verifyAndPlaceRazorpayOrderAction(input) {
|
|
|
629
687
|
const platformFee = Math.round(groupTotal * (razorpayFeePercent / 100) * 100) / 100;
|
|
630
688
|
const orderTotal = Math.max(0, groupTotal - couponDiscount) + shippingFee;
|
|
631
689
|
total += orderTotal;
|
|
632
|
-
//
|
|
633
|
-
const groupRevealDeadlineRzp = orderType === "prize-draw" && group[0].product
|
|
634
|
-
? computePrizeRevealDeadline(group[0].product)
|
|
635
|
-
: undefined;
|
|
690
|
+
// S-SBUNI-RULES 2026-05-13 — order-item decoration via rule registry.
|
|
636
691
|
const orderItems = group.map(({ item, product }) => {
|
|
637
|
-
const
|
|
638
|
-
|
|
692
|
+
const lt = (product?.listingType ?? "standard");
|
|
693
|
+
const itemRule = getListingRule(lt);
|
|
694
|
+
// SB-UNI-5 2026-05-13 — bundle cart-lines surface the locked
|
|
695
|
+
// bundlePriceInPaise (item.price), not the representative member's
|
|
696
|
+
// product.price. Stock decrement still runs per member elsewhere.
|
|
697
|
+
const isBundle = Boolean(item.bundleCategorySlug && item.bundleProductIds?.length);
|
|
698
|
+
const unitPrice = isBundle ? item.price : product.price;
|
|
699
|
+
const bundleFields = isBundle
|
|
700
|
+
? {
|
|
701
|
+
bundleCategorySlug: item.bundleCategorySlug,
|
|
702
|
+
bundleProductIds: item.bundleProductIds,
|
|
703
|
+
}
|
|
704
|
+
: {};
|
|
705
|
+
const baseLine = {
|
|
639
706
|
productId: item.productId,
|
|
640
707
|
productTitle: item.productTitle,
|
|
641
708
|
quantity: item.quantity,
|
|
642
|
-
unitPrice
|
|
643
|
-
totalPrice:
|
|
644
|
-
...
|
|
645
|
-
? {
|
|
646
|
-
listingType: "prize-draw",
|
|
647
|
-
prizeRevealStatus: product?.prizeRevealStatus === "open"
|
|
648
|
-
? "open"
|
|
649
|
-
: "pending",
|
|
650
|
-
prizeRevealDeadline: groupRevealDeadlineRzp?.toISOString(),
|
|
651
|
-
}
|
|
652
|
-
: product?.listingType
|
|
653
|
-
? { listingType: product.listingType }
|
|
654
|
-
: {}),
|
|
709
|
+
unitPrice,
|
|
710
|
+
totalPrice: unitPrice * item.quantity,
|
|
711
|
+
...bundleFields,
|
|
655
712
|
};
|
|
713
|
+
return itemRule.decorateOrderItem(baseLine, product);
|
|
656
714
|
});
|
|
657
715
|
const totalQuantity = group.reduce((sum, { item }) => sum + item.quantity, 0);
|
|
658
716
|
const imageUrls = [
|
|
@@ -660,16 +718,15 @@ export async function verifyAndPlaceRazorpayOrderAction(input) {
|
|
|
660
718
|
.map(({ product }) => product?.mainImage)
|
|
661
719
|
.filter((url) => typeof url === "string" && url.length > 0)),
|
|
662
720
|
];
|
|
663
|
-
//
|
|
664
|
-
|
|
665
|
-
const
|
|
666
|
-
const
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
: {};
|
|
721
|
+
// S-SBUNI-RULES 2026-05-13 — order-doc decoration via rule registry.
|
|
722
|
+
const lt0Rzp = (group[0].product?.listingType ?? "standard");
|
|
723
|
+
const groupRuleRzp = getListingRule(lt0Rzp);
|
|
724
|
+
const extraOrderFields = {
|
|
725
|
+
...groupRuleRzp.decorateOrderDoc(group[0].item, group[0].product),
|
|
726
|
+
...(orderType === "prize-draw" && group[0].product
|
|
727
|
+
? { prizeRevealDeadline: computePrizeRevealDeadline(group[0].product) }
|
|
728
|
+
: {}),
|
|
729
|
+
};
|
|
673
730
|
const order = await unitOfWork.orders.create({
|
|
674
731
|
productId: firstItem.productId,
|
|
675
732
|
productTitle: firstItem.productTitle,
|
|
@@ -697,7 +754,7 @@ export async function verifyAndPlaceRazorpayOrderAction(input) {
|
|
|
697
754
|
couponDiscount: couponDiscount > 0 ? couponDiscount : undefined,
|
|
698
755
|
appliedDiscounts: appliedDiscounts.length > 0 ? appliedDiscounts : undefined,
|
|
699
756
|
imageUrls: imageUrls.length > 0 ? imageUrls : undefined,
|
|
700
|
-
...
|
|
757
|
+
...extraOrderFields,
|
|
701
758
|
});
|
|
702
759
|
orderIds.push(order.id);
|
|
703
760
|
if (userEmail) {
|
|
@@ -723,18 +780,20 @@ export async function verifyAndPlaceRazorpayOrderAction(input) {
|
|
|
723
780
|
paid: true,
|
|
724
781
|
});
|
|
725
782
|
}
|
|
783
|
+
// SB-UNI-5 2026-05-13 — bundle-aware batch decrement. We iterate the
|
|
784
|
+
// cumulative `decrements` map (one entry per unique member product) so a
|
|
785
|
+
// bundle's members + any sibling cart line touching the same product
|
|
786
|
+
// collapse into ONE update per product.
|
|
726
787
|
await unitOfWork.runBatch((batch) => {
|
|
727
|
-
for (const
|
|
788
|
+
for (const [pid, qtyDelta] of expansionPaid.decrements) {
|
|
789
|
+
const product = productByIdPaid.get(pid);
|
|
728
790
|
if (!product)
|
|
729
791
|
continue;
|
|
730
|
-
const
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
unitOfWork.products.updateInBatch(batch, item.productId, {
|
|
736
|
-
availableQuantity: product.availableQuantity - item.quantity,
|
|
737
|
-
...prizeBump,
|
|
792
|
+
const lt = (product.listingType ?? "standard");
|
|
793
|
+
const batchRule = getListingRule(lt);
|
|
794
|
+
unitOfWork.products.updateInBatch(batch, pid, {
|
|
795
|
+
availableQuantity: product.availableQuantity - qtyDelta,
|
|
796
|
+
...batchRule.stockDecrementExtras(product, qtyDelta),
|
|
738
797
|
});
|
|
739
798
|
}
|
|
740
799
|
unitOfWork.carts.updateInBatch(batch, uid, {
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bundle cart-item expansion helpers — S-SBUNI-5 2026-05-13.
|
|
3
|
+
*
|
|
4
|
+
* Checkout treats each cart item as one OrderItem row (bundles stay grouped
|
|
5
|
+
* in the order) but stock decrement has to fan out to the bundle's member
|
|
6
|
+
* products. These helpers do that fan-out without breaking the existing
|
|
7
|
+
* per-cart-item validation loops.
|
|
8
|
+
*
|
|
9
|
+
* Two-step model:
|
|
10
|
+
* 1. `getExpandedDecrements(cartItems)` — returns the unique product-id set
|
|
11
|
+
* to fetch + a per-product total-quantity map. Multiple cart items that
|
|
12
|
+
* touch the same product (e.g. two bundles sharing one member, or a
|
|
13
|
+
* bundle + a regular item) get summed.
|
|
14
|
+
* 2. `validateCartItemStock(item, productById)` — for one cart item, looks
|
|
15
|
+
* up every required product (bundle members OR the single productId) +
|
|
16
|
+
* returns the worst-case shortfall: `null` if all OK, or the first
|
|
17
|
+
* unavailable member's id + availableQty.
|
|
18
|
+
*
|
|
19
|
+
* Note: validation in-tx is still subject to product writes from a parallel
|
|
20
|
+
* checkout — the Firestore transaction guarantees the second checkout sees
|
|
21
|
+
* the first's decrement before re-running its own validation.
|
|
22
|
+
*/
|
|
23
|
+
import type { CartItemDocument } from "../../../../features/cart/schemas";
|
|
24
|
+
import type { ProductDocument } from "../../../../features/products/schemas/firestore";
|
|
25
|
+
export interface BundleExpansion {
|
|
26
|
+
/** Unique product ids referenced by any cart item (regular or bundle member). */
|
|
27
|
+
productIds: string[];
|
|
28
|
+
/**
|
|
29
|
+
* Sum of requested quantities per product id. Bundle members get
|
|
30
|
+
* `item.quantity` per occurrence (so 2x of a bundle with member X
|
|
31
|
+
* decrements X by 2, not 1).
|
|
32
|
+
*/
|
|
33
|
+
decrements: Map<string, number>;
|
|
34
|
+
}
|
|
35
|
+
/** Resolve the member-product ids for a cart item. Bundle items expand to
|
|
36
|
+
* `bundleProductIds[]`; regular items collapse to `[productId]`. */
|
|
37
|
+
export declare function getCartItemMemberIds(item: CartItemDocument): string[];
|
|
38
|
+
/** Build the unique-product-id + per-product decrement map for a cart. */
|
|
39
|
+
export declare function getExpandedDecrements(cartItems: CartItemDocument[]): BundleExpansion;
|
|
40
|
+
export interface StockShortfall {
|
|
41
|
+
/** The member product id that ran out (or never existed). */
|
|
42
|
+
productId: string;
|
|
43
|
+
/** Quantity Firestore still has for that product. */
|
|
44
|
+
availableQty: number;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* For a single cart item: returns `null` when every required product is
|
|
48
|
+
* published + has enough stock, or the first shortfall encountered. The
|
|
49
|
+
* caller supplies a `productById` lookup populated from the in-tx fetch
|
|
50
|
+
* (so the same query runs once for the whole cart).
|
|
51
|
+
*
|
|
52
|
+
* `decrements` lets the caller account for OTHER cart items touching the
|
|
53
|
+
* same product — pass the cumulative map from `getExpandedDecrements` so
|
|
54
|
+
* the available quantity is compared against the WHOLE cart's demand for
|
|
55
|
+
* that product, not just this item's slice.
|
|
56
|
+
*/
|
|
57
|
+
export declare function validateCartItemStock(item: CartItemDocument, productById: Map<string, ProductDocument>, decrements: Map<string, number>): StockShortfall | null;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bundle cart-item expansion helpers — S-SBUNI-5 2026-05-13.
|
|
3
|
+
*
|
|
4
|
+
* Checkout treats each cart item as one OrderItem row (bundles stay grouped
|
|
5
|
+
* in the order) but stock decrement has to fan out to the bundle's member
|
|
6
|
+
* products. These helpers do that fan-out without breaking the existing
|
|
7
|
+
* per-cart-item validation loops.
|
|
8
|
+
*
|
|
9
|
+
* Two-step model:
|
|
10
|
+
* 1. `getExpandedDecrements(cartItems)` — returns the unique product-id set
|
|
11
|
+
* to fetch + a per-product total-quantity map. Multiple cart items that
|
|
12
|
+
* touch the same product (e.g. two bundles sharing one member, or a
|
|
13
|
+
* bundle + a regular item) get summed.
|
|
14
|
+
* 2. `validateCartItemStock(item, productById)` — for one cart item, looks
|
|
15
|
+
* up every required product (bundle members OR the single productId) +
|
|
16
|
+
* returns the worst-case shortfall: `null` if all OK, or the first
|
|
17
|
+
* unavailable member's id + availableQty.
|
|
18
|
+
*
|
|
19
|
+
* Note: validation in-tx is still subject to product writes from a parallel
|
|
20
|
+
* checkout — the Firestore transaction guarantees the second checkout sees
|
|
21
|
+
* the first's decrement before re-running its own validation.
|
|
22
|
+
*/
|
|
23
|
+
import { ProductStatusValues } from "../../../../features/products/schemas/firestore";
|
|
24
|
+
/** Resolve the member-product ids for a cart item. Bundle items expand to
|
|
25
|
+
* `bundleProductIds[]`; regular items collapse to `[productId]`. */
|
|
26
|
+
export function getCartItemMemberIds(item) {
|
|
27
|
+
if (item.bundleProductIds && item.bundleProductIds.length > 0) {
|
|
28
|
+
return item.bundleProductIds;
|
|
29
|
+
}
|
|
30
|
+
return [item.productId];
|
|
31
|
+
}
|
|
32
|
+
/** Build the unique-product-id + per-product decrement map for a cart. */
|
|
33
|
+
export function getExpandedDecrements(cartItems) {
|
|
34
|
+
const decrements = new Map();
|
|
35
|
+
for (const item of cartItems) {
|
|
36
|
+
const memberIds = getCartItemMemberIds(item);
|
|
37
|
+
for (const pid of memberIds) {
|
|
38
|
+
decrements.set(pid, (decrements.get(pid) ?? 0) + item.quantity);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
productIds: [...decrements.keys()],
|
|
43
|
+
decrements,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* For a single cart item: returns `null` when every required product is
|
|
48
|
+
* published + has enough stock, or the first shortfall encountered. The
|
|
49
|
+
* caller supplies a `productById` lookup populated from the in-tx fetch
|
|
50
|
+
* (so the same query runs once for the whole cart).
|
|
51
|
+
*
|
|
52
|
+
* `decrements` lets the caller account for OTHER cart items touching the
|
|
53
|
+
* same product — pass the cumulative map from `getExpandedDecrements` so
|
|
54
|
+
* the available quantity is compared against the WHOLE cart's demand for
|
|
55
|
+
* that product, not just this item's slice.
|
|
56
|
+
*/
|
|
57
|
+
export function validateCartItemStock(item, productById, decrements) {
|
|
58
|
+
const memberIds = getCartItemMemberIds(item);
|
|
59
|
+
for (const pid of memberIds) {
|
|
60
|
+
const product = productById.get(pid);
|
|
61
|
+
if (!product || product.status !== ProductStatusValues.PUBLISHED) {
|
|
62
|
+
return { productId: pid, availableQty: product?.availableQuantity ?? 0 };
|
|
63
|
+
}
|
|
64
|
+
const totalNeeded = decrements.get(pid) ?? item.quantity;
|
|
65
|
+
if (product.availableQuantity < totalNeeded) {
|
|
66
|
+
return { productId: pid, availableQty: product.availableQuantity };
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* maxPerUser gate + prize reveal deadline helper for the checkout pipeline
|
|
3
|
+
* (SB6-C, SB8-A).
|
|
3
4
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* `enforcePrizePoolCap` was removed in S-SBUNI-RULES (2026-05-13) — the
|
|
6
|
+
* equivalent check now lives in `prizeDrawRule.preflightChecks` in the rule
|
|
7
|
+
* registry and is invoked via `runSyncPreflight`.
|
|
7
8
|
*/
|
|
8
9
|
import type { ProductDocument } from "../../../../features/products/schemas/firestore";
|
|
9
10
|
import type { CartItemDocument } from "../../../../features/cart/schemas/firestore";
|
|
@@ -46,14 +47,3 @@ export declare function enforceMaxPerUserForCart(args: {
|
|
|
46
47
|
* Returns `undefined` if the product lacks the prize-draw fields.
|
|
47
48
|
*/
|
|
48
49
|
export declare function computePrizeRevealDeadline(product: Pick<ProductDocument, "prizeRevealWindowStart" | "prizeRevealWindowEnd" | "prizeRevealDeadlineDays">, now?: Date): Date | undefined;
|
|
49
|
-
/**
|
|
50
|
-
* Inside the existing checkout transaction, validate the prize-pool cap for
|
|
51
|
-
* each prize-draw line. Caller must pass the fresh product snapshot read
|
|
52
|
-
* within the transaction (so `prizeCurrentEntries` is up-to-date).
|
|
53
|
-
*
|
|
54
|
-
* Throws ValidationError if any item would overflow the pool.
|
|
55
|
-
*/
|
|
56
|
-
export declare function enforcePrizePoolCap(args: {
|
|
57
|
-
productSnapshot: ProductDocument;
|
|
58
|
-
requestedQuantity: number;
|
|
59
|
-
}): void;
|