@pisell/pisellos 0.0.567 → 0.0.568
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/index.js +6 -9
- package/dist/model/strategy/adapter/walletPass/evaluator.js +0 -2
- package/dist/model/strategy/adapter/walletPass/example.js +3 -26
- package/dist/model/strategy/adapter/walletPass/utils.js +0 -3
- 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 +1 -4
- package/dist/modules/OpenData/index.js +0 -2
- package/dist/modules/Order/index.d.ts +1 -1
- package/dist/modules/Order/index.js +14 -41
- package/dist/modules/Payment/index.js +142 -270
- package/dist/modules/Payment/walletpass.js +4 -5
- package/dist/modules/Rules/index.js +0 -7
- package/dist/modules/Schedule/utils.js +0 -1
- package/dist/modules/Step/index.js +0 -1
- package/dist/plugins/request.js +1 -4
- package/dist/plugins/window.js +2 -6
- package/dist/server/index.js +88 -121
- package/dist/server/modules/menu/index.js +32 -41
- package/dist/server/modules/products/index.js +29 -49
- package/dist/server/modules/quotation/index.js +29 -38
- package/dist/server/modules/resource/index.js +3 -4
- package/dist/server/modules/schedule/index.js +32 -40
- package/dist/server/utils/product.js +0 -1
- 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.d.ts +1 -1
- package/dist/solution/BookingTicket/index.js +5 -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 -246
- 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.d.ts +5 -0
- package/dist/solution/VenueBooking/index.js +364 -327
- 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.js +130 -131
- package/lib/effects/index.js +46 -57
- package/lib/index.js +92 -53
- 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 +84 -63
- package/lib/model/strategy/adapter/promotion/evaluator.js +381 -231
- package/lib/model/strategy/adapter/promotion/examples.js +139 -159
- package/lib/model/strategy/adapter/promotion/index.js +1 -49
- package/lib/model/strategy/adapter/promotion/type.js +199 -35
- package/lib/model/strategy/adapter/type.js +4 -16
- package/lib/model/strategy/adapter/walletPass/evaluator.js +180 -152
- 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 +652 -402
- 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 +22 -45
- 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 +306 -241
- package/lib/modules/Cart/utils/cartRelationForms.js +29 -35
- package/lib/modules/Cart/utils/cartResource.js +72 -78
- package/lib/modules/Cart/utils/changePrice.js +26 -62
- package/lib/modules/Cart/utils/index.js +106 -48
- package/lib/modules/Customer/constants.js +13 -34
- package/lib/modules/Customer/index.js +216 -153
- 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 +117 -113
- 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 +63 -74
- 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 +332 -320
- package/lib/modules/Order/types.js +24 -33
- package/lib/modules/Order/utils.js +350 -284
- 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 +566 -432
- package/lib/modules/Payment/mx51.js +1 -0
- package/lib/modules/Payment/types.js +216 -107
- package/lib/modules/Payment/utils.js +50 -47
- package/lib/modules/Payment/walletpass.js +221 -203
- 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 +111 -110
- package/lib/modules/ProductList/types.js +9 -31
- package/lib/modules/Quotation/index.js +60 -82
- 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/Rules/index.js +1172 -872
- package/lib/modules/Rules/types.js +17 -40
- package/lib/modules/SalesSummary/index.js +59 -68
- package/lib/modules/SalesSummary/types.js +4 -16
- package/lib/modules/SalesSummary/utils.js +176 -193
- 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 -167
- package/lib/modules/Schedule/index.js +97 -107
- 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 -70
- package/lib/modules/Summary/types.js +4 -16
- package/lib/modules/Summary/utils.js +782 -429
- package/lib/modules/index.js +212 -57
- package/lib/plugins/app.js +4 -16
- package/lib/plugins/index.js +36 -25
- 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.js +882 -623
- package/lib/server/modules/index.js +85 -59
- package/lib/server/modules/menu/index.js +146 -122
- package/lib/server/modules/menu/types.js +18 -31
- package/lib/server/modules/order/index.js +195 -231
- package/lib/server/modules/order/types.js +107 -32
- package/lib/server/modules/order/utils/filterBookings.js +174 -140
- package/lib/server/modules/order/utils/filterOrders.js +89 -60
- package/lib/server/modules/products/index.js +430 -335
- package/lib/server/modules/products/types.js +41 -34
- package/lib/server/modules/quotation/index.js +76 -77
- package/lib/server/modules/quotation/types.js +21 -31
- package/lib/server/modules/resource/index.js +216 -187
- package/lib/server/modules/resource/types.js +42 -33
- package/lib/server/modules/schedule/index.js +134 -119
- 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/time.js +40 -49
- package/lib/solution/BookingByStep/index.d.ts +1 -1
- package/lib/solution/BookingByStep/index.js +1530 -1343
- 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.d.ts +1 -1
- package/lib/solution/BookingTicket/index.js +147 -122
- package/lib/solution/BookingTicket/types.js +38 -64
- package/lib/solution/BookingTicket/utils/scan/cloudSearch.js +64 -80
- package/lib/solution/BookingTicket/utils/scan/handleScan.js +82 -80
- package/lib/solution/BookingTicket/utils/scan/index.js +90 -94
- package/lib/solution/BookingTicket/utils/scan/scanCache.js +90 -43
- 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 +227 -158
- 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 +218 -200
- package/lib/solution/Sales/types.js +27 -33
- package/lib/solution/ScanOrder/index.js +803 -840
- package/lib/solution/ScanOrder/types.js +34 -34
- package/lib/solution/ScanOrder/utils.js +372 -331
- package/lib/solution/ShopDiscount/index.js +230 -236
- package/lib/solution/ShopDiscount/types.js +15 -37
- package/lib/solution/ShopDiscount/utils.js +309 -172
- package/lib/solution/VenueBooking/index.d.ts +5 -0
- package/lib/solution/VenueBooking/index.js +886 -929
- package/lib/solution/VenueBooking/types.js +20 -40
- package/lib/solution/VenueBooking/utils/dateSummary.js +22 -54
- package/lib/solution/VenueBooking/utils/resource.js +31 -54
- package/lib/solution/VenueBooking/utils/slotMerge.js +88 -106
- package/lib/solution/VenueBooking/utils/smartPricing.js +112 -100
- package/lib/solution/VenueBooking/utils/timeSlot.js +133 -132
- package/lib/solution/VenueBooking/utils.js +130 -67
- package/lib/solution/index.js +102 -37
- package/lib/store/createStore.js +32 -29
- package/lib/types/index.js +4 -16
- package/lib/utils/task.js +36 -36
- package/lib/utils/watch.js +81 -47
- package/package.json +2 -1
|
@@ -1,88 +1,97 @@
|
|
|
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/solution/ShopDiscount/utils.ts
|
|
30
|
-
var utils_exports = {};
|
|
31
|
-
__export(utils_exports, {
|
|
32
|
-
calculateOrderLevelDiscountAllocation: () => calculateOrderLevelDiscountAllocation,
|
|
33
|
-
filterDiscountListByBookingTime: () => filterDiscountListByBookingTime,
|
|
34
|
-
getDateIsInSchedule: () => getDateIsInSchedule,
|
|
35
|
-
getDiscountAmount: () => getDiscountAmount,
|
|
36
|
-
getDiscountListAmount: () => getDiscountListAmount,
|
|
37
|
-
getDiscountListAmountTotal: () => getDiscountListAmountTotal,
|
|
38
|
-
isAllNormalProduct: () => isAllNormalProduct,
|
|
39
|
-
isNormalProductByDurationSchedule: () => isNormalProductByDurationSchedule,
|
|
40
|
-
isOrderLevelFixedAmountDiscount: () => isOrderLevelFixedAmountDiscount,
|
|
41
|
-
uniqueById: () => uniqueById
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
42
5
|
});
|
|
43
|
-
|
|
44
|
-
var
|
|
45
|
-
var
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
6
|
+
exports.uniqueById = exports.isOrderLevelFixedAmountDiscount = exports.isNormalProductByDurationSchedule = exports.isAllNormalProduct = exports.getDiscountListAmountTotal = exports.getDiscountListAmount = exports.getDiscountAmount = exports.getDateIsInSchedule = exports.filterDiscountListByBookingTime = exports.calculateOrderLevelDiscountAllocation = void 0;
|
|
7
|
+
var _decimal = _interopRequireDefault(require("decimal.js"));
|
|
8
|
+
var _dayjs = _interopRequireDefault(require("dayjs"));
|
|
9
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
10
|
+
const uniqueById = (arr, key = 'id') => {
|
|
11
|
+
const seen = new Set();
|
|
12
|
+
return arr.filter(item => !seen.has(item[key]) && seen.add(item[key]));
|
|
49
13
|
};
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
14
|
+
|
|
15
|
+
// 非预约商品 通过时长和日程判断是否是普通商品
|
|
16
|
+
exports.uniqueById = uniqueById;
|
|
17
|
+
const isNormalProductByDurationSchedule = item => {
|
|
18
|
+
return !item?.duration && !item?.schedules?.length && !item?.['schedule.ids']?.length;
|
|
53
19
|
};
|
|
54
|
-
|
|
55
|
-
|
|
20
|
+
|
|
21
|
+
// 是否全部是普通商品
|
|
22
|
+
exports.isNormalProductByDurationSchedule = isNormalProductByDurationSchedule;
|
|
23
|
+
const isAllNormalProduct = items => {
|
|
24
|
+
return items.every(item => isNormalProductByDurationSchedule(item));
|
|
56
25
|
};
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 获取折扣金额 基于折扣卡类型计算
|
|
29
|
+
* 商品券:直接返回商品价格
|
|
30
|
+
* 折扣卡:根据折扣卡类型计算 固定金额:直接返回折扣卡金额 百分比:根据折扣卡金额计算
|
|
31
|
+
* @param discount
|
|
32
|
+
* @param total
|
|
33
|
+
* @param price
|
|
34
|
+
* @returns
|
|
35
|
+
*/
|
|
36
|
+
/**
|
|
37
|
+
* 获取折扣金额 基于折扣卡类型计算
|
|
38
|
+
* 商品券:直接返回商品价格
|
|
39
|
+
* 折扣卡:根据折扣卡类型计算 固定金额:直接返回折扣卡金额 百分比:根据折扣卡金额计算
|
|
40
|
+
* @param discount
|
|
41
|
+
* @param total
|
|
42
|
+
* @param price
|
|
43
|
+
* @returns
|
|
44
|
+
*/
|
|
45
|
+
exports.isAllNormalProduct = isAllNormalProduct;
|
|
46
|
+
const getDiscountAmount = (discount, total, price) => {
|
|
47
|
+
// 商品券
|
|
48
|
+
if (discount.tag === 'good_pass') {
|
|
49
|
+
return new _decimal.default(price).minus(new _decimal.default(price || 0)).toNumber();
|
|
61
50
|
}
|
|
62
|
-
|
|
51
|
+
|
|
52
|
+
// 判断是否是固定金额
|
|
53
|
+
const isFixedAmount = discount?.metadata?.discount_card_type === 'fixed_amount';
|
|
54
|
+
|
|
55
|
+
// 固定金额 小于0时返回0
|
|
63
56
|
if (isFixedAmount) {
|
|
64
|
-
return Math.max(new
|
|
57
|
+
return Math.max(new _decimal.default(price).minus(new _decimal.default((discount.amount ?? discount.par_value) || 0)).toNumber(), 0);
|
|
65
58
|
}
|
|
66
|
-
|
|
59
|
+
|
|
60
|
+
// 百分比:根据折扣卡金额计算
|
|
61
|
+
return new _decimal.default(100).minus(discount.par_value || 0).div(100).mul(new _decimal.default(price)).toNumber();
|
|
67
62
|
};
|
|
68
|
-
|
|
63
|
+
exports.getDiscountAmount = getDiscountAmount;
|
|
64
|
+
const getDiscountListAmountTotal = discount => {
|
|
69
65
|
return discount.reduce((acc, cur) => {
|
|
70
|
-
return new
|
|
71
|
-
}, new
|
|
66
|
+
return new _decimal.default(acc).plus(new _decimal.default(cur.num || 1).mul(new _decimal.default(cur.amount || 0))).toNumber();
|
|
67
|
+
}, new _decimal.default(0));
|
|
72
68
|
};
|
|
73
|
-
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* 获取折扣金额 计算每个折扣的金额
|
|
72
|
+
* @param discount
|
|
73
|
+
* @returns
|
|
74
|
+
*/
|
|
75
|
+
exports.getDiscountListAmountTotal = getDiscountListAmountTotal;
|
|
76
|
+
const getDiscountListAmount = discount => {
|
|
74
77
|
return discount.reduce((acc, cur) => {
|
|
75
|
-
return new
|
|
76
|
-
}, new
|
|
78
|
+
return new _decimal.default(acc).plus(new _decimal.default(cur.amount || 0)).toNumber();
|
|
79
|
+
}, new _decimal.default(0));
|
|
77
80
|
};
|
|
78
|
-
|
|
81
|
+
|
|
82
|
+
// 日程项接口定义
|
|
83
|
+
exports.getDiscountListAmount = getDiscountListAmount;
|
|
84
|
+
// 获取时间是否在日程范围内
|
|
85
|
+
const getDateIsInSchedule = (dateTime, scheduleList) => {
|
|
79
86
|
if (!dateTime || !scheduleList || scheduleList.length === 0) {
|
|
80
87
|
return false;
|
|
81
88
|
}
|
|
82
|
-
const targetDate = (0,
|
|
89
|
+
const targetDate = (0, _dayjs.default)(dateTime);
|
|
83
90
|
if (!targetDate.isValid()) {
|
|
84
91
|
return false;
|
|
85
92
|
}
|
|
93
|
+
|
|
94
|
+
// 遍历所有日程项
|
|
86
95
|
for (const schedule of scheduleList) {
|
|
87
96
|
if (isTimeInScheduleItem(dateTime, schedule)) {
|
|
88
97
|
return true;
|
|
@@ -90,284 +99,422 @@ var getDateIsInSchedule = (dateTime, scheduleList) => {
|
|
|
90
99
|
}
|
|
91
100
|
return false;
|
|
92
101
|
};
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
102
|
+
|
|
103
|
+
// 判断时间是否在单个日程项范围内
|
|
104
|
+
exports.getDateIsInSchedule = getDateIsInSchedule;
|
|
105
|
+
const isTimeInScheduleItem = (dateTime, schedule) => {
|
|
106
|
+
const targetDate = (0, _dayjs.default)(dateTime);
|
|
107
|
+
const targetDateString = dateTime.split(' ')[0]; // YYYY-MM-DD
|
|
108
|
+
const targetTimeString = dateTime.split(' ')[1] || '00:00:00'; // HH:mm:ss
|
|
109
|
+
|
|
97
110
|
switch (schedule.type) {
|
|
98
|
-
case
|
|
111
|
+
case 'standard':
|
|
99
112
|
return isInStandardSchedule(targetDate, targetDateString, targetTimeString, schedule);
|
|
100
|
-
case
|
|
113
|
+
case 'time-slots':
|
|
101
114
|
return isInTimeSlotsSchedule(targetDate, targetDateString, targetTimeString, schedule);
|
|
102
|
-
case
|
|
115
|
+
case 'designation':
|
|
103
116
|
return isInDesignationSchedule(targetDate, targetDateString, targetTimeString, schedule);
|
|
104
117
|
default:
|
|
105
118
|
return false;
|
|
106
119
|
}
|
|
107
120
|
};
|
|
108
|
-
|
|
121
|
+
|
|
122
|
+
// 处理标准类型日程
|
|
123
|
+
const isInStandardSchedule = (targetDate, targetDateString, targetTimeString, schedule) => {
|
|
109
124
|
if (!schedule.start_time || !schedule.end_time) {
|
|
110
125
|
return false;
|
|
111
126
|
}
|
|
112
|
-
const startDate = (0,
|
|
113
|
-
const endDate = (0,
|
|
127
|
+
const startDate = (0, _dayjs.default)(schedule.start_time);
|
|
128
|
+
const endDate = (0, _dayjs.default)(schedule.end_time);
|
|
129
|
+
|
|
130
|
+
// 先检查是否在基础时间范围内(不考虑重复)
|
|
114
131
|
const isInBasicRange = targetDate.isSameOrAfter(startDate) && targetDate.isSameOrBefore(endDate);
|
|
115
|
-
if (schedule.repeat_type ===
|
|
132
|
+
if (schedule.repeat_type === 'none') {
|
|
116
133
|
return isInBasicRange;
|
|
117
134
|
}
|
|
135
|
+
|
|
136
|
+
// 处理重复日程
|
|
118
137
|
return isInRepeatingSchedule(targetDate, targetDateString, targetTimeString, schedule, startDate, endDate);
|
|
119
138
|
};
|
|
120
|
-
|
|
139
|
+
|
|
140
|
+
// 处理时间段类型日程
|
|
141
|
+
const isInTimeSlotsSchedule = (targetDate, targetDateString, targetTimeString, schedule) => {
|
|
121
142
|
if (!schedule.start_time) {
|
|
122
143
|
return false;
|
|
123
144
|
}
|
|
124
|
-
|
|
125
|
-
|
|
145
|
+
|
|
146
|
+
// 如果不重复,使用原有逻辑
|
|
147
|
+
if (schedule.repeat_type === 'none') {
|
|
148
|
+
const scheduleDate = schedule.start_time.split(' ')[0];
|
|
149
|
+
|
|
150
|
+
// 检查日期是否匹配
|
|
126
151
|
if (targetDateString !== scheduleDate) {
|
|
127
152
|
return false;
|
|
128
153
|
}
|
|
154
|
+
|
|
155
|
+
// 检查是否在任一时间段内
|
|
129
156
|
return checkTimeSlotsForDate(targetDate, targetDateString, schedule.time_slot);
|
|
130
157
|
}
|
|
158
|
+
|
|
159
|
+
// 处理重复日程
|
|
131
160
|
return isInTimeSlotsRepeatingSchedule(targetDate, targetDateString, targetTimeString, schedule);
|
|
132
161
|
};
|
|
133
|
-
|
|
162
|
+
|
|
163
|
+
// 处理指定类型日程
|
|
164
|
+
const isInDesignationSchedule = (targetDate, targetDateString, targetTimeString, schedule) => {
|
|
134
165
|
if (!schedule.designation) {
|
|
135
166
|
return false;
|
|
136
167
|
}
|
|
137
168
|
for (const designation of schedule.designation) {
|
|
138
169
|
const startDateTime = `${designation.start_date} ${designation.start_time}:00`;
|
|
139
170
|
const endDateTime = `${designation.end_date} ${designation.end_time}:00`;
|
|
140
|
-
const startDate = (0,
|
|
141
|
-
const endDate = (0,
|
|
171
|
+
const startDate = (0, _dayjs.default)(startDateTime);
|
|
172
|
+
const endDate = (0, _dayjs.default)(endDateTime);
|
|
142
173
|
if (targetDate.isSameOrAfter(startDate) && targetDate.isSameOrBefore(endDate)) {
|
|
143
174
|
return true;
|
|
144
175
|
}
|
|
145
176
|
}
|
|
146
177
|
return false;
|
|
147
178
|
};
|
|
148
|
-
|
|
179
|
+
|
|
180
|
+
// 处理重复日程逻辑
|
|
181
|
+
const isInRepeatingSchedule = (targetDate, targetDateString, targetTimeString, schedule, startDate, endDate) => {
|
|
149
182
|
if (!schedule.repeat_rule) {
|
|
150
183
|
return false;
|
|
151
184
|
}
|
|
152
|
-
const {
|
|
153
|
-
|
|
185
|
+
const {
|
|
186
|
+
repeat_rule,
|
|
187
|
+
repeat_type
|
|
188
|
+
} = schedule;
|
|
189
|
+
const targetDateOnly = (0, _dayjs.default)(targetDateString); // 只取日期部分
|
|
190
|
+
|
|
191
|
+
// 首先检查是否在包含日期中(包含日期是额外增加的有效日期)
|
|
154
192
|
if (repeat_rule.included_date && repeat_rule.included_date.length > 0) {
|
|
155
193
|
for (const includedDate of repeat_rule.included_date) {
|
|
156
|
-
const includeStartDate = (0,
|
|
157
|
-
const includeEndDate = (0,
|
|
158
|
-
if (targetDateOnly.isSameOrAfter(includeStartDate,
|
|
194
|
+
const includeStartDate = (0, _dayjs.default)(includedDate.start);
|
|
195
|
+
const includeEndDate = (0, _dayjs.default)(includedDate.end);
|
|
196
|
+
if (targetDateOnly.isSameOrAfter(includeStartDate, 'day') && targetDateOnly.isSameOrBefore(includeEndDate, 'day')) {
|
|
197
|
+
// 在包含日期中,直接返回true,无需检查其他条件
|
|
159
198
|
return true;
|
|
160
199
|
}
|
|
161
200
|
}
|
|
162
201
|
}
|
|
202
|
+
|
|
203
|
+
// 检查是否在排除日期中
|
|
163
204
|
for (const excludedDate of repeat_rule.excluded_date) {
|
|
164
|
-
const excludeStartDate = (0,
|
|
165
|
-
const excludeEndDate = (0,
|
|
166
|
-
|
|
205
|
+
const excludeStartDate = (0, _dayjs.default)(excludedDate.start);
|
|
206
|
+
const excludeEndDate = (0, _dayjs.default)(excludedDate.end);
|
|
207
|
+
// 检查目标日期是否在排除范围内(包含边界)
|
|
208
|
+
if (targetDateOnly.isSameOrAfter(excludeStartDate, 'day') && targetDateOnly.isSameOrBefore(excludeEndDate, 'day')) {
|
|
167
209
|
return false;
|
|
168
210
|
}
|
|
169
211
|
}
|
|
170
|
-
|
|
171
|
-
|
|
212
|
+
|
|
213
|
+
// 检查重复规则的结束条件
|
|
214
|
+
if (repeat_rule.end.type === 'date' && repeat_rule.end.end_date) {
|
|
215
|
+
const ruleEndDate = (0, _dayjs.default)(repeat_rule.end.end_date);
|
|
172
216
|
if (targetDate.isAfter(ruleEndDate)) {
|
|
173
217
|
return false;
|
|
174
218
|
}
|
|
175
219
|
}
|
|
220
|
+
|
|
221
|
+
// 根据重复类型检查
|
|
176
222
|
switch (repeat_type) {
|
|
177
|
-
case
|
|
223
|
+
case 'daily':
|
|
178
224
|
return isInDailyRepeat(targetDate, startDate, endDate, repeat_rule);
|
|
179
|
-
case
|
|
225
|
+
case 'weekly':
|
|
180
226
|
return isInWeeklyRepeat(targetDate, startDate, endDate, repeat_rule);
|
|
181
227
|
default:
|
|
182
228
|
return false;
|
|
229
|
+
// 不支持月和年重复
|
|
183
230
|
}
|
|
184
231
|
};
|
|
185
|
-
|
|
186
|
-
|
|
232
|
+
|
|
233
|
+
// 每日重复逻辑
|
|
234
|
+
const isInDailyRepeat = (targetDate, startDate, endDate, repeatRule) => {
|
|
235
|
+
const daysDiff = targetDate.diff(startDate, 'day');
|
|
187
236
|
if (daysDiff < 0) {
|
|
188
237
|
return false;
|
|
189
238
|
}
|
|
239
|
+
|
|
240
|
+
// 检查频率
|
|
190
241
|
if (daysDiff % repeatRule.frequency !== 0) {
|
|
191
242
|
return false;
|
|
192
243
|
}
|
|
244
|
+
|
|
245
|
+
// 检查时间是否在日程的时间范围内
|
|
193
246
|
const targetTimeOfDay = targetDate.hour() * 3600 + targetDate.minute() * 60 + targetDate.second();
|
|
194
247
|
const startTimeOfDay = startDate.hour() * 3600 + startDate.minute() * 60 + startDate.second();
|
|
195
248
|
const endTimeOfDay = endDate.hour() * 3600 + endDate.minute() * 60 + endDate.second();
|
|
249
|
+
|
|
250
|
+
// 每日重复:每天都在相同的时间段内有效
|
|
251
|
+
// 例如:01:00:00 - 16:00:00,每天都是这个时间段
|
|
196
252
|
return targetTimeOfDay >= startTimeOfDay && targetTimeOfDay <= endTimeOfDay;
|
|
197
253
|
};
|
|
198
|
-
|
|
199
|
-
|
|
254
|
+
|
|
255
|
+
// 每周重复逻辑
|
|
256
|
+
const isInWeeklyRepeat = (targetDate, startDate, endDate, repeatRule) => {
|
|
257
|
+
const targetDayOfWeek = targetDate.day(); // 0=周日, 1=周一, ..., 6=周六
|
|
258
|
+
|
|
259
|
+
// 检查是否在指定的星期几内
|
|
200
260
|
if (!repeatRule.frequency_date.includes(targetDayOfWeek)) {
|
|
201
261
|
return false;
|
|
202
262
|
}
|
|
203
|
-
|
|
263
|
+
|
|
264
|
+
// 计算周差
|
|
265
|
+
const weeksDiff = targetDate.diff(startDate, 'week');
|
|
204
266
|
if (weeksDiff < 0) {
|
|
205
267
|
return false;
|
|
206
268
|
}
|
|
269
|
+
|
|
270
|
+
// 检查频率
|
|
207
271
|
if (weeksDiff % repeatRule.frequency !== 0) {
|
|
208
272
|
return false;
|
|
209
273
|
}
|
|
274
|
+
|
|
275
|
+
// 检查时间是否在日程的时间范围内
|
|
210
276
|
const targetTimeOfDay = targetDate.hour() * 3600 + targetDate.minute() * 60 + targetDate.second();
|
|
211
277
|
const startTimeOfDay = startDate.hour() * 3600 + startDate.minute() * 60 + startDate.second();
|
|
212
278
|
const endTimeOfDay = endDate.hour() * 3600 + endDate.minute() * 60 + endDate.second();
|
|
213
279
|
return targetTimeOfDay >= startTimeOfDay && targetTimeOfDay <= endTimeOfDay;
|
|
214
280
|
};
|
|
215
|
-
|
|
281
|
+
|
|
282
|
+
// 处理 time-slots 类型的重复日程
|
|
283
|
+
const isInTimeSlotsRepeatingSchedule = (targetDate, targetDateString, targetTimeString, schedule) => {
|
|
216
284
|
if (!schedule.repeat_rule || !schedule.start_time) {
|
|
217
285
|
return false;
|
|
218
286
|
}
|
|
219
|
-
const {
|
|
220
|
-
|
|
221
|
-
|
|
287
|
+
const {
|
|
288
|
+
repeat_rule,
|
|
289
|
+
repeat_type
|
|
290
|
+
} = schedule;
|
|
291
|
+
const startDate = (0, _dayjs.default)(schedule.start_time);
|
|
292
|
+
const targetDateOnly = (0, _dayjs.default)(targetDateString);
|
|
293
|
+
|
|
294
|
+
// 检查包含日期
|
|
222
295
|
if (repeat_rule.included_date && repeat_rule.included_date.length > 0) {
|
|
223
296
|
for (const includedDate of repeat_rule.included_date) {
|
|
224
|
-
const includeStartDate = (0,
|
|
225
|
-
const includeEndDate = (0,
|
|
226
|
-
if (targetDateOnly.isSameOrAfter(includeStartDate,
|
|
297
|
+
const includeStartDate = (0, _dayjs.default)(includedDate.start);
|
|
298
|
+
const includeEndDate = (0, _dayjs.default)(includedDate.end);
|
|
299
|
+
if (targetDateOnly.isSameOrAfter(includeStartDate, 'day') && targetDateOnly.isSameOrBefore(includeEndDate, 'day')) {
|
|
300
|
+
// 在包含日期中,检查时间段
|
|
227
301
|
return checkTimeSlotsForDate(targetDate, targetDateString, schedule.time_slot);
|
|
228
302
|
}
|
|
229
303
|
}
|
|
230
304
|
}
|
|
305
|
+
|
|
306
|
+
// 检查排除日期
|
|
231
307
|
for (const excludedDate of repeat_rule.excluded_date) {
|
|
232
|
-
const excludeStartDate = (0,
|
|
233
|
-
const excludeEndDate = (0,
|
|
234
|
-
if (targetDateOnly.isSameOrAfter(excludeStartDate,
|
|
308
|
+
const excludeStartDate = (0, _dayjs.default)(excludedDate.start);
|
|
309
|
+
const excludeEndDate = (0, _dayjs.default)(excludedDate.end);
|
|
310
|
+
if (targetDateOnly.isSameOrAfter(excludeStartDate, 'day') && targetDateOnly.isSameOrBefore(excludeEndDate, 'day')) {
|
|
235
311
|
return false;
|
|
236
312
|
}
|
|
237
313
|
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
314
|
+
|
|
315
|
+
// 检查重复规则的结束条件
|
|
316
|
+
if (repeat_rule.end.type === 'date' && repeat_rule.end.end_date) {
|
|
317
|
+
const ruleEndDate = (0, _dayjs.default)(repeat_rule.end.end_date);
|
|
318
|
+
if (targetDateOnly.isAfter(ruleEndDate, 'day')) {
|
|
241
319
|
return false;
|
|
242
320
|
}
|
|
243
321
|
}
|
|
322
|
+
|
|
323
|
+
// 检查目标日期是否在重复规则范围内
|
|
244
324
|
let isDateValid = false;
|
|
245
325
|
switch (repeat_type) {
|
|
246
|
-
case
|
|
326
|
+
case 'daily':
|
|
247
327
|
isDateValid = isInDailyRepeatForDate(targetDateOnly, startDate, repeat_rule);
|
|
248
328
|
break;
|
|
249
|
-
case
|
|
329
|
+
case 'weekly':
|
|
250
330
|
isDateValid = isInWeeklyRepeatForDate(targetDateOnly, startDate, repeat_rule);
|
|
251
331
|
break;
|
|
252
332
|
default:
|
|
253
333
|
return false;
|
|
254
334
|
}
|
|
335
|
+
|
|
336
|
+
// 如果日期有效,检查时间段
|
|
255
337
|
if (isDateValid) {
|
|
256
338
|
return checkTimeSlotsForDate(targetDate, targetDateString, schedule.time_slot);
|
|
257
339
|
}
|
|
258
340
|
return false;
|
|
259
341
|
};
|
|
260
|
-
|
|
342
|
+
|
|
343
|
+
// 检查目标时间是否在某个时间段内(使用目标日期 + time_slot 时间)
|
|
344
|
+
const checkTimeSlotsForDate = (targetDate, targetDateString, timeSlots) => {
|
|
261
345
|
for (const timeSlot of timeSlots) {
|
|
262
346
|
const slotStart = `${targetDateString} ${timeSlot.start_time}:00`;
|
|
263
347
|
const slotEnd = `${targetDateString} ${timeSlot.end_time}:00`;
|
|
264
|
-
const slotStartDate = (0,
|
|
265
|
-
const slotEndDate = (0,
|
|
348
|
+
const slotStartDate = (0, _dayjs.default)(slotStart);
|
|
349
|
+
const slotEndDate = (0, _dayjs.default)(slotEnd);
|
|
266
350
|
if (targetDate.isSameOrAfter(slotStartDate) && targetDate.isSameOrBefore(slotEndDate)) {
|
|
267
351
|
return true;
|
|
268
352
|
}
|
|
269
353
|
}
|
|
270
354
|
return false;
|
|
271
355
|
};
|
|
272
|
-
|
|
273
|
-
|
|
356
|
+
|
|
357
|
+
// 检查日期是否满足每日重复规则(只检查日期,不检查时间)
|
|
358
|
+
const isInDailyRepeatForDate = (targetDate, startDate, repeatRule) => {
|
|
359
|
+
const daysDiff = targetDate.diff(startDate, 'day');
|
|
274
360
|
if (daysDiff < 0) {
|
|
275
361
|
return false;
|
|
276
362
|
}
|
|
363
|
+
|
|
364
|
+
// 检查频率
|
|
277
365
|
return daysDiff % repeatRule.frequency === 0;
|
|
278
366
|
};
|
|
279
|
-
|
|
280
|
-
|
|
367
|
+
|
|
368
|
+
// 检查日期是否满足每周重复规则(只检查日期,不检查时间)
|
|
369
|
+
const isInWeeklyRepeatForDate = (targetDate, startDate, repeatRule) => {
|
|
370
|
+
const targetDayOfWeek = targetDate.day(); // 0=周日, 1=周一, ..., 6=周六
|
|
371
|
+
|
|
372
|
+
// 检查是否在指定的星期几内
|
|
281
373
|
if (!repeatRule.frequency_date.includes(targetDayOfWeek)) {
|
|
282
374
|
return false;
|
|
283
375
|
}
|
|
284
|
-
|
|
376
|
+
|
|
377
|
+
// 计算周差
|
|
378
|
+
const weeksDiff = targetDate.diff(startDate, 'week');
|
|
285
379
|
if (weeksDiff < 0) {
|
|
286
380
|
return false;
|
|
287
381
|
}
|
|
382
|
+
|
|
383
|
+
// 检查频率
|
|
288
384
|
return weeksDiff % repeatRule.frequency === 0;
|
|
289
385
|
};
|
|
290
|
-
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* 根据预约时间过滤优惠券列表
|
|
389
|
+
* @param discountList 优惠券列表
|
|
390
|
+
* @param bookingTime 预约时间 (格式: YYYY-MM-DD HH:mm:ss)
|
|
391
|
+
* @returns 过滤后的优惠券列表
|
|
392
|
+
*/
|
|
393
|
+
const filterDiscountListByBookingTime = (discountList, bookingTime) => {
|
|
291
394
|
if (!bookingTime) {
|
|
292
395
|
return discountList;
|
|
293
396
|
}
|
|
294
|
-
return discountList.filter(
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
return getDateIsInSchedule(bookingTime, ((_b = discount.custom_schedule_snapshot) == null ? void 0 : _b.data) || []);
|
|
397
|
+
return discountList.filter(discount => {
|
|
398
|
+
if (discount?.metadata?.validity_type === "custom_schedule_validity") {
|
|
399
|
+
return getDateIsInSchedule(bookingTime, discount.custom_schedule_snapshot?.data || []);
|
|
298
400
|
}
|
|
299
401
|
try {
|
|
300
402
|
const isHasStart = !!discount.start_date && !!discount.start_time;
|
|
301
|
-
const startDate = (0,
|
|
302
|
-
const bookingTimeDayjs = (0,
|
|
403
|
+
const startDate = (0, _dayjs.default)(`${discount.start_date} ${discount.start_time}`);
|
|
404
|
+
const bookingTimeDayjs = (0, _dayjs.default)(bookingTime);
|
|
405
|
+
|
|
406
|
+
// 判断预约时间是否在开始时间之后
|
|
303
407
|
const isAfterStart = isHasStart ? bookingTimeDayjs.isSameOrAfter(startDate) : true;
|
|
304
|
-
|
|
408
|
+
|
|
409
|
+
// 判断预约时间是否在过期时间之前(expire_time 为空时为长期有效)
|
|
410
|
+
const isBeforeExpire = discount.expire_time ? bookingTimeDayjs.isSameOrBefore((0, _dayjs.default)(discount.expire_time)) : true;
|
|
305
411
|
return isAfterStart && isBeforeExpire;
|
|
306
412
|
} catch (error) {
|
|
307
413
|
return true;
|
|
308
414
|
}
|
|
309
415
|
});
|
|
310
416
|
};
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
417
|
+
/**
|
|
418
|
+
* 判断是否是订单级别的固定金额折扣卡
|
|
419
|
+
* @param discount 折扣
|
|
420
|
+
* @returns 是否是订单级别的固定金额折扣卡
|
|
421
|
+
*/
|
|
422
|
+
exports.filterDiscountListByBookingTime = filterDiscountListByBookingTime;
|
|
423
|
+
const isOrderLevelFixedAmountDiscount = discount => {
|
|
424
|
+
return discount.tag === 'product_discount_card' && discount?.metadata?.discount_card_type === 'fixed_amount' && discount?.metadata?.discount_calculation_mode === 'order_level';
|
|
314
425
|
};
|
|
315
|
-
|
|
316
|
-
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* 计算订单级别固定金额折扣卡的分摊结果
|
|
429
|
+
* @param discount 折扣卡
|
|
430
|
+
* @param applicableProducts 适用商品列表 { productId, amount, quantity, parentQuantity }
|
|
431
|
+
* @returns 分摊结果 Map<productId, { discountAmount, difference }>
|
|
432
|
+
* 注意:
|
|
433
|
+
* - discountAmount 是单价折扣金额(不是总折扣金额)
|
|
434
|
+
* - difference(总差值)优先直接加到数量为1的单商品折扣上,如果没有则存储在metadata中
|
|
435
|
+
*/
|
|
436
|
+
exports.isOrderLevelFixedAmountDiscount = isOrderLevelFixedAmountDiscount;
|
|
437
|
+
const calculateOrderLevelDiscountAllocation = (discount, applicableProducts) => {
|
|
438
|
+
const result = new Map();
|
|
317
439
|
if (applicableProducts.length === 0) {
|
|
318
440
|
return result;
|
|
319
441
|
}
|
|
442
|
+
|
|
443
|
+
// 计算适用商品的总金额(数量 × 单价)
|
|
320
444
|
const totalAmount = applicableProducts.reduce((acc, product) => {
|
|
321
|
-
return new
|
|
445
|
+
return new _decimal.default(acc).plus(new _decimal.default(product.amount).mul(product.quantity)).toNumber();
|
|
322
446
|
}, 0);
|
|
323
447
|
if (totalAmount <= 0) {
|
|
324
448
|
return result;
|
|
325
449
|
}
|
|
326
|
-
|
|
450
|
+
|
|
451
|
+
// 固定减免金额
|
|
452
|
+
const fixedAmount = new _decimal.default(discount.par_value || 0).toNumber();
|
|
453
|
+
|
|
454
|
+
// 如果固定金额 >= 商品总金额,折扣最高为商品金额(免费)
|
|
327
455
|
const actualDiscountTotal = Math.min(fixedAmount, totalAmount);
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
456
|
+
|
|
457
|
+
// 计算每个商品的单价折扣金额
|
|
458
|
+
let allocatedTotal = new _decimal.default(0);
|
|
459
|
+
applicableProducts.forEach(product => {
|
|
460
|
+
// 🔥 分摊公式改为:(单价 / 适用商品总金额) × 固定金额
|
|
461
|
+
// 这样只有一次取整,避免精度损失
|
|
462
|
+
const rawDiscountPerItem = new _decimal.default(product.amount).div(totalAmount).mul(actualDiscountTotal);
|
|
463
|
+
|
|
464
|
+
// 保留两位小数,向下取整(单价折扣)
|
|
465
|
+
const discountAmountPerItem = rawDiscountPerItem.toDecimalPlaces(2, _decimal.default.ROUND_DOWN).toNumber();
|
|
332
466
|
result.set(product.productId, {
|
|
333
467
|
discountAmount: discountAmountPerItem,
|
|
334
468
|
// 单价折扣金额
|
|
335
|
-
difference: 0
|
|
336
|
-
// 默认差值为0
|
|
469
|
+
difference: 0 // 默认差值为0
|
|
337
470
|
});
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
);
|
|
471
|
+
|
|
472
|
+
// 累加实际分配的总折扣金额(单价折扣 × 数量)
|
|
473
|
+
allocatedTotal = allocatedTotal.plus(new _decimal.default(discountAmountPerItem).mul(product.quantity));
|
|
341
474
|
});
|
|
342
|
-
|
|
475
|
+
|
|
476
|
+
// 🔥 计算总差值
|
|
477
|
+
// 总差值 = 固定金额 - 所有商品的 (单价折扣 × 数量) 之和
|
|
478
|
+
const totalDifference = new _decimal.default(actualDiscountTotal).minus(allocatedTotal).toDecimalPlaces(2, _decimal.default.ROUND_HALF_UP).toNumber();
|
|
343
479
|
if (totalDifference > 0) {
|
|
344
|
-
|
|
345
|
-
|
|
480
|
+
// 🔥 判断商品是否是真正的"数量为1"
|
|
481
|
+
// 主商品:quantity === 1
|
|
482
|
+
// 子商品:quantity === 1 且 parentQuantity === 1
|
|
483
|
+
const isSingleQuantity = product => {
|
|
484
|
+
if (product.parentQuantity !== undefined) {
|
|
485
|
+
// 子商品:需要同时满足自身数量为1且主商品数量为1
|
|
346
486
|
return product.quantity === 1 && product.parentQuantity === 1;
|
|
347
487
|
}
|
|
488
|
+
// 主商品:只需要自身数量为1
|
|
348
489
|
return product.quantity === 1;
|
|
349
490
|
};
|
|
491
|
+
|
|
492
|
+
// 🔥 筛选出数量为1的商品
|
|
350
493
|
const singleQuantityProducts = applicableProducts.filter(isSingleQuantity);
|
|
351
494
|
if (singleQuantityProducts.length > 0) {
|
|
352
|
-
|
|
495
|
+
// 🔥 优先级1:找到有足够剩余空间且金额最大的商品
|
|
496
|
+
// 剩余空间 = 商品价格 - 当前折扣金额 >= 差值
|
|
497
|
+
const productsWithEnoughSpace = singleQuantityProducts.filter(product => {
|
|
353
498
|
const allocation = result.get(product.productId);
|
|
354
|
-
if (!allocation)
|
|
355
|
-
|
|
356
|
-
const remainingSpace = new import_decimal.default(product.amount).minus(allocation.discountAmount).toNumber();
|
|
499
|
+
if (!allocation) return false;
|
|
500
|
+
const remainingSpace = new _decimal.default(product.amount).minus(allocation.discountAmount).toNumber();
|
|
357
501
|
return remainingSpace >= totalDifference;
|
|
358
502
|
});
|
|
359
503
|
if (productsWithEnoughSpace.length > 0) {
|
|
504
|
+
// 按金额降序排序,选择金额最大的
|
|
360
505
|
productsWithEnoughSpace.sort((a, b) => b.amount - a.amount);
|
|
361
506
|
const targetProduct = productsWithEnoughSpace[0];
|
|
362
507
|
const targetAllocation = result.get(targetProduct.productId);
|
|
363
508
|
if (targetAllocation) {
|
|
509
|
+
// 差值直接加到折扣金额上
|
|
364
510
|
result.set(targetProduct.productId, {
|
|
365
|
-
discountAmount: new
|
|
366
|
-
difference: 0
|
|
367
|
-
// 不需要存储差值
|
|
511
|
+
discountAmount: new _decimal.default(targetAllocation.discountAmount).plus(totalDifference).toNumber(),
|
|
512
|
+
difference: 0 // 不需要存储差值
|
|
368
513
|
});
|
|
369
514
|
}
|
|
370
515
|
} else {
|
|
516
|
+
// 🔥 优先级2:所有数量为1的商品都没有足够剩余空间
|
|
517
|
+
// 选择金额最大的商品,差值存储在metadata中
|
|
371
518
|
singleQuantityProducts.sort((a, b) => b.amount - a.amount);
|
|
372
519
|
const targetProduct = singleQuantityProducts[0];
|
|
373
520
|
const targetAllocation = result.get(targetProduct.productId);
|
|
@@ -379,6 +526,8 @@ var calculateOrderLevelDiscountAllocation = (discount, applicableProducts) => {
|
|
|
379
526
|
}
|
|
380
527
|
}
|
|
381
528
|
} else {
|
|
529
|
+
// 🔥 优先级3:没有数量为1的商品,走原有逻辑
|
|
530
|
+
// 差值存储在最后一个商品的metadata中
|
|
382
531
|
const lastProduct = applicableProducts[applicableProducts.length - 1];
|
|
383
532
|
const lastProductAllocation = result.get(lastProduct.productId);
|
|
384
533
|
if (lastProductAllocation) {
|
|
@@ -391,16 +540,4 @@ var calculateOrderLevelDiscountAllocation = (discount, applicableProducts) => {
|
|
|
391
540
|
}
|
|
392
541
|
return result;
|
|
393
542
|
};
|
|
394
|
-
|
|
395
|
-
0 && (module.exports = {
|
|
396
|
-
calculateOrderLevelDiscountAllocation,
|
|
397
|
-
filterDiscountListByBookingTime,
|
|
398
|
-
getDateIsInSchedule,
|
|
399
|
-
getDiscountAmount,
|
|
400
|
-
getDiscountListAmount,
|
|
401
|
-
getDiscountListAmountTotal,
|
|
402
|
-
isAllNormalProduct,
|
|
403
|
-
isNormalProductByDurationSchedule,
|
|
404
|
-
isOrderLevelFixedAmountDiscount,
|
|
405
|
-
uniqueById
|
|
406
|
-
});
|
|
543
|
+
exports.calculateOrderLevelDiscountAllocation = calculateOrderLevelDiscountAllocation;
|