@pisell/pisellos 0.10.41 → 0.10.42

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.
Files changed (50) hide show
  1. package/dist/model/strategy/adapter/promotion/index.js +9 -0
  2. package/dist/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +58 -19
  3. package/dist/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +24 -12
  4. package/dist/modules/Discount/entitlementPass.d.ts +34 -0
  5. package/dist/modules/Discount/entitlementPass.js +147 -0
  6. package/dist/modules/Discount/index.d.ts +2 -0
  7. package/dist/modules/Discount/index.js +39 -13
  8. package/dist/modules/Discount/types.d.ts +26 -5
  9. package/dist/modules/Order/index.d.ts +6 -1
  10. package/dist/modules/Order/index.js +283 -118
  11. package/dist/modules/Order/utils/orderCollectionIdentity.js +16 -12
  12. package/dist/modules/Order/utils.js +17 -1
  13. package/dist/modules/ResourcePlanner/planner.d.ts +2 -2
  14. package/dist/modules/ResourcePlanner/planner.js +163 -46
  15. package/dist/modules/ResourcePlanner/types.d.ts +17 -0
  16. package/dist/modules/Rules/index.js +310 -134
  17. package/dist/solution/BaseSales/index.d.ts +2 -0
  18. package/dist/solution/BaseSales/index.js +9 -1
  19. package/dist/solution/BaseSales/utils/cartPromotion.js +141 -126
  20. package/dist/solution/BookingByStep/index.d.ts +1 -1
  21. package/dist/solution/BookingTicket/utils/scan/cloudSearch.js +2 -0
  22. package/dist/solution/ShopDiscount/index.d.ts +1 -0
  23. package/dist/solution/ShopDiscount/index.js +41 -21
  24. package/dist/solution/ShopDiscount/utils.js +3 -2
  25. package/dist/solution/UnifiedBookingSales/index.js +94 -21
  26. package/lib/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +35 -6
  27. package/lib/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +13 -6
  28. package/lib/modules/Discount/entitlementPass.d.ts +34 -0
  29. package/lib/modules/Discount/entitlementPass.js +141 -0
  30. package/lib/modules/Discount/index.d.ts +2 -0
  31. package/lib/modules/Discount/index.js +25 -9
  32. package/lib/modules/Discount/types.d.ts +26 -5
  33. package/lib/modules/Order/index.d.ts +6 -1
  34. package/lib/modules/Order/index.js +172 -28
  35. package/lib/modules/Order/utils/orderCollectionIdentity.js +14 -11
  36. package/lib/modules/Order/utils.js +19 -1
  37. package/lib/modules/ResourcePlanner/planner.d.ts +2 -2
  38. package/lib/modules/ResourcePlanner/planner.js +137 -35
  39. package/lib/modules/ResourcePlanner/types.d.ts +17 -0
  40. package/lib/modules/Rules/index.js +230 -96
  41. package/lib/solution/BaseSales/index.d.ts +2 -0
  42. package/lib/solution/BaseSales/index.js +7 -1
  43. package/lib/solution/BaseSales/utils/cartPromotion.js +12 -4
  44. package/lib/solution/BookingByStep/index.d.ts +1 -1
  45. package/lib/solution/BookingTicket/utils/scan/cloudSearch.js +2 -0
  46. package/lib/solution/ShopDiscount/index.d.ts +1 -0
  47. package/lib/solution/ShopDiscount/index.js +26 -6
  48. package/lib/solution/ShopDiscount/utils.js +3 -2
  49. package/lib/solution/UnifiedBookingSales/index.js +86 -22
  50. package/package.json +1 -1
@@ -11,6 +11,7 @@ var _utils2 = require("../Cart/utils");
11
11
  var _decimal = _interopRequireDefault(require("decimal.js"));
12
12
  var _lodashEs = require("lodash-es");
13
13
  var _dayjs = _interopRequireDefault(require("dayjs"));
14
+ var _entitlementPass = require("../Discount/entitlementPass");
14
15
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
15
16
  class RulesModule extends _BaseModule.BaseModule {
16
17
  defaultName = 'rules';
@@ -97,7 +98,7 @@ class RulesModule extends _BaseModule.BaseModule {
97
98
 
98
99
  // 合并新旧折扣列表,并计算折扣结果,注意,如果旧折扣里有 isEditMode 为 true 的优惠券,则不合并
99
100
  // 保留之前扫码得到的商品券(isScan: true)
100
- const filteredOldDiscountList = oldDiscountList.filter(discount => !discount.isEditMode && (discount.tag !== 'good_pass' || discount.isScan));
101
+ const filteredOldDiscountList = oldDiscountList.filter(discount => !discount.isEditMode && (!(0, _entitlementPass.isFullCoveragePass)(discount) || discount.isScan));
101
102
  const mergedDiscountList = (0, _utils.uniqueById)([...filteredOldDiscountList, ...newDiscountList]);
102
103
  const result = this.calcDiscount({
103
104
  discountList: mergedDiscountList,
@@ -221,8 +222,8 @@ class RulesModule extends _BaseModule.BaseModule {
221
222
  if (!selectedDiscount) return false;
222
223
  const uid = getOriginProductUid(originProduct);
223
224
  if (!uid || !reapplyBookingDiscountProductUids.has(uid)) return false;
224
- const discountType = selectedDiscount.tag || selectedDiscount.type;
225
- return selectedDiscount.isEditMode === true && selectedDiscount.isSelected === true && ['good_pass', 'discount_card', 'product_discount_card'].includes(discountType);
225
+ const discountType = selectedDiscount.tag || selectedDiscount.type || '';
226
+ return selectedDiscount.isEditMode === true && selectedDiscount.isSelected === true && ['entitlement_pass', 'good_pass', 'discount_card', 'product_discount_card'].includes(discountType);
226
227
  };
227
228
  const resolveReapplyBookingDiscountPercent = (originProduct, selectedDiscount) => {
228
229
  if (!shouldReapplyBookingDiscount(originProduct, selectedDiscount)) return undefined;
@@ -267,8 +268,8 @@ class RulesModule extends _BaseModule.BaseModule {
267
268
  };
268
269
  }
269
270
  const canUseEditModeDiscountForReapply = discount => {
270
- const discountType = discount.tag || discount.type;
271
- return reapplyBookingDiscountProductUids.size > 0 && discount.isEditMode === true && discount.isSelected === true && ['good_pass', 'discount_card', 'product_discount_card'].includes(discountType);
271
+ const discountType = discount.tag || discount.type || '';
272
+ return reapplyBookingDiscountProductUids.size > 0 && discount.isEditMode === true && discount.isSelected === true && ['entitlement_pass', 'good_pass', 'discount_card', 'product_discount_card'].includes(discountType);
272
273
  };
273
274
 
274
275
  // 过滤掉手动取消false的优惠券,但仅用于应用优惠逻辑。
@@ -342,6 +343,7 @@ class RulesModule extends _BaseModule.BaseModule {
342
343
 
343
344
  // 辅助函数:判断是否是商品券
344
345
  const isGoodPass = discount => discount.tag === 'good_pass';
346
+ const isEntitlement = discount => (0, _entitlementPass.isEntitlementPass)(discount);
345
347
 
346
348
  // 辅助函数:判断是否是订单级折扣卡
347
349
  const isOrderLevelDiscount = discount => discount.tag === 'product_discount_card' && discount.metadata?.discount_calculation_mode === 'order_level';
@@ -403,6 +405,14 @@ class RulesModule extends _BaseModule.BaseModule {
403
405
  }
404
406
  return 0;
405
407
  }
408
+ const entitlementA = isEntitlement(a);
409
+ const entitlementB = isEntitlement(b);
410
+ if (entitlementA || entitlementB) {
411
+ if (entitlementA && entitlementB) {
412
+ return (0, _entitlementPass.compareEntitlementPassPriority)(a, b);
413
+ }
414
+ return entitlementA ? -1 : 1;
415
+ }
406
416
 
407
417
  // 1. 先按优先级排序
408
418
  const priorityA = getPriority(a);
@@ -473,14 +483,22 @@ class RulesModule extends _BaseModule.BaseModule {
473
483
  const evaluator = this.getWalletPassEvaluator();
474
484
  if (evaluator) {
475
485
  addModeDiscount.forEach(discount => {
476
- const discountType = discount.tag || discount.type;
477
- // 只对 good_pass 和 discount_card 类型执行策略检查
478
- if (['good_pass', 'discount_card', 'product_discount_card'].includes(discountType)) {
486
+ const discountType = discount.tag || discount.type || '';
487
+ // 对两类全额抵扣卡和折扣卡执行附加策略检查。
488
+ if (['entitlement_pass', 'good_pass', 'discount_card', 'product_discount_card'].includes(discountType)) {
489
+ const entitlementQuota = (0, _entitlementPass.isEntitlementPass)(discount) ? (0, _entitlementPass.resolveEntitlementQuota)(discount) : null;
490
+ if ((0, _entitlementPass.isEntitlementPass)(discount) && !entitlementQuota) {
491
+ discount.config = {
492
+ isAvailable: false
493
+ };
494
+ return;
495
+ }
496
+
479
497
  // 转换 discount 为 Voucher 格式
480
498
  const voucher = {
481
499
  id: discount.id,
482
500
  amount: Number(discount.par_value || 0),
483
- balance: Number(discount.balance || 0),
501
+ balance: entitlementQuota?.type === 'unlimited' ? Number.MAX_SAFE_INTEGER : entitlementQuota?.type === 'limited' ? entitlementQuota.balance : Number(discount.balance || 0),
484
502
  type: discountType,
485
503
  product_id: discount.product_id,
486
504
  unified_available_status: 1,
@@ -536,11 +554,118 @@ class RulesModule extends _BaseModule.BaseModule {
536
554
  });
537
555
  */
538
556
 
539
- // 标记已使用的优惠券
557
+ // good_pass / 折扣卡保持原有的一次性使用标记。
540
558
  const usedDiscounts = new Map();
541
559
 
560
+ // Entitlement Pass 单独记录本次重算新增分配的次数。
561
+ const entitlementUsageCounts = new Map();
562
+
542
563
  // 🔥 统计每个 product_id(商品券商品id)已使用的数量,用于 maxUsagePerOrder 限制
543
564
  const usedProductIdCounts = new Map();
565
+ const markDiscountUsed = (discount, count = 1) => {
566
+ if ((0, _entitlementPass.isEntitlementPass)(discount)) {
567
+ entitlementUsageCounts.set(discount.id, (entitlementUsageCounts.get(discount.id) || 0) + count);
568
+ return;
569
+ }
570
+ usedDiscounts.set(discount.id, true);
571
+ };
572
+ const isDiscountUsed = discount => {
573
+ if ((0, _entitlementPass.isEntitlementPass)(discount)) {
574
+ return (entitlementUsageCounts.get(discount.id) || 0) > 0;
575
+ }
576
+ return usedDiscounts.get(discount.id) === true;
577
+ };
578
+ const buildFullCoveragePassUsageGroups = (discounts, requestedCount) => {
579
+ const groups = [];
580
+ const pendingUsedDiscounts = new Set(Array.from(usedDiscounts.entries()).filter(([, used]) => used).map(([discountId]) => discountId));
581
+ const pendingEntitlementCounts = new Map(entitlementUsageCounts);
582
+ const pendingProductCounts = new Map(usedProductIdCounts);
583
+ const firstGoodPass = discounts.find(discount => (0, _entitlementPass.isFullCoveragePass)(discount) && !(0, _entitlementPass.isEntitlementPass)(discount));
584
+ const legacyGoodPassPerLineLimit = firstGoodPass ? Number(firstGoodPass.config?.maxUsagePerOrder || 0) : 0;
585
+ let allocatedGoodPassCount = 0;
586
+ let remaining = Math.max(Math.floor(requestedCount), 0);
587
+ for (const discount of discounts) {
588
+ if (remaining <= 0) break;
589
+ if (!(0, _entitlementPass.isFullCoveragePass)(discount)) continue;
590
+ const currentEntitlementCount = pendingEntitlementCounts.get(discount.id) || 0;
591
+ let availableCount = (0, _entitlementPass.isEntitlementPass)(discount) ? (0, _entitlementPass.getRemainingEntitlementUses)(discount, currentEntitlementCount, remaining) : pendingUsedDiscounts.has(discount.id) ? 0 : 1;
592
+ if (!(0, _entitlementPass.isEntitlementPass)(discount) && legacyGoodPassPerLineLimit > 0) {
593
+ availableCount = Math.min(availableCount, Math.max(Math.floor(legacyGoodPassPerLineLimit) - allocatedGoodPassCount, 0));
594
+ }
595
+ const maxUsagePerOrder = Number(discount.config?.maxUsagePerOrder || 0);
596
+ if ((0, _entitlementPass.isEntitlementPass)(discount) && maxUsagePerOrder > 0) {
597
+ const currentProductCount = pendingProductCounts.get(discount.product_id) || 0;
598
+ availableCount = Math.min(availableCount, Math.max(Math.floor(maxUsagePerOrder) - currentProductCount, 0));
599
+ }
600
+ const allocatedCount = Math.min(availableCount, remaining);
601
+ if (allocatedCount <= 0) continue;
602
+ const existingGroup = groups.find(group => String(group.discount.id) === String(discount.id));
603
+ if (existingGroup) {
604
+ existingGroup.count += allocatedCount;
605
+ } else {
606
+ groups.push({
607
+ discount,
608
+ count: allocatedCount
609
+ });
610
+ }
611
+ if ((0, _entitlementPass.isEntitlementPass)(discount)) {
612
+ pendingEntitlementCounts.set(discount.id, currentEntitlementCount + allocatedCount);
613
+ } else {
614
+ pendingUsedDiscounts.add(discount.id);
615
+ allocatedGoodPassCount += allocatedCount;
616
+ }
617
+ pendingProductCounts.set(discount.product_id, (pendingProductCounts.get(discount.product_id) || 0) + allocatedCount);
618
+ remaining -= allocatedCount;
619
+ }
620
+ return groups;
621
+ };
622
+ const mergeEntitlementAppliedProductDetails = details => {
623
+ const mergedDetails = [];
624
+ const mergedIndexByKey = new Map();
625
+ const toComparableNumber = value => {
626
+ const numberValue = Number(value);
627
+ return Number.isFinite(numberValue) ? String(numberValue) : String(value ?? '');
628
+ };
629
+ for (const detail of details || []) {
630
+ if (!(0, _entitlementPass.isEntitlementPass)(detail)) {
631
+ mergedDetails.push(detail);
632
+ continue;
633
+ }
634
+ const resourceId = detail?.discount?.resource_id ?? detail?.resource_id;
635
+ if (resourceId === undefined || resourceId === null) {
636
+ mergedDetails.push(detail);
637
+ continue;
638
+ }
639
+ const key = [resourceId, detail?.discount?.product_id ?? '', detail?.metadata?.custom_product_bundle_map_id ?? '', toComparableNumber(detail?.amount), toComparableNumber(detail?.discount?.fixed_amount)].join('|');
640
+ const rawUsageCount = Number(detail?.metadata?.num ?? detail?._num ?? 1);
641
+ const usageCount = Number.isFinite(rawUsageCount) && rawUsageCount > 0 ? rawUsageCount : 1;
642
+ const mergedIndex = mergedIndexByKey.get(key);
643
+ if (mergedIndex === undefined) {
644
+ mergedIndexByKey.set(key, mergedDetails.length);
645
+ mergedDetails.push({
646
+ ...detail,
647
+ metadata: {
648
+ ...(detail?.metadata || {}),
649
+ num: usageCount
650
+ },
651
+ _num: usageCount
652
+ });
653
+ continue;
654
+ }
655
+ const existed = mergedDetails[mergedIndex];
656
+ const existedUsageCount = Number(existed?.metadata?.num ?? existed?._num ?? 1);
657
+ const totalUsageCount = existedUsageCount + usageCount;
658
+ mergedDetails[mergedIndex] = {
659
+ ...existed,
660
+ metadata: {
661
+ ...(existed?.metadata || {}),
662
+ num: totalUsageCount
663
+ },
664
+ _num: totalUsageCount
665
+ };
666
+ }
667
+ return mergedDetails;
668
+ };
544
669
 
545
670
  // 记录每个优惠券适用的商品ID
546
671
  const discountApplicability = new Map();
@@ -586,7 +711,7 @@ class RulesModule extends _BaseModule.BaseModule {
586
711
  }
587
712
 
588
713
  // 编辑的商品使用了优惠券不可用
589
- const isAvailableProduct = flatItem.type === 'main' ? !(product?.booking_id && product?.discount_list?.length && product?.discount_list?.every(d => d.id && ['good_pass', 'discount_card', 'product_discount_card'].includes(d.tag || d.type))) : !(flatItem?.booking_id && !!flatItem?.bundleItem?.discount_list?.length && flatItem?.bundleItem?.discount_list?.every(d => d.id));
714
+ const isAvailableProduct = flatItem.type === 'main' ? !(product?.booking_id && product?.discount_list?.length && product?.discount_list?.every(d => d.id && ['entitlement_pass', 'good_pass', 'discount_card', 'product_discount_card'].includes(d.tag || d.type))) : !(flatItem?.booking_id && !!flatItem?.bundleItem?.discount_list?.length && flatItem?.bundleItem?.discount_list?.every(d => d.id));
590
715
  if (!isAvailableProduct) {
591
716
  return false;
592
717
  }
@@ -777,23 +902,23 @@ class RulesModule extends _BaseModule.BaseModule {
777
902
  const isLimitedProduct = (limitedData.type === 'product_all' && limitedData.filter !== 1 || limitedData.type === 'product_all' && limitedData.filter === 1 && !limitedData.exclude_product_ids.includes(product.id) || limitedData.product_ids && limitedData.product_ids.includes(product.id)) && isHolderMatch;
778
903
 
779
904
  // 编辑的商品 使用了优惠券不可用
780
- const isAvailableProduct = flatItem.type === 'main' ? !(product?.booking_id && product?.discount_list?.length && product?.discount_list?.every(discount => discount.id && ['good_pass', 'discount_card', 'product_discount_card'].includes(discount.tag || discount.type))) : !(flatItem?.booking_id && !!flatItem?.bundleItem?.discount_list?.length && flatItem?.bundleItem?.discount_list?.every(discount => discount.id));
905
+ const isAvailableProduct = flatItem.type === 'main' ? !(product?.booking_id && product?.discount_list?.length && product?.discount_list?.every(discount => discount.id && ['entitlement_pass', 'good_pass', 'discount_card', 'product_discount_card'].includes(discount.tag || discount.type))) : !(flatItem?.booking_id && !!flatItem?.bundleItem?.discount_list?.length && flatItem?.bundleItem?.discount_list?.every(discount => discount.id));
781
906
 
782
907
  // 套餐是否可用判断
783
908
  const isBundleAvailable = this.checkPackageSubItemUsageRules(discount, flatItem);
784
909
 
785
910
  // 判断优惠券是否适用于该商品
786
- if (isAvailableProduct && !isItemRewardProductDiscount && isLimitedProduct && timeLimit && isBundleAvailable && discount.config?.isAvailable) {
911
+ if (isAvailableProduct && !isItemRewardProductDiscount && isLimitedProduct && timeLimit && isBundleAvailable && discount.config?.isAvailable && (!(0, _entitlementPass.isEntitlementPass)(discount) || (0, _entitlementPass.getRemainingEntitlementUses)(discount, 0, 1) > 0)) {
787
912
  // 记录此优惠券适用的商品
788
913
  discountApplicability.get(discount.id)?.push(product.id);
789
914
 
790
915
  // 记录可抵扣的商品详情
791
916
  const applicableProducts = discountApplicableProducts.get(discount.id) || [];
792
917
  const discountType = discount.tag || discount.type;
793
- const isGoodPass = discountType === 'good_pass';
918
+ const isFullCoverage = (0, _entitlementPass.isFullCoveragePass)(discount);
794
919
 
795
920
  // 使用数量
796
- const num = isGoodPass || flatItem?.type === 'main' ? 1 : product.num;
921
+ const num = isFullCoverage || flatItem?.type === 'main' ? 1 : product.num;
797
922
  const productData = {
798
923
  amount: product.price * num,
799
924
  type: discountType,
@@ -911,7 +1036,7 @@ class RulesModule extends _BaseModule.BaseModule {
911
1036
  const hasExistingWalletDiscount = product.discount_list?.length && product?.discount_list?.every(discount => {
912
1037
  const discountIdentity = discount.id ?? discount.discount?.resource_id;
913
1038
  const discountType = discount.tag || discount.type;
914
- return Boolean(discountIdentity && ['good_pass', 'discount_card', 'product_discount_card'].includes(discountType));
1039
+ return Boolean(discountIdentity && ['entitlement_pass', 'good_pass', 'discount_card', 'product_discount_card'].includes(discountType));
915
1040
  });
916
1041
 
917
1042
  // 已持久化且已有钱包券/折扣卡的商品行跳过,保留后端历史折扣结果。
@@ -928,8 +1053,8 @@ class RulesModule extends _BaseModule.BaseModule {
928
1053
  // 找到适用于此商品的所有优惠券,仅考虑isSelected不为false的优惠券
929
1054
  const applicableDiscounts = sortedDiscountList.filter(discount => {
930
1055
  if (this.isItemRewardProductDiscount(product)) return false;
931
- // 🔥 检查策略可用性(针对 good_pass 和折扣卡)
932
- const discountType = discount.tag || discount.type;
1056
+ // 🔥 检查策略可用性(针对全额抵扣卡和折扣卡)
1057
+ const discountType = discount.tag || discount.type || '';
933
1058
  const currentOriginUid = getOriginProductUid(originProduct);
934
1059
  const isReapplyEditModeDiscountForProduct = discount.isEditMode && reapplyBookingDiscountProductUids.size > 0 && currentOriginUid && reapplyBookingDiscountProductUids.has(String(currentOriginUid)) && discount.isSelected === true;
935
1060
  if (discount.isEditMode && reapplyBookingDiscountProductUids.size > 0) {
@@ -937,7 +1062,7 @@ class RulesModule extends _BaseModule.BaseModule {
937
1062
  if (!uid || !reapplyBookingDiscountProductUids.has(uid)) return false;
938
1063
  if (!discount.isSelected) return false;
939
1064
  }
940
- if (['good_pass', 'discount_card', 'product_discount_card'].includes(discountType) && !isReapplyEditModeDiscountForProduct) {
1065
+ if (['entitlement_pass', 'good_pass', 'discount_card', 'product_discount_card'].includes(discountType) && !isReapplyEditModeDiscountForProduct) {
941
1066
  // 如果策略检查后没有config,说明不可用
942
1067
  if (discount.config === undefined || !discount?.config?.isAvailable) {
943
1068
  return false;
@@ -945,17 +1070,22 @@ class RulesModule extends _BaseModule.BaseModule {
945
1070
  }
946
1071
 
947
1072
  // 如果商品价格为 0,其实不需要使用任何优惠券,直接 return true
948
- // 商品券时主商品价格为0不可用
949
- if ((Number(product.price) <= 0 || !product.price) && !product.discount_list?.length && (discount.tag || discount.type) === 'good_pass') return false;
1073
+ // 全额抵扣卡在主商品价格为 0 时不可用。
1074
+ if ((Number(product.price) <= 0 || !product.price) && !product.discount_list?.length && (0, _entitlementPass.isFullCoveragePass)(discount)) return false;
950
1075
 
951
1076
  // 折扣卡商品价格为0时不可用
952
- if ((Number(product.price) <= 0 || !product.price) && !product.discount_list?.find(n => n.discount?.resource_id === discount.id) && (discount.tag || discount.type) !== 'good_pass') return false;
953
- // 如果优惠券已被使用,则跳过
954
- const targetUsedDiscounts = usedDiscounts.get(discount.id);
955
- if (targetUsedDiscounts && (discount.tag || discount.type) === 'good_pass') return false;
1077
+ if ((Number(product.price) <= 0 || !product.price) && !product.discount_list?.find(n => n.discount?.resource_id === discount.id) && !(0, _entitlementPass.isFullCoveragePass)(discount)) return false;
1078
+ // good_pass 保持一张实例只使用一次。
1079
+ if ((discount.tag || discount.type) === 'good_pass' && usedDiscounts.get(discount.id)) {
1080
+ return false;
1081
+ }
1082
+ // 有限次数卡按后端实时 balance 约束本次新增分配;无限卡不看余额。
1083
+ if ((0, _entitlementPass.isEntitlementPass)(discount) && (0, _entitlementPass.getRemainingEntitlementUses)(discount, entitlementUsageCounts.get(discount.id) || 0, 1) <= 0) {
1084
+ return false;
1085
+ }
956
1086
 
957
1087
  // 🔥 检查 maxUsagePerOrder 限制:同一 product_id 的商品券使用数量不能超过配置值
958
- if ((discount.tag || discount.type) === 'good_pass') {
1088
+ if ((0, _entitlementPass.isFullCoveragePass)(discount)) {
959
1089
  const maxUsagePerOrder = discount.config?.maxUsagePerOrder;
960
1090
  if (maxUsagePerOrder && maxUsagePerOrder > 0) {
961
1091
  const currentUsedCount = usedProductIdCounts.get(discount.product_id) || 0;
@@ -1003,28 +1133,28 @@ class RulesModule extends _BaseModule.BaseModule {
1003
1133
  // 用户显式点击某张卡券时,本次选择优先于自动排序和扫码默认选择。
1004
1134
  // 目标仍必须在当前商品的 applicableDiscounts 中,因此不会绕过 Holder、时间或商品范围校验。
1005
1135
  const explicitlySelectedDiscount = options?.isSelected === true && options.discountId !== undefined ? applicableDiscounts.find(discount => {
1006
- const discountType = discount.tag || discount.type;
1007
- return String(discount.id) === String(options.discountId) && ['good_pass', 'discount_card', 'product_discount_card'].includes(discountType);
1136
+ const discountType = discount.tag || discount.type || '';
1137
+ return String(discount.id) === String(options.discountId) && ['entitlement_pass', 'good_pass', 'discount_card', 'product_discount_card'].includes(discountType);
1008
1138
  }) : undefined;
1009
1139
  // 折扣卡会复用于整条商品行;商品券必须继续按单张拆分,不能复用该分支。
1010
- const explicitlySelectedDiscountCard = explicitlySelectedDiscount && (explicitlySelectedDiscount.tag || explicitlySelectedDiscount.type) !== 'good_pass' ? explicitlySelectedDiscount : undefined;
1140
+ const explicitlySelectedDiscountCard = explicitlySelectedDiscount && !(0, _entitlementPass.isFullCoveragePass)(explicitlySelectedDiscount) ? explicitlySelectedDiscount : undefined;
1011
1141
 
1012
1142
  // 商品数量等变化会触发无 discountId 的重算。只要当前行已应用的折扣卡仍在
1013
1143
  // applicableDiscounts 中,就应保留该行选择,避免被自动排序中力度更大的卡替换。
1014
1144
  const appliedDiscountCardIds = new Set((product.discount_list || []).filter(item => ['discount_card', 'product_discount_card'].includes(item?.tag || item?.type)).map(item => item?.discount?.resource_id ?? item?.resource_id ?? item?.id).filter(id => id !== undefined && id !== null).map(String));
1015
1145
  const appliedDiscountCard = options?.scan ? undefined : applicableDiscounts.find(discount => {
1016
- const discountType = discount.tag || discount.type;
1146
+ const discountType = discount.tag || discount.type || '';
1017
1147
  return discount.isSelected === true && ['discount_card', 'product_discount_card'].includes(discountType) && appliedDiscountCardIds.has(String(discount.id));
1018
1148
  });
1019
1149
 
1020
- // 优先级:本次显式选择 > 当前行既有选择 > 扫码选择 > 自动排序第一项。
1021
- const scannedSelectedDiscountCard = applicableDiscounts.find(n => n.isScan && n.isSelected && (n.tag || n.type) !== 'good_pass');
1150
+ // Entitlement Pass 始终按协议优先级使用;没有新卡时沿用原选择优先级。
1151
+ const scannedSelectedDiscountCard = applicableDiscounts.find(n => n.isScan && n.isSelected && !(0, _entitlementPass.isFullCoveragePass)(n));
1022
1152
  const selectedDiscountCard = explicitlySelectedDiscountCard || appliedDiscountCard || scannedSelectedDiscountCard;
1023
- const selectedDiscount = explicitlySelectedDiscount || selectedDiscountCard || applicableDiscounts[0];
1024
- const prioritizedApplicableDiscounts = explicitlySelectedDiscount && (explicitlySelectedDiscount.tag || explicitlySelectedDiscount.type) === 'good_pass' ? [explicitlySelectedDiscount, ...applicableDiscounts.filter(discount => {
1025
- const discountType = discount.tag || discount.type;
1026
- return discountType === 'good_pass' && String(discount.id) !== String(explicitlySelectedDiscount.id);
1027
- }), ...applicableDiscounts.filter(discount => (discount.tag || discount.type) !== 'good_pass')] : applicableDiscounts;
1153
+ const highestPriorityEntitlementPass = applicableDiscounts.find(discount => (0, _entitlementPass.isEntitlementPass)(discount));
1154
+ const selectedDiscount = highestPriorityEntitlementPass || explicitlySelectedDiscount || selectedDiscountCard || applicableDiscounts[0];
1155
+ const prioritizedApplicableDiscounts = !highestPriorityEntitlementPass && explicitlySelectedDiscount && (0, _entitlementPass.isFullCoveragePass)(explicitlySelectedDiscount) ? [explicitlySelectedDiscount, ...applicableDiscounts.filter(discount => {
1156
+ return (0, _entitlementPass.isFullCoveragePass)(discount) && String(discount.id) !== String(explicitlySelectedDiscount.id);
1157
+ }), ...applicableDiscounts.filter(discount => !(0, _entitlementPass.isFullCoveragePass)(discount))] : applicableDiscounts;
1028
1158
 
1029
1159
  // 如果是手动折扣,则不适用优惠券
1030
1160
  let isManualDiscount = false;
@@ -1140,19 +1270,13 @@ class RulesModule extends _BaseModule.BaseModule {
1140
1270
  return;
1141
1271
  }
1142
1272
 
1143
- // 是否需要拆分(商品券需要拆分)
1144
- const isNeedSplit = (selectedDiscount.tag || selectedDiscount.type) === 'good_pass';
1273
+ // good_pass 与 Entitlement Pass 都沿用全额抵扣卡的商品行拆分机制。
1274
+ const isNeedSplit = (0, _entitlementPass.isFullCoveragePass)(selectedDiscount);
1145
1275
 
1146
1276
  // 需要拆分出来的数量
1147
1277
  const totalQuantity = product.quantity || product.num || 1;
1148
- const availableGoodPassCount = applicableDiscounts.filter(item => (item.tag || item.type) === 'good_pass').length;
1149
-
1150
- // 🔥 如果selectedDiscount有config配置,则使用config.maxUsagePerOrder限制使用次数
1151
- let maxUsageLimit = availableGoodPassCount;
1152
- if (selectedDiscount.config && selectedDiscount.config.maxUsagePerOrder) {
1153
- maxUsageLimit = Math.min(availableGoodPassCount, selectedDiscount.config.maxUsagePerOrder);
1154
- }
1155
- const splitCount = isNeedSplit ? Math.min(product.quantity || product.num || 1, maxUsageLimit) : 1;
1278
+ const fullCoveragePassUsageGroups = isNeedSplit ? buildFullCoveragePassUsageGroups(prioritizedApplicableDiscounts, totalQuantity) : [];
1279
+ const splitCount = isNeedSplit ? fullCoveragePassUsageGroups.reduce((total, group) => total + group.count, 0) : 1;
1156
1280
  const arr = [];
1157
1281
 
1158
1282
  // 🔥 主商品和bundle子商品分别处理
@@ -1160,22 +1284,22 @@ class RulesModule extends _BaseModule.BaseModule {
1160
1284
  // 主商品:保持原有逻辑
1161
1285
  if (splitCount < totalQuantity && isNeedSplit) {
1162
1286
  const remainingQuantity = totalQuantity - splitCount;
1163
- const applicableNonGoodPassById = new Map(applicableDiscounts.filter(discount => (discount.tag || discount.type) !== 'good_pass').map(discount => [String(discount.id), discount]));
1287
+ const applicableNonFullCoveragePassById = new Map(applicableDiscounts.filter(discount => !(0, _entitlementPass.isFullCoveragePass)(discount)).map(discount => [String(discount.id), discount]));
1164
1288
  const remainderDiscountList = (product.discount_list || []).filter(discount => {
1165
1289
  const discountType = discount.tag || discount.type;
1166
1290
  if (discountType === 'promotion') return true;
1167
- if (discountType === 'good_pass') return false;
1291
+ if ((0, _entitlementPass.isFullCoveragePass)(discount)) return false;
1168
1292
  const resourceId = discount.discount?.resource_id ?? discount.id;
1169
- return resourceId !== undefined && resourceId !== null && applicableNonGoodPassById.has(String(resourceId));
1293
+ return resourceId !== undefined && resourceId !== null && applicableNonFullCoveragePassById.has(String(resourceId));
1170
1294
  }).map(discount => {
1171
1295
  if ((discount.tag || discount.type) === 'promotion') return discount;
1172
1296
  const resourceId = discount.discount?.resource_id ?? discount.id;
1173
- const matchedDiscount = applicableNonGoodPassById.get(String(resourceId));
1297
+ const matchedDiscount = applicableNonFullCoveragePassById.get(String(resourceId));
1174
1298
  if (matchedDiscount) {
1175
1299
  // 实际场景:3 件折扣卡商品中拆出 1 件改用商品券后,
1176
1300
  // 剩余 2 件仍在使用原折扣卡;必须同步登记折扣卡的使用状态,
1177
1301
  // 否则 updatedDiscountList 会把它改成未选中并清空 savedAmount。
1178
- usedDiscounts.set(matchedDiscount.id, true);
1302
+ markDiscountUsed(matchedDiscount);
1179
1303
  const appliedProducts = appliedDiscountProducts.get(matchedDiscount.id) || [];
1180
1304
  appliedProducts.push({
1181
1305
  ...discount,
@@ -1189,14 +1313,14 @@ class RulesModule extends _BaseModule.BaseModule {
1189
1313
  _num: remainingQuantity
1190
1314
  };
1191
1315
  });
1192
- const hasRetainedWalletDiscount = remainderDiscountList.some(discount => !['promotion', 'good_pass'].includes(discount.tag || discount.type));
1316
+ const hasRetainedWalletDiscount = remainderDiscountList.some(discount => (discount.tag || discount.type) !== 'promotion' && !(0, _entitlementPass.isFullCoveragePass)(discount));
1193
1317
  let total = product.origin_total ?? product.total;
1194
1318
  if ((product.discount_list || []).some(item => item.type === 'promotion')) {
1195
1319
  total = product.total ?? product.origin_total;
1196
1320
  }
1197
1321
  arr.push(this.hooks.setProduct(originProduct, {
1198
1322
  // 商品券只替换被拆出的 splitCount 件。余量行原本仍有效的折扣卡
1199
- // 不能随 good_pass 拆分一起清除;已取消或已失效的折扣不会进入此列表。
1323
+ // 不能随全额抵扣卡拆分一起清除;已取消或已失效的折扣不会进入此列表。
1200
1324
  discount_list: remainderDiscountList,
1201
1325
  quantity: remainingQuantity,
1202
1326
  _id: product._id.split('___')[0],
@@ -1208,14 +1332,21 @@ class RulesModule extends _BaseModule.BaseModule {
1208
1332
  } : {})
1209
1333
  }));
1210
1334
  }
1211
- for (let i = 0; i < splitCount; i++) {
1212
- const selectedDiscount = isNeedSplit ? prioritizedApplicableDiscounts[i] : selectedDiscountCard || applicableDiscounts[i];
1335
+ const discountUsageGroups = isNeedSplit ? fullCoveragePassUsageGroups : [{
1336
+ discount: selectedDiscount,
1337
+ count: 1
1338
+ }];
1339
+ for (let i = 0; i < discountUsageGroups.length; i++) {
1340
+ const {
1341
+ discount: selectedDiscount,
1342
+ count: appliedUseCount
1343
+ } = discountUsageGroups[i];
1213
1344
  // 标记优惠券为已使用
1214
- usedDiscounts.set(selectedDiscount.id, true);
1345
+ markDiscountUsed(selectedDiscount, appliedUseCount);
1215
1346
  // 🔥 更新 product_id 使用计数
1216
- if ((selectedDiscount.tag || selectedDiscount.type) === 'good_pass') {
1347
+ if ((0, _entitlementPass.isFullCoveragePass)(selectedDiscount)) {
1217
1348
  const currentCount = usedProductIdCounts.get(selectedDiscount.product_id) || 0;
1218
- usedProductIdCounts.set(selectedDiscount.product_id, currentCount + 1);
1349
+ usedProductIdCounts.set(selectedDiscount.product_id, currentCount + appliedUseCount);
1219
1350
  }
1220
1351
 
1221
1352
  // 记录实际应用了优惠券的商品信息
@@ -1277,7 +1408,7 @@ class RulesModule extends _BaseModule.BaseModule {
1277
1408
  const mainProductDiscountAmount = amount;
1278
1409
  const lineDiscountAmount = new _decimal.default(mainProductDiscountAmount).plus(optionDiscountAmount).toNumber();
1279
1410
  const discountType = selectedDiscount.tag || selectedDiscount.type;
1280
- const isGoodPass = discountType === 'good_pass';
1411
+ const isFullCoverage = (0, _entitlementPass.isFullCoveragePass)(selectedDiscount);
1281
1412
  const discountDetail = {
1282
1413
  amount: lineDiscountAmount,
1283
1414
  type: selectedDiscount.tag === 'product_discount_card' ? 'discount_card' : discountType,
@@ -1293,10 +1424,10 @@ class RulesModule extends _BaseModule.BaseModule {
1293
1424
  discount_product_id: selectedDiscount.product_id
1294
1425
  },
1295
1426
  // 前端使用的num数量,为了计算优惠金额
1296
- _num: isGoodPass ? 1 : product.num,
1427
+ _num: isFullCoverage ? appliedUseCount : product.num,
1297
1428
  config: selectedDiscount?.config,
1298
1429
  metadata: {
1299
- num: 1,
1430
+ num: isFullCoverage ? appliedUseCount : 1,
1300
1431
  discount_rule_uncheck_flag: selectedDiscount?.discount_rule_uncheck_flag,
1301
1432
  // 🔥 order_level 分摊差值
1302
1433
  ...(productDiscountDifference !== undefined && {
@@ -1315,9 +1446,9 @@ class RulesModule extends _BaseModule.BaseModule {
1315
1446
  if (product.isClient) {
1316
1447
  arr.push(this.hooks.setProduct(originProduct, {
1317
1448
  discount_list: [discountDetail],
1318
- // good_pass:主商品价以折后价为准;勿用 product.price - amount(amount 含 option 优惠时会算错)
1319
- price: isGoodPass ? mainProductSellingPrice : product.price,
1320
- quantity: isNeedSplit ? 1 : product.quantity,
1449
+ // 全额抵扣卡:主商品价以折后价为准;勿用 product.price - amount(amount 含 option 优惠时会算错)
1450
+ price: isFullCoverage ? mainProductSellingPrice : product.price,
1451
+ quantity: isNeedSplit ? appliedUseCount : product.quantity,
1321
1452
  origin_total: (0, _utils2.getProductOriginTotalPrice)({
1322
1453
  product: {
1323
1454
  original_price: product.original_price
@@ -1334,8 +1465,8 @@ class RulesModule extends _BaseModule.BaseModule {
1334
1465
  arr.push(this.hooks.setProduct(originProduct, {
1335
1466
  discount_list: this.filterDiscountListByType(product.discount_list, 'promotion').concat([discountDetail]),
1336
1467
  _id: product._id.split('___')[0] + '___' + selectedDiscount.id + index,
1337
- price: selectedDiscount.tag === 'good_pass' ? 0 : product.price,
1338
- quantity: isNeedSplit ? 1 : product.quantity,
1468
+ price: isFullCoverage ? 0 : product.price,
1469
+ quantity: isNeedSplit ? appliedUseCount : product.quantity,
1339
1470
  total: total,
1340
1471
  origin_total: productOriginTotal,
1341
1472
  main_product_selling_price: mainProductSellingPrice,
@@ -1352,14 +1483,17 @@ class RulesModule extends _BaseModule.BaseModule {
1352
1483
  const discountNum = splitCount;
1353
1484
  const normalNum = totalQuantity - discountNum;
1354
1485
 
1355
- // 生成有折扣的商品(每张商品券对应 num: 1)
1356
- for (let i = 0; i < discountNum; i++) {
1357
- const selectedDiscount = prioritizedApplicableDiscounts[i];
1358
- usedDiscounts.set(selectedDiscount.id, true);
1486
+ // 同一卡实例的本次新增使用合并为一条,metadata.num 记录实际次数。
1487
+ for (let i = 0; i < fullCoveragePassUsageGroups.length; i++) {
1488
+ const {
1489
+ discount: selectedDiscount,
1490
+ count: appliedUseCount
1491
+ } = fullCoveragePassUsageGroups[i];
1492
+ markDiscountUsed(selectedDiscount, appliedUseCount);
1359
1493
  // 🔥 更新 product_id 使用计数
1360
- if ((selectedDiscount.tag || selectedDiscount.type) === 'good_pass') {
1494
+ if ((0, _entitlementPass.isFullCoveragePass)(selectedDiscount)) {
1361
1495
  const currentCount = usedProductIdCounts.get(selectedDiscount.product_id) || 0;
1362
- usedProductIdCounts.set(selectedDiscount.product_id, currentCount + 1);
1496
+ usedProductIdCounts.set(selectedDiscount.product_id, currentCount + appliedUseCount);
1363
1497
  }
1364
1498
 
1365
1499
  // 🔥 生成唯一的 _id
@@ -1371,7 +1505,7 @@ class RulesModule extends _BaseModule.BaseModule {
1371
1505
  debugger;
1372
1506
  const discountDetail = {
1373
1507
  amount: bundleDiscountAmount,
1374
- type: 'good_pass',
1508
+ type: selectedDiscount.tag || selectedDiscount.type,
1375
1509
  discount: {
1376
1510
  fixed_amount: product.origin_total,
1377
1511
  discount_calculation_mode: selectedDiscount?.metadata?.discount_calculation_mode,
@@ -1385,9 +1519,9 @@ class RulesModule extends _BaseModule.BaseModule {
1385
1519
  // 🔥 使用拆分后的唯一 _id
1386
1520
  custom_product_bundle_map_id: uniqueId,
1387
1521
  discount_rule_uncheck_flag: selectedDiscount?.discount_rule_uncheck_flag,
1388
- num: 1
1522
+ num: appliedUseCount
1389
1523
  },
1390
- _num: 1,
1524
+ _num: appliedUseCount,
1391
1525
  config: selectedDiscount?.config
1392
1526
  };
1393
1527
 
@@ -1399,11 +1533,11 @@ class RulesModule extends _BaseModule.BaseModule {
1399
1533
  ...flatItem,
1400
1534
  // 🔥 使用唯一的 _id
1401
1535
  _id: uniqueId,
1402
- num: 1,
1403
- quantity: 1,
1536
+ num: appliedUseCount,
1537
+ quantity: appliedUseCount,
1404
1538
  price: discountedPrice,
1405
1539
  // 折扣后的价格
1406
- total: discountedPrice,
1540
+ total: new _decimal.default(discountedPrice).mul(appliedUseCount).toNumber(),
1407
1541
  discount_list: [discountDetail],
1408
1542
  processed: true,
1409
1543
  _discountId: selectedDiscount.id
@@ -1425,9 +1559,9 @@ class RulesModule extends _BaseModule.BaseModule {
1425
1559
  } else {
1426
1560
  // 折扣卡:不拆分数量,直接应用
1427
1561
  const selectedDiscount = selectedDiscountCard || applicableDiscounts[0];
1428
- usedDiscounts.set(selectedDiscount.id, true);
1562
+ markDiscountUsed(selectedDiscount);
1429
1563
  // 🔥 更新 product_id 使用计数(虽然此处主要是折扣卡,但保持一致性)
1430
- if ((selectedDiscount.tag || selectedDiscount.type) === 'good_pass') {
1564
+ if ((0, _entitlementPass.isFullCoveragePass)(selectedDiscount)) {
1431
1565
  const currentCount = usedProductIdCounts.get(selectedDiscount.product_id) || 0;
1432
1566
  usedProductIdCounts.set(selectedDiscount.product_id, currentCount + 1);
1433
1567
  }
@@ -1560,11 +1694,10 @@ class RulesModule extends _BaseModule.BaseModule {
1560
1694
  // 没有bundle,直接使用主商品处理结果
1561
1695
  result.push(...mainProductArr);
1562
1696
  } else {
1563
- // 🔥 有bundle,需要检查子商品是否应用了商品券
1697
+ // 🔥 有 bundle,需要检查子商品是否应用了全额抵扣卡
1564
1698
  // 1. 先收集所有bundle子商品的处理结果
1565
1699
  const bundleProcessingInfo = new Map();
1566
- let hasGoodPassApplied = false; // 标记是否有子商品应用了商品券
1567
-
1700
+ let hasFullCoveragePassApplied = false;
1568
1701
  if (product.bundle && Array.isArray(product.bundle)) {
1569
1702
  product.bundle.forEach((bundleItem, bundleIndex) => {
1570
1703
  const bundleItemId = `${product._id}_bundle_${bundleIndex}`;
@@ -1575,10 +1708,9 @@ class RulesModule extends _BaseModule.BaseModule {
1575
1708
  } else {
1576
1709
  // 处理过的bundle item
1577
1710
  bundleProcessingInfo.set(bundleIndex, processedBundleItems);
1578
- // 🔥 检查是否应用了商品券(good_pass)
1579
- const hasGoodPass = processedBundleItems.some(item => item.discount_list?.some(discount => discount.type === 'good_pass' || discount.tag === 'good_pass'));
1580
- if (hasGoodPass) {
1581
- hasGoodPassApplied = true;
1711
+ const hasFullCoveragePass = processedBundleItems.some(item => item.discount_list?.some(discount => (0, _entitlementPass.isFullCoveragePass)(discount)));
1712
+ if (hasFullCoveragePass) {
1713
+ hasFullCoveragePassApplied = true;
1582
1714
  }
1583
1715
  }
1584
1716
  });
@@ -1586,7 +1718,7 @@ class RulesModule extends _BaseModule.BaseModule {
1586
1718
 
1587
1719
  // 🔥 2. 如果有子商品应用了商品券,且主商品数量大于1,需要拆分主商品
1588
1720
  const mainProductQuantity = mainProductArr[0] ? this.hooks.getProduct(mainProductArr[0]).quantity : 1;
1589
- if (hasGoodPassApplied && mainProductArr.length === 1 && mainProductQuantity > 1) {
1721
+ if (hasFullCoveragePassApplied && mainProductArr.length === 1 && mainProductQuantity > 1) {
1590
1722
  const mainProduct = mainProductArr[0];
1591
1723
  const mainProductData = this.hooks.getProduct(mainProduct);
1592
1724
 
@@ -1890,6 +2022,8 @@ class RulesModule extends _BaseModule.BaseModule {
1890
2022
 
1891
2023
  // 获取此优惠券实际应用的商品详情
1892
2024
  const appliedProductDetails = appliedDiscountProducts.get(discount.id) || [];
2025
+ const committedAppliedProductDetails = (0, _entitlementPass.isEntitlementPass)(discount) ? mergeEntitlementAppliedProductDetails(discount.committedAppliedProductDetails || []) : [];
2026
+ const allAppliedProductDetails = (0, _entitlementPass.isEntitlementPass)(discount) ? mergeEntitlementAppliedProductDetails([...committedAppliedProductDetails, ...appliedProductDetails]) : appliedProductDetails;
1893
2027
 
1894
2028
  // 根据是否有适用商品决定isAvailable
1895
2029
  const isAvailable = applicableProducts.length > 0;
@@ -1904,11 +2038,11 @@ class RulesModule extends _BaseModule.BaseModule {
1904
2038
  applicableProductIds: applicableProducts,
1905
2039
  // 添加可抵扣的商品详情
1906
2040
  applicableProductDetails: applicableProductDetails,
1907
- // 添加实际抵扣的商品详情(对于未选中的优惠券,为空数组)
1908
- appliedProductDetails: []
2041
+ // 已提交的历史使用不可撤销;只取消本次新增分配。
2042
+ appliedProductDetails: committedAppliedProductDetails
1909
2043
  };
1910
2044
  }
1911
- if (usedDiscounts.get(discount.id)) {
2045
+ if (isDiscountUsed(discount)) {
1912
2046
  return {
1913
2047
  ...discount,
1914
2048
  isSelected: true,
@@ -1920,7 +2054,7 @@ class RulesModule extends _BaseModule.BaseModule {
1920
2054
  // 添加可抵扣的商品详情
1921
2055
  applicableProductDetails: applicableProductDetails,
1922
2056
  // 添加实际抵扣的商品详情
1923
- appliedProductDetails: appliedProductDetails
2057
+ appliedProductDetails: allAppliedProductDetails
1924
2058
  };
1925
2059
  }
1926
2060
  let isSelected = typeof discount.isSelected === 'boolean' ? discount.isSelected : undefined;
@@ -1928,7 +2062,7 @@ class RulesModule extends _BaseModule.BaseModule {
1928
2062
  // 如果是可用的 并且没有手动标记 则设置为选中
1929
2063
  if (isAvailable && appliedProductDetails.length && !discount.isManualSelect) {
1930
2064
  isSelected = true;
1931
- } else if (!appliedProductDetails.length) {
2065
+ } else if (!appliedProductDetails.length && !committedAppliedProductDetails.length) {
1932
2066
  isSelected = false;
1933
2067
  }
1934
2068
  return {
@@ -1942,8 +2076,8 @@ class RulesModule extends _BaseModule.BaseModule {
1942
2076
  applicableProductIds: applicableProducts,
1943
2077
  // 添加可抵扣的商品详情
1944
2078
  applicableProductDetails: applicableProductDetails,
1945
- // 添加实际抵扣的商品详情(对于未使用的优惠券,为空数组)
1946
- appliedProductDetails: []
2079
+ // 编辑订单的历史使用继续保留,本次没有新增使用。
2080
+ appliedProductDetails: committedAppliedProductDetails
1947
2081
  };
1948
2082
  });
1949
2083
  return {