@pisell/pisellos 0.0.567 → 0.0.568

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (228) hide show
  1. package/dist/core/index.js +6 -9
  2. package/dist/model/strategy/adapter/walletPass/evaluator.js +0 -2
  3. package/dist/model/strategy/adapter/walletPass/example.js +3 -26
  4. package/dist/model/strategy/adapter/walletPass/utils.js +0 -3
  5. package/dist/model/strategy/strategy-example.js +5 -19
  6. package/dist/modules/Cart/index.js +0 -3
  7. package/dist/modules/Cart/utils/cartProduct.js +0 -1
  8. package/dist/modules/Date/index.js +1 -5
  9. package/dist/modules/Discount/index.js +1 -4
  10. package/dist/modules/OpenData/index.js +0 -2
  11. package/dist/modules/Order/index.d.ts +1 -1
  12. package/dist/modules/Order/index.js +14 -41
  13. package/dist/modules/Payment/index.js +142 -270
  14. package/dist/modules/Payment/walletpass.js +4 -5
  15. package/dist/modules/Rules/index.js +0 -7
  16. package/dist/modules/Schedule/utils.js +0 -1
  17. package/dist/modules/Step/index.js +0 -1
  18. package/dist/plugins/request.js +1 -4
  19. package/dist/plugins/window.js +2 -6
  20. package/dist/server/index.js +88 -121
  21. package/dist/server/modules/menu/index.js +32 -41
  22. package/dist/server/modules/products/index.js +29 -49
  23. package/dist/server/modules/quotation/index.js +29 -38
  24. package/dist/server/modules/resource/index.js +3 -4
  25. package/dist/server/modules/schedule/index.js +32 -40
  26. package/dist/server/utils/product.js +0 -1
  27. package/dist/solution/BookingByStep/index.d.ts +1 -1
  28. package/dist/solution/BookingByStep/index.js +7 -59
  29. package/dist/solution/BookingByStep/utils/capacity.js +1 -11
  30. package/dist/solution/BookingByStep/utils/resources.js +1 -3
  31. package/dist/solution/BookingByStep/utils/timeslots.js +2 -43
  32. package/dist/solution/BookingTicket/index.d.ts +1 -1
  33. package/dist/solution/BookingTicket/index.js +5 -7
  34. package/dist/solution/BookingTicket/utils/scan/handleScan.js +25 -31
  35. package/dist/solution/BookingTicket/utils/scan/index.js +6 -10
  36. package/dist/solution/BuyTickets/index.js +9 -12
  37. package/dist/solution/Checkout/index.js +177 -246
  38. package/dist/solution/Checkout/utils/index.js +4 -16
  39. package/dist/solution/RegisterAndLogin/index.js +30 -76
  40. package/dist/solution/ScanOrder/index.js +50 -60
  41. package/dist/solution/ShopDiscount/index.js +2 -7
  42. package/dist/solution/VenueBooking/index.d.ts +5 -0
  43. package/dist/solution/VenueBooking/index.js +364 -327
  44. package/dist/utils/task.js +15 -18
  45. package/dist/utils/watch.js +0 -1
  46. package/lib/apis/picoding.js +2 -0
  47. package/lib/core/index.js +130 -131
  48. package/lib/effects/index.js +46 -57
  49. package/lib/index.js +92 -53
  50. package/lib/model/index.js +14 -21
  51. package/lib/model/strategy/adapter/dataVariant/adapter.js +38 -50
  52. package/lib/model/strategy/adapter/dataVariant/evaluator.js +162 -236
  53. package/lib/model/strategy/adapter/dataVariant/examples.js +229 -237
  54. package/lib/model/strategy/adapter/dataVariant/index.js +39 -34
  55. package/lib/model/strategy/adapter/dataVariant/type.js +57 -28
  56. package/lib/model/strategy/adapter/index.js +100 -64
  57. package/lib/model/strategy/adapter/itemRule/adapter.js +156 -144
  58. package/lib/model/strategy/adapter/itemRule/evaluator.js +36 -48
  59. package/lib/model/strategy/adapter/itemRule/examples.js +295 -225
  60. package/lib/model/strategy/adapter/itemRule/index.js +83 -57
  61. package/lib/model/strategy/adapter/itemRule/type.js +97 -36
  62. package/lib/model/strategy/adapter/promotion/adapter.js +84 -63
  63. package/lib/model/strategy/adapter/promotion/evaluator.js +381 -231
  64. package/lib/model/strategy/adapter/promotion/examples.js +139 -159
  65. package/lib/model/strategy/adapter/promotion/index.js +1 -49
  66. package/lib/model/strategy/adapter/promotion/type.js +199 -35
  67. package/lib/model/strategy/adapter/type.js +4 -16
  68. package/lib/model/strategy/adapter/walletPass/evaluator.js +180 -152
  69. package/lib/model/strategy/adapter/walletPass/example.js +217 -190
  70. package/lib/model/strategy/adapter/walletPass/index.js +75 -51
  71. package/lib/model/strategy/adapter/walletPass/locales.js +36 -58
  72. package/lib/model/strategy/adapter/walletPass/type.js +4 -16
  73. package/lib/model/strategy/adapter/walletPass/utils.js +652 -402
  74. package/lib/model/strategy/engine.js +270 -168
  75. package/lib/model/strategy/index.js +49 -34
  76. package/lib/model/strategy/strategy-example.js +243 -239
  77. package/lib/model/strategy/type.js +100 -40
  78. package/lib/modules/Account/index.js +39 -53
  79. package/lib/modules/Account/types.js +20 -33
  80. package/lib/modules/AccountList/index.js +116 -154
  81. package/lib/modules/AccountList/types.js +30 -31
  82. package/lib/modules/AccountList/utils.js +18 -30
  83. package/lib/modules/BaseModule.js +22 -45
  84. package/lib/modules/Cart/index.js +170 -124
  85. package/lib/modules/Cart/types.js +46 -51
  86. package/lib/modules/Cart/utils/cartAccount.js +40 -39
  87. package/lib/modules/Cart/utils/cartDate.js +56 -61
  88. package/lib/modules/Cart/utils/cartDiscount.js +28 -33
  89. package/lib/modules/Cart/utils/cartNote.js +27 -32
  90. package/lib/modules/Cart/utils/cartProduct.js +306 -241
  91. package/lib/modules/Cart/utils/cartRelationForms.js +29 -35
  92. package/lib/modules/Cart/utils/cartResource.js +72 -78
  93. package/lib/modules/Cart/utils/changePrice.js +26 -62
  94. package/lib/modules/Cart/utils/index.js +106 -48
  95. package/lib/modules/Customer/constants.js +13 -34
  96. package/lib/modules/Customer/index.js +216 -153
  97. package/lib/modules/Customer/types.js +38 -35
  98. package/lib/modules/Date/index.js +116 -115
  99. package/lib/modules/Date/types.js +16 -28
  100. package/lib/modules/Date/utils.js +174 -123
  101. package/lib/modules/Discount/index.js +117 -113
  102. package/lib/modules/Discount/types.js +9 -31
  103. package/lib/modules/Guests/index.js +30 -56
  104. package/lib/modules/Guests/types.js +23 -33
  105. package/lib/modules/Holder/index.js +209 -189
  106. package/lib/modules/Holder/types.js +4 -16
  107. package/lib/modules/Holder/utils.js +20 -49
  108. package/lib/modules/OpenData/index.js +63 -74
  109. package/lib/modules/OpenData/types.js +4 -16
  110. package/lib/modules/OpenData/utils.js +44 -78
  111. package/lib/modules/Order/index.d.ts +1 -1
  112. package/lib/modules/Order/index.js +332 -320
  113. package/lib/modules/Order/types.js +24 -33
  114. package/lib/modules/Order/utils.js +350 -284
  115. package/lib/modules/Payment/cash.js +20 -33
  116. package/lib/modules/Payment/cashRecommendationAlgorithm.js +145 -76
  117. package/lib/modules/Payment/eftpos.js +16 -30
  118. package/lib/modules/Payment/index.js +566 -432
  119. package/lib/modules/Payment/mx51.js +1 -0
  120. package/lib/modules/Payment/types.js +216 -107
  121. package/lib/modules/Payment/utils.js +50 -47
  122. package/lib/modules/Payment/walletpass.js +221 -203
  123. package/lib/modules/Product/index.js +51 -46
  124. package/lib/modules/Product/types.js +4 -16
  125. package/lib/modules/Product/utils.js +32 -33
  126. package/lib/modules/ProductList/index.js +111 -110
  127. package/lib/modules/ProductList/types.js +9 -31
  128. package/lib/modules/Quotation/index.js +60 -82
  129. package/lib/modules/Quotation/types.js +4 -16
  130. package/lib/modules/Resource/index.js +27 -59
  131. package/lib/modules/Resource/types.js +22 -32
  132. package/lib/modules/Resource/utils.js +30 -38
  133. package/lib/modules/Rules/index.js +1172 -872
  134. package/lib/modules/Rules/types.js +17 -40
  135. package/lib/modules/SalesSummary/index.js +59 -68
  136. package/lib/modules/SalesSummary/types.js +4 -16
  137. package/lib/modules/SalesSummary/utils.js +176 -193
  138. package/lib/modules/ScanOrderLogger/index.js +59 -79
  139. package/lib/modules/ScanOrderLogger/providers/feishu.js +54 -78
  140. package/lib/modules/ScanOrderLogger/providers/grafana.js +13 -36
  141. package/lib/modules/ScanOrderLogger/types.js +4 -16
  142. package/lib/modules/Schedule/getDateIsInSchedule.js +334 -167
  143. package/lib/modules/Schedule/index.js +97 -107
  144. package/lib/modules/Schedule/type.js +4 -16
  145. package/lib/modules/Schedule/types.js +4 -16
  146. package/lib/modules/Schedule/utils.js +433 -291
  147. package/lib/modules/Step/index.js +40 -52
  148. package/lib/modules/Step/tyeps.js +4 -16
  149. package/lib/modules/Summary/index.js +66 -70
  150. package/lib/modules/Summary/types.js +4 -16
  151. package/lib/modules/Summary/utils.js +782 -429
  152. package/lib/modules/index.js +212 -57
  153. package/lib/plugins/app.js +4 -16
  154. package/lib/plugins/index.js +36 -25
  155. package/lib/plugins/request.js +137 -126
  156. package/lib/plugins/shopStore.js +4 -16
  157. package/lib/plugins/user.js +4 -16
  158. package/lib/plugins/window.js +171 -179
  159. package/lib/server/index.js +882 -623
  160. package/lib/server/modules/index.js +85 -59
  161. package/lib/server/modules/menu/index.js +146 -122
  162. package/lib/server/modules/menu/types.js +18 -31
  163. package/lib/server/modules/order/index.js +195 -231
  164. package/lib/server/modules/order/types.js +107 -32
  165. package/lib/server/modules/order/utils/filterBookings.js +174 -140
  166. package/lib/server/modules/order/utils/filterOrders.js +89 -60
  167. package/lib/server/modules/products/index.js +430 -335
  168. package/lib/server/modules/products/types.js +41 -34
  169. package/lib/server/modules/quotation/index.js +76 -77
  170. package/lib/server/modules/quotation/types.js +21 -31
  171. package/lib/server/modules/resource/index.js +216 -187
  172. package/lib/server/modules/resource/types.js +42 -33
  173. package/lib/server/modules/schedule/index.js +134 -119
  174. package/lib/server/modules/schedule/types.js +14 -21
  175. package/lib/server/modules/schedule/utils.js +334 -163
  176. package/lib/server/types.js +4 -16
  177. package/lib/server/utils/index.js +25 -23
  178. package/lib/server/utils/product.js +196 -139
  179. package/lib/server/utils/schedule.js +34 -41
  180. package/lib/server/utils/time.js +40 -49
  181. package/lib/solution/BookingByStep/index.d.ts +1 -1
  182. package/lib/solution/BookingByStep/index.js +1530 -1343
  183. package/lib/solution/BookingByStep/types.js +40 -99
  184. package/lib/solution/BookingByStep/utils/capacity.js +192 -160
  185. package/lib/solution/BookingByStep/utils/products.js +42 -52
  186. package/lib/solution/BookingByStep/utils/resources.js +527 -330
  187. package/lib/solution/BookingByStep/utils/stock.js +65 -44
  188. package/lib/solution/BookingByStep/utils/timeslots.js +129 -131
  189. package/lib/solution/BookingTicket/index.d.ts +1 -1
  190. package/lib/solution/BookingTicket/index.js +147 -122
  191. package/lib/solution/BookingTicket/types.js +38 -64
  192. package/lib/solution/BookingTicket/utils/scan/cloudSearch.js +64 -80
  193. package/lib/solution/BookingTicket/utils/scan/handleScan.js +82 -80
  194. package/lib/solution/BookingTicket/utils/scan/index.js +90 -94
  195. package/lib/solution/BookingTicket/utils/scan/scanCache.js +90 -43
  196. package/lib/solution/BuyTickets/index.js +67 -70
  197. package/lib/solution/BuyTickets/types.js +22 -38
  198. package/lib/solution/Checkout/index.js +1451 -1158
  199. package/lib/solution/Checkout/types.js +91 -61
  200. package/lib/solution/Checkout/utils/index.js +227 -158
  201. package/lib/solution/PlaceOrder/index.js +55 -0
  202. package/lib/solution/RegisterAndLogin/config.js +493 -460
  203. package/lib/solution/RegisterAndLogin/index.js +633 -630
  204. package/lib/solution/RegisterAndLogin/types.js +189 -76
  205. package/lib/solution/RegisterAndLogin/utils.js +183 -125
  206. package/lib/solution/Sales/index.js +218 -200
  207. package/lib/solution/Sales/types.js +27 -33
  208. package/lib/solution/ScanOrder/index.js +803 -840
  209. package/lib/solution/ScanOrder/types.js +34 -34
  210. package/lib/solution/ScanOrder/utils.js +372 -331
  211. package/lib/solution/ShopDiscount/index.js +230 -236
  212. package/lib/solution/ShopDiscount/types.js +15 -37
  213. package/lib/solution/ShopDiscount/utils.js +309 -172
  214. package/lib/solution/VenueBooking/index.d.ts +5 -0
  215. package/lib/solution/VenueBooking/index.js +886 -929
  216. package/lib/solution/VenueBooking/types.js +20 -40
  217. package/lib/solution/VenueBooking/utils/dateSummary.js +22 -54
  218. package/lib/solution/VenueBooking/utils/resource.js +31 -54
  219. package/lib/solution/VenueBooking/utils/slotMerge.js +88 -106
  220. package/lib/solution/VenueBooking/utils/smartPricing.js +112 -100
  221. package/lib/solution/VenueBooking/utils/timeSlot.js +133 -132
  222. package/lib/solution/VenueBooking/utils.js +130 -67
  223. package/lib/solution/index.js +102 -37
  224. package/lib/store/createStore.js +32 -29
  225. package/lib/types/index.js +4 -16
  226. package/lib/utils/task.js +36 -36
  227. package/lib/utils/watch.js +81 -47
  228. package/package.json +2 -1
@@ -1,58 +1,32 @@
1
- var __create = Object.create;
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __getProtoOf = Object.getPrototypeOf;
6
- var __hasOwnProp = Object.prototype.hasOwnProperty;
7
- var __export = (target, all) => {
8
- for (var name in all)
9
- __defProp(target, name, { get: all[name], enumerable: true });
10
- };
11
- var __copyProps = (to, from, except, desc) => {
12
- if (from && typeof from === "object" || typeof from === "function") {
13
- for (let key of __getOwnPropNames(from))
14
- if (!__hasOwnProp.call(to, key) && key !== except)
15
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
- }
17
- return to;
18
- };
19
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20
- // If the importer is in node compatibility mode or this is not an ESM
21
- // file that has been converted to a CommonJS file using a Babel-
22
- // compatible transform (i.e. "__esModule" has not been set), then set
23
- // "default" to the CommonJS "module.exports" for node compatibility.
24
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
25
- mod
26
- ));
27
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
-
29
- // src/modules/Rules/index.ts
30
- var Rules_exports = {};
31
- __export(Rules_exports, {
32
- RulesModule: () => RulesModule
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
33
5
  });
34
- module.exports = __toCommonJS(Rules_exports);
35
- var import_BaseModule = require("../BaseModule");
36
- var import_types = require("./types");
37
- var import_utils = require("../../solution/ShopDiscount/utils");
38
- var import_utils2 = require("../Cart/utils");
39
- var import_decimal = __toESM(require("decimal.js"));
40
- var import_lodash_es = require("lodash-es");
41
- var import_dayjs = __toESM(require("dayjs"));
42
- var import_utils3 = require("../../solution/ShopDiscount/utils");
43
- var import_utils4 = require("../../solution/ShopDiscount/utils");
44
- var RulesModule = class extends import_BaseModule.BaseModule {
6
+ exports.RulesModule = void 0;
7
+ var _BaseModule = require("../BaseModule");
8
+ var _types = require("./types");
9
+ var _utils = require("../../solution/ShopDiscount/utils");
10
+ var _utils2 = require("../Cart/utils");
11
+ var _decimal = _interopRequireDefault(require("decimal.js"));
12
+ var _lodashEs = require("lodash-es");
13
+ var _dayjs = _interopRequireDefault(require("dayjs"));
14
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
15
+ class RulesModule extends _BaseModule.BaseModule {
16
+ defaultName = 'rules';
17
+ defaultVersion = '1.0.0';
18
+ store;
19
+ window; // WindowPlugin 实例
20
+ hooks;
45
21
  constructor(name, version) {
46
22
  super(name, version);
47
- this.defaultName = "rules";
48
- this.defaultVersion = "1.0.0";
49
23
  this.hooks = {};
50
24
  }
51
25
  async initialize(core, options) {
52
26
  this.core = core;
53
- this.hooks = options == null ? void 0 : options.hooks;
54
- this.store = options == null ? void 0 : options.store;
55
- this.window = core.getPlugin("window");
27
+ this.hooks = options?.hooks;
28
+ this.store = options?.store;
29
+ this.window = core.getPlugin('window');
56
30
  }
57
31
  async setRulesList(rulesList) {
58
32
  this.store.rulesList = rulesList;
@@ -62,22 +36,24 @@ var RulesModule = class extends import_BaseModule.BaseModule {
62
36
  return this.store.rulesList;
63
37
  }
64
38
  getWalletPassEvaluator() {
65
- var _a, _b;
66
- return (_b = (_a = this.window).getWalletPassEvaluator) == null ? void 0 : _b.call(_a);
39
+ return this.window.getWalletPassEvaluator?.();
67
40
  }
41
+
68
42
  // 商品不需要holder,则不需要判断,直接返回true,商品需要holder但是还没填写,那么暂时不使用带有holder的券,直到填写才去匹配
69
43
  checkHolderMatch(discount, product, holders) {
70
- var _a;
71
- if (((_a = discount.holder) == null ? void 0 : _a.holder_type) !== "form")
72
- return true;
73
- const orderHolderId = Array.isArray(holders) && holders.length > 0 ? holders[0].form_record_id : void 0;
44
+ // 非表单类型 holder 视为匹配
45
+ if (discount.holder?.holder_type !== 'form') return true;
46
+ // 主预约holder, 目前(20251124)默认只考虑单个holder的情况
47
+ const orderHolderId = Array.isArray(holders) && holders.length > 0 ? holders[0].form_record_id : undefined;
74
48
  const productHolderId = Array.isArray(product.holder_id) ? product.holder_id[0] : product.holder_id;
75
- if (!product.isNeedHolder)
76
- return true;
77
- if (!orderHolderId && !productHolderId)
78
- return false;
49
+ // 商品不需要选holder,则不对比holder,直接返回true,
50
+ if (!product.isNeedHolder) return true;
51
+ // 商品需要holder,但父预约及商品都无holder,返回false
52
+ if (!orderHolderId && !productHolderId) return false;
53
+ // 最终直接匹配 holder 是否相同
79
54
  return (productHolderId || orderHolderId) === discount.holder.holder_id;
80
55
  }
56
+
81
57
  // 判断discountList 是否可以对当前productList生效
82
58
  isDiscountListAvailable({
83
59
  oldDiscountList,
@@ -87,6 +63,7 @@ var RulesModule = class extends import_BaseModule.BaseModule {
87
63
  holders,
88
64
  isFormSubject
89
65
  }) {
66
+ // 首先检查是否有新的优惠券可应用
90
67
  if (!newDiscountList || newDiscountList.length === 0) {
91
68
  return {
92
69
  isAvailable: false,
@@ -94,25 +71,24 @@ var RulesModule = class extends import_BaseModule.BaseModule {
94
71
  productList
95
72
  };
96
73
  }
97
- if (productList.every((item) => {
98
- var _a;
74
+
75
+ // 编辑商品已有商品券
76
+ if (productList.every(item => {
99
77
  const product = this.hooks.getProduct(item);
100
- return product.booking_id && (((_a = product.discount_list) == null ? void 0 : _a.length) || product.total == 0);
78
+ return product.booking_id && (product.discount_list?.length || product.total == 0);
101
79
  })) {
102
80
  return {
103
81
  isAvailable: false,
104
82
  discountList: oldDiscountList,
105
83
  productList,
106
- unavailableReason: import_types.UnavailableReason.AlreadyUsed
84
+ unavailableReason: _types.UnavailableReason.AlreadyUsed
107
85
  };
108
86
  }
109
- const filteredOldDiscountList = oldDiscountList.filter(
110
- (discount) => !discount.isEditMode && (discount.tag !== "good_pass" || discount.isScan)
111
- );
112
- const mergedDiscountList = (0, import_utils.uniqueById)([
113
- ...filteredOldDiscountList,
114
- ...newDiscountList
115
- ]);
87
+
88
+ // 合并新旧折扣列表,并计算折扣结果,注意,如果旧折扣里有 isEditMode 为 true 的优惠券,则不合并
89
+ // 保留之前扫码得到的商品券(isScan: true)
90
+ const filteredOldDiscountList = oldDiscountList.filter(discount => !discount.isEditMode && (discount.tag !== 'good_pass' || discount.isScan));
91
+ const mergedDiscountList = (0, _utils.uniqueById)([...filteredOldDiscountList, ...newDiscountList]);
116
92
  const result = this.calcDiscount({
117
93
  discountList: mergedDiscountList,
118
94
  productList: [...productList],
@@ -122,26 +98,31 @@ var RulesModule = class extends import_BaseModule.BaseModule {
122
98
  }, {
123
99
  scan: true
124
100
  });
101
+
102
+ // 检查是否有产品使用了新折扣
125
103
  let hasApplicableDiscount = false;
126
- const newDiscountIds = newDiscountList.map((discount) => discount.id);
127
- result.productList.forEach((product) => {
128
- const { discount_list, bundle } = this.hooks.getProduct(product);
129
- const allDiscountList = [...discount_list || []];
130
- (bundle || []).forEach((item) => {
131
- allDiscountList.push(...(item == null ? void 0 : item.discount_list) || []);
104
+ const newDiscountIds = newDiscountList.map(discount => discount.id);
105
+
106
+ // 遍历处理后的产品列表,检查是否有产品使用了新折扣
107
+ result.productList.forEach(product => {
108
+ const {
109
+ discount_list,
110
+ bundle
111
+ } = this.hooks.getProduct(product);
112
+ const allDiscountList = [...(discount_list || [])];
113
+ (bundle || []).forEach(item => {
114
+ allDiscountList.push(...(item?.discount_list || []));
132
115
  });
133
116
  if (allDiscountList && allDiscountList.length > 0) {
134
- const usedNewDiscount = allDiscountList.some(
135
- (discount) => {
136
- var _a;
137
- return newDiscountIds.includes((_a = discount == null ? void 0 : discount.discount) == null ? void 0 : _a.resource_id);
138
- }
139
- );
117
+ // 检查是否有使用新折扣的情况
118
+ const usedNewDiscount = allDiscountList.some(discount => newDiscountIds.includes(discount?.discount?.resource_id));
140
119
  if (usedNewDiscount) {
141
120
  hasApplicableDiscount = true;
142
121
  }
143
122
  }
144
123
  });
124
+
125
+ // 如果券不可用,判断不可用原因
145
126
  let unavailableReason;
146
127
  if (!hasApplicableDiscount) {
147
128
  unavailableReason = this.getUnavailableReason(newDiscountList, productList);
@@ -154,39 +135,42 @@ var RulesModule = class extends import_BaseModule.BaseModule {
154
135
  };
155
136
  }
156
137
  filterDiscountListByType(discountList, type) {
157
- return (discountList || []).filter((item) => item.type === type || item.tag === type);
138
+ return (discountList || []).filter(item => item.type === type || item.tag === type);
158
139
  }
140
+
159
141
  // 获取券不可用的原因
160
142
  getUnavailableReason(discountList, productList) {
161
- var _a;
143
+ // 检查时间限制
162
144
  for (const discount of discountList) {
163
145
  for (const item of productList) {
164
146
  const product = this.hooks.getProduct(item);
165
- const bookingTime = ((product == null ? void 0 : product.startDate) || (0, import_dayjs.default)()).format("YYYY-MM-DD HH:mm:ss");
166
- const filteredList = (0, import_utils.filterDiscountListByBookingTime)([discount], bookingTime);
147
+ const bookingTime = (product?.startDate || (0, _dayjs.default)()).format('YYYY-MM-DD HH:mm:ss');
148
+ const filteredList = (0, _utils.filterDiscountListByBookingTime)([discount], bookingTime);
167
149
  if (filteredList.length === 0) {
168
- return import_types.UnavailableReason.TimeLimit;
150
+ return _types.UnavailableReason.TimeLimit;
169
151
  }
170
152
  }
171
153
  }
154
+
155
+ // 检查商品适用性
172
156
  for (const discount of discountList) {
173
157
  const limitedData = discount.limited_relation_product_data;
174
158
  let hasApplicableProduct = false;
175
159
  for (const item of productList) {
176
160
  const product = this.hooks.getProduct(item);
177
- if (limitedData.type === "product_all") {
161
+ if (limitedData.type === 'product_all') {
178
162
  hasApplicableProduct = true;
179
163
  break;
180
- } else if ((_a = limitedData.product_ids) == null ? void 0 : _a.includes(product.id)) {
164
+ } else if (limitedData.product_ids?.includes(product.id)) {
181
165
  hasApplicableProduct = true;
182
166
  break;
183
167
  }
184
168
  }
185
169
  if (!hasApplicableProduct) {
186
- return import_types.UnavailableReason.ProductNotApplicable;
170
+ return _types.UnavailableReason.ProductNotApplicable;
187
171
  }
188
172
  }
189
- return import_types.UnavailableReason.Unknown;
173
+ return _types.UnavailableReason.Unknown;
190
174
  }
191
175
  calcDiscount({
192
176
  discountList,
@@ -197,36 +181,45 @@ var RulesModule = class extends import_BaseModule.BaseModule {
197
181
  }, options) {
198
182
  const editModeDiscount = [];
199
183
  const addModeDiscount = [];
200
- discountList.forEach((discount) => {
184
+ discountList.forEach(discount => {
201
185
  if (discount.isEditMode) {
202
186
  editModeDiscount.push(discount);
203
187
  } else {
204
188
  addModeDiscount.push(discount);
205
189
  }
206
190
  });
207
- const editModeOrderLevelProductIds = /* @__PURE__ */ new Set();
208
- editModeDiscount.forEach((discount) => {
209
- var _a, _b;
210
- if (((_a = discount.discount) == null ? void 0 : _a.discount_calculation_mode) === "order_level") {
211
- editModeOrderLevelProductIds.add((_b = discount == null ? void 0 : discount.discount) == null ? void 0 : _b.discount_product_id);
191
+
192
+ // 🔥 检查 editModeDiscount 中是否有订单级别的折扣,如果有则禁用 addModeDiscount 中相同 product_id 的折扣卡
193
+ const editModeOrderLevelProductIds = new Set();
194
+ editModeDiscount.forEach(discount => {
195
+ if (discount.discount?.discount_calculation_mode === 'order_level') {
196
+ editModeOrderLevelProductIds.add(discount?.discount?.discount_product_id);
212
197
  }
213
198
  });
199
+
200
+ // 如果存在订单级别的编辑模式折扣,禁用 addModeDiscount 中相同 product_id 的折扣卡
214
201
  if (editModeOrderLevelProductIds.size > 0) {
215
- addModeDiscount.forEach((discount) => {
202
+ addModeDiscount.forEach(discount => {
216
203
  if (editModeOrderLevelProductIds.has(discount.product_id)) {
217
204
  discount.isDisabled = true;
218
205
  }
219
206
  });
220
207
  }
221
- const filteredDiscountList = addModeDiscount.filter((discount) => {
208
+
209
+ // 过滤掉手动取消false的优惠券,但仅用于应用优惠逻辑
210
+ const filteredDiscountList = addModeDiscount.filter(discount => {
222
211
  return !discount.isManualSelect;
223
212
  });
224
- const flattenProductsWithBundle = (productList2) => {
213
+
214
+ // 🔥 扁平化商品列表:将 bundle 子商品展开为虚拟商品
215
+ const flattenProductsWithBundle = productList => {
225
216
  const flattened = [];
226
- productList2.forEach((originProduct) => {
217
+ productList.forEach(originProduct => {
227
218
  const product = this.hooks.getProduct(originProduct);
219
+
220
+ // 1. 添加主商品
228
221
  flattened.push({
229
- type: "main",
222
+ type: 'main',
230
223
  originProduct,
231
224
  product,
232
225
  price: Number(product.price || 0),
@@ -237,10 +230,12 @@ var RulesModule = class extends import_BaseModule.BaseModule {
237
230
  num: product.num,
238
231
  booking_id: product.booking_id
239
232
  });
233
+
234
+ // 2. 展开 bundle 子商品
240
235
  if (product.bundle && Array.isArray(product.bundle) && product.bundle.length > 0) {
241
236
  product.bundle.forEach((bundleItem, bundleIndex) => {
242
237
  flattened.push({
243
- type: "bundle",
238
+ type: 'bundle',
244
239
  originProduct,
245
240
  parentProduct: product,
246
241
  bundleItem,
@@ -253,8 +248,8 @@ var RulesModule = class extends import_BaseModule.BaseModule {
253
248
  parentId: product._id,
254
249
  num: bundleItem.num || 1,
255
250
  quantity: bundleItem.num || 1,
256
- total: new import_decimal.default(bundleItem.price || 0).mul(bundleItem.num || 1).toNumber(),
257
- origin_total: new import_decimal.default(bundleItem.price || 0).mul(bundleItem.num || 1).toNumber(),
251
+ total: new _decimal.default(bundleItem.price || 0).mul(bundleItem.num || 1).toNumber(),
252
+ origin_total: new _decimal.default(bundleItem.price || 0).mul(bundleItem.num || 1).toNumber(),
258
253
  original_price: bundleItem.original_price,
259
254
  // 继承主商品属性
260
255
  booking_id: bundleItem.booking_id,
@@ -265,121 +260,161 @@ var RulesModule = class extends import_BaseModule.BaseModule {
265
260
  });
266
261
  return flattened;
267
262
  };
263
+
264
+ // 优惠力度排序,传进来的数据里可能有商品券,也可能有优惠券
265
+ // 新的优先级排序规则(从高到低):
266
+ // 1. 带有Holder的商品券(metadata?.holder?.type === 'custom' && tag === 'good_pass')
267
+ // 2. 带有Holder的商品级折扣卡(metadata?.holder?.type === 'custom' && tag === 'product_discount_card' && discount_calculation_mode !== 'order_level')
268
+ // 3. 带有Holder的订单级折扣卡(metadata?.holder?.type === 'custom' && tag === 'product_discount_card' && discount_calculation_mode === 'order_level')
269
+ // 4. Customer商品券(metadata?.holder?.type !== 'custom' && tag === 'good_pass')
270
+ // 5. Customer商品级折扣卡(metadata?.holder?.type !== 'custom' && tag === 'product_discount_card' && discount_calculation_mode !== 'order_level')
271
+ // 6. Customer订单级固定金额折扣(metadata?.holder?.type !== 'custom' && tag === 'product_discount_card' && discount_calculation_mode === 'order_level')
272
+ // 同一优先级内部:固定金额优先于百分比,金额/折扣力度越大越优先,最后按过期时间排序
268
273
  const sortedDiscountList = [...filteredDiscountList].sort((a, b) => {
269
- const isHolderDiscount = (discount) => {
270
- var _a, _b;
271
- return ((_b = (_a = discount.metadata) == null ? void 0 : _a.holder) == null ? void 0 : _b.type) === "custom";
272
- };
273
- const isGoodPass = (discount) => discount.tag === "good_pass";
274
- const isOrderLevelDiscount = (discount) => {
275
- var _a;
276
- return discount.tag === "product_discount_card" && ((_a = discount.metadata) == null ? void 0 : _a.discount_calculation_mode) === "order_level";
277
- };
278
- const isItemLevelDiscount = (discount) => {
279
- var _a;
280
- return discount.tag === "product_discount_card" && ((_a = discount.metadata) == null ? void 0 : _a.discount_calculation_mode) !== "order_level";
281
- };
282
- const getPriority = (discount) => {
274
+ // 辅助函数:判断是否是带有Holder的优惠券
275
+ const isHolderDiscount = discount => discount.metadata?.holder?.type === 'custom';
276
+
277
+ // 辅助函数:判断是否是商品券
278
+ const isGoodPass = discount => discount.tag === 'good_pass';
279
+
280
+ // 辅助函数:判断是否是订单级折扣卡
281
+ const isOrderLevelDiscount = discount => discount.tag === 'product_discount_card' && discount.metadata?.discount_calculation_mode === 'order_level';
282
+
283
+ // 辅助函数:判断是否是商品级折扣卡
284
+ const isItemLevelDiscount = discount => discount.tag === 'product_discount_card' && discount.metadata?.discount_calculation_mode !== 'order_level';
285
+
286
+ // 辅助函数:获取优先级(数字越小优先级越高)
287
+ const getPriority = discount => {
283
288
  const isHolder = isHolderDiscount(discount);
284
289
  if (isHolder) {
285
- if (isGoodPass(discount))
286
- return 1;
287
- if (isItemLevelDiscount(discount))
288
- return 2;
289
- if (isOrderLevelDiscount(discount))
290
- return 3;
290
+ if (isGoodPass(discount)) return 1; // 带有Holder的商品券
291
+ if (isItemLevelDiscount(discount)) return 2; // 带有Holder的商品级折扣卡
292
+ if (isOrderLevelDiscount(discount)) return 3; // 带有Holder的订单级折扣卡
291
293
  } else {
292
- if (isGoodPass(discount))
293
- return 4;
294
- if (isItemLevelDiscount(discount))
295
- return 5;
296
- if (isOrderLevelDiscount(discount))
297
- return 6;
294
+ if (isGoodPass(discount)) return 4; // Customer商品券
295
+ if (isItemLevelDiscount(discount)) return 5; // Customer商品级折扣卡
296
+ if (isOrderLevelDiscount(discount)) return 6; // Customer订单级固定金额折扣
298
297
  }
299
- return 7;
298
+ return 7; // 其他情况
300
299
  };
300
+
301
+ // 辅助函数:按过期时间比较
301
302
  function compareByExpireTime(itemA, itemB) {
302
303
  if (itemA.expire_time && itemB.expire_time) {
303
304
  const timeA = new Date(itemA.expire_time).getTime();
304
305
  const timeB = new Date(itemB.expire_time).getTime();
305
306
  return timeA - timeB;
306
307
  }
308
+ // 有过期时间的优先级高于永久的
307
309
  return itemA.expire_time ? -1 : itemB.expire_time ? 1 : 0;
308
310
  }
311
+
312
+ // 辅助函数:同类型折扣卡内部排序(固定金额优先于百分比,金额/折扣力度排序)
309
313
  function compareDiscountCardValue(itemA, itemB) {
310
- var _a, _b;
311
- const typeA = ((_a = itemA.metadata) == null ? void 0 : _a.discount_card_type) || "percent";
312
- const typeB = ((_b = itemB.metadata) == null ? void 0 : _b.discount_card_type) || "percent";
313
- if (typeA === "fixed_amount" && typeB === "percent")
314
- return -1;
315
- if (typeA === "percent" && typeB === "fixed_amount")
316
- return 1;
317
- if (typeA === "fixed_amount" && typeB === "fixed_amount") {
314
+ const typeA = itemA.metadata?.discount_card_type || 'percent';
315
+ const typeB = itemB.metadata?.discount_card_type || 'percent';
316
+
317
+ // 1. 固定金额优先于百分比
318
+ if (typeA === 'fixed_amount' && typeB === 'percent') return -1;
319
+ if (typeA === 'percent' && typeB === 'fixed_amount') return 1;
320
+
321
+ // 2. 都是固定金额时,金额越大越优先
322
+ if (typeA === 'fixed_amount' && typeB === 'fixed_amount') {
318
323
  if (itemA.par_value !== itemB.par_value) {
319
- const valueA = new import_decimal.default(itemA.par_value || 0);
320
- const valueB = new import_decimal.default(itemB.par_value || 0);
321
- return valueB.minus(valueA).toNumber();
324
+ const valueA = new _decimal.default(itemA.par_value || 0);
325
+ const valueB = new _decimal.default(itemB.par_value || 0);
326
+ return valueB.minus(valueA).toNumber(); // 金额大的在前
322
327
  }
323
328
  }
324
- if (typeA === "percent" && typeB === "percent") {
329
+
330
+ // 3. 都是百分比时,折扣越大越优先(par_value越大越优先)
331
+ if (typeA === 'percent' && typeB === 'percent') {
325
332
  if (itemA.par_value !== itemB.par_value) {
326
- const valueA = new import_decimal.default(100).minus(itemA.par_value || 0);
327
- const valueB = new import_decimal.default(100).minus(itemB.par_value || 0);
328
- return valueA.minus(valueB).toNumber();
333
+ const valueA = new _decimal.default(100).minus(itemA.par_value || 0);
334
+ const valueB = new _decimal.default(100).minus(itemB.par_value || 0);
335
+ return valueA.minus(valueB).toNumber(); // 折扣大的在前
329
336
  }
330
337
  }
331
338
  return 0;
332
339
  }
340
+
341
+ // 1. 先按优先级排序
333
342
  const priorityA = getPriority(a);
334
343
  const priorityB = getPriority(b);
335
344
  if (priorityA !== priorityB) {
336
345
  return priorityA - priorityB;
337
346
  }
347
+
348
+ // 2. 同一优先级内部排序
349
+ // 如果是商品券(优先级1或4),按过期时间排序
338
350
  if (isGoodPass(a) && isGoodPass(b)) {
339
351
  return compareByExpireTime(a, b);
340
352
  }
341
- if (a.tag === "product_discount_card" && b.tag === "product_discount_card") {
353
+
354
+ // 如果是折扣卡(优先级2、3、5、6),按折扣力度排序,然后按过期时间
355
+ if (a.tag === 'product_discount_card' && b.tag === 'product_discount_card') {
342
356
  const valueCompare = compareDiscountCardValue(a, b);
343
- if (valueCompare !== 0)
344
- return valueCompare;
357
+ if (valueCompare !== 0) return valueCompare;
345
358
  return compareByExpireTime(a, b);
346
359
  }
360
+
361
+ // 3. 其他情况按照过期时间排序
347
362
  return compareByExpireTime(a, b);
348
363
  });
364
+
365
+ // const newProductList = [];
366
+ //
367
+ // productList.forEach(item => {
368
+ // const product = this.hooks.getProduct(item);
369
+ // if (product.quantity > 1) {
370
+ // for (let i = 1; i < product.quantity; i++) {
371
+ // newProductList.push(product)
372
+ // }
373
+ // } else {
374
+ // newProductList.push(item)
375
+ // }
376
+ // })
377
+
378
+ // 🔥 扁平化商品列表(包含主商品和bundle子商品)
349
379
  const flattenedList = flattenProductsWithBundle(productList);
350
- const isEditModeAddNewProduct = flattenedList.find((n) => n.booking_id) && flattenedList.find((n) => !n.booking_id);
380
+
381
+ // 识别出来是不是在编辑的界面里又新增了商品
382
+ // 这种情况下,如果有可用的优惠券,也会自动勾选上
383
+ const isEditModeAddNewProduct = flattenedList.find(n => n.booking_id) && flattenedList.find(n => !n.booking_id);
384
+
385
+ // 对扁平化后的列表按价格降序排序(用于应用优惠券时优先选择高价商品)
351
386
  const sortedFlattenedList = flattenedList.sort((a, b) => {
352
- var _a, _b;
353
- const priceA = new import_decimal.default(
354
- a.type === "bundle" ? a.original_price ?? a.price ?? "0" : a.price || "0"
355
- );
356
- const priceB = new import_decimal.default(
357
- b.type === "bundle" ? b.original_price ?? b.price ?? "0" : b.price || "0"
358
- );
387
+ // 子商品优先使用 original_price,主商品使用 price
388
+ const priceA = new _decimal.default(a.type === 'bundle' ? a.original_price ?? a.price ?? '0' : a.price || '0');
389
+ const priceB = new _decimal.default(b.type === 'bundle' ? b.original_price ?? b.price ?? '0' : b.price || '0');
359
390
  if (priceA.equals(priceB)) {
391
+ // 价格相同时,主商品优先
360
392
  if (a.type !== b.type) {
361
- return a.type === "main" ? -1 : 1;
393
+ return a.type === 'main' ? -1 : 1;
362
394
  }
363
- if (a.type === "main" && b.type === "main") {
395
+ // 都是主商品时,按原有逻辑排序
396
+ if (a.type === 'main' && b.type === 'main') {
364
397
  if (a.product.quantity === b.product.quantity) {
365
- return (((_a = b.product.discount_list) == null ? void 0 : _a.length) || 0) - (((_b = a.product.discount_list) == null ? void 0 : _b.length) || 0);
398
+ return (b.product.discount_list?.length || 0) - (a.product.discount_list?.length || 0);
366
399
  }
367
400
  return a.product.quantity - b.product.quantity;
368
401
  }
369
- if (a.type === "bundle" && b.type === "bundle") {
402
+ // 都是bundle时,按数量排序
403
+ if (a.type === 'bundle' && b.type === 'bundle') {
370
404
  return (a.num || 1) - (b.num || 1);
371
405
  }
372
406
  }
373
407
  return priceB.minus(priceA).toNumber();
374
408
  });
409
+
410
+ // 🔥 为每个折扣卡/商品券执行策略检查并附加config
375
411
  const evaluator = this.getWalletPassEvaluator();
376
412
  if (evaluator) {
377
- addModeDiscount.forEach((discount) => {
378
- var _a, _b;
413
+ addModeDiscount.forEach(discount => {
379
414
  const discountType = discount.tag || discount.type;
380
- if (["good_pass", "discount_card", "product_discount_card"].includes(
381
- discountType
382
- )) {
415
+ // 只对 good_pass 和 discount_card 类型执行策略检查
416
+ if (['good_pass', 'discount_card', 'product_discount_card'].includes(discountType)) {
417
+ // 转换 discount 为 Voucher 格式
383
418
  const voucher = {
384
419
  id: discount.id,
385
420
  amount: Number(discount.par_value || 0),
@@ -387,24 +422,27 @@ var RulesModule = class extends import_BaseModule.BaseModule {
387
422
  type: discountType,
388
423
  product_id: discount.product_id,
389
424
  unified_available_status: 1,
390
- available_product_type: (_a = discount.limited_relation_product_data) == null ? void 0 : _a.type,
391
- available_product_ids: (_b = discount.limited_relation_product_data) == null ? void 0 : _b.product_ids
425
+ available_product_type: discount.limited_relation_product_data?.type,
426
+ available_product_ids: discount.limited_relation_product_data?.product_ids
392
427
  };
393
- const productsForEvaluate = sortedFlattenedList.map((item) => {
394
- var _a2;
395
- return {
396
- product_id: item.id,
397
- price: item.price || 0,
398
- quantity: item.quantity || item.num || 1,
399
- selling_price: item.price || 0,
400
- product_options: item.type === "main" ? (_a2 = item.product) == null ? void 0 : _a2.options : void 0
401
- };
402
- });
428
+
429
+ // 转换 sortedFlattenedList 为 Product 格式
430
+ const productsForEvaluate = sortedFlattenedList.map(item => ({
431
+ product_id: item.id,
432
+ price: item.price || 0,
433
+ quantity: item.quantity || item.num || 1,
434
+ selling_price: item.price || 0,
435
+ product_options: item.type === 'main' ? item.product?.options : undefined
436
+ }));
437
+
438
+ // 调用策略检查
403
439
  const result = evaluator.checkVoucherAvailability({
404
- orderTotalAmount,
440
+ orderTotalAmount: orderTotalAmount,
405
441
  products: productsForEvaluate,
406
442
  vouchers: [voucher]
407
443
  });
444
+
445
+ // 将 config 附加到 discount 对象
408
446
  if (result.isAvailable) {
409
447
  discount.config = {
410
448
  ...result.config,
@@ -418,35 +456,74 @@ var RulesModule = class extends import_BaseModule.BaseModule {
418
456
  }
419
457
  });
420
458
  }
459
+ // 检查商品是否被排除(全品类)
421
460
  const isProductIncludedByLimitedData = (limitedData, productId) => {
422
- const excludedIds = (limitedData == null ? void 0 : limitedData.exclude_product_ids) || [];
423
- const isExcluded = (limitedData == null ? void 0 : limitedData.filter) === 1 && excludedIds.some((id) => String(id) === String(productId));
461
+ const excludedIds = limitedData?.exclude_product_ids || [];
462
+ const isExcluded = limitedData?.filter === 1 && excludedIds.some(id => String(id) === String(productId));
424
463
  return !isExcluded;
425
464
  };
426
- const usedDiscounts = /* @__PURE__ */ new Map();
427
- const usedProductIdCounts = /* @__PURE__ */ new Map();
428
- const usedDiscountCardLimitCounts = /* @__PURE__ */ new Map();
429
- editModeDiscount.forEach((discount) => {
430
- var _a;
465
+
466
+ /**
467
+ // 对productList按价格降序排序(用于应用优惠券时优先选择高价商品) 价格相同时使用quantity 排序
468
+ const sortedProductList = [...productList].sort((a, b) => {
469
+ const aProduct = this.hooks.getProduct(a);
470
+ const bProduct = this.hooks.getProduct(b);
471
+ const priceA = new Decimal((aProduct.price as string) || '0');
472
+ const priceB = new Decimal((bProduct.price as string) || '0');
473
+ if (priceA.toNumber() === priceB.toNumber()) {
474
+ if (aProduct.quantity === bProduct.quantity) {
475
+ return bProduct.discount_list?.length - aProduct.discount_list?.length;
476
+ }
477
+ return aProduct.quantity - bProduct.quantity;
478
+ }
479
+ return priceB.toNumber() - priceA.toNumber();
480
+ });
481
+ */
482
+
483
+ // 标记已使用的优惠券
484
+ const usedDiscounts = new Map();
485
+
486
+ // 🔥 统计每个 product_id(商品券商品id)已使用的数量,用于 maxUsagePerOrder 限制
487
+ const usedProductIdCounts = new Map();
488
+
489
+ // 🔥 统计每张折扣卡的 applicableProductLimit 已使用次数(跨商品全局计数)
490
+ const usedDiscountCardLimitCounts = new Map();
491
+
492
+ // 🔥 预先将 editModeDiscount 中的折扣卡计入 applicableProductLimit 已使用次数
493
+ editModeDiscount.forEach(discount => {
431
494
  const discountType = discount.tag || discount.type;
432
- if (["discount_card", "product_discount_card"].includes(discountType)) {
495
+ if (['discount_card', 'product_discount_card'].includes(discountType)) {
433
496
  const currentCount = usedDiscountCardLimitCounts.get(discount.id) || 0;
434
- usedDiscountCardLimitCounts.set(discount.id, currentCount + (((_a = discount.metadata) == null ? void 0 : _a.num) || 1));
497
+ usedDiscountCardLimitCounts.set(discount.id, currentCount + (discount.metadata?.num || 1));
435
498
  }
436
499
  });
437
- const discountApplicability = /* @__PURE__ */ new Map();
438
- const discountApplicableProducts = /* @__PURE__ */ new Map();
439
- addModeDiscount.forEach((discount) => {
500
+
501
+ // 记录每个优惠券适用的商品ID
502
+ const discountApplicability = new Map();
503
+
504
+ // 记录每个优惠券适用的商品详细信息
505
+ const discountApplicableProducts = new Map();
506
+
507
+ // 初始化每个优惠券的适用性和可抵扣商品
508
+ addModeDiscount.forEach(discount => {
440
509
  discountApplicability.set(discount.id, []);
441
510
  discountApplicableProducts.set(discount.id, []);
442
511
  });
443
- const orderLevelDiscountAllocations = /* @__PURE__ */ new Map();
444
- const orderLevelDiscountApplicableItems = /* @__PURE__ */ new Map();
445
- const itemApplicableDiscounts = /* @__PURE__ */ new Map();
512
+
513
+ // 预处理 order_level 固定金额折扣卡的分摊信息
514
+ // Map<discountId, Map<productId, { discountAmount, difference }>>
515
+ const orderLevelDiscountAllocations = new Map();
516
+
517
+ // 第一步:收集所有被勾选的 order_level 折扣卡及其适用商品
518
+ // Map<discountId, Set<flatItemId>> - 记录每个折扣卡适用的商品
519
+ const orderLevelDiscountApplicableItems = new Map();
520
+ // Map<flatItemId, Set<discountId>> - 记录每个商品可以被哪些折扣卡使用
521
+ const itemApplicableDiscounts = new Map();
522
+
523
+ // 辅助函数:检查商品是否对某个折扣卡可用
446
524
  const checkItemApplicableForDiscount = (flatItem, discount) => {
447
- var _a, _b, _c, _d, _e, _f, _g, _h;
448
525
  let product;
449
- if (flatItem.type === "main") {
526
+ if (flatItem.type === 'main') {
450
527
  product = flatItem.product;
451
528
  } else {
452
529
  product = {
@@ -457,27 +534,38 @@ var RulesModule = class extends import_BaseModule.BaseModule {
457
534
  num: flatItem.num,
458
535
  booking_id: flatItem.booking_id,
459
536
  discount_list: flatItem.discount_list || [],
460
- startDate: (_a = flatItem.parentProduct) == null ? void 0 : _a.startDate
537
+ startDate: flatItem.parentProduct?.startDate
461
538
  };
462
539
  }
463
- 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)));
540
+
541
+ // 编辑的商品使用了优惠券不可用
542
+ const isAvailableProduct = flatItem.type === 'main' ? !(product?.booking_id && product?.discount_list?.length && product?.discount_list?.every(d => d.id && ['good_pass', 'discount_card', 'product_discount_card'].includes(d.tag || d.type))) : !(flatItem?.booking_id && !!flatItem?.bundleItem?.discount_list?.length && flatItem?.bundleItem?.discount_list?.every(d => d.id));
464
543
  if (!isAvailableProduct) {
465
544
  return false;
466
545
  }
467
- if ((0, import_lodash_es.isBoolean)(product.vouchersApplicable) && !product.vouchersApplicable) {
546
+
547
+ // vouchersApplicable 为 false 的商品不参与订单级别折扣均摊
548
+ if ((0, _lodashEs.isBoolean)(product.vouchersApplicable) && !product.vouchersApplicable) {
468
549
  return false;
469
550
  }
551
+
552
+ // 商品价格为0时不可用
470
553
  if (Number(product.price) <= 0 || !product.price) {
471
554
  return false;
472
555
  }
473
556
  const limitedData = discount.limited_relation_product_data;
474
- 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;
557
+
558
+ // 时间限制检查
559
+ const timeLimit = !!(0, _utils.filterDiscountListByBookingTime)([discount], (product?.startDate || (0, _dayjs.default)()).format('YYYY-MM-DD HH:mm:ss')).length;
475
560
  if (!timeLimit) {
476
561
  return false;
477
562
  }
563
+
564
+ // 判断商品是否适用
478
565
  let isLimitedProduct = false;
479
- if (limitedData.type === "product_all") {
480
- if (limitedData.filter === 1 && ((_h = limitedData.exclude_product_ids) == null ? void 0 : _h.includes(product.id))) {
566
+ if (limitedData.type === 'product_all') {
567
+ // 全品类,但需要检查是否被排除
568
+ if (limitedData.filter === 1 && limitedData.exclude_product_ids?.includes(product.id)) {
481
569
  isLimitedProduct = false;
482
570
  } else {
483
571
  isLimitedProduct = true;
@@ -485,33 +573,51 @@ var RulesModule = class extends import_BaseModule.BaseModule {
485
573
  } else if (limitedData.product_ids && limitedData.product_ids.includes(product.id)) {
486
574
  isLimitedProduct = true;
487
575
  }
576
+
577
+ // 套餐规则检查
488
578
  const isBundleAvailable = this.checkPackageSubItemUsageRules(discount, flatItem);
489
579
  return isLimitedProduct && isBundleAvailable;
490
580
  };
491
- const selectedOrderLevelDiscounts = sortedDiscountList.filter((discount) => {
492
- return (0, import_utils3.isOrderLevelFixedAmountDiscount)(discount) && discount.isSelected !== false;
581
+
582
+ // 收集所有被勾选的 order_level 折扣卡及其适用商品
583
+ const selectedOrderLevelDiscounts = sortedDiscountList.filter(discount => {
584
+ return (0, _utils.isOrderLevelFixedAmountDiscount)(discount) && discount.isSelected !== false;
493
585
  });
494
- selectedOrderLevelDiscounts.forEach((discount) => {
495
- const applicableItemIds = /* @__PURE__ */ new Set();
496
- sortedFlattenedList.forEach((flatItem) => {
586
+
587
+ // 遍历所有被勾选的 order_level 折扣卡,建立双向映射
588
+ selectedOrderLevelDiscounts.forEach(discount => {
589
+ const applicableItemIds = new Set();
590
+ sortedFlattenedList.forEach(flatItem => {
497
591
  if (checkItemApplicableForDiscount(flatItem, discount)) {
498
592
  applicableItemIds.add(flatItem._id);
593
+
594
+ // 记录商品可以被哪些折扣卡使用
499
595
  if (!itemApplicableDiscounts.has(flatItem._id)) {
500
- itemApplicableDiscounts.set(flatItem._id, /* @__PURE__ */ new Set());
596
+ itemApplicableDiscounts.set(flatItem._id, new Set());
501
597
  }
502
598
  itemApplicableDiscounts.get(flatItem._id).add(discount.id);
503
599
  }
504
600
  });
505
601
  orderLevelDiscountApplicableItems.set(discount.id, applicableItemIds);
506
602
  });
507
- const occupiedItems = /* @__PURE__ */ new Map();
508
- selectedOrderLevelDiscounts.forEach((discount) => {
603
+
604
+ // 专属折扣卡定义:限定了特定商品(product_ids),而不是全品类(product_all)的折扣卡
605
+ // 如果专属折扣卡被勾选,它适用的商品会被占用,其他折扣卡均摊时应排除这些商品
606
+ // Map<flatItemId, discountId> - 记录被占用的商品及其对应的折扣卡
607
+ const occupiedItems = new Map();
608
+
609
+ // 按排序顺序遍历折扣卡,判断哪些是专属折扣卡
610
+ selectedOrderLevelDiscounts.forEach(discount => {
509
611
  const limitedData = discount.limited_relation_product_data;
510
- const isExclusiveDiscount = limitedData.type !== "product_all";
612
+
613
+ // 判断是否是专属折扣卡(只能用于特定商品,而不是全品类)
614
+ const isExclusiveDiscount = limitedData.type !== 'product_all';
511
615
  if (isExclusiveDiscount) {
616
+ // 专属折扣卡:它适用的所有商品都会被它占用
512
617
  const applicableItems = orderLevelDiscountApplicableItems.get(discount.id);
513
618
  if (applicableItems) {
514
- applicableItems.forEach((itemId) => {
619
+ applicableItems.forEach(itemId => {
620
+ // 如果商品还没被占用,则标记为被当前折扣卡占用
515
621
  if (!occupiedItems.has(itemId)) {
516
622
  occupiedItems.set(itemId, discount.id);
517
623
  }
@@ -519,23 +625,26 @@ var RulesModule = class extends import_BaseModule.BaseModule {
519
625
  }
520
626
  }
521
627
  });
522
- console.log("occupiedItems", occupiedItems, "orderLevelDiscountApplicableItems", orderLevelDiscountApplicableItems);
523
- sortedDiscountList.forEach((discount) => {
524
- if (!(0, import_utils3.isOrderLevelFixedAmountDiscount)(discount)) {
628
+ // 🔥 第三步:识别 order_level 折扣卡并预计算分摊(排除被其他专属折扣卡占用的商品)
629
+ sortedDiscountList.forEach(discount => {
630
+ if (!(0, _utils.isOrderLevelFixedAmountDiscount)(discount)) {
525
631
  return;
526
632
  }
633
+
634
+ // 收集该折扣卡适用的商品(排除被其他专属折扣卡占用的商品)
527
635
  const applicableProducts = [];
528
- sortedFlattenedList.forEach((flatItem) => {
529
- var _a, _b, _c;
636
+ sortedFlattenedList.forEach(flatItem => {
637
+ // 🔥 检查该商品是否被其他专属折扣卡占用
530
638
  const occupyingDiscountId = occupiedItems.get(flatItem._id);
531
- if (occupyingDiscountId !== void 0 && occupyingDiscountId !== discount.id) {
639
+ if (occupyingDiscountId !== undefined && occupyingDiscountId !== discount.id) {
640
+ // 该商品被其他专属折扣卡占用,跳过
532
641
  return;
533
642
  }
534
643
  if (!checkItemApplicableForDiscount(flatItem, discount)) {
535
644
  return;
536
645
  }
537
646
  let product;
538
- if (flatItem.type === "main") {
647
+ if (flatItem.type === 'main') {
539
648
  product = flatItem.product;
540
649
  } else {
541
650
  product = {
@@ -546,34 +655,53 @@ var RulesModule = class extends import_BaseModule.BaseModule {
546
655
  num: flatItem.num
547
656
  };
548
657
  }
549
- const quantity = flatItem.type === "main" ? product.num || product.quantity || 1 : (product.num || product.quantity || 1) * (((_a = flatItem.parentProduct) == null ? void 0 : _a.num) || ((_b = flatItem.parentProduct) == null ? void 0 : _b.quantity) || 1);
550
- const originalAmount = flatItem.type === "main" ? Number(product.price ?? 0) : Number(flatItem.original_price ?? flatItem.price ?? 0);
658
+
659
+ // 对于 bundle 子商品,quantity 需要乘以主商品的购买数量
660
+ const quantity = flatItem.type === 'main' ? product.num || product.quantity || 1 : (product.num || product.quantity || 1) * (flatItem.parentProduct?.num || flatItem.parentProduct?.quantity || 1);
661
+
662
+ // 对于主商品:使用 price
663
+ // 对于子商品:优先使用 flatItem.original_price,否则使用 flatItem.price
664
+ const originalAmount = flatItem.type === 'main' ? Number(product.price ?? 0) : Number(flatItem.original_price ?? flatItem.price ?? 0);
665
+
666
+ // 传递 parentQuantity 用于差值处理时判断是否是真正的"数量为1"
551
667
  const productData = {
552
668
  productId: flatItem._id,
553
669
  amount: originalAmount,
554
- quantity
670
+ quantity: quantity
555
671
  };
556
- if (flatItem.type === "bundle") {
557
- productData.parentQuantity = ((_c = flatItem.parentProduct) == null ? void 0 : _c.quantity) || 1;
672
+
673
+ // 子商品需要传递主商品数量
674
+ if (flatItem.type === 'bundle') {
675
+ productData.parentQuantity = flatItem.parentProduct?.quantity || 1;
558
676
  }
559
677
  applicableProducts.push(productData);
560
678
  });
679
+
680
+ // 计算分摊
561
681
  if (applicableProducts.length > 0) {
562
- const allocation = (0, import_utils4.calculateOrderLevelDiscountAllocation)(discount, applicableProducts);
682
+ const allocation = (0, _utils.calculateOrderLevelDiscountAllocation)(discount, applicableProducts);
563
683
  orderLevelDiscountAllocations.set(discount.id, allocation);
564
684
  }
565
685
  });
566
- const processedProductsMap = /* @__PURE__ */ new Map();
567
- const appliedDiscountProducts = /* @__PURE__ */ new Map();
568
- sortedFlattenedList.forEach((flatItem) => {
569
- var _a;
686
+
687
+ // 处理后的商品列表(按排序后的顺序处理,记录优惠信息)
688
+ const processedProductsMap = new Map();
689
+
690
+ // 记录已应用优惠券的商品信息
691
+ const appliedDiscountProducts = new Map();
692
+
693
+ // 首先遍历所有商品和所有优惠券,确定每个优惠券的适用范围,包括isSelected为false的优惠券
694
+ // 🔥 使用扁平化后的列表,包含主商品和bundle子商品
695
+ sortedFlattenedList.forEach(flatItem => {
696
+ // 获取商品数据
570
697
  let product, originProduct;
571
- if (flatItem.type === "main") {
698
+ if (flatItem.type === 'main') {
572
699
  product = flatItem.product;
573
700
  originProduct = flatItem.originProduct;
574
701
  } else {
702
+ // bundle子商品:构造虚拟商品对象
575
703
  product = {
576
- startDate: (_a = flatItem == null ? void 0 : flatItem.parentProduct) == null ? void 0 : _a.startDate,
704
+ startDate: flatItem?.parentProduct?.startDate,
577
705
  _id: flatItem._id,
578
706
  id: flatItem.id,
579
707
  price: flatItem.price,
@@ -586,43 +714,45 @@ var RulesModule = class extends import_BaseModule.BaseModule {
586
714
  };
587
715
  originProduct = flatItem.originProduct;
588
716
  }
589
- addModeDiscount.forEach((discount) => {
590
- var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j;
591
- const limitedData = discount == null ? void 0 : discount.limited_relation_product_data;
592
- const _tempVar = (flatItem == null ? void 0 : flatItem.type) === "bundle" ? flatItem == null ? void 0 : flatItem.parentProduct : flatItem == null ? void 0 : flatItem.product;
593
- const isHolderMatch = this.checkHolderMatch(
594
- discount,
595
- {
596
- isNeedHolder: isFormSubject && !(_tempVar == null ? void 0 : _tempVar.isNormalProduct),
597
- holder_id: (_tempVar == null ? void 0 : _tempVar.holder_id) || product.holder_id
598
- },
599
- holders
600
- );
717
+ addModeDiscount.forEach(discount => {
718
+ const limitedData = discount?.limited_relation_product_data;
719
+ // 拿到discount配置的holder信息 product信息 product.holder 加在 isLimitedProduct
720
+ const _tempVar = flatItem?.type === 'bundle' ? flatItem?.parentProduct : flatItem?.product;
721
+ const isHolderMatch = this.checkHolderMatch(discount, {
722
+ isNeedHolder: isFormSubject && !_tempVar?.isNormalProduct,
723
+ holder_id: _tempVar?.holder_id || product.holder_id
724
+ }, holders);
601
725
  let timeLimit = true;
602
- timeLimit = !!(0, import_utils.filterDiscountListByBookingTime)([discount], ((product == null ? void 0 : product.startDate) || (0, import_dayjs.default)()).format("YYYY-MM-DD HH:mm:ss")).length;
603
- const isLimitedProduct = (limitedData.type === "product_all" && limitedData.filter !== 1 || limitedData.type === "product_all" && limitedData.filter === 1 && !limitedData.exclude_product_ids.includes(product.id) || limitedData.product_ids && limitedData.product_ids.includes(product.id)) && isHolderMatch;
604
- 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(
605
- (discount2) => discount2.id && [
606
- "good_pass",
607
- "discount_card",
608
- "product_discount_card"
609
- ].includes(discount2.tag || discount2.type)
610
- ))) : !((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)));
726
+ timeLimit = !!(0, _utils.filterDiscountListByBookingTime)([discount], (product?.startDate || (0, _dayjs.default)()).format('YYYY-MM-DD HH:mm:ss')).length;
727
+ // 是符合折扣的商品
728
+ const isLimitedProduct = (limitedData.type === 'product_all' && limitedData.filter !== 1 || limitedData.type === 'product_all' && limitedData.filter === 1 && !limitedData.exclude_product_ids.includes(product.id) || limitedData.product_ids && limitedData.product_ids.includes(product.id)) && isHolderMatch;
729
+
730
+ // 编辑的商品 使用了优惠券不可用
731
+ const isAvailableProduct = flatItem.type === 'main' ? !(product?.booking_id && product?.discount_list?.length && product?.discount_list?.every(discount => discount.id && ['good_pass', 'discount_card', 'product_discount_card'].includes(discount.tag || discount.type))) : !(flatItem?.booking_id && !!flatItem?.bundleItem?.discount_list?.length && flatItem?.bundleItem?.discount_list?.every(discount => discount.id));
732
+
733
+ // 套餐是否可用判断
611
734
  const isBundleAvailable = this.checkPackageSubItemUsageRules(discount, flatItem);
612
- if (isAvailableProduct && isLimitedProduct && timeLimit && isBundleAvailable && ((_g = discount.config) == null ? void 0 : _g.isAvailable)) {
613
- (_h = discountApplicability.get(discount.id)) == null ? void 0 : _h.push(product.id);
735
+
736
+ // 判断优惠券是否适用于该商品
737
+ if (isAvailableProduct && isLimitedProduct && timeLimit && isBundleAvailable && discount.config?.isAvailable) {
738
+ // 记录此优惠券适用的商品
739
+ discountApplicability.get(discount.id)?.push(product.id);
740
+
741
+ // 记录可抵扣的商品详情
614
742
  const applicableProducts = discountApplicableProducts.get(discount.id) || [];
615
743
  const discountType = discount.tag || discount.type;
616
- const isGoodPass = discountType === "good_pass";
617
- const num = isGoodPass || (flatItem == null ? void 0 : flatItem.type) === "main" ? 1 : product.num;
744
+ const isGoodPass = discountType === 'good_pass';
745
+
746
+ // 使用数量
747
+ const num = isGoodPass || flatItem?.type === 'main' ? 1 : product.num;
618
748
  const productData = {
619
749
  amount: product.price * num,
620
750
  type: discountType,
621
751
  tag: discountType,
622
752
  discount: {
623
- discount_card_type: (_i = discount == null ? void 0 : discount.metadata) == null ? void 0 : _i.discount_card_type,
753
+ discount_card_type: discount?.metadata?.discount_card_type,
624
754
  fixed_amount: product.price,
625
- discount_calculation_mode: (_j = discount == null ? void 0 : discount.metadata) == null ? void 0 : _j.discount_calculation_mode,
755
+ discount_calculation_mode: discount?.metadata?.discount_calculation_mode,
626
756
  resource_id: discount.id,
627
757
  title: discount.format_title,
628
758
  original_amount: product.price || product.origin_total,
@@ -631,8 +761,8 @@ var RulesModule = class extends import_BaseModule.BaseModule {
631
761
  discount_product_id: discount.product_id
632
762
  },
633
763
  metadata: {
634
- num,
635
- discount_rule_uncheck_flag: discount == null ? void 0 : discount.discount_rule_uncheck_flag
764
+ num: num,
765
+ discount_rule_uncheck_flag: discount?.discount_rule_uncheck_flag
636
766
  }
637
767
  };
638
768
  applicableProducts.push(productData);
@@ -640,112 +770,132 @@ var RulesModule = class extends import_BaseModule.BaseModule {
640
770
  }
641
771
  });
642
772
  });
643
- const processedFlatItemsMap = /* @__PURE__ */ new Map();
644
- const applyDiscountToOptions = (options2, discount) => {
645
- if (!(options2 == null ? void 0 : options2.length))
646
- return { discountedOptions: options2, optionDiscountAmount: 0 };
773
+
774
+ // 🔥 用于存储扁平化商品处理结果的Map
775
+ const processedFlatItemsMap = new Map();
776
+
777
+ // 🔥 Option 折扣辅助函数
778
+ const applyDiscountToOptions = (options, discount) => {
779
+ if (!options?.length) return {
780
+ discountedOptions: options,
781
+ optionDiscountAmount: 0
782
+ };
647
783
  let optionDiscountAmount = 0;
648
- const discountedOptions = options2.map((option) => {
784
+ const discountedOptions = options.map(option => {
785
+ // 折前单价:优先 price,缺失再 add_price;已写回折后价时用 _original_price(或旧字段 _original_add_price)
649
786
  const rawUnit = option._original_price ?? option._original_add_price ?? option.price ?? option.add_price;
650
787
  const baseUnitNum = Number(rawUnit ?? 0);
651
- if (baseUnitNum <= 0)
652
- return option;
653
- const discountedPrice = (0, import_utils.getDiscountAmount)(discount, baseUnitNum, baseUnitNum);
788
+ if (baseUnitNum <= 0) return option;
789
+ const discountedPrice = (0, _utils.getDiscountAmount)(discount, baseUnitNum, baseUnitNum);
654
790
  const optQty = Number(option.num ?? option.quantity ?? 1);
655
- optionDiscountAmount = new import_decimal.default(optionDiscountAmount).plus(new import_decimal.default(baseUnitNum).minus(discountedPrice).mul(optQty)).toNumber();
656
- const { _original_add_price, ...rest } = option;
791
+ optionDiscountAmount = new _decimal.default(optionDiscountAmount).plus(new _decimal.default(baseUnitNum).minus(discountedPrice).mul(optQty)).toNumber();
792
+ const {
793
+ _original_add_price,
794
+ ...rest
795
+ } = option;
657
796
  const tmpl = rest.price ?? rest.add_price;
658
- const nextPrice = typeof tmpl === "string" ? String(discountedPrice) : discountedPrice;
797
+ const nextPrice = typeof tmpl === 'string' ? String(discountedPrice) : discountedPrice;
659
798
  return {
660
799
  ...rest,
661
- _original_price: option._original_price ?? (_original_add_price !== void 0 ? _original_add_price : rawUnit),
800
+ _original_price: option._original_price ?? (_original_add_price !== undefined ? _original_add_price : rawUnit),
662
801
  price: nextPrice
663
802
  };
664
803
  });
665
- return { discountedOptions, optionDiscountAmount };
804
+ return {
805
+ discountedOptions,
806
+ optionDiscountAmount
807
+ };
666
808
  };
667
- const restoreOptionPrices = (options2) => {
668
- if (!(options2 == null ? void 0 : options2.length))
669
- return options2;
670
- return options2.map((option) => {
671
- const orig = option._original_price !== void 0 ? option._original_price : option._original_add_price;
672
- if (orig !== void 0) {
673
- const { _original_price, _original_add_price, ...rest } = option;
674
- return { ...rest, price: orig };
809
+ const restoreOptionPrices = options => {
810
+ if (!options?.length) return options;
811
+ return options.map(option => {
812
+ const orig = option._original_price !== undefined ? option._original_price : option._original_add_price;
813
+ if (orig !== undefined) {
814
+ const {
815
+ _original_price,
816
+ _original_add_price,
817
+ ...rest
818
+ } = option;
819
+ return {
820
+ ...rest,
821
+ price: orig
822
+ };
675
823
  }
676
824
  return option;
677
825
  });
678
826
  };
679
- const getOptionTotal = (options2) => {
680
- if (!(options2 == null ? void 0 : options2.length))
681
- return 0;
682
- return options2.reduce((sum, opt) => {
827
+ const getOptionTotal = options => {
828
+ if (!options?.length) return 0;
829
+ return options.reduce((sum, opt) => {
683
830
  const unit = Number(opt.price ?? opt.add_price ?? 0);
684
831
  const n = Number(opt.num ?? opt.quantity ?? 1);
685
- return new import_decimal.default(sum).plus(new import_decimal.default(unit).mul(n)).toNumber();
832
+ return new _decimal.default(sum).plus(new _decimal.default(unit).mul(n)).toNumber();
686
833
  }, 0);
687
834
  };
835
+
836
+ // 然后再处理应用哪些优惠券,此时只考虑filteredDiscountList中的优惠券
837
+ // 🔥 使用扁平化后的列表进行处理
688
838
  sortedFlattenedList.forEach((flatItem, index) => {
689
- 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, _C, _D, _E, _F, _G, _H, _I, _J, _K, _L, _M;
839
+ // 获取商品数据
690
840
  let product, originProduct;
691
- if (flatItem.type === "main") {
841
+ if (flatItem.type === 'main') {
692
842
  product = flatItem.product;
693
843
  originProduct = flatItem.originProduct;
694
844
  } else {
845
+ // bundle子商品
695
846
  product = {
696
- startDate: (_a = flatItem == null ? void 0 : flatItem.parentProduct) == null ? void 0 : _a.startDate,
847
+ startDate: flatItem?.parentProduct?.startDate,
697
848
  _id: flatItem._id,
698
849
  id: flatItem.id,
699
850
  price: flatItem.price,
700
851
  quantity: flatItem.quantity,
701
852
  num: flatItem.num,
702
853
  total: flatItem.total,
703
- original_price: (_b = flatItem == null ? void 0 : flatItem.bundleItem) == null ? void 0 : _b.original_price,
704
- origin_total: (_c = flatItem == null ? void 0 : flatItem.bundleItem) == null ? void 0 : _c.original_price,
854
+ original_price: flatItem?.bundleItem?.original_price,
855
+ origin_total: flatItem?.bundleItem?.original_price,
705
856
  booking_id: flatItem.booking_id,
706
- discount_list: ((_d = flatItem == null ? void 0 : flatItem.bundleItem) == null ? void 0 : _d.discount_list) || []
857
+ discount_list: flatItem?.bundleItem?.discount_list || []
707
858
  };
708
859
  originProduct = flatItem.originProduct;
709
860
  }
710
- if ((product == null ? void 0 : product.booking_id) && ((_e = product.discount_list) == null ? void 0 : _e.length) && ((_f = product == null ? void 0 : product.discount_list) == null ? void 0 : _f.every(
711
- (discount) => discount.id && ["good_pass", "discount_card", "product_discount_card"].includes(
712
- discount.tag || discount.type
713
- )
714
- ))) {
715
- if (flatItem.type === "main") {
861
+
862
+ // 已有优惠的商品跳过
863
+ if (product?.booking_id && product.discount_list?.length && product?.discount_list?.every(discount => discount.id && ['good_pass', 'discount_card', 'product_discount_card'].includes(discount.tag || discount.type))) {
864
+ if (flatItem.type === 'main') {
716
865
  processedProductsMap.set(product._id, [originProduct]);
717
866
  } else {
718
- processedFlatItemsMap.set(flatItem._id, [
719
- {
720
- ...flatItem,
721
- processed: true
722
- }
723
- ]);
867
+ processedFlatItemsMap.set(flatItem._id, [{
868
+ ...flatItem,
869
+ processed: true
870
+ }]);
724
871
  }
725
872
  return;
726
873
  }
727
- const applicableDiscounts = sortedDiscountList.filter((discount) => {
728
- var _a2, _b2, _c2, _d2, _e2;
729
- const discountType2 = discount.tag || discount.type;
730
- if (["good_pass", "discount_card", "product_discount_card"].includes(
731
- discountType2
732
- )) {
733
- if (discount.config === void 0 || !((_a2 = discount == null ? void 0 : discount.config) == null ? void 0 : _a2.isAvailable)) {
874
+
875
+ // 找到适用于此商品的所有优惠券,仅考虑isSelected不为false的优惠券
876
+ const applicableDiscounts = sortedDiscountList.filter(discount => {
877
+ // 🔥 检查策略可用性(针对 good_pass 和折扣卡)
878
+ const discountType = discount.tag || discount.type;
879
+ if (['good_pass', 'discount_card', 'product_discount_card'].includes(discountType)) {
880
+ // 如果策略检查后没有config,说明不可用
881
+ if (discount.config === undefined || !discount?.config?.isAvailable) {
734
882
  return false;
735
883
  }
736
884
  }
737
- if ((Number(product.price) <= 0 || !product.price) && !((_b2 = product.discount_list) == null ? void 0 : _b2.length) && (discount.tag || discount.type) === "good_pass")
738
- return false;
739
- if ((Number(product.price) <= 0 || !product.price) && !((_c2 = product.discount_list) == null ? void 0 : _c2.find((n) => {
740
- var _a3;
741
- return ((_a3 = n.discount) == null ? void 0 : _a3.resource_id) === discount.id;
742
- })) && (discount.tag || discount.type) !== "good_pass")
743
- return false;
885
+
886
+ // 如果商品价格为 0,其实不需要使用任何优惠券,直接 return true
887
+ // 商品券时主商品价格为0不可用
888
+ if ((Number(product.price) <= 0 || !product.price) && !product.discount_list?.length && (discount.tag || discount.type) === 'good_pass') return false;
889
+
890
+ // 折扣卡商品价格为0时不可用
891
+ if ((Number(product.price) <= 0 || !product.price) && !product.discount_list?.find(n => n.discount?.resource_id === discount.id) && (discount.tag || discount.type) !== 'good_pass') return false;
892
+ // 如果优惠券已被使用,则跳过
744
893
  const targetUsedDiscounts = usedDiscounts.get(discount.id);
745
- if (targetUsedDiscounts && (discount.tag || discount.type) === "good_pass")
746
- return false;
747
- if ((discount.tag || discount.type) === "good_pass") {
748
- const maxUsagePerOrder = (_d2 = discount.config) == null ? void 0 : _d2.maxUsagePerOrder;
894
+ if (targetUsedDiscounts && (discount.tag || discount.type) === 'good_pass') return false;
895
+
896
+ // 🔥 检查 maxUsagePerOrder 限制:同一 product_id 的商品券使用数量不能超过配置值
897
+ if ((discount.tag || discount.type) === 'good_pass') {
898
+ const maxUsagePerOrder = discount.config?.maxUsagePerOrder;
749
899
  if (maxUsagePerOrder && maxUsagePerOrder > 0) {
750
900
  const currentUsedCount = usedProductIdCounts.get(discount.product_id) || 0;
751
901
  if (currentUsedCount >= maxUsagePerOrder) {
@@ -753,9 +903,11 @@ var RulesModule = class extends import_BaseModule.BaseModule {
753
903
  }
754
904
  }
755
905
  }
906
+
907
+ // 🔥 检查折扣卡的 applicableProductLimit 限制:折扣卡可使用的最大次数(跨商品全局)
756
908
  const discountTypeForLimit = discount.tag || discount.type;
757
- if (["discount_card", "product_discount_card"].includes(discountTypeForLimit)) {
758
- const applicableProductLimitConfig = ((_e2 = discount.config) == null ? void 0 : _e2.applicableProductLimit) || 0;
909
+ if (['discount_card', 'product_discount_card'].includes(discountTypeForLimit)) {
910
+ const applicableProductLimitConfig = discount.config?.applicableProductLimit || 0;
759
911
  if (applicableProductLimitConfig > 0) {
760
912
  const currentUsedLimitCount = usedDiscountCardLimitCounts.get(discount.id) || 0;
761
913
  if (currentUsedLimitCount >= applicableProductLimitConfig) {
@@ -764,31 +916,34 @@ var RulesModule = class extends import_BaseModule.BaseModule {
764
916
  }
765
917
  }
766
918
  const limitedData = discount.limited_relation_product_data;
767
- const _tempVar = (flatItem == null ? void 0 : flatItem.type) === "bundle" ? flatItem == null ? void 0 : flatItem.parentProduct : flatItem == null ? void 0 : flatItem.product;
768
- const isHolderMatch = this.checkHolderMatch(
769
- discount,
770
- {
771
- isNeedHolder: isFormSubject && !(_tempVar == null ? void 0 : _tempVar.isNormalProduct),
772
- holder_id: (_tempVar == null ? void 0 : _tempVar.holder_id) || product.holder_id
773
- },
774
- holders
775
- );
776
- if (!isHolderMatch)
777
- return false;
919
+
920
+ // 拿到discount配置的holder信息 product信息 product.holder 不可用return false
921
+ const _tempVar = flatItem?.type === 'bundle' ? flatItem?.parentProduct : flatItem?.product;
922
+ const isHolderMatch = this.checkHolderMatch(discount, {
923
+ isNeedHolder: isFormSubject && !_tempVar?.isNormalProduct,
924
+ holder_id: _tempVar?.holder_id || product.holder_id
925
+ }, holders);
926
+ // 如果 holder 不匹配,则不适用
927
+ if (!isHolderMatch) return false;
778
928
  let timeLimit = true;
779
- timeLimit = !!(0, import_utils.filterDiscountListByBookingTime)([discount], (product.startDate || (0, import_dayjs.default)()).format("YYYY-MM-DD HH:mm:ss")).length;
929
+ timeLimit = !!(0, _utils.filterDiscountListByBookingTime)([discount], (product.startDate || (0, _dayjs.default)()).format('YYYY-MM-DD HH:mm:ss')).length;
780
930
  if (!timeLimit) {
781
931
  return false;
782
932
  }
783
- if (limitedData.type === "product_all") {
933
+
934
+ // 判断优惠券是否适用于该商品
935
+ if (limitedData.type === 'product_all') {
784
936
  if (!isProductIncludedByLimitedData(limitedData, product.id)) {
785
937
  return false;
786
938
  }
939
+
940
+ // 检查 package_sub_item_usage_rules
787
941
  if (!this.checkPackageSubItemUsageRules(discount, flatItem)) {
788
942
  return false;
789
943
  }
790
944
  return true;
791
945
  } else if (limitedData.product_ids && limitedData.product_ids.includes(product.id)) {
946
+ // 检查 package_sub_item_usage_rules
792
947
  if (!this.checkPackageSubItemUsageRules(discount, flatItem)) {
793
948
  return false;
794
949
  }
@@ -796,273 +951,247 @@ var RulesModule = class extends import_BaseModule.BaseModule {
796
951
  }
797
952
  return false;
798
953
  });
799
- const selectedDiscountCard = applicableDiscounts.find(
800
- (n) => n.isScan && n.isSelected && (n.tag || n.type) !== "good_pass"
801
- );
954
+
955
+ // 选择第一个适用的优惠券(已经按过期时间排序)
956
+ // 如果有 isScan 且 isSelected 的折扣卡,则优先选中,否则再选择第一个
957
+ // 用于处理:先手动扫描了一张折扣卡,扫描完以后带入客户信息,但是之前那张折扣卡不是最优惠的情况
958
+ const selectedDiscountCard = applicableDiscounts.find(n => n.isScan && n.isSelected && (n.tag || n.type) !== 'good_pass');
802
959
  const selectedDiscount = selectedDiscountCard || applicableDiscounts[0];
960
+
961
+ // 如果是手动折扣,则不适用优惠券
803
962
  let isManualDiscount = false;
804
- if (flatItem.type === "main") {
805
- isManualDiscount = typeof product.isManualDiscount === "boolean" ? product.isManualDiscount : product.total != product.origin_total && (product.bundle || []).every(
806
- (item) => {
807
- var _a2;
808
- return !((_a2 = item.discount_list || []) == null ? void 0 : _a2.length);
809
- }
810
- ) && (!((_g = product.discount_list) == null ? void 0 : _g.length) || ((_i = (_h = product == null ? void 0 : product.discount_list) == null ? void 0 : _h.every) == null ? void 0 : _i.call(
811
- _h,
812
- (item) => item.type === "product"
813
- )));
963
+ if (flatItem.type === 'main') {
964
+ // 主商品:判断自身是否手动折扣
965
+ isManualDiscount = typeof product.isManualDiscount === 'boolean' ? product.isManualDiscount : product.total != product.origin_total && (product.bundle || []).every(item => !(item.discount_list || [])?.length) && (!product.discount_list?.length || product?.discount_list?.every?.(item => item.type === 'product'));
814
966
  if (product.inPromotion) {
815
967
  isManualDiscount = false;
816
968
  }
817
969
  } else {
970
+ // bundle子商品:判断父主商品是否手动折扣
818
971
  const parentProduct = flatItem.parentProduct;
819
972
  if (parentProduct) {
820
- isManualDiscount = typeof parentProduct.isManualDiscount === "boolean" ? parentProduct.isManualDiscount : parentProduct.total != parentProduct.origin_total && (parentProduct.bundle || []).every(
821
- (item) => {
822
- var _a2;
823
- return !((_a2 = item.discount_list || []) == null ? void 0 : _a2.length);
824
- }
825
- ) && (!((_j = parentProduct.discount_list) == null ? void 0 : _j.length) || ((_l = (_k = parentProduct == null ? void 0 : parentProduct.discount_list) == null ? void 0 : _k.every) == null ? void 0 : _l.call(
826
- _k,
827
- (item) => item.type === "product"
828
- )));
973
+ isManualDiscount = typeof parentProduct.isManualDiscount === 'boolean' ? parentProduct.isManualDiscount : parentProduct.total != parentProduct.origin_total && (parentProduct.bundle || []).every(item => !(item.discount_list || [])?.length) && (!parentProduct.discount_list?.length || parentProduct?.discount_list?.every?.(item => item.type === 'product'));
829
974
  }
830
975
  }
831
- if (options == null ? void 0 : options.discountId) {
832
- if (flatItem.type === "main" && ((_m = product.discount_list) == null ? void 0 : _m.some(
833
- (item) => {
834
- var _a2;
835
- return ((_a2 = item.discount) == null ? void 0 : _a2.resource_id) === options.discountId;
836
- }
837
- ))) {
976
+
977
+ // 勾选时覆盖手动折扣
978
+ if (options?.discountId) {
979
+ // 主商品:检查自己的 discount_list
980
+ if (flatItem.type === 'main' && product.discount_list?.some(item => item.discount?.resource_id === options.discountId)) {
838
981
  isManualDiscount = false;
839
982
  }
840
- if (flatItem.type === "bundle") {
841
- if (((_n = product.discount_list) == null ? void 0 : _n.some(
842
- (item) => {
843
- var _a2;
844
- return ((_a2 = item.discount) == null ? void 0 : _a2.resource_id) === options.discountId;
845
- }
846
- )) || ((_p = (_o = flatItem.parentProduct) == null ? void 0 : _o.discount_list) == null ? void 0 : _p.some(
847
- (item) => {
848
- var _a2;
849
- return ((_a2 = item.discount) == null ? void 0 : _a2.resource_id) === options.discountId;
850
- }
851
- ))) {
983
+ // bundle子商品:检查自己的 discount_list 或父主商品的 discount_list
984
+ if (flatItem.type === 'bundle') {
985
+ if (product.discount_list?.some(item => item.discount?.resource_id === options.discountId) || flatItem.parentProduct?.discount_list?.some(item => item.discount?.resource_id === options.discountId)) {
852
986
  isManualDiscount = false;
853
987
  }
854
988
  }
855
989
  }
856
- if (options == null ? void 0 : options.selectedList) {
857
- if (flatItem.type === "main" && ((_q = product.discount_list) == null ? void 0 : _q.some(
858
- (item) => {
859
- var _a2;
860
- return (_a2 = options == null ? void 0 : options.selectedList) == null ? void 0 : _a2.some(
861
- (n) => {
862
- var _a3;
863
- return n.discountId === ((_a3 = item.discount) == null ? void 0 : _a3.resource_id);
864
- }
865
- );
866
- }
867
- ))) {
990
+ if (options?.selectedList) {
991
+ // 主商品:检查自己的 discount_list
992
+ if (flatItem.type === 'main' && product.discount_list?.some(item => options?.selectedList?.some(n => n.discountId === item.discount?.resource_id))) {
868
993
  isManualDiscount = false;
869
994
  }
870
- if (flatItem.type === "bundle") {
871
- if (((_r = product.discount_list) == null ? void 0 : _r.some(
872
- (item) => {
873
- var _a2;
874
- return (_a2 = options == null ? void 0 : options.selectedList) == null ? void 0 : _a2.some(
875
- (n) => {
876
- var _a3;
877
- return n.discountId === ((_a3 = item.discount) == null ? void 0 : _a3.resource_id);
878
- }
879
- );
880
- }
881
- )) || ((_t = (_s = flatItem.parentProduct) == null ? void 0 : _s.discount_list) == null ? void 0 : _t.some(
882
- (item) => {
883
- var _a2;
884
- return (_a2 = options == null ? void 0 : options.selectedList) == null ? void 0 : _a2.some(
885
- (n) => {
886
- var _a3;
887
- return n.discountId === ((_a3 = item.discount) == null ? void 0 : _a3.resource_id);
888
- }
889
- );
890
- }
891
- ))) {
995
+ // bundle子商品:检查自己的 discount_list 或父主商品的 discount_list
996
+ if (flatItem.type === 'bundle') {
997
+ if (product.discount_list?.some(item => options?.selectedList?.some(n => n.discountId === item.discount?.resource_id)) || flatItem.parentProduct?.discount_list?.some(item => options?.selectedList?.some(n => n.discountId === item.discount?.resource_id))) {
892
998
  isManualDiscount = false;
893
999
  }
894
1000
  }
895
1001
  }
896
- if (applicableDiscounts.length === 0 || isManualDiscount || (0, import_lodash_es.isBoolean)(product.vouchersApplicable) && !product.vouchersApplicable) {
897
- if (flatItem.type === "main") {
1002
+
1003
+ // 如果没有适用的优惠券,或者手动折扣,则不适用优惠券
1004
+ if (applicableDiscounts.length === 0 || isManualDiscount || (0, _lodashEs.isBoolean)(product.vouchersApplicable) && !product.vouchersApplicable) {
1005
+ if (flatItem.type === 'main') {
1006
+ // 主商品:保持原有逻辑,还原 option 价格
898
1007
  const restoredOptions = restoreOptionPrices(product.options);
899
1008
  if (product.isClient) {
900
- processedProductsMap.set(product._id, [
901
- this.hooks.setProduct(originProduct, {
902
- ...isManualDiscount ? {} : {
903
- origin_total: (0, import_utils2.getProductOriginTotalPrice)({
904
- product: {
905
- original_price: product.original_price
906
- },
907
- bundle: product.bundle,
908
- options: restoredOptions
909
- }),
910
- variant: originProduct._productInit.variant,
911
- original_price: originProduct._productInit.original_price,
912
- total: (0, import_utils2.getProductTotalPrice)({
913
- product: {
914
- price: product.price
915
- },
916
- bundle: product.bundle,
917
- options: restoredOptions
918
- }),
919
- price: product.price,
1009
+ processedProductsMap.set(product._id, [this.hooks.setProduct(originProduct, {
1010
+ ...(isManualDiscount ? {} : {
1011
+ origin_total: (0, _utils2.getProductOriginTotalPrice)({
1012
+ product: {
1013
+ original_price: product.original_price
1014
+ },
1015
+ bundle: product.bundle,
920
1016
  options: restoredOptions
921
- },
922
- discount_list: this.filterDiscountListByType(product.discount_list, "promotion")
923
- })
924
- ]);
1017
+ }),
1018
+ variant: originProduct._productInit.variant,
1019
+ original_price: originProduct._productInit.original_price,
1020
+ total: (0, _utils2.getProductTotalPrice)({
1021
+ product: {
1022
+ price: product.price
1023
+ },
1024
+ bundle: product.bundle,
1025
+ options: restoredOptions
1026
+ }),
1027
+ price: product.price,
1028
+ options: restoredOptions
1029
+ }),
1030
+ discount_list: this.filterDiscountListByType(product.discount_list, 'promotion')
1031
+ })]);
925
1032
  } else {
926
- let total = product.inPromotion ? ((_u = product == null ? void 0 : product._promotion) == null ? void 0 : _u.finalPrice) ?? product.origin_total ?? product.total : product.origin_total ?? product.total;
1033
+ let total = product.inPromotion ? product?._promotion?.finalPrice ?? product.origin_total ?? product.total : product.origin_total ?? product.total;
927
1034
  let main_product_selling_price = product.price ?? product.main_product_selling_price;
928
- if ((product.discount_list || []).some((item) => item.type === "promotion") || (0, import_lodash_es.isBoolean)(product.vouchersApplicable) && !product.vouchersApplicable) {
1035
+ if ((product.discount_list || []).some(item => item.type === 'promotion') || (0, _lodashEs.isBoolean)(product.vouchersApplicable) && !product.vouchersApplicable) {
929
1036
  total = product.total ?? product.origin_total;
930
1037
  main_product_selling_price = product.main_product_selling_price ?? main_product_selling_price;
931
1038
  }
932
- processedProductsMap.set(product._id, [
933
- this.hooks.setProduct(originProduct, {
934
- ...isManualDiscount ? {
935
- price: product.price
936
- } : {
937
- _id: product._id.split("___")[0] + "___" + index,
938
- total,
939
- original_price: product.original_price,
940
- price: product.price,
941
- main_product_selling_price,
942
- options: restoredOptions
943
- },
944
- discount_list: this.filterDiscountListByType(product.discount_list, "promotion")
945
- })
946
- ]);
1039
+ processedProductsMap.set(product._id, [this.hooks.setProduct(originProduct, {
1040
+ ...(isManualDiscount ? {
1041
+ price: product.price
1042
+ } : {
1043
+ _id: product._id.split('___')[0] + '___' + index,
1044
+ total,
1045
+ original_price: product.original_price,
1046
+ price: product.price,
1047
+ main_product_selling_price,
1048
+ options: restoredOptions
1049
+ }),
1050
+ discount_list: this.filterDiscountListByType(product.discount_list, 'promotion')
1051
+ })]);
947
1052
  }
948
1053
  } else {
1054
+ // bundle子商品:保存到扁平化Map
949
1055
  processedFlatItemsMap.set(flatItem._id, [{
950
1056
  ...flatItem,
951
- discount_list: this.filterDiscountListByType(((_v = flatItem.bundleItem) == null ? void 0 : _v.discount_list) || [], "promotion"),
1057
+ discount_list: this.filterDiscountListByType(flatItem.bundleItem?.discount_list || [], 'promotion'),
952
1058
  price: isManualDiscount ? flatItem.bundleItem.price : flatItem.bundleItem.original_price,
953
1059
  processed: true
954
1060
  }]);
955
1061
  }
956
1062
  return;
957
1063
  }
958
- if (applicableDiscounts.length && product.booking_id && typeof selectedDiscount.isManualSelect === "undefined" && !(options == null ? void 0 : options.scan) && !isEditModeAddNewProduct) {
1064
+ if (applicableDiscounts.length && product.booking_id && typeof selectedDiscount.isManualSelect === 'undefined' && !options?.scan && !isEditModeAddNewProduct) {
959
1065
  return;
960
1066
  }
1067
+
1068
+ // 是否需要拆分(商品券需要拆分,折扣卡在 applicableProductLimit > 0 时也需要拆分)
961
1069
  const discountType = selectedDiscount.tag || selectedDiscount.type;
962
- const isGoodPass = discountType === "good_pass";
963
- const isDiscountCard = ["discount_card", "product_discount_card"].includes(discountType);
964
- const applicableProductLimit = ((_w = selectedDiscount.config) == null ? void 0 : _w.applicableProductLimit) || 0;
1070
+ const isGoodPass = discountType === 'good_pass';
1071
+ const isDiscountCard = ['discount_card', 'product_discount_card'].includes(discountType);
1072
+ const applicableProductLimit = selectedDiscount.config?.applicableProductLimit || 0;
1073
+
1074
+ // 商品券始终需要拆分,折扣卡在 applicableProductLimit > 0 时需要拆分
965
1075
  const isNeedSplit = isGoodPass || isDiscountCard && applicableProductLimit > 0;
1076
+
1077
+ // 需要拆分出来的数量
966
1078
  const totalQuantity = product.quantity || product.num || 1;
967
- const availableGoodPassCount = applicableDiscounts.filter(
968
- (item) => (item.tag || item.type) === "good_pass"
969
- ).length;
1079
+ const availableGoodPassCount = applicableDiscounts.filter(item => (item.tag || item.type) === 'good_pass').length;
1080
+
1081
+ // 🔥 计算最大使用次数限制
970
1082
  let maxUsageLimit;
971
1083
  if (isGoodPass) {
1084
+ // 商品券:使用 maxUsagePerOrder 限制
972
1085
  maxUsageLimit = availableGoodPassCount;
973
1086
  if (selectedDiscount.config && selectedDiscount.config.maxUsagePerOrder) {
974
- maxUsageLimit = Math.min(
975
- availableGoodPassCount,
976
- selectedDiscount.config.maxUsagePerOrder
977
- );
1087
+ maxUsageLimit = Math.min(availableGoodPassCount, selectedDiscount.config.maxUsagePerOrder);
978
1088
  }
979
1089
  } else if (isDiscountCard && applicableProductLimit > 0) {
1090
+ // 折扣卡:使用 applicableProductLimit 限制(一个商品使用一次折扣卡算1次)
1091
+ // 需要减去已使用次数(跨商品全局计数)
980
1092
  const usedLimitCount = usedDiscountCardLimitCounts.get(selectedDiscount.id) || 0;
981
1093
  maxUsageLimit = Math.max(0, applicableProductLimit - usedLimitCount);
982
1094
  } else {
1095
+ // 其他情况不拆分
983
1096
  maxUsageLimit = 1;
984
1097
  }
985
1098
  const splitCount = isNeedSplit ? Math.min(product.quantity || product.num || 1, maxUsageLimit) : 1;
986
1099
  const arr = [];
987
- if (flatItem.type === "main") {
1100
+
1101
+ // 🔥 主商品和bundle子商品分别处理
1102
+ if (flatItem.type === 'main') {
1103
+ // 主商品:保持原有逻辑
988
1104
  if (splitCount < totalQuantity && isNeedSplit) {
989
1105
  let total = product.origin_total ?? product.total;
990
- if ((product.discount_list || []).some((item) => item.type === "promotion")) {
1106
+ if ((product.discount_list || []).some(item => item.type === 'promotion')) {
991
1107
  total = product.total ?? product.origin_total;
992
1108
  }
993
- arr.push(
994
- this.hooks.setProduct(originProduct, {
995
- discount_list: this.filterDiscountListByType(product.discount_list, "promotion"),
996
- quantity: totalQuantity - splitCount,
997
- _id: product._id.split("___")[0],
998
- total
999
- })
1000
- );
1109
+ arr.push(this.hooks.setProduct(originProduct, {
1110
+ discount_list: this.filterDiscountListByType(product.discount_list, 'promotion'),
1111
+ quantity: totalQuantity - splitCount,
1112
+ _id: product._id.split('___')[0],
1113
+ total
1114
+ }));
1001
1115
  }
1002
1116
  for (let i = 0; i < splitCount; i++) {
1117
+ // 商品券:每个拆分商品使用不同的商品券(从 applicableDiscounts 按索引取)
1118
+ // 折扣卡:所有拆分商品使用同一张折扣卡(selectedDiscountCard 或 applicableDiscounts[0])
1003
1119
  const currentSelectedDiscount = isGoodPass ? selectedDiscountCard || applicableDiscounts[i] : selectedDiscountCard || applicableDiscounts[0];
1120
+ // 标记优惠券为已使用
1004
1121
  usedDiscounts.set(currentSelectedDiscount.id, true);
1005
- if ((currentSelectedDiscount.tag || currentSelectedDiscount.type) === "good_pass") {
1122
+ // 🔥 更新 product_id 使用计数
1123
+ if ((currentSelectedDiscount.tag || currentSelectedDiscount.type) === 'good_pass') {
1006
1124
  const currentCount = usedProductIdCounts.get(currentSelectedDiscount.product_id) || 0;
1007
1125
  usedProductIdCounts.set(currentSelectedDiscount.product_id, currentCount + 1);
1008
1126
  }
1127
+ // 🔥 更新折扣卡 applicableProductLimit 使用计数(跨商品全局)
1009
1128
  const currentDiscountTypeForCount = currentSelectedDiscount.tag || currentSelectedDiscount.type;
1010
- if (["discount_card", "product_discount_card"].includes(currentDiscountTypeForCount)) {
1129
+ if (['discount_card', 'product_discount_card'].includes(currentDiscountTypeForCount)) {
1011
1130
  const currentLimitCount = usedDiscountCardLimitCounts.get(currentSelectedDiscount.id) || 0;
1012
1131
  usedDiscountCardLimitCounts.set(currentSelectedDiscount.id, currentLimitCount + 1);
1013
1132
  }
1133
+
1134
+ // 记录实际应用了优惠券的商品信息
1014
1135
  const appliedProducts = appliedDiscountProducts.get(currentSelectedDiscount.id) || [];
1136
+
1137
+ // 优先从 origin_total拿,可能会拿不到(比如用户端预约在没有配置 original_price 的情况下)
1015
1138
  let productOriginTotal = product.origin_total || product.total || 0;
1016
- if (this.filterDiscountListByType(product.discount_list, "promotion").length && product.origin_total) {
1139
+ // 如果当前 product 有 discount_list,则先从 origin_total 拿
1140
+ if (this.filterDiscountListByType(product.discount_list, 'promotion').length && product.origin_total) {
1017
1141
  productOriginTotal = product.origin_total;
1018
1142
  }
1019
- if (Number(((_x = originProduct == null ? void 0 : originProduct._productInit) == null ? void 0 : _x.original_price) || 0) > 0 && product.origin_total && product.total && product.origin_total !== product.total) {
1143
+ // 如果originProduct?._productInit?.original_price为 0,product.origin_total可能为空,此时取 product.total
1144
+ if (Number(originProduct?._productInit?.original_price || 0) > 0 && product.origin_total && product.total && product.origin_total !== product.total) {
1020
1145
  productOriginTotal = product.total;
1021
1146
  }
1022
- const isOrderLevel = (0, import_utils3.isOrderLevelFixedAmountDiscount)(selectedDiscount);
1147
+
1148
+ // 计算使用折扣卡/商品券以后,单个商品的总 total
1149
+ // 🔥 检查是否是 order_level 固定金额折扣卡
1150
+ const isOrderLevel = (0, _utils.isOrderLevelFixedAmountDiscount)(selectedDiscount);
1023
1151
  const orderLevelAllocation = isOrderLevel ? orderLevelDiscountAllocations.get(selectedDiscount.id) : null;
1024
- const productAllocation = orderLevelAllocation == null ? void 0 : orderLevelAllocation.get(flatItem._id);
1025
- const isDeductOptionPrice = !!((_y = selectedDiscount.config) == null ? void 0 : _y.deductOptionPrice);
1152
+ const productAllocation = orderLevelAllocation?.get(flatItem._id);
1153
+ const isDeductOptionPrice = !!selectedDiscount.config?.deductOptionPrice;
1154
+
1155
+ // 主商品折后价(不含 option);勿与 option 合计混在同一变量,否则 main_product_selling_price 错误
1026
1156
  let mainProductSellingPrice;
1027
1157
  let amount;
1028
1158
  let productDiscountDifference;
1029
1159
  let discountedOptions = product.options;
1030
1160
  let optionDiscountAmount = 0;
1031
1161
  if (isOrderLevel && productAllocation) {
1162
+ // order_level:使用预计算的分摊金额
1032
1163
  amount = productAllocation.discountAmount;
1033
1164
  productDiscountDifference = productAllocation.difference;
1034
- mainProductSellingPrice = Math.max(new import_decimal.default(product.price).minus(amount).toNumber(), 0);
1165
+ mainProductSellingPrice = Math.max(new _decimal.default(product.price).minus(amount).toNumber(), 0);
1035
1166
  } else {
1036
- mainProductSellingPrice = (0, import_utils.getDiscountAmount)(
1037
- currentSelectedDiscount,
1038
- product.price,
1039
- product.price
1040
- );
1041
- amount = new import_decimal.default(product.price).minus(new import_decimal.default(mainProductSellingPrice)).toNumber();
1167
+ // item_level 或其他类型:使用原有逻辑
1168
+ mainProductSellingPrice = (0, _utils.getDiscountAmount)(currentSelectedDiscount, product.price, product.price);
1169
+ amount = new _decimal.default(product.price).minus(new _decimal.default(mainProductSellingPrice)).toNumber();
1042
1170
  }
1043
- if (isDeductOptionPrice && ((_z = product.options) == null ? void 0 : _z.length)) {
1044
- const optionResult = applyDiscountToOptions(
1045
- product.options,
1046
- currentSelectedDiscount
1047
- );
1171
+ if (isDeductOptionPrice && product.options?.length) {
1172
+ const optionResult = applyDiscountToOptions(product.options, currentSelectedDiscount);
1048
1173
  discountedOptions = optionResult.discountedOptions;
1049
1174
  optionDiscountAmount = optionResult.optionDiscountAmount;
1050
1175
  }
1051
- const optionsForLineTotal = isDeductOptionPrice && ((_A = product.options) == null ? void 0 : _A.length) ? discountedOptions : product.options;
1176
+
1177
+ // total = 主商品折后 + option 行合计;开启 option 抵扣时必须用 discountedOptions,不能用未折扣的 product.options 再算一遍
1178
+ const optionsForLineTotal = isDeductOptionPrice && product.options?.length ? discountedOptions : product.options;
1052
1179
  const optionsLineTotal = getOptionTotal(optionsForLineTotal || []);
1053
- const total = new import_decimal.default(mainProductSellingPrice).plus(optionsLineTotal).toNumber();
1180
+ const total = new _decimal.default(mainProductSellingPrice).plus(optionsLineTotal).toNumber();
1181
+
1182
+ // discount_list.amount / fixed_amount:行级总优惠 = 主商品优惠 + option 优惠(与 total 变化一致)
1054
1183
  const mainProductDiscountAmount = amount;
1055
- const lineDiscountAmount = new import_decimal.default(mainProductDiscountAmount).plus(optionDiscountAmount).toNumber();
1056
- const currentDiscountType = currentSelectedDiscount.tag === "product_discount_card" ? "discount_card" : currentSelectedDiscount.tag;
1057
- const discountType2 = selectedDiscount.tag || selectedDiscount.type;
1058
- const currentIsGoodPass = currentDiscountType === "good_pass";
1184
+ const lineDiscountAmount = new _decimal.default(mainProductDiscountAmount).plus(optionDiscountAmount).toNumber();
1185
+ const currentDiscountType = currentSelectedDiscount.tag === 'product_discount_card' ? 'discount_card' : currentSelectedDiscount.tag;
1186
+ const discountType = selectedDiscount.tag || selectedDiscount.type;
1187
+ const currentIsGoodPass = currentDiscountType === 'good_pass';
1059
1188
  const discountDetail = {
1060
1189
  amount: lineDiscountAmount,
1061
- type: selectedDiscount.tag === "product_discount_card" ? "discount_card" : discountType2,
1190
+ type: selectedDiscount.tag === 'product_discount_card' ? 'discount_card' : discountType,
1062
1191
  discount: {
1063
- discount_card_type: (_B = selectedDiscount == null ? void 0 : selectedDiscount.metadata) == null ? void 0 : _B.discount_card_type,
1192
+ discount_card_type: selectedDiscount?.metadata?.discount_card_type,
1064
1193
  fixed_amount: lineDiscountAmount,
1065
- discount_calculation_mode: (_C = selectedDiscount == null ? void 0 : selectedDiscount.metadata) == null ? void 0 : _C.discount_calculation_mode,
1194
+ discount_calculation_mode: selectedDiscount?.metadata?.discount_calculation_mode,
1066
1195
  resource_id: selectedDiscount.id,
1067
1196
  title: selectedDiscount.format_title,
1068
1197
  original_amount: product.price,
@@ -1072,12 +1201,14 @@ var RulesModule = class extends import_BaseModule.BaseModule {
1072
1201
  },
1073
1202
  // 前端使用的num数量,为了计算优惠金额(拆分时为1)
1074
1203
  _num: isNeedSplit ? 1 : product.num,
1075
- config: currentSelectedDiscount == null ? void 0 : currentSelectedDiscount.config,
1204
+ config: currentSelectedDiscount?.config,
1076
1205
  metadata: {
1077
1206
  num: 1,
1078
- discount_rule_uncheck_flag: selectedDiscount == null ? void 0 : selectedDiscount.discount_rule_uncheck_flag,
1207
+ discount_rule_uncheck_flag: selectedDiscount?.discount_rule_uncheck_flag,
1079
1208
  // 🔥 order_level 分摊差值
1080
- ...productDiscountDifference !== void 0 && { product_discount_difference: productDiscountDifference },
1209
+ ...(productDiscountDifference !== undefined && {
1210
+ product_discount_difference: productDiscountDifference
1211
+ }),
1081
1212
  /** 仅主商品上的优惠金额(不含 option) */
1082
1213
  mainProductDiscountAmount,
1083
1214
  optionDiscountAmount
@@ -1085,73 +1216,82 @@ var RulesModule = class extends import_BaseModule.BaseModule {
1085
1216
  };
1086
1217
  appliedProducts.push(discountDetail);
1087
1218
  appliedDiscountProducts.set(currentSelectedDiscount.id, appliedProducts);
1219
+
1220
+ // 记录应用了优惠券的商品
1221
+ // 后续更新价格改为 getProductTotalPrice getProductOriginTotalPrice逻辑
1088
1222
  if (product.isClient) {
1089
- arr.push(
1090
- this.hooks.setProduct(originProduct, {
1091
- discount_list: [discountDetail],
1092
- // good_pass:主商品价以折后价为准;勿用 product.price - amount(amount 含 option 优惠时会算错)
1093
- price: currentIsGoodPass ? mainProductSellingPrice : product.price,
1094
- quantity: isNeedSplit ? 1 : product.quantity,
1095
- origin_total: (0, import_utils2.getProductOriginTotalPrice)({
1096
- product: {
1097
- original_price: product.original_price
1098
- },
1099
- bundle: product.bundle,
1100
- options: restoreOptionPrices(product.options)
1101
- }),
1102
- variant: originProduct._productInit.variant,
1103
- original_price: new import_decimal.default(product.price || 0).toNumber(),
1104
- total,
1105
- options: discountedOptions
1106
- })
1107
- );
1223
+ arr.push(this.hooks.setProduct(originProduct, {
1224
+ discount_list: [discountDetail],
1225
+ // good_pass:主商品价以折后价为准;勿用 product.price - amount(amount 含 option 优惠时会算错)
1226
+ price: currentIsGoodPass ? mainProductSellingPrice : product.price,
1227
+ quantity: isNeedSplit ? 1 : product.quantity,
1228
+ origin_total: (0, _utils2.getProductOriginTotalPrice)({
1229
+ product: {
1230
+ original_price: product.original_price
1231
+ },
1232
+ bundle: product.bundle,
1233
+ options: restoreOptionPrices(product.options)
1234
+ }),
1235
+ variant: originProduct._productInit.variant,
1236
+ original_price: new _decimal.default(product.price || 0).toNumber(),
1237
+ total: total,
1238
+ options: discountedOptions
1239
+ }));
1108
1240
  } else {
1109
- arr.push(
1110
- this.hooks.setProduct(originProduct, {
1111
- discount_list: this.filterDiscountListByType(product.discount_list, "promotion").concat([discountDetail]),
1112
- _id: product._id.split("___")[0] + "___" + currentSelectedDiscount.id + "_" + i + "_" + index,
1113
- price: currentIsGoodPass ? 0 : product.price,
1114
- quantity: isNeedSplit ? 1 : product.quantity,
1115
- total,
1116
- origin_total: productOriginTotal,
1117
- main_product_selling_price: mainProductSellingPrice,
1118
- options: discountedOptions
1119
- })
1120
- );
1241
+ arr.push(this.hooks.setProduct(originProduct, {
1242
+ discount_list: this.filterDiscountListByType(product.discount_list, 'promotion').concat([discountDetail]),
1243
+ _id: product._id.split('___')[0] + '___' + currentSelectedDiscount.id + '_' + i + '_' + index,
1244
+ price: currentIsGoodPass ? 0 : product.price,
1245
+ quantity: isNeedSplit ? 1 : product.quantity,
1246
+ total: total,
1247
+ origin_total: productOriginTotal,
1248
+ main_product_selling_price: mainProductSellingPrice,
1249
+ options: discountedOptions
1250
+ }));
1121
1251
  }
1122
1252
  }
1123
1253
  processedProductsMap.set(product._id, arr);
1124
1254
  } else {
1255
+ // 🔥 bundle子商品:支持拆分(商品券和折扣卡在 applicableProductLimit > 0 时都需要拆分)
1125
1256
  const processedItems = [];
1126
1257
  if (isNeedSplit) {
1258
+ // 需要拆分数量(商品券或折扣卡有 applicableProductLimit 限制)
1127
1259
  const discountNum = splitCount;
1128
1260
  const normalNum = totalQuantity - discountNum;
1261
+
1262
+ // 生成有折扣的商品(每个拆分商品 num: 1)
1129
1263
  for (let i = 0; i < discountNum; i++) {
1264
+ // 商品券:每个拆分商品使用不同的商品券
1265
+ // 折扣卡:所有拆分商品使用同一张折扣卡
1130
1266
  const currentBundleDiscount = isGoodPass ? selectedDiscountCard || applicableDiscounts[i] : selectedDiscountCard || applicableDiscounts[0];
1131
1267
  usedDiscounts.set(currentBundleDiscount.id, true);
1132
- if ((currentBundleDiscount.tag || currentBundleDiscount.type) === "good_pass") {
1268
+ // 🔥 更新 product_id 使用计数
1269
+ if ((currentBundleDiscount.tag || currentBundleDiscount.type) === 'good_pass') {
1133
1270
  const currentCount = usedProductIdCounts.get(currentBundleDiscount.product_id) || 0;
1134
1271
  usedProductIdCounts.set(currentBundleDiscount.product_id, currentCount + 1);
1135
1272
  }
1273
+ // 🔥 更新折扣卡 applicableProductLimit 使用计数(跨商品全局)
1136
1274
  const bundleDiscountTypeForCount = currentBundleDiscount.tag || currentBundleDiscount.type;
1137
- if (["discount_card", "product_discount_card"].includes(bundleDiscountTypeForCount)) {
1275
+ if (['discount_card', 'product_discount_card'].includes(bundleDiscountTypeForCount)) {
1138
1276
  const currentBundleLimitCount = usedDiscountCardLimitCounts.get(currentBundleDiscount.id) || 0;
1139
1277
  usedDiscountCardLimitCounts.set(currentBundleDiscount.id, currentBundleLimitCount + 1);
1140
1278
  }
1279
+
1280
+ // 🔥 生成唯一的 _id
1141
1281
  const uniqueId = `${flatItem._id}_split_${i}`;
1142
- const discountedPrice = (0, import_utils.getDiscountAmount)(
1143
- currentBundleDiscount,
1144
- product.origin_total,
1145
- product.origin_total
1146
- );
1147
- const bundleDiscountAmount = new import_decimal.default(product.origin_total || 0).minus(discountedPrice).toNumber();
1148
- const bundleDiscountType = currentBundleDiscount.tag === "product_discount_card" ? "discount_card" : currentBundleDiscount.tag;
1282
+
1283
+ // 计算折扣后的价格(使用 getDiscountAmount,内部已处理 maxDeductionAmount)
1284
+ const discountedPrice = (0, _utils.getDiscountAmount)(currentBundleDiscount, product.origin_total, product.origin_total);
1285
+ const bundleDiscountAmount = new _decimal.default(product.origin_total || 0).minus(discountedPrice).toNumber();
1286
+
1287
+ // 确定折扣类型
1288
+ const bundleDiscountType = currentBundleDiscount.tag === 'product_discount_card' ? 'discount_card' : currentBundleDiscount.tag;
1149
1289
  const discountDetail = {
1150
1290
  amount: bundleDiscountAmount,
1151
1291
  type: bundleDiscountType,
1152
1292
  discount: {
1153
- discount_card_type: (_D = currentBundleDiscount == null ? void 0 : currentBundleDiscount.metadata) == null ? void 0 : _D.discount_card_type,
1154
- discount_calculation_mode: (_E = currentBundleDiscount == null ? void 0 : currentBundleDiscount.metadata) == null ? void 0 : _E.discount_calculation_mode,
1293
+ discount_card_type: currentBundleDiscount?.metadata?.discount_card_type,
1294
+ discount_calculation_mode: currentBundleDiscount?.metadata?.discount_calculation_mode,
1155
1295
  fixed_amount: bundleDiscountAmount,
1156
1296
  resource_id: currentBundleDiscount.id,
1157
1297
  title: currentBundleDiscount.format_title,
@@ -1163,12 +1303,14 @@ var RulesModule = class extends import_BaseModule.BaseModule {
1163
1303
  metadata: {
1164
1304
  // 🔥 使用拆分后的唯一 _id
1165
1305
  custom_product_bundle_map_id: uniqueId,
1166
- discount_rule_uncheck_flag: selectedDiscount == null ? void 0 : selectedDiscount.discount_rule_uncheck_flag,
1306
+ discount_rule_uncheck_flag: selectedDiscount?.discount_rule_uncheck_flag,
1167
1307
  num: 1
1168
1308
  },
1169
1309
  _num: 1,
1170
- config: currentBundleDiscount == null ? void 0 : currentBundleDiscount.config
1310
+ config: currentBundleDiscount?.config
1171
1311
  };
1312
+
1313
+ // 记录实际应用的折扣
1172
1314
  const appliedProducts = appliedDiscountProducts.get(currentBundleDiscount.id) || [];
1173
1315
  appliedProducts.push(discountDetail);
1174
1316
  appliedDiscountProducts.set(currentBundleDiscount.id, appliedProducts);
@@ -1186,28 +1328,29 @@ var RulesModule = class extends import_BaseModule.BaseModule {
1186
1328
  _discountId: currentBundleDiscount.id
1187
1329
  });
1188
1330
  }
1331
+
1332
+ // 🔥 对拆分后的剩余商品继续匹配其他可用折扣(而不是等到下次执行才应用)
1189
1333
  let remainingNum = normalNum;
1190
1334
  let continueSplitIndex = discountNum;
1191
1335
  while (remainingNum > 0) {
1192
- const nextApplicableDiscounts = sortedDiscountList.filter((nextDiscount) => {
1193
- var _a2, _b2, _c2, _d2;
1336
+ // 重新查找适用于剩余商品的可用折扣(已使用的会被自动过滤掉)
1337
+ const nextApplicableDiscounts = sortedDiscountList.filter(nextDiscount => {
1194
1338
  const nextDiscountType = nextDiscount.tag || nextDiscount.type;
1195
- if (["good_pass", "discount_card", "product_discount_card"].includes(nextDiscountType)) {
1196
- if (nextDiscount.config === void 0 || !((_a2 = nextDiscount == null ? void 0 : nextDiscount.config) == null ? void 0 : _a2.isAvailable)) {
1339
+ // 检查策略可用性
1340
+ if (['good_pass', 'discount_card', 'product_discount_card'].includes(nextDiscountType)) {
1341
+ if (nextDiscount.config === undefined || !nextDiscount?.config?.isAvailable) {
1197
1342
  return false;
1198
1343
  }
1199
1344
  }
1200
- if ((Number(product.total) <= 0 || !product.total) && !((_b2 = product.discount_list) == null ? void 0 : _b2.find(
1201
- (n) => {
1202
- var _a3;
1203
- return ((_a3 = n.discount) == null ? void 0 : _a3.resource_id) === nextDiscount.id;
1204
- }
1205
- )) && nextDiscountType !== "good_pass")
1206
- return false;
1207
- if (usedDiscounts.get(nextDiscount.id) && nextDiscountType === "good_pass")
1208
- return false;
1209
- if (nextDiscountType === "good_pass") {
1210
- const maxUsagePerOrder = (_c2 = nextDiscount.config) == null ? void 0 : _c2.maxUsagePerOrder;
1345
+ // 折扣卡时总价为0时不可用
1346
+ if ((Number(product.total) <= 0 || !product.total) && !product.discount_list?.find(n => n.discount?.resource_id === nextDiscount.id) && nextDiscountType !== 'good_pass') return false;
1347
+
1348
+ // 商品券已被使用则跳过
1349
+ if (usedDiscounts.get(nextDiscount.id) && nextDiscountType === 'good_pass') return false;
1350
+
1351
+ // 检查 maxUsagePerOrder 限制
1352
+ if (nextDiscountType === 'good_pass') {
1353
+ const maxUsagePerOrder = nextDiscount.config?.maxUsagePerOrder;
1211
1354
  if (maxUsagePerOrder && maxUsagePerOrder > 0) {
1212
1355
  const currentUsedCount = usedProductIdCounts.get(nextDiscount.product_id) || 0;
1213
1356
  if (currentUsedCount >= maxUsagePerOrder) {
@@ -1215,8 +1358,10 @@ var RulesModule = class extends import_BaseModule.BaseModule {
1215
1358
  }
1216
1359
  }
1217
1360
  }
1218
- if (["discount_card", "product_discount_card"].includes(nextDiscountType)) {
1219
- const limitConfig = ((_d2 = nextDiscount.config) == null ? void 0 : _d2.applicableProductLimit) || 0;
1361
+
1362
+ // 检查折扣卡的 applicableProductLimit 限制
1363
+ if (['discount_card', 'product_discount_card'].includes(nextDiscountType)) {
1364
+ const limitConfig = nextDiscount.config?.applicableProductLimit || 0;
1220
1365
  if (limitConfig > 0) {
1221
1366
  const currentUsedLimitCount = usedDiscountCardLimitCounts.get(nextDiscount.id) || 0;
1222
1367
  if (currentUsedLimitCount >= limitConfig) {
@@ -1225,7 +1370,7 @@ var RulesModule = class extends import_BaseModule.BaseModule {
1225
1370
  }
1226
1371
  }
1227
1372
  const limitedData = nextDiscount.limited_relation_product_data;
1228
- if (limitedData.type === "product_all") {
1373
+ if (limitedData.type === 'product_all') {
1229
1374
  if (limitedData.filter === 1 && limitedData.exclude_product_ids.includes(product.id)) {
1230
1375
  return false;
1231
1376
  }
@@ -1241,6 +1386,8 @@ var RulesModule = class extends import_BaseModule.BaseModule {
1241
1386
  }
1242
1387
  return false;
1243
1388
  });
1389
+
1390
+ // 没有更多可用折扣,剩余商品无折扣存储
1244
1391
  if (nextApplicableDiscounts.length === 0) {
1245
1392
  processedItems.push({
1246
1393
  ...flatItem,
@@ -1252,37 +1399,40 @@ var RulesModule = class extends import_BaseModule.BaseModule {
1252
1399
  });
1253
1400
  break;
1254
1401
  }
1255
- const nextSelectedDiscountCard = nextApplicableDiscounts.find(
1256
- (n) => n.isScan && n.isSelected && (n.tag || n.type) !== "good_pass"
1257
- );
1402
+
1403
+ // 选择最优折扣
1404
+ const nextSelectedDiscountCard = nextApplicableDiscounts.find(n => n.isScan && n.isSelected && (n.tag || n.type) !== 'good_pass');
1258
1405
  const nextSelectedDiscount = nextSelectedDiscountCard || nextApplicableDiscounts[0];
1406
+
1407
+ // 判断新折扣是否需要拆分
1259
1408
  const nextDiscType = nextSelectedDiscount.tag || nextSelectedDiscount.type;
1260
- const nextIsGoodPass = nextDiscType === "good_pass";
1261
- const nextIsDiscountCard = ["discount_card", "product_discount_card"].includes(nextDiscType);
1262
- const nextApplicableProductLimit = ((_F = nextSelectedDiscount.config) == null ? void 0 : _F.applicableProductLimit) || 0;
1409
+ const nextIsGoodPass = nextDiscType === 'good_pass';
1410
+ const nextIsDiscountCard = ['discount_card', 'product_discount_card'].includes(nextDiscType);
1411
+ const nextApplicableProductLimit = nextSelectedDiscount.config?.applicableProductLimit || 0;
1263
1412
  const nextIsNeedSplit = nextIsGoodPass || nextIsDiscountCard && nextApplicableProductLimit > 0;
1264
1413
  if (!nextIsNeedSplit) {
1414
+ // 🔥 不需要拆分:直接应用折扣到所有剩余商品
1265
1415
  const nextBundleDiscount = nextSelectedDiscountCard || nextApplicableDiscounts[0];
1266
1416
  usedDiscounts.set(nextBundleDiscount.id, true);
1267
- if ((nextBundleDiscount.tag || nextBundleDiscount.type) === "good_pass") {
1417
+ if ((nextBundleDiscount.tag || nextBundleDiscount.type) === 'good_pass') {
1268
1418
  const curCount = usedProductIdCounts.get(nextBundleDiscount.product_id) || 0;
1269
1419
  usedProductIdCounts.set(nextBundleDiscount.product_id, curCount + 1);
1270
1420
  }
1271
1421
  const nextNoSplitType = nextBundleDiscount.tag || nextBundleDiscount.type;
1272
- if (["discount_card", "product_discount_card"].includes(nextNoSplitType)) {
1422
+ if (['discount_card', 'product_discount_card'].includes(nextNoSplitType)) {
1273
1423
  const curCount = usedDiscountCardLimitCounts.get(nextBundleDiscount.id) || 0;
1274
1424
  usedDiscountCardLimitCounts.set(nextBundleDiscount.id, curCount + remainingNum);
1275
1425
  }
1276
1426
  const nextOriginTotal = product.original_price || product.price || 0;
1277
- const nextTargetTotal = (0, import_utils.getDiscountAmount)(nextBundleDiscount, nextOriginTotal, nextOriginTotal);
1278
- const nextDiscountAmount = new import_decimal.default(nextOriginTotal).minus(nextTargetTotal).toNumber();
1427
+ const nextTargetTotal = (0, _utils.getDiscountAmount)(nextBundleDiscount, nextOriginTotal, nextOriginTotal);
1428
+ const nextDiscountAmount = new _decimal.default(nextOriginTotal).minus(nextTargetTotal).toNumber();
1279
1429
  const nextUniqueId = `${flatItem._id}_split_${continueSplitIndex}`;
1280
- const nextBundleDiscType = nextBundleDiscount.tag === "product_discount_card" ? "discount_card" : nextBundleDiscount.tag;
1430
+ const nextBundleDiscType = nextBundleDiscount.tag === 'product_discount_card' ? 'discount_card' : nextBundleDiscount.tag;
1281
1431
  const nextDiscountDetail = {
1282
1432
  amount: nextDiscountAmount * remainingNum,
1283
1433
  type: nextBundleDiscType,
1284
1434
  discount: {
1285
- discount_card_type: (_G = nextBundleDiscount == null ? void 0 : nextBundleDiscount.metadata) == null ? void 0 : _G.discount_card_type,
1435
+ discount_card_type: nextBundleDiscount?.metadata?.discount_card_type,
1286
1436
  fixed_amount: nextDiscountAmount,
1287
1437
  resource_id: nextBundleDiscount.id,
1288
1438
  title: nextBundleDiscount.format_title,
@@ -1295,8 +1445,8 @@ var RulesModule = class extends import_BaseModule.BaseModule {
1295
1445
  custom_product_bundle_map_id: nextUniqueId,
1296
1446
  num: remainingNum
1297
1447
  },
1298
- config: nextBundleDiscount == null ? void 0 : nextBundleDiscount.config,
1299
- _num: remainingNum * (((_H = flatItem == null ? void 0 : flatItem.parentProduct) == null ? void 0 : _H.num) || 1)
1448
+ config: nextBundleDiscount?.config,
1449
+ _num: remainingNum * (flatItem?.parentProduct?.num || 1)
1300
1450
  };
1301
1451
  const nextAppliedProducts = appliedDiscountProducts.get(nextBundleDiscount.id) || [];
1302
1452
  nextAppliedProducts.push(nextDiscountDetail);
@@ -1307,25 +1457,22 @@ var RulesModule = class extends import_BaseModule.BaseModule {
1307
1457
  num: remainingNum,
1308
1458
  quantity: remainingNum,
1309
1459
  total: nextTargetTotal,
1310
- price: new import_decimal.default(nextOriginTotal || 0).minus(nextDiscountDetail.discount.fixed_amount).toNumber(),
1460
+ price: new _decimal.default(nextOriginTotal || 0).minus(nextDiscountDetail.discount.fixed_amount).toNumber(),
1311
1461
  discount_list: [nextDiscountDetail],
1312
1462
  processed: true
1313
1463
  });
1314
1464
  remainingNum = 0;
1315
1465
  break;
1316
1466
  }
1467
+
1468
+ // 🔥 需要拆分:计算最大使用次数
1317
1469
  let nextMaxUsageLimit;
1318
1470
  if (nextIsGoodPass) {
1319
- const nextGoodPassCount = nextApplicableDiscounts.filter(
1320
- (item) => (item.tag || item.type) === "good_pass"
1321
- ).length;
1471
+ const nextGoodPassCount = nextApplicableDiscounts.filter(item => (item.tag || item.type) === 'good_pass').length;
1322
1472
  nextMaxUsageLimit = nextGoodPassCount;
1323
- if ((_I = nextSelectedDiscount.config) == null ? void 0 : _I.maxUsagePerOrder) {
1473
+ if (nextSelectedDiscount.config?.maxUsagePerOrder) {
1324
1474
  const usedCount = usedProductIdCounts.get(nextSelectedDiscount.product_id) || 0;
1325
- nextMaxUsageLimit = Math.min(
1326
- nextGoodPassCount,
1327
- Math.max(0, nextSelectedDiscount.config.maxUsagePerOrder - usedCount)
1328
- );
1475
+ nextMaxUsageLimit = Math.min(nextGoodPassCount, Math.max(0, nextSelectedDiscount.config.maxUsagePerOrder - usedCount));
1329
1476
  }
1330
1477
  } else if (nextIsDiscountCard && nextApplicableProductLimit > 0) {
1331
1478
  const usedLimitCount = usedDiscountCardLimitCounts.get(nextSelectedDiscount.id) || 0;
@@ -1335,6 +1482,7 @@ var RulesModule = class extends import_BaseModule.BaseModule {
1335
1482
  }
1336
1483
  const nextSplitCount = Math.min(remainingNum, nextMaxUsageLimit);
1337
1484
  if (nextSplitCount === 0) {
1485
+ // 已达使用上限,剩余商品无折扣存储
1338
1486
  processedItems.push({
1339
1487
  ...flatItem,
1340
1488
  _id: `${flatItem._id}_split_rest`,
@@ -1345,31 +1493,29 @@ var RulesModule = class extends import_BaseModule.BaseModule {
1345
1493
  });
1346
1494
  break;
1347
1495
  }
1496
+
1497
+ // 生成有折扣的商品
1348
1498
  for (let j = 0; j < nextSplitCount; j++) {
1349
1499
  const nextBundleDiscount = nextIsGoodPass ? nextSelectedDiscountCard || nextApplicableDiscounts[j] : nextSelectedDiscountCard || nextApplicableDiscounts[0];
1350
1500
  usedDiscounts.set(nextBundleDiscount.id, true);
1351
- if ((nextBundleDiscount.tag || nextBundleDiscount.type) === "good_pass") {
1501
+ if ((nextBundleDiscount.tag || nextBundleDiscount.type) === 'good_pass') {
1352
1502
  const curCount = usedProductIdCounts.get(nextBundleDiscount.product_id) || 0;
1353
1503
  usedProductIdCounts.set(nextBundleDiscount.product_id, curCount + 1);
1354
1504
  }
1355
1505
  const nextBundleDiscTypeForCount = nextBundleDiscount.tag || nextBundleDiscount.type;
1356
- if (["discount_card", "product_discount_card"].includes(nextBundleDiscTypeForCount)) {
1506
+ if (['discount_card', 'product_discount_card'].includes(nextBundleDiscTypeForCount)) {
1357
1507
  const curLimitCount = usedDiscountCardLimitCounts.get(nextBundleDiscount.id) || 0;
1358
1508
  usedDiscountCardLimitCounts.set(nextBundleDiscount.id, curLimitCount + 1);
1359
1509
  }
1360
1510
  const nextUniqueId = `${flatItem._id}_split_${continueSplitIndex}`;
1361
- const nextDiscountedPrice = (0, import_utils.getDiscountAmount)(
1362
- nextBundleDiscount,
1363
- product.origin_total,
1364
- product.origin_total
1365
- );
1366
- const nextBundleDiscAmount = new import_decimal.default(product.origin_total || 0).minus(nextDiscountedPrice).toNumber();
1367
- const nextBundleDiscTypeStr = nextBundleDiscount.tag === "product_discount_card" ? "discount_card" : nextBundleDiscount.tag;
1511
+ const nextDiscountedPrice = (0, _utils.getDiscountAmount)(nextBundleDiscount, product.origin_total, product.origin_total);
1512
+ const nextBundleDiscAmount = new _decimal.default(product.origin_total || 0).minus(nextDiscountedPrice).toNumber();
1513
+ const nextBundleDiscTypeStr = nextBundleDiscount.tag === 'product_discount_card' ? 'discount_card' : nextBundleDiscount.tag;
1368
1514
  const nextDiscountDetail = {
1369
1515
  amount: nextBundleDiscAmount,
1370
1516
  type: nextBundleDiscTypeStr,
1371
1517
  discount: {
1372
- discount_card_type: (_J = nextBundleDiscount == null ? void 0 : nextBundleDiscount.metadata) == null ? void 0 : _J.discount_card_type,
1518
+ discount_card_type: nextBundleDiscount?.metadata?.discount_card_type,
1373
1519
  fixed_amount: nextBundleDiscAmount,
1374
1520
  resource_id: nextBundleDiscount.id,
1375
1521
  title: nextBundleDiscount.format_title,
@@ -1383,7 +1529,7 @@ var RulesModule = class extends import_BaseModule.BaseModule {
1383
1529
  num: 1
1384
1530
  },
1385
1531
  _num: 1,
1386
- config: nextBundleDiscount == null ? void 0 : nextBundleDiscount.config
1532
+ config: nextBundleDiscount?.config
1387
1533
  };
1388
1534
  const nextAppliedProducts = appliedDiscountProducts.get(nextBundleDiscount.id) || [];
1389
1535
  nextAppliedProducts.push(nextDiscountDetail);
@@ -1404,46 +1550,53 @@ var RulesModule = class extends import_BaseModule.BaseModule {
1404
1550
  remainingNum -= nextSplitCount;
1405
1551
  }
1406
1552
  } else {
1553
+ // 不拆分:直接应用折扣
1407
1554
  const currentBundleDiscount = selectedDiscountCard || applicableDiscounts[0];
1408
1555
  usedDiscounts.set(currentBundleDiscount.id, true);
1409
- if ((currentBundleDiscount.tag || currentBundleDiscount.type) === "good_pass") {
1556
+ // 🔥 更新 product_id 使用计数
1557
+ if ((currentBundleDiscount.tag || currentBundleDiscount.type) === 'good_pass') {
1410
1558
  const currentCount = usedProductIdCounts.get(currentBundleDiscount.product_id) || 0;
1411
1559
  usedProductIdCounts.set(currentBundleDiscount.product_id, currentCount + 1);
1412
1560
  }
1561
+ // 🔥 更新折扣卡 applicableProductLimit 使用计数(不拆分时按商品数量计数)
1413
1562
  const noSplitDiscountType = currentBundleDiscount.tag || currentBundleDiscount.type;
1414
- if (["discount_card", "product_discount_card"].includes(noSplitDiscountType)) {
1563
+ if (['discount_card', 'product_discount_card'].includes(noSplitDiscountType)) {
1415
1564
  const bundleQuantity = product.num || 1;
1416
1565
  const currentNoSplitCount = usedDiscountCardLimitCounts.get(currentBundleDiscount.id) || 0;
1417
1566
  usedDiscountCardLimitCounts.set(currentBundleDiscount.id, currentNoSplitCount + bundleQuantity);
1418
1567
  }
1419
1568
  const productOriginTotal = product.original_price || product.price || 0;
1420
- const isOrderLevel = (0, import_utils3.isOrderLevelFixedAmountDiscount)(selectedDiscount);
1569
+
1570
+ // 🔥 检查是否是 order_level 固定金额折扣卡
1571
+ const isOrderLevel = (0, _utils.isOrderLevelFixedAmountDiscount)(selectedDiscount);
1421
1572
  const orderLevelAllocation = isOrderLevel ? orderLevelDiscountAllocations.get(selectedDiscount.id) : null;
1422
- const productAllocation = orderLevelAllocation == null ? void 0 : orderLevelAllocation.get(flatItem._id);
1573
+ const productAllocation = orderLevelAllocation?.get(flatItem._id);
1423
1574
  let targetProductTotal;
1424
1575
  let fixedAmountPerItem;
1425
1576
  let productDiscountDifference;
1426
1577
  if (isOrderLevel && productAllocation) {
1578
+ // order_level:使用预计算的单价折扣金额(已经是单价,无需再除以数量)
1427
1579
  fixedAmountPerItem = productAllocation.discountAmount;
1428
1580
  productDiscountDifference = productAllocation.difference;
1429
- targetProductTotal = Math.max(new import_decimal.default(productOriginTotal).minus(fixedAmountPerItem).toNumber(), 0);
1581
+ targetProductTotal = Math.max(new _decimal.default(productOriginTotal).minus(fixedAmountPerItem).toNumber(), 0);
1430
1582
  } else {
1431
- targetProductTotal = (0, import_utils.getDiscountAmount)(
1432
- currentBundleDiscount,
1433
- productOriginTotal,
1434
- productOriginTotal
1435
- );
1436
- fixedAmountPerItem = new import_decimal.default(productOriginTotal).minus(targetProductTotal).toNumber();
1583
+ // item_level 或其他类型:使用原有逻辑
1584
+ targetProductTotal = (0, _utils.getDiscountAmount)(currentBundleDiscount, productOriginTotal, productOriginTotal);
1585
+ fixedAmountPerItem = new _decimal.default(productOriginTotal).minus(targetProductTotal).toNumber();
1437
1586
  }
1438
- const bundleActualDiscountAmount = new import_decimal.default(productOriginTotal).minus(targetProductTotal).toNumber();
1587
+
1588
+ // 计算实际折扣金额
1589
+ const bundleActualDiscountAmount = new _decimal.default(productOriginTotal).minus(targetProductTotal).toNumber();
1590
+
1591
+ // 🔥 使用当前的 _id 作为唯一标识
1439
1592
  const uniqueId = flatItem._id;
1440
1593
  const discountDetail = {
1441
1594
  amount: bundleActualDiscountAmount * (product.num || 1),
1442
- type: currentBundleDiscount.tag === "product_discount_card" ? "discount_card" : currentBundleDiscount.tag,
1595
+ type: currentBundleDiscount.tag === 'product_discount_card' ? 'discount_card' : currentBundleDiscount.tag,
1443
1596
  discount: {
1444
- discount_card_type: (_K = currentBundleDiscount == null ? void 0 : currentBundleDiscount.metadata) == null ? void 0 : _K.discount_card_type,
1597
+ discount_card_type: currentBundleDiscount?.metadata?.discount_card_type,
1445
1598
  fixed_amount: bundleActualDiscountAmount,
1446
- discount_calculation_mode: (_L = currentBundleDiscount == null ? void 0 : currentBundleDiscount.metadata) == null ? void 0 : _L.discount_calculation_mode,
1599
+ discount_calculation_mode: currentBundleDiscount?.metadata?.discount_calculation_mode,
1447
1600
  resource_id: currentBundleDiscount.id,
1448
1601
  title: currentBundleDiscount.format_title,
1449
1602
  original_amount: product.original_price,
@@ -1454,41 +1607,50 @@ var RulesModule = class extends import_BaseModule.BaseModule {
1454
1607
  metadata: {
1455
1608
  // 🔥 使用唯一的 _id
1456
1609
  custom_product_bundle_map_id: uniqueId,
1457
- discount_rule_uncheck_flag: selectedDiscount == null ? void 0 : selectedDiscount.discount_rule_uncheck_flag,
1610
+ discount_rule_uncheck_flag: selectedDiscount?.discount_rule_uncheck_flag,
1458
1611
  num: product.num || 1,
1459
1612
  // 🔥 order_level 分摊差值
1460
- ...productDiscountDifference !== void 0 && { product_discount_difference: productDiscountDifference }
1613
+ ...(productDiscountDifference !== undefined && {
1614
+ product_discount_difference: productDiscountDifference
1615
+ })
1461
1616
  },
1462
- config: currentBundleDiscount == null ? void 0 : currentBundleDiscount.config,
1463
- _num: (product.num || 1) * (((_M = flatItem == null ? void 0 : flatItem.parentProduct) == null ? void 0 : _M.num) || 1)
1617
+ config: currentBundleDiscount?.config,
1618
+ _num: (product.num || 1) * (flatItem?.parentProduct?.num || 1)
1464
1619
  };
1620
+
1621
+ // 记录实际应用的折扣
1465
1622
  const appliedProducts = appliedDiscountProducts.get(currentBundleDiscount.id) || [];
1466
1623
  appliedProducts.push(discountDetail);
1467
1624
  appliedDiscountProducts.set(currentBundleDiscount.id, appliedProducts);
1468
1625
  processedItems.push({
1469
1626
  ...flatItem,
1470
1627
  total: targetProductTotal,
1471
- price: new import_decimal.default(productOriginTotal || 0).minus(fixedAmountPerItem).toNumber(),
1472
- discount_list: this.filterDiscountListByType(flatItem.discount_list, "promotion").concat([discountDetail]),
1628
+ price: new _decimal.default(productOriginTotal || 0).minus(fixedAmountPerItem).toNumber(),
1629
+ discount_list: this.filterDiscountListByType(flatItem.discount_list, 'promotion').concat([discountDetail]),
1473
1630
  processed: true
1474
1631
  });
1475
1632
  }
1476
1633
  processedFlatItemsMap.set(flatItem._id, processedItems);
1477
1634
  }
1478
1635
  });
1479
- const reconstructProductsWithBundle = (processedProductsMap2, processedFlatItemsMap2, originalProductList) => {
1636
+
1637
+ // 🔥 重组:将处理后的bundle子商品重新组装回主商品
1638
+ const reconstructProductsWithBundle = (processedProductsMap, processedFlatItemsMap, originalProductList) => {
1480
1639
  const result = [];
1481
- originalProductList.forEach((originProduct) => {
1640
+ originalProductList.forEach(originProduct => {
1482
1641
  const product = this.hooks.getProduct(originProduct);
1483
- const mainProductArr = processedProductsMap2.get(product._id);
1642
+
1643
+ // 获取主商品处理结果
1644
+ const mainProductArr = processedProductsMap.get(product._id);
1484
1645
  if (!mainProductArr || mainProductArr.length === 0) {
1646
+ // 如果没有处理结果,返回默认商品(还原 option 价格)
1485
1647
  const getDefaultProduct = () => {
1486
1648
  const restoredOptions = restoreOptionPrices(product.options);
1487
1649
  if (product.isClient) {
1488
1650
  return this.hooks.setProduct(originProduct, {
1489
- discount_list: this.filterDiscountListByType(product.discount_list, "promotion"),
1651
+ discount_list: this.filterDiscountListByType(product.discount_list, 'promotion'),
1490
1652
  price: product.price,
1491
- origin_total: (0, import_utils2.getProductOriginTotalPrice)({
1653
+ origin_total: (0, _utils2.getProductOriginTotalPrice)({
1492
1654
  product: {
1493
1655
  original_price: product.original_price
1494
1656
  },
@@ -1497,7 +1659,7 @@ var RulesModule = class extends import_BaseModule.BaseModule {
1497
1659
  }),
1498
1660
  variant: originProduct._productInit.variant,
1499
1661
  original_price: originProduct._productInit.original_price,
1500
- total: (0, import_utils2.getProductTotalPrice)({
1662
+ total: (0, _utils2.getProductTotalPrice)({
1501
1663
  product: {
1502
1664
  price: product.price
1503
1665
  },
@@ -1508,7 +1670,7 @@ var RulesModule = class extends import_BaseModule.BaseModule {
1508
1670
  });
1509
1671
  } else {
1510
1672
  return this.hooks.setProduct(originProduct, {
1511
- discount_list: this.filterDiscountListByType(product.discount_list, "promotion"),
1673
+ discount_list: this.filterDiscountListByType(product.discount_list, 'promotion'),
1512
1674
  total: product.total,
1513
1675
  origin_total: product.origin_total,
1514
1676
  price: product.price,
@@ -1519,332 +1681,360 @@ var RulesModule = class extends import_BaseModule.BaseModule {
1519
1681
  result.push(getDefaultProduct());
1520
1682
  return;
1521
1683
  }
1684
+
1685
+ // 检查是否有bundle子商品需要重组
1522
1686
  const hasBundle = product.bundle && Array.isArray(product.bundle) && product.bundle.length > 0;
1523
1687
  if (!hasBundle) {
1688
+ // 没有bundle,直接使用主商品处理结果
1524
1689
  result.push(...mainProductArr);
1525
1690
  } else {
1526
- const bundleProcessingInfo = /* @__PURE__ */ new Map();
1527
- let hasGoodPassApplied = false;
1691
+ // 🔥 有bundle,需要检查子商品是否应用了商品券
1692
+ // 1. 先收集所有bundle子商品的处理结果
1693
+ const bundleProcessingInfo = new Map();
1694
+ let hasGoodPassApplied = false; // 标记是否有子商品应用了商品券
1695
+
1528
1696
  if (product.bundle && Array.isArray(product.bundle)) {
1529
1697
  product.bundle.forEach((bundleItem, bundleIndex) => {
1530
1698
  const bundleItemId = `${product._id}_bundle_${bundleIndex}`;
1531
- const processedBundleItems = processedFlatItemsMap2.get(bundleItemId);
1699
+ const processedBundleItems = processedFlatItemsMap.get(bundleItemId);
1532
1700
  if (!processedBundleItems || processedBundleItems.length === 0) {
1701
+ // 未处理的bundle item,保持原样
1533
1702
  bundleProcessingInfo.set(bundleIndex, [bundleItem]);
1534
1703
  } else {
1704
+ // 处理过的bundle item
1535
1705
  bundleProcessingInfo.set(bundleIndex, processedBundleItems);
1536
- const hasGoodPass = processedBundleItems.some(
1537
- (item) => {
1538
- var _a;
1539
- return (_a = item.discount_list) == null ? void 0 : _a.some(
1540
- (discount) => discount.type === "good_pass" || discount.tag === "good_pass"
1541
- );
1542
- }
1543
- );
1706
+ // 🔥 检查是否应用了商品券(good_pass)
1707
+ const hasGoodPass = processedBundleItems.some(item => item.discount_list?.some(discount => discount.type === 'good_pass' || discount.tag === 'good_pass'));
1544
1708
  if (hasGoodPass) {
1545
1709
  hasGoodPassApplied = true;
1546
1710
  }
1547
1711
  }
1548
1712
  });
1549
1713
  }
1714
+
1715
+ // 🔥 2. 如果有子商品应用了商品券,且主商品数量大于1,需要拆分主商品
1550
1716
  const mainProductQuantity = mainProductArr[0] ? this.hooks.getProduct(mainProductArr[0]).quantity : 1;
1551
1717
  if (hasGoodPassApplied && mainProductArr.length === 1 && mainProductQuantity > 1) {
1552
1718
  const mainProduct = mainProductArr[0];
1553
1719
  const mainProductData = this.hooks.getProduct(mainProduct);
1720
+
1721
+ // 🔥 方案:拆分成2个主商品
1722
+ // 1. 一个主商品(qty=1)包含拆分后的bundle
1723
+ // 2. 一个主商品(qty=原quantity-1)包含原始未拆分的bundle
1724
+
1725
+ // 第一个:包含拆分后的bundle (qty=1)
1554
1726
  const newBundleWithDiscount = [];
1555
- (product.bundle || []).forEach(
1556
- (bundleItem, bundleIndex) => {
1557
- const processedItems = bundleProcessingInfo.get(
1558
- bundleIndex
1559
- ) || [bundleItem];
1560
- if (processedItems.length > 1) {
1561
- processedItems.forEach((item) => {
1562
- const updatedDiscountList2 = (item.discount_list || []).map((discount) => {
1563
- var _a;
1564
- return {
1565
- ...discount,
1566
- metadata: {
1567
- ...discount.metadata,
1568
- num: item.num,
1569
- custom_product_bundle_map_id: (_a = item.metadata) == null ? void 0 : _a.custom_product_bundle_map_id
1570
- }
1571
- // num: item.num, // 使用拆分后的 num
1572
- };
1573
- });
1574
- newBundleWithDiscount.push({
1575
- ...bundleItem,
1576
- _id: item._id,
1577
- product_id: bundleItem.product_id,
1578
- price: item.price,
1727
+ (product.bundle || []).forEach((bundleItem, bundleIndex) => {
1728
+ const processedItems = bundleProcessingInfo.get(bundleIndex) || [bundleItem];
1729
+ if (processedItems.length > 1) {
1730
+ // 被拆分的子商品,添加所有拆分项
1731
+ processedItems.forEach(item => {
1732
+ // 🔥 更新 discount_list 中的 num,使其与拆分后的 item.num 一致
1733
+ const updatedDiscountList = (item.discount_list || []).map(discount => ({
1734
+ ...discount,
1735
+ metadata: {
1736
+ ...discount.metadata,
1579
1737
  num: item.num,
1580
- quantity: item.num,
1581
- discount_list: updatedDiscountList2
1582
- });
1738
+ custom_product_bundle_map_id: item.metadata?.custom_product_bundle_map_id
1739
+ }
1740
+ // num: item.num, // 使用拆分后的 num
1741
+ }));
1742
+ newBundleWithDiscount.push({
1743
+ ...bundleItem,
1744
+ _id: item._id,
1745
+ product_id: bundleItem.product_id,
1746
+ price: item.price,
1747
+ num: item.num,
1748
+ quantity: item.num,
1749
+ discount_list: updatedDiscountList
1583
1750
  });
1584
- } else {
1585
- const item = processedItems[0];
1586
- if (item.processed) {
1587
- const updatedDiscountList2 = (item.discount_list || []).map((discount) => {
1588
- var _a;
1589
- return {
1590
- ...discount,
1591
- metadata: {
1592
- ...discount.metadata,
1593
- num: item.num,
1594
- custom_product_bundle_map_id: (_a = item.metadata) == null ? void 0 : _a.custom_product_bundle_map_id
1595
- }
1596
- // num: item.num, // 使用当前的 num
1597
- };
1598
- });
1599
- newBundleWithDiscount.push({
1600
- ...bundleItem,
1601
- _id: item._id,
1602
- product_id: bundleItem.product_id,
1603
- price: item.price,
1751
+ });
1752
+ } else {
1753
+ // 未拆分的子商品,保持原样
1754
+ const item = processedItems[0];
1755
+ if (item.processed) {
1756
+ // 🔥 同样需要更新 discount_list 中的 num
1757
+ const updatedDiscountList = (item.discount_list || []).map(discount => ({
1758
+ ...discount,
1759
+ metadata: {
1760
+ ...discount.metadata,
1604
1761
  num: item.num,
1605
- quantity: item.num,
1606
- discount_list: updatedDiscountList2
1607
- });
1608
- } else {
1609
- newBundleWithDiscount.push(item);
1610
- }
1762
+ custom_product_bundle_map_id: item.metadata?.custom_product_bundle_map_id
1763
+ }
1764
+ // num: item.num, // 使用当前的 num
1765
+ }));
1766
+ newBundleWithDiscount.push({
1767
+ ...bundleItem,
1768
+ _id: item._id,
1769
+ product_id: bundleItem.product_id,
1770
+ price: item.price,
1771
+ num: item.num,
1772
+ quantity: item.num,
1773
+ discount_list: updatedDiscountList
1774
+ });
1775
+ } else {
1776
+ newBundleWithDiscount.push(item);
1611
1777
  }
1612
1778
  }
1613
- );
1779
+ });
1780
+
1781
+ // 计算第一个主商品的总价(包含拆分后的bundle)
1614
1782
  let newTotalWithDiscount = Number(mainProductData.price || 0);
1615
- let newOriginTotalWithDiscount = Number(
1616
- mainProductData.original_price || mainProductData.price || 0
1617
- );
1618
- const updatedMainDiscountList = mainProductData.discount_list.map((discount) => {
1619
- var _a, _b;
1620
- if ((_a = discount == null ? void 0 : discount.metadata) == null ? void 0 : _a.custom_product_bundle_map_id) {
1783
+ let newOriginTotalWithDiscount = Number(mainProductData.original_price || mainProductData.price || 0);
1784
+
1785
+ // 🔥 更新主商品自己的 discount_list 中的 num(quantity=1)
1786
+ const updatedMainDiscountList = mainProductData.discount_list.map(discount => {
1787
+ if (discount?.metadata?.custom_product_bundle_map_id) {
1788
+ // bundle的discount_list保持不变
1621
1789
  return discount;
1622
1790
  }
1791
+ // 主商品自己的discount,更新num为1
1623
1792
  return {
1624
1793
  ...discount,
1625
1794
  // num: 1,
1626
1795
  metadata: {
1627
1796
  ...discount.metadata,
1628
- custom_product_bundle_map_id: (_b = discount == null ? void 0 : discount.metadata) == null ? void 0 : _b.custom_product_bundle_map_id,
1797
+ custom_product_bundle_map_id: discount?.metadata?.custom_product_bundle_map_id,
1629
1798
  num: 1
1630
1799
  }
1631
1800
  };
1632
1801
  });
1633
- const mainDiscountList = updatedMainDiscountList.filter(
1634
- (item) => {
1635
- var _a;
1636
- return !((_a = item == null ? void 0 : item.metadata) == null ? void 0 : _a.custom_product_bundle_map_id);
1637
- }
1638
- );
1802
+
1803
+ // 🔥 使用更新后的列表计算折扣金额
1804
+ const mainDiscountList = updatedMainDiscountList.filter(item => !item?.metadata?.custom_product_bundle_map_id);
1639
1805
  if (mainDiscountList && mainDiscountList.length > 0) {
1640
- const allDiscountAmount = (0, import_utils.getDiscountListAmountTotal)(mainDiscountList);
1641
- newTotalWithDiscount = new import_decimal.default(mainProductData.price || 0).minus(allDiscountAmount).toNumber() ?? newTotalWithDiscount;
1806
+ const allDiscountAmount = (0, _utils.getDiscountListAmountTotal)(mainDiscountList);
1807
+ newTotalWithDiscount = new _decimal.default(mainProductData.price || 0).minus(allDiscountAmount).toNumber() ?? newTotalWithDiscount;
1642
1808
  newOriginTotalWithDiscount = mainProductData.origin_total ?? newOriginTotalWithDiscount;
1643
1809
  }
1810
+
1811
+ // 累加bundle的价格(只累加一次)
1644
1812
  if (newBundleWithDiscount.length > 0) {
1645
- newBundleWithDiscount.forEach((item) => {
1813
+ newBundleWithDiscount.forEach(item => {
1646
1814
  newTotalWithDiscount += Number(item.price) * Number(item.num);
1647
1815
  });
1648
- newBundleWithDiscount.forEach((item) => {
1649
- var _a, _b, _c;
1650
- const originalPrice = ((_c = (_b = (_a = item.discount_list) == null ? void 0 : _a[0]) == null ? void 0 : _b.discount) == null ? void 0 : _c.original_amount) || item.price;
1816
+ newBundleWithDiscount.forEach(item => {
1817
+ const originalPrice = item.discount_list?.[0]?.discount?.original_amount || item.price;
1651
1818
  newOriginTotalWithDiscount += Number(originalPrice) * Number(item.num);
1652
1819
  });
1653
1820
  }
1654
- if (product == null ? void 0 : product.options) {
1821
+
1822
+ // 累加options的价格(options不参与折扣,在最终设置total时处理)
1823
+ if (product?.options) {
1655
1824
  newTotalWithDiscount = product.options.reduce((accumulator, currentValue) => {
1656
- const currentPrice = new import_decimal.default(currentValue.price || 0);
1657
- const currentNum = new import_decimal.default(currentValue.num || 0);
1825
+ const currentPrice = new _decimal.default(currentValue.price || 0);
1826
+ const currentNum = new _decimal.default(currentValue.num || 0);
1658
1827
  return accumulator.add(currentPrice.mul(currentNum));
1659
- }, new import_decimal.default(newTotalWithDiscount)).toNumber();
1828
+ }, new _decimal.default(newTotalWithDiscount)).toNumber();
1660
1829
  }
1661
- result.push(
1662
- this.hooks.setProduct(mainProduct, {
1663
- ...mainProductData,
1664
- _id: `${mainProductData._id.split("___")[0]}___split_discount`,
1665
- quantity: 1,
1666
- discount_list: updatedMainDiscountList,
1667
- bundle: newBundleWithDiscount,
1668
- total: new import_decimal.default(newTotalWithDiscount).toDecimalPlaces(2, import_decimal.default.ROUND_HALF_UP).toNumber(),
1669
- origin_total: new import_decimal.default(newOriginTotalWithDiscount).toDecimalPlaces(2, import_decimal.default.ROUND_HALF_UP).toNumber()
1670
- })
1671
- );
1830
+
1831
+ // 添加第一个主商品:qty=1,包含拆分后的bundle
1832
+ result.push(this.hooks.setProduct(mainProduct, {
1833
+ ...mainProductData,
1834
+ _id: `${mainProductData._id.split('___')[0]}___split_discount`,
1835
+ quantity: 1,
1836
+ discount_list: updatedMainDiscountList,
1837
+ bundle: newBundleWithDiscount,
1838
+ total: new _decimal.default(newTotalWithDiscount).toDecimalPlaces(2, _decimal.default.ROUND_HALF_UP).toNumber(),
1839
+ origin_total: new _decimal.default(newOriginTotalWithDiscount).toDecimalPlaces(2, _decimal.default.ROUND_HALF_UP).toNumber()
1840
+ }));
1841
+
1842
+ // 第二个:包含原始bundle (qty=原quantity-1)
1672
1843
  if (mainProductQuantity > 1) {
1673
1844
  const newBundleOriginal = [];
1674
- (product.bundle || []).forEach(
1675
- (bundleItem, bundleIndex) => {
1676
- newBundleOriginal.push({
1677
- ...bundleItem,
1678
- discount_list: []
1679
- });
1680
- }
1681
- );
1845
+ (product.bundle || []).forEach((bundleItem, bundleIndex) => {
1846
+ // 使用原始的bundle配置,不包含拆分
1847
+ newBundleOriginal.push({
1848
+ ...bundleItem,
1849
+ discount_list: []
1850
+ });
1851
+ });
1852
+
1853
+ // 计算第二个主商品的总价
1682
1854
  let newTotalOriginal = Number(mainProductData.price || 0);
1683
- let newOriginTotalOriginal = Number(
1684
- mainProductData.original_price || mainProductData.price || 0
1685
- );
1686
- const updatedMainDiscountListOriginal = mainProductData.discount_list.map((discount) => {
1687
- var _a, _b;
1688
- if ((_a = discount == null ? void 0 : discount.metadata) == null ? void 0 : _a.custom_product_bundle_map_id) {
1855
+ let newOriginTotalOriginal = Number(mainProductData.original_price || mainProductData.price || 0);
1856
+
1857
+ // 🔥 更新主商品自己的 discount_list 中的 num(quantity=原quantity-1)
1858
+ const updatedMainDiscountListOriginal = mainProductData.discount_list.map(discount => {
1859
+ if (discount?.metadata?.custom_product_bundle_map_id) {
1860
+ // bundle的discount_list保持不变
1689
1861
  return discount;
1690
1862
  }
1863
+ // 主商品自己的discount,更新num为 mainProductQuantity - 1
1691
1864
  return {
1692
1865
  ...discount,
1693
1866
  metadata: {
1694
1867
  ...discount.metadata,
1695
1868
  num: mainProductQuantity - 1,
1696
- custom_product_bundle_map_id: (_b = discount == null ? void 0 : discount.metadata) == null ? void 0 : _b.custom_product_bundle_map_id
1869
+ custom_product_bundle_map_id: discount?.metadata?.custom_product_bundle_map_id
1697
1870
  }
1698
1871
  // num: mainProductQuantity - 1,
1699
1872
  };
1700
1873
  });
1701
- const mainDiscountListOriginal = updatedMainDiscountListOriginal.filter(
1702
- (item) => {
1703
- var _a;
1704
- return !((_a = item == null ? void 0 : item.metadata) == null ? void 0 : _a.custom_product_bundle_map_id);
1705
- }
1706
- );
1874
+
1875
+ // 🔥 使用更新后的列表计算折扣金额
1876
+ const mainDiscountListOriginal = updatedMainDiscountListOriginal.filter(item => !item?.metadata?.custom_product_bundle_map_id);
1707
1877
  if (mainDiscountListOriginal && mainDiscountListOriginal.length > 0) {
1708
- const allDiscountAmount = (0, import_utils.getDiscountListAmount)(
1709
- mainDiscountListOriginal
1710
- );
1711
- newTotalOriginal = new import_decimal.default(mainProductData.price || 0).minus(allDiscountAmount).toNumber() ?? newTotalOriginal;
1878
+ const allDiscountAmount = (0, _utils.getDiscountListAmount)(mainDiscountListOriginal);
1879
+ newTotalOriginal = new _decimal.default(mainProductData.price || 0).minus(allDiscountAmount).toNumber() ?? newTotalOriginal;
1712
1880
  newOriginTotalOriginal = mainProductData.origin_total ?? newOriginTotalOriginal;
1713
1881
  }
1882
+
1883
+ // 累加bundle的价格(只累加一次)
1714
1884
  if (newBundleOriginal.length > 0) {
1715
- newBundleOriginal.forEach((item) => {
1885
+ newBundleOriginal.forEach(item => {
1716
1886
  newTotalOriginal += Number(item.price) * Number(item.num);
1717
1887
  newOriginTotalOriginal += Number(item.price) * Number(item.num);
1718
1888
  });
1719
1889
  }
1720
- if (product == null ? void 0 : product.options) {
1890
+
1891
+ // 累加options的价格(options不参与折扣,在最终设置total时处理)
1892
+ if (product?.options) {
1721
1893
  newTotalOriginal = product.options.reduce((accumulator, currentValue) => {
1722
- const currentPrice = new import_decimal.default(currentValue.price || 0);
1723
- const currentNum = new import_decimal.default(currentValue.num || 0);
1894
+ const currentPrice = new _decimal.default(currentValue.price || 0);
1895
+ const currentNum = new _decimal.default(currentValue.num || 0);
1724
1896
  return accumulator.add(currentPrice.mul(currentNum));
1725
- }, new import_decimal.default(newTotalOriginal)).toNumber();
1897
+ }, new _decimal.default(newTotalOriginal)).toNumber();
1726
1898
  }
1727
- newTotalOriginal = new import_decimal.default(newTotalOriginal).toDecimalPlaces(2, import_decimal.default.ROUND_HALF_UP).toNumber();
1728
- newOriginTotalOriginal = new import_decimal.default(newOriginTotalOriginal).toDecimalPlaces(2, import_decimal.default.ROUND_HALF_UP).toNumber();
1729
- result.push(
1730
- this.hooks.setProduct(mainProduct, {
1731
- ...mainProductData,
1732
- _id: `${mainProductData._id.split("___")[0]}___split_normal`,
1733
- quantity: mainProductQuantity - 1,
1734
- discount_list: updatedMainDiscountListOriginal,
1735
- bundle: newBundleOriginal,
1736
- total: newTotalOriginal,
1737
- origin_total: newOriginTotalOriginal
1738
- })
1739
- );
1899
+ newTotalOriginal = new _decimal.default(newTotalOriginal).toDecimalPlaces(2, _decimal.default.ROUND_HALF_UP).toNumber();
1900
+ newOriginTotalOriginal = new _decimal.default(newOriginTotalOriginal).toDecimalPlaces(2, _decimal.default.ROUND_HALF_UP).toNumber();
1901
+
1902
+ // 添加第二个主商品:qty=原quantity-1,包含原始bundle
1903
+ result.push(this.hooks.setProduct(mainProduct, {
1904
+ ...mainProductData,
1905
+ _id: `${mainProductData._id.split('___')[0]}___split_normal`,
1906
+ quantity: mainProductQuantity - 1,
1907
+ discount_list: updatedMainDiscountListOriginal,
1908
+ bundle: newBundleOriginal,
1909
+ total: newTotalOriginal,
1910
+ origin_total: newOriginTotalOriginal
1911
+ }));
1740
1912
  }
1741
1913
  } else {
1742
- mainProductArr.forEach((mainProduct) => {
1743
- var _a, _b, _c;
1914
+ // 🔥 没有子商品被拆分,使用原有逻辑
1915
+ mainProductArr.forEach(mainProduct => {
1744
1916
  const mainProductData = this.hooks.getProduct(mainProduct);
1917
+
1918
+ // 重组bundle
1745
1919
  const newBundle = [];
1746
1920
  if (product.bundle && Array.isArray(product.bundle)) {
1747
1921
  product.bundle.forEach((bundleItem, bundleIndex) => {
1748
1922
  const bundleItemId = `${product._id}_bundle_${bundleIndex}`;
1749
- const processedBundleItems = processedFlatItemsMap2.get(bundleItemId);
1923
+ const processedBundleItems = processedFlatItemsMap.get(bundleItemId);
1750
1924
  if (!processedBundleItems || processedBundleItems.length === 0) {
1925
+ // 未处理的bundle item,保持原样
1751
1926
  newBundle.push(bundleItem);
1752
1927
  } else {
1753
- processedBundleItems.forEach((item) => {
1754
- const updatedDiscountList2 = (item.discount_list || []).map((discount) => {
1755
- var _a2;
1756
- return {
1757
- ...discount,
1758
- metadata: {
1759
- ...discount.metadata,
1760
- num: item.num,
1761
- custom_product_bundle_map_id: (_a2 = discount == null ? void 0 : discount.metadata) == null ? void 0 : _a2.custom_product_bundle_map_id
1762
- }
1763
- // num: item.num, // 使用拆分后的 num
1764
- };
1765
- });
1928
+ // 🔥 关键:拆分后的bundle item
1929
+ processedBundleItems.forEach(item => {
1930
+ // 🔥 更新 discount_list 中的 num,使其与拆分后的 item.num 一致
1931
+ const updatedDiscountList = (item.discount_list || []).map(discount => ({
1932
+ ...discount,
1933
+ metadata: {
1934
+ ...discount.metadata,
1935
+ num: item.num,
1936
+ custom_product_bundle_map_id: discount?.metadata?.custom_product_bundle_map_id
1937
+ }
1938
+ // num: item.num, // 使用拆分后的 num
1939
+ }));
1766
1940
  newBundle.push({
1767
1941
  ...bundleItem,
1768
1942
  _id: item._id,
1769
1943
  product_id: bundleItem.product_id,
1770
1944
  price: item.price,
1771
1945
  num: item.num,
1772
- discount_list: updatedDiscountList2,
1773
- bundle_selling_price: bundleItem.bundle_selling_price !== void 0 ? item.price : void 0
1946
+ discount_list: updatedDiscountList,
1947
+ bundle_selling_price: bundleItem.bundle_selling_price !== undefined ? item.price : undefined
1774
1948
  });
1775
1949
  });
1776
1950
  }
1777
1951
  });
1778
1952
  }
1953
+
1954
+ // 🔥 重新计算主商品的总价(包含bundle)
1779
1955
  let newTotal = Number(mainProductData.price || 0);
1780
1956
  let newOriginTotal = Number(mainProductData.original_price || mainProductData.price || 0);
1781
- const isManualDiscount = typeof mainProductData.isManualDiscount === "boolean" ? mainProductData.isManualDiscount : mainProductData.total != mainProductData.origin_total && (!((_a = mainProductData.discount_list) == null ? void 0 : _a.length) || ((_c = (_b = mainProductData == null ? void 0 : mainProductData.discount_list) == null ? void 0 : _b.every) == null ? void 0 : _c.call(_b, (item) => item.type === "product")));
1957
+
1958
+ // 判断是否是手动折扣
1959
+ const isManualDiscount = typeof mainProductData.isManualDiscount === 'boolean' ? mainProductData.isManualDiscount : mainProductData.total != mainProductData.origin_total && (!mainProductData.discount_list?.length || mainProductData?.discount_list?.every?.(item => item.type === 'product'));
1960
+
1961
+ // 如果是手动折扣,使用原有的total和origin_total,不重新计算
1782
1962
  if (isManualDiscount) {
1783
1963
  newTotal = mainProductData.total ?? newTotal;
1784
1964
  newOriginTotal = mainProductData.origin_total ?? newOriginTotal;
1785
1965
  } else {
1786
- const mainDiscountList = mainProductData.discount_list.filter(
1787
- (item) => {
1788
- var _a2;
1789
- return !((_a2 = item == null ? void 0 : item.metadata) == null ? void 0 : _a2.custom_product_bundle_map_id);
1790
- }
1791
- );
1966
+ // 不是手动折扣时,才重新计算
1967
+ const mainDiscountList = mainProductData.discount_list.filter(item => !item?.metadata?.custom_product_bundle_map_id);
1968
+
1969
+ // 如果主商品本身有折扣,需要重新计算主商品价格
1792
1970
  if (mainDiscountList && mainDiscountList.length > 0) {
1793
- const allDiscountAmount = (0, import_utils.getDiscountListAmount)(mainDiscountList);
1794
- newTotal = new import_decimal.default(mainProductData.price || 0).minus(allDiscountAmount).toNumber() ?? newTotal;
1971
+ const allDiscountAmount = (0, _utils.getDiscountListAmount)(mainDiscountList);
1972
+ newTotal = new _decimal.default(mainProductData.price || 0).minus(allDiscountAmount).toNumber() ?? newTotal;
1795
1973
  newOriginTotal = mainProductData.origin_total ?? newOriginTotal;
1796
1974
  }
1975
+
1976
+ // 累加bundle的价格(只累加一次)
1797
1977
  if (newBundle.length > 0) {
1798
- newBundle.forEach((item) => {
1799
- var _a2, _b2, _c2;
1978
+ newBundle.forEach(item => {
1800
1979
  newTotal += Number(item.price) * Number(item.num);
1801
- const originalPrice = ((_c2 = (_b2 = (_a2 = item.discount_list) == null ? void 0 : _a2[0]) == null ? void 0 : _b2.discount) == null ? void 0 : _c2.original_amount) || item.price;
1980
+ const originalPrice = item.discount_list?.[0]?.discount?.original_amount || item.price;
1802
1981
  newOriginTotal += Number(originalPrice) * Number(item.num);
1803
1982
  });
1804
1983
  }
1805
1984
  }
1806
- if (product == null ? void 0 : product.options) {
1985
+
1986
+ // 累加options的价格(options不参与折扣,在最终设置total时处理)
1987
+ if (product?.options) {
1807
1988
  newTotal = product.options.reduce((accumulator, currentValue) => {
1808
- const currentPrice = new import_decimal.default(currentValue.price || 0);
1809
- const currentNum = new import_decimal.default(currentValue.num || 0);
1989
+ const currentPrice = new _decimal.default(currentValue.price || 0);
1990
+ const currentNum = new _decimal.default(currentValue.num || 0);
1810
1991
  return accumulator.add(currentPrice.mul(currentNum));
1811
- }, new import_decimal.default(newTotal)).toNumber();
1992
+ }, new _decimal.default(newTotal)).toNumber();
1812
1993
  }
1813
- newTotal = new import_decimal.default(newTotal).toDecimalPlaces(2, import_decimal.default.ROUND_HALF_UP).toNumber();
1814
- newOriginTotal = new import_decimal.default(newOriginTotal).toDecimalPlaces(2, import_decimal.default.ROUND_HALF_UP).toNumber();
1815
- result.push(
1816
- this.hooks.setProduct(mainProduct, {
1817
- ...mainProductData,
1818
- bundle: newBundle,
1819
- total: newTotal,
1820
- origin_total: newOriginTotal
1821
- })
1822
- );
1994
+ newTotal = new _decimal.default(newTotal).toDecimalPlaces(2, _decimal.default.ROUND_HALF_UP).toNumber();
1995
+ newOriginTotal = new _decimal.default(newOriginTotal).toDecimalPlaces(2, _decimal.default.ROUND_HALF_UP).toNumber();
1996
+
1997
+ // 生成最终的主商品
1998
+ result.push(this.hooks.setProduct(mainProduct, {
1999
+ ...mainProductData,
2000
+ bundle: newBundle,
2001
+ total: newTotal,
2002
+ origin_total: newOriginTotal
2003
+ }));
1823
2004
  });
1824
2005
  }
1825
2006
  }
1826
2007
  });
1827
2008
  return result;
1828
2009
  };
1829
- const processedProductList = reconstructProductsWithBundle(
1830
- processedProductsMap,
1831
- processedFlatItemsMap,
1832
- productList
1833
- );
1834
- const updatedDiscountList = addModeDiscount.map((discount) => {
2010
+
2011
+ // 按原始顺序构建处理后的商品列表
2012
+ const processedProductList = reconstructProductsWithBundle(processedProductsMap, processedFlatItemsMap, productList);
2013
+
2014
+ // 按原始顺序更新优惠券列表,标记已使用和可用的优惠券
2015
+ const updatedDiscountList = addModeDiscount.map(discount => {
2016
+ // 获取此优惠券适用的商品列表
1835
2017
  const applicableProducts = discountApplicability.get(discount.id) || [];
2018
+
2019
+ // 获取此优惠券适用的商品详情
1836
2020
  const applicableProductDetails = discountApplicableProducts.get(discount.id) || [];
2021
+
2022
+ // 获取此优惠券实际应用的商品详情
1837
2023
  const appliedProductDetails = appliedDiscountProducts.get(discount.id) || [];
2024
+
2025
+ // 根据是否有适用商品决定isAvailable
1838
2026
  const isAvailable = applicableProducts.length > 0;
1839
- if (typeof discount.isSelected === "boolean" && discount.isSelected === false && discount.isManualSelect) {
2027
+
2028
+ // 如果此优惠券被明确设置为不选中,保持其状态,但正确设置isAvailable
2029
+ if (typeof discount.isSelected === 'boolean' && discount.isSelected === false && discount.isManualSelect) {
1840
2030
  return {
1841
2031
  ...discount,
1842
2032
  isSelected: false,
1843
- isAvailable,
2033
+ isAvailable: isAvailable,
1844
2034
  // 正确设置可用性
1845
2035
  applicableProductIds: applicableProducts,
1846
2036
  // 添加可抵扣的商品详情
1847
- applicableProductDetails,
2037
+ applicableProductDetails: applicableProductDetails,
1848
2038
  // 添加实际抵扣的商品详情(对于未选中的优惠券,为空数组)
1849
2039
  appliedProductDetails: []
1850
2040
  };
@@ -1853,18 +2043,20 @@ var RulesModule = class extends import_BaseModule.BaseModule {
1853
2043
  return {
1854
2044
  ...discount,
1855
2045
  isSelected: true,
1856
- isManualSelect: (options == null ? void 0 : options.scan) ? false : discount.isManualSelect,
2046
+ isManualSelect: options?.scan ? false : discount.isManualSelect,
1857
2047
  // 标记为可用,因为它已被应用
1858
2048
  isAvailable: true,
1859
2049
  // 记录适用的商品IDs
1860
2050
  applicableProductIds: applicableProducts,
1861
2051
  // 添加可抵扣的商品详情
1862
- applicableProductDetails,
2052
+ applicableProductDetails: applicableProductDetails,
1863
2053
  // 添加实际抵扣的商品详情
1864
- appliedProductDetails
2054
+ appliedProductDetails: appliedProductDetails
1865
2055
  };
1866
2056
  }
1867
- let isSelected = typeof discount.isSelected === "boolean" ? discount.isSelected : void 0;
2057
+ let isSelected = typeof discount.isSelected === 'boolean' ? discount.isSelected : undefined;
2058
+
2059
+ // 如果是可用的 并且没有手动标记 则设置为选中
1868
2060
  if (isAvailable && appliedProductDetails.length && !discount.isManualSelect) {
1869
2061
  isSelected = true;
1870
2062
  } else if (!appliedProductDetails.length) {
@@ -1873,24 +2065,24 @@ var RulesModule = class extends import_BaseModule.BaseModule {
1873
2065
  return {
1874
2066
  ...discount,
1875
2067
  // 如果有适用的商品,标记为可用
1876
- isAvailable,
2068
+ isAvailable: isAvailable,
1877
2069
  isSelected,
1878
2070
  // 如果是扫码进来的,要手动设置为手动选择:false
1879
- isManualSelect: (options == null ? void 0 : options.scan) ? false : discount.isManualSelect,
2071
+ isManualSelect: options?.scan ? false : discount.isManualSelect,
1880
2072
  // 记录适用的商品IDs
1881
2073
  applicableProductIds: applicableProducts,
1882
2074
  // 添加可抵扣的商品详情
1883
- applicableProductDetails,
2075
+ applicableProductDetails: applicableProductDetails,
1884
2076
  // 添加实际抵扣的商品详情(对于未使用的优惠券,为空数组)
1885
2077
  appliedProductDetails: []
1886
2078
  };
1887
2079
  });
1888
- console.log(processedProductList, "processedProductList");
1889
2080
  return {
1890
2081
  productList: processedProductList,
1891
2082
  discountList: [...editModeDiscount, ...updatedDiscountList]
1892
2083
  };
1893
2084
  }
2085
+
1894
2086
  /**
1895
2087
  * 检查优惠是否符合 PackageSubItemUsageRules 配置
1896
2088
  * @param discount 优惠券
@@ -1898,92 +2090,200 @@ var RulesModule = class extends import_BaseModule.BaseModule {
1898
2090
  * @returns 是否可用
1899
2091
  */
1900
2092
  checkPackageSubItemUsageRules(discount, flatItem) {
1901
- var _a, _b, _c, _d, _e, _f, _g;
1902
2093
  const limitedData = discount.limited_relation_product_data;
1903
- const usageRules = limitedData == null ? void 0 : limitedData.package_sub_item_usage_rules;
1904
- const rules = ["original_price", ...(usageRules == null ? void 0 : usageRules.rules) || []];
2094
+ const usageRules = limitedData?.package_sub_item_usage_rules;
2095
+ const rules = ['original_price', ...(usageRules?.rules || [])];
2096
+
2097
+ // 如果没有配置 package_sub_item_usage_rules,则默认可用
1905
2098
  if (!usageRules) {
1906
2099
  return true;
1907
2100
  }
1908
2101
  const ruleType = usageRules.type;
1909
- const packageScope = usageRules == null ? void 0 : usageRules.package_scope;
1910
- const { type: scopeType, exclude_bundle_product_ids = [], include_bundle_product_ids = [], filter = 0 } = packageScope || {};
1911
- const isMainProduct = flatItem.type === "main";
1912
- const isBundleItem = flatItem.type === "bundle";
2102
+ // 套餐适用范围配置
2103
+ const packageScope = usageRules?.package_scope;
2104
+ const {
2105
+ type: scopeType,
2106
+ exclude_bundle_product_ids = [],
2107
+ include_bundle_product_ids = [],
2108
+ filter = 0
2109
+ } = packageScope || {};
2110
+ const isMainProduct = flatItem.type === 'main'; // 单独购买
2111
+ const isBundleItem = flatItem.type === 'bundle'; // 套餐中购买
2112
+
2113
+ // 主商品直接可用
1913
2114
  if (isMainProduct) {
1914
2115
  return true;
1915
2116
  }
1916
- if (ruleType === "universal_discount") {
2117
+
2118
+ // universal_discount: 单独购买和套餐中(子商品为原价)均可使用
2119
+ if (ruleType === 'universal_discount') {
1917
2120
  if (isMainProduct) {
1918
- return true;
2121
+ return true; // 单独购买时可用
1919
2122
  }
1920
2123
  if (isBundleItem) {
1921
- const priceType = (_a = flatItem.bundleItem) == null ? void 0 : _a.price_type;
1922
- const priceTypeExt = (_b = flatItem.bundleItem) == null ? void 0 : _b.price_type_ext;
1923
- const mainProductId = ((_d = (_c = flatItem == null ? void 0 : flatItem.originProduct) == null ? void 0 : _c._productOrigin) == null ? void 0 : _d.id) || ((_e = flatItem == null ? void 0 : flatItem.parentProduct) == null ? void 0 : _e.id) || 0;
1924
- const isOriginalPrice = priceType === "markup" && priceTypeExt === "product_price";
1925
- const isMarkupPrice = priceType === "markup" && (priceTypeExt === "" || !priceTypeExt);
2124
+ // 套餐中购买时,判断是否为原价
2125
+ const priceType = flatItem.bundleItem?.price_type;
2126
+ const priceTypeExt = flatItem.bundleItem?.price_type_ext;
2127
+ // 主商品id
2128
+ const mainProductId = flatItem?.originProduct?._productOrigin?.id || flatItem?.parentProduct?.id || 0;
2129
+ // original_price 对应:
2130
+ // 1. price_type: "markup" && price_type_ext: "product_price"
2131
+ // markup_price 对应:
2132
+ // price_type: "markup" && price_type_ext: ""
2133
+ /** 原价 */
2134
+ const isOriginalPrice = priceType === 'markup' && priceTypeExt === 'product_price';
2135
+ /** 加价 */
2136
+ const isMarkupPrice = priceType === 'markup' && (priceTypeExt === '' || !priceTypeExt);
2137
+
2138
+ // 检查 rules
1926
2139
  if (rules.length > 0) {
1927
- const _isOriginalPrice = isOriginalPrice && rules.includes("original_price");
1928
- const _isMarkupPrice = isMarkupPrice && rules.includes("markup_price");
2140
+ const _isOriginalPrice = isOriginalPrice && rules.includes('original_price');
2141
+ const _isMarkupPrice = isMarkupPrice && rules.includes('markup_price');
2142
+ // 价格是否符合规则:原价或加价
1929
2143
  const isPriceValid = _isOriginalPrice || _isMarkupPrice;
2144
+ // 套餐适用范围校验
1930
2145
  let isScopeValid = false;
1931
- if (scopeType === "all_packages" || scopeType === "product_all") {
2146
+
2147
+ // 排除套餐判断, product_all值兼容旧数据
2148
+ if (scopeType === 'all_packages' || scopeType === 'product_all') {
2149
+ // 所有套餐可用
1932
2150
  if (!filter) {
1933
2151
  isScopeValid = true;
1934
2152
  } else {
1935
2153
  isScopeValid = !exclude_bundle_product_ids.includes(Number(mainProductId));
1936
2154
  }
1937
2155
  }
1938
- if (scopeType === "specific_packages" || scopeType === "products") {
2156
+
2157
+ // 包含套餐判断, products值兼容旧数据
2158
+ if (scopeType === 'specific_packages' || scopeType === 'products') {
1939
2159
  isScopeValid = include_bundle_product_ids.includes(Number(mainProductId));
1940
2160
  }
2161
+
2162
+ // 价格和套餐适用范围都符合,则可用
1941
2163
  if (isPriceValid && isScopeValid) {
1942
2164
  return true;
1943
2165
  }
2166
+
2167
+ // 检查原价
2168
+ // if (isOriginalPrice && rules.includes('original_price')) {
2169
+ // return true;
2170
+ // }
2171
+
2172
+ // 检查加价
2173
+ // if (isMarkupPrice && rules.includes('markup_price')) {
2174
+ // return true;
2175
+ // }
2176
+
2177
+ // 如果都不匹配,则不可用
1944
2178
  return false;
1945
2179
  }
2180
+
2181
+ // 原价包括:price_type: "markup" && price_type_ext: "product_price"
1946
2182
  return isOriginalPrice;
1947
2183
  }
1948
2184
  return true;
1949
2185
  }
1950
- if (ruleType === "package_exclusive") {
2186
+
2187
+ // package_exclusive: 仅在套餐中(子商品为原价)时可使用
2188
+ if (ruleType === 'package_exclusive') {
1951
2189
  if (isMainProduct) {
1952
- return false;
2190
+ return false; // 单独购买时不可用
1953
2191
  }
1954
2192
  if (isBundleItem) {
1955
- const priceType = (_f = flatItem.bundleItem) == null ? void 0 : _f.price_type;
1956
- const priceTypeExt = (_g = flatItem.bundleItem) == null ? void 0 : _g.price_type_ext;
1957
- const isOriginalPrice = priceType === "markup" && priceTypeExt === "product_price";
1958
- const isMarkupPrice = priceType === "markup" && (priceTypeExt === "" || !priceTypeExt);
2193
+ // 套餐中购买时,判断是否为原价
2194
+ const priceType = flatItem.bundleItem?.price_type;
2195
+ const priceTypeExt = flatItem.bundleItem?.price_type_ext;
2196
+
2197
+ // original_price 对应:
2198
+ // 1. price_type: "markup" && price_type_ext: "product_price"
2199
+ // markup_price 对应:
2200
+ // price_type: "markup" && price_type_ext: ""
2201
+ /** 原价 */
2202
+ const isOriginalPrice = priceType === 'markup' && priceTypeExt === 'product_price';
2203
+ /** 加价 */
2204
+ const isMarkupPrice = priceType === 'markup' && (priceTypeExt === '' || !priceTypeExt);
2205
+
2206
+ // 检查 rules
1959
2207
  if (rules.length > 0) {
1960
- if (isOriginalPrice && rules.includes("original_price")) {
2208
+ // 检查原价
2209
+ if (isOriginalPrice && rules.includes('original_price')) {
1961
2210
  return true;
1962
2211
  }
1963
- if (isMarkupPrice && rules.includes("markup_price")) {
2212
+
2213
+ // 检查加价
2214
+ if (isMarkupPrice && rules.includes('markup_price')) {
1964
2215
  return true;
1965
2216
  }
2217
+
2218
+ // 如果都不匹配,则不可用
1966
2219
  return false;
1967
2220
  }
2221
+
2222
+ // 原价包括:price_type: "markup" && price_type_ext: "product_price"
1968
2223
  return isOriginalPrice;
1969
2224
  }
1970
2225
  return false;
1971
2226
  }
1972
- if (ruleType === "single_item_promo") {
1973
- return isMainProduct;
2227
+
2228
+ // single_item_promo: 仅在单独购买时可使用
2229
+ if (ruleType === 'single_item_promo') {
2230
+ return isMainProduct; // 只有单独购买时可用
2231
+ }
2232
+
2233
+ /*
2234
+ // custom_usage_rules: 根据自定义规则判断
2235
+ if (ruleType === 'custom_usage_rules') {
2236
+ const customRules = usageRules.custom_usage_rules;
2237
+ if (!customRules) {
2238
+ return true; // 如果没有自定义规则,默认可用
2239
+ }
2240
+ const { types, package_sub_item_rules } = customRules;
2241
+ // 判断商品类型是否在允许的类型中
2242
+ if (isMainProduct) {
2243
+ // 主商品对应 standalone_product
2244
+ if (!types.includes('standalone_product')) {
2245
+ return false;
2246
+ }
2247
+ return true; // 主商品不需要判断 package_sub_item_rules
2248
+ }
2249
+ if (isBundleItem) {
2250
+ // bundle子商品对应 package_sub_item
2251
+ if (!types.includes('package_sub_item')) {
2252
+ return false;
2253
+ }
2254
+ // 如果包含 package_sub_item,还需要判断 price_type
2255
+ const priceType = flatItem.bundleItem?.price_type;
2256
+ const priceTypeExt = flatItem.bundleItem?.price_type_ext;
2257
+ // 检查 package_sub_item_rules
2258
+ if (package_sub_item_rules && package_sub_item_rules.length > 0) {
2259
+ // original_price 对应:
2260
+ // 1. price_type: "markup" && price_type_ext: "product_price"
2261
+ // markup_price 对应:
2262
+ // price_type: "markup" && price_type_ext: ""
2263
+ const isOriginalPrice = (priceType === 'markup' && priceTypeExt === 'product_price');
2264
+ const isMarkupPrice = priceType === 'markup' && (priceTypeExt === '' || !priceTypeExt);
2265
+ if (isOriginalPrice && package_sub_item_rules.includes('original_price')) {
2266
+ return true;
2267
+ }
2268
+ if (isMarkupPrice && package_sub_item_rules.includes('markup_price')) {
2269
+ return true;
2270
+ }
2271
+ // 如果都不匹配,则不可用
2272
+ return false;
2273
+ }
2274
+ return true; // 如果没有 package_sub_item_rules 配置,默认可用
2275
+ }
2276
+ return true;
1974
2277
  }
2278
+ * */
2279
+
2280
+ // 默认可用
1975
2281
  return true;
1976
2282
  }
1977
2283
  async destroy() {
1978
2284
  this.core.effects.offByModuleDestroy(this.name);
1979
2285
  await this.core.effects.emit(`${this.name}:onDestroy`, {});
1980
- console.log("[Rules] 已销毁");
1981
2286
  }
1982
- async clear() {
1983
- console.log("[Rules] clear");
1984
- }
1985
- };
1986
- // Annotate the CommonJS export names for ESM import in node:
1987
- 0 && (module.exports = {
1988
- RulesModule
1989
- });
2287
+ async clear() {}
2288
+ }
2289
+ exports.RulesModule = RulesModule;