@pisell/pisellos 2.1.107 → 2.1.108

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.
@@ -41,7 +41,7 @@ export interface Voucher {
41
41
  allowCrossProduct: boolean;
42
42
  /** 可用商品数量上限 (仅多商品) 默认为0,表示不限制商品数量。 */
43
43
  applicableProductLimit: number;
44
- /** 单商品可用卡券上限(同一 Wallet Pass 商品生成的卡券对同一商品的每个 unit 最多抵扣次数,总上限 = maxPassesPerItem × quantity)。默认为 0,表示不限制。 */
44
+ /** 单订单行每单位可用同一 Wallet Pass 券次数;该行总券次上限 = maxPassesPerItem × 该行 quantity(按行唯一键区分)。0 表示不限制。 */
45
45
  maxPassesPerItem: number;
46
46
  };
47
47
  }
@@ -50,6 +50,10 @@ export interface Voucher {
50
50
  */
51
51
  export interface Product {
52
52
  product_id: number;
53
+ /** 订单明细 id,参与行唯一键兜底解析 */
54
+ id?: number;
55
+ /** 行级唯一串,参与行唯一键解析 */
56
+ product_unique_string?: string;
53
57
  price: number;
54
58
  quantity: number;
55
59
  name?: string;
@@ -61,6 +65,9 @@ export interface Product {
61
65
  /** 主商品税费 */
62
66
  tax_fee: number;
63
67
  metadata: {
68
+ /** 行唯一标识,优先用于 Wallet Pass 按行配额 */
69
+ product_unique?: string;
70
+ unique_identification_number?: string;
64
71
  main_product_attached_bundle_tax_fee?: number;
65
72
  main_product_attached_bundle_surcharge_fee?: number;
66
73
  surcharge_rounding_remainder?: number;
@@ -129,7 +136,7 @@ export interface EvaluatorInput {
129
136
  allowCrossProduct: boolean;
130
137
  /** 可用商品数量上限 (仅多商品) 默认为0,表示不限制商品数量。 */
131
138
  applicableProductLimit: number;
132
- /** 单商品可用卡券上限(同一 Wallet Pass 商品生成的卡券对同一商品的每个 unit 最多抵扣次数,总上限 = maxPassesPerItem × quantity)。默认为 0,表示不限制。 */
139
+ /** 单订单行每单位可用同一 Wallet Pass 券次数;行总上限 = maxPassesPerItem × 该行 quantity(按行唯一键计)。0 表示不限制。 */
133
140
  maxPassesPerItem: number;
134
141
  }>[];
135
142
  }
@@ -1,4 +1,10 @@
1
1
  import { Product, Voucher } from './type';
2
+ /** 订单商品数量 */
3
+ export declare const getProductQuantity: (product: any) => any;
4
+ /**
5
+ * 订单商品行唯一键,用于 maxPassesPerItem 按行、按件配额(同 SPU 不同规格视为不同行)。
6
+ */
7
+ export declare function resolveWalletPassLineKey(product: any, indexInOrder: number): string;
2
8
  export declare const getApplicableProductIds: (voucher: Voucher) => number[] | null;
3
9
  /**
4
10
  * 优惠券处理函数
@@ -24,26 +30,20 @@ export declare function recalculateVouchers(allVouchers: any[], selectedVouchers
24
30
  selectedWithDetails: any[];
25
31
  };
26
32
  /**
27
- * 获取主商品价格
33
+ * 获取主商品价格(单价,不含舍入余数)
28
34
  * @param product 商品
29
35
  * @param isDeductTaxAndFee 是否抵扣税费与附加费
30
- * @returns 商品价格
36
+ * @returns 商品单价
31
37
  */
32
38
  export declare const getMainProductPrice: (product: Product, isDeductTaxAndFee: boolean) => number;
33
39
  /**
34
- * 获取套餐子商品价格
40
+ * 获取套餐子商品价格(不含舍入余数)
35
41
  * @param bundleItem 套餐子商品
36
42
  * @param parentQuantity 父商品数量
37
43
  * @param isDeductTaxAndFee 是否抵扣税费与附加费
38
- * @returns 子商品总价格
44
+ * @returns 子商品总价格(不含舍入余数)
39
45
  */
40
46
  export declare const getBundleItemPrice: (bundleItem: any, parentQuantity: number, isDeductTaxAndFee: boolean) => number;
41
- /**
42
- * 获取商品数量
43
- * @param product 商品
44
- * @returns 商品数量
45
- */
46
- export declare const getProductQuantity: (product: any) => any;
47
47
  export declare const getBundleItemIsOriginalPrice: (item: any) => boolean;
48
48
  export declare const getBundleItemIsMarkupPrice: (item: any) => boolean;
49
49
  export declare const getBundleItemIsDiscountPrice: (item: any) => boolean;
@@ -12,6 +12,63 @@ function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symb
12
12
  function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
13
13
  function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
14
14
  import Decimal from 'decimal.js';
15
+ /** 订单商品数量 */
16
+ export var getProductQuantity = function getProductQuantity(product) {
17
+ return product.quantity || product.product_quantity || 1;
18
+ };
19
+
20
+ /**
21
+ * 订单商品行唯一键,用于 maxPassesPerItem 按行、按件配额(同 SPU 不同规格视为不同行)。
22
+ */
23
+ export function resolveWalletPassLineKey(product, indexInOrder) {
24
+ var m = (product === null || product === void 0 ? void 0 : product.metadata) || {};
25
+ var u1 = m.product_unique;
26
+ if (u1 != null && String(u1).length > 0) return String(u1);
27
+ var u2 = m.unique_identification_number;
28
+ if (u2 != null && String(u2).length > 0) return String(u2);
29
+ if ((product === null || product === void 0 ? void 0 : product.id) != null && String(product.id).length > 0) return "order_item:".concat(product.id);
30
+ if ((product === null || product === void 0 ? void 0 : product.product_unique_string) != null && String(product.product_unique_string).length > 0) {
31
+ return String(product.product_unique_string);
32
+ }
33
+ return "order_line_".concat(indexInOrder);
34
+ }
35
+ function getExpandedOrderLineQuantity(p) {
36
+ if ((p === null || p === void 0 ? void 0 : p._orderLineQuantity) != null && p._orderLineQuantity > 0) return p._orderLineQuantity;
37
+ return getProductQuantity(p);
38
+ }
39
+
40
+ /** 该行允许消耗的「券次」上限:maxPassesPerItem × 订单行件数 */
41
+ function getMaxPassSlotsForExpandedLine(maxPassesPerItem, p) {
42
+ if (maxPassesPerItem <= 0) return Infinity;
43
+ return maxPassesPerItem * getExpandedOrderLineQuantity(p);
44
+ }
45
+
46
+ /**
47
+ * 本次抵扣占用多少「券次」配额:有抵扣时至少占 1,并与按金额折算的件数挂钩,不超过剩余额度。
48
+ */
49
+ function computePassSlotsIncrement(deductAmount, deductQty, maxPassesPerItem, orderLineQuantity, currentUsage) {
50
+ if (maxPassesPerItem <= 0 || !deductAmount.greaterThan(0)) return 0;
51
+ var cap = maxPassesPerItem * orderLineQuantity;
52
+ var room = Math.max(0, cap - currentUsage);
53
+ if (room <= 0) return 0;
54
+ var raw = Math.max(1, Math.ceil(Number(deductQty)) || 1);
55
+ return Math.min(raw, room);
56
+ }
57
+ function applyMaxPassUsageIncrements(usageMap, walletPassProductId, maxPassesPerItem, deductionDetails) {
58
+ deductionDetails.forEach(function (detail) {
59
+ var _usageMap$get;
60
+ var lineKey = detail.lineKey;
61
+ if (!lineKey || maxPassesPerItem <= 0) return;
62
+ var orderLineQty = detail.orderLineQuantity != null && detail.orderLineQuantity > 0 ? detail.orderLineQuantity : 1;
63
+ var cur = ((_usageMap$get = usageMap.get(walletPassProductId)) === null || _usageMap$get === void 0 ? void 0 : _usageMap$get.get(lineKey)) || 0;
64
+ var inc = computePassSlotsIncrement(new Decimal(detail.deductAmount), detail.deductQuantity, maxPassesPerItem, orderLineQty, cur);
65
+ if (inc <= 0) return;
66
+ if (!usageMap.has(walletPassProductId)) usageMap.set(walletPassProductId, new Map());
67
+ var inner = usageMap.get(walletPassProductId);
68
+ inner.set(lineKey, cur + inc);
69
+ });
70
+ }
71
+
15
72
  // 辅助函数:根据 deductTaxAndFee 配置获取推荐使用金额
16
73
  var getRecommendedAmount = function getRecommendedAmount(voucher) {
17
74
  var _config$deductTaxAndF;
@@ -101,18 +158,6 @@ var getApplicableProducts = function getApplicableProducts(voucher, productsData
101
158
  });
102
159
  };
103
160
 
104
- /**
105
- * 计算指定 product_id 在展开后的商品列表中的总 quantity
106
- * 处理同一 product_id 可能分散在多条记录中的情况(如3个独立的 quantity=1 条目)
107
- */
108
- var getTotalQuantityByProductId = function getTotalQuantityByProductId(allProducts, productId) {
109
- return allProducts.filter(function (p) {
110
- return p.product_id === productId;
111
- }).reduce(function (sum, p) {
112
- return sum + getProductQuantity(p);
113
- }, 0);
114
- };
115
-
116
161
  /**
117
162
  * 优惠券处理函数
118
163
  * @param applicableVouchers 可用的券列表
@@ -130,29 +175,19 @@ export function processVouchers(applicableVouchers, orderTotalAmount, products)
130
175
  var productsCopy = expandProductsWithBundleItems(products, true);
131
176
  var remainingOrderAmount = new Decimal(orderTotalAmount); // 订单剩余应付金额
132
177
 
133
- // ========== 辅助工具:maxPassesPerItem(单商品可用卡券上限)追踪 ==========
134
- // 获取指定 Wallet Pass 商品对指定订单商品行的已用卡券次数
135
- var getItemPassUsage = function getItemPassUsage(usageMap, walletPassProductId, orderItemProductId) {
136
- var _usageMap$get;
137
- return ((_usageMap$get = usageMap.get(walletPassProductId)) === null || _usageMap$get === void 0 ? void 0 : _usageMap$get.get(orderItemProductId)) || 0;
178
+ // ========== 辅助工具:maxPassesPerItem(按订单行 + 件数)追踪 ==========
179
+ var getItemPassUsage = function getItemPassUsage(usageMap, walletPassProductId, lineKey) {
180
+ var _usageMap$get2;
181
+ return ((_usageMap$get2 = usageMap.get(walletPassProductId)) === null || _usageMap$get2 === void 0 ? void 0 : _usageMap$get2.get(lineKey)) || 0;
138
182
  };
139
183
 
140
- // 更新指定 Wallet Pass 商品对指定订单商品行的已用卡券次数
141
- var incrementItemPassUsage = function incrementItemPassUsage(usageMap, walletPassProductId, orderItemProductId) {
142
- var count = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 1;
143
- if (!usageMap.has(walletPassProductId)) {
144
- usageMap.set(walletPassProductId, new Map());
145
- }
146
- var innerMap = usageMap.get(walletPassProductId);
147
- innerMap.set(orderItemProductId, (innerMap.get(orderItemProductId) || 0) + count);
148
- };
149
-
150
- // 按 maxPassesPerItem 过滤商品:排除已达到单商品可用卡券上限的商品
151
- // maxPassesPerItem 是 per-unit 的限制,总上限 = maxPassesPerItem × 该 product_id 的总 quantity
152
- var filterByMaxPassesPerItem = function filterByMaxPassesPerItem(products, allProducts, usageMap, walletPassProductId, maxPassesPerItem) {
153
- if (maxPassesPerItem <= 0) return products; // 0 = 不限制
184
+ // maxPassesPerItem 过滤:排除已达到该行「券次」上限的展开行(按 _walletPassLineKey)
185
+ var filterByMaxPassesPerItem = function filterByMaxPassesPerItem(products, usageMap, walletPassProductId, maxPassesPerItem) {
186
+ if (maxPassesPerItem <= 0) return products;
154
187
  return products.filter(function (p) {
155
- return getItemPassUsage(usageMap, walletPassProductId, p.product_id) < maxPassesPerItem * getTotalQuantityByProductId(allProducts, p.product_id);
188
+ var lineKey = p._walletPassLineKey;
189
+ if (!lineKey) return true;
190
+ return getItemPassUsage(usageMap, walletPassProductId, lineKey) < getMaxPassSlotsForExpandedLine(maxPassesPerItem, p);
156
191
  });
157
192
  };
158
193
  // ================================================================
@@ -189,7 +224,7 @@ export function processVouchers(applicableVouchers, orderTotalAmount, products)
189
224
 
190
225
  // 按 maxPassesPerItem 过滤:排除已达到单商品可用卡券上限的商品
191
226
  if (itemPassUsage) {
192
- applicableProducts = filterByMaxPassesPerItem(applicableProducts, productsData, itemPassUsage, voucher.product_id, maxPassesPerItem);
227
+ applicableProducts = filterByMaxPassesPerItem(applicableProducts, itemPassUsage, voucher.product_id, maxPassesPerItem);
193
228
  }
194
229
  if (applicableProducts.length === 0) {
195
230
  return new Decimal(0);
@@ -301,7 +336,9 @@ export function processVouchers(applicableVouchers, orderTotalAmount, products)
301
336
  var availableAfterPassLimit = getApplicableProducts(voucher, productsData).filter(function (p) {
302
337
  return p[amountField].greaterThan(0);
303
338
  }).filter(function (p) {
304
- return getItemPassUsage(itemPassUsage, product_id, p.product_id) < maxPassesPerItem * getTotalQuantityByProductId(productsData, p.product_id);
339
+ var lineKey = p._walletPassLineKey;
340
+ if (!lineKey) return true;
341
+ return getItemPassUsage(itemPassUsage, product_id, lineKey) < getMaxPassSlotsForExpandedLine(maxPassesPerItem, p);
305
342
  });
306
343
  if (availableAfterPassLimit.length === 0) {
307
344
  return {
@@ -337,7 +374,7 @@ export function processVouchers(applicableVouchers, orderTotalAmount, products)
337
374
  // 重置商品余额追踪(同时维护含税和不含税两个金额池)
338
375
  var productsForRecommendation = expandProductsWithBundleItems(products, true);
339
376
  remainingOrderAmount = new Decimal(orderTotalAmount);
340
- // 追踪推荐阶段每个 Wallet Pass 商品对每个订单商品行的已使用卡券次数
377
+ // 追踪推荐阶段每个 Wallet Pass 商品对每个订单商品行的已使用卡券次数(按行唯一键)
341
378
  var itemPassUsageMap = new Map();
342
379
 
343
380
  /**
@@ -398,7 +435,7 @@ export function processVouchers(applicableVouchers, orderTotalAmount, products)
398
435
  });
399
436
 
400
437
  // 按 maxPassesPerItem 过滤:排除已达到单商品可用卡券上限的商品
401
- applicableProducts = filterByMaxPassesPerItem(applicableProducts, productsForRecommendation, itemPassUsageMap, product_id, maxPassesPerItem);
438
+ applicableProducts = filterByMaxPassesPerItem(applicableProducts, itemPassUsageMap, product_id, maxPassesPerItem);
402
439
  if (applicableProducts.length === 0) return false;
403
440
 
404
441
  // ========== 关键修改:在应用券之前,基于当前剩余金额计算 _available_max_amount ==========
@@ -492,6 +529,8 @@ export function processVouchers(applicableVouchers, orderTotalAmount, products)
492
529
  product_id: _product.product_id,
493
530
  parent_product_id: _product.parent_product_id || null,
494
531
  is_bundle_item: _product.is_bundle_item || false,
532
+ lineKey: _product._walletPassLineKey,
533
+ orderLineQuantity: getExpandedOrderLineQuantity(_product),
495
534
  deductAmount: actualDeductAmount.toNumber(),
496
535
  // 转换为数字
497
536
  deductQuantity: actualDeductQty // 抵扣涉及的数量(用于记录)
@@ -525,6 +564,8 @@ export function processVouchers(applicableVouchers, orderTotalAmount, products)
525
564
  product_id: targetProduct.product_id,
526
565
  parent_product_id: targetProduct.parent_product_id || null,
527
566
  is_bundle_item: targetProduct.is_bundle_item || false,
567
+ lineKey: targetProduct._walletPassLineKey,
568
+ orderLineQuantity: getExpandedOrderLineQuantity(targetProduct),
528
569
  deductAmount: _actualDeductAmount.toNumber(),
529
570
  deductQuantity: _actualDeductQty
530
571
  });
@@ -536,11 +577,7 @@ export function processVouchers(applicableVouchers, orderTotalAmount, products)
536
577
 
537
578
  // 更新券使用次数(按 product_id 统计)
538
579
  usedVoucherCounts.set(product_id, (usedVoucherCounts.get(product_id) || 0) + 1);
539
-
540
- // 更新 maxPassesPerItem 追踪:按实际抵扣数量递增
541
- deductionDetails.forEach(function (detail) {
542
- incrementItemPassUsage(itemPassUsageMap, product_id, detail.product_id, detail.deductQuantity);
543
- });
580
+ applyMaxPassUsageIncrements(itemPassUsageMap, product_id, maxPassesPerItem, deductionDetails);
544
581
 
545
582
  // 添加到推荐列表(包含基于当前剩余金额计算的 available_max_amount)
546
583
  recommendedVouchers.push(_objectSpread(_objectSpread({}, voucher), {}, {
@@ -623,25 +660,18 @@ export function recalculateVouchers(allVouchers, selectedVouchers, orderTotalAmo
623
660
  var remainingOrderAmount = new Decimal(orderTotalAmount);
624
661
  var selectedWithDetails = [];
625
662
 
626
- // 追踪每个 Wallet Pass 商品对每个订单商品行的已使用卡券次数
627
- // Map<walletPassProductId, Map<orderItemProductId, usedCount>>
663
+ // 追踪每个 Wallet Pass 商品对每个订单商品行的已使用卡券次数(按行唯一键)
628
664
  var itemPassUsageMap = new Map();
629
- var getItemPassUsage = function getItemPassUsage(walletPassProductId, orderItemProductId) {
665
+ var getItemPassUsageRecalc = function getItemPassUsageRecalc(walletPassProductId, lineKey) {
630
666
  var _itemPassUsageMap$get;
631
- return ((_itemPassUsageMap$get = itemPassUsageMap.get(walletPassProductId)) === null || _itemPassUsageMap$get === void 0 ? void 0 : _itemPassUsageMap$get.get(orderItemProductId)) || 0;
632
- };
633
- var incrementItemPassUsage = function incrementItemPassUsage(walletPassProductId, orderItemProductId) {
634
- var count = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1;
635
- if (!itemPassUsageMap.has(walletPassProductId)) {
636
- itemPassUsageMap.set(walletPassProductId, new Map());
637
- }
638
- var innerMap = itemPassUsageMap.get(walletPassProductId);
639
- innerMap.set(orderItemProductId, (innerMap.get(orderItemProductId) || 0) + count);
667
+ return ((_itemPassUsageMap$get = itemPassUsageMap.get(walletPassProductId)) === null || _itemPassUsageMap$get === void 0 ? void 0 : _itemPassUsageMap$get.get(lineKey)) || 0;
640
668
  };
641
- var filterByMaxPassesPerItem = function filterByMaxPassesPerItem(products, walletPassProductId, maxPassesPerItem) {
642
- if (maxPassesPerItem <= 0) return products; // 0 = 不限制
669
+ var filterByMaxPassesPerItemRecalc = function filterByMaxPassesPerItemRecalc(products, walletPassProductId, maxPassesPerItem) {
670
+ if (maxPassesPerItem <= 0) return products;
643
671
  return products.filter(function (p) {
644
- return getItemPassUsage(walletPassProductId, p.product_id) < maxPassesPerItem * getTotalQuantityByProductId(productsForCalc, p.product_id);
672
+ var lineKey = p._walletPassLineKey;
673
+ if (!lineKey) return true;
674
+ return getItemPassUsageRecalc(walletPassProductId, lineKey) < getMaxPassSlotsForExpandedLine(maxPassesPerItem, p);
645
675
  });
646
676
  };
647
677
 
@@ -666,8 +696,8 @@ export function recalculateVouchers(allVouchers, selectedVouchers, orderTotalAmo
666
696
  return p[amountField].greaterThan(0);
667
697
  });
668
698
 
669
- // 按 maxPassesPerItem 过滤:排除已达到单商品可用卡券上限的商品
670
- applicableProducts = filterByMaxPassesPerItem(applicableProducts, selectedVoucher.product_id, maxPassesPerItem);
699
+ // 按 maxPassesPerItem 过滤:排除已达到该行券次上限的展开行
700
+ applicableProducts = filterByMaxPassesPerItemRecalc(applicableProducts, selectedVoucher.product_id, maxPassesPerItem);
671
701
  if (applicableProducts.length === 0) {
672
702
  // 无适用商品,跳过
673
703
  selectedWithDetails.push(_objectSpread(_objectSpread({}, selectedVoucher), {}, {
@@ -718,6 +748,8 @@ export function recalculateVouchers(allVouchers, selectedVouchers, orderTotalAmo
718
748
  product_id: product.product_id,
719
749
  parent_product_id: product.parent_product_id || null,
720
750
  is_bundle_item: product.is_bundle_item || false,
751
+ lineKey: product._walletPassLineKey,
752
+ orderLineQuantity: getExpandedOrderLineQuantity(product),
721
753
  deductAmount: actualDeductAmount.toNumber(),
722
754
  // 转换为数字
723
755
  deductQuantity: actualDeductQty // 抵扣涉及的数量(用于记录)
@@ -751,6 +783,8 @@ export function recalculateVouchers(allVouchers, selectedVouchers, orderTotalAmo
751
783
  product_id: targetProduct.product_id,
752
784
  parent_product_id: targetProduct.parent_product_id || null,
753
785
  is_bundle_item: targetProduct.is_bundle_item || false,
786
+ lineKey: targetProduct._walletPassLineKey,
787
+ orderLineQuantity: getExpandedOrderLineQuantity(targetProduct),
754
788
  deductAmount: _actualDeductAmount2.toNumber(),
755
789
  deductQuantity: _actualDeductQty2
756
790
  });
@@ -759,11 +793,7 @@ export function recalculateVouchers(allVouchers, selectedVouchers, orderTotalAmo
759
793
 
760
794
  // 更新订单剩余金额
761
795
  remainingOrderAmount = remainingOrderAmount.minus(totalDeducted);
762
-
763
- // 更新 maxPassesPerItem 追踪:按实际抵扣数量递增
764
- deductionDetails.forEach(function (detail) {
765
- incrementItemPassUsage(selectedVoucher.product_id, detail.product_id, detail.deductQuantity);
766
- });
796
+ applyMaxPassUsageIncrements(itemPassUsageMap, selectedVoucher.product_id, maxPassesPerItem, deductionDetails);
767
797
  selectedWithDetails.push(_objectSpread(_objectSpread({}, selectedVoucher), {}, {
768
798
  actualDeduction: totalDeducted.toNumber(),
769
799
  // 转换为数字
@@ -836,7 +866,7 @@ export function recalculateVouchers(allVouchers, selectedVouchers, orderTotalAmo
836
866
  });
837
867
 
838
868
  // 按 maxPassesPerItem 过滤:排除已达到单商品可用卡券上限的商品
839
- applicableProducts = filterByMaxPassesPerItem(applicableProducts, product_id, maxPassesPerItem);
869
+ applicableProducts = filterByMaxPassesPerItemRecalc(applicableProducts, product_id, maxPassesPerItem);
840
870
  if (applicableProducts.length === 0) {
841
871
  isAvailable = false;
842
872
  reasonCode = 'not_meet_the_required_conditions';
@@ -910,10 +940,10 @@ export function recalculateVouchers(allVouchers, selectedVouchers, orderTotalAmo
910
940
  }
911
941
 
912
942
  /**
913
- * 获取主商品价格
943
+ * 获取主商品价格(单价,不含舍入余数)
914
944
  * @param product 商品
915
945
  * @param isDeductTaxAndFee 是否抵扣税费与附加费
916
- * @returns 商品价格
946
+ * @returns 商品单价
917
947
  */
918
948
  export var getMainProductPrice = function getMainProductPrice(product, isDeductTaxAndFee) {
919
949
  var _product$metadata, _product$metadata2, _product$metadata3, _product$metadata4, _product$metadata5;
@@ -956,11 +986,11 @@ export var getMainProductPrice = function getMainProductPrice(product, isDeductT
956
986
  };
957
987
 
958
988
  /**
959
- * 获取套餐子商品价格
989
+ * 获取套餐子商品价格(不含舍入余数)
960
990
  * @param bundleItem 套餐子商品
961
991
  * @param parentQuantity 父商品数量
962
992
  * @param isDeductTaxAndFee 是否抵扣税费与附加费
963
- * @returns 子商品总价格
993
+ * @returns 子商品总价格(不含舍入余数)
964
994
  */
965
995
  export var getBundleItemPrice = function getBundleItemPrice(bundleItem, parentQuantity, isDeductTaxAndFee) {
966
996
  var _bundleItem$bundle_se2;
@@ -991,8 +1021,9 @@ export var getBundleItemPrice = function getBundleItemPrice(bundleItem, parentQu
991
1021
  */
992
1022
  var expandProductsWithBundleItems = function expandProductsWithBundleItems(products, deductTaxAndFee) {
993
1023
  var expandedProducts = [];
994
- products.forEach(function (product) {
1024
+ products.forEach(function (product, indexInOrder) {
995
1025
  var productQuantity = getProductQuantity(product);
1026
+ var parentLineKey = resolveWalletPassLineKey(product, indexInOrder);
996
1027
 
997
1028
  // 计算主商品单价(含税和不含税)
998
1029
  var unitPriceWithTax = getMainProductPrice(product, true);
@@ -1002,6 +1033,8 @@ var expandProductsWithBundleItems = function expandProductsWithBundleItems(produ
1002
1033
  expandedProducts.push(_objectSpread(_objectSpread({}, product), {}, {
1003
1034
  is_bundle_item: false,
1004
1035
  parent_product_id: null,
1036
+ _walletPassLineKey: parentLineKey,
1037
+ _orderLineQuantity: productQuantity,
1005
1038
  // 单价(用于按 quantity 计算抵扣)
1006
1039
  unitPriceWithTax: new Decimal(unitPriceWithTax),
1007
1040
  unitPricePure: new Decimal(unitPricePure),
@@ -1018,6 +1051,7 @@ var expandProductsWithBundleItems = function expandProductsWithBundleItems(produ
1018
1051
  product.product_bundle.forEach(function (bundleItem) {
1019
1052
  if (getBundleItemIsOriginalPrice(bundleItem)) {
1020
1053
  var bundleQuantity = bundleItem.num * productQuantity;
1054
+ var bundleLineKey = "".concat(parentLineKey, "#bundle:").concat(bundleItem.bundle_id, ":").concat(bundleItem.bundle_product_id);
1021
1055
  // 计算子商品单价(注意:getBundleItemPrice 返回的是总价,需要除以数量得到单价)
1022
1056
  var bundleUnitPriceWithTax = new Decimal(getBundleItemPrice(bundleItem, 1, true)).dividedBy(bundleItem.num);
1023
1057
  var bundleUnitPricePure = new Decimal(getBundleItemPrice(bundleItem, 1, false)).dividedBy(bundleItem.num);
@@ -1030,6 +1064,8 @@ var expandProductsWithBundleItems = function expandProductsWithBundleItems(produ
1030
1064
  parent_product_id: product.product_id,
1031
1065
  quantity: bundleQuantity,
1032
1066
  // 子商品数量 * 主商品数量
1067
+ _walletPassLineKey: bundleLineKey,
1068
+ _orderLineQuantity: bundleQuantity,
1033
1069
  // 单价(用于按 quantity 计算抵扣)
1034
1070
  unitPriceWithTax: bundleUnitPriceWithTax,
1035
1071
  unitPricePure: bundleUnitPricePure,
@@ -1047,15 +1083,6 @@ var expandProductsWithBundleItems = function expandProductsWithBundleItems(produ
1047
1083
  return expandedProducts;
1048
1084
  };
1049
1085
 
1050
- /**
1051
- * 获取商品数量
1052
- * @param product 商品
1053
- * @returns 商品数量
1054
- */
1055
- export var getProductQuantity = function getProductQuantity(product) {
1056
- return product.quantity || product.product_quantity || 1;
1057
- };
1058
-
1059
1086
  // bundle商品是否是原价
1060
1087
  export var getBundleItemIsOriginalPrice = function getBundleItemIsOriginalPrice(item) {
1061
1088
  return (item === null || item === void 0 ? void 0 : item.price_type) === 'markup' && (item === null || item === void 0 ? void 0 : item.price_type_ext) === 'product_price';
@@ -49,5 +49,5 @@ export declare class Product extends BaseModule implements Module {
49
49
  getCategories(): ProductCategory[];
50
50
  setOtherParams(key: string, value: any): void;
51
51
  getOtherParams(): any;
52
- getProductType(): "normal" | "duration" | "session";
52
+ getProductType(): "duration" | "session" | "normal";
53
53
  }
@@ -311,7 +311,7 @@ export declare class BookingByStepImpl extends BaseModule implements Module {
311
311
  date: string;
312
312
  status: string;
313
313
  week: string;
314
- weekNum: 0 | 2 | 1 | 3 | 4 | 5 | 6;
314
+ weekNum: 0 | 1 | 2 | 3 | 4 | 5 | 6;
315
315
  }[]>;
316
316
  submitTimeSlot(timeSlots: TimeSliceItem): void;
317
317
  private getScheduleDataByIds;
@@ -358,7 +358,7 @@ export declare class BookingByStepImpl extends BaseModule implements Module {
358
358
  };
359
359
  setOtherData(key: string, value: any): void;
360
360
  getOtherData(key: string): any;
361
- getProductTypeById(id: number): Promise<"normal" | "duration" | "session">;
361
+ getProductTypeById(id: number): Promise<"duration" | "session" | "normal">;
362
362
  /**
363
363
  * 提供给 UI 的方法,减轻 UI 层的计算压力,UI 层只需要传递 cartItemId 和 resourceCode 即返回对应的 renderList
364
364
  *
@@ -115,7 +115,7 @@ export declare class BookingTicketImpl extends BaseModule implements Module {
115
115
  * 获取当前的客户搜索条件
116
116
  * @returns 当前搜索条件
117
117
  */
118
- getCurrentCustomerSearchParams(): Omit<import("../../modules").ShopGetCustomerListParams, "skip" | "num">;
118
+ getCurrentCustomerSearchParams(): Omit<import("../../modules").ShopGetCustomerListParams, "num" | "skip">;
119
119
  /**
120
120
  * 获取客户列表状态(包含滚动加载相关状态)
121
121
  * @returns 客户状态
@@ -41,7 +41,7 @@ export interface Voucher {
41
41
  allowCrossProduct: boolean;
42
42
  /** 可用商品数量上限 (仅多商品) 默认为0,表示不限制商品数量。 */
43
43
  applicableProductLimit: number;
44
- /** 单商品可用卡券上限(同一 Wallet Pass 商品生成的卡券对同一商品的每个 unit 最多抵扣次数,总上限 = maxPassesPerItem × quantity)。默认为 0,表示不限制。 */
44
+ /** 单订单行每单位可用同一 Wallet Pass 券次数;该行总券次上限 = maxPassesPerItem × 该行 quantity(按行唯一键区分)。0 表示不限制。 */
45
45
  maxPassesPerItem: number;
46
46
  };
47
47
  }
@@ -50,6 +50,10 @@ export interface Voucher {
50
50
  */
51
51
  export interface Product {
52
52
  product_id: number;
53
+ /** 订单明细 id,参与行唯一键兜底解析 */
54
+ id?: number;
55
+ /** 行级唯一串,参与行唯一键解析 */
56
+ product_unique_string?: string;
53
57
  price: number;
54
58
  quantity: number;
55
59
  name?: string;
@@ -61,6 +65,9 @@ export interface Product {
61
65
  /** 主商品税费 */
62
66
  tax_fee: number;
63
67
  metadata: {
68
+ /** 行唯一标识,优先用于 Wallet Pass 按行配额 */
69
+ product_unique?: string;
70
+ unique_identification_number?: string;
64
71
  main_product_attached_bundle_tax_fee?: number;
65
72
  main_product_attached_bundle_surcharge_fee?: number;
66
73
  surcharge_rounding_remainder?: number;
@@ -129,7 +136,7 @@ export interface EvaluatorInput {
129
136
  allowCrossProduct: boolean;
130
137
  /** 可用商品数量上限 (仅多商品) 默认为0,表示不限制商品数量。 */
131
138
  applicableProductLimit: number;
132
- /** 单商品可用卡券上限(同一 Wallet Pass 商品生成的卡券对同一商品的每个 unit 最多抵扣次数,总上限 = maxPassesPerItem × quantity)。默认为 0,表示不限制。 */
139
+ /** 单订单行每单位可用同一 Wallet Pass 券次数;行总上限 = maxPassesPerItem × 该行 quantity(按行唯一键计)。0 表示不限制。 */
133
140
  maxPassesPerItem: number;
134
141
  }>[];
135
142
  }
@@ -1,4 +1,10 @@
1
1
  import { Product, Voucher } from './type';
2
+ /** 订单商品数量 */
3
+ export declare const getProductQuantity: (product: any) => any;
4
+ /**
5
+ * 订单商品行唯一键,用于 maxPassesPerItem 按行、按件配额(同 SPU 不同规格视为不同行)。
6
+ */
7
+ export declare function resolveWalletPassLineKey(product: any, indexInOrder: number): string;
2
8
  export declare const getApplicableProductIds: (voucher: Voucher) => number[] | null;
3
9
  /**
4
10
  * 优惠券处理函数
@@ -24,26 +30,20 @@ export declare function recalculateVouchers(allVouchers: any[], selectedVouchers
24
30
  selectedWithDetails: any[];
25
31
  };
26
32
  /**
27
- * 获取主商品价格
33
+ * 获取主商品价格(单价,不含舍入余数)
28
34
  * @param product 商品
29
35
  * @param isDeductTaxAndFee 是否抵扣税费与附加费
30
- * @returns 商品价格
36
+ * @returns 商品单价
31
37
  */
32
38
  export declare const getMainProductPrice: (product: Product, isDeductTaxAndFee: boolean) => number;
33
39
  /**
34
- * 获取套餐子商品价格
40
+ * 获取套餐子商品价格(不含舍入余数)
35
41
  * @param bundleItem 套餐子商品
36
42
  * @param parentQuantity 父商品数量
37
43
  * @param isDeductTaxAndFee 是否抵扣税费与附加费
38
- * @returns 子商品总价格
44
+ * @returns 子商品总价格(不含舍入余数)
39
45
  */
40
46
  export declare const getBundleItemPrice: (bundleItem: any, parentQuantity: number, isDeductTaxAndFee: boolean) => number;
41
- /**
42
- * 获取商品数量
43
- * @param product 商品
44
- * @returns 商品数量
45
- */
46
- export declare const getProductQuantity: (product: any) => any;
47
47
  export declare const getBundleItemIsOriginalPrice: (item: any) => boolean;
48
48
  export declare const getBundleItemIsMarkupPrice: (item: any) => boolean;
49
49
  export declare const getBundleItemIsDiscountPrice: (item: any) => boolean;
@@ -38,10 +38,72 @@ __export(utils_exports, {
38
38
  getMainProductPrice: () => getMainProductPrice,
39
39
  getProductQuantity: () => getProductQuantity,
40
40
  processVouchers: () => processVouchers,
41
- recalculateVouchers: () => recalculateVouchers
41
+ recalculateVouchers: () => recalculateVouchers,
42
+ resolveWalletPassLineKey: () => resolveWalletPassLineKey
42
43
  });
43
44
  module.exports = __toCommonJS(utils_exports);
44
45
  var import_decimal = __toESM(require("decimal.js"));
46
+ var getProductQuantity = (product) => {
47
+ return product.quantity || product.product_quantity || 1;
48
+ };
49
+ function resolveWalletPassLineKey(product, indexInOrder) {
50
+ const m = (product == null ? void 0 : product.metadata) || {};
51
+ const u1 = m.product_unique;
52
+ if (u1 != null && String(u1).length > 0)
53
+ return String(u1);
54
+ const u2 = m.unique_identification_number;
55
+ if (u2 != null && String(u2).length > 0)
56
+ return String(u2);
57
+ if ((product == null ? void 0 : product.id) != null && String(product.id).length > 0)
58
+ return `order_item:${product.id}`;
59
+ if ((product == null ? void 0 : product.product_unique_string) != null && String(product.product_unique_string).length > 0) {
60
+ return String(product.product_unique_string);
61
+ }
62
+ return `order_line_${indexInOrder}`;
63
+ }
64
+ function getExpandedOrderLineQuantity(p) {
65
+ if ((p == null ? void 0 : p._orderLineQuantity) != null && p._orderLineQuantity > 0)
66
+ return p._orderLineQuantity;
67
+ return getProductQuantity(p);
68
+ }
69
+ function getMaxPassSlotsForExpandedLine(maxPassesPerItem, p) {
70
+ if (maxPassesPerItem <= 0)
71
+ return Infinity;
72
+ return maxPassesPerItem * getExpandedOrderLineQuantity(p);
73
+ }
74
+ function computePassSlotsIncrement(deductAmount, deductQty, maxPassesPerItem, orderLineQuantity, currentUsage) {
75
+ if (maxPassesPerItem <= 0 || !deductAmount.greaterThan(0))
76
+ return 0;
77
+ const cap = maxPassesPerItem * orderLineQuantity;
78
+ const room = Math.max(0, cap - currentUsage);
79
+ if (room <= 0)
80
+ return 0;
81
+ const raw = Math.max(1, Math.ceil(Number(deductQty)) || 1);
82
+ return Math.min(raw, room);
83
+ }
84
+ function applyMaxPassUsageIncrements(usageMap, walletPassProductId, maxPassesPerItem, deductionDetails) {
85
+ deductionDetails.forEach((detail) => {
86
+ var _a;
87
+ const lineKey = detail.lineKey;
88
+ if (!lineKey || maxPassesPerItem <= 0)
89
+ return;
90
+ const orderLineQty = detail.orderLineQuantity != null && detail.orderLineQuantity > 0 ? detail.orderLineQuantity : 1;
91
+ const cur = ((_a = usageMap.get(walletPassProductId)) == null ? void 0 : _a.get(lineKey)) || 0;
92
+ const inc = computePassSlotsIncrement(
93
+ new import_decimal.default(detail.deductAmount),
94
+ detail.deductQuantity,
95
+ maxPassesPerItem,
96
+ orderLineQty,
97
+ cur
98
+ );
99
+ if (inc <= 0)
100
+ return;
101
+ if (!usageMap.has(walletPassProductId))
102
+ usageMap.set(walletPassProductId, /* @__PURE__ */ new Map());
103
+ const inner = usageMap.get(walletPassProductId);
104
+ inner.set(lineKey, cur + inc);
105
+ });
106
+ }
45
107
  var getRecommendedAmount = (voucher) => {
46
108
  console.log("voucher312", voucher);
47
109
  const { config, recommended_usage_amount, recommended_pure_product_usage_amount } = voucher;
@@ -82,30 +144,23 @@ var getApplicableProducts = (voucher, productsData) => {
82
144
  }
83
145
  return productsData.filter((p) => applicableProductIds.includes(p.product_id));
84
146
  };
85
- var getTotalQuantityByProductId = (allProducts, productId) => {
86
- return allProducts.filter((p) => p.product_id === productId).reduce((sum, p) => sum + getProductQuantity(p), 0);
87
- };
88
147
  function processVouchers(applicableVouchers, orderTotalAmount, products) {
89
148
  console.log(products, "products123");
90
149
  const productsCopy = expandProductsWithBundleItems(products, true);
91
150
  let remainingOrderAmount = new import_decimal.default(orderTotalAmount);
92
- const getItemPassUsage = (usageMap, walletPassProductId, orderItemProductId) => {
151
+ const getItemPassUsage = (usageMap, walletPassProductId, lineKey) => {
93
152
  var _a;
94
- return ((_a = usageMap.get(walletPassProductId)) == null ? void 0 : _a.get(orderItemProductId)) || 0;
153
+ return ((_a = usageMap.get(walletPassProductId)) == null ? void 0 : _a.get(lineKey)) || 0;
95
154
  };
96
- const incrementItemPassUsage = (usageMap, walletPassProductId, orderItemProductId, count = 1) => {
97
- if (!usageMap.has(walletPassProductId)) {
98
- usageMap.set(walletPassProductId, /* @__PURE__ */ new Map());
99
- }
100
- const innerMap = usageMap.get(walletPassProductId);
101
- innerMap.set(orderItemProductId, (innerMap.get(orderItemProductId) || 0) + count);
102
- };
103
- const filterByMaxPassesPerItem = (products2, allProducts, usageMap, walletPassProductId, maxPassesPerItem) => {
155
+ const filterByMaxPassesPerItem = (products2, usageMap, walletPassProductId, maxPassesPerItem) => {
104
156
  if (maxPassesPerItem <= 0)
105
157
  return products2;
106
- return products2.filter(
107
- (p) => getItemPassUsage(usageMap, walletPassProductId, p.product_id) < maxPassesPerItem * getTotalQuantityByProductId(allProducts, p.product_id)
108
- );
158
+ return products2.filter((p) => {
159
+ const lineKey = p._walletPassLineKey;
160
+ if (!lineKey)
161
+ return true;
162
+ return getItemPassUsage(usageMap, walletPassProductId, lineKey) < getMaxPassSlotsForExpandedLine(maxPassesPerItem, p);
163
+ });
109
164
  };
110
165
  const calculateAvailableMaxAmount = (voucher, productsData, itemPassUsage) => {
111
166
  const { config } = voucher;
@@ -119,7 +174,7 @@ function processVouchers(applicableVouchers, orderTotalAmount, products) {
119
174
  const amountField = deductTaxAndFee ? "remainingAmountWithTax" : "remainingAmountPure";
120
175
  let applicableProducts = getApplicableProducts(voucher, productsData).filter((p) => p[amountField].greaterThan(0));
121
176
  if (itemPassUsage) {
122
- applicableProducts = filterByMaxPassesPerItem(applicableProducts, productsData, itemPassUsage, voucher.product_id, maxPassesPerItem);
177
+ applicableProducts = filterByMaxPassesPerItem(applicableProducts, itemPassUsage, voucher.product_id, maxPassesPerItem);
123
178
  }
124
179
  if (applicableProducts.length === 0) {
125
180
  return new import_decimal.default(0);
@@ -185,7 +240,12 @@ function processVouchers(applicableVouchers, orderTotalAmount, products) {
185
240
  if (maxPassesPerItem > 0 && itemPassUsage) {
186
241
  const deductTaxAndFee = (config == null ? void 0 : config.deductTaxAndFee) ?? true;
187
242
  const amountField = deductTaxAndFee ? "remainingAmountWithTax" : "remainingAmountPure";
188
- const availableAfterPassLimit = getApplicableProducts(voucher, productsData).filter((p) => p[amountField].greaterThan(0)).filter((p) => getItemPassUsage(itemPassUsage, product_id, p.product_id) < maxPassesPerItem * getTotalQuantityByProductId(productsData, p.product_id));
243
+ const availableAfterPassLimit = getApplicableProducts(voucher, productsData).filter((p) => p[amountField].greaterThan(0)).filter((p) => {
244
+ const lineKey = p._walletPassLineKey;
245
+ if (!lineKey)
246
+ return true;
247
+ return getItemPassUsage(itemPassUsage, product_id, lineKey) < getMaxPassSlotsForExpandedLine(maxPassesPerItem, p);
248
+ });
189
249
  if (availableAfterPassLimit.length === 0) {
190
250
  return { isAvailable: false, reasonCode: "max_passes_per_item_reached" };
191
251
  }
@@ -235,7 +295,7 @@ function processVouchers(applicableVouchers, orderTotalAmount, products) {
235
295
  voucher,
236
296
  productsForRecommendation
237
297
  ).filter((p) => p[amountField].greaterThan(0));
238
- applicableProducts = filterByMaxPassesPerItem(applicableProducts, productsForRecommendation, itemPassUsageMap, product_id, maxPassesPerItem);
298
+ applicableProducts = filterByMaxPassesPerItem(applicableProducts, itemPassUsageMap, product_id, maxPassesPerItem);
239
299
  if (applicableProducts.length === 0)
240
300
  return false;
241
301
  const usageAmount = typeof voucher.edit_current_amount === "number" ? voucher.edit_current_amount : getRecommendedAmount(voucher);
@@ -313,6 +373,8 @@ function processVouchers(applicableVouchers, orderTotalAmount, products) {
313
373
  product_id: product.product_id,
314
374
  parent_product_id: product.parent_product_id || null,
315
375
  is_bundle_item: product.is_bundle_item || false,
376
+ lineKey: product._walletPassLineKey,
377
+ orderLineQuantity: getExpandedOrderLineQuantity(product),
316
378
  deductAmount: actualDeductAmount.toNumber(),
317
379
  // 转换为数字
318
380
  deductQuantity: actualDeductQty
@@ -338,6 +400,8 @@ function processVouchers(applicableVouchers, orderTotalAmount, products) {
338
400
  product_id: targetProduct.product_id,
339
401
  parent_product_id: targetProduct.parent_product_id || null,
340
402
  is_bundle_item: targetProduct.is_bundle_item || false,
403
+ lineKey: targetProduct._walletPassLineKey,
404
+ orderLineQuantity: getExpandedOrderLineQuantity(targetProduct),
341
405
  deductAmount: actualDeductAmount.toNumber(),
342
406
  deductQuantity: actualDeductQty
343
407
  });
@@ -346,9 +410,7 @@ function processVouchers(applicableVouchers, orderTotalAmount, products) {
346
410
  if (totalDeducted.greaterThan(0)) {
347
411
  remainingOrderAmount = remainingOrderAmount.minus(totalDeducted);
348
412
  usedVoucherCounts.set(product_id, (usedVoucherCounts.get(product_id) || 0) + 1);
349
- deductionDetails.forEach((detail) => {
350
- incrementItemPassUsage(itemPassUsageMap, product_id, detail.product_id, detail.deductQuantity);
351
- });
413
+ applyMaxPassUsageIncrements(itemPassUsageMap, product_id, maxPassesPerItem, deductionDetails);
352
414
  recommendedVouchers.push({
353
415
  ...voucher,
354
416
  actualDeduction: totalDeducted.toNumber(),
@@ -391,23 +453,19 @@ function recalculateVouchers(allVouchers, selectedVouchers, orderTotalAmount, pr
391
453
  let remainingOrderAmount = new import_decimal.default(orderTotalAmount);
392
454
  const selectedWithDetails = [];
393
455
  const itemPassUsageMap = /* @__PURE__ */ new Map();
394
- const getItemPassUsage = (walletPassProductId, orderItemProductId) => {
456
+ const getItemPassUsageRecalc = (walletPassProductId, lineKey) => {
395
457
  var _a;
396
- return ((_a = itemPassUsageMap.get(walletPassProductId)) == null ? void 0 : _a.get(orderItemProductId)) || 0;
458
+ return ((_a = itemPassUsageMap.get(walletPassProductId)) == null ? void 0 : _a.get(lineKey)) || 0;
397
459
  };
398
- const incrementItemPassUsage = (walletPassProductId, orderItemProductId, count = 1) => {
399
- if (!itemPassUsageMap.has(walletPassProductId)) {
400
- itemPassUsageMap.set(walletPassProductId, /* @__PURE__ */ new Map());
401
- }
402
- const innerMap = itemPassUsageMap.get(walletPassProductId);
403
- innerMap.set(orderItemProductId, (innerMap.get(orderItemProductId) || 0) + count);
404
- };
405
- const filterByMaxPassesPerItem = (products2, walletPassProductId, maxPassesPerItem) => {
460
+ const filterByMaxPassesPerItemRecalc = (products2, walletPassProductId, maxPassesPerItem) => {
406
461
  if (maxPassesPerItem <= 0)
407
462
  return products2;
408
- return products2.filter(
409
- (p) => getItemPassUsage(walletPassProductId, p.product_id) < maxPassesPerItem * getTotalQuantityByProductId(productsForCalc, p.product_id)
410
- );
463
+ return products2.filter((p) => {
464
+ const lineKey = p._walletPassLineKey;
465
+ if (!lineKey)
466
+ return true;
467
+ return getItemPassUsageRecalc(walletPassProductId, lineKey) < getMaxPassSlotsForExpandedLine(maxPassesPerItem, p);
468
+ });
411
469
  };
412
470
  selectedVouchers.forEach((selectedVoucher) => {
413
471
  const { config, id } = selectedVoucher;
@@ -418,7 +476,7 @@ function recalculateVouchers(allVouchers, selectedVouchers, orderTotalAmount, pr
418
476
  selectedVoucher,
419
477
  productsForCalc
420
478
  ).filter((p) => p[amountField].greaterThan(0));
421
- applicableProducts = filterByMaxPassesPerItem(applicableProducts, selectedVoucher.product_id, maxPassesPerItem);
479
+ applicableProducts = filterByMaxPassesPerItemRecalc(applicableProducts, selectedVoucher.product_id, maxPassesPerItem);
422
480
  if (applicableProducts.length === 0) {
423
481
  selectedWithDetails.push({
424
482
  ...selectedVoucher,
@@ -461,6 +519,8 @@ function recalculateVouchers(allVouchers, selectedVouchers, orderTotalAmount, pr
461
519
  product_id: product.product_id,
462
520
  parent_product_id: product.parent_product_id || null,
463
521
  is_bundle_item: product.is_bundle_item || false,
522
+ lineKey: product._walletPassLineKey,
523
+ orderLineQuantity: getExpandedOrderLineQuantity(product),
464
524
  deductAmount: actualDeductAmount.toNumber(),
465
525
  // 转换为数字
466
526
  deductQuantity: actualDeductQty
@@ -486,15 +546,15 @@ function recalculateVouchers(allVouchers, selectedVouchers, orderTotalAmount, pr
486
546
  product_id: targetProduct.product_id,
487
547
  parent_product_id: targetProduct.parent_product_id || null,
488
548
  is_bundle_item: targetProduct.is_bundle_item || false,
549
+ lineKey: targetProduct._walletPassLineKey,
550
+ orderLineQuantity: getExpandedOrderLineQuantity(targetProduct),
489
551
  deductAmount: actualDeductAmount.toNumber(),
490
552
  deductQuantity: actualDeductQty
491
553
  });
492
554
  }
493
555
  const totalDeducted = maxDeduction.minus(deductionLeft);
494
556
  remainingOrderAmount = remainingOrderAmount.minus(totalDeducted);
495
- deductionDetails.forEach((detail) => {
496
- incrementItemPassUsage(selectedVoucher.product_id, detail.product_id, detail.deductQuantity);
497
- });
557
+ applyMaxPassUsageIncrements(itemPassUsageMap, selectedVoucher.product_id, maxPassesPerItem, deductionDetails);
498
558
  selectedWithDetails.push({
499
559
  ...selectedVoucher,
500
560
  actualDeduction: totalDeducted.toNumber(),
@@ -544,7 +604,7 @@ function recalculateVouchers(allVouchers, selectedVouchers, orderTotalAmount, pr
544
604
  voucher,
545
605
  productsForCalc
546
606
  ).filter((p) => p[amountField].greaterThan(0));
547
- applicableProducts = filterByMaxPassesPerItem(applicableProducts, product_id, maxPassesPerItem);
607
+ applicableProducts = filterByMaxPassesPerItemRecalc(applicableProducts, product_id, maxPassesPerItem);
548
608
  if (applicableProducts.length === 0) {
549
609
  isAvailable = false;
550
610
  reasonCode = "not_meet_the_required_conditions";
@@ -649,14 +709,17 @@ var getBundleItemPrice = (bundleItem, parentQuantity, isDeductTaxAndFee) => {
649
709
  };
650
710
  var expandProductsWithBundleItems = (products, deductTaxAndFee) => {
651
711
  const expandedProducts = [];
652
- products.forEach((product) => {
712
+ products.forEach((product, indexInOrder) => {
653
713
  const productQuantity = getProductQuantity(product);
714
+ const parentLineKey = resolveWalletPassLineKey(product, indexInOrder);
654
715
  const unitPriceWithTax = getMainProductPrice(product, true);
655
716
  const unitPricePure = getMainProductPrice(product, false);
656
717
  expandedProducts.push({
657
718
  ...product,
658
719
  is_bundle_item: false,
659
720
  parent_product_id: null,
721
+ _walletPassLineKey: parentLineKey,
722
+ _orderLineQuantity: productQuantity,
660
723
  // 单价(用于按 quantity 计算抵扣)
661
724
  unitPriceWithTax: new import_decimal.default(unitPriceWithTax),
662
725
  unitPricePure: new import_decimal.default(unitPricePure),
@@ -671,6 +734,7 @@ var expandProductsWithBundleItems = (products, deductTaxAndFee) => {
671
734
  product.product_bundle.forEach((bundleItem) => {
672
735
  if (getBundleItemIsOriginalPrice(bundleItem)) {
673
736
  const bundleQuantity = bundleItem.num * productQuantity;
737
+ const bundleLineKey = `${parentLineKey}#bundle:${bundleItem.bundle_id}:${bundleItem.bundle_product_id}`;
674
738
  const bundleUnitPriceWithTax = new import_decimal.default(getBundleItemPrice(bundleItem, 1, true)).dividedBy(bundleItem.num);
675
739
  const bundleUnitPricePure = new import_decimal.default(getBundleItemPrice(bundleItem, 1, false)).dividedBy(bundleItem.num);
676
740
  expandedProducts.push({
@@ -681,6 +745,8 @@ var expandProductsWithBundleItems = (products, deductTaxAndFee) => {
681
745
  parent_product_id: product.product_id,
682
746
  quantity: bundleQuantity,
683
747
  // 子商品数量 * 主商品数量
748
+ _walletPassLineKey: bundleLineKey,
749
+ _orderLineQuantity: bundleQuantity,
684
750
  // 单价(用于按 quantity 计算抵扣)
685
751
  unitPriceWithTax: bundleUnitPriceWithTax,
686
752
  unitPricePure: bundleUnitPricePure,
@@ -697,9 +763,6 @@ var expandProductsWithBundleItems = (products, deductTaxAndFee) => {
697
763
  });
698
764
  return expandedProducts;
699
765
  };
700
- var getProductQuantity = (product) => {
701
- return product.quantity || product.product_quantity || 1;
702
- };
703
766
  var getBundleItemIsOriginalPrice = (item) => {
704
767
  return (item == null ? void 0 : item.price_type) === "markup" && (item == null ? void 0 : item.price_type_ext) === "product_price";
705
768
  };
@@ -723,5 +786,6 @@ var getBundleItemIsMarkupOrDiscountPrice = (item) => {
723
786
  getMainProductPrice,
724
787
  getProductQuantity,
725
788
  processVouchers,
726
- recalculateVouchers
789
+ recalculateVouchers,
790
+ resolveWalletPassLineKey
727
791
  });
@@ -49,5 +49,5 @@ export declare class Product extends BaseModule implements Module {
49
49
  getCategories(): ProductCategory[];
50
50
  setOtherParams(key: string, value: any): void;
51
51
  getOtherParams(): any;
52
- getProductType(): "normal" | "duration" | "session";
52
+ getProductType(): "duration" | "session" | "normal";
53
53
  }
@@ -311,7 +311,7 @@ export declare class BookingByStepImpl extends BaseModule implements Module {
311
311
  date: string;
312
312
  status: string;
313
313
  week: string;
314
- weekNum: 0 | 2 | 1 | 3 | 4 | 5 | 6;
314
+ weekNum: 0 | 1 | 2 | 3 | 4 | 5 | 6;
315
315
  }[]>;
316
316
  submitTimeSlot(timeSlots: TimeSliceItem): void;
317
317
  private getScheduleDataByIds;
@@ -358,7 +358,7 @@ export declare class BookingByStepImpl extends BaseModule implements Module {
358
358
  };
359
359
  setOtherData(key: string, value: any): void;
360
360
  getOtherData(key: string): any;
361
- getProductTypeById(id: number): Promise<"normal" | "duration" | "session">;
361
+ getProductTypeById(id: number): Promise<"duration" | "session" | "normal">;
362
362
  /**
363
363
  * 提供给 UI 的方法,减轻 UI 层的计算压力,UI 层只需要传递 cartItemId 和 resourceCode 即返回对应的 renderList
364
364
  *
@@ -115,7 +115,7 @@ export declare class BookingTicketImpl extends BaseModule implements Module {
115
115
  * 获取当前的客户搜索条件
116
116
  * @returns 当前搜索条件
117
117
  */
118
- getCurrentCustomerSearchParams(): Omit<import("../../modules").ShopGetCustomerListParams, "skip" | "num">;
118
+ getCurrentCustomerSearchParams(): Omit<import("../../modules").ShopGetCustomerListParams, "num" | "skip">;
119
119
  /**
120
120
  * 获取客户列表状态(包含滚动加载相关状态)
121
121
  * @returns 客户状态
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "private": false,
3
3
  "name": "@pisell/pisellos",
4
- "version": "2.1.107",
4
+ "version": "2.1.108",
5
5
  "description": "一个可扩展的前端模块化SDK框架,支持插件系统",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",