@pisell/pisellos 2.3.73 → 2.3.75
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.
- package/dist/core/index.d.ts +7 -0
- package/dist/core/index.js +111 -71
- package/dist/model/strategy/adapter/promotion/index.js +9 -0
- package/dist/model/strategy/adapter/walletPass/example.js +3 -26
- package/dist/model/strategy/strategy-example.js +5 -19
- package/dist/modules/Cart/index.js +0 -3
- package/dist/modules/Cart/utils/cartProduct.js +0 -1
- package/dist/modules/Date/index.js +1 -5
- package/dist/modules/Discount/index.js +2 -4
- package/dist/modules/OpenData/index.js +0 -2
- package/dist/modules/Order/index.d.ts +1 -1
- package/dist/modules/Order/index.js +36 -64
- package/dist/modules/Payment/index.js +97 -162
- package/dist/modules/Quotation/index.d.ts +6 -0
- package/dist/modules/Quotation/index.js +75 -19
- package/dist/modules/Rules/index.js +1 -4
- package/dist/modules/Schedule/index.js +0 -1
- package/dist/modules/Schedule/utils.js +0 -1
- package/dist/modules/Step/index.js +0 -1
- package/dist/plugins/request.d.ts +1 -0
- package/dist/plugins/request.js +1 -4
- package/dist/plugins/window.js +2 -6
- package/dist/server/index.d.ts +8 -2
- package/dist/server/index.js +303 -269
- package/dist/server/modules/floor-plan/index.d.ts +4 -0
- package/dist/server/modules/floor-plan/index.js +240 -98
- package/dist/server/modules/menu/index.js +75 -59
- package/dist/server/modules/order/index.d.ts +17 -7
- package/dist/server/modules/order/index.js +421 -211
- package/dist/server/modules/products/index.d.ts +8 -2
- package/dist/server/modules/products/index.js +191 -117
- package/dist/server/modules/quotation/index.js +29 -38
- package/dist/server/modules/resource/index.js +24 -17
- package/dist/server/modules/schedule/index.js +27 -35
- package/dist/server/utils/product.js +0 -1
- package/dist/solution/BaseSales/index.js +38 -41
- package/dist/solution/BookingByStep/index.d.ts +1 -1
- package/dist/solution/BookingByStep/index.js +7 -59
- package/dist/solution/BookingByStep/utils/capacity.js +1 -11
- package/dist/solution/BookingByStep/utils/resources.js +1 -3
- package/dist/solution/BookingByStep/utils/timeslots.js +2 -43
- package/dist/solution/BookingTicket/index.js +0 -2
- package/dist/solution/BookingTicket/utils/scan/cloudSearch.js +6 -7
- package/dist/solution/BookingTicket/utils/scan/handleScan.js +25 -31
- package/dist/solution/BookingTicket/utils/scan/index.js +6 -10
- package/dist/solution/BuyTickets/index.js +9 -12
- package/dist/solution/Checkout/index.js +177 -252
- package/dist/solution/Checkout/utils/index.js +4 -16
- package/dist/solution/RegisterAndLogin/index.js +30 -76
- package/dist/solution/ScanOrder/index.js +50 -60
- package/dist/solution/ShopDiscount/index.js +2 -7
- package/dist/solution/VenueBooking/index.js +45 -55
- package/dist/utils/task.js +15 -18
- package/dist/utils/watch.js +0 -1
- package/lib/apis/picoding.js +2 -0
- package/lib/core/index.d.ts +7 -0
- package/lib/core/index.js +149 -131
- package/lib/effects/index.js +46 -57
- package/lib/index.js +82 -49
- package/lib/model/index.js +14 -21
- package/lib/model/strategy/adapter/dataVariant/adapter.js +38 -50
- package/lib/model/strategy/adapter/dataVariant/evaluator.js +162 -236
- package/lib/model/strategy/adapter/dataVariant/examples.js +229 -237
- package/lib/model/strategy/adapter/dataVariant/index.js +39 -34
- package/lib/model/strategy/adapter/dataVariant/type.js +57 -28
- package/lib/model/strategy/adapter/index.js +100 -64
- package/lib/model/strategy/adapter/itemRule/adapter.js +156 -144
- package/lib/model/strategy/adapter/itemRule/evaluator.js +36 -48
- package/lib/model/strategy/adapter/itemRule/examples.js +295 -225
- package/lib/model/strategy/adapter/itemRule/index.js +83 -57
- package/lib/model/strategy/adapter/itemRule/type.js +97 -36
- package/lib/model/strategy/adapter/promotion/adapter.js +91 -69
- package/lib/model/strategy/adapter/promotion/evaluator.js +444 -310
- package/lib/model/strategy/adapter/promotion/examples.js +222 -234
- package/lib/model/strategy/adapter/promotion/index.js +1 -0
- package/lib/model/strategy/adapter/promotion/type.js +243 -36
- package/lib/model/strategy/adapter/type.js +4 -16
- package/lib/model/strategy/adapter/walletPass/evaluator.js +180 -150
- package/lib/model/strategy/adapter/walletPass/example.js +217 -190
- package/lib/model/strategy/adapter/walletPass/index.js +75 -51
- package/lib/model/strategy/adapter/walletPass/locales.js +36 -58
- package/lib/model/strategy/adapter/walletPass/type.js +4 -16
- package/lib/model/strategy/adapter/walletPass/utils.js +588 -378
- package/lib/model/strategy/engine.js +270 -168
- package/lib/model/strategy/index.js +49 -34
- package/lib/model/strategy/strategy-example.js +243 -239
- package/lib/model/strategy/type.js +100 -40
- package/lib/modules/Account/index.js +39 -53
- package/lib/modules/Account/types.js +20 -33
- package/lib/modules/AccountList/index.js +116 -154
- package/lib/modules/AccountList/types.js +30 -31
- package/lib/modules/AccountList/utils.js +18 -30
- package/lib/modules/BaseModule.js +23 -42
- package/lib/modules/BookingContext/index.js +158 -136
- package/lib/modules/BookingContext/types.js +46 -32
- package/lib/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +138 -146
- package/lib/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +78 -83
- package/lib/modules/BookingContext/utils/cacheItemToBookingInput.js +170 -150
- package/lib/modules/BookingContext/utils/flexible.js +38 -55
- package/lib/modules/BookingContext/utils/formatResourceList.js +19 -34
- package/lib/modules/BookingContext/utils/index.js +124 -41
- package/lib/modules/BookingContext/utils/noResource.js +58 -70
- package/lib/modules/BookingContext/utils/orderLineDisplay.js +14 -41
- package/lib/modules/BookingContext/utils/productExtend.js +202 -155
- package/lib/modules/BookingContext/utils/resourceErrors.js +53 -53
- package/lib/modules/BookingContext/utils/resourceSelection.js +19 -41
- package/lib/modules/BookingContext/utils/resources.js +209 -167
- package/lib/modules/BookingContext/utils/serviceTimes.js +115 -116
- package/lib/modules/BookingContext/utils/timeSlices.js +76 -86
- package/lib/modules/Cart/index.js +170 -124
- package/lib/modules/Cart/types.js +46 -51
- package/lib/modules/Cart/utils/cartAccount.js +40 -39
- package/lib/modules/Cart/utils/cartDate.js +56 -61
- package/lib/modules/Cart/utils/cartDiscount.js +28 -33
- package/lib/modules/Cart/utils/cartNote.js +27 -32
- package/lib/modules/Cart/utils/cartProduct.js +321 -251
- package/lib/modules/Cart/utils/cartRelationForms.js +29 -35
- package/lib/modules/Cart/utils/cartResource.js +70 -78
- package/lib/modules/Cart/utils/changePrice.js +22 -50
- package/lib/modules/Cart/utils/index.js +106 -48
- package/lib/modules/Customer/constants.js +13 -34
- package/lib/modules/Customer/index.js +235 -172
- package/lib/modules/Customer/types.js +38 -35
- package/lib/modules/Date/index.js +116 -115
- package/lib/modules/Date/types.js +16 -28
- package/lib/modules/Date/utils.js +174 -123
- package/lib/modules/Discount/index.js +122 -127
- package/lib/modules/Discount/types.js +9 -31
- package/lib/modules/Guests/index.js +30 -56
- package/lib/modules/Guests/types.js +23 -33
- package/lib/modules/Holder/index.js +209 -189
- package/lib/modules/Holder/types.js +4 -16
- package/lib/modules/Holder/utils.js +20 -49
- package/lib/modules/OpenData/index.js +70 -76
- package/lib/modules/OpenData/types.js +4 -16
- package/lib/modules/OpenData/utils.js +44 -78
- package/lib/modules/Order/index.d.ts +1 -1
- package/lib/modules/Order/index.js +1713 -2118
- package/lib/modules/Order/payment-utils.js +77 -120
- package/lib/modules/Order/types.js +88 -35
- package/lib/modules/Order/utils/bundleDiscountHydration.js +41 -74
- package/lib/modules/Order/utils/discountProductLineIdentity.js +142 -100
- package/lib/modules/Order/utils/manualProductDiscount.js +124 -123
- package/lib/modules/Order/utils/orderCollectionIdentity.js +184 -244
- package/lib/modules/Order/utils.js +814 -647
- package/lib/modules/Payment/cash.js +20 -33
- package/lib/modules/Payment/cashRecommendationAlgorithm.js +145 -76
- package/lib/modules/Payment/eftpos.js +16 -30
- package/lib/modules/Payment/index.js +532 -399
- package/lib/modules/Payment/mx51.js +1 -0
- package/lib/modules/Payment/types.js +230 -108
- package/lib/modules/Payment/utils.js +52 -51
- package/lib/modules/Payment/walletpass.js +485 -660
- package/lib/modules/Product/index.js +51 -46
- package/lib/modules/Product/types.js +4 -16
- package/lib/modules/Product/utils.js +32 -33
- package/lib/modules/ProductList/index.js +113 -123
- package/lib/modules/ProductList/types.js +9 -31
- package/lib/modules/Quotation/index.d.ts +6 -0
- package/lib/modules/Quotation/index.js +110 -103
- package/lib/modules/Quotation/types.js +4 -16
- package/lib/modules/Resource/index.js +27 -59
- package/lib/modules/Resource/types.js +22 -32
- package/lib/modules/Resource/utils.js +30 -38
- package/lib/modules/ResourcePlanner/index.js +25 -23
- package/lib/modules/ResourcePlanner/localClock.js +54 -119
- package/lib/modules/ResourcePlanner/planner.js +589 -770
- package/lib/modules/ResourcePlanner/types.js +10 -29
- package/lib/modules/Rules/index.js +1218 -959
- package/lib/modules/Rules/types.js +17 -40
- package/lib/modules/SalesSummary/index.js +85 -90
- package/lib/modules/SalesSummary/types.js +4 -16
- package/lib/modules/SalesSummary/utils.js +355 -430
- package/lib/modules/ScanOrderLogger/index.js +59 -79
- package/lib/modules/ScanOrderLogger/providers/feishu.js +54 -78
- package/lib/modules/ScanOrderLogger/providers/grafana.js +13 -36
- package/lib/modules/ScanOrderLogger/types.js +4 -16
- package/lib/modules/Schedule/getDateIsInSchedule.js +334 -163
- package/lib/modules/Schedule/index.js +100 -114
- package/lib/modules/Schedule/type.js +4 -16
- package/lib/modules/Schedule/types.js +4 -16
- package/lib/modules/Schedule/utils.js +433 -291
- package/lib/modules/Step/index.js +40 -52
- package/lib/modules/Step/tyeps.js +4 -16
- package/lib/modules/Summary/index.js +66 -71
- package/lib/modules/Summary/types.js +4 -16
- package/lib/modules/Summary/utils.js +773 -418
- package/lib/modules/SurchargeList/index.js +46 -60
- package/lib/modules/SurchargeList/types.js +4 -16
- package/lib/modules/index.js +245 -63
- package/lib/plugins/app.js +4 -16
- package/lib/plugins/index.js +36 -25
- package/lib/plugins/request.d.ts +1 -0
- package/lib/plugins/request.js +137 -126
- package/lib/plugins/shopStore.js +4 -16
- package/lib/plugins/user.js +4 -16
- package/lib/plugins/window.js +171 -179
- package/lib/server/index.d.ts +8 -2
- package/lib/server/index.js +3083 -2663
- package/lib/server/modules/floor-plan/index.d.ts +4 -0
- package/lib/server/modules/floor-plan/index.js +159 -127
- package/lib/server/modules/floor-plan/types.js +15 -30
- package/lib/server/modules/index.js +106 -68
- package/lib/server/modules/menu/index.js +170 -124
- package/lib/server/modules/menu/types.js +18 -31
- package/lib/server/modules/order/index.d.ts +17 -7
- package/lib/server/modules/order/index.js +916 -977
- package/lib/server/modules/order/types.js +122 -33
- package/lib/server/modules/order/utils/filterBookings.js +219 -194
- package/lib/server/modules/order/utils/filterOrders.js +118 -92
- package/lib/server/modules/payment/index.js +59 -83
- package/lib/server/modules/payment/types.js +4 -16
- package/lib/server/modules/products/index.d.ts +8 -2
- package/lib/server/modules/products/index.js +656 -471
- package/lib/server/modules/products/types.js +44 -34
- package/lib/server/modules/quotation/index.js +137 -172
- package/lib/server/modules/quotation/types.js +21 -31
- package/lib/server/modules/resource/index.js +225 -186
- package/lib/server/modules/resource/types.js +42 -33
- package/lib/server/modules/schedule/index.js +135 -123
- package/lib/server/modules/schedule/types.js +14 -21
- package/lib/server/modules/schedule/utils.js +334 -163
- package/lib/server/types.js +4 -16
- package/lib/server/utils/index.js +25 -23
- package/lib/server/utils/product.js +196 -139
- package/lib/server/utils/schedule.js +34 -41
- package/lib/server/utils/small-ticket.js +479 -456
- package/lib/server/utils/time.js +40 -49
- package/lib/solution/BaseSales/index.js +1186 -1410
- package/lib/solution/BaseSales/types.js +42 -38
- package/lib/solution/BaseSales/utils/cartPromotion.js +642 -498
- package/lib/solution/BaseSales/utils/parseSalesResponse.js +85 -105
- package/lib/solution/BaseSales/utils/quotationPrice.js +56 -114
- package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +108 -145
- package/lib/solution/BaseSales/utils.js +158 -143
- package/lib/solution/BookingByStep/index.d.ts +1 -1
- package/lib/solution/BookingByStep/index.js +1527 -1340
- package/lib/solution/BookingByStep/types.js +40 -99
- package/lib/solution/BookingByStep/utils/capacity.js +192 -160
- package/lib/solution/BookingByStep/utils/products.js +42 -52
- package/lib/solution/BookingByStep/utils/resources.js +527 -330
- package/lib/solution/BookingByStep/utils/stock.js +65 -44
- package/lib/solution/BookingByStep/utils/timeslots.js +129 -131
- package/lib/solution/BookingTicket/index.js +607 -522
- package/lib/solution/BookingTicket/types.js +99 -77
- package/lib/solution/BookingTicket/utils/addProductDecision.js +226 -189
- package/lib/solution/BookingTicket/utils/addTimeAvailability.js +58 -89
- package/lib/solution/BookingTicket/utils/bookingStatus.js +107 -78
- package/lib/solution/BookingTicket/utils/cartView.js +114 -99
- package/lib/solution/BookingTicket/utils/exampleData.js +185 -0
- package/lib/solution/BookingTicket/utils/orderCustomer.js +18 -41
- package/lib/solution/BookingTicket/utils/resolveBestAddTimePlan.js +219 -86
- package/lib/solution/BookingTicket/utils/scan/applyGlobalScan.js +168 -116
- package/lib/solution/BookingTicket/utils/scan/cloudSearch.js +79 -91
- package/lib/solution/BookingTicket/utils/scan/formatGlobalScan.js +54 -41
- package/lib/solution/BookingTicket/utils/scan/handleScan.js +84 -81
- package/lib/solution/BookingTicket/utils/scan/index.js +98 -104
- package/lib/solution/BookingTicket/utils/scan/scanCache.js +90 -43
- package/lib/solution/BookingTicket/utils/scan/scanProductValues.js +52 -64
- package/lib/solution/BookingTicket/utils/sessionCatalogStale.js +56 -58
- package/lib/solution/BookingTicket/utils/weighingProductSku.js +34 -48
- package/lib/solution/BuyTickets/index.js +67 -70
- package/lib/solution/BuyTickets/types.js +22 -38
- package/lib/solution/Checkout/index.js +1451 -1158
- package/lib/solution/Checkout/types.js +91 -61
- package/lib/solution/Checkout/utils/index.js +228 -156
- package/lib/solution/PlaceOrder/index.js +55 -0
- package/lib/solution/RegisterAndLogin/config.js +493 -460
- package/lib/solution/RegisterAndLogin/index.js +633 -630
- package/lib/solution/RegisterAndLogin/types.js +189 -76
- package/lib/solution/RegisterAndLogin/utils.js +183 -125
- package/lib/solution/Sales/index.js +328 -324
- package/lib/solution/Sales/types.js +27 -33
- package/lib/solution/ScanOrder/index.js +832 -864
- package/lib/solution/ScanOrder/types.js +33 -34
- package/lib/solution/ScanOrder/utils.js +409 -374
- package/lib/solution/ShopDiscount/index.js +226 -232
- package/lib/solution/ShopDiscount/types.js +15 -37
- package/lib/solution/ShopDiscount/utils.js +300 -172
- package/lib/solution/UnifiedBookingSales/index.js +1035 -1201
- package/lib/solution/UnifiedBookingSales/remoteOccupancyCache.js +99 -159
- package/lib/solution/UnifiedBookingSales/types.js +10 -32
- package/lib/solution/VenueBooking/index.js +811 -879
- package/lib/solution/VenueBooking/types.js +19 -39
- package/lib/solution/VenueBooking/utils/dateSummary.js +27 -56
- package/lib/solution/VenueBooking/utils/resource.js +31 -54
- package/lib/solution/VenueBooking/utils/slotMerge.js +94 -107
- package/lib/solution/VenueBooking/utils/timeSlot.js +132 -132
- package/lib/solution/VenueBooking/utils.js +130 -67
- package/lib/solution/index.js +124 -41
- package/lib/store/createStore.js +32 -29
- package/lib/types/index.js +4 -16
- package/lib/utils/payment-number.js +26 -46
- package/lib/utils/task.js +36 -36
- package/lib/utils/watch.js +81 -47
- package/package.json +2 -1
|
@@ -1,49 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
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
|
-
resolveWalletPassLineKey: () => resolveWalletPassLineKey
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
43
5
|
});
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
6
|
+
exports.getProductQuantity = exports.getMainProductPrice = exports.getBundleItemPrice = exports.getBundleItemIsOriginalPrice = exports.getBundleItemIsMarkupPrice = exports.getBundleItemIsMarkupOrDiscountPrice = exports.getBundleItemIsDiscountPrice = exports.getApplicableProductIds = void 0;
|
|
7
|
+
exports.processVouchers = processVouchers;
|
|
8
|
+
exports.recalculateVouchers = recalculateVouchers;
|
|
9
|
+
exports.resolveWalletPassLineKey = resolveWalletPassLineKey;
|
|
10
|
+
var _decimal = _interopRequireDefault(require("decimal.js"));
|
|
11
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
12
|
+
const DEFAULT_WALLET_PASS_CALCULATION_CONFIG = {
|
|
47
13
|
maxDeductionAmount: 99999,
|
|
48
14
|
maxUsagePerOrder: 0,
|
|
49
15
|
allowCrossProduct: true,
|
|
@@ -55,338 +21,477 @@ var DEFAULT_WALLET_PASS_CALCULATION_CONFIG = {
|
|
|
55
21
|
function getWalletPassCalculationConfig(voucher) {
|
|
56
22
|
return {
|
|
57
23
|
...DEFAULT_WALLET_PASS_CALCULATION_CONFIG,
|
|
58
|
-
...(voucher
|
|
24
|
+
...(voucher?.config || {})
|
|
59
25
|
};
|
|
60
26
|
}
|
|
61
|
-
|
|
27
|
+
|
|
28
|
+
/** 订单商品数量 */
|
|
29
|
+
const getProductQuantity = product => {
|
|
62
30
|
return product.quantity || product.product_quantity || 1;
|
|
63
31
|
};
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* 订单商品行唯一键,用于 maxPassesPerItem 按行、按件配额(同 SPU 不同规格视为不同行)。
|
|
35
|
+
*/
|
|
36
|
+
exports.getProductQuantity = getProductQuantity;
|
|
64
37
|
function resolveWalletPassLineKey(product, indexInOrder) {
|
|
65
|
-
const m =
|
|
38
|
+
const m = product?.metadata || {};
|
|
66
39
|
const u1 = m.product_unique;
|
|
67
|
-
if (u1 != null && String(u1).length > 0)
|
|
68
|
-
return String(u1);
|
|
40
|
+
if (u1 != null && String(u1).length > 0) return String(u1);
|
|
69
41
|
const u2 = m.unique_identification_number;
|
|
70
|
-
if (u2 != null && String(u2).length > 0)
|
|
71
|
-
|
|
72
|
-
if (
|
|
73
|
-
return `order_item:${product.id}`;
|
|
74
|
-
if ((product == null ? void 0 : product.product_unique_string) != null && String(product.product_unique_string).length > 0) {
|
|
42
|
+
if (u2 != null && String(u2).length > 0) return String(u2);
|
|
43
|
+
if (product?.id != null && String(product.id).length > 0) return `order_item:${product.id}`;
|
|
44
|
+
if (product?.product_unique_string != null && String(product.product_unique_string).length > 0) {
|
|
75
45
|
return String(product.product_unique_string);
|
|
76
46
|
}
|
|
77
47
|
return `order_line_${indexInOrder}`;
|
|
78
48
|
}
|
|
79
49
|
function getExpandedOrderLineQuantity(p) {
|
|
80
|
-
if (
|
|
81
|
-
return p._orderLineQuantity;
|
|
50
|
+
if (p?._orderLineQuantity != null && p._orderLineQuantity > 0) return p._orderLineQuantity;
|
|
82
51
|
return getProductQuantity(p);
|
|
83
52
|
}
|
|
53
|
+
|
|
54
|
+
/** 该行允许消耗的「券次」上限:maxPassesPerItem × 订单行件数 */
|
|
84
55
|
function getMaxPassSlotsForExpandedLine(maxPassesPerItem, p) {
|
|
85
|
-
if (maxPassesPerItem <= 0)
|
|
86
|
-
return Infinity;
|
|
56
|
+
if (maxPassesPerItem <= 0) return Infinity;
|
|
87
57
|
return maxPassesPerItem * getExpandedOrderLineQuantity(p);
|
|
88
58
|
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* 本次抵扣占用多少「券次」配额:有抵扣时至少占 1,并与按金额折算的件数挂钩,不超过剩余额度。
|
|
62
|
+
*/
|
|
89
63
|
function computePassSlotsIncrement(deductAmount, deductQty, maxPassesPerItem, orderLineQuantity, currentUsage) {
|
|
90
|
-
if (maxPassesPerItem <= 0 || !deductAmount.greaterThan(0))
|
|
91
|
-
return 0;
|
|
64
|
+
if (maxPassesPerItem <= 0 || !deductAmount.greaterThan(0)) return 0;
|
|
92
65
|
const cap = maxPassesPerItem * orderLineQuantity;
|
|
93
66
|
const room = Math.max(0, cap - currentUsage);
|
|
94
|
-
if (room <= 0)
|
|
95
|
-
return 0;
|
|
67
|
+
if (room <= 0) return 0;
|
|
96
68
|
const raw = Math.max(1, Math.ceil(Number(deductQty)) || 1);
|
|
97
69
|
return Math.min(raw, room);
|
|
98
70
|
}
|
|
99
71
|
function applyMaxPassUsageIncrements(usageMap, walletPassProductId, maxPassesPerItem, deductionDetails) {
|
|
100
|
-
deductionDetails.forEach(
|
|
101
|
-
var _a;
|
|
72
|
+
deductionDetails.forEach(detail => {
|
|
102
73
|
const lineKey = detail.lineKey;
|
|
103
|
-
if (!lineKey || maxPassesPerItem <= 0)
|
|
104
|
-
return;
|
|
74
|
+
if (!lineKey || maxPassesPerItem <= 0) return;
|
|
105
75
|
const orderLineQty = detail.orderLineQuantity != null && detail.orderLineQuantity > 0 ? detail.orderLineQuantity : 1;
|
|
106
|
-
const cur =
|
|
107
|
-
const inc = computePassSlotsIncrement(
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
maxPassesPerItem,
|
|
111
|
-
orderLineQty,
|
|
112
|
-
cur
|
|
113
|
-
);
|
|
114
|
-
if (inc <= 0)
|
|
115
|
-
return;
|
|
116
|
-
if (!usageMap.has(walletPassProductId))
|
|
117
|
-
usageMap.set(walletPassProductId, /* @__PURE__ */ new Map());
|
|
76
|
+
const cur = usageMap.get(walletPassProductId)?.get(lineKey) || 0;
|
|
77
|
+
const inc = computePassSlotsIncrement(new _decimal.default(detail.deductAmount), detail.deductQuantity, maxPassesPerItem, orderLineQty, cur);
|
|
78
|
+
if (inc <= 0) return;
|
|
79
|
+
if (!usageMap.has(walletPassProductId)) usageMap.set(walletPassProductId, new Map());
|
|
118
80
|
const inner = usageMap.get(walletPassProductId);
|
|
119
81
|
inner.set(lineKey, cur + inc);
|
|
120
82
|
});
|
|
121
83
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
84
|
+
|
|
85
|
+
// 辅助函数:根据 deductTaxAndFee 配置获取推荐使用金额
|
|
86
|
+
const getRecommendedAmount = voucher => {
|
|
87
|
+
const {
|
|
88
|
+
config,
|
|
89
|
+
recommended_usage_amount,
|
|
90
|
+
recommended_pure_product_usage_amount
|
|
91
|
+
} = voucher;
|
|
92
|
+
const deductTaxAndFee = config?.deductTaxAndFee ?? true; // 默认开启抵扣税费
|
|
93
|
+
|
|
94
|
+
// 扫码接口的 recommended_* 金额包含搜索时已选券的上下文,可能只是当时的
|
|
95
|
+
// 剩余可抵扣额。取消其他券后,前端重算需要回到资产自身金额基准,再由
|
|
96
|
+
// 剩余订单金额、适用商品金额和策略上限重新裁剪。
|
|
125
97
|
const rawRecalculationBaseAmount = voucher._wallet_asset_recalculation_base_amount;
|
|
126
|
-
if (rawRecalculationBaseAmount !==
|
|
98
|
+
if (rawRecalculationBaseAmount !== undefined && rawRecalculationBaseAmount !== null) {
|
|
127
99
|
const recalculationBaseAmount = Number(rawRecalculationBaseAmount);
|
|
128
100
|
if (Number.isFinite(recalculationBaseAmount) && recalculationBaseAmount >= 0) {
|
|
129
101
|
return recalculationBaseAmount;
|
|
130
102
|
}
|
|
131
103
|
}
|
|
104
|
+
|
|
105
|
+
// 如果开启抵扣税费,使用 recommended_usage_amount(包含税费)
|
|
106
|
+
// 如果关闭抵扣税费,使用 recommended_pure_product_usage_amount(仅商品金额)
|
|
132
107
|
return deductTaxAndFee ? recommended_usage_amount : recommended_pure_product_usage_amount ?? recommended_usage_amount;
|
|
133
108
|
};
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
109
|
+
|
|
110
|
+
// 辅助函数:获取适用商品ID列表
|
|
111
|
+
const getApplicableProductIds = voucher => {
|
|
112
|
+
const {
|
|
113
|
+
available_product_type,
|
|
114
|
+
available_product_ids
|
|
115
|
+
} = voucher;
|
|
116
|
+
|
|
117
|
+
// 如果没有 available_product_type,默认为 product_all
|
|
118
|
+
const productType = available_product_type || 'product_all';
|
|
119
|
+
|
|
120
|
+
// product_all 时,返回 null 表示适用所有商品
|
|
121
|
+
if (productType === 'product_all') {
|
|
138
122
|
return null;
|
|
139
123
|
}
|
|
140
|
-
|
|
124
|
+
|
|
125
|
+
// product_collection 或 products 时,返回指定的商品ID列表
|
|
126
|
+
if (productType === 'product_collection' || productType === 'products') {
|
|
141
127
|
return available_product_ids || [];
|
|
142
128
|
}
|
|
129
|
+
|
|
130
|
+
// 其他情况默认返回空数组(不适用任何商品)
|
|
143
131
|
return [];
|
|
144
132
|
};
|
|
145
|
-
|
|
133
|
+
|
|
134
|
+
// 辅助函数:计算适用商品的总金额(基于剩余金额)
|
|
135
|
+
exports.getApplicableProductIds = getApplicableProductIds;
|
|
136
|
+
const getApplicableProductsAmount = (voucher, productsData) => {
|
|
146
137
|
const applicableProductIds = getApplicableProductIds(voucher);
|
|
147
|
-
const {
|
|
148
|
-
|
|
149
|
-
|
|
138
|
+
const {
|
|
139
|
+
config
|
|
140
|
+
} = voucher;
|
|
141
|
+
const deductTaxAndFee = config?.deductTaxAndFee ?? true;
|
|
142
|
+
|
|
143
|
+
// 根据券的配置选择使用哪个金额字段
|
|
144
|
+
const amountField = deductTaxAndFee ? 'remainingAmountWithTax' : 'remainingAmountPure';
|
|
145
|
+
|
|
146
|
+
// 如果为 null,适用于所有商品
|
|
150
147
|
if (applicableProductIds === null) {
|
|
151
|
-
return productsData.filter(
|
|
148
|
+
return productsData.filter(p => p[amountField].greaterThan(0)).reduce((sum, p) => sum.plus(p[amountField]), new _decimal.default(0));
|
|
152
149
|
}
|
|
150
|
+
|
|
151
|
+
// 如果为空数组,不适用任何商品
|
|
153
152
|
if (applicableProductIds.length === 0) {
|
|
154
|
-
return new
|
|
153
|
+
return new _decimal.default(0);
|
|
155
154
|
}
|
|
156
|
-
|
|
155
|
+
|
|
156
|
+
// 计算指定商品的总金额
|
|
157
|
+
return productsData.filter(p => applicableProductIds.includes(p.product_id) && p[amountField].greaterThan(0)).reduce((sum, p) => sum.plus(p[amountField]), new _decimal.default(0));
|
|
157
158
|
};
|
|
158
|
-
|
|
159
|
+
|
|
160
|
+
// 辅助函数:获取适用的商品列表
|
|
161
|
+
const getApplicableProducts = (voucher, productsData) => {
|
|
159
162
|
const applicableProductIds = getApplicableProductIds(voucher);
|
|
163
|
+
|
|
164
|
+
// 如果为 null,适用于所有商品
|
|
160
165
|
if (applicableProductIds === null) {
|
|
161
166
|
return productsData;
|
|
162
167
|
}
|
|
168
|
+
|
|
169
|
+
// 如果为空数组,不适用任何商品
|
|
163
170
|
if (applicableProductIds.length === 0) {
|
|
164
171
|
return [];
|
|
165
172
|
}
|
|
166
|
-
|
|
173
|
+
|
|
174
|
+
// 返回指定的商品列表
|
|
175
|
+
return productsData.filter(p => applicableProductIds.includes(p.product_id));
|
|
167
176
|
};
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* 优惠券处理函数
|
|
180
|
+
* @param applicableVouchers 可用的券列表
|
|
181
|
+
* @param orderTotalAmount 订单总金额
|
|
182
|
+
* @param products 订单商品列表
|
|
183
|
+
* @returns 返回推荐券列表和全部列表,每个券包含 _available_max_amount 和 _unified_available_status
|
|
184
|
+
*/
|
|
168
185
|
function processVouchers(applicableVouchers, orderTotalAmount, products) {
|
|
186
|
+
// 拆分商品数据,同时维护含税和不含税两个金额池
|
|
187
|
+
// remainingAmountWithTax: 含税费的剩余可抵扣金额
|
|
188
|
+
// remainingAmountPure: 纯商品金额(不含税费)的剩余可抵扣金额
|
|
189
|
+
// 在抵扣时,会根据券的 deductTaxAndFee 配置选择从哪个金额池扣除
|
|
169
190
|
const productsCopy = expandProductsWithBundleItems(products, true);
|
|
170
|
-
let remainingOrderAmount = new
|
|
191
|
+
let remainingOrderAmount = new _decimal.default(orderTotalAmount); // 订单剩余应付金额
|
|
192
|
+
|
|
193
|
+
// ========== 辅助工具:maxPassesPerItem(按订单行 + 件数)追踪 ==========
|
|
171
194
|
const getItemPassUsage = (usageMap, walletPassProductId, lineKey) => {
|
|
172
|
-
|
|
173
|
-
return ((_a = usageMap.get(walletPassProductId)) == null ? void 0 : _a.get(lineKey)) || 0;
|
|
195
|
+
return usageMap.get(walletPassProductId)?.get(lineKey) || 0;
|
|
174
196
|
};
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
197
|
+
|
|
198
|
+
// 按 maxPassesPerItem 过滤:排除已达到该行「券次」上限的展开行(按 _walletPassLineKey)
|
|
199
|
+
const filterByMaxPassesPerItem = (products, usageMap, walletPassProductId, maxPassesPerItem) => {
|
|
200
|
+
if (maxPassesPerItem <= 0) return products;
|
|
201
|
+
return products.filter(p => {
|
|
179
202
|
const lineKey = p._walletPassLineKey;
|
|
180
|
-
if (!lineKey)
|
|
181
|
-
return true;
|
|
203
|
+
if (!lineKey) return true;
|
|
182
204
|
return getItemPassUsage(usageMap, walletPassProductId, lineKey) < getMaxPassSlotsForExpandedLine(maxPassesPerItem, p);
|
|
183
205
|
});
|
|
184
206
|
};
|
|
207
|
+
// ================================================================
|
|
208
|
+
|
|
209
|
+
// 辅助函数:计算单张券的 _available_max_amount
|
|
185
210
|
const calculateAvailableMaxAmount = (voucher, productsData, itemPassUsage) => {
|
|
186
|
-
const {
|
|
187
|
-
|
|
211
|
+
const {
|
|
212
|
+
config
|
|
213
|
+
} = voucher;
|
|
214
|
+
const {
|
|
215
|
+
maxDeductionAmount = 0,
|
|
216
|
+
allowCrossProduct = true,
|
|
217
|
+
applicableProductLimit = 0,
|
|
218
|
+
deductTaxAndFee = true,
|
|
219
|
+
maxPassesPerItem = 0
|
|
220
|
+
} = config ?? {};
|
|
221
|
+
|
|
222
|
+
// 根据 deductTaxAndFee 配置获取推荐金额
|
|
188
223
|
const recommendedAmount = getRecommendedAmount(voucher);
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
const
|
|
195
|
-
|
|
224
|
+
|
|
225
|
+
// 基础值 = min(recommendedAmount, maxDeductionAmount)
|
|
226
|
+
const baseAmount = _decimal.default.min(new _decimal.default(recommendedAmount), new _decimal.default(maxDeductionAmount));
|
|
227
|
+
|
|
228
|
+
// 根据券的配置选择使用哪个单价字段和金额字段
|
|
229
|
+
const unitPriceField = deductTaxAndFee ? 'unitPriceWithTax' : 'unitPricePure';
|
|
230
|
+
const amountField = deductTaxAndFee ? 'remainingAmountWithTax' : 'remainingAmountPure';
|
|
231
|
+
|
|
232
|
+
// 获取适用商品列表(只筛选有剩余金额的商品)
|
|
233
|
+
let applicableProducts = getApplicableProducts(voucher, productsData).filter(p => p[amountField].greaterThan(0));
|
|
234
|
+
|
|
235
|
+
// 按 maxPassesPerItem 过滤:排除已达到单商品可用卡券上限的商品
|
|
196
236
|
if (itemPassUsage) {
|
|
197
237
|
applicableProducts = filterByMaxPassesPerItem(applicableProducts, itemPassUsage, voucher.product_id, maxPassesPerItem);
|
|
198
238
|
}
|
|
199
239
|
if (applicableProducts.length === 0) {
|
|
200
|
-
return new
|
|
240
|
+
return new _decimal.default(0);
|
|
201
241
|
}
|
|
202
|
-
|
|
242
|
+
|
|
243
|
+
// 根据跨商品配置计算适用商品金额
|
|
244
|
+
let finalApplicableAmount = new _decimal.default(0);
|
|
203
245
|
if (allowCrossProduct) {
|
|
246
|
+
// 跨商品券:可以抵扣多个商品
|
|
204
247
|
if (applicableProductLimit > 0) {
|
|
248
|
+
// 有数量限制:按剩余金额从高到低排序,按动态计算的可抵扣数量累计直到达到 limit
|
|
205
249
|
const sortedProducts = [...applicableProducts].sort((a, b) => a[amountField].comparedTo(b[amountField]) > 0 ? -1 : 1);
|
|
206
250
|
let remainingLimit = applicableProductLimit;
|
|
207
251
|
for (const product of sortedProducts) {
|
|
208
|
-
if (remainingLimit <= 0)
|
|
209
|
-
|
|
252
|
+
if (remainingLimit <= 0) break;
|
|
253
|
+
// 动态计算当前可抵扣数量 = ceil(剩余金额 / 单价)
|
|
210
254
|
const currentAvailableQty = Math.ceil(product[amountField].dividedBy(product[unitPriceField]).toNumber());
|
|
211
255
|
const deductQty = Math.min(currentAvailableQty, remainingLimit);
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
product[amountField]
|
|
215
|
-
);
|
|
256
|
+
// 实际可抵扣金额 = min(数量 * 单价, 剩余金额)
|
|
257
|
+
const deductAmount = _decimal.default.min(product[unitPriceField].times(deductQty), product[amountField]);
|
|
216
258
|
finalApplicableAmount = finalApplicableAmount.plus(deductAmount);
|
|
217
259
|
remainingLimit -= deductQty;
|
|
218
260
|
}
|
|
219
261
|
} else {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
new import_decimal.default(0)
|
|
223
|
-
);
|
|
262
|
+
// 无数量限制:所有适用商品的剩余金额总和
|
|
263
|
+
finalApplicableAmount = applicableProducts.reduce((sum, p) => sum.plus(p[amountField]), new _decimal.default(0));
|
|
224
264
|
}
|
|
225
265
|
} else {
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
266
|
+
// 非跨商品券:选择单价最高的商品(优先抵扣高价商品)
|
|
267
|
+
const maxProduct = applicableProducts.reduce((max, p) => p[unitPriceField].greaterThan(max[unitPriceField]) ? p : max);
|
|
268
|
+
// maxPassesPerItem 限制每张券最多抵扣的单位数
|
|
229
269
|
if (maxPassesPerItem > 0) {
|
|
230
|
-
finalApplicableAmount =
|
|
231
|
-
maxProduct[unitPriceField].times(maxPassesPerItem),
|
|
232
|
-
maxProduct[amountField]
|
|
233
|
-
);
|
|
270
|
+
finalApplicableAmount = _decimal.default.min(maxProduct[unitPriceField].times(maxPassesPerItem), maxProduct[amountField]);
|
|
234
271
|
} else {
|
|
235
272
|
finalApplicableAmount = maxProduct[amountField];
|
|
236
273
|
}
|
|
237
274
|
}
|
|
238
|
-
|
|
275
|
+
|
|
276
|
+
// 返回最小值
|
|
277
|
+
return _decimal.default.min(baseAmount, finalApplicableAmount, remainingOrderAmount);
|
|
239
278
|
};
|
|
240
|
-
|
|
241
|
-
|
|
279
|
+
|
|
280
|
+
// 辅助函数:判断券是否可用,返回可用性和原因代码
|
|
281
|
+
const isVoucherAvailable = (voucher, productsData, usedVoucherCounts, itemPassUsage) => {
|
|
282
|
+
const {
|
|
283
|
+
config,
|
|
284
|
+
product_id
|
|
285
|
+
} = voucher;
|
|
286
|
+
|
|
287
|
+
// 根据 deductTaxAndFee 配置获取推荐金额
|
|
242
288
|
const recommendedAmount = getRecommendedAmount(voucher);
|
|
289
|
+
|
|
290
|
+
// 余额为 0
|
|
243
291
|
if (recommendedAmount <= 0) {
|
|
244
|
-
return {
|
|
292
|
+
return {
|
|
293
|
+
isAvailable: false,
|
|
294
|
+
reasonCode: 'not_meet_the_required_conditions'
|
|
295
|
+
};
|
|
245
296
|
}
|
|
297
|
+
|
|
298
|
+
// 订单金额已抵扣完
|
|
246
299
|
if (remainingOrderAmount.lessThanOrEqualTo(0)) {
|
|
247
|
-
return {
|
|
300
|
+
return {
|
|
301
|
+
isAvailable: false,
|
|
302
|
+
reasonCode: 'exceeds_the_maximum_deduction_limit'
|
|
303
|
+
};
|
|
248
304
|
}
|
|
305
|
+
|
|
306
|
+
// 适用商品金额已被抵扣完
|
|
249
307
|
const applicableAmount = getApplicableProductsAmount(voucher, productsData);
|
|
250
308
|
if (applicableAmount.lessThanOrEqualTo(0)) {
|
|
251
|
-
return {
|
|
309
|
+
return {
|
|
310
|
+
isAvailable: false,
|
|
311
|
+
reasonCode: 'not_meet_the_required_conditions'
|
|
312
|
+
};
|
|
252
313
|
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
314
|
+
|
|
315
|
+
// 检查 maxUsagePerOrder(同 product_id 的券),0 表示不限制
|
|
316
|
+
if ((config?.maxUsagePerOrder || 0) > 0) {
|
|
317
|
+
const usedCount = usedVoucherCounts.get(product_id) || 0;
|
|
318
|
+
if (usedCount >= (config?.maxUsagePerOrder || 0)) {
|
|
319
|
+
return {
|
|
320
|
+
isAvailable: false,
|
|
321
|
+
reasonCode: 'usage_limit_reached'
|
|
322
|
+
};
|
|
257
323
|
}
|
|
258
324
|
}
|
|
259
|
-
|
|
325
|
+
|
|
326
|
+
// 检查 maxPassesPerItem:如果所有适用商品都已达到单商品可用卡券上限,则不可用
|
|
327
|
+
const maxPassesPerItem = config?.maxPassesPerItem || 0;
|
|
260
328
|
if (maxPassesPerItem > 0 && itemPassUsage) {
|
|
261
|
-
const deductTaxAndFee =
|
|
262
|
-
const amountField = deductTaxAndFee ?
|
|
263
|
-
const availableAfterPassLimit = getApplicableProducts(voucher, productsData).filter(
|
|
329
|
+
const deductTaxAndFee = config?.deductTaxAndFee ?? true;
|
|
330
|
+
const amountField = deductTaxAndFee ? 'remainingAmountWithTax' : 'remainingAmountPure';
|
|
331
|
+
const availableAfterPassLimit = getApplicableProducts(voucher, productsData).filter(p => p[amountField].greaterThan(0)).filter(p => {
|
|
264
332
|
const lineKey = p._walletPassLineKey;
|
|
265
|
-
if (!lineKey)
|
|
266
|
-
return true;
|
|
333
|
+
if (!lineKey) return true;
|
|
267
334
|
return getItemPassUsage(itemPassUsage, product_id, lineKey) < getMaxPassSlotsForExpandedLine(maxPassesPerItem, p);
|
|
268
335
|
});
|
|
269
336
|
if (availableAfterPassLimit.length === 0) {
|
|
270
|
-
return {
|
|
337
|
+
return {
|
|
338
|
+
isAvailable: false,
|
|
339
|
+
reasonCode: 'max_passes_per_item_reached'
|
|
340
|
+
};
|
|
271
341
|
}
|
|
272
342
|
}
|
|
273
|
-
return {
|
|
343
|
+
return {
|
|
344
|
+
isAvailable: true
|
|
345
|
+
};
|
|
274
346
|
};
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
);
|
|
281
|
-
const availabilityResult = isVoucherAvailable(
|
|
282
|
-
voucher,
|
|
283
|
-
productsCopy,
|
|
284
|
-
usedVoucherCountsForAll
|
|
285
|
-
);
|
|
347
|
+
|
|
348
|
+
// ========== 第一步:为所有券计算初始的 _available_max_amount 和 _unified_available_status ==========
|
|
349
|
+
const usedVoucherCountsForAll = new Map();
|
|
350
|
+
const allVouchersWithStatus = applicableVouchers.map(voucher => {
|
|
351
|
+
const _available_max_amount = calculateAvailableMaxAmount(voucher, productsCopy);
|
|
352
|
+
const availabilityResult = isVoucherAvailable(voucher, productsCopy, usedVoucherCountsForAll);
|
|
286
353
|
const _unified_available_status = availabilityResult.isAvailable ? 1 : 0;
|
|
287
354
|
return {
|
|
288
355
|
...voucher,
|
|
289
356
|
_available_max_amount: _available_max_amount.toNumber(),
|
|
290
357
|
// 转换为数字
|
|
291
358
|
_unified_available_status,
|
|
292
|
-
...availabilityResult.reasonCode && {
|
|
359
|
+
...(availabilityResult.reasonCode && {
|
|
360
|
+
reasonCode: availabilityResult.reasonCode
|
|
361
|
+
})
|
|
293
362
|
};
|
|
294
363
|
});
|
|
364
|
+
|
|
365
|
+
// ========== 第二步:计算推荐券列表 ==========
|
|
295
366
|
const recommendedVouchers = [];
|
|
296
|
-
const usedVoucherCounts =
|
|
367
|
+
const usedVoucherCounts = new Map(); // 跟踪每个券 ID 的使用次数
|
|
368
|
+
|
|
369
|
+
// 重置商品余额追踪(同时维护含税和不含税两个金额池)
|
|
297
370
|
const productsForRecommendation = expandProductsWithBundleItems(products, true);
|
|
298
|
-
remainingOrderAmount = new
|
|
299
|
-
|
|
300
|
-
const
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
371
|
+
remainingOrderAmount = new _decimal.default(orderTotalAmount);
|
|
372
|
+
// 追踪推荐阶段每个 Wallet Pass 商品对每个订单商品行的已使用卡券次数(按行唯一键)
|
|
373
|
+
const itemPassUsageMap = new Map();
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
// 分类券:未跨商品券 vs 跨商品券
|
|
377
|
+
const nonCrossProductVouchers = applicableVouchers.filter(
|
|
378
|
+
(v) => !v.config?.allowCrossProduct,
|
|
379
|
+
);
|
|
380
|
+
const crossProductVouchers = applicableVouchers.filter(
|
|
381
|
+
(v) => v.config?.allowCrossProduct,
|
|
382
|
+
);
|
|
383
|
+
// 按 tag 分组
|
|
384
|
+
const groupByType = (vouchers: any[]) => {
|
|
385
|
+
const groups = new Map<string, any[]>();
|
|
386
|
+
vouchers.forEach((v) => {
|
|
387
|
+
const tag = v.tag;
|
|
388
|
+
if (!groups.has(tag)) {
|
|
389
|
+
groups.set(tag, []);
|
|
390
|
+
}
|
|
391
|
+
groups.get(tag)!.push(v);
|
|
392
|
+
});
|
|
393
|
+
return groups;
|
|
394
|
+
};
|
|
395
|
+
// 对每组券按金额(balance)从大到小排序
|
|
396
|
+
const sortVouchersByAmount = (vouchers: Voucher[]) => {
|
|
397
|
+
return [...vouchers].sort((a, b) => getRecommendedAmount(b) - getRecommendedAmount(a));
|
|
398
|
+
};
|
|
399
|
+
*/
|
|
400
|
+
|
|
401
|
+
// 处理单张券的抵扣
|
|
402
|
+
const applyVoucher = voucher => {
|
|
403
|
+
// 检查是否可用
|
|
404
|
+
const availabilityCheck = isVoucherAvailable(voucher, productsForRecommendation, usedVoucherCounts, itemPassUsageMap);
|
|
307
405
|
if (!availabilityCheck.isAvailable) {
|
|
308
406
|
return false;
|
|
309
407
|
}
|
|
310
|
-
const {
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
408
|
+
const {
|
|
409
|
+
config,
|
|
410
|
+
id,
|
|
411
|
+
product_id
|
|
412
|
+
} = voucher;
|
|
413
|
+
const {
|
|
414
|
+
maxDeductionAmount = 0,
|
|
415
|
+
allowCrossProduct = true,
|
|
416
|
+
applicableProductLimit = 0,
|
|
417
|
+
deductTaxAndFee = true,
|
|
418
|
+
maxPassesPerItem = 0
|
|
419
|
+
} = config ?? {};
|
|
420
|
+
|
|
421
|
+
// 根据券的配置选择使用哪个单价字段和金额字段
|
|
422
|
+
const unitPriceField = deductTaxAndFee ? 'unitPriceWithTax' : 'unitPricePure';
|
|
423
|
+
const amountField = deductTaxAndFee ? 'remainingAmountWithTax' : 'remainingAmountPure';
|
|
424
|
+
|
|
425
|
+
// 获取适用商品(只筛选有剩余金额的商品)
|
|
426
|
+
let applicableProducts = getApplicableProducts(voucher, productsForRecommendation).filter(p => p[amountField].greaterThan(0));
|
|
427
|
+
|
|
428
|
+
// 按 maxPassesPerItem 过滤:排除已达到单商品可用卡券上限的商品
|
|
318
429
|
applicableProducts = filterByMaxPassesPerItem(applicableProducts, itemPassUsageMap, product_id, maxPassesPerItem);
|
|
319
|
-
if (applicableProducts.length === 0)
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
);
|
|
326
|
-
let calculatedAvailableMaxAmount = new import_decimal.default(0);
|
|
430
|
+
if (applicableProducts.length === 0) return false;
|
|
431
|
+
|
|
432
|
+
// ========== 关键修改:在应用券之前,基于当前剩余金额计算 _available_max_amount ==========
|
|
433
|
+
// 优先使用用户手动编辑的金额,否则根据 deductTaxAndFee 配置获取推荐金额
|
|
434
|
+
const usageAmount = typeof voucher.edit_current_amount === 'number' ? voucher.edit_current_amount : getRecommendedAmount(voucher);
|
|
435
|
+
const baseAmount = _decimal.default.min(new _decimal.default(usageAmount), new _decimal.default(maxDeductionAmount));
|
|
436
|
+
let calculatedAvailableMaxAmount = new _decimal.default(0);
|
|
327
437
|
if (allowCrossProduct) {
|
|
438
|
+
// 跨商品券:按 quantity 限制计算可抵扣金额
|
|
328
439
|
if (applicableProductLimit > 0) {
|
|
440
|
+
// 按剩余金额从高到低排序
|
|
329
441
|
const sortedProducts = [...applicableProducts].sort((a, b) => a[amountField].comparedTo(b[amountField]) > 0 ? -1 : 1);
|
|
330
442
|
let remainingLimit = applicableProductLimit;
|
|
331
443
|
for (const product of sortedProducts) {
|
|
332
|
-
if (remainingLimit <= 0)
|
|
333
|
-
|
|
444
|
+
if (remainingLimit <= 0) break;
|
|
445
|
+
// 动态计算当前可抵扣数量 = ceil(剩余金额 / 单价)
|
|
334
446
|
const currentAvailableQty = Math.ceil(product[amountField].dividedBy(product[unitPriceField]).toNumber());
|
|
335
447
|
const deductQty = Math.min(currentAvailableQty, remainingLimit);
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
product[amountField]
|
|
339
|
-
);
|
|
448
|
+
// 实际可抵扣金额 = min(数量 * 单价, 剩余金额)
|
|
449
|
+
const deductAmount = _decimal.default.min(product[unitPriceField].times(deductQty), product[amountField]);
|
|
340
450
|
calculatedAvailableMaxAmount = calculatedAvailableMaxAmount.plus(deductAmount);
|
|
341
451
|
remainingLimit -= deductQty;
|
|
342
452
|
}
|
|
343
453
|
} else {
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
new import_decimal.default(0)
|
|
347
|
-
);
|
|
454
|
+
// 无数量限制:所有适用商品的剩余金额总和
|
|
455
|
+
calculatedAvailableMaxAmount = applicableProducts.reduce((sum, p) => sum.plus(p[amountField]), new _decimal.default(0));
|
|
348
456
|
}
|
|
349
457
|
} else {
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
458
|
+
// 非跨商品券:选择单价最高的商品(优先抵扣高价商品)
|
|
459
|
+
const maxProduct = applicableProducts.reduce((max, p) => p[unitPriceField].greaterThan(max[unitPriceField]) ? p : max);
|
|
460
|
+
// maxPassesPerItem 限制每张券最多抵扣的单位数
|
|
353
461
|
if (maxPassesPerItem > 0) {
|
|
354
|
-
calculatedAvailableMaxAmount =
|
|
355
|
-
maxProduct[unitPriceField].times(maxPassesPerItem),
|
|
356
|
-
maxProduct[amountField]
|
|
357
|
-
);
|
|
462
|
+
calculatedAvailableMaxAmount = _decimal.default.min(maxProduct[unitPriceField].times(maxPassesPerItem), maxProduct[amountField]);
|
|
358
463
|
} else {
|
|
359
464
|
calculatedAvailableMaxAmount = maxProduct[amountField];
|
|
360
465
|
}
|
|
361
466
|
}
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
new import_decimal.default(maxDeductionAmount),
|
|
370
|
-
remainingOrderAmount
|
|
371
|
-
);
|
|
467
|
+
|
|
468
|
+
// 取最小值:min(recommended_usage_amount, maxDeductionAmount, 适用商品金额, 订单剩余金额)
|
|
469
|
+
const availableMaxAmount = _decimal.default.min(baseAmount, calculatedAvailableMaxAmount, remainingOrderAmount);
|
|
470
|
+
// ======================================================================================
|
|
471
|
+
|
|
472
|
+
// 计算本次抵扣金额(使用同样的 usageAmount)
|
|
473
|
+
const maxDeduction = _decimal.default.min(new _decimal.default(usageAmount), new _decimal.default(maxDeductionAmount), remainingOrderAmount);
|
|
372
474
|
let deductionLeft = maxDeduction;
|
|
373
475
|
const deductionDetails = [];
|
|
374
476
|
if (allowCrossProduct) {
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
);
|
|
477
|
+
// 跨商品券:按剩余金额从高到低抵扣,受 applicableProductLimit 限制
|
|
478
|
+
const sortedProducts = [...applicableProducts].sort((a, b) => a[amountField].comparedTo(b[amountField]) > 0 ? -1 : 1);
|
|
378
479
|
let remainingLimit = applicableProductLimit > 0 ? applicableProductLimit : Infinity;
|
|
379
480
|
for (const product of sortedProducts) {
|
|
380
|
-
if (deductionLeft.lessThanOrEqualTo(0) || remainingLimit <= 0)
|
|
381
|
-
|
|
481
|
+
if (deductionLeft.lessThanOrEqualTo(0) || remainingLimit <= 0) break;
|
|
482
|
+
|
|
483
|
+
// 动态计算当前可抵扣数量 = ceil(剩余金额 / 单价)
|
|
382
484
|
const currentAvailableQty = Math.ceil(product[amountField].dividedBy(product[unitPriceField]).toNumber());
|
|
383
485
|
const availableQty = Math.min(currentAvailableQty, remainingLimit);
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
);
|
|
388
|
-
|
|
486
|
+
|
|
487
|
+
// 计算本商品最大可抵扣金额 = min(数量 * 单价, 剩余金额)
|
|
488
|
+
const maxDeductForProduct = _decimal.default.min(product[unitPriceField].times(availableQty), product[amountField]);
|
|
489
|
+
const actualDeductAmount = _decimal.default.min(deductionLeft, maxDeductForProduct);
|
|
490
|
+
|
|
491
|
+
// 计算实际抵扣的数量(用于记录和配额计算)
|
|
389
492
|
const actualDeductQty = Math.ceil(actualDeductAmount.dividedBy(product[unitPriceField]).toNumber());
|
|
493
|
+
|
|
494
|
+
// 只更新商品的剩余金额,不更新 remainingQuantity
|
|
390
495
|
product[amountField] = product[amountField].minus(actualDeductAmount);
|
|
391
496
|
deductionLeft = deductionLeft.minus(actualDeductAmount);
|
|
392
497
|
remainingLimit -= actualDeductQty;
|
|
@@ -398,23 +503,24 @@ function processVouchers(applicableVouchers, orderTotalAmount, products) {
|
|
|
398
503
|
orderLineQuantity: getExpandedOrderLineQuantity(product),
|
|
399
504
|
deductAmount: actualDeductAmount.toNumber(),
|
|
400
505
|
// 转换为数字
|
|
401
|
-
deductQuantity: actualDeductQty
|
|
402
|
-
// 抵扣涉及的数量(用于记录)
|
|
506
|
+
deductQuantity: actualDeductQty // 抵扣涉及的数量(用于记录)
|
|
403
507
|
});
|
|
404
508
|
}
|
|
405
509
|
} else {
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
510
|
+
// 非跨商品券:选择单价最高的商品(优先抵扣高价商品)
|
|
511
|
+
const targetProduct = applicableProducts.reduce((max, p) => p[unitPriceField].greaterThan(max[unitPriceField]) ? p : max);
|
|
512
|
+
|
|
513
|
+
// maxPassesPerItem 限制每张券最多抵扣的单位数
|
|
409
514
|
let maxDeductForProduct = targetProduct[amountField];
|
|
410
515
|
if (maxPassesPerItem > 0) {
|
|
411
|
-
maxDeductForProduct =
|
|
412
|
-
targetProduct[unitPriceField].times(maxPassesPerItem),
|
|
413
|
-
targetProduct[amountField]
|
|
414
|
-
);
|
|
516
|
+
maxDeductForProduct = _decimal.default.min(targetProduct[unitPriceField].times(maxPassesPerItem), targetProduct[amountField]);
|
|
415
517
|
}
|
|
416
|
-
const actualDeductAmount =
|
|
518
|
+
const actualDeductAmount = _decimal.default.min(deductionLeft, maxDeductForProduct);
|
|
519
|
+
|
|
520
|
+
// 计算实际抵扣的数量
|
|
417
521
|
const actualDeductQty = Math.ceil(actualDeductAmount.dividedBy(targetProduct[unitPriceField]).toNumber());
|
|
522
|
+
|
|
523
|
+
// 只更新商品的剩余金额,不更新 remainingQuantity
|
|
418
524
|
targetProduct[amountField] = targetProduct[amountField].minus(actualDeductAmount);
|
|
419
525
|
deductionLeft = deductionLeft.minus(actualDeductAmount);
|
|
420
526
|
deductionDetails.push({
|
|
@@ -429,9 +535,14 @@ function processVouchers(applicableVouchers, orderTotalAmount, products) {
|
|
|
429
535
|
}
|
|
430
536
|
const totalDeducted = maxDeduction.minus(deductionLeft);
|
|
431
537
|
if (totalDeducted.greaterThan(0)) {
|
|
538
|
+
// 更新订单剩余金额
|
|
432
539
|
remainingOrderAmount = remainingOrderAmount.minus(totalDeducted);
|
|
540
|
+
|
|
541
|
+
// 更新券使用次数(按 product_id 统计)
|
|
433
542
|
usedVoucherCounts.set(product_id, (usedVoucherCounts.get(product_id) || 0) + 1);
|
|
434
543
|
applyMaxPassUsageIncrements(itemPassUsageMap, product_id, maxPassesPerItem, deductionDetails);
|
|
544
|
+
|
|
545
|
+
// 添加到推荐列表(包含基于当前剩余金额计算的 available_max_amount)
|
|
435
546
|
recommendedVouchers.push({
|
|
436
547
|
...voucher,
|
|
437
548
|
actualDeduction: totalDeducted.toNumber(),
|
|
@@ -445,17 +556,47 @@ function processVouchers(applicableVouchers, orderTotalAmount, products) {
|
|
|
445
556
|
}
|
|
446
557
|
return false;
|
|
447
558
|
};
|
|
448
|
-
|
|
559
|
+
|
|
560
|
+
/**
|
|
561
|
+
// 第一轮:处理未跨商品券
|
|
562
|
+
const nonCrossGroups = groupByType(nonCrossProductVouchers);
|
|
563
|
+
nonCrossGroups.forEach((vouchersInGroup) => {
|
|
564
|
+
const sortedVouchers = sortVouchersByAmount(vouchersInGroup);
|
|
565
|
+
sortedVouchers.forEach((voucher) => {
|
|
566
|
+
applyVoucher(voucher);
|
|
567
|
+
});
|
|
568
|
+
});
|
|
569
|
+
// 第二轮:处理跨商品券
|
|
570
|
+
const crossGroups = groupByType(crossProductVouchers);
|
|
571
|
+
crossGroups.forEach((vouchersInGroup) => {
|
|
572
|
+
const sortedVouchers = sortVouchersByAmount(vouchersInGroup);
|
|
573
|
+
sortedVouchers.forEach((voucher) => {
|
|
574
|
+
applyVoucher(voucher);
|
|
575
|
+
});
|
|
576
|
+
});
|
|
577
|
+
*/
|
|
578
|
+
|
|
579
|
+
// 直接按 applicableVouchers 的顺序处理所有券
|
|
580
|
+
applicableVouchers.forEach(voucher => {
|
|
449
581
|
applyVoucher(voucher);
|
|
450
582
|
});
|
|
451
|
-
|
|
452
|
-
recommendedVouchers
|
|
583
|
+
|
|
584
|
+
// recommendedVouchers 已经包含了基于应用时计算的 _available_max_amount 和 _unified_available_status
|
|
585
|
+
|
|
586
|
+
// ========== 第三步:构建 all 数组,以 recommended 为基础 ==========
|
|
587
|
+
// 创建 recommended 券的 Map,以 id 为 key
|
|
588
|
+
const recommendedMap = new Map();
|
|
589
|
+
recommendedVouchers.forEach(v => {
|
|
453
590
|
recommendedMap.set(v.id, v);
|
|
454
591
|
});
|
|
455
|
-
|
|
592
|
+
|
|
593
|
+
// 遍历所有券,如果在 recommended 中则使用增强数据,否则使用基础数据
|
|
594
|
+
const allWithEnhancedData = allVouchersWithStatus.map(voucher => {
|
|
456
595
|
if (recommendedMap.has(voucher.id)) {
|
|
596
|
+
// 使用 recommended 中的增强数据
|
|
457
597
|
return recommendedMap.get(voucher.id);
|
|
458
598
|
} else {
|
|
599
|
+
// 不在 recommended 中的券,补充基础字段
|
|
459
600
|
return {
|
|
460
601
|
...voucher,
|
|
461
602
|
actualDeduction: 0,
|
|
@@ -469,28 +610,41 @@ function processVouchers(applicableVouchers, orderTotalAmount, products) {
|
|
|
469
610
|
transformList: allWithEnhancedData
|
|
470
611
|
};
|
|
471
612
|
}
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* 重新计算优惠券状态(基于已选券的增量计算)
|
|
616
|
+
* @param allVouchers 所有原始券列表
|
|
617
|
+
* @param selectedVouchers 已选中的券列表(按选中顺序)
|
|
618
|
+
* @param orderTotalAmount 订单总金额
|
|
619
|
+
* @param products 订单商品列表
|
|
620
|
+
* @returns 返回更新后的所有券列表和已选券的详细抵扣信息
|
|
621
|
+
*/
|
|
472
622
|
function recalculateVouchers(allVouchers, selectedVouchers, orderTotalAmount, products) {
|
|
623
|
+
// 深拷贝并拆分商品列表(包含主商品和原价子商品,同时维护含税和不含税两个金额池),用于计算
|
|
473
624
|
const productsForCalc = expandProductsWithBundleItems(products, true);
|
|
474
|
-
let remainingOrderAmount = new
|
|
625
|
+
let remainingOrderAmount = new _decimal.default(orderTotalAmount);
|
|
475
626
|
const selectedWithDetails = [];
|
|
476
|
-
const usedVoucherCounts =
|
|
477
|
-
|
|
627
|
+
const usedVoucherCounts = new Map();
|
|
628
|
+
|
|
629
|
+
// 追踪每个 Wallet Pass 商品对每个订单商品行的已使用卡券次数(按行唯一键)
|
|
630
|
+
const itemPassUsageMap = new Map();
|
|
478
631
|
const getItemPassUsageRecalc = (walletPassProductId, lineKey) => {
|
|
479
|
-
|
|
480
|
-
return ((_a = itemPassUsageMap.get(walletPassProductId)) == null ? void 0 : _a.get(lineKey)) || 0;
|
|
632
|
+
return itemPassUsageMap.get(walletPassProductId)?.get(lineKey) || 0;
|
|
481
633
|
};
|
|
482
|
-
const filterByMaxPassesPerItemRecalc = (
|
|
483
|
-
if (maxPassesPerItem <= 0)
|
|
484
|
-
|
|
485
|
-
return products2.filter((p) => {
|
|
634
|
+
const filterByMaxPassesPerItemRecalc = (products, walletPassProductId, maxPassesPerItem) => {
|
|
635
|
+
if (maxPassesPerItem <= 0) return products;
|
|
636
|
+
return products.filter(p => {
|
|
486
637
|
const lineKey = p._walletPassLineKey;
|
|
487
|
-
if (!lineKey)
|
|
488
|
-
return true;
|
|
638
|
+
if (!lineKey) return true;
|
|
489
639
|
return getItemPassUsageRecalc(walletPassProductId, lineKey) < getMaxPassSlotsForExpandedLine(maxPassesPerItem, p);
|
|
490
640
|
});
|
|
491
641
|
};
|
|
492
|
-
|
|
493
|
-
|
|
642
|
+
|
|
643
|
+
// 第一步:按顺序应用已选中的券,计算实际抵扣
|
|
644
|
+
selectedVouchers.forEach(selectedVoucher => {
|
|
645
|
+
const {
|
|
646
|
+
product_id
|
|
647
|
+
} = selectedVoucher;
|
|
494
648
|
const config = getWalletPassCalculationConfig(selectedVoucher);
|
|
495
649
|
const {
|
|
496
650
|
maxDeductionAmount,
|
|
@@ -509,18 +663,22 @@ function recalculateVouchers(allVouchers, selectedVouchers, orderTotalAmount, pr
|
|
|
509
663
|
deductionDetails: [],
|
|
510
664
|
_available_max_amount: 0,
|
|
511
665
|
_unified_available_status: 0,
|
|
512
|
-
reasonCode:
|
|
666
|
+
reasonCode: 'usage_limit_reached'
|
|
513
667
|
});
|
|
514
668
|
return;
|
|
515
669
|
}
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
670
|
+
|
|
671
|
+
// 根据券的配置选择使用哪个单价字段和金额字段
|
|
672
|
+
const unitPriceField = deductTaxAndFee ? 'unitPriceWithTax' : 'unitPricePure';
|
|
673
|
+
const amountField = deductTaxAndFee ? 'remainingAmountWithTax' : 'remainingAmountPure';
|
|
674
|
+
|
|
675
|
+
// 获取适用商品(只筛选有剩余金额的商品)
|
|
676
|
+
let applicableProducts = getApplicableProducts(selectedVoucher, productsForCalc).filter(p => p[amountField].greaterThan(0));
|
|
677
|
+
|
|
678
|
+
// 按 maxPassesPerItem 过滤:排除已达到该行券次上限的展开行
|
|
522
679
|
applicableProducts = filterByMaxPassesPerItemRecalc(applicableProducts, selectedVoucher.product_id, maxPassesPerItem);
|
|
523
680
|
if (applicableProducts.length === 0) {
|
|
681
|
+
// 无适用商品,跳过
|
|
524
682
|
selectedWithDetails.push({
|
|
525
683
|
...selectedVoucher,
|
|
526
684
|
config,
|
|
@@ -528,34 +686,38 @@ function recalculateVouchers(allVouchers, selectedVouchers, orderTotalAmount, pr
|
|
|
528
686
|
deductionDetails: [],
|
|
529
687
|
_available_max_amount: 0,
|
|
530
688
|
_unified_available_status: 0,
|
|
531
|
-
reasonCode:
|
|
689
|
+
reasonCode: 'not_meet_the_required_conditions'
|
|
532
690
|
});
|
|
533
691
|
return;
|
|
534
692
|
}
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
);
|
|
693
|
+
|
|
694
|
+
// 计算本次抵扣金额:
|
|
695
|
+
// - 用户手动编辑时以编辑金额为准;
|
|
696
|
+
// - 自动推荐/重算时沿用上一轮基于剩余待付金额算出的 _available_max_amount,
|
|
697
|
+
// 避免 recalculateVouchers 又回退到后端单券 recommended_usage_amount 造成超付。
|
|
698
|
+
const usageAmount = typeof selectedVoucher.edit_current_amount === 'number' ? selectedVoucher.edit_current_amount : selectedVoucher._available_max_amount ?? getRecommendedAmount(selectedVoucher);
|
|
699
|
+
const maxDeduction = _decimal.default.min(new _decimal.default(usageAmount), new _decimal.default(maxDeductionAmount), remainingOrderAmount);
|
|
541
700
|
let deductionLeft = maxDeduction;
|
|
542
701
|
const deductionDetails = [];
|
|
543
702
|
if (allowCrossProduct) {
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
);
|
|
703
|
+
// 跨商品券:按剩余金额从高到低抵扣,受 applicableProductLimit 限制
|
|
704
|
+
const sortedProducts = [...applicableProducts].sort((a, b) => a[amountField].comparedTo(b[amountField]) > 0 ? -1 : 1);
|
|
547
705
|
let remainingLimit = applicableProductLimit > 0 ? applicableProductLimit : Infinity;
|
|
548
706
|
for (const product of sortedProducts) {
|
|
549
|
-
if (deductionLeft.lessThanOrEqualTo(0) || remainingLimit <= 0)
|
|
550
|
-
|
|
707
|
+
if (deductionLeft.lessThanOrEqualTo(0) || remainingLimit <= 0) break;
|
|
708
|
+
|
|
709
|
+
// 动态计算当前可抵扣数量 = ceil(剩余金额 / 单价)
|
|
551
710
|
const currentAvailableQty = Math.ceil(product[amountField].dividedBy(product[unitPriceField]).toNumber());
|
|
552
711
|
const availableQty = Math.min(currentAvailableQty, remainingLimit);
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
);
|
|
557
|
-
|
|
712
|
+
|
|
713
|
+
// 计算本商品最大可抵扣金额 = min(数量 * 单价, 剩余金额)
|
|
714
|
+
const maxDeductForProduct = _decimal.default.min(product[unitPriceField].times(availableQty), product[amountField]);
|
|
715
|
+
const actualDeductAmount = _decimal.default.min(deductionLeft, maxDeductForProduct);
|
|
716
|
+
|
|
717
|
+
// 计算实际抵扣的数量(用于记录和配额计算)
|
|
558
718
|
const actualDeductQty = Math.ceil(actualDeductAmount.dividedBy(product[unitPriceField]).toNumber());
|
|
719
|
+
|
|
720
|
+
// 只更新商品的剩余金额,不更新 remainingQuantity
|
|
559
721
|
product[amountField] = product[amountField].minus(actualDeductAmount);
|
|
560
722
|
deductionLeft = deductionLeft.minus(actualDeductAmount);
|
|
561
723
|
remainingLimit -= actualDeductQty;
|
|
@@ -567,23 +729,24 @@ function recalculateVouchers(allVouchers, selectedVouchers, orderTotalAmount, pr
|
|
|
567
729
|
orderLineQuantity: getExpandedOrderLineQuantity(product),
|
|
568
730
|
deductAmount: actualDeductAmount.toNumber(),
|
|
569
731
|
// 转换为数字
|
|
570
|
-
deductQuantity: actualDeductQty
|
|
571
|
-
// 抵扣涉及的数量(用于记录)
|
|
732
|
+
deductQuantity: actualDeductQty // 抵扣涉及的数量(用于记录)
|
|
572
733
|
});
|
|
573
734
|
}
|
|
574
735
|
} else {
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
736
|
+
// 非跨商品券:选择单价最高的商品(优先抵扣高价商品)
|
|
737
|
+
const targetProduct = applicableProducts.reduce((max, p) => p[unitPriceField].greaterThan(max[unitPriceField]) ? p : max);
|
|
738
|
+
|
|
739
|
+
// maxPassesPerItem 限制每张券最多抵扣的单位数
|
|
578
740
|
let maxDeductForProduct = targetProduct[amountField];
|
|
579
741
|
if (maxPassesPerItem > 0) {
|
|
580
|
-
maxDeductForProduct =
|
|
581
|
-
targetProduct[unitPriceField].times(maxPassesPerItem),
|
|
582
|
-
targetProduct[amountField]
|
|
583
|
-
);
|
|
742
|
+
maxDeductForProduct = _decimal.default.min(targetProduct[unitPriceField].times(maxPassesPerItem), targetProduct[amountField]);
|
|
584
743
|
}
|
|
585
|
-
const actualDeductAmount =
|
|
744
|
+
const actualDeductAmount = _decimal.default.min(deductionLeft, maxDeductForProduct);
|
|
745
|
+
|
|
746
|
+
// 计算实际抵扣的数量
|
|
586
747
|
const actualDeductQty = Math.ceil(actualDeductAmount.dividedBy(targetProduct[unitPriceField]).toNumber());
|
|
748
|
+
|
|
749
|
+
// 只更新商品的剩余金额,不更新 remainingQuantity
|
|
587
750
|
targetProduct[amountField] = targetProduct[amountField].minus(actualDeductAmount);
|
|
588
751
|
deductionLeft = deductionLeft.minus(actualDeductAmount);
|
|
589
752
|
deductionDetails.push({
|
|
@@ -597,6 +760,8 @@ function recalculateVouchers(allVouchers, selectedVouchers, orderTotalAmount, pr
|
|
|
597
760
|
});
|
|
598
761
|
}
|
|
599
762
|
const totalDeducted = maxDeduction.minus(deductionLeft);
|
|
763
|
+
|
|
764
|
+
// 更新订单剩余金额
|
|
600
765
|
remainingOrderAmount = remainingOrderAmount.minus(totalDeducted);
|
|
601
766
|
applyMaxPassUsageIncrements(itemPassUsageMap, selectedVoucher.product_id, maxPassesPerItem, deductionDetails);
|
|
602
767
|
selectedWithDetails.push({
|
|
@@ -613,15 +778,20 @@ function recalculateVouchers(allVouchers, selectedVouchers, orderTotalAmount, pr
|
|
|
613
778
|
usedVoucherCounts.set(product_id, usedCount + 1);
|
|
614
779
|
}
|
|
615
780
|
});
|
|
616
|
-
|
|
617
|
-
|
|
781
|
+
|
|
782
|
+
// 第二步:为所有券重新计算 _available_max_amount 和 _unified_available_status
|
|
783
|
+
const selectedIds = new Set(selectedVouchers.map(v => v.id));
|
|
784
|
+
const allWithUpdatedStatus = allVouchers.map(voucher => {
|
|
785
|
+
// 如果是已选中的券,使用详细计算结果
|
|
618
786
|
if (selectedIds.has(voucher.id)) {
|
|
619
|
-
const selectedDetail = selectedWithDetails.find(
|
|
620
|
-
(v) => v.id === voucher.id
|
|
621
|
-
);
|
|
787
|
+
const selectedDetail = selectedWithDetails.find(v => v.id === voucher.id);
|
|
622
788
|
return selectedDetail || voucher;
|
|
623
789
|
}
|
|
624
|
-
|
|
790
|
+
|
|
791
|
+
// 未选中的券,重新计算其可用状态
|
|
792
|
+
const {
|
|
793
|
+
product_id
|
|
794
|
+
} = voucher;
|
|
625
795
|
const config = getWalletPassCalculationConfig(voucher);
|
|
626
796
|
const {
|
|
627
797
|
maxDeductionAmount,
|
|
@@ -630,83 +800,78 @@ function recalculateVouchers(allVouchers, selectedVouchers, orderTotalAmount, pr
|
|
|
630
800
|
deductTaxAndFee,
|
|
631
801
|
maxPassesPerItem
|
|
632
802
|
} = config;
|
|
633
|
-
|
|
634
|
-
|
|
803
|
+
|
|
804
|
+
// 根据券的配置选择使用哪个单价字段和金额字段
|
|
805
|
+
const unitPriceField = deductTaxAndFee ? 'unitPriceWithTax' : 'unitPricePure';
|
|
806
|
+
const amountField = deductTaxAndFee ? 'remainingAmountWithTax' : 'remainingAmountPure';
|
|
807
|
+
|
|
808
|
+
// 根据 deductTaxAndFee 配置获取推荐金额
|
|
635
809
|
const recommendedAmount = getRecommendedAmount(voucher);
|
|
810
|
+
|
|
811
|
+
// 检查基本可用性
|
|
636
812
|
let isAvailable = true;
|
|
637
|
-
let calculatedMaxAmount = new
|
|
813
|
+
let calculatedMaxAmount = new _decimal.default(0);
|
|
638
814
|
let reasonCode;
|
|
639
815
|
if (recommendedAmount <= 0) {
|
|
640
816
|
isAvailable = false;
|
|
641
|
-
reasonCode =
|
|
817
|
+
reasonCode = 'not_meet_the_required_conditions';
|
|
642
818
|
} else if (remainingOrderAmount.lessThanOrEqualTo(0)) {
|
|
643
819
|
isAvailable = false;
|
|
644
|
-
reasonCode =
|
|
820
|
+
reasonCode = 'exceeds_the_maximum_deduction_limit';
|
|
645
821
|
} else {
|
|
822
|
+
// 检查 maxUsagePerOrder(按 product_id 统计),0 表示不限制
|
|
646
823
|
if (config.maxUsagePerOrder > 0) {
|
|
647
824
|
const usedCount = usedVoucherCounts.get(product_id) || 0;
|
|
648
825
|
if (usedCount >= config.maxUsagePerOrder) {
|
|
649
826
|
isAvailable = false;
|
|
650
|
-
reasonCode =
|
|
827
|
+
reasonCode = 'usage_limit_reached';
|
|
651
828
|
}
|
|
652
829
|
}
|
|
653
830
|
if (isAvailable) {
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
831
|
+
// 获取适用商品(只筛选有剩余金额的商品)
|
|
832
|
+
let applicableProducts = getApplicableProducts(voucher, productsForCalc).filter(p => p[amountField].greaterThan(0));
|
|
833
|
+
|
|
834
|
+
// 按 maxPassesPerItem 过滤:排除已达到单商品可用卡券上限的商品
|
|
658
835
|
applicableProducts = filterByMaxPassesPerItemRecalc(applicableProducts, product_id, maxPassesPerItem);
|
|
659
836
|
if (applicableProducts.length === 0) {
|
|
660
837
|
isAvailable = false;
|
|
661
|
-
reasonCode =
|
|
838
|
+
reasonCode = 'not_meet_the_required_conditions';
|
|
662
839
|
} else {
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
new import_decimal.default(maxDeductionAmount)
|
|
666
|
-
);
|
|
840
|
+
// 计算 _available_max_amount
|
|
841
|
+
const baseAmount = _decimal.default.min(new _decimal.default(recommendedAmount), new _decimal.default(maxDeductionAmount));
|
|
667
842
|
if (allowCrossProduct) {
|
|
668
843
|
if (applicableProductLimit > 0) {
|
|
844
|
+
// 按剩余金额从高到低排序,按动态计算的可抵扣数量累计直到达到 limit
|
|
669
845
|
const sortedProducts = [...applicableProducts].sort((a, b) => a[amountField].comparedTo(b[amountField]) > 0 ? -1 : 1);
|
|
670
846
|
let remainingLimit = applicableProductLimit;
|
|
671
847
|
for (const product of sortedProducts) {
|
|
672
|
-
if (remainingLimit <= 0)
|
|
673
|
-
|
|
848
|
+
if (remainingLimit <= 0) break;
|
|
849
|
+
// 动态计算当前可抵扣数量 = ceil(剩余金额 / 单价)
|
|
674
850
|
const currentAvailableQty = Math.ceil(product[amountField].dividedBy(product[unitPriceField]).toNumber());
|
|
675
851
|
const deductQty = Math.min(currentAvailableQty, remainingLimit);
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
product[amountField]
|
|
679
|
-
);
|
|
852
|
+
// 实际可抵扣金额 = min(数量 * 单价, 剩余金额)
|
|
853
|
+
const deductAmount = _decimal.default.min(product[unitPriceField].times(deductQty), product[amountField]);
|
|
680
854
|
calculatedMaxAmount = calculatedMaxAmount.plus(deductAmount);
|
|
681
855
|
remainingLimit -= deductQty;
|
|
682
856
|
}
|
|
683
857
|
} else {
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
new import_decimal.default(0)
|
|
687
|
-
);
|
|
858
|
+
// 无数量限制:所有适用商品的剩余金额总和
|
|
859
|
+
calculatedMaxAmount = applicableProducts.reduce((sum, p) => sum.plus(p[amountField]), new _decimal.default(0));
|
|
688
860
|
}
|
|
689
861
|
} else {
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
862
|
+
// 非跨商品券:选择单价最高的商品(优先抵扣高价商品)
|
|
863
|
+
const maxProduct = applicableProducts.reduce((max, p) => p[unitPriceField].greaterThan(max[unitPriceField]) ? p : max);
|
|
864
|
+
// maxPassesPerItem 限制每张券最多抵扣的单位数
|
|
693
865
|
if (maxPassesPerItem > 0) {
|
|
694
|
-
calculatedMaxAmount =
|
|
695
|
-
maxProduct[unitPriceField].times(maxPassesPerItem),
|
|
696
|
-
maxProduct[amountField]
|
|
697
|
-
);
|
|
866
|
+
calculatedMaxAmount = _decimal.default.min(maxProduct[unitPriceField].times(maxPassesPerItem), maxProduct[amountField]);
|
|
698
867
|
} else {
|
|
699
868
|
calculatedMaxAmount = maxProduct[amountField];
|
|
700
869
|
}
|
|
701
870
|
}
|
|
702
|
-
calculatedMaxAmount =
|
|
703
|
-
baseAmount,
|
|
704
|
-
calculatedMaxAmount,
|
|
705
|
-
remainingOrderAmount
|
|
706
|
-
);
|
|
871
|
+
calculatedMaxAmount = _decimal.default.min(baseAmount, calculatedMaxAmount, remainingOrderAmount);
|
|
707
872
|
if (calculatedMaxAmount.lessThanOrEqualTo(0)) {
|
|
708
873
|
isAvailable = false;
|
|
709
|
-
reasonCode =
|
|
874
|
+
reasonCode = 'exceeds_the_maximum_deduction_limit';
|
|
710
875
|
}
|
|
711
876
|
}
|
|
712
877
|
}
|
|
@@ -717,7 +882,9 @@ function recalculateVouchers(allVouchers, selectedVouchers, orderTotalAmount, pr
|
|
|
717
882
|
_available_max_amount: calculatedMaxAmount.toNumber(),
|
|
718
883
|
// 转换为数字
|
|
719
884
|
_unified_available_status: isAvailable ? 1 : 0,
|
|
720
|
-
...reasonCode && {
|
|
885
|
+
...(reasonCode && {
|
|
886
|
+
reasonCode
|
|
887
|
+
})
|
|
721
888
|
};
|
|
722
889
|
});
|
|
723
890
|
return {
|
|
@@ -725,47 +892,87 @@ function recalculateVouchers(allVouchers, selectedVouchers, orderTotalAmount, pr
|
|
|
725
892
|
selectedWithDetails
|
|
726
893
|
};
|
|
727
894
|
}
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
895
|
+
|
|
896
|
+
/**
|
|
897
|
+
* 获取主商品价格(单价,不含舍入余数)
|
|
898
|
+
* @param product 商品
|
|
899
|
+
* @param isDeductTaxAndFee 是否抵扣税费与附加费
|
|
900
|
+
* @returns 商品单价
|
|
901
|
+
*/
|
|
902
|
+
const getMainProductPrice = (product, isDeductTaxAndFee) => {
|
|
903
|
+
let mainProductPrice = new _decimal.default(product?.main_product_selling_price || product.metadata?.main_product_selling_price || 0);
|
|
904
|
+
for (let bundleItem of product?.product_bundle || []) {
|
|
905
|
+
// 子商品是加价或减价才需要计算
|
|
732
906
|
if (getBundleItemIsMarkupOrDiscountPrice(bundleItem)) {
|
|
733
|
-
|
|
907
|
+
// 子商品价格
|
|
908
|
+
const bundleItemPrice = new _decimal.default(bundleItem.bundle_selling_price ?? 0);
|
|
734
909
|
mainProductPrice = mainProductPrice.add(bundleItemPrice.times(bundleItem.num));
|
|
735
910
|
}
|
|
736
911
|
}
|
|
737
|
-
let taxFee = new
|
|
912
|
+
let taxFee = new _decimal.default(product?.tax_fee || product?.metadata?.main_product_attached_bundle_tax_fee || 0).add(product?.metadata?.tax_fee_rounding_remainder || 0);
|
|
738
913
|
if (product.is_price_include_tax === 1) {
|
|
739
|
-
taxFee = new
|
|
914
|
+
taxFee = new _decimal.default(0);
|
|
740
915
|
}
|
|
741
|
-
|
|
916
|
+
|
|
917
|
+
// 税费
|
|
918
|
+
// 附加费
|
|
919
|
+
const surchargeFee = new _decimal.default(product?.metadata?.main_product_attached_bundle_surcharge_fee || 0).add(product?.metadata?.surcharge_rounding_remainder || 0);
|
|
920
|
+
|
|
921
|
+
// 税费附加费总额
|
|
742
922
|
const taxAndFeeTotal = taxFee.add(surchargeFee);
|
|
923
|
+
|
|
924
|
+
// 如果需要抵扣税费与附加费,则加上税费附加费总额
|
|
743
925
|
if (isDeductTaxAndFee) {
|
|
744
926
|
mainProductPrice = mainProductPrice.add(taxAndFeeTotal);
|
|
745
927
|
}
|
|
746
928
|
return mainProductPrice.toNumber();
|
|
747
929
|
};
|
|
748
|
-
|
|
749
|
-
|
|
930
|
+
|
|
931
|
+
/**
|
|
932
|
+
* 获取套餐子商品价格(不含舍入余数)
|
|
933
|
+
* @param bundleItem 套餐子商品
|
|
934
|
+
* @param parentQuantity 父商品数量
|
|
935
|
+
* @param isDeductTaxAndFee 是否抵扣税费与附加费
|
|
936
|
+
* @returns 子商品总价格(不含舍入余数)
|
|
937
|
+
*/
|
|
938
|
+
exports.getMainProductPrice = getMainProductPrice;
|
|
939
|
+
const getBundleItemPrice = (bundleItem, parentQuantity, isDeductTaxAndFee) => {
|
|
940
|
+
// 子商品基础价格 = bundle_selling_price * num * 主商品数量
|
|
750
941
|
const totalQuantity = bundleItem.num * parentQuantity;
|
|
751
|
-
let bundleItemPrice = new
|
|
942
|
+
let bundleItemPrice = new _decimal.default(bundleItem.bundle_selling_price ?? 0).times(totalQuantity);
|
|
943
|
+
|
|
944
|
+
// 根据 deductTaxAndFee 配置决定是否加上税费和附加费
|
|
752
945
|
if (isDeductTaxAndFee) {
|
|
753
|
-
|
|
946
|
+
// 税费
|
|
947
|
+
let taxFee = new _decimal.default(bundleItem.tax_fee ?? 0).times(totalQuantity);
|
|
754
948
|
if (bundleItem.is_price_include_tax === 1) {
|
|
755
|
-
taxFee = new
|
|
949
|
+
taxFee = new _decimal.default(0);
|
|
756
950
|
}
|
|
757
|
-
|
|
951
|
+
// 附加费
|
|
952
|
+
const surchargeFee = new _decimal.default(bundleItem.metadata?.surcharge_fee ?? 0).times(totalQuantity);
|
|
758
953
|
bundleItemPrice = bundleItemPrice.add(taxFee).add(surchargeFee);
|
|
759
954
|
}
|
|
760
955
|
return bundleItemPrice.toNumber();
|
|
761
956
|
};
|
|
762
|
-
|
|
957
|
+
|
|
958
|
+
/**
|
|
959
|
+
* 将商品数据拆分,包含主商品和原价子商品
|
|
960
|
+
* @param products 原始商品列表
|
|
961
|
+
* @param deductTaxAndFee 是否抵扣税费与附加费(已废弃,保留参数以兼容旧代码)
|
|
962
|
+
* @returns 拆分后的商品数据(包含主商品和子商品,同时维护含税和不含税两个金额池,以及单价和剩余数量)
|
|
963
|
+
*/
|
|
964
|
+
exports.getBundleItemPrice = getBundleItemPrice;
|
|
965
|
+
const expandProductsWithBundleItems = (products, deductTaxAndFee) => {
|
|
763
966
|
const expandedProducts = [];
|
|
764
967
|
products.forEach((product, indexInOrder) => {
|
|
765
968
|
const productQuantity = getProductQuantity(product);
|
|
766
969
|
const parentLineKey = resolveWalletPassLineKey(product, indexInOrder);
|
|
970
|
+
|
|
971
|
+
// 计算主商品单价(含税和不含税)
|
|
767
972
|
const unitPriceWithTax = getMainProductPrice(product, true);
|
|
768
973
|
const unitPricePure = getMainProductPrice(product, false);
|
|
974
|
+
|
|
975
|
+
// 1. 添加主商品(同时计算含税和不含税两个金额,以及单价和剩余数量)
|
|
769
976
|
expandedProducts.push({
|
|
770
977
|
...product,
|
|
771
978
|
is_bundle_item: false,
|
|
@@ -773,22 +980,27 @@ var expandProductsWithBundleItems = (products, deductTaxAndFee) => {
|
|
|
773
980
|
_walletPassLineKey: parentLineKey,
|
|
774
981
|
_orderLineQuantity: productQuantity,
|
|
775
982
|
// 单价(用于按 quantity 计算抵扣)
|
|
776
|
-
unitPriceWithTax: new
|
|
777
|
-
unitPricePure: new
|
|
983
|
+
unitPriceWithTax: new _decimal.default(unitPriceWithTax),
|
|
984
|
+
unitPricePure: new _decimal.default(unitPricePure),
|
|
778
985
|
// 剩余可抵扣数量
|
|
779
986
|
remainingQuantity: productQuantity,
|
|
780
987
|
// 含税费的剩余金额
|
|
781
|
-
remainingAmountWithTax: new
|
|
988
|
+
remainingAmountWithTax: new _decimal.default(unitPriceWithTax).times(productQuantity),
|
|
782
989
|
// 纯商品金额(不含税费)
|
|
783
|
-
remainingAmountPure: new
|
|
990
|
+
remainingAmountPure: new _decimal.default(unitPricePure).times(productQuantity)
|
|
784
991
|
});
|
|
992
|
+
|
|
993
|
+
// 2. 添加原价子商品(作为独立商品项)
|
|
785
994
|
if (product.product_bundle && product.product_bundle.length > 0) {
|
|
786
|
-
product.product_bundle.forEach(
|
|
995
|
+
product.product_bundle.forEach(bundleItem => {
|
|
787
996
|
if (getBundleItemIsOriginalPrice(bundleItem)) {
|
|
788
997
|
const bundleQuantity = bundleItem.num * productQuantity;
|
|
789
998
|
const bundleLineKey = `${parentLineKey}#bundle:${bundleItem.bundle_id}:${bundleItem.bundle_product_id}`;
|
|
790
|
-
|
|
791
|
-
const
|
|
999
|
+
// 计算子商品单价(注意:getBundleItemPrice 返回的是总价,需要除以数量得到单价)
|
|
1000
|
+
const bundleUnitPriceWithTax = new _decimal.default(getBundleItemPrice(bundleItem, 1, true)).dividedBy(bundleItem.num);
|
|
1001
|
+
const bundleUnitPricePure = new _decimal.default(getBundleItemPrice(bundleItem, 1, false)).dividedBy(bundleItem.num);
|
|
1002
|
+
|
|
1003
|
+
// 原价子商品作为独立商品
|
|
792
1004
|
expandedProducts.push({
|
|
793
1005
|
...bundleItem,
|
|
794
1006
|
product_id: bundleItem.bundle_product_id,
|
|
@@ -805,9 +1017,9 @@ var expandProductsWithBundleItems = (products, deductTaxAndFee) => {
|
|
|
805
1017
|
// 剩余可抵扣数量
|
|
806
1018
|
remainingQuantity: bundleQuantity,
|
|
807
1019
|
// 含税费的剩余金额
|
|
808
|
-
remainingAmountWithTax: new
|
|
1020
|
+
remainingAmountWithTax: new _decimal.default(getBundleItemPrice(bundleItem, productQuantity, true)),
|
|
809
1021
|
// 纯商品金额(不含税费)
|
|
810
|
-
remainingAmountPure: new
|
|
1022
|
+
remainingAmountPure: new _decimal.default(getBundleItemPrice(bundleItem, productQuantity, false))
|
|
811
1023
|
});
|
|
812
1024
|
}
|
|
813
1025
|
});
|
|
@@ -815,29 +1027,27 @@ var expandProductsWithBundleItems = (products, deductTaxAndFee) => {
|
|
|
815
1027
|
});
|
|
816
1028
|
return expandedProducts;
|
|
817
1029
|
};
|
|
818
|
-
|
|
819
|
-
|
|
1030
|
+
|
|
1031
|
+
// bundle商品是否是原价
|
|
1032
|
+
const getBundleItemIsOriginalPrice = item => {
|
|
1033
|
+
return item?.price_type === 'markup' && item?.price_type_ext === 'product_price';
|
|
820
1034
|
};
|
|
821
|
-
|
|
822
|
-
|
|
1035
|
+
|
|
1036
|
+
// bundle商品是否是加价
|
|
1037
|
+
exports.getBundleItemIsOriginalPrice = getBundleItemIsOriginalPrice;
|
|
1038
|
+
const getBundleItemIsMarkupPrice = item => {
|
|
1039
|
+
return item?.price_type === 'markup' && (item?.price_type_ext === '' || !item?.price_type_ext);
|
|
823
1040
|
};
|
|
824
|
-
|
|
825
|
-
|
|
1041
|
+
|
|
1042
|
+
// bundle商品是否是减价
|
|
1043
|
+
exports.getBundleItemIsMarkupPrice = getBundleItemIsMarkupPrice;
|
|
1044
|
+
const getBundleItemIsDiscountPrice = item => {
|
|
1045
|
+
return item?.price_type === 'markdown' && (item?.price_type_ext === '' || !item?.price_type_ext);
|
|
826
1046
|
};
|
|
827
|
-
|
|
1047
|
+
|
|
1048
|
+
// bundle商品是否是加价或减价
|
|
1049
|
+
exports.getBundleItemIsDiscountPrice = getBundleItemIsDiscountPrice;
|
|
1050
|
+
const getBundleItemIsMarkupOrDiscountPrice = item => {
|
|
828
1051
|
return getBundleItemIsMarkupPrice(item) || getBundleItemIsDiscountPrice(item);
|
|
829
1052
|
};
|
|
830
|
-
|
|
831
|
-
0 && (module.exports = {
|
|
832
|
-
getApplicableProductIds,
|
|
833
|
-
getBundleItemIsDiscountPrice,
|
|
834
|
-
getBundleItemIsMarkupOrDiscountPrice,
|
|
835
|
-
getBundleItemIsMarkupPrice,
|
|
836
|
-
getBundleItemIsOriginalPrice,
|
|
837
|
-
getBundleItemPrice,
|
|
838
|
-
getMainProductPrice,
|
|
839
|
-
getProductQuantity,
|
|
840
|
-
processVouchers,
|
|
841
|
-
recalculateVouchers,
|
|
842
|
-
resolveWalletPassLineKey
|
|
843
|
-
});
|
|
1053
|
+
exports.getBundleItemIsMarkupOrDiscountPrice = getBundleItemIsMarkupOrDiscountPrice;
|