@pisell/pisellos 0.10.46 → 0.10.48

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.
@@ -45,6 +45,8 @@ interface ApplicableProductDetails {
45
45
  num: number;
46
46
  metadata?: {
47
47
  product_discount_difference?: number;
48
+ /** 月卡扣除本单新增使用后的剩余次数;不限次数时为 null。 */
49
+ balance?: number | null;
48
50
  };
49
51
  discount?: {
50
52
  fixed_amount?: number;
@@ -16,8 +16,8 @@ export declare class RulesModule extends BaseModule implements Module, RulesModu
16
16
  getWalletPassEvaluator(): import("../..").WalletPassEvaluator | undefined;
17
17
  private normalizeHolderRecordIds;
18
18
  /**
19
- * 特定 form Holder 卡只授权给绑定同一 Holder Record 的商品行。
20
- * required/isNeedHolder 仅控制是否强制选择 Holder,不参与优惠资格。
19
+ * 商品未开启 Holder 选择时跳过匹配;已开启时,特定 form Holder
20
+ * 只适用于同一 Holder Record,是否必填不影响匹配规则。
21
21
  */
22
22
  private checkHolderMatch;
23
23
  isDiscountListAvailable({ oldDiscountList, newDiscountList, productList, orderTotalAmount, holders, isFormSubject, }: {
@@ -39,7 +39,7 @@ import { UnavailableReason } from "./types";
39
39
  import { uniqueById, getDiscountAmount, getDiscountListAmountTotal, getDiscountListAmount, filterDiscountListByBookingTime, getDiscountTimeUnavailableReason } from "../../solution/ShopDiscount/utils";
40
40
  import { getProductOriginTotalPrice, getProductTotalPrice } from "../Cart/utils";
41
41
  import Decimal from 'decimal.js';
42
- import { isBoolean } from 'lodash-es';
42
+ import { cloneDeep, isBoolean } from 'lodash-es';
43
43
  import dayjs from 'dayjs';
44
44
  import { isOrderLevelFixedAmountDiscount } from "../../solution/ShopDiscount/utils";
45
45
  import { calculateOrderLevelDiscountAllocation } from "../../solution/ShopDiscount/utils";
@@ -130,15 +130,23 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
130
130
  }
131
131
 
132
132
  /**
133
- * 特定 form Holder 卡只授权给绑定同一 Holder Record 的商品行。
134
- * required/isNeedHolder 仅控制是否强制选择 Holder,不参与优惠资格。
133
+ * 商品未开启 Holder 选择时跳过匹配;已开启时,特定 form Holder
134
+ * 只适用于同一 Holder Record,是否必填不影响匹配规则。
135
135
  */
136
136
  }, {
137
137
  key: "checkHolderMatch",
138
- value: function checkHolderMatch(discount, product, holders) {
139
- 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;
140
140
  // 非表单类型 holder 视为匹配
141
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
+ }
142
150
  var discountHolderIds = this.normalizeHolderRecordIds(discount.holder.holder_id);
143
151
  if (!discountHolderIds.length) return false;
144
152
  var productHolderIds = this.normalizeHolderRecordIds(product.holder_id);
@@ -153,14 +161,14 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
153
161
  // 判断discountList 是否可以对当前productList生效
154
162
  }, {
155
163
  key: "isDiscountListAvailable",
156
- value: function isDiscountListAvailable(_ref) {
164
+ value: function isDiscountListAvailable(_ref7) {
157
165
  var _this2 = this;
158
- var oldDiscountList = _ref.oldDiscountList,
159
- newDiscountList = _ref.newDiscountList,
160
- productList = _ref.productList,
161
- orderTotalAmount = _ref.orderTotalAmount,
162
- holders = _ref.holders,
163
- 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;
164
172
  // 首先检查是否有新的优惠券可应用
165
173
  if (!newDiscountList || newDiscountList.length === 0) {
166
174
  return {
@@ -352,14 +360,14 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
352
360
  }
353
361
  }, {
354
362
  key: "calcDiscount",
355
- value: function calcDiscount(_ref2, options) {
363
+ value: function calcDiscount(_ref8, options) {
356
364
  var _options$selectedList,
357
365
  _this4 = this;
358
- var discountList = _ref2.discountList,
359
- productList = _ref2.productList,
360
- holders = _ref2.holders,
361
- isFormSubject = _ref2.isFormSubject,
362
- orderTotalAmount = _ref2.orderTotalAmount;
366
+ var discountList = _ref8.discountList,
367
+ productList = _ref8.productList,
368
+ holders = _ref8.holders,
369
+ isFormSubject = _ref8.isFormSubject,
370
+ orderTotalAmount = _ref8.orderTotalAmount;
363
371
  var resolveIsFormSubject = function resolveIsFormSubject(product) {
364
372
  return typeof isFormSubject === 'function' ? Boolean(isFormSubject(product)) : Boolean(isFormSubject);
365
373
  };
@@ -471,7 +479,7 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
471
479
  // 2. 展开 bundle 子商品
472
480
  if (product.bundle && Array.isArray(product.bundle) && product.bundle.length > 0) {
473
481
  product.bundle.forEach(function (bundleItem, bundleIndex) {
474
- var _ref3, _bundleItem$bundle_pr;
482
+ var _ref9, _bundleItem$bundle_pr;
475
483
  flattened.push({
476
484
  type: 'bundle',
477
485
  originProduct: originProduct,
@@ -483,7 +491,7 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
483
491
  // 新增商品运行态通常带 _bundle_product_id;订单 hydrate 后只有
484
492
  // bundle_product_id。Change time 必须兼容两种结构,否则 bundle
485
493
  // 子项无法命中折扣卡的 limited_relation_product_data.product_ids。
486
- 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,
494
+ 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,
487
495
  _id: "".concat(product._id, "_bundle_").concat(bundleIndex),
488
496
  parentId: product._id,
489
497
  num: bundleItem.num || 1,
@@ -643,10 +651,10 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
643
651
 
644
652
  // 对扁平化后的列表按价格降序排序(用于应用优惠券时优先选择高价商品)
645
653
  var sortedFlattenedList = flattenedList.sort(function (a, b) {
646
- var _ref4, _a$original_price, _ref5, _b$original_price;
654
+ var _ref10, _a$original_price, _ref11, _b$original_price;
647
655
  // 子商品优先使用 original_price,主商品使用 price
648
- 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');
649
- 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');
656
+ 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');
657
+ 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');
650
658
  if (priceA.equals(priceB)) {
651
659
  // 价格相同时,主商品优先
652
660
  if (a.type !== b.type) {
@@ -780,13 +788,13 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
780
788
  var buildFullCoveragePassUsageGroups = function buildFullCoveragePassUsageGroups(discounts, requestedCount) {
781
789
  var _firstGoodPass$config;
782
790
  var groups = [];
783
- var pendingUsedDiscounts = new Set(Array.from(usedDiscounts.entries()).filter(function (_ref6) {
784
- var _ref7 = _slicedToArray(_ref6, 2),
785
- used = _ref7[1];
791
+ var pendingUsedDiscounts = new Set(Array.from(usedDiscounts.entries()).filter(function (_ref12) {
792
+ var _ref13 = _slicedToArray(_ref12, 2),
793
+ used = _ref13[1];
786
794
  return used;
787
- }).map(function (_ref8) {
788
- var _ref9 = _slicedToArray(_ref8, 1),
789
- discountId = _ref9[0];
795
+ }).map(function (_ref14) {
796
+ var _ref15 = _slicedToArray(_ref14, 1),
797
+ discountId = _ref15[0];
790
798
  return discountId;
791
799
  }));
792
800
  var pendingEntitlementCounts = new Map(entitlementUsageCounts);
@@ -861,7 +869,7 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
861
869
  _step6;
862
870
  try {
863
871
  for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
864
- var _detail$discount$reso, _detail$discount, _detail$discount$prod, _detail$discount2, _detail$metadata$cust, _detail$metadata, _detail$discount3, _ref10, _detail$metadata$num, _detail$metadata2, _ref11, _existed$metadata$num, _existed$metadata;
872
+ 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;
865
873
  var detail = _step6.value;
866
874
  if (!isEntitlementPass(detail)) {
867
875
  mergedDetails.push(detail);
@@ -873,7 +881,7 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
873
881
  continue;
874
882
  }
875
883
  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('|');
876
- var rawUsageCount = Number((_ref10 = (_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 && _ref10 !== void 0 ? _ref10 : 1);
884
+ 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);
877
885
  var usageCount = Number.isFinite(rawUsageCount) && rawUsageCount > 0 ? rawUsageCount : 1;
878
886
  var mergedIndex = mergedIndexByKey.get(key);
879
887
  if (mergedIndex === undefined) {
@@ -887,7 +895,7 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
887
895
  continue;
888
896
  }
889
897
  var existed = mergedDetails[mergedIndex];
890
- var existedUsageCount = Number((_ref11 = (_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 && _ref11 !== void 0 ? _ref11 : 1);
898
+ 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);
891
899
  var totalUsageCount = existedUsageCount + usageCount;
892
900
  mergedDetails[mergedIndex] = _objectSpread(_objectSpread({}, existed), {}, {
893
901
  metadata: _objectSpread(_objectSpread({}, (existed === null || existed === void 0 ? void 0 : existed.metadata) || {}), {}, {
@@ -1054,7 +1062,7 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
1054
1062
  // 收集该折扣卡适用的商品(排除被其他专属折扣卡占用的商品)
1055
1063
  var applicableProducts = [];
1056
1064
  sortedFlattenedList.forEach(function (flatItem) {
1057
- var _flatItem$parentProdu2, _flatItem$parentProdu3, _product$price, _ref12, _flatItem$original_pr;
1065
+ var _flatItem$parentProdu2, _flatItem$parentProdu3, _product$price, _ref18, _flatItem$original_pr;
1058
1066
  // 🔥 检查该商品是否被其他专属折扣卡占用
1059
1067
  var occupyingDiscountId = occupiedItems.get(flatItem._id);
1060
1068
  if (occupyingDiscountId !== undefined && occupyingDiscountId !== discount.id) {
@@ -1082,7 +1090,7 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
1082
1090
 
1083
1091
  // 对于主商品:使用 price
1084
1092
  // 对于子商品:优先使用 flatItem.original_price,否则使用 flatItem.price
1085
- var originalAmount = flatItem.type === 'main' ? Number((_product$price = product.price) !== null && _product$price !== void 0 ? _product$price : 0) : Number((_ref12 = (_flatItem$original_pr = flatItem.original_price) !== null && _flatItem$original_pr !== void 0 ? _flatItem$original_pr : flatItem.price) !== null && _ref12 !== void 0 ? _ref12 : 0);
1093
+ 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);
1086
1094
 
1087
1095
  // 传递 parentQuantity 用于差值处理时判断是否是真正的"数量为1"
1088
1096
  var productData = {
@@ -1146,7 +1154,7 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
1146
1154
  var isHolderMatch = _this4.checkHolderMatch(discount, {
1147
1155
  isNeedHolder: resolveIsFormSubject(_tempVar) && !(_tempVar !== null && _tempVar !== void 0 && _tempVar.isNormalProduct),
1148
1156
  holder_id: (_tempVar === null || _tempVar === void 0 ? void 0 : _tempVar.holder_id) || product.holder_id
1149
- }, holders);
1157
+ }, flatItem.originProduct, holders);
1150
1158
  var timeReason = getDiscountTimeUnavailableReason(discount, (((_product5 = product) === null || _product5 === void 0 ? void 0 : _product5.startDate) || dayjs()).format('YYYY-MM-DD HH:mm:ss'));
1151
1159
  var timeLimit = !timeReason;
1152
1160
  // 是符合折扣的商品
@@ -1219,13 +1227,13 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
1219
1227
  };
1220
1228
  var optionDiscountAmount = 0;
1221
1229
  var discountedOptions = options.map(function (option) {
1222
- var _ref13, _ref14, _option$_original_pri, _ref15, _option$num, _rest$price, _option$_original_pri2;
1230
+ var _ref19, _ref20, _option$_original_pri, _ref21, _option$num, _rest$price, _option$_original_pri2;
1223
1231
  // 折前单价:优先 price,缺失再 add_price;已写回折后价时用 _original_price(或旧字段 _original_add_price)
1224
- var rawUnit = (_ref13 = (_ref14 = (_option$_original_pri = option._original_price) !== null && _option$_original_pri !== void 0 ? _option$_original_pri : option._original_add_price) !== null && _ref14 !== void 0 ? _ref14 : option.price) !== null && _ref13 !== void 0 ? _ref13 : option.add_price;
1232
+ 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;
1225
1233
  var baseUnitNum = Number(rawUnit !== null && rawUnit !== void 0 ? rawUnit : 0);
1226
1234
  if (baseUnitNum <= 0) return option;
1227
1235
  var discountedPrice = getDiscountAmount(discount, baseUnitNum, baseUnitNum);
1228
- var optQty = Number((_ref15 = (_option$num = option.num) !== null && _option$num !== void 0 ? _option$num : option.quantity) !== null && _ref15 !== void 0 ? _ref15 : 1);
1236
+ var optQty = Number((_ref21 = (_option$num = option.num) !== null && _option$num !== void 0 ? _option$num : option.quantity) !== null && _ref21 !== void 0 ? _ref21 : 1);
1229
1237
  optionDiscountAmount = new Decimal(optionDiscountAmount).plus(new Decimal(baseUnitNum).minus(discountedPrice).mul(optQty)).toNumber();
1230
1238
  var _original_add_price = option._original_add_price,
1231
1239
  rest = _objectWithoutProperties(option, _excluded);
@@ -1259,9 +1267,9 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
1259
1267
  var getOptionTotal = function getOptionTotal(options) {
1260
1268
  if (!(options !== null && options !== void 0 && options.length)) return 0;
1261
1269
  return options.reduce(function (sum, opt) {
1262
- var _ref16, _opt$price, _ref17, _opt$num;
1263
- var unit = Number((_ref16 = (_opt$price = opt.price) !== null && _opt$price !== void 0 ? _opt$price : opt.add_price) !== null && _ref16 !== void 0 ? _ref16 : 0);
1264
- var n = Number((_ref17 = (_opt$num = opt.num) !== null && _opt$num !== void 0 ? _opt$num : opt.quantity) !== null && _ref17 !== void 0 ? _ref17 : 1);
1270
+ var _ref22, _opt$price, _ref23, _opt$num;
1271
+ var unit = Number((_ref22 = (_opt$price = opt.price) !== null && _opt$price !== void 0 ? _opt$price : opt.add_price) !== null && _ref22 !== void 0 ? _ref22 : 0);
1272
+ var n = Number((_ref23 = (_opt$num = opt.num) !== null && _opt$num !== void 0 ? _opt$num : opt.quantity) !== null && _ref23 !== void 0 ? _ref23 : 1);
1265
1273
  return new Decimal(sum).plus(new Decimal(unit).mul(n)).toNumber();
1266
1274
  }, 0);
1267
1275
  };
@@ -1369,7 +1377,7 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
1369
1377
  var isHolderMatch = _this4.checkHolderMatch(discount, {
1370
1378
  isNeedHolder: resolveIsFormSubject(_tempVar) && !(_tempVar !== null && _tempVar !== void 0 && _tempVar.isNormalProduct),
1371
1379
  holder_id: (_tempVar === null || _tempVar === void 0 ? void 0 : _tempVar.holder_id) || product.holder_id
1372
- }, holders);
1380
+ }, flatItem.originProduct, holders);
1373
1381
  // 如果 holder 不匹配,则不适用
1374
1382
  if (!isHolderMatch) return false;
1375
1383
  var timeLimit = true;
@@ -1412,8 +1420,8 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
1412
1420
  var appliedDiscountCardIds = new Set((product.discount_list || []).filter(function (item) {
1413
1421
  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));
1414
1422
  }).map(function (item) {
1415
- var _ref18, _item$discount$resour, _item$discount;
1416
- return (_ref18 = (_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 && _ref18 !== void 0 ? _ref18 : item === null || item === void 0 ? void 0 : item.id;
1423
+ var _ref24, _item$discount$resour, _item$discount;
1424
+ 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;
1417
1425
  }).filter(function (id) {
1418
1426
  return id !== undefined && id !== null;
1419
1427
  }).map(String));
@@ -1446,8 +1454,8 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
1446
1454
  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) {
1447
1455
  return item.type === 'product';
1448
1456
  })) || product.total != product.origin_total && (product.bundle || []).every(function (item) {
1449
- var _ref19;
1450
- return !((_ref19 = item.discount_list || []) !== null && _ref19 !== void 0 && _ref19.length);
1457
+ var _ref25;
1458
+ return !((_ref25 = item.discount_list || []) !== null && _ref25 !== void 0 && _ref25.length);
1451
1459
  }) && (!((_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) {
1452
1460
  return item.type === 'product';
1453
1461
  })));
@@ -1463,8 +1471,8 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
1463
1471
  isManualDiscount = typeof parentProduct.isManualDiscount === 'boolean' ? parentProduct.isManualDiscount : ((_parentProduct$discou = parentProduct.discount_list) === null || _parentProduct$discou === void 0 ? void 0 : _parentProduct$discou.some(function (item) {
1464
1472
  return item.type === 'product';
1465
1473
  })) || parentProduct.total != parentProduct.origin_total && (parentProduct.bundle || []).every(function (item) {
1466
- var _ref20;
1467
- return !((_ref20 = item.discount_list || []) !== null && _ref20 !== void 0 && _ref20.length);
1474
+ var _ref26;
1475
+ return !((_ref26 = item.discount_list || []) !== null && _ref26 !== void 0 && _ref26.length);
1468
1476
  }) && (!((_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) {
1469
1477
  return item.type === 'product';
1470
1478
  })));
@@ -1557,8 +1565,8 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
1557
1565
  discount_list: isManualDiscount ? _this4.resolveDiscountListForManualOverride(product.discount_list) : _this4.filterDiscountListByType(product.discount_list, 'promotion')
1558
1566
  }))]);
1559
1567
  } else {
1560
- var _ref21, _product$_promotion$f, _product12, _product$origin_total;
1561
- var total = product.inPromotion ? (_ref21 = (_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 && _ref21 !== void 0 ? _ref21 : product.total : (_product$origin_total = product.origin_total) !== null && _product$origin_total !== void 0 ? _product$origin_total : product.total;
1568
+ var _ref27, _product$_promotion$f, _product12, _product$origin_total;
1569
+ 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;
1562
1570
  var main_product_selling_price = product.price;
1563
1571
  if ((product.discount_list || []).some(function (item) {
1564
1572
  return item.type === 'promotion';
@@ -2258,7 +2266,7 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
2258
2266
  } else {
2259
2267
  // 🔥 没有子商品被拆分,使用原有逻辑
2260
2268
  mainProductArr.forEach(function (mainProduct) {
2261
- var _ref23, _mainProductData$main, _mainProductData$disc, _mainProductData$disc2, _mainProductData$disc3;
2269
+ var _ref29, _mainProductData$main, _mainProductData$disc, _mainProductData$disc2, _mainProductData$disc3;
2262
2270
  var mainProductData = _this4.hooks.getProduct(mainProduct);
2263
2271
 
2264
2272
  // 重组bundle
@@ -2273,9 +2281,9 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
2273
2281
  } else {
2274
2282
  // 🔥 关键:拆分后的bundle item
2275
2283
  processedBundleItems.forEach(function (item) {
2276
- var _item$price, _ref22, _item$bundle_selling_;
2284
+ var _item$price, _ref28, _item$bundle_selling_;
2277
2285
  var nextBundlePrice = (_item$price = item.price) !== null && _item$price !== void 0 ? _item$price : bundleItem.price;
2278
- var nextBundleSellingPrice = (_ref22 = (_item$bundle_selling_ = item.bundle_selling_price) !== null && _item$bundle_selling_ !== void 0 ? _item$bundle_selling_ : item.price) !== null && _ref22 !== void 0 ? _ref22 : bundleItem.bundle_selling_price;
2286
+ 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;
2279
2287
  // 🔥 更新 discount_list 中的 num,使其与拆分后的 item.num 一致
2280
2288
  var updatedDiscountList = (item.discount_list || []).map(function (discount) {
2281
2289
  var _discount$metadata10;
@@ -2302,7 +2310,7 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
2302
2310
 
2303
2311
  // 主商品已经在前面的 setProduct 中应用过折扣;这里只替换 bundle,
2304
2312
  // 避免把主商品 discount_list 再扣一次。
2305
- var mainSellingPrice = (_ref23 = (_mainProductData$main = mainProductData.main_product_selling_price) !== null && _mainProductData$main !== void 0 ? _mainProductData$main : mainProductData.price) !== null && _ref23 !== void 0 ? _ref23 : 0;
2313
+ 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;
2306
2314
  var newTotal = Number(mainSellingPrice || 0);
2307
2315
  var newOriginTotal = Number(mainProductData.original_price || mainProductData.price || 0);
2308
2316
 
@@ -2435,9 +2443,65 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
2435
2443
  appliedProductDetails: committedAppliedProductDetails
2436
2444
  });
2437
2445
  });
2446
+
2447
+ // 整单分配完成后统一记录卡余额;历史使用已包含在服务端余额中,只扣本次新增次数。
2448
+ var entitlementBalances = new Map();
2449
+ addModeDiscount.forEach(function (discount) {
2450
+ var quota = resolveEntitlementQuota(discount);
2451
+ if (!quota) return;
2452
+ entitlementBalances.set(String(discount.id), quota.type === 'unlimited' ? null : Math.max(quota.balance - (entitlementUsageCounts.get(discount.id) || 0), 0));
2453
+ });
2454
+ var getEntitlementBalance = function getEntitlementBalance(detail) {
2455
+ var _detail$discount$reso2, _detail$discount4;
2456
+ if (!isEntitlementPass(detail)) return undefined;
2457
+ 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;
2458
+ return entitlementBalances.get(String(resourceId));
2459
+ };
2460
+ var needsEntitlementBalance = function needsEntitlementBalance(detail) {
2461
+ var _detail$metadata3;
2462
+ var balance = getEntitlementBalance(detail);
2463
+ return balance !== undefined && (detail === null || detail === void 0 || (_detail$metadata3 = detail.metadata) === null || _detail$metadata3 === void 0 ? void 0 : _detail$metadata3.balance) !== balance;
2464
+ };
2465
+ var withEntitlementBalance = function withEntitlementBalance(detail) {
2466
+ if (!needsEntitlementBalance(detail)) return detail;
2467
+ return _objectSpread(_objectSpread({}, detail), {}, {
2468
+ metadata: _objectSpread(_objectSpread({}, detail.metadata || {}), {}, {
2469
+ balance: getEntitlementBalance(detail)
2470
+ })
2471
+ });
2472
+ };
2473
+ var getProductDiscountDetails = function getProductDiscountDetails(originProduct) {
2474
+ var product = _this4.hooks.getProduct(originProduct);
2475
+ return [].concat(_toConsumableArray(product.discount_list || []), _toConsumableArray((product.bundle || []).flatMap(function (bundle) {
2476
+ return bundle.discount_list || [];
2477
+ })));
2478
+ };
2479
+ var productsWithEntitlementBalances = entitlementBalances.size ? processedProductList.map(function (product) {
2480
+ if (!getProductDiscountDetails(product).some(needsEntitlementBalance)) {
2481
+ return product;
2482
+ }
2483
+ // 历史商品及套餐可能仍引用输入快照;仅复制有变化的行,并直接更新 metadata。
2484
+ // 不再次调用价格换算钩子,避免只补余额时改变商品价格。
2485
+ var nextProduct = cloneDeep(product);
2486
+ getProductDiscountDetails(nextProduct).forEach(function (detail) {
2487
+ if (!needsEntitlementBalance(detail)) return;
2488
+ detail.metadata = withEntitlementBalance(detail).metadata;
2489
+ });
2490
+ return nextProduct;
2491
+ }) : processedProductList;
2492
+ var discountsWithEntitlementBalances = [].concat(editModeDiscount, _toConsumableArray(updatedDiscountList)).map(function (discount) {
2493
+ if (!isEntitlementPass(discount) || !entitlementBalances.has(String(discount.id))) {
2494
+ return discount;
2495
+ }
2496
+ return _objectSpread(_objectSpread({}, discount), {}, {
2497
+ appliedProductDetails: (discount.appliedProductDetails || []).map(withEntitlementBalance)
2498
+ }, discount.committedAppliedProductDetails ? {
2499
+ committedAppliedProductDetails: discount.committedAppliedProductDetails.map(withEntitlementBalance)
2500
+ } : {});
2501
+ });
2438
2502
  return {
2439
- productList: processedProductList,
2440
- discountList: [].concat(editModeDiscount, _toConsumableArray(updatedDiscountList))
2503
+ productList: productsWithEntitlementBalances,
2504
+ discountList: discountsWithEntitlementBalances
2441
2505
  };
2442
2506
  }
2443
2507
 
@@ -2462,15 +2526,15 @@ export var RulesModule = /*#__PURE__*/function (_BaseModule) {
2462
2526
  var ruleType = usageRules.type;
2463
2527
  // 套餐适用范围配置
2464
2528
  var packageScope = usageRules === null || usageRules === void 0 ? void 0 : usageRules.package_scope;
2465
- var _ref24 = packageScope || {},
2466
- _ref24$type = _ref24.type,
2467
- scopeType = _ref24$type === void 0 ? 'product_all' : _ref24$type,
2468
- _ref24$exclude_bundle = _ref24.exclude_bundle_product_ids,
2469
- exclude_bundle_product_ids = _ref24$exclude_bundle === void 0 ? [] : _ref24$exclude_bundle,
2470
- _ref24$include_bundle = _ref24.include_bundle_product_ids,
2471
- include_bundle_product_ids = _ref24$include_bundle === void 0 ? [] : _ref24$include_bundle,
2472
- _ref24$filter = _ref24.filter,
2473
- filter = _ref24$filter === void 0 ? 0 : _ref24$filter;
2529
+ var _ref30 = packageScope || {},
2530
+ _ref30$type = _ref30.type,
2531
+ scopeType = _ref30$type === void 0 ? 'product_all' : _ref30$type,
2532
+ _ref30$exclude_bundle = _ref30.exclude_bundle_product_ids,
2533
+ exclude_bundle_product_ids = _ref30$exclude_bundle === void 0 ? [] : _ref30$exclude_bundle,
2534
+ _ref30$include_bundle = _ref30.include_bundle_product_ids,
2535
+ include_bundle_product_ids = _ref30$include_bundle === void 0 ? [] : _ref30$include_bundle,
2536
+ _ref30$filter = _ref30.filter,
2537
+ filter = _ref30$filter === void 0 ? 0 : _ref30$filter;
2474
2538
  var isMainProduct = flatItem.type === 'main'; // 单独购买
2475
2539
  var isBundleItem = flatItem.type === 'bundle'; // 套餐中购买
2476
2540
  var bundleMainProductId = (_flatItem$product = flatItem.product) === null || _flatItem$product === void 0 ? void 0 : _flatItem$product.product_id;
@@ -36,6 +36,8 @@ export type OrderSurchargeType = string;
36
36
  export interface OrderProductDiscountMetadata {
37
37
  /** 设备端本地唯一标识,用于与服务端记录对齐 */
38
38
  unique_identification_number?: string;
39
+ /** 月卡扣除本单新增使用后的剩余次数;不限次数时为 null。 */
40
+ balance?: number | null;
39
41
  [key: string]: unknown;
40
42
  }
41
43
  /**