@pisell/pisellos 2.1.105 → 2.1.107

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 (98) hide show
  1. package/dist/index.d.ts +1 -0
  2. package/dist/index.js +1 -0
  3. package/dist/model/index.d.ts +1 -0
  4. package/dist/model/index.js +1 -0
  5. package/dist/model/strategy/adapter/index.d.ts +3 -0
  6. package/dist/model/strategy/adapter/index.js +4 -0
  7. package/dist/model/strategy/adapter/type.d.ts +28 -0
  8. package/dist/model/strategy/adapter/type.js +1 -0
  9. package/dist/model/strategy/adapter/walletPass/evaluator.d.ts +84 -0
  10. package/dist/model/strategy/adapter/walletPass/evaluator.js +498 -0
  11. package/dist/model/strategy/adapter/walletPass/example.d.ts +4 -0
  12. package/dist/model/strategy/adapter/walletPass/example.js +258 -0
  13. package/dist/model/strategy/adapter/walletPass/index.d.ts +32 -0
  14. package/dist/model/strategy/adapter/walletPass/index.js +182 -0
  15. package/dist/model/strategy/adapter/walletPass/locales.d.ts +1 -0
  16. package/dist/model/strategy/adapter/walletPass/locales.js +26 -0
  17. package/dist/model/strategy/adapter/walletPass/type.d.ts +169 -0
  18. package/dist/model/strategy/adapter/walletPass/type.js +1 -0
  19. package/dist/model/strategy/adapter/walletPass/utils.d.ts +50 -0
  20. package/dist/model/strategy/adapter/walletPass/utils.js +1077 -0
  21. package/dist/model/strategy/index.d.ts +94 -0
  22. package/dist/model/strategy/index.js +551 -0
  23. package/dist/model/strategy/strategy-example.d.ts +5 -0
  24. package/dist/model/strategy/strategy-example.js +331 -0
  25. package/dist/model/strategy/type.d.ts +228 -0
  26. package/dist/model/strategy/type.js +94 -0
  27. package/dist/modules/Cart/types.d.ts +2 -0
  28. package/dist/modules/Cart/utils/cartProduct.js +11 -0
  29. package/dist/modules/Discount/types.d.ts +16 -0
  30. package/dist/modules/Payment/index.d.ts +2 -1
  31. package/dist/modules/Payment/index.js +10 -7
  32. package/dist/modules/Payment/utils.js +3 -0
  33. package/dist/modules/Payment/walletpass.d.ts +23 -0
  34. package/dist/modules/Payment/walletpass.js +191 -95
  35. package/dist/modules/Rules/index.d.ts +9 -5
  36. package/dist/modules/Rules/index.js +538 -123
  37. package/dist/modules/Rules/types.d.ts +1 -0
  38. package/dist/modules/Summary/types.d.ts +2 -0
  39. package/dist/modules/Summary/utils.d.ts +6 -0
  40. package/dist/modules/Summary/utils.js +21 -0
  41. package/dist/plugins/window.d.ts +2 -0
  42. package/dist/solution/BookingByStep/index.d.ts +1 -0
  43. package/dist/solution/BookingTicket/index.d.ts +1 -1
  44. package/dist/solution/Checkout/index.js +2 -0
  45. package/dist/solution/ShopDiscount/index.d.ts +1 -0
  46. package/dist/solution/ShopDiscount/index.js +23 -7
  47. package/dist/solution/ShopDiscount/types.d.ts +1 -0
  48. package/dist/solution/ShopDiscount/types.js +2 -1
  49. package/dist/solution/ShopDiscount/utils.js +26 -12
  50. package/lib/index.d.ts +1 -0
  51. package/lib/index.js +3 -1
  52. package/lib/model/index.d.ts +1 -0
  53. package/lib/model/index.js +23 -0
  54. package/lib/model/strategy/adapter/index.d.ts +3 -0
  55. package/lib/model/strategy/adapter/index.js +45 -0
  56. package/lib/model/strategy/adapter/type.d.ts +28 -0
  57. package/lib/model/strategy/adapter/type.js +17 -0
  58. package/lib/model/strategy/adapter/walletPass/evaluator.d.ts +84 -0
  59. package/lib/model/strategy/adapter/walletPass/evaluator.js +422 -0
  60. package/lib/model/strategy/adapter/walletPass/example.d.ts +4 -0
  61. package/lib/model/strategy/adapter/walletPass/example.js +207 -0
  62. package/lib/model/strategy/adapter/walletPass/index.d.ts +32 -0
  63. package/lib/model/strategy/adapter/walletPass/index.js +142 -0
  64. package/lib/model/strategy/adapter/walletPass/locales.d.ts +1 -0
  65. package/lib/model/strategy/adapter/walletPass/locales.js +54 -0
  66. package/lib/model/strategy/adapter/walletPass/type.d.ts +169 -0
  67. package/lib/model/strategy/adapter/walletPass/type.js +17 -0
  68. package/lib/model/strategy/adapter/walletPass/utils.d.ts +50 -0
  69. package/lib/model/strategy/adapter/walletPass/utils.js +727 -0
  70. package/lib/model/strategy/index.d.ts +94 -0
  71. package/lib/model/strategy/index.js +413 -0
  72. package/lib/model/strategy/strategy-example.d.ts +5 -0
  73. package/lib/model/strategy/strategy-example.js +318 -0
  74. package/lib/model/strategy/type.d.ts +228 -0
  75. package/lib/model/strategy/type.js +44 -0
  76. package/lib/modules/Cart/types.d.ts +2 -0
  77. package/lib/modules/Cart/utils/cartProduct.js +9 -0
  78. package/lib/modules/Discount/types.d.ts +16 -0
  79. package/lib/modules/Payment/index.d.ts +2 -1
  80. package/lib/modules/Payment/index.js +1 -0
  81. package/lib/modules/Payment/utils.js +3 -0
  82. package/lib/modules/Payment/walletpass.d.ts +23 -0
  83. package/lib/modules/Payment/walletpass.js +94 -17
  84. package/lib/modules/Rules/index.d.ts +9 -5
  85. package/lib/modules/Rules/index.js +666 -258
  86. package/lib/modules/Rules/types.d.ts +1 -0
  87. package/lib/modules/Summary/types.d.ts +2 -0
  88. package/lib/modules/Summary/utils.d.ts +6 -0
  89. package/lib/modules/Summary/utils.js +15 -0
  90. package/lib/plugins/window.d.ts +2 -0
  91. package/lib/solution/BookingByStep/index.d.ts +1 -0
  92. package/lib/solution/BookingTicket/index.d.ts +1 -1
  93. package/lib/solution/Checkout/index.js +2 -0
  94. package/lib/solution/ShopDiscount/index.d.ts +1 -0
  95. package/lib/solution/ShopDiscount/index.js +22 -8
  96. package/lib/solution/ShopDiscount/types.d.ts +1 -0
  97. package/lib/solution/ShopDiscount/utils.js +10 -6
  98. package/package.json +1 -1
@@ -0,0 +1,727 @@
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/model/strategy/adapter/walletPass/utils.ts
30
+ var utils_exports = {};
31
+ __export(utils_exports, {
32
+ getApplicableProductIds: () => getApplicableProductIds,
33
+ getBundleItemIsDiscountPrice: () => getBundleItemIsDiscountPrice,
34
+ getBundleItemIsMarkupOrDiscountPrice: () => getBundleItemIsMarkupOrDiscountPrice,
35
+ getBundleItemIsMarkupPrice: () => getBundleItemIsMarkupPrice,
36
+ getBundleItemIsOriginalPrice: () => getBundleItemIsOriginalPrice,
37
+ getBundleItemPrice: () => getBundleItemPrice,
38
+ getMainProductPrice: () => getMainProductPrice,
39
+ getProductQuantity: () => getProductQuantity,
40
+ processVouchers: () => processVouchers,
41
+ recalculateVouchers: () => recalculateVouchers
42
+ });
43
+ module.exports = __toCommonJS(utils_exports);
44
+ var import_decimal = __toESM(require("decimal.js"));
45
+ var getRecommendedAmount = (voucher) => {
46
+ console.log("voucher312", voucher);
47
+ const { config, recommended_usage_amount, recommended_pure_product_usage_amount } = voucher;
48
+ const deductTaxAndFee = (config == null ? void 0 : config.deductTaxAndFee) ?? true;
49
+ return deductTaxAndFee ? recommended_usage_amount : recommended_pure_product_usage_amount ?? recommended_usage_amount;
50
+ };
51
+ var getApplicableProductIds = (voucher) => {
52
+ const { available_product_type, available_product_ids } = voucher;
53
+ const productType = available_product_type || "product_all";
54
+ if (productType === "product_all") {
55
+ return null;
56
+ }
57
+ if (productType === "product_collection" || productType === "products") {
58
+ return available_product_ids || [];
59
+ }
60
+ return [];
61
+ };
62
+ var getApplicableProductsAmount = (voucher, productsData) => {
63
+ const applicableProductIds = getApplicableProductIds(voucher);
64
+ const { config } = voucher;
65
+ const deductTaxAndFee = (config == null ? void 0 : config.deductTaxAndFee) ?? true;
66
+ const amountField = deductTaxAndFee ? "remainingAmountWithTax" : "remainingAmountPure";
67
+ if (applicableProductIds === null) {
68
+ return productsData.filter((p) => p[amountField].greaterThan(0)).reduce((sum, p) => sum.plus(p[amountField]), new import_decimal.default(0));
69
+ }
70
+ if (applicableProductIds.length === 0) {
71
+ return new import_decimal.default(0);
72
+ }
73
+ return productsData.filter((p) => applicableProductIds.includes(p.product_id) && p[amountField].greaterThan(0)).reduce((sum, p) => sum.plus(p[amountField]), new import_decimal.default(0));
74
+ };
75
+ var getApplicableProducts = (voucher, productsData) => {
76
+ const applicableProductIds = getApplicableProductIds(voucher);
77
+ if (applicableProductIds === null) {
78
+ return productsData;
79
+ }
80
+ if (applicableProductIds.length === 0) {
81
+ return [];
82
+ }
83
+ return productsData.filter((p) => applicableProductIds.includes(p.product_id));
84
+ };
85
+ var getTotalQuantityByProductId = (allProducts, productId) => {
86
+ return allProducts.filter((p) => p.product_id === productId).reduce((sum, p) => sum + getProductQuantity(p), 0);
87
+ };
88
+ function processVouchers(applicableVouchers, orderTotalAmount, products) {
89
+ console.log(products, "products123");
90
+ const productsCopy = expandProductsWithBundleItems(products, true);
91
+ let remainingOrderAmount = new import_decimal.default(orderTotalAmount);
92
+ const getItemPassUsage = (usageMap, walletPassProductId, orderItemProductId) => {
93
+ var _a;
94
+ return ((_a = usageMap.get(walletPassProductId)) == null ? void 0 : _a.get(orderItemProductId)) || 0;
95
+ };
96
+ const incrementItemPassUsage = (usageMap, walletPassProductId, orderItemProductId, count = 1) => {
97
+ if (!usageMap.has(walletPassProductId)) {
98
+ usageMap.set(walletPassProductId, /* @__PURE__ */ new Map());
99
+ }
100
+ const innerMap = usageMap.get(walletPassProductId);
101
+ innerMap.set(orderItemProductId, (innerMap.get(orderItemProductId) || 0) + count);
102
+ };
103
+ const filterByMaxPassesPerItem = (products2, allProducts, usageMap, walletPassProductId, maxPassesPerItem) => {
104
+ if (maxPassesPerItem <= 0)
105
+ return products2;
106
+ return products2.filter(
107
+ (p) => getItemPassUsage(usageMap, walletPassProductId, p.product_id) < maxPassesPerItem * getTotalQuantityByProductId(allProducts, p.product_id)
108
+ );
109
+ };
110
+ const calculateAvailableMaxAmount = (voucher, productsData, itemPassUsage) => {
111
+ const { config } = voucher;
112
+ const { maxDeductionAmount = 0, allowCrossProduct = true, applicableProductLimit = 0, deductTaxAndFee = true, maxPassesPerItem = 0 } = config ?? {};
113
+ const recommendedAmount = getRecommendedAmount(voucher);
114
+ const baseAmount = import_decimal.default.min(
115
+ new import_decimal.default(recommendedAmount),
116
+ new import_decimal.default(maxDeductionAmount)
117
+ );
118
+ const unitPriceField = deductTaxAndFee ? "unitPriceWithTax" : "unitPricePure";
119
+ const amountField = deductTaxAndFee ? "remainingAmountWithTax" : "remainingAmountPure";
120
+ let applicableProducts = getApplicableProducts(voucher, productsData).filter((p) => p[amountField].greaterThan(0));
121
+ if (itemPassUsage) {
122
+ applicableProducts = filterByMaxPassesPerItem(applicableProducts, productsData, itemPassUsage, voucher.product_id, maxPassesPerItem);
123
+ }
124
+ if (applicableProducts.length === 0) {
125
+ return new import_decimal.default(0);
126
+ }
127
+ let finalApplicableAmount = new import_decimal.default(0);
128
+ if (allowCrossProduct) {
129
+ if (applicableProductLimit > 0) {
130
+ const sortedProducts = [...applicableProducts].sort((a, b) => a[amountField].comparedTo(b[amountField]) > 0 ? -1 : 1);
131
+ let remainingLimit = applicableProductLimit;
132
+ for (const product of sortedProducts) {
133
+ if (remainingLimit <= 0)
134
+ break;
135
+ const currentAvailableQty = Math.ceil(product[amountField].dividedBy(product[unitPriceField]).toNumber());
136
+ const deductQty = Math.min(currentAvailableQty, remainingLimit);
137
+ const deductAmount = import_decimal.default.min(
138
+ product[unitPriceField].times(deductQty),
139
+ product[amountField]
140
+ );
141
+ finalApplicableAmount = finalApplicableAmount.plus(deductAmount);
142
+ remainingLimit -= deductQty;
143
+ }
144
+ } else {
145
+ finalApplicableAmount = applicableProducts.reduce(
146
+ (sum, p) => sum.plus(p[amountField]),
147
+ new import_decimal.default(0)
148
+ );
149
+ }
150
+ } else {
151
+ const maxProduct = applicableProducts.reduce(
152
+ (max, p) => p[unitPriceField].greaterThan(max[unitPriceField]) ? p : max
153
+ );
154
+ if (maxPassesPerItem > 0) {
155
+ finalApplicableAmount = import_decimal.default.min(
156
+ maxProduct[unitPriceField].times(maxPassesPerItem),
157
+ maxProduct[amountField]
158
+ );
159
+ } else {
160
+ finalApplicableAmount = maxProduct[amountField];
161
+ }
162
+ }
163
+ return import_decimal.default.min(baseAmount, finalApplicableAmount, remainingOrderAmount);
164
+ };
165
+ const isVoucherAvailable = (voucher, productsData, usedVoucherCounts2, itemPassUsage) => {
166
+ const { config, id, product_id } = voucher;
167
+ const recommendedAmount = getRecommendedAmount(voucher);
168
+ if (recommendedAmount <= 0) {
169
+ return { isAvailable: false, reasonCode: "not_meet_the_required_conditions" };
170
+ }
171
+ if (remainingOrderAmount.lessThanOrEqualTo(0)) {
172
+ return { isAvailable: false, reasonCode: "exceeds_the_maximum_deduction_limit" };
173
+ }
174
+ const applicableAmount = getApplicableProductsAmount(voucher, productsData);
175
+ if (applicableAmount.lessThanOrEqualTo(0)) {
176
+ return { isAvailable: false, reasonCode: "not_meet_the_required_conditions" };
177
+ }
178
+ if (((config == null ? void 0 : config.maxUsagePerOrder) || 0) > 0) {
179
+ const usedCount = usedVoucherCounts2.get(product_id) || 0;
180
+ if (usedCount >= ((config == null ? void 0 : config.maxUsagePerOrder) || 0)) {
181
+ return { isAvailable: false, reasonCode: "usage_limit_reached" };
182
+ }
183
+ }
184
+ const maxPassesPerItem = (config == null ? void 0 : config.maxPassesPerItem) || 0;
185
+ if (maxPassesPerItem > 0 && itemPassUsage) {
186
+ const deductTaxAndFee = (config == null ? void 0 : config.deductTaxAndFee) ?? true;
187
+ const amountField = deductTaxAndFee ? "remainingAmountWithTax" : "remainingAmountPure";
188
+ const availableAfterPassLimit = getApplicableProducts(voucher, productsData).filter((p) => p[amountField].greaterThan(0)).filter((p) => getItemPassUsage(itemPassUsage, product_id, p.product_id) < maxPassesPerItem * getTotalQuantityByProductId(productsData, p.product_id));
189
+ if (availableAfterPassLimit.length === 0) {
190
+ return { isAvailable: false, reasonCode: "max_passes_per_item_reached" };
191
+ }
192
+ }
193
+ return { isAvailable: true };
194
+ };
195
+ const usedVoucherCountsForAll = /* @__PURE__ */ new Map();
196
+ const allVouchersWithStatus = applicableVouchers.map((voucher) => {
197
+ const _available_max_amount = calculateAvailableMaxAmount(
198
+ voucher,
199
+ productsCopy
200
+ );
201
+ const availabilityResult = isVoucherAvailable(
202
+ voucher,
203
+ productsCopy,
204
+ usedVoucherCountsForAll
205
+ );
206
+ const _unified_available_status = availabilityResult.isAvailable ? 1 : 0;
207
+ return {
208
+ ...voucher,
209
+ _available_max_amount: _available_max_amount.toNumber(),
210
+ // 转换为数字
211
+ _unified_available_status,
212
+ ...availabilityResult.reasonCode && { reasonCode: availabilityResult.reasonCode }
213
+ };
214
+ });
215
+ const recommendedVouchers = [];
216
+ const usedVoucherCounts = /* @__PURE__ */ new Map();
217
+ const productsForRecommendation = expandProductsWithBundleItems(products, true);
218
+ remainingOrderAmount = new import_decimal.default(orderTotalAmount);
219
+ const itemPassUsageMap = /* @__PURE__ */ new Map();
220
+ const applyVoucher = (voucher) => {
221
+ const availabilityCheck = isVoucherAvailable(
222
+ voucher,
223
+ productsForRecommendation,
224
+ usedVoucherCounts,
225
+ itemPassUsageMap
226
+ );
227
+ if (!availabilityCheck.isAvailable) {
228
+ return false;
229
+ }
230
+ const { config, id, product_id } = voucher;
231
+ const { maxDeductionAmount = 0, allowCrossProduct = true, applicableProductLimit = 0, deductTaxAndFee = true, maxPassesPerItem = 0 } = config ?? {};
232
+ const unitPriceField = deductTaxAndFee ? "unitPriceWithTax" : "unitPricePure";
233
+ const amountField = deductTaxAndFee ? "remainingAmountWithTax" : "remainingAmountPure";
234
+ let applicableProducts = getApplicableProducts(
235
+ voucher,
236
+ productsForRecommendation
237
+ ).filter((p) => p[amountField].greaterThan(0));
238
+ applicableProducts = filterByMaxPassesPerItem(applicableProducts, productsForRecommendation, itemPassUsageMap, product_id, maxPassesPerItem);
239
+ if (applicableProducts.length === 0)
240
+ return false;
241
+ const usageAmount = typeof voucher.edit_current_amount === "number" ? voucher.edit_current_amount : getRecommendedAmount(voucher);
242
+ const baseAmount = import_decimal.default.min(
243
+ new import_decimal.default(usageAmount),
244
+ new import_decimal.default(maxDeductionAmount)
245
+ );
246
+ let calculatedAvailableMaxAmount = new import_decimal.default(0);
247
+ if (allowCrossProduct) {
248
+ if (applicableProductLimit > 0) {
249
+ const sortedProducts = [...applicableProducts].sort((a, b) => a[amountField].comparedTo(b[amountField]) > 0 ? -1 : 1);
250
+ let remainingLimit = applicableProductLimit;
251
+ for (const product of sortedProducts) {
252
+ if (remainingLimit <= 0)
253
+ break;
254
+ const currentAvailableQty = Math.ceil(product[amountField].dividedBy(product[unitPriceField]).toNumber());
255
+ const deductQty = Math.min(currentAvailableQty, remainingLimit);
256
+ const deductAmount = import_decimal.default.min(
257
+ product[unitPriceField].times(deductQty),
258
+ product[amountField]
259
+ );
260
+ calculatedAvailableMaxAmount = calculatedAvailableMaxAmount.plus(deductAmount);
261
+ remainingLimit -= deductQty;
262
+ }
263
+ } else {
264
+ calculatedAvailableMaxAmount = applicableProducts.reduce(
265
+ (sum, p) => sum.plus(p[amountField]),
266
+ new import_decimal.default(0)
267
+ );
268
+ }
269
+ } else {
270
+ const maxProduct = applicableProducts.reduce(
271
+ (max, p) => p[unitPriceField].greaterThan(max[unitPriceField]) ? p : max
272
+ );
273
+ if (maxPassesPerItem > 0) {
274
+ calculatedAvailableMaxAmount = import_decimal.default.min(
275
+ maxProduct[unitPriceField].times(maxPassesPerItem),
276
+ maxProduct[amountField]
277
+ );
278
+ } else {
279
+ calculatedAvailableMaxAmount = maxProduct[amountField];
280
+ }
281
+ }
282
+ const availableMaxAmount = import_decimal.default.min(
283
+ baseAmount,
284
+ calculatedAvailableMaxAmount,
285
+ remainingOrderAmount
286
+ );
287
+ const maxDeduction = import_decimal.default.min(
288
+ new import_decimal.default(usageAmount),
289
+ new import_decimal.default(maxDeductionAmount)
290
+ );
291
+ let deductionLeft = maxDeduction;
292
+ const deductionDetails = [];
293
+ if (allowCrossProduct) {
294
+ const sortedProducts = [...applicableProducts].sort(
295
+ (a, b) => a[amountField].comparedTo(b[amountField]) > 0 ? -1 : 1
296
+ );
297
+ let remainingLimit = applicableProductLimit > 0 ? applicableProductLimit : Infinity;
298
+ for (const product of sortedProducts) {
299
+ if (deductionLeft.lessThanOrEqualTo(0) || remainingLimit <= 0)
300
+ break;
301
+ const currentAvailableQty = Math.ceil(product[amountField].dividedBy(product[unitPriceField]).toNumber());
302
+ const availableQty = Math.min(currentAvailableQty, remainingLimit);
303
+ const maxDeductForProduct = import_decimal.default.min(
304
+ product[unitPriceField].times(availableQty),
305
+ product[amountField]
306
+ );
307
+ const actualDeductAmount = import_decimal.default.min(deductionLeft, maxDeductForProduct);
308
+ const actualDeductQty = Math.ceil(actualDeductAmount.dividedBy(product[unitPriceField]).toNumber());
309
+ product[amountField] = product[amountField].minus(actualDeductAmount);
310
+ deductionLeft = deductionLeft.minus(actualDeductAmount);
311
+ remainingLimit -= actualDeductQty;
312
+ deductionDetails.push({
313
+ product_id: product.product_id,
314
+ parent_product_id: product.parent_product_id || null,
315
+ is_bundle_item: product.is_bundle_item || false,
316
+ deductAmount: actualDeductAmount.toNumber(),
317
+ // 转换为数字
318
+ deductQuantity: actualDeductQty
319
+ // 抵扣涉及的数量(用于记录)
320
+ });
321
+ }
322
+ } else {
323
+ const targetProduct = applicableProducts.reduce(
324
+ (max, p) => p[unitPriceField].greaterThan(max[unitPriceField]) ? p : max
325
+ );
326
+ let maxDeductForProduct = targetProduct[amountField];
327
+ if (maxPassesPerItem > 0) {
328
+ maxDeductForProduct = import_decimal.default.min(
329
+ targetProduct[unitPriceField].times(maxPassesPerItem),
330
+ targetProduct[amountField]
331
+ );
332
+ }
333
+ const actualDeductAmount = import_decimal.default.min(deductionLeft, maxDeductForProduct);
334
+ const actualDeductQty = Math.ceil(actualDeductAmount.dividedBy(targetProduct[unitPriceField]).toNumber());
335
+ targetProduct[amountField] = targetProduct[amountField].minus(actualDeductAmount);
336
+ deductionLeft = deductionLeft.minus(actualDeductAmount);
337
+ deductionDetails.push({
338
+ product_id: targetProduct.product_id,
339
+ parent_product_id: targetProduct.parent_product_id || null,
340
+ is_bundle_item: targetProduct.is_bundle_item || false,
341
+ deductAmount: actualDeductAmount.toNumber(),
342
+ deductQuantity: actualDeductQty
343
+ });
344
+ }
345
+ const totalDeducted = maxDeduction.minus(deductionLeft);
346
+ if (totalDeducted.greaterThan(0)) {
347
+ remainingOrderAmount = remainingOrderAmount.minus(totalDeducted);
348
+ usedVoucherCounts.set(product_id, (usedVoucherCounts.get(product_id) || 0) + 1);
349
+ deductionDetails.forEach((detail) => {
350
+ incrementItemPassUsage(itemPassUsageMap, product_id, detail.product_id, detail.deductQuantity);
351
+ });
352
+ recommendedVouchers.push({
353
+ ...voucher,
354
+ actualDeduction: totalDeducted.toNumber(),
355
+ // 转换为数字
356
+ deductionDetails,
357
+ _available_max_amount: availableMaxAmount.toNumber(),
358
+ // 转换为数字
359
+ _unified_available_status: 1
360
+ });
361
+ return true;
362
+ }
363
+ return false;
364
+ };
365
+ applicableVouchers.forEach((voucher) => {
366
+ applyVoucher(voucher);
367
+ });
368
+ const recommendedMap = /* @__PURE__ */ new Map();
369
+ recommendedVouchers.forEach((v) => {
370
+ recommendedMap.set(v.id, v);
371
+ });
372
+ const allWithEnhancedData = allVouchersWithStatus.map((voucher) => {
373
+ if (recommendedMap.has(voucher.id)) {
374
+ return recommendedMap.get(voucher.id);
375
+ } else {
376
+ return {
377
+ ...voucher,
378
+ actualDeduction: 0,
379
+ deductionDetails: []
380
+ // 保留 reasonCode(如果不可用的话)
381
+ };
382
+ }
383
+ });
384
+ return {
385
+ recommended: recommendedVouchers,
386
+ transformList: allWithEnhancedData
387
+ };
388
+ }
389
+ function recalculateVouchers(allVouchers, selectedVouchers, orderTotalAmount, products) {
390
+ const productsForCalc = expandProductsWithBundleItems(products, true);
391
+ let remainingOrderAmount = new import_decimal.default(orderTotalAmount);
392
+ const selectedWithDetails = [];
393
+ const itemPassUsageMap = /* @__PURE__ */ new Map();
394
+ const getItemPassUsage = (walletPassProductId, orderItemProductId) => {
395
+ var _a;
396
+ return ((_a = itemPassUsageMap.get(walletPassProductId)) == null ? void 0 : _a.get(orderItemProductId)) || 0;
397
+ };
398
+ const incrementItemPassUsage = (walletPassProductId, orderItemProductId, count = 1) => {
399
+ if (!itemPassUsageMap.has(walletPassProductId)) {
400
+ itemPassUsageMap.set(walletPassProductId, /* @__PURE__ */ new Map());
401
+ }
402
+ const innerMap = itemPassUsageMap.get(walletPassProductId);
403
+ innerMap.set(orderItemProductId, (innerMap.get(orderItemProductId) || 0) + count);
404
+ };
405
+ const filterByMaxPassesPerItem = (products2, walletPassProductId, maxPassesPerItem) => {
406
+ if (maxPassesPerItem <= 0)
407
+ return products2;
408
+ return products2.filter(
409
+ (p) => getItemPassUsage(walletPassProductId, p.product_id) < maxPassesPerItem * getTotalQuantityByProductId(productsForCalc, p.product_id)
410
+ );
411
+ };
412
+ selectedVouchers.forEach((selectedVoucher) => {
413
+ const { config, id } = selectedVoucher;
414
+ const { maxDeductionAmount, allowCrossProduct, applicableProductLimit, deductTaxAndFee = true, maxPassesPerItem = 0 } = config;
415
+ const unitPriceField = deductTaxAndFee ? "unitPriceWithTax" : "unitPricePure";
416
+ const amountField = deductTaxAndFee ? "remainingAmountWithTax" : "remainingAmountPure";
417
+ let applicableProducts = getApplicableProducts(
418
+ selectedVoucher,
419
+ productsForCalc
420
+ ).filter((p) => p[amountField].greaterThan(0));
421
+ applicableProducts = filterByMaxPassesPerItem(applicableProducts, selectedVoucher.product_id, maxPassesPerItem);
422
+ if (applicableProducts.length === 0) {
423
+ selectedWithDetails.push({
424
+ ...selectedVoucher,
425
+ actualDeduction: 0,
426
+ deductionDetails: [],
427
+ _available_max_amount: 0,
428
+ _unified_available_status: 0,
429
+ reasonCode: "not_meet_the_required_conditions"
430
+ });
431
+ return;
432
+ }
433
+ const usageAmount = typeof selectedVoucher.edit_current_amount === "number" ? selectedVoucher.edit_current_amount : getRecommendedAmount(selectedVoucher);
434
+ const maxDeduction = import_decimal.default.min(
435
+ new import_decimal.default(usageAmount),
436
+ new import_decimal.default(maxDeductionAmount),
437
+ remainingOrderAmount
438
+ );
439
+ let deductionLeft = maxDeduction;
440
+ const deductionDetails = [];
441
+ if (allowCrossProduct) {
442
+ const sortedProducts = [...applicableProducts].sort(
443
+ (a, b) => a[amountField].comparedTo(b[amountField]) > 0 ? -1 : 1
444
+ );
445
+ let remainingLimit = applicableProductLimit > 0 ? applicableProductLimit : Infinity;
446
+ for (const product of sortedProducts) {
447
+ if (deductionLeft.lessThanOrEqualTo(0) || remainingLimit <= 0)
448
+ break;
449
+ const currentAvailableQty = Math.ceil(product[amountField].dividedBy(product[unitPriceField]).toNumber());
450
+ const availableQty = Math.min(currentAvailableQty, remainingLimit);
451
+ const maxDeductForProduct = import_decimal.default.min(
452
+ product[unitPriceField].times(availableQty),
453
+ product[amountField]
454
+ );
455
+ const actualDeductAmount = import_decimal.default.min(deductionLeft, maxDeductForProduct);
456
+ const actualDeductQty = Math.ceil(actualDeductAmount.dividedBy(product[unitPriceField]).toNumber());
457
+ product[amountField] = product[amountField].minus(actualDeductAmount);
458
+ deductionLeft = deductionLeft.minus(actualDeductAmount);
459
+ remainingLimit -= actualDeductQty;
460
+ deductionDetails.push({
461
+ product_id: product.product_id,
462
+ parent_product_id: product.parent_product_id || null,
463
+ is_bundle_item: product.is_bundle_item || false,
464
+ deductAmount: actualDeductAmount.toNumber(),
465
+ // 转换为数字
466
+ deductQuantity: actualDeductQty
467
+ // 抵扣涉及的数量(用于记录)
468
+ });
469
+ }
470
+ } else {
471
+ const targetProduct = applicableProducts.reduce(
472
+ (max, p) => p[unitPriceField].greaterThan(max[unitPriceField]) ? p : max
473
+ );
474
+ let maxDeductForProduct = targetProduct[amountField];
475
+ if (maxPassesPerItem > 0) {
476
+ maxDeductForProduct = import_decimal.default.min(
477
+ targetProduct[unitPriceField].times(maxPassesPerItem),
478
+ targetProduct[amountField]
479
+ );
480
+ }
481
+ const actualDeductAmount = import_decimal.default.min(deductionLeft, maxDeductForProduct);
482
+ const actualDeductQty = Math.ceil(actualDeductAmount.dividedBy(targetProduct[unitPriceField]).toNumber());
483
+ targetProduct[amountField] = targetProduct[amountField].minus(actualDeductAmount);
484
+ deductionLeft = deductionLeft.minus(actualDeductAmount);
485
+ deductionDetails.push({
486
+ product_id: targetProduct.product_id,
487
+ parent_product_id: targetProduct.parent_product_id || null,
488
+ is_bundle_item: targetProduct.is_bundle_item || false,
489
+ deductAmount: actualDeductAmount.toNumber(),
490
+ deductQuantity: actualDeductQty
491
+ });
492
+ }
493
+ const totalDeducted = maxDeduction.minus(deductionLeft);
494
+ remainingOrderAmount = remainingOrderAmount.minus(totalDeducted);
495
+ deductionDetails.forEach((detail) => {
496
+ incrementItemPassUsage(selectedVoucher.product_id, detail.product_id, detail.deductQuantity);
497
+ });
498
+ selectedWithDetails.push({
499
+ ...selectedVoucher,
500
+ actualDeduction: totalDeducted.toNumber(),
501
+ // 转换为数字
502
+ deductionDetails,
503
+ _available_max_amount: totalDeducted.toNumber(),
504
+ // 转换为数字
505
+ _unified_available_status: totalDeducted.greaterThan(0) ? 1 : 0
506
+ });
507
+ });
508
+ const selectedIds = new Set(selectedVouchers.map((v) => v.id));
509
+ const usedVoucherCounts = /* @__PURE__ */ new Map();
510
+ selectedVouchers.forEach((v) => {
511
+ usedVoucherCounts.set(v.product_id, (usedVoucherCounts.get(v.product_id) || 0) + 1);
512
+ });
513
+ const allWithUpdatedStatus = allVouchers.map((voucher) => {
514
+ if (selectedIds.has(voucher.id)) {
515
+ const selectedDetail = selectedWithDetails.find(
516
+ (v) => v.id === voucher.id
517
+ );
518
+ return selectedDetail || voucher;
519
+ }
520
+ const { config, id, product_id } = voucher;
521
+ const { maxDeductionAmount, allowCrossProduct, applicableProductLimit, deductTaxAndFee = true, maxPassesPerItem = 0 } = config;
522
+ const unitPriceField = deductTaxAndFee ? "unitPriceWithTax" : "unitPricePure";
523
+ const amountField = deductTaxAndFee ? "remainingAmountWithTax" : "remainingAmountPure";
524
+ const recommendedAmount = getRecommendedAmount(voucher);
525
+ let isAvailable = true;
526
+ let calculatedMaxAmount = new import_decimal.default(0);
527
+ let reasonCode;
528
+ if (recommendedAmount <= 0) {
529
+ isAvailable = false;
530
+ reasonCode = "not_meet_the_required_conditions";
531
+ } else if (remainingOrderAmount.lessThanOrEqualTo(0)) {
532
+ isAvailable = false;
533
+ reasonCode = "exceeds_the_maximum_deduction_limit";
534
+ } else {
535
+ if (config.maxUsagePerOrder > 0) {
536
+ const usedCount = usedVoucherCounts.get(product_id) || 0;
537
+ if (usedCount >= config.maxUsagePerOrder) {
538
+ isAvailable = false;
539
+ reasonCode = "usage_limit_reached";
540
+ }
541
+ }
542
+ if (isAvailable) {
543
+ let applicableProducts = getApplicableProducts(
544
+ voucher,
545
+ productsForCalc
546
+ ).filter((p) => p[amountField].greaterThan(0));
547
+ applicableProducts = filterByMaxPassesPerItem(applicableProducts, product_id, maxPassesPerItem);
548
+ if (applicableProducts.length === 0) {
549
+ isAvailable = false;
550
+ reasonCode = "not_meet_the_required_conditions";
551
+ } else {
552
+ const baseAmount = import_decimal.default.min(
553
+ new import_decimal.default(recommendedAmount),
554
+ new import_decimal.default(maxDeductionAmount)
555
+ );
556
+ if (allowCrossProduct) {
557
+ if (applicableProductLimit > 0) {
558
+ const sortedProducts = [...applicableProducts].sort((a, b) => a[amountField].comparedTo(b[amountField]) > 0 ? -1 : 1);
559
+ let remainingLimit = applicableProductLimit;
560
+ for (const product of sortedProducts) {
561
+ if (remainingLimit <= 0)
562
+ break;
563
+ const currentAvailableQty = Math.ceil(product[amountField].dividedBy(product[unitPriceField]).toNumber());
564
+ const deductQty = Math.min(currentAvailableQty, remainingLimit);
565
+ const deductAmount = import_decimal.default.min(
566
+ product[unitPriceField].times(deductQty),
567
+ product[amountField]
568
+ );
569
+ calculatedMaxAmount = calculatedMaxAmount.plus(deductAmount);
570
+ remainingLimit -= deductQty;
571
+ }
572
+ } else {
573
+ calculatedMaxAmount = applicableProducts.reduce(
574
+ (sum, p) => sum.plus(p[amountField]),
575
+ new import_decimal.default(0)
576
+ );
577
+ }
578
+ } else {
579
+ const maxProduct = applicableProducts.reduce(
580
+ (max, p) => p[unitPriceField].greaterThan(max[unitPriceField]) ? p : max
581
+ );
582
+ if (maxPassesPerItem > 0) {
583
+ calculatedMaxAmount = import_decimal.default.min(
584
+ maxProduct[unitPriceField].times(maxPassesPerItem),
585
+ maxProduct[amountField]
586
+ );
587
+ } else {
588
+ calculatedMaxAmount = maxProduct[amountField];
589
+ }
590
+ }
591
+ calculatedMaxAmount = import_decimal.default.min(
592
+ baseAmount,
593
+ calculatedMaxAmount,
594
+ remainingOrderAmount
595
+ );
596
+ if (calculatedMaxAmount.lessThanOrEqualTo(0)) {
597
+ isAvailable = false;
598
+ reasonCode = "exceeds_the_maximum_deduction_limit";
599
+ }
600
+ }
601
+ }
602
+ }
603
+ return {
604
+ ...voucher,
605
+ _available_max_amount: calculatedMaxAmount.toNumber(),
606
+ // 转换为数字
607
+ _unified_available_status: isAvailable ? 1 : 0,
608
+ ...reasonCode && { reasonCode }
609
+ };
610
+ });
611
+ return {
612
+ allWithUpdatedStatus,
613
+ selectedWithDetails
614
+ };
615
+ }
616
+ var getMainProductPrice = (product, isDeductTaxAndFee) => {
617
+ var _a, _b, _c, _d, _e;
618
+ let mainProductPrice = new import_decimal.default((product == null ? void 0 : product.main_product_selling_price) || ((_a = product.metadata) == null ? void 0 : _a.main_product_selling_price) || 0);
619
+ for (let bundleItem of (product == null ? void 0 : product.product_bundle) || []) {
620
+ if (getBundleItemIsMarkupOrDiscountPrice(bundleItem)) {
621
+ const bundleItemPrice = new import_decimal.default(bundleItem.bundle_selling_price ?? 0);
622
+ mainProductPrice = mainProductPrice.add(bundleItemPrice.times(bundleItem.num));
623
+ }
624
+ }
625
+ let taxFee = new import_decimal.default((product == null ? void 0 : product.tax_fee) || ((_b = product == null ? void 0 : product.metadata) == null ? void 0 : _b.main_product_attached_bundle_tax_fee) || 0).add(((_c = product == null ? void 0 : product.metadata) == null ? void 0 : _c.tax_fee_rounding_remainder) || 0);
626
+ if (product.is_price_include_tax === 1) {
627
+ taxFee = new import_decimal.default(0);
628
+ }
629
+ const surchargeFee = new import_decimal.default(((_d = product == null ? void 0 : product.metadata) == null ? void 0 : _d.main_product_attached_bundle_surcharge_fee) || 0).add(((_e = product == null ? void 0 : product.metadata) == null ? void 0 : _e.surcharge_rounding_remainder) || 0);
630
+ const taxAndFeeTotal = taxFee.add(surchargeFee);
631
+ if (isDeductTaxAndFee) {
632
+ mainProductPrice = mainProductPrice.add(taxAndFeeTotal);
633
+ }
634
+ return mainProductPrice.toNumber();
635
+ };
636
+ var getBundleItemPrice = (bundleItem, parentQuantity, isDeductTaxAndFee) => {
637
+ var _a;
638
+ const totalQuantity = bundleItem.num * parentQuantity;
639
+ let bundleItemPrice = new import_decimal.default(bundleItem.bundle_selling_price ?? 0).times(totalQuantity);
640
+ if (isDeductTaxAndFee) {
641
+ let taxFee = new import_decimal.default(bundleItem.tax_fee ?? 0).times(totalQuantity);
642
+ if (bundleItem.is_price_include_tax === 1) {
643
+ taxFee = new import_decimal.default(0);
644
+ }
645
+ const surchargeFee = new import_decimal.default(((_a = bundleItem.metadata) == null ? void 0 : _a.surcharge_fee) ?? 0).times(totalQuantity);
646
+ bundleItemPrice = bundleItemPrice.add(taxFee).add(surchargeFee);
647
+ }
648
+ return bundleItemPrice.toNumber();
649
+ };
650
+ var expandProductsWithBundleItems = (products, deductTaxAndFee) => {
651
+ const expandedProducts = [];
652
+ products.forEach((product) => {
653
+ const productQuantity = getProductQuantity(product);
654
+ const unitPriceWithTax = getMainProductPrice(product, true);
655
+ const unitPricePure = getMainProductPrice(product, false);
656
+ expandedProducts.push({
657
+ ...product,
658
+ is_bundle_item: false,
659
+ parent_product_id: null,
660
+ // 单价(用于按 quantity 计算抵扣)
661
+ unitPriceWithTax: new import_decimal.default(unitPriceWithTax),
662
+ unitPricePure: new import_decimal.default(unitPricePure),
663
+ // 剩余可抵扣数量
664
+ remainingQuantity: productQuantity,
665
+ // 含税费的剩余金额
666
+ remainingAmountWithTax: new import_decimal.default(unitPriceWithTax).times(productQuantity),
667
+ // 纯商品金额(不含税费)
668
+ remainingAmountPure: new import_decimal.default(unitPricePure).times(productQuantity)
669
+ });
670
+ if (product.product_bundle && product.product_bundle.length > 0) {
671
+ product.product_bundle.forEach((bundleItem) => {
672
+ if (getBundleItemIsOriginalPrice(bundleItem)) {
673
+ const bundleQuantity = bundleItem.num * productQuantity;
674
+ const bundleUnitPriceWithTax = new import_decimal.default(getBundleItemPrice(bundleItem, 1, true)).dividedBy(bundleItem.num);
675
+ const bundleUnitPricePure = new import_decimal.default(getBundleItemPrice(bundleItem, 1, false)).dividedBy(bundleItem.num);
676
+ expandedProducts.push({
677
+ ...bundleItem,
678
+ product_id: bundleItem.bundle_product_id,
679
+ // 使用 bundle_product_id 作为 product_id
680
+ is_bundle_item: true,
681
+ parent_product_id: product.product_id,
682
+ quantity: bundleQuantity,
683
+ // 子商品数量 * 主商品数量
684
+ // 单价(用于按 quantity 计算抵扣)
685
+ unitPriceWithTax: bundleUnitPriceWithTax,
686
+ unitPricePure: bundleUnitPricePure,
687
+ // 剩余可抵扣数量
688
+ remainingQuantity: bundleQuantity,
689
+ // 含税费的剩余金额
690
+ remainingAmountWithTax: new import_decimal.default(getBundleItemPrice(bundleItem, productQuantity, true)),
691
+ // 纯商品金额(不含税费)
692
+ remainingAmountPure: new import_decimal.default(getBundleItemPrice(bundleItem, productQuantity, false))
693
+ });
694
+ }
695
+ });
696
+ }
697
+ });
698
+ return expandedProducts;
699
+ };
700
+ var getProductQuantity = (product) => {
701
+ return product.quantity || product.product_quantity || 1;
702
+ };
703
+ var getBundleItemIsOriginalPrice = (item) => {
704
+ return (item == null ? void 0 : item.price_type) === "markup" && (item == null ? void 0 : item.price_type_ext) === "product_price";
705
+ };
706
+ var getBundleItemIsMarkupPrice = (item) => {
707
+ return (item == null ? void 0 : item.price_type) === "markup" && ((item == null ? void 0 : item.price_type_ext) === "" || !(item == null ? void 0 : item.price_type_ext));
708
+ };
709
+ var getBundleItemIsDiscountPrice = (item) => {
710
+ return (item == null ? void 0 : item.price_type) === "markdown" && ((item == null ? void 0 : item.price_type_ext) === "" || !(item == null ? void 0 : item.price_type_ext));
711
+ };
712
+ var getBundleItemIsMarkupOrDiscountPrice = (item) => {
713
+ return getBundleItemIsMarkupPrice(item) || getBundleItemIsDiscountPrice(item);
714
+ };
715
+ // Annotate the CommonJS export names for ESM import in node:
716
+ 0 && (module.exports = {
717
+ getApplicableProductIds,
718
+ getBundleItemIsDiscountPrice,
719
+ getBundleItemIsMarkupOrDiscountPrice,
720
+ getBundleItemIsMarkupPrice,
721
+ getBundleItemIsOriginalPrice,
722
+ getBundleItemPrice,
723
+ getMainProductPrice,
724
+ getProductQuantity,
725
+ processVouchers,
726
+ recalculateVouchers
727
+ });