@pisell/pisellos 2.2.309 → 2.2.310

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 (121) hide show
  1. package/dist/model/strategy/adapter/walletPass/evaluator.d.ts +2 -0
  2. package/dist/model/strategy/adapter/walletPass/evaluator.js +4 -2
  3. package/dist/modules/BookingContext/index.js +4 -4
  4. package/dist/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +58 -19
  5. package/dist/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +24 -12
  6. package/dist/modules/Customer/index.js +6 -3
  7. package/dist/modules/Customer/types.d.ts +2 -0
  8. package/dist/modules/Discount/availability.d.ts +7 -0
  9. package/dist/modules/Discount/availability.js +49 -0
  10. package/dist/modules/Discount/entitlementPass.d.ts +34 -0
  11. package/dist/modules/Discount/entitlementPass.js +147 -0
  12. package/dist/modules/Discount/index.d.ts +2 -2
  13. package/dist/modules/Discount/index.js +37 -151
  14. package/dist/modules/Discount/types.d.ts +36 -5
  15. package/dist/modules/OpenData/index.d.ts +1 -1
  16. package/dist/modules/OpenData/index.js +21 -12
  17. package/dist/modules/Order/index.d.ts +32 -4
  18. package/dist/modules/Order/index.js +733 -373
  19. package/dist/modules/Order/types.d.ts +17 -0
  20. package/dist/modules/Order/utils/orderCollectionIdentity.js +16 -12
  21. package/dist/modules/Order/utils.d.ts +14 -2
  22. package/dist/modules/Order/utils.js +275 -76
  23. package/dist/modules/Payment/index.d.ts +0 -5
  24. package/dist/modules/Payment/index.js +35 -85
  25. package/dist/modules/Payment/types.d.ts +21 -1
  26. package/dist/modules/Payment/types.js +2 -0
  27. package/dist/modules/Payment/walletpass.d.ts +1 -1
  28. package/dist/modules/Payment/walletpass.js +80 -47
  29. package/dist/modules/ProductList/index.js +18 -5
  30. package/dist/modules/Rules/index.d.ts +2 -2
  31. package/dist/modules/Rules/index.js +480 -186
  32. package/dist/modules/Rules/types.d.ts +4 -0
  33. package/dist/server/index.js +8 -5
  34. package/dist/server/modules/order/index.js +6 -0
  35. package/dist/server/modules/order/types.d.ts +12 -0
  36. package/dist/server/modules/payment/index.d.ts +25 -6
  37. package/dist/server/modules/payment/index.js +660 -133
  38. package/dist/server/utils/small-ticket.d.ts +8 -5
  39. package/dist/server/utils/small-ticket.js +1269 -199
  40. package/dist/solution/BaseSales/index.d.ts +11 -2
  41. package/dist/solution/BaseSales/index.js +1463 -1173
  42. package/dist/solution/BaseSales/types.d.ts +7 -3
  43. package/dist/solution/BaseSales/utils/cartPromotion.js +141 -126
  44. package/dist/solution/BookingByStep/index.d.ts +1 -1
  45. package/dist/solution/BookingTicket/index.d.ts +12 -3
  46. package/dist/solution/BookingTicket/index.js +663 -542
  47. package/dist/solution/BookingTicket/types.d.ts +5 -1
  48. package/dist/solution/BookingTicket/utils/scan/applyGlobalScan.d.ts +2 -0
  49. package/dist/solution/BookingTicket/utils/scan/applyGlobalScan.js +338 -147
  50. package/dist/solution/BookingTicket/utils/scan/cloudSearch.js +8 -4
  51. package/dist/solution/BookingTicket/utils/scan/formatGlobalScan.d.ts +6 -0
  52. package/dist/solution/BookingTicket/utils/scan/formatGlobalScan.js +19 -6
  53. package/dist/solution/ShopDiscount/index.d.ts +2 -0
  54. package/dist/solution/ShopDiscount/index.js +99 -42
  55. package/dist/solution/ShopDiscount/utils.d.ts +4 -2
  56. package/dist/solution/ShopDiscount/utils.js +43 -23
  57. package/dist/solution/UnifiedBookingSales/index.d.ts +2 -2
  58. package/dist/solution/UnifiedBookingSales/index.js +5 -5
  59. package/dist/utils/os-private-payload.d.ts +5 -0
  60. package/dist/utils/os-private-payload.js +17 -0
  61. package/lib/model/strategy/adapter/walletPass/evaluator.d.ts +2 -0
  62. package/lib/model/strategy/adapter/walletPass/evaluator.js +2 -0
  63. package/lib/modules/BookingContext/index.js +3 -3
  64. package/lib/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +35 -6
  65. package/lib/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +13 -6
  66. package/lib/modules/Customer/index.js +5 -1
  67. package/lib/modules/Customer/types.d.ts +2 -0
  68. package/lib/modules/Discount/availability.d.ts +7 -0
  69. package/lib/modules/Discount/availability.js +43 -0
  70. package/lib/modules/Discount/entitlementPass.d.ts +34 -0
  71. package/lib/modules/Discount/entitlementPass.js +141 -0
  72. package/lib/modules/Discount/index.d.ts +2 -2
  73. package/lib/modules/Discount/index.js +23 -129
  74. package/lib/modules/Discount/types.d.ts +36 -5
  75. package/lib/modules/OpenData/index.d.ts +1 -1
  76. package/lib/modules/OpenData/index.js +11 -2
  77. package/lib/modules/Order/index.d.ts +32 -4
  78. package/lib/modules/Order/index.js +341 -71
  79. package/lib/modules/Order/types.d.ts +17 -0
  80. package/lib/modules/Order/utils/orderCollectionIdentity.js +14 -11
  81. package/lib/modules/Order/utils.d.ts +14 -2
  82. package/lib/modules/Order/utils.js +186 -3
  83. package/lib/modules/Payment/index.d.ts +0 -5
  84. package/lib/modules/Payment/index.js +3 -28
  85. package/lib/modules/Payment/types.d.ts +21 -1
  86. package/lib/modules/Payment/types.js +1 -0
  87. package/lib/modules/Payment/walletpass.d.ts +1 -1
  88. package/lib/modules/Payment/walletpass.js +29 -9
  89. package/lib/modules/ProductList/index.js +14 -1
  90. package/lib/modules/Rules/index.d.ts +2 -2
  91. package/lib/modules/Rules/index.js +357 -110
  92. package/lib/modules/Rules/types.d.ts +4 -0
  93. package/lib/server/index.js +8 -5
  94. package/lib/server/modules/order/index.js +6 -0
  95. package/lib/server/modules/order/types.d.ts +12 -0
  96. package/lib/server/modules/payment/index.d.ts +25 -6
  97. package/lib/server/modules/payment/index.js +350 -78
  98. package/lib/server/utils/small-ticket.d.ts +8 -5
  99. package/lib/server/utils/small-ticket.js +1009 -121
  100. package/lib/solution/BaseSales/index.d.ts +11 -2
  101. package/lib/solution/BaseSales/index.js +164 -30
  102. package/lib/solution/BaseSales/types.d.ts +7 -3
  103. package/lib/solution/BaseSales/utils/cartPromotion.js +12 -4
  104. package/lib/solution/BookingByStep/index.d.ts +1 -1
  105. package/lib/solution/BookingTicket/index.d.ts +12 -3
  106. package/lib/solution/BookingTicket/index.js +103 -41
  107. package/lib/solution/BookingTicket/types.d.ts +5 -1
  108. package/lib/solution/BookingTicket/utils/scan/applyGlobalScan.d.ts +2 -0
  109. package/lib/solution/BookingTicket/utils/scan/applyGlobalScan.js +174 -42
  110. package/lib/solution/BookingTicket/utils/scan/cloudSearch.js +8 -4
  111. package/lib/solution/BookingTicket/utils/scan/formatGlobalScan.d.ts +6 -0
  112. package/lib/solution/BookingTicket/utils/scan/formatGlobalScan.js +11 -3
  113. package/lib/solution/ShopDiscount/index.d.ts +2 -0
  114. package/lib/solution/ShopDiscount/index.js +55 -9
  115. package/lib/solution/ShopDiscount/utils.d.ts +4 -2
  116. package/lib/solution/ShopDiscount/utils.js +42 -25
  117. package/lib/solution/UnifiedBookingSales/index.d.ts +2 -2
  118. package/lib/solution/UnifiedBookingSales/index.js +5 -5
  119. package/lib/utils/os-private-payload.d.ts +5 -0
  120. package/lib/utils/os-private-payload.js +22 -0
  121. package/package.json +1 -1
@@ -2,6 +2,10 @@ var _excluded = ["_original_add_price"],
2
2
  _excluded2 = ["_original_price", "_original_add_price"];
3
3
  function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
4
4
  function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
5
+ function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
6
+ function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
7
+ function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
8
+ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
5
9
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
6
10
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
7
11
  function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
@@ -32,13 +36,15 @@ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol"
32
36
  function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
33
37
  import { BaseModule } from "../BaseModule";
34
38
  import { UnavailableReason } from "./types";
35
- import { uniqueById, getDiscountAmount, getDiscountListAmountTotal, getDiscountListAmount, filterDiscountListByBookingTime } from "../../solution/ShopDiscount/utils";
39
+ import { uniqueById, getDiscountAmount, getDiscountListAmountTotal, getDiscountListAmount, filterDiscountListByBookingTime, getDiscountTimeUnavailableReason } from "../../solution/ShopDiscount/utils";
36
40
  import { getProductOriginTotalPrice, getProductTotalPrice } from "../Cart/utils";
37
41
  import Decimal from 'decimal.js';
38
- import { isBoolean } from 'lodash-es';
42
+ import { cloneDeep, isBoolean } from 'lodash-es';
39
43
  import dayjs from 'dayjs';
40
44
  import { isOrderLevelFixedAmountDiscount } from "../../solution/ShopDiscount/utils";
41
45
  import { calculateOrderLevelDiscountAllocation } from "../../solution/ShopDiscount/utils";
46
+ import { compareEntitlementPassPriority, getRemainingEntitlementUses, isEntitlementPass, isFullCoveragePass, resolveEntitlementQuota } from "../Discount/entitlementPass";
47
+ import { getDiscountUsageBlockReason, getDiscountUsageLimitFailKey } from "../Discount/availability";
42
48
  export var RulesModule = /*#__PURE__*/function (_BaseModule) {
43
49
  _inherits(RulesModule, _BaseModule);
44
50
  var _super = _createSuper(RulesModule);
@@ -124,15 +130,23 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
124
130
  }
125
131
 
126
132
  /**
127
- * 特定 form Holder 卡只授权给绑定同一 Holder Record 的商品行。
128
- * required/isNeedHolder 仅控制是否强制选择 Holder,不参与优惠资格。
133
+ * 商品未开启 Holder 选择时跳过匹配;已开启时,特定 form Holder 卡
134
+ * 只适用于同一 Holder Record,是否必填不影响匹配规则。
129
135
  */
130
136
  }, {
131
137
  key: "checkHolderMatch",
132
- value: function checkHolderMatch(discount, product, holders) {
133
- var _discount$holder;
138
+ value: function checkHolderMatch(discount, product, holderSource, holders) {
139
+ var _discount$holder, _ref, _ref2, _ref3, _ref4, _ref5, _ref6, _holderSource$holder_, _holderSource$metadat, _holderSource$origin, _holderSource$metadat2, _holderSource$_origin, _holderSource$_extend, _holderSource$_extend2, _holderSource$_extend3;
134
140
  // 非表单类型 holder 视为匹配
135
141
  if (((_discount$holder = discount.holder) === null || _discount$holder === void 0 ? void 0 : _discount$holder.holder_type) !== 'form') return true;
142
+
143
+ // getProduct 会裁剪商品字段,从原始商品读取配置,套餐子项沿用父商品。
144
+ var holderConfig = (_ref = (_ref2 = (_ref3 = (_ref4 = (_ref5 = (_ref6 = (_holderSource$holder_ = holderSource === null || holderSource === void 0 ? void 0 : holderSource.holder_config) !== null && _holderSource$holder_ !== void 0 ? _holderSource$holder_ : holderSource === null || holderSource === void 0 || (_holderSource$metadat = holderSource.metadata) === null || _holderSource$metadat === void 0 ? void 0 : _holderSource$metadat.holder_config) !== null && _ref6 !== void 0 ? _ref6 : holderSource === null || holderSource === void 0 || (_holderSource$origin = holderSource.origin) === null || _holderSource$origin === void 0 ? void 0 : _holderSource$origin.holder_config) !== null && _ref5 !== void 0 ? _ref5 : holderSource === null || holderSource === void 0 || (_holderSource$metadat2 = holderSource.metadata) === null || _holderSource$metadat2 === void 0 || (_holderSource$metadat2 = _holderSource$metadat2.origin) === null || _holderSource$metadat2 === void 0 ? void 0 : _holderSource$metadat2.holder_config) !== null && _ref4 !== void 0 ? _ref4 : holderSource === null || holderSource === void 0 || (_holderSource$_origin = holderSource._origin) === null || _holderSource$_origin === void 0 ? void 0 : _holderSource$_origin.holder_config) !== null && _ref3 !== void 0 ? _ref3 : holderSource === null || holderSource === void 0 || (_holderSource$_extend = holderSource._extend) === null || _holderSource$_extend === void 0 ? void 0 : _holderSource$_extend.holder_config) !== null && _ref2 !== void 0 ? _ref2 : holderSource === null || holderSource === void 0 || (_holderSource$_extend2 = holderSource._extend) === null || _holderSource$_extend2 === void 0 || (_holderSource$_extend2 = _holderSource$_extend2.product) === null || _holderSource$_extend2 === void 0 ? void 0 : _holderSource$_extend2.holder_config) !== null && _ref !== void 0 ? _ref : holderSource === null || holderSource === void 0 || (_holderSource$_extend3 = holderSource._extend) === null || _holderSource$_extend3 === void 0 || (_holderSource$_extend3 = _holderSource$_extend3.product) === null || _holderSource$_extend3 === void 0 || (_holderSource$_extend3 = _holderSource$_extend3.metadata) === null || _holderSource$_extend3 === void 0 ? void 0 : _holderSource$_extend3.holder_config;
145
+ var holderStatus = holderConfig === null || holderConfig === void 0 ? void 0 : holderConfig.status;
146
+ var holderEnabled = holderStatus === true || ['enable', '1'].includes(String(holderStatus).trim().toLowerCase());
147
+ if (!holderEnabled || (holderConfig === null || holderConfig === void 0 ? void 0 : holderConfig.resource_id) === undefined || (holderConfig === null || holderConfig === void 0 ? void 0 : holderConfig.resource_id) === null || (holderConfig === null || holderConfig === void 0 ? void 0 : holderConfig.resource_id) === '') {
148
+ return true;
149
+ }
136
150
  var discountHolderIds = this.normalizeHolderRecordIds(discount.holder.holder_id);
137
151
  if (!discountHolderIds.length) return false;
138
152
  var productHolderIds = this.normalizeHolderRecordIds(product.holder_id);
@@ -147,14 +161,14 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
147
161
  // 判断discountList 是否可以对当前productList生效
148
162
  }, {
149
163
  key: "isDiscountListAvailable",
150
- value: function isDiscountListAvailable(_ref) {
164
+ value: function isDiscountListAvailable(_ref7) {
151
165
  var _this2 = this;
152
- var oldDiscountList = _ref.oldDiscountList,
153
- newDiscountList = _ref.newDiscountList,
154
- productList = _ref.productList,
155
- orderTotalAmount = _ref.orderTotalAmount,
156
- holders = _ref.holders,
157
- isFormSubject = _ref.isFormSubject;
166
+ var oldDiscountList = _ref7.oldDiscountList,
167
+ newDiscountList = _ref7.newDiscountList,
168
+ productList = _ref7.productList,
169
+ orderTotalAmount = _ref7.orderTotalAmount,
170
+ holders = _ref7.holders,
171
+ isFormSubject = _ref7.isFormSubject;
158
172
  // 首先检查是否有新的优惠券可应用
159
173
  if (!newDiscountList || newDiscountList.length === 0) {
160
174
  return {
@@ -177,11 +191,17 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
177
191
  unavailableReason: UnavailableReason.AlreadyUsed
178
192
  };
179
193
  }
194
+ var scannedEntitlementPassIds = newDiscountList.filter(function (discount) {
195
+ return isEntitlementPass(discount);
196
+ }).map(function (discount) {
197
+ return discount.id;
198
+ });
199
+ var scannedEntitlementPassIdSet = new Set(scannedEntitlementPassIds.map(String));
180
200
 
181
- // 合并新旧折扣列表,并计算折扣结果,注意,如果旧折扣里有 isEditMode 为 true 的优惠券,则不合并
182
- // 保留之前扫码得到的商品券(isScan: true)
201
+ // 客户次卡必须保留在完整列表中;同一实例以本次扫码数据更新。
202
+ // 历史折扣不参与试算,商品券继续沿用仅保留扫码来源的规则。
183
203
  var filteredOldDiscountList = oldDiscountList.filter(function (discount) {
184
- return !discount.isEditMode && (discount.tag !== 'good_pass' || discount.isScan);
204
+ return !discount.isEditMode && (!isFullCoveragePass(discount) || isEntitlementPass(discount) || discount.isScan) && !scannedEntitlementPassIdSet.has(String(discount.id));
185
205
  });
186
206
  var mergedDiscountList = uniqueById([].concat(_toConsumableArray(filteredOldDiscountList), _toConsumableArray(newDiscountList)));
187
207
  var result = this.calcDiscount({
@@ -191,7 +211,8 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
191
211
  holders: holders,
192
212
  isFormSubject: isFormSubject
193
213
  }, {
194
- scan: true
214
+ scan: true,
215
+ preferredEntitlementPassIds: scannedEntitlementPassIds
195
216
  });
196
217
 
197
218
  // 检查是否有产品使用了新折扣
@@ -346,14 +367,15 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
346
367
  }
347
368
  }, {
348
369
  key: "calcDiscount",
349
- value: function calcDiscount(_ref2, options) {
370
+ value: function calcDiscount(_ref8, options) {
350
371
  var _options$selectedList,
351
372
  _this4 = this;
352
- var discountList = _ref2.discountList,
353
- productList = _ref2.productList,
354
- holders = _ref2.holders,
355
- isFormSubject = _ref2.isFormSubject,
356
- orderTotalAmount = _ref2.orderTotalAmount;
373
+ var discountList = _ref8.discountList,
374
+ productList = _ref8.productList,
375
+ holders = _ref8.holders,
376
+ isFormSubject = _ref8.isFormSubject,
377
+ orderTotalAmount = _ref8.orderTotalAmount;
378
+ var preferredEntitlementPassIds = new Set(((options === null || options === void 0 ? void 0 : options.preferredEntitlementPassIds) || []).map(String));
357
379
  var resolveIsFormSubject = function resolveIsFormSubject(product) {
358
380
  return typeof isFormSubject === 'function' ? Boolean(isFormSubject(product)) : Boolean(isFormSubject);
359
381
  };
@@ -380,7 +402,7 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
380
402
  var uid = getOriginProductUid(originProduct);
381
403
  if (!uid || !reapplyBookingDiscountProductUids.has(uid)) return false;
382
404
  var discountType = selectedDiscount.tag || selectedDiscount.type;
383
- return selectedDiscount.isEditMode === true && selectedDiscount.isSelected === true && ['good_pass', 'discount_card', 'product_discount_card'].includes(discountType);
405
+ return selectedDiscount.isEditMode === true && selectedDiscount.isSelected === true && ['entitlement_pass', 'good_pass', 'discount_card', 'product_discount_card'].includes(discountType !== null && discountType !== void 0 ? discountType : '');
384
406
  };
385
407
  var resolveReapplyBookingDiscountPercent = function resolveReapplyBookingDiscountPercent(originProduct, selectedDiscount) {
386
408
  var _discount2;
@@ -398,7 +420,12 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
398
420
  if (discount.isEditMode) {
399
421
  editModeDiscount.push(discount);
400
422
  } else {
401
- addModeDiscount.push(discount);
423
+ // 试算不能把 config / 不可用原因写回原有选券;每次重新计算以清除旧原因。
424
+ addModeDiscount.push(_objectSpread(_objectSpread({}, discount), {}, {
425
+ usageLimitFailKey: getDiscountUsageLimitFailKey(discount),
426
+ reason: undefined,
427
+ reasonCode: getDiscountUsageBlockReason(discount)
428
+ }));
402
429
  }
403
430
  });
404
431
 
@@ -429,7 +456,7 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
429
456
  }
430
457
  var canUseEditModeDiscountForReapply = function canUseEditModeDiscountForReapply(discount) {
431
458
  var discountType = discount.tag || discount.type;
432
- return reapplyBookingDiscountProductUids.size > 0 && discount.isEditMode === true && discount.isSelected === true && ['good_pass', 'discount_card', 'product_discount_card'].includes(discountType);
459
+ return reapplyBookingDiscountProductUids.size > 0 && discount.isEditMode === true && discount.isSelected === true && ['entitlement_pass', 'good_pass', 'discount_card', 'product_discount_card'].includes(discountType !== null && discountType !== void 0 ? discountType : '');
433
460
  };
434
461
 
435
462
  // 过滤掉手动取消false的优惠券,但仅用于应用优惠逻辑。
@@ -461,7 +488,7 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
461
488
  // 2. 展开 bundle 子商品
462
489
  if (product.bundle && Array.isArray(product.bundle) && product.bundle.length > 0) {
463
490
  product.bundle.forEach(function (bundleItem, bundleIndex) {
464
- var _ref3, _bundleItem$bundle_pr;
491
+ var _ref9, _bundleItem$bundle_pr;
465
492
  flattened.push({
466
493
  type: 'bundle',
467
494
  originProduct: originProduct,
@@ -473,7 +500,7 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
473
500
  // 新增商品运行态通常带 _bundle_product_id;订单 hydrate 后只有
474
501
  // bundle_product_id。Change time 必须兼容两种结构,否则 bundle
475
502
  // 子项无法命中折扣卡的 limited_relation_product_data.product_ids。
476
- id: (_ref3 = (_bundleItem$bundle_pr = bundleItem.bundle_product_id) !== null && _bundleItem$bundle_pr !== void 0 ? _bundleItem$bundle_pr : bundleItem._bundle_product_id) !== null && _ref3 !== void 0 ? _ref3 : bundleItem.product_id,
503
+ id: (_ref9 = (_bundleItem$bundle_pr = bundleItem.bundle_product_id) !== null && _bundleItem$bundle_pr !== void 0 ? _bundleItem$bundle_pr : bundleItem._bundle_product_id) !== null && _ref9 !== void 0 ? _ref9 : bundleItem.product_id,
477
504
  _id: "".concat(product._id, "_bundle_").concat(bundleIndex),
478
505
  parentId: product._id,
479
506
  num: bundleItem.num || 1,
@@ -511,6 +538,9 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
511
538
  var isGoodPass = function isGoodPass(discount) {
512
539
  return discount.tag === 'good_pass';
513
540
  };
541
+ var isEntitlement = function isEntitlement(discount) {
542
+ return isEntitlementPass(discount);
543
+ };
514
544
 
515
545
  // 辅助函数:判断是否是订单级折扣卡
516
546
  var isOrderLevelDiscount = function isOrderLevelDiscount(discount) {
@@ -579,6 +609,14 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
579
609
  }
580
610
  return 0;
581
611
  }
612
+ var entitlementA = isEntitlement(a);
613
+ var entitlementB = isEntitlement(b);
614
+ if (entitlementA || entitlementB) {
615
+ if (entitlementA && entitlementB) {
616
+ return compareEntitlementPassPriority(a, b);
617
+ }
618
+ return entitlementA ? -1 : 1;
619
+ }
582
620
 
583
621
  // 1. 先按优先级排序
584
622
  var priorityA = getPriority(a);
@@ -622,10 +660,10 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
622
660
 
623
661
  // 对扁平化后的列表按价格降序排序(用于应用优惠券时优先选择高价商品)
624
662
  var sortedFlattenedList = flattenedList.sort(function (a, b) {
625
- var _ref4, _a$original_price, _ref5, _b$original_price;
663
+ var _ref10, _a$original_price, _ref11, _b$original_price;
626
664
  // 子商品优先使用 original_price,主商品使用 price
627
- var priceA = new Decimal(a.type === 'bundle' ? (_ref4 = (_a$original_price = a.original_price) !== null && _a$original_price !== void 0 ? _a$original_price : a.price) !== null && _ref4 !== void 0 ? _ref4 : '0' : a.price || '0');
628
- var priceB = new Decimal(b.type === 'bundle' ? (_ref5 = (_b$original_price = b.original_price) !== null && _b$original_price !== void 0 ? _b$original_price : b.price) !== null && _ref5 !== void 0 ? _ref5 : '0' : b.price || '0');
665
+ var priceA = new Decimal(a.type === 'bundle' ? (_ref10 = (_a$original_price = a.original_price) !== null && _a$original_price !== void 0 ? _a$original_price : a.price) !== null && _ref10 !== void 0 ? _ref10 : '0' : a.price || '0');
666
+ var priceB = new Decimal(b.type === 'bundle' ? (_ref11 = (_b$original_price = b.original_price) !== null && _b$original_price !== void 0 ? _b$original_price : b.price) !== null && _ref11 !== void 0 ? _ref11 : '0' : b.price || '0');
629
667
  if (priceA.equals(priceB)) {
630
668
  // 价格相同时,主商品优先
631
669
  if (a.type !== b.type) {
@@ -649,20 +687,35 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
649
687
 
650
688
  // 🔥 为每个折扣卡/商品券执行策略检查并附加config
651
689
  var evaluator = this.getWalletPassEvaluator();
690
+ addModeDiscount.forEach(function (discount) {
691
+ if (discount.reasonCode) discount.config = {
692
+ isAvailable: false
693
+ };
694
+ });
652
695
  if (evaluator) {
653
696
  addModeDiscount.forEach(function (discount) {
697
+ // 即使没有匹配的动态策略,服务端状态和次数限制也不能被覆盖为可用。
698
+ if (discount.reasonCode) return;
654
699
  var discountType = discount.tag || discount.type;
655
- // 只对 good_pass 和 discount_card 类型执行策略检查
656
- if (['good_pass', 'discount_card', 'product_discount_card'].includes(discountType)) {
657
- var _discount$limited_rel, _discount$limited_rel2;
700
+ // 对两类全额抵扣卡和折扣卡执行附加策略检查。
701
+ if (['entitlement_pass', 'good_pass', 'discount_card', 'product_discount_card'].includes(discountType !== null && discountType !== void 0 ? discountType : '')) {
702
+ var _discount$unified_ava, _discount$limited_rel, _discount$limited_rel2;
703
+ var entitlementQuota = isEntitlementPass(discount) ? resolveEntitlementQuota(discount) : null;
704
+ if (isEntitlementPass(discount) && !entitlementQuota) {
705
+ discount.config = {
706
+ isAvailable: false
707
+ };
708
+ return;
709
+ }
710
+
658
711
  // 转换 discount 为 Voucher 格式
659
712
  var voucher = {
660
713
  id: discount.id,
661
714
  amount: Number(discount.par_value || 0),
662
- balance: Number(discount.balance || 0),
715
+ balance: (entitlementQuota === null || entitlementQuota === void 0 ? void 0 : entitlementQuota.type) === 'unlimited' ? Number.MAX_SAFE_INTEGER : (entitlementQuota === null || entitlementQuota === void 0 ? void 0 : entitlementQuota.type) === 'limited' ? entitlementQuota.balance : Number(discount.balance || 0),
663
716
  type: discountType,
664
717
  product_id: discount.product_id,
665
- unified_available_status: 1,
718
+ unified_available_status: (_discount$unified_ava = discount.unified_available_status) !== null && _discount$unified_ava !== void 0 ? _discount$unified_ava : 1,
666
719
  available_product_type: (_discount$limited_rel = discount.limited_relation_product_data) === null || _discount$limited_rel === void 0 ? void 0 : _discount$limited_rel.type,
667
720
  available_product_ids: (_discount$limited_rel2 = discount.limited_relation_product_data) === null || _discount$limited_rel2 === void 0 ? void 0 : _discount$limited_rel2.product_ids
668
721
  };
@@ -695,6 +748,8 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
695
748
  discount.config = {
696
749
  isAvailable: false
697
750
  };
751
+ discount.reason = result.reason;
752
+ discount.reasonCode = result.reasonCode || 'not_meet_the_required_conditions';
698
753
  }
699
754
  }
700
755
  });
@@ -717,17 +772,162 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
717
772
  });
718
773
  */
719
774
 
720
- // 标记已使用的优惠券
775
+ // good_pass / 折扣卡保持原有的一次性使用标记。
721
776
  var usedDiscounts = new Map();
722
777
 
778
+ // Entitlement Pass 单独记录本次重算新增分配的次数。
779
+ var entitlementUsageCounts = new Map();
780
+
723
781
  // 🔥 统计每个 product_id(商品券商品id)已使用的数量,用于 maxUsagePerOrder 限制
724
782
  var usedProductIdCounts = new Map();
783
+ var markDiscountUsed = function markDiscountUsed(discount) {
784
+ var count = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
785
+ if (isEntitlementPass(discount)) {
786
+ entitlementUsageCounts.set(discount.id, (entitlementUsageCounts.get(discount.id) || 0) + count);
787
+ return;
788
+ }
789
+ usedDiscounts.set(discount.id, true);
790
+ };
791
+ var isDiscountUsed = function isDiscountUsed(discount) {
792
+ if (isEntitlementPass(discount)) {
793
+ return (entitlementUsageCounts.get(discount.id) || 0) > 0;
794
+ }
795
+ return usedDiscounts.get(discount.id) === true;
796
+ };
797
+ var buildFullCoveragePassUsageGroups = function buildFullCoveragePassUsageGroups(discounts, requestedCount) {
798
+ var _firstGoodPass$config;
799
+ var groups = [];
800
+ var pendingUsedDiscounts = new Set(Array.from(usedDiscounts.entries()).filter(function (_ref12) {
801
+ var _ref13 = _slicedToArray(_ref12, 2),
802
+ used = _ref13[1];
803
+ return used;
804
+ }).map(function (_ref14) {
805
+ var _ref15 = _slicedToArray(_ref14, 1),
806
+ discountId = _ref15[0];
807
+ return discountId;
808
+ }));
809
+ var pendingEntitlementCounts = new Map(entitlementUsageCounts);
810
+ var pendingProductCounts = new Map(usedProductIdCounts);
811
+ var firstGoodPass = discounts.find(function (discount) {
812
+ return isFullCoveragePass(discount) && !isEntitlementPass(discount);
813
+ });
814
+ var legacyGoodPassPerLineLimit = firstGoodPass ? Number(((_firstGoodPass$config = firstGoodPass.config) === null || _firstGoodPass$config === void 0 ? void 0 : _firstGoodPass$config.maxUsagePerOrder) || 0) : 0;
815
+ var allocatedGoodPassCount = 0;
816
+ var remaining = Math.max(Math.floor(requestedCount), 0);
817
+ var _iterator5 = _createForOfIteratorHelper(discounts),
818
+ _step5;
819
+ try {
820
+ var _loop = function _loop() {
821
+ var _discount$config;
822
+ var discount = _step5.value;
823
+ if (remaining <= 0) return 0; // break
824
+ if (!isFullCoveragePass(discount)) return 1; // continue
825
+ var currentEntitlementCount = pendingEntitlementCounts.get(discount.id) || 0;
826
+ var availableCount = isEntitlementPass(discount) ? getRemainingEntitlementUses(discount, currentEntitlementCount, remaining) : pendingUsedDiscounts.has(discount.id) ? 0 : 1;
827
+ if (!isEntitlementPass(discount) && legacyGoodPassPerLineLimit > 0) {
828
+ availableCount = Math.min(availableCount, Math.max(Math.floor(legacyGoodPassPerLineLimit) - allocatedGoodPassCount, 0));
829
+ }
830
+ var maxUsagePerOrder = Number(((_discount$config = discount.config) === null || _discount$config === void 0 ? void 0 : _discount$config.maxUsagePerOrder) || 0);
831
+ if (isEntitlementPass(discount) && maxUsagePerOrder > 0) {
832
+ var currentProductCount = pendingProductCounts.get(discount.product_id) || 0;
833
+ availableCount = Math.min(availableCount, Math.max(Math.floor(maxUsagePerOrder) - currentProductCount, 0));
834
+ }
835
+ var allocatedCount = Math.min(availableCount, remaining);
836
+ if (allocatedCount <= 0) return 1; // continue
837
+ var existingGroup = groups.find(function (group) {
838
+ return String(group.discount.id) === String(discount.id);
839
+ });
840
+ if (existingGroup) {
841
+ existingGroup.count += allocatedCount;
842
+ } else {
843
+ groups.push({
844
+ discount: discount,
845
+ count: allocatedCount
846
+ });
847
+ }
848
+ if (isEntitlementPass(discount)) {
849
+ pendingEntitlementCounts.set(discount.id, currentEntitlementCount + allocatedCount);
850
+ } else {
851
+ pendingUsedDiscounts.add(discount.id);
852
+ allocatedGoodPassCount += allocatedCount;
853
+ }
854
+ pendingProductCounts.set(discount.product_id, (pendingProductCounts.get(discount.product_id) || 0) + allocatedCount);
855
+ remaining -= allocatedCount;
856
+ },
857
+ _ret;
858
+ for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
859
+ _ret = _loop();
860
+ if (_ret === 0) break;
861
+ if (_ret === 1) continue;
862
+ }
863
+ } catch (err) {
864
+ _iterator5.e(err);
865
+ } finally {
866
+ _iterator5.f();
867
+ }
868
+ return groups;
869
+ };
870
+ var mergeEntitlementAppliedProductDetails = function mergeEntitlementAppliedProductDetails(details) {
871
+ var mergedDetails = [];
872
+ var mergedIndexByKey = new Map();
873
+ var toComparableNumber = function toComparableNumber(value) {
874
+ var numberValue = Number(value);
875
+ return Number.isFinite(numberValue) ? String(numberValue) : String(value !== null && value !== void 0 ? value : '');
876
+ };
877
+ var _iterator6 = _createForOfIteratorHelper(details || []),
878
+ _step6;
879
+ try {
880
+ for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
881
+ var _detail$discount$reso, _detail$discount, _detail$discount$prod, _detail$discount2, _detail$metadata$cust, _detail$metadata, _detail$discount3, _ref16, _detail$metadata$num, _detail$metadata2, _ref17, _existed$metadata$num, _existed$metadata;
882
+ var detail = _step6.value;
883
+ if (!isEntitlementPass(detail)) {
884
+ mergedDetails.push(detail);
885
+ continue;
886
+ }
887
+ var resourceId = (_detail$discount$reso = detail === null || detail === void 0 || (_detail$discount = detail.discount) === null || _detail$discount === void 0 ? void 0 : _detail$discount.resource_id) !== null && _detail$discount$reso !== void 0 ? _detail$discount$reso : detail === null || detail === void 0 ? void 0 : detail.resource_id;
888
+ if (resourceId === undefined || resourceId === null) {
889
+ mergedDetails.push(detail);
890
+ continue;
891
+ }
892
+ var key = [resourceId, (_detail$discount$prod = detail === null || detail === void 0 || (_detail$discount2 = detail.discount) === null || _detail$discount2 === void 0 ? void 0 : _detail$discount2.product_id) !== null && _detail$discount$prod !== void 0 ? _detail$discount$prod : '', (_detail$metadata$cust = detail === null || detail === void 0 || (_detail$metadata = detail.metadata) === null || _detail$metadata === void 0 ? void 0 : _detail$metadata.custom_product_bundle_map_id) !== null && _detail$metadata$cust !== void 0 ? _detail$metadata$cust : '', toComparableNumber(detail === null || detail === void 0 ? void 0 : detail.amount), toComparableNumber(detail === null || detail === void 0 || (_detail$discount3 = detail.discount) === null || _detail$discount3 === void 0 ? void 0 : _detail$discount3.fixed_amount)].join('|');
893
+ var rawUsageCount = Number((_ref16 = (_detail$metadata$num = detail === null || detail === void 0 || (_detail$metadata2 = detail.metadata) === null || _detail$metadata2 === void 0 ? void 0 : _detail$metadata2.num) !== null && _detail$metadata$num !== void 0 ? _detail$metadata$num : detail === null || detail === void 0 ? void 0 : detail._num) !== null && _ref16 !== void 0 ? _ref16 : 1);
894
+ var usageCount = Number.isFinite(rawUsageCount) && rawUsageCount > 0 ? rawUsageCount : 1;
895
+ var mergedIndex = mergedIndexByKey.get(key);
896
+ if (mergedIndex === undefined) {
897
+ mergedIndexByKey.set(key, mergedDetails.length);
898
+ mergedDetails.push(_objectSpread(_objectSpread({}, detail), {}, {
899
+ metadata: _objectSpread(_objectSpread({}, (detail === null || detail === void 0 ? void 0 : detail.metadata) || {}), {}, {
900
+ num: usageCount
901
+ }),
902
+ _num: usageCount
903
+ }));
904
+ continue;
905
+ }
906
+ var existed = mergedDetails[mergedIndex];
907
+ var existedUsageCount = Number((_ref17 = (_existed$metadata$num = existed === null || existed === void 0 || (_existed$metadata = existed.metadata) === null || _existed$metadata === void 0 ? void 0 : _existed$metadata.num) !== null && _existed$metadata$num !== void 0 ? _existed$metadata$num : existed === null || existed === void 0 ? void 0 : existed._num) !== null && _ref17 !== void 0 ? _ref17 : 1);
908
+ var totalUsageCount = existedUsageCount + usageCount;
909
+ mergedDetails[mergedIndex] = _objectSpread(_objectSpread({}, existed), {}, {
910
+ metadata: _objectSpread(_objectSpread({}, (existed === null || existed === void 0 ? void 0 : existed.metadata) || {}), {}, {
911
+ num: totalUsageCount
912
+ }),
913
+ _num: totalUsageCount
914
+ });
915
+ }
916
+ } catch (err) {
917
+ _iterator6.e(err);
918
+ } finally {
919
+ _iterator6.f();
920
+ }
921
+ return mergedDetails;
922
+ };
725
923
 
726
924
  // 记录每个优惠券适用的商品ID
727
925
  var discountApplicability = new Map();
728
926
 
729
927
  // 记录每个优惠券适用的商品详细信息
730
928
  var discountApplicableProducts = new Map();
929
+ var discountTimeReasons = new Map();
930
+ var discountHolderMismatch = new Set();
731
931
 
732
932
  // 初始化每个优惠券的适用性和可抵扣商品
733
933
  addModeDiscount.forEach(function (discount) {
@@ -747,7 +947,8 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
747
947
 
748
948
  // 辅助函数:检查商品是否对某个折扣卡可用
749
949
  var checkItemApplicableForDiscount = function checkItemApplicableForDiscount(flatItem, discount) {
750
- var _product, _product2, _product3, _flatItem$bundleItem, _flatItem$bundleItem2, _product4;
950
+ var _discount$config2, _product, _product2, _product3, _flatItem$bundleItem, _flatItem$bundleItem2, _product4;
951
+ if (((_discount$config2 = discount.config) === null || _discount$config2 === void 0 ? void 0 : _discount$config2.isAvailable) === false) return false;
751
952
  var product;
752
953
  if (flatItem.type === 'main') {
753
954
  product = flatItem.product;
@@ -770,7 +971,7 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
770
971
 
771
972
  // 编辑的商品使用了优惠券不可用
772
973
  var isAvailableProduct = flatItem.type === 'main' ? !((_product = product) !== null && _product !== void 0 && _product.booking_id && (_product2 = product) !== null && _product2 !== void 0 && (_product2 = _product2.discount_list) !== null && _product2 !== void 0 && _product2.length && (_product3 = product) !== null && _product3 !== void 0 && (_product3 = _product3.discount_list) !== null && _product3 !== void 0 && _product3.every(function (d) {
773
- return d.id && ['good_pass', 'discount_card', 'product_discount_card'].includes(d.tag || d.type);
974
+ return d.id && ['entitlement_pass', 'good_pass', 'discount_card', 'product_discount_card'].includes(d.tag || d.type);
774
975
  })) : !(flatItem !== null && flatItem !== void 0 && flatItem.booking_id && !!(flatItem !== null && flatItem !== void 0 && (_flatItem$bundleItem = flatItem.bundleItem) !== null && _flatItem$bundleItem !== void 0 && (_flatItem$bundleItem = _flatItem$bundleItem.discount_list) !== null && _flatItem$bundleItem !== void 0 && _flatItem$bundleItem.length) && flatItem !== null && flatItem !== void 0 && (_flatItem$bundleItem2 = flatItem.bundleItem) !== null && _flatItem$bundleItem2 !== void 0 && (_flatItem$bundleItem2 = _flatItem$bundleItem2.discount_list) !== null && _flatItem$bundleItem2 !== void 0 && _flatItem$bundleItem2.every(function (d) {
775
976
  return d.id;
776
977
  }));
@@ -870,7 +1071,7 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
870
1071
  // 收集该折扣卡适用的商品(排除被其他专属折扣卡占用的商品)
871
1072
  var applicableProducts = [];
872
1073
  sortedFlattenedList.forEach(function (flatItem) {
873
- var _flatItem$parentProdu2, _flatItem$parentProdu3, _product$price, _ref6, _flatItem$original_pr;
1074
+ var _flatItem$parentProdu2, _flatItem$parentProdu3, _product$price, _ref18, _flatItem$original_pr;
874
1075
  // 🔥 检查该商品是否被其他专属折扣卡占用
875
1076
  var occupyingDiscountId = occupiedItems.get(flatItem._id);
876
1077
  if (occupyingDiscountId !== undefined && occupyingDiscountId !== discount.id) {
@@ -898,7 +1099,7 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
898
1099
 
899
1100
  // 对于主商品:使用 price
900
1101
  // 对于子商品:优先使用 flatItem.original_price,否则使用 flatItem.price
901
- var originalAmount = flatItem.type === 'main' ? Number((_product$price = product.price) !== null && _product$price !== void 0 ? _product$price : 0) : Number((_ref6 = (_flatItem$original_pr = flatItem.original_price) !== null && _flatItem$original_pr !== void 0 ? _flatItem$original_pr : flatItem.price) !== null && _ref6 !== void 0 ? _ref6 : 0);
1102
+ var originalAmount = flatItem.type === 'main' ? Number((_product$price = product.price) !== null && _product$price !== void 0 ? _product$price : 0) : Number((_ref18 = (_flatItem$original_pr = flatItem.original_price) !== null && _flatItem$original_pr !== void 0 ? _flatItem$original_pr : flatItem.price) !== null && _ref18 !== void 0 ? _ref18 : 0);
902
1103
 
903
1104
  // 传递 parentQuantity 用于差值处理时判断是否是真正的"数量为1"
904
1105
  var productData = {
@@ -955,42 +1156,49 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
955
1156
  }
956
1157
  var isItemRewardProductDiscount = _this4.isItemRewardProductDiscount(product);
957
1158
  addModeDiscount.forEach(function (discount) {
958
- var _product5, _product6, _product7, _product8, _flatItem$bundleItem3, _flatItem$bundleItem4, _discount$config;
1159
+ var _product5, _product6, _product7, _product8, _flatItem$bundleItem3, _flatItem$bundleItem4, _discount$config3;
959
1160
  var limitedData = discount === null || discount === void 0 ? void 0 : discount.limited_relation_product_data;
960
1161
  // 拿到discount配置的holder信息 product信息 product.holder 加在 isLimitedProduct
961
1162
  var _tempVar = (flatItem === null || flatItem === void 0 ? void 0 : flatItem.type) === 'bundle' ? flatItem === null || flatItem === void 0 ? void 0 : flatItem.parentProduct : flatItem === null || flatItem === void 0 ? void 0 : flatItem.product;
962
1163
  var isHolderMatch = _this4.checkHolderMatch(discount, {
963
1164
  isNeedHolder: resolveIsFormSubject(_tempVar) && !(_tempVar !== null && _tempVar !== void 0 && _tempVar.isNormalProduct),
964
1165
  holder_id: (_tempVar === null || _tempVar === void 0 ? void 0 : _tempVar.holder_id) || product.holder_id
965
- }, holders);
966
- var timeLimit = true;
967
- timeLimit = !!filterDiscountListByBookingTime([discount], (((_product5 = product) === null || _product5 === void 0 ? void 0 : _product5.startDate) || dayjs()).format('YYYY-MM-DD HH:mm:ss')).length;
1166
+ }, flatItem.originProduct, holders);
1167
+ var timeReason = getDiscountTimeUnavailableReason(discount, (((_product5 = product) === null || _product5 === void 0 ? void 0 : _product5.startDate) || dayjs()).format('YYYY-MM-DD HH:mm:ss'));
1168
+ var timeLimit = !timeReason;
968
1169
  // 是符合折扣的商品
969
- var 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;
1170
+ var isProductInScope = 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);
1171
+ var isLimitedProduct = isProductInScope && isHolderMatch;
970
1172
 
971
1173
  // 编辑的商品 使用了优惠券不可用
972
1174
  var isAvailableProduct = flatItem.type === 'main' ? !((_product6 = product) !== null && _product6 !== void 0 && _product6.booking_id && (_product7 = product) !== null && _product7 !== void 0 && (_product7 = _product7.discount_list) !== null && _product7 !== void 0 && _product7.length && (_product8 = product) !== null && _product8 !== void 0 && (_product8 = _product8.discount_list) !== null && _product8 !== void 0 && _product8.every(function (discount) {
973
- return discount.id && ['good_pass', 'discount_card', 'product_discount_card'].includes(discount.tag || discount.type);
1175
+ return discount.id && ['entitlement_pass', 'good_pass', 'discount_card', 'product_discount_card'].includes(discount.tag || discount.type);
974
1176
  })) : !(flatItem !== null && flatItem !== void 0 && flatItem.booking_id && !!(flatItem !== null && flatItem !== void 0 && (_flatItem$bundleItem3 = flatItem.bundleItem) !== null && _flatItem$bundleItem3 !== void 0 && (_flatItem$bundleItem3 = _flatItem$bundleItem3.discount_list) !== null && _flatItem$bundleItem3 !== void 0 && _flatItem$bundleItem3.length) && flatItem !== null && flatItem !== void 0 && (_flatItem$bundleItem4 = flatItem.bundleItem) !== null && _flatItem$bundleItem4 !== void 0 && (_flatItem$bundleItem4 = _flatItem$bundleItem4.discount_list) !== null && _flatItem$bundleItem4 !== void 0 && _flatItem$bundleItem4.every(function (discount) {
975
1177
  return discount.id;
976
1178
  }));
977
1179
 
978
1180
  // 套餐是否可用判断
979
1181
  var isBundleAvailable = _this4.checkPackageSubItemUsageRules(discount, flatItem);
1182
+ if (isProductInScope && isBundleAvailable && !isItemRewardProductDiscount) {
1183
+ var timeReasons = discountTimeReasons.get(discount.id) || [];
1184
+ timeReasons.push(timeReason);
1185
+ discountTimeReasons.set(discount.id, timeReasons);
1186
+ if (!isHolderMatch && timeLimit) discountHolderMismatch.add(discount.id);
1187
+ }
980
1188
 
981
1189
  // 判断优惠券是否适用于该商品
982
- if (isAvailableProduct && !isItemRewardProductDiscount && isLimitedProduct && timeLimit && isBundleAvailable && (_discount$config = discount.config) !== null && _discount$config !== void 0 && _discount$config.isAvailable) {
983
- var _discountApplicabilit, _discount$metadata4, _discount$metadata5;
1190
+ if (isAvailableProduct && !isItemRewardProductDiscount && isLimitedProduct && timeLimit && isBundleAvailable && (_discount$config3 = discount.config) !== null && _discount$config3 !== void 0 && _discount$config3.isAvailable && (!isEntitlementPass(discount) || getRemainingEntitlementUses(discount, 0, 1) > 0)) {
1191
+ var _discountApplicabilit, _discount$metadata4, _discount$metadata5, _discount$metadata6;
984
1192
  // 记录此优惠券适用的商品
985
1193
  (_discountApplicabilit = discountApplicability.get(discount.id)) === null || _discountApplicabilit === void 0 || _discountApplicabilit.push(product.id);
986
1194
 
987
1195
  // 记录可抵扣的商品详情
988
1196
  var applicableProducts = discountApplicableProducts.get(discount.id) || [];
989
1197
  var discountType = discount.tag || discount.type;
990
- var isGoodPass = discountType === 'good_pass';
1198
+ var isFullCoverage = isFullCoveragePass(discount);
991
1199
 
992
1200
  // 使用数量
993
- var num = isGoodPass || (flatItem === null || flatItem === void 0 ? void 0 : flatItem.type) === 'main' ? 1 : product.num;
1201
+ var num = isFullCoverage || (flatItem === null || flatItem === void 0 ? void 0 : flatItem.type) === 'main' ? 1 : product.num;
994
1202
  var productData = {
995
1203
  amount: product.price * num,
996
1204
  type: discountType,
@@ -1007,6 +1215,7 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
1007
1215
  discount_product_id: discount.product_id
1008
1216
  },
1009
1217
  metadata: {
1218
+ encoded: ((_discount$metadata6 = discount.metadata) === null || _discount$metadata6 === void 0 ? void 0 : _discount$metadata6.encoded) || discount.encoded,
1010
1219
  num: num,
1011
1220
  discount_rule_uncheck_flag: discount === null || discount === void 0 ? void 0 : discount.discount_rule_uncheck_flag
1012
1221
  }
@@ -1028,13 +1237,13 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
1028
1237
  };
1029
1238
  var optionDiscountAmount = 0;
1030
1239
  var discountedOptions = options.map(function (option) {
1031
- var _ref7, _ref8, _option$_original_pri, _ref9, _option$num, _rest$price, _option$_original_pri2;
1240
+ var _ref19, _ref20, _option$_original_pri, _ref21, _option$num, _rest$price, _option$_original_pri2;
1032
1241
  // 折前单价:优先 price,缺失再 add_price;已写回折后价时用 _original_price(或旧字段 _original_add_price)
1033
- var rawUnit = (_ref7 = (_ref8 = (_option$_original_pri = option._original_price) !== null && _option$_original_pri !== void 0 ? _option$_original_pri : option._original_add_price) !== null && _ref8 !== void 0 ? _ref8 : option.price) !== null && _ref7 !== void 0 ? _ref7 : option.add_price;
1242
+ var rawUnit = (_ref19 = (_ref20 = (_option$_original_pri = option._original_price) !== null && _option$_original_pri !== void 0 ? _option$_original_pri : option._original_add_price) !== null && _ref20 !== void 0 ? _ref20 : option.price) !== null && _ref19 !== void 0 ? _ref19 : option.add_price;
1034
1243
  var baseUnitNum = Number(rawUnit !== null && rawUnit !== void 0 ? rawUnit : 0);
1035
1244
  if (baseUnitNum <= 0) return option;
1036
1245
  var discountedPrice = getDiscountAmount(discount, baseUnitNum, baseUnitNum);
1037
- var optQty = Number((_ref9 = (_option$num = option.num) !== null && _option$num !== void 0 ? _option$num : option.quantity) !== null && _ref9 !== void 0 ? _ref9 : 1);
1246
+ var optQty = Number((_ref21 = (_option$num = option.num) !== null && _option$num !== void 0 ? _option$num : option.quantity) !== null && _ref21 !== void 0 ? _ref21 : 1);
1038
1247
  optionDiscountAmount = new Decimal(optionDiscountAmount).plus(new Decimal(baseUnitNum).minus(discountedPrice).mul(optQty)).toNumber();
1039
1248
  var _original_add_price = option._original_add_price,
1040
1249
  rest = _objectWithoutProperties(option, _excluded);
@@ -1068,9 +1277,9 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
1068
1277
  var getOptionTotal = function getOptionTotal(options) {
1069
1278
  if (!(options !== null && options !== void 0 && options.length)) return 0;
1070
1279
  return options.reduce(function (sum, opt) {
1071
- var _ref10, _opt$price, _ref11, _opt$num;
1072
- var unit = Number((_ref10 = (_opt$price = opt.price) !== null && _opt$price !== void 0 ? _opt$price : opt.add_price) !== null && _ref10 !== void 0 ? _ref10 : 0);
1073
- var n = Number((_ref11 = (_opt$num = opt.num) !== null && _opt$num !== void 0 ? _opt$num : opt.quantity) !== null && _ref11 !== void 0 ? _ref11 : 1);
1280
+ var _ref22, _opt$price, _ref23, _opt$num;
1281
+ var unit = Number((_ref22 = (_opt$price = opt.price) !== null && _opt$price !== void 0 ? _opt$price : opt.add_price) !== null && _ref22 !== void 0 ? _ref22 : 0);
1282
+ var n = Number((_ref23 = (_opt$num = opt.num) !== null && _opt$num !== void 0 ? _opt$num : opt.quantity) !== null && _ref23 !== void 0 ? _ref23 : 1);
1074
1283
  return new Decimal(sum).plus(new Decimal(unit).mul(n)).toNumber();
1075
1284
  }, 0);
1076
1285
  };
@@ -1107,7 +1316,7 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
1107
1316
  var _discount$id, _discount$discount4;
1108
1317
  var discountIdentity = (_discount$id = discount.id) !== null && _discount$id !== void 0 ? _discount$id : (_discount$discount4 = discount.discount) === null || _discount$discount4 === void 0 ? void 0 : _discount$discount4.resource_id;
1109
1318
  var discountType = discount.tag || discount.type;
1110
- return Boolean(discountIdentity && ['good_pass', 'discount_card', 'product_discount_card'].includes(discountType));
1319
+ return Boolean(discountIdentity && ['entitlement_pass', 'good_pass', 'discount_card', 'product_discount_card'].includes(discountType));
1111
1320
  }));
1112
1321
 
1113
1322
  // 已持久化且已有钱包券/折扣卡的商品行跳过,保留后端历史折扣结果。
@@ -1126,7 +1335,7 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
1126
1335
  var applicableDiscounts = sortedDiscountList.filter(function (discount) {
1127
1336
  var _product$discount_lis3, _product$discount_lis4;
1128
1337
  if (_this4.isItemRewardProductDiscount(product)) return false;
1129
- // 🔥 检查策略可用性(针对 good_pass 和折扣卡)
1338
+ // 🔥 检查策略可用性(针对全额抵扣卡和折扣卡)
1130
1339
  var discountType = discount.tag || discount.type;
1131
1340
  var currentOriginUid = getOriginProductUid(originProduct);
1132
1341
  var isReapplyEditModeDiscountForProduct = discount.isEditMode && reapplyBookingDiscountProductUids.size > 0 && currentOriginUid && reapplyBookingDiscountProductUids.has(String(currentOriginUid)) && discount.isSelected === true;
@@ -1135,31 +1344,36 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
1135
1344
  if (!uid || !reapplyBookingDiscountProductUids.has(uid)) return false;
1136
1345
  if (!discount.isSelected) return false;
1137
1346
  }
1138
- if (['good_pass', 'discount_card', 'product_discount_card'].includes(discountType) && !isReapplyEditModeDiscountForProduct) {
1139
- var _discount$config2;
1347
+ if (['entitlement_pass', 'good_pass', 'discount_card', 'product_discount_card'].includes(discountType !== null && discountType !== void 0 ? discountType : '') && !isReapplyEditModeDiscountForProduct) {
1348
+ var _discount$config4;
1140
1349
  // 如果策略检查后没有config,说明不可用
1141
- if (discount.config === undefined || !(discount !== null && discount !== void 0 && (_discount$config2 = discount.config) !== null && _discount$config2 !== void 0 && _discount$config2.isAvailable)) {
1350
+ if (discount.config === undefined || !(discount !== null && discount !== void 0 && (_discount$config4 = discount.config) !== null && _discount$config4 !== void 0 && _discount$config4.isAvailable)) {
1142
1351
  return false;
1143
1352
  }
1144
1353
  }
1145
1354
 
1146
1355
  // 如果商品价格为 0,其实不需要使用任何优惠券,直接 return true
1147
- // 商品券时主商品价格为0不可用
1148
- if ((Number(product.price) <= 0 || !product.price) && !((_product$discount_lis3 = product.discount_list) !== null && _product$discount_lis3 !== void 0 && _product$discount_lis3.length) && (discount.tag || discount.type) === 'good_pass') return false;
1356
+ // 全额抵扣卡在主商品价格为 0 时不可用。
1357
+ if ((Number(product.price) <= 0 || !product.price) && !((_product$discount_lis3 = product.discount_list) !== null && _product$discount_lis3 !== void 0 && _product$discount_lis3.length) && isFullCoveragePass(discount)) return false;
1149
1358
 
1150
1359
  // 折扣卡商品价格为0时不可用
1151
1360
  if ((Number(product.price) <= 0 || !product.price) && !((_product$discount_lis4 = product.discount_list) !== null && _product$discount_lis4 !== void 0 && _product$discount_lis4.find(function (n) {
1152
1361
  var _n$discount;
1153
1362
  return ((_n$discount = n.discount) === null || _n$discount === void 0 ? void 0 : _n$discount.resource_id) === discount.id;
1154
- })) && (discount.tag || discount.type) !== 'good_pass') return false;
1155
- // 如果优惠券已被使用,则跳过
1156
- var targetUsedDiscounts = usedDiscounts.get(discount.id);
1157
- if (targetUsedDiscounts && (discount.tag || discount.type) === 'good_pass') return false;
1363
+ })) && !isFullCoveragePass(discount)) return false;
1364
+ // good_pass 保持一张实例只使用一次。
1365
+ if ((discount.tag || discount.type) === 'good_pass' && usedDiscounts.get(discount.id)) {
1366
+ return false;
1367
+ }
1368
+ // 有限次数卡按后端实时 balance 约束本次新增分配;无限卡不看余额。
1369
+ if (isEntitlementPass(discount) && getRemainingEntitlementUses(discount, entitlementUsageCounts.get(discount.id) || 0, 1) <= 0) {
1370
+ return false;
1371
+ }
1158
1372
 
1159
1373
  // 🔥 检查 maxUsagePerOrder 限制:同一 product_id 的商品券使用数量不能超过配置值
1160
- if ((discount.tag || discount.type) === 'good_pass') {
1161
- var _discount$config3;
1162
- var maxUsagePerOrder = (_discount$config3 = discount.config) === null || _discount$config3 === void 0 ? void 0 : _discount$config3.maxUsagePerOrder;
1374
+ if (isFullCoveragePass(discount)) {
1375
+ var _discount$config5;
1376
+ var maxUsagePerOrder = (_discount$config5 = discount.config) === null || _discount$config5 === void 0 ? void 0 : _discount$config5.maxUsagePerOrder;
1163
1377
  if (maxUsagePerOrder && maxUsagePerOrder > 0) {
1164
1378
  var currentUsedCount = usedProductIdCounts.get(discount.product_id) || 0;
1165
1379
  if (currentUsedCount >= maxUsagePerOrder) {
@@ -1174,7 +1388,7 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
1174
1388
  var isHolderMatch = _this4.checkHolderMatch(discount, {
1175
1389
  isNeedHolder: resolveIsFormSubject(_tempVar) && !(_tempVar !== null && _tempVar !== void 0 && _tempVar.isNormalProduct),
1176
1390
  holder_id: (_tempVar === null || _tempVar === void 0 ? void 0 : _tempVar.holder_id) || product.holder_id
1177
- }, holders);
1391
+ }, flatItem.originProduct, holders);
1178
1392
  // 如果 holder 不匹配,则不适用
1179
1393
  if (!isHolderMatch) return false;
1180
1394
  var timeLimit = true;
@@ -1203,41 +1417,50 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
1203
1417
  return false;
1204
1418
  });
1205
1419
 
1206
- // 用户显式点击某张卡券时,本次选择优先于自动排序和扫码默认选择。
1420
+ // 识别用户本次显式选择的卡券,供次卡和折扣卡各自的优先级使用。
1207
1421
  // 目标仍必须在当前商品的 applicableDiscounts 中,因此不会绕过 Holder、时间或商品范围校验。
1208
1422
  var explicitlySelectedDiscount = (options === null || options === void 0 ? void 0 : options.isSelected) === true && options.discountId !== undefined ? applicableDiscounts.find(function (discount) {
1209
1423
  var discountType = discount.tag || discount.type;
1210
- return String(discount.id) === String(options.discountId) && ['good_pass', 'discount_card', 'product_discount_card'].includes(discountType);
1424
+ return String(discount.id) === String(options.discountId) && ['entitlement_pass', 'good_pass', 'discount_card', 'product_discount_card'].includes(discountType !== null && discountType !== void 0 ? discountType : '');
1211
1425
  }) : undefined;
1212
1426
  // 折扣卡会复用于整条商品行;商品券必须继续按单张拆分,不能复用该分支。
1213
- var explicitlySelectedDiscountCard = explicitlySelectedDiscount && (explicitlySelectedDiscount.tag || explicitlySelectedDiscount.type) !== 'good_pass' ? explicitlySelectedDiscount : undefined;
1427
+ var explicitlySelectedDiscountCard = explicitlySelectedDiscount && !isFullCoveragePass(explicitlySelectedDiscount) ? explicitlySelectedDiscount : undefined;
1214
1428
 
1215
- // 商品数量等变化会触发无 discountId 的重算。只要当前行已应用的折扣卡仍在
1429
+ // 商品数量等变化会触发无 discountId 的重算。只要当前行已应用的卡仍在
1216
1430
  // applicableDiscounts 中,就应保留该行选择,避免被自动排序中力度更大的卡替换。
1217
- var appliedDiscountCardIds = new Set((product.discount_list || []).filter(function (item) {
1218
- return ['discount_card', 'product_discount_card'].includes((item === null || item === void 0 ? void 0 : item.tag) || (item === null || item === void 0 ? void 0 : item.type));
1431
+ var appliedWalletDiscountIds = new Set((product.discount_list || []).filter(function (item) {
1432
+ return ['entitlement_pass', 'discount_card', 'product_discount_card'].includes((item === null || item === void 0 ? void 0 : item.tag) || (item === null || item === void 0 ? void 0 : item.type));
1219
1433
  }).map(function (item) {
1220
- var _ref12, _item$discount$resour, _item$discount;
1221
- return (_ref12 = (_item$discount$resour = item === null || item === void 0 || (_item$discount = item.discount) === null || _item$discount === void 0 ? void 0 : _item$discount.resource_id) !== null && _item$discount$resour !== void 0 ? _item$discount$resour : item === null || item === void 0 ? void 0 : item.resource_id) !== null && _ref12 !== void 0 ? _ref12 : item === null || item === void 0 ? void 0 : item.id;
1434
+ var _ref24, _item$discount$resour, _item$discount;
1435
+ return (_ref24 = (_item$discount$resour = item === null || item === void 0 || (_item$discount = item.discount) === null || _item$discount === void 0 ? void 0 : _item$discount.resource_id) !== null && _item$discount$resour !== void 0 ? _item$discount$resour : item === null || item === void 0 ? void 0 : item.resource_id) !== null && _ref24 !== void 0 ? _ref24 : item === null || item === void 0 ? void 0 : item.id;
1222
1436
  }).filter(function (id) {
1223
1437
  return id !== undefined && id !== null;
1224
1438
  }).map(String));
1225
1439
  var appliedDiscountCard = options !== null && options !== void 0 && options.scan ? undefined : applicableDiscounts.find(function (discount) {
1226
1440
  var discountType = discount.tag || discount.type;
1227
- return discount.isSelected === true && ['discount_card', 'product_discount_card'].includes(discountType) && appliedDiscountCardIds.has(String(discount.id));
1441
+ return discount.isSelected === true && ['discount_card', 'product_discount_card'].includes(discountType !== null && discountType !== void 0 ? discountType : '') && appliedWalletDiscountIds.has(String(discount.id));
1442
+ });
1443
+ var appliedEntitlementPass = options !== null && options !== void 0 && options.scan || (options === null || options === void 0 ? void 0 : options.preserveEntitlementSelection) === false ? undefined : applicableDiscounts.find(function (discount) {
1444
+ return isEntitlementPass(discount) && discount.isSelected === true && appliedWalletDiscountIds.has(String(discount.id));
1228
1445
  });
1229
1446
 
1230
- // 优先级:本次显式选择 > 当前行既有选择 > 扫码选择 > 自动排序第一项。
1447
+ // 次卡内部按本次手选/扫码、当前行已应用、默认排序选择。
1231
1448
  var scannedSelectedDiscountCard = applicableDiscounts.find(function (n) {
1232
- return n.isScan && n.isSelected && (n.tag || n.type) !== 'good_pass';
1449
+ return n.isScan && n.isSelected && !isFullCoveragePass(n);
1233
1450
  });
1234
1451
  var selectedDiscountCard = explicitlySelectedDiscountCard || appliedDiscountCard || scannedSelectedDiscountCard;
1235
- var selectedDiscount = explicitlySelectedDiscount || selectedDiscountCard || applicableDiscounts[0];
1236
- var prioritizedApplicableDiscounts = explicitlySelectedDiscount && (explicitlySelectedDiscount.tag || explicitlySelectedDiscount.type) === 'good_pass' ? [explicitlySelectedDiscount].concat(_toConsumableArray(applicableDiscounts.filter(function (discount) {
1237
- var discountType = discount.tag || discount.type;
1238
- return discountType === 'good_pass' && String(discount.id) !== String(explicitlySelectedDiscount.id);
1452
+ var selectedEntitlementPass = (explicitlySelectedDiscount && isEntitlementPass(explicitlySelectedDiscount) ? explicitlySelectedDiscount : undefined) || applicableDiscounts.find(function (discount) {
1453
+ return isEntitlementPass(discount) && preferredEntitlementPassIds.has(String(discount.id));
1454
+ }) || appliedEntitlementPass || applicableDiscounts.find(function (discount) {
1455
+ return isEntitlementPass(discount);
1456
+ });
1457
+ // 手选优先;普通重算保留当前折扣卡,扫码和最优选择仍使用原有优先级。
1458
+ var selectedDiscount = explicitlySelectedDiscount || ((options === null || options === void 0 ? void 0 : options.preserveEntitlementSelection) !== false && preferredEntitlementPassIds.size === 0 ? appliedDiscountCard : undefined) || selectedEntitlementPass || selectedDiscountCard || applicableDiscounts[0];
1459
+ // 实际次数分配必须使用同一顺序,避免选中状态切换后仍将次数分配给默认卡。
1460
+ var prioritizedApplicableDiscounts = selectedDiscount && isFullCoveragePass(selectedDiscount) ? [selectedDiscount].concat(_toConsumableArray(applicableDiscounts.filter(function (discount) {
1461
+ return isFullCoveragePass(discount) && String(discount.id) !== String(selectedDiscount.id);
1239
1462
  })), _toConsumableArray(applicableDiscounts.filter(function (discount) {
1240
- return (discount.tag || discount.type) !== 'good_pass';
1463
+ return !isFullCoveragePass(discount);
1241
1464
  }))) : applicableDiscounts;
1242
1465
 
1243
1466
  // 如果是手动折扣,则不适用优惠券
@@ -1249,8 +1472,8 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
1249
1472
  isManualDiscount = typeof product.isManualDiscount === 'boolean' ? product.isManualDiscount : ((_product$discount_lis5 = product.discount_list) === null || _product$discount_lis5 === void 0 ? void 0 : _product$discount_lis5.some(function (item) {
1250
1473
  return item.type === 'product';
1251
1474
  })) || product.total != product.origin_total && (product.bundle || []).every(function (item) {
1252
- var _ref13;
1253
- return !((_ref13 = item.discount_list || []) !== null && _ref13 !== void 0 && _ref13.length);
1475
+ var _ref25;
1476
+ return !((_ref25 = item.discount_list || []) !== null && _ref25 !== void 0 && _ref25.length);
1254
1477
  }) && (!((_product$discount_lis6 = product.discount_list) !== null && _product$discount_lis6 !== void 0 && _product$discount_lis6.length) || ((_product11 = product) === null || _product11 === void 0 || (_product11 = _product11.discount_list) === null || _product11 === void 0 || (_product11$every = _product11.every) === null || _product11$every === void 0 ? void 0 : _product11$every.call(_product11, function (item) {
1255
1478
  return item.type === 'product';
1256
1479
  })));
@@ -1266,8 +1489,8 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
1266
1489
  isManualDiscount = typeof parentProduct.isManualDiscount === 'boolean' ? parentProduct.isManualDiscount : ((_parentProduct$discou = parentProduct.discount_list) === null || _parentProduct$discou === void 0 ? void 0 : _parentProduct$discou.some(function (item) {
1267
1490
  return item.type === 'product';
1268
1491
  })) || parentProduct.total != parentProduct.origin_total && (parentProduct.bundle || []).every(function (item) {
1269
- var _ref14;
1270
- return !((_ref14 = item.discount_list || []) !== null && _ref14 !== void 0 && _ref14.length);
1492
+ var _ref26;
1493
+ return !((_ref26 = item.discount_list || []) !== null && _ref26 !== void 0 && _ref26.length);
1271
1494
  }) && (!((_parentProduct$discou2 = parentProduct.discount_list) !== null && _parentProduct$discou2 !== void 0 && _parentProduct$discou2.length) || (parentProduct === null || parentProduct === void 0 || (_parentProduct$discou3 = parentProduct.discount_list) === null || _parentProduct$discou3 === void 0 || (_parentProduct$discou4 = _parentProduct$discou3.every) === null || _parentProduct$discou4 === void 0 ? void 0 : _parentProduct$discou4.call(_parentProduct$discou3, function (item) {
1272
1495
  return item.type === 'product';
1273
1496
  })));
@@ -1360,8 +1583,8 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
1360
1583
  discount_list: isManualDiscount ? _this4.resolveDiscountListForManualOverride(product.discount_list) : _this4.filterDiscountListByType(product.discount_list, 'promotion')
1361
1584
  }))]);
1362
1585
  } else {
1363
- var _ref15, _product$_promotion$f, _product12, _product$origin_total;
1364
- var total = product.inPromotion ? (_ref15 = (_product$_promotion$f = (_product12 = product) === null || _product12 === void 0 || (_product12 = _product12._promotion) === null || _product12 === void 0 ? void 0 : _product12.finalPrice) !== null && _product$_promotion$f !== void 0 ? _product$_promotion$f : product.origin_total) !== null && _ref15 !== void 0 ? _ref15 : product.total : (_product$origin_total = product.origin_total) !== null && _product$origin_total !== void 0 ? _product$origin_total : product.total;
1586
+ var _ref27, _product$_promotion$f, _product12, _product$origin_total;
1587
+ var total = product.inPromotion ? (_ref27 = (_product$_promotion$f = (_product12 = product) === null || _product12 === void 0 || (_product12 = _product12._promotion) === null || _product12 === void 0 ? void 0 : _product12.finalPrice) !== null && _product$_promotion$f !== void 0 ? _product$_promotion$f : product.origin_total) !== null && _ref27 !== void 0 ? _ref27 : product.total : (_product$origin_total = product.origin_total) !== null && _product$origin_total !== void 0 ? _product$origin_total : product.total;
1365
1588
  var main_product_selling_price = product.price;
1366
1589
  if ((product.discount_list || []).some(function (item) {
1367
1590
  return item.type === 'promotion';
@@ -1402,21 +1625,15 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
1402
1625
  return;
1403
1626
  }
1404
1627
 
1405
- // 是否需要拆分(商品券需要拆分)
1406
- var isNeedSplit = (selectedDiscount.tag || selectedDiscount.type) === 'good_pass';
1628
+ // good_pass 与 Entitlement Pass 都沿用全额抵扣卡的商品行拆分机制。
1629
+ var isNeedSplit = isFullCoveragePass(selectedDiscount);
1407
1630
 
1408
1631
  // 需要拆分出来的数量
1409
1632
  var totalQuantity = product.quantity || product.num || 1;
1410
- var availableGoodPassCount = applicableDiscounts.filter(function (item) {
1411
- return (item.tag || item.type) === 'good_pass';
1412
- }).length;
1413
-
1414
- // 🔥 如果selectedDiscount有config配置,则使用config.maxUsagePerOrder限制使用次数
1415
- var maxUsageLimit = availableGoodPassCount;
1416
- if (selectedDiscount.config && selectedDiscount.config.maxUsagePerOrder) {
1417
- maxUsageLimit = Math.min(availableGoodPassCount, selectedDiscount.config.maxUsagePerOrder);
1418
- }
1419
- var splitCount = isNeedSplit ? Math.min(product.quantity || product.num || 1, maxUsageLimit) : 1;
1633
+ var fullCoveragePassUsageGroups = isNeedSplit ? buildFullCoveragePassUsageGroups(prioritizedApplicableDiscounts, totalQuantity) : [];
1634
+ var splitCount = isNeedSplit ? fullCoveragePassUsageGroups.reduce(function (total, group) {
1635
+ return total + group.count;
1636
+ }, 0) : 1;
1420
1637
  var arr = [];
1421
1638
 
1422
1639
  // 🔥 主商品和bundle子商品分别处理
@@ -1425,8 +1642,8 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
1425
1642
  if (splitCount < totalQuantity && isNeedSplit) {
1426
1643
  var _product$origin_total2, _product$main_product2;
1427
1644
  var remainingQuantity = totalQuantity - splitCount;
1428
- var applicableNonGoodPassById = new Map(applicableDiscounts.filter(function (discount) {
1429
- return (discount.tag || discount.type) !== 'good_pass';
1645
+ var applicableNonFullCoveragePassById = new Map(applicableDiscounts.filter(function (discount) {
1646
+ return !isFullCoveragePass(discount);
1430
1647
  }).map(function (discount) {
1431
1648
  return [String(discount.id), discount];
1432
1649
  }));
@@ -1434,19 +1651,19 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
1434
1651
  var _discount$discount$re, _discount$discount5;
1435
1652
  var discountType = discount.tag || discount.type;
1436
1653
  if (discountType === 'promotion') return true;
1437
- if (discountType === 'good_pass') return false;
1654
+ if (isFullCoveragePass(discount)) return false;
1438
1655
  var resourceId = (_discount$discount$re = (_discount$discount5 = discount.discount) === null || _discount$discount5 === void 0 ? void 0 : _discount$discount5.resource_id) !== null && _discount$discount$re !== void 0 ? _discount$discount$re : discount.id;
1439
- return resourceId !== undefined && resourceId !== null && applicableNonGoodPassById.has(String(resourceId));
1656
+ return resourceId !== undefined && resourceId !== null && applicableNonFullCoveragePassById.has(String(resourceId));
1440
1657
  }).map(function (discount) {
1441
1658
  var _discount$discount$re2, _discount$discount6;
1442
1659
  if ((discount.tag || discount.type) === 'promotion') return discount;
1443
1660
  var resourceId = (_discount$discount$re2 = (_discount$discount6 = discount.discount) === null || _discount$discount6 === void 0 ? void 0 : _discount$discount6.resource_id) !== null && _discount$discount$re2 !== void 0 ? _discount$discount$re2 : discount.id;
1444
- var matchedDiscount = applicableNonGoodPassById.get(String(resourceId));
1661
+ var matchedDiscount = applicableNonFullCoveragePassById.get(String(resourceId));
1445
1662
  if (matchedDiscount) {
1446
1663
  // 实际场景:3 件折扣卡商品中拆出 1 件改用商品券后,
1447
1664
  // 剩余 2 件仍在使用原折扣卡;必须同步登记折扣卡的使用状态,
1448
1665
  // 否则 updatedDiscountList 会把它改成未选中并清空 savedAmount。
1449
- usedDiscounts.set(matchedDiscount.id, true);
1666
+ markDiscountUsed(matchedDiscount);
1450
1667
  var appliedProducts = appliedDiscountProducts.get(matchedDiscount.id) || [];
1451
1668
  appliedProducts.push(_objectSpread(_objectSpread({}, discount), {}, {
1452
1669
  _num: remainingQuantity
@@ -1459,7 +1676,7 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
1459
1676
  });
1460
1677
  });
1461
1678
  var hasRetainedWalletDiscount = remainderDiscountList.some(function (discount) {
1462
- return !['promotion', 'good_pass'].includes(discount.tag || discount.type);
1679
+ return (discount.tag || discount.type) !== 'promotion' && !isFullCoveragePass(discount);
1463
1680
  });
1464
1681
  var _total = (_product$origin_total2 = product.origin_total) !== null && _product$origin_total2 !== void 0 ? _product$origin_total2 : product.total;
1465
1682
  if ((product.discount_list || []).some(function (item) {
@@ -1470,7 +1687,7 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
1470
1687
  }
1471
1688
  arr.push(_this4.hooks.setProduct(originProduct, _objectSpread({
1472
1689
  // 商品券只替换被拆出的 splitCount 件。余量行原本仍有效的折扣卡
1473
- // 不能随 good_pass 拆分一起清除;已取消或已失效的折扣不会进入此列表。
1690
+ // 不能随全额抵扣卡拆分一起清除;已取消或已失效的折扣不会进入此列表。
1474
1691
  discount_list: remainderDiscountList,
1475
1692
  quantity: remainingQuantity,
1476
1693
  _id: product._id.split('___')[0],
@@ -1479,15 +1696,21 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
1479
1696
  main_product_selling_price: (_product$main_product2 = product.main_product_selling_price) !== null && _product$main_product2 !== void 0 ? _product$main_product2 : product.price
1480
1697
  } : {})));
1481
1698
  }
1482
- for (var i = 0; i < splitCount; i++) {
1483
- var _originProduct4, _selectedDiscount$con, _product$options, _product$options2, _selectedDiscount$met, _selectedDiscount$met2, _reapplyDiscountPerce;
1484
- var _selectedDiscount = isNeedSplit ? prioritizedApplicableDiscounts[i] : selectedDiscountCard || applicableDiscounts[i];
1699
+ var discountUsageGroups = isNeedSplit ? fullCoveragePassUsageGroups : [{
1700
+ discount: selectedDiscount,
1701
+ count: 1
1702
+ }];
1703
+ for (var i = 0; i < discountUsageGroups.length; i++) {
1704
+ var _originProduct4, _selectedDiscount$con, _product$options, _product$options2, _selectedDiscount$met, _selectedDiscount$met2, _reapplyDiscountPerce, _selectedDiscount$met3;
1705
+ var _discountUsageGroups$ = discountUsageGroups[i],
1706
+ _selectedDiscount = _discountUsageGroups$.discount,
1707
+ appliedUseCount = _discountUsageGroups$.count;
1485
1708
  // 标记优惠券为已使用
1486
- usedDiscounts.set(_selectedDiscount.id, true);
1709
+ markDiscountUsed(_selectedDiscount, appliedUseCount);
1487
1710
  // 🔥 更新 product_id 使用计数
1488
- if ((_selectedDiscount.tag || _selectedDiscount.type) === 'good_pass') {
1711
+ if (isFullCoveragePass(_selectedDiscount)) {
1489
1712
  var currentCount = usedProductIdCounts.get(_selectedDiscount.product_id) || 0;
1490
- usedProductIdCounts.set(_selectedDiscount.product_id, currentCount + 1);
1713
+ usedProductIdCounts.set(_selectedDiscount.product_id, currentCount + appliedUseCount);
1491
1714
  }
1492
1715
 
1493
1716
  // 记录实际应用了优惠券的商品信息
@@ -1549,7 +1772,7 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
1549
1772
  var mainProductDiscountAmount = amount;
1550
1773
  var lineDiscountAmount = new Decimal(mainProductDiscountAmount).plus(optionDiscountAmount).toNumber();
1551
1774
  var discountType = _selectedDiscount.tag || _selectedDiscount.type;
1552
- var isGoodPass = discountType === 'good_pass';
1775
+ var isFullCoverage = isFullCoveragePass(_selectedDiscount);
1553
1776
  var discountDetail = {
1554
1777
  amount: lineDiscountAmount,
1555
1778
  type: _selectedDiscount.tag === 'product_discount_card' ? 'discount_card' : discountType,
@@ -1565,10 +1788,11 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
1565
1788
  discount_product_id: _selectedDiscount.product_id
1566
1789
  },
1567
1790
  // 前端使用的num数量,为了计算优惠金额
1568
- _num: isGoodPass ? 1 : product.num,
1791
+ _num: isFullCoverage ? appliedUseCount : product.num,
1569
1792
  config: _selectedDiscount === null || _selectedDiscount === void 0 ? void 0 : _selectedDiscount.config,
1570
1793
  metadata: _objectSpread(_objectSpread({
1571
- num: 1,
1794
+ encoded: ((_selectedDiscount$met3 = _selectedDiscount.metadata) === null || _selectedDiscount$met3 === void 0 ? void 0 : _selectedDiscount$met3.encoded) || _selectedDiscount.encoded,
1795
+ num: isFullCoverage ? appliedUseCount : 1,
1572
1796
  discount_rule_uncheck_flag: _selectedDiscount === null || _selectedDiscount === void 0 ? void 0 : _selectedDiscount.discount_rule_uncheck_flag
1573
1797
  }, productDiscountDifference !== undefined && {
1574
1798
  product_discount_difference: productDiscountDifference
@@ -1586,9 +1810,9 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
1586
1810
  if (product.isClient) {
1587
1811
  arr.push(_this4.hooks.setProduct(originProduct, {
1588
1812
  discount_list: [discountDetail],
1589
- // good_pass:主商品价以折后价为准;勿用 product.price - amount(amount 含 option 优惠时会算错)
1590
- price: isGoodPass ? mainProductSellingPrice : product.price,
1591
- quantity: isNeedSplit ? 1 : product.quantity,
1813
+ // 全额抵扣卡:主商品价以折后价为准;勿用 product.price - amount(amount 含 option 优惠时会算错)
1814
+ price: isFullCoverage ? mainProductSellingPrice : product.price,
1815
+ quantity: isNeedSplit ? appliedUseCount : product.quantity,
1592
1816
  origin_total: getProductOriginTotalPrice({
1593
1817
  product: {
1594
1818
  original_price: product.original_price
@@ -1605,8 +1829,8 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
1605
1829
  arr.push(_this4.hooks.setProduct(originProduct, {
1606
1830
  discount_list: _this4.filterDiscountListByType(product.discount_list, 'promotion').concat([discountDetail]),
1607
1831
  _id: product._id.split('___')[0] + '___' + _selectedDiscount.id + index,
1608
- price: _selectedDiscount.tag === 'good_pass' ? 0 : product.price,
1609
- quantity: isNeedSplit ? 1 : product.quantity,
1832
+ price: isFullCoverage ? 0 : product.price,
1833
+ quantity: isNeedSplit ? appliedUseCount : product.quantity,
1610
1834
  total: _total2,
1611
1835
  origin_total: productOriginTotal,
1612
1836
  main_product_selling_price: mainProductSellingPrice,
@@ -1623,15 +1847,17 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
1623
1847
  var discountNum = splitCount;
1624
1848
  var normalNum = totalQuantity - discountNum;
1625
1849
 
1626
- // 生成有折扣的商品(每张商品券对应 num: 1)
1627
- for (var _i = 0; _i < discountNum; _i++) {
1628
- var _selectedDiscount2$me;
1629
- var _selectedDiscount2 = prioritizedApplicableDiscounts[_i];
1630
- usedDiscounts.set(_selectedDiscount2.id, true);
1850
+ // 同一卡实例的本次新增使用合并为一条,metadata.num 记录实际次数。
1851
+ for (var _i = 0; _i < fullCoveragePassUsageGroups.length; _i++) {
1852
+ var _selectedDiscount2$me, _selectedDiscount2$me2;
1853
+ var _fullCoveragePassUsag = fullCoveragePassUsageGroups[_i],
1854
+ _selectedDiscount2 = _fullCoveragePassUsag.discount,
1855
+ _appliedUseCount = _fullCoveragePassUsag.count;
1856
+ markDiscountUsed(_selectedDiscount2, _appliedUseCount);
1631
1857
  // 🔥 更新 product_id 使用计数
1632
- if ((_selectedDiscount2.tag || _selectedDiscount2.type) === 'good_pass') {
1858
+ if (isFullCoveragePass(_selectedDiscount2)) {
1633
1859
  var _currentCount = usedProductIdCounts.get(_selectedDiscount2.product_id) || 0;
1634
- usedProductIdCounts.set(_selectedDiscount2.product_id, _currentCount + 1);
1860
+ usedProductIdCounts.set(_selectedDiscount2.product_id, _currentCount + _appliedUseCount);
1635
1861
  }
1636
1862
 
1637
1863
  // 🔥 生成唯一的 _id
@@ -1639,11 +1865,12 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
1639
1865
 
1640
1866
  // 计算折扣后的价格(使用 getDiscountAmount,内部已处理 maxDeductionAmount)
1641
1867
  var discountedPrice = getDiscountAmount(_selectedDiscount2, product.origin_total, product.origin_total);
1642
- var bundleDiscountAmount = new Decimal(product.origin_total || 0).minus(discountedPrice).toNumber();
1868
+ var bundleDiscountAmountPerItem = new Decimal(product.origin_total || 0).minus(discountedPrice).toNumber();
1869
+ var bundleDiscountAmount = new Decimal(bundleDiscountAmountPerItem).mul(_appliedUseCount).toNumber();
1643
1870
  debugger;
1644
1871
  var _discountDetail = {
1645
1872
  amount: bundleDiscountAmount,
1646
- type: 'good_pass',
1873
+ type: _selectedDiscount2.tag || _selectedDiscount2.type,
1647
1874
  discount: {
1648
1875
  fixed_amount: product.origin_total,
1649
1876
  discount_calculation_mode: _selectedDiscount2 === null || _selectedDiscount2 === void 0 || (_selectedDiscount2$me = _selectedDiscount2.metadata) === null || _selectedDiscount2$me === void 0 ? void 0 : _selectedDiscount2$me.discount_calculation_mode,
@@ -1654,12 +1881,13 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
1654
1881
  discount_product_id: _selectedDiscount2.product_id
1655
1882
  },
1656
1883
  metadata: {
1884
+ encoded: ((_selectedDiscount2$me2 = _selectedDiscount2.metadata) === null || _selectedDiscount2$me2 === void 0 ? void 0 : _selectedDiscount2$me2.encoded) || _selectedDiscount2.encoded,
1657
1885
  // 🔥 使用拆分后的唯一 _id
1658
1886
  custom_product_bundle_map_id: uniqueId,
1659
1887
  discount_rule_uncheck_flag: _selectedDiscount2 === null || _selectedDiscount2 === void 0 ? void 0 : _selectedDiscount2.discount_rule_uncheck_flag,
1660
- num: 1
1888
+ num: _appliedUseCount
1661
1889
  },
1662
- _num: 1,
1890
+ _num: _appliedUseCount,
1663
1891
  config: _selectedDiscount2 === null || _selectedDiscount2 === void 0 ? void 0 : _selectedDiscount2.config
1664
1892
  };
1665
1893
 
@@ -1670,11 +1898,11 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
1670
1898
  processedItems.push(_objectSpread(_objectSpread({}, flatItem), {}, {
1671
1899
  // 🔥 使用唯一的 _id
1672
1900
  _id: uniqueId,
1673
- num: 1,
1674
- quantity: 1,
1901
+ num: _appliedUseCount,
1902
+ quantity: _appliedUseCount,
1675
1903
  price: discountedPrice,
1676
1904
  // 折扣后的价格
1677
- total: discountedPrice,
1905
+ total: new Decimal(discountedPrice).mul(_appliedUseCount).toNumber(),
1678
1906
  discount_list: [_discountDetail],
1679
1907
  processed: true,
1680
1908
  _discountId: _selectedDiscount2.id
@@ -1693,12 +1921,12 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
1693
1921
  }));
1694
1922
  }
1695
1923
  } else {
1696
- var _selectedDiscount3$me, _selectedDiscount3$me2, _reapplyDiscountPerce2, _flatItem$parentProdu9;
1924
+ var _selectedDiscount3$me, _selectedDiscount3$me2, _reapplyDiscountPerce2, _selectedDiscount3$me3, _flatItem$parentProdu9;
1697
1925
  // 折扣卡:不拆分数量,直接应用
1698
1926
  var _selectedDiscount3 = selectedDiscountCard || applicableDiscounts[0];
1699
- usedDiscounts.set(_selectedDiscount3.id, true);
1927
+ markDiscountUsed(_selectedDiscount3);
1700
1928
  // 🔥 更新 product_id 使用计数(虽然此处主要是折扣卡,但保持一致性)
1701
- if ((_selectedDiscount3.tag || _selectedDiscount3.type) === 'good_pass') {
1929
+ if (isFullCoveragePass(_selectedDiscount3)) {
1702
1930
  var _currentCount2 = usedProductIdCounts.get(_selectedDiscount3.product_id) || 0;
1703
1931
  usedProductIdCounts.set(_selectedDiscount3.product_id, _currentCount2 + 1);
1704
1932
  }
@@ -1748,6 +1976,7 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
1748
1976
  discount_product_id: _selectedDiscount3.product_id
1749
1977
  },
1750
1978
  metadata: _objectSpread({
1979
+ encoded: ((_selectedDiscount3$me3 = _selectedDiscount3.metadata) === null || _selectedDiscount3$me3 === void 0 ? void 0 : _selectedDiscount3$me3.encoded) || _selectedDiscount3.encoded,
1751
1980
  // 🔥 使用唯一的 _id
1752
1981
  custom_product_bundle_map_id: _uniqueId,
1753
1982
  discount_rule_uncheck_flag: _selectedDiscount3 === null || _selectedDiscount3 === void 0 ? void 0 : _selectedDiscount3.discount_rule_uncheck_flag,
@@ -1828,11 +2057,10 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
1828
2057
  // 没有bundle,直接使用主商品处理结果
1829
2058
  result.push.apply(result, _toConsumableArray(mainProductArr));
1830
2059
  } else {
1831
- // 🔥 有bundle,需要检查子商品是否应用了商品券
2060
+ // 🔥 有 bundle,需要检查子商品是否应用了全额抵扣卡
1832
2061
  // 1. 先收集所有bundle子商品的处理结果
1833
2062
  var bundleProcessingInfo = new Map();
1834
- var hasGoodPassApplied = false; // 标记是否有子商品应用了商品券
1835
-
2063
+ var hasFullCoveragePassApplied = false;
1836
2064
  if (product.bundle && Array.isArray(product.bundle)) {
1837
2065
  product.bundle.forEach(function (bundleItem, bundleIndex) {
1838
2066
  var bundleItemId = "".concat(product._id, "_bundle_").concat(bundleIndex);
@@ -1843,15 +2071,14 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
1843
2071
  } else {
1844
2072
  // 处理过的bundle item
1845
2073
  bundleProcessingInfo.set(bundleIndex, processedBundleItems);
1846
- // 🔥 检查是否应用了商品券(good_pass)
1847
- var hasGoodPass = processedBundleItems.some(function (item) {
2074
+ var hasFullCoveragePass = processedBundleItems.some(function (item) {
1848
2075
  var _item$discount_list;
1849
2076
  return (_item$discount_list = item.discount_list) === null || _item$discount_list === void 0 ? void 0 : _item$discount_list.some(function (discount) {
1850
- return discount.type === 'good_pass' || discount.tag === 'good_pass';
2077
+ return isFullCoveragePass(discount);
1851
2078
  });
1852
2079
  });
1853
- if (hasGoodPass) {
1854
- hasGoodPassApplied = true;
2080
+ if (hasFullCoveragePass) {
2081
+ hasFullCoveragePassApplied = true;
1855
2082
  }
1856
2083
  }
1857
2084
  });
@@ -1859,7 +2086,7 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
1859
2086
 
1860
2087
  // 🔥 2. 如果有子商品应用了商品券,且主商品数量大于1,需要拆分主商品
1861
2088
  var mainProductQuantity = mainProductArr[0] ? _this4.hooks.getProduct(mainProductArr[0]).quantity : 1;
1862
- if (hasGoodPassApplied && mainProductArr.length === 1 && mainProductQuantity > 1) {
2089
+ if (hasFullCoveragePassApplied && mainProductArr.length === 1 && mainProductQuantity > 1) {
1863
2090
  var mainProduct = mainProductArr[0];
1864
2091
  var mainProductData = _this4.hooks.getProduct(mainProduct);
1865
2092
 
@@ -1927,8 +2154,8 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
1927
2154
 
1928
2155
  // 🔥 更新主商品自己的 discount_list 中的 num(quantity=1)
1929
2156
  var updatedMainDiscountList = mainProductData.discount_list.map(function (discount) {
1930
- var _discount$metadata6, _discount$metadata7;
1931
- if (discount !== null && discount !== void 0 && (_discount$metadata6 = discount.metadata) !== null && _discount$metadata6 !== void 0 && _discount$metadata6.custom_product_bundle_map_id) {
2157
+ var _discount$metadata7, _discount$metadata8;
2158
+ if (discount !== null && discount !== void 0 && (_discount$metadata7 = discount.metadata) !== null && _discount$metadata7 !== void 0 && _discount$metadata7.custom_product_bundle_map_id) {
1932
2159
  // bundle的discount_list保持不变
1933
2160
  return discount;
1934
2161
  }
@@ -1936,7 +2163,7 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
1936
2163
  return _objectSpread(_objectSpread({}, discount), {}, {
1937
2164
  // num: 1,
1938
2165
  metadata: _objectSpread(_objectSpread({}, discount.metadata), {}, {
1939
- custom_product_bundle_map_id: discount === null || discount === void 0 || (_discount$metadata7 = discount.metadata) === null || _discount$metadata7 === void 0 ? void 0 : _discount$metadata7.custom_product_bundle_map_id,
2166
+ custom_product_bundle_map_id: discount === null || discount === void 0 || (_discount$metadata8 = discount.metadata) === null || _discount$metadata8 === void 0 ? void 0 : _discount$metadata8.custom_product_bundle_map_id,
1940
2167
  num: 1
1941
2168
  })
1942
2169
  });
@@ -2001,8 +2228,8 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
2001
2228
 
2002
2229
  // 🔥 更新主商品自己的 discount_list 中的 num(quantity=原quantity-1)
2003
2230
  var updatedMainDiscountListOriginal = mainProductData.discount_list.map(function (discount) {
2004
- var _discount$metadata8, _discount$metadata9;
2005
- if (discount !== null && discount !== void 0 && (_discount$metadata8 = discount.metadata) !== null && _discount$metadata8 !== void 0 && _discount$metadata8.custom_product_bundle_map_id) {
2231
+ var _discount$metadata9, _discount$metadata10;
2232
+ if (discount !== null && discount !== void 0 && (_discount$metadata9 = discount.metadata) !== null && _discount$metadata9 !== void 0 && _discount$metadata9.custom_product_bundle_map_id) {
2006
2233
  // bundle的discount_list保持不变
2007
2234
  return discount;
2008
2235
  }
@@ -2010,7 +2237,7 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
2010
2237
  return _objectSpread(_objectSpread({}, discount), {}, {
2011
2238
  metadata: _objectSpread(_objectSpread({}, discount.metadata), {}, {
2012
2239
  num: mainProductQuantity - 1,
2013
- custom_product_bundle_map_id: discount === null || discount === void 0 || (_discount$metadata9 = discount.metadata) === null || _discount$metadata9 === void 0 ? void 0 : _discount$metadata9.custom_product_bundle_map_id
2240
+ custom_product_bundle_map_id: discount === null || discount === void 0 || (_discount$metadata10 = discount.metadata) === null || _discount$metadata10 === void 0 ? void 0 : _discount$metadata10.custom_product_bundle_map_id
2014
2241
  })
2015
2242
  // num: mainProductQuantity - 1,
2016
2243
  });
@@ -2060,7 +2287,7 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
2060
2287
  } else {
2061
2288
  // 🔥 没有子商品被拆分,使用原有逻辑
2062
2289
  mainProductArr.forEach(function (mainProduct) {
2063
- var _ref17, _mainProductData$main, _mainProductData$disc, _mainProductData$disc2, _mainProductData$disc3;
2290
+ var _ref29, _mainProductData$main, _mainProductData$disc, _mainProductData$disc2, _mainProductData$disc3;
2064
2291
  var mainProductData = _this4.hooks.getProduct(mainProduct);
2065
2292
 
2066
2293
  // 重组bundle
@@ -2075,16 +2302,16 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
2075
2302
  } else {
2076
2303
  // 🔥 关键:拆分后的bundle item
2077
2304
  processedBundleItems.forEach(function (item) {
2078
- var _item$price, _ref16, _item$bundle_selling_;
2305
+ var _item$price, _ref28, _item$bundle_selling_;
2079
2306
  var nextBundlePrice = (_item$price = item.price) !== null && _item$price !== void 0 ? _item$price : bundleItem.price;
2080
- var nextBundleSellingPrice = (_ref16 = (_item$bundle_selling_ = item.bundle_selling_price) !== null && _item$bundle_selling_ !== void 0 ? _item$bundle_selling_ : item.price) !== null && _ref16 !== void 0 ? _ref16 : bundleItem.bundle_selling_price;
2307
+ var nextBundleSellingPrice = (_ref28 = (_item$bundle_selling_ = item.bundle_selling_price) !== null && _item$bundle_selling_ !== void 0 ? _item$bundle_selling_ : item.price) !== null && _ref28 !== void 0 ? _ref28 : bundleItem.bundle_selling_price;
2081
2308
  // 🔥 更新 discount_list 中的 num,使其与拆分后的 item.num 一致
2082
2309
  var updatedDiscountList = (item.discount_list || []).map(function (discount) {
2083
- var _discount$metadata10;
2310
+ var _discount$metadata11;
2084
2311
  return _objectSpread(_objectSpread({}, discount), {}, {
2085
2312
  metadata: _objectSpread(_objectSpread({}, discount.metadata), {}, {
2086
2313
  num: item.num,
2087
- custom_product_bundle_map_id: discount === null || discount === void 0 || (_discount$metadata10 = discount.metadata) === null || _discount$metadata10 === void 0 ? void 0 : _discount$metadata10.custom_product_bundle_map_id
2314
+ custom_product_bundle_map_id: discount === null || discount === void 0 || (_discount$metadata11 = discount.metadata) === null || _discount$metadata11 === void 0 ? void 0 : _discount$metadata11.custom_product_bundle_map_id
2088
2315
  })
2089
2316
  // num: item.num, // 使用拆分后的 num
2090
2317
  });
@@ -2104,7 +2331,7 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
2104
2331
 
2105
2332
  // 主商品已经在前面的 setProduct 中应用过折扣;这里只替换 bundle,
2106
2333
  // 避免把主商品 discount_list 再扣一次。
2107
- var mainSellingPrice = (_ref17 = (_mainProductData$main = mainProductData.main_product_selling_price) !== null && _mainProductData$main !== void 0 ? _mainProductData$main : mainProductData.price) !== null && _ref17 !== void 0 ? _ref17 : 0;
2334
+ var mainSellingPrice = (_ref29 = (_mainProductData$main = mainProductData.main_product_selling_price) !== null && _mainProductData$main !== void 0 ? _mainProductData$main : mainProductData.price) !== null && _ref29 !== void 0 ? _ref29 : 0;
2108
2335
  var newTotal = Number(mainSellingPrice || 0);
2109
2336
  var newOriginTotal = Number(mainProductData.original_price || mainProductData.price || 0);
2110
2337
 
@@ -2173,25 +2400,36 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
2173
2400
 
2174
2401
  // 获取此优惠券实际应用的商品详情
2175
2402
  var appliedProductDetails = appliedDiscountProducts.get(discount.id) || [];
2403
+ var committedAppliedProductDetails = isEntitlementPass(discount) ? mergeEntitlementAppliedProductDetails(discount.committedAppliedProductDetails || []) : [];
2404
+ var allAppliedProductDetails = isEntitlementPass(discount) ? mergeEntitlementAppliedProductDetails([].concat(_toConsumableArray(committedAppliedProductDetails), _toConsumableArray(appliedProductDetails))) : appliedProductDetails;
2176
2405
 
2177
2406
  // 根据是否有适用商品决定isAvailable
2178
2407
  var isAvailable = applicableProducts.length > 0;
2408
+ var timeReasons = discountTimeReasons.get(discount.id) || [];
2409
+ var timeReason = timeReasons.length ? timeReasons.every(Boolean) ? timeReasons[0] : undefined : getDiscountTimeUnavailableReason(discount, dayjs().format('YYYY-MM-DD HH:mm:ss'));
2410
+ var reasonCode = isAvailable ? undefined : getDiscountUsageBlockReason(discount) || timeReason || discount.reasonCode || (discountHolderMismatch.has(discount.id) ? 'holder_not_matched' : 'product_not_applicable');
2411
+ var availabilityReason = {
2412
+ reasonCode: reasonCode,
2413
+ reason: reasonCode && reasonCode === discount.reasonCode ? discount.reason : undefined
2414
+ };
2179
2415
 
2180
2416
  // 如果此优惠券被明确设置为不选中,保持其状态,但正确设置isAvailable
2181
2417
  if (typeof discount.isSelected === 'boolean' && discount.isSelected === false && discount.isManualSelect) {
2182
- return _objectSpread(_objectSpread({}, discount), {}, {
2418
+ return _objectSpread(_objectSpread(_objectSpread({}, discount), availabilityReason), {}, {
2183
2419
  isSelected: false,
2184
2420
  isAvailable: isAvailable,
2185
2421
  // 正确设置可用性
2186
2422
  applicableProductIds: applicableProducts,
2187
2423
  // 添加可抵扣的商品详情
2188
2424
  applicableProductDetails: applicableProductDetails,
2189
- // 添加实际抵扣的商品详情(对于未选中的优惠券,为空数组)
2190
- appliedProductDetails: []
2425
+ // 已提交的历史使用不可撤销;只取消本次新增分配。
2426
+ appliedProductDetails: committedAppliedProductDetails
2191
2427
  });
2192
2428
  }
2193
- if (usedDiscounts.get(discount.id)) {
2429
+ if (isDiscountUsed(discount)) {
2194
2430
  return _objectSpread(_objectSpread({}, discount), {}, {
2431
+ reason: undefined,
2432
+ reasonCode: undefined,
2195
2433
  isSelected: true,
2196
2434
  isManualSelect: options !== null && options !== void 0 && options.scan ? false : discount.isManualSelect,
2197
2435
  // 标记为可用,因为它已被应用
@@ -2201,7 +2439,7 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
2201
2439
  // 添加可抵扣的商品详情
2202
2440
  applicableProductDetails: applicableProductDetails,
2203
2441
  // 添加实际抵扣的商品详情
2204
- appliedProductDetails: appliedProductDetails
2442
+ appliedProductDetails: allAppliedProductDetails
2205
2443
  });
2206
2444
  }
2207
2445
  var isSelected = typeof discount.isSelected === 'boolean' ? discount.isSelected : undefined;
@@ -2209,10 +2447,10 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
2209
2447
  // 如果是可用的 并且没有手动标记 则设置为选中
2210
2448
  if (isAvailable && appliedProductDetails.length && !discount.isManualSelect) {
2211
2449
  isSelected = true;
2212
- } else if (!appliedProductDetails.length) {
2450
+ } else if (!appliedProductDetails.length && !committedAppliedProductDetails.length) {
2213
2451
  isSelected = false;
2214
2452
  }
2215
- return _objectSpread(_objectSpread({}, discount), {}, {
2453
+ return _objectSpread(_objectSpread(_objectSpread({}, discount), availabilityReason), {}, {
2216
2454
  // 如果有适用的商品,标记为可用
2217
2455
  isAvailable: isAvailable,
2218
2456
  isSelected: isSelected,
@@ -2222,13 +2460,69 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
2222
2460
  applicableProductIds: applicableProducts,
2223
2461
  // 添加可抵扣的商品详情
2224
2462
  applicableProductDetails: applicableProductDetails,
2225
- // 添加实际抵扣的商品详情(对于未使用的优惠券,为空数组)
2226
- appliedProductDetails: []
2463
+ // 编辑订单的历史使用继续保留,本次没有新增使用。
2464
+ appliedProductDetails: committedAppliedProductDetails
2465
+ });
2466
+ });
2467
+
2468
+ // 整单分配完成后统一记录卡余额;历史使用已包含在服务端余额中,只扣本次新增次数。
2469
+ var entitlementBalances = new Map();
2470
+ addModeDiscount.forEach(function (discount) {
2471
+ var quota = resolveEntitlementQuota(discount);
2472
+ if (!quota) return;
2473
+ entitlementBalances.set(String(discount.id), quota.type === 'unlimited' ? null : Math.max(quota.balance - (entitlementUsageCounts.get(discount.id) || 0), 0));
2474
+ });
2475
+ var getEntitlementBalance = function getEntitlementBalance(detail) {
2476
+ var _detail$discount$reso2, _detail$discount4;
2477
+ if (!isEntitlementPass(detail)) return undefined;
2478
+ var resourceId = (_detail$discount$reso2 = detail === null || detail === void 0 || (_detail$discount4 = detail.discount) === null || _detail$discount4 === void 0 ? void 0 : _detail$discount4.resource_id) !== null && _detail$discount$reso2 !== void 0 ? _detail$discount$reso2 : detail === null || detail === void 0 ? void 0 : detail.resource_id;
2479
+ return entitlementBalances.get(String(resourceId));
2480
+ };
2481
+ var needsEntitlementBalance = function needsEntitlementBalance(detail) {
2482
+ var _detail$metadata3;
2483
+ var balance = getEntitlementBalance(detail);
2484
+ return balance !== undefined && (detail === null || detail === void 0 || (_detail$metadata3 = detail.metadata) === null || _detail$metadata3 === void 0 ? void 0 : _detail$metadata3.balance) !== balance;
2485
+ };
2486
+ var withEntitlementBalance = function withEntitlementBalance(detail) {
2487
+ if (!needsEntitlementBalance(detail)) return detail;
2488
+ return _objectSpread(_objectSpread({}, detail), {}, {
2489
+ metadata: _objectSpread(_objectSpread({}, detail.metadata || {}), {}, {
2490
+ balance: getEntitlementBalance(detail)
2491
+ })
2227
2492
  });
2493
+ };
2494
+ var getProductDiscountDetails = function getProductDiscountDetails(originProduct) {
2495
+ var product = _this4.hooks.getProduct(originProduct);
2496
+ return [].concat(_toConsumableArray(product.discount_list || []), _toConsumableArray((product.bundle || []).flatMap(function (bundle) {
2497
+ return bundle.discount_list || [];
2498
+ })));
2499
+ };
2500
+ var productsWithEntitlementBalances = entitlementBalances.size ? processedProductList.map(function (product) {
2501
+ if (!getProductDiscountDetails(product).some(needsEntitlementBalance)) {
2502
+ return product;
2503
+ }
2504
+ // 历史商品及套餐可能仍引用输入快照;仅复制有变化的行,并直接更新 metadata。
2505
+ // 不再次调用价格换算钩子,避免只补余额时改变商品价格。
2506
+ var nextProduct = cloneDeep(product);
2507
+ getProductDiscountDetails(nextProduct).forEach(function (detail) {
2508
+ if (!needsEntitlementBalance(detail)) return;
2509
+ detail.metadata = withEntitlementBalance(detail).metadata;
2510
+ });
2511
+ return nextProduct;
2512
+ }) : processedProductList;
2513
+ var discountsWithEntitlementBalances = [].concat(editModeDiscount, _toConsumableArray(updatedDiscountList)).map(function (discount) {
2514
+ if (!isEntitlementPass(discount) || !entitlementBalances.has(String(discount.id))) {
2515
+ return discount;
2516
+ }
2517
+ return _objectSpread(_objectSpread({}, discount), {}, {
2518
+ appliedProductDetails: (discount.appliedProductDetails || []).map(withEntitlementBalance)
2519
+ }, discount.committedAppliedProductDetails ? {
2520
+ committedAppliedProductDetails: discount.committedAppliedProductDetails.map(withEntitlementBalance)
2521
+ } : {});
2228
2522
  });
2229
2523
  return {
2230
- productList: processedProductList,
2231
- discountList: [].concat(editModeDiscount, _toConsumableArray(updatedDiscountList))
2524
+ productList: productsWithEntitlementBalances,
2525
+ discountList: discountsWithEntitlementBalances
2232
2526
  };
2233
2527
  }
2234
2528
 
@@ -2253,15 +2547,15 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
2253
2547
  var ruleType = usageRules.type;
2254
2548
  // 套餐适用范围配置
2255
2549
  var packageScope = usageRules === null || usageRules === void 0 ? void 0 : usageRules.package_scope;
2256
- var _ref18 = packageScope || {},
2257
- _ref18$type = _ref18.type,
2258
- scopeType = _ref18$type === void 0 ? 'product_all' : _ref18$type,
2259
- _ref18$exclude_bundle = _ref18.exclude_bundle_product_ids,
2260
- exclude_bundle_product_ids = _ref18$exclude_bundle === void 0 ? [] : _ref18$exclude_bundle,
2261
- _ref18$include_bundle = _ref18.include_bundle_product_ids,
2262
- include_bundle_product_ids = _ref18$include_bundle === void 0 ? [] : _ref18$include_bundle,
2263
- _ref18$filter = _ref18.filter,
2264
- filter = _ref18$filter === void 0 ? 0 : _ref18$filter;
2550
+ var _ref30 = packageScope || {},
2551
+ _ref30$type = _ref30.type,
2552
+ scopeType = _ref30$type === void 0 ? 'product_all' : _ref30$type,
2553
+ _ref30$exclude_bundle = _ref30.exclude_bundle_product_ids,
2554
+ exclude_bundle_product_ids = _ref30$exclude_bundle === void 0 ? [] : _ref30$exclude_bundle,
2555
+ _ref30$include_bundle = _ref30.include_bundle_product_ids,
2556
+ include_bundle_product_ids = _ref30$include_bundle === void 0 ? [] : _ref30$include_bundle,
2557
+ _ref30$filter = _ref30.filter,
2558
+ filter = _ref30$filter === void 0 ? 0 : _ref30$filter;
2265
2559
  var isMainProduct = flatItem.type === 'main'; // 单独购买
2266
2560
  var isBundleItem = flatItem.type === 'bundle'; // 套餐中购买
2267
2561
  var bundleMainProductId = (_flatItem$product = flatItem.product) === null || _flatItem$product === void 0 ? void 0 : _flatItem$product.product_id;