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