@pisell/pisellos 1.0.134 → 1.0.136

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.
@@ -19,7 +19,8 @@ var defaultStrategyMetadataCustom = {
19
19
  maxUsagePerOrder: 0,
20
20
  allowCrossProduct: true,
21
21
  applicableProductLimit: 0,
22
- deductTaxAndFee: true
22
+ deductTaxAndFee: true,
23
+ maxPassesPerItem: 0
23
24
  };
24
25
 
25
26
  /**
@@ -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
  /**
@@ -130,6 +132,8 @@ export interface EvaluatorInput {
130
132
  allowCrossProduct: boolean;
131
133
  /** 可用商品数量上限 (仅多商品) 默认为0,表示不限制商品数量。 */
132
134
  applicableProductLimit: number;
135
+ /** 单商品可用卡券上限(同一 Wallet Pass 商品生成的卡券对同一商品行最多抵扣次数)。默认为 0,表示不限制。 */
136
+ maxPassesPerItem: number;
133
137
  }>[];
134
138
  }
135
139
  /**
@@ -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$deductTaxAndF3 = config.deductTaxAndFee,
558
- deductTaxAndFee = _config$deductTaxAndF3 === void 0 ? true : _config$deductTaxAndF3;
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$deductTaxAndF4 = config.deductTaxAndFee,
696
- deductTaxAndFee = _config$deductTaxAndF4 === void 0 ? true : _config$deductTaxAndF4;
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';
@@ -1167,14 +1167,15 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
1167
1167
  discount_list: []
1168
1168
  }))]);
1169
1169
  } else {
1170
+ var _product$main_product, _product$main_product2;
1170
1171
  processedProductsMap.set(product._id, [_this3.hooks.setProduct(originProduct, _objectSpread(_objectSpread({}, isManualDiscount ? {
1171
1172
  price: product.price,
1172
- main_product_selling_price: product.price
1173
+ main_product_selling_price: (_product$main_product = product.main_product_selling_price) !== null && _product$main_product !== void 0 ? _product$main_product : product.price
1173
1174
  } : {
1174
1175
  _id: product._id.split('___')[0] + '___' + index,
1175
1176
  total: product.origin_total || product.total,
1176
1177
  price: product.price,
1177
- main_product_selling_price: product.price
1178
+ main_product_selling_price: (_product$main_product2 = product.main_product_selling_price) !== null && _product$main_product2 !== void 0 ? _product$main_product2 : product.price
1178
1179
  }), {}, {
1179
1180
  discount_list: []
1180
1181
  }))]);
@@ -41,7 +41,8 @@ var defaultStrategyMetadataCustom = {
41
41
  maxUsagePerOrder: 0,
42
42
  allowCrossProduct: true,
43
43
  applicableProductLimit: 0,
44
- deductTaxAndFee: true
44
+ deductTaxAndFee: true,
45
+ maxPassesPerItem: 0
45
46
  };
46
47
  var WalletPassEvaluator = class {
47
48
  constructor() {
@@ -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
  /**
@@ -130,6 +132,8 @@ export interface EvaluatorInput {
130
132
  allowCrossProduct: boolean;
131
133
  /** 可用商品数量上限 (仅多商品) 默认为0,表示不限制商品数量。 */
132
134
  applicableProductLimit: number;
135
+ /** 单商品可用卡券上限(同一 Wallet Pass 商品生成的卡券对同一商品行最多抵扣次数)。默认为 0,表示不限制。 */
136
+ maxPassesPerItem: number;
133
137
  }>[];
134
138
  }
135
139
  /**
@@ -90,9 +90,27 @@ function processVouchers(applicableVouchers, orderTotalAmount, products) {
90
90
  console.log(products, "products123");
91
91
  const productsCopy = expandProductsWithBundleItems(products, true);
92
92
  let remainingOrderAmount = new import_decimal.default(orderTotalAmount);
93
- const calculateAvailableMaxAmount = (voucher, productsData) => {
93
+ const getItemPassUsage = (usageMap, walletPassProductId, orderItemProductId) => {
94
+ var _a;
95
+ return ((_a = usageMap.get(walletPassProductId)) == null ? void 0 : _a.get(orderItemProductId)) || 0;
96
+ };
97
+ const incrementItemPassUsage = (usageMap, walletPassProductId, orderItemProductId) => {
98
+ if (!usageMap.has(walletPassProductId)) {
99
+ usageMap.set(walletPassProductId, /* @__PURE__ */ new Map());
100
+ }
101
+ const innerMap = usageMap.get(walletPassProductId);
102
+ innerMap.set(orderItemProductId, (innerMap.get(orderItemProductId) || 0) + 1);
103
+ };
104
+ const filterByMaxPassesPerItem = (products2, usageMap, walletPassProductId, maxPassesPerItem) => {
105
+ if (maxPassesPerItem <= 0)
106
+ return products2;
107
+ return products2.filter(
108
+ (p) => getItemPassUsage(usageMap, walletPassProductId, p.product_id) < maxPassesPerItem
109
+ );
110
+ };
111
+ const calculateAvailableMaxAmount = (voucher, productsData, itemPassUsage) => {
94
112
  const { config } = voucher;
95
- const { maxDeductionAmount = 0, allowCrossProduct = true, applicableProductLimit = 0, deductTaxAndFee = true } = config ?? {};
113
+ const { maxDeductionAmount = 0, allowCrossProduct = true, applicableProductLimit = 0, deductTaxAndFee = true, maxPassesPerItem = 0 } = config ?? {};
96
114
  const recommendedAmount = getRecommendedAmount(voucher);
97
115
  const baseAmount = import_decimal.default.min(
98
116
  new import_decimal.default(recommendedAmount),
@@ -100,7 +118,10 @@ function processVouchers(applicableVouchers, orderTotalAmount, products) {
100
118
  );
101
119
  const unitPriceField = deductTaxAndFee ? "unitPriceWithTax" : "unitPricePure";
102
120
  const amountField = deductTaxAndFee ? "remainingAmountWithTax" : "remainingAmountPure";
103
- const applicableProducts = getApplicableProducts(voucher, productsData).filter((p) => p[amountField].greaterThan(0));
121
+ let applicableProducts = getApplicableProducts(voucher, productsData).filter((p) => p[amountField].greaterThan(0));
122
+ if (itemPassUsage) {
123
+ applicableProducts = filterByMaxPassesPerItem(applicableProducts, itemPassUsage, voucher.product_id, maxPassesPerItem);
124
+ }
104
125
  if (applicableProducts.length === 0) {
105
126
  return new import_decimal.default(0);
106
127
  }
@@ -140,7 +161,7 @@ function processVouchers(applicableVouchers, orderTotalAmount, products) {
140
161
  }
141
162
  return import_decimal.default.min(baseAmount, finalApplicableAmount, remainingOrderAmount);
142
163
  };
143
- const isVoucherAvailable = (voucher, productsData, usedVoucherCounts2) => {
164
+ const isVoucherAvailable = (voucher, productsData, usedVoucherCounts2, itemPassUsage) => {
144
165
  const { config, id, product_id } = voucher;
145
166
  const recommendedAmount = getRecommendedAmount(voucher);
146
167
  if (recommendedAmount <= 0) {
@@ -159,6 +180,15 @@ function processVouchers(applicableVouchers, orderTotalAmount, products) {
159
180
  return { isAvailable: false, reasonCode: "usage_limit_reached" };
160
181
  }
161
182
  }
183
+ const maxPassesPerItem = (config == null ? void 0 : config.maxPassesPerItem) || 0;
184
+ if (maxPassesPerItem > 0 && itemPassUsage) {
185
+ const deductTaxAndFee = (config == null ? void 0 : config.deductTaxAndFee) ?? true;
186
+ const amountField = deductTaxAndFee ? "remainingAmountWithTax" : "remainingAmountPure";
187
+ const availableAfterPassLimit = getApplicableProducts(voucher, productsData).filter((p) => p[amountField].greaterThan(0)).filter((p) => getItemPassUsage(itemPassUsage, product_id, p.product_id) < maxPassesPerItem);
188
+ if (availableAfterPassLimit.length === 0) {
189
+ return { isAvailable: false, reasonCode: "max_passes_per_item_reached" };
190
+ }
191
+ }
162
192
  return { isAvailable: true };
163
193
  };
164
194
  const usedVoucherCountsForAll = /* @__PURE__ */ new Map();
@@ -185,23 +215,26 @@ function processVouchers(applicableVouchers, orderTotalAmount, products) {
185
215
  const usedVoucherCounts = /* @__PURE__ */ new Map();
186
216
  const productsForRecommendation = expandProductsWithBundleItems(products, true);
187
217
  remainingOrderAmount = new import_decimal.default(orderTotalAmount);
218
+ const itemPassUsageMap = /* @__PURE__ */ new Map();
188
219
  const applyVoucher = (voucher) => {
189
220
  const availabilityCheck = isVoucherAvailable(
190
221
  voucher,
191
222
  productsForRecommendation,
192
- usedVoucherCounts
223
+ usedVoucherCounts,
224
+ itemPassUsageMap
193
225
  );
194
226
  if (!availabilityCheck.isAvailable) {
195
227
  return false;
196
228
  }
197
229
  const { config, id, product_id } = voucher;
198
- const { maxDeductionAmount = 0, allowCrossProduct = true, applicableProductLimit = 0, deductTaxAndFee = true } = config ?? {};
230
+ const { maxDeductionAmount = 0, allowCrossProduct = true, applicableProductLimit = 0, deductTaxAndFee = true, maxPassesPerItem = 0 } = config ?? {};
199
231
  const unitPriceField = deductTaxAndFee ? "unitPriceWithTax" : "unitPricePure";
200
232
  const amountField = deductTaxAndFee ? "remainingAmountWithTax" : "remainingAmountPure";
201
233
  let applicableProducts = getApplicableProducts(
202
234
  voucher,
203
235
  productsForRecommendation
204
236
  ).filter((p) => p[amountField].greaterThan(0));
237
+ applicableProducts = filterByMaxPassesPerItem(applicableProducts, itemPassUsageMap, product_id, maxPassesPerItem);
205
238
  if (applicableProducts.length === 0)
206
239
  return false;
207
240
  const usageAmount = typeof voucher.edit_current_amount === "number" ? voucher.edit_current_amount : getRecommendedAmount(voucher);
@@ -311,6 +344,9 @@ function processVouchers(applicableVouchers, orderTotalAmount, products) {
311
344
  if (totalDeducted.greaterThan(0)) {
312
345
  remainingOrderAmount = remainingOrderAmount.minus(totalDeducted);
313
346
  usedVoucherCounts.set(product_id, (usedVoucherCounts.get(product_id) || 0) + 1);
347
+ deductionDetails.forEach((detail) => {
348
+ incrementItemPassUsage(itemPassUsageMap, product_id, detail.product_id);
349
+ });
314
350
  recommendedVouchers.push({
315
351
  ...voucher,
316
352
  actualDeduction: totalDeducted.toNumber(),
@@ -352,15 +388,35 @@ function recalculateVouchers(allVouchers, selectedVouchers, orderTotalAmount, pr
352
388
  const productsForCalc = expandProductsWithBundleItems(products, true);
353
389
  let remainingOrderAmount = new import_decimal.default(orderTotalAmount);
354
390
  const selectedWithDetails = [];
391
+ const itemPassUsageMap = /* @__PURE__ */ new Map();
392
+ const getItemPassUsage = (walletPassProductId, orderItemProductId) => {
393
+ var _a;
394
+ return ((_a = itemPassUsageMap.get(walletPassProductId)) == null ? void 0 : _a.get(orderItemProductId)) || 0;
395
+ };
396
+ const incrementItemPassUsage = (walletPassProductId, orderItemProductId) => {
397
+ if (!itemPassUsageMap.has(walletPassProductId)) {
398
+ itemPassUsageMap.set(walletPassProductId, /* @__PURE__ */ new Map());
399
+ }
400
+ const innerMap = itemPassUsageMap.get(walletPassProductId);
401
+ innerMap.set(orderItemProductId, (innerMap.get(orderItemProductId) || 0) + 1);
402
+ };
403
+ const filterByMaxPassesPerItem = (products2, walletPassProductId, maxPassesPerItem) => {
404
+ if (maxPassesPerItem <= 0)
405
+ return products2;
406
+ return products2.filter(
407
+ (p) => getItemPassUsage(walletPassProductId, p.product_id) < maxPassesPerItem
408
+ );
409
+ };
355
410
  selectedVouchers.forEach((selectedVoucher) => {
356
411
  const { config, id } = selectedVoucher;
357
- const { maxDeductionAmount, allowCrossProduct, applicableProductLimit, deductTaxAndFee = true } = config;
412
+ const { maxDeductionAmount, allowCrossProduct, applicableProductLimit, deductTaxAndFee = true, maxPassesPerItem = 0 } = config;
358
413
  const unitPriceField = deductTaxAndFee ? "unitPriceWithTax" : "unitPricePure";
359
414
  const amountField = deductTaxAndFee ? "remainingAmountWithTax" : "remainingAmountPure";
360
415
  let applicableProducts = getApplicableProducts(
361
416
  selectedVoucher,
362
417
  productsForCalc
363
418
  ).filter((p) => p[amountField].greaterThan(0));
419
+ applicableProducts = filterByMaxPassesPerItem(applicableProducts, selectedVoucher.product_id, maxPassesPerItem);
364
420
  if (applicableProducts.length === 0) {
365
421
  selectedWithDetails.push({
366
422
  ...selectedVoucher,
@@ -435,6 +491,9 @@ function recalculateVouchers(allVouchers, selectedVouchers, orderTotalAmount, pr
435
491
  }
436
492
  const totalDeducted = maxDeduction.minus(deductionLeft);
437
493
  remainingOrderAmount = remainingOrderAmount.minus(totalDeducted);
494
+ deductionDetails.forEach((detail) => {
495
+ incrementItemPassUsage(selectedVoucher.product_id, detail.product_id);
496
+ });
438
497
  selectedWithDetails.push({
439
498
  ...selectedVoucher,
440
499
  actualDeduction: totalDeducted.toNumber(),
@@ -458,7 +517,7 @@ function recalculateVouchers(allVouchers, selectedVouchers, orderTotalAmount, pr
458
517
  return selectedDetail || voucher;
459
518
  }
460
519
  const { config, id, product_id } = voucher;
461
- const { maxDeductionAmount, allowCrossProduct, applicableProductLimit, deductTaxAndFee = true } = config;
520
+ const { maxDeductionAmount, allowCrossProduct, applicableProductLimit, deductTaxAndFee = true, maxPassesPerItem = 0 } = config;
462
521
  const unitPriceField = deductTaxAndFee ? "unitPriceWithTax" : "unitPricePure";
463
522
  const amountField = deductTaxAndFee ? "remainingAmountWithTax" : "remainingAmountPure";
464
523
  const recommendedAmount = getRecommendedAmount(voucher);
@@ -484,6 +543,7 @@ function recalculateVouchers(allVouchers, selectedVouchers, orderTotalAmount, pr
484
543
  voucher,
485
544
  productsForCalc
486
545
  ).filter((p) => p[amountField].greaterThan(0));
546
+ applicableProducts = filterByMaxPassesPerItem(applicableProducts, product_id, maxPassesPerItem);
487
547
  if (applicableProducts.length === 0) {
488
548
  isAvailable = false;
489
549
  reasonCode = "not_meet_the_required_conditions";
@@ -836,12 +836,12 @@ var RulesModule = class extends import_BaseModule.BaseModule {
836
836
  this.hooks.setProduct(originProduct, {
837
837
  ...isManualDiscount ? {
838
838
  price: product.price,
839
- main_product_selling_price: product.price
839
+ main_product_selling_price: product.main_product_selling_price ?? product.price
840
840
  } : {
841
841
  _id: product._id.split("___")[0] + "___" + index,
842
842
  total: product.origin_total || product.total,
843
843
  price: product.price,
844
- main_product_selling_price: product.price
844
+ main_product_selling_price: product.main_product_selling_price ?? product.price
845
845
  },
846
846
  discount_list: []
847
847
  })
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "private": false,
3
3
  "name": "@pisell/pisellos",
4
- "version": "1.0.134",
4
+ "version": "1.0.136",
5
5
  "description": "一个可扩展的前端模块化SDK框架,支持插件系统",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",