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