@pisell/pisellos 2.2.76 → 2.2.78
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/model/strategy/adapter/promotion/evaluator.js +81 -18
- package/dist/model/strategy/adapter/walletPass/evaluator.js +2 -1
- package/dist/model/strategy/adapter/walletPass/locales.js +3 -0
- package/dist/model/strategy/adapter/walletPass/type.d.ts +4 -0
- package/dist/model/strategy/adapter/walletPass/utils.js +108 -9
- package/lib/model/strategy/adapter/promotion/evaluator.js +40 -0
- package/lib/model/strategy/adapter/walletPass/evaluator.js +2 -1
- package/lib/model/strategy/adapter/walletPass/locales.js +3 -0
- package/lib/model/strategy/adapter/walletPass/type.d.ts +4 -0
- package/lib/model/strategy/adapter/walletPass/utils.js +68 -8
- package/package.json +1 -1
|
@@ -908,11 +908,74 @@ export var PromotionEvaluator = /*#__PURE__*/function () {
|
|
|
908
908
|
// 更新已处理数量
|
|
909
909
|
processedQuantityMap.set(_key, _processedQty + _availableQty);
|
|
910
910
|
}
|
|
911
|
+
|
|
912
|
+
// 8. 尾差调整:确保促销商品的 finalPrice × quantity 之和精确等于 groupPrice × groupCount
|
|
911
913
|
} catch (err) {
|
|
912
914
|
_iterator13.e(err);
|
|
913
915
|
} finally {
|
|
914
916
|
_iterator13.f();
|
|
915
917
|
}
|
|
918
|
+
if (groupCount > 0) {
|
|
919
|
+
var expectedPromoTotal = new Decimal(groupPrice).mul(groupCount);
|
|
920
|
+
var promoItems = result.filter(function (p) {
|
|
921
|
+
return p.inPromotion;
|
|
922
|
+
});
|
|
923
|
+
var actualPromoTotal = new Decimal(0);
|
|
924
|
+
var _iterator14 = _createForOfIteratorHelper(promoItems),
|
|
925
|
+
_step14;
|
|
926
|
+
try {
|
|
927
|
+
for (_iterator14.s(); !(_step14 = _iterator14.n()).done;) {
|
|
928
|
+
var _p = _step14.value;
|
|
929
|
+
actualPromoTotal = actualPromoTotal.plus(new Decimal(_p.finalPrice).mul(_p.quantity));
|
|
930
|
+
}
|
|
931
|
+
} catch (err) {
|
|
932
|
+
_iterator14.e(err);
|
|
933
|
+
} finally {
|
|
934
|
+
_iterator14.f();
|
|
935
|
+
}
|
|
936
|
+
var roundingDiff = actualPromoTotal.minus(expectedPromoTotal);
|
|
937
|
+
if (!roundingDiff.eq(0)) {
|
|
938
|
+
// 优先找 quantity=1 的促销商品调整(差额直接加减到单价上)
|
|
939
|
+
var adjustTarget = promoItems.find(function (p) {
|
|
940
|
+
return p.quantity === 1;
|
|
941
|
+
});
|
|
942
|
+
if (adjustTarget) {
|
|
943
|
+
adjustTarget.finalPrice = this.formatPrice(new Decimal(adjustTarget.finalPrice).minus(roundingDiff));
|
|
944
|
+
} else if (promoItems.length > 0) {
|
|
945
|
+
// 没有 quantity=1 的促销商品,从最后一个促销商品中拆出1件做尾差调整
|
|
946
|
+
var lastPromo = promoItems[promoItems.length - 1];
|
|
947
|
+
var adjustedUnitPrice = this.formatPrice(new Decimal(lastPromo.finalPrice).minus(roundingDiff));
|
|
948
|
+
|
|
949
|
+
// 原商品减少1件
|
|
950
|
+
lastPromo.quantity -= 1;
|
|
951
|
+
lastPromo.isSplit = true;
|
|
952
|
+
|
|
953
|
+
// 拆出1件用调整后的价格
|
|
954
|
+
result.push(_objectSpread(_objectSpread({}, lastPromo), {}, {
|
|
955
|
+
id: this.generateRandomId(),
|
|
956
|
+
originalId: lastPromo.originalId || lastPromo.id,
|
|
957
|
+
quantity: 1,
|
|
958
|
+
finalPrice: adjustedUnitPrice,
|
|
959
|
+
isSplit: true
|
|
960
|
+
}));
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
// 重新计算 finalAmount
|
|
964
|
+
finalAmount = new Decimal(0);
|
|
965
|
+
var _iterator15 = _createForOfIteratorHelper(result),
|
|
966
|
+
_step15;
|
|
967
|
+
try {
|
|
968
|
+
for (_iterator15.s(); !(_step15 = _iterator15.n()).done;) {
|
|
969
|
+
var p = _step15.value;
|
|
970
|
+
finalAmount = finalAmount.plus(new Decimal(p.finalPrice).mul(p.quantity));
|
|
971
|
+
}
|
|
972
|
+
} catch (err) {
|
|
973
|
+
_iterator15.e(err);
|
|
974
|
+
} finally {
|
|
975
|
+
_iterator15.f();
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
}
|
|
916
979
|
return {
|
|
917
980
|
products: result,
|
|
918
981
|
originalAmount: originalAmount.toNumber(),
|
|
@@ -947,11 +1010,11 @@ export var PromotionEvaluator = /*#__PURE__*/function () {
|
|
|
947
1010
|
// 对于 bundle 子商品匹配,促销数量 = 主商品数量 × bundle子商品数量
|
|
948
1011
|
var totalPromoQty = 0;
|
|
949
1012
|
var productInfoMap = new Map();
|
|
950
|
-
var
|
|
951
|
-
|
|
1013
|
+
var _iterator16 = _createForOfIteratorHelper(groupProducts),
|
|
1014
|
+
_step16;
|
|
952
1015
|
try {
|
|
953
|
-
for (
|
|
954
|
-
var product =
|
|
1016
|
+
for (_iterator16.s(); !(_step16 = _iterator16.n()).done;) {
|
|
1017
|
+
var product = _step16.value;
|
|
955
1018
|
var key = this.getProductKey(product);
|
|
956
1019
|
var processedQty = processedQuantityMap.get(key) || 0;
|
|
957
1020
|
var availableQty = product.quantity - processedQty;
|
|
@@ -977,18 +1040,18 @@ export var PromotionEvaluator = /*#__PURE__*/function () {
|
|
|
977
1040
|
|
|
978
1041
|
// 2. 判断是否满足促销条件
|
|
979
1042
|
} catch (err) {
|
|
980
|
-
|
|
1043
|
+
_iterator16.e(err);
|
|
981
1044
|
} finally {
|
|
982
|
-
|
|
1045
|
+
_iterator16.f();
|
|
983
1046
|
}
|
|
984
1047
|
var isFulfilled = totalPromoQty >= buyQuantity;
|
|
985
1048
|
|
|
986
1049
|
// 3. 生成商品结果(第二次遍历)
|
|
987
|
-
var
|
|
988
|
-
|
|
1050
|
+
var _iterator17 = _createForOfIteratorHelper(groupProducts),
|
|
1051
|
+
_step17;
|
|
989
1052
|
try {
|
|
990
|
-
for (
|
|
991
|
-
var _product2 =
|
|
1053
|
+
for (_iterator17.s(); !(_step17 = _iterator17.n()).done;) {
|
|
1054
|
+
var _product2 = _step17.value;
|
|
992
1055
|
var _key2 = this.getProductKey(_product2);
|
|
993
1056
|
var info = productInfoMap.get(_key2);
|
|
994
1057
|
if (!info || info.availableQty <= 0) continue;
|
|
@@ -1016,9 +1079,9 @@ export var PromotionEvaluator = /*#__PURE__*/function () {
|
|
|
1016
1079
|
|
|
1017
1080
|
// 4. 计算赠品数量(使用促销数量)
|
|
1018
1081
|
} catch (err) {
|
|
1019
|
-
|
|
1082
|
+
_iterator17.e(err);
|
|
1020
1083
|
} finally {
|
|
1021
|
-
|
|
1084
|
+
_iterator17.f();
|
|
1022
1085
|
}
|
|
1023
1086
|
var giftCount = 0;
|
|
1024
1087
|
if (isFulfilled) {
|
|
@@ -1047,11 +1110,11 @@ export var PromotionEvaluator = /*#__PURE__*/function () {
|
|
|
1047
1110
|
key: "getRemainingProducts",
|
|
1048
1111
|
value: function getRemainingProducts(allProducts, processedQuantityMap) {
|
|
1049
1112
|
var result = [];
|
|
1050
|
-
var
|
|
1051
|
-
|
|
1113
|
+
var _iterator18 = _createForOfIteratorHelper(allProducts),
|
|
1114
|
+
_step18;
|
|
1052
1115
|
try {
|
|
1053
|
-
for (
|
|
1054
|
-
var product =
|
|
1116
|
+
for (_iterator18.s(); !(_step18 = _iterator18.n()).done;) {
|
|
1117
|
+
var product = _step18.value;
|
|
1055
1118
|
var key = this.getProductKey(product);
|
|
1056
1119
|
var processedQty = processedQuantityMap.get(key) || 0;
|
|
1057
1120
|
var remainingQty = product.quantity - processedQty;
|
|
@@ -1066,9 +1129,9 @@ export var PromotionEvaluator = /*#__PURE__*/function () {
|
|
|
1066
1129
|
}
|
|
1067
1130
|
}
|
|
1068
1131
|
} catch (err) {
|
|
1069
|
-
|
|
1132
|
+
_iterator18.e(err);
|
|
1070
1133
|
} finally {
|
|
1071
|
-
|
|
1134
|
+
_iterator18.f();
|
|
1072
1135
|
}
|
|
1073
1136
|
return result;
|
|
1074
1137
|
}
|
|
@@ -3,6 +3,7 @@ export var locales = {
|
|
|
3
3
|
'not_meet_the_required_conditions': '未达到使用条件',
|
|
4
4
|
'exceeds_the_maximum_deduction_limit': '超出最大抵扣金额限制',
|
|
5
5
|
'usage_limit_reached': '已达到使用次数上限',
|
|
6
|
+
'max_passes_per_item_reached': '该商品已达到卡券使用上限',
|
|
6
7
|
'not_available_for_this_channel': '当前渠道不可使用',
|
|
7
8
|
'not_valid_for_this_order_type': '当前订单类型不适用'
|
|
8
9
|
},
|
|
@@ -10,6 +11,7 @@ export var locales = {
|
|
|
10
11
|
'not_meet_the_required_conditions': 'Not meet the required conditions.',
|
|
11
12
|
'exceeds_the_maximum_deduction_limit': 'Exceeds the maximum deduction limit.',
|
|
12
13
|
'usage_limit_reached': 'Usage limit reached.',
|
|
14
|
+
'max_passes_per_item_reached': 'Max passes per item reached.',
|
|
13
15
|
'not_available_for_this_channel': 'Not available for this channel.',
|
|
14
16
|
'not_valid_for_this_order_type': 'Not valid for this order type.'
|
|
15
17
|
},
|
|
@@ -17,6 +19,7 @@ export var locales = {
|
|
|
17
19
|
'not_meet_the_required_conditions': '未達使用條件',
|
|
18
20
|
'exceeds_the_maximum_deduction_limit': '超出最大抵扣金額限制',
|
|
19
21
|
'usage_limit_reached': '已達使用次數上限',
|
|
22
|
+
'max_passes_per_item_reached': '該商品已達卡券使用上限',
|
|
20
23
|
'not_available_for_this_channel': '當前渠道不可使用',
|
|
21
24
|
'not_valid_for_this_order_type': '當前訂單類型不適用'
|
|
22
25
|
}
|
|
@@ -41,6 +41,8 @@ export interface Voucher {
|
|
|
41
41
|
allowCrossProduct: boolean;
|
|
42
42
|
/** 可用商品数量上限 (仅多商品) 默认为0,表示不限制商品数量。 */
|
|
43
43
|
applicableProductLimit: number;
|
|
44
|
+
/** 单商品可用卡券上限(同一 Wallet Pass 商品生成的卡券对同一商品行最多抵扣次数)。默认为 0,表示不限制。 */
|
|
45
|
+
maxPassesPerItem: number;
|
|
44
46
|
};
|
|
45
47
|
}
|
|
46
48
|
/**
|
|
@@ -127,6 +129,8 @@ export interface EvaluatorInput {
|
|
|
127
129
|
allowCrossProduct: boolean;
|
|
128
130
|
/** 可用商品数量上限 (仅多商品) 默认为0,表示不限制商品数量。 */
|
|
129
131
|
applicableProductLimit: number;
|
|
132
|
+
/** 单商品可用卡券上限(同一 Wallet Pass 商品生成的卡券对同一商品行最多抵扣次数)。默认为 0,表示不限制。 */
|
|
133
|
+
maxPassesPerItem: number;
|
|
130
134
|
}>[];
|
|
131
135
|
}
|
|
132
136
|
/**
|
|
@@ -118,8 +118,33 @@ export function processVouchers(applicableVouchers, orderTotalAmount, products)
|
|
|
118
118
|
var productsCopy = expandProductsWithBundleItems(products, true);
|
|
119
119
|
var remainingOrderAmount = new Decimal(orderTotalAmount); // 订单剩余应付金额
|
|
120
120
|
|
|
121
|
+
// ========== 辅助工具:maxPassesPerItem(单商品可用卡券上限)追踪 ==========
|
|
122
|
+
// 获取指定 Wallet Pass 商品对指定订单商品行的已用卡券次数
|
|
123
|
+
var getItemPassUsage = function getItemPassUsage(usageMap, walletPassProductId, orderItemProductId) {
|
|
124
|
+
var _usageMap$get;
|
|
125
|
+
return ((_usageMap$get = usageMap.get(walletPassProductId)) === null || _usageMap$get === void 0 ? void 0 : _usageMap$get.get(orderItemProductId)) || 0;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
// 更新指定 Wallet Pass 商品对指定订单商品行的已用卡券次数 +1
|
|
129
|
+
var incrementItemPassUsage = function incrementItemPassUsage(usageMap, walletPassProductId, orderItemProductId) {
|
|
130
|
+
if (!usageMap.has(walletPassProductId)) {
|
|
131
|
+
usageMap.set(walletPassProductId, new Map());
|
|
132
|
+
}
|
|
133
|
+
var innerMap = usageMap.get(walletPassProductId);
|
|
134
|
+
innerMap.set(orderItemProductId, (innerMap.get(orderItemProductId) || 0) + 1);
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
// 按 maxPassesPerItem 过滤商品:排除已达到单商品可用卡券上限的商品
|
|
138
|
+
var filterByMaxPassesPerItem = function filterByMaxPassesPerItem(products, usageMap, walletPassProductId, maxPassesPerItem) {
|
|
139
|
+
if (maxPassesPerItem <= 0) return products; // 0 = 不限制
|
|
140
|
+
return products.filter(function (p) {
|
|
141
|
+
return getItemPassUsage(usageMap, walletPassProductId, p.product_id) < maxPassesPerItem;
|
|
142
|
+
});
|
|
143
|
+
};
|
|
144
|
+
// ================================================================
|
|
145
|
+
|
|
121
146
|
// 辅助函数:计算单张券的 _available_max_amount
|
|
122
|
-
var calculateAvailableMaxAmount = function calculateAvailableMaxAmount(voucher, productsData) {
|
|
147
|
+
var calculateAvailableMaxAmount = function calculateAvailableMaxAmount(voucher, productsData, itemPassUsage) {
|
|
123
148
|
var config = voucher.config;
|
|
124
149
|
var _ref2 = config !== null && config !== void 0 ? config : {},
|
|
125
150
|
_ref2$maxDeductionAmo = _ref2.maxDeductionAmount,
|
|
@@ -129,7 +154,9 @@ export function processVouchers(applicableVouchers, orderTotalAmount, products)
|
|
|
129
154
|
_ref2$applicableProdu = _ref2.applicableProductLimit,
|
|
130
155
|
applicableProductLimit = _ref2$applicableProdu === void 0 ? 0 : _ref2$applicableProdu,
|
|
131
156
|
_ref2$deductTaxAndFee = _ref2.deductTaxAndFee,
|
|
132
|
-
deductTaxAndFee = _ref2$deductTaxAndFee === void 0 ? true : _ref2$deductTaxAndFee
|
|
157
|
+
deductTaxAndFee = _ref2$deductTaxAndFee === void 0 ? true : _ref2$deductTaxAndFee,
|
|
158
|
+
_ref2$maxPassesPerIte = _ref2.maxPassesPerItem,
|
|
159
|
+
maxPassesPerItem = _ref2$maxPassesPerIte === void 0 ? 0 : _ref2$maxPassesPerIte;
|
|
133
160
|
|
|
134
161
|
// 根据 deductTaxAndFee 配置获取推荐金额
|
|
135
162
|
var recommendedAmount = getRecommendedAmount(voucher);
|
|
@@ -145,6 +172,11 @@ export function processVouchers(applicableVouchers, orderTotalAmount, products)
|
|
|
145
172
|
var applicableProducts = getApplicableProducts(voucher, productsData).filter(function (p) {
|
|
146
173
|
return p[amountField].greaterThan(0);
|
|
147
174
|
});
|
|
175
|
+
|
|
176
|
+
// 按 maxPassesPerItem 过滤:排除已达到单商品可用卡券上限的商品
|
|
177
|
+
if (itemPassUsage) {
|
|
178
|
+
applicableProducts = filterByMaxPassesPerItem(applicableProducts, itemPassUsage, voucher.product_id, maxPassesPerItem);
|
|
179
|
+
}
|
|
148
180
|
if (applicableProducts.length === 0) {
|
|
149
181
|
return new Decimal(0);
|
|
150
182
|
}
|
|
@@ -202,7 +234,7 @@ export function processVouchers(applicableVouchers, orderTotalAmount, products)
|
|
|
202
234
|
};
|
|
203
235
|
|
|
204
236
|
// 辅助函数:判断券是否可用,返回可用性和原因代码
|
|
205
|
-
var isVoucherAvailable = function isVoucherAvailable(voucher, productsData, usedVoucherCounts) {
|
|
237
|
+
var isVoucherAvailable = function isVoucherAvailable(voucher, productsData, usedVoucherCounts, itemPassUsage) {
|
|
206
238
|
var config = voucher.config,
|
|
207
239
|
id = voucher.id,
|
|
208
240
|
product_id = voucher.product_id;
|
|
@@ -245,6 +277,25 @@ export function processVouchers(applicableVouchers, orderTotalAmount, products)
|
|
|
245
277
|
};
|
|
246
278
|
}
|
|
247
279
|
}
|
|
280
|
+
|
|
281
|
+
// 检查 maxPassesPerItem:如果所有适用商品都已达到单商品可用卡券上限,则不可用
|
|
282
|
+
var maxPassesPerItem = (config === null || config === void 0 ? void 0 : config.maxPassesPerItem) || 0;
|
|
283
|
+
if (maxPassesPerItem > 0 && itemPassUsage) {
|
|
284
|
+
var _config$deductTaxAndF3;
|
|
285
|
+
var deductTaxAndFee = (_config$deductTaxAndF3 = config === null || config === void 0 ? void 0 : config.deductTaxAndFee) !== null && _config$deductTaxAndF3 !== void 0 ? _config$deductTaxAndF3 : true;
|
|
286
|
+
var amountField = deductTaxAndFee ? 'remainingAmountWithTax' : 'remainingAmountPure';
|
|
287
|
+
var availableAfterPassLimit = getApplicableProducts(voucher, productsData).filter(function (p) {
|
|
288
|
+
return p[amountField].greaterThan(0);
|
|
289
|
+
}).filter(function (p) {
|
|
290
|
+
return getItemPassUsage(itemPassUsage, product_id, p.product_id) < maxPassesPerItem;
|
|
291
|
+
});
|
|
292
|
+
if (availableAfterPassLimit.length === 0) {
|
|
293
|
+
return {
|
|
294
|
+
isAvailable: false,
|
|
295
|
+
reasonCode: 'max_passes_per_item_reached'
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
}
|
|
248
299
|
return {
|
|
249
300
|
isAvailable: true
|
|
250
301
|
};
|
|
@@ -272,6 +323,8 @@ export function processVouchers(applicableVouchers, orderTotalAmount, products)
|
|
|
272
323
|
// 重置商品余额追踪(同时维护含税和不含税两个金额池)
|
|
273
324
|
var productsForRecommendation = expandProductsWithBundleItems(products, true);
|
|
274
325
|
remainingOrderAmount = new Decimal(orderTotalAmount);
|
|
326
|
+
// 追踪推荐阶段每个 Wallet Pass 商品对每个订单商品行的已使用卡券次数
|
|
327
|
+
var itemPassUsageMap = new Map();
|
|
275
328
|
|
|
276
329
|
/**
|
|
277
330
|
// 分类券:未跨商品券 vs 跨商品券
|
|
@@ -302,7 +355,7 @@ export function processVouchers(applicableVouchers, orderTotalAmount, products)
|
|
|
302
355
|
// 处理单张券的抵扣
|
|
303
356
|
var applyVoucher = function applyVoucher(voucher) {
|
|
304
357
|
// 检查是否可用
|
|
305
|
-
var availabilityCheck = isVoucherAvailable(voucher, productsForRecommendation, usedVoucherCounts);
|
|
358
|
+
var availabilityCheck = isVoucherAvailable(voucher, productsForRecommendation, usedVoucherCounts, itemPassUsageMap);
|
|
306
359
|
if (!availabilityCheck.isAvailable) {
|
|
307
360
|
return false;
|
|
308
361
|
}
|
|
@@ -317,7 +370,9 @@ export function processVouchers(applicableVouchers, orderTotalAmount, products)
|
|
|
317
370
|
_ref3$applicableProdu = _ref3.applicableProductLimit,
|
|
318
371
|
applicableProductLimit = _ref3$applicableProdu === void 0 ? 0 : _ref3$applicableProdu,
|
|
319
372
|
_ref3$deductTaxAndFee = _ref3.deductTaxAndFee,
|
|
320
|
-
deductTaxAndFee = _ref3$deductTaxAndFee === void 0 ? true : _ref3$deductTaxAndFee
|
|
373
|
+
deductTaxAndFee = _ref3$deductTaxAndFee === void 0 ? true : _ref3$deductTaxAndFee,
|
|
374
|
+
_ref3$maxPassesPerIte = _ref3.maxPassesPerItem,
|
|
375
|
+
maxPassesPerItem = _ref3$maxPassesPerIte === void 0 ? 0 : _ref3$maxPassesPerIte;
|
|
321
376
|
|
|
322
377
|
// 根据券的配置选择使用哪个单价字段和金额字段
|
|
323
378
|
var unitPriceField = deductTaxAndFee ? 'unitPriceWithTax' : 'unitPricePure';
|
|
@@ -327,6 +382,9 @@ export function processVouchers(applicableVouchers, orderTotalAmount, products)
|
|
|
327
382
|
var applicableProducts = getApplicableProducts(voucher, productsForRecommendation).filter(function (p) {
|
|
328
383
|
return p[amountField].greaterThan(0);
|
|
329
384
|
});
|
|
385
|
+
|
|
386
|
+
// 按 maxPassesPerItem 过滤:排除已达到单商品可用卡券上限的商品
|
|
387
|
+
applicableProducts = filterByMaxPassesPerItem(applicableProducts, itemPassUsageMap, product_id, maxPassesPerItem);
|
|
330
388
|
if (applicableProducts.length === 0) return false;
|
|
331
389
|
|
|
332
390
|
// ========== 关键修改:在应用券之前,基于当前剩余金额计算 _available_max_amount ==========
|
|
@@ -466,6 +524,11 @@ export function processVouchers(applicableVouchers, orderTotalAmount, products)
|
|
|
466
524
|
// 更新券使用次数(按 product_id 统计)
|
|
467
525
|
usedVoucherCounts.set(product_id, (usedVoucherCounts.get(product_id) || 0) + 1);
|
|
468
526
|
|
|
527
|
+
// 更新 maxPassesPerItem 追踪:记录每个被抵扣的商品行
|
|
528
|
+
deductionDetails.forEach(function (detail) {
|
|
529
|
+
incrementItemPassUsage(itemPassUsageMap, product_id, detail.product_id);
|
|
530
|
+
});
|
|
531
|
+
|
|
469
532
|
// 添加到推荐列表(包含基于当前剩余金额计算的 available_max_amount)
|
|
470
533
|
recommendedVouchers.push(_objectSpread(_objectSpread({}, voucher), {}, {
|
|
471
534
|
actualDeduction: totalDeducted.toNumber(),
|
|
@@ -547,6 +610,27 @@ export function recalculateVouchers(allVouchers, selectedVouchers, orderTotalAmo
|
|
|
547
610
|
var remainingOrderAmount = new Decimal(orderTotalAmount);
|
|
548
611
|
var selectedWithDetails = [];
|
|
549
612
|
|
|
613
|
+
// 追踪每个 Wallet Pass 商品对每个订单商品行的已使用卡券次数
|
|
614
|
+
// Map<walletPassProductId, Map<orderItemProductId, usedCount>>
|
|
615
|
+
var itemPassUsageMap = new Map();
|
|
616
|
+
var getItemPassUsage = function getItemPassUsage(walletPassProductId, orderItemProductId) {
|
|
617
|
+
var _itemPassUsageMap$get;
|
|
618
|
+
return ((_itemPassUsageMap$get = itemPassUsageMap.get(walletPassProductId)) === null || _itemPassUsageMap$get === void 0 ? void 0 : _itemPassUsageMap$get.get(orderItemProductId)) || 0;
|
|
619
|
+
};
|
|
620
|
+
var incrementItemPassUsage = function incrementItemPassUsage(walletPassProductId, orderItemProductId) {
|
|
621
|
+
if (!itemPassUsageMap.has(walletPassProductId)) {
|
|
622
|
+
itemPassUsageMap.set(walletPassProductId, new Map());
|
|
623
|
+
}
|
|
624
|
+
var innerMap = itemPassUsageMap.get(walletPassProductId);
|
|
625
|
+
innerMap.set(orderItemProductId, (innerMap.get(orderItemProductId) || 0) + 1);
|
|
626
|
+
};
|
|
627
|
+
var filterByMaxPassesPerItem = function filterByMaxPassesPerItem(products, walletPassProductId, maxPassesPerItem) {
|
|
628
|
+
if (maxPassesPerItem <= 0) return products; // 0 = 不限制
|
|
629
|
+
return products.filter(function (p) {
|
|
630
|
+
return getItemPassUsage(walletPassProductId, p.product_id) < maxPassesPerItem;
|
|
631
|
+
});
|
|
632
|
+
};
|
|
633
|
+
|
|
550
634
|
// 第一步:按顺序应用已选中的券,计算实际抵扣
|
|
551
635
|
selectedVouchers.forEach(function (selectedVoucher) {
|
|
552
636
|
var config = selectedVoucher.config,
|
|
@@ -554,8 +638,10 @@ export function recalculateVouchers(allVouchers, selectedVouchers, orderTotalAmo
|
|
|
554
638
|
var maxDeductionAmount = config.maxDeductionAmount,
|
|
555
639
|
allowCrossProduct = config.allowCrossProduct,
|
|
556
640
|
applicableProductLimit = config.applicableProductLimit,
|
|
557
|
-
_config$
|
|
558
|
-
deductTaxAndFee = _config$
|
|
641
|
+
_config$deductTaxAndF4 = config.deductTaxAndFee,
|
|
642
|
+
deductTaxAndFee = _config$deductTaxAndF4 === void 0 ? true : _config$deductTaxAndF4,
|
|
643
|
+
_config$maxPassesPerI = config.maxPassesPerItem,
|
|
644
|
+
maxPassesPerItem = _config$maxPassesPerI === void 0 ? 0 : _config$maxPassesPerI;
|
|
559
645
|
|
|
560
646
|
// 根据券的配置选择使用哪个单价字段和金额字段
|
|
561
647
|
var unitPriceField = deductTaxAndFee ? 'unitPriceWithTax' : 'unitPricePure';
|
|
@@ -565,6 +651,9 @@ export function recalculateVouchers(allVouchers, selectedVouchers, orderTotalAmo
|
|
|
565
651
|
var applicableProducts = getApplicableProducts(selectedVoucher, productsForCalc).filter(function (p) {
|
|
566
652
|
return p[amountField].greaterThan(0);
|
|
567
653
|
});
|
|
654
|
+
|
|
655
|
+
// 按 maxPassesPerItem 过滤:排除已达到单商品可用卡券上限的商品
|
|
656
|
+
applicableProducts = filterByMaxPassesPerItem(applicableProducts, selectedVoucher.product_id, maxPassesPerItem);
|
|
568
657
|
if (applicableProducts.length === 0) {
|
|
569
658
|
// 无适用商品,跳过
|
|
570
659
|
selectedWithDetails.push(_objectSpread(_objectSpread({}, selectedVoucher), {}, {
|
|
@@ -658,6 +747,11 @@ export function recalculateVouchers(allVouchers, selectedVouchers, orderTotalAmo
|
|
|
658
747
|
|
|
659
748
|
// 更新订单剩余金额
|
|
660
749
|
remainingOrderAmount = remainingOrderAmount.minus(totalDeducted);
|
|
750
|
+
|
|
751
|
+
// 更新 maxPassesPerItem 追踪:记录每个被抵扣的商品行
|
|
752
|
+
deductionDetails.forEach(function (detail) {
|
|
753
|
+
incrementItemPassUsage(selectedVoucher.product_id, detail.product_id);
|
|
754
|
+
});
|
|
661
755
|
selectedWithDetails.push(_objectSpread(_objectSpread({}, selectedVoucher), {}, {
|
|
662
756
|
actualDeduction: totalDeducted.toNumber(),
|
|
663
757
|
// 转换为数字
|
|
@@ -692,8 +786,10 @@ export function recalculateVouchers(allVouchers, selectedVouchers, orderTotalAmo
|
|
|
692
786
|
var maxDeductionAmount = config.maxDeductionAmount,
|
|
693
787
|
allowCrossProduct = config.allowCrossProduct,
|
|
694
788
|
applicableProductLimit = config.applicableProductLimit,
|
|
695
|
-
_config$
|
|
696
|
-
deductTaxAndFee = _config$
|
|
789
|
+
_config$deductTaxAndF5 = config.deductTaxAndFee,
|
|
790
|
+
deductTaxAndFee = _config$deductTaxAndF5 === void 0 ? true : _config$deductTaxAndF5,
|
|
791
|
+
_config$maxPassesPerI2 = config.maxPassesPerItem,
|
|
792
|
+
maxPassesPerItem = _config$maxPassesPerI2 === void 0 ? 0 : _config$maxPassesPerI2;
|
|
697
793
|
|
|
698
794
|
// 根据券的配置选择使用哪个单价字段和金额字段
|
|
699
795
|
var unitPriceField = deductTaxAndFee ? 'unitPriceWithTax' : 'unitPricePure';
|
|
@@ -726,6 +822,9 @@ export function recalculateVouchers(allVouchers, selectedVouchers, orderTotalAmo
|
|
|
726
822
|
var applicableProducts = getApplicableProducts(voucher, productsForCalc).filter(function (p) {
|
|
727
823
|
return p[amountField].greaterThan(0);
|
|
728
824
|
});
|
|
825
|
+
|
|
826
|
+
// 按 maxPassesPerItem 过滤:排除已达到单商品可用卡券上限的商品
|
|
827
|
+
applicableProducts = filterByMaxPassesPerItem(applicableProducts, product_id, maxPassesPerItem);
|
|
729
828
|
if (applicableProducts.length === 0) {
|
|
730
829
|
isAvailable = false;
|
|
731
830
|
reasonCode = 'not_meet_the_required_conditions';
|
|
@@ -645,6 +645,46 @@ var PromotionEvaluator = class {
|
|
|
645
645
|
}
|
|
646
646
|
processedQuantityMap.set(key, processedQty + availableQty);
|
|
647
647
|
}
|
|
648
|
+
if (groupCount > 0) {
|
|
649
|
+
const expectedPromoTotal = new import_decimal.default(groupPrice).mul(groupCount);
|
|
650
|
+
const promoItems = result.filter((p) => p.inPromotion);
|
|
651
|
+
let actualPromoTotal = new import_decimal.default(0);
|
|
652
|
+
for (const p of promoItems) {
|
|
653
|
+
actualPromoTotal = actualPromoTotal.plus(
|
|
654
|
+
new import_decimal.default(p.finalPrice).mul(p.quantity)
|
|
655
|
+
);
|
|
656
|
+
}
|
|
657
|
+
const roundingDiff = actualPromoTotal.minus(expectedPromoTotal);
|
|
658
|
+
if (!roundingDiff.eq(0)) {
|
|
659
|
+
const adjustTarget = promoItems.find((p) => p.quantity === 1);
|
|
660
|
+
if (adjustTarget) {
|
|
661
|
+
adjustTarget.finalPrice = this.formatPrice(
|
|
662
|
+
new import_decimal.default(adjustTarget.finalPrice).minus(roundingDiff)
|
|
663
|
+
);
|
|
664
|
+
} else if (promoItems.length > 0) {
|
|
665
|
+
const lastPromo = promoItems[promoItems.length - 1];
|
|
666
|
+
const adjustedUnitPrice = this.formatPrice(
|
|
667
|
+
new import_decimal.default(lastPromo.finalPrice).minus(roundingDiff)
|
|
668
|
+
);
|
|
669
|
+
lastPromo.quantity -= 1;
|
|
670
|
+
lastPromo.isSplit = true;
|
|
671
|
+
result.push({
|
|
672
|
+
...lastPromo,
|
|
673
|
+
id: this.generateRandomId(),
|
|
674
|
+
originalId: lastPromo.originalId || lastPromo.id,
|
|
675
|
+
quantity: 1,
|
|
676
|
+
finalPrice: adjustedUnitPrice,
|
|
677
|
+
isSplit: true
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
finalAmount = new import_decimal.default(0);
|
|
681
|
+
for (const p of result) {
|
|
682
|
+
finalAmount = finalAmount.plus(
|
|
683
|
+
new import_decimal.default(p.finalPrice).mul(p.quantity)
|
|
684
|
+
);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
}
|
|
648
688
|
return {
|
|
649
689
|
products: result,
|
|
650
690
|
originalAmount: originalAmount.toNumber(),
|
|
@@ -27,6 +27,7 @@ var locales = {
|
|
|
27
27
|
"not_meet_the_required_conditions": "未达到使用条件",
|
|
28
28
|
"exceeds_the_maximum_deduction_limit": "超出最大抵扣金额限制",
|
|
29
29
|
"usage_limit_reached": "已达到使用次数上限",
|
|
30
|
+
"max_passes_per_item_reached": "该商品已达到卡券使用上限",
|
|
30
31
|
"not_available_for_this_channel": "当前渠道不可使用",
|
|
31
32
|
"not_valid_for_this_order_type": "当前订单类型不适用"
|
|
32
33
|
},
|
|
@@ -34,6 +35,7 @@ var locales = {
|
|
|
34
35
|
"not_meet_the_required_conditions": "Not meet the required conditions.",
|
|
35
36
|
"exceeds_the_maximum_deduction_limit": "Exceeds the maximum deduction limit.",
|
|
36
37
|
"usage_limit_reached": "Usage limit reached.",
|
|
38
|
+
"max_passes_per_item_reached": "Max passes per item reached.",
|
|
37
39
|
"not_available_for_this_channel": "Not available for this channel.",
|
|
38
40
|
"not_valid_for_this_order_type": "Not valid for this order type."
|
|
39
41
|
},
|
|
@@ -41,6 +43,7 @@ var locales = {
|
|
|
41
43
|
"not_meet_the_required_conditions": "未達使用條件",
|
|
42
44
|
"exceeds_the_maximum_deduction_limit": "超出最大抵扣金額限制",
|
|
43
45
|
"usage_limit_reached": "已達使用次數上限",
|
|
46
|
+
"max_passes_per_item_reached": "該商品已達卡券使用上限",
|
|
44
47
|
"not_available_for_this_channel": "當前渠道不可使用",
|
|
45
48
|
"not_valid_for_this_order_type": "當前訂單類型不適用"
|
|
46
49
|
}
|
|
@@ -41,6 +41,8 @@ export interface Voucher {
|
|
|
41
41
|
allowCrossProduct: boolean;
|
|
42
42
|
/** 可用商品数量上限 (仅多商品) 默认为0,表示不限制商品数量。 */
|
|
43
43
|
applicableProductLimit: number;
|
|
44
|
+
/** 单商品可用卡券上限(同一 Wallet Pass 商品生成的卡券对同一商品行最多抵扣次数)。默认为 0,表示不限制。 */
|
|
45
|
+
maxPassesPerItem: number;
|
|
44
46
|
};
|
|
45
47
|
}
|
|
46
48
|
/**
|
|
@@ -127,6 +129,8 @@ export interface EvaluatorInput {
|
|
|
127
129
|
allowCrossProduct: boolean;
|
|
128
130
|
/** 可用商品数量上限 (仅多商品) 默认为0,表示不限制商品数量。 */
|
|
129
131
|
applicableProductLimit: number;
|
|
132
|
+
/** 单商品可用卡券上限(同一 Wallet Pass 商品生成的卡券对同一商品行最多抵扣次数)。默认为 0,表示不限制。 */
|
|
133
|
+
maxPassesPerItem: number;
|
|
130
134
|
}>[];
|
|
131
135
|
}
|
|
132
136
|
/**
|
|
@@ -86,9 +86,27 @@ function processVouchers(applicableVouchers, orderTotalAmount, products) {
|
|
|
86
86
|
console.log(products, "products123");
|
|
87
87
|
const productsCopy = expandProductsWithBundleItems(products, true);
|
|
88
88
|
let remainingOrderAmount = new import_decimal.default(orderTotalAmount);
|
|
89
|
-
const
|
|
89
|
+
const getItemPassUsage = (usageMap, walletPassProductId, orderItemProductId) => {
|
|
90
|
+
var _a;
|
|
91
|
+
return ((_a = usageMap.get(walletPassProductId)) == null ? void 0 : _a.get(orderItemProductId)) || 0;
|
|
92
|
+
};
|
|
93
|
+
const incrementItemPassUsage = (usageMap, walletPassProductId, orderItemProductId) => {
|
|
94
|
+
if (!usageMap.has(walletPassProductId)) {
|
|
95
|
+
usageMap.set(walletPassProductId, /* @__PURE__ */ new Map());
|
|
96
|
+
}
|
|
97
|
+
const innerMap = usageMap.get(walletPassProductId);
|
|
98
|
+
innerMap.set(orderItemProductId, (innerMap.get(orderItemProductId) || 0) + 1);
|
|
99
|
+
};
|
|
100
|
+
const filterByMaxPassesPerItem = (products2, usageMap, walletPassProductId, maxPassesPerItem) => {
|
|
101
|
+
if (maxPassesPerItem <= 0)
|
|
102
|
+
return products2;
|
|
103
|
+
return products2.filter(
|
|
104
|
+
(p) => getItemPassUsage(usageMap, walletPassProductId, p.product_id) < maxPassesPerItem
|
|
105
|
+
);
|
|
106
|
+
};
|
|
107
|
+
const calculateAvailableMaxAmount = (voucher, productsData, itemPassUsage) => {
|
|
90
108
|
const { config } = voucher;
|
|
91
|
-
const { maxDeductionAmount = 0, allowCrossProduct = true, applicableProductLimit = 0, deductTaxAndFee = true } = config ?? {};
|
|
109
|
+
const { maxDeductionAmount = 0, allowCrossProduct = true, applicableProductLimit = 0, deductTaxAndFee = true, maxPassesPerItem = 0 } = config ?? {};
|
|
92
110
|
const recommendedAmount = getRecommendedAmount(voucher);
|
|
93
111
|
const baseAmount = import_decimal.default.min(
|
|
94
112
|
new import_decimal.default(recommendedAmount),
|
|
@@ -96,7 +114,10 @@ function processVouchers(applicableVouchers, orderTotalAmount, products) {
|
|
|
96
114
|
);
|
|
97
115
|
const unitPriceField = deductTaxAndFee ? "unitPriceWithTax" : "unitPricePure";
|
|
98
116
|
const amountField = deductTaxAndFee ? "remainingAmountWithTax" : "remainingAmountPure";
|
|
99
|
-
|
|
117
|
+
let applicableProducts = getApplicableProducts(voucher, productsData).filter((p) => p[amountField].greaterThan(0));
|
|
118
|
+
if (itemPassUsage) {
|
|
119
|
+
applicableProducts = filterByMaxPassesPerItem(applicableProducts, itemPassUsage, voucher.product_id, maxPassesPerItem);
|
|
120
|
+
}
|
|
100
121
|
if (applicableProducts.length === 0) {
|
|
101
122
|
return new import_decimal.default(0);
|
|
102
123
|
}
|
|
@@ -136,7 +157,7 @@ function processVouchers(applicableVouchers, orderTotalAmount, products) {
|
|
|
136
157
|
}
|
|
137
158
|
return import_decimal.default.min(baseAmount, finalApplicableAmount, remainingOrderAmount);
|
|
138
159
|
};
|
|
139
|
-
const isVoucherAvailable = (voucher, productsData, usedVoucherCounts2) => {
|
|
160
|
+
const isVoucherAvailable = (voucher, productsData, usedVoucherCounts2, itemPassUsage) => {
|
|
140
161
|
const { config, id, product_id } = voucher;
|
|
141
162
|
const recommendedAmount = getRecommendedAmount(voucher);
|
|
142
163
|
if (recommendedAmount <= 0) {
|
|
@@ -155,6 +176,15 @@ function processVouchers(applicableVouchers, orderTotalAmount, products) {
|
|
|
155
176
|
return { isAvailable: false, reasonCode: "usage_limit_reached" };
|
|
156
177
|
}
|
|
157
178
|
}
|
|
179
|
+
const maxPassesPerItem = (config == null ? void 0 : config.maxPassesPerItem) || 0;
|
|
180
|
+
if (maxPassesPerItem > 0 && itemPassUsage) {
|
|
181
|
+
const deductTaxAndFee = (config == null ? void 0 : config.deductTaxAndFee) ?? true;
|
|
182
|
+
const amountField = deductTaxAndFee ? "remainingAmountWithTax" : "remainingAmountPure";
|
|
183
|
+
const availableAfterPassLimit = getApplicableProducts(voucher, productsData).filter((p) => p[amountField].greaterThan(0)).filter((p) => getItemPassUsage(itemPassUsage, product_id, p.product_id) < maxPassesPerItem);
|
|
184
|
+
if (availableAfterPassLimit.length === 0) {
|
|
185
|
+
return { isAvailable: false, reasonCode: "max_passes_per_item_reached" };
|
|
186
|
+
}
|
|
187
|
+
}
|
|
158
188
|
return { isAvailable: true };
|
|
159
189
|
};
|
|
160
190
|
const usedVoucherCountsForAll = /* @__PURE__ */ new Map();
|
|
@@ -181,23 +211,26 @@ function processVouchers(applicableVouchers, orderTotalAmount, products) {
|
|
|
181
211
|
const usedVoucherCounts = /* @__PURE__ */ new Map();
|
|
182
212
|
const productsForRecommendation = expandProductsWithBundleItems(products, true);
|
|
183
213
|
remainingOrderAmount = new import_decimal.default(orderTotalAmount);
|
|
214
|
+
const itemPassUsageMap = /* @__PURE__ */ new Map();
|
|
184
215
|
const applyVoucher = (voucher) => {
|
|
185
216
|
const availabilityCheck = isVoucherAvailable(
|
|
186
217
|
voucher,
|
|
187
218
|
productsForRecommendation,
|
|
188
|
-
usedVoucherCounts
|
|
219
|
+
usedVoucherCounts,
|
|
220
|
+
itemPassUsageMap
|
|
189
221
|
);
|
|
190
222
|
if (!availabilityCheck.isAvailable) {
|
|
191
223
|
return false;
|
|
192
224
|
}
|
|
193
225
|
const { config, id, product_id } = voucher;
|
|
194
|
-
const { maxDeductionAmount = 0, allowCrossProduct = true, applicableProductLimit = 0, deductTaxAndFee = true } = config ?? {};
|
|
226
|
+
const { maxDeductionAmount = 0, allowCrossProduct = true, applicableProductLimit = 0, deductTaxAndFee = true, maxPassesPerItem = 0 } = config ?? {};
|
|
195
227
|
const unitPriceField = deductTaxAndFee ? "unitPriceWithTax" : "unitPricePure";
|
|
196
228
|
const amountField = deductTaxAndFee ? "remainingAmountWithTax" : "remainingAmountPure";
|
|
197
229
|
let applicableProducts = getApplicableProducts(
|
|
198
230
|
voucher,
|
|
199
231
|
productsForRecommendation
|
|
200
232
|
).filter((p) => p[amountField].greaterThan(0));
|
|
233
|
+
applicableProducts = filterByMaxPassesPerItem(applicableProducts, itemPassUsageMap, product_id, maxPassesPerItem);
|
|
201
234
|
if (applicableProducts.length === 0)
|
|
202
235
|
return false;
|
|
203
236
|
const usageAmount = typeof voucher.edit_current_amount === "number" ? voucher.edit_current_amount : getRecommendedAmount(voucher);
|
|
@@ -307,6 +340,9 @@ function processVouchers(applicableVouchers, orderTotalAmount, products) {
|
|
|
307
340
|
if (totalDeducted.greaterThan(0)) {
|
|
308
341
|
remainingOrderAmount = remainingOrderAmount.minus(totalDeducted);
|
|
309
342
|
usedVoucherCounts.set(product_id, (usedVoucherCounts.get(product_id) || 0) + 1);
|
|
343
|
+
deductionDetails.forEach((detail) => {
|
|
344
|
+
incrementItemPassUsage(itemPassUsageMap, product_id, detail.product_id);
|
|
345
|
+
});
|
|
310
346
|
recommendedVouchers.push({
|
|
311
347
|
...voucher,
|
|
312
348
|
actualDeduction: totalDeducted.toNumber(),
|
|
@@ -348,15 +384,35 @@ function recalculateVouchers(allVouchers, selectedVouchers, orderTotalAmount, pr
|
|
|
348
384
|
const productsForCalc = expandProductsWithBundleItems(products, true);
|
|
349
385
|
let remainingOrderAmount = new import_decimal.default(orderTotalAmount);
|
|
350
386
|
const selectedWithDetails = [];
|
|
387
|
+
const itemPassUsageMap = /* @__PURE__ */ new Map();
|
|
388
|
+
const getItemPassUsage = (walletPassProductId, orderItemProductId) => {
|
|
389
|
+
var _a;
|
|
390
|
+
return ((_a = itemPassUsageMap.get(walletPassProductId)) == null ? void 0 : _a.get(orderItemProductId)) || 0;
|
|
391
|
+
};
|
|
392
|
+
const incrementItemPassUsage = (walletPassProductId, orderItemProductId) => {
|
|
393
|
+
if (!itemPassUsageMap.has(walletPassProductId)) {
|
|
394
|
+
itemPassUsageMap.set(walletPassProductId, /* @__PURE__ */ new Map());
|
|
395
|
+
}
|
|
396
|
+
const innerMap = itemPassUsageMap.get(walletPassProductId);
|
|
397
|
+
innerMap.set(orderItemProductId, (innerMap.get(orderItemProductId) || 0) + 1);
|
|
398
|
+
};
|
|
399
|
+
const filterByMaxPassesPerItem = (products2, walletPassProductId, maxPassesPerItem) => {
|
|
400
|
+
if (maxPassesPerItem <= 0)
|
|
401
|
+
return products2;
|
|
402
|
+
return products2.filter(
|
|
403
|
+
(p) => getItemPassUsage(walletPassProductId, p.product_id) < maxPassesPerItem
|
|
404
|
+
);
|
|
405
|
+
};
|
|
351
406
|
selectedVouchers.forEach((selectedVoucher) => {
|
|
352
407
|
const { config, id } = selectedVoucher;
|
|
353
|
-
const { maxDeductionAmount, allowCrossProduct, applicableProductLimit, deductTaxAndFee = true } = config;
|
|
408
|
+
const { maxDeductionAmount, allowCrossProduct, applicableProductLimit, deductTaxAndFee = true, maxPassesPerItem = 0 } = config;
|
|
354
409
|
const unitPriceField = deductTaxAndFee ? "unitPriceWithTax" : "unitPricePure";
|
|
355
410
|
const amountField = deductTaxAndFee ? "remainingAmountWithTax" : "remainingAmountPure";
|
|
356
411
|
let applicableProducts = getApplicableProducts(
|
|
357
412
|
selectedVoucher,
|
|
358
413
|
productsForCalc
|
|
359
414
|
).filter((p) => p[amountField].greaterThan(0));
|
|
415
|
+
applicableProducts = filterByMaxPassesPerItem(applicableProducts, selectedVoucher.product_id, maxPassesPerItem);
|
|
360
416
|
if (applicableProducts.length === 0) {
|
|
361
417
|
selectedWithDetails.push({
|
|
362
418
|
...selectedVoucher,
|
|
@@ -431,6 +487,9 @@ function recalculateVouchers(allVouchers, selectedVouchers, orderTotalAmount, pr
|
|
|
431
487
|
}
|
|
432
488
|
const totalDeducted = maxDeduction.minus(deductionLeft);
|
|
433
489
|
remainingOrderAmount = remainingOrderAmount.minus(totalDeducted);
|
|
490
|
+
deductionDetails.forEach((detail) => {
|
|
491
|
+
incrementItemPassUsage(selectedVoucher.product_id, detail.product_id);
|
|
492
|
+
});
|
|
434
493
|
selectedWithDetails.push({
|
|
435
494
|
...selectedVoucher,
|
|
436
495
|
actualDeduction: totalDeducted.toNumber(),
|
|
@@ -454,7 +513,7 @@ function recalculateVouchers(allVouchers, selectedVouchers, orderTotalAmount, pr
|
|
|
454
513
|
return selectedDetail || voucher;
|
|
455
514
|
}
|
|
456
515
|
const { config, id, product_id } = voucher;
|
|
457
|
-
const { maxDeductionAmount, allowCrossProduct, applicableProductLimit, deductTaxAndFee = true } = config;
|
|
516
|
+
const { maxDeductionAmount, allowCrossProduct, applicableProductLimit, deductTaxAndFee = true, maxPassesPerItem = 0 } = config;
|
|
458
517
|
const unitPriceField = deductTaxAndFee ? "unitPriceWithTax" : "unitPricePure";
|
|
459
518
|
const amountField = deductTaxAndFee ? "remainingAmountWithTax" : "remainingAmountPure";
|
|
460
519
|
const recommendedAmount = getRecommendedAmount(voucher);
|
|
@@ -480,6 +539,7 @@ function recalculateVouchers(allVouchers, selectedVouchers, orderTotalAmount, pr
|
|
|
480
539
|
voucher,
|
|
481
540
|
productsForCalc
|
|
482
541
|
).filter((p) => p[amountField].greaterThan(0));
|
|
542
|
+
applicableProducts = filterByMaxPassesPerItem(applicableProducts, product_id, maxPassesPerItem);
|
|
483
543
|
if (applicableProducts.length === 0) {
|
|
484
544
|
isAvailable = false;
|
|
485
545
|
reasonCode = "not_meet_the_required_conditions";
|