@pisell/pisellos 3.0.75 → 3.0.77

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.
@@ -37,6 +37,7 @@ var import_types = require("./types");
37
37
  var import_utils = require("../../solution/ShopDiscount/utils");
38
38
  var import_utils2 = require("../Cart/utils");
39
39
  var import_decimal = __toESM(require("decimal.js"));
40
+ var import_lodash_es = require("lodash-es");
40
41
  var import_dayjs = __toESM(require("dayjs"));
41
42
  var RulesModule = class extends import_BaseModule.BaseModule {
42
43
  constructor(name, version) {
@@ -96,11 +97,13 @@ var RulesModule = class extends import_BaseModule.BaseModule {
96
97
  productList
97
98
  };
98
99
  }
99
- const filteredOldDiscountList = oldDiscountList.filter((discount) => !discount.isEditMode && discount.tag !== "good_pass");
100
- const mergedDiscountList = (0, import_utils.uniqueById)((0, import_utils.uniqueById)([
100
+ const filteredOldDiscountList = oldDiscountList.filter(
101
+ (discount) => !discount.isEditMode && (discount.tag !== "good_pass" || discount.isScan)
102
+ );
103
+ const mergedDiscountList = (0, import_utils.uniqueById)([
101
104
  ...filteredOldDiscountList,
102
105
  ...newDiscountList
103
- ]), "product_id");
106
+ ]);
104
107
  const result = this.calcDiscount({
105
108
  discountList: mergedDiscountList,
106
109
  productList: [...productList],
@@ -151,6 +154,20 @@ var RulesModule = class extends import_BaseModule.BaseModule {
151
154
  addModeDiscount.push(discount);
152
155
  }
153
156
  });
157
+ const editModeOrderLevelProductIds = /* @__PURE__ */ new Set();
158
+ editModeDiscount.forEach((discount) => {
159
+ var _a, _b;
160
+ if (((_a = discount.discount) == null ? void 0 : _a.discount_calculation_mode) === "order_level") {
161
+ editModeOrderLevelProductIds.add((_b = discount == null ? void 0 : discount.discount) == null ? void 0 : _b.discount_product_id);
162
+ }
163
+ });
164
+ if (editModeOrderLevelProductIds.size > 0) {
165
+ addModeDiscount.forEach((discount) => {
166
+ if (editModeOrderLevelProductIds.has(discount.product_id)) {
167
+ discount.isDisabled = true;
168
+ }
169
+ });
170
+ }
154
171
  const filteredDiscountList = addModeDiscount.filter((discount) => {
155
172
  return !discount.isManualSelect;
156
173
  });
@@ -198,51 +215,95 @@ var RulesModule = class extends import_BaseModule.BaseModule {
198
215
  return flattened;
199
216
  };
200
217
  const sortedDiscountList = [...filteredDiscountList].sort((a, b) => {
201
- var _a, _b;
202
- if (a.tag === "good_pass" && b.tag !== "good_pass")
203
- return -1;
204
- if (b.tag === "good_pass" && a.tag !== "good_pass")
205
- return 1;
206
- if (a.tag === "good_pass" && b.tag === "good_pass") {
207
- return compareByExpireTime(a, b);
208
- } else if (a.tag === "product_discount_card" && b.tag === "product_discount_card") {
209
- const typeA = ((_a = a.metadata) == null ? void 0 : _a.discount_card_type) || "percent";
210
- const typeB = ((_b = b.metadata) == null ? void 0 : _b.discount_card_type) || "percent";
218
+ const isHolderDiscount = (discount) => {
219
+ var _a, _b;
220
+ return ((_b = (_a = discount.metadata) == null ? void 0 : _a.holder) == null ? void 0 : _b.type) === "custom";
221
+ };
222
+ const isGoodPass = (discount) => discount.tag === "good_pass";
223
+ const isOrderLevelDiscount = (discount) => {
224
+ var _a;
225
+ return discount.tag === "product_discount_card" && ((_a = discount.metadata) == null ? void 0 : _a.discount_calculation_mode) === "order_level";
226
+ };
227
+ const isItemLevelDiscount = (discount) => {
228
+ var _a;
229
+ return discount.tag === "product_discount_card" && ((_a = discount.metadata) == null ? void 0 : _a.discount_calculation_mode) !== "order_level";
230
+ };
231
+ const getPriority = (discount) => {
232
+ const isHolder = isHolderDiscount(discount);
233
+ if (isHolder) {
234
+ if (isGoodPass(discount))
235
+ return 1;
236
+ if (isItemLevelDiscount(discount))
237
+ return 2;
238
+ if (isOrderLevelDiscount(discount))
239
+ return 3;
240
+ } else {
241
+ if (isGoodPass(discount))
242
+ return 4;
243
+ if (isItemLevelDiscount(discount))
244
+ return 5;
245
+ if (isOrderLevelDiscount(discount))
246
+ return 6;
247
+ }
248
+ return 7;
249
+ };
250
+ function compareByExpireTime(itemA, itemB) {
251
+ if (itemA.expire_time && itemB.expire_time) {
252
+ const timeA = new Date(itemA.expire_time).getTime();
253
+ const timeB = new Date(itemB.expire_time).getTime();
254
+ return timeA - timeB;
255
+ }
256
+ return itemA.expire_time ? -1 : itemB.expire_time ? 1 : 0;
257
+ }
258
+ function compareDiscountCardValue(itemA, itemB) {
259
+ var _a, _b;
260
+ const typeA = ((_a = itemA.metadata) == null ? void 0 : _a.discount_card_type) || "percent";
261
+ const typeB = ((_b = itemB.metadata) == null ? void 0 : _b.discount_card_type) || "percent";
211
262
  if (typeA === "fixed_amount" && typeB === "percent")
212
263
  return -1;
213
264
  if (typeA === "percent" && typeB === "fixed_amount")
214
265
  return 1;
215
266
  if (typeA === "fixed_amount" && typeB === "fixed_amount") {
216
- if (a.par_value !== b.par_value) {
217
- const valueA = new import_decimal.default(a.par_value || 0);
218
- const valueB = new import_decimal.default(b.par_value || 0);
267
+ if (itemA.par_value !== itemB.par_value) {
268
+ const valueA = new import_decimal.default(itemA.par_value || 0);
269
+ const valueB = new import_decimal.default(itemB.par_value || 0);
219
270
  return valueB.minus(valueA).toNumber();
220
271
  }
221
272
  }
222
273
  if (typeA === "percent" && typeB === "percent") {
223
- if (a.par_value !== b.par_value) {
224
- const valueA = new import_decimal.default(100).minus(a.par_value || 0);
225
- const valueB = new import_decimal.default(100).minus(b.par_value || 0);
274
+ if (itemA.par_value !== itemB.par_value) {
275
+ const valueA = new import_decimal.default(100).minus(itemA.par_value || 0);
276
+ const valueB = new import_decimal.default(100).minus(itemB.par_value || 0);
226
277
  return valueA.minus(valueB).toNumber();
227
278
  }
228
279
  }
280
+ return 0;
281
+ }
282
+ const priorityA = getPriority(a);
283
+ const priorityB = getPriority(b);
284
+ if (priorityA !== priorityB) {
285
+ return priorityA - priorityB;
286
+ }
287
+ if (isGoodPass(a) && isGoodPass(b)) {
229
288
  return compareByExpireTime(a, b);
230
289
  }
231
- return compareByExpireTime(a, b);
232
- function compareByExpireTime(itemA, itemB) {
233
- if (itemA.expire_time && itemB.expire_time) {
234
- const timeA = new Date(itemA.expire_time).getTime();
235
- const timeB = new Date(itemB.expire_time).getTime();
236
- return timeA - timeB;
237
- }
238
- return itemA.expire_time ? -1 : itemB.expire_time ? 1 : 0;
290
+ if (a.tag === "product_discount_card" && b.tag === "product_discount_card") {
291
+ const valueCompare = compareDiscountCardValue(a, b);
292
+ if (valueCompare !== 0)
293
+ return valueCompare;
294
+ return compareByExpireTime(a, b);
239
295
  }
296
+ return compareByExpireTime(a, b);
240
297
  });
241
298
  const flattenedList = flattenProductsWithBundle(productList);
242
299
  const sortedFlattenedList = flattenedList.sort((a, b) => {
243
300
  var _a, _b;
244
- const priceA = new import_decimal.default(a.price || "0");
245
- const priceB = new import_decimal.default(b.price || "0");
301
+ const priceA = new import_decimal.default(
302
+ a.type === "bundle" ? a.original_price ?? a.price ?? "0" : a.price || "0"
303
+ );
304
+ const priceB = new import_decimal.default(
305
+ b.type === "bundle" ? b.original_price ?? b.price ?? "0" : b.price || "0"
306
+ );
246
307
  if (priceA.equals(priceB)) {
247
308
  if (a.type !== b.type) {
248
309
  return a.type === "main" ? -1 : 1;
@@ -266,6 +327,129 @@ var RulesModule = class extends import_BaseModule.BaseModule {
266
327
  discountApplicability.set(discount.id, []);
267
328
  discountApplicableProducts.set(discount.id, []);
268
329
  });
330
+ const orderLevelDiscountAllocations = /* @__PURE__ */ new Map();
331
+ const orderLevelDiscountApplicableItems = /* @__PURE__ */ new Map();
332
+ const itemApplicableDiscounts = /* @__PURE__ */ new Map();
333
+ const checkItemApplicableForDiscount = (flatItem, discount) => {
334
+ var _a, _b, _c, _d, _e, _f, _g, _h;
335
+ let product;
336
+ if (flatItem.type === "main") {
337
+ product = flatItem.product;
338
+ } else {
339
+ product = {
340
+ _id: flatItem._id,
341
+ id: flatItem.id,
342
+ price: flatItem.price,
343
+ quantity: flatItem.quantity,
344
+ num: flatItem.num,
345
+ booking_id: flatItem.booking_id,
346
+ discount_list: flatItem.discount_list || [],
347
+ startDate: (_a = flatItem.parentProduct) == null ? void 0 : _a.startDate
348
+ };
349
+ }
350
+ const isAvailableProduct = flatItem.type === "main" ? !((product == null ? void 0 : product.booking_id) && ((_b = product == null ? void 0 : product.discount_list) == null ? void 0 : _b.length) && ((_c = product == null ? void 0 : product.discount_list) == null ? void 0 : _c.every((d) => d.id && ["good_pass", "discount_card", "product_discount_card"].includes(d.tag || d.type)))) : !((flatItem == null ? void 0 : flatItem.booking_id) && !!((_e = (_d = flatItem == null ? void 0 : flatItem.bundleItem) == null ? void 0 : _d.discount_list) == null ? void 0 : _e.length) && ((_g = (_f = flatItem == null ? void 0 : flatItem.bundleItem) == null ? void 0 : _f.discount_list) == null ? void 0 : _g.every((d) => d.id)));
351
+ if (!isAvailableProduct) {
352
+ return false;
353
+ }
354
+ if ((0, import_lodash_es.isBoolean)(product.vouchersApplicable) && !product.vouchersApplicable) {
355
+ return false;
356
+ }
357
+ if (Number(product.price) <= 0 || !product.price) {
358
+ return false;
359
+ }
360
+ const limitedData = discount.limited_relation_product_data;
361
+ const timeLimit = !!(0, import_utils.filterDiscountListByBookingTime)([discount], ((product == null ? void 0 : product.startDate) || (0, import_dayjs.default)()).format("YYYY-MM-DD HH:mm:ss")).length;
362
+ if (!timeLimit) {
363
+ return false;
364
+ }
365
+ let isLimitedProduct = false;
366
+ if (limitedData.type === "product_all") {
367
+ if (limitedData.filter === 1 && ((_h = limitedData.exclude_product_ids) == null ? void 0 : _h.includes(product.id))) {
368
+ isLimitedProduct = false;
369
+ } else {
370
+ isLimitedProduct = true;
371
+ }
372
+ } else if (limitedData.product_ids && limitedData.product_ids.includes(product.id)) {
373
+ isLimitedProduct = true;
374
+ }
375
+ const isBundleAvailable = this.checkPackageSubItemUsageRules(discount, flatItem);
376
+ return isLimitedProduct && isBundleAvailable;
377
+ };
378
+ const selectedOrderLevelDiscounts = sortedDiscountList.filter((discount) => {
379
+ return (0, import_utils.isOrderLevelFixedAmountDiscount)(discount) && discount.isSelected !== false;
380
+ });
381
+ selectedOrderLevelDiscounts.forEach((discount) => {
382
+ const applicableItemIds = /* @__PURE__ */ new Set();
383
+ sortedFlattenedList.forEach((flatItem) => {
384
+ if (checkItemApplicableForDiscount(flatItem, discount)) {
385
+ applicableItemIds.add(flatItem._id);
386
+ if (!itemApplicableDiscounts.has(flatItem._id)) {
387
+ itemApplicableDiscounts.set(flatItem._id, /* @__PURE__ */ new Set());
388
+ }
389
+ itemApplicableDiscounts.get(flatItem._id).add(discount.id);
390
+ }
391
+ });
392
+ orderLevelDiscountApplicableItems.set(discount.id, applicableItemIds);
393
+ });
394
+ const occupiedItems = /* @__PURE__ */ new Map();
395
+ selectedOrderLevelDiscounts.forEach((discount) => {
396
+ const limitedData = discount.limited_relation_product_data;
397
+ const isExclusiveDiscount = limitedData.type !== "product_all";
398
+ if (isExclusiveDiscount) {
399
+ const applicableItems = orderLevelDiscountApplicableItems.get(discount.id);
400
+ if (applicableItems) {
401
+ applicableItems.forEach((itemId) => {
402
+ if (!occupiedItems.has(itemId)) {
403
+ occupiedItems.set(itemId, discount.id);
404
+ }
405
+ });
406
+ }
407
+ }
408
+ });
409
+ console.log("occupiedItems", occupiedItems, "orderLevelDiscountApplicableItems", orderLevelDiscountApplicableItems);
410
+ sortedDiscountList.forEach((discount) => {
411
+ if (!(0, import_utils.isOrderLevelFixedAmountDiscount)(discount)) {
412
+ return;
413
+ }
414
+ const applicableProducts = [];
415
+ sortedFlattenedList.forEach((flatItem) => {
416
+ var _a, _b;
417
+ const occupyingDiscountId = occupiedItems.get(flatItem._id);
418
+ if (occupyingDiscountId !== void 0 && occupyingDiscountId !== discount.id) {
419
+ return;
420
+ }
421
+ if (!checkItemApplicableForDiscount(flatItem, discount)) {
422
+ return;
423
+ }
424
+ let product;
425
+ if (flatItem.type === "main") {
426
+ product = flatItem.product;
427
+ } else {
428
+ product = {
429
+ _id: flatItem._id,
430
+ id: flatItem.id,
431
+ price: flatItem.price,
432
+ quantity: flatItem.quantity,
433
+ num: flatItem.num
434
+ };
435
+ }
436
+ const quantity = flatItem.type === "main" ? product.quantity || 1 : (product.num || 1) * (((_a = flatItem.parentProduct) == null ? void 0 : _a.quantity) || 1);
437
+ const originalAmount = flatItem.type === "main" ? Number(product.price ?? 0) : Number(flatItem.original_price ?? flatItem.price ?? 0);
438
+ const productData = {
439
+ productId: flatItem._id,
440
+ amount: originalAmount,
441
+ quantity
442
+ };
443
+ if (flatItem.type === "bundle") {
444
+ productData.parentQuantity = ((_b = flatItem.parentProduct) == null ? void 0 : _b.quantity) || 1;
445
+ }
446
+ applicableProducts.push(productData);
447
+ });
448
+ if (applicableProducts.length > 0) {
449
+ const allocation = (0, import_utils.calculateOrderLevelDiscountAllocation)(discount, applicableProducts);
450
+ orderLevelDiscountAllocations.set(discount.id, allocation);
451
+ }
452
+ });
269
453
  const processedProductsMap = /* @__PURE__ */ new Map();
270
454
  const appliedDiscountProducts = /* @__PURE__ */ new Map();
271
455
  sortedFlattenedList.forEach((flatItem) => {
@@ -290,7 +474,7 @@ var RulesModule = class extends import_BaseModule.BaseModule {
290
474
  originProduct = flatItem.originProduct;
291
475
  }
292
476
  addModeDiscount.forEach((discount) => {
293
- var _a2, _b, _c, _d, _e, _f;
477
+ var _a2, _b, _c, _d, _e, _f, _g, _h, _i;
294
478
  const limitedData = discount == null ? void 0 : discount.limited_relation_product_data;
295
479
  const _tempVar = (flatItem == null ? void 0 : flatItem.type) === "bundle" ? flatItem == null ? void 0 : flatItem.parentProduct : flatItem == null ? void 0 : flatItem.product;
296
480
  const isHolderMatch = this.checkHolderMatch(
@@ -304,10 +488,16 @@ var RulesModule = class extends import_BaseModule.BaseModule {
304
488
  let timeLimit = true;
305
489
  timeLimit = !!(0, import_utils.filterDiscountListByBookingTime)([discount], ((product == null ? void 0 : product.startDate) || (0, import_dayjs.default)()).format("YYYY-MM-DD HH:mm:ss")).length;
306
490
  const isLimitedProduct = (limitedData.type === "product_all" || limitedData.product_ids && limitedData.product_ids.includes(product.id)) && isHolderMatch;
307
- const isAvailableProduct = flatItem.type === "main" ? !((product == null ? void 0 : product.booking_id) && ((_a2 = product == null ? void 0 : product.discount_list) == null ? void 0 : _a2.length) && ((_b = product == null ? void 0 : product.discount_list) == null ? void 0 : _b.every((discount2) => discount2.id && ["good_pass", "discount_card", "product_discount_card"].includes(discount2.tag || discount2.type)))) : !((flatItem == null ? void 0 : flatItem.booking_id) && ((_d = (_c = flatItem == null ? void 0 : flatItem.bundleItem) == null ? void 0 : _c.metadata) == null ? void 0 : _d.custom_product_bundle_map_id));
491
+ const isAvailableProduct = flatItem.type === "main" ? !((product == null ? void 0 : product.booking_id) && ((_a2 = product == null ? void 0 : product.discount_list) == null ? void 0 : _a2.length) && ((_b = product == null ? void 0 : product.discount_list) == null ? void 0 : _b.every(
492
+ (discount2) => discount2.id && [
493
+ "good_pass",
494
+ "discount_card",
495
+ "product_discount_card"
496
+ ].includes(discount2.tag || discount2.type)
497
+ ))) : !((flatItem == null ? void 0 : flatItem.booking_id) && !!((_d = (_c = flatItem == null ? void 0 : flatItem.bundleItem) == null ? void 0 : _c.discount_list) == null ? void 0 : _d.length) && ((_f = (_e = flatItem == null ? void 0 : flatItem.bundleItem) == null ? void 0 : _e.discount_list) == null ? void 0 : _f.every((discount2) => discount2.id)));
308
498
  const isBundleAvailable = this.checkPackageSubItemUsageRules(discount, flatItem);
309
499
  if (isAvailableProduct && isLimitedProduct && isBundleAvailable && timeLimit) {
310
- (_e = discountApplicability.get(discount.id)) == null ? void 0 : _e.push(product.id);
500
+ (_g = discountApplicability.get(discount.id)) == null ? void 0 : _g.push(product.id);
311
501
  const applicableProducts = discountApplicableProducts.get(discount.id) || [];
312
502
  const discountType = discount.tag || discount.type;
313
503
  const isGoodPass = discountType === "good_pass";
@@ -317,8 +507,9 @@ var RulesModule = class extends import_BaseModule.BaseModule {
317
507
  type: discountType,
318
508
  tag: discountType,
319
509
  discount: {
320
- discount_card_type: (_f = discount == null ? void 0 : discount.metadata) == null ? void 0 : _f.discount_card_type,
510
+ discount_card_type: (_h = discount == null ? void 0 : discount.metadata) == null ? void 0 : _h.discount_card_type,
321
511
  fixed_amount: product.price,
512
+ discount_calculation_mode: (_i = discount == null ? void 0 : discount.metadata) == null ? void 0 : _i.discount_calculation_mode,
322
513
  resource_id: discount.id,
323
514
  title: discount.format_title,
324
515
  original_amount: product.price || product.origin_total,
@@ -336,7 +527,7 @@ var RulesModule = class extends import_BaseModule.BaseModule {
336
527
  });
337
528
  const processedFlatItemsMap = /* @__PURE__ */ new Map();
338
529
  sortedFlattenedList.forEach((flatItem, index) => {
339
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y;
530
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B;
340
531
  let product, originProduct;
341
532
  if (flatItem.type === "main") {
342
533
  product = flatItem.product;
@@ -553,16 +744,29 @@ var RulesModule = class extends import_BaseModule.BaseModule {
553
744
  if (Number(((_v = originProduct == null ? void 0 : originProduct._productInit) == null ? void 0 : _v.original_price) || 0) > 0 && product.origin_total && product.total && product.origin_total !== product.total) {
554
745
  productOriginTotal = product.total;
555
746
  }
556
- const targetProductTotal = (0, import_utils.getDiscountAmount)(selectedDiscount2, product.price, product.price);
747
+ const isOrderLevel = (0, import_utils.isOrderLevelFixedAmountDiscount)(selectedDiscount2);
748
+ const orderLevelAllocation = isOrderLevel ? orderLevelDiscountAllocations.get(selectedDiscount2.id) : null;
749
+ const productAllocation = orderLevelAllocation == null ? void 0 : orderLevelAllocation.get(flatItem._id);
750
+ let targetProductTotal;
751
+ let amount;
752
+ let productDiscountDifference;
753
+ if (isOrderLevel && productAllocation) {
754
+ amount = productAllocation.discountAmount;
755
+ productDiscountDifference = productAllocation.difference;
756
+ targetProductTotal = Math.max(new import_decimal.default(product.price).minus(amount).toNumber(), 0);
757
+ } else {
758
+ targetProductTotal = (0, import_utils.getDiscountAmount)(selectedDiscount2, product.price, product.price);
759
+ amount = new import_decimal.default(product.price).minus(new import_decimal.default(targetProductTotal)).toNumber();
760
+ }
557
761
  const discountType = selectedDiscount2.tag || selectedDiscount2.type;
558
762
  const isGoodPass = discountType === "good_pass";
559
- const amount = new import_decimal.default(product.price).minus(new import_decimal.default(targetProductTotal)).toNumber();
560
763
  const discountDetail = {
561
764
  amount,
562
765
  type: selectedDiscount2.tag === "product_discount_card" ? "discount_card" : discountType,
563
766
  discount: {
564
767
  discount_card_type: (_w = selectedDiscount2 == null ? void 0 : selectedDiscount2.metadata) == null ? void 0 : _w.discount_card_type,
565
768
  fixed_amount: amount,
769
+ discount_calculation_mode: (_x = selectedDiscount2 == null ? void 0 : selectedDiscount2.metadata) == null ? void 0 : _x.discount_calculation_mode,
566
770
  resource_id: selectedDiscount2.id,
567
771
  title: selectedDiscount2.format_title,
568
772
  original_amount: product.price,
@@ -572,7 +776,9 @@ var RulesModule = class extends import_BaseModule.BaseModule {
572
776
  // 前端使用的num数量,为了计算优惠金额
573
777
  _num: isGoodPass ? 1 : product.num,
574
778
  metadata: {
575
- num: 1
779
+ num: 1,
780
+ // 🔥 order_level 分摊差值
781
+ ...productDiscountDifference !== void 0 && { product_discount_difference: productDiscountDifference }
576
782
  }
577
783
  };
578
784
  appliedProducts.push(discountDetail);
@@ -629,6 +835,7 @@ var RulesModule = class extends import_BaseModule.BaseModule {
629
835
  type: "good_pass",
630
836
  discount: {
631
837
  fixed_amount: product.origin_total,
838
+ discount_calculation_mode: (_y = selectedDiscount2 == null ? void 0 : selectedDiscount2.metadata) == null ? void 0 : _y.discount_calculation_mode,
632
839
  resource_id: selectedDiscount2.id,
633
840
  title: selectedDiscount2.format_title,
634
841
  original_amount: product.origin_total,
@@ -673,18 +880,33 @@ var RulesModule = class extends import_BaseModule.BaseModule {
673
880
  const selectedDiscount2 = selectedDiscountCard || applicableDiscounts[0];
674
881
  usedDiscounts.set(selectedDiscount2.id, true);
675
882
  const productOriginTotal = product.original_price || product.price || 0;
676
- const targetProductTotal = (0, import_utils.getDiscountAmount)(
677
- selectedDiscount2,
678
- productOriginTotal,
679
- productOriginTotal
680
- );
883
+ const isOrderLevel = (0, import_utils.isOrderLevelFixedAmountDiscount)(selectedDiscount2);
884
+ const orderLevelAllocation = isOrderLevel ? orderLevelDiscountAllocations.get(selectedDiscount2.id) : null;
885
+ const productAllocation = orderLevelAllocation == null ? void 0 : orderLevelAllocation.get(flatItem._id);
886
+ let targetProductTotal;
887
+ let fixedAmountPerItem;
888
+ let productDiscountDifference;
889
+ if (isOrderLevel && productAllocation) {
890
+ fixedAmountPerItem = productAllocation.discountAmount;
891
+ productDiscountDifference = productAllocation.difference;
892
+ targetProductTotal = Math.max(new import_decimal.default(productOriginTotal).minus(fixedAmountPerItem).toNumber(), 0);
893
+ } else {
894
+ targetProductTotal = (0, import_utils.getDiscountAmount)(
895
+ selectedDiscount2,
896
+ productOriginTotal,
897
+ productOriginTotal
898
+ );
899
+ fixedAmountPerItem = new import_decimal.default(productOriginTotal).minus(targetProductTotal).toNumber();
900
+ }
901
+ debugger;
681
902
  const uniqueId = flatItem._id;
682
903
  const discountDetail = {
683
- amount: new import_decimal.default(productOriginTotal).minus(targetProductTotal).toNumber() * (product.num || 1),
904
+ amount: fixedAmountPerItem * (product.num || 1),
684
905
  type: selectedDiscount2.tag === "product_discount_card" ? "discount_card" : selectedDiscount2.tag,
685
906
  discount: {
686
- discount_card_type: (_x = selectedDiscount2 == null ? void 0 : selectedDiscount2.metadata) == null ? void 0 : _x.discount_card_type,
687
- fixed_amount: new import_decimal.default(productOriginTotal).minus(targetProductTotal).toNumber(),
907
+ discount_card_type: (_z = selectedDiscount2 == null ? void 0 : selectedDiscount2.metadata) == null ? void 0 : _z.discount_card_type,
908
+ fixed_amount: fixedAmountPerItem,
909
+ discount_calculation_mode: (_A = selectedDiscount2 == null ? void 0 : selectedDiscount2.metadata) == null ? void 0 : _A.discount_calculation_mode,
688
910
  resource_id: selectedDiscount2.id,
689
911
  title: selectedDiscount2.format_title,
690
912
  original_amount: product.original_price,
@@ -694,9 +916,11 @@ var RulesModule = class extends import_BaseModule.BaseModule {
694
916
  metadata: {
695
917
  // 🔥 使用唯一的 _id
696
918
  custom_product_bundle_map_id: uniqueId,
697
- num: product.num || 1
919
+ num: product.num || 1,
920
+ // 🔥 order_level 分摊差值
921
+ ...productDiscountDifference !== void 0 && { product_discount_difference: productDiscountDifference }
698
922
  },
699
- _num: (product.num || 1) * (((_y = flatItem == null ? void 0 : flatItem.parentProduct) == null ? void 0 : _y.num) || 1)
923
+ _num: (product.num || 1) * (((_B = flatItem == null ? void 0 : flatItem.parentProduct) == null ? void 0 : _B.num) || 1)
700
924
  };
701
925
  const appliedProducts = appliedDiscountProducts.get(selectedDiscount2.id) || [];
702
926
  appliedProducts.push(discountDetail);
@@ -704,7 +928,7 @@ var RulesModule = class extends import_BaseModule.BaseModule {
704
928
  processedItems.push({
705
929
  ...flatItem,
706
930
  total: targetProductTotal,
707
- price: new import_decimal.default(productOriginTotal || 0).minus(discountDetail.discount.fixed_amount).toNumber(),
931
+ price: new import_decimal.default(productOriginTotal || 0).minus(fixedAmountPerItem).toNumber(),
708
932
  discount_list: [discountDetail],
709
933
  processed: true
710
934
  });
@@ -794,6 +1018,7 @@ var RulesModule = class extends import_BaseModule.BaseModule {
794
1018
  return {
795
1019
  ...discount,
796
1020
  metadata: {
1021
+ ...discount.metadata,
797
1022
  num: item.num,
798
1023
  custom_product_bundle_map_id: (_a = item.metadata) == null ? void 0 : _a.custom_product_bundle_map_id
799
1024
  }
@@ -817,6 +1042,7 @@ var RulesModule = class extends import_BaseModule.BaseModule {
817
1042
  return {
818
1043
  ...discount,
819
1044
  metadata: {
1045
+ ...discount.metadata,
820
1046
  num: item.num,
821
1047
  custom_product_bundle_map_id: (_a = item.metadata) == null ? void 0 : _a.custom_product_bundle_map_id
822
1048
  }
@@ -847,6 +1073,7 @@ var RulesModule = class extends import_BaseModule.BaseModule {
847
1073
  ...discount,
848
1074
  // num: 1,
849
1075
  metadata: {
1076
+ ...discount.metadata,
850
1077
  custom_product_bundle_map_id: (_b = discount == null ? void 0 : discount.metadata) == null ? void 0 : _b.custom_product_bundle_map_id,
851
1078
  num: 1
852
1079
  }
@@ -905,6 +1132,7 @@ var RulesModule = class extends import_BaseModule.BaseModule {
905
1132
  return {
906
1133
  ...discount,
907
1134
  metadata: {
1135
+ ...discount.metadata,
908
1136
  num: mainProductQuantity - 1,
909
1137
  custom_product_bundle_map_id: (_b = discount == null ? void 0 : discount.metadata) == null ? void 0 : _b.custom_product_bundle_map_id
910
1138
  }
@@ -963,6 +1191,7 @@ var RulesModule = class extends import_BaseModule.BaseModule {
963
1191
  return {
964
1192
  ...discount,
965
1193
  metadata: {
1194
+ ...discount.metadata,
966
1195
  num: item.num,
967
1196
  custom_product_bundle_map_id: (_a2 = discount == null ? void 0 : discount.metadata) == null ? void 0 : _a2.custom_product_bundle_map_id
968
1197
  }
@@ -2,6 +2,11 @@ import Decimal from 'decimal.js';
2
2
  import { CartItem } from '../Cart/types';
3
3
  import { ISummaryState } from './types';
4
4
  export declare const calculatePriceDetails: (shopInfo: any, items: CartItem[]) => ISummaryState['summary'];
5
+ /**
6
+ * 获取子商品折扣信息
7
+ * @param item
8
+ */
9
+ export declare const getBundleDiscountList: (bundle: any[]) => any[];
5
10
  /**
6
11
  * 计算商品小计(不含其他费用)
7
12
  * @param items - 购物车商品数组
@@ -32,7 +32,8 @@ __export(utils_exports, {
32
32
  calculateDeposit: () => calculateDeposit,
33
33
  calculatePriceDetails: () => calculatePriceDetails,
34
34
  calculateSubtotal: () => calculateSubtotal,
35
- calculateTaxFee: () => calculateTaxFee
35
+ calculateTaxFee: () => calculateTaxFee,
36
+ getBundleDiscountList: () => getBundleDiscountList
36
37
  });
37
38
  module.exports = __toCommonJS(utils_exports);
38
39
  var import_decimal = __toESM(require("decimal.js"));
@@ -50,13 +51,35 @@ var calculatePriceDetails = (shopInfo, items) => {
50
51
  deposit
51
52
  };
52
53
  };
54
+ var getBundleDiscountList = (bundle) => {
55
+ if (!bundle) {
56
+ return [];
57
+ }
58
+ let discountList = [];
59
+ bundle.forEach((d) => {
60
+ if (d.discount_list && Array.isArray(d.discount_list)) {
61
+ discountList.push(...d.discount_list.filter((item) => !item.id));
62
+ }
63
+ });
64
+ return discountList;
65
+ };
66
+ var getProductDiscountProductDiscountDifference = (item) => {
67
+ var _a, _b, _c, _d;
68
+ const mainDiscountList = ((_b = (_a = item._origin) == null ? void 0 : _a.product) == null ? void 0 : _b.discount_list) || [];
69
+ const bundleDiscountList = getBundleDiscountList(((_d = (_c = item._origin) == null ? void 0 : _c.product) == null ? void 0 : _d.product_bundle) || []);
70
+ const discountList = [...mainDiscountList, ...bundleDiscountList];
71
+ return discountList.reduce((pre, cur) => {
72
+ return pre + (cur.metadata.product_discount_difference || 0);
73
+ }, 0);
74
+ };
53
75
  var calculateSubtotal = (items) => {
54
76
  if (!(items == null ? void 0 : items.length)) {
55
77
  return "0.00";
56
78
  }
57
79
  const subtotal = items.reduce((sum, item) => {
58
80
  const cartItemTotalPrice = new import_decimal.default(item.summaryTotal || 0);
59
- return sum.plus(cartItemTotalPrice);
81
+ const productDiscountProductDiscountDifference = getProductDiscountProductDiscountDifference(item);
82
+ return sum.plus(cartItemTotalPrice).sub(productDiscountProductDiscountDifference);
60
83
  }, new import_decimal.default(0));
61
84
  return subtotal.toFixed(2);
62
85
  };
@@ -103,5 +126,6 @@ var calculateDeposit = (items) => {
103
126
  calculateDeposit,
104
127
  calculatePriceDetails,
105
128
  calculateSubtotal,
106
- calculateTaxFee
129
+ calculateTaxFee,
130
+ getBundleDiscountList
107
131
  });
@@ -361,7 +361,7 @@ var ShopDiscountImpl = class extends import_BaseModule.BaseModule {
361
361
  if (item.booking_id) {
362
362
  const product = (_a2 = this.hooks) == null ? void 0 : _a2.getProduct(item);
363
363
  (item.discount_list || []).forEach((discount) => {
364
- var _a3, _b, _c;
364
+ var _a3, _b, _c, _d, _e;
365
365
  if (discount.id && ["good_pass", "discount_card"].includes(discount.type)) {
366
366
  const index = editModeDiscountList.findIndex((n) => {
367
367
  var _a4;
@@ -371,7 +371,7 @@ var ShopDiscountImpl = class extends import_BaseModule.BaseModule {
371
371
  editModeDiscountList[index] = {
372
372
  ...editModeDiscountList[index],
373
373
  amount: new import_decimal.default(discount.amount || 0).plus(new import_decimal.default(editModeDiscountList[index].amount || 0)).toNumber(),
374
- savedAmount: new import_decimal.default(discount.amount || 0).times((product == null ? void 0 : product.quantity) || (product == null ? void 0 : product.num) || 1).plus(new import_decimal.default(editModeDiscountList[index].savedAmount || 0)).toNumber()
374
+ savedAmount: new import_decimal.default(discount.amount || 0).times((product == null ? void 0 : product.quantity) || (product == null ? void 0 : product.num) || 1).plus(((_a3 = discount == null ? void 0 : discount.metadata) == null ? void 0 : _a3.product_discount_difference) || 0).plus(new import_decimal.default(editModeDiscountList[index].savedAmount || 0)).toNumber()
375
375
  };
376
376
  } else {
377
377
  if (discount.type && !discount.tag) {
@@ -381,13 +381,13 @@ var ShopDiscountImpl = class extends import_BaseModule.BaseModule {
381
381
  ...discount,
382
382
  isEditMode: true,
383
383
  limited_relation_product_data: {},
384
- savedAmount: discount.amount * ((product == null ? void 0 : product.quantity) || (product == null ? void 0 : product.num) || 1),
384
+ savedAmount: new import_decimal.default(discount.amount || 0).times((product == null ? void 0 : product.quantity) || (product == null ? void 0 : product.num) || 1).plus(((_b = discount == null ? void 0 : discount.metadata) == null ? void 0 : _b.product_discount_difference) || 0).toNumber(),
385
385
  isAvailable: true,
386
- id: ((_a3 = discount.discount) == null ? void 0 : _a3.resource_id) || discount.id,
387
- format_title: ((_b = discount.discount) == null ? void 0 : _b.title) || discount.format_title,
386
+ id: ((_c = discount.discount) == null ? void 0 : _c.resource_id) || discount.id,
387
+ format_title: ((_d = discount.discount) == null ? void 0 : _d.title) || discount.format_title,
388
388
  isDisabled: true,
389
389
  isSelected: true,
390
- product_id: ((_c = discount.discount) == null ? void 0 : _c.product_id) || discount.product_id
390
+ product_id: ((_e = discount.discount) == null ? void 0 : _e.product_id) || discount.product_id
391
391
  });
392
392
  }
393
393
  }
@@ -65,3 +65,27 @@ export declare const getDateIsInSchedule: (dateTime: string, scheduleList: Sched
65
65
  * @returns 过滤后的优惠券列表
66
66
  */
67
67
  export declare const filterDiscountListByBookingTime: (discountList: Discount[], bookingTime: string | null) => Discount[];
68
+ /**
69
+ * 判断是否是订单级别的固定金额折扣卡
70
+ * @param discount 折扣
71
+ * @returns 是否是订单级别的固定金额折扣卡
72
+ */
73
+ export declare const isOrderLevelFixedAmountDiscount: (discount: Discount) => boolean;
74
+ /**
75
+ * 计算订单级别固定金额折扣卡的分摊结果
76
+ * @param discount 折扣卡
77
+ * @param applicableProducts 适用商品列表 { productId, amount, quantity, parentQuantity }
78
+ * @returns 分摊结果 Map<productId, { discountAmount, difference }>
79
+ * 注意:
80
+ * - discountAmount 是单价折扣金额(不是总折扣金额)
81
+ * - difference(总差值)优先直接加到数量为1的单商品折扣上,如果没有则存储在metadata中
82
+ */
83
+ export declare const calculateOrderLevelDiscountAllocation: (discount: Discount, applicableProducts: Array<{
84
+ productId: string | number;
85
+ amount: number;
86
+ quantity: number;
87
+ parentQuantity?: number;
88
+ }>) => Map<string | number, {
89
+ discountAmount: number;
90
+ difference: number;
91
+ }>;