@pisell/pisellos 0.0.567 → 0.0.569
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/core/index.js +6 -9
- package/dist/model/strategy/adapter/promotion/evaluator.js +3 -2
- package/dist/model/strategy/adapter/walletPass/evaluator.js +0 -2
- package/dist/model/strategy/adapter/walletPass/example.js +3 -26
- package/dist/model/strategy/adapter/walletPass/utils.js +7 -7
- package/dist/model/strategy/strategy-example.js +5 -19
- package/dist/modules/BaseModule.d.ts +2 -2
- package/dist/modules/BaseModule.js +4 -4
- package/dist/modules/BookingContext/index.d.ts +48 -0
- package/dist/modules/BookingContext/index.js +566 -0
- package/dist/modules/BookingContext/types.d.ts +102 -0
- package/dist/modules/BookingContext/types.js +51 -0
- package/dist/modules/BookingContext/utils/buildCacheItemFromOrderLine.d.ts +16 -0
- package/dist/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +302 -0
- package/dist/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.d.ts +8 -0
- package/dist/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +149 -0
- package/dist/modules/BookingContext/utils/cacheItemToBookingInput.d.ts +76 -0
- package/dist/modules/BookingContext/utils/cacheItemToBookingInput.js +249 -0
- package/dist/modules/BookingContext/utils/flexible.d.ts +28 -0
- package/dist/modules/BookingContext/utils/flexible.js +56 -0
- package/dist/modules/BookingContext/utils/formatResourceList.d.ts +8 -0
- package/dist/modules/BookingContext/utils/formatResourceList.js +38 -0
- package/dist/modules/BookingContext/utils/index.d.ts +11 -0
- package/dist/modules/BookingContext/utils/index.js +11 -0
- package/dist/modules/BookingContext/utils/noResource.d.ts +13 -0
- package/dist/modules/BookingContext/utils/noResource.js +77 -0
- package/dist/modules/BookingContext/utils/orderLineDisplay.d.ts +1 -0
- package/dist/modules/BookingContext/utils/orderLineDisplay.js +36 -0
- package/dist/modules/BookingContext/utils/productExtend.d.ts +72 -0
- package/dist/modules/BookingContext/utils/productExtend.js +339 -0
- package/dist/modules/BookingContext/utils/resourceErrors.d.ts +41 -0
- package/dist/modules/BookingContext/utils/resourceErrors.js +74 -0
- package/dist/modules/BookingContext/utils/resourceSelection.d.ts +7 -0
- package/dist/modules/BookingContext/utils/resourceSelection.js +24 -0
- package/dist/modules/BookingContext/utils/resources.d.ts +80 -0
- package/dist/modules/BookingContext/utils/resources.js +486 -0
- package/dist/modules/BookingContext/utils/serviceTimes.d.ts +55 -0
- package/dist/modules/BookingContext/utils/serviceTimes.js +151 -0
- package/dist/modules/BookingContext/utils/timeSlices.d.ts +42 -0
- package/dist/modules/BookingContext/utils/timeSlices.js +100 -0
- package/dist/modules/Cart/index.js +0 -3
- package/dist/modules/Cart/utils/cartProduct.js +26 -6
- package/dist/modules/Cart/utils/changePrice.js +27 -20
- package/dist/modules/Customer/index.d.ts +20 -0
- package/dist/modules/Customer/index.js +172 -83
- package/dist/modules/Customer/types.d.ts +2 -0
- package/dist/modules/Date/index.js +1 -5
- package/dist/modules/Discount/index.d.ts +2 -1
- package/dist/modules/Discount/index.js +37 -12
- package/dist/modules/Discount/types.d.ts +1 -0
- package/dist/modules/OpenData/index.d.ts +8 -0
- package/dist/modules/OpenData/index.js +35 -17
- package/dist/modules/Order/index.d.ts +449 -24
- package/dist/modules/Order/index.js +5437 -608
- package/dist/modules/Order/types.d.ts +693 -88
- package/dist/modules/Order/types.js +68 -1
- package/dist/modules/Order/utils/bundleDiscountHydration.d.ts +5 -0
- package/dist/modules/Order/utils/bundleDiscountHydration.js +144 -0
- package/dist/modules/Order/utils/manualProductDiscount.d.ts +106 -0
- package/dist/modules/Order/utils/manualProductDiscount.js +210 -0
- package/dist/modules/Order/utils/orderCollectionIdentity.d.ts +53 -0
- package/dist/modules/Order/utils/orderCollectionIdentity.js +405 -0
- package/dist/modules/Order/utils.d.ts +167 -18
- package/dist/modules/Order/utils.js +960 -165
- package/dist/modules/Payment/index.d.ts +10 -9
- package/dist/modules/Payment/index.js +508 -874
- package/dist/modules/Payment/types.d.ts +40 -16
- package/dist/modules/Payment/utils.d.ts +1 -1
- package/dist/modules/Payment/utils.js +4 -3
- package/dist/modules/Payment/walletpass.js +48 -34
- package/dist/modules/Product/types.d.ts +29 -7
- package/dist/modules/ProductList/index.d.ts +17 -13
- package/dist/modules/ProductList/index.js +184 -74
- package/dist/modules/ProductList/types.d.ts +17 -0
- package/dist/modules/Quotation/index.d.ts +22 -2
- package/dist/modules/Quotation/index.js +108 -42
- package/dist/modules/Quotation/types.d.ts +19 -0
- package/dist/modules/ResourcePlanner/index.d.ts +24 -0
- package/dist/modules/ResourcePlanner/index.js +181 -0
- package/dist/modules/ResourcePlanner/planner.d.ts +14 -0
- package/dist/modules/ResourcePlanner/planner.js +1069 -0
- package/dist/modules/ResourcePlanner/types.d.ts +227 -0
- package/dist/modules/ResourcePlanner/types.js +1 -0
- package/dist/modules/Rules/index.d.ts +8 -10
- package/dist/modules/Rules/index.js +219 -114
- package/dist/modules/Rules/types.d.ts +14 -3
- package/dist/modules/SalesSummary/index.d.ts +23 -26
- package/dist/modules/SalesSummary/index.js +152 -46
- package/dist/modules/SalesSummary/types.d.ts +9 -1
- package/dist/modules/SalesSummary/utils.d.ts +3 -22
- package/dist/modules/SalesSummary/utils.js +803 -163
- package/dist/modules/Schedule/getDateIsInSchedule.js +14 -7
- package/dist/modules/Schedule/index.d.ts +18 -5
- package/dist/modules/Schedule/index.js +78 -24
- package/dist/modules/Schedule/types.d.ts +2 -0
- package/dist/modules/Schedule/utils.js +0 -1
- package/dist/modules/Step/index.js +0 -1
- package/dist/modules/Summary/index.js +4 -3
- package/dist/modules/SurchargeList/index.d.ts +18 -0
- package/dist/modules/SurchargeList/index.js +185 -0
- package/dist/modules/SurchargeList/types.d.ts +15 -0
- package/dist/modules/SurchargeList/types.js +1 -0
- package/dist/modules/index.d.ts +3 -0
- package/dist/modules/index.js +4 -1
- package/dist/plugins/app.d.ts +2 -0
- package/dist/plugins/request.d.ts +1 -0
- package/dist/plugins/request.js +1 -4
- package/dist/plugins/window.js +2 -6
- package/dist/server/index.d.ts +462 -8
- package/dist/server/index.js +6137 -714
- package/dist/server/modules/floor-plan/index.d.ts +39 -0
- package/dist/server/modules/floor-plan/index.js +595 -0
- package/dist/server/modules/floor-plan/types.d.ts +43 -0
- package/dist/server/modules/floor-plan/types.js +13 -0
- package/dist/server/modules/index.d.ts +7 -2
- package/dist/server/modules/index.js +6 -0
- package/dist/server/modules/menu/index.js +32 -41
- package/dist/server/modules/order/index.d.ts +207 -14
- package/dist/server/modules/order/index.js +2326 -379
- package/dist/server/modules/order/types.d.ts +76 -5
- package/dist/server/modules/order/types.js +14 -1
- package/dist/server/modules/order/utils/filterBookings.d.ts +7 -1
- package/dist/server/modules/order/utils/filterBookings.js +118 -11
- package/dist/server/modules/order/utils/filterOrders.js +242 -14
- package/dist/server/modules/payment/index.d.ts +28 -0
- package/dist/server/modules/payment/index.js +305 -0
- package/dist/server/modules/payment/types.d.ts +9 -0
- package/dist/server/modules/payment/types.js +1 -0
- package/dist/server/modules/products/index.d.ts +105 -14
- package/dist/server/modules/products/index.js +1397 -540
- package/dist/server/modules/products/types.d.ts +34 -1
- package/dist/server/modules/products/types.js +4 -0
- package/dist/server/modules/quotation/index.d.ts +39 -0
- package/dist/server/modules/quotation/index.js +289 -86
- package/dist/server/modules/resource/index.d.ts +2 -3
- package/dist/server/modules/resource/index.js +105 -84
- package/dist/server/modules/schedule/index.d.ts +6 -4
- package/dist/server/modules/schedule/index.js +137 -114
- package/dist/server/test.json +713 -0
- package/dist/server/types.d.ts +2 -0
- package/dist/server/utils/product.js +2 -3
- package/dist/server/utils/small-ticket.d.ts +71 -0
- package/dist/server/utils/small-ticket.js +963 -0
- package/dist/solution/BaseSales/index.d.ts +378 -0
- package/dist/solution/BaseSales/index.js +4006 -0
- package/dist/solution/BaseSales/types.d.ts +394 -0
- package/dist/solution/BaseSales/types.js +40 -0
- package/dist/solution/BaseSales/utils/cartPromotion.d.ts +287 -0
- package/dist/solution/BaseSales/utils/cartPromotion.js +1450 -0
- package/dist/solution/BaseSales/utils/parseSalesResponse.d.ts +144 -0
- package/dist/solution/BaseSales/utils/parseSalesResponse.js +157 -0
- package/dist/solution/BaseSales/utils/quotationPrice.d.ts +18 -0
- package/dist/solution/BaseSales/utils/quotationPrice.js +250 -0
- package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.d.ts +63 -0
- package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +218 -0
- package/dist/solution/BaseSales/utils.d.ts +40 -0
- package/dist/solution/BaseSales/utils.js +263 -0
- package/dist/solution/BookingByStep/index.js +7 -59
- package/dist/solution/BookingByStep/utils/capacity.js +1 -11
- package/dist/solution/BookingByStep/utils/resources.js +1 -3
- package/dist/solution/BookingByStep/utils/timeslots.js +2 -43
- package/dist/solution/BookingTicket/index.d.ts +393 -24
- package/dist/solution/BookingTicket/index.js +2506 -445
- package/dist/solution/BookingTicket/types.d.ts +191 -6
- package/dist/solution/BookingTicket/types.js +83 -10
- package/dist/solution/BookingTicket/utils/addProductDecision.d.ts +117 -0
- package/dist/solution/BookingTicket/utils/addProductDecision.js +346 -0
- package/dist/solution/BookingTicket/utils/addTimeAvailability.d.ts +35 -0
- package/dist/solution/BookingTicket/utils/addTimeAvailability.js +95 -0
- package/dist/solution/BookingTicket/utils/bookingStatus.d.ts +77 -0
- package/dist/solution/BookingTicket/utils/bookingStatus.js +136 -0
- package/dist/solution/BookingTicket/utils/cartView.d.ts +29 -0
- package/dist/solution/BookingTicket/utils/cartView.js +209 -0
- package/dist/solution/BookingTicket/utils/exampleData.d.ts +123 -0
- package/dist/solution/BookingTicket/utils/exampleData.js +183 -0
- package/dist/solution/BookingTicket/utils/orderCustomer.d.ts +4 -0
- package/dist/solution/BookingTicket/utils/orderCustomer.js +31 -0
- package/dist/solution/BookingTicket/utils/resolveBestAddTimePlan.d.ts +185 -0
- package/dist/solution/BookingTicket/utils/resolveBestAddTimePlan.js +438 -0
- package/dist/solution/BookingTicket/utils/scan/applyGlobalScan.d.ts +36 -0
- package/dist/solution/BookingTicket/utils/scan/applyGlobalScan.js +400 -0
- package/dist/solution/BookingTicket/utils/scan/cloudSearch.d.ts +4 -0
- package/dist/solution/BookingTicket/utils/scan/cloudSearch.js +41 -1
- package/dist/solution/BookingTicket/utils/scan/formatGlobalScan.d.ts +32 -0
- package/dist/solution/BookingTicket/utils/scan/formatGlobalScan.js +56 -0
- package/dist/solution/BookingTicket/utils/scan/handleScan.d.ts +3 -1
- package/dist/solution/BookingTicket/utils/scan/handleScan.js +36 -34
- package/dist/solution/BookingTicket/utils/scan/index.d.ts +5 -0
- package/dist/solution/BookingTicket/utils/scan/index.js +26 -18
- package/dist/solution/BookingTicket/utils/scan/scanProductValues.d.ts +21 -0
- package/dist/solution/BookingTicket/utils/scan/scanProductValues.js +150 -0
- package/dist/solution/BookingTicket/utils/sessionCatalogStale.d.ts +23 -0
- package/dist/solution/BookingTicket/utils/sessionCatalogStale.js +78 -0
- package/dist/solution/BuyTickets/index.js +9 -12
- package/dist/solution/Checkout/index.d.ts +1 -0
- package/dist/solution/Checkout/index.js +199 -277
- package/dist/solution/Checkout/utils/index.js +8 -20
- package/dist/solution/RegisterAndLogin/config.js +2 -10
- package/dist/solution/RegisterAndLogin/index.js +30 -76
- package/dist/solution/Sales/index.d.ts +50 -5
- package/dist/solution/Sales/index.js +484 -92
- package/dist/solution/Sales/types.d.ts +26 -2
- package/dist/solution/ScanOrder/index.d.ts +27 -21
- package/dist/solution/ScanOrder/index.js +509 -454
- package/dist/solution/ScanOrder/types.d.ts +33 -11
- package/dist/solution/ScanOrder/types.js +4 -5
- package/dist/solution/ScanOrder/utils.d.ts +4 -1
- package/dist/solution/ScanOrder/utils.js +109 -34
- package/dist/solution/ShopDiscount/index.js +6 -11
- package/dist/solution/UnifiedBookingSales/index.d.ts +183 -0
- package/dist/solution/UnifiedBookingSales/index.js +1871 -0
- package/dist/solution/UnifiedBookingSales/types.d.ts +143 -0
- package/dist/solution/UnifiedBookingSales/types.js +11 -0
- package/dist/solution/VenueBooking/index.d.ts +34 -20
- package/dist/solution/VenueBooking/index.js +630 -505
- package/dist/solution/VenueBooking/sales-section-4-annotated.json +0 -2
- package/dist/solution/VenueBooking/utils/slotMerge.d.ts +6 -0
- package/dist/solution/VenueBooking/utils/slotMerge.js +9 -3
- package/dist/solution/index.d.ts +2 -0
- package/dist/solution/index.js +3 -1
- package/dist/types/index.d.ts +11 -1
- package/dist/utils/task.js +15 -18
- package/dist/utils/watch.js +0 -1
- package/lib/apis/picoding.js +2 -0
- package/lib/core/index.js +130 -131
- package/lib/effects/index.js +46 -57
- package/lib/index.js +92 -53
- package/lib/model/index.js +14 -21
- package/lib/model/strategy/adapter/dataVariant/adapter.js +38 -50
- package/lib/model/strategy/adapter/dataVariant/evaluator.js +162 -236
- package/lib/model/strategy/adapter/dataVariant/examples.js +229 -237
- package/lib/model/strategy/adapter/dataVariant/index.js +39 -34
- package/lib/model/strategy/adapter/dataVariant/type.js +57 -28
- package/lib/model/strategy/adapter/index.js +100 -64
- package/lib/model/strategy/adapter/itemRule/adapter.js +156 -144
- package/lib/model/strategy/adapter/itemRule/evaluator.js +36 -48
- package/lib/model/strategy/adapter/itemRule/examples.js +295 -225
- package/lib/model/strategy/adapter/itemRule/index.js +83 -57
- package/lib/model/strategy/adapter/itemRule/type.js +97 -36
- package/lib/model/strategy/adapter/promotion/adapter.js +84 -63
- package/lib/model/strategy/adapter/promotion/evaluator.js +383 -232
- package/lib/model/strategy/adapter/promotion/examples.js +139 -159
- package/lib/model/strategy/adapter/promotion/index.js +1 -49
- package/lib/model/strategy/adapter/promotion/type.js +199 -35
- package/lib/model/strategy/adapter/type.js +4 -16
- package/lib/model/strategy/adapter/walletPass/evaluator.js +180 -152
- package/lib/model/strategy/adapter/walletPass/example.js +217 -190
- package/lib/model/strategy/adapter/walletPass/index.js +75 -51
- package/lib/model/strategy/adapter/walletPass/locales.js +36 -58
- package/lib/model/strategy/adapter/walletPass/type.js +4 -16
- package/lib/model/strategy/adapter/walletPass/utils.js +654 -402
- package/lib/model/strategy/engine.js +270 -168
- package/lib/model/strategy/index.js +49 -34
- package/lib/model/strategy/strategy-example.js +243 -239
- package/lib/model/strategy/type.js +100 -40
- package/lib/modules/Account/index.js +39 -53
- package/lib/modules/Account/types.js +20 -33
- package/lib/modules/AccountList/index.js +116 -154
- package/lib/modules/AccountList/types.js +30 -31
- package/lib/modules/AccountList/utils.js +18 -30
- package/lib/modules/BaseModule.d.ts +2 -2
- package/lib/modules/BaseModule.js +26 -49
- package/lib/modules/BookingContext/index.d.ts +48 -0
- package/lib/modules/BookingContext/index.js +390 -0
- package/lib/modules/BookingContext/types.d.ts +102 -0
- package/lib/modules/BookingContext/types.js +48 -0
- package/lib/modules/BookingContext/utils/buildCacheItemFromOrderLine.d.ts +16 -0
- package/lib/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +286 -0
- package/lib/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.d.ts +8 -0
- package/lib/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +148 -0
- package/lib/modules/BookingContext/utils/cacheItemToBookingInput.d.ts +76 -0
- package/lib/modules/BookingContext/utils/cacheItemToBookingInput.js +242 -0
- package/lib/modules/BookingContext/utils/flexible.d.ts +28 -0
- package/lib/modules/BookingContext/utils/flexible.js +63 -0
- package/lib/modules/BookingContext/utils/formatResourceList.d.ts +8 -0
- package/lib/modules/BookingContext/utils/formatResourceList.js +35 -0
- package/lib/modules/BookingContext/utils/index.d.ts +11 -0
- package/lib/modules/BookingContext/utils/index.js +126 -0
- package/lib/modules/BookingContext/utils/noResource.d.ts +13 -0
- package/lib/modules/BookingContext/utils/noResource.js +78 -0
- package/lib/modules/BookingContext/utils/orderLineDisplay.d.ts +1 -0
- package/lib/modules/BookingContext/utils/orderLineDisplay.js +29 -0
- package/lib/modules/BookingContext/utils/productExtend.d.ts +72 -0
- package/lib/modules/BookingContext/utils/productExtend.js +330 -0
- package/lib/modules/BookingContext/utils/resourceErrors.d.ts +41 -0
- package/lib/modules/BookingContext/utils/resourceErrors.js +80 -0
- package/lib/modules/BookingContext/utils/resourceSelection.d.ts +7 -0
- package/lib/modules/BookingContext/utils/resourceSelection.js +24 -0
- package/lib/modules/BookingContext/utils/resources.d.ts +80 -0
- package/lib/modules/BookingContext/utils/resources.js +419 -0
- package/lib/modules/BookingContext/utils/serviceTimes.d.ts +55 -0
- package/lib/modules/BookingContext/utils/serviceTimes.js +161 -0
- package/lib/modules/BookingContext/utils/timeSlices.d.ts +42 -0
- package/lib/modules/BookingContext/utils/timeSlices.js +114 -0
- package/lib/modules/Cart/index.js +170 -124
- package/lib/modules/Cart/types.js +46 -51
- package/lib/modules/Cart/utils/cartAccount.js +40 -39
- package/lib/modules/Cart/utils/cartDate.js +56 -61
- package/lib/modules/Cart/utils/cartDiscount.js +28 -33
- package/lib/modules/Cart/utils/cartNote.js +27 -32
- package/lib/modules/Cart/utils/cartProduct.js +331 -244
- package/lib/modules/Cart/utils/cartRelationForms.js +29 -35
- package/lib/modules/Cart/utils/cartResource.js +72 -78
- package/lib/modules/Cart/utils/changePrice.js +37 -67
- package/lib/modules/Cart/utils/index.js +106 -48
- package/lib/modules/Customer/constants.js +13 -34
- package/lib/modules/Customer/index.d.ts +20 -0
- package/lib/modules/Customer/index.js +270 -154
- package/lib/modules/Customer/types.d.ts +2 -0
- package/lib/modules/Customer/types.js +38 -35
- package/lib/modules/Date/index.js +116 -115
- package/lib/modules/Date/types.js +16 -28
- package/lib/modules/Date/utils.js +174 -123
- package/lib/modules/Discount/index.d.ts +2 -1
- package/lib/modules/Discount/index.js +133 -113
- package/lib/modules/Discount/types.d.ts +1 -0
- package/lib/modules/Discount/types.js +9 -31
- package/lib/modules/Guests/index.js +30 -56
- package/lib/modules/Guests/types.js +23 -33
- package/lib/modules/Holder/index.js +209 -189
- package/lib/modules/Holder/types.js +4 -16
- package/lib/modules/Holder/utils.js +20 -49
- package/lib/modules/OpenData/index.d.ts +8 -0
- package/lib/modules/OpenData/index.js +81 -74
- package/lib/modules/OpenData/types.js +4 -16
- package/lib/modules/OpenData/utils.js +44 -78
- package/lib/modules/Order/index.d.ts +449 -24
- package/lib/modules/Order/index.js +3834 -489
- package/lib/modules/Order/types.d.ts +693 -88
- package/lib/modules/Order/types.js +78 -33
- package/lib/modules/Order/utils/bundleDiscountHydration.d.ts +5 -0
- package/lib/modules/Order/utils/bundleDiscountHydration.js +106 -0
- package/lib/modules/Order/utils/manualProductDiscount.d.ts +106 -0
- package/lib/modules/Order/utils/manualProductDiscount.js +207 -0
- package/lib/modules/Order/utils/orderCollectionIdentity.d.ts +53 -0
- package/lib/modules/Order/utils/orderCollectionIdentity.js +353 -0
- package/lib/modules/Order/utils.d.ts +167 -18
- package/lib/modules/Order/utils.js +1247 -345
- package/lib/modules/Payment/cash.js +20 -33
- package/lib/modules/Payment/cashRecommendationAlgorithm.js +145 -76
- package/lib/modules/Payment/eftpos.js +16 -30
- package/lib/modules/Payment/index.d.ts +10 -9
- package/lib/modules/Payment/index.js +542 -502
- package/lib/modules/Payment/mx51.js +1 -0
- package/lib/modules/Payment/types.d.ts +40 -16
- package/lib/modules/Payment/types.js +216 -107
- package/lib/modules/Payment/utils.d.ts +1 -1
- package/lib/modules/Payment/utils.js +53 -49
- package/lib/modules/Payment/walletpass.js +231 -203
- package/lib/modules/Product/index.js +51 -46
- package/lib/modules/Product/types.d.ts +29 -7
- package/lib/modules/Product/types.js +4 -16
- package/lib/modules/Product/utils.js +32 -33
- package/lib/modules/ProductList/index.d.ts +17 -13
- package/lib/modules/ProductList/index.js +160 -118
- package/lib/modules/ProductList/types.d.ts +17 -0
- package/lib/modules/ProductList/types.js +9 -31
- package/lib/modules/Quotation/index.d.ts +22 -2
- package/lib/modules/Quotation/index.js +123 -89
- package/lib/modules/Quotation/types.d.ts +19 -0
- package/lib/modules/Quotation/types.js +4 -16
- package/lib/modules/Resource/index.js +27 -59
- package/lib/modules/Resource/types.js +22 -32
- package/lib/modules/Resource/utils.js +30 -38
- package/lib/modules/ResourcePlanner/index.d.ts +24 -0
- package/lib/modules/ResourcePlanner/index.js +147 -0
- package/lib/modules/ResourcePlanner/planner.d.ts +14 -0
- package/lib/modules/ResourcePlanner/planner.js +895 -0
- package/lib/modules/ResourcePlanner/types.d.ts +227 -0
- package/lib/modules/ResourcePlanner/types.js +5 -0
- package/lib/modules/Rules/index.d.ts +8 -10
- package/lib/modules/Rules/index.js +1286 -894
- package/lib/modules/Rules/types.d.ts +14 -3
- package/lib/modules/Rules/types.js +17 -40
- package/lib/modules/SalesSummary/index.d.ts +23 -26
- package/lib/modules/SalesSummary/index.js +130 -71
- package/lib/modules/SalesSummary/types.d.ts +9 -1
- package/lib/modules/SalesSummary/types.js +4 -16
- package/lib/modules/SalesSummary/utils.d.ts +3 -22
- package/lib/modules/SalesSummary/utils.js +738 -250
- package/lib/modules/ScanOrderLogger/index.js +59 -79
- package/lib/modules/ScanOrderLogger/providers/feishu.js +54 -78
- package/lib/modules/ScanOrderLogger/providers/grafana.js +13 -36
- package/lib/modules/ScanOrderLogger/types.js +4 -16
- package/lib/modules/Schedule/getDateIsInSchedule.js +345 -171
- package/lib/modules/Schedule/index.d.ts +18 -5
- package/lib/modules/Schedule/index.js +139 -114
- package/lib/modules/Schedule/type.js +4 -16
- package/lib/modules/Schedule/types.d.ts +2 -0
- package/lib/modules/Schedule/types.js +4 -16
- package/lib/modules/Schedule/utils.js +433 -291
- package/lib/modules/Step/index.js +40 -52
- package/lib/modules/Step/tyeps.js +4 -16
- package/lib/modules/Summary/index.js +66 -70
- package/lib/modules/Summary/types.js +4 -16
- package/lib/modules/Summary/utils.js +782 -429
- package/lib/modules/SurchargeList/index.d.ts +18 -0
- package/lib/modules/SurchargeList/index.js +95 -0
- package/lib/modules/SurchargeList/types.d.ts +15 -0
- package/lib/modules/SurchargeList/types.js +5 -0
- package/lib/modules/index.d.ts +3 -0
- package/lib/modules/index.js +245 -57
- package/lib/plugins/app.d.ts +2 -0
- package/lib/plugins/app.js +4 -16
- package/lib/plugins/index.js +36 -25
- package/lib/plugins/request.d.ts +1 -0
- package/lib/plugins/request.js +137 -126
- package/lib/plugins/shopStore.js +4 -16
- package/lib/plugins/user.js +4 -16
- package/lib/plugins/window.js +171 -179
- package/lib/server/index.d.ts +462 -8
- package/lib/server/index.js +4668 -713
- package/lib/server/modules/floor-plan/index.d.ts +39 -0
- package/lib/server/modules/floor-plan/index.js +306 -0
- package/lib/server/modules/floor-plan/types.d.ts +43 -0
- package/lib/server/modules/floor-plan/types.js +19 -0
- package/lib/server/modules/index.d.ts +7 -2
- package/lib/server/modules/index.js +106 -59
- package/lib/server/modules/menu/index.js +146 -122
- package/lib/server/modules/menu/types.js +18 -31
- package/lib/server/modules/order/index.d.ts +207 -14
- package/lib/server/modules/order/index.js +1463 -316
- package/lib/server/modules/order/types.d.ts +76 -5
- package/lib/server/modules/order/types.js +117 -32
- package/lib/server/modules/order/utils/filterBookings.d.ts +7 -1
- package/lib/server/modules/order/utils/filterBookings.js +288 -146
- package/lib/server/modules/order/utils/filterOrders.js +318 -64
- package/lib/server/modules/payment/index.d.ts +28 -0
- package/lib/server/modules/payment/index.js +168 -0
- package/lib/server/modules/payment/types.d.ts +9 -0
- package/lib/server/modules/payment/types.js +5 -0
- package/lib/server/modules/products/index.d.ts +105 -14
- package/lib/server/modules/products/index.js +969 -383
- package/lib/server/modules/products/types.d.ts +34 -1
- package/lib/server/modules/products/types.js +44 -34
- package/lib/server/modules/quotation/index.d.ts +39 -0
- package/lib/server/modules/quotation/index.js +220 -77
- package/lib/server/modules/quotation/types.js +21 -31
- package/lib/server/modules/resource/index.d.ts +2 -3
- package/lib/server/modules/resource/index.js +269 -214
- package/lib/server/modules/resource/types.js +42 -33
- package/lib/server/modules/schedule/index.d.ts +6 -4
- package/lib/server/modules/schedule/index.js +158 -132
- package/lib/server/modules/schedule/types.js +14 -21
- package/lib/server/modules/schedule/utils.js +334 -163
- package/lib/server/test.json +713 -0
- package/lib/server/types.d.ts +2 -0
- package/lib/server/types.js +4 -16
- package/lib/server/utils/index.js +25 -23
- package/lib/server/utils/product.js +197 -140
- package/lib/server/utils/schedule.js +34 -41
- package/lib/server/utils/small-ticket.d.ts +71 -0
- package/lib/server/utils/small-ticket.js +926 -0
- package/lib/server/utils/time.js +40 -49
- package/lib/solution/BaseSales/index.d.ts +378 -0
- package/lib/solution/BaseSales/index.js +2367 -0
- package/lib/solution/BaseSales/types.d.ts +394 -0
- package/lib/solution/BaseSales/types.js +47 -0
- package/lib/solution/BaseSales/utils/cartPromotion.d.ts +287 -0
- package/lib/solution/BaseSales/utils/cartPromotion.js +1155 -0
- package/lib/solution/BaseSales/utils/parseSalesResponse.d.ts +144 -0
- package/lib/solution/BaseSales/utils/parseSalesResponse.js +140 -0
- package/lib/solution/BaseSales/utils/quotationPrice.d.ts +18 -0
- package/lib/solution/BaseSales/utils/quotationPrice.js +231 -0
- package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.d.ts +63 -0
- package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +215 -0
- package/lib/solution/BaseSales/utils.d.ts +40 -0
- package/lib/solution/BaseSales/utils.js +259 -0
- package/lib/solution/BookingByStep/index.js +1530 -1343
- package/lib/solution/BookingByStep/types.js +40 -99
- package/lib/solution/BookingByStep/utils/capacity.js +192 -160
- package/lib/solution/BookingByStep/utils/products.js +42 -52
- package/lib/solution/BookingByStep/utils/resources.js +527 -330
- package/lib/solution/BookingByStep/utils/stock.js +65 -44
- package/lib/solution/BookingByStep/utils/timeslots.js +129 -131
- package/lib/solution/BookingTicket/index.d.ts +393 -24
- package/lib/solution/BookingTicket/index.js +1459 -160
- package/lib/solution/BookingTicket/types.d.ts +191 -6
- package/lib/solution/BookingTicket/types.js +101 -66
- package/lib/solution/BookingTicket/utils/addProductDecision.d.ts +117 -0
- package/lib/solution/BookingTicket/utils/addProductDecision.js +353 -0
- package/lib/solution/BookingTicket/utils/addTimeAvailability.d.ts +35 -0
- package/lib/solution/BookingTicket/utils/addTimeAvailability.js +94 -0
- package/lib/solution/BookingTicket/utils/bookingStatus.d.ts +77 -0
- package/lib/solution/BookingTicket/utils/bookingStatus.js +136 -0
- package/lib/solution/BookingTicket/utils/cartView.d.ts +29 -0
- package/lib/solution/BookingTicket/utils/cartView.js +200 -0
- package/lib/solution/BookingTicket/utils/exampleData.d.ts +123 -0
- package/lib/solution/BookingTicket/utils/exampleData.js +185 -0
- package/lib/solution/BookingTicket/utils/orderCustomer.d.ts +4 -0
- package/lib/solution/BookingTicket/utils/orderCustomer.js +35 -0
- package/lib/solution/BookingTicket/utils/resolveBestAddTimePlan.d.ts +185 -0
- package/lib/solution/BookingTicket/utils/resolveBestAddTimePlan.js +387 -0
- package/lib/solution/BookingTicket/utils/scan/applyGlobalScan.d.ts +36 -0
- package/lib/solution/BookingTicket/utils/scan/applyGlobalScan.js +220 -0
- package/lib/solution/BookingTicket/utils/scan/cloudSearch.d.ts +4 -0
- package/lib/solution/BookingTicket/utils/scan/cloudSearch.js +85 -80
- package/lib/solution/BookingTicket/utils/scan/formatGlobalScan.d.ts +32 -0
- package/lib/solution/BookingTicket/utils/scan/formatGlobalScan.js +64 -0
- package/lib/solution/BookingTicket/utils/scan/handleScan.d.ts +3 -1
- package/lib/solution/BookingTicket/utils/scan/handleScan.js +84 -80
- package/lib/solution/BookingTicket/utils/scan/index.d.ts +5 -0
- package/lib/solution/BookingTicket/utils/scan/index.js +103 -95
- package/lib/solution/BookingTicket/utils/scan/scanCache.js +90 -43
- package/lib/solution/BookingTicket/utils/scan/scanProductValues.d.ts +21 -0
- package/lib/solution/BookingTicket/utils/scan/scanProductValues.js +115 -0
- package/lib/solution/BookingTicket/utils/sessionCatalogStale.d.ts +23 -0
- package/lib/solution/BookingTicket/utils/sessionCatalogStale.js +83 -0
- package/lib/solution/BuyTickets/index.js +67 -70
- package/lib/solution/BuyTickets/types.js +22 -38
- package/lib/solution/Checkout/index.d.ts +1 -0
- package/lib/solution/Checkout/index.js +1461 -1169
- package/lib/solution/Checkout/types.js +91 -61
- package/lib/solution/Checkout/utils/index.js +228 -159
- package/lib/solution/PlaceOrder/index.js +55 -0
- package/lib/solution/RegisterAndLogin/config.js +495 -470
- package/lib/solution/RegisterAndLogin/index.js +633 -630
- package/lib/solution/RegisterAndLogin/types.js +189 -76
- package/lib/solution/RegisterAndLogin/utils.js +183 -125
- package/lib/solution/Sales/index.d.ts +50 -5
- package/lib/solution/Sales/index.js +495 -220
- package/lib/solution/Sales/types.d.ts +26 -2
- package/lib/solution/Sales/types.js +27 -33
- package/lib/solution/ScanOrder/index.d.ts +27 -21
- package/lib/solution/ScanOrder/index.js +908 -904
- package/lib/solution/ScanOrder/types.d.ts +33 -11
- package/lib/solution/ScanOrder/types.js +33 -34
- package/lib/solution/ScanOrder/utils.d.ts +4 -1
- package/lib/solution/ScanOrder/utils.js +457 -344
- package/lib/solution/ShopDiscount/index.js +230 -236
- package/lib/solution/ShopDiscount/types.js +15 -37
- package/lib/solution/ShopDiscount/utils.js +309 -172
- package/lib/solution/UnifiedBookingSales/index.d.ts +183 -0
- package/lib/solution/UnifiedBookingSales/index.js +1050 -0
- package/lib/solution/UnifiedBookingSales/types.d.ts +143 -0
- package/lib/solution/UnifiedBookingSales/types.js +14 -0
- package/lib/solution/VenueBooking/index.d.ts +34 -20
- package/lib/solution/VenueBooking/index.js +982 -950
- package/lib/solution/VenueBooking/sales-section-4-annotated.json +0 -2
- package/lib/solution/VenueBooking/types.js +20 -40
- package/lib/solution/VenueBooking/utils/dateSummary.js +22 -54
- package/lib/solution/VenueBooking/utils/resource.js +31 -54
- package/lib/solution/VenueBooking/utils/slotMerge.d.ts +6 -0
- package/lib/solution/VenueBooking/utils/slotMerge.js +96 -108
- package/lib/solution/VenueBooking/utils/smartPricing.js +112 -100
- package/lib/solution/VenueBooking/utils/timeSlot.js +133 -132
- package/lib/solution/VenueBooking/utils.js +130 -67
- package/lib/solution/index.d.ts +2 -0
- package/lib/solution/index.js +124 -37
- package/lib/store/createStore.js +32 -29
- package/lib/types/index.d.ts +11 -1
- package/lib/types/index.js +4 -16
- package/lib/utils/task.js +36 -36
- package/lib/utils/watch.js +81 -47
- package/package.json +2 -1
- package/dist/solution/Checkout/appointmentDemo.json +0 -1
- package/lib/solution/Checkout/appointmentDemo.json +0 -1
|
@@ -1,58 +1,32 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
-
var __export = (target, all) => {
|
|
8
|
-
for (var name in all)
|
|
9
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
-
};
|
|
11
|
-
var __copyProps = (to, from, except, desc) => {
|
|
12
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
-
for (let key of __getOwnPropNames(from))
|
|
14
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
-
}
|
|
17
|
-
return to;
|
|
18
|
-
};
|
|
19
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
20
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
21
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
22
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
23
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
24
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
25
|
-
mod
|
|
26
|
-
));
|
|
27
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
|
-
|
|
29
|
-
// src/modules/Rules/index.ts
|
|
30
|
-
var Rules_exports = {};
|
|
31
|
-
__export(Rules_exports, {
|
|
32
|
-
RulesModule: () => RulesModule
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
33
5
|
});
|
|
34
|
-
|
|
35
|
-
var
|
|
36
|
-
var
|
|
37
|
-
var
|
|
38
|
-
var
|
|
39
|
-
var
|
|
40
|
-
var
|
|
41
|
-
var
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
6
|
+
exports.RulesModule = void 0;
|
|
7
|
+
var _BaseModule = require("../BaseModule");
|
|
8
|
+
var _types = require("./types");
|
|
9
|
+
var _utils = require("../../solution/ShopDiscount/utils");
|
|
10
|
+
var _utils2 = require("../Cart/utils");
|
|
11
|
+
var _decimal = _interopRequireDefault(require("decimal.js"));
|
|
12
|
+
var _lodashEs = require("lodash-es");
|
|
13
|
+
var _dayjs = _interopRequireDefault(require("dayjs"));
|
|
14
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
15
|
+
class RulesModule extends _BaseModule.BaseModule {
|
|
16
|
+
defaultName = 'rules';
|
|
17
|
+
defaultVersion = '1.0.0';
|
|
18
|
+
store;
|
|
19
|
+
window; // WindowPlugin 实例
|
|
20
|
+
hooks;
|
|
45
21
|
constructor(name, version) {
|
|
46
22
|
super(name, version);
|
|
47
|
-
this.defaultName = "rules";
|
|
48
|
-
this.defaultVersion = "1.0.0";
|
|
49
23
|
this.hooks = {};
|
|
50
24
|
}
|
|
51
25
|
async initialize(core, options) {
|
|
52
26
|
this.core = core;
|
|
53
|
-
this.hooks = options
|
|
54
|
-
this.store = options
|
|
55
|
-
this.window = core.getPlugin(
|
|
27
|
+
this.hooks = options?.hooks;
|
|
28
|
+
this.store = options?.store;
|
|
29
|
+
this.window = core.getPlugin('window');
|
|
56
30
|
}
|
|
57
31
|
async setRulesList(rulesList) {
|
|
58
32
|
this.store.rulesList = rulesList;
|
|
@@ -62,22 +36,25 @@ var RulesModule = class extends import_BaseModule.BaseModule {
|
|
|
62
36
|
return this.store.rulesList;
|
|
63
37
|
}
|
|
64
38
|
getWalletPassEvaluator() {
|
|
65
|
-
|
|
66
|
-
return (_b = (_a = this.window).getWalletPassEvaluator) == null ? void 0 : _b.call(_a);
|
|
39
|
+
return this.window.getWalletPassEvaluator?.();
|
|
67
40
|
}
|
|
41
|
+
|
|
68
42
|
// 商品不需要holder,则不需要判断,直接返回true,商品需要holder但是还没填写,那么暂时不使用带有holder的券,直到填写才去匹配
|
|
69
43
|
checkHolderMatch(discount, product, holders) {
|
|
70
|
-
|
|
71
|
-
if (
|
|
72
|
-
|
|
73
|
-
|
|
44
|
+
// 非表单类型 holder 视为匹配
|
|
45
|
+
if (discount.holder?.holder_type !== 'form') return true;
|
|
46
|
+
// 主预约holder, 目前(20251124)默认只考虑单个holder的情况
|
|
47
|
+
// 旧版 holders[0].form_record_id 新版 holders[0]
|
|
48
|
+
const orderHolderId = Array.isArray(holders) && holders.length > 0 ? holders[0].form_record_id || holders[0] : undefined;
|
|
74
49
|
const productHolderId = Array.isArray(product.holder_id) ? product.holder_id[0] : product.holder_id;
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
50
|
+
// 商品不需要选holder,则不对比holder,直接返回true,
|
|
51
|
+
if (!product.isNeedHolder) return true;
|
|
52
|
+
// 商品需要holder,但父预约及商品都无holder,返回false
|
|
53
|
+
if (!orderHolderId && !productHolderId) return false;
|
|
54
|
+
// 最终直接匹配 holder 是否相同
|
|
79
55
|
return (productHolderId || orderHolderId) === discount.holder.holder_id;
|
|
80
56
|
}
|
|
57
|
+
|
|
81
58
|
// 判断discountList 是否可以对当前productList生效
|
|
82
59
|
isDiscountListAvailable({
|
|
83
60
|
oldDiscountList,
|
|
@@ -87,6 +64,7 @@ var RulesModule = class extends import_BaseModule.BaseModule {
|
|
|
87
64
|
holders,
|
|
88
65
|
isFormSubject
|
|
89
66
|
}) {
|
|
67
|
+
// 首先检查是否有新的优惠券可应用
|
|
90
68
|
if (!newDiscountList || newDiscountList.length === 0) {
|
|
91
69
|
return {
|
|
92
70
|
isAvailable: false,
|
|
@@ -94,54 +72,58 @@ var RulesModule = class extends import_BaseModule.BaseModule {
|
|
|
94
72
|
productList
|
|
95
73
|
};
|
|
96
74
|
}
|
|
97
|
-
|
|
98
|
-
|
|
75
|
+
|
|
76
|
+
// 编辑商品已有商品券
|
|
77
|
+
if (productList.every(item => {
|
|
99
78
|
const product = this.hooks.getProduct(item);
|
|
100
|
-
return product.booking_id && (
|
|
79
|
+
return product.booking_id && (product.discount_list?.length || product.total == 0);
|
|
101
80
|
})) {
|
|
102
81
|
return {
|
|
103
82
|
isAvailable: false,
|
|
104
83
|
discountList: oldDiscountList,
|
|
105
84
|
productList,
|
|
106
|
-
unavailableReason:
|
|
85
|
+
unavailableReason: _types.UnavailableReason.AlreadyUsed
|
|
107
86
|
};
|
|
108
87
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
...newDiscountList
|
|
115
|
-
]);
|
|
88
|
+
|
|
89
|
+
// 合并新旧折扣列表,并计算折扣结果,注意,如果旧折扣里有 isEditMode 为 true 的优惠券,则不合并
|
|
90
|
+
// 保留之前扫码得到的商品券(isScan: true)
|
|
91
|
+
const filteredOldDiscountList = oldDiscountList.filter(discount => !discount.isEditMode && (discount.tag !== 'good_pass' || discount.isScan));
|
|
92
|
+
const mergedDiscountList = (0, _utils.uniqueById)([...filteredOldDiscountList, ...newDiscountList]);
|
|
116
93
|
const result = this.calcDiscount({
|
|
117
94
|
discountList: mergedDiscountList,
|
|
118
95
|
productList: [...productList],
|
|
119
|
-
holders,
|
|
120
96
|
orderTotalAmount,
|
|
97
|
+
holders,
|
|
121
98
|
isFormSubject
|
|
122
99
|
}, {
|
|
123
100
|
scan: true
|
|
124
101
|
});
|
|
102
|
+
|
|
103
|
+
// 检查是否有产品使用了新折扣
|
|
125
104
|
let hasApplicableDiscount = false;
|
|
126
|
-
const newDiscountIds = newDiscountList.map(
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
105
|
+
const newDiscountIds = newDiscountList.map(discount => discount.id);
|
|
106
|
+
|
|
107
|
+
// 遍历处理后的产品列表,检查是否有产品使用了新折扣
|
|
108
|
+
result.productList.forEach(product => {
|
|
109
|
+
const {
|
|
110
|
+
discount_list,
|
|
111
|
+
bundle
|
|
112
|
+
} = this.hooks.getProduct(product);
|
|
113
|
+
const allDiscountList = [...(discount_list || [])];
|
|
114
|
+
(bundle || []).forEach(item => {
|
|
115
|
+
allDiscountList.push(...(item?.discount_list || []));
|
|
132
116
|
});
|
|
133
117
|
if (allDiscountList && allDiscountList.length > 0) {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
var _a;
|
|
137
|
-
return newDiscountIds.includes((_a = discount == null ? void 0 : discount.discount) == null ? void 0 : _a.resource_id);
|
|
138
|
-
}
|
|
139
|
-
);
|
|
118
|
+
// 检查是否有使用新折扣的情况
|
|
119
|
+
const usedNewDiscount = allDiscountList.some(discount => newDiscountIds.includes(discount?.discount?.resource_id));
|
|
140
120
|
if (usedNewDiscount) {
|
|
141
121
|
hasApplicableDiscount = true;
|
|
142
122
|
}
|
|
143
123
|
}
|
|
144
124
|
});
|
|
125
|
+
|
|
126
|
+
// 如果券不可用,判断不可用原因
|
|
145
127
|
let unavailableReason;
|
|
146
128
|
if (!hasApplicableDiscount) {
|
|
147
129
|
unavailableReason = this.getUnavailableReason(newDiscountList, productList);
|
|
@@ -154,39 +136,51 @@ var RulesModule = class extends import_BaseModule.BaseModule {
|
|
|
154
136
|
};
|
|
155
137
|
}
|
|
156
138
|
filterDiscountListByType(discountList, type) {
|
|
157
|
-
return (discountList || []).filter(
|
|
139
|
+
return (discountList || []).filter(item => item.type === type || item.tag === type);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** 剔除指定 type/tag 的折扣项,保留手动改价(type=product) / 券等 */
|
|
143
|
+
excludeDiscountListByType(discountList, type) {
|
|
144
|
+
return (discountList || []).filter(item => item.type !== type && item.tag !== type);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** 手动改价场景:去掉 promotion,保留 type=product 等行内折扣 */
|
|
148
|
+
resolveDiscountListForManualOverride(discountList) {
|
|
149
|
+
return this.excludeDiscountListByType(discountList, 'promotion');
|
|
158
150
|
}
|
|
159
151
|
// 获取券不可用的原因
|
|
160
152
|
getUnavailableReason(discountList, productList) {
|
|
161
|
-
|
|
153
|
+
// 检查时间限制
|
|
162
154
|
for (const discount of discountList) {
|
|
163
155
|
for (const item of productList) {
|
|
164
156
|
const product = this.hooks.getProduct(item);
|
|
165
|
-
const bookingTime = (
|
|
166
|
-
const filteredList = (0,
|
|
157
|
+
const bookingTime = (product?.startDate || (0, _dayjs.default)()).format('YYYY-MM-DD HH:mm:ss');
|
|
158
|
+
const filteredList = (0, _utils.filterDiscountListByBookingTime)([discount], bookingTime);
|
|
167
159
|
if (filteredList.length === 0) {
|
|
168
|
-
return
|
|
160
|
+
return _types.UnavailableReason.TimeLimit;
|
|
169
161
|
}
|
|
170
162
|
}
|
|
171
163
|
}
|
|
164
|
+
|
|
165
|
+
// 检查商品适用性
|
|
172
166
|
for (const discount of discountList) {
|
|
173
167
|
const limitedData = discount.limited_relation_product_data;
|
|
174
168
|
let hasApplicableProduct = false;
|
|
175
169
|
for (const item of productList) {
|
|
176
170
|
const product = this.hooks.getProduct(item);
|
|
177
|
-
if (limitedData.type ===
|
|
171
|
+
if (limitedData.type === 'product_all') {
|
|
178
172
|
hasApplicableProduct = true;
|
|
179
173
|
break;
|
|
180
|
-
} else if (
|
|
174
|
+
} else if (limitedData.product_ids?.includes(product.id)) {
|
|
181
175
|
hasApplicableProduct = true;
|
|
182
176
|
break;
|
|
183
177
|
}
|
|
184
178
|
}
|
|
185
179
|
if (!hasApplicableProduct) {
|
|
186
|
-
return
|
|
180
|
+
return _types.UnavailableReason.ProductNotApplicable;
|
|
187
181
|
}
|
|
188
182
|
}
|
|
189
|
-
return
|
|
183
|
+
return _types.UnavailableReason.Unknown;
|
|
190
184
|
}
|
|
191
185
|
calcDiscount({
|
|
192
186
|
discountList,
|
|
@@ -195,38 +189,84 @@ var RulesModule = class extends import_BaseModule.BaseModule {
|
|
|
195
189
|
isFormSubject,
|
|
196
190
|
orderTotalAmount
|
|
197
191
|
}, options) {
|
|
192
|
+
const resolveIsFormSubject = product => typeof isFormSubject === 'function' ? Boolean(isFormSubject(product)) : Boolean(isFormSubject);
|
|
193
|
+
|
|
194
|
+
// 识别出来是不是在编辑的界面里又新增了商品
|
|
195
|
+
// 这种情况下,如果有可用的优惠券,也会自动勾选上
|
|
196
|
+
const isEditModeAddNewProduct = productList.find(n => n.booking_id) && productList.find(n => !n.booking_id);
|
|
197
|
+
const reapplyBookingDiscountProductUids = new Set((options?.reapplyBookingDiscountProductUids || []).filter(uid => uid !== undefined && uid !== null && uid !== '').map(String));
|
|
198
|
+
const getOriginProductUid = originProduct => {
|
|
199
|
+
const uid = originProduct?.metadata?.unique_identification_number ?? originProduct?.unique_identification_number;
|
|
200
|
+
if (uid === undefined || uid === null || uid === '') return undefined;
|
|
201
|
+
return String(uid);
|
|
202
|
+
};
|
|
203
|
+
const shouldReapplyBookingDiscount = (originProduct, selectedDiscount) => {
|
|
204
|
+
if (!selectedDiscount) return false;
|
|
205
|
+
const uid = getOriginProductUid(originProduct);
|
|
206
|
+
if (!uid || !reapplyBookingDiscountProductUids.has(uid)) return false;
|
|
207
|
+
const discountType = selectedDiscount.tag || selectedDiscount.type;
|
|
208
|
+
return selectedDiscount.isEditMode === true && selectedDiscount.isSelected === true && ['good_pass', 'discount_card', 'product_discount_card'].includes(discountType);
|
|
209
|
+
};
|
|
210
|
+
const resolveReapplyEditModeDiscountPercent = (originProduct, selectedDiscount) => {
|
|
211
|
+
if (!shouldReapplyBookingDiscount(originProduct, selectedDiscount)) return undefined;
|
|
212
|
+
const discountType = selectedDiscount?.tag || selectedDiscount?.type;
|
|
213
|
+
if (discountType !== 'discount_card' && discountType !== 'product_discount_card') {
|
|
214
|
+
return undefined;
|
|
215
|
+
}
|
|
216
|
+
const percent = Number(selectedDiscount.discount?.percent);
|
|
217
|
+
return Number.isFinite(percent) ? percent : undefined;
|
|
218
|
+
};
|
|
198
219
|
const editModeDiscount = [];
|
|
199
220
|
const addModeDiscount = [];
|
|
200
|
-
discountList.forEach(
|
|
221
|
+
discountList.forEach(discount => {
|
|
201
222
|
if (discount.isEditMode) {
|
|
202
223
|
editModeDiscount.push(discount);
|
|
203
224
|
} else {
|
|
204
225
|
addModeDiscount.push(discount);
|
|
205
226
|
}
|
|
206
227
|
});
|
|
207
|
-
|
|
208
|
-
editModeDiscount
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
228
|
+
|
|
229
|
+
// 🔥 检查 editModeDiscount 中是否有订单级别的折扣,如果有则禁用 addModeDiscount 中相同 product_id 的折扣卡
|
|
230
|
+
const editModeOrderLevelProductIds = new Set();
|
|
231
|
+
editModeDiscount.forEach(discount => {
|
|
232
|
+
if (discount.discount?.discount_calculation_mode === 'order_level') {
|
|
233
|
+
editModeOrderLevelProductIds.add(discount?.discount?.discount_product_id);
|
|
212
234
|
}
|
|
213
235
|
});
|
|
236
|
+
|
|
237
|
+
// 如果存在订单级别的编辑模式折扣,禁用 addModeDiscount 中相同 product_id 的折扣卡
|
|
214
238
|
if (editModeOrderLevelProductIds.size > 0) {
|
|
215
|
-
addModeDiscount.forEach(
|
|
239
|
+
addModeDiscount.forEach(discount => {
|
|
216
240
|
if (editModeOrderLevelProductIds.has(discount.product_id)) {
|
|
217
241
|
discount.isDisabled = true;
|
|
218
242
|
}
|
|
219
243
|
});
|
|
220
244
|
}
|
|
221
|
-
const
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
245
|
+
const hasDiscountInput = editModeDiscount.length > 0 || addModeDiscount.length > 0 || Boolean(options?.discountId) || Boolean(options?.selectedList?.length) || Boolean(options?.scan);
|
|
246
|
+
if (!hasDiscountInput) {
|
|
247
|
+
return {
|
|
248
|
+
discountList,
|
|
249
|
+
productList
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
const canUseEditModeDiscountForReapply = discount => {
|
|
253
|
+
const discountType = discount.tag || discount.type;
|
|
254
|
+
return reapplyBookingDiscountProductUids.size > 0 && discount.isEditMode === true && discount.isSelected === true && ['good_pass', 'discount_card', 'product_discount_card'].includes(discountType);
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
// 过滤掉手动取消false的优惠券,但仅用于应用优惠逻辑。
|
|
258
|
+
// Change time 重报价需要把已选编辑态折扣卡重新放入候选,再由目标 uid 限定作用范围。
|
|
259
|
+
const filteredDiscountList = [...addModeDiscount.filter(discount => !discount.isManualSelect), ...editModeDiscount.filter(canUseEditModeDiscountForReapply)];
|
|
260
|
+
|
|
261
|
+
// 🔥 扁平化商品列表:将 bundle 子商品展开为虚拟商品
|
|
262
|
+
const flattenProductsWithBundle = productList => {
|
|
225
263
|
const flattened = [];
|
|
226
|
-
|
|
264
|
+
productList.forEach(originProduct => {
|
|
227
265
|
const product = this.hooks.getProduct(originProduct);
|
|
266
|
+
|
|
267
|
+
// 1. 添加主商品
|
|
228
268
|
flattened.push({
|
|
229
|
-
type:
|
|
269
|
+
type: 'main',
|
|
230
270
|
originProduct,
|
|
231
271
|
product,
|
|
232
272
|
price: Number(product.price || 0),
|
|
@@ -237,10 +277,12 @@ var RulesModule = class extends import_BaseModule.BaseModule {
|
|
|
237
277
|
num: product.num,
|
|
238
278
|
booking_id: product.booking_id
|
|
239
279
|
});
|
|
280
|
+
|
|
281
|
+
// 2. 展开 bundle 子商品
|
|
240
282
|
if (product.bundle && Array.isArray(product.bundle) && product.bundle.length > 0) {
|
|
241
283
|
product.bundle.forEach((bundleItem, bundleIndex) => {
|
|
242
284
|
flattened.push({
|
|
243
|
-
type:
|
|
285
|
+
type: 'bundle',
|
|
244
286
|
originProduct,
|
|
245
287
|
parentProduct: product,
|
|
246
288
|
bundleItem,
|
|
@@ -253,8 +295,8 @@ var RulesModule = class extends import_BaseModule.BaseModule {
|
|
|
253
295
|
parentId: product._id,
|
|
254
296
|
num: bundleItem.num || 1,
|
|
255
297
|
quantity: bundleItem.num || 1,
|
|
256
|
-
total: new
|
|
257
|
-
origin_total: new
|
|
298
|
+
total: new _decimal.default(bundleItem.price || 0).mul(bundleItem.num || 1).toNumber(),
|
|
299
|
+
origin_total: new _decimal.default(bundleItem.price || 0).mul(bundleItem.num || 1).toNumber(),
|
|
258
300
|
original_price: bundleItem.original_price,
|
|
259
301
|
// 继承主商品属性
|
|
260
302
|
booking_id: bundleItem.booking_id,
|
|
@@ -265,121 +307,157 @@ var RulesModule = class extends import_BaseModule.BaseModule {
|
|
|
265
307
|
});
|
|
266
308
|
return flattened;
|
|
267
309
|
};
|
|
310
|
+
|
|
311
|
+
// 优惠力度排序,传进来的数据里可能有商品券,也可能有优惠券
|
|
312
|
+
// 新的优先级排序规则(从高到低):
|
|
313
|
+
// 1. 带有Holder的商品券(metadata?.holder?.type === 'custom' && tag === 'good_pass')
|
|
314
|
+
// 2. 带有Holder的商品级折扣卡(metadata?.holder?.type === 'custom' && tag === 'product_discount_card' && discount_calculation_mode !== 'order_level')
|
|
315
|
+
// 3. 带有Holder的订单级折扣卡(metadata?.holder?.type === 'custom' && tag === 'product_discount_card' && discount_calculation_mode === 'order_level')
|
|
316
|
+
// 4. Customer商品券(metadata?.holder?.type !== 'custom' && tag === 'good_pass')
|
|
317
|
+
// 5. Customer商品级折扣卡(metadata?.holder?.type !== 'custom' && tag === 'product_discount_card' && discount_calculation_mode !== 'order_level')
|
|
318
|
+
// 6. Customer订单级固定金额折扣(metadata?.holder?.type !== 'custom' && tag === 'product_discount_card' && discount_calculation_mode === 'order_level')
|
|
319
|
+
// 同一优先级内部:固定金额优先于百分比,金额/折扣力度越大越优先,最后按过期时间排序
|
|
268
320
|
const sortedDiscountList = [...filteredDiscountList].sort((a, b) => {
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
const isGoodPass =
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
const getPriority =
|
|
321
|
+
// 辅助函数:判断是否是带有Holder的优惠券
|
|
322
|
+
const isHolderDiscount = discount => discount.metadata?.holder?.type === 'custom';
|
|
323
|
+
|
|
324
|
+
// 辅助函数:判断是否是商品券
|
|
325
|
+
const isGoodPass = discount => discount.tag === 'good_pass';
|
|
326
|
+
|
|
327
|
+
// 辅助函数:判断是否是订单级折扣卡
|
|
328
|
+
const isOrderLevelDiscount = discount => discount.tag === 'product_discount_card' && discount.metadata?.discount_calculation_mode === 'order_level';
|
|
329
|
+
|
|
330
|
+
// 辅助函数:判断是否是商品级折扣卡
|
|
331
|
+
const isItemLevelDiscount = discount => discount.tag === 'product_discount_card' && discount.metadata?.discount_calculation_mode !== 'order_level';
|
|
332
|
+
|
|
333
|
+
// 辅助函数:获取优先级(数字越小优先级越高)
|
|
334
|
+
const getPriority = discount => {
|
|
283
335
|
const isHolder = isHolderDiscount(discount);
|
|
284
336
|
if (isHolder) {
|
|
285
|
-
if (isGoodPass(discount))
|
|
286
|
-
|
|
287
|
-
if (
|
|
288
|
-
return 2;
|
|
289
|
-
if (isOrderLevelDiscount(discount))
|
|
290
|
-
return 3;
|
|
337
|
+
if (isGoodPass(discount)) return 1; // 带有Holder的商品券
|
|
338
|
+
if (isItemLevelDiscount(discount)) return 2; // 带有Holder的商品级折扣卡
|
|
339
|
+
if (isOrderLevelDiscount(discount)) return 3; // 带有Holder的订单级折扣卡
|
|
291
340
|
} else {
|
|
292
|
-
if (isGoodPass(discount))
|
|
293
|
-
|
|
294
|
-
if (
|
|
295
|
-
return 5;
|
|
296
|
-
if (isOrderLevelDiscount(discount))
|
|
297
|
-
return 6;
|
|
341
|
+
if (isGoodPass(discount)) return 4; // Customer商品券
|
|
342
|
+
if (isItemLevelDiscount(discount)) return 5; // Customer商品级折扣卡
|
|
343
|
+
if (isOrderLevelDiscount(discount)) return 6; // Customer订单级固定金额折扣
|
|
298
344
|
}
|
|
299
|
-
return 7;
|
|
345
|
+
return 7; // 其他情况
|
|
300
346
|
};
|
|
347
|
+
|
|
348
|
+
// 辅助函数:按过期时间比较
|
|
301
349
|
function compareByExpireTime(itemA, itemB) {
|
|
302
350
|
if (itemA.expire_time && itemB.expire_time) {
|
|
303
351
|
const timeA = new Date(itemA.expire_time).getTime();
|
|
304
352
|
const timeB = new Date(itemB.expire_time).getTime();
|
|
305
353
|
return timeA - timeB;
|
|
306
354
|
}
|
|
355
|
+
// 有过期时间的优先级高于永久的
|
|
307
356
|
return itemA.expire_time ? -1 : itemB.expire_time ? 1 : 0;
|
|
308
357
|
}
|
|
358
|
+
|
|
359
|
+
// 辅助函数:同类型折扣卡内部排序(固定金额优先于百分比,金额/折扣力度排序)
|
|
309
360
|
function compareDiscountCardValue(itemA, itemB) {
|
|
310
|
-
|
|
311
|
-
const
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
if (typeA ===
|
|
316
|
-
|
|
317
|
-
|
|
361
|
+
const typeA = itemA.metadata?.discount_card_type || 'percent';
|
|
362
|
+
const typeB = itemB.metadata?.discount_card_type || 'percent';
|
|
363
|
+
|
|
364
|
+
// 1. 固定金额优先于百分比
|
|
365
|
+
if (typeA === 'fixed_amount' && typeB === 'percent') return -1;
|
|
366
|
+
if (typeA === 'percent' && typeB === 'fixed_amount') return 1;
|
|
367
|
+
|
|
368
|
+
// 2. 都是固定金额时,金额越大越优先
|
|
369
|
+
if (typeA === 'fixed_amount' && typeB === 'fixed_amount') {
|
|
318
370
|
if (itemA.par_value !== itemB.par_value) {
|
|
319
|
-
const valueA = new
|
|
320
|
-
const valueB = new
|
|
321
|
-
return valueB.minus(valueA).toNumber();
|
|
371
|
+
const valueA = new _decimal.default(itemA.par_value || 0);
|
|
372
|
+
const valueB = new _decimal.default(itemB.par_value || 0);
|
|
373
|
+
return valueB.minus(valueA).toNumber(); // 金额大的在前
|
|
322
374
|
}
|
|
323
375
|
}
|
|
324
|
-
|
|
376
|
+
|
|
377
|
+
// 3. 都是百分比时,折扣越大越优先(par_value越大越优先)
|
|
378
|
+
if (typeA === 'percent' && typeB === 'percent') {
|
|
325
379
|
if (itemA.par_value !== itemB.par_value) {
|
|
326
|
-
const valueA = new
|
|
327
|
-
const valueB = new
|
|
328
|
-
return valueA.minus(valueB).toNumber();
|
|
380
|
+
const valueA = new _decimal.default(100).minus(itemA.par_value || 0);
|
|
381
|
+
const valueB = new _decimal.default(100).minus(itemB.par_value || 0);
|
|
382
|
+
return valueA.minus(valueB).toNumber(); // 折扣大的在前
|
|
329
383
|
}
|
|
330
384
|
}
|
|
331
385
|
return 0;
|
|
332
386
|
}
|
|
387
|
+
|
|
388
|
+
// 1. 先按优先级排序
|
|
333
389
|
const priorityA = getPriority(a);
|
|
334
390
|
const priorityB = getPriority(b);
|
|
335
391
|
if (priorityA !== priorityB) {
|
|
336
392
|
return priorityA - priorityB;
|
|
337
393
|
}
|
|
394
|
+
|
|
395
|
+
// 2. 同一优先级内部排序
|
|
396
|
+
// 如果是商品券(优先级1或4),按过期时间排序
|
|
338
397
|
if (isGoodPass(a) && isGoodPass(b)) {
|
|
339
398
|
return compareByExpireTime(a, b);
|
|
340
399
|
}
|
|
341
|
-
|
|
400
|
+
|
|
401
|
+
// 如果是折扣卡(优先级2、3、5、6),按折扣力度排序,然后按过期时间
|
|
402
|
+
if (a.tag === 'product_discount_card' && b.tag === 'product_discount_card') {
|
|
342
403
|
const valueCompare = compareDiscountCardValue(a, b);
|
|
343
|
-
if (valueCompare !== 0)
|
|
344
|
-
return valueCompare;
|
|
404
|
+
if (valueCompare !== 0) return valueCompare;
|
|
345
405
|
return compareByExpireTime(a, b);
|
|
346
406
|
}
|
|
407
|
+
|
|
408
|
+
// 3. 其他情况按照过期时间排序
|
|
347
409
|
return compareByExpireTime(a, b);
|
|
348
410
|
});
|
|
411
|
+
|
|
412
|
+
// const newProductList = [];
|
|
413
|
+
//
|
|
414
|
+
// productList.forEach(item => {
|
|
415
|
+
// const product = this.hooks.getProduct(item);
|
|
416
|
+
// if (product.quantity > 1) {
|
|
417
|
+
// for (let i = 1; i < product.quantity; i++) {
|
|
418
|
+
// newProductList.push(product)
|
|
419
|
+
// }
|
|
420
|
+
// } else {
|
|
421
|
+
// newProductList.push(item)
|
|
422
|
+
// }
|
|
423
|
+
// })
|
|
424
|
+
|
|
425
|
+
// 🔥 扁平化商品列表(包含主商品和bundle子商品)
|
|
349
426
|
const flattenedList = flattenProductsWithBundle(productList);
|
|
350
|
-
|
|
427
|
+
|
|
428
|
+
// 对扁平化后的列表按价格降序排序(用于应用优惠券时优先选择高价商品)
|
|
351
429
|
const sortedFlattenedList = flattenedList.sort((a, b) => {
|
|
352
|
-
|
|
353
|
-
const priceA = new
|
|
354
|
-
|
|
355
|
-
);
|
|
356
|
-
const priceB = new import_decimal.default(
|
|
357
|
-
b.type === "bundle" ? b.original_price ?? b.price ?? "0" : b.price || "0"
|
|
358
|
-
);
|
|
430
|
+
// 子商品优先使用 original_price,主商品使用 price
|
|
431
|
+
const priceA = new _decimal.default(a.type === 'bundle' ? a.original_price ?? a.price ?? '0' : a.price || '0');
|
|
432
|
+
const priceB = new _decimal.default(b.type === 'bundle' ? b.original_price ?? b.price ?? '0' : b.price || '0');
|
|
359
433
|
if (priceA.equals(priceB)) {
|
|
434
|
+
// 价格相同时,主商品优先
|
|
360
435
|
if (a.type !== b.type) {
|
|
361
|
-
return a.type ===
|
|
436
|
+
return a.type === 'main' ? -1 : 1;
|
|
362
437
|
}
|
|
363
|
-
|
|
438
|
+
// 都是主商品时,按原有逻辑排序
|
|
439
|
+
if (a.type === 'main' && b.type === 'main') {
|
|
364
440
|
if (a.product.quantity === b.product.quantity) {
|
|
365
|
-
return (
|
|
441
|
+
return (b.product.discount_list?.length || 0) - (a.product.discount_list?.length || 0);
|
|
366
442
|
}
|
|
367
443
|
return a.product.quantity - b.product.quantity;
|
|
368
444
|
}
|
|
369
|
-
|
|
445
|
+
// 都是bundle时,按数量排序
|
|
446
|
+
if (a.type === 'bundle' && b.type === 'bundle') {
|
|
370
447
|
return (a.num || 1) - (b.num || 1);
|
|
371
448
|
}
|
|
372
449
|
}
|
|
373
450
|
return priceB.minus(priceA).toNumber();
|
|
374
451
|
});
|
|
452
|
+
|
|
453
|
+
// 🔥 为每个折扣卡/商品券执行策略检查并附加config
|
|
375
454
|
const evaluator = this.getWalletPassEvaluator();
|
|
376
455
|
if (evaluator) {
|
|
377
|
-
addModeDiscount.forEach(
|
|
378
|
-
var _a, _b;
|
|
456
|
+
addModeDiscount.forEach(discount => {
|
|
379
457
|
const discountType = discount.tag || discount.type;
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
458
|
+
// 只对 good_pass 和 discount_card 类型执行策略检查
|
|
459
|
+
if (['good_pass', 'discount_card', 'product_discount_card'].includes(discountType)) {
|
|
460
|
+
// 转换 discount 为 Voucher 格式
|
|
383
461
|
const voucher = {
|
|
384
462
|
id: discount.id,
|
|
385
463
|
amount: Number(discount.par_value || 0),
|
|
@@ -387,24 +465,27 @@ var RulesModule = class extends import_BaseModule.BaseModule {
|
|
|
387
465
|
type: discountType,
|
|
388
466
|
product_id: discount.product_id,
|
|
389
467
|
unified_available_status: 1,
|
|
390
|
-
available_product_type:
|
|
391
|
-
available_product_ids:
|
|
468
|
+
available_product_type: discount.limited_relation_product_data?.type,
|
|
469
|
+
available_product_ids: discount.limited_relation_product_data?.product_ids
|
|
392
470
|
};
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
471
|
+
|
|
472
|
+
// 转换 sortedFlattenedList 为 Product 格式
|
|
473
|
+
const productsForEvaluate = sortedFlattenedList.map(item => ({
|
|
474
|
+
product_id: item.id,
|
|
475
|
+
price: item.price || 0,
|
|
476
|
+
quantity: item.quantity || item.num || 1,
|
|
477
|
+
selling_price: item.price || 0,
|
|
478
|
+
product_options: item.type === 'main' ? item.product?.options : undefined
|
|
479
|
+
}));
|
|
480
|
+
|
|
481
|
+
// 调用策略检查
|
|
403
482
|
const result = evaluator.checkVoucherAvailability({
|
|
404
|
-
orderTotalAmount,
|
|
483
|
+
orderTotalAmount: orderTotalAmount,
|
|
405
484
|
products: productsForEvaluate,
|
|
406
485
|
vouchers: [voucher]
|
|
407
486
|
});
|
|
487
|
+
|
|
488
|
+
// 将 config 附加到 discount 对象
|
|
408
489
|
if (result.isAvailable) {
|
|
409
490
|
discount.config = {
|
|
410
491
|
...result.config,
|
|
@@ -418,35 +499,74 @@ var RulesModule = class extends import_BaseModule.BaseModule {
|
|
|
418
499
|
}
|
|
419
500
|
});
|
|
420
501
|
}
|
|
502
|
+
// 检查商品是否被排除(全品类)
|
|
421
503
|
const isProductIncludedByLimitedData = (limitedData, productId) => {
|
|
422
|
-
const excludedIds =
|
|
423
|
-
const isExcluded =
|
|
504
|
+
const excludedIds = limitedData?.exclude_product_ids || [];
|
|
505
|
+
const isExcluded = limitedData?.filter === 1 && excludedIds.some(id => String(id) === String(productId));
|
|
424
506
|
return !isExcluded;
|
|
425
507
|
};
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
// 对productList按价格降序排序(用于应用优惠券时优先选择高价商品) 价格相同时使用quantity 排序
|
|
511
|
+
const sortedProductList = [...productList].sort((a, b) => {
|
|
512
|
+
const aProduct = this.hooks.getProduct(a);
|
|
513
|
+
const bProduct = this.hooks.getProduct(b);
|
|
514
|
+
const priceA = new Decimal((aProduct.price as string) || '0');
|
|
515
|
+
const priceB = new Decimal((bProduct.price as string) || '0');
|
|
516
|
+
if (priceA.toNumber() === priceB.toNumber()) {
|
|
517
|
+
if (aProduct.quantity === bProduct.quantity) {
|
|
518
|
+
return bProduct.discount_list?.length - aProduct.discount_list?.length;
|
|
519
|
+
}
|
|
520
|
+
return aProduct.quantity - bProduct.quantity;
|
|
521
|
+
}
|
|
522
|
+
return priceB.toNumber() - priceA.toNumber();
|
|
523
|
+
});
|
|
524
|
+
*/
|
|
525
|
+
|
|
526
|
+
// 标记已使用的优惠券
|
|
527
|
+
const usedDiscounts = new Map();
|
|
528
|
+
|
|
529
|
+
// 🔥 统计每个 product_id(商品券商品id)已使用的数量,用于 maxUsagePerOrder 限制
|
|
530
|
+
const usedProductIdCounts = new Map();
|
|
531
|
+
|
|
532
|
+
// 🔥 统计每张折扣卡的 applicableProductLimit 已使用次数(跨商品全局计数)
|
|
533
|
+
const usedDiscountCardLimitCounts = new Map();
|
|
534
|
+
|
|
535
|
+
// 🔥 预先将 editModeDiscount 中的折扣卡计入 applicableProductLimit 已使用次数
|
|
536
|
+
editModeDiscount.forEach(discount => {
|
|
431
537
|
const discountType = discount.tag || discount.type;
|
|
432
|
-
if ([
|
|
538
|
+
if (['discount_card', 'product_discount_card'].includes(discountType)) {
|
|
433
539
|
const currentCount = usedDiscountCardLimitCounts.get(discount.id) || 0;
|
|
434
|
-
usedDiscountCardLimitCounts.set(discount.id, currentCount + (
|
|
540
|
+
usedDiscountCardLimitCounts.set(discount.id, currentCount + (discount.metadata?.num || 1));
|
|
435
541
|
}
|
|
436
542
|
});
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
543
|
+
|
|
544
|
+
// 记录每个优惠券适用的商品ID
|
|
545
|
+
const discountApplicability = new Map();
|
|
546
|
+
|
|
547
|
+
// 记录每个优惠券适用的商品详细信息
|
|
548
|
+
const discountApplicableProducts = new Map();
|
|
549
|
+
|
|
550
|
+
// 初始化每个优惠券的适用性和可抵扣商品
|
|
551
|
+
addModeDiscount.forEach(discount => {
|
|
440
552
|
discountApplicability.set(discount.id, []);
|
|
441
553
|
discountApplicableProducts.set(discount.id, []);
|
|
442
554
|
});
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
555
|
+
|
|
556
|
+
// 预处理 order_level 固定金额折扣卡的分摊信息
|
|
557
|
+
// Map<discountId, Map<productId, { discountAmount, difference }>>
|
|
558
|
+
const orderLevelDiscountAllocations = new Map();
|
|
559
|
+
|
|
560
|
+
// 第一步:收集所有被勾选的 order_level 折扣卡及其适用商品
|
|
561
|
+
// Map<discountId, Set<flatItemId>> - 记录每个折扣卡适用的商品
|
|
562
|
+
const orderLevelDiscountApplicableItems = new Map();
|
|
563
|
+
// Map<flatItemId, Set<discountId>> - 记录每个商品可以被哪些折扣卡使用
|
|
564
|
+
const itemApplicableDiscounts = new Map();
|
|
565
|
+
|
|
566
|
+
// 辅助函数:检查商品是否对某个折扣卡可用
|
|
446
567
|
const checkItemApplicableForDiscount = (flatItem, discount) => {
|
|
447
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
448
568
|
let product;
|
|
449
|
-
if (flatItem.type ===
|
|
569
|
+
if (flatItem.type === 'main') {
|
|
450
570
|
product = flatItem.product;
|
|
451
571
|
} else {
|
|
452
572
|
product = {
|
|
@@ -457,27 +577,38 @@ var RulesModule = class extends import_BaseModule.BaseModule {
|
|
|
457
577
|
num: flatItem.num,
|
|
458
578
|
booking_id: flatItem.booking_id,
|
|
459
579
|
discount_list: flatItem.discount_list || [],
|
|
460
|
-
startDate:
|
|
580
|
+
startDate: flatItem.parentProduct?.startDate
|
|
461
581
|
};
|
|
462
582
|
}
|
|
463
|
-
|
|
583
|
+
|
|
584
|
+
// 编辑的商品使用了优惠券不可用
|
|
585
|
+
const isAvailableProduct = flatItem.type === 'main' ? !(product?.booking_id && product?.discount_list?.length && product?.discount_list?.every(d => d.id && ['good_pass', 'discount_card', 'product_discount_card'].includes(d.tag || d.type))) : !(flatItem?.booking_id && !!flatItem?.bundleItem?.discount_list?.length && flatItem?.bundleItem?.discount_list?.every(d => d.id));
|
|
464
586
|
if (!isAvailableProduct) {
|
|
465
587
|
return false;
|
|
466
588
|
}
|
|
467
|
-
|
|
589
|
+
|
|
590
|
+
// vouchersApplicable 为 false 的商品不参与订单级别折扣均摊
|
|
591
|
+
if ((0, _lodashEs.isBoolean)(product.vouchersApplicable) && !product.vouchersApplicable) {
|
|
468
592
|
return false;
|
|
469
593
|
}
|
|
594
|
+
|
|
595
|
+
// 商品价格为0时不可用
|
|
470
596
|
if (Number(product.price) <= 0 || !product.price) {
|
|
471
597
|
return false;
|
|
472
598
|
}
|
|
473
599
|
const limitedData = discount.limited_relation_product_data;
|
|
474
|
-
|
|
600
|
+
|
|
601
|
+
// 时间限制检查
|
|
602
|
+
const timeLimit = !!(0, _utils.filterDiscountListByBookingTime)([discount], (product?.startDate || (0, _dayjs.default)()).format('YYYY-MM-DD HH:mm:ss')).length;
|
|
475
603
|
if (!timeLimit) {
|
|
476
604
|
return false;
|
|
477
605
|
}
|
|
606
|
+
|
|
607
|
+
// 判断商品是否适用
|
|
478
608
|
let isLimitedProduct = false;
|
|
479
|
-
if (limitedData.type ===
|
|
480
|
-
|
|
609
|
+
if (limitedData.type === 'product_all') {
|
|
610
|
+
// 全品类,但需要检查是否被排除
|
|
611
|
+
if (limitedData.filter === 1 && limitedData.exclude_product_ids?.includes(product.id)) {
|
|
481
612
|
isLimitedProduct = false;
|
|
482
613
|
} else {
|
|
483
614
|
isLimitedProduct = true;
|
|
@@ -485,33 +616,51 @@ var RulesModule = class extends import_BaseModule.BaseModule {
|
|
|
485
616
|
} else if (limitedData.product_ids && limitedData.product_ids.includes(product.id)) {
|
|
486
617
|
isLimitedProduct = true;
|
|
487
618
|
}
|
|
619
|
+
|
|
620
|
+
// 套餐规则检查
|
|
488
621
|
const isBundleAvailable = this.checkPackageSubItemUsageRules(discount, flatItem);
|
|
489
622
|
return isLimitedProduct && isBundleAvailable;
|
|
490
623
|
};
|
|
491
|
-
|
|
492
|
-
|
|
624
|
+
|
|
625
|
+
// 收集所有被勾选的 order_level 折扣卡及其适用商品
|
|
626
|
+
const selectedOrderLevelDiscounts = sortedDiscountList.filter(discount => {
|
|
627
|
+
return (0, _utils.isOrderLevelFixedAmountDiscount)(discount) && discount.isSelected !== false;
|
|
493
628
|
});
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
629
|
+
|
|
630
|
+
// 遍历所有被勾选的 order_level 折扣卡,建立双向映射
|
|
631
|
+
selectedOrderLevelDiscounts.forEach(discount => {
|
|
632
|
+
const applicableItemIds = new Set();
|
|
633
|
+
sortedFlattenedList.forEach(flatItem => {
|
|
497
634
|
if (checkItemApplicableForDiscount(flatItem, discount)) {
|
|
498
635
|
applicableItemIds.add(flatItem._id);
|
|
636
|
+
|
|
637
|
+
// 记录商品可以被哪些折扣卡使用
|
|
499
638
|
if (!itemApplicableDiscounts.has(flatItem._id)) {
|
|
500
|
-
itemApplicableDiscounts.set(flatItem._id,
|
|
639
|
+
itemApplicableDiscounts.set(flatItem._id, new Set());
|
|
501
640
|
}
|
|
502
641
|
itemApplicableDiscounts.get(flatItem._id).add(discount.id);
|
|
503
642
|
}
|
|
504
643
|
});
|
|
505
644
|
orderLevelDiscountApplicableItems.set(discount.id, applicableItemIds);
|
|
506
645
|
});
|
|
507
|
-
|
|
508
|
-
|
|
646
|
+
|
|
647
|
+
// 专属折扣卡定义:限定了特定商品(product_ids),而不是全品类(product_all)的折扣卡
|
|
648
|
+
// 如果专属折扣卡被勾选,它适用的商品会被占用,其他折扣卡均摊时应排除这些商品
|
|
649
|
+
// Map<flatItemId, discountId> - 记录被占用的商品及其对应的折扣卡
|
|
650
|
+
const occupiedItems = new Map();
|
|
651
|
+
|
|
652
|
+
// 按排序顺序遍历折扣卡,判断哪些是专属折扣卡
|
|
653
|
+
selectedOrderLevelDiscounts.forEach(discount => {
|
|
509
654
|
const limitedData = discount.limited_relation_product_data;
|
|
510
|
-
|
|
655
|
+
|
|
656
|
+
// 判断是否是专属折扣卡(只能用于特定商品,而不是全品类)
|
|
657
|
+
const isExclusiveDiscount = limitedData.type !== 'product_all';
|
|
511
658
|
if (isExclusiveDiscount) {
|
|
659
|
+
// 专属折扣卡:它适用的所有商品都会被它占用
|
|
512
660
|
const applicableItems = orderLevelDiscountApplicableItems.get(discount.id);
|
|
513
661
|
if (applicableItems) {
|
|
514
|
-
applicableItems.forEach(
|
|
662
|
+
applicableItems.forEach(itemId => {
|
|
663
|
+
// 如果商品还没被占用,则标记为被当前折扣卡占用
|
|
515
664
|
if (!occupiedItems.has(itemId)) {
|
|
516
665
|
occupiedItems.set(itemId, discount.id);
|
|
517
666
|
}
|
|
@@ -519,23 +668,27 @@ var RulesModule = class extends import_BaseModule.BaseModule {
|
|
|
519
668
|
}
|
|
520
669
|
}
|
|
521
670
|
});
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
671
|
+
|
|
672
|
+
// 🔥 第三步:识别 order_level 折扣卡并预计算分摊(排除被其他专属折扣卡占用的商品)
|
|
673
|
+
sortedDiscountList.forEach(discount => {
|
|
674
|
+
if (!(0, _utils.isOrderLevelFixedAmountDiscount)(discount)) {
|
|
525
675
|
return;
|
|
526
676
|
}
|
|
677
|
+
|
|
678
|
+
// 收集该折扣卡适用的商品(排除被其他专属折扣卡占用的商品)
|
|
527
679
|
const applicableProducts = [];
|
|
528
|
-
sortedFlattenedList.forEach(
|
|
529
|
-
|
|
680
|
+
sortedFlattenedList.forEach(flatItem => {
|
|
681
|
+
// 🔥 检查该商品是否被其他专属折扣卡占用
|
|
530
682
|
const occupyingDiscountId = occupiedItems.get(flatItem._id);
|
|
531
|
-
if (occupyingDiscountId !==
|
|
683
|
+
if (occupyingDiscountId !== undefined && occupyingDiscountId !== discount.id) {
|
|
684
|
+
// 该商品被其他专属折扣卡占用,跳过
|
|
532
685
|
return;
|
|
533
686
|
}
|
|
534
687
|
if (!checkItemApplicableForDiscount(flatItem, discount)) {
|
|
535
688
|
return;
|
|
536
689
|
}
|
|
537
690
|
let product;
|
|
538
|
-
if (flatItem.type ===
|
|
691
|
+
if (flatItem.type === 'main') {
|
|
539
692
|
product = flatItem.product;
|
|
540
693
|
} else {
|
|
541
694
|
product = {
|
|
@@ -546,34 +699,53 @@ var RulesModule = class extends import_BaseModule.BaseModule {
|
|
|
546
699
|
num: flatItem.num
|
|
547
700
|
};
|
|
548
701
|
}
|
|
549
|
-
|
|
550
|
-
|
|
702
|
+
|
|
703
|
+
// 对于 bundle 子商品,quantity 需要乘以主商品的购买数量
|
|
704
|
+
const quantity = flatItem.type === 'main' ? product.num || product.quantity || 1 : (product.num || product.quantity || 1) * (flatItem.parentProduct?.num || flatItem.parentProduct?.quantity || 1);
|
|
705
|
+
|
|
706
|
+
// 对于主商品:使用 price
|
|
707
|
+
// 对于子商品:优先使用 flatItem.original_price,否则使用 flatItem.price
|
|
708
|
+
const originalAmount = flatItem.type === 'main' ? Number(product.price ?? 0) : Number(flatItem.original_price ?? flatItem.price ?? 0);
|
|
709
|
+
|
|
710
|
+
// 传递 parentQuantity 用于差值处理时判断是否是真正的"数量为1"
|
|
551
711
|
const productData = {
|
|
552
712
|
productId: flatItem._id,
|
|
553
713
|
amount: originalAmount,
|
|
554
|
-
quantity
|
|
714
|
+
quantity: quantity
|
|
555
715
|
};
|
|
556
|
-
|
|
557
|
-
|
|
716
|
+
|
|
717
|
+
// 子商品需要传递主商品数量
|
|
718
|
+
if (flatItem.type === 'bundle') {
|
|
719
|
+
productData.parentQuantity = flatItem.parentProduct?.quantity || 1;
|
|
558
720
|
}
|
|
559
721
|
applicableProducts.push(productData);
|
|
560
722
|
});
|
|
723
|
+
|
|
724
|
+
// 计算分摊
|
|
561
725
|
if (applicableProducts.length > 0) {
|
|
562
|
-
const allocation = (0,
|
|
726
|
+
const allocation = (0, _utils.calculateOrderLevelDiscountAllocation)(discount, applicableProducts);
|
|
563
727
|
orderLevelDiscountAllocations.set(discount.id, allocation);
|
|
564
728
|
}
|
|
565
729
|
});
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
730
|
+
|
|
731
|
+
// 处理后的商品列表(按排序后的顺序处理,记录优惠信息)
|
|
732
|
+
const processedProductsMap = new Map();
|
|
733
|
+
|
|
734
|
+
// 记录已应用优惠券的商品信息
|
|
735
|
+
const appliedDiscountProducts = new Map();
|
|
736
|
+
|
|
737
|
+
// 首先遍历所有商品和所有优惠券,确定每个优惠券的适用范围,包括isSelected为false的优惠券
|
|
738
|
+
// 🔥 使用扁平化后的列表,包含主商品和bundle子商品
|
|
739
|
+
sortedFlattenedList.forEach(flatItem => {
|
|
740
|
+
// 获取商品数据
|
|
570
741
|
let product, originProduct;
|
|
571
|
-
if (flatItem.type ===
|
|
742
|
+
if (flatItem.type === 'main') {
|
|
572
743
|
product = flatItem.product;
|
|
573
744
|
originProduct = flatItem.originProduct;
|
|
574
745
|
} else {
|
|
746
|
+
// bundle子商品:构造虚拟商品对象
|
|
575
747
|
product = {
|
|
576
|
-
startDate:
|
|
748
|
+
startDate: flatItem?.parentProduct?.startDate,
|
|
577
749
|
_id: flatItem._id,
|
|
578
750
|
id: flatItem.id,
|
|
579
751
|
price: flatItem.price,
|
|
@@ -586,53 +758,55 @@ var RulesModule = class extends import_BaseModule.BaseModule {
|
|
|
586
758
|
};
|
|
587
759
|
originProduct = flatItem.originProduct;
|
|
588
760
|
}
|
|
589
|
-
addModeDiscount.forEach(
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
const _tempVar =
|
|
593
|
-
const isHolderMatch = this.checkHolderMatch(
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
holder_id: (_tempVar == null ? void 0 : _tempVar.holder_id) || product.holder_id
|
|
598
|
-
},
|
|
599
|
-
holders
|
|
600
|
-
);
|
|
761
|
+
addModeDiscount.forEach(discount => {
|
|
762
|
+
const limitedData = discount?.limited_relation_product_data;
|
|
763
|
+
// 拿到discount配置的holder信息 product信息 product.holder 加在 isLimitedProduct
|
|
764
|
+
const _tempVar = flatItem?.type === 'bundle' ? flatItem?.parentProduct : flatItem?.product;
|
|
765
|
+
const isHolderMatch = this.checkHolderMatch(discount, {
|
|
766
|
+
isNeedHolder: resolveIsFormSubject(_tempVar) && !_tempVar?.isNormalProduct,
|
|
767
|
+
holder_id: _tempVar?.holder_id || product.holder_id
|
|
768
|
+
}, holders);
|
|
601
769
|
let timeLimit = true;
|
|
602
|
-
timeLimit = !!(0,
|
|
603
|
-
|
|
604
|
-
const
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
))) : !((flatItem == null ? void 0 : flatItem.booking_id) && !!((_d = (_c = flatItem == null ? void 0 : flatItem.bundleItem) == null ? void 0 : _c.discount_list) == null ? void 0 : _d.length) && ((_f = (_e = flatItem == null ? void 0 : flatItem.bundleItem) == null ? void 0 : _e.discount_list) == null ? void 0 : _f.every((discount2) => discount2.id)));
|
|
770
|
+
timeLimit = !!(0, _utils.filterDiscountListByBookingTime)([discount], (product?.startDate || (0, _dayjs.default)()).format('YYYY-MM-DD HH:mm:ss')).length;
|
|
771
|
+
// 是符合折扣的商品
|
|
772
|
+
const isLimitedProduct = (limitedData.type === 'product_all' && limitedData.filter !== 1 || limitedData.type === 'product_all' && limitedData.filter === 1 && !limitedData.exclude_product_ids.includes(product.id) || limitedData.product_ids && limitedData.product_ids.includes(product.id)) && isHolderMatch;
|
|
773
|
+
|
|
774
|
+
// 编辑的商品 使用了优惠券不可用
|
|
775
|
+
const isAvailableProduct = flatItem.type === 'main' ? !(product?.booking_id && product?.discount_list?.length && product?.discount_list?.every(discount => discount.id && ['good_pass', 'discount_card', 'product_discount_card'].includes(discount.tag || discount.type))) : !(flatItem?.booking_id && !!flatItem?.bundleItem?.discount_list?.length && flatItem?.bundleItem?.discount_list?.every(discount => discount.id));
|
|
776
|
+
|
|
777
|
+
// 套餐是否可用判断
|
|
611
778
|
const isBundleAvailable = this.checkPackageSubItemUsageRules(discount, flatItem);
|
|
612
|
-
|
|
613
|
-
|
|
779
|
+
|
|
780
|
+
// 判断优惠券是否适用于该商品
|
|
781
|
+
if (isAvailableProduct && isLimitedProduct && timeLimit && isBundleAvailable && discount.config?.isAvailable) {
|
|
782
|
+
// 记录此优惠券适用的商品
|
|
783
|
+
discountApplicability.get(discount.id)?.push(product.id);
|
|
784
|
+
|
|
785
|
+
// 记录可抵扣的商品详情
|
|
614
786
|
const applicableProducts = discountApplicableProducts.get(discount.id) || [];
|
|
615
787
|
const discountType = discount.tag || discount.type;
|
|
616
|
-
const isGoodPass = discountType ===
|
|
617
|
-
|
|
788
|
+
const isGoodPass = discountType === 'good_pass';
|
|
789
|
+
|
|
790
|
+
// 使用数量
|
|
791
|
+
const num = isGoodPass || flatItem?.type === 'main' ? 1 : product.num;
|
|
618
792
|
const productData = {
|
|
619
793
|
amount: product.price * num,
|
|
620
794
|
type: discountType,
|
|
621
795
|
tag: discountType,
|
|
622
796
|
discount: {
|
|
623
|
-
discount_card_type:
|
|
797
|
+
discount_card_type: discount?.metadata?.discount_card_type,
|
|
624
798
|
fixed_amount: product.price,
|
|
625
|
-
discount_calculation_mode:
|
|
799
|
+
discount_calculation_mode: discount?.metadata?.discount_calculation_mode,
|
|
626
800
|
resource_id: discount.id,
|
|
627
801
|
title: discount.format_title,
|
|
628
802
|
original_amount: product.price || product.origin_total,
|
|
629
803
|
pre_value: discount.par_value,
|
|
630
|
-
product_id: originProduct.id,
|
|
804
|
+
product_id: originProduct.id || originProduct.product_id,
|
|
631
805
|
discount_product_id: discount.product_id
|
|
632
806
|
},
|
|
633
807
|
metadata: {
|
|
634
|
-
num,
|
|
635
|
-
discount_rule_uncheck_flag: discount
|
|
808
|
+
num: num,
|
|
809
|
+
discount_rule_uncheck_flag: discount?.discount_rule_uncheck_flag
|
|
636
810
|
}
|
|
637
811
|
};
|
|
638
812
|
applicableProducts.push(productData);
|
|
@@ -640,112 +814,145 @@ var RulesModule = class extends import_BaseModule.BaseModule {
|
|
|
640
814
|
}
|
|
641
815
|
});
|
|
642
816
|
});
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
817
|
+
|
|
818
|
+
// 🔥 用于存储扁平化商品处理结果的Map
|
|
819
|
+
const processedFlatItemsMap = new Map();
|
|
820
|
+
|
|
821
|
+
// 🔥 Option 折扣辅助函数
|
|
822
|
+
const applyDiscountToOptions = (options, discount) => {
|
|
823
|
+
if (!options?.length) return {
|
|
824
|
+
discountedOptions: options,
|
|
825
|
+
optionDiscountAmount: 0
|
|
826
|
+
};
|
|
647
827
|
let optionDiscountAmount = 0;
|
|
648
|
-
const discountedOptions =
|
|
828
|
+
const discountedOptions = options.map(option => {
|
|
829
|
+
// 折前单价:优先 price,缺失再 add_price;已写回折后价时用 _original_price(或旧字段 _original_add_price)
|
|
649
830
|
const rawUnit = option._original_price ?? option._original_add_price ?? option.price ?? option.add_price;
|
|
650
831
|
const baseUnitNum = Number(rawUnit ?? 0);
|
|
651
|
-
if (baseUnitNum <= 0)
|
|
652
|
-
|
|
653
|
-
const discountedPrice = (0, import_utils.getDiscountAmount)(discount, baseUnitNum, baseUnitNum);
|
|
832
|
+
if (baseUnitNum <= 0) return option;
|
|
833
|
+
const discountedPrice = (0, _utils.getDiscountAmount)(discount, baseUnitNum, baseUnitNum);
|
|
654
834
|
const optQty = Number(option.num ?? option.quantity ?? 1);
|
|
655
|
-
optionDiscountAmount = new
|
|
656
|
-
const {
|
|
835
|
+
optionDiscountAmount = new _decimal.default(optionDiscountAmount).plus(new _decimal.default(baseUnitNum).minus(discountedPrice).mul(optQty)).toNumber();
|
|
836
|
+
const {
|
|
837
|
+
_original_add_price,
|
|
838
|
+
...rest
|
|
839
|
+
} = option;
|
|
657
840
|
const tmpl = rest.price ?? rest.add_price;
|
|
658
|
-
const nextPrice = typeof tmpl ===
|
|
841
|
+
const nextPrice = typeof tmpl === 'string' ? String(discountedPrice) : discountedPrice;
|
|
659
842
|
return {
|
|
660
843
|
...rest,
|
|
661
|
-
_original_price: option._original_price ?? (_original_add_price !==
|
|
844
|
+
_original_price: option._original_price ?? (_original_add_price !== undefined ? _original_add_price : rawUnit),
|
|
662
845
|
price: nextPrice
|
|
663
846
|
};
|
|
664
847
|
});
|
|
665
|
-
return {
|
|
848
|
+
return {
|
|
849
|
+
discountedOptions,
|
|
850
|
+
optionDiscountAmount
|
|
851
|
+
};
|
|
666
852
|
};
|
|
667
|
-
const restoreOptionPrices =
|
|
668
|
-
if (!
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
853
|
+
const restoreOptionPrices = options => {
|
|
854
|
+
if (!options?.length) return options;
|
|
855
|
+
return options.map(option => {
|
|
856
|
+
const orig = option._original_price !== undefined ? option._original_price : option._original_add_price;
|
|
857
|
+
if (orig !== undefined) {
|
|
858
|
+
const {
|
|
859
|
+
_original_price,
|
|
860
|
+
_original_add_price,
|
|
861
|
+
...rest
|
|
862
|
+
} = option;
|
|
863
|
+
return {
|
|
864
|
+
...rest,
|
|
865
|
+
price: orig
|
|
866
|
+
};
|
|
675
867
|
}
|
|
676
868
|
return option;
|
|
677
869
|
});
|
|
678
870
|
};
|
|
679
|
-
const getOptionTotal =
|
|
680
|
-
if (!
|
|
681
|
-
|
|
682
|
-
return options2.reduce((sum, opt) => {
|
|
871
|
+
const getOptionTotal = options => {
|
|
872
|
+
if (!options?.length) return 0;
|
|
873
|
+
return options.reduce((sum, opt) => {
|
|
683
874
|
const unit = Number(opt.price ?? opt.add_price ?? 0);
|
|
684
875
|
const n = Number(opt.num ?? opt.quantity ?? 1);
|
|
685
|
-
return new
|
|
876
|
+
return new _decimal.default(sum).plus(new _decimal.default(unit).mul(n)).toNumber();
|
|
686
877
|
}, 0);
|
|
687
878
|
};
|
|
879
|
+
|
|
880
|
+
// 然后再处理应用哪些优惠券,此时只考虑filteredDiscountList中的优惠券
|
|
881
|
+
// 🔥 使用扁平化后的列表进行处理
|
|
688
882
|
sortedFlattenedList.forEach((flatItem, index) => {
|
|
689
|
-
|
|
883
|
+
// 获取商品数据
|
|
690
884
|
let product, originProduct;
|
|
691
|
-
if (flatItem.type ===
|
|
885
|
+
if (flatItem.type === 'main') {
|
|
692
886
|
product = flatItem.product;
|
|
693
887
|
originProduct = flatItem.originProduct;
|
|
694
888
|
} else {
|
|
889
|
+
// bundle子商品
|
|
695
890
|
product = {
|
|
696
|
-
startDate:
|
|
891
|
+
startDate: flatItem?.parentProduct?.startDate,
|
|
697
892
|
_id: flatItem._id,
|
|
698
893
|
id: flatItem.id,
|
|
699
894
|
price: flatItem.price,
|
|
700
895
|
quantity: flatItem.quantity,
|
|
701
896
|
num: flatItem.num,
|
|
702
897
|
total: flatItem.total,
|
|
703
|
-
original_price:
|
|
704
|
-
origin_total:
|
|
898
|
+
original_price: flatItem?.bundleItem?.original_price,
|
|
899
|
+
origin_total: flatItem?.bundleItem?.original_price,
|
|
705
900
|
booking_id: flatItem.booking_id,
|
|
706
|
-
discount_list:
|
|
901
|
+
discount_list: flatItem?.bundleItem?.discount_list || []
|
|
707
902
|
};
|
|
708
903
|
originProduct = flatItem.originProduct;
|
|
709
904
|
}
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
905
|
+
const isPersistedProduct = product?.booking_id || originProduct?.order_detail_id || originProduct?.orderDetailId || originProduct?.booking_id;
|
|
906
|
+
const hasExistingWalletDiscount = product.discount_list?.length && product?.discount_list?.every(discount => {
|
|
907
|
+
const discountIdentity = discount.id ?? discount.discount?.resource_id;
|
|
908
|
+
const discountType = discount.tag || discount.type;
|
|
909
|
+
return Boolean(discountIdentity && ['good_pass', 'discount_card', 'product_discount_card'].includes(discountType));
|
|
910
|
+
});
|
|
911
|
+
|
|
912
|
+
// 已持久化且已有钱包券/折扣卡的商品行跳过,保留后端历史折扣结果。
|
|
913
|
+
if (isPersistedProduct && hasExistingWalletDiscount) {
|
|
914
|
+
if (flatItem.type === 'main') {
|
|
716
915
|
processedProductsMap.set(product._id, [originProduct]);
|
|
717
916
|
} else {
|
|
718
|
-
processedFlatItemsMap.set(flatItem._id, [
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
}
|
|
723
|
-
]);
|
|
917
|
+
processedFlatItemsMap.set(flatItem._id, [{
|
|
918
|
+
...flatItem,
|
|
919
|
+
processed: true
|
|
920
|
+
}]);
|
|
724
921
|
}
|
|
725
922
|
return;
|
|
726
923
|
}
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
)
|
|
733
|
-
|
|
924
|
+
|
|
925
|
+
// 找到适用于此商品的所有优惠券,仅考虑isSelected不为false的优惠券
|
|
926
|
+
const applicableDiscounts = sortedDiscountList.filter(discount => {
|
|
927
|
+
// 🔥 检查策略可用性(针对 good_pass 和折扣卡)
|
|
928
|
+
const discountType = discount.tag || discount.type;
|
|
929
|
+
const currentOriginUid = getOriginProductUid(originProduct);
|
|
930
|
+
const isReapplyEditModeDiscountForProduct = discount.isEditMode && reapplyBookingDiscountProductUids.size > 0 && currentOriginUid && reapplyBookingDiscountProductUids.has(String(currentOriginUid)) && discount.isSelected === true;
|
|
931
|
+
if (discount.isEditMode && reapplyBookingDiscountProductUids.size > 0) {
|
|
932
|
+
const uid = currentOriginUid;
|
|
933
|
+
if (!uid || !reapplyBookingDiscountProductUids.has(uid)) return false;
|
|
934
|
+
if (!discount.isSelected) return false;
|
|
935
|
+
}
|
|
936
|
+
if (['good_pass', 'discount_card', 'product_discount_card'].includes(discountType) && !isReapplyEditModeDiscountForProduct) {
|
|
937
|
+
// 如果策略检查后没有config,说明不可用
|
|
938
|
+
if (discount.config === undefined || !discount?.config?.isAvailable) {
|
|
734
939
|
return false;
|
|
735
940
|
}
|
|
736
941
|
}
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
942
|
+
|
|
943
|
+
// 如果商品价格为 0,其实不需要使用任何优惠券,直接 return true
|
|
944
|
+
// 商品券时主商品价格为0不可用
|
|
945
|
+
if ((Number(product.price) <= 0 || !product.price) && !product.discount_list?.length && (discount.tag || discount.type) === 'good_pass') return false;
|
|
946
|
+
|
|
947
|
+
// 折扣卡商品价格为0时不可用
|
|
948
|
+
if ((Number(product.price) <= 0 || !product.price) && !product.discount_list?.find(n => n.discount?.resource_id === discount.id) && (discount.tag || discount.type) !== 'good_pass') return false;
|
|
949
|
+
// 如果优惠券已被使用,则跳过
|
|
744
950
|
const targetUsedDiscounts = usedDiscounts.get(discount.id);
|
|
745
|
-
if (targetUsedDiscounts && (discount.tag || discount.type) ===
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
951
|
+
if (targetUsedDiscounts && (discount.tag || discount.type) === 'good_pass') return false;
|
|
952
|
+
|
|
953
|
+
// 🔥 检查 maxUsagePerOrder 限制:同一 product_id 的商品券使用数量不能超过配置值
|
|
954
|
+
if ((discount.tag || discount.type) === 'good_pass') {
|
|
955
|
+
const maxUsagePerOrder = discount.config?.maxUsagePerOrder;
|
|
749
956
|
if (maxUsagePerOrder && maxUsagePerOrder > 0) {
|
|
750
957
|
const currentUsedCount = usedProductIdCounts.get(discount.product_id) || 0;
|
|
751
958
|
if (currentUsedCount >= maxUsagePerOrder) {
|
|
@@ -753,9 +960,11 @@ var RulesModule = class extends import_BaseModule.BaseModule {
|
|
|
753
960
|
}
|
|
754
961
|
}
|
|
755
962
|
}
|
|
963
|
+
|
|
964
|
+
// 🔥 检查折扣卡的 applicableProductLimit 限制:折扣卡可使用的最大次数(跨商品全局)
|
|
756
965
|
const discountTypeForLimit = discount.tag || discount.type;
|
|
757
|
-
if ([
|
|
758
|
-
const applicableProductLimitConfig =
|
|
966
|
+
if (['discount_card', 'product_discount_card'].includes(discountTypeForLimit)) {
|
|
967
|
+
const applicableProductLimitConfig = discount.config?.applicableProductLimit || 0;
|
|
759
968
|
if (applicableProductLimitConfig > 0) {
|
|
760
969
|
const currentUsedLimitCount = usedDiscountCardLimitCounts.get(discount.id) || 0;
|
|
761
970
|
if (currentUsedLimitCount >= applicableProductLimitConfig) {
|
|
@@ -764,31 +973,34 @@ var RulesModule = class extends import_BaseModule.BaseModule {
|
|
|
764
973
|
}
|
|
765
974
|
}
|
|
766
975
|
const limitedData = discount.limited_relation_product_data;
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
);
|
|
776
|
-
if (!isHolderMatch)
|
|
777
|
-
return false;
|
|
976
|
+
|
|
977
|
+
// 拿到discount配置的holder信息 product信息 product.holder 不可用return false
|
|
978
|
+
const _tempVar = flatItem?.type === 'bundle' ? flatItem?.parentProduct : flatItem?.product;
|
|
979
|
+
const isHolderMatch = this.checkHolderMatch(discount, {
|
|
980
|
+
isNeedHolder: resolveIsFormSubject(_tempVar) && !_tempVar?.isNormalProduct,
|
|
981
|
+
holder_id: _tempVar?.holder_id || product.holder_id
|
|
982
|
+
}, holders);
|
|
983
|
+
// 如果 holder 不匹配,则不适用
|
|
984
|
+
if (!isHolderMatch) return false;
|
|
778
985
|
let timeLimit = true;
|
|
779
|
-
timeLimit = !!(0,
|
|
986
|
+
timeLimit = !!(0, _utils.filterDiscountListByBookingTime)([discount], (product.startDate || (0, _dayjs.default)()).format('YYYY-MM-DD HH:mm:ss')).length;
|
|
780
987
|
if (!timeLimit) {
|
|
781
988
|
return false;
|
|
782
989
|
}
|
|
783
|
-
|
|
990
|
+
|
|
991
|
+
// 判断优惠券是否适用于该商品
|
|
992
|
+
if (limitedData.type === 'product_all') {
|
|
784
993
|
if (!isProductIncludedByLimitedData(limitedData, product.id)) {
|
|
785
994
|
return false;
|
|
786
995
|
}
|
|
996
|
+
|
|
997
|
+
// 检查 package_sub_item_usage_rules
|
|
787
998
|
if (!this.checkPackageSubItemUsageRules(discount, flatItem)) {
|
|
788
999
|
return false;
|
|
789
1000
|
}
|
|
790
1001
|
return true;
|
|
791
1002
|
} else if (limitedData.product_ids && limitedData.product_ids.includes(product.id)) {
|
|
1003
|
+
// 检查 package_sub_item_usage_rules
|
|
792
1004
|
if (!this.checkPackageSubItemUsageRules(discount, flatItem)) {
|
|
793
1005
|
return false;
|
|
794
1006
|
}
|
|
@@ -796,379 +1008,372 @@ var RulesModule = class extends import_BaseModule.BaseModule {
|
|
|
796
1008
|
}
|
|
797
1009
|
return false;
|
|
798
1010
|
});
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
1011
|
+
|
|
1012
|
+
// 选择第一个适用的优惠券(已经按过期时间排序)
|
|
1013
|
+
// 如果有 isScan 且 isSelected 的折扣卡,则优先选中,否则再选择第一个
|
|
1014
|
+
// 用于处理:先手动扫描了一张折扣卡,扫描完以后带入客户信息,但是之前那张折扣卡不是最优惠的情况
|
|
1015
|
+
const selectedDiscountCard = applicableDiscounts.find(n => n.isScan && n.isSelected && (n.tag || n.type) !== 'good_pass');
|
|
802
1016
|
const selectedDiscount = selectedDiscountCard || applicableDiscounts[0];
|
|
1017
|
+
|
|
1018
|
+
// 如果是手动折扣,则不适用优惠券
|
|
803
1019
|
let isManualDiscount = false;
|
|
804
|
-
if (flatItem.type ===
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
var _a2;
|
|
808
|
-
return !((_a2 = item.discount_list || []) == null ? void 0 : _a2.length);
|
|
809
|
-
}
|
|
810
|
-
) && (!((_g = product.discount_list) == null ? void 0 : _g.length) || ((_i = (_h = product == null ? void 0 : product.discount_list) == null ? void 0 : _h.every) == null ? void 0 : _i.call(
|
|
811
|
-
_h,
|
|
812
|
-
(item) => item.type === "product"
|
|
813
|
-
)));
|
|
1020
|
+
if (flatItem.type === 'main') {
|
|
1021
|
+
// 主商品:判断自身是否手动折扣
|
|
1022
|
+
isManualDiscount = typeof product.isManualDiscount === 'boolean' ? product.isManualDiscount : product.discount_list?.some(item => item.type === 'product') || product.total != product.origin_total && (product.bundle || []).every(item => !(item.discount_list || [])?.length) && (!product.discount_list?.length || product?.discount_list?.every?.(item => item.type === 'product'));
|
|
814
1023
|
if (product.inPromotion) {
|
|
815
1024
|
isManualDiscount = false;
|
|
816
1025
|
}
|
|
817
1026
|
} else {
|
|
1027
|
+
// bundle子商品:判断父主商品是否手动折扣
|
|
818
1028
|
const parentProduct = flatItem.parentProduct;
|
|
819
1029
|
if (parentProduct) {
|
|
820
|
-
isManualDiscount = typeof parentProduct.isManualDiscount ===
|
|
821
|
-
(item) => {
|
|
822
|
-
var _a2;
|
|
823
|
-
return !((_a2 = item.discount_list || []) == null ? void 0 : _a2.length);
|
|
824
|
-
}
|
|
825
|
-
) && (!((_j = parentProduct.discount_list) == null ? void 0 : _j.length) || ((_l = (_k = parentProduct == null ? void 0 : parentProduct.discount_list) == null ? void 0 : _k.every) == null ? void 0 : _l.call(
|
|
826
|
-
_k,
|
|
827
|
-
(item) => item.type === "product"
|
|
828
|
-
)));
|
|
1030
|
+
isManualDiscount = typeof parentProduct.isManualDiscount === 'boolean' ? parentProduct.isManualDiscount : parentProduct.discount_list?.some(item => item.type === 'product') || parentProduct.total != parentProduct.origin_total && (parentProduct.bundle || []).every(item => !(item.discount_list || [])?.length) && (!parentProduct.discount_list?.length || parentProduct?.discount_list?.every?.(item => item.type === 'product'));
|
|
829
1031
|
}
|
|
830
1032
|
}
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
}
|
|
837
|
-
))) {
|
|
1033
|
+
|
|
1034
|
+
// 勾选时覆盖手动折扣
|
|
1035
|
+
if (options?.discountId) {
|
|
1036
|
+
// 主商品:检查自己的 discount_list
|
|
1037
|
+
if (flatItem.type === 'main' && product.discount_list?.some(item => item.discount?.resource_id === options.discountId)) {
|
|
838
1038
|
isManualDiscount = false;
|
|
839
1039
|
}
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
var _a2;
|
|
844
|
-
return ((_a2 = item.discount) == null ? void 0 : _a2.resource_id) === options.discountId;
|
|
845
|
-
}
|
|
846
|
-
)) || ((_p = (_o = flatItem.parentProduct) == null ? void 0 : _o.discount_list) == null ? void 0 : _p.some(
|
|
847
|
-
(item) => {
|
|
848
|
-
var _a2;
|
|
849
|
-
return ((_a2 = item.discount) == null ? void 0 : _a2.resource_id) === options.discountId;
|
|
850
|
-
}
|
|
851
|
-
))) {
|
|
1040
|
+
// bundle子商品:检查自己的 discount_list 或父主商品的 discount_list
|
|
1041
|
+
if (flatItem.type === 'bundle') {
|
|
1042
|
+
if (product.discount_list?.some(item => item.discount?.resource_id === options.discountId) || flatItem.parentProduct?.discount_list?.some(item => item.discount?.resource_id === options.discountId)) {
|
|
852
1043
|
isManualDiscount = false;
|
|
853
1044
|
}
|
|
854
1045
|
}
|
|
855
1046
|
}
|
|
856
|
-
if (options
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
var _a2;
|
|
860
|
-
return (_a2 = options == null ? void 0 : options.selectedList) == null ? void 0 : _a2.some(
|
|
861
|
-
(n) => {
|
|
862
|
-
var _a3;
|
|
863
|
-
return n.discountId === ((_a3 = item.discount) == null ? void 0 : _a3.resource_id);
|
|
864
|
-
}
|
|
865
|
-
);
|
|
866
|
-
}
|
|
867
|
-
))) {
|
|
1047
|
+
if (options?.selectedList) {
|
|
1048
|
+
// 主商品:检查自己的 discount_list
|
|
1049
|
+
if (flatItem.type === 'main' && product.discount_list?.some(item => options?.selectedList?.some(n => n.discountId === item.discount?.resource_id))) {
|
|
868
1050
|
isManualDiscount = false;
|
|
869
1051
|
}
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
var _a2;
|
|
874
|
-
return (_a2 = options == null ? void 0 : options.selectedList) == null ? void 0 : _a2.some(
|
|
875
|
-
(n) => {
|
|
876
|
-
var _a3;
|
|
877
|
-
return n.discountId === ((_a3 = item.discount) == null ? void 0 : _a3.resource_id);
|
|
878
|
-
}
|
|
879
|
-
);
|
|
880
|
-
}
|
|
881
|
-
)) || ((_t = (_s = flatItem.parentProduct) == null ? void 0 : _s.discount_list) == null ? void 0 : _t.some(
|
|
882
|
-
(item) => {
|
|
883
|
-
var _a2;
|
|
884
|
-
return (_a2 = options == null ? void 0 : options.selectedList) == null ? void 0 : _a2.some(
|
|
885
|
-
(n) => {
|
|
886
|
-
var _a3;
|
|
887
|
-
return n.discountId === ((_a3 = item.discount) == null ? void 0 : _a3.resource_id);
|
|
888
|
-
}
|
|
889
|
-
);
|
|
890
|
-
}
|
|
891
|
-
))) {
|
|
1052
|
+
// bundle子商品:检查自己的 discount_list 或父主商品的 discount_list
|
|
1053
|
+
if (flatItem.type === 'bundle') {
|
|
1054
|
+
if (product.discount_list?.some(item => options?.selectedList?.some(n => n.discountId === item.discount?.resource_id)) || flatItem.parentProduct?.discount_list?.some(item => options?.selectedList?.some(n => n.discountId === item.discount?.resource_id))) {
|
|
892
1055
|
isManualDiscount = false;
|
|
893
1056
|
}
|
|
894
1057
|
}
|
|
895
1058
|
}
|
|
896
|
-
|
|
897
|
-
|
|
1059
|
+
|
|
1060
|
+
// 如果没有适用的优惠券,或者手动折扣,则不适用优惠券
|
|
1061
|
+
if (applicableDiscounts.length === 0 || isManualDiscount || (0, _lodashEs.isBoolean)(product.vouchersApplicable) && !product.vouchersApplicable) {
|
|
1062
|
+
if (flatItem.type === 'main') {
|
|
1063
|
+
// 主商品:保持原有逻辑,还原 option 价格
|
|
898
1064
|
const restoredOptions = restoreOptionPrices(product.options);
|
|
899
1065
|
if (product.isClient) {
|
|
900
|
-
processedProductsMap.set(product._id, [
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
bundle: product.bundle,
|
|
908
|
-
options: restoredOptions
|
|
909
|
-
}),
|
|
910
|
-
variant: originProduct._productInit.variant,
|
|
911
|
-
original_price: originProduct._productInit.original_price,
|
|
912
|
-
total: (0, import_utils2.getProductTotalPrice)({
|
|
913
|
-
product: {
|
|
914
|
-
price: product.price
|
|
915
|
-
},
|
|
916
|
-
bundle: product.bundle,
|
|
917
|
-
options: restoredOptions
|
|
918
|
-
}),
|
|
919
|
-
price: product.price,
|
|
1066
|
+
processedProductsMap.set(product._id, [this.hooks.setProduct(originProduct, {
|
|
1067
|
+
...(isManualDiscount ? {} : {
|
|
1068
|
+
origin_total: (0, _utils2.getProductOriginTotalPrice)({
|
|
1069
|
+
product: {
|
|
1070
|
+
original_price: product.original_price
|
|
1071
|
+
},
|
|
1072
|
+
bundle: product.bundle,
|
|
920
1073
|
options: restoredOptions
|
|
921
|
-
},
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
1074
|
+
}),
|
|
1075
|
+
variant: originProduct._productInit.variant,
|
|
1076
|
+
original_price: originProduct._productInit.original_price,
|
|
1077
|
+
total: (0, _utils2.getProductTotalPrice)({
|
|
1078
|
+
product: {
|
|
1079
|
+
price: product.price
|
|
1080
|
+
},
|
|
1081
|
+
bundle: product.bundle,
|
|
1082
|
+
options: restoredOptions
|
|
1083
|
+
}),
|
|
1084
|
+
price: product.price,
|
|
1085
|
+
options: restoredOptions
|
|
1086
|
+
}),
|
|
1087
|
+
discount_list: isManualDiscount ? this.resolveDiscountListForManualOverride(product.discount_list) : this.filterDiscountListByType(product.discount_list, 'promotion')
|
|
1088
|
+
})]);
|
|
925
1089
|
} else {
|
|
926
|
-
let total = product.inPromotion ?
|
|
1090
|
+
let total = product.inPromotion ? product?._promotion?.finalPrice ?? product.origin_total ?? product.total : product.origin_total ?? product.total;
|
|
927
1091
|
let main_product_selling_price = product.price ?? product.main_product_selling_price;
|
|
928
|
-
if ((product.discount_list || []).some(
|
|
1092
|
+
if ((product.discount_list || []).some(item => item.type === 'promotion') || (0, _lodashEs.isBoolean)(product.vouchersApplicable) && !product.vouchersApplicable) {
|
|
929
1093
|
total = product.total ?? product.origin_total;
|
|
930
1094
|
main_product_selling_price = product.main_product_selling_price ?? main_product_selling_price;
|
|
931
1095
|
}
|
|
932
|
-
processedProductsMap.set(product._id, [
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
discount_list: this.filterDiscountListByType(product.discount_list, "promotion")
|
|
945
|
-
})
|
|
946
|
-
]);
|
|
1096
|
+
processedProductsMap.set(product._id, [this.hooks.setProduct(originProduct, {
|
|
1097
|
+
...(isManualDiscount ? {
|
|
1098
|
+
price: product.price
|
|
1099
|
+
} : {
|
|
1100
|
+
_id: product._id.split('___')[0] + '___' + index,
|
|
1101
|
+
total,
|
|
1102
|
+
price: product.price,
|
|
1103
|
+
main_product_selling_price,
|
|
1104
|
+
options: restoredOptions
|
|
1105
|
+
}),
|
|
1106
|
+
discount_list: isManualDiscount ? this.resolveDiscountListForManualOverride(product.discount_list) : this.filterDiscountListByType(product.discount_list, 'promotion')
|
|
1107
|
+
})]);
|
|
947
1108
|
}
|
|
948
1109
|
} else {
|
|
1110
|
+
// bundle子商品:保存到扁平化Map
|
|
949
1111
|
processedFlatItemsMap.set(flatItem._id, [{
|
|
950
1112
|
...flatItem,
|
|
951
|
-
discount_list: this.
|
|
1113
|
+
discount_list: isManualDiscount ? this.resolveDiscountListForManualOverride(flatItem.bundleItem?.discount_list || []) : this.filterDiscountListByType(flatItem.bundleItem?.discount_list || [], 'promotion'),
|
|
952
1114
|
price: isManualDiscount ? flatItem.bundleItem.price : flatItem.bundleItem.original_price,
|
|
953
1115
|
processed: true
|
|
954
1116
|
}]);
|
|
955
1117
|
}
|
|
956
1118
|
return;
|
|
957
1119
|
}
|
|
958
|
-
|
|
1120
|
+
const shouldReapplyBookingDiscountForProduct = shouldReapplyBookingDiscount(originProduct, selectedDiscount);
|
|
1121
|
+
if (applicableDiscounts.length && product.booking_id && typeof selectedDiscount.isManualSelect === 'undefined' && !options?.scan && !isEditModeAddNewProduct && !shouldReapplyBookingDiscountForProduct) {
|
|
959
1122
|
return;
|
|
960
1123
|
}
|
|
1124
|
+
|
|
1125
|
+
// 是否需要拆分(商品券需要拆分,折扣卡在 applicableProductLimit > 0 时也需要拆分)
|
|
961
1126
|
const discountType = selectedDiscount.tag || selectedDiscount.type;
|
|
962
|
-
const isGoodPass = discountType ===
|
|
963
|
-
const isDiscountCard = [
|
|
964
|
-
const applicableProductLimit =
|
|
1127
|
+
const isGoodPass = discountType === 'good_pass';
|
|
1128
|
+
const isDiscountCard = ['discount_card', 'product_discount_card'].includes(discountType);
|
|
1129
|
+
const applicableProductLimit = selectedDiscount.config?.applicableProductLimit || 0;
|
|
1130
|
+
|
|
1131
|
+
// 商品券始终需要拆分,折扣卡在 applicableProductLimit > 0 时需要拆分
|
|
965
1132
|
const isNeedSplit = isGoodPass || isDiscountCard && applicableProductLimit > 0;
|
|
1133
|
+
|
|
1134
|
+
// 需要拆分出来的数量
|
|
966
1135
|
const totalQuantity = product.quantity || product.num || 1;
|
|
967
|
-
const availableGoodPassCount = applicableDiscounts.filter(
|
|
968
|
-
|
|
969
|
-
|
|
1136
|
+
const availableGoodPassCount = applicableDiscounts.filter(item => (item.tag || item.type) === 'good_pass').length;
|
|
1137
|
+
|
|
1138
|
+
// 🔥 计算最大使用次数限制
|
|
970
1139
|
let maxUsageLimit;
|
|
971
1140
|
if (isGoodPass) {
|
|
1141
|
+
// 商品券:使用 maxUsagePerOrder 限制
|
|
972
1142
|
maxUsageLimit = availableGoodPassCount;
|
|
973
1143
|
if (selectedDiscount.config && selectedDiscount.config.maxUsagePerOrder) {
|
|
974
|
-
maxUsageLimit = Math.min(
|
|
975
|
-
availableGoodPassCount,
|
|
976
|
-
selectedDiscount.config.maxUsagePerOrder
|
|
977
|
-
);
|
|
1144
|
+
maxUsageLimit = Math.min(availableGoodPassCount, selectedDiscount.config.maxUsagePerOrder);
|
|
978
1145
|
}
|
|
979
1146
|
} else if (isDiscountCard && applicableProductLimit > 0) {
|
|
1147
|
+
// 折扣卡:使用 applicableProductLimit 限制(一个商品使用一次折扣卡算1次)
|
|
1148
|
+
// 需要减去已使用次数(跨商品全局计数)
|
|
980
1149
|
const usedLimitCount = usedDiscountCardLimitCounts.get(selectedDiscount.id) || 0;
|
|
981
1150
|
maxUsageLimit = Math.max(0, applicableProductLimit - usedLimitCount);
|
|
982
1151
|
} else {
|
|
1152
|
+
// 其他情况不拆分
|
|
983
1153
|
maxUsageLimit = 1;
|
|
984
1154
|
}
|
|
985
1155
|
const splitCount = isNeedSplit ? Math.min(product.quantity || product.num || 1, maxUsageLimit) : 1;
|
|
986
1156
|
const arr = [];
|
|
987
|
-
|
|
1157
|
+
|
|
1158
|
+
// 🔥 主商品和bundle子商品分别处理
|
|
1159
|
+
if (flatItem.type === 'main') {
|
|
1160
|
+
// 主商品:保持原有逻辑
|
|
988
1161
|
if (splitCount < totalQuantity && isNeedSplit) {
|
|
989
1162
|
let total = product.origin_total ?? product.total;
|
|
990
|
-
if ((product.discount_list || []).some(
|
|
1163
|
+
if ((product.discount_list || []).some(item => item.type === 'promotion')) {
|
|
991
1164
|
total = product.total ?? product.origin_total;
|
|
992
1165
|
}
|
|
993
|
-
arr.push(
|
|
994
|
-
this.
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
})
|
|
1000
|
-
);
|
|
1166
|
+
arr.push(this.hooks.setProduct(originProduct, {
|
|
1167
|
+
discount_list: this.filterDiscountListByType(product.discount_list, 'promotion'),
|
|
1168
|
+
quantity: totalQuantity - splitCount,
|
|
1169
|
+
_id: product._id.split('___')[0],
|
|
1170
|
+
total
|
|
1171
|
+
}));
|
|
1001
1172
|
}
|
|
1002
1173
|
for (let i = 0; i < splitCount; i++) {
|
|
1174
|
+
// 商品券:每个拆分商品使用不同的商品券(从 applicableDiscounts 按索引取)
|
|
1175
|
+
// 折扣卡:所有拆分商品使用同一张折扣卡(selectedDiscountCard 或 applicableDiscounts[0])
|
|
1003
1176
|
const currentSelectedDiscount = isGoodPass ? selectedDiscountCard || applicableDiscounts[i] : selectedDiscountCard || applicableDiscounts[0];
|
|
1177
|
+
// 标记优惠券为已使用
|
|
1004
1178
|
usedDiscounts.set(currentSelectedDiscount.id, true);
|
|
1005
|
-
|
|
1179
|
+
// 🔥 更新 product_id 使用计数
|
|
1180
|
+
if ((currentSelectedDiscount.tag || currentSelectedDiscount.type) === 'good_pass') {
|
|
1006
1181
|
const currentCount = usedProductIdCounts.get(currentSelectedDiscount.product_id) || 0;
|
|
1007
1182
|
usedProductIdCounts.set(currentSelectedDiscount.product_id, currentCount + 1);
|
|
1008
1183
|
}
|
|
1184
|
+
// 🔥 更新折扣卡 applicableProductLimit 使用计数(跨商品全局)
|
|
1009
1185
|
const currentDiscountTypeForCount = currentSelectedDiscount.tag || currentSelectedDiscount.type;
|
|
1010
|
-
if ([
|
|
1186
|
+
if (['discount_card', 'product_discount_card'].includes(currentDiscountTypeForCount)) {
|
|
1011
1187
|
const currentLimitCount = usedDiscountCardLimitCounts.get(currentSelectedDiscount.id) || 0;
|
|
1012
1188
|
usedDiscountCardLimitCounts.set(currentSelectedDiscount.id, currentLimitCount + 1);
|
|
1013
1189
|
}
|
|
1190
|
+
|
|
1191
|
+
// 记录实际应用了优惠券的商品信息
|
|
1014
1192
|
const appliedProducts = appliedDiscountProducts.get(currentSelectedDiscount.id) || [];
|
|
1193
|
+
|
|
1194
|
+
// 优先从 origin_total拿,可能会拿不到(比如用户端预约在没有配置 original_price 的情况下)
|
|
1015
1195
|
let productOriginTotal = product.origin_total || product.total || 0;
|
|
1016
|
-
|
|
1196
|
+
// 如果当前 product 有 discount_list,则先从 origin_total 拿
|
|
1197
|
+
if (this.filterDiscountListByType(product.discount_list, 'promotion').length && product.origin_total) {
|
|
1017
1198
|
productOriginTotal = product.origin_total;
|
|
1018
1199
|
}
|
|
1019
|
-
|
|
1200
|
+
// 如果originProduct?._productInit?.original_price为 0,product.origin_total可能为空,此时取 product.total
|
|
1201
|
+
if (Number(originProduct?._productInit?.original_price || 0) > 0 && product.origin_total && product.total && product.origin_total !== product.total) {
|
|
1020
1202
|
productOriginTotal = product.total;
|
|
1021
1203
|
}
|
|
1022
|
-
|
|
1204
|
+
|
|
1205
|
+
// 计算使用折扣卡/商品券以后,单个商品的总 total
|
|
1206
|
+
// 🔥 检查是否是 order_level 固定金额折扣卡
|
|
1207
|
+
const isOrderLevel = (0, _utils.isOrderLevelFixedAmountDiscount)(selectedDiscount);
|
|
1023
1208
|
const orderLevelAllocation = isOrderLevel ? orderLevelDiscountAllocations.get(selectedDiscount.id) : null;
|
|
1024
|
-
const productAllocation = orderLevelAllocation
|
|
1025
|
-
const isDeductOptionPrice = !!
|
|
1209
|
+
const productAllocation = orderLevelAllocation?.get(flatItem._id);
|
|
1210
|
+
const isDeductOptionPrice = !!selectedDiscount.config?.deductOptionPrice;
|
|
1211
|
+
|
|
1212
|
+
// 主商品折后价(不含 option);勿与 option 合计混在同一变量,否则 main_product_selling_price 错误
|
|
1026
1213
|
let mainProductSellingPrice;
|
|
1027
1214
|
let amount;
|
|
1028
1215
|
let productDiscountDifference;
|
|
1029
1216
|
let discountedOptions = product.options;
|
|
1030
1217
|
let optionDiscountAmount = 0;
|
|
1218
|
+
let reapplyDiscountPercent;
|
|
1031
1219
|
if (isOrderLevel && productAllocation) {
|
|
1220
|
+
// order_level:使用预计算的分摊金额
|
|
1032
1221
|
amount = productAllocation.discountAmount;
|
|
1033
1222
|
productDiscountDifference = productAllocation.difference;
|
|
1034
|
-
mainProductSellingPrice = Math.max(new
|
|
1223
|
+
mainProductSellingPrice = Math.max(new _decimal.default(product.price).minus(amount).toNumber(), 0);
|
|
1224
|
+
} else if (resolveReapplyEditModeDiscountPercent(originProduct, selectedDiscount) !== undefined) {
|
|
1225
|
+
// Change time 重套编辑态折扣卡时,历史 par_value 不是运行时折扣口径,优先使用后端快照里的 percent。
|
|
1226
|
+
const percent = resolveReapplyEditModeDiscountPercent(originProduct, selectedDiscount);
|
|
1227
|
+
reapplyDiscountPercent = new _decimal.default(percent).toFixed(2);
|
|
1228
|
+
mainProductSellingPrice = new _decimal.default(product.price || 0).mul(new _decimal.default(100).minus(percent)).div(100).toDecimalPlaces(2).toNumber();
|
|
1229
|
+
amount = new _decimal.default(product.price).minus(mainProductSellingPrice).toNumber();
|
|
1035
1230
|
} else {
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
product.price
|
|
1040
|
-
);
|
|
1041
|
-
amount = new import_decimal.default(product.price).minus(new import_decimal.default(mainProductSellingPrice)).toNumber();
|
|
1231
|
+
// item_level 或其他类型:使用原有逻辑
|
|
1232
|
+
mainProductSellingPrice = (0, _utils.getDiscountAmount)(selectedDiscount, product.price, product.price);
|
|
1233
|
+
amount = new _decimal.default(product.price).minus(new _decimal.default(mainProductSellingPrice)).toNumber();
|
|
1042
1234
|
}
|
|
1043
|
-
if (isDeductOptionPrice &&
|
|
1044
|
-
const optionResult = applyDiscountToOptions(
|
|
1045
|
-
product.options,
|
|
1046
|
-
currentSelectedDiscount
|
|
1047
|
-
);
|
|
1235
|
+
if (isDeductOptionPrice && product.options?.length) {
|
|
1236
|
+
const optionResult = applyDiscountToOptions(product.options, selectedDiscount);
|
|
1048
1237
|
discountedOptions = optionResult.discountedOptions;
|
|
1049
1238
|
optionDiscountAmount = optionResult.optionDiscountAmount;
|
|
1050
1239
|
}
|
|
1051
|
-
|
|
1240
|
+
|
|
1241
|
+
// total = 主商品折后 + option 行合计;开启 option 抵扣时必须用 discountedOptions,不能用未折扣的 product.options 再算一遍
|
|
1242
|
+
const optionsForLineTotal = isDeductOptionPrice && product.options?.length ? discountedOptions : product.options;
|
|
1052
1243
|
const optionsLineTotal = getOptionTotal(optionsForLineTotal || []);
|
|
1053
|
-
const total = new
|
|
1244
|
+
const total = new _decimal.default(mainProductSellingPrice).plus(optionsLineTotal).toNumber();
|
|
1245
|
+
|
|
1246
|
+
// discount_list.amount / fixed_amount:行级总优惠 = 主商品优惠 + option 优惠(与 total 变化一致)
|
|
1054
1247
|
const mainProductDiscountAmount = amount;
|
|
1055
|
-
const lineDiscountAmount = new
|
|
1056
|
-
const
|
|
1057
|
-
const
|
|
1058
|
-
const currentIsGoodPass = currentDiscountType === "good_pass";
|
|
1248
|
+
const lineDiscountAmount = new _decimal.default(mainProductDiscountAmount).plus(optionDiscountAmount).toNumber();
|
|
1249
|
+
const discountType = selectedDiscount.tag || selectedDiscount.type;
|
|
1250
|
+
const currentIsGoodPass = discountType === 'good_pass';
|
|
1059
1251
|
const discountDetail = {
|
|
1060
1252
|
amount: lineDiscountAmount,
|
|
1061
|
-
type: selectedDiscount.tag ===
|
|
1253
|
+
type: selectedDiscount.tag === 'product_discount_card' ? 'discount_card' : discountType,
|
|
1062
1254
|
discount: {
|
|
1063
|
-
discount_card_type:
|
|
1255
|
+
discount_card_type: selectedDiscount?.metadata?.discount_card_type,
|
|
1064
1256
|
fixed_amount: lineDiscountAmount,
|
|
1065
|
-
discount_calculation_mode:
|
|
1257
|
+
discount_calculation_mode: selectedDiscount?.metadata?.discount_calculation_mode,
|
|
1066
1258
|
resource_id: selectedDiscount.id,
|
|
1067
1259
|
title: selectedDiscount.format_title,
|
|
1068
1260
|
original_amount: product.price,
|
|
1069
|
-
product_id: originProduct.id,
|
|
1070
|
-
percent:
|
|
1071
|
-
discount_product_id:
|
|
1261
|
+
product_id: originProduct.id || originProduct.product_id,
|
|
1262
|
+
percent: reapplyDiscountPercent ?? selectedDiscount.par_value,
|
|
1263
|
+
discount_product_id: selectedDiscount.product_id
|
|
1072
1264
|
},
|
|
1073
1265
|
// 前端使用的num数量,为了计算优惠金额(拆分时为1)
|
|
1074
1266
|
_num: isNeedSplit ? 1 : product.num,
|
|
1075
|
-
config: currentSelectedDiscount
|
|
1267
|
+
config: currentSelectedDiscount?.config,
|
|
1076
1268
|
metadata: {
|
|
1077
1269
|
num: 1,
|
|
1078
|
-
discount_rule_uncheck_flag: selectedDiscount
|
|
1270
|
+
discount_rule_uncheck_flag: selectedDiscount?.discount_rule_uncheck_flag,
|
|
1079
1271
|
// 🔥 order_level 分摊差值
|
|
1080
|
-
...productDiscountDifference !==
|
|
1272
|
+
...(productDiscountDifference !== undefined && {
|
|
1273
|
+
product_discount_difference: productDiscountDifference
|
|
1274
|
+
}),
|
|
1081
1275
|
/** 仅主商品上的优惠金额(不含 option) */
|
|
1082
1276
|
mainProductDiscountAmount,
|
|
1083
1277
|
optionDiscountAmount
|
|
1084
1278
|
}
|
|
1085
1279
|
};
|
|
1086
1280
|
appliedProducts.push(discountDetail);
|
|
1087
|
-
appliedDiscountProducts.set(
|
|
1281
|
+
appliedDiscountProducts.set(selectedDiscount.id, appliedProducts);
|
|
1282
|
+
|
|
1283
|
+
// 记录应用了优惠券的商品
|
|
1284
|
+
// 后续更新价格改为 getProductTotalPrice getProductOriginTotalPrice逻辑
|
|
1088
1285
|
if (product.isClient) {
|
|
1089
|
-
arr.push(
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
})
|
|
1107
|
-
);
|
|
1286
|
+
arr.push(this.hooks.setProduct(originProduct, {
|
|
1287
|
+
discount_list: [discountDetail],
|
|
1288
|
+
// good_pass:主商品价以折后价为准;勿用 product.price - amount(amount 含 option 优惠时会算错)
|
|
1289
|
+
price: isGoodPass ? mainProductSellingPrice : product.price,
|
|
1290
|
+
quantity: isNeedSplit ? 1 : product.quantity,
|
|
1291
|
+
origin_total: (0, _utils2.getProductOriginTotalPrice)({
|
|
1292
|
+
product: {
|
|
1293
|
+
original_price: product.original_price
|
|
1294
|
+
},
|
|
1295
|
+
bundle: product.bundle,
|
|
1296
|
+
options: restoreOptionPrices(product.options)
|
|
1297
|
+
}),
|
|
1298
|
+
variant: originProduct._productInit.variant,
|
|
1299
|
+
original_price: new _decimal.default(product.price || 0).toNumber(),
|
|
1300
|
+
total: total,
|
|
1301
|
+
options: discountedOptions
|
|
1302
|
+
}));
|
|
1108
1303
|
} else {
|
|
1109
|
-
arr.push(
|
|
1110
|
-
this.
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
})
|
|
1120
|
-
);
|
|
1304
|
+
arr.push(this.hooks.setProduct(originProduct, {
|
|
1305
|
+
discount_list: this.filterDiscountListByType(product.discount_list, 'promotion').concat([discountDetail]),
|
|
1306
|
+
_id: product._id.split('___')[0] + '___' + currentSelectedDiscount.id + '_' + i + '_' + index,
|
|
1307
|
+
price: currentIsGoodPass ? 0 : product.price,
|
|
1308
|
+
quantity: isNeedSplit ? 1 : product.quantity,
|
|
1309
|
+
total: total,
|
|
1310
|
+
origin_total: productOriginTotal,
|
|
1311
|
+
main_product_selling_price: mainProductSellingPrice,
|
|
1312
|
+
options: discountedOptions
|
|
1313
|
+
}));
|
|
1121
1314
|
}
|
|
1122
1315
|
}
|
|
1123
1316
|
processedProductsMap.set(product._id, arr);
|
|
1124
1317
|
} else {
|
|
1318
|
+
// 🔥 bundle子商品:支持拆分(商品券和折扣卡在 applicableProductLimit > 0 时都需要拆分)
|
|
1125
1319
|
const processedItems = [];
|
|
1126
1320
|
if (isNeedSplit) {
|
|
1321
|
+
// 需要拆分数量(商品券或折扣卡有 applicableProductLimit 限制)
|
|
1127
1322
|
const discountNum = splitCount;
|
|
1128
1323
|
const normalNum = totalQuantity - discountNum;
|
|
1324
|
+
|
|
1325
|
+
// 生成有折扣的商品(每个拆分商品 num: 1)
|
|
1129
1326
|
for (let i = 0; i < discountNum; i++) {
|
|
1327
|
+
// 商品券:每个拆分商品使用不同的商品券
|
|
1328
|
+
// 折扣卡:所有拆分商品使用同一张折扣卡
|
|
1130
1329
|
const currentBundleDiscount = isGoodPass ? selectedDiscountCard || applicableDiscounts[i] : selectedDiscountCard || applicableDiscounts[0];
|
|
1131
1330
|
usedDiscounts.set(currentBundleDiscount.id, true);
|
|
1132
|
-
|
|
1331
|
+
// 🔥 更新 product_id 使用计数
|
|
1332
|
+
if ((currentBundleDiscount.tag || currentBundleDiscount.type) === 'good_pass') {
|
|
1133
1333
|
const currentCount = usedProductIdCounts.get(currentBundleDiscount.product_id) || 0;
|
|
1134
1334
|
usedProductIdCounts.set(currentBundleDiscount.product_id, currentCount + 1);
|
|
1135
1335
|
}
|
|
1336
|
+
// 🔥 更新折扣卡 applicableProductLimit 使用计数(跨商品全局)
|
|
1136
1337
|
const bundleDiscountTypeForCount = currentBundleDiscount.tag || currentBundleDiscount.type;
|
|
1137
|
-
if ([
|
|
1338
|
+
if (['discount_card', 'product_discount_card'].includes(bundleDiscountTypeForCount)) {
|
|
1138
1339
|
const currentBundleLimitCount = usedDiscountCardLimitCounts.get(currentBundleDiscount.id) || 0;
|
|
1139
1340
|
usedDiscountCardLimitCounts.set(currentBundleDiscount.id, currentBundleLimitCount + 1);
|
|
1140
1341
|
}
|
|
1342
|
+
|
|
1343
|
+
// 🔥 生成唯一的 _id
|
|
1141
1344
|
const uniqueId = `${flatItem._id}_split_${i}`;
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1345
|
+
|
|
1346
|
+
// 计算折扣后的价格(使用 getDiscountAmount,内部已处理 maxDeductionAmount)
|
|
1347
|
+
const discountedPrice = (0, _utils.getDiscountAmount)(currentBundleDiscount, product.origin_total, product.origin_total);
|
|
1348
|
+
const bundleDiscountAmount = new _decimal.default(product.origin_total || 0).minus(discountedPrice).toNumber();
|
|
1349
|
+
debugger;
|
|
1350
|
+
|
|
1351
|
+
// 确定折扣类型
|
|
1352
|
+
const bundleDiscountType = currentBundleDiscount.tag === 'product_discount_card' ? 'discount_card' : currentBundleDiscount.tag;
|
|
1149
1353
|
const discountDetail = {
|
|
1150
1354
|
amount: bundleDiscountAmount,
|
|
1151
1355
|
type: bundleDiscountType,
|
|
1152
1356
|
discount: {
|
|
1153
|
-
discount_card_type:
|
|
1154
|
-
discount_calculation_mode:
|
|
1357
|
+
discount_card_type: currentBundleDiscount?.metadata?.discount_card_type,
|
|
1358
|
+
discount_calculation_mode: currentBundleDiscount?.metadata?.discount_calculation_mode,
|
|
1155
1359
|
fixed_amount: bundleDiscountAmount,
|
|
1156
1360
|
resource_id: currentBundleDiscount.id,
|
|
1157
1361
|
title: currentBundleDiscount.format_title,
|
|
1158
1362
|
original_amount: product.origin_total,
|
|
1159
|
-
product_id: product.id,
|
|
1160
|
-
|
|
1161
|
-
discount_product_id: currentBundleDiscount.product_id
|
|
1363
|
+
product_id: product.id || product.product_id,
|
|
1364
|
+
discount_product_id: selectedDiscount.product_id
|
|
1162
1365
|
},
|
|
1163
1366
|
metadata: {
|
|
1164
1367
|
// 🔥 使用拆分后的唯一 _id
|
|
1165
1368
|
custom_product_bundle_map_id: uniqueId,
|
|
1166
|
-
discount_rule_uncheck_flag: selectedDiscount
|
|
1369
|
+
discount_rule_uncheck_flag: selectedDiscount?.discount_rule_uncheck_flag,
|
|
1167
1370
|
num: 1
|
|
1168
1371
|
},
|
|
1169
1372
|
_num: 1,
|
|
1170
|
-
config: currentBundleDiscount
|
|
1373
|
+
config: currentBundleDiscount?.config
|
|
1171
1374
|
};
|
|
1375
|
+
|
|
1376
|
+
// 记录实际应用的折扣
|
|
1172
1377
|
const appliedProducts = appliedDiscountProducts.get(currentBundleDiscount.id) || [];
|
|
1173
1378
|
appliedProducts.push(discountDetail);
|
|
1174
1379
|
appliedDiscountProducts.set(currentBundleDiscount.id, appliedProducts);
|
|
@@ -1186,28 +1391,29 @@ var RulesModule = class extends import_BaseModule.BaseModule {
|
|
|
1186
1391
|
_discountId: currentBundleDiscount.id
|
|
1187
1392
|
});
|
|
1188
1393
|
}
|
|
1394
|
+
|
|
1395
|
+
// 🔥 对拆分后的剩余商品继续匹配其他可用折扣(而不是等到下次执行才应用)
|
|
1189
1396
|
let remainingNum = normalNum;
|
|
1190
1397
|
let continueSplitIndex = discountNum;
|
|
1191
1398
|
while (remainingNum > 0) {
|
|
1192
|
-
|
|
1193
|
-
|
|
1399
|
+
// 重新查找适用于剩余商品的可用折扣(已使用的会被自动过滤掉)
|
|
1400
|
+
const nextApplicableDiscounts = sortedDiscountList.filter(nextDiscount => {
|
|
1194
1401
|
const nextDiscountType = nextDiscount.tag || nextDiscount.type;
|
|
1195
|
-
|
|
1196
|
-
|
|
1402
|
+
// 检查策略可用性
|
|
1403
|
+
if (['good_pass', 'discount_card', 'product_discount_card'].includes(nextDiscountType)) {
|
|
1404
|
+
if (nextDiscount.config === undefined || !nextDiscount?.config?.isAvailable) {
|
|
1197
1405
|
return false;
|
|
1198
1406
|
}
|
|
1199
1407
|
}
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
if (
|
|
1208
|
-
|
|
1209
|
-
if (nextDiscountType === "good_pass") {
|
|
1210
|
-
const maxUsagePerOrder = (_c2 = nextDiscount.config) == null ? void 0 : _c2.maxUsagePerOrder;
|
|
1408
|
+
// 折扣卡时总价为0时不可用
|
|
1409
|
+
if ((Number(product.total) <= 0 || !product.total) && !product.discount_list?.find(n => n.discount?.resource_id === nextDiscount.id) && nextDiscountType !== 'good_pass') return false;
|
|
1410
|
+
|
|
1411
|
+
// 商品券已被使用则跳过
|
|
1412
|
+
if (usedDiscounts.get(nextDiscount.id) && nextDiscountType === 'good_pass') return false;
|
|
1413
|
+
|
|
1414
|
+
// 检查 maxUsagePerOrder 限制
|
|
1415
|
+
if (nextDiscountType === 'good_pass') {
|
|
1416
|
+
const maxUsagePerOrder = nextDiscount.config?.maxUsagePerOrder;
|
|
1211
1417
|
if (maxUsagePerOrder && maxUsagePerOrder > 0) {
|
|
1212
1418
|
const currentUsedCount = usedProductIdCounts.get(nextDiscount.product_id) || 0;
|
|
1213
1419
|
if (currentUsedCount >= maxUsagePerOrder) {
|
|
@@ -1215,8 +1421,10 @@ var RulesModule = class extends import_BaseModule.BaseModule {
|
|
|
1215
1421
|
}
|
|
1216
1422
|
}
|
|
1217
1423
|
}
|
|
1218
|
-
|
|
1219
|
-
|
|
1424
|
+
|
|
1425
|
+
// 检查折扣卡的 applicableProductLimit 限制
|
|
1426
|
+
if (['discount_card', 'product_discount_card'].includes(nextDiscountType)) {
|
|
1427
|
+
const limitConfig = nextDiscount.config?.applicableProductLimit || 0;
|
|
1220
1428
|
if (limitConfig > 0) {
|
|
1221
1429
|
const currentUsedLimitCount = usedDiscountCardLimitCounts.get(nextDiscount.id) || 0;
|
|
1222
1430
|
if (currentUsedLimitCount >= limitConfig) {
|
|
@@ -1225,7 +1433,7 @@ var RulesModule = class extends import_BaseModule.BaseModule {
|
|
|
1225
1433
|
}
|
|
1226
1434
|
}
|
|
1227
1435
|
const limitedData = nextDiscount.limited_relation_product_data;
|
|
1228
|
-
if (limitedData.type ===
|
|
1436
|
+
if (limitedData.type === 'product_all') {
|
|
1229
1437
|
if (limitedData.filter === 1 && limitedData.exclude_product_ids.includes(product.id)) {
|
|
1230
1438
|
return false;
|
|
1231
1439
|
}
|
|
@@ -1241,6 +1449,8 @@ var RulesModule = class extends import_BaseModule.BaseModule {
|
|
|
1241
1449
|
}
|
|
1242
1450
|
return false;
|
|
1243
1451
|
});
|
|
1452
|
+
|
|
1453
|
+
// 没有更多可用折扣,剩余商品无折扣存储
|
|
1244
1454
|
if (nextApplicableDiscounts.length === 0) {
|
|
1245
1455
|
processedItems.push({
|
|
1246
1456
|
...flatItem,
|
|
@@ -1252,37 +1462,40 @@ var RulesModule = class extends import_BaseModule.BaseModule {
|
|
|
1252
1462
|
});
|
|
1253
1463
|
break;
|
|
1254
1464
|
}
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
);
|
|
1465
|
+
|
|
1466
|
+
// 选择最优折扣
|
|
1467
|
+
const nextSelectedDiscountCard = nextApplicableDiscounts.find(n => n.isScan && n.isSelected && (n.tag || n.type) !== 'good_pass');
|
|
1258
1468
|
const nextSelectedDiscount = nextSelectedDiscountCard || nextApplicableDiscounts[0];
|
|
1469
|
+
|
|
1470
|
+
// 判断新折扣是否需要拆分
|
|
1259
1471
|
const nextDiscType = nextSelectedDiscount.tag || nextSelectedDiscount.type;
|
|
1260
|
-
const nextIsGoodPass = nextDiscType ===
|
|
1261
|
-
const nextIsDiscountCard = [
|
|
1262
|
-
const nextApplicableProductLimit =
|
|
1472
|
+
const nextIsGoodPass = nextDiscType === 'good_pass';
|
|
1473
|
+
const nextIsDiscountCard = ['discount_card', 'product_discount_card'].includes(nextDiscType);
|
|
1474
|
+
const nextApplicableProductLimit = nextSelectedDiscount.config?.applicableProductLimit || 0;
|
|
1263
1475
|
const nextIsNeedSplit = nextIsGoodPass || nextIsDiscountCard && nextApplicableProductLimit > 0;
|
|
1264
1476
|
if (!nextIsNeedSplit) {
|
|
1477
|
+
// 🔥 不需要拆分:直接应用折扣到所有剩余商品
|
|
1265
1478
|
const nextBundleDiscount = nextSelectedDiscountCard || nextApplicableDiscounts[0];
|
|
1266
1479
|
usedDiscounts.set(nextBundleDiscount.id, true);
|
|
1267
|
-
if ((nextBundleDiscount.tag || nextBundleDiscount.type) ===
|
|
1480
|
+
if ((nextBundleDiscount.tag || nextBundleDiscount.type) === 'good_pass') {
|
|
1268
1481
|
const curCount = usedProductIdCounts.get(nextBundleDiscount.product_id) || 0;
|
|
1269
1482
|
usedProductIdCounts.set(nextBundleDiscount.product_id, curCount + 1);
|
|
1270
1483
|
}
|
|
1271
1484
|
const nextNoSplitType = nextBundleDiscount.tag || nextBundleDiscount.type;
|
|
1272
|
-
if ([
|
|
1485
|
+
if (['discount_card', 'product_discount_card'].includes(nextNoSplitType)) {
|
|
1273
1486
|
const curCount = usedDiscountCardLimitCounts.get(nextBundleDiscount.id) || 0;
|
|
1274
1487
|
usedDiscountCardLimitCounts.set(nextBundleDiscount.id, curCount + remainingNum);
|
|
1275
1488
|
}
|
|
1276
1489
|
const nextOriginTotal = product.original_price || product.price || 0;
|
|
1277
|
-
const nextTargetTotal = (0,
|
|
1278
|
-
const nextDiscountAmount = new
|
|
1490
|
+
const nextTargetTotal = (0, _utils.getDiscountAmount)(nextBundleDiscount, nextOriginTotal, nextOriginTotal);
|
|
1491
|
+
const nextDiscountAmount = new _decimal.default(nextOriginTotal).minus(nextTargetTotal).toNumber();
|
|
1279
1492
|
const nextUniqueId = `${flatItem._id}_split_${continueSplitIndex}`;
|
|
1280
|
-
const nextBundleDiscType = nextBundleDiscount.tag ===
|
|
1493
|
+
const nextBundleDiscType = nextBundleDiscount.tag === 'product_discount_card' ? 'discount_card' : nextBundleDiscount.tag;
|
|
1281
1494
|
const nextDiscountDetail = {
|
|
1282
1495
|
amount: nextDiscountAmount * remainingNum,
|
|
1283
1496
|
type: nextBundleDiscType,
|
|
1284
1497
|
discount: {
|
|
1285
|
-
discount_card_type:
|
|
1498
|
+
discount_card_type: nextBundleDiscount?.metadata?.discount_card_type,
|
|
1286
1499
|
fixed_amount: nextDiscountAmount,
|
|
1287
1500
|
resource_id: nextBundleDiscount.id,
|
|
1288
1501
|
title: nextBundleDiscount.format_title,
|
|
@@ -1295,8 +1508,8 @@ var RulesModule = class extends import_BaseModule.BaseModule {
|
|
|
1295
1508
|
custom_product_bundle_map_id: nextUniqueId,
|
|
1296
1509
|
num: remainingNum
|
|
1297
1510
|
},
|
|
1298
|
-
config: nextBundleDiscount
|
|
1299
|
-
_num: remainingNum * (
|
|
1511
|
+
config: nextBundleDiscount?.config,
|
|
1512
|
+
_num: remainingNum * (flatItem?.parentProduct?.num || 1)
|
|
1300
1513
|
};
|
|
1301
1514
|
const nextAppliedProducts = appliedDiscountProducts.get(nextBundleDiscount.id) || [];
|
|
1302
1515
|
nextAppliedProducts.push(nextDiscountDetail);
|
|
@@ -1307,25 +1520,22 @@ var RulesModule = class extends import_BaseModule.BaseModule {
|
|
|
1307
1520
|
num: remainingNum,
|
|
1308
1521
|
quantity: remainingNum,
|
|
1309
1522
|
total: nextTargetTotal,
|
|
1310
|
-
price: new
|
|
1523
|
+
price: new _decimal.default(nextOriginTotal || 0).minus(nextDiscountDetail.discount.fixed_amount).toNumber(),
|
|
1311
1524
|
discount_list: [nextDiscountDetail],
|
|
1312
1525
|
processed: true
|
|
1313
1526
|
});
|
|
1314
1527
|
remainingNum = 0;
|
|
1315
1528
|
break;
|
|
1316
1529
|
}
|
|
1530
|
+
|
|
1531
|
+
// 🔥 需要拆分:计算最大使用次数
|
|
1317
1532
|
let nextMaxUsageLimit;
|
|
1318
1533
|
if (nextIsGoodPass) {
|
|
1319
|
-
const nextGoodPassCount = nextApplicableDiscounts.filter(
|
|
1320
|
-
(item) => (item.tag || item.type) === "good_pass"
|
|
1321
|
-
).length;
|
|
1534
|
+
const nextGoodPassCount = nextApplicableDiscounts.filter(item => (item.tag || item.type) === 'good_pass').length;
|
|
1322
1535
|
nextMaxUsageLimit = nextGoodPassCount;
|
|
1323
|
-
if (
|
|
1536
|
+
if (nextSelectedDiscount.config?.maxUsagePerOrder) {
|
|
1324
1537
|
const usedCount = usedProductIdCounts.get(nextSelectedDiscount.product_id) || 0;
|
|
1325
|
-
nextMaxUsageLimit = Math.min(
|
|
1326
|
-
nextGoodPassCount,
|
|
1327
|
-
Math.max(0, nextSelectedDiscount.config.maxUsagePerOrder - usedCount)
|
|
1328
|
-
);
|
|
1538
|
+
nextMaxUsageLimit = Math.min(nextGoodPassCount, Math.max(0, nextSelectedDiscount.config.maxUsagePerOrder - usedCount));
|
|
1329
1539
|
}
|
|
1330
1540
|
} else if (nextIsDiscountCard && nextApplicableProductLimit > 0) {
|
|
1331
1541
|
const usedLimitCount = usedDiscountCardLimitCounts.get(nextSelectedDiscount.id) || 0;
|
|
@@ -1335,6 +1545,7 @@ var RulesModule = class extends import_BaseModule.BaseModule {
|
|
|
1335
1545
|
}
|
|
1336
1546
|
const nextSplitCount = Math.min(remainingNum, nextMaxUsageLimit);
|
|
1337
1547
|
if (nextSplitCount === 0) {
|
|
1548
|
+
// 已达使用上限,剩余商品无折扣存储
|
|
1338
1549
|
processedItems.push({
|
|
1339
1550
|
...flatItem,
|
|
1340
1551
|
_id: `${flatItem._id}_split_rest`,
|
|
@@ -1345,31 +1556,29 @@ var RulesModule = class extends import_BaseModule.BaseModule {
|
|
|
1345
1556
|
});
|
|
1346
1557
|
break;
|
|
1347
1558
|
}
|
|
1559
|
+
|
|
1560
|
+
// 生成有折扣的商品
|
|
1348
1561
|
for (let j = 0; j < nextSplitCount; j++) {
|
|
1349
1562
|
const nextBundleDiscount = nextIsGoodPass ? nextSelectedDiscountCard || nextApplicableDiscounts[j] : nextSelectedDiscountCard || nextApplicableDiscounts[0];
|
|
1350
1563
|
usedDiscounts.set(nextBundleDiscount.id, true);
|
|
1351
|
-
if ((nextBundleDiscount.tag || nextBundleDiscount.type) ===
|
|
1564
|
+
if ((nextBundleDiscount.tag || nextBundleDiscount.type) === 'good_pass') {
|
|
1352
1565
|
const curCount = usedProductIdCounts.get(nextBundleDiscount.product_id) || 0;
|
|
1353
1566
|
usedProductIdCounts.set(nextBundleDiscount.product_id, curCount + 1);
|
|
1354
1567
|
}
|
|
1355
1568
|
const nextBundleDiscTypeForCount = nextBundleDiscount.tag || nextBundleDiscount.type;
|
|
1356
|
-
if ([
|
|
1569
|
+
if (['discount_card', 'product_discount_card'].includes(nextBundleDiscTypeForCount)) {
|
|
1357
1570
|
const curLimitCount = usedDiscountCardLimitCounts.get(nextBundleDiscount.id) || 0;
|
|
1358
1571
|
usedDiscountCardLimitCounts.set(nextBundleDiscount.id, curLimitCount + 1);
|
|
1359
1572
|
}
|
|
1360
1573
|
const nextUniqueId = `${flatItem._id}_split_${continueSplitIndex}`;
|
|
1361
|
-
const nextDiscountedPrice = (0,
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
product.origin_total
|
|
1365
|
-
);
|
|
1366
|
-
const nextBundleDiscAmount = new import_decimal.default(product.origin_total || 0).minus(nextDiscountedPrice).toNumber();
|
|
1367
|
-
const nextBundleDiscTypeStr = nextBundleDiscount.tag === "product_discount_card" ? "discount_card" : nextBundleDiscount.tag;
|
|
1574
|
+
const nextDiscountedPrice = (0, _utils.getDiscountAmount)(nextBundleDiscount, product.origin_total, product.origin_total);
|
|
1575
|
+
const nextBundleDiscAmount = new _decimal.default(product.origin_total || 0).minus(nextDiscountedPrice).toNumber();
|
|
1576
|
+
const nextBundleDiscTypeStr = nextBundleDiscount.tag === 'product_discount_card' ? 'discount_card' : nextBundleDiscount.tag;
|
|
1368
1577
|
const nextDiscountDetail = {
|
|
1369
1578
|
amount: nextBundleDiscAmount,
|
|
1370
1579
|
type: nextBundleDiscTypeStr,
|
|
1371
1580
|
discount: {
|
|
1372
|
-
discount_card_type:
|
|
1581
|
+
discount_card_type: nextBundleDiscount?.metadata?.discount_card_type,
|
|
1373
1582
|
fixed_amount: nextBundleDiscAmount,
|
|
1374
1583
|
resource_id: nextBundleDiscount.id,
|
|
1375
1584
|
title: nextBundleDiscount.format_title,
|
|
@@ -1383,7 +1592,7 @@ var RulesModule = class extends import_BaseModule.BaseModule {
|
|
|
1383
1592
|
num: 1
|
|
1384
1593
|
},
|
|
1385
1594
|
_num: 1,
|
|
1386
|
-
config: nextBundleDiscount
|
|
1595
|
+
config: nextBundleDiscount?.config
|
|
1387
1596
|
};
|
|
1388
1597
|
const nextAppliedProducts = appliedDiscountProducts.get(nextBundleDiscount.id) || [];
|
|
1389
1598
|
nextAppliedProducts.push(nextDiscountDetail);
|
|
@@ -1404,91 +1613,108 @@ var RulesModule = class extends import_BaseModule.BaseModule {
|
|
|
1404
1613
|
remainingNum -= nextSplitCount;
|
|
1405
1614
|
}
|
|
1406
1615
|
} else {
|
|
1616
|
+
// 不拆分:直接应用折扣
|
|
1407
1617
|
const currentBundleDiscount = selectedDiscountCard || applicableDiscounts[0];
|
|
1408
1618
|
usedDiscounts.set(currentBundleDiscount.id, true);
|
|
1409
|
-
|
|
1619
|
+
// 🔥 更新 product_id 使用计数
|
|
1620
|
+
if ((currentBundleDiscount.tag || currentBundleDiscount.type) === 'good_pass') {
|
|
1410
1621
|
const currentCount = usedProductIdCounts.get(currentBundleDiscount.product_id) || 0;
|
|
1411
1622
|
usedProductIdCounts.set(currentBundleDiscount.product_id, currentCount + 1);
|
|
1412
1623
|
}
|
|
1624
|
+
// 🔥 更新折扣卡 applicableProductLimit 使用计数(不拆分时按商品数量计数)
|
|
1413
1625
|
const noSplitDiscountType = currentBundleDiscount.tag || currentBundleDiscount.type;
|
|
1414
|
-
if ([
|
|
1626
|
+
if (['discount_card', 'product_discount_card'].includes(noSplitDiscountType)) {
|
|
1415
1627
|
const bundleQuantity = product.num || 1;
|
|
1416
1628
|
const currentNoSplitCount = usedDiscountCardLimitCounts.get(currentBundleDiscount.id) || 0;
|
|
1417
1629
|
usedDiscountCardLimitCounts.set(currentBundleDiscount.id, currentNoSplitCount + bundleQuantity);
|
|
1418
1630
|
}
|
|
1419
1631
|
const productOriginTotal = product.original_price || product.price || 0;
|
|
1420
|
-
|
|
1632
|
+
|
|
1633
|
+
// 🔥 检查是否是 order_level 固定金额折扣卡
|
|
1634
|
+
const isOrderLevel = (0, _utils.isOrderLevelFixedAmountDiscount)(selectedDiscount);
|
|
1421
1635
|
const orderLevelAllocation = isOrderLevel ? orderLevelDiscountAllocations.get(selectedDiscount.id) : null;
|
|
1422
|
-
const productAllocation = orderLevelAllocation
|
|
1636
|
+
const productAllocation = orderLevelAllocation?.get(flatItem._id);
|
|
1423
1637
|
let targetProductTotal;
|
|
1424
1638
|
let fixedAmountPerItem;
|
|
1425
1639
|
let productDiscountDifference;
|
|
1426
1640
|
if (isOrderLevel && productAllocation) {
|
|
1641
|
+
// order_level:使用预计算的单价折扣金额(已经是单价,无需再除以数量)
|
|
1427
1642
|
fixedAmountPerItem = productAllocation.discountAmount;
|
|
1428
1643
|
productDiscountDifference = productAllocation.difference;
|
|
1429
|
-
targetProductTotal = Math.max(new
|
|
1644
|
+
targetProductTotal = Math.max(new _decimal.default(productOriginTotal).minus(fixedAmountPerItem).toNumber(), 0);
|
|
1430
1645
|
} else {
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
productOriginTotal
|
|
1435
|
-
);
|
|
1436
|
-
fixedAmountPerItem = new import_decimal.default(productOriginTotal).minus(targetProductTotal).toNumber();
|
|
1646
|
+
// item_level 或其他类型:使用原有逻辑
|
|
1647
|
+
targetProductTotal = (0, _utils.getDiscountAmount)(currentBundleDiscount, productOriginTotal, productOriginTotal);
|
|
1648
|
+
fixedAmountPerItem = new _decimal.default(productOriginTotal).minus(targetProductTotal).toNumber();
|
|
1437
1649
|
}
|
|
1438
|
-
|
|
1650
|
+
|
|
1651
|
+
// 计算实际折扣金额
|
|
1652
|
+
const bundleActualDiscountAmount = new _decimal.default(productOriginTotal).minus(targetProductTotal).toNumber();
|
|
1653
|
+
|
|
1654
|
+
// 🔥 使用当前的 _id 作为唯一标识
|
|
1439
1655
|
const uniqueId = flatItem._id;
|
|
1656
|
+
debugger;
|
|
1440
1657
|
const discountDetail = {
|
|
1441
1658
|
amount: bundleActualDiscountAmount * (product.num || 1),
|
|
1442
|
-
type: currentBundleDiscount.tag ===
|
|
1659
|
+
type: currentBundleDiscount.tag === 'product_discount_card' ? 'discount_card' : currentBundleDiscount.tag,
|
|
1443
1660
|
discount: {
|
|
1444
|
-
discount_card_type:
|
|
1661
|
+
discount_card_type: currentBundleDiscount?.metadata?.discount_card_type,
|
|
1445
1662
|
fixed_amount: bundleActualDiscountAmount,
|
|
1446
|
-
discount_calculation_mode:
|
|
1663
|
+
discount_calculation_mode: currentBundleDiscount?.metadata?.discount_calculation_mode,
|
|
1447
1664
|
resource_id: currentBundleDiscount.id,
|
|
1448
1665
|
title: currentBundleDiscount.format_title,
|
|
1449
1666
|
original_amount: product.original_price,
|
|
1450
|
-
product_id: product.id,
|
|
1451
|
-
percent:
|
|
1452
|
-
discount_product_id:
|
|
1667
|
+
product_id: product.id || product.product_id,
|
|
1668
|
+
percent: selectedDiscount.par_value,
|
|
1669
|
+
discount_product_id: selectedDiscount.product_id
|
|
1453
1670
|
},
|
|
1454
1671
|
metadata: {
|
|
1455
1672
|
// 🔥 使用唯一的 _id
|
|
1456
1673
|
custom_product_bundle_map_id: uniqueId,
|
|
1457
|
-
discount_rule_uncheck_flag: selectedDiscount
|
|
1674
|
+
discount_rule_uncheck_flag: selectedDiscount?.discount_rule_uncheck_flag,
|
|
1458
1675
|
num: product.num || 1,
|
|
1459
1676
|
// 🔥 order_level 分摊差值
|
|
1460
|
-
...productDiscountDifference !==
|
|
1677
|
+
...(productDiscountDifference !== undefined && {
|
|
1678
|
+
product_discount_difference: productDiscountDifference
|
|
1679
|
+
})
|
|
1461
1680
|
},
|
|
1462
|
-
config: currentBundleDiscount
|
|
1463
|
-
_num: (product.num || 1) * (
|
|
1681
|
+
config: currentBundleDiscount?.config,
|
|
1682
|
+
_num: (product.num || 1) * (flatItem?.parentProduct?.num || 1)
|
|
1464
1683
|
};
|
|
1684
|
+
|
|
1685
|
+
// 记录实际应用的折扣
|
|
1465
1686
|
const appliedProducts = appliedDiscountProducts.get(currentBundleDiscount.id) || [];
|
|
1466
1687
|
appliedProducts.push(discountDetail);
|
|
1467
1688
|
appliedDiscountProducts.set(currentBundleDiscount.id, appliedProducts);
|
|
1468
1689
|
processedItems.push({
|
|
1469
1690
|
...flatItem,
|
|
1470
1691
|
total: targetProductTotal,
|
|
1471
|
-
price: new
|
|
1472
|
-
discount_list: this.filterDiscountListByType(flatItem.discount_list,
|
|
1692
|
+
price: new _decimal.default(productOriginTotal || 0).minus(fixedAmountPerItem).toNumber(),
|
|
1693
|
+
discount_list: this.filterDiscountListByType(flatItem.discount_list, 'promotion').concat([discountDetail]),
|
|
1473
1694
|
processed: true
|
|
1474
1695
|
});
|
|
1475
1696
|
}
|
|
1476
1697
|
processedFlatItemsMap.set(flatItem._id, processedItems);
|
|
1477
1698
|
}
|
|
1478
1699
|
});
|
|
1479
|
-
|
|
1700
|
+
|
|
1701
|
+
// 🔥 重组:将处理后的bundle子商品重新组装回主商品
|
|
1702
|
+
const reconstructProductsWithBundle = (processedProductsMap, processedFlatItemsMap, originalProductList) => {
|
|
1480
1703
|
const result = [];
|
|
1481
|
-
originalProductList.forEach(
|
|
1704
|
+
originalProductList.forEach(originProduct => {
|
|
1482
1705
|
const product = this.hooks.getProduct(originProduct);
|
|
1483
|
-
|
|
1706
|
+
|
|
1707
|
+
// 获取主商品处理结果
|
|
1708
|
+
const mainProductArr = processedProductsMap.get(product._id);
|
|
1484
1709
|
if (!mainProductArr || mainProductArr.length === 0) {
|
|
1710
|
+
// 如果没有处理结果,返回默认商品(还原 option 价格)
|
|
1485
1711
|
const getDefaultProduct = () => {
|
|
1486
1712
|
const restoredOptions = restoreOptionPrices(product.options);
|
|
1487
1713
|
if (product.isClient) {
|
|
1488
1714
|
return this.hooks.setProduct(originProduct, {
|
|
1489
|
-
discount_list: this.filterDiscountListByType(product.discount_list,
|
|
1715
|
+
discount_list: this.filterDiscountListByType(product.discount_list, 'promotion'),
|
|
1490
1716
|
price: product.price,
|
|
1491
|
-
origin_total: (0,
|
|
1717
|
+
origin_total: (0, _utils2.getProductOriginTotalPrice)({
|
|
1492
1718
|
product: {
|
|
1493
1719
|
original_price: product.original_price
|
|
1494
1720
|
},
|
|
@@ -1497,7 +1723,7 @@ var RulesModule = class extends import_BaseModule.BaseModule {
|
|
|
1497
1723
|
}),
|
|
1498
1724
|
variant: originProduct._productInit.variant,
|
|
1499
1725
|
original_price: originProduct._productInit.original_price,
|
|
1500
|
-
total: (0,
|
|
1726
|
+
total: (0, _utils2.getProductTotalPrice)({
|
|
1501
1727
|
product: {
|
|
1502
1728
|
price: product.price
|
|
1503
1729
|
},
|
|
@@ -1508,7 +1734,7 @@ var RulesModule = class extends import_BaseModule.BaseModule {
|
|
|
1508
1734
|
});
|
|
1509
1735
|
} else {
|
|
1510
1736
|
return this.hooks.setProduct(originProduct, {
|
|
1511
|
-
discount_list: this.filterDiscountListByType(product.discount_list,
|
|
1737
|
+
discount_list: this.filterDiscountListByType(product.discount_list, 'promotion'),
|
|
1512
1738
|
total: product.total,
|
|
1513
1739
|
origin_total: product.origin_total,
|
|
1514
1740
|
price: product.price,
|
|
@@ -1519,332 +1745,359 @@ var RulesModule = class extends import_BaseModule.BaseModule {
|
|
|
1519
1745
|
result.push(getDefaultProduct());
|
|
1520
1746
|
return;
|
|
1521
1747
|
}
|
|
1748
|
+
|
|
1749
|
+
// 检查是否有bundle子商品需要重组
|
|
1522
1750
|
const hasBundle = product.bundle && Array.isArray(product.bundle) && product.bundle.length > 0;
|
|
1523
1751
|
if (!hasBundle) {
|
|
1752
|
+
// 没有bundle,直接使用主商品处理结果
|
|
1524
1753
|
result.push(...mainProductArr);
|
|
1525
1754
|
} else {
|
|
1526
|
-
|
|
1527
|
-
|
|
1755
|
+
// 🔥 有bundle,需要检查子商品是否应用了商品券
|
|
1756
|
+
// 1. 先收集所有bundle子商品的处理结果
|
|
1757
|
+
const bundleProcessingInfo = new Map();
|
|
1758
|
+
let hasGoodPassApplied = false; // 标记是否有子商品应用了商品券
|
|
1759
|
+
|
|
1528
1760
|
if (product.bundle && Array.isArray(product.bundle)) {
|
|
1529
1761
|
product.bundle.forEach((bundleItem, bundleIndex) => {
|
|
1530
1762
|
const bundleItemId = `${product._id}_bundle_${bundleIndex}`;
|
|
1531
|
-
const processedBundleItems =
|
|
1763
|
+
const processedBundleItems = processedFlatItemsMap.get(bundleItemId);
|
|
1532
1764
|
if (!processedBundleItems || processedBundleItems.length === 0) {
|
|
1765
|
+
// 未处理的bundle item,保持原样
|
|
1533
1766
|
bundleProcessingInfo.set(bundleIndex, [bundleItem]);
|
|
1534
1767
|
} else {
|
|
1768
|
+
// 处理过的bundle item
|
|
1535
1769
|
bundleProcessingInfo.set(bundleIndex, processedBundleItems);
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
var _a;
|
|
1539
|
-
return (_a = item.discount_list) == null ? void 0 : _a.some(
|
|
1540
|
-
(discount) => discount.type === "good_pass" || discount.tag === "good_pass"
|
|
1541
|
-
);
|
|
1542
|
-
}
|
|
1543
|
-
);
|
|
1770
|
+
// 🔥 检查是否应用了商品券(good_pass)
|
|
1771
|
+
const hasGoodPass = processedBundleItems.some(item => item.discount_list?.some(discount => discount.type === 'good_pass' || discount.tag === 'good_pass'));
|
|
1544
1772
|
if (hasGoodPass) {
|
|
1545
1773
|
hasGoodPassApplied = true;
|
|
1546
1774
|
}
|
|
1547
1775
|
}
|
|
1548
1776
|
});
|
|
1549
1777
|
}
|
|
1778
|
+
|
|
1779
|
+
// 🔥 2. 如果有子商品应用了商品券,且主商品数量大于1,需要拆分主商品
|
|
1550
1780
|
const mainProductQuantity = mainProductArr[0] ? this.hooks.getProduct(mainProductArr[0]).quantity : 1;
|
|
1551
1781
|
if (hasGoodPassApplied && mainProductArr.length === 1 && mainProductQuantity > 1) {
|
|
1552
1782
|
const mainProduct = mainProductArr[0];
|
|
1553
1783
|
const mainProductData = this.hooks.getProduct(mainProduct);
|
|
1784
|
+
|
|
1785
|
+
// 🔥 方案:拆分成2个主商品
|
|
1786
|
+
// 1. 一个主商品(qty=1)包含拆分后的bundle
|
|
1787
|
+
// 2. 一个主商品(qty=原quantity-1)包含原始未拆分的bundle
|
|
1788
|
+
|
|
1789
|
+
// 第一个:包含拆分后的bundle (qty=1)
|
|
1554
1790
|
const newBundleWithDiscount = [];
|
|
1555
|
-
(product.bundle || []).forEach(
|
|
1556
|
-
(
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
...discount,
|
|
1566
|
-
metadata: {
|
|
1567
|
-
...discount.metadata,
|
|
1568
|
-
num: item.num,
|
|
1569
|
-
custom_product_bundle_map_id: (_a = item.metadata) == null ? void 0 : _a.custom_product_bundle_map_id
|
|
1570
|
-
}
|
|
1571
|
-
// num: item.num, // 使用拆分后的 num
|
|
1572
|
-
};
|
|
1573
|
-
});
|
|
1574
|
-
newBundleWithDiscount.push({
|
|
1575
|
-
...bundleItem,
|
|
1576
|
-
_id: item._id,
|
|
1577
|
-
product_id: bundleItem.product_id,
|
|
1578
|
-
price: item.price,
|
|
1791
|
+
(product.bundle || []).forEach((bundleItem, bundleIndex) => {
|
|
1792
|
+
const processedItems = bundleProcessingInfo.get(bundleIndex) || [bundleItem];
|
|
1793
|
+
if (processedItems.length > 1) {
|
|
1794
|
+
// 被拆分的子商品,添加所有拆分项
|
|
1795
|
+
processedItems.forEach(item => {
|
|
1796
|
+
// 🔥 更新 discount_list 中的 num,使其与拆分后的 item.num 一致
|
|
1797
|
+
const updatedDiscountList = (item.discount_list || []).map(discount => ({
|
|
1798
|
+
...discount,
|
|
1799
|
+
metadata: {
|
|
1800
|
+
...discount.metadata,
|
|
1579
1801
|
num: item.num,
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1802
|
+
custom_product_bundle_map_id: item.metadata?.custom_product_bundle_map_id
|
|
1803
|
+
}
|
|
1804
|
+
// num: item.num, // 使用拆分后的 num
|
|
1805
|
+
}));
|
|
1806
|
+
newBundleWithDiscount.push({
|
|
1807
|
+
...bundleItem,
|
|
1808
|
+
_id: item._id,
|
|
1809
|
+
product_id: bundleItem.product_id,
|
|
1810
|
+
price: item.price,
|
|
1811
|
+
num: item.num,
|
|
1812
|
+
quantity: item.num,
|
|
1813
|
+
discount_list: updatedDiscountList
|
|
1583
1814
|
});
|
|
1584
|
-
}
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
custom_product_bundle_map_id: (_a = item.metadata) == null ? void 0 : _a.custom_product_bundle_map_id
|
|
1595
|
-
}
|
|
1596
|
-
// num: item.num, // 使用当前的 num
|
|
1597
|
-
};
|
|
1598
|
-
});
|
|
1599
|
-
newBundleWithDiscount.push({
|
|
1600
|
-
...bundleItem,
|
|
1601
|
-
_id: item._id,
|
|
1602
|
-
product_id: bundleItem.product_id,
|
|
1603
|
-
price: item.price,
|
|
1815
|
+
});
|
|
1816
|
+
} else {
|
|
1817
|
+
// 未拆分的子商品,保持原样
|
|
1818
|
+
const item = processedItems[0];
|
|
1819
|
+
if (item.processed) {
|
|
1820
|
+
// 🔥 同样需要更新 discount_list 中的 num
|
|
1821
|
+
const updatedDiscountList = (item.discount_list || []).map(discount => ({
|
|
1822
|
+
...discount,
|
|
1823
|
+
metadata: {
|
|
1824
|
+
...discount.metadata,
|
|
1604
1825
|
num: item.num,
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
}
|
|
1609
|
-
|
|
1610
|
-
|
|
1826
|
+
custom_product_bundle_map_id: item.metadata?.custom_product_bundle_map_id
|
|
1827
|
+
}
|
|
1828
|
+
// num: item.num, // 使用当前的 num
|
|
1829
|
+
}));
|
|
1830
|
+
newBundleWithDiscount.push({
|
|
1831
|
+
...bundleItem,
|
|
1832
|
+
_id: item._id,
|
|
1833
|
+
product_id: bundleItem.product_id,
|
|
1834
|
+
price: item.price,
|
|
1835
|
+
num: item.num,
|
|
1836
|
+
quantity: item.num,
|
|
1837
|
+
discount_list: updatedDiscountList
|
|
1838
|
+
});
|
|
1839
|
+
} else {
|
|
1840
|
+
newBundleWithDiscount.push(item);
|
|
1611
1841
|
}
|
|
1612
1842
|
}
|
|
1613
|
-
);
|
|
1843
|
+
});
|
|
1844
|
+
|
|
1845
|
+
// 计算第一个主商品的总价(包含拆分后的bundle)
|
|
1614
1846
|
let newTotalWithDiscount = Number(mainProductData.price || 0);
|
|
1615
|
-
let newOriginTotalWithDiscount = Number(
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
const updatedMainDiscountList = mainProductData.discount_list.map(
|
|
1619
|
-
|
|
1620
|
-
|
|
1847
|
+
let newOriginTotalWithDiscount = Number(mainProductData.original_price || mainProductData.price || 0);
|
|
1848
|
+
|
|
1849
|
+
// 🔥 更新主商品自己的 discount_list 中的 num(quantity=1)
|
|
1850
|
+
const updatedMainDiscountList = mainProductData.discount_list.map(discount => {
|
|
1851
|
+
if (discount?.metadata?.custom_product_bundle_map_id) {
|
|
1852
|
+
// bundle的discount_list保持不变
|
|
1621
1853
|
return discount;
|
|
1622
1854
|
}
|
|
1855
|
+
// 主商品自己的discount,更新num为1
|
|
1623
1856
|
return {
|
|
1624
1857
|
...discount,
|
|
1625
1858
|
// num: 1,
|
|
1626
1859
|
metadata: {
|
|
1627
1860
|
...discount.metadata,
|
|
1628
|
-
custom_product_bundle_map_id:
|
|
1861
|
+
custom_product_bundle_map_id: discount?.metadata?.custom_product_bundle_map_id,
|
|
1629
1862
|
num: 1
|
|
1630
1863
|
}
|
|
1631
1864
|
};
|
|
1632
1865
|
});
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
return !((_a = item == null ? void 0 : item.metadata) == null ? void 0 : _a.custom_product_bundle_map_id);
|
|
1637
|
-
}
|
|
1638
|
-
);
|
|
1866
|
+
|
|
1867
|
+
// 🔥 使用更新后的列表计算折扣金额
|
|
1868
|
+
const mainDiscountList = updatedMainDiscountList.filter(item => !item?.metadata?.custom_product_bundle_map_id);
|
|
1639
1869
|
if (mainDiscountList && mainDiscountList.length > 0) {
|
|
1640
|
-
const allDiscountAmount = (0,
|
|
1641
|
-
newTotalWithDiscount = new
|
|
1870
|
+
const allDiscountAmount = (0, _utils.getDiscountListAmountTotal)(mainDiscountList);
|
|
1871
|
+
newTotalWithDiscount = new _decimal.default(mainProductData.price || 0).minus(allDiscountAmount).toNumber() ?? newTotalWithDiscount;
|
|
1642
1872
|
newOriginTotalWithDiscount = mainProductData.origin_total ?? newOriginTotalWithDiscount;
|
|
1643
1873
|
}
|
|
1874
|
+
|
|
1875
|
+
// 累加bundle的价格(只累加一次)
|
|
1644
1876
|
if (newBundleWithDiscount.length > 0) {
|
|
1645
|
-
newBundleWithDiscount.forEach(
|
|
1877
|
+
newBundleWithDiscount.forEach(item => {
|
|
1646
1878
|
newTotalWithDiscount += Number(item.price) * Number(item.num);
|
|
1647
1879
|
});
|
|
1648
|
-
newBundleWithDiscount.forEach(
|
|
1649
|
-
|
|
1650
|
-
const originalPrice = ((_c = (_b = (_a = item.discount_list) == null ? void 0 : _a[0]) == null ? void 0 : _b.discount) == null ? void 0 : _c.original_amount) || item.price;
|
|
1880
|
+
newBundleWithDiscount.forEach(item => {
|
|
1881
|
+
const originalPrice = item.discount_list?.[0]?.discount?.original_amount || item.price;
|
|
1651
1882
|
newOriginTotalWithDiscount += Number(originalPrice) * Number(item.num);
|
|
1652
1883
|
});
|
|
1653
1884
|
}
|
|
1654
|
-
|
|
1885
|
+
|
|
1886
|
+
// 累加options的价格(options不参与折扣,在最终设置total时处理)
|
|
1887
|
+
if (product?.options) {
|
|
1655
1888
|
newTotalWithDiscount = product.options.reduce((accumulator, currentValue) => {
|
|
1656
|
-
const currentPrice = new
|
|
1657
|
-
const currentNum = new
|
|
1889
|
+
const currentPrice = new _decimal.default(currentValue.price || 0);
|
|
1890
|
+
const currentNum = new _decimal.default(currentValue.num || 0);
|
|
1658
1891
|
return accumulator.add(currentPrice.mul(currentNum));
|
|
1659
|
-
}, new
|
|
1892
|
+
}, new _decimal.default(newTotalWithDiscount)).toNumber();
|
|
1660
1893
|
}
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
);
|
|
1894
|
+
|
|
1895
|
+
// 添加第一个主商品:qty=1,包含拆分后的bundle
|
|
1896
|
+
result.push(this.hooks.setProduct(mainProduct, {
|
|
1897
|
+
...mainProductData,
|
|
1898
|
+
_id: `${mainProductData._id.split('___')[0]}___split_discount`,
|
|
1899
|
+
quantity: 1,
|
|
1900
|
+
discount_list: updatedMainDiscountList,
|
|
1901
|
+
bundle: newBundleWithDiscount,
|
|
1902
|
+
total: new _decimal.default(newTotalWithDiscount).toDecimalPlaces(2, _decimal.default.ROUND_HALF_UP).toNumber(),
|
|
1903
|
+
origin_total: new _decimal.default(newOriginTotalWithDiscount).toDecimalPlaces(2, _decimal.default.ROUND_HALF_UP).toNumber()
|
|
1904
|
+
}));
|
|
1905
|
+
|
|
1906
|
+
// 第二个:包含原始bundle (qty=原quantity-1)
|
|
1672
1907
|
if (mainProductQuantity > 1) {
|
|
1673
1908
|
const newBundleOriginal = [];
|
|
1674
|
-
(product.bundle || []).forEach(
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1909
|
+
(product.bundle || []).forEach((bundleItem, bundleIndex) => {
|
|
1910
|
+
// 使用原始的bundle配置,不包含拆分
|
|
1911
|
+
newBundleOriginal.push({
|
|
1912
|
+
...bundleItem,
|
|
1913
|
+
discount_list: []
|
|
1914
|
+
});
|
|
1915
|
+
});
|
|
1916
|
+
|
|
1917
|
+
// 计算第二个主商品的总价
|
|
1682
1918
|
let newTotalOriginal = Number(mainProductData.price || 0);
|
|
1683
|
-
let newOriginTotalOriginal = Number(
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
const updatedMainDiscountListOriginal = mainProductData.discount_list.map(
|
|
1687
|
-
|
|
1688
|
-
|
|
1919
|
+
let newOriginTotalOriginal = Number(mainProductData.original_price || mainProductData.price || 0);
|
|
1920
|
+
|
|
1921
|
+
// 🔥 更新主商品自己的 discount_list 中的 num(quantity=原quantity-1)
|
|
1922
|
+
const updatedMainDiscountListOriginal = mainProductData.discount_list.map(discount => {
|
|
1923
|
+
if (discount?.metadata?.custom_product_bundle_map_id) {
|
|
1924
|
+
// bundle的discount_list保持不变
|
|
1689
1925
|
return discount;
|
|
1690
1926
|
}
|
|
1927
|
+
// 主商品自己的discount,更新num为 mainProductQuantity - 1
|
|
1691
1928
|
return {
|
|
1692
1929
|
...discount,
|
|
1693
1930
|
metadata: {
|
|
1694
1931
|
...discount.metadata,
|
|
1695
1932
|
num: mainProductQuantity - 1,
|
|
1696
|
-
custom_product_bundle_map_id:
|
|
1933
|
+
custom_product_bundle_map_id: discount?.metadata?.custom_product_bundle_map_id
|
|
1697
1934
|
}
|
|
1698
1935
|
// num: mainProductQuantity - 1,
|
|
1699
1936
|
};
|
|
1700
1937
|
});
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
return !((_a = item == null ? void 0 : item.metadata) == null ? void 0 : _a.custom_product_bundle_map_id);
|
|
1705
|
-
}
|
|
1706
|
-
);
|
|
1938
|
+
|
|
1939
|
+
// 🔥 使用更新后的列表计算折扣金额
|
|
1940
|
+
const mainDiscountListOriginal = updatedMainDiscountListOriginal.filter(item => !item?.metadata?.custom_product_bundle_map_id);
|
|
1707
1941
|
if (mainDiscountListOriginal && mainDiscountListOriginal.length > 0) {
|
|
1708
|
-
const allDiscountAmount = (0,
|
|
1709
|
-
|
|
1710
|
-
);
|
|
1711
|
-
newTotalOriginal = new import_decimal.default(mainProductData.price || 0).minus(allDiscountAmount).toNumber() ?? newTotalOriginal;
|
|
1942
|
+
const allDiscountAmount = (0, _utils.getDiscountListAmount)(mainDiscountListOriginal);
|
|
1943
|
+
newTotalOriginal = new _decimal.default(mainProductData.price || 0).minus(allDiscountAmount).toNumber() ?? newTotalOriginal;
|
|
1712
1944
|
newOriginTotalOriginal = mainProductData.origin_total ?? newOriginTotalOriginal;
|
|
1713
1945
|
}
|
|
1946
|
+
|
|
1947
|
+
// 累加bundle的价格(只累加一次)
|
|
1714
1948
|
if (newBundleOriginal.length > 0) {
|
|
1715
|
-
newBundleOriginal.forEach(
|
|
1949
|
+
newBundleOriginal.forEach(item => {
|
|
1716
1950
|
newTotalOriginal += Number(item.price) * Number(item.num);
|
|
1717
1951
|
newOriginTotalOriginal += Number(item.price) * Number(item.num);
|
|
1718
1952
|
});
|
|
1719
1953
|
}
|
|
1720
|
-
|
|
1954
|
+
|
|
1955
|
+
// 累加options的价格(options不参与折扣,在最终设置total时处理)
|
|
1956
|
+
if (product?.options) {
|
|
1721
1957
|
newTotalOriginal = product.options.reduce((accumulator, currentValue) => {
|
|
1722
|
-
const currentPrice = new
|
|
1723
|
-
const currentNum = new
|
|
1958
|
+
const currentPrice = new _decimal.default(currentValue.price || 0);
|
|
1959
|
+
const currentNum = new _decimal.default(currentValue.num || 0);
|
|
1724
1960
|
return accumulator.add(currentPrice.mul(currentNum));
|
|
1725
|
-
}, new
|
|
1961
|
+
}, new _decimal.default(newTotalOriginal)).toNumber();
|
|
1726
1962
|
}
|
|
1727
|
-
newTotalOriginal = new
|
|
1728
|
-
newOriginTotalOriginal = new
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
);
|
|
1963
|
+
newTotalOriginal = new _decimal.default(newTotalOriginal).toDecimalPlaces(2, _decimal.default.ROUND_HALF_UP).toNumber();
|
|
1964
|
+
newOriginTotalOriginal = new _decimal.default(newOriginTotalOriginal).toDecimalPlaces(2, _decimal.default.ROUND_HALF_UP).toNumber();
|
|
1965
|
+
|
|
1966
|
+
// 添加第二个主商品:qty=原quantity-1,包含原始bundle
|
|
1967
|
+
result.push(this.hooks.setProduct(mainProduct, {
|
|
1968
|
+
...mainProductData,
|
|
1969
|
+
_id: `${mainProductData._id.split('___')[0]}___split_normal`,
|
|
1970
|
+
quantity: mainProductQuantity - 1,
|
|
1971
|
+
discount_list: updatedMainDiscountListOriginal,
|
|
1972
|
+
bundle: newBundleOriginal,
|
|
1973
|
+
total: newTotalOriginal,
|
|
1974
|
+
origin_total: newOriginTotalOriginal
|
|
1975
|
+
}));
|
|
1740
1976
|
}
|
|
1741
1977
|
} else {
|
|
1742
|
-
|
|
1743
|
-
|
|
1978
|
+
// 🔥 没有子商品被拆分,使用原有逻辑
|
|
1979
|
+
mainProductArr.forEach(mainProduct => {
|
|
1744
1980
|
const mainProductData = this.hooks.getProduct(mainProduct);
|
|
1981
|
+
|
|
1982
|
+
// 重组bundle
|
|
1745
1983
|
const newBundle = [];
|
|
1746
1984
|
if (product.bundle && Array.isArray(product.bundle)) {
|
|
1747
1985
|
product.bundle.forEach((bundleItem, bundleIndex) => {
|
|
1748
1986
|
const bundleItemId = `${product._id}_bundle_${bundleIndex}`;
|
|
1749
|
-
const processedBundleItems =
|
|
1987
|
+
const processedBundleItems = processedFlatItemsMap.get(bundleItemId);
|
|
1750
1988
|
if (!processedBundleItems || processedBundleItems.length === 0) {
|
|
1989
|
+
// 未处理的bundle item,保持原样
|
|
1751
1990
|
newBundle.push(bundleItem);
|
|
1752
1991
|
} else {
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
}
|
|
1765
|
-
|
|
1992
|
+
// 🔥 关键:拆分后的bundle item
|
|
1993
|
+
processedBundleItems.forEach(item => {
|
|
1994
|
+
const nextBundlePrice = item.price ?? bundleItem.price;
|
|
1995
|
+
const nextBundleSellingPrice = item.bundle_selling_price ?? item.price ?? bundleItem.bundle_selling_price;
|
|
1996
|
+
// 🔥 更新 discount_list 中的 num,使其与拆分后的 item.num 一致
|
|
1997
|
+
const updatedDiscountList = (item.discount_list || []).map(discount => ({
|
|
1998
|
+
...discount,
|
|
1999
|
+
metadata: {
|
|
2000
|
+
...discount.metadata,
|
|
2001
|
+
num: item.num,
|
|
2002
|
+
custom_product_bundle_map_id: discount?.metadata?.custom_product_bundle_map_id
|
|
2003
|
+
}
|
|
2004
|
+
// num: item.num, // 使用拆分后的 num
|
|
2005
|
+
}));
|
|
1766
2006
|
newBundle.push({
|
|
1767
2007
|
...bundleItem,
|
|
1768
2008
|
_id: item._id,
|
|
1769
2009
|
product_id: bundleItem.product_id,
|
|
1770
|
-
price:
|
|
2010
|
+
price: nextBundlePrice,
|
|
1771
2011
|
num: item.num,
|
|
1772
|
-
discount_list:
|
|
1773
|
-
bundle_selling_price: bundleItem.bundle_selling_price !==
|
|
2012
|
+
discount_list: updatedDiscountList,
|
|
2013
|
+
bundle_selling_price: bundleItem.bundle_selling_price !== undefined ? nextBundleSellingPrice : undefined
|
|
1774
2014
|
});
|
|
1775
2015
|
});
|
|
1776
2016
|
}
|
|
1777
2017
|
});
|
|
1778
2018
|
}
|
|
1779
|
-
|
|
2019
|
+
|
|
2020
|
+
// 主商品已经在前面的 setProduct 中应用过折扣;这里只替换 bundle,
|
|
2021
|
+
// 避免把主商品 discount_list 再扣一次。
|
|
2022
|
+
const mainSellingPrice = mainProductData.main_product_selling_price ?? mainProductData.price ?? 0;
|
|
2023
|
+
let newTotal = Number(mainSellingPrice || 0);
|
|
1780
2024
|
let newOriginTotal = Number(mainProductData.original_price || mainProductData.price || 0);
|
|
1781
|
-
|
|
2025
|
+
|
|
2026
|
+
// 判断是否是手动折扣
|
|
2027
|
+
const isManualDiscount = typeof mainProductData.isManualDiscount === 'boolean' ? mainProductData.isManualDiscount : mainProductData.total != mainProductData.origin_total && (!mainProductData.discount_list?.length || mainProductData?.discount_list?.every?.(item => item.type === 'product'));
|
|
2028
|
+
|
|
2029
|
+
// 如果是手动折扣,使用原有的total和origin_total,不重新计算
|
|
1782
2030
|
if (isManualDiscount) {
|
|
1783
2031
|
newTotal = mainProductData.total ?? newTotal;
|
|
1784
2032
|
newOriginTotal = mainProductData.origin_total ?? newOriginTotal;
|
|
1785
2033
|
} else {
|
|
1786
|
-
|
|
1787
|
-
(item) => {
|
|
1788
|
-
var _a2;
|
|
1789
|
-
return !((_a2 = item == null ? void 0 : item.metadata) == null ? void 0 : _a2.custom_product_bundle_map_id);
|
|
1790
|
-
}
|
|
1791
|
-
);
|
|
1792
|
-
if (mainDiscountList && mainDiscountList.length > 0) {
|
|
1793
|
-
const allDiscountAmount = (0, import_utils.getDiscountListAmount)(mainDiscountList);
|
|
1794
|
-
newTotal = new import_decimal.default(mainProductData.price || 0).minus(allDiscountAmount).toNumber() ?? newTotal;
|
|
1795
|
-
newOriginTotal = mainProductData.origin_total ?? newOriginTotal;
|
|
1796
|
-
}
|
|
2034
|
+
// 累加bundle的折后价格(只累加一次)
|
|
1797
2035
|
if (newBundle.length > 0) {
|
|
1798
|
-
newBundle.forEach(
|
|
1799
|
-
var _a2, _b2, _c2;
|
|
2036
|
+
newBundle.forEach(item => {
|
|
1800
2037
|
newTotal += Number(item.price) * Number(item.num);
|
|
1801
|
-
const originalPrice =
|
|
2038
|
+
const originalPrice = item.discount_list?.[0]?.discount?.original_amount || item.price;
|
|
2039
|
+
newOriginTotal += Number(originalPrice) * Number(item.num);
|
|
2040
|
+
});
|
|
2041
|
+
// 计算原始总价(不考虑折扣)
|
|
2042
|
+
newBundle.forEach(item => {
|
|
2043
|
+
const originalPrice = item.discount_list?.[0]?.discount?.original_amount || item.price;
|
|
1802
2044
|
newOriginTotal += Number(originalPrice) * Number(item.num);
|
|
1803
2045
|
});
|
|
1804
2046
|
}
|
|
1805
2047
|
}
|
|
1806
|
-
|
|
2048
|
+
|
|
2049
|
+
// 累加options的价格(options不参与折扣,在最终设置total时处理)
|
|
2050
|
+
if (product?.options) {
|
|
1807
2051
|
newTotal = product.options.reduce((accumulator, currentValue) => {
|
|
1808
|
-
const currentPrice = new
|
|
1809
|
-
const currentNum = new
|
|
2052
|
+
const currentPrice = new _decimal.default(currentValue.price || 0);
|
|
2053
|
+
const currentNum = new _decimal.default(currentValue.num || 0);
|
|
1810
2054
|
return accumulator.add(currentPrice.mul(currentNum));
|
|
1811
|
-
}, new
|
|
2055
|
+
}, new _decimal.default(newTotal)).toNumber();
|
|
1812
2056
|
}
|
|
1813
|
-
newTotal = new
|
|
1814
|
-
newOriginTotal = new
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
);
|
|
2057
|
+
newTotal = new _decimal.default(newTotal).toDecimalPlaces(2, _decimal.default.ROUND_HALF_UP).toNumber();
|
|
2058
|
+
newOriginTotal = new _decimal.default(newOriginTotal).toDecimalPlaces(2, _decimal.default.ROUND_HALF_UP).toNumber();
|
|
2059
|
+
|
|
2060
|
+
// 生成最终的主商品
|
|
2061
|
+
result.push(this.hooks.setProduct(mainProduct, {
|
|
2062
|
+
...mainProductData,
|
|
2063
|
+
bundle: newBundle,
|
|
2064
|
+
total: newTotal,
|
|
2065
|
+
origin_total: newOriginTotal
|
|
2066
|
+
}));
|
|
1823
2067
|
});
|
|
1824
2068
|
}
|
|
1825
2069
|
}
|
|
1826
2070
|
});
|
|
1827
2071
|
return result;
|
|
1828
2072
|
};
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
const updatedDiscountList = addModeDiscount.map(
|
|
2073
|
+
|
|
2074
|
+
// 按原始顺序构建处理后的商品列表
|
|
2075
|
+
const processedProductList = reconstructProductsWithBundle(processedProductsMap, processedFlatItemsMap, productList);
|
|
2076
|
+
|
|
2077
|
+
// 按原始顺序更新优惠券列表,标记已使用和可用的优惠券
|
|
2078
|
+
const updatedDiscountList = addModeDiscount.map(discount => {
|
|
2079
|
+
// 获取此优惠券适用的商品列表
|
|
1835
2080
|
const applicableProducts = discountApplicability.get(discount.id) || [];
|
|
2081
|
+
|
|
2082
|
+
// 获取此优惠券适用的商品详情
|
|
1836
2083
|
const applicableProductDetails = discountApplicableProducts.get(discount.id) || [];
|
|
2084
|
+
|
|
2085
|
+
// 获取此优惠券实际应用的商品详情
|
|
1837
2086
|
const appliedProductDetails = appliedDiscountProducts.get(discount.id) || [];
|
|
2087
|
+
|
|
2088
|
+
// 根据是否有适用商品决定isAvailable
|
|
1838
2089
|
const isAvailable = applicableProducts.length > 0;
|
|
1839
|
-
|
|
2090
|
+
|
|
2091
|
+
// 如果此优惠券被明确设置为不选中,保持其状态,但正确设置isAvailable
|
|
2092
|
+
if (typeof discount.isSelected === 'boolean' && discount.isSelected === false && discount.isManualSelect) {
|
|
1840
2093
|
return {
|
|
1841
2094
|
...discount,
|
|
1842
2095
|
isSelected: false,
|
|
1843
|
-
isAvailable,
|
|
2096
|
+
isAvailable: isAvailable,
|
|
1844
2097
|
// 正确设置可用性
|
|
1845
2098
|
applicableProductIds: applicableProducts,
|
|
1846
2099
|
// 添加可抵扣的商品详情
|
|
1847
|
-
applicableProductDetails,
|
|
2100
|
+
applicableProductDetails: applicableProductDetails,
|
|
1848
2101
|
// 添加实际抵扣的商品详情(对于未选中的优惠券,为空数组)
|
|
1849
2102
|
appliedProductDetails: []
|
|
1850
2103
|
};
|
|
@@ -1853,18 +2106,20 @@ var RulesModule = class extends import_BaseModule.BaseModule {
|
|
|
1853
2106
|
return {
|
|
1854
2107
|
...discount,
|
|
1855
2108
|
isSelected: true,
|
|
1856
|
-
isManualSelect:
|
|
2109
|
+
isManualSelect: options?.scan ? false : discount.isManualSelect,
|
|
1857
2110
|
// 标记为可用,因为它已被应用
|
|
1858
2111
|
isAvailable: true,
|
|
1859
2112
|
// 记录适用的商品IDs
|
|
1860
2113
|
applicableProductIds: applicableProducts,
|
|
1861
2114
|
// 添加可抵扣的商品详情
|
|
1862
|
-
applicableProductDetails,
|
|
2115
|
+
applicableProductDetails: applicableProductDetails,
|
|
1863
2116
|
// 添加实际抵扣的商品详情
|
|
1864
|
-
appliedProductDetails
|
|
2117
|
+
appliedProductDetails: appliedProductDetails
|
|
1865
2118
|
};
|
|
1866
2119
|
}
|
|
1867
|
-
let isSelected = typeof discount.isSelected ===
|
|
2120
|
+
let isSelected = typeof discount.isSelected === 'boolean' ? discount.isSelected : undefined;
|
|
2121
|
+
|
|
2122
|
+
// 如果是可用的 并且没有手动标记 则设置为选中
|
|
1868
2123
|
if (isAvailable && appliedProductDetails.length && !discount.isManualSelect) {
|
|
1869
2124
|
isSelected = true;
|
|
1870
2125
|
} else if (!appliedProductDetails.length) {
|
|
@@ -1873,24 +2128,24 @@ var RulesModule = class extends import_BaseModule.BaseModule {
|
|
|
1873
2128
|
return {
|
|
1874
2129
|
...discount,
|
|
1875
2130
|
// 如果有适用的商品,标记为可用
|
|
1876
|
-
isAvailable,
|
|
2131
|
+
isAvailable: isAvailable,
|
|
1877
2132
|
isSelected,
|
|
1878
2133
|
// 如果是扫码进来的,要手动设置为手动选择:false
|
|
1879
|
-
isManualSelect:
|
|
2134
|
+
isManualSelect: options?.scan ? false : discount.isManualSelect,
|
|
1880
2135
|
// 记录适用的商品IDs
|
|
1881
2136
|
applicableProductIds: applicableProducts,
|
|
1882
2137
|
// 添加可抵扣的商品详情
|
|
1883
|
-
applicableProductDetails,
|
|
2138
|
+
applicableProductDetails: applicableProductDetails,
|
|
1884
2139
|
// 添加实际抵扣的商品详情(对于未使用的优惠券,为空数组)
|
|
1885
2140
|
appliedProductDetails: []
|
|
1886
2141
|
};
|
|
1887
2142
|
});
|
|
1888
|
-
console.log(processedProductList, "processedProductList");
|
|
1889
2143
|
return {
|
|
1890
2144
|
productList: processedProductList,
|
|
1891
2145
|
discountList: [...editModeDiscount, ...updatedDiscountList]
|
|
1892
2146
|
};
|
|
1893
2147
|
}
|
|
2148
|
+
|
|
1894
2149
|
/**
|
|
1895
2150
|
* 检查优惠是否符合 PackageSubItemUsageRules 配置
|
|
1896
2151
|
* @param discount 优惠券
|
|
@@ -1898,92 +2153,229 @@ var RulesModule = class extends import_BaseModule.BaseModule {
|
|
|
1898
2153
|
* @returns 是否可用
|
|
1899
2154
|
*/
|
|
1900
2155
|
checkPackageSubItemUsageRules(discount, flatItem) {
|
|
1901
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
1902
2156
|
const limitedData = discount.limited_relation_product_data;
|
|
1903
|
-
const usageRules = limitedData
|
|
1904
|
-
const rules = [
|
|
2157
|
+
const usageRules = limitedData?.package_sub_item_usage_rules;
|
|
2158
|
+
const rules = ['original_price', ...(usageRules?.rules || [])];
|
|
2159
|
+
|
|
2160
|
+
// 如果没有配置 package_sub_item_usage_rules,则默认可用
|
|
1905
2161
|
if (!usageRules) {
|
|
1906
2162
|
return true;
|
|
1907
2163
|
}
|
|
1908
2164
|
const ruleType = usageRules.type;
|
|
1909
|
-
|
|
1910
|
-
const
|
|
1911
|
-
const
|
|
1912
|
-
|
|
2165
|
+
// 套餐适用范围配置
|
|
2166
|
+
const packageScope = usageRules?.package_scope;
|
|
2167
|
+
const {
|
|
2168
|
+
type: scopeType,
|
|
2169
|
+
exclude_bundle_product_ids = [],
|
|
2170
|
+
include_bundle_product_ids = [],
|
|
2171
|
+
filter = 0
|
|
2172
|
+
} = packageScope || {};
|
|
2173
|
+
const isMainProduct = flatItem.type === 'main'; // 单独购买
|
|
2174
|
+
const isBundleItem = flatItem.type === 'bundle'; // 套餐中购买
|
|
2175
|
+
const bundleMainProductId = flatItem.product?.product_id;
|
|
2176
|
+
|
|
2177
|
+
// 主商品直接可用
|
|
1913
2178
|
if (isMainProduct) {
|
|
1914
2179
|
return true;
|
|
1915
2180
|
}
|
|
1916
|
-
|
|
2181
|
+
|
|
2182
|
+
// universal_discount: 单独购买和套餐中(子商品为原价)均可使用
|
|
2183
|
+
if (ruleType === 'universal_discount') {
|
|
1917
2184
|
if (isMainProduct) {
|
|
1918
|
-
return true;
|
|
2185
|
+
return true; // 单独购买时可用
|
|
1919
2186
|
}
|
|
1920
2187
|
if (isBundleItem) {
|
|
1921
|
-
|
|
1922
|
-
const
|
|
1923
|
-
const
|
|
1924
|
-
|
|
1925
|
-
const
|
|
2188
|
+
// 套餐中购买时,判断是否为原价
|
|
2189
|
+
const priceType = flatItem.bundleItem?.price_type;
|
|
2190
|
+
const priceTypeExt = flatItem.bundleItem?.price_type_ext;
|
|
2191
|
+
// 主商品id
|
|
2192
|
+
const mainProductId = flatItem?.originProduct?._productOrigin?.id || flatItem?.parentProduct?.id || 0;
|
|
2193
|
+
// original_price 对应:
|
|
2194
|
+
// 1. price_type: "markup" && price_type_ext: "product_price"
|
|
2195
|
+
// markup_price 对应:
|
|
2196
|
+
// price_type: "markup" && price_type_ext: ""
|
|
2197
|
+
/** 原价 */
|
|
2198
|
+
const isOriginalPrice = priceType === 'markup' && priceTypeExt === 'product_price';
|
|
2199
|
+
/** 加价 */
|
|
2200
|
+
const isMarkupPrice = priceType === 'markup' && (priceTypeExt === '' || !priceTypeExt);
|
|
2201
|
+
|
|
2202
|
+
// 检查 rules
|
|
1926
2203
|
if (rules.length > 0) {
|
|
1927
|
-
const _isOriginalPrice = isOriginalPrice && rules.includes(
|
|
1928
|
-
const _isMarkupPrice = isMarkupPrice && rules.includes(
|
|
2204
|
+
const _isOriginalPrice = isOriginalPrice && rules.includes('original_price');
|
|
2205
|
+
const _isMarkupPrice = isMarkupPrice && rules.includes('markup_price');
|
|
2206
|
+
// 价格是否符合规则:原价或加价
|
|
1929
2207
|
const isPriceValid = _isOriginalPrice || _isMarkupPrice;
|
|
2208
|
+
// 套餐适用范围校验
|
|
1930
2209
|
let isScopeValid = false;
|
|
1931
|
-
|
|
2210
|
+
|
|
2211
|
+
// 排除套餐判断, product_all值兼容旧数据
|
|
2212
|
+
if (scopeType === 'all_packages' || scopeType === 'product_all') {
|
|
2213
|
+
// 所有套餐可用
|
|
1932
2214
|
if (!filter) {
|
|
1933
2215
|
isScopeValid = true;
|
|
1934
2216
|
} else {
|
|
1935
|
-
isScopeValid = !exclude_bundle_product_ids.includes(Number(
|
|
2217
|
+
isScopeValid = !exclude_bundle_product_ids.includes(Number(bundleMainProductId));
|
|
1936
2218
|
}
|
|
1937
2219
|
}
|
|
1938
|
-
|
|
1939
|
-
|
|
2220
|
+
|
|
2221
|
+
// 包含套餐判断, products值兼容旧数据
|
|
2222
|
+
if (scopeType === 'specific_packages' || scopeType === 'products') {
|
|
2223
|
+
isScopeValid = include_bundle_product_ids.includes(Number(bundleMainProductId));
|
|
1940
2224
|
}
|
|
2225
|
+
|
|
2226
|
+
// 价格和套餐适用范围都符合,则可用
|
|
1941
2227
|
if (isPriceValid && isScopeValid) {
|
|
1942
2228
|
return true;
|
|
1943
2229
|
}
|
|
2230
|
+
|
|
2231
|
+
// 检查原价
|
|
2232
|
+
// if (isOriginalPrice && rules.includes('original_price')) {
|
|
2233
|
+
// return true;
|
|
2234
|
+
// }
|
|
2235
|
+
|
|
2236
|
+
// 检查加价
|
|
2237
|
+
// if (isMarkupPrice && rules.includes('markup_price')) {
|
|
2238
|
+
// return true;
|
|
2239
|
+
// }
|
|
2240
|
+
|
|
2241
|
+
// 如果都不匹配,则不可用
|
|
1944
2242
|
return false;
|
|
1945
2243
|
}
|
|
2244
|
+
|
|
2245
|
+
// 原价包括:price_type: "markup" && price_type_ext: "product_price"
|
|
1946
2246
|
return isOriginalPrice;
|
|
1947
2247
|
}
|
|
1948
2248
|
return true;
|
|
1949
2249
|
}
|
|
1950
|
-
|
|
2250
|
+
|
|
2251
|
+
// package_exclusive: 仅在套餐中(子商品为原价)时可使用
|
|
2252
|
+
if (ruleType === 'package_exclusive') {
|
|
1951
2253
|
if (isMainProduct) {
|
|
1952
|
-
return false;
|
|
2254
|
+
return false; // 单独购买时不可用
|
|
1953
2255
|
}
|
|
1954
2256
|
if (isBundleItem) {
|
|
1955
|
-
|
|
1956
|
-
const
|
|
1957
|
-
const
|
|
1958
|
-
|
|
2257
|
+
// 套餐中购买时,判断是否为原价
|
|
2258
|
+
const priceType = flatItem.bundleItem?.price_type;
|
|
2259
|
+
const priceTypeExt = flatItem.bundleItem?.price_type_ext;
|
|
2260
|
+
|
|
2261
|
+
// original_price 对应:
|
|
2262
|
+
// 1. price_type: "markup" && price_type_ext: "product_price"
|
|
2263
|
+
// markup_price 对应:
|
|
2264
|
+
// price_type: "markup" && price_type_ext: ""
|
|
2265
|
+
/** 原价 */
|
|
2266
|
+
const isOriginalPrice = priceType === 'markup' && priceTypeExt === 'product_price';
|
|
2267
|
+
/** 加价 */
|
|
2268
|
+
const isMarkupPrice = priceType === 'markup' && (priceTypeExt === '' || !priceTypeExt);
|
|
2269
|
+
|
|
2270
|
+
// 检查 rules
|
|
1959
2271
|
if (rules.length > 0) {
|
|
1960
|
-
|
|
1961
|
-
|
|
2272
|
+
const _isOriginalPrice = isOriginalPrice && rules.includes('original_price');
|
|
2273
|
+
const _isMarkupPrice = isMarkupPrice && rules.includes('markup_price');
|
|
2274
|
+
// 价格是否符合规则:原价或加价
|
|
2275
|
+
const isPriceValid = _isOriginalPrice || _isMarkupPrice;
|
|
2276
|
+
// 套餐适用范围校验
|
|
2277
|
+
let isScopeValid = false;
|
|
2278
|
+
|
|
2279
|
+
// 排除套餐判断, product_all值兼容旧数据
|
|
2280
|
+
if (scopeType === 'all_packages' || scopeType === 'product_all') {
|
|
2281
|
+
// 所有套餐可用
|
|
2282
|
+
if (!filter) {
|
|
2283
|
+
isScopeValid = true;
|
|
2284
|
+
} else {
|
|
2285
|
+
isScopeValid = !exclude_bundle_product_ids.includes(Number(bundleMainProductId));
|
|
2286
|
+
}
|
|
1962
2287
|
}
|
|
1963
|
-
|
|
2288
|
+
|
|
2289
|
+
// 包含套餐判断, products值兼容旧数据
|
|
2290
|
+
if (scopeType === 'specific_packages' || scopeType === 'products') {
|
|
2291
|
+
isScopeValid = include_bundle_product_ids.includes(Number(bundleMainProductId));
|
|
2292
|
+
}
|
|
2293
|
+
|
|
2294
|
+
// 价格和套餐适用范围都符合,则可用
|
|
2295
|
+
if (isPriceValid && isScopeValid) {
|
|
1964
2296
|
return true;
|
|
1965
2297
|
}
|
|
2298
|
+
|
|
2299
|
+
// 检查原价
|
|
2300
|
+
// if (isOriginalPrice && rules.includes('original_price')) {
|
|
2301
|
+
// return true;
|
|
2302
|
+
// }
|
|
2303
|
+
|
|
2304
|
+
// 检查加价
|
|
2305
|
+
// if (isMarkupPrice && rules.includes('markup_price')) {
|
|
2306
|
+
// return true;
|
|
2307
|
+
// }
|
|
2308
|
+
|
|
2309
|
+
// 如果都不匹配,则不可用
|
|
1966
2310
|
return false;
|
|
1967
2311
|
}
|
|
2312
|
+
|
|
2313
|
+
// 原价包括:price_type: "markup" && price_type_ext: "product_price"
|
|
1968
2314
|
return isOriginalPrice;
|
|
1969
2315
|
}
|
|
1970
2316
|
return false;
|
|
1971
2317
|
}
|
|
1972
|
-
|
|
1973
|
-
|
|
2318
|
+
|
|
2319
|
+
// single_item_promo: 仅在单独购买时可使用
|
|
2320
|
+
if (ruleType === 'single_item_promo') {
|
|
2321
|
+
return isMainProduct; // 只有单独购买时可用
|
|
2322
|
+
}
|
|
2323
|
+
|
|
2324
|
+
/*
|
|
2325
|
+
// custom_usage_rules: 根据自定义规则判断
|
|
2326
|
+
if (ruleType === 'custom_usage_rules') {
|
|
2327
|
+
const customRules = usageRules.custom_usage_rules;
|
|
2328
|
+
if (!customRules) {
|
|
2329
|
+
return true; // 如果没有自定义规则,默认可用
|
|
2330
|
+
}
|
|
2331
|
+
const { types, package_sub_item_rules } = customRules;
|
|
2332
|
+
// 判断商品类型是否在允许的类型中
|
|
2333
|
+
if (isMainProduct) {
|
|
2334
|
+
// 主商品对应 standalone_product
|
|
2335
|
+
if (!types.includes('standalone_product')) {
|
|
2336
|
+
return false;
|
|
2337
|
+
}
|
|
2338
|
+
return true; // 主商品不需要判断 package_sub_item_rules
|
|
2339
|
+
}
|
|
2340
|
+
if (isBundleItem) {
|
|
2341
|
+
// bundle子商品对应 package_sub_item
|
|
2342
|
+
if (!types.includes('package_sub_item')) {
|
|
2343
|
+
return false;
|
|
2344
|
+
}
|
|
2345
|
+
// 如果包含 package_sub_item,还需要判断 price_type
|
|
2346
|
+
const priceType = flatItem.bundleItem?.price_type;
|
|
2347
|
+
const priceTypeExt = flatItem.bundleItem?.price_type_ext;
|
|
2348
|
+
// 检查 package_sub_item_rules
|
|
2349
|
+
if (package_sub_item_rules && package_sub_item_rules.length > 0) {
|
|
2350
|
+
// original_price 对应:
|
|
2351
|
+
// 1. price_type: "markup" && price_type_ext: "product_price"
|
|
2352
|
+
// markup_price 对应:
|
|
2353
|
+
// price_type: "markup" && price_type_ext: ""
|
|
2354
|
+
const isOriginalPrice = (priceType === 'markup' && priceTypeExt === 'product_price');
|
|
2355
|
+
const isMarkupPrice = priceType === 'markup' && (priceTypeExt === '' || !priceTypeExt);
|
|
2356
|
+
if (isOriginalPrice && package_sub_item_rules.includes('original_price')) {
|
|
2357
|
+
return true;
|
|
2358
|
+
}
|
|
2359
|
+
if (isMarkupPrice && package_sub_item_rules.includes('markup_price')) {
|
|
2360
|
+
return true;
|
|
2361
|
+
}
|
|
2362
|
+
// 如果都不匹配,则不可用
|
|
2363
|
+
return false;
|
|
2364
|
+
}
|
|
2365
|
+
return true; // 如果没有 package_sub_item_rules 配置,默认可用
|
|
2366
|
+
}
|
|
2367
|
+
return true;
|
|
1974
2368
|
}
|
|
2369
|
+
* */
|
|
2370
|
+
|
|
2371
|
+
// 默认可用
|
|
1975
2372
|
return true;
|
|
1976
2373
|
}
|
|
1977
2374
|
async destroy() {
|
|
1978
2375
|
this.core.effects.offByModuleDestroy(this.name);
|
|
1979
2376
|
await this.core.effects.emit(`${this.name}:onDestroy`, {});
|
|
1980
|
-
|
|
2377
|
+
super.destroy();
|
|
1981
2378
|
}
|
|
1982
|
-
async clear() {
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
};
|
|
1986
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
1987
|
-
0 && (module.exports = {
|
|
1988
|
-
RulesModule
|
|
1989
|
-
});
|
|
2379
|
+
async clear() {}
|
|
2380
|
+
}
|
|
2381
|
+
exports.RulesModule = RulesModule;
|