UNPKG

@pisell/pisellos

Version:

一个可扩展的前端模块化SDK框架,支持插件系统

836 lines (834 loc) 39.6 kB
var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/model/strategy/adapter/walletPass/utils.ts var utils_exports = {}; __export(utils_exports, { getApplicableProductIds: () => getApplicableProductIds, getBundleItemDiscountDifference: () => getBundleItemDiscountDifference, getBundleItemIsDiscountPrice: () => getBundleItemIsDiscountPrice, getBundleItemIsMarkupOrDiscountPrice: () => getBundleItemIsMarkupOrDiscountPrice, getBundleItemIsMarkupPrice: () => getBundleItemIsMarkupPrice, getBundleItemIsOriginalPrice: () => getBundleItemIsOriginalPrice, getBundleItemPrice: () => getBundleItemPrice, getBundleItemTaxAndFeeRoundingRemainder: () => getBundleItemTaxAndFeeRoundingRemainder, getMainProductPrice: () => getMainProductPrice, getProductDiscountDifference: () => getProductDiscountDifference, getProductQuantity: () => getProductQuantity, getTaxAndFeeRoundingRemainder: () => getTaxAndFeeRoundingRemainder, processVouchers: () => processVouchers, recalculateVouchers: () => recalculateVouchers, resolveWalletPassLineKey: () => resolveWalletPassLineKey }); module.exports = __toCommonJS(utils_exports); var import_decimal = __toESM(require("decimal.js")); var getProductQuantity = (product) => { return product.quantity || product.product_quantity || 1; }; function resolveWalletPassLineKey(product, indexInOrder) { const m = (product == null ? void 0 : product.metadata) || {}; const u1 = m.product_unique; if (u1 != null && String(u1).length > 0) return String(u1); const u2 = m.unique_identification_number; if (u2 != null && String(u2).length > 0) return String(u2); if ((product == null ? void 0 : product.id) != null && String(product.id).length > 0) return `order_item:${product.id}`; if ((product == null ? void 0 : product.product_unique_string) != null && String(product.product_unique_string).length > 0) { return String(product.product_unique_string); } return `order_line_${indexInOrder}`; } function getExpandedOrderLineQuantity(p) { if ((p == null ? void 0 : p._orderLineQuantity) != null && p._orderLineQuantity > 0) return p._orderLineQuantity; return getProductQuantity(p); } function getMaxPassSlotsForExpandedLine(maxPassesPerItem, p) { if (maxPassesPerItem <= 0) return Infinity; return maxPassesPerItem * getExpandedOrderLineQuantity(p); } function computePassSlotsIncrement(deductAmount, deductQty, maxPassesPerItem, orderLineQuantity, currentUsage) { if (maxPassesPerItem <= 0 || !deductAmount.greaterThan(0)) return 0; const cap = maxPassesPerItem * orderLineQuantity; const room = Math.max(0, cap - currentUsage); if (room <= 0) return 0; const raw = Math.max(1, Math.ceil(Number(deductQty)) || 1); return Math.min(raw, room); } function applyMaxPassUsageIncrements(usageMap, walletPassProductId, maxPassesPerItem, deductionDetails) { deductionDetails.forEach((detail) => { var _a; const lineKey = detail.lineKey; if (!lineKey || maxPassesPerItem <= 0) return; const orderLineQty = detail.orderLineQuantity != null && detail.orderLineQuantity > 0 ? detail.orderLineQuantity : 1; const cur = ((_a = usageMap.get(walletPassProductId)) == null ? void 0 : _a.get(lineKey)) || 0; const inc = computePassSlotsIncrement( new import_decimal.default(detail.deductAmount), detail.deductQuantity, maxPassesPerItem, orderLineQty, cur ); if (inc <= 0) return; if (!usageMap.has(walletPassProductId)) usageMap.set(walletPassProductId, /* @__PURE__ */ new Map()); const inner = usageMap.get(walletPassProductId); inner.set(lineKey, cur + inc); }); } var getRecommendedAmount = (voucher) => { console.log("voucher312", voucher); const { config, recommended_usage_amount, recommended_pure_product_usage_amount } = voucher; const deductTaxAndFee = (config == null ? void 0 : config.deductTaxAndFee) ?? true; return deductTaxAndFee ? recommended_usage_amount : recommended_pure_product_usage_amount ?? recommended_usage_amount; }; var getApplicableProductIds = (voucher) => { const { available_product_type, available_product_ids } = voucher; const productType = available_product_type || "product_all"; if (productType === "product_all") { return null; } if (productType === "product_collection" || productType === "products") { return available_product_ids || []; } return []; }; var getApplicableProductsAmount = (voucher, productsData) => { const applicableProductIds = getApplicableProductIds(voucher); const { config } = voucher; const deductTaxAndFee = (config == null ? void 0 : config.deductTaxAndFee) ?? true; const amountField = deductTaxAndFee ? "remainingAmountWithTax" : "remainingAmountPure"; if (applicableProductIds === null) { return productsData.filter((p) => p[amountField].greaterThan(0)).reduce((sum, p) => sum.plus(p[amountField]), new import_decimal.default(0)); } if (applicableProductIds.length === 0) { return new import_decimal.default(0); } 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)); }; var getApplicableProducts = (voucher, productsData) => { const applicableProductIds = getApplicableProductIds(voucher); if (applicableProductIds === null) { return productsData; } if (applicableProductIds.length === 0) { return []; } return productsData.filter((p) => applicableProductIds.includes(p.product_id)); }; function processVouchers(applicableVouchers, orderTotalAmount, products) { console.log(products, "products123"); const productsCopy = expandProductsWithBundleItems(products, true); let remainingOrderAmount = new import_decimal.default(orderTotalAmount); const getItemPassUsage = (usageMap, walletPassProductId, lineKey) => { var _a; return ((_a = usageMap.get(walletPassProductId)) == null ? void 0 : _a.get(lineKey)) || 0; }; const filterByMaxPassesPerItem = (products2, usageMap, walletPassProductId, maxPassesPerItem) => { if (maxPassesPerItem <= 0) return products2; return products2.filter((p) => { const lineKey = p._walletPassLineKey; if (!lineKey) return true; return getItemPassUsage(usageMap, walletPassProductId, lineKey) < getMaxPassSlotsForExpandedLine(maxPassesPerItem, p); }); }; const calculateAvailableMaxAmount = (voucher, productsData, itemPassUsage) => { const { config } = voucher; const { maxDeductionAmount = 0, allowCrossProduct = true, applicableProductLimit = 0, deductTaxAndFee = true, maxPassesPerItem = 0 } = config ?? {}; const recommendedAmount = getRecommendedAmount(voucher); const baseAmount = import_decimal.default.min( new import_decimal.default(recommendedAmount), new import_decimal.default(maxDeductionAmount) ); const unitPriceField = deductTaxAndFee ? "unitPriceWithTax" : "unitPricePure"; const amountField = deductTaxAndFee ? "remainingAmountWithTax" : "remainingAmountPure"; let applicableProducts = getApplicableProducts(voucher, productsData).filter((p) => p[amountField].greaterThan(0)); if (itemPassUsage) { applicableProducts = filterByMaxPassesPerItem(applicableProducts, itemPassUsage, voucher.product_id, maxPassesPerItem); } if (applicableProducts.length === 0) { return new import_decimal.default(0); } let finalApplicableAmount = new import_decimal.default(0); if (allowCrossProduct) { if (applicableProductLimit > 0) { const sortedProducts = [...applicableProducts].sort((a, b) => a[amountField].comparedTo(b[amountField]) > 0 ? -1 : 1); let remainingLimit = applicableProductLimit; for (const product of sortedProducts) { if (remainingLimit <= 0) break; const currentAvailableQty = Math.ceil(product[amountField].dividedBy(product[unitPriceField]).toNumber()); const deductQty = Math.min(currentAvailableQty, remainingLimit); const deductAmount = import_decimal.default.min( product[unitPriceField].times(deductQty), product[amountField] ); finalApplicableAmount = finalApplicableAmount.plus(deductAmount); remainingLimit -= deductQty; } } else { finalApplicableAmount = applicableProducts.reduce( (sum, p) => sum.plus(p[amountField]), new import_decimal.default(0) ); } } else { const maxProduct = applicableProducts.reduce( (max, p) => p[unitPriceField].greaterThan(max[unitPriceField]) ? p : max ); if (maxPassesPerItem > 0) { finalApplicableAmount = import_decimal.default.min( maxProduct[unitPriceField].times(maxPassesPerItem), maxProduct[amountField] ); } else { finalApplicableAmount = maxProduct[amountField]; } } return import_decimal.default.min(baseAmount, finalApplicableAmount, remainingOrderAmount); }; const isVoucherAvailable = (voucher, productsData, usedVoucherCounts2, itemPassUsage) => { const { config, id, product_id } = voucher; const recommendedAmount = getRecommendedAmount(voucher); if (recommendedAmount <= 0) { return { isAvailable: false, reasonCode: "not_meet_the_required_conditions" }; } if (remainingOrderAmount.lessThanOrEqualTo(0)) { return { isAvailable: false, reasonCode: "exceeds_the_maximum_deduction_limit" }; } const applicableAmount = getApplicableProductsAmount(voucher, productsData); if (applicableAmount.lessThanOrEqualTo(0)) { return { isAvailable: false, reasonCode: "not_meet_the_required_conditions" }; } if (((config == null ? void 0 : config.maxUsagePerOrder) || 0) > 0) { const usedCount = usedVoucherCounts2.get(product_id) || 0; if (usedCount >= ((config == null ? void 0 : config.maxUsagePerOrder) || 0)) { return { isAvailable: false, reasonCode: "usage_limit_reached" }; } } const maxPassesPerItem = (config == null ? void 0 : config.maxPassesPerItem) || 0; if (maxPassesPerItem > 0 && itemPassUsage) { const deductTaxAndFee = (config == null ? void 0 : config.deductTaxAndFee) ?? true; const amountField = deductTaxAndFee ? "remainingAmountWithTax" : "remainingAmountPure"; const availableAfterPassLimit = getApplicableProducts(voucher, productsData).filter((p) => p[amountField].greaterThan(0)).filter((p) => { const lineKey = p._walletPassLineKey; if (!lineKey) return true; return getItemPassUsage(itemPassUsage, product_id, lineKey) < getMaxPassSlotsForExpandedLine(maxPassesPerItem, p); }); if (availableAfterPassLimit.length === 0) { return { isAvailable: false, reasonCode: "max_passes_per_item_reached" }; } } return { isAvailable: true }; }; const usedVoucherCountsForAll = /* @__PURE__ */ new Map(); const allVouchersWithStatus = applicableVouchers.map((voucher) => { const _available_max_amount = calculateAvailableMaxAmount( voucher, productsCopy ); const availabilityResult = isVoucherAvailable( voucher, productsCopy, usedVoucherCountsForAll ); const _unified_available_status = availabilityResult.isAvailable ? 1 : 0; return { ...voucher, _available_max_amount: _available_max_amount.toNumber(), // 转换为数字 _unified_available_status, ...availabilityResult.reasonCode && { reasonCode: availabilityResult.reasonCode } }; }); const recommendedVouchers = []; const usedVoucherCounts = /* @__PURE__ */ new Map(); const productsForRecommendation = expandProductsWithBundleItems(products, true); remainingOrderAmount = new import_decimal.default(orderTotalAmount); const itemPassUsageMap = /* @__PURE__ */ new Map(); const applyVoucher = (voucher) => { const availabilityCheck = isVoucherAvailable( voucher, productsForRecommendation, usedVoucherCounts, itemPassUsageMap ); if (!availabilityCheck.isAvailable) { return false; } const { config, id, product_id } = voucher; const { maxDeductionAmount = 0, allowCrossProduct = true, applicableProductLimit = 0, deductTaxAndFee = true, maxPassesPerItem = 0 } = config ?? {}; const unitPriceField = deductTaxAndFee ? "unitPriceWithTax" : "unitPricePure"; const amountField = deductTaxAndFee ? "remainingAmountWithTax" : "remainingAmountPure"; let applicableProducts = getApplicableProducts( voucher, productsForRecommendation ).filter((p) => p[amountField].greaterThan(0)); applicableProducts = filterByMaxPassesPerItem(applicableProducts, itemPassUsageMap, product_id, maxPassesPerItem); if (applicableProducts.length === 0) return false; const usageAmount = typeof voucher.edit_current_amount === "number" ? voucher.edit_current_amount : getRecommendedAmount(voucher); const baseAmount = import_decimal.default.min( new import_decimal.default(usageAmount), new import_decimal.default(maxDeductionAmount) ); let calculatedAvailableMaxAmount = new import_decimal.default(0); if (allowCrossProduct) { if (applicableProductLimit > 0) { const sortedProducts = [...applicableProducts].sort((a, b) => a[amountField].comparedTo(b[amountField]) > 0 ? -1 : 1); let remainingLimit = applicableProductLimit; for (const product of sortedProducts) { if (remainingLimit <= 0) break; const currentAvailableQty = Math.ceil(product[amountField].dividedBy(product[unitPriceField]).toNumber()); const deductQty = Math.min(currentAvailableQty, remainingLimit); const deductAmount = import_decimal.default.min( product[unitPriceField].times(deductQty), product[amountField] ); calculatedAvailableMaxAmount = calculatedAvailableMaxAmount.plus(deductAmount); remainingLimit -= deductQty; } } else { calculatedAvailableMaxAmount = applicableProducts.reduce( (sum, p) => sum.plus(p[amountField]), new import_decimal.default(0) ); } } else { const maxProduct = applicableProducts.reduce( (max, p) => p[unitPriceField].greaterThan(max[unitPriceField]) ? p : max ); if (maxPassesPerItem > 0) { calculatedAvailableMaxAmount = import_decimal.default.min( maxProduct[unitPriceField].times(maxPassesPerItem), maxProduct[amountField] ); } else { calculatedAvailableMaxAmount = maxProduct[amountField]; } } const availableMaxAmount = import_decimal.default.min( baseAmount, calculatedAvailableMaxAmount, remainingOrderAmount ); const maxDeduction = import_decimal.default.min( new import_decimal.default(usageAmount), new import_decimal.default(maxDeductionAmount) ); let deductionLeft = maxDeduction; const deductionDetails = []; if (allowCrossProduct) { const sortedProducts = [...applicableProducts].sort( (a, b) => a[amountField].comparedTo(b[amountField]) > 0 ? -1 : 1 ); let remainingLimit = applicableProductLimit > 0 ? applicableProductLimit : Infinity; for (const product of sortedProducts) { if (deductionLeft.lessThanOrEqualTo(0) || remainingLimit <= 0) break; const currentAvailableQty = Math.ceil(product[amountField].dividedBy(product[unitPriceField]).toNumber()); const availableQty = Math.min(currentAvailableQty, remainingLimit); const maxDeductForProduct = import_decimal.default.min( product[unitPriceField].times(availableQty), product[amountField] ); const actualDeductAmount = import_decimal.default.min(deductionLeft, maxDeductForProduct); const actualDeductQty = Math.ceil(actualDeductAmount.dividedBy(product[unitPriceField]).toNumber()); product[amountField] = product[amountField].minus(actualDeductAmount); deductionLeft = deductionLeft.minus(actualDeductAmount); remainingLimit -= actualDeductQty; deductionDetails.push({ product_id: product.product_id, parent_product_id: product.parent_product_id || null, is_bundle_item: product.is_bundle_item || false, lineKey: product._walletPassLineKey, orderLineQuantity: getExpandedOrderLineQuantity(product), deductAmount: actualDeductAmount.toNumber(), // 转换为数字 deductQuantity: actualDeductQty // 抵扣涉及的数量(用于记录) }); } } else { const targetProduct = applicableProducts.reduce( (max, p) => p[unitPriceField].greaterThan(max[unitPriceField]) ? p : max ); let maxDeductForProduct = targetProduct[amountField]; if (maxPassesPerItem > 0) { maxDeductForProduct = import_decimal.default.min( targetProduct[unitPriceField].times(maxPassesPerItem), targetProduct[amountField] ); } const actualDeductAmount = import_decimal.default.min(deductionLeft, maxDeductForProduct); const actualDeductQty = Math.ceil(actualDeductAmount.dividedBy(targetProduct[unitPriceField]).toNumber()); targetProduct[amountField] = targetProduct[amountField].minus(actualDeductAmount); deductionLeft = deductionLeft.minus(actualDeductAmount); deductionDetails.push({ product_id: targetProduct.product_id, parent_product_id: targetProduct.parent_product_id || null, is_bundle_item: targetProduct.is_bundle_item || false, lineKey: targetProduct._walletPassLineKey, orderLineQuantity: getExpandedOrderLineQuantity(targetProduct), deductAmount: actualDeductAmount.toNumber(), deductQuantity: actualDeductQty }); } const totalDeducted = maxDeduction.minus(deductionLeft); if (totalDeducted.greaterThan(0)) { remainingOrderAmount = remainingOrderAmount.minus(totalDeducted); usedVoucherCounts.set(product_id, (usedVoucherCounts.get(product_id) || 0) + 1); applyMaxPassUsageIncrements(itemPassUsageMap, product_id, maxPassesPerItem, deductionDetails); recommendedVouchers.push({ ...voucher, actualDeduction: totalDeducted.toNumber(), // 转换为数字 deductionDetails, _available_max_amount: availableMaxAmount.toNumber(), // 转换为数字 _unified_available_status: 1 }); return true; } return false; }; applicableVouchers.forEach((voucher) => { applyVoucher(voucher); }); const recommendedMap = /* @__PURE__ */ new Map(); recommendedVouchers.forEach((v) => { recommendedMap.set(v.id, v); }); const allWithEnhancedData = allVouchersWithStatus.map((voucher) => { if (recommendedMap.has(voucher.id)) { return recommendedMap.get(voucher.id); } else { return { ...voucher, actualDeduction: 0, deductionDetails: [] // 保留 reasonCode(如果不可用的话) }; } }); return { recommended: recommendedVouchers, transformList: allWithEnhancedData }; } function recalculateVouchers(allVouchers, selectedVouchers, orderTotalAmount, products) { const productsForCalc = expandProductsWithBundleItems(products, true); let remainingOrderAmount = new import_decimal.default(orderTotalAmount); const selectedWithDetails = []; const itemPassUsageMap = /* @__PURE__ */ new Map(); const getItemPassUsageRecalc = (walletPassProductId, lineKey) => { var _a; return ((_a = itemPassUsageMap.get(walletPassProductId)) == null ? void 0 : _a.get(lineKey)) || 0; }; const filterByMaxPassesPerItemRecalc = (products2, walletPassProductId, maxPassesPerItem) => { if (maxPassesPerItem <= 0) return products2; return products2.filter((p) => { const lineKey = p._walletPassLineKey; if (!lineKey) return true; return getItemPassUsageRecalc(walletPassProductId, lineKey) < getMaxPassSlotsForExpandedLine(maxPassesPerItem, p); }); }; selectedVouchers.forEach((selectedVoucher) => { const { config, id } = selectedVoucher; const { maxDeductionAmount, allowCrossProduct, applicableProductLimit, deductTaxAndFee = true, maxPassesPerItem = 0 } = config; const unitPriceField = deductTaxAndFee ? "unitPriceWithTax" : "unitPricePure"; const amountField = deductTaxAndFee ? "remainingAmountWithTax" : "remainingAmountPure"; let applicableProducts = getApplicableProducts( selectedVoucher, productsForCalc ).filter((p) => p[amountField].greaterThan(0)); applicableProducts = filterByMaxPassesPerItemRecalc(applicableProducts, selectedVoucher.product_id, maxPassesPerItem); if (applicableProducts.length === 0) { selectedWithDetails.push({ ...selectedVoucher, actualDeduction: 0, deductionDetails: [], _available_max_amount: 0, _unified_available_status: 0, reasonCode: "not_meet_the_required_conditions" }); return; } const usageAmount = typeof selectedVoucher.edit_current_amount === "number" ? selectedVoucher.edit_current_amount : getRecommendedAmount(selectedVoucher); const maxDeduction = import_decimal.default.min( new import_decimal.default(usageAmount), new import_decimal.default(maxDeductionAmount), remainingOrderAmount ); let deductionLeft = maxDeduction; const deductionDetails = []; if (allowCrossProduct) { const sortedProducts = [...applicableProducts].sort( (a, b) => a[amountField].comparedTo(b[amountField]) > 0 ? -1 : 1 ); let remainingLimit = applicableProductLimit > 0 ? applicableProductLimit : Infinity; for (const product of sortedProducts) { if (deductionLeft.lessThanOrEqualTo(0) || remainingLimit <= 0) break; const currentAvailableQty = Math.ceil(product[amountField].dividedBy(product[unitPriceField]).toNumber()); const availableQty = Math.min(currentAvailableQty, remainingLimit); const maxDeductForProduct = import_decimal.default.min( product[unitPriceField].times(availableQty), product[amountField] ); const actualDeductAmount = import_decimal.default.min(deductionLeft, maxDeductForProduct); const actualDeductQty = Math.ceil(actualDeductAmount.dividedBy(product[unitPriceField]).toNumber()); product[amountField] = product[amountField].minus(actualDeductAmount); deductionLeft = deductionLeft.minus(actualDeductAmount); remainingLimit -= actualDeductQty; deductionDetails.push({ product_id: product.product_id, parent_product_id: product.parent_product_id || null, is_bundle_item: product.is_bundle_item || false, lineKey: product._walletPassLineKey, orderLineQuantity: getExpandedOrderLineQuantity(product), deductAmount: actualDeductAmount.toNumber(), // 转换为数字 deductQuantity: actualDeductQty // 抵扣涉及的数量(用于记录) }); } } else { const targetProduct = applicableProducts.reduce( (max, p) => p[unitPriceField].greaterThan(max[unitPriceField]) ? p : max ); let maxDeductForProduct = targetProduct[amountField]; if (maxPassesPerItem > 0) { maxDeductForProduct = import_decimal.default.min( targetProduct[unitPriceField].times(maxPassesPerItem), targetProduct[amountField] ); } const actualDeductAmount = import_decimal.default.min(deductionLeft, maxDeductForProduct); const actualDeductQty = Math.ceil(actualDeductAmount.dividedBy(targetProduct[unitPriceField]).toNumber()); targetProduct[amountField] = targetProduct[amountField].minus(actualDeductAmount); deductionLeft = deductionLeft.minus(actualDeductAmount); deductionDetails.push({ product_id: targetProduct.product_id, parent_product_id: targetProduct.parent_product_id || null, is_bundle_item: targetProduct.is_bundle_item || false, lineKey: targetProduct._walletPassLineKey, orderLineQuantity: getExpandedOrderLineQuantity(targetProduct), deductAmount: actualDeductAmount.toNumber(), deductQuantity: actualDeductQty }); } const totalDeducted = maxDeduction.minus(deductionLeft); remainingOrderAmount = remainingOrderAmount.minus(totalDeducted); applyMaxPassUsageIncrements(itemPassUsageMap, selectedVoucher.product_id, maxPassesPerItem, deductionDetails); selectedWithDetails.push({ ...selectedVoucher, actualDeduction: totalDeducted.toNumber(), // 转换为数字 deductionDetails, _available_max_amount: totalDeducted.toNumber(), // 转换为数字 _unified_available_status: totalDeducted.greaterThan(0) ? 1 : 0 }); }); const selectedIds = new Set(selectedVouchers.map((v) => v.id)); const usedVoucherCounts = /* @__PURE__ */ new Map(); selectedVouchers.forEach((v) => { usedVoucherCounts.set(v.product_id, (usedVoucherCounts.get(v.product_id) || 0) + 1); }); const allWithUpdatedStatus = allVouchers.map((voucher) => { if (selectedIds.has(voucher.id)) { const selectedDetail = selectedWithDetails.find( (v) => v.id === voucher.id ); return selectedDetail || voucher; } const { config, id, product_id } = voucher; const { maxDeductionAmount, allowCrossProduct, applicableProductLimit, deductTaxAndFee = true, maxPassesPerItem = 0 } = config; const unitPriceField = deductTaxAndFee ? "unitPriceWithTax" : "unitPricePure"; const amountField = deductTaxAndFee ? "remainingAmountWithTax" : "remainingAmountPure"; const recommendedAmount = getRecommendedAmount(voucher); let isAvailable = true; let calculatedMaxAmount = new import_decimal.default(0); let reasonCode; if (recommendedAmount <= 0) { isAvailable = false; reasonCode = "not_meet_the_required_conditions"; } else if (remainingOrderAmount.lessThanOrEqualTo(0)) { isAvailable = false; reasonCode = "exceeds_the_maximum_deduction_limit"; } else { if (config.maxUsagePerOrder > 0) { const usedCount = usedVoucherCounts.get(product_id) || 0; if (usedCount >= config.maxUsagePerOrder) { isAvailable = false; reasonCode = "usage_limit_reached"; } } if (isAvailable) { let applicableProducts = getApplicableProducts( voucher, productsForCalc ).filter((p) => p[amountField].greaterThan(0)); applicableProducts = filterByMaxPassesPerItemRecalc(applicableProducts, product_id, maxPassesPerItem); if (applicableProducts.length === 0) { isAvailable = false; reasonCode = "not_meet_the_required_conditions"; } else { const baseAmount = import_decimal.default.min( new import_decimal.default(recommendedAmount), new import_decimal.default(maxDeductionAmount) ); if (allowCrossProduct) { if (applicableProductLimit > 0) { const sortedProducts = [...applicableProducts].sort((a, b) => a[amountField].comparedTo(b[amountField]) > 0 ? -1 : 1); let remainingLimit = applicableProductLimit; for (const product of sortedProducts) { if (remainingLimit <= 0) break; const currentAvailableQty = Math.ceil(product[amountField].dividedBy(product[unitPriceField]).toNumber()); const deductQty = Math.min(currentAvailableQty, remainingLimit); const deductAmount = import_decimal.default.min( product[unitPriceField].times(deductQty), product[amountField] ); calculatedMaxAmount = calculatedMaxAmount.plus(deductAmount); remainingLimit -= deductQty; } } else { calculatedMaxAmount = applicableProducts.reduce( (sum, p) => sum.plus(p[amountField]), new import_decimal.default(0) ); } } else { const maxProduct = applicableProducts.reduce( (max, p) => p[unitPriceField].greaterThan(max[unitPriceField]) ? p : max ); if (maxPassesPerItem > 0) { calculatedMaxAmount = import_decimal.default.min( maxProduct[unitPriceField].times(maxPassesPerItem), maxProduct[amountField] ); } else { calculatedMaxAmount = maxProduct[amountField]; } } calculatedMaxAmount = import_decimal.default.min( baseAmount, calculatedMaxAmount, remainingOrderAmount ); if (calculatedMaxAmount.lessThanOrEqualTo(0)) { isAvailable = false; reasonCode = "exceeds_the_maximum_deduction_limit"; } } } } return { ...voucher, _available_max_amount: calculatedMaxAmount.toNumber(), // 转换为数字 _unified_available_status: isAvailable ? 1 : 0, ...reasonCode && { reasonCode } }; }); return { allWithUpdatedStatus, selectedWithDetails }; } var getTaxAndFeeRoundingRemainder = (product, isDeductTaxAndFee) => { var _a, _b; if (!isDeductTaxAndFee) { return 0; } let taxFeeRoundingRemainder = new import_decimal.default(0); if (product.is_price_include_tax !== 1) { taxFeeRoundingRemainder = new import_decimal.default(((_a = product == null ? void 0 : product.metadata) == null ? void 0 : _a.tax_fee_rounding_remainder) || 0); } const surchargeFeeRoundingRemainder = new import_decimal.default(((_b = product == null ? void 0 : product.metadata) == null ? void 0 : _b.surcharge_rounding_remainder) || 0); return taxFeeRoundingRemainder.add(surchargeFeeRoundingRemainder).toNumber(); }; var getProductDiscountDifference = (product) => { var _a; return new import_decimal.default(((_a = product == null ? void 0 : product.metadata) == null ? void 0 : _a.product_discount_difference) || 0).toNumber(); }; var getMainProductPrice = (product, isDeductTaxAndFee) => { var _a, _b, _c, _d; 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); for (let bundleItem of (product == null ? void 0 : product.product_bundle) || []) { if (getBundleItemIsMarkupOrDiscountPrice(bundleItem)) { const bundleItemPrice = new import_decimal.default(bundleItem.bundle_selling_price ?? 0); mainProductPrice = mainProductPrice.add(bundleItemPrice.times(bundleItem.num)); } } 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); if (product.is_price_include_tax === 1) { taxFee = new import_decimal.default(0); } const surchargeFee = new import_decimal.default(((_d = product == null ? void 0 : product.metadata) == null ? void 0 : _d.main_product_attached_bundle_surcharge_fee) || 0); const taxAndFeeTotal = taxFee.add(surchargeFee); if (isDeductTaxAndFee) { mainProductPrice = mainProductPrice.add(taxAndFeeTotal); } return mainProductPrice.toNumber(); }; var getBundleItemTaxAndFeeRoundingRemainder = (bundleItem, isDeductTaxAndFee) => { var _a, _b; if (!isDeductTaxAndFee) { return 0; } let taxFeeRoundingRemainder = new import_decimal.default(0); if (bundleItem.is_price_include_tax !== 1) { taxFeeRoundingRemainder = new import_decimal.default(((_a = bundleItem.metadata) == null ? void 0 : _a.tax_fee_rounding_remainder) || 0); } const surchargeFeeRoundingRemainder = new import_decimal.default(((_b = bundleItem.metadata) == null ? void 0 : _b.surcharge_rounding_remainder) || 0); return taxFeeRoundingRemainder.add(surchargeFeeRoundingRemainder).toNumber(); }; var getBundleItemDiscountDifference = (bundleItem) => { var _a; return new import_decimal.default(((_a = bundleItem.metadata) == null ? void 0 : _a.product_discount_difference) || 0).toNumber(); }; var getBundleItemPrice = (bundleItem, parentQuantity, isDeductTaxAndFee) => { var _a; const totalQuantity = bundleItem.num * parentQuantity; let bundleItemPrice = new import_decimal.default(bundleItem.bundle_selling_price ?? 0).times(totalQuantity); if (isDeductTaxAndFee) { let taxFee = new import_decimal.default(bundleItem.tax_fee ?? 0).times(totalQuantity); if (bundleItem.is_price_include_tax === 1) { taxFee = new import_decimal.default(0); } const surchargeFee = new import_decimal.default(((_a = bundleItem.metadata) == null ? void 0 : _a.surcharge_fee) ?? 0).times(totalQuantity); bundleItemPrice = bundleItemPrice.add(taxFee).add(surchargeFee); } return bundleItemPrice.toNumber(); }; var expandProductsWithBundleItems = (products, deductTaxAndFee) => { const expandedProducts = []; products.forEach((product, indexInOrder) => { const productQuantity = getProductQuantity(product); const parentLineKey = resolveWalletPassLineKey(product, indexInOrder); const unitPriceWithTax = getMainProductPrice(product, true); const unitPricePure = getMainProductPrice(product, false); const roundingRemainderWithTax = getTaxAndFeeRoundingRemainder(product, true); const discountDifference = getProductDiscountDifference(product); expandedProducts.push({ ...product, is_bundle_item: false, parent_product_id: null, _walletPassLineKey: parentLineKey, _orderLineQuantity: productQuantity, // 单价(用于按 quantity 计算抵扣) unitPriceWithTax: new import_decimal.default(unitPriceWithTax), unitPricePure: new import_decimal.default(unitPricePure), // 剩余可抵扣数量 remainingQuantity: productQuantity, // 含税费的剩余金额(单价 * 数量 + 舍入余数 - 商品差额) remainingAmountWithTax: new import_decimal.default(unitPriceWithTax).times(productQuantity).add(roundingRemainderWithTax).sub(discountDifference), // 纯商品金额(不含税费,也需要减去商品差额) remainingAmountPure: new import_decimal.default(unitPricePure).times(productQuantity).sub(discountDifference) }); if (product.product_bundle && product.product_bundle.length > 0) { product.product_bundle.forEach((bundleItem) => { if (getBundleItemIsOriginalPrice(bundleItem)) { const bundleQuantity = bundleItem.num * productQuantity; const bundleLineKey = `${parentLineKey}#bundle:${bundleItem.bundle_id}:${bundleItem.bundle_product_id}`; const bundleUnitPriceWithTax = new import_decimal.default(getBundleItemPrice(bundleItem, 1, true)).dividedBy(bundleItem.num); const bundleUnitPricePure = new import_decimal.default(getBundleItemPrice(bundleItem, 1, false)).dividedBy(bundleItem.num); const bundleRoundingRemainder = getBundleItemTaxAndFeeRoundingRemainder(bundleItem, true); const bundleDiscountDifference = getBundleItemDiscountDifference(bundleItem); expandedProducts.push({ ...bundleItem, product_id: bundleItem.bundle_product_id, // 使用 bundle_product_id 作为 product_id is_bundle_item: true, parent_product_id: product.product_id, quantity: bundleQuantity, // 子商品数量 * 主商品数量 _walletPassLineKey: bundleLineKey, _orderLineQuantity: bundleQuantity, // 单价(用于按 quantity 计算抵扣) unitPriceWithTax: bundleUnitPriceWithTax, unitPricePure: bundleUnitPricePure, // 剩余可抵扣数量 remainingQuantity: bundleQuantity, // 含税费的剩余金额(总价 + 舍入余数 - 商品差额) remainingAmountWithTax: new import_decimal.default(getBundleItemPrice(bundleItem, productQuantity, true)).add(bundleRoundingRemainder).sub(bundleDiscountDifference), // 纯商品金额(不含税费,也需要减去商品差额) remainingAmountPure: new import_decimal.default(getBundleItemPrice(bundleItem, productQuantity, false)).sub(bundleDiscountDifference) }); } }); } }); return expandedProducts; }; var getBundleItemIsOriginalPrice = (item) => { return (item == null ? void 0 : item.price_type) === "markup" && (item == null ? void 0 : item.price_type_ext) === "product_price"; }; var getBundleItemIsMarkupPrice = (item) => { 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)); }; var getBundleItemIsDiscountPrice = (item) => { 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)); }; var getBundleItemIsMarkupOrDiscountPrice = (item) => { return getBundleItemIsMarkupPrice(item) || getBundleItemIsDiscountPrice(item); }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { getApplicableProductIds, getBundleItemDiscountDifference, getBundleItemIsDiscountPrice, getBundleItemIsMarkupOrDiscountPrice, getBundleItemIsMarkupPrice, getBundleItemIsOriginalPrice, getBundleItemPrice, getBundleItemTaxAndFeeRoundingRemainder, getMainProductPrice, getProductDiscountDifference, getProductQuantity, getTaxAndFeeRoundingRemainder, processVouchers, recalculateVouchers, resolveWalletPassLineKey });