@pisell/pisellos 0.0.377 → 0.0.379

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.
@@ -1,10 +1,10 @@
1
1
  function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
2
- 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; } } }; }
3
2
  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; }
4
3
  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; }
5
4
  function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
6
5
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
7
6
  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); }
7
+ 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; } } }; }
8
8
  function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
9
9
  function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
10
10
  function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
@@ -49,19 +49,21 @@ export var getApplicableProductIds = function getApplicableProductIds(voucher) {
49
49
  return [];
50
50
  };
51
51
 
52
- // 辅助函数:计算适用商品的总金额
52
+ // 辅助函数:计算适用商品的总金额(基于剩余金额)
53
53
  var getApplicableProductsAmount = function getApplicableProductsAmount(voucher, productsData) {
54
54
  var _config$deductTaxAndF2;
55
55
  var applicableProductIds = getApplicableProductIds(voucher);
56
56
  var config = voucher.config;
57
57
  var deductTaxAndFee = (_config$deductTaxAndF2 = config === null || config === void 0 ? void 0 : config.deductTaxAndFee) !== null && _config$deductTaxAndF2 !== void 0 ? _config$deductTaxAndF2 : true;
58
58
 
59
- // 根据券的配置选择使用哪个金额
59
+ // 根据券的配置选择使用哪个金额字段
60
60
  var amountField = deductTaxAndFee ? 'remainingAmountWithTax' : 'remainingAmountPure';
61
61
 
62
62
  // 如果为 null,适用于所有商品
63
63
  if (applicableProductIds === null) {
64
- return productsData.reduce(function (sum, p) {
64
+ return productsData.filter(function (p) {
65
+ return p[amountField].greaterThan(0);
66
+ }).reduce(function (sum, p) {
65
67
  return sum.plus(p[amountField]);
66
68
  }, new Decimal(0));
67
69
  }
@@ -73,7 +75,7 @@ var getApplicableProductsAmount = function getApplicableProductsAmount(voucher,
73
75
 
74
76
  // 计算指定商品的总金额
75
77
  return productsData.filter(function (p) {
76
- return applicableProductIds.includes(p.product_id);
78
+ return applicableProductIds.includes(p.product_id) && p[amountField].greaterThan(0);
77
79
  }).reduce(function (sum, p) {
78
80
  return sum.plus(p[amountField]);
79
81
  }, new Decimal(0));
@@ -135,39 +137,64 @@ export function processVouchers(applicableVouchers, orderTotalAmount, products)
135
137
  // 基础值 = min(recommendedAmount, maxDeductionAmount)
136
138
  var baseAmount = Decimal.min(new Decimal(recommendedAmount), new Decimal(maxDeductionAmount));
137
139
 
138
- // 获取适用商品列表
139
- var applicableProducts = getApplicableProducts(voucher, productsData);
140
+ // 根据券的配置选择使用哪个单价字段和金额字段
141
+ var unitPriceField = deductTaxAndFee ? 'unitPriceWithTax' : 'unitPricePure';
142
+ var amountField = deductTaxAndFee ? 'remainingAmountWithTax' : 'remainingAmountPure';
143
+
144
+ // 获取适用商品列表(只筛选有剩余金额的商品)
145
+ var applicableProducts = getApplicableProducts(voucher, productsData).filter(function (p) {
146
+ return p[amountField].greaterThan(0);
147
+ });
140
148
  if (applicableProducts.length === 0) {
141
149
  return new Decimal(0);
142
150
  }
143
151
 
144
- // 根据券的配置选择使用哪个金额字段
145
- var amountField = deductTaxAndFee ? 'remainingAmountWithTax' : 'remainingAmountPure';
146
-
147
152
  // 根据跨商品配置计算适用商品金额
148
153
  var finalApplicableAmount = new Decimal(0);
149
154
  if (allowCrossProduct) {
150
155
  // 跨商品券:可以抵扣多个商品
151
156
  if (applicableProductLimit > 0) {
152
- // 有商品数量限制:按金额从高到低排序,取前 N 个商品
157
+ // 有数量限制:按单价从高到低排序,按动态计算的可抵扣数量累计直到达到 limit
153
158
  var sortedProducts = _toConsumableArray(applicableProducts).sort(function (a, b) {
154
- return a[amountField].comparedTo(b[amountField]) > 0 ? -1 : 1;
155
- }).slice(0, applicableProductLimit);
156
- finalApplicableAmount = sortedProducts.reduce(function (sum, p) {
157
- return sum.plus(p[amountField]);
158
- }, new Decimal(0));
159
+ return a[unitPriceField].comparedTo(b[unitPriceField]) > 0 ? -1 : 1;
160
+ });
161
+ var remainingLimit = applicableProductLimit;
162
+ var _iterator = _createForOfIteratorHelper(sortedProducts),
163
+ _step;
164
+ try {
165
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
166
+ var product = _step.value;
167
+ if (remainingLimit <= 0) break;
168
+ // 动态计算当前可抵扣数量 = ceil(剩余金额 / 单价)
169
+ var currentAvailableQty = Math.ceil(product[amountField].dividedBy(product[unitPriceField]).toNumber());
170
+ var deductQty = Math.min(currentAvailableQty, remainingLimit);
171
+ // 实际可抵扣金额 = min(数量 * 单价, 剩余金额)
172
+ var deductAmount = Decimal.min(product[unitPriceField].times(deductQty), product[amountField]);
173
+ finalApplicableAmount = finalApplicableAmount.plus(deductAmount);
174
+ remainingLimit -= deductQty;
175
+ }
176
+ } catch (err) {
177
+ _iterator.e(err);
178
+ } finally {
179
+ _iterator.f();
180
+ }
159
181
  } else {
160
- // 无商品数量限制:所有适用商品的总和
182
+ // 无数量限制:所有适用商品的剩余金额总和
161
183
  finalApplicableAmount = applicableProducts.reduce(function (sum, p) {
162
184
  return sum.plus(p[amountField]);
163
185
  }, new Decimal(0));
164
186
  }
165
187
  } else {
166
- // 非跨商品券:只能抵扣单个商品,取金额最高的商品
188
+ // 非跨商品券:只能抵扣单个商品(单价最高的)
167
189
  var maxProduct = applicableProducts.reduce(function (max, p) {
168
- return p[amountField].greaterThan(max[amountField]) ? p : max;
190
+ return p[unitPriceField].greaterThan(max[unitPriceField]) ? p : max;
169
191
  });
170
- finalApplicableAmount = maxProduct[amountField];
192
+ // 动态计算当前可抵扣数量
193
+ var _currentAvailableQty = Math.ceil(maxProduct[amountField].dividedBy(maxProduct[unitPriceField]).toNumber());
194
+ // 非跨商品券也受 applicableProductLimit 限制
195
+ var _deductQty = applicableProductLimit > 0 ? Math.min(_currentAvailableQty, applicableProductLimit) : _currentAvailableQty;
196
+ // 实际可抵扣金额 = min(数量 * 单价, 剩余金额)
197
+ finalApplicableAmount = Decimal.min(maxProduct[unitPriceField].times(_deductQty), maxProduct[amountField]);
171
198
  }
172
199
 
173
200
  // 返回最小值
@@ -292,38 +319,63 @@ export function processVouchers(applicableVouchers, orderTotalAmount, products)
292
319
  _ref3$deductTaxAndFee = _ref3.deductTaxAndFee,
293
320
  deductTaxAndFee = _ref3$deductTaxAndFee === void 0 ? true : _ref3$deductTaxAndFee;
294
321
 
295
- // 根据券的配置选择使用哪个金额字段
322
+ // 根据券的配置选择使用哪个单价字段和金额字段
323
+ var unitPriceField = deductTaxAndFee ? 'unitPriceWithTax' : 'unitPricePure';
296
324
  var amountField = deductTaxAndFee ? 'remainingAmountWithTax' : 'remainingAmountPure';
297
325
 
298
- // 获取适用商品
326
+ // 获取适用商品(只筛选有剩余金额的商品)
299
327
  var applicableProducts = getApplicableProducts(voucher, productsForRecommendation).filter(function (p) {
300
328
  return p[amountField].greaterThan(0);
301
329
  });
302
330
  if (applicableProducts.length === 0) return false;
303
331
 
304
- // 考虑 applicableProductLimit
305
- if (allowCrossProduct && applicableProductLimit > 0) {
306
- applicableProducts = applicableProducts.sort(function (a, b) {
307
- return a[amountField].comparedTo(b[amountField]) > 0 ? -1 : 1;
308
- }).slice(0, applicableProductLimit);
309
- }
310
-
311
332
  // ========== 关键修改:在应用券之前,基于当前剩余金额计算 _available_max_amount ==========
312
333
  // 优先使用用户手动编辑的金额,否则根据 deductTaxAndFee 配置获取推荐金额
313
334
  var usageAmount = typeof voucher.edit_current_amount === 'number' ? voucher.edit_current_amount : getRecommendedAmount(voucher);
314
335
  var baseAmount = Decimal.min(new Decimal(usageAmount), new Decimal(maxDeductionAmount));
315
336
  var calculatedAvailableMaxAmount = new Decimal(0);
316
337
  if (allowCrossProduct) {
317
- // 跨商品券:所有适用商品的剩余金额之和
318
- calculatedAvailableMaxAmount = applicableProducts.reduce(function (sum, p) {
319
- return sum.plus(p[amountField]);
320
- }, new Decimal(0));
338
+ // 跨商品券:按 quantity 限制计算可抵扣金额
339
+ if (applicableProductLimit > 0) {
340
+ var sortedProducts = _toConsumableArray(applicableProducts).sort(function (a, b) {
341
+ return a[unitPriceField].comparedTo(b[unitPriceField]) > 0 ? -1 : 1;
342
+ });
343
+ var remainingLimit = applicableProductLimit;
344
+ var _iterator2 = _createForOfIteratorHelper(sortedProducts),
345
+ _step2;
346
+ try {
347
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
348
+ var product = _step2.value;
349
+ if (remainingLimit <= 0) break;
350
+ // 动态计算当前可抵扣数量 = ceil(剩余金额 / 单价)
351
+ var currentAvailableQty = Math.ceil(product[amountField].dividedBy(product[unitPriceField]).toNumber());
352
+ var deductQty = Math.min(currentAvailableQty, remainingLimit);
353
+ // 实际可抵扣金额 = min(数量 * 单价, 剩余金额)
354
+ var deductAmount = Decimal.min(product[unitPriceField].times(deductQty), product[amountField]);
355
+ calculatedAvailableMaxAmount = calculatedAvailableMaxAmount.plus(deductAmount);
356
+ remainingLimit -= deductQty;
357
+ }
358
+ } catch (err) {
359
+ _iterator2.e(err);
360
+ } finally {
361
+ _iterator2.f();
362
+ }
363
+ } else {
364
+ // 无数量限制:所有适用商品的剩余金额总和
365
+ calculatedAvailableMaxAmount = applicableProducts.reduce(function (sum, p) {
366
+ return sum.plus(p[amountField]);
367
+ }, new Decimal(0));
368
+ }
321
369
  } else {
322
- // 非跨商品券:单个最高金额商品
370
+ // 非跨商品券:单个最高单价商品,也受 applicableProductLimit 限制
323
371
  var maxProduct = applicableProducts.reduce(function (max, p) {
324
- return p[amountField].greaterThan(max[amountField]) ? p : max;
372
+ return p[unitPriceField].greaterThan(max[unitPriceField]) ? p : max;
325
373
  });
326
- calculatedAvailableMaxAmount = maxProduct[amountField];
374
+ // 动态计算当前可抵扣数量
375
+ var _currentAvailableQty2 = Math.ceil(maxProduct[amountField].dividedBy(maxProduct[unitPriceField]).toNumber());
376
+ var _deductQty2 = applicableProductLimit > 0 ? Math.min(_currentAvailableQty2, applicableProductLimit) : _currentAvailableQty2;
377
+ // 实际可抵扣金额 = min(数量 * 单价, 剩余金额)
378
+ calculatedAvailableMaxAmount = Decimal.min(maxProduct[unitPriceField].times(_deductQty2), maxProduct[amountField]);
327
379
  }
328
380
 
329
381
  // 取最小值:min(recommended_usage_amount, maxDeductionAmount, 适用商品金额, 订单剩余金额)
@@ -335,44 +387,74 @@ export function processVouchers(applicableVouchers, orderTotalAmount, products)
335
387
  var deductionLeft = maxDeduction;
336
388
  var deductionDetails = [];
337
389
  if (allowCrossProduct) {
338
- // 跨商品券:按剩余应付金额从高到低抵扣
339
- var sortedProducts = _toConsumableArray(applicableProducts).sort(function (a, b) {
340
- return a[amountField].comparedTo(b[amountField]) > 0 ? -1 : 1;
390
+ // 跨商品券:按单价从高到低抵扣,受 applicableProductLimit 限制
391
+ var _sortedProducts = _toConsumableArray(applicableProducts).sort(function (a, b) {
392
+ return a[unitPriceField].comparedTo(b[unitPriceField]) > 0 ? -1 : 1;
341
393
  });
342
- var _iterator = _createForOfIteratorHelper(sortedProducts),
343
- _step;
394
+ var _remainingLimit = applicableProductLimit > 0 ? applicableProductLimit : Infinity;
395
+ var _iterator3 = _createForOfIteratorHelper(_sortedProducts),
396
+ _step3;
344
397
  try {
345
- for (_iterator.s(); !(_step = _iterator.n()).done;) {
346
- var product = _step.value;
347
- if (deductionLeft.lessThanOrEqualTo(0)) break;
348
- var deductAmount = Decimal.min(deductionLeft, product[amountField]);
349
- product[amountField] = product[amountField].minus(deductAmount);
350
- deductionLeft = deductionLeft.minus(deductAmount);
398
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
399
+ var _product = _step3.value;
400
+ if (deductionLeft.lessThanOrEqualTo(0) || _remainingLimit <= 0) break;
401
+
402
+ // 动态计算当前可抵扣数量 = ceil(剩余金额 / 单价)
403
+ var _currentAvailableQty3 = Math.ceil(_product[amountField].dividedBy(_product[unitPriceField]).toNumber());
404
+ var availableQty = Math.min(_currentAvailableQty3, _remainingLimit);
405
+
406
+ // 计算本商品最大可抵扣金额 = min(数量 * 单价, 剩余金额)
407
+ var maxDeductForProduct = Decimal.min(_product[unitPriceField].times(availableQty), _product[amountField]);
408
+ var actualDeductAmount = Decimal.min(deductionLeft, maxDeductForProduct);
409
+
410
+ // 计算实际抵扣的数量(用于记录和配额计算)
411
+ var actualDeductQty = Math.ceil(actualDeductAmount.dividedBy(_product[unitPriceField]).toNumber());
412
+
413
+ // 只更新商品的剩余金额,不更新 remainingQuantity
414
+ _product[amountField] = _product[amountField].minus(actualDeductAmount);
415
+ deductionLeft = deductionLeft.minus(actualDeductAmount);
416
+ _remainingLimit -= actualDeductQty;
351
417
  deductionDetails.push({
352
- product_id: product.product_id,
353
- parent_product_id: product.parent_product_id || null,
354
- is_bundle_item: product.is_bundle_item || false,
355
- deductAmount: deductAmount.toNumber() // 转换为数字
418
+ product_id: _product.product_id,
419
+ parent_product_id: _product.parent_product_id || null,
420
+ is_bundle_item: _product.is_bundle_item || false,
421
+ deductAmount: actualDeductAmount.toNumber(),
422
+ // 转换为数字
423
+ deductQuantity: actualDeductQty // 抵扣涉及的数量(用于记录)
356
424
  });
357
425
  }
358
426
  } catch (err) {
359
- _iterator.e(err);
427
+ _iterator3.e(err);
360
428
  } finally {
361
- _iterator.f();
429
+ _iterator3.f();
362
430
  }
363
431
  } else {
364
- // 非跨商品券:只抵扣一个商品(金额最高的)
432
+ // 非跨商品券:只抵扣一个商品(单价最高的),也受 applicableProductLimit 限制
365
433
  var targetProduct = applicableProducts.reduce(function (max, p) {
366
- return p[amountField].greaterThan(max[amountField]) ? p : max;
434
+ return p[unitPriceField].greaterThan(max[unitPriceField]) ? p : max;
367
435
  });
368
- var _deductAmount = Decimal.min(deductionLeft, targetProduct[amountField]);
369
- targetProduct[amountField] = targetProduct[amountField].minus(_deductAmount);
370
- deductionLeft = deductionLeft.minus(_deductAmount);
436
+
437
+ // 动态计算当前可抵扣数量
438
+ var _currentAvailableQty4 = Math.ceil(targetProduct[amountField].dividedBy(targetProduct[unitPriceField]).toNumber());
439
+ var _availableQty = applicableProductLimit > 0 ? Math.min(_currentAvailableQty4, applicableProductLimit) : _currentAvailableQty4;
440
+
441
+ // 计算本商品最大可抵扣金额 = min(数量 * 单价, 剩余金额)
442
+ var _maxDeductForProduct = Decimal.min(targetProduct[unitPriceField].times(_availableQty), targetProduct[amountField]);
443
+ var _actualDeductAmount = Decimal.min(deductionLeft, _maxDeductForProduct);
444
+
445
+ // 计算实际抵扣的数量
446
+ var _actualDeductQty = Math.ceil(_actualDeductAmount.dividedBy(targetProduct[unitPriceField]).toNumber());
447
+
448
+ // 只更新商品的剩余金额,不更新 remainingQuantity
449
+ targetProduct[amountField] = targetProduct[amountField].minus(_actualDeductAmount);
450
+ deductionLeft = deductionLeft.minus(_actualDeductAmount);
371
451
  deductionDetails.push({
372
452
  product_id: targetProduct.product_id,
373
453
  parent_product_id: targetProduct.parent_product_id || null,
374
454
  is_bundle_item: targetProduct.is_bundle_item || false,
375
- deductAmount: _deductAmount.toNumber() // 转换为数字
455
+ deductAmount: _actualDeductAmount.toNumber(),
456
+ // 转换为数字
457
+ deductQuantity: _actualDeductQty // 抵扣涉及的数量(用于记录)
376
458
  });
377
459
  }
378
460
  var totalDeducted = maxDeduction.minus(deductionLeft);
@@ -474,10 +556,11 @@ export function recalculateVouchers(allVouchers, selectedVouchers, orderTotalAmo
474
556
  _config$deductTaxAndF3 = config.deductTaxAndFee,
475
557
  deductTaxAndFee = _config$deductTaxAndF3 === void 0 ? true : _config$deductTaxAndF3;
476
558
 
477
- // 根据券的配置选择使用哪个金额字段
559
+ // 根据券的配置选择使用哪个单价字段和金额字段
560
+ var unitPriceField = deductTaxAndFee ? 'unitPriceWithTax' : 'unitPricePure';
478
561
  var amountField = deductTaxAndFee ? 'remainingAmountWithTax' : 'remainingAmountPure';
479
562
 
480
- // 获取适用商品
563
+ // 获取适用商品(只筛选有剩余金额的商品)
481
564
  var applicableProducts = getApplicableProducts(selectedVoucher, productsForCalc).filter(function (p) {
482
565
  return p[amountField].greaterThan(0);
483
566
  });
@@ -493,13 +576,6 @@ export function recalculateVouchers(allVouchers, selectedVouchers, orderTotalAmo
493
576
  return;
494
577
  }
495
578
 
496
- // 考虑 applicableProductLimit
497
- if (allowCrossProduct && applicableProductLimit > 0) {
498
- applicableProducts = applicableProducts.sort(function (a, b) {
499
- return a[amountField].comparedTo(b[amountField]) > 0 ? -1 : 1;
500
- }).slice(0, applicableProductLimit);
501
- }
502
-
503
579
  // 计算本次抵扣金额
504
580
  // 优先使用用户手动编辑的金额,否则根据 deductTaxAndFee 配置获取推荐金额
505
581
  var usageAmount = typeof selectedVoucher.edit_current_amount === 'number' ? selectedVoucher.edit_current_amount : getRecommendedAmount(selectedVoucher);
@@ -507,44 +583,74 @@ export function recalculateVouchers(allVouchers, selectedVouchers, orderTotalAmo
507
583
  var deductionLeft = maxDeduction;
508
584
  var deductionDetails = [];
509
585
  if (allowCrossProduct) {
510
- // 跨商品券:按剩余应付金额从高到低抵扣
586
+ // 跨商品券:按单价从高到低抵扣,受 applicableProductLimit 限制
511
587
  var sortedProducts = _toConsumableArray(applicableProducts).sort(function (a, b) {
512
- return a[amountField].comparedTo(b[amountField]) > 0 ? -1 : 1;
588
+ return a[unitPriceField].comparedTo(b[unitPriceField]) > 0 ? -1 : 1;
513
589
  });
514
- var _iterator2 = _createForOfIteratorHelper(sortedProducts),
515
- _step2;
590
+ var remainingLimit = applicableProductLimit > 0 ? applicableProductLimit : Infinity;
591
+ var _iterator4 = _createForOfIteratorHelper(sortedProducts),
592
+ _step4;
516
593
  try {
517
- for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
518
- var product = _step2.value;
519
- if (deductionLeft.lessThanOrEqualTo(0)) break;
520
- var deductAmount = Decimal.min(deductionLeft, product[amountField]);
521
- product[amountField] = product[amountField].minus(deductAmount);
522
- deductionLeft = deductionLeft.minus(deductAmount);
594
+ for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
595
+ var product = _step4.value;
596
+ if (deductionLeft.lessThanOrEqualTo(0) || remainingLimit <= 0) break;
597
+
598
+ // 动态计算当前可抵扣数量 = ceil(剩余金额 / 单价)
599
+ var currentAvailableQty = Math.ceil(product[amountField].dividedBy(product[unitPriceField]).toNumber());
600
+ var availableQty = Math.min(currentAvailableQty, remainingLimit);
601
+
602
+ // 计算本商品最大可抵扣金额 = min(数量 * 单价, 剩余金额)
603
+ var maxDeductForProduct = Decimal.min(product[unitPriceField].times(availableQty), product[amountField]);
604
+ var actualDeductAmount = Decimal.min(deductionLeft, maxDeductForProduct);
605
+
606
+ // 计算实际抵扣的数量(用于记录和配额计算)
607
+ var actualDeductQty = Math.ceil(actualDeductAmount.dividedBy(product[unitPriceField]).toNumber());
608
+
609
+ // 只更新商品的剩余金额,不更新 remainingQuantity
610
+ product[amountField] = product[amountField].minus(actualDeductAmount);
611
+ deductionLeft = deductionLeft.minus(actualDeductAmount);
612
+ remainingLimit -= actualDeductQty;
523
613
  deductionDetails.push({
524
614
  product_id: product.product_id,
525
615
  parent_product_id: product.parent_product_id || null,
526
616
  is_bundle_item: product.is_bundle_item || false,
527
- deductAmount: deductAmount.toNumber() // 转换为数字
617
+ deductAmount: actualDeductAmount.toNumber(),
618
+ // 转换为数字
619
+ deductQuantity: actualDeductQty // 抵扣涉及的数量(用于记录)
528
620
  });
529
621
  }
530
622
  } catch (err) {
531
- _iterator2.e(err);
623
+ _iterator4.e(err);
532
624
  } finally {
533
- _iterator2.f();
625
+ _iterator4.f();
534
626
  }
535
627
  } else {
536
- // 非跨商品券:只抵扣一个商品(金额最高的)
628
+ // 非跨商品券:只抵扣一个商品(单价最高的),也受 applicableProductLimit 限制
537
629
  var targetProduct = applicableProducts.reduce(function (max, p) {
538
- return p[amountField].greaterThan(max[amountField]) ? p : max;
630
+ return p[unitPriceField].greaterThan(max[unitPriceField]) ? p : max;
539
631
  });
540
- var _deductAmount2 = Decimal.min(deductionLeft, targetProduct[amountField]);
541
- targetProduct[amountField] = targetProduct[amountField].minus(_deductAmount2);
542
- deductionLeft = deductionLeft.minus(_deductAmount2);
632
+
633
+ // 动态计算当前可抵扣数量
634
+ var _currentAvailableQty5 = Math.ceil(targetProduct[amountField].dividedBy(targetProduct[unitPriceField]).toNumber());
635
+ var _availableQty2 = applicableProductLimit > 0 ? Math.min(_currentAvailableQty5, applicableProductLimit) : _currentAvailableQty5;
636
+
637
+ // 计算本商品最大可抵扣金额 = min(数量 * 单价, 剩余金额)
638
+ var _maxDeductForProduct2 = Decimal.min(targetProduct[unitPriceField].times(_availableQty2), targetProduct[amountField]);
639
+ var _actualDeductAmount2 = Decimal.min(deductionLeft, _maxDeductForProduct2);
640
+
641
+ // 计算实际抵扣的数量
642
+ var _actualDeductQty2 = Math.ceil(_actualDeductAmount2.dividedBy(targetProduct[unitPriceField]).toNumber());
643
+
644
+ // 只更新商品的剩余金额,不更新 remainingQuantity
645
+ targetProduct[amountField] = targetProduct[amountField].minus(_actualDeductAmount2);
646
+ deductionLeft = deductionLeft.minus(_actualDeductAmount2);
543
647
  deductionDetails.push({
544
648
  product_id: targetProduct.product_id,
545
649
  parent_product_id: targetProduct.parent_product_id || null,
546
650
  is_bundle_item: targetProduct.is_bundle_item || false,
547
- deductAmount: _deductAmount2.toNumber() // 转换为数字
651
+ deductAmount: _actualDeductAmount2.toNumber(),
652
+ // 转换为数字
653
+ deductQuantity: _actualDeductQty2 // 抵扣涉及的数量(用于记录)
548
654
  });
549
655
  }
550
656
  var totalDeducted = maxDeduction.minus(deductionLeft);
@@ -588,7 +694,8 @@ export function recalculateVouchers(allVouchers, selectedVouchers, orderTotalAmo
588
694
  _config$deductTaxAndF4 = config.deductTaxAndFee,
589
695
  deductTaxAndFee = _config$deductTaxAndF4 === void 0 ? true : _config$deductTaxAndF4;
590
696
 
591
- // 根据券的配置选择使用哪个金额字段
697
+ // 根据券的配置选择使用哪个单价字段和金额字段
698
+ var unitPriceField = deductTaxAndFee ? 'unitPriceWithTax' : 'unitPricePure';
592
699
  var amountField = deductTaxAndFee ? 'remainingAmountWithTax' : 'remainingAmountPure';
593
700
 
594
701
  // 根据 deductTaxAndFee 配置获取推荐金额
@@ -614,7 +721,7 @@ export function recalculateVouchers(allVouchers, selectedVouchers, orderTotalAmo
614
721
  }
615
722
  }
616
723
  if (isAvailable) {
617
- // 获取适用商品
724
+ // 获取适用商品(只筛选有剩余金额的商品)
618
725
  var applicableProducts = getApplicableProducts(voucher, productsForCalc).filter(function (p) {
619
726
  return p[amountField].greaterThan(0);
620
727
  });
@@ -626,22 +733,46 @@ export function recalculateVouchers(allVouchers, selectedVouchers, orderTotalAmo
626
733
  var baseAmount = Decimal.min(new Decimal(recommendedAmount), new Decimal(maxDeductionAmount));
627
734
  if (allowCrossProduct) {
628
735
  if (applicableProductLimit > 0) {
736
+ // 按单价从高到低排序,按动态计算的可抵扣数量累计直到达到 limit
629
737
  var sortedProducts = _toConsumableArray(applicableProducts).sort(function (a, b) {
630
- return a[amountField].comparedTo(b[amountField]) > 0 ? -1 : 1;
631
- }).slice(0, applicableProductLimit);
632
- calculatedMaxAmount = sortedProducts.reduce(function (sum, p) {
633
- return sum.plus(p[amountField]);
634
- }, new Decimal(0));
738
+ return a[unitPriceField].comparedTo(b[unitPriceField]) > 0 ? -1 : 1;
739
+ });
740
+ var remainingLimit = applicableProductLimit;
741
+ var _iterator5 = _createForOfIteratorHelper(sortedProducts),
742
+ _step5;
743
+ try {
744
+ for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
745
+ var product = _step5.value;
746
+ if (remainingLimit <= 0) break;
747
+ // 动态计算当前可抵扣数量 = ceil(剩余金额 / 单价)
748
+ var currentAvailableQty = Math.ceil(product[amountField].dividedBy(product[unitPriceField]).toNumber());
749
+ var deductQty = Math.min(currentAvailableQty, remainingLimit);
750
+ // 实际可抵扣金额 = min(数量 * 单价, 剩余金额)
751
+ var deductAmount = Decimal.min(product[unitPriceField].times(deductQty), product[amountField]);
752
+ calculatedMaxAmount = calculatedMaxAmount.plus(deductAmount);
753
+ remainingLimit -= deductQty;
754
+ }
755
+ } catch (err) {
756
+ _iterator5.e(err);
757
+ } finally {
758
+ _iterator5.f();
759
+ }
635
760
  } else {
761
+ // 无数量限制:所有适用商品的剩余金额总和
636
762
  calculatedMaxAmount = applicableProducts.reduce(function (sum, p) {
637
763
  return sum.plus(p[amountField]);
638
764
  }, new Decimal(0));
639
765
  }
640
766
  } else {
767
+ // 非跨商品券:单个最高单价商品,也受 applicableProductLimit 限制
641
768
  var maxProduct = applicableProducts.reduce(function (max, p) {
642
- return p[amountField].greaterThan(max[amountField]) ? p : max;
769
+ return p[unitPriceField].greaterThan(max[unitPriceField]) ? p : max;
643
770
  });
644
- calculatedMaxAmount = maxProduct[amountField];
771
+ // 动态计算当前可抵扣数量
772
+ var _currentAvailableQty6 = Math.ceil(maxProduct[amountField].dividedBy(maxProduct[unitPriceField]).toNumber());
773
+ var _deductQty3 = applicableProductLimit > 0 ? Math.min(_currentAvailableQty6, applicableProductLimit) : _currentAvailableQty6;
774
+ // 实际可抵扣金额 = min(数量 * 单价, 剩余金额)
775
+ calculatedMaxAmount = Decimal.min(maxProduct[unitPriceField].times(_deductQty3), maxProduct[amountField]);
645
776
  }
646
777
  calculatedMaxAmount = Decimal.min(baseAmount, calculatedMaxAmount, remainingOrderAmount);
647
778
  if (calculatedMaxAmount.lessThanOrEqualTo(0)) {
@@ -674,11 +805,11 @@ export function recalculateVouchers(allVouchers, selectedVouchers, orderTotalAmo
674
805
  export var getMainProductPrice = function getMainProductPrice(product, isDeductTaxAndFee) {
675
806
  var _product$metadata, _product$metadata2, _product$metadata3;
676
807
  var mainProductPrice = new Decimal((product === null || product === void 0 ? void 0 : product.main_product_selling_price) || ((_product$metadata = product.metadata) === null || _product$metadata === void 0 ? void 0 : _product$metadata.main_product_selling_price) || 0);
677
- var _iterator3 = _createForOfIteratorHelper((product === null || product === void 0 ? void 0 : product.product_bundle) || []),
678
- _step3;
808
+ var _iterator6 = _createForOfIteratorHelper((product === null || product === void 0 ? void 0 : product.product_bundle) || []),
809
+ _step6;
679
810
  try {
680
- for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
681
- var bundleItem = _step3.value;
811
+ for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
812
+ var bundleItem = _step6.value;
682
813
  // 子商品是加价或减价才需要计算
683
814
  if (getBundleItemIsMarkupOrDiscountPrice(bundleItem)) {
684
815
  var _bundleItem$bundle_se;
@@ -690,9 +821,9 @@ export var getMainProductPrice = function getMainProductPrice(product, isDeductT
690
821
 
691
822
  // 税费
692
823
  } catch (err) {
693
- _iterator3.e(err);
824
+ _iterator6.e(err);
694
825
  } finally {
695
- _iterator3.f();
826
+ _iterator6.f();
696
827
  }
697
828
  var taxFee = new Decimal((product === null || product === void 0 ? void 0 : product.tax_fee) || (product === null || product === void 0 || (_product$metadata2 = product.metadata) === null || _product$metadata2 === void 0 ? void 0 : _product$metadata2.main_product_attached_bundle_tax_fee) || 0);
698
829
  // 附加费
@@ -737,35 +868,54 @@ export var getBundleItemPrice = function getBundleItemPrice(bundleItem, parentQu
737
868
  * 将商品数据拆分,包含主商品和原价子商品
738
869
  * @param products 原始商品列表
739
870
  * @param deductTaxAndFee 是否抵扣税费与附加费(已废弃,保留参数以兼容旧代码)
740
- * @returns 拆分后的商品数据(包含主商品和子商品,同时维护含税和不含税两个金额池)
871
+ * @returns 拆分后的商品数据(包含主商品和子商品,同时维护含税和不含税两个金额池,以及单价和剩余数量)
741
872
  */
742
873
  var expandProductsWithBundleItems = function expandProductsWithBundleItems(products, deductTaxAndFee) {
743
874
  var expandedProducts = [];
744
875
  products.forEach(function (product) {
745
876
  var productQuantity = getProductQuantity(product);
746
877
 
747
- // 1. 添加主商品(同时计算含税和不含税两个金额)
878
+ // 计算主商品单价(含税和不含税)
879
+ var unitPriceWithTax = getMainProductPrice(product, true);
880
+ var unitPricePure = getMainProductPrice(product, false);
881
+
882
+ // 1. 添加主商品(同时计算含税和不含税两个金额,以及单价和剩余数量)
748
883
  expandedProducts.push(_objectSpread(_objectSpread({}, product), {}, {
749
884
  is_bundle_item: false,
750
885
  parent_product_id: null,
886
+ // 单价(用于按 quantity 计算抵扣)
887
+ unitPriceWithTax: new Decimal(unitPriceWithTax),
888
+ unitPricePure: new Decimal(unitPricePure),
889
+ // 剩余可抵扣数量
890
+ remainingQuantity: productQuantity,
751
891
  // 含税费的剩余金额
752
- remainingAmountWithTax: new Decimal(getMainProductPrice(product, true)).times(productQuantity),
892
+ remainingAmountWithTax: new Decimal(unitPriceWithTax).times(productQuantity),
753
893
  // 纯商品金额(不含税费)
754
- remainingAmountPure: new Decimal(getMainProductPrice(product, false)).times(productQuantity)
894
+ remainingAmountPure: new Decimal(unitPricePure).times(productQuantity)
755
895
  }));
756
896
 
757
897
  // 2. 添加原价子商品(作为独立商品项)
758
898
  if (product.product_bundle && product.product_bundle.length > 0) {
759
899
  product.product_bundle.forEach(function (bundleItem) {
760
900
  if (getBundleItemIsOriginalPrice(bundleItem)) {
901
+ var bundleQuantity = bundleItem.num * productQuantity;
902
+ // 计算子商品单价(注意:getBundleItemPrice 返回的是总价,需要除以数量得到单价)
903
+ var bundleUnitPriceWithTax = new Decimal(getBundleItemPrice(bundleItem, 1, true)).dividedBy(bundleItem.num);
904
+ var bundleUnitPricePure = new Decimal(getBundleItemPrice(bundleItem, 1, false)).dividedBy(bundleItem.num);
905
+
761
906
  // 原价子商品作为独立商品
762
907
  expandedProducts.push(_objectSpread(_objectSpread({}, bundleItem), {}, {
763
908
  product_id: bundleItem.bundle_product_id,
764
909
  // 使用 bundle_product_id 作为 product_id
765
910
  is_bundle_item: true,
766
911
  parent_product_id: product.product_id,
767
- quantity: bundleItem.num * productQuantity,
912
+ quantity: bundleQuantity,
768
913
  // 子商品数量 * 主商品数量
914
+ // 单价(用于按 quantity 计算抵扣)
915
+ unitPriceWithTax: bundleUnitPriceWithTax,
916
+ unitPricePure: bundleUnitPricePure,
917
+ // 剩余可抵扣数量
918
+ remainingQuantity: bundleQuantity,
769
919
  // 含税费的剩余金额
770
920
  remainingAmountWithTax: new Decimal(getBundleItemPrice(bundleItem, productQuantity, true)),
771
921
  // 纯商品金额(不含税费)