@pisell/pisellos 0.10.14 → 0.10.16
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/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 +75 -91
- package/dist/modules/Order/types.d.ts +5 -1
- package/dist/modules/Order/utils/virtualSettlement.d.ts +8 -1
- package/dist/modules/Order/utils/virtualSettlement.js +15 -2
- package/dist/modules/Order/utils.js +7 -1
- package/dist/modules/Payment/index.js +97 -162
- package/dist/modules/Product/types.d.ts +2 -0
- 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 +107 -76
- package/dist/solution/BaseSales/types.d.ts +2 -0
- package/dist/solution/BaseSales/utils/cartPromotion.d.ts +5 -0
- package/dist/solution/BaseSales/utils/cartPromotion.js +15 -6
- package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.d.ts +3 -0
- package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +7 -1
- package/dist/solution/BaseSales/utils.js +6 -3
- package/dist/solution/BookingByStep/index.d.ts +2 -2
- 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 +8 -4
- package/dist/solution/BookingTicket/utils/addProductDecision.js +3 -1
- 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/ScanOrder/types.d.ts +2 -0
- package/dist/solution/ScanOrder/utils.js +6 -3
- 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 +131 -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 +209 -315
- 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 +43 -48
- 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 +133 -143
- 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 +1810 -2238
- package/lib/modules/Order/payment-utils.js +77 -120
- package/lib/modules/Order/types.d.ts +5 -1
- package/lib/modules/Order/types.js +87 -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/virtualSettlement.d.ts +8 -1
- package/lib/modules/Order/utils/virtualSettlement.js +126 -164
- package/lib/modules/Order/utils.js +834 -668
- 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.d.ts +2 -0
- package/lib/modules/Product/types.js +4 -16
- package/lib/modules/Product/utils.js +32 -33
- package/lib/modules/ProductList/index.js +138 -161
- package/lib/modules/ProductList/types.js +9 -31
- package/lib/modules/Quotation/index.js +66 -100
- 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 +591 -772
- 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 +78 -89
- package/lib/modules/SalesSummary/types.js +4 -16
- package/lib/modules/SalesSummary/utils.js +359 -442
- 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 +3027 -2655
- package/lib/server/modules/floor-plan/index.js +102 -123
- 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 +146 -122
- package/lib/server/modules/menu/types.js +18 -31
- package/lib/server/modules/order/index.js +758 -956
- 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 +575 -459
- 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 +213 -184
- 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 +1276 -1485
- package/lib/solution/BaseSales/types.d.ts +2 -0
- package/lib/solution/BaseSales/types.js +42 -38
- package/lib/solution/BaseSales/utils/cartPromotion.d.ts +5 -0
- package/lib/solution/BaseSales/utils/cartPromotion.js +652 -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.d.ts +3 -0
- package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +138 -180
- package/lib/solution/BaseSales/utils.js +161 -143
- package/lib/solution/BookingByStep/index.d.ts +2 -2
- 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 +598 -514
- package/lib/solution/BookingTicket/types.js +99 -77
- package/lib/solution/BookingTicket/utils/addProductDecision.js +229 -190
- 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 +822 -852
- package/lib/solution/ScanOrder/types.d.ts +2 -0
- package/lib/solution/ScanOrder/types.js +33 -34
- package/lib/solution/ScanOrder/utils.js +412 -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 +1052 -1213
- 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,93 +1,88 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
var
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
var _exportNames = {
|
|
7
|
+
buildPlannerDiscountResult: true,
|
|
8
|
+
normalizeProductForAvailability: true,
|
|
9
|
+
buildAvailabilityResourcesFromV2: true,
|
|
10
|
+
collectAvailabilityResourceIds: true,
|
|
11
|
+
buildAvailabilityLineIdentity: true,
|
|
12
|
+
buildBookingFromAvailabilityAssignment: true,
|
|
13
|
+
buildBookingFromAvailabilityAssignments: true,
|
|
14
|
+
UnifiedBookingSalesImpl: true,
|
|
15
|
+
UnifiedBookingSales: true,
|
|
16
|
+
AvailabilityError: true
|
|
10
17
|
};
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
18
|
+
Object.defineProperty(exports, "AvailabilityError", {
|
|
19
|
+
enumerable: true,
|
|
20
|
+
get: function () {
|
|
21
|
+
return _ResourcePlanner.AvailabilityError;
|
|
16
22
|
}
|
|
17
|
-
return to;
|
|
18
|
-
};
|
|
19
|
-
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
|
|
20
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
-
mod
|
|
27
|
-
));
|
|
28
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
-
|
|
30
|
-
// src/solution/UnifiedBookingSales/index.ts
|
|
31
|
-
var UnifiedBookingSales_exports = {};
|
|
32
|
-
__export(UnifiedBookingSales_exports, {
|
|
33
|
-
AvailabilityError: () => import_ResourcePlanner2.AvailabilityError,
|
|
34
|
-
UnifiedBookingSales: () => UnifiedBookingSales,
|
|
35
|
-
UnifiedBookingSalesImpl: () => UnifiedBookingSalesImpl,
|
|
36
|
-
buildAvailabilityLineIdentity: () => buildAvailabilityLineIdentity,
|
|
37
|
-
buildAvailabilityResourcesFromV2: () => buildAvailabilityResourcesFromV2,
|
|
38
|
-
buildBookingFromAvailabilityAssignment: () => buildBookingFromAvailabilityAssignment,
|
|
39
|
-
buildBookingFromAvailabilityAssignments: () => buildBookingFromAvailabilityAssignments,
|
|
40
|
-
buildPlannerDiscountResult: () => buildPlannerDiscountResult,
|
|
41
|
-
collectAvailabilityResourceIds: () => collectAvailabilityResourceIds,
|
|
42
|
-
normalizeProductForAvailability: () => normalizeProductForAvailability
|
|
43
23
|
});
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
var
|
|
53
|
-
var
|
|
54
|
-
|
|
55
|
-
var
|
|
56
|
-
var
|
|
57
|
-
var
|
|
24
|
+
exports.UnifiedBookingSalesImpl = exports.UnifiedBookingSales = void 0;
|
|
25
|
+
exports.buildAvailabilityLineIdentity = buildAvailabilityLineIdentity;
|
|
26
|
+
exports.buildAvailabilityResourcesFromV2 = buildAvailabilityResourcesFromV2;
|
|
27
|
+
exports.buildBookingFromAvailabilityAssignment = buildBookingFromAvailabilityAssignment;
|
|
28
|
+
exports.buildBookingFromAvailabilityAssignments = buildBookingFromAvailabilityAssignments;
|
|
29
|
+
exports.buildPlannerDiscountResult = buildPlannerDiscountResult;
|
|
30
|
+
exports.collectAvailabilityResourceIds = collectAvailabilityResourceIds;
|
|
31
|
+
exports.normalizeProductForAvailability = normalizeProductForAvailability;
|
|
32
|
+
var _dayjs = _interopRequireDefault(require("dayjs"));
|
|
33
|
+
var _lodashEs = require("lodash-es");
|
|
34
|
+
var _modules = require("../../modules");
|
|
35
|
+
var _getDateIsInSchedule = require("../../modules/Schedule/getDateIsInSchedule");
|
|
36
|
+
var _ResourcePlanner = require("../../modules/ResourcePlanner");
|
|
37
|
+
var _types = require("./types");
|
|
38
|
+
Object.keys(_types).forEach(function (key) {
|
|
39
|
+
if (key === "default" || key === "__esModule") return;
|
|
40
|
+
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
|
|
41
|
+
if (key in exports && exports[key] === _types[key]) return;
|
|
42
|
+
Object.defineProperty(exports, key, {
|
|
43
|
+
enumerable: true,
|
|
44
|
+
get: function () {
|
|
45
|
+
return _types[key];
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
var _remoteOccupancyCache = require("./remoteOccupancyCache");
|
|
50
|
+
var _BookingTicket = require("../BookingTicket");
|
|
51
|
+
var _localClock = require("../../modules/ResourcePlanner/localClock");
|
|
52
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
53
|
+
const OPEN_DATA_SECTION_CODES = ['sale', 'reservation', 'fulfillment', 'menu', 'workflow', 'basic', 'checkout', 'availability'];
|
|
54
|
+
const OPEN_DATA_CACHE_TTL = 5 * 60 * 1000;
|
|
58
55
|
function normalizePositiveInteger(value) {
|
|
59
56
|
const normalized = Number(value);
|
|
60
|
-
if (!Number.isInteger(normalized) || normalized <= 0)
|
|
61
|
-
return void 0;
|
|
57
|
+
if (!Number.isInteger(normalized) || normalized <= 0) return undefined;
|
|
62
58
|
return normalized;
|
|
63
59
|
}
|
|
64
60
|
function isRecord(value) {
|
|
65
|
-
return !!value && typeof value ===
|
|
61
|
+
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
66
62
|
}
|
|
67
63
|
function pickLocalizedText(value) {
|
|
68
|
-
if (!value)
|
|
69
|
-
|
|
70
|
-
if (typeof value ===
|
|
71
|
-
return value;
|
|
72
|
-
if (typeof value === "object") {
|
|
64
|
+
if (!value) return undefined;
|
|
65
|
+
if (typeof value === 'string') return value;
|
|
66
|
+
if (typeof value === 'object') {
|
|
73
67
|
return value.en || value.zh || value.default || Object.values(value)[0];
|
|
74
68
|
}
|
|
75
69
|
return String(value);
|
|
76
70
|
}
|
|
77
71
|
function resolvePlannerDiscountId(discount) {
|
|
78
|
-
|
|
79
|
-
return ((_a = discount.discount) == null ? void 0 : _a.resource_id) ?? discount.resource_id ?? discount.id;
|
|
72
|
+
return discount.discount?.resource_id ?? discount.resource_id ?? discount.id;
|
|
80
73
|
}
|
|
81
74
|
function resolvePlannerDiscountTitle(discount) {
|
|
82
|
-
|
|
83
|
-
return pickLocalizedText(
|
|
84
|
-
discount.name || discount.format_title || ((_a = discount.discount) == null ? void 0 : _a.title) || discount.title
|
|
85
|
-
);
|
|
75
|
+
return pickLocalizedText(discount.name || discount.format_title || discount.discount?.title || discount.title);
|
|
86
76
|
}
|
|
87
77
|
function isPlannerDiscountAvailable(discount) {
|
|
88
|
-
|
|
89
|
-
return discount.isDisabled !== true && discount.isAvailable !== false && ((_a = discount.config) == null ? void 0 : _a.isAvailable) !== false;
|
|
78
|
+
return discount.isDisabled !== true && discount.isAvailable !== false && discount.config?.isAvailable !== false;
|
|
90
79
|
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Converts Rules/Order discount internals into a small UI-facing diagnostic. The result keeps
|
|
83
|
+
* candidate-card state separate from line-level applications: a selected card is not proof that
|
|
84
|
+
* it actually discounted a planner product.
|
|
85
|
+
*/
|
|
91
86
|
function buildPlannerDiscountResult(params) {
|
|
92
87
|
const {
|
|
93
88
|
customerId,
|
|
@@ -101,39 +96,34 @@ function buildPlannerDiscountResult(params) {
|
|
|
101
96
|
} = params;
|
|
102
97
|
const items = (discountList || []).reduce((result, discount) => {
|
|
103
98
|
const id = resolvePlannerDiscountId(discount);
|
|
104
|
-
if (id !==
|
|
99
|
+
if (id !== undefined && id !== null) {
|
|
105
100
|
result.push({
|
|
106
101
|
id,
|
|
107
102
|
name: resolvePlannerDiscountTitle(discount),
|
|
108
103
|
type: discount.tag || discount.type,
|
|
109
104
|
available: isPlannerDiscountAvailable(discount),
|
|
110
105
|
selected: discount.isSelected === true,
|
|
111
|
-
savedAmount: Number(discount.savedAmount || 0) ||
|
|
106
|
+
savedAmount: Number(discount.savedAmount || 0) || undefined,
|
|
112
107
|
unavailableReason: discount.unavailableReason || discount.reason
|
|
113
108
|
});
|
|
114
109
|
}
|
|
115
110
|
return result;
|
|
116
111
|
}, []);
|
|
117
|
-
const discountNameById = new Map(items.map(
|
|
112
|
+
const discountNameById = new Map(items.map(item => [String(item.id), item.name]));
|
|
118
113
|
const applications = [];
|
|
119
|
-
(products || []).forEach(
|
|
120
|
-
const productDiscounts = [
|
|
121
|
-
|
|
122
|
-
...Array.isArray(product.product_bundle) ? product.product_bundle.flatMap((bundle) => (bundle == null ? void 0 : bundle.discount_list) || []) : []
|
|
123
|
-
];
|
|
124
|
-
productDiscounts.forEach((discount) => {
|
|
125
|
-
var _a;
|
|
114
|
+
(products || []).forEach(product => {
|
|
115
|
+
const productDiscounts = [...(Array.isArray(product.discount_list) ? product.discount_list : []), ...(Array.isArray(product.product_bundle) ? product.product_bundle.flatMap(bundle => bundle?.discount_list || []) : [])];
|
|
116
|
+
productDiscounts.forEach(discount => {
|
|
126
117
|
const type = discount.tag || discount.type;
|
|
127
|
-
if (![
|
|
128
|
-
return;
|
|
118
|
+
if (!['good_pass', 'discount_card', 'product_discount_card'].includes(type)) return;
|
|
129
119
|
const discountId = resolvePlannerDiscountId(discount);
|
|
130
120
|
applications.push({
|
|
131
121
|
discountId,
|
|
132
|
-
discountName: discountId ===
|
|
122
|
+
discountName: discountId === undefined ? undefined : discountNameById.get(String(discountId)),
|
|
133
123
|
discountType: type,
|
|
134
124
|
productId: product.product_id ?? product.id,
|
|
135
125
|
productTitle: pickLocalizedText(product.title || product.name || product.product_title),
|
|
136
|
-
productUid:
|
|
126
|
+
productUid: product.metadata?.unique_identification_number || product.unique_identification_number,
|
|
137
127
|
amount: Number(discount.amount || 0)
|
|
138
128
|
});
|
|
139
129
|
});
|
|
@@ -142,17 +132,16 @@ function buildPlannerDiscountResult(params) {
|
|
|
142
132
|
status,
|
|
143
133
|
customerId,
|
|
144
134
|
preferredDiscountFound,
|
|
145
|
-
preferredDiscountApplied: preferredDiscountId ===
|
|
146
|
-
availableDiscounts: items.filter(
|
|
147
|
-
selectedDiscounts: items.filter(
|
|
135
|
+
preferredDiscountApplied: preferredDiscountId === undefined || preferredDiscountId === null ? undefined : items.some(item => String(item.id) === String(preferredDiscountId) && item.selected) && applications.some(item => String(item.discountId) === String(preferredDiscountId)),
|
|
136
|
+
availableDiscounts: items.filter(item => item.available),
|
|
137
|
+
selectedDiscounts: items.filter(item => item.selected),
|
|
148
138
|
applications,
|
|
149
139
|
summaryBefore,
|
|
150
140
|
summaryAfter
|
|
151
141
|
};
|
|
152
142
|
}
|
|
153
143
|
function toNumberId(value) {
|
|
154
|
-
if (value ===
|
|
155
|
-
return void 0;
|
|
144
|
+
if (value === undefined || value === null || value === '') return undefined;
|
|
156
145
|
const parsed = Number(value);
|
|
157
146
|
return Number.isFinite(parsed) ? parsed : String(value);
|
|
158
147
|
}
|
|
@@ -161,51 +150,45 @@ function toCustomerId(value) {
|
|
|
161
150
|
return Number.isFinite(parsed) && parsed > 1 ? parsed : null;
|
|
162
151
|
}
|
|
163
152
|
function resolveTempOrderCustomerId(tempOrder) {
|
|
164
|
-
|
|
165
|
-
return toCustomerId(
|
|
166
|
-
(tempOrder == null ? void 0 : tempOrder.customer_id) || ((_a = tempOrder == null ? void 0 : tempOrder.customer) == null ? void 0 : _a.id) || ((_c = (_b = tempOrder == null ? void 0 : tempOrder._extend) == null ? void 0 : _b.customerSnapshot) == null ? void 0 : _c.id) || ((_e = (_d = tempOrder == null ? void 0 : tempOrder._extend) == null ? void 0 : _d.customerSnapshot) == null ? void 0 : _e.customer_id)
|
|
167
|
-
);
|
|
153
|
+
return toCustomerId(tempOrder?.customer_id || tempOrder?.customer?.id || tempOrder?._extend?.customerSnapshot?.id || tempOrder?._extend?.customerSnapshot?.customer_id);
|
|
168
154
|
}
|
|
169
155
|
function isLocalOrderId(orderId) {
|
|
170
|
-
return typeof orderId ===
|
|
156
|
+
return typeof orderId === 'string' && orderId.startsWith('local_order');
|
|
171
157
|
}
|
|
172
158
|
function isCustomerBoundDiscount(discount) {
|
|
173
|
-
const type =
|
|
174
|
-
return type ===
|
|
159
|
+
const type = discount?.type ?? discount?.tag;
|
|
160
|
+
return type === 'good_pass' || type === 'discount_card' || type === 'product_discount_card';
|
|
175
161
|
}
|
|
176
162
|
function filterCustomerBoundDiscounts(discountList) {
|
|
177
|
-
if (!Array.isArray(discountList))
|
|
178
|
-
|
|
179
|
-
return discountList.filter((discount) => !isCustomerBoundDiscount(discount));
|
|
163
|
+
if (!Array.isArray(discountList)) return [];
|
|
164
|
+
return discountList.filter(discount => !isCustomerBoundDiscount(discount));
|
|
180
165
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
startTime:
|
|
184
|
-
endTime:
|
|
166
|
+
const DEFAULT_AVAILABILITY_SLOT_STEP_MINUTES = 30;
|
|
167
|
+
const DEFAULT_AVAILABILITY_BUSINESS_HOURS = {
|
|
168
|
+
startTime: '09:00',
|
|
169
|
+
endTime: '18:00'
|
|
185
170
|
};
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
171
|
+
const MAX_AVAILABILITY_RANGE_DAYS = 90;
|
|
172
|
+
const MAX_AVAILABILITY_CONTEXT_REGISTRY = 128;
|
|
173
|
+
const MAX_AVAILABILITY_PROJECTION_POOL = 16;
|
|
174
|
+
const MAX_REMOTE_OCCUPANCY_RESOURCE_DATE_CELLS = 4096;
|
|
190
175
|
function isValidClockTime(value) {
|
|
191
176
|
const match = /^(\d{2}):(\d{2})$/.exec(value);
|
|
192
|
-
if (!match)
|
|
193
|
-
return false;
|
|
177
|
+
if (!match) return false;
|
|
194
178
|
return Number(match[1]) < 24 && Number(match[2]) < 60;
|
|
195
179
|
}
|
|
196
180
|
function assertValidAvailabilityTimezone(timezoneName) {
|
|
197
|
-
if (!String(timezoneName ||
|
|
198
|
-
throw new
|
|
181
|
+
if (!String(timezoneName || '').trim()) {
|
|
182
|
+
throw new _ResourcePlanner.AvailabilityError('INVALID_TARGET_FILTER', 'timezone label 不能为空', {
|
|
199
183
|
timezone: timezoneName
|
|
200
184
|
});
|
|
201
185
|
}
|
|
202
186
|
}
|
|
203
187
|
function normalizeAvailabilityLocalDateTime(value, fixedOffsetMinutes) {
|
|
204
|
-
if (value == null || value ===
|
|
205
|
-
return null;
|
|
188
|
+
if (value == null || value === '') return null;
|
|
206
189
|
try {
|
|
207
|
-
const localText = typeof
|
|
208
|
-
return (0,
|
|
190
|
+
const localText = typeof value?.format === 'function' ? value.format('YYYY-MM-DDTHH:mm:ss') : String(value);
|
|
191
|
+
return (0, _localClock.formatLocalDateTime)((0, _localClock.parseLocalDateTime)(localText), fixedOffsetMinutes);
|
|
209
192
|
} catch {
|
|
210
193
|
return null;
|
|
211
194
|
}
|
|
@@ -213,46 +196,42 @@ function normalizeAvailabilityLocalDateTime(value, fixedOffsetMinutes) {
|
|
|
213
196
|
function normalizeAvailabilityLocalDateTimeRange(params) {
|
|
214
197
|
const startText = normalizeAvailabilityLocalDateTime(params.startAt, params.fixedOffsetMinutes);
|
|
215
198
|
const endText = normalizeAvailabilityLocalDateTime(params.endAt, params.fixedOffsetMinutes);
|
|
216
|
-
if (!startText || !endText)
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
let endScalar = (0, import_localClock.localDateTimeToScalar)((0, import_localClock.parseLocalDateTime)(endText));
|
|
199
|
+
if (!startText || !endText) return null;
|
|
200
|
+
const startScalar = (0, _localClock.localDateTimeToScalar)((0, _localClock.parseLocalDateTime)(startText));
|
|
201
|
+
let endScalar = (0, _localClock.localDateTimeToScalar)((0, _localClock.parseLocalDateTime)(endText));
|
|
220
202
|
if (endScalar <= startScalar && params.rollOverEnd) {
|
|
221
|
-
endScalar = (0,
|
|
203
|
+
endScalar = (0, _localClock.addLocalCalendarDays)(endScalar, 1);
|
|
222
204
|
}
|
|
223
|
-
if (endScalar <= startScalar)
|
|
224
|
-
return null;
|
|
205
|
+
if (endScalar <= startScalar) return null;
|
|
225
206
|
return {
|
|
226
207
|
startAt: startText,
|
|
227
|
-
endAt: (0,
|
|
208
|
+
endAt: (0, _localClock.formatLocalDateTime)(endScalar, params.fixedOffsetMinutes),
|
|
228
209
|
startScalar,
|
|
229
210
|
endScalar
|
|
230
211
|
};
|
|
231
212
|
}
|
|
232
213
|
function sameAvailabilityId(left, right) {
|
|
233
|
-
return left !==
|
|
214
|
+
return left !== undefined && left !== null && right !== undefined && right !== null && String(left) === String(right);
|
|
234
215
|
}
|
|
235
216
|
function uniqueAvailabilityIds(values) {
|
|
236
|
-
const result =
|
|
237
|
-
values.forEach(
|
|
217
|
+
const result = new Map();
|
|
218
|
+
values.forEach(value => {
|
|
238
219
|
const id = toNumberId(value);
|
|
239
|
-
if (id !==
|
|
240
|
-
result.set(String(id), id);
|
|
220
|
+
if (id !== undefined) result.set(String(id), id);
|
|
241
221
|
});
|
|
242
222
|
return Array.from(result.values());
|
|
243
223
|
}
|
|
244
224
|
function assertAvailabilityCatalogProductIds(productIds, context) {
|
|
245
|
-
if (productIds == null)
|
|
246
|
-
return;
|
|
225
|
+
if (productIds == null) return;
|
|
247
226
|
if (!Array.isArray(productIds)) {
|
|
248
|
-
throw new
|
|
227
|
+
throw new _ResourcePlanner.AvailabilityError('INVALID_TARGET_FILTER', `${context} 必须是 number[]`, {
|
|
249
228
|
context,
|
|
250
229
|
productIds
|
|
251
230
|
});
|
|
252
231
|
}
|
|
253
|
-
const invalidIds = productIds.filter(
|
|
232
|
+
const invalidIds = productIds.filter(id => typeof id !== 'number' || !Number.isFinite(id));
|
|
254
233
|
if (invalidIds.length > 0) {
|
|
255
|
-
throw new
|
|
234
|
+
throw new _ResourcePlanner.AvailabilityError('INVALID_TARGET_FILTER', `${context} 只能包含有限数值商品 ID`, {
|
|
256
235
|
context,
|
|
257
236
|
invalidIds,
|
|
258
237
|
productIds
|
|
@@ -260,74 +239,63 @@ function assertAvailabilityCatalogProductIds(productIds, context) {
|
|
|
260
239
|
}
|
|
261
240
|
}
|
|
262
241
|
function normalizeResourceType(_value) {
|
|
263
|
-
|
|
242
|
+
// 当前业务只支持每个资源组选择一个具体资源。
|
|
243
|
+
// 保留公共联合类型以兼容已有调用方,但暂不启用 multiple/capacity 分支。
|
|
244
|
+
return 'single';
|
|
264
245
|
}
|
|
265
246
|
function resolveProductKind(product) {
|
|
266
|
-
|
|
267
|
-
if (product.
|
|
268
|
-
|
|
269
|
-
if (product.
|
|
270
|
-
return
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
if (
|
|
274
|
-
|
|
275
|
-
}
|
|
276
|
-
const scheduleIds = product["schedule.ids"] || product.schedule_ids;
|
|
277
|
-
if (Array.isArray(scheduleIds) && scheduleIds.length > 0)
|
|
278
|
-
return "session";
|
|
279
|
-
return "normal";
|
|
247
|
+
if (product.duration || product.service_duration) return 'duration';
|
|
248
|
+
if (product.extension_type === 'venue_slot') return 'venue_slot';
|
|
249
|
+
if (['session_product', 'session_ticket'].includes(product.extension_type)) return 'session';
|
|
250
|
+
if (product.cartDetailValue?.session || product._extend?.other?.session || product.session) {
|
|
251
|
+
return 'session';
|
|
252
|
+
}
|
|
253
|
+
const scheduleIds = product['schedule.ids'] || product.schedule_ids;
|
|
254
|
+
if (Array.isArray(scheduleIds) && scheduleIds.length > 0) return 'session';
|
|
255
|
+
return 'normal';
|
|
280
256
|
}
|
|
281
257
|
function expandScheduleDateRanges(values) {
|
|
282
|
-
if (!Array.isArray(values))
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
if (typeof value === "string") {
|
|
258
|
+
if (!Array.isArray(values)) return [];
|
|
259
|
+
const dates = new Set();
|
|
260
|
+
values.forEach(value => {
|
|
261
|
+
if (typeof value === 'string') {
|
|
287
262
|
try {
|
|
288
|
-
dates.add((0,
|
|
263
|
+
dates.add((0, _localClock.formatLocalDate)((0, _localClock.parseLocalDate)(value.slice(0, 10))));
|
|
289
264
|
} catch {
|
|
265
|
+
// Ignore invalid configuration entries.
|
|
290
266
|
}
|
|
291
267
|
return;
|
|
292
268
|
}
|
|
293
|
-
const startValue =
|
|
294
|
-
const endValue =
|
|
269
|
+
const startValue = value?.start || value?.start_date || value?.date;
|
|
270
|
+
const endValue = value?.end || value?.end_date || startValue;
|
|
295
271
|
let start;
|
|
296
272
|
let end;
|
|
297
273
|
try {
|
|
298
|
-
start = (0,
|
|
299
|
-
end = (0,
|
|
274
|
+
start = (0, _localClock.localDateToScalarStart)((0, _localClock.parseLocalDate)(String(startValue).slice(0, 10)));
|
|
275
|
+
end = (0, _localClock.localDateToScalarStart)((0, _localClock.parseLocalDate)(String(endValue).slice(0, 10)));
|
|
300
276
|
} catch {
|
|
301
277
|
return;
|
|
302
278
|
}
|
|
303
|
-
if (end < start)
|
|
304
|
-
return;
|
|
279
|
+
if (end < start) return;
|
|
305
280
|
let cursor = start;
|
|
306
281
|
let guard = 0;
|
|
307
282
|
while (cursor <= end && guard < MAX_AVAILABILITY_RANGE_DAYS) {
|
|
308
|
-
dates.add((0,
|
|
309
|
-
cursor = (0,
|
|
283
|
+
dates.add((0, _localClock.formatLocalDate)(cursor));
|
|
284
|
+
cursor = (0, _localClock.addLocalCalendarDays)(cursor, 1);
|
|
310
285
|
guard += 1;
|
|
311
286
|
}
|
|
312
287
|
});
|
|
313
288
|
return Array.from(dates).sort();
|
|
314
289
|
}
|
|
315
290
|
function normalizeRequirementGroups(product) {
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
return [];
|
|
320
|
-
return resources.filter((resource) => (resource == null ? void 0 : resource.status) !== 0).map((resource, index) => {
|
|
321
|
-
var _a2, _b, _c;
|
|
291
|
+
const resources = product.product_resource?.resources;
|
|
292
|
+
if (!Array.isArray(resources)) return [];
|
|
293
|
+
return resources.filter(resource => resource?.status !== 0).map((resource, index) => {
|
|
322
294
|
const formId = toNumberId(resource.id ?? resource.form_id);
|
|
323
295
|
const required = resource.required ?? resource.status === 1;
|
|
324
296
|
const resourceType = normalizeResourceType(resource.type);
|
|
325
|
-
const combinedIds =
|
|
326
|
-
const resourceIds = uniqueAvailabilityIds([
|
|
327
|
-
...Array.isArray(resource.default_resource) ? resource.default_resource : [],
|
|
328
|
-
...Array.isArray(resource.optional_resource) ? resource.optional_resource : [],
|
|
329
|
-
...Array.isArray(combinedIds) ? combinedIds : []
|
|
330
|
-
]);
|
|
297
|
+
const combinedIds = resource.combined_resource?.status === 1 ? resource.combined_resource?.resource_ids : [];
|
|
298
|
+
const resourceIds = uniqueAvailabilityIds([...(Array.isArray(resource.default_resource) ? resource.default_resource : []), ...(Array.isArray(resource.optional_resource) ? resource.optional_resource : []), ...(Array.isArray(combinedIds) ? combinedIds : [])]);
|
|
331
299
|
const min = required ? 1 : 0;
|
|
332
300
|
const max = 1;
|
|
333
301
|
return {
|
|
@@ -340,30 +308,25 @@ function normalizeRequirementGroups(product) {
|
|
|
340
308
|
min,
|
|
341
309
|
max,
|
|
342
310
|
resourceIds,
|
|
343
|
-
capacityRequired: Math.max(1, Number(
|
|
344
|
-
resource.capacity_required ?? ((_c = product.capacity) == null ? void 0 : _c.default) ?? product.capacity ?? 1
|
|
345
|
-
) || 1),
|
|
311
|
+
capacityRequired: Math.max(1, Number(resource.capacity_required ?? product.capacity?.default ?? product.capacity ?? 1) || 1),
|
|
346
312
|
raw: resource
|
|
347
313
|
};
|
|
348
314
|
});
|
|
349
315
|
}
|
|
350
316
|
function normalizeScheduleRepeatType(value) {
|
|
351
|
-
if (value ===
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
return "weekly";
|
|
355
|
-
return "none";
|
|
317
|
+
if (value === 'daily' || value === 1 || value === '1') return 'daily';
|
|
318
|
+
if (value === 'weekly' || value === 2 || value === '2') return 'weekly';
|
|
319
|
+
return 'none';
|
|
356
320
|
}
|
|
357
321
|
function normalizeAbsoluteSessionRange(params) {
|
|
358
|
-
const fixedOffsetMinutes = params.fixedOffsetMinutes ?? (0,
|
|
322
|
+
const fixedOffsetMinutes = params.fixedOffsetMinutes ?? (0, _localClock.captureRuntimeOffsetMinutes)();
|
|
359
323
|
const range = normalizeAvailabilityLocalDateTimeRange({
|
|
360
324
|
startAt: params.startAt,
|
|
361
325
|
endAt: params.endAt,
|
|
362
326
|
fixedOffsetMinutes,
|
|
363
327
|
rollOverEnd: true
|
|
364
328
|
});
|
|
365
|
-
if (!range)
|
|
366
|
-
return null;
|
|
329
|
+
if (!range) return null;
|
|
367
330
|
return {
|
|
368
331
|
startAt: range.startAt,
|
|
369
332
|
endAt: range.endAt,
|
|
@@ -375,7 +338,6 @@ function normalizeAbsoluteSessionRange(params) {
|
|
|
375
338
|
};
|
|
376
339
|
}
|
|
377
340
|
function normalizeScheduleItem(schedule, fixedOffsetMinutes) {
|
|
378
|
-
var _a, _b;
|
|
379
341
|
const rules = [];
|
|
380
342
|
const ranges = [];
|
|
381
343
|
const scheduleId = toNumberId(schedule.id ?? schedule.schedule_id);
|
|
@@ -383,61 +345,61 @@ function normalizeScheduleItem(schedule, fixedOffsetMinutes) {
|
|
|
383
345
|
const repeatRule = schedule.repeat_rule || {};
|
|
384
346
|
const startAt = schedule.start_at || schedule.start || schedule.start_time;
|
|
385
347
|
const endAt = schedule.end_at || schedule.end || schedule.end_time;
|
|
386
|
-
const startDate = startAt ? String(startAt).slice(0, 10) :
|
|
387
|
-
const configuredEndDate =
|
|
388
|
-
const endDate = configuredEndDate ? String(configuredEndDate).slice(0, 10) :
|
|
348
|
+
const startDate = startAt ? String(startAt).slice(0, 10) : undefined;
|
|
349
|
+
const configuredEndDate = repeatRule.end?.type === 'date' ? repeatRule.end?.end_date : undefined;
|
|
350
|
+
const endDate = configuredEndDate ? String(configuredEndDate).slice(0, 10) : undefined;
|
|
389
351
|
const includedDates = expandScheduleDateRanges(repeatRule.included_date);
|
|
390
352
|
const excludedDates = expandScheduleDateRanges(repeatRule.excluded_date);
|
|
391
|
-
const addRule =
|
|
392
|
-
const mode = repeatType ===
|
|
353
|
+
const addRule = params => {
|
|
354
|
+
const mode = repeatType === 'weekly' ? 'weekly' : 'daily';
|
|
393
355
|
rules.push({
|
|
394
|
-
id: `${String(scheduleId ??
|
|
356
|
+
id: `${String(scheduleId ?? 'schedule')}:${params.idSuffix}`,
|
|
395
357
|
mode,
|
|
396
358
|
startTime: params.startTime.slice(0, 5),
|
|
397
359
|
endTime: params.endTime.slice(0, 5),
|
|
398
360
|
startDate,
|
|
399
361
|
endDate,
|
|
400
362
|
frequency: Math.max(1, Number(repeatRule.frequency || 1)),
|
|
401
|
-
weekdays: repeatType ===
|
|
363
|
+
weekdays: repeatType === 'weekly' ? (repeatRule.frequency_date || []).map(Number).filter(Number.isFinite) : undefined,
|
|
402
364
|
includeDates: includedDates,
|
|
403
365
|
excludeDates: excludedDates,
|
|
404
366
|
scheduleRef: {
|
|
405
367
|
scheduleId,
|
|
406
368
|
timeSlotId: params.timeSlotId,
|
|
407
|
-
source: `schedule_${schedule.type ||
|
|
369
|
+
source: `schedule_${schedule.type || 'standard'}`
|
|
408
370
|
}
|
|
409
371
|
});
|
|
410
372
|
};
|
|
411
|
-
if (schedule.type ===
|
|
373
|
+
if (schedule.type === 'designation') {
|
|
412
374
|
const designations = Array.isArray(schedule.designation) ? schedule.designation : schedule.designation ? [schedule.designation] : [];
|
|
413
|
-
designations.forEach(
|
|
375
|
+
designations.forEach(designation => {
|
|
414
376
|
const range = normalizeAbsoluteSessionRange({
|
|
415
377
|
startAt: `${designation.start_date} ${designation.start_time}`,
|
|
416
378
|
endAt: `${designation.end_date || designation.start_date} ${designation.end_time}`,
|
|
417
379
|
scheduleId,
|
|
418
|
-
source:
|
|
380
|
+
source: 'schedule_designation',
|
|
419
381
|
fixedOffsetMinutes
|
|
420
382
|
});
|
|
421
|
-
if (range)
|
|
422
|
-
ranges.push(range);
|
|
383
|
+
if (range) ranges.push(range);
|
|
423
384
|
});
|
|
424
|
-
return {
|
|
385
|
+
return {
|
|
386
|
+
rules,
|
|
387
|
+
ranges
|
|
388
|
+
};
|
|
425
389
|
}
|
|
426
|
-
if (schedule.type ===
|
|
390
|
+
if (schedule.type === 'time-slots' && Array.isArray(schedule.time_slot)) {
|
|
427
391
|
schedule.time_slot.forEach((slot, index) => {
|
|
428
|
-
if (!slot.start_time || !slot.end_time)
|
|
429
|
-
|
|
430
|
-
if (repeatType === "none" && startDate) {
|
|
392
|
+
if (!slot.start_time || !slot.end_time) return;
|
|
393
|
+
if (repeatType === 'none' && startDate) {
|
|
431
394
|
const range = normalizeAbsoluteSessionRange({
|
|
432
395
|
startAt: `${startDate} ${slot.start_time}`,
|
|
433
396
|
endAt: `${startDate} ${slot.end_time}`,
|
|
434
397
|
scheduleId,
|
|
435
398
|
timeSlotId: toNumberId(slot.id),
|
|
436
|
-
source:
|
|
399
|
+
source: 'schedule_time_slot',
|
|
437
400
|
fixedOffsetMinutes
|
|
438
401
|
});
|
|
439
|
-
if (range)
|
|
440
|
-
ranges.push(range);
|
|
402
|
+
if (range) ranges.push(range);
|
|
441
403
|
return;
|
|
442
404
|
}
|
|
443
405
|
addRule({
|
|
@@ -447,69 +409,70 @@ function normalizeScheduleItem(schedule, fixedOffsetMinutes) {
|
|
|
447
409
|
timeSlotId: toNumberId(slot.id)
|
|
448
410
|
});
|
|
449
411
|
});
|
|
450
|
-
return {
|
|
412
|
+
return {
|
|
413
|
+
rules,
|
|
414
|
+
ranges
|
|
415
|
+
};
|
|
451
416
|
}
|
|
452
417
|
if (startAt && endAt) {
|
|
453
|
-
if (repeatType ===
|
|
418
|
+
if (repeatType === 'none') {
|
|
454
419
|
const range = normalizeAbsoluteSessionRange({
|
|
455
420
|
startAt,
|
|
456
421
|
endAt,
|
|
457
422
|
scheduleId,
|
|
458
|
-
source:
|
|
423
|
+
source: 'schedule_standard',
|
|
459
424
|
fixedOffsetMinutes
|
|
460
425
|
});
|
|
461
|
-
if (range)
|
|
462
|
-
ranges.push(range);
|
|
426
|
+
if (range) ranges.push(range);
|
|
463
427
|
} else {
|
|
464
428
|
try {
|
|
465
429
|
addRule({
|
|
466
|
-
idSuffix:
|
|
467
|
-
startTime: (0,
|
|
468
|
-
endTime: (0,
|
|
430
|
+
idSuffix: 'standard',
|
|
431
|
+
startTime: (0, _localClock.formatLocalTime)(String(startAt), false),
|
|
432
|
+
endTime: (0, _localClock.formatLocalTime)(String(endAt), false)
|
|
469
433
|
});
|
|
470
434
|
} catch {
|
|
435
|
+
// Invalid schedule boundaries do not produce a recurring rule.
|
|
471
436
|
}
|
|
472
437
|
}
|
|
473
438
|
}
|
|
474
|
-
return {
|
|
439
|
+
return {
|
|
440
|
+
rules,
|
|
441
|
+
ranges
|
|
442
|
+
};
|
|
475
443
|
}
|
|
476
444
|
function resolveProductScheduleItems(product, scheduleList) {
|
|
477
|
-
const scheduleMap = new Map(scheduleList.map(
|
|
478
|
-
const rawSchedules = [product.schedules, product.schedule, product._schedule].flatMap(
|
|
479
|
-
const explicitIds = uniqueAvailabilityIds([
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
...rawSchedules.filter((item) => typeof item !== "object")
|
|
483
|
-
]);
|
|
484
|
-
const result = /* @__PURE__ */ new Map();
|
|
485
|
-
rawSchedules.filter((item) => item && typeof item === "object").forEach((schedule) => {
|
|
445
|
+
const scheduleMap = new Map(scheduleList.map(schedule => [String(schedule.id), schedule]));
|
|
446
|
+
const rawSchedules = [product.schedules, product.schedule, product._schedule].flatMap(value => Array.isArray(value) ? value : value ? [value] : []);
|
|
447
|
+
const explicitIds = uniqueAvailabilityIds([...(Array.isArray(product['schedule.ids']) ? product['schedule.ids'] : []), ...(Array.isArray(product.schedule_ids) ? product.schedule_ids : []), ...rawSchedules.filter(item => typeof item !== 'object')]);
|
|
448
|
+
const result = new Map();
|
|
449
|
+
rawSchedules.filter(item => item && typeof item === 'object').forEach(schedule => {
|
|
486
450
|
result.set(String(schedule.id ?? JSON.stringify(schedule)), schedule);
|
|
487
451
|
});
|
|
488
|
-
explicitIds.forEach(
|
|
452
|
+
explicitIds.forEach(id => {
|
|
489
453
|
const schedule = scheduleMap.get(String(id));
|
|
490
|
-
if (schedule)
|
|
491
|
-
result.set(String(id), schedule);
|
|
454
|
+
if (schedule) result.set(String(id), schedule);
|
|
492
455
|
});
|
|
493
456
|
return Array.from(result.values());
|
|
494
457
|
}
|
|
495
|
-
|
|
496
|
-
|
|
458
|
+
|
|
459
|
+
/** Converts ProductList and schedule records into the pure Availability vocabulary. */
|
|
460
|
+
function normalizeProductForAvailability(product, scheduleList = [], fixedOffsetMinutes = (0, _localClock.captureRuntimeOffsetMinutes)()) {
|
|
497
461
|
const raw = product;
|
|
498
462
|
const kind = resolveProductKind(raw);
|
|
499
463
|
const sessionRules = [];
|
|
500
464
|
const sessionRanges = [];
|
|
501
|
-
const explicitSession =
|
|
465
|
+
const explicitSession = raw.cartDetailValue?.session || raw._extend?.other?.session || raw.session;
|
|
502
466
|
const explicitRange = normalizeAbsoluteSessionRange({
|
|
503
|
-
startAt: explicitSession
|
|
504
|
-
endAt: explicitSession
|
|
505
|
-
scheduleId: toNumberId(explicitSession
|
|
506
|
-
timeSlotId: toNumberId(explicitSession
|
|
507
|
-
source:
|
|
467
|
+
startAt: explicitSession?.start_at,
|
|
468
|
+
endAt: explicitSession?.end_at,
|
|
469
|
+
scheduleId: toNumberId(explicitSession?.schedule_id),
|
|
470
|
+
timeSlotId: toNumberId(explicitSession?.time_slot_id),
|
|
471
|
+
source: 'product_session',
|
|
508
472
|
fixedOffsetMinutes
|
|
509
473
|
});
|
|
510
|
-
if (explicitRange)
|
|
511
|
-
|
|
512
|
-
resolveProductScheduleItems(raw, scheduleList).forEach((schedule) => {
|
|
474
|
+
if (explicitRange) sessionRanges.push(explicitRange);
|
|
475
|
+
resolveProductScheduleItems(raw, scheduleList).forEach(schedule => {
|
|
513
476
|
const normalized = normalizeScheduleItem(schedule, fixedOffsetMinutes);
|
|
514
477
|
sessionRules.push(...normalized.rules);
|
|
515
478
|
sessionRanges.push(...normalized.ranges);
|
|
@@ -518,11 +481,11 @@ function normalizeProductForAvailability(product, scheduleList = [], fixedOffset
|
|
|
518
481
|
id: raw.id ?? raw.product_id,
|
|
519
482
|
title: pickLocalizedText(raw.title || raw.name || raw.product_title),
|
|
520
483
|
kind,
|
|
521
|
-
durationMinutes: kind ===
|
|
484
|
+
durationMinutes: kind === 'duration' ? Math.max(1, Number(raw.duration?.value || raw.duration || raw.service_duration || 30) || 30) : undefined,
|
|
522
485
|
requirementGroups: normalizeRequirementGroups(raw),
|
|
523
486
|
sessionRules,
|
|
524
487
|
sessionRanges,
|
|
525
|
-
capacityRequired: Math.max(1, Number(
|
|
488
|
+
capacityRequired: Math.max(1, Number(raw.capacity?.default || raw.capacity || 1) || 1),
|
|
526
489
|
quantity: Math.max(1, Number(raw.num || raw.quantity || 1) || 1),
|
|
527
490
|
cutOffPolicy: normalizeAvailabilityCutOffPolicy(raw.cut_off_time),
|
|
528
491
|
raw
|
|
@@ -530,102 +493,121 @@ function normalizeProductForAvailability(product, scheduleList = [], fixedOffset
|
|
|
530
493
|
}
|
|
531
494
|
function normalizeAvailabilityCutOffNumber(value, context) {
|
|
532
495
|
if (value == null) {
|
|
533
|
-
return {
|
|
496
|
+
return {
|
|
497
|
+
reason: `${context} 缺失`
|
|
498
|
+
};
|
|
534
499
|
}
|
|
535
|
-
if (typeof value ===
|
|
536
|
-
return {
|
|
500
|
+
if (typeof value === 'string' && !value.trim()) {
|
|
501
|
+
return {
|
|
502
|
+
reason: `${context} 缺失`
|
|
503
|
+
};
|
|
537
504
|
}
|
|
538
505
|
const normalized = Number(value);
|
|
539
506
|
if (!Number.isFinite(normalized) || normalized < 0) {
|
|
540
|
-
return {
|
|
507
|
+
return {
|
|
508
|
+
reason: `${context} 必须是非负有限数`
|
|
509
|
+
};
|
|
541
510
|
}
|
|
542
|
-
return {
|
|
511
|
+
return {
|
|
512
|
+
value: normalized
|
|
513
|
+
};
|
|
543
514
|
}
|
|
544
515
|
function normalizeAvailabilityCutOffInteger(value, context) {
|
|
545
516
|
const numeric = normalizeAvailabilityCutOffNumber(value, context);
|
|
546
|
-
if (numeric.reason)
|
|
547
|
-
return numeric;
|
|
517
|
+
if (numeric.reason) return numeric;
|
|
548
518
|
if (!Number.isInteger(numeric.value)) {
|
|
549
|
-
return {
|
|
519
|
+
return {
|
|
520
|
+
reason: `${context} 必须是非负整数`
|
|
521
|
+
};
|
|
550
522
|
}
|
|
551
|
-
return {
|
|
523
|
+
return {
|
|
524
|
+
value: numeric.value
|
|
525
|
+
};
|
|
552
526
|
}
|
|
553
527
|
function invalidAvailabilityCutOffPolicy(reason) {
|
|
554
|
-
return {
|
|
528
|
+
return {
|
|
529
|
+
type: 'invalid',
|
|
530
|
+
reason
|
|
531
|
+
};
|
|
555
532
|
}
|
|
556
533
|
function normalizeAvailabilityFutureDay(raw) {
|
|
557
|
-
if (!Object.prototype.hasOwnProperty.call(raw,
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
534
|
+
if (!Object.prototype.hasOwnProperty.call(raw, 'future_day')) return {};
|
|
535
|
+
const futureDay = normalizeAvailabilityCutOffInteger(raw.future_day, 'cut_off_time.future_day');
|
|
536
|
+
return futureDay.reason ? {
|
|
537
|
+
reason: futureDay.reason
|
|
538
|
+
} : {
|
|
539
|
+
futureDay: futureDay.value
|
|
540
|
+
};
|
|
561
541
|
}
|
|
562
542
|
function normalizeAvailabilityCutOffPolicy(raw) {
|
|
563
|
-
if (raw == null || raw ===
|
|
564
|
-
return void 0;
|
|
543
|
+
if (raw == null || raw === '') return undefined;
|
|
565
544
|
if (!isRecord(raw)) {
|
|
566
|
-
return invalidAvailabilityCutOffPolicy(
|
|
545
|
+
return invalidAvailabilityCutOffPolicy('cut_off_time 必须是对象');
|
|
567
546
|
}
|
|
568
547
|
const futureDay = normalizeAvailabilityFutureDay(raw);
|
|
569
548
|
if (futureDay.reason) {
|
|
570
549
|
return invalidAvailabilityCutOffPolicy(futureDay.reason);
|
|
571
550
|
}
|
|
572
|
-
const type = String(raw.type ??
|
|
551
|
+
const type = String(raw.type ?? '').trim();
|
|
573
552
|
if (!type) {
|
|
574
|
-
return invalidAvailabilityCutOffPolicy(
|
|
553
|
+
return invalidAvailabilityCutOffPolicy('cut_off_time.type 缺失');
|
|
575
554
|
}
|
|
576
|
-
if (type ===
|
|
555
|
+
if (type === 'ongoing') {
|
|
577
556
|
if (!isRecord(raw.ongoing)) {
|
|
578
|
-
return invalidAvailabilityCutOffPolicy(
|
|
557
|
+
return invalidAvailabilityCutOffPolicy('cut_off_time.ongoing 缺失');
|
|
579
558
|
}
|
|
580
|
-
const ongoingType = String(raw.ongoing.type ??
|
|
581
|
-
if (ongoingType ===
|
|
559
|
+
const ongoingType = String(raw.ongoing.type ?? '').trim();
|
|
560
|
+
if (ongoingType === 'before_end') {
|
|
582
561
|
return {
|
|
583
|
-
type:
|
|
584
|
-
mode:
|
|
585
|
-
...futureDay.futureDay ===
|
|
562
|
+
type: 'ongoing',
|
|
563
|
+
mode: 'before_end',
|
|
564
|
+
...(futureDay.futureDay === undefined ? {} : {
|
|
565
|
+
futureDay: futureDay.futureDay
|
|
566
|
+
})
|
|
586
567
|
};
|
|
587
568
|
}
|
|
588
|
-
if (ongoingType ===
|
|
589
|
-
const graceMinutes = normalizeAvailabilityCutOffNumber(
|
|
590
|
-
raw.ongoing.unit,
|
|
591
|
-
"cut_off_time.ongoing.unit"
|
|
592
|
-
);
|
|
569
|
+
if (ongoingType === 'after_start') {
|
|
570
|
+
const graceMinutes = normalizeAvailabilityCutOffNumber(raw.ongoing.unit, 'cut_off_time.ongoing.unit');
|
|
593
571
|
return graceMinutes.reason ? invalidAvailabilityCutOffPolicy(graceMinutes.reason) : {
|
|
594
|
-
type:
|
|
595
|
-
mode:
|
|
572
|
+
type: 'ongoing',
|
|
573
|
+
mode: 'after_start',
|
|
596
574
|
graceMinutes: graceMinutes.value,
|
|
597
|
-
...futureDay.futureDay ===
|
|
575
|
+
...(futureDay.futureDay === undefined ? {} : {
|
|
576
|
+
futureDay: futureDay.futureDay
|
|
577
|
+
})
|
|
598
578
|
};
|
|
599
579
|
}
|
|
600
580
|
return invalidAvailabilityCutOffPolicy(`未知的 cut_off_time.ongoing.type: ${ongoingType}`);
|
|
601
581
|
}
|
|
602
|
-
if (type ===
|
|
582
|
+
if (type === 'before_start') {
|
|
603
583
|
return {
|
|
604
|
-
type:
|
|
605
|
-
...futureDay.futureDay ===
|
|
584
|
+
type: 'before_start',
|
|
585
|
+
...(futureDay.futureDay === undefined ? {} : {
|
|
586
|
+
futureDay: futureDay.futureDay
|
|
587
|
+
})
|
|
606
588
|
};
|
|
607
589
|
}
|
|
608
|
-
if (type ===
|
|
609
|
-
const unit = normalizeAvailabilityCutOffNumber(raw.unit,
|
|
610
|
-
if (unit.reason)
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
return invalidAvailabilityCutOffPolicy("cut_off_time.unit_type 必须是 days|hours|minutes");
|
|
590
|
+
if (type === 'advance') {
|
|
591
|
+
const unit = normalizeAvailabilityCutOffNumber(raw.unit, 'cut_off_time.unit');
|
|
592
|
+
if (unit.reason) return invalidAvailabilityCutOffPolicy(unit.reason);
|
|
593
|
+
const unitType = String(raw.unit_type ?? '').trim();
|
|
594
|
+
if (!['days', 'hours', 'minutes'].includes(unitType)) {
|
|
595
|
+
return invalidAvailabilityCutOffPolicy('cut_off_time.unit_type 必须是 days|hours|minutes');
|
|
615
596
|
}
|
|
616
597
|
return {
|
|
617
|
-
type:
|
|
598
|
+
type: 'advance',
|
|
618
599
|
unit: unit.value,
|
|
619
|
-
unitType,
|
|
620
|
-
...futureDay.futureDay ===
|
|
600
|
+
unitType: unitType,
|
|
601
|
+
...(futureDay.futureDay === undefined ? {} : {
|
|
602
|
+
futureDay: futureDay.futureDay
|
|
603
|
+
})
|
|
621
604
|
};
|
|
622
605
|
}
|
|
623
606
|
return invalidAvailabilityCutOffPolicy(`未知的 cut_off_time.type: ${type}`);
|
|
624
607
|
}
|
|
625
|
-
function normalizeResource(raw, fixedOffsetMinutes = (0,
|
|
608
|
+
function normalizeResource(raw, fixedOffsetMinutes = (0, _localClock.captureRuntimeOffsetMinutes)()) {
|
|
626
609
|
const id = raw.resourceId ?? raw.resource_id ?? raw.id;
|
|
627
|
-
if (id ===
|
|
628
|
-
return null;
|
|
610
|
+
if (id === undefined || id === null || id === '') return null;
|
|
629
611
|
const rawWindows = raw.windows || raw.times || [];
|
|
630
612
|
return {
|
|
631
613
|
id,
|
|
@@ -634,123 +616,129 @@ function normalizeResource(raw, fixedOffsetMinutes = (0, import_localClock.captu
|
|
|
634
616
|
code: raw.code,
|
|
635
617
|
resourceType: normalizeResourceType(raw.type || raw.resourceType),
|
|
636
618
|
capacity: Math.max(1, Number(raw.capacity || raw.max_capacity || 1) || 1),
|
|
637
|
-
windows: (Array.isArray(rawWindows) ? rawWindows : []).flatMap(
|
|
619
|
+
windows: (Array.isArray(rawWindows) ? rawWindows : []).flatMap(window => {
|
|
638
620
|
const range = normalizeAvailabilityLocalDateTimeRange({
|
|
639
621
|
startAt: window.startAt || window.start_at || window.start,
|
|
640
622
|
endAt: window.endAt || window.end_at || window.end,
|
|
641
623
|
fixedOffsetMinutes,
|
|
642
624
|
rollOverEnd: true
|
|
643
625
|
});
|
|
644
|
-
if (!range)
|
|
645
|
-
|
|
646
|
-
|
|
626
|
+
if (!range) return [];
|
|
627
|
+
return [{
|
|
628
|
+
startAt: range.startAt,
|
|
629
|
+
endAt: range.endAt,
|
|
630
|
+
source: window.source,
|
|
631
|
+
raw: window
|
|
632
|
+
}];
|
|
647
633
|
}),
|
|
648
634
|
raw
|
|
649
635
|
};
|
|
650
636
|
}
|
|
651
637
|
function normalizeScheduleIds(raw) {
|
|
652
638
|
const ids = raw.schedule ?? raw.schedule_ids ?? raw.scheduleIds ?? [];
|
|
653
|
-
return (Array.isArray(ids) ? ids : [ids]).map(
|
|
639
|
+
return (Array.isArray(ids) ? ids : [ids]).map(id => toNumberId(id)).filter(id => id !== undefined);
|
|
654
640
|
}
|
|
655
641
|
function dateRangeDays(dateRange) {
|
|
656
|
-
if (!
|
|
657
|
-
return [];
|
|
642
|
+
if (!dateRange?.startDate || !dateRange?.endDate) return [];
|
|
658
643
|
const dates = [];
|
|
659
|
-
const startScalar = (0,
|
|
660
|
-
const endScalar = (0,
|
|
661
|
-
if (endScalar < startScalar)
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
dates.push((0, import_localClock.formatLocalDate)(cursor));
|
|
644
|
+
const startScalar = (0, _localClock.localDateToScalarStart)((0, _localClock.parseLocalDate)(dateRange.startDate));
|
|
645
|
+
const endScalar = (0, _localClock.localDateToScalarStart)((0, _localClock.parseLocalDate)(dateRange.endDate));
|
|
646
|
+
if (endScalar < startScalar) return dates;
|
|
647
|
+
for (let cursor = startScalar; cursor <= endScalar; cursor = (0, _localClock.addLocalCalendarDays)(cursor, 1)) {
|
|
648
|
+
dates.push((0, _localClock.formatLocalDate)(cursor));
|
|
665
649
|
}
|
|
666
650
|
return dates;
|
|
667
651
|
}
|
|
668
|
-
function normalizeResourceOccupancy(rawEvent, resourceId, source =
|
|
669
|
-
var _a, _b;
|
|
652
|
+
function normalizeResourceOccupancy(rawEvent, resourceId, source = 'remote', fixedOffsetMinutes = (0, _localClock.captureRuntimeOffsetMinutes)()) {
|
|
670
653
|
const range = normalizeAvailabilityLocalDateTimeRange({
|
|
671
654
|
startAt: rawEvent.start_at ?? rawEvent.startAt ?? rawEvent.start_time ?? rawEvent.startTime,
|
|
672
655
|
endAt: rawEvent.end_at ?? rawEvent.endAt ?? rawEvent.end_time ?? rawEvent.endTime,
|
|
673
656
|
fixedOffsetMinutes,
|
|
674
657
|
rollOverEnd: false
|
|
675
658
|
});
|
|
676
|
-
if (!range)
|
|
677
|
-
return null;
|
|
659
|
+
if (!range) return null;
|
|
678
660
|
return {
|
|
679
|
-
id: rawEvent.id == null ?
|
|
661
|
+
id: rawEvent.id == null ? undefined : String(rawEvent.id),
|
|
680
662
|
resourceId,
|
|
681
663
|
startAt: range.startAt,
|
|
682
664
|
endAt: range.endAt,
|
|
683
665
|
pax: Number(rawEvent.pax ?? rawEvent.number ?? rawEvent.capacity ?? 1) || 1,
|
|
684
|
-
source: rawEvent.source ===
|
|
666
|
+
source: rawEvent.source === 'cart' || rawEvent.source === 'selection' ? rawEvent.source : source,
|
|
685
667
|
scheduleEventId: toNumberId(rawEvent.schedule_event_id ?? rawEvent.scheduleEventId),
|
|
686
668
|
serverEventId: toNumberId(rawEvent.schedule_event_id ?? rawEvent.server_event_id ?? rawEvent.id),
|
|
687
|
-
bookingUid: rawEvent.booking_uid ?? rawEvent.bookingUid ??
|
|
688
|
-
productUid: rawEvent.product_uid ?? rawEvent.productUid ??
|
|
669
|
+
bookingUid: rawEvent.booking_uid ?? rawEvent.bookingUid ?? rawEvent.metadata?.unique_identification_number,
|
|
670
|
+
productUid: rawEvent.product_uid ?? rawEvent.productUid ?? rawEvent.metadata?.product_uid,
|
|
689
671
|
raw: rawEvent
|
|
690
672
|
};
|
|
691
673
|
}
|
|
692
674
|
function buildResourceWindowFallbackTimes(params) {
|
|
693
|
-
const {
|
|
694
|
-
|
|
675
|
+
const {
|
|
676
|
+
raw,
|
|
677
|
+
dates,
|
|
678
|
+
scheduleIds
|
|
679
|
+
} = params;
|
|
680
|
+
const fixedOffsetMinutes = params.fixedOffsetMinutes ?? (0, _localClock.captureRuntimeOffsetMinutes)();
|
|
695
681
|
const boundary = normalizeAvailabilityLocalDateTimeRange({
|
|
696
682
|
startAt: raw.start_time || raw.start_at || raw.startTime,
|
|
697
683
|
endAt: raw.end_time || raw.end_at || raw.endTime,
|
|
698
684
|
fixedOffsetMinutes,
|
|
699
685
|
rollOverEnd: true
|
|
700
686
|
});
|
|
701
|
-
if (!boundary)
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
const
|
|
705
|
-
const dayEnd = (0, import_localClock.localDateTimeToScalar)((0, import_localClock.parseLocalDateTime)(`${date}T23:59:59`));
|
|
687
|
+
if (!boundary) return [];
|
|
688
|
+
return dates.flatMap(date => {
|
|
689
|
+
const dayStart = (0, _localClock.localDateTimeToScalar)((0, _localClock.parseLocalDateTime)(`${date}T00:00:00`));
|
|
690
|
+
const dayEnd = (0, _localClock.localDateTimeToScalar)((0, _localClock.parseLocalDateTime)(`${date}T23:59:59`));
|
|
706
691
|
const start = Math.max(boundary.startScalar, dayStart);
|
|
707
692
|
const end = Math.min(boundary.endScalar, dayEnd);
|
|
708
|
-
if (end <= start)
|
|
709
|
-
return [];
|
|
693
|
+
if (end <= start) return [];
|
|
710
694
|
return [{
|
|
711
|
-
start_at: (0,
|
|
712
|
-
end_at: (0,
|
|
695
|
+
start_at: (0, _localClock.formatLocalDateTime)(start, fixedOffsetMinutes),
|
|
696
|
+
end_at: (0, _localClock.formatLocalDateTime)(end, fixedOffsetMinutes),
|
|
713
697
|
schedule_ids: scheduleIds,
|
|
714
|
-
source:
|
|
698
|
+
source: 'resource_window_fallback'
|
|
715
699
|
}];
|
|
716
700
|
});
|
|
717
701
|
}
|
|
702
|
+
|
|
703
|
+
/**
|
|
704
|
+
* Materializes resource work windows locally from v2's schedule ids, then keeps its flat
|
|
705
|
+
* event_list as resource occupancy records. This deliberately avoids the older API's times -> event_list
|
|
706
|
+
* response nesting and lets one resource response serve any requested date range.
|
|
707
|
+
*/
|
|
718
708
|
function buildAvailabilityResourcesFromV2(params) {
|
|
719
|
-
const scheduleMap = new Map(
|
|
720
|
-
(params.scheduleList || []).map((schedule) => [String(schedule.id), schedule])
|
|
721
|
-
);
|
|
709
|
+
const scheduleMap = new Map((params.scheduleList || []).map(schedule => [String(schedule.id), schedule]));
|
|
722
710
|
const dates = dateRangeDays(params.dateRange);
|
|
723
711
|
const resourceOccupancies = [];
|
|
724
|
-
const fixedOffsetMinutes = params.fixedOffsetMinutes ?? (0,
|
|
725
|
-
const resources = (params.rawResources || []).map(
|
|
712
|
+
const fixedOffsetMinutes = params.fixedOffsetMinutes ?? (0, _localClock.captureRuntimeOffsetMinutes)();
|
|
713
|
+
const resources = (params.rawResources || []).map(raw => {
|
|
726
714
|
const resource = normalizeResource(raw, fixedOffsetMinutes);
|
|
727
|
-
if (!resource)
|
|
728
|
-
return null;
|
|
715
|
+
if (!resource) return null;
|
|
729
716
|
const explicitWindows = [...resource.windows];
|
|
730
717
|
const scheduleIds = normalizeScheduleIds(raw);
|
|
731
|
-
const schedules = scheduleIds.map(
|
|
732
|
-
const matchedScheduleIds = new Set(schedules.map(
|
|
733
|
-
const missingScheduleIds = scheduleIds.filter(
|
|
718
|
+
const schedules = scheduleIds.map(id => scheduleMap.get(String(id))).filter(Boolean);
|
|
719
|
+
const matchedScheduleIds = new Set(schedules.map(schedule => String(schedule.id)));
|
|
720
|
+
const missingScheduleIds = scheduleIds.filter(id => !matchedScheduleIds.has(String(id)));
|
|
734
721
|
if (missingScheduleIds.length > 0) {
|
|
735
722
|
resource.raw = {
|
|
736
|
-
...resource.raw || raw,
|
|
723
|
+
...(resource.raw || raw),
|
|
737
724
|
_availabilityMissingScheduleIds: missingScheduleIds
|
|
738
725
|
};
|
|
739
726
|
}
|
|
740
|
-
const materializedWindows = dates.flatMap(
|
|
727
|
+
const materializedWindows = dates.flatMap(date => schedules.flatMap(schedule => (0, _getDateIsInSchedule.getScheduleStartEndTimePoints)(date, [schedule]).flatMap(slot => {
|
|
741
728
|
const range = normalizeAvailabilityLocalDateTimeRange({
|
|
742
729
|
startAt: slot.start_at,
|
|
743
730
|
endAt: slot.end_at,
|
|
744
731
|
fixedOffsetMinutes,
|
|
745
732
|
rollOverEnd: true
|
|
746
733
|
});
|
|
747
|
-
if (!range)
|
|
748
|
-
return [];
|
|
734
|
+
if (!range) return [];
|
|
749
735
|
return [{
|
|
750
736
|
startAt: range.startAt,
|
|
751
737
|
endAt: range.endAt,
|
|
752
738
|
source: `resource_schedule:${schedule.id}`,
|
|
753
|
-
raw: {
|
|
739
|
+
raw: {
|
|
740
|
+
scheduleId: schedule.id
|
|
741
|
+
}
|
|
754
742
|
}];
|
|
755
743
|
})));
|
|
756
744
|
resource.windows = materializedWindows.length > 0 ? materializedWindows : explicitWindows;
|
|
@@ -760,7 +748,7 @@ function buildAvailabilityResourcesFromV2(params) {
|
|
|
760
748
|
dates,
|
|
761
749
|
scheduleIds,
|
|
762
750
|
fixedOffsetMinutes
|
|
763
|
-
}).map(
|
|
751
|
+
}).map(window => ({
|
|
764
752
|
startAt: window.start_at,
|
|
765
753
|
endAt: window.end_at,
|
|
766
754
|
source: window.source,
|
|
@@ -768,40 +756,35 @@ function buildAvailabilityResourcesFromV2(params) {
|
|
|
768
756
|
}));
|
|
769
757
|
}
|
|
770
758
|
const eventList = Array.isArray(raw.event_list) ? raw.event_list : [];
|
|
771
|
-
eventList.forEach(
|
|
772
|
-
const occupancy = normalizeResourceOccupancy(event, resource.id,
|
|
773
|
-
if (occupancy)
|
|
774
|
-
resourceOccupancies.push(occupancy);
|
|
759
|
+
eventList.forEach(event => {
|
|
760
|
+
const occupancy = normalizeResourceOccupancy(event, resource.id, 'remote', fixedOffsetMinutes);
|
|
761
|
+
if (occupancy) resourceOccupancies.push(occupancy);
|
|
775
762
|
});
|
|
776
763
|
return resource;
|
|
777
|
-
}).filter(
|
|
764
|
+
}).filter(resource => resource !== null);
|
|
778
765
|
return {
|
|
779
766
|
resources,
|
|
780
767
|
resourceOccupancies
|
|
781
768
|
};
|
|
782
769
|
}
|
|
770
|
+
|
|
771
|
+
/** Combines URL-locked resources with every resource referenced by the selected products. */
|
|
783
772
|
function collectAvailabilityResourceIds(products, explicitResourceIds = []) {
|
|
784
|
-
const ids =
|
|
785
|
-
const add =
|
|
786
|
-
if (value ===
|
|
787
|
-
return;
|
|
773
|
+
const ids = new Map();
|
|
774
|
+
const add = value => {
|
|
775
|
+
if (value === undefined || value === null || value === '') return;
|
|
788
776
|
ids.set(String(value), value);
|
|
789
777
|
};
|
|
790
778
|
explicitResourceIds.forEach(add);
|
|
791
|
-
products.forEach(
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
var _a2;
|
|
795
|
-
(_a2 = requirement.resourceIds) == null ? void 0 : _a2.forEach(add);
|
|
779
|
+
products.forEach(product => {
|
|
780
|
+
product.requirementGroups?.forEach(requirement => {
|
|
781
|
+
requirement.resourceIds?.forEach(add);
|
|
796
782
|
});
|
|
797
783
|
});
|
|
798
784
|
return Array.from(ids.values());
|
|
799
785
|
}
|
|
800
786
|
function getProductLineUid(product) {
|
|
801
|
-
|
|
802
|
-
return String(
|
|
803
|
-
((_a = product == null ? void 0 : product.metadata) == null ? void 0 : _a.unique_identification_number) ?? (product == null ? void 0 : product.unique_identification_number) ?? ""
|
|
804
|
-
);
|
|
787
|
+
return String(product?.metadata?.unique_identification_number ?? product?.unique_identification_number ?? '');
|
|
805
788
|
}
|
|
806
789
|
function buildAvailabilityLineIdentity(product) {
|
|
807
790
|
return {
|
|
@@ -821,25 +804,22 @@ function buildBookingFromAvailabilityAssignment(params) {
|
|
|
821
804
|
});
|
|
822
805
|
}
|
|
823
806
|
function buildBookingFromAvailabilityAssignments(params) {
|
|
824
|
-
const normalizedAssignments = (params.assignments || []).map(
|
|
807
|
+
const normalizedAssignments = (params.assignments || []).map(assignment => {
|
|
825
808
|
const resourceId = assignment.resourceId ?? assignment.resource_id;
|
|
826
|
-
const matchedGroup = (params.product.requirementGroups || []).find(
|
|
827
|
-
var _a;
|
|
828
|
-
return (_a = group.resourceIds) == null ? void 0 : _a.some((id) => sameAvailabilityId(id, resourceId));
|
|
829
|
-
});
|
|
809
|
+
const matchedGroup = (params.product.requirementGroups || []).find(group => group.resourceIds?.some(id => sameAvailabilityId(id, resourceId)));
|
|
830
810
|
return {
|
|
831
811
|
productId: assignment.productId ?? assignment.product_id,
|
|
832
812
|
resourceId,
|
|
833
|
-
formId: assignment.formId ?? assignment.form_id ??
|
|
813
|
+
formId: assignment.formId ?? assignment.form_id ?? matchedGroup?.formId,
|
|
834
814
|
startAt: assignment.startAt ?? assignment.start_at,
|
|
835
815
|
endAt: assignment.endAt ?? assignment.end_at,
|
|
836
|
-
capacityRequired: assignment.capacityRequired ?? assignment.capacity_required ??
|
|
816
|
+
capacityRequired: assignment.capacityRequired ?? assignment.capacity_required ?? matchedGroup?.capacityRequired,
|
|
837
817
|
scheduleId: assignment.scheduleId ?? assignment.schedule_id,
|
|
838
818
|
timeSlotId: assignment.timeSlotId ?? assignment.time_slot_id
|
|
839
819
|
};
|
|
840
820
|
});
|
|
841
821
|
const first = normalizedAssignments[0];
|
|
842
|
-
const fixedOffsetMinutes = params.fixedOffsetMinutes ?? (0,
|
|
822
|
+
const fixedOffsetMinutes = params.fixedOffsetMinutes ?? (0, _localClock.captureRuntimeOffsetMinutes)();
|
|
843
823
|
const range = normalizeAvailabilityLocalDateTimeRange({
|
|
844
824
|
startAt: first.startAt,
|
|
845
825
|
endAt: first.endAt,
|
|
@@ -847,24 +827,24 @@ function buildBookingFromAvailabilityAssignments(params) {
|
|
|
847
827
|
rollOverEnd: false
|
|
848
828
|
});
|
|
849
829
|
if (!range) {
|
|
850
|
-
throw new
|
|
830
|
+
throw new _ResourcePlanner.AvailabilityError('INVALID_TARGET_FILTER', 'assignment 时间范围非法', {
|
|
851
831
|
assignments: params.assignments
|
|
852
832
|
});
|
|
853
833
|
}
|
|
854
834
|
const candidate = {
|
|
855
835
|
key: `${String(first.productId)}:${range.startAt}:${range.endAt}`,
|
|
856
836
|
productId: first.productId,
|
|
857
|
-
source:
|
|
837
|
+
source: 'duration_window',
|
|
858
838
|
startAt: range.startAt,
|
|
859
839
|
endAt: range.endAt,
|
|
860
840
|
bookingStartAt: range.startAt,
|
|
861
841
|
bookingEndAt: range.endAt,
|
|
862
842
|
date: range.startAt.slice(0, 10),
|
|
863
|
-
startTime: (0,
|
|
864
|
-
endTime: (0,
|
|
865
|
-
timezone: String(params.timezone || Intl.DateTimeFormat().resolvedOptions().timeZone ||
|
|
843
|
+
startTime: (0, _localClock.formatLocalTime)(range.startAt, false),
|
|
844
|
+
endTime: (0, _localClock.formatLocalTime)(range.endAt, false),
|
|
845
|
+
timezone: String(params.timezone || Intl.DateTimeFormat().resolvedOptions().timeZone || 'runtime'),
|
|
866
846
|
scheduleRefs: [],
|
|
867
|
-
primaryScheduleRef:
|
|
847
|
+
primaryScheduleRef: undefined
|
|
868
848
|
};
|
|
869
849
|
return buildBookingFromAvailabilitySelection({
|
|
870
850
|
assignments: normalizedAssignments,
|
|
@@ -873,18 +853,13 @@ function buildBookingFromAvailabilityAssignments(params) {
|
|
|
873
853
|
});
|
|
874
854
|
}
|
|
875
855
|
function getBookingUid(booking) {
|
|
876
|
-
|
|
877
|
-
return String(
|
|
878
|
-
((_a = booking == null ? void 0 : booking.metadata) == null ? void 0 : _a.unique_identification_number) ?? (booking == null ? void 0 : booking.booking_uid) ?? ""
|
|
879
|
-
);
|
|
856
|
+
return String(booking?.metadata?.unique_identification_number ?? booking?.booking_uid ?? '');
|
|
880
857
|
}
|
|
881
858
|
function stableValue(value) {
|
|
882
|
-
if (Array.isArray(value))
|
|
883
|
-
|
|
884
|
-
if (!value || typeof value !== "object")
|
|
885
|
-
return value;
|
|
859
|
+
if (Array.isArray(value)) return value.map(stableValue);
|
|
860
|
+
if (!value || typeof value !== 'object') return value;
|
|
886
861
|
return Object.keys(value).sort().reduce((result, key) => {
|
|
887
|
-
if (value[key] !==
|
|
862
|
+
if (value[key] !== undefined && typeof value[key] !== 'function') {
|
|
888
863
|
result[key] = stableValue(value[key]);
|
|
889
864
|
}
|
|
890
865
|
return result;
|
|
@@ -899,21 +874,17 @@ function hashText(value) {
|
|
|
899
874
|
return (hash >>> 0).toString(36);
|
|
900
875
|
}
|
|
901
876
|
function resolveOrderRevision(tempOrder) {
|
|
902
|
-
if (!tempOrder)
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
productBundle: product.product_bundle
|
|
914
|
-
};
|
|
915
|
-
}).sort((left, right) => left.uid.localeCompare(right.uid));
|
|
916
|
-
const bookings = (Array.isArray(tempOrder.bookings) ? tempOrder.bookings : []).map((booking) => ({
|
|
877
|
+
if (!tempOrder) return 'order:none';
|
|
878
|
+
const products = (Array.isArray(tempOrder.products) ? tempOrder.products : []).map(product => ({
|
|
879
|
+
uid: getProductLineUid(product),
|
|
880
|
+
productId: product.product_id ?? product.id,
|
|
881
|
+
variantId: product.product_variant_id ?? 0,
|
|
882
|
+
num: product.num ?? product.quantity ?? 1,
|
|
883
|
+
bookingUid: product.booking_uid ?? product.metadata?.booking_uid,
|
|
884
|
+
productSku: product.product_sku,
|
|
885
|
+
productBundle: product.product_bundle
|
|
886
|
+
})).sort((left, right) => left.uid.localeCompare(right.uid));
|
|
887
|
+
const bookings = (Array.isArray(tempOrder.bookings) ? tempOrder.bookings : []).map(booking => ({
|
|
917
888
|
uid: getBookingUid(booking),
|
|
918
889
|
scheduleEventId: booking.schedule_event_id,
|
|
919
890
|
productUid: booking.product_uid,
|
|
@@ -924,10 +895,12 @@ function resolveOrderRevision(tempOrder) {
|
|
|
924
895
|
endTime: booking.end_time,
|
|
925
896
|
resources: booking.resources
|
|
926
897
|
})).sort((left, right) => left.uid.localeCompare(right.uid));
|
|
927
|
-
return `order:${hashText(JSON.stringify(stableValue({
|
|
898
|
+
return `order:${hashText(JSON.stringify(stableValue({
|
|
899
|
+
products,
|
|
900
|
+
bookings
|
|
901
|
+
})))}`;
|
|
928
902
|
}
|
|
929
903
|
function buildBookingFromAvailabilitySelection(params) {
|
|
930
|
-
var _a, _b, _c, _d;
|
|
931
904
|
const {
|
|
932
905
|
assignments,
|
|
933
906
|
candidate,
|
|
@@ -939,144 +912,147 @@ function buildBookingFromAvailabilitySelection(params) {
|
|
|
939
912
|
} = params;
|
|
940
913
|
const bookingStartAt = candidate.bookingStartAt || candidate.startAt;
|
|
941
914
|
const bookingEndAt = candidate.bookingEndAt || candidate.endAt;
|
|
942
|
-
const startScalar = (0,
|
|
943
|
-
const endScalar = (0,
|
|
944
|
-
const scheduleId =
|
|
945
|
-
const timeSlotId =
|
|
946
|
-
if (product.kind ===
|
|
947
|
-
throw new
|
|
915
|
+
const startScalar = (0, _localClock.localDateTimeToScalar)((0, _localClock.parseLocalDateTime)(bookingStartAt));
|
|
916
|
+
const endScalar = (0, _localClock.localDateTimeToScalar)((0, _localClock.parseLocalDateTime)(bookingEndAt));
|
|
917
|
+
const scheduleId = candidate.primaryScheduleRef?.scheduleId ?? assignments.find(assignment => assignment.scheduleId != null)?.scheduleId;
|
|
918
|
+
const timeSlotId = candidate.primaryScheduleRef?.timeSlotId ?? assignments.find(assignment => assignment.timeSlotId != null)?.timeSlotId;
|
|
919
|
+
if (product.kind === 'session' && scheduleId == null) {
|
|
920
|
+
throw new _ResourcePlanner.AvailabilityError('REVALIDATION_FAILED', 'session 候选缺少真实 schedule identity', {
|
|
948
921
|
productId: product.id,
|
|
949
922
|
candidateKey: candidate.key,
|
|
950
923
|
scheduleRefs: candidate.scheduleRefs
|
|
951
924
|
});
|
|
952
925
|
}
|
|
953
|
-
const normalizedHolder = holder && typeof holder ===
|
|
954
|
-
const normalizedHolderIds = Array.isArray(holderIds) ? (0,
|
|
926
|
+
const normalizedHolder = holder && typeof holder === 'object' ? (0, _lodashEs.cloneDeep)(holder) : undefined;
|
|
927
|
+
const normalizedHolderIds = Array.isArray(holderIds) ? (0, _lodashEs.cloneDeep)(holderIds) : holderIds;
|
|
955
928
|
return {
|
|
956
929
|
start_date: bookingStartAt.slice(0, 10),
|
|
957
930
|
start_time: bookingStartAt.slice(11, 16),
|
|
958
931
|
end_date: bookingEndAt.slice(0, 10),
|
|
959
932
|
end_time: bookingEndAt.slice(11, 16),
|
|
960
|
-
duration: Math.max(1, Math.trunc((endScalar - startScalar) / (60 *
|
|
961
|
-
like_status:
|
|
933
|
+
duration: Math.max(1, Math.trunc((endScalar - startScalar) / (60 * 1000))),
|
|
934
|
+
like_status: 'common',
|
|
962
935
|
schedule_id: scheduleId ?? 0,
|
|
963
|
-
...normalizedHolder ? {
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
936
|
+
...(normalizedHolder ? {
|
|
937
|
+
holder: normalizedHolder
|
|
938
|
+
} : {}),
|
|
939
|
+
resources: assignments.map(assignment => {
|
|
940
|
+
// requirementSelections 的提交协议只携带资源 ID;购物车展示则需要名称。
|
|
941
|
+
// commit 此时仍持有本次不可变 projection,因此直接按 ID 取回名称并
|
|
942
|
+
// 固化到 booking,避免 UI 再依赖 legacy BookingContext resourcesOriginMap。
|
|
943
|
+
const matchedResource = resources.find(resource => sameAvailabilityId(resource.id, assignment.resourceId));
|
|
944
|
+
const resourceName = String(matchedResource?.name || '').trim();
|
|
967
945
|
return {
|
|
968
946
|
id: assignment.resourceId,
|
|
969
947
|
resource_id: assignment.resourceId,
|
|
970
948
|
form_id: assignment.formId,
|
|
971
|
-
relation_type:
|
|
949
|
+
relation_type: 'form',
|
|
972
950
|
relation_id: assignment.resourceId,
|
|
973
|
-
like_status:
|
|
951
|
+
like_status: 'common',
|
|
974
952
|
capacity: assignment.capacityRequired || 1,
|
|
975
|
-
...resourceName ? {
|
|
953
|
+
...(resourceName ? {
|
|
954
|
+
metadata: {
|
|
955
|
+
resource_name: resourceName
|
|
956
|
+
}
|
|
957
|
+
} : {})
|
|
976
958
|
};
|
|
977
959
|
}),
|
|
978
960
|
metadata: {
|
|
979
|
-
...bookingUid ? {
|
|
961
|
+
...(bookingUid ? {
|
|
962
|
+
unique_identification_number: bookingUid
|
|
963
|
+
} : {}),
|
|
980
964
|
unified_booking_sales: true,
|
|
981
965
|
availability_candidate_key: candidate.key,
|
|
982
966
|
availability_source: candidate.source,
|
|
983
967
|
availability_slice_start_at: candidate.startAt,
|
|
984
968
|
availability_slice_end_at: candidate.endAt,
|
|
985
969
|
availability_schedule_refs: candidate.scheduleRefs,
|
|
986
|
-
...timeSlotId == null ? {} : {
|
|
970
|
+
...(timeSlotId == null ? {} : {
|
|
971
|
+
time_slot_id: timeSlotId
|
|
972
|
+
}),
|
|
987
973
|
product_kind: product.kind,
|
|
988
|
-
...normalizedHolderIds ===
|
|
989
|
-
|
|
974
|
+
...(normalizedHolderIds === undefined ? {} : {
|
|
975
|
+
holder_id: normalizedHolderIds
|
|
976
|
+
}),
|
|
977
|
+
...(normalizedHolder ? {
|
|
978
|
+
holder: (0, _lodashEs.cloneDeep)(normalizedHolder)
|
|
979
|
+
} : {})
|
|
990
980
|
}
|
|
991
981
|
};
|
|
992
982
|
}
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
this.availabilityRemoteResourceQueryInFlight = /* @__PURE__ */ new Map();
|
|
1016
|
-
this.availabilityRemoteResourceQuerySequence = 0;
|
|
1017
|
-
this.availabilityRemoteResourceEpoch = 0;
|
|
1018
|
-
this.availabilityCommitLocks = /* @__PURE__ */ new Set();
|
|
1019
|
-
}
|
|
983
|
+
class UnifiedBookingSalesImpl extends _BookingTicket.BookingTicket {
|
|
984
|
+
defaultName = 'unifiedBookingSales';
|
|
985
|
+
defaultVersion = '1.0.0';
|
|
986
|
+
store = {};
|
|
987
|
+
unifiedProductCatalog = [];
|
|
988
|
+
openDataTarget = null;
|
|
989
|
+
loadOpenDataInFlight = null;
|
|
990
|
+
loadOpenDataInFlightTarget = null;
|
|
991
|
+
availabilityRuntimeOffsetMinutes = null;
|
|
992
|
+
availabilityScheduleCatalog = null;
|
|
993
|
+
availabilityProjectionSequence = 0;
|
|
994
|
+
availabilityContextSequence = 0;
|
|
995
|
+
availabilityLifecycleGeneration = 0;
|
|
996
|
+
availabilityContextRegistry = new Map();
|
|
997
|
+
availabilityProjectionPool = new Map();
|
|
998
|
+
availabilityPrepareInFlight = new Map();
|
|
999
|
+
availabilityProjectionRefreshInFlight = new Map();
|
|
1000
|
+
availabilityRemoteOccupancyCache = new _remoteOccupancyCache.AvailabilityRemoteOccupancyCache(MAX_REMOTE_OCCUPANCY_RESOURCE_DATE_CELLS);
|
|
1001
|
+
availabilityRemoteResourceQueryInFlight = new Map();
|
|
1002
|
+
availabilityRemoteResourceQuerySequence = 0;
|
|
1003
|
+
availabilityRemoteResourceEpoch = 0;
|
|
1004
|
+
availabilityCommitLocks = new Set();
|
|
1020
1005
|
async initialize(core, options = {}) {
|
|
1021
|
-
var _a, _b, _c, _d;
|
|
1022
1006
|
this.availabilityScheduleCatalog = null;
|
|
1023
1007
|
this.openDataTarget = null;
|
|
1024
|
-
this.availabilityRuntimeOffsetMinutes = (0,
|
|
1008
|
+
this.availabilityRuntimeOffsetMinutes = (0, _localClock.captureRuntimeOffsetMinutes)();
|
|
1025
1009
|
this.clearAvailabilityRemoteResourceState();
|
|
1026
1010
|
await super.initialize(core, options);
|
|
1027
|
-
const openDataBusinessCode = String(
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
const openDataChannel = String(((_c = this.otherParams) == null ? void 0 : _c.channel) ?? "").trim();
|
|
1031
|
-
if (((_d = this.otherParams) == null ? void 0 : _d.openData) && openDataBusinessCode && openDataChannel) {
|
|
1011
|
+
const openDataBusinessCode = String(this.otherParams?.businessCode ?? this.otherParams?.business_code ?? '').trim();
|
|
1012
|
+
const openDataChannel = String(this.otherParams?.channel ?? '').trim();
|
|
1013
|
+
if (this.otherParams?.openData && openDataBusinessCode && openDataChannel) {
|
|
1032
1014
|
this.openDataTarget = `${openDataBusinessCode}+${openDataChannel}`;
|
|
1033
1015
|
}
|
|
1034
1016
|
this.captureAvailabilityScheduleCatalog();
|
|
1035
1017
|
}
|
|
1036
1018
|
getSubmitOrderSalesChannel() {
|
|
1037
|
-
|
|
1038
|
-
const channel = String(((_a = this.otherParams) == null ? void 0 : _a.channel) ?? "").trim();
|
|
1019
|
+
const channel = String(this.otherParams?.channel ?? '').trim();
|
|
1039
1020
|
return channel || super.getSubmitOrderSalesChannel();
|
|
1040
1021
|
}
|
|
1041
1022
|
createAvailabilityFacadeError(code, message, details) {
|
|
1042
|
-
return new
|
|
1023
|
+
return new _ResourcePlanner.AvailabilityError(code, message, details);
|
|
1043
1024
|
}
|
|
1044
1025
|
getAvailabilityScopeDescriptor(otherParams = this.otherParams || {}) {
|
|
1045
1026
|
return {
|
|
1046
|
-
cacheId: this.cacheId ||
|
|
1047
|
-
businessCode: String(otherParams.businessCode ?? otherParams.business_code ??
|
|
1048
|
-
channel: String(otherParams.channel ??
|
|
1049
|
-
platform: String(otherParams.platform ??
|
|
1050
|
-
businessTimezone: String(otherParams.businessTimezone ??
|
|
1027
|
+
cacheId: this.cacheId || '',
|
|
1028
|
+
businessCode: String(otherParams.businessCode ?? otherParams.business_code ?? ''),
|
|
1029
|
+
channel: String(otherParams.channel ?? ''),
|
|
1030
|
+
platform: String(otherParams.platform ?? ''),
|
|
1031
|
+
businessTimezone: String(otherParams.businessTimezone ?? '')
|
|
1051
1032
|
};
|
|
1052
1033
|
}
|
|
1053
1034
|
normalizeAvailabilityIdList(values) {
|
|
1054
|
-
if (!Array.isArray(values) || values.length === 0)
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
values.forEach((value) => {
|
|
1035
|
+
if (!Array.isArray(values) || values.length === 0) return undefined;
|
|
1036
|
+
const uniqueMap = new Map();
|
|
1037
|
+
values.forEach(value => {
|
|
1058
1038
|
uniqueMap.set(String(value), value);
|
|
1059
1039
|
});
|
|
1060
1040
|
return Array.from(uniqueMap.entries()).sort(([left], [right]) => left.localeCompare(right)).map(([, value]) => value);
|
|
1061
1041
|
}
|
|
1062
1042
|
normalizeAvailabilityStringList(values) {
|
|
1063
|
-
if (!Array.isArray(values) || values.length === 0)
|
|
1064
|
-
return void 0;
|
|
1043
|
+
if (!Array.isArray(values) || values.length === 0) return undefined;
|
|
1065
1044
|
return Array.from(new Set(values.map(String))).sort((left, right) => left.localeCompare(right));
|
|
1066
1045
|
}
|
|
1067
1046
|
normalizeAvailabilityPrepareDescriptor(params) {
|
|
1068
|
-
var _a;
|
|
1069
1047
|
this.assertAvailabilityPrepareParams(params);
|
|
1070
|
-
const timezone = String(
|
|
1071
|
-
params.timezone || ((_a = this.otherParams) == null ? void 0 : _a.businessTimezone) || Intl.DateTimeFormat().resolvedOptions().timeZone || "Asia/Shanghai"
|
|
1072
|
-
);
|
|
1048
|
+
const timezone = String(params.timezone || this.otherParams?.businessTimezone || Intl.DateTimeFormat().resolvedOptions().timeZone || 'Asia/Shanghai');
|
|
1073
1049
|
assertValidAvailabilityTimezone(timezone);
|
|
1074
1050
|
const dateRange = this.normalizeAvailabilityDateRange(params, timezone);
|
|
1075
1051
|
const slotStepMinutes = params.slotStepMinutes || DEFAULT_AVAILABILITY_SLOT_STEP_MINUTES;
|
|
1076
1052
|
const businessHours = params.businessHours || DEFAULT_AVAILABILITY_BUSINESS_HOURS;
|
|
1077
|
-
const mode = params.mode ||
|
|
1053
|
+
const mode = params.mode || 'appointment';
|
|
1078
1054
|
if (!isValidClockTime(businessHours.startTime) || !isValidClockTime(businessHours.endTime)) {
|
|
1079
|
-
throw new
|
|
1055
|
+
throw new _ResourcePlanner.AvailabilityError('INVALID_TARGET_FILTER', 'businessHours 必须使用 HH:mm', {
|
|
1080
1056
|
businessHours
|
|
1081
1057
|
});
|
|
1082
1058
|
}
|
|
@@ -1088,63 +1064,58 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
1088
1064
|
dateRange,
|
|
1089
1065
|
timezone,
|
|
1090
1066
|
slotStepMinutes,
|
|
1091
|
-
businessHours: {
|
|
1067
|
+
businessHours: {
|
|
1068
|
+
...businessHours
|
|
1069
|
+
},
|
|
1092
1070
|
_availabilityOperatingHours: params._availabilityOperatingHours ? {
|
|
1093
1071
|
source: params._availabilityOperatingHours.source,
|
|
1094
1072
|
scheduleIds: [...params._availabilityOperatingHours.scheduleIds]
|
|
1095
|
-
} :
|
|
1073
|
+
} : undefined,
|
|
1096
1074
|
mode
|
|
1097
1075
|
});
|
|
1098
1076
|
}
|
|
1099
1077
|
async resolveAvailabilityPrepareDefaults(params) {
|
|
1100
|
-
|
|
1101
|
-
const
|
|
1102
|
-
const needsTimeSlice = params.slotStepMinutes === void 0;
|
|
1078
|
+
const needsOperatingHours = params.businessHours === undefined;
|
|
1079
|
+
const needsTimeSlice = params.slotStepMinutes === undefined;
|
|
1103
1080
|
if (!needsOperatingHours && !needsTimeSlice) {
|
|
1104
1081
|
return {
|
|
1105
1082
|
...params,
|
|
1106
1083
|
_availabilityOperatingHours: {
|
|
1107
|
-
source:
|
|
1084
|
+
source: 'prepare_business_hours',
|
|
1108
1085
|
scheduleIds: []
|
|
1109
1086
|
}
|
|
1110
1087
|
};
|
|
1111
1088
|
}
|
|
1112
|
-
const businessCode = String(
|
|
1113
|
-
|
|
1114
|
-
).trim();
|
|
1115
|
-
const channel = String(((_c = this.otherParams) == null ? void 0 : _c.channel) ?? "").trim();
|
|
1089
|
+
const businessCode = String(this.otherParams?.businessCode ?? this.otherParams?.business_code ?? '').trim();
|
|
1090
|
+
const channel = String(this.otherParams?.channel ?? '').trim();
|
|
1116
1091
|
const currentTarget = businessCode && channel ? `${businessCode}+${channel}` : null;
|
|
1117
|
-
let openData = !currentTarget || this.openDataTarget === currentTarget ?
|
|
1092
|
+
let openData = !currentTarget || this.openDataTarget === currentTarget ? this.otherParams?.openData : null;
|
|
1118
1093
|
if (!openData && this.store.openData) {
|
|
1119
1094
|
try {
|
|
1120
1095
|
openData = await this.getOpenData();
|
|
1121
1096
|
} catch (error) {
|
|
1122
1097
|
if (needsOperatingHours) {
|
|
1123
|
-
throw this.createAvailabilityFacadeError(
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
channel,
|
|
1129
|
-
cause: error instanceof Error ? error.message : String(error)
|
|
1130
|
-
}
|
|
1131
|
-
);
|
|
1098
|
+
throw this.createAvailabilityFacadeError('OPERATING_HOURS_RESOLVE_FAILED', '无法读取店铺营业时间配置,请重试', {
|
|
1099
|
+
businessCode,
|
|
1100
|
+
channel,
|
|
1101
|
+
cause: error instanceof Error ? error.message : String(error)
|
|
1102
|
+
});
|
|
1132
1103
|
}
|
|
1133
|
-
console.warn(
|
|
1104
|
+
console.warn('[UnifiedBookingSales] OpenData 时间切片配置加载失败,回退为默认值', error);
|
|
1134
1105
|
}
|
|
1135
1106
|
}
|
|
1136
|
-
const operatingScheduleIds = this.normalizeAvailabilityIdList(
|
|
1137
|
-
|
|
1138
|
-
) || [];
|
|
1139
|
-
const configuredTimeSlice = needsTimeSlice ? normalizePositiveInteger(openData == null ? void 0 : openData["reservation.time_slice"]) : void 0;
|
|
1107
|
+
const operatingScheduleIds = this.normalizeAvailabilityIdList(Array.isArray(openData?.['availability.operating_hours']) ? openData?.['availability.operating_hours'] : undefined) || [];
|
|
1108
|
+
const configuredTimeSlice = needsTimeSlice ? normalizePositiveInteger(openData?.['reservation.time_slice']) : undefined;
|
|
1140
1109
|
return {
|
|
1141
1110
|
...params,
|
|
1142
|
-
...configuredTimeSlice ? {
|
|
1111
|
+
...(configuredTimeSlice ? {
|
|
1112
|
+
slotStepMinutes: configuredTimeSlice
|
|
1113
|
+
} : {}),
|
|
1143
1114
|
_availabilityOperatingHours: needsOperatingHours ? {
|
|
1144
|
-
source: operatingScheduleIds.length > 0 ?
|
|
1115
|
+
source: operatingScheduleIds.length > 0 ? 'open_data_schedule' : 'default_business_hours',
|
|
1145
1116
|
scheduleIds: operatingScheduleIds
|
|
1146
1117
|
} : {
|
|
1147
|
-
source:
|
|
1118
|
+
source: 'prepare_business_hours',
|
|
1148
1119
|
scheduleIds: []
|
|
1149
1120
|
}
|
|
1150
1121
|
};
|
|
@@ -1176,24 +1147,27 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
1176
1147
|
resourceIds: this.normalizeAvailabilityIdList(params.resourceIds) || []
|
|
1177
1148
|
}));
|
|
1178
1149
|
}
|
|
1179
|
-
async setOtherParams(params, {
|
|
1180
|
-
|
|
1150
|
+
async setOtherParams(params, {
|
|
1151
|
+
cover = false
|
|
1152
|
+
} = {}) {
|
|
1181
1153
|
const previousScope = this.getAvailabilityScopeDescriptor(this.otherParams || {});
|
|
1182
|
-
const explicitlySetsOpenData = Object.prototype.hasOwnProperty.call(params,
|
|
1183
|
-
await super.setOtherParams(params, {
|
|
1154
|
+
const explicitlySetsOpenData = Object.prototype.hasOwnProperty.call(params, 'openData');
|
|
1155
|
+
await super.setOtherParams(params, {
|
|
1156
|
+
cover
|
|
1157
|
+
});
|
|
1184
1158
|
const nextScope = this.getAvailabilityScopeDescriptor(this.otherParams || {});
|
|
1185
1159
|
if (explicitlySetsOpenData) {
|
|
1186
|
-
this.openDataTarget =
|
|
1160
|
+
this.openDataTarget = this.otherParams?.openData && nextScope.businessCode && nextScope.channel ? `${nextScope.businessCode}+${nextScope.channel}` : null;
|
|
1187
1161
|
}
|
|
1188
1162
|
const shouldResetAvailabilityState = previousScope.cacheId !== nextScope.cacheId || previousScope.businessCode !== nextScope.businessCode || previousScope.channel !== nextScope.channel || previousScope.platform !== nextScope.platform || previousScope.businessTimezone !== nextScope.businessTimezone;
|
|
1189
1163
|
if (shouldResetAvailabilityState) {
|
|
1190
|
-
await this.resetAvailabilityProjectionState(
|
|
1164
|
+
await this.resetAvailabilityProjectionState('set_other_params_reset');
|
|
1191
1165
|
}
|
|
1192
1166
|
}
|
|
1193
1167
|
async destroy() {
|
|
1194
1168
|
this.availabilityScheduleCatalog = null;
|
|
1195
1169
|
this.availabilityRuntimeOffsetMinutes = null;
|
|
1196
|
-
await this.resetAvailabilityProjectionState(
|
|
1170
|
+
await this.resetAvailabilityProjectionState('destroy');
|
|
1197
1171
|
await super.destroy();
|
|
1198
1172
|
}
|
|
1199
1173
|
createAvailabilityContextId() {
|
|
@@ -1205,35 +1179,45 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
1205
1179
|
}
|
|
1206
1180
|
getAvailabilityRuntimeOffsetMinutes() {
|
|
1207
1181
|
if (this.availabilityRuntimeOffsetMinutes === null) {
|
|
1208
|
-
this.availabilityRuntimeOffsetMinutes = (0,
|
|
1182
|
+
this.availabilityRuntimeOffsetMinutes = (0, _localClock.captureRuntimeOffsetMinutes)();
|
|
1209
1183
|
}
|
|
1210
1184
|
return this.availabilityRuntimeOffsetMinutes;
|
|
1211
1185
|
}
|
|
1212
|
-
getAvailabilityRuntimeDateTime(value =
|
|
1213
|
-
return (0,
|
|
1186
|
+
getAvailabilityRuntimeDateTime(value = new Date()) {
|
|
1187
|
+
return (0, _localClock.formatLocalDateTime)((0, _localClock.localDateToScalar)(value), this.getAvailabilityRuntimeOffsetMinutes());
|
|
1214
1188
|
}
|
|
1215
1189
|
cloneAvailabilityPrepareParams(params) {
|
|
1216
|
-
|
|
1217
|
-
return (0, import_lodash_es.cloneDeep)({
|
|
1190
|
+
return (0, _lodashEs.cloneDeep)({
|
|
1218
1191
|
...params,
|
|
1219
|
-
productIds: params.productIds ? [...params.productIds] :
|
|
1220
|
-
resourceIds: params.resourceIds ? [...params.resourceIds] :
|
|
1221
|
-
rawProducts: params.rawProducts ? (0,
|
|
1222
|
-
rawResources: params.rawResources ? (0,
|
|
1223
|
-
rawOccupancies: params.rawOccupancies ? (0,
|
|
1224
|
-
editingProductLineUids: params.editingProductLineUids ? [...params.editingProductLineUids] :
|
|
1225
|
-
dateRange: params.dateRange ? {
|
|
1226
|
-
|
|
1192
|
+
productIds: params.productIds ? [...params.productIds] : undefined,
|
|
1193
|
+
resourceIds: params.resourceIds ? [...params.resourceIds] : undefined,
|
|
1194
|
+
rawProducts: params.rawProducts ? (0, _lodashEs.cloneDeep)(params.rawProducts) : undefined,
|
|
1195
|
+
rawResources: params.rawResources ? (0, _lodashEs.cloneDeep)(params.rawResources) : undefined,
|
|
1196
|
+
rawOccupancies: params.rawOccupancies ? (0, _lodashEs.cloneDeep)(params.rawOccupancies) : undefined,
|
|
1197
|
+
editingProductLineUids: params.editingProductLineUids ? [...params.editingProductLineUids] : undefined,
|
|
1198
|
+
dateRange: params.dateRange ? {
|
|
1199
|
+
...params.dateRange
|
|
1200
|
+
} : undefined,
|
|
1201
|
+
businessHours: params.businessHours ? {
|
|
1202
|
+
...params.businessHours
|
|
1203
|
+
} : undefined,
|
|
1227
1204
|
_availabilityOperatingHours: params._availabilityOperatingHours ? {
|
|
1228
1205
|
source: params._availabilityOperatingHours.source,
|
|
1229
1206
|
scheduleIds: [...params._availabilityOperatingHours.scheduleIds]
|
|
1230
|
-
} :
|
|
1231
|
-
catalogScope:
|
|
1207
|
+
} : undefined,
|
|
1208
|
+
catalogScope: params.catalogScope?.type === 'schedule' ? {
|
|
1209
|
+
...params.catalogScope
|
|
1210
|
+
} : params.catalogScope?.type === 'productList' ? {
|
|
1211
|
+
type: 'productList',
|
|
1212
|
+
params: {
|
|
1213
|
+
...params.catalogScope.params
|
|
1214
|
+
}
|
|
1215
|
+
} : undefined
|
|
1232
1216
|
});
|
|
1233
1217
|
}
|
|
1234
1218
|
getAvailabilityDurationCandidateAnchorAt(projection) {
|
|
1235
|
-
const value = projection
|
|
1236
|
-
return typeof value ===
|
|
1219
|
+
const value = projection?.meta.durationCandidateAnchorAt;
|
|
1220
|
+
return typeof value === 'string' && value.trim() ? value : undefined;
|
|
1237
1221
|
}
|
|
1238
1222
|
createAvailabilityContextRef(context) {
|
|
1239
1223
|
return {
|
|
@@ -1241,19 +1225,14 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
1241
1225
|
};
|
|
1242
1226
|
}
|
|
1243
1227
|
async emitAvailabilityDiagnostic(payload) {
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
...payload,
|
|
1250
|
-
registrySize: this.availabilityContextRegistry.size,
|
|
1251
|
-
projectionPoolSize: this.availabilityProjectionPool.size
|
|
1252
|
-
}
|
|
1253
|
-
));
|
|
1228
|
+
await this.core?.effects?.emit?.(`${this.name}:${_types.UnifiedBookingSalesHooks.onAvailabilityProjectionDiagnostic}`, {
|
|
1229
|
+
...payload,
|
|
1230
|
+
registrySize: this.availabilityContextRegistry.size,
|
|
1231
|
+
projectionPoolSize: this.availabilityProjectionPool.size
|
|
1232
|
+
});
|
|
1254
1233
|
}
|
|
1255
1234
|
touchAvailabilityContext(context) {
|
|
1256
|
-
context.lastAccessedAt =
|
|
1235
|
+
context.lastAccessedAt = new Date().toISOString();
|
|
1257
1236
|
this.availabilityContextRegistry.delete(context.contextId);
|
|
1258
1237
|
this.availabilityContextRegistry.set(context.contextId, context);
|
|
1259
1238
|
}
|
|
@@ -1263,14 +1242,12 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
1263
1242
|
return entry;
|
|
1264
1243
|
}
|
|
1265
1244
|
removeAvailabilityProjection(projectionKey) {
|
|
1266
|
-
if (!projectionKey)
|
|
1267
|
-
return;
|
|
1245
|
+
if (!projectionKey) return;
|
|
1268
1246
|
this.availabilityProjectionPool.delete(projectionKey);
|
|
1269
1247
|
}
|
|
1270
1248
|
removeAvailabilityContext(contextId) {
|
|
1271
1249
|
const context = this.availabilityContextRegistry.get(contextId);
|
|
1272
|
-
if (!context)
|
|
1273
|
-
return;
|
|
1250
|
+
if (!context) return;
|
|
1274
1251
|
this.removeAvailabilityProjection(context.activeProjectionKey);
|
|
1275
1252
|
this.availabilityProjectionRefreshInFlight.delete(contextId);
|
|
1276
1253
|
this.availabilityCommitLocks.delete(contextId);
|
|
@@ -1279,12 +1256,11 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
1279
1256
|
evictAvailabilityProjectionPool() {
|
|
1280
1257
|
while (this.availabilityProjectionPool.size > MAX_AVAILABILITY_PROJECTION_POOL) {
|
|
1281
1258
|
const oldestEntry = this.availabilityProjectionPool.entries().next().value;
|
|
1282
|
-
if (!oldestEntry)
|
|
1283
|
-
break;
|
|
1259
|
+
if (!oldestEntry) break;
|
|
1284
1260
|
const [projectionKey, projectionEntry] = oldestEntry;
|
|
1285
1261
|
this.availabilityProjectionPool.delete(projectionKey);
|
|
1286
1262
|
const context = this.availabilityContextRegistry.get(projectionEntry.contextId);
|
|
1287
|
-
if (
|
|
1263
|
+
if (context?.activeProjectionKey === projectionKey) {
|
|
1288
1264
|
context.activeProjectionKey = null;
|
|
1289
1265
|
context.refreshNonce += 1;
|
|
1290
1266
|
}
|
|
@@ -1293,8 +1269,7 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
1293
1269
|
evictAvailabilityContextRegistry() {
|
|
1294
1270
|
while (this.availabilityContextRegistry.size > MAX_AVAILABILITY_CONTEXT_REGISTRY) {
|
|
1295
1271
|
const oldestContext = this.availabilityContextRegistry.entries().next().value;
|
|
1296
|
-
if (!oldestContext)
|
|
1297
|
-
break;
|
|
1272
|
+
if (!oldestContext) break;
|
|
1298
1273
|
this.removeAvailabilityContext(oldestContext[0]);
|
|
1299
1274
|
}
|
|
1300
1275
|
}
|
|
@@ -1304,7 +1279,7 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
1304
1279
|
this.availabilityProjectionPool.clear();
|
|
1305
1280
|
this.availabilityPrepareInFlight.clear();
|
|
1306
1281
|
this.availabilityProjectionRefreshInFlight.clear();
|
|
1307
|
-
this.availabilityContextRegistry.forEach(
|
|
1282
|
+
this.availabilityContextRegistry.forEach(context => {
|
|
1308
1283
|
context.activeVersion += 1;
|
|
1309
1284
|
context.activeProjectionKey = null;
|
|
1310
1285
|
context.lastKnownOrderRevision = nextOrderRevision;
|
|
@@ -1329,40 +1304,39 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
1329
1304
|
});
|
|
1330
1305
|
}
|
|
1331
1306
|
isSessionStoragePersistEnabled() {
|
|
1332
|
-
|
|
1333
|
-
return Boolean(
|
|
1334
|
-
this.cacheId && ((_a = this.otherParams) == null ? void 0 : _a.enableSessionStoragePersist) === true
|
|
1335
|
-
);
|
|
1307
|
+
return Boolean(this.cacheId && this.otherParams?.enableSessionStoragePersist === true);
|
|
1336
1308
|
}
|
|
1337
1309
|
shouldUseSessionStorageForSubModule(moduleName) {
|
|
1338
|
-
return this.isSessionStoragePersistEnabled() && moduleName ===
|
|
1310
|
+
return this.isSessionStoragePersistEnabled() && moduleName === 'order';
|
|
1339
1311
|
}
|
|
1312
|
+
|
|
1340
1313
|
/**
|
|
1341
1314
|
* 读取与当前 cacheId 同分桶的 Solution 会话状态。
|
|
1342
1315
|
* 该状态独立于 otherParams,适合页面皮肤保存短生命周期 UI 状态。
|
|
1343
1316
|
*/
|
|
1344
1317
|
getSessionStateValue(key) {
|
|
1345
|
-
if (!key || !this.isSessionStoragePersistEnabled())
|
|
1346
|
-
return void 0;
|
|
1318
|
+
if (!key || !this.isSessionStoragePersistEnabled()) return undefined;
|
|
1347
1319
|
const cacheData = this.readSessionCacheData();
|
|
1348
|
-
const solutionCache = cacheData
|
|
1320
|
+
const solutionCache = cacheData?.[this.name];
|
|
1349
1321
|
if (!isRecord(solutionCache) || !isRecord(solutionCache.sessionState)) {
|
|
1350
|
-
return
|
|
1322
|
+
return undefined;
|
|
1351
1323
|
}
|
|
1352
1324
|
return solutionCache.sessionState[key];
|
|
1353
1325
|
}
|
|
1326
|
+
|
|
1354
1327
|
/**
|
|
1355
1328
|
* 写入与当前 cacheId 同分桶的 Solution 会话状态。
|
|
1356
1329
|
* value 为 undefined 时删除该 key;存储失败不阻断业务流程。
|
|
1357
1330
|
*/
|
|
1358
1331
|
setSessionStateValue(key, value) {
|
|
1359
|
-
if (!key || !this.isSessionStoragePersistEnabled())
|
|
1360
|
-
return;
|
|
1332
|
+
if (!key || !this.isSessionStoragePersistEnabled()) return;
|
|
1361
1333
|
const cacheData = this.readSessionCacheData();
|
|
1362
|
-
const solutionCache = cacheData
|
|
1334
|
+
const solutionCache = cacheData?.[this.name];
|
|
1363
1335
|
const currentSessionState = isRecord(solutionCache) && isRecord(solutionCache.sessionState) ? solutionCache.sessionState : {};
|
|
1364
|
-
const nextSessionState = {
|
|
1365
|
-
|
|
1336
|
+
const nextSessionState = {
|
|
1337
|
+
...currentSessionState
|
|
1338
|
+
};
|
|
1339
|
+
if (value === undefined) {
|
|
1366
1340
|
delete nextSessionState[key];
|
|
1367
1341
|
} else {
|
|
1368
1342
|
nextSessionState[key] = value;
|
|
@@ -1370,52 +1344,54 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
1370
1344
|
this.checkSaveCache({
|
|
1371
1345
|
cacheId: this.cacheId,
|
|
1372
1346
|
fatherModule: this.name,
|
|
1373
|
-
store: {
|
|
1374
|
-
|
|
1347
|
+
store: {
|
|
1348
|
+
sessionState: nextSessionState
|
|
1349
|
+
},
|
|
1350
|
+
cacheKey: ['sessionState']
|
|
1375
1351
|
});
|
|
1376
1352
|
}
|
|
1353
|
+
|
|
1377
1354
|
/**
|
|
1378
1355
|
* 清除当前 cacheId 的可恢复订单快照,同时保留 Solution UI sessionState。
|
|
1379
1356
|
* 用于支付跳转等终态场景,避免浏览器返回时恢复已提交订单。
|
|
1380
1357
|
*/
|
|
1381
1358
|
clearSessionStorageOrderSnapshot() {
|
|
1382
|
-
|
|
1383
|
-
(_b = (_a = this.store.order) == null ? void 0 : _a.clearSessionStoragePersistedTempOrder) == null ? void 0 : _b.call(_a);
|
|
1359
|
+
this.store.order?.clearSessionStoragePersistedTempOrder?.();
|
|
1384
1360
|
}
|
|
1361
|
+
|
|
1385
1362
|
/**
|
|
1386
1363
|
* Kiosk 不能把已收款订单留在本地等待重试:默认直接等待云端 checkout。
|
|
1387
1364
|
* 兼容仍需要 WebPOS 待同步模式的入口可显式传 checkoutSyncTaskEnabled: true。
|
|
1388
1365
|
*/
|
|
1389
1366
|
shouldUseCheckoutSyncTask() {
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
return typeof configured === "boolean" ? configured : false;
|
|
1367
|
+
const configured = this.otherParams?.checkoutSyncTaskEnabled;
|
|
1368
|
+
return typeof configured === 'boolean' ? configured : false;
|
|
1393
1369
|
}
|
|
1370
|
+
|
|
1394
1371
|
/** UnifiedBookingSales 的真实结账默认请求收据数据,供云端确认后再本地打印。 */
|
|
1395
1372
|
getDefaultCheckoutSmallTicketDataFlag() {
|
|
1396
1373
|
return 1;
|
|
1397
1374
|
}
|
|
1398
1375
|
getRegisteredModuleNames() {
|
|
1399
|
-
return [...super.getRegisteredModuleNames(),
|
|
1376
|
+
return [...super.getRegisteredModuleNames(), 'openData'];
|
|
1400
1377
|
}
|
|
1401
1378
|
createSubModule(moduleName) {
|
|
1402
|
-
if (moduleName ===
|
|
1403
|
-
return new
|
|
1379
|
+
if (moduleName === 'openData') {
|
|
1380
|
+
return new _modules.OpenDataModule(`${this.name}_openData`);
|
|
1404
1381
|
}
|
|
1405
1382
|
return super.createSubModule(moduleName);
|
|
1406
1383
|
}
|
|
1407
1384
|
async addNewOrder() {
|
|
1408
|
-
|
|
1409
|
-
const sessionRecord = this.isSessionStoragePersistEnabled() ? (_b = (_a = this.store.order) == null ? void 0 : _a.consumeSessionStorageRestore) == null ? void 0 : _b.call(_a) : null;
|
|
1385
|
+
const sessionRecord = this.isSessionStoragePersistEnabled() ? this.store.order?.consumeSessionStorageRestore?.() : null;
|
|
1410
1386
|
if (sessionRecord) {
|
|
1411
1387
|
await this.loadSalesDetail({
|
|
1412
1388
|
preloadedSalesDetail: sessionRecord,
|
|
1413
|
-
hydrateSource:
|
|
1389
|
+
hydrateSource: 'sessionStorage'
|
|
1414
1390
|
});
|
|
1415
1391
|
}
|
|
1416
1392
|
const tempOrder = await super.addNewOrder();
|
|
1417
1393
|
if (sessionRecord) {
|
|
1418
|
-
|
|
1394
|
+
this.store.order?.persistTempOrder();
|
|
1419
1395
|
}
|
|
1420
1396
|
if (this.isAuthenticatedCustomerUserPlatform()) {
|
|
1421
1397
|
try {
|
|
@@ -1424,102 +1400,82 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
1424
1400
|
await this.refreshLegacySessionCustomerDiscounts(tempOrder);
|
|
1425
1401
|
}
|
|
1426
1402
|
} catch (error) {
|
|
1427
|
-
console.warn(
|
|
1428
|
-
"[UnifiedBookingSales] Failed to sync authenticated order customer after addNewOrder",
|
|
1429
|
-
error
|
|
1430
|
-
);
|
|
1403
|
+
console.warn('[UnifiedBookingSales] Failed to sync authenticated order customer after addNewOrder', error);
|
|
1431
1404
|
}
|
|
1432
1405
|
} else if (sessionRecord) {
|
|
1433
1406
|
try {
|
|
1434
1407
|
await this.refreshLegacySessionCustomerDiscounts(tempOrder);
|
|
1435
1408
|
} catch (error) {
|
|
1436
|
-
console.warn(
|
|
1437
|
-
"[UnifiedBookingSales] Failed to refresh cached customer discounts after addNewOrder",
|
|
1438
|
-
error
|
|
1439
|
-
);
|
|
1409
|
+
console.warn('[UnifiedBookingSales] Failed to refresh cached customer discounts after addNewOrder', error);
|
|
1440
1410
|
}
|
|
1441
1411
|
}
|
|
1442
|
-
return
|
|
1412
|
+
return this.store.order?.getTempOrder() || tempOrder;
|
|
1443
1413
|
}
|
|
1444
1414
|
async syncAuthenticatedOrderCustomer() {
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
const authenticatedCustomerId = toCustomerId(authenticatedCustomer == null ? void 0 : authenticatedCustomer.id);
|
|
1450
|
-
if (!authenticatedCustomerId)
|
|
1451
|
-
return false;
|
|
1415
|
+
if (!this.isAuthenticatedCustomerUserPlatform()) return false;
|
|
1416
|
+
const authenticatedCustomer = this.core?.getPlugin?.('user')?.get?.();
|
|
1417
|
+
const authenticatedCustomerId = toCustomerId(authenticatedCustomer?.id);
|
|
1418
|
+
if (!authenticatedCustomerId) return false;
|
|
1452
1419
|
const tempOrder = this.getTempOrder();
|
|
1453
1420
|
const cachedCustomerId = resolveTempOrderCustomerId(tempOrder);
|
|
1454
1421
|
if (cachedCustomerId !== null && cachedCustomerId !== authenticatedCustomerId) {
|
|
1455
1422
|
await this.resetCachedCustomerDiscountState();
|
|
1456
1423
|
}
|
|
1457
|
-
|
|
1424
|
+
this.store.customer?.setSelectedCustomer?.(authenticatedCustomer);
|
|
1458
1425
|
this.setOrderCustomer(authenticatedCustomer);
|
|
1459
|
-
await Promise.all([
|
|
1460
|
-
this.refreshDiscountConfigAfterOrderCustomerChange(authenticatedCustomerId),
|
|
1461
|
-
this.waitForOrderCustomerPromotionRefresh()
|
|
1462
|
-
]);
|
|
1426
|
+
await Promise.all([this.refreshDiscountConfigAfterOrderCustomerChange(authenticatedCustomerId), this.waitForOrderCustomerPromotionRefresh()]);
|
|
1463
1427
|
return true;
|
|
1464
1428
|
}
|
|
1465
1429
|
isAuthenticatedCustomerUserPlatform() {
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
return platform === "pc" || platform === "h5";
|
|
1430
|
+
const platform = String(this.otherParams?.platform || '').toLowerCase();
|
|
1431
|
+
return platform === 'pc' || platform === 'h5';
|
|
1469
1432
|
}
|
|
1470
1433
|
resolveDiscountOrderIdentity(tempOrder) {
|
|
1471
|
-
|
|
1472
|
-
return (tempOrder == null ? void 0 : tempOrder.order_id) || ((_c = (_b = (_a = this.store.order) == null ? void 0 : _a.getOrderIdentity) == null ? void 0 : _b.call(_a)) == null ? void 0 : _c.orderId) || null;
|
|
1434
|
+
return tempOrder?.order_id || this.store.order?.getOrderIdentity?.()?.orderId || null;
|
|
1473
1435
|
}
|
|
1474
1436
|
resolveDiscountAction(orderId) {
|
|
1475
|
-
if (!orderId || isLocalOrderId(orderId))
|
|
1476
|
-
|
|
1477
|
-
return "edit";
|
|
1437
|
+
if (!orderId || isLocalOrderId(orderId)) return 'create';
|
|
1438
|
+
return 'edit';
|
|
1478
1439
|
}
|
|
1479
1440
|
async resetCachedCustomerDiscountState() {
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
(_d = (_c = this.store.customer) == null ? void 0 : _c.setSelectedCustomer) == null ? void 0 : _d.call(_c, null);
|
|
1441
|
+
this.store.order?.clearOrderCustomer?.();
|
|
1442
|
+
this.store.customer?.setSelectedCustomer?.(null);
|
|
1483
1443
|
this.discountConfigCacheKey = null;
|
|
1484
1444
|
this.hasManualDiscountSelection = false;
|
|
1485
|
-
const orderStore =
|
|
1445
|
+
const orderStore = this.store.order?.store || {};
|
|
1486
1446
|
const discountModule = orderStore.discount;
|
|
1487
|
-
const filteredOriginalDiscounts = filterCustomerBoundDiscounts(
|
|
1488
|
-
|
|
1489
|
-
);
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
await ((_m = (_l = this.store.order) == null ? void 0 : _l.recalculateSummary) == null ? void 0 : _m.call(_l, { createIfMissing: true }));
|
|
1497
|
-
(_o = (_n = this.store.order) == null ? void 0 : _n.persistTempOrder) == null ? void 0 : _o.call(_n);
|
|
1447
|
+
const filteredOriginalDiscounts = filterCustomerBoundDiscounts(discountModule?.getOriginalDiscountList?.() || []);
|
|
1448
|
+
const filteredCurrentDiscounts = filterCustomerBoundDiscounts(discountModule?.getDiscountList?.() || []);
|
|
1449
|
+
await discountModule?.setOriginalDiscountList?.(filteredOriginalDiscounts);
|
|
1450
|
+
await discountModule?.setDiscountList?.(filteredCurrentDiscounts);
|
|
1451
|
+
this.store.order?.applyDiscount?.();
|
|
1452
|
+
await this.store.order?.recalculateSummary?.({
|
|
1453
|
+
createIfMissing: true
|
|
1454
|
+
});
|
|
1455
|
+
this.store.order?.persistTempOrder?.();
|
|
1498
1456
|
}
|
|
1499
1457
|
async refreshLegacySessionCustomerDiscounts(tempOrder) {
|
|
1500
1458
|
const customerId = resolveTempOrderCustomerId(tempOrder);
|
|
1501
|
-
if (!customerId)
|
|
1502
|
-
return void 0;
|
|
1459
|
+
if (!customerId) return undefined;
|
|
1503
1460
|
const loadDiscountConfig = super.loadDiscountConfig.bind(this);
|
|
1504
1461
|
return await loadDiscountConfig({
|
|
1505
1462
|
customerId,
|
|
1506
|
-
action: this.resolveDiscountAction(
|
|
1507
|
-
this.resolveDiscountOrderIdentity(tempOrder)
|
|
1508
|
-
)
|
|
1463
|
+
action: this.resolveDiscountAction(this.resolveDiscountOrderIdentity(tempOrder))
|
|
1509
1464
|
});
|
|
1510
1465
|
}
|
|
1466
|
+
|
|
1511
1467
|
/**
|
|
1512
1468
|
* Loads OpenData without borrowing BookingTicket. Cache ownership is tied to the exact
|
|
1513
1469
|
* business/channel target so a reused solution instance cannot leak another entry's menus.
|
|
1514
1470
|
*/
|
|
1515
1471
|
async loadOpenData(params) {
|
|
1516
1472
|
if (!this.store.openData) {
|
|
1517
|
-
throw new Error(
|
|
1473
|
+
throw new Error('[UnifiedBookingSales] openData 模块未初始化');
|
|
1518
1474
|
}
|
|
1519
|
-
const businessCode = String(params.businessCode ||
|
|
1520
|
-
const channel = String(params.channel ||
|
|
1475
|
+
const businessCode = String(params.businessCode || '').trim();
|
|
1476
|
+
const channel = String(params.channel || '').trim();
|
|
1521
1477
|
if (!businessCode || !channel) {
|
|
1522
|
-
throw new Error(
|
|
1478
|
+
throw new Error('[UnifiedBookingSales] OpenData 需要 businessCode 与 channel');
|
|
1523
1479
|
}
|
|
1524
1480
|
const target = `${businessCode}+${channel}`;
|
|
1525
1481
|
const cachedData = this.store.openData.getOpenData();
|
|
@@ -1536,7 +1492,7 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
1536
1492
|
}
|
|
1537
1493
|
this.loadOpenDataInFlightTarget = target;
|
|
1538
1494
|
this.loadOpenDataInFlight = this.store.openData.fetchOpenData({
|
|
1539
|
-
scope:
|
|
1495
|
+
scope: 'board',
|
|
1540
1496
|
target,
|
|
1541
1497
|
section_code: [...OPEN_DATA_SECTION_CODES]
|
|
1542
1498
|
});
|
|
@@ -1553,40 +1509,39 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
1553
1509
|
}
|
|
1554
1510
|
}
|
|
1555
1511
|
async getOpenData() {
|
|
1556
|
-
|
|
1557
|
-
const
|
|
1558
|
-
|
|
1559
|
-
if (!businessCode || !channel)
|
|
1560
|
-
return null;
|
|
1512
|
+
const businessCode = String(this.otherParams?.businessCode || '').trim();
|
|
1513
|
+
const channel = String(this.otherParams?.channel || '').trim();
|
|
1514
|
+
if (!businessCode || !channel) return null;
|
|
1561
1515
|
const target = `${businessCode}+${channel}`;
|
|
1562
|
-
const cachedData =
|
|
1563
|
-
if (cachedData && this.openDataTarget === target)
|
|
1564
|
-
|
|
1565
|
-
|
|
1516
|
+
const cachedData = this.store.openData?.getOpenData() || null;
|
|
1517
|
+
if (cachedData && this.openDataTarget === target) return cachedData;
|
|
1518
|
+
return this.loadOpenData({
|
|
1519
|
+
businessCode,
|
|
1520
|
+
channel
|
|
1521
|
+
});
|
|
1566
1522
|
}
|
|
1523
|
+
|
|
1567
1524
|
/**
|
|
1568
1525
|
* Load a resource policy without depending on the legacy appointmentBooking solution.
|
|
1569
1526
|
*/
|
|
1570
1527
|
async getProtocol(policyId) {
|
|
1571
|
-
const normalizedPolicyId = String(policyId ??
|
|
1528
|
+
const normalizedPolicyId = String(policyId ?? '').trim();
|
|
1572
1529
|
if (!normalizedPolicyId) {
|
|
1573
|
-
throw new Error(
|
|
1530
|
+
throw new Error('[UnifiedBookingSales] getProtocol 需要 policyId');
|
|
1574
1531
|
}
|
|
1575
|
-
return this.request.get(
|
|
1576
|
-
`/shop-policy/${encodeURIComponent(normalizedPolicyId)}`
|
|
1577
|
-
);
|
|
1532
|
+
return this.request.get(`/shop-policy/${encodeURIComponent(normalizedPolicyId)}`);
|
|
1578
1533
|
}
|
|
1534
|
+
|
|
1579
1535
|
/**
|
|
1580
1536
|
* Loads the product catalog used by both UI and Planner. Schedule data is requested with each
|
|
1581
1537
|
* product because session products carry their fixed intervals there.
|
|
1582
1538
|
*/
|
|
1583
1539
|
async loadProducts(params = {}, options) {
|
|
1584
|
-
if (!this.store.products)
|
|
1585
|
-
|
|
1586
|
-
const
|
|
1587
|
-
const schedule_datetime = params.schedule_datetime || `${schedule_date} ${(0, import_dayjs.default)().format("HH:mm:ss")}`;
|
|
1540
|
+
if (!this.store.products) throw new Error('[UnifiedBookingSales] products 模块未初始化');
|
|
1541
|
+
const schedule_date = params.schedule_date || (0, _dayjs.default)().format('YYYY-MM-DD');
|
|
1542
|
+
const schedule_datetime = params.schedule_datetime || `${schedule_date} ${(0, _dayjs.default)().format('HH:mm:ss')}`;
|
|
1588
1543
|
const result = await this.store.products.loadProducts({
|
|
1589
|
-
with_count: [
|
|
1544
|
+
with_count: ['bundleGroup', 'optionGroup'],
|
|
1590
1545
|
with_schedule: 1,
|
|
1591
1546
|
...params,
|
|
1592
1547
|
schedule_date,
|
|
@@ -1597,19 +1552,19 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
1597
1552
|
await this.core.effects.emit(`${this.name}:onProductsLoaded`, result);
|
|
1598
1553
|
return result;
|
|
1599
1554
|
}
|
|
1555
|
+
|
|
1600
1556
|
/** Date-first entry point. It delegates protocol details and cache ownership to ScheduleModule. */
|
|
1601
1557
|
async loadScheduleAvailableDate(params) {
|
|
1602
|
-
if (!this.store.schedule)
|
|
1603
|
-
throw new Error("[UnifiedBookingSales] schedule 模块未初始化");
|
|
1558
|
+
if (!this.store.schedule) throw new Error('[UnifiedBookingSales] schedule 模块未初始化');
|
|
1604
1559
|
return this.store.schedule.loadScheduleAvailableDate(params);
|
|
1605
1560
|
}
|
|
1561
|
+
|
|
1606
1562
|
/**
|
|
1607
1563
|
* Implements the date-first product pool: the chosen date's product ids plus globally sellable
|
|
1608
1564
|
* other_product_ids. If a batch product query is unexpectedly empty, retry one id at a time so
|
|
1609
1565
|
* a partial backend incompatibility does not hide the entire date's catalog.
|
|
1610
1566
|
*/
|
|
1611
1567
|
async loadProductsByScheduleDate(params) {
|
|
1612
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
1613
1568
|
const {
|
|
1614
1569
|
date,
|
|
1615
1570
|
custom_page_id,
|
|
@@ -1617,11 +1572,10 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
1617
1572
|
product_ids,
|
|
1618
1573
|
...productLoadParams
|
|
1619
1574
|
} = params;
|
|
1620
|
-
if (!this.store.schedule)
|
|
1621
|
-
|
|
1622
|
-
const
|
|
1623
|
-
const
|
|
1624
|
-
const hasOtherProducts = (((_d = (_c = this.store.schedule).getOtherProductsIds) == null ? void 0 : _d.call(_c)) || []).length > 0;
|
|
1575
|
+
if (!this.store.schedule) throw new Error('[UnifiedBookingSales] schedule 模块未初始化');
|
|
1576
|
+
const cachedDates = this.store.schedule.getAvailabilityScheduleDateList?.() || [];
|
|
1577
|
+
const hasCachedDate = cachedDates.some(item => item.date === date);
|
|
1578
|
+
const hasOtherProducts = (this.store.schedule.getOtherProductsIds?.() || []).length > 0;
|
|
1625
1579
|
if (!hasCachedDate && !hasOtherProducts) {
|
|
1626
1580
|
await this.loadScheduleAvailableDate({
|
|
1627
1581
|
startDate: date,
|
|
@@ -1630,12 +1584,9 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
1630
1584
|
channel
|
|
1631
1585
|
});
|
|
1632
1586
|
}
|
|
1633
|
-
const scheduleDate =
|
|
1634
|
-
const productIdFilter =
|
|
1635
|
-
const availableProductIds = [
|
|
1636
|
-
...(scheduleDate == null ? void 0 : scheduleDate.product_ids) || [],
|
|
1637
|
-
...((_i = (_h = this.store.schedule).getOtherProductsIds) == null ? void 0 : _i.call(_h)) || []
|
|
1638
|
-
].filter((id, index, self) => self.indexOf(id) === index).filter((id) => !productIdFilter || productIdFilter.has(String(id)));
|
|
1587
|
+
const scheduleDate = this.store.schedule.getAvailabilityScheduleDateList?.()?.find(item => item.date === date);
|
|
1588
|
+
const productIdFilter = product_ids?.length ? new Set(product_ids.map(id => String(id))) : null;
|
|
1589
|
+
const availableProductIds = [...(scheduleDate?.product_ids || []), ...(this.store.schedule.getOtherProductsIds?.() || [])].filter((id, index, self) => self.indexOf(id) === index).filter(id => !productIdFilter || productIdFilter.has(String(id)));
|
|
1639
1590
|
if (!availableProductIds.length) {
|
|
1640
1591
|
this.unifiedProductCatalog = [];
|
|
1641
1592
|
await this.core.effects.emit(`${this.name}:onProductsLoaded`, []);
|
|
@@ -1646,8 +1597,7 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
1646
1597
|
product_ids: availableProductIds,
|
|
1647
1598
|
schedule_date: date
|
|
1648
1599
|
});
|
|
1649
|
-
if (products.length || availableProductIds.length <= 1)
|
|
1650
|
-
return products;
|
|
1600
|
+
if (products.length || availableProductIds.length <= 1) return products;
|
|
1651
1601
|
const fallbackProducts = [];
|
|
1652
1602
|
for (const productId of availableProductIds) {
|
|
1653
1603
|
const result = await this.loadProducts({
|
|
@@ -1655,9 +1605,9 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
1655
1605
|
product_ids: [productId],
|
|
1656
1606
|
schedule_date: date
|
|
1657
1607
|
});
|
|
1658
|
-
result.forEach(
|
|
1659
|
-
const id =
|
|
1660
|
-
if (!fallbackProducts.some(
|
|
1608
|
+
result.forEach(product => {
|
|
1609
|
+
const id = product?.id ?? product?.product_id;
|
|
1610
|
+
if (!fallbackProducts.some(item => String(item?.id ?? item?.product_id) === String(id))) {
|
|
1661
1611
|
fallbackProducts.push(product);
|
|
1662
1612
|
}
|
|
1663
1613
|
});
|
|
@@ -1666,10 +1616,14 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
1666
1616
|
await this.core.effects.emit(`${this.name}:onProductsLoaded`, fallbackProducts);
|
|
1667
1617
|
return fallbackProducts;
|
|
1668
1618
|
}
|
|
1619
|
+
|
|
1669
1620
|
/** Returns a copy so UI consumers cannot accidentally mutate the solution's product cache. */
|
|
1670
1621
|
getProductCatalog() {
|
|
1671
|
-
return {
|
|
1622
|
+
return {
|
|
1623
|
+
products: [...this.unifiedProductCatalog]
|
|
1624
|
+
};
|
|
1672
1625
|
}
|
|
1626
|
+
|
|
1673
1627
|
/**
|
|
1674
1628
|
* Read-only compatibility view for salesSdk skins. It reconnects a booking to its product but
|
|
1675
1629
|
* must not be used to mutate tempOrder; all writes go through BaseSales / planner APIs.
|
|
@@ -1701,6 +1655,7 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
1701
1655
|
// }),
|
|
1702
1656
|
// };
|
|
1703
1657
|
// }
|
|
1658
|
+
|
|
1704
1659
|
/**
|
|
1705
1660
|
* Loads the current customer's product coupons and discount cards after planner lines exist,
|
|
1706
1661
|
* then optionally lets the existing Rules engine select the best valid combination. This is a
|
|
@@ -1708,7 +1663,7 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
1708
1663
|
*/
|
|
1709
1664
|
async applyPlannerDiscounts(params = {}) {
|
|
1710
1665
|
const tempOrder = this.getTempOrder();
|
|
1711
|
-
const requestedCustomerId = params.customerId ??
|
|
1666
|
+
const requestedCustomerId = params.customerId ?? tempOrder?.customer_id;
|
|
1712
1667
|
const parsedCustomerId = Number(requestedCustomerId);
|
|
1713
1668
|
const customerId = Number.isFinite(parsedCustomerId) && parsedCustomerId > 1 ? parsedCustomerId : null;
|
|
1714
1669
|
const summaryBefore = await this.getSummary();
|
|
@@ -1716,31 +1671,39 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
1716
1671
|
return buildPlannerDiscountResult({
|
|
1717
1672
|
customerId: null,
|
|
1718
1673
|
discountList: this.getDiscountList(),
|
|
1719
|
-
products:
|
|
1674
|
+
products: tempOrder?.products || [],
|
|
1720
1675
|
summaryBefore,
|
|
1721
1676
|
summaryAfter: summaryBefore,
|
|
1722
|
-
status:
|
|
1677
|
+
status: 'no_customer'
|
|
1723
1678
|
});
|
|
1724
1679
|
}
|
|
1725
|
-
await this.loadDiscountConfig({
|
|
1680
|
+
await this.loadDiscountConfig({
|
|
1681
|
+
customerId,
|
|
1682
|
+
action: 'create'
|
|
1683
|
+
});
|
|
1726
1684
|
const preferredDiscountId = params.preferredDiscountId;
|
|
1727
|
-
const preferredDiscount = preferredDiscountId ===
|
|
1685
|
+
const preferredDiscount = preferredDiscountId === undefined || preferredDiscountId === null ? undefined : this.getDiscountList().find(discount => String(resolvePlannerDiscountId(discount) ?? '') === String(preferredDiscountId));
|
|
1728
1686
|
if (preferredDiscount) {
|
|
1687
|
+
// A requested card is an explicit UI choice, not a best-effort preference. Clear prior
|
|
1688
|
+
// automatic selections first; otherwise Rules may keep the old selected card and silently
|
|
1689
|
+
// apply it instead of the card the customer clicked.
|
|
1729
1690
|
const preferredId = resolvePlannerDiscountId(preferredDiscount);
|
|
1730
|
-
const selectedOtherDiscounts = this.getDiscountList().filter(
|
|
1691
|
+
const selectedOtherDiscounts = this.getDiscountList().filter(discount => discount.isSelected === true && String(resolvePlannerDiscountId(discount) ?? '') !== String(preferredId));
|
|
1731
1692
|
for (const discount of selectedOtherDiscounts) {
|
|
1732
1693
|
const discountId = Number(resolvePlannerDiscountId(discount));
|
|
1733
1694
|
if (Number.isFinite(discountId)) {
|
|
1734
|
-
await this.setDiscountSelected({
|
|
1695
|
+
await this.setDiscountSelected({
|
|
1696
|
+
discountId,
|
|
1697
|
+
isSelected: false
|
|
1698
|
+
});
|
|
1735
1699
|
}
|
|
1736
1700
|
}
|
|
1737
1701
|
await this.setDiscountSelected({
|
|
1738
1702
|
discountId: Number(preferredId),
|
|
1739
1703
|
isSelected: true
|
|
1740
1704
|
});
|
|
1741
|
-
} else if (preferredDiscountId ===
|
|
1742
|
-
if (params.applyBestDiscount !== false)
|
|
1743
|
-
await this.bestDiscount();
|
|
1705
|
+
} else if (preferredDiscountId === undefined || preferredDiscountId === null) {
|
|
1706
|
+
if (params.applyBestDiscount !== false) await this.bestDiscount();
|
|
1744
1707
|
} else if (params.applyBestDiscount !== false) {
|
|
1745
1708
|
await this.bestDiscount();
|
|
1746
1709
|
}
|
|
@@ -1748,206 +1711,204 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
1748
1711
|
return buildPlannerDiscountResult({
|
|
1749
1712
|
customerId,
|
|
1750
1713
|
discountList: this.getDiscountList(),
|
|
1751
|
-
products:
|
|
1714
|
+
products: nextTempOrder?.products || [],
|
|
1752
1715
|
summaryBefore,
|
|
1753
1716
|
summaryAfter: await this.getSummary(),
|
|
1754
|
-
status: params.applyBestDiscount === false && !preferredDiscount ?
|
|
1717
|
+
status: params.applyBestDiscount === false && !preferredDiscount ? 'loaded' : 'applied',
|
|
1755
1718
|
preferredDiscountId,
|
|
1756
|
-
preferredDiscountFound: preferredDiscountId ===
|
|
1719
|
+
preferredDiscountFound: preferredDiscountId === undefined || preferredDiscountId === null ? undefined : Boolean(preferredDiscount)
|
|
1757
1720
|
});
|
|
1758
1721
|
}
|
|
1759
1722
|
buildUnifiedOrderProduct(sourceProduct, ownershipKey, product) {
|
|
1760
|
-
const normalizedProductRaw =
|
|
1723
|
+
const normalizedProductRaw = product?.raw || {};
|
|
1761
1724
|
const orderProduct = this.transformBaseProductToOrderProduct({
|
|
1762
1725
|
payload: {
|
|
1763
1726
|
...sourceProduct,
|
|
1764
|
-
id: sourceProduct.id ??
|
|
1765
|
-
product_id: sourceProduct.product_id ?? sourceProduct.id ??
|
|
1766
|
-
quantity: sourceProduct.quantity ?? sourceProduct.num ??
|
|
1727
|
+
id: sourceProduct.id ?? product?.id,
|
|
1728
|
+
product_id: sourceProduct.product_id ?? sourceProduct.id ?? product?.id,
|
|
1729
|
+
quantity: sourceProduct.quantity ?? sourceProduct.num ?? product?.quantity ?? 1,
|
|
1767
1730
|
price: sourceProduct.price ?? normalizedProductRaw.price,
|
|
1768
1731
|
selling_price: sourceProduct.selling_price ?? sourceProduct.price ?? normalizedProductRaw.selling_price ?? normalizedProductRaw.price,
|
|
1769
1732
|
metadata: {
|
|
1770
|
-
...sourceProduct.metadata || {},
|
|
1733
|
+
...(sourceProduct.metadata || {}),
|
|
1771
1734
|
[ownershipKey]: true
|
|
1772
1735
|
}
|
|
1773
1736
|
},
|
|
1774
1737
|
sourceProduct
|
|
1775
1738
|
});
|
|
1776
|
-
if (orderProduct && typeof orderProduct ===
|
|
1739
|
+
if (orderProduct && typeof orderProduct === 'object') {
|
|
1740
|
+
// BaseSales may rebuild metadata from its price source. Stamp ownership after that transform
|
|
1741
|
+
// so each UnifiedBookingSales entry point can replace only its own temporary lines.
|
|
1777
1742
|
orderProduct.metadata = {
|
|
1778
|
-
...orderProduct.metadata || {},
|
|
1743
|
+
...(orderProduct.metadata || {}),
|
|
1779
1744
|
[ownershipKey]: true
|
|
1780
1745
|
};
|
|
1781
1746
|
}
|
|
1782
1747
|
return orderProduct;
|
|
1783
1748
|
}
|
|
1749
|
+
|
|
1784
1750
|
/** Replacing a staged selection is idempotent and never removes unrelated cart lines. */
|
|
1785
1751
|
async removeExistingStagedLines(ownershipKey) {
|
|
1786
|
-
|
|
1787
|
-
const existingLines = (((_a = this.getTempOrder()) == null ? void 0 : _a.products) || []).filter((product) => {
|
|
1788
|
-
var _a2;
|
|
1789
|
-
return ((_a2 = product == null ? void 0 : product.metadata) == null ? void 0 : _a2[ownershipKey]) === true;
|
|
1790
|
-
});
|
|
1752
|
+
const existingLines = (this.getTempOrder()?.products || []).filter(product => product?.metadata?.[ownershipKey] === true);
|
|
1791
1753
|
if (existingLines.length > 0) {
|
|
1792
1754
|
await this.removeProductsFromOrder(existingLines.map(buildAvailabilityLineIdentity));
|
|
1793
1755
|
}
|
|
1794
1756
|
}
|
|
1795
1757
|
async stageProducts(params, ownershipKey) {
|
|
1796
|
-
var _a;
|
|
1797
1758
|
const productIds = params.productIds || [];
|
|
1798
|
-
if (!productIds.length)
|
|
1799
|
-
|
|
1800
|
-
if ((_a = params.rawProducts) == null ? void 0 : _a.length) {
|
|
1759
|
+
if (!productIds.length) return this.getTempOrder();
|
|
1760
|
+
if (params.rawProducts?.length) {
|
|
1801
1761
|
this.unifiedProductCatalog = params.rawProducts;
|
|
1802
1762
|
} else {
|
|
1803
|
-
const existingIds = new Set((this.unifiedProductCatalog || []).map(
|
|
1804
|
-
const missingIds = productIds.filter(
|
|
1763
|
+
const existingIds = new Set((this.unifiedProductCatalog || []).map(product => String(product.id ?? product.product_id)));
|
|
1764
|
+
const missingIds = productIds.filter(id => !existingIds.has(String(id)));
|
|
1805
1765
|
if (missingIds.length || params.productLoadParams) {
|
|
1806
1766
|
await this.loadProducts({
|
|
1807
|
-
...params.productLoadParams || {},
|
|
1767
|
+
...(params.productLoadParams || {}),
|
|
1808
1768
|
product_ids: productIds.map(Number).filter(Number.isFinite)
|
|
1809
1769
|
});
|
|
1810
1770
|
}
|
|
1811
1771
|
}
|
|
1812
1772
|
await this.removeExistingStagedLines(ownershipKey);
|
|
1813
|
-
const productIdSet = new Set(productIds.map(
|
|
1814
|
-
const selectedProducts = (this.unifiedProductCatalog || []).filter(
|
|
1773
|
+
const productIdSet = new Set(productIds.map(id => String(id)));
|
|
1774
|
+
const selectedProducts = (this.unifiedProductCatalog || []).filter(product => productIdSet.has(String(product.id ?? product.product_id)));
|
|
1815
1775
|
for (const sourceProduct of selectedProducts) {
|
|
1816
1776
|
const productInput = this.buildUnifiedOrderProduct(sourceProduct, ownershipKey);
|
|
1817
|
-
if (productInput)
|
|
1818
|
-
await this.addProductToOrder(productInput);
|
|
1777
|
+
if (productInput) await this.addProductToOrder(productInput);
|
|
1819
1778
|
}
|
|
1820
1779
|
return this.getTempOrder();
|
|
1821
1780
|
}
|
|
1781
|
+
|
|
1822
1782
|
/**
|
|
1823
1783
|
* Adds ordinary retail products without asking ResourcePlanner for booking facts.
|
|
1824
1784
|
* Its staged lines have separate ownership so planner and retail skins can coexist safely.
|
|
1825
1785
|
*/
|
|
1826
1786
|
async stageRetailProducts(params) {
|
|
1827
|
-
return this.stageProducts(params,
|
|
1787
|
+
return this.stageProducts(params, 'unified_booking_sales_retail');
|
|
1828
1788
|
}
|
|
1789
|
+
|
|
1829
1790
|
/**
|
|
1830
1791
|
* Adds one retail line without replacing the cart's existing retail selection.
|
|
1831
1792
|
* This is intentionally separate from stageRetailProducts(), whose contract is
|
|
1832
1793
|
* "replace the staged selection" for demo and wizard-like callers.
|
|
1833
1794
|
*/
|
|
1834
1795
|
async addRetailProduct(params) {
|
|
1835
|
-
var _a, _b;
|
|
1836
1796
|
const quantity = Math.floor(Number(params.quantity ?? 1));
|
|
1837
1797
|
if (!Number.isFinite(quantity) || quantity <= 0) {
|
|
1838
|
-
throw new Error(
|
|
1798
|
+
throw new Error('[UnifiedBookingSales] retail 商品数量必须大于 0');
|
|
1839
1799
|
}
|
|
1840
|
-
const productId = params.productId ??
|
|
1800
|
+
const productId = params.productId ?? params.product?.id ?? params.product?.product_id;
|
|
1841
1801
|
let sourceProduct = params.product;
|
|
1842
|
-
if (!sourceProduct && productId !==
|
|
1843
|
-
sourceProduct = this.unifiedProductCatalog.find(
|
|
1844
|
-
(product) => String(product.id ?? product.product_id) === String(productId)
|
|
1845
|
-
);
|
|
1802
|
+
if (!sourceProduct && productId !== undefined && productId !== null) {
|
|
1803
|
+
sourceProduct = this.unifiedProductCatalog.find(product => String(product.id ?? product.product_id) === String(productId));
|
|
1846
1804
|
}
|
|
1847
|
-
if (!sourceProduct && productId !==
|
|
1805
|
+
if (!sourceProduct && productId !== undefined && productId !== null) {
|
|
1848
1806
|
const products = await this.loadProducts({
|
|
1849
|
-
...params.productLoadParams || {},
|
|
1807
|
+
...(params.productLoadParams || {}),
|
|
1850
1808
|
product_ids: [Number(productId)].filter(Number.isFinite)
|
|
1851
1809
|
});
|
|
1852
|
-
sourceProduct = products.find(
|
|
1853
|
-
(product) => String(product.id ?? product.product_id) === String(productId)
|
|
1854
|
-
);
|
|
1810
|
+
sourceProduct = products.find(product => String(product.id ?? product.product_id) === String(productId));
|
|
1855
1811
|
}
|
|
1856
1812
|
if (!sourceProduct) {
|
|
1857
|
-
throw new Error(`[UnifiedBookingSales] 未找到零售商品 ${String(productId ??
|
|
1813
|
+
throw new Error(`[UnifiedBookingSales] 未找到零售商品 ${String(productId ?? '')}`.trim());
|
|
1858
1814
|
}
|
|
1859
|
-
const orderProduct = this.buildUnifiedOrderProduct(
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
},
|
|
1864
|
-
"unified_booking_sales_retail"
|
|
1865
|
-
);
|
|
1815
|
+
const orderProduct = this.buildUnifiedOrderProduct({
|
|
1816
|
+
...sourceProduct,
|
|
1817
|
+
quantity
|
|
1818
|
+
}, 'unified_booking_sales_retail');
|
|
1866
1819
|
if (!orderProduct) {
|
|
1867
|
-
throw new Error(
|
|
1820
|
+
throw new Error('[UnifiedBookingSales] 无法生成零售商品订单行');
|
|
1868
1821
|
}
|
|
1869
1822
|
return this.addProductToOrder(orderProduct);
|
|
1870
1823
|
}
|
|
1871
1824
|
normalizeAvailabilityDateRange(params, timezone) {
|
|
1872
|
-
|
|
1873
|
-
const
|
|
1874
|
-
const
|
|
1875
|
-
const endDate = ((_b = params.dateRange) == null ? void 0 : _b.endDate) || (0, import_localClock.formatLocalDate)((0, import_localClock.addLocalCalendarDays)((0, import_localClock.localDateToScalarStart)((0, import_localClock.parseLocalDate)(startDate)), 31));
|
|
1825
|
+
const today = (0, _localClock.formatLocalDate)((0, _localClock.localDateToScalar)(new Date()));
|
|
1826
|
+
const startDate = params.dateRange?.startDate || today;
|
|
1827
|
+
const endDate = params.dateRange?.endDate || (0, _localClock.formatLocalDate)((0, _localClock.addLocalCalendarDays)((0, _localClock.localDateToScalarStart)((0, _localClock.parseLocalDate)(startDate)), 31));
|
|
1876
1828
|
let startScalar;
|
|
1877
1829
|
let endScalar;
|
|
1878
1830
|
try {
|
|
1879
|
-
startScalar = (0,
|
|
1880
|
-
endScalar = (0,
|
|
1831
|
+
startScalar = (0, _localClock.localDateToScalarStart)((0, _localClock.parseLocalDate)(startDate));
|
|
1832
|
+
endScalar = (0, _localClock.localDateToScalarStart)((0, _localClock.parseLocalDate)(endDate));
|
|
1881
1833
|
} catch (error) {
|
|
1882
|
-
throw new
|
|
1883
|
-
dateRange: {
|
|
1834
|
+
throw new _ResourcePlanner.AvailabilityError('INVALID_DATE_RANGE', '日期范围必须是有效的 YYYY-MM-DD 且结束日期不早于开始日期', {
|
|
1835
|
+
dateRange: {
|
|
1836
|
+
startDate,
|
|
1837
|
+
endDate
|
|
1838
|
+
},
|
|
1884
1839
|
timezone,
|
|
1885
1840
|
cause: error instanceof Error ? error.message : String(error)
|
|
1886
1841
|
});
|
|
1887
1842
|
}
|
|
1888
1843
|
if (endScalar < startScalar) {
|
|
1889
|
-
throw new
|
|
1890
|
-
dateRange: {
|
|
1844
|
+
throw new _ResourcePlanner.AvailabilityError('INVALID_DATE_RANGE', '日期范围必须是有效的 YYYY-MM-DD 且结束日期不早于开始日期', {
|
|
1845
|
+
dateRange: {
|
|
1846
|
+
startDate,
|
|
1847
|
+
endDate
|
|
1848
|
+
},
|
|
1891
1849
|
timezone
|
|
1892
1850
|
});
|
|
1893
1851
|
}
|
|
1894
|
-
const inclusiveDays = Math.trunc((endScalar - startScalar) / (24 * 60 * 60 *
|
|
1852
|
+
const inclusiveDays = Math.trunc((endScalar - startScalar) / (24 * 60 * 60 * 1000)) + 1;
|
|
1895
1853
|
if (inclusiveDays > MAX_AVAILABILITY_RANGE_DAYS) {
|
|
1896
|
-
throw new
|
|
1897
|
-
dateRange: {
|
|
1854
|
+
throw new _ResourcePlanner.AvailabilityError('RANGE_TOO_LARGE', '日期范围最多包含 90 个自然日', {
|
|
1855
|
+
dateRange: {
|
|
1856
|
+
startDate,
|
|
1857
|
+
endDate
|
|
1858
|
+
},
|
|
1898
1859
|
inclusiveDays
|
|
1899
1860
|
});
|
|
1900
1861
|
}
|
|
1901
|
-
return {
|
|
1862
|
+
return {
|
|
1863
|
+
startDate,
|
|
1864
|
+
endDate
|
|
1865
|
+
};
|
|
1902
1866
|
}
|
|
1903
1867
|
assertAvailabilityPrepareParams(params) {
|
|
1904
|
-
|
|
1905
|
-
assertAvailabilityCatalogProductIds(params.productIds, "prepare.productIds");
|
|
1868
|
+
assertAvailabilityCatalogProductIds(params.productIds, 'prepare.productIds');
|
|
1906
1869
|
const hasProductAnchor = Array.isArray(params.productIds) && params.productIds.length > 0;
|
|
1907
1870
|
const hasResourceAnchor = Array.isArray(params.resourceIds) && params.resourceIds.length > 0;
|
|
1908
|
-
const hasDateAnchor = Boolean(
|
|
1871
|
+
const hasDateAnchor = Boolean(params.dateRange?.startDate || params.dateRange?.endDate);
|
|
1909
1872
|
if (!hasProductAnchor && !hasResourceAnchor && !hasDateAnchor) {
|
|
1910
|
-
throw new
|
|
1873
|
+
throw new _ResourcePlanner.AvailabilityError('MISSING_ANCHOR', 'prepare 至少需要 productIds、resourceIds 或 dateRange 之一', {
|
|
1911
1874
|
params
|
|
1912
1875
|
});
|
|
1913
1876
|
}
|
|
1914
1877
|
if (!hasProductAnchor && !params.catalogScope) {
|
|
1915
|
-
throw new
|
|
1878
|
+
throw new _ResourcePlanner.AvailabilityError('MISSING_CATALOG_SCOPE', '缺少 productIds 时必须提供 catalogScope', {
|
|
1916
1879
|
params
|
|
1917
1880
|
});
|
|
1918
1881
|
}
|
|
1919
|
-
if (params.slotStepMinutes !==
|
|
1882
|
+
if (params.slotStepMinutes !== undefined) {
|
|
1920
1883
|
const step = Number(params.slotStepMinutes);
|
|
1921
1884
|
if (!Number.isInteger(step) || step <= 0) {
|
|
1922
|
-
throw new
|
|
1885
|
+
throw new _ResourcePlanner.AvailabilityError('INVALID_TARGET_FILTER', 'slotStepMinutes 必须是正整数', {
|
|
1923
1886
|
slotStepMinutes: params.slotStepMinutes
|
|
1924
1887
|
});
|
|
1925
1888
|
}
|
|
1926
1889
|
}
|
|
1927
1890
|
}
|
|
1928
1891
|
captureAvailabilityScheduleCatalog() {
|
|
1929
|
-
var _a, _b;
|
|
1930
1892
|
if (!this.store.schedule) {
|
|
1931
|
-
throw new Error(
|
|
1893
|
+
throw new Error('[UnifiedBookingSales] schedule 模块未初始化');
|
|
1932
1894
|
}
|
|
1933
|
-
this.availabilityScheduleCatalog = (0,
|
|
1934
|
-
((_b = (_a = this.store.schedule).getScheduleList) == null ? void 0 : _b.call(_a)) || []
|
|
1935
|
-
);
|
|
1895
|
+
this.availabilityScheduleCatalog = (0, _lodashEs.cloneDeep)(this.store.schedule.getScheduleList?.() || []);
|
|
1936
1896
|
}
|
|
1897
|
+
|
|
1937
1898
|
/** Returns the solution-lifetime schedule snapshot captured after initialization. */
|
|
1938
1899
|
getAvailabilityScheduleCatalog() {
|
|
1939
1900
|
if (this.availabilityScheduleCatalog === null) {
|
|
1940
|
-
throw new Error(
|
|
1901
|
+
throw new Error('[UnifiedBookingSales] schedule 快照未初始化');
|
|
1941
1902
|
}
|
|
1942
1903
|
return this.availabilityScheduleCatalog;
|
|
1943
1904
|
}
|
|
1944
1905
|
buildAvailabilityOperatingHoursSnapshot(params) {
|
|
1945
1906
|
const descriptor = params.prepare._availabilityOperatingHours || {
|
|
1946
|
-
source: params.prepare.businessHours ?
|
|
1907
|
+
source: params.prepare.businessHours ? 'prepare_business_hours' : 'default_business_hours',
|
|
1947
1908
|
scheduleIds: []
|
|
1948
1909
|
};
|
|
1949
|
-
if (descriptor.source !==
|
|
1950
|
-
const
|
|
1910
|
+
if (descriptor.source !== 'open_data_schedule') {
|
|
1911
|
+
const windows = dateRangeDays(params.dateRange).flatMap(date => {
|
|
1951
1912
|
const range = normalizeAvailabilityLocalDateTimeRange({
|
|
1952
1913
|
startAt: `${date}T${params.businessHours.startTime}:00`,
|
|
1953
1914
|
endAt: `${date}T${params.businessHours.endTime}:00`,
|
|
@@ -1964,18 +1925,14 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
1964
1925
|
source: descriptor.source,
|
|
1965
1926
|
scheduleIds: [],
|
|
1966
1927
|
missingScheduleIds: [],
|
|
1967
|
-
windows
|
|
1928
|
+
windows
|
|
1968
1929
|
};
|
|
1969
1930
|
}
|
|
1970
|
-
const scheduleMap = new Map(
|
|
1971
|
-
|
|
1972
|
-
);
|
|
1973
|
-
const
|
|
1974
|
-
const
|
|
1975
|
-
const missingScheduleIds = descriptor.scheduleIds.filter(
|
|
1976
|
-
(scheduleId) => !matchedScheduleIds.has(String(scheduleId))
|
|
1977
|
-
);
|
|
1978
|
-
const windows = dateRangeDays(params.dateRange).flatMap((date) => (0, import_getDateIsInSchedule.getScheduleStartEndTimePoints)(date, schedules).flatMap((slot) => {
|
|
1931
|
+
const scheduleMap = new Map(params.scheduleList.map(schedule => [String(schedule.id), schedule]));
|
|
1932
|
+
const schedules = descriptor.scheduleIds.map(scheduleId => scheduleMap.get(String(scheduleId))).filter(schedule => Boolean(schedule));
|
|
1933
|
+
const matchedScheduleIds = new Set(schedules.map(schedule => String(schedule.id)));
|
|
1934
|
+
const missingScheduleIds = descriptor.scheduleIds.filter(scheduleId => !matchedScheduleIds.has(String(scheduleId)));
|
|
1935
|
+
const windows = dateRangeDays(params.dateRange).flatMap(date => (0, _getDateIsInSchedule.getScheduleStartEndTimePoints)(date, schedules).flatMap(slot => {
|
|
1979
1936
|
const range = normalizeAvailabilityLocalDateTimeRange({
|
|
1980
1937
|
startAt: slot.start_at,
|
|
1981
1938
|
endAt: slot.end_at,
|
|
@@ -1985,7 +1942,7 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
1985
1942
|
return range ? [{
|
|
1986
1943
|
startAt: range.startAt,
|
|
1987
1944
|
endAt: range.endAt,
|
|
1988
|
-
source: `operating_schedule:${descriptor.scheduleIds.map(String).join(
|
|
1945
|
+
source: `operating_schedule:${descriptor.scheduleIds.map(String).join(',')}`
|
|
1989
1946
|
}] : [];
|
|
1990
1947
|
}));
|
|
1991
1948
|
return {
|
|
@@ -1996,8 +1953,10 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
1996
1953
|
};
|
|
1997
1954
|
}
|
|
1998
1955
|
async loadAvailabilityProducts(params) {
|
|
1999
|
-
|
|
2000
|
-
|
|
1956
|
+
const {
|
|
1957
|
+
prepare,
|
|
1958
|
+
dateRange
|
|
1959
|
+
} = params;
|
|
2001
1960
|
const explicitProductIds = prepare.productIds || [];
|
|
2002
1961
|
let rawProducts = [];
|
|
2003
1962
|
if (Array.isArray(prepare.rawProducts)) {
|
|
@@ -2008,21 +1967,18 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
2008
1967
|
product_ids: explicitProductIds,
|
|
2009
1968
|
schedule_date: dateRange.startDate
|
|
2010
1969
|
});
|
|
2011
|
-
} else if (
|
|
1970
|
+
} else if (prepare.catalogScope?.type === 'schedule') {
|
|
2012
1971
|
await this.loadScheduleAvailableDate({
|
|
2013
1972
|
startDate: dateRange.startDate,
|
|
2014
1973
|
endDate: dateRange.endDate,
|
|
2015
|
-
custom_page_id: Number(prepare.catalogScope.customPageId) ||
|
|
1974
|
+
custom_page_id: Number(prepare.catalogScope.customPageId) || undefined,
|
|
2016
1975
|
channel: prepare.catalogScope.channel
|
|
2017
1976
|
});
|
|
2018
1977
|
const dateSet = new Set(dateRangeDays(dateRange));
|
|
2019
|
-
const scheduleProductIds = (
|
|
2020
|
-
const productIds = uniqueAvailabilityIds([
|
|
2021
|
-
...scheduleProductIds,
|
|
2022
|
-
...((_e = (_d = this.store.schedule).getOtherProductsIds) == null ? void 0 : _e.call(_d)) || []
|
|
2023
|
-
]);
|
|
1978
|
+
const scheduleProductIds = (this.store.schedule.getAvailabilityScheduleDateList?.() || []).filter(item => dateSet.has(item.date)).flatMap(item => item.product_ids || []);
|
|
1979
|
+
const productIds = uniqueAvailabilityIds([...scheduleProductIds, ...(this.store.schedule.getOtherProductsIds?.() || [])]);
|
|
2024
1980
|
if (productIds.length > 0) {
|
|
2025
|
-
assertAvailabilityCatalogProductIds(productIds,
|
|
1981
|
+
assertAvailabilityCatalogProductIds(productIds, 'prepare.catalogScope.scheduleProductIds');
|
|
2026
1982
|
rawProducts = await this.loadProducts({
|
|
2027
1983
|
product_ids: productIds,
|
|
2028
1984
|
schedule_date: dateRange.startDate
|
|
@@ -2030,36 +1986,27 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
2030
1986
|
} else {
|
|
2031
1987
|
this.unifiedProductCatalog = [];
|
|
2032
1988
|
}
|
|
2033
|
-
} else if (
|
|
1989
|
+
} else if (prepare.catalogScope?.type === 'productList') {
|
|
2034
1990
|
rawProducts = await this.loadProducts({
|
|
2035
1991
|
...prepare.catalogScope.params,
|
|
2036
1992
|
schedule_date: prepare.catalogScope.params.schedule_date || dateRange.startDate
|
|
2037
1993
|
});
|
|
2038
1994
|
}
|
|
2039
|
-
const normalized = rawProducts.map(
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
this.getAvailabilityRuntimeOffsetMinutes()
|
|
2043
|
-
));
|
|
2044
|
-
if (!explicitProductIds.length && ((_g = prepare.resourceIds) == null ? void 0 : _g.length)) {
|
|
2045
|
-
return normalized.filter((product) => {
|
|
2046
|
-
var _a2;
|
|
2047
|
-
return (_a2 = product.requirementGroups) == null ? void 0 : _a2.some((group) => {
|
|
2048
|
-
var _a3;
|
|
2049
|
-
return (_a3 = group.resourceIds) == null ? void 0 : _a3.some((resourceId) => prepare.resourceIds.some((id) => sameAvailabilityId(id, resourceId)));
|
|
2050
|
-
});
|
|
2051
|
-
});
|
|
1995
|
+
const normalized = rawProducts.map(product => normalizeProductForAvailability(product, params.scheduleList, this.getAvailabilityRuntimeOffsetMinutes()));
|
|
1996
|
+
if (!explicitProductIds.length && prepare.resourceIds?.length) {
|
|
1997
|
+
return normalized.filter(product => product.requirementGroups?.some(group => group.resourceIds?.some(resourceId => prepare.resourceIds.some(id => sameAvailabilityId(id, resourceId)))));
|
|
2052
1998
|
}
|
|
2053
1999
|
const productIdSet = explicitProductIds.length ? new Set(explicitProductIds.map(String)) : null;
|
|
2054
|
-
return productIdSet ? normalized.filter(
|
|
2000
|
+
return productIdSet ? normalized.filter(product => productIdSet.has(String(product.id))) : normalized;
|
|
2055
2001
|
}
|
|
2002
|
+
|
|
2056
2003
|
/**
|
|
2057
2004
|
* Fetches resource metadata plus flat occupied events. Work windows are intentionally derived
|
|
2058
2005
|
* locally in buildAvailabilityResourcesFromV2 from /schedule, rather than requested from the legacy
|
|
2059
2006
|
* resource dates endpoint.
|
|
2060
2007
|
*/
|
|
2061
2008
|
async fetchAvailabilityResourcesV2(params) {
|
|
2062
|
-
const response = await this.request.get(
|
|
2009
|
+
const response = await this.request.get('/schedule/resource/list/v2', {
|
|
2063
2010
|
start_date: params.dateRange.startDate,
|
|
2064
2011
|
end_date: params.dateRange.endDate,
|
|
2065
2012
|
resource_ids: params.resourceIds,
|
|
@@ -2067,17 +2014,14 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
2067
2014
|
}, {
|
|
2068
2015
|
useCache: false
|
|
2069
2016
|
});
|
|
2070
|
-
const data = response
|
|
2071
|
-
if (Array.isArray(data))
|
|
2072
|
-
|
|
2073
|
-
if (Array.isArray(data == null ? void 0 : data.list))
|
|
2074
|
-
return data.list;
|
|
2017
|
+
const data = response?.data;
|
|
2018
|
+
if (Array.isArray(data)) return data;
|
|
2019
|
+
if (Array.isArray(data?.list)) return data.list;
|
|
2075
2020
|
return [];
|
|
2076
2021
|
}
|
|
2077
2022
|
async fetchAndPublishAvailabilityResourcesV2(params) {
|
|
2078
2023
|
const normalizedResourceIds = this.normalizeAvailabilityIdList(params.resourceIds) || [];
|
|
2079
|
-
if (normalizedResourceIds.length === 0)
|
|
2080
|
-
return [];
|
|
2024
|
+
if (normalizedResourceIds.length === 0) return [];
|
|
2081
2025
|
const scopeKey = this.buildAvailabilityRemoteResourceScopeKey(params.timezone);
|
|
2082
2026
|
const cacheParams = {
|
|
2083
2027
|
scopeKey,
|
|
@@ -2091,8 +2035,8 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
2091
2035
|
timezone: params.timezone,
|
|
2092
2036
|
dateRange: params.dateRange,
|
|
2093
2037
|
resourceIds: normalizedResourceIds
|
|
2094
|
-
}).catch(
|
|
2095
|
-
console.warn(
|
|
2038
|
+
}).catch(error => {
|
|
2039
|
+
console.warn('[UnifiedBookingSales] Remote occupancy background refresh failed', error);
|
|
2096
2040
|
});
|
|
2097
2041
|
}
|
|
2098
2042
|
return cachedRawResources;
|
|
@@ -2105,8 +2049,7 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
2105
2049
|
}
|
|
2106
2050
|
async refreshAvailabilityRemoteResourceQuery(params) {
|
|
2107
2051
|
const normalizedResourceIds = this.normalizeAvailabilityIdList(params.resourceIds) || [];
|
|
2108
|
-
if (normalizedResourceIds.length === 0)
|
|
2109
|
-
return [];
|
|
2052
|
+
if (normalizedResourceIds.length === 0) return [];
|
|
2110
2053
|
const queryKey = this.buildAvailabilityRemoteResourceQueryKey({
|
|
2111
2054
|
timezone: params.timezone,
|
|
2112
2055
|
dateRange: params.dateRange,
|
|
@@ -2114,7 +2057,7 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
2114
2057
|
});
|
|
2115
2058
|
const epoch = this.availabilityRemoteResourceEpoch;
|
|
2116
2059
|
const existing = this.availabilityRemoteResourceQueryInFlight.get(queryKey);
|
|
2117
|
-
if (
|
|
2060
|
+
if (existing?.epoch === epoch) {
|
|
2118
2061
|
return await existing.promise;
|
|
2119
2062
|
}
|
|
2120
2063
|
this.availabilityRemoteResourceQuerySequence += 1;
|
|
@@ -2142,7 +2085,7 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
2142
2085
|
return rawResources;
|
|
2143
2086
|
} finally {
|
|
2144
2087
|
const current = this.availabilityRemoteResourceQueryInFlight.get(queryKey);
|
|
2145
|
-
if (
|
|
2088
|
+
if (current?.promise === promise) {
|
|
2146
2089
|
this.availabilityRemoteResourceQueryInFlight.delete(queryKey);
|
|
2147
2090
|
}
|
|
2148
2091
|
}
|
|
@@ -2163,7 +2106,7 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
2163
2106
|
fixedOffsetMinutes: this.getAvailabilityRuntimeOffsetMinutes()
|
|
2164
2107
|
});
|
|
2165
2108
|
}
|
|
2166
|
-
if (params.fetchPolicy ===
|
|
2109
|
+
if (params.fetchPolicy === 'cache_only') {
|
|
2167
2110
|
const cachedRawResources = this.availabilityRemoteOccupancyCache.read({
|
|
2168
2111
|
scopeKey: this.buildAvailabilityRemoteResourceScopeKey(params.timezone),
|
|
2169
2112
|
resourceIds,
|
|
@@ -2177,7 +2120,7 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
2177
2120
|
fixedOffsetMinutes: this.getAvailabilityRuntimeOffsetMinutes()
|
|
2178
2121
|
});
|
|
2179
2122
|
}
|
|
2180
|
-
return params.fallbackResources ? (0,
|
|
2123
|
+
return params.fallbackResources ? (0, _lodashEs.cloneDeep)(params.fallbackResources) : {
|
|
2181
2124
|
resources: [],
|
|
2182
2125
|
resourceOccupancies: []
|
|
2183
2126
|
};
|
|
@@ -2196,44 +2139,46 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
2196
2139
|
});
|
|
2197
2140
|
}
|
|
2198
2141
|
resolveEditingOccupancyExclusions(tempOrder, productLineUids = []) {
|
|
2199
|
-
const productUids =
|
|
2200
|
-
const bookingUids =
|
|
2201
|
-
const scheduleEventIds =
|
|
2202
|
-
if (!productLineUids.length)
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2142
|
+
const productUids = new Set();
|
|
2143
|
+
const bookingUids = new Set();
|
|
2144
|
+
const scheduleEventIds = new Set();
|
|
2145
|
+
if (!productLineUids.length) return {
|
|
2146
|
+
productUids,
|
|
2147
|
+
bookingUids,
|
|
2148
|
+
scheduleEventIds
|
|
2149
|
+
};
|
|
2150
|
+
const products = Array.isArray(tempOrder?.products) ? tempOrder.products : [];
|
|
2151
|
+
const bookings = Array.isArray(tempOrder?.bookings) ? tempOrder.bookings : [];
|
|
2152
|
+
productLineUids.forEach(requestedUid => {
|
|
2153
|
+
const product = products.find(line => getProductLineUid(line) === String(requestedUid));
|
|
2209
2154
|
if (!product) {
|
|
2210
|
-
throw new
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
{ productLineUid: requestedUid }
|
|
2214
|
-
);
|
|
2155
|
+
throw new _ResourcePlanner.AvailabilityError('SELF_EXCLUSION_RESOLVE_FAILED', '无法解析编辑商品行,不能安全排除自身占用', {
|
|
2156
|
+
productLineUid: requestedUid
|
|
2157
|
+
});
|
|
2215
2158
|
}
|
|
2216
2159
|
const productUid = getProductLineUid(product);
|
|
2217
|
-
const linkedBookingUid = String(product.booking_uid ??
|
|
2218
|
-
const linkedBookings = bookings.filter(
|
|
2160
|
+
const linkedBookingUid = String(product.booking_uid ?? product.metadata?.booking_uid ?? '');
|
|
2161
|
+
const linkedBookings = bookings.filter(booking => linkedBookingUid && getBookingUid(booking) === linkedBookingUid || productUid && String(booking.product_uid ?? '') === productUid);
|
|
2219
2162
|
if (!productUid || !linkedBookings.length) {
|
|
2220
|
-
throw new
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
);
|
|
2163
|
+
throw new _ResourcePlanner.AvailabilityError('SELF_EXCLUSION_RESOLVE_FAILED', '编辑商品行缺少可验证的 booking 关联', {
|
|
2164
|
+
productLineUid: requestedUid,
|
|
2165
|
+
linkedBookingUid
|
|
2166
|
+
});
|
|
2225
2167
|
}
|
|
2226
2168
|
productUids.add(productUid);
|
|
2227
|
-
linkedBookings.forEach(
|
|
2169
|
+
linkedBookings.forEach(booking => {
|
|
2228
2170
|
const bookingUid = getBookingUid(booking);
|
|
2229
|
-
if (bookingUid)
|
|
2230
|
-
bookingUids.add(bookingUid);
|
|
2171
|
+
if (bookingUid) bookingUids.add(bookingUid);
|
|
2231
2172
|
if (booking.schedule_event_id != null) {
|
|
2232
2173
|
scheduleEventIds.add(String(booking.schedule_event_id));
|
|
2233
2174
|
}
|
|
2234
2175
|
});
|
|
2235
2176
|
});
|
|
2236
|
-
return {
|
|
2177
|
+
return {
|
|
2178
|
+
productUids,
|
|
2179
|
+
bookingUids,
|
|
2180
|
+
scheduleEventIds
|
|
2181
|
+
};
|
|
2237
2182
|
}
|
|
2238
2183
|
occupancyIsExcluded(occupancy, exclusions) {
|
|
2239
2184
|
if (occupancy.scheduleEventId != null) {
|
|
@@ -2248,37 +2193,37 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
2248
2193
|
return false;
|
|
2249
2194
|
}
|
|
2250
2195
|
collectCartResourceOccupancies(tempOrder, exclusions) {
|
|
2251
|
-
const bookings = Array.isArray(tempOrder
|
|
2252
|
-
return bookings.flatMap(
|
|
2253
|
-
var _a;
|
|
2196
|
+
const bookings = Array.isArray(tempOrder?.bookings) ? tempOrder.bookings : [];
|
|
2197
|
+
return bookings.flatMap(booking => {
|
|
2254
2198
|
const bookingUid = getBookingUid(booking);
|
|
2255
|
-
const productUid = String(booking.product_uid ??
|
|
2199
|
+
const productUid = String(booking.product_uid ?? booking.metadata?.product_uid ?? '');
|
|
2256
2200
|
const scheduleEventId = toNumberId(booking.schedule_event_id);
|
|
2257
|
-
const startAt = booking.start_at || `${booking.start_date ||
|
|
2258
|
-
const endAt = booking.end_at || `${booking.end_date || booking.start_date ||
|
|
2201
|
+
const startAt = booking.start_at || `${booking.start_date || ''} ${booking.start_time || ''}`.trim();
|
|
2202
|
+
const endAt = booking.end_at || `${booking.end_date || booking.start_date || ''} ${booking.end_time || ''}`.trim();
|
|
2259
2203
|
const resources = Array.isArray(booking.resources) ? booking.resources : [];
|
|
2260
|
-
return resources.flatMap(
|
|
2204
|
+
return resources.flatMap(resource => {
|
|
2261
2205
|
const resourceId = resource.resource_id ?? resource.relation_id ?? resource.id;
|
|
2262
|
-
if (resourceId == null || !startAt || !endAt)
|
|
2263
|
-
return [];
|
|
2206
|
+
if (resourceId == null || !startAt || !endAt) return [];
|
|
2264
2207
|
const range = normalizeAvailabilityLocalDateTimeRange({
|
|
2265
2208
|
startAt,
|
|
2266
2209
|
endAt,
|
|
2267
2210
|
fixedOffsetMinutes: this.getAvailabilityRuntimeOffsetMinutes(),
|
|
2268
2211
|
rollOverEnd: false
|
|
2269
2212
|
});
|
|
2270
|
-
if (!range)
|
|
2271
|
-
return [];
|
|
2213
|
+
if (!range) return [];
|
|
2272
2214
|
const occupancy = {
|
|
2273
2215
|
resourceId,
|
|
2274
2216
|
startAt: range.startAt,
|
|
2275
2217
|
endAt: range.endAt,
|
|
2276
2218
|
pax: Math.max(1, Number(resource.capacity ?? booking.capacity ?? 1) || 1),
|
|
2277
|
-
source:
|
|
2219
|
+
source: 'cart',
|
|
2278
2220
|
scheduleEventId,
|
|
2279
|
-
bookingUid: bookingUid ||
|
|
2280
|
-
productUid: productUid ||
|
|
2281
|
-
raw: {
|
|
2221
|
+
bookingUid: bookingUid || undefined,
|
|
2222
|
+
productUid: productUid || undefined,
|
|
2223
|
+
raw: {
|
|
2224
|
+
booking,
|
|
2225
|
+
resource
|
|
2226
|
+
}
|
|
2282
2227
|
};
|
|
2283
2228
|
return this.occupancyIsExcluded(occupancy, exclusions) ? [] : [occupancy];
|
|
2284
2229
|
});
|
|
@@ -2290,30 +2235,22 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
2290
2235
|
getAvailabilityContextEntry(contextId) {
|
|
2291
2236
|
const context = this.availabilityContextRegistry.get(contextId);
|
|
2292
2237
|
if (!context) {
|
|
2293
|
-
throw this.createAvailabilityFacadeError(
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
contextId,
|
|
2298
|
-
lifecycleGeneration: this.availabilityLifecycleGeneration
|
|
2299
|
-
}
|
|
2300
|
-
);
|
|
2238
|
+
throw this.createAvailabilityFacadeError('AVAILABILITY_CONTEXT_NOT_FOUND', 'availability context 不存在或已失效,请重新 prepare', {
|
|
2239
|
+
contextId,
|
|
2240
|
+
lifecycleGeneration: this.availabilityLifecycleGeneration
|
|
2241
|
+
});
|
|
2301
2242
|
}
|
|
2302
2243
|
this.touchAvailabilityContext(context);
|
|
2303
2244
|
return context;
|
|
2304
2245
|
}
|
|
2305
2246
|
getAvailabilityProjectionByKey(projectionKey) {
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
return null;
|
|
2309
|
-
return ((_a = this.availabilityProjectionPool.get(projectionKey)) == null ? void 0 : _a.projection) || null;
|
|
2247
|
+
if (!projectionKey) return null;
|
|
2248
|
+
return this.availabilityProjectionPool.get(projectionKey)?.projection || null;
|
|
2310
2249
|
}
|
|
2311
2250
|
getActiveAvailabilityProjectionEntry(context) {
|
|
2312
|
-
if (!context.activeProjectionKey)
|
|
2313
|
-
return null;
|
|
2251
|
+
if (!context.activeProjectionKey) return null;
|
|
2314
2252
|
const entry = this.availabilityProjectionPool.get(context.activeProjectionKey);
|
|
2315
|
-
if (!entry)
|
|
2316
|
-
return null;
|
|
2253
|
+
if (!entry) return null;
|
|
2317
2254
|
return this.touchAvailabilityProjection(entry);
|
|
2318
2255
|
}
|
|
2319
2256
|
decorateAvailabilityCandidate(contextId, contextVersion, candidate) {
|
|
@@ -2325,38 +2262,30 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
2325
2262
|
}
|
|
2326
2263
|
assertAvailabilityCandidateFreshness(context, candidate) {
|
|
2327
2264
|
if (candidate.availabilityContextId !== context.contextId) {
|
|
2328
|
-
throw this.createAvailabilityFacadeError(
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
candidateContextId: candidate.availabilityContextId,
|
|
2333
|
-
contextId: context.contextId
|
|
2334
|
-
}
|
|
2335
|
-
);
|
|
2265
|
+
throw this.createAvailabilityFacadeError('AVAILABILITY_SELECTION_STALE', '候选与 availability context 不匹配,请重新获取候选', {
|
|
2266
|
+
candidateContextId: candidate.availabilityContextId,
|
|
2267
|
+
contextId: context.contextId
|
|
2268
|
+
});
|
|
2336
2269
|
}
|
|
2337
2270
|
const currentContextVersion = context.activeVersion;
|
|
2338
2271
|
if (candidate.availabilityContextVersion !== currentContextVersion) {
|
|
2339
|
-
throw this.createAvailabilityFacadeError(
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
currentContextVersion,
|
|
2346
|
-
snapshotId: context.lastSnapshotId
|
|
2347
|
-
}
|
|
2348
|
-
);
|
|
2272
|
+
throw this.createAvailabilityFacadeError('AVAILABILITY_SELECTION_STALE', '候选已过期,请重新获取最新时间段', {
|
|
2273
|
+
contextId: context.contextId,
|
|
2274
|
+
candidateContextVersion: candidate.availabilityContextVersion,
|
|
2275
|
+
currentContextVersion,
|
|
2276
|
+
snapshotId: context.lastSnapshotId
|
|
2277
|
+
});
|
|
2349
2278
|
}
|
|
2350
2279
|
}
|
|
2351
2280
|
assertContextualQueryFreshness(context, request) {
|
|
2352
|
-
if (request.target ===
|
|
2353
|
-
request.candidates.forEach(
|
|
2281
|
+
if (request.target === 'time') {
|
|
2282
|
+
request.candidates.forEach(candidate => {
|
|
2354
2283
|
this.assertAvailabilityCandidateFreshness(context, candidate);
|
|
2355
2284
|
});
|
|
2356
2285
|
return;
|
|
2357
2286
|
}
|
|
2358
|
-
if (request.target ===
|
|
2359
|
-
request.selectedRanges.forEach(
|
|
2287
|
+
if (request.target === 'resource') {
|
|
2288
|
+
request.selectedRanges.forEach(candidate => {
|
|
2360
2289
|
this.assertAvailabilityCandidateFreshness(context, candidate);
|
|
2361
2290
|
});
|
|
2362
2291
|
}
|
|
@@ -2374,7 +2303,7 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
2374
2303
|
preparedAt: params.projection.meta.preparedAt,
|
|
2375
2304
|
snapshotId: params.projection.meta.snapshotId
|
|
2376
2305
|
};
|
|
2377
|
-
if (
|
|
2306
|
+
if (previousContext?.activeProjectionKey && previousContext.activeProjectionKey !== projectionKey) {
|
|
2378
2307
|
this.removeAvailabilityProjection(previousContext.activeProjectionKey);
|
|
2379
2308
|
}
|
|
2380
2309
|
this.availabilityProjectionPool.set(projectionKey, entry);
|
|
@@ -2385,13 +2314,13 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
2385
2314
|
prepareKey: params.prepareKey,
|
|
2386
2315
|
activeVersion: contextVersion,
|
|
2387
2316
|
activeProjectionKey: projectionKey,
|
|
2388
|
-
createdAt:
|
|
2389
|
-
lastAccessedAt:
|
|
2317
|
+
createdAt: previousContext?.createdAt ?? params.projection.meta.preparedAt,
|
|
2318
|
+
lastAccessedAt: new Date().toISOString(),
|
|
2390
2319
|
lastKnownOrderRevision: params.orderRevision,
|
|
2391
2320
|
lastPreparedAt: params.projection.meta.preparedAt,
|
|
2392
2321
|
lastSnapshotId: params.projection.meta.snapshotId,
|
|
2393
|
-
durationCandidateAnchorAt:
|
|
2394
|
-
refreshNonce:
|
|
2322
|
+
durationCandidateAnchorAt: previousContext?.durationCandidateAnchorAt ?? this.getAvailabilityDurationCandidateAnchorAt(params.projection),
|
|
2323
|
+
refreshNonce: previousContext?.refreshNonce ?? 0,
|
|
2395
2324
|
lifecycleGeneration: this.availabilityLifecycleGeneration
|
|
2396
2325
|
};
|
|
2397
2326
|
this.touchAvailabilityContext(nextContext);
|
|
@@ -2404,7 +2333,7 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
2404
2333
|
const dateRange = normalizedPrepareParams.dateRange;
|
|
2405
2334
|
const slotStepMinutes = normalizedPrepareParams.slotStepMinutes || DEFAULT_AVAILABILITY_SLOT_STEP_MINUTES;
|
|
2406
2335
|
const businessHours = normalizedPrepareParams.businessHours || DEFAULT_AVAILABILITY_BUSINESS_HOURS;
|
|
2407
|
-
const mode = normalizedPrepareParams.mode ||
|
|
2336
|
+
const mode = normalizedPrepareParams.mode || 'appointment';
|
|
2408
2337
|
const scheduleList = this.getAvailabilityScheduleCatalog();
|
|
2409
2338
|
const operatingHours = this.buildAvailabilityOperatingHoursSnapshot({
|
|
2410
2339
|
prepare: normalizedPrepareParams,
|
|
@@ -2424,39 +2353,32 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
2424
2353
|
products,
|
|
2425
2354
|
rawResources: normalizedPrepareParams.rawResources,
|
|
2426
2355
|
scheduleList,
|
|
2427
|
-
fetchPolicy: options.resourceFetchPolicy ||
|
|
2356
|
+
fetchPolicy: options.resourceFetchPolicy || 'default',
|
|
2428
2357
|
fallbackResources: options.fallbackResources
|
|
2429
2358
|
});
|
|
2430
2359
|
const tempOrder = this.getTempOrder();
|
|
2431
|
-
const exclusions = this.resolveEditingOccupancyExclusions(
|
|
2432
|
-
|
|
2433
|
-
normalizedPrepareParams.editingProductLineUids
|
|
2434
|
-
);
|
|
2435
|
-
const injectedRawOccupancies = (normalizedPrepareParams.rawOccupancies || []).flatMap((occupancy) => {
|
|
2360
|
+
const exclusions = this.resolveEditingOccupancyExclusions(tempOrder, normalizedPrepareParams.editingProductLineUids);
|
|
2361
|
+
const injectedRawOccupancies = (normalizedPrepareParams.rawOccupancies || []).flatMap(occupancy => {
|
|
2436
2362
|
const range = normalizeAvailabilityLocalDateTimeRange({
|
|
2437
2363
|
startAt: occupancy.startAt,
|
|
2438
2364
|
endAt: occupancy.endAt,
|
|
2439
2365
|
fixedOffsetMinutes: this.getAvailabilityRuntimeOffsetMinutes(),
|
|
2440
2366
|
rollOverEnd: false
|
|
2441
2367
|
});
|
|
2442
|
-
if (!range)
|
|
2443
|
-
return [];
|
|
2368
|
+
if (!range) return [];
|
|
2444
2369
|
return [{
|
|
2445
2370
|
...occupancy,
|
|
2446
2371
|
startAt: range.startAt,
|
|
2447
2372
|
endAt: range.endAt
|
|
2448
2373
|
}];
|
|
2449
2374
|
});
|
|
2450
|
-
const remoteOccupancies = [
|
|
2451
|
-
...loadedResources.resourceOccupancies,
|
|
2452
|
-
...loadedResources.includesInjectedRawOccupancies ? [] : injectedRawOccupancies
|
|
2453
|
-
].filter((occupancy) => !this.occupancyIsExcluded(occupancy, exclusions));
|
|
2375
|
+
const remoteOccupancies = [...loadedResources.resourceOccupancies, ...(loadedResources.includesInjectedRawOccupancies ? [] : injectedRawOccupancies)].filter(occupancy => !this.occupancyIsExcluded(occupancy, exclusions));
|
|
2454
2376
|
const cartOccupancies = this.collectCartResourceOccupancies(tempOrder, exclusions);
|
|
2455
2377
|
const preparedAt = this.getAvailabilityRuntimeDateTime();
|
|
2456
2378
|
const durationCandidateAnchorAt = options.durationCandidateAnchorAt || preparedAt;
|
|
2457
2379
|
this.availabilityProjectionSequence += 1;
|
|
2458
2380
|
const orderRevision = this.getCurrentOrderRevision();
|
|
2459
|
-
const projection = (0,
|
|
2381
|
+
const projection = (0, _ResourcePlanner.createAvailabilityProjection)({
|
|
2460
2382
|
products,
|
|
2461
2383
|
resources: loadedResources.resources,
|
|
2462
2384
|
resourceOccupancies: [...remoteOccupancies, ...cartOccupancies],
|
|
@@ -2487,38 +2409,37 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
2487
2409
|
async getSharedPreparedAvailabilitySnapshot(params) {
|
|
2488
2410
|
const lifecycleGeneration = this.availabilityLifecycleGeneration;
|
|
2489
2411
|
const normalizedParams = this.normalizeAvailabilityPrepareDescriptor(params);
|
|
2490
|
-
const hasRawInputs = params.rawProducts !==
|
|
2412
|
+
const hasRawInputs = params.rawProducts !== undefined || params.rawResources !== undefined || params.rawOccupancies !== undefined;
|
|
2491
2413
|
const prepareKey = hasRawInputs ? JSON.stringify(stableValue({
|
|
2492
2414
|
scope: this.getAvailabilityScopeDescriptor(),
|
|
2493
2415
|
prepare: {
|
|
2494
2416
|
...normalizedParams,
|
|
2495
|
-
rawProducts:
|
|
2496
|
-
rawResources:
|
|
2497
|
-
rawOccupancies:
|
|
2417
|
+
rawProducts: undefined,
|
|
2418
|
+
rawResources: undefined,
|
|
2419
|
+
rawOccupancies: undefined
|
|
2498
2420
|
},
|
|
2499
2421
|
rawInputs: {
|
|
2500
|
-
products: params.rawProducts !==
|
|
2501
|
-
resources: params.rawResources !==
|
|
2502
|
-
occupancies: params.rawOccupancies !==
|
|
2422
|
+
products: params.rawProducts !== undefined,
|
|
2423
|
+
resources: params.rawResources !== undefined,
|
|
2424
|
+
occupancies: params.rawOccupancies !== undefined
|
|
2503
2425
|
}
|
|
2504
2426
|
})) : this.buildAvailabilityPrepareKey(normalizedParams);
|
|
2505
2427
|
if (hasRawInputs) {
|
|
2506
2428
|
const snapshot = await this.buildAvailabilityProjectionSnapshot(normalizedParams);
|
|
2507
2429
|
if (lifecycleGeneration !== this.availabilityLifecycleGeneration) {
|
|
2508
|
-
throw this.createAvailabilityFacadeError(
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
expectedLifecycleGeneration: lifecycleGeneration,
|
|
2514
|
-
currentLifecycleGeneration: this.availabilityLifecycleGeneration
|
|
2515
|
-
}
|
|
2516
|
-
);
|
|
2430
|
+
throw this.createAvailabilityFacadeError('AVAILABILITY_CONTEXT_NOT_FOUND', 'prepare 期间业务作用域或实例生命周期已变化,请重新 prepare', {
|
|
2431
|
+
prepareKey,
|
|
2432
|
+
expectedLifecycleGeneration: lifecycleGeneration,
|
|
2433
|
+
currentLifecycleGeneration: this.availabilityLifecycleGeneration
|
|
2434
|
+
});
|
|
2517
2435
|
}
|
|
2518
|
-
return {
|
|
2436
|
+
return {
|
|
2437
|
+
prepareKey,
|
|
2438
|
+
snapshot
|
|
2439
|
+
};
|
|
2519
2440
|
}
|
|
2520
2441
|
const existing = this.availabilityPrepareInFlight.get(prepareKey);
|
|
2521
|
-
const promise =
|
|
2442
|
+
const promise = existing?.lifecycleGeneration === lifecycleGeneration ? existing.promise : this.buildAvailabilityProjectionSnapshot(normalizedParams);
|
|
2522
2443
|
if (!existing || existing.lifecycleGeneration !== lifecycleGeneration) {
|
|
2523
2444
|
this.availabilityPrepareInFlight.set(prepareKey, {
|
|
2524
2445
|
lifecycleGeneration,
|
|
@@ -2529,42 +2450,36 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
2529
2450
|
try {
|
|
2530
2451
|
const snapshot = await promise;
|
|
2531
2452
|
if (lifecycleGeneration !== this.availabilityLifecycleGeneration) {
|
|
2532
|
-
throw this.createAvailabilityFacadeError(
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
expectedLifecycleGeneration: lifecycleGeneration,
|
|
2538
|
-
currentLifecycleGeneration: this.availabilityLifecycleGeneration
|
|
2539
|
-
}
|
|
2540
|
-
);
|
|
2453
|
+
throw this.createAvailabilityFacadeError('AVAILABILITY_CONTEXT_NOT_FOUND', 'prepare 期间业务作用域或实例生命周期已变化,请重新 prepare', {
|
|
2454
|
+
prepareKey,
|
|
2455
|
+
expectedLifecycleGeneration: lifecycleGeneration,
|
|
2456
|
+
currentLifecycleGeneration: this.availabilityLifecycleGeneration
|
|
2457
|
+
});
|
|
2541
2458
|
}
|
|
2542
|
-
return {
|
|
2459
|
+
return {
|
|
2460
|
+
prepareKey,
|
|
2461
|
+
snapshot
|
|
2462
|
+
};
|
|
2543
2463
|
} finally {
|
|
2544
2464
|
const current = this.availabilityPrepareInFlight.get(prepareKey);
|
|
2545
|
-
if (
|
|
2465
|
+
if (current?.promise === promise) {
|
|
2546
2466
|
this.availabilityPrepareInFlight.delete(prepareKey);
|
|
2547
2467
|
}
|
|
2548
2468
|
}
|
|
2549
2469
|
}
|
|
2550
2470
|
async refreshAvailabilityContextProjection(params) {
|
|
2551
2471
|
const existingInFlight = this.availabilityProjectionRefreshInFlight.get(params.contextId);
|
|
2552
|
-
if (existingInFlight)
|
|
2553
|
-
return await existingInFlight;
|
|
2472
|
+
if (existingInFlight) return await existingInFlight;
|
|
2554
2473
|
const context = this.getAvailabilityContextEntry(params.contextId);
|
|
2555
2474
|
const expectedVersion = context.activeVersion;
|
|
2556
2475
|
const expectedRefreshNonce = context.refreshNonce;
|
|
2557
2476
|
const expectedLifecycleGeneration = this.availabilityLifecycleGeneration;
|
|
2558
2477
|
const refreshPromise = (async () => {
|
|
2559
|
-
const snapshot = await this.buildAvailabilityProjectionSnapshot(
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
durationCandidateAnchorAt: context.durationCandidateAnchorAt
|
|
2563
|
-
}
|
|
2564
|
-
);
|
|
2478
|
+
const snapshot = await this.buildAvailabilityProjectionSnapshot(this.cloneAvailabilityPrepareParams(context.prepareParams), {
|
|
2479
|
+
durationCandidateAnchorAt: context.durationCandidateAnchorAt
|
|
2480
|
+
});
|
|
2565
2481
|
const currentContext = this.availabilityContextRegistry.get(params.contextId);
|
|
2566
|
-
if (!currentContext)
|
|
2567
|
-
return null;
|
|
2482
|
+
if (!currentContext) return null;
|
|
2568
2483
|
if (expectedLifecycleGeneration !== this.availabilityLifecycleGeneration || currentContext.activeVersion !== expectedVersion || currentContext.refreshNonce !== expectedRefreshNonce || currentContext.lifecycleGeneration !== expectedLifecycleGeneration) {
|
|
2569
2484
|
return null;
|
|
2570
2485
|
}
|
|
@@ -2577,7 +2492,7 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
2577
2492
|
incrementVersion: true
|
|
2578
2493
|
});
|
|
2579
2494
|
await this.emitAvailabilityDiagnostic({
|
|
2580
|
-
action:
|
|
2495
|
+
action: 'refresh',
|
|
2581
2496
|
contextId: currentContext.contextId,
|
|
2582
2497
|
contextVersion: projectionEntry.contextVersion,
|
|
2583
2498
|
projection: projectionEntry.projection,
|
|
@@ -2605,7 +2520,11 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
2605
2520
|
const currentOrderRevision = this.getCurrentOrderRevision();
|
|
2606
2521
|
const projectionEntry = this.getActiveAvailabilityProjectionEntry(context);
|
|
2607
2522
|
if (projectionEntry && projectionEntry.orderRevision === currentOrderRevision) {
|
|
2608
|
-
return {
|
|
2523
|
+
return {
|
|
2524
|
+
context,
|
|
2525
|
+
projectionEntry,
|
|
2526
|
+
refreshed: false
|
|
2527
|
+
};
|
|
2609
2528
|
}
|
|
2610
2529
|
const refreshedProjectionEntry = await this.refreshAvailabilityContextProjection({
|
|
2611
2530
|
contextId,
|
|
@@ -2626,7 +2545,7 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
2626
2545
|
refreshed: true
|
|
2627
2546
|
};
|
|
2628
2547
|
}
|
|
2629
|
-
throw new
|
|
2548
|
+
throw new _ResourcePlanner.AvailabilityError('STALE_PROJECTION', 'availability projection 刷新期间购物车持续变化,请重新 prepare', {
|
|
2630
2549
|
contextId,
|
|
2631
2550
|
reason
|
|
2632
2551
|
});
|
|
@@ -2635,50 +2554,52 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
2635
2554
|
if (this.availabilityCommitLocks.has(contextId)) {
|
|
2636
2555
|
const waitingContext = this.availabilityContextRegistry.get(contextId);
|
|
2637
2556
|
await this.emitAvailabilityDiagnostic({
|
|
2638
|
-
action:
|
|
2557
|
+
action: 'commit_lock_wait',
|
|
2639
2558
|
contextId,
|
|
2640
|
-
contextVersion:
|
|
2641
|
-
projection: this.getAvailabilityProjectionByKey(waitingContext
|
|
2642
|
-
snapshotId: waitingContext
|
|
2643
|
-
orderRevision: waitingContext
|
|
2644
|
-
reason:
|
|
2559
|
+
contextVersion: waitingContext?.activeVersion ?? null,
|
|
2560
|
+
projection: this.getAvailabilityProjectionByKey(waitingContext?.activeProjectionKey),
|
|
2561
|
+
snapshotId: waitingContext?.lastSnapshotId,
|
|
2562
|
+
orderRevision: waitingContext?.lastKnownOrderRevision,
|
|
2563
|
+
reason: 'AVAILABILITY_COMMIT_IN_PROGRESS'
|
|
2564
|
+
});
|
|
2565
|
+
throw this.createAvailabilityFacadeError('AVAILABILITY_COMMIT_IN_PROGRESS', '同一 availability context 已有 commit 正在进行中', {
|
|
2566
|
+
contextId
|
|
2645
2567
|
});
|
|
2646
|
-
throw this.createAvailabilityFacadeError(
|
|
2647
|
-
"AVAILABILITY_COMMIT_IN_PROGRESS",
|
|
2648
|
-
"同一 availability context 已有 commit 正在进行中",
|
|
2649
|
-
{ contextId }
|
|
2650
|
-
);
|
|
2651
2568
|
}
|
|
2652
2569
|
this.availabilityCommitLocks.add(contextId);
|
|
2653
2570
|
const currentContext = this.availabilityContextRegistry.get(contextId);
|
|
2654
2571
|
await this.emitAvailabilityDiagnostic({
|
|
2655
|
-
action:
|
|
2572
|
+
action: 'commit_lock_acquired',
|
|
2656
2573
|
contextId,
|
|
2657
|
-
contextVersion:
|
|
2658
|
-
projection: this.getAvailabilityProjectionByKey(currentContext
|
|
2659
|
-
snapshotId: currentContext
|
|
2660
|
-
orderRevision: currentContext
|
|
2661
|
-
reason:
|
|
2574
|
+
contextVersion: currentContext?.activeVersion ?? null,
|
|
2575
|
+
projection: this.getAvailabilityProjectionByKey(currentContext?.activeProjectionKey),
|
|
2576
|
+
snapshotId: currentContext?.lastSnapshotId,
|
|
2577
|
+
orderRevision: currentContext?.lastKnownOrderRevision,
|
|
2578
|
+
reason: 'commit_start'
|
|
2662
2579
|
});
|
|
2663
2580
|
}
|
|
2664
2581
|
async releaseAvailabilityCommitLock(contextId) {
|
|
2665
2582
|
this.availabilityCommitLocks.delete(contextId);
|
|
2666
2583
|
const releasedContext = this.availabilityContextRegistry.get(contextId);
|
|
2667
2584
|
await this.emitAvailabilityDiagnostic({
|
|
2668
|
-
action:
|
|
2585
|
+
action: 'commit_lock_released',
|
|
2669
2586
|
contextId,
|
|
2670
|
-
contextVersion:
|
|
2671
|
-
projection: this.getAvailabilityProjectionByKey(releasedContext
|
|
2672
|
-
snapshotId: releasedContext
|
|
2673
|
-
orderRevision: releasedContext
|
|
2674
|
-
reason:
|
|
2587
|
+
contextVersion: releasedContext?.activeVersion ?? null,
|
|
2588
|
+
projection: this.getAvailabilityProjectionByKey(releasedContext?.activeProjectionKey),
|
|
2589
|
+
snapshotId: releasedContext?.lastSnapshotId,
|
|
2590
|
+
orderRevision: releasedContext?.lastKnownOrderRevision,
|
|
2591
|
+
reason: 'commit_end'
|
|
2675
2592
|
});
|
|
2676
2593
|
}
|
|
2594
|
+
|
|
2677
2595
|
/** IO boundary: loads catalog, schedules, resources, occupancies, and registers one context. */
|
|
2678
2596
|
async prepareAvailabilityProjection(params) {
|
|
2679
2597
|
this.assertAvailabilityPrepareParams(params);
|
|
2680
2598
|
const paramsWithDefaults = await this.resolveAvailabilityPrepareDefaults(params);
|
|
2681
|
-
const {
|
|
2599
|
+
const {
|
|
2600
|
+
prepareKey,
|
|
2601
|
+
snapshot
|
|
2602
|
+
} = await this.getSharedPreparedAvailabilitySnapshot(paramsWithDefaults);
|
|
2682
2603
|
const contextId = this.createAvailabilityContextId();
|
|
2683
2604
|
const projectionEntry = this.storeAvailabilityProjectionEntry({
|
|
2684
2605
|
contextId,
|
|
@@ -2695,62 +2616,59 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
2695
2616
|
contextVersion: context.activeVersion,
|
|
2696
2617
|
projection: projectionEntry.projection,
|
|
2697
2618
|
refreshed: false,
|
|
2698
|
-
reason:
|
|
2619
|
+
reason: 'prepare'
|
|
2699
2620
|
};
|
|
2700
2621
|
await this.emitAvailabilityDiagnostic({
|
|
2701
|
-
action:
|
|
2622
|
+
action: 'prepare',
|
|
2702
2623
|
contextId,
|
|
2703
2624
|
contextVersion: context.activeVersion,
|
|
2704
2625
|
projection: projectionEntry.projection,
|
|
2705
2626
|
snapshotId: context.lastSnapshotId,
|
|
2706
2627
|
orderRevision: context.lastKnownOrderRevision,
|
|
2707
|
-
reason:
|
|
2628
|
+
reason: 'prepare'
|
|
2708
2629
|
});
|
|
2709
|
-
await this.core.effects.emit(
|
|
2710
|
-
`${this.name}:${import_types.UnifiedBookingSalesHooks.onAvailabilityProjectionPrepared}`,
|
|
2711
|
-
payload
|
|
2712
|
-
);
|
|
2630
|
+
await this.core.effects.emit(`${this.name}:${_types.UnifiedBookingSalesHooks.onAvailabilityProjectionPrepared}`, payload);
|
|
2713
2631
|
return handle;
|
|
2714
2632
|
}
|
|
2633
|
+
|
|
2715
2634
|
/** Pure configuration calculation over the current context projection. */
|
|
2716
2635
|
async getProductTimeRanges(contextId, request = {}) {
|
|
2717
|
-
const {
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
);
|
|
2636
|
+
const {
|
|
2637
|
+
context,
|
|
2638
|
+
projectionEntry
|
|
2639
|
+
} = await this.ensureAvailabilityContextProjection(contextId, 'get_product_time_ranges');
|
|
2721
2640
|
const contextVersion = context.activeVersion;
|
|
2722
|
-
return (0,
|
|
2641
|
+
return (0, _ResourcePlanner.getProductTimeRanges)(projectionEntry.projection, request).map(group => ({
|
|
2723
2642
|
...group,
|
|
2724
|
-
ranges: group.ranges.map(
|
|
2643
|
+
ranges: group.ranges.map(candidate => this.decorateAvailabilityCandidate(context.contextId, contextVersion, candidate))
|
|
2725
2644
|
}));
|
|
2726
2645
|
}
|
|
2646
|
+
|
|
2727
2647
|
/** Thin facade: auto-refresh on cart drift, then delegate to the pure projection query. */
|
|
2728
2648
|
async queryBookingAvailability(contextId, request) {
|
|
2729
|
-
const {
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2649
|
+
const {
|
|
2650
|
+
context,
|
|
2651
|
+
projectionEntry,
|
|
2652
|
+
refreshed
|
|
2653
|
+
} = await this.ensureAvailabilityContextProjection(contextId, 'query_booking_availability');
|
|
2733
2654
|
this.assertContextualQueryFreshness(context, request);
|
|
2734
|
-
const result = (0,
|
|
2735
|
-
projectionEntry.projection,
|
|
2736
|
-
request
|
|
2737
|
-
);
|
|
2655
|
+
const result = (0, _ResourcePlanner.queryAvailabilityProjection)(projectionEntry.projection, request);
|
|
2738
2656
|
const contextVersion = context.activeVersion;
|
|
2739
|
-
const contextualResult = result.target ===
|
|
2657
|
+
const contextualResult = result.target === 'time' ? {
|
|
2740
2658
|
...result,
|
|
2741
|
-
data: result.data.map(
|
|
2659
|
+
data: result.data.map(group => ({
|
|
2742
2660
|
...group,
|
|
2743
|
-
times: group.times.map(
|
|
2661
|
+
times: group.times.map(item => ({
|
|
2744
2662
|
...item,
|
|
2745
2663
|
availabilityContextId: context.contextId,
|
|
2746
2664
|
availabilityContextVersion: contextVersion
|
|
2747
2665
|
}))
|
|
2748
2666
|
}))
|
|
2749
|
-
} : result.target ===
|
|
2667
|
+
} : result.target === 'resource' ? {
|
|
2750
2668
|
...result,
|
|
2751
|
-
data: result.data.map(
|
|
2669
|
+
data: result.data.map(group => ({
|
|
2752
2670
|
...group,
|
|
2753
|
-
ranges: group.ranges.map(
|
|
2671
|
+
ranges: group.ranges.map(rangeGroup => ({
|
|
2754
2672
|
...rangeGroup,
|
|
2755
2673
|
range: {
|
|
2756
2674
|
...rangeGroup.range,
|
|
@@ -2768,65 +2686,55 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
2768
2686
|
};
|
|
2769
2687
|
}
|
|
2770
2688
|
releaseAvailabilityContext(contextId) {
|
|
2771
|
-
var _a;
|
|
2772
2689
|
const context = this.availabilityContextRegistry.get(contextId);
|
|
2773
|
-
if (!context)
|
|
2774
|
-
return;
|
|
2690
|
+
if (!context) return;
|
|
2775
2691
|
this.removeAvailabilityContext(contextId);
|
|
2776
2692
|
void this.emitAvailabilityDiagnostic({
|
|
2777
|
-
action:
|
|
2693
|
+
action: 'release',
|
|
2778
2694
|
contextId,
|
|
2779
2695
|
contextVersion: context.activeVersion,
|
|
2780
|
-
projection:
|
|
2696
|
+
projection: this.getActiveAvailabilityProjectionEntry(context)?.projection || null,
|
|
2781
2697
|
snapshotId: context.lastSnapshotId,
|
|
2782
2698
|
orderRevision: context.lastKnownOrderRevision,
|
|
2783
|
-
reason:
|
|
2699
|
+
reason: 'explicit_release'
|
|
2784
2700
|
});
|
|
2785
2701
|
}
|
|
2786
2702
|
assertAvailabilityCommitWriteCAS(params) {
|
|
2787
2703
|
const context = this.availabilityContextRegistry.get(params.contextId);
|
|
2788
2704
|
if (!context) {
|
|
2789
|
-
throw this.createAvailabilityFacadeError(
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
{ contextId: params.contextId }
|
|
2793
|
-
);
|
|
2705
|
+
throw this.createAvailabilityFacadeError('AVAILABILITY_CONTEXT_NOT_FOUND', 'availability context 不存在或已失效,请重新 prepare', {
|
|
2706
|
+
contextId: params.contextId
|
|
2707
|
+
});
|
|
2794
2708
|
}
|
|
2795
2709
|
if (this.availabilityLifecycleGeneration !== params.expectedLifecycleGeneration || context.lifecycleGeneration !== params.expectedLifecycleGeneration || context.activeVersion !== params.expectedVersion || context.refreshNonce !== params.expectedRefreshNonce || this.getCurrentOrderRevision() !== params.expectedOrderRevision) {
|
|
2796
|
-
throw this.createAvailabilityFacadeError(
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
expectedOrderRevision: params.expectedOrderRevision,
|
|
2808
|
-
currentOrderRevision: this.getCurrentOrderRevision()
|
|
2809
|
-
}
|
|
2810
|
-
);
|
|
2710
|
+
throw this.createAvailabilityFacadeError('AVAILABILITY_SELECTION_STALE', 'availability context 已变化,当前选择已失效,请重新获取候选', {
|
|
2711
|
+
contextId: params.contextId,
|
|
2712
|
+
expectedVersion: params.expectedVersion,
|
|
2713
|
+
currentVersion: context.activeVersion,
|
|
2714
|
+
expectedRefreshNonce: params.expectedRefreshNonce,
|
|
2715
|
+
currentRefreshNonce: context.refreshNonce,
|
|
2716
|
+
expectedLifecycleGeneration: params.expectedLifecycleGeneration,
|
|
2717
|
+
currentLifecycleGeneration: this.availabilityLifecycleGeneration,
|
|
2718
|
+
expectedOrderRevision: params.expectedOrderRevision,
|
|
2719
|
+
currentOrderRevision: this.getCurrentOrderRevision()
|
|
2720
|
+
});
|
|
2811
2721
|
}
|
|
2812
2722
|
return context;
|
|
2813
2723
|
}
|
|
2814
2724
|
findProductLineByUid(productLineUid) {
|
|
2815
|
-
|
|
2816
|
-
return (((_a = this.getTempOrder()) == null ? void 0 : _a.products) || []).find((product) => getProductLineUid(product) === String(productLineUid));
|
|
2725
|
+
return (this.getTempOrder()?.products || []).find(product => getProductLineUid(product) === String(productLineUid));
|
|
2817
2726
|
}
|
|
2818
2727
|
findLinkedBooking(product) {
|
|
2819
|
-
var _a, _b;
|
|
2820
2728
|
const productUid = getProductLineUid(product);
|
|
2821
|
-
const bookingUid = String(product.booking_uid ??
|
|
2822
|
-
return (
|
|
2729
|
+
const bookingUid = String(product.booking_uid ?? product.metadata?.booking_uid ?? '');
|
|
2730
|
+
return (this.getTempOrder()?.bookings || []).find(booking => bookingUid && getBookingUid(booking) === bookingUid || productUid && String(booking.product_uid ?? '') === productUid);
|
|
2823
2731
|
}
|
|
2732
|
+
|
|
2824
2733
|
/** Revalidates against the latest landed occupancy cache, then mutates exactly one product line. */
|
|
2825
2734
|
async commitAvailabilitySelection(contextId, params) {
|
|
2826
|
-
|
|
2827
|
-
const normalizedContextId = String(contextId || "").trim();
|
|
2735
|
+
const normalizedContextId = String(contextId || '').trim();
|
|
2828
2736
|
if (!normalizedContextId) {
|
|
2829
|
-
throw new
|
|
2737
|
+
throw new _ResourcePlanner.AvailabilityError('INVALID_TARGET_FILTER', 'commitAvailabilitySelection 缺少 contextId', {
|
|
2830
2738
|
candidate: params.candidate
|
|
2831
2739
|
});
|
|
2832
2740
|
}
|
|
@@ -2834,25 +2742,25 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
2834
2742
|
try {
|
|
2835
2743
|
const bookingCount = Number(params.bookingCount ?? 1);
|
|
2836
2744
|
if (!Number.isInteger(bookingCount) || bookingCount <= 0) {
|
|
2837
|
-
throw new
|
|
2745
|
+
throw new _ResourcePlanner.AvailabilityError('INVALID_RESERVATION_QUANTITY', 'bookingCount 必须是正整数', {
|
|
2838
2746
|
bookingCount: params.bookingCount
|
|
2839
2747
|
});
|
|
2840
2748
|
}
|
|
2841
|
-
if (params.mode ===
|
|
2842
|
-
throw new
|
|
2749
|
+
if (params.mode === 'edit' && bookingCount !== 1) {
|
|
2750
|
+
throw new _ResourcePlanner.AvailabilityError('INVALID_RESERVATION_QUANTITY', '编辑预约商品时 bookingCount 必须为 1', {
|
|
2843
2751
|
bookingCount
|
|
2844
2752
|
});
|
|
2845
2753
|
}
|
|
2846
|
-
const orderProductQuantity =
|
|
2847
|
-
if (orderProductQuantity !==
|
|
2848
|
-
throw new
|
|
2754
|
+
const orderProductQuantity = params.orderProduct?.num ?? params.orderProduct?.quantity ?? params.orderProduct?._extend?.quantity;
|
|
2755
|
+
if (orderProductQuantity !== undefined && orderProductQuantity !== null && Number(orderProductQuantity) !== bookingCount) {
|
|
2756
|
+
throw new _ResourcePlanner.AvailabilityError('INVALID_RESERVATION_QUANTITY', 'orderProduct 数量与 bookingCount 不一致', {
|
|
2849
2757
|
bookingCount,
|
|
2850
2758
|
orderProductQuantity
|
|
2851
2759
|
});
|
|
2852
2760
|
}
|
|
2853
2761
|
const partySize = Number(params.partySize);
|
|
2854
2762
|
if (!Number.isInteger(partySize) || partySize <= 0) {
|
|
2855
|
-
throw new
|
|
2763
|
+
throw new _ResourcePlanner.AvailabilityError('INVALID_RESERVATION_QUANTITY', 'partySize 必须是正整数', {
|
|
2856
2764
|
partySize: params.partySize
|
|
2857
2765
|
});
|
|
2858
2766
|
}
|
|
@@ -2860,104 +2768,77 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
2860
2768
|
context,
|
|
2861
2769
|
projectionEntry,
|
|
2862
2770
|
refreshed
|
|
2863
|
-
} = await this.ensureAvailabilityContextProjection(
|
|
2864
|
-
normalizedContextId,
|
|
2865
|
-
"commit_precheck"
|
|
2866
|
-
);
|
|
2771
|
+
} = await this.ensureAvailabilityContextProjection(normalizedContextId, 'commit_precheck');
|
|
2867
2772
|
if (refreshed) {
|
|
2868
|
-
throw this.createAvailabilityFacadeError(
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
currentContextVersion: context.activeVersion,
|
|
2874
|
-
candidateContextVersion: params.candidate.availabilityContextVersion
|
|
2875
|
-
}
|
|
2876
|
-
);
|
|
2773
|
+
throw this.createAvailabilityFacadeError('AVAILABILITY_SELECTION_STALE', 'availability context 已刷新,请重新获取候选', {
|
|
2774
|
+
contextId: normalizedContextId,
|
|
2775
|
+
currentContextVersion: context.activeVersion,
|
|
2776
|
+
candidateContextVersion: params.candidate.availabilityContextVersion
|
|
2777
|
+
});
|
|
2877
2778
|
}
|
|
2878
2779
|
const boundEditingUids = context.prepareParams.editingProductLineUids || [];
|
|
2879
|
-
if (params.mode ===
|
|
2780
|
+
if (params.mode === 'edit') {
|
|
2880
2781
|
if (!params.productLineUid || !boundEditingUids.includes(String(params.productLineUid))) {
|
|
2881
|
-
throw this.createAvailabilityFacadeError(
|
|
2882
|
-
"AVAILABILITY_SELECTION_STALE",
|
|
2883
|
-
"edit context 与当前商品行不匹配,请重新 prepare 编辑上下文",
|
|
2884
|
-
{
|
|
2885
|
-
contextId: normalizedContextId,
|
|
2886
|
-
productLineUid: params.productLineUid,
|
|
2887
|
-
editingProductLineUids: boundEditingUids
|
|
2888
|
-
}
|
|
2889
|
-
);
|
|
2890
|
-
}
|
|
2891
|
-
} else if (boundEditingUids.length > 0) {
|
|
2892
|
-
throw this.createAvailabilityFacadeError(
|
|
2893
|
-
"AVAILABILITY_SELECTION_STALE",
|
|
2894
|
-
"edit context 不能用于 add,请重新 prepare 新增上下文",
|
|
2895
|
-
{
|
|
2782
|
+
throw this.createAvailabilityFacadeError('AVAILABILITY_SELECTION_STALE', 'edit context 与当前商品行不匹配,请重新 prepare 编辑上下文', {
|
|
2896
2783
|
contextId: normalizedContextId,
|
|
2784
|
+
productLineUid: params.productLineUid,
|
|
2897
2785
|
editingProductLineUids: boundEditingUids
|
|
2898
|
-
}
|
|
2899
|
-
|
|
2786
|
+
});
|
|
2787
|
+
}
|
|
2788
|
+
} else if (boundEditingUids.length > 0) {
|
|
2789
|
+
throw this.createAvailabilityFacadeError('AVAILABILITY_SELECTION_STALE', 'edit context 不能用于 add,请重新 prepare 新增上下文', {
|
|
2790
|
+
contextId: normalizedContextId,
|
|
2791
|
+
editingProductLineUids: boundEditingUids
|
|
2792
|
+
});
|
|
2900
2793
|
}
|
|
2901
2794
|
if (projectionEntry.contextVersion !== context.activeVersion) {
|
|
2902
|
-
throw this.createAvailabilityFacadeError(
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
currentContextVersion: context.activeVersion,
|
|
2908
|
-
projectionContextVersion: projectionEntry.contextVersion
|
|
2909
|
-
}
|
|
2910
|
-
);
|
|
2795
|
+
throw this.createAvailabilityFacadeError('AVAILABILITY_SELECTION_STALE', 'availability context 已刷新,请重新获取候选', {
|
|
2796
|
+
contextId: normalizedContextId,
|
|
2797
|
+
currentContextVersion: context.activeVersion,
|
|
2798
|
+
projectionContextVersion: projectionEntry.contextVersion
|
|
2799
|
+
});
|
|
2911
2800
|
}
|
|
2912
2801
|
if (projectionEntry.orderRevision !== this.getCurrentOrderRevision()) {
|
|
2913
|
-
throw this.createAvailabilityFacadeError(
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
projectionOrderRevision: projectionEntry.orderRevision,
|
|
2920
|
-
currentOrderRevision: this.getCurrentOrderRevision()
|
|
2921
|
-
}
|
|
2922
|
-
);
|
|
2802
|
+
throw this.createAvailabilityFacadeError('AVAILABILITY_SELECTION_STALE', '购物车已变化,请重新获取候选', {
|
|
2803
|
+
contextId: normalizedContextId,
|
|
2804
|
+
currentContextVersion: context.activeVersion,
|
|
2805
|
+
projectionOrderRevision: projectionEntry.orderRevision,
|
|
2806
|
+
currentOrderRevision: this.getCurrentOrderRevision()
|
|
2807
|
+
});
|
|
2923
2808
|
}
|
|
2924
2809
|
if (projectionEntry.contextVersion !== params.candidate.availabilityContextVersion) {
|
|
2925
|
-
throw this.createAvailabilityFacadeError(
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
candidateContextVersion: params.candidate.availabilityContextVersion,
|
|
2931
|
-
currentContextVersion: context.activeVersion
|
|
2932
|
-
}
|
|
2933
|
-
);
|
|
2810
|
+
throw this.createAvailabilityFacadeError('AVAILABILITY_SELECTION_STALE', '候选已过期,请重新获取最新时间段', {
|
|
2811
|
+
contextId: normalizedContextId,
|
|
2812
|
+
candidateContextVersion: params.candidate.availabilityContextVersion,
|
|
2813
|
+
currentContextVersion: context.activeVersion
|
|
2814
|
+
});
|
|
2934
2815
|
}
|
|
2935
2816
|
this.assertAvailabilityCandidateFreshness(context, params.candidate);
|
|
2936
|
-
const orderProductId =
|
|
2817
|
+
const orderProductId = params.orderProduct?.product_id ?? params.orderProduct?.id;
|
|
2937
2818
|
if (!sameAvailabilityId(orderProductId, params.candidate.productId)) {
|
|
2938
|
-
throw new
|
|
2819
|
+
throw new _ResourcePlanner.AvailabilityError('INVALID_TARGET_FILTER', 'orderProduct 与 candidate 商品不一致', {
|
|
2939
2820
|
orderProductId,
|
|
2940
2821
|
candidateProductId: params.candidate.productId
|
|
2941
2822
|
});
|
|
2942
2823
|
}
|
|
2943
|
-
if (params.mode ===
|
|
2944
|
-
throw new
|
|
2824
|
+
if (params.mode === 'edit' && !params.productLineUid) {
|
|
2825
|
+
throw new _ResourcePlanner.AvailabilityError('PRODUCT_LINE_NOT_FOUND', 'edit 模式必须提供 productLineUid');
|
|
2945
2826
|
}
|
|
2946
|
-
const editingLine = params.mode ===
|
|
2947
|
-
if (params.mode ===
|
|
2948
|
-
throw new
|
|
2827
|
+
const editingLine = params.mode === 'edit' ? this.findProductLineByUid(String(params.productLineUid)) : undefined;
|
|
2828
|
+
if (params.mode === 'edit' && !editingLine) {
|
|
2829
|
+
throw new _ResourcePlanner.AvailabilityError('PRODUCT_LINE_NOT_FOUND', '找不到要编辑的预约商品行', {
|
|
2949
2830
|
productLineUid: params.productLineUid
|
|
2950
2831
|
});
|
|
2951
2832
|
}
|
|
2952
2833
|
if (editingLine && !sameAvailabilityId(editingLine.product_id ?? editingLine.id, params.candidate.productId)) {
|
|
2953
|
-
throw new
|
|
2834
|
+
throw new _ResourcePlanner.AvailabilityError('INVALID_TARGET_FILTER', '编辑商品行与 candidate 商品不一致', {
|
|
2954
2835
|
productLineUid: params.productLineUid,
|
|
2955
2836
|
productLineProductId: editingLine.product_id ?? editingLine.id,
|
|
2956
2837
|
candidateProductId: params.candidate.productId
|
|
2957
2838
|
});
|
|
2958
2839
|
}
|
|
2959
2840
|
if (!Array.isArray(params.requirementSelections)) {
|
|
2960
|
-
throw new
|
|
2841
|
+
throw new _ResourcePlanner.AvailabilityError('INVALID_TARGET_FILTER', 'requirementSelections 必须是数组', {
|
|
2961
2842
|
requirementSelections: params.requirementSelections
|
|
2962
2843
|
});
|
|
2963
2844
|
}
|
|
@@ -2967,82 +2848,75 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
2967
2848
|
expectedVersion: context.activeVersion,
|
|
2968
2849
|
expectedRefreshNonce: context.refreshNonce,
|
|
2969
2850
|
expectedLifecycleGeneration: this.availabilityLifecycleGeneration,
|
|
2970
|
-
expectedOrderRevision: String(baseProjection.meta.orderRevision ??
|
|
2851
|
+
expectedOrderRevision: String(baseProjection.meta.orderRevision ?? '')
|
|
2971
2852
|
};
|
|
2972
2853
|
const prepareParams = baseProjection.meta.prepareParams;
|
|
2973
2854
|
if (!prepareParams) {
|
|
2974
|
-
throw new
|
|
2855
|
+
throw new _ResourcePlanner.AvailabilityError('REVALIDATION_FAILED', 'projection 缺少 prepareParams,无法执行缓存重校验');
|
|
2975
2856
|
}
|
|
2976
2857
|
const fallbackResources = {
|
|
2977
|
-
resources: (0,
|
|
2978
|
-
resourceOccupancies: (0,
|
|
2979
|
-
[...baseProjection.resourceOccupancies || []].filter((occupancy) => occupancy.source !== "cart")
|
|
2980
|
-
),
|
|
2858
|
+
resources: (0, _lodashEs.cloneDeep)([...(baseProjection.resources || [])]),
|
|
2859
|
+
resourceOccupancies: (0, _lodashEs.cloneDeep)([...(baseProjection.resourceOccupancies || [])].filter(occupancy => occupancy.source !== 'cart')),
|
|
2981
2860
|
includesInjectedRawOccupancies: true
|
|
2982
2861
|
};
|
|
2983
2862
|
const commitSnapshot = await this.buildAvailabilityProjectionSnapshot({
|
|
2984
2863
|
...this.cloneAvailabilityPrepareParams(prepareParams),
|
|
2985
|
-
editingProductLineUids: params.mode ===
|
|
2864
|
+
editingProductLineUids: params.mode === 'edit' && params.productLineUid ? [params.productLineUid] : []
|
|
2986
2865
|
}, {
|
|
2987
|
-
resourceFetchPolicy:
|
|
2866
|
+
resourceFetchPolicy: 'cache_only',
|
|
2988
2867
|
fallbackResources,
|
|
2989
2868
|
durationCandidateAnchorAt: this.getAvailabilityDurationCandidateAnchorAt(baseProjection) || context.durationCandidateAnchorAt
|
|
2990
2869
|
});
|
|
2991
2870
|
const refreshedProjection = commitSnapshot.projection;
|
|
2992
|
-
if (String(refreshedProjection.meta.orderRevision ??
|
|
2871
|
+
if (String(refreshedProjection.meta.orderRevision ?? '') !== String(baseProjection.meta.orderRevision ?? '')) {
|
|
2993
2872
|
await this.refreshAvailabilityContextProjection({
|
|
2994
2873
|
contextId: normalizedContextId,
|
|
2995
|
-
reason:
|
|
2996
|
-
previousOrderRevision: String(baseProjection.meta.orderRevision ??
|
|
2874
|
+
reason: 'commit_order_revision_changed',
|
|
2875
|
+
previousOrderRevision: String(baseProjection.meta.orderRevision ?? '')
|
|
2997
2876
|
});
|
|
2998
2877
|
const latestContext = this.getAvailabilityContextEntry(normalizedContextId);
|
|
2999
|
-
throw this.createAvailabilityFacadeError(
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
snapshotId: baseProjection.meta.snapshotId,
|
|
3009
|
-
refreshedSnapshotId: refreshedProjection.meta.snapshotId
|
|
3010
|
-
}
|
|
3011
|
-
);
|
|
2878
|
+
throw this.createAvailabilityFacadeError('AVAILABILITY_SELECTION_STALE', '最终缓存重校验期间购物车已变化,请重新获取候选', {
|
|
2879
|
+
contextId: normalizedContextId,
|
|
2880
|
+
candidateContextVersion: params.candidate.availabilityContextVersion,
|
|
2881
|
+
currentContextVersion: latestContext.activeVersion,
|
|
2882
|
+
projectionOrderRevision: baseProjection.meta.orderRevision,
|
|
2883
|
+
refreshedOrderRevision: refreshedProjection.meta.orderRevision,
|
|
2884
|
+
snapshotId: baseProjection.meta.snapshotId,
|
|
2885
|
+
refreshedSnapshotId: refreshedProjection.meta.snapshotId
|
|
2886
|
+
});
|
|
3012
2887
|
}
|
|
3013
2888
|
this.assertAvailabilityCommitWriteCAS(commitWriteGuard);
|
|
3014
|
-
const refreshedRanges = (0,
|
|
2889
|
+
const refreshedRanges = (0, _ResourcePlanner.getProductTimeRanges)(refreshedProjection, {
|
|
3015
2890
|
productIds: [params.candidate.productId],
|
|
3016
2891
|
dateRange: {
|
|
3017
2892
|
startDate: params.candidate.startAt.slice(0, 10),
|
|
3018
2893
|
endDate: params.candidate.startAt.slice(0, 10)
|
|
3019
2894
|
}
|
|
3020
|
-
}).flatMap(
|
|
3021
|
-
const refreshedCandidateBase = refreshedRanges.find(
|
|
2895
|
+
}).flatMap(group => group.ranges);
|
|
2896
|
+
const refreshedCandidateBase = refreshedRanges.find(candidate => candidate.startAt === params.candidate.startAt && candidate.endAt === params.candidate.endAt && candidate.bookingStartAt === params.candidate.bookingStartAt && candidate.bookingEndAt === params.candidate.bookingEndAt);
|
|
3022
2897
|
if (!refreshedCandidateBase) {
|
|
3023
|
-
throw new
|
|
2898
|
+
throw new _ResourcePlanner.AvailabilityError('REVALIDATION_FAILED', '缓存重校验后候选时间已不在商品配置中', {
|
|
3024
2899
|
contextId: normalizedContextId,
|
|
3025
2900
|
candidate: params.candidate,
|
|
3026
2901
|
refreshedSnapshotId: refreshedProjection.meta.snapshotId
|
|
3027
2902
|
});
|
|
3028
2903
|
}
|
|
3029
|
-
const validation = (0,
|
|
2904
|
+
const validation = (0, _ResourcePlanner.validateAvailabilitySelection)(refreshedProjection, {
|
|
3030
2905
|
candidate: refreshedCandidateBase,
|
|
3031
2906
|
requirementSelections: params.requirementSelections,
|
|
3032
2907
|
partySize,
|
|
3033
2908
|
bookingCount
|
|
3034
2909
|
});
|
|
3035
2910
|
if (!validation.ok) {
|
|
3036
|
-
const capacityConflict = validation.conflicts.find(
|
|
3037
|
-
throw new
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
validation,
|
|
3042
|
-
refreshedSnapshotId: refreshedProjection.meta.snapshotId
|
|
3043
|
-
}
|
|
3044
|
-
);
|
|
2911
|
+
const capacityConflict = validation.conflicts.find(conflict => conflict.code === 'resource_capacity_exceeded');
|
|
2912
|
+
throw new _ResourcePlanner.AvailabilityError('REVALIDATION_FAILED', capacityConflict?.message || '缓存重校验后资源已不可提交', {
|
|
2913
|
+
validation,
|
|
2914
|
+
refreshedSnapshotId: refreshedProjection.meta.snapshotId
|
|
2915
|
+
});
|
|
3045
2916
|
}
|
|
2917
|
+
// Revalidation failure must leave the caller's candidate reusable. Only publish the
|
|
2918
|
+
// refreshed projection/version after candidate and resource validation has passed;
|
|
2919
|
+
// otherwise a second Confirm from the still-open modal would incorrectly become stale.
|
|
3046
2920
|
const revalidationEntry = this.storeAvailabilityProjectionEntry({
|
|
3047
2921
|
contextId: normalizedContextId,
|
|
3048
2922
|
prepareParams: commitSnapshot.prepareParams,
|
|
@@ -3053,14 +2927,14 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
3053
2927
|
});
|
|
3054
2928
|
const revalidatedContext = this.getAvailabilityContextEntry(normalizedContextId);
|
|
3055
2929
|
await this.emitAvailabilityDiagnostic({
|
|
3056
|
-
action:
|
|
2930
|
+
action: 'refresh',
|
|
3057
2931
|
contextId: normalizedContextId,
|
|
3058
2932
|
contextVersion: revalidatedContext.activeVersion,
|
|
3059
2933
|
projection: refreshedProjection,
|
|
3060
2934
|
snapshotId: revalidationEntry.snapshotId,
|
|
3061
2935
|
orderRevision: revalidationEntry.orderRevision,
|
|
3062
2936
|
previousOrderRevision: baseProjection.meta.orderRevision,
|
|
3063
|
-
reason:
|
|
2937
|
+
reason: 'commit_cached_occupancy_revalidation'
|
|
3064
2938
|
});
|
|
3065
2939
|
commitWriteGuard = {
|
|
3066
2940
|
contextId: normalizedContextId,
|
|
@@ -3069,21 +2943,17 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
3069
2943
|
expectedLifecycleGeneration: this.availabilityLifecycleGeneration,
|
|
3070
2944
|
expectedOrderRevision: revalidationEntry.orderRevision
|
|
3071
2945
|
};
|
|
3072
|
-
const refreshedCandidate = this.decorateAvailabilityCandidate(
|
|
3073
|
-
|
|
3074
|
-
revalidatedContext.activeVersion,
|
|
3075
|
-
refreshedCandidateBase
|
|
3076
|
-
);
|
|
3077
|
-
const product = refreshedProjection.products.find((item) => sameAvailabilityId(item.id, refreshedCandidate.productId));
|
|
2946
|
+
const refreshedCandidate = this.decorateAvailabilityCandidate(normalizedContextId, revalidatedContext.activeVersion, refreshedCandidateBase);
|
|
2947
|
+
const product = refreshedProjection.products.find(item => sameAvailabilityId(item.id, refreshedCandidate.productId));
|
|
3078
2948
|
if (!product) {
|
|
3079
|
-
throw new
|
|
2949
|
+
throw new _ResourcePlanner.AvailabilityError('REVALIDATION_FAILED', '缓存重校验后商品不在 projection 中', {
|
|
3080
2950
|
productId: refreshedCandidate.productId
|
|
3081
2951
|
});
|
|
3082
2952
|
}
|
|
3083
|
-
const existingBooking = editingLine ? this.findLinkedBooking(editingLine) :
|
|
3084
|
-
const existingBookingUid = existingBooking ? getBookingUid(existingBooking) :
|
|
3085
|
-
if (params.mode ===
|
|
3086
|
-
throw new
|
|
2953
|
+
const existingBooking = editingLine ? this.findLinkedBooking(editingLine) : undefined;
|
|
2954
|
+
const existingBookingUid = existingBooking ? getBookingUid(existingBooking) : '';
|
|
2955
|
+
if (params.mode === 'edit' && !existingBookingUid) {
|
|
2956
|
+
throw new _ResourcePlanner.AvailabilityError('PRODUCT_LINE_NOT_FOUND', '编辑商品行缺少关联 booking', {
|
|
3087
2957
|
productLineUid: params.productLineUid
|
|
3088
2958
|
});
|
|
3089
2959
|
}
|
|
@@ -3093,39 +2963,39 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
3093
2963
|
candidate: refreshedCandidate,
|
|
3094
2964
|
product,
|
|
3095
2965
|
resources: refreshedProjection.resources,
|
|
3096
|
-
bookingUid: existingBookingUid ||
|
|
3097
|
-
holder:
|
|
3098
|
-
holderIds:
|
|
2966
|
+
bookingUid: existingBookingUid || undefined,
|
|
2967
|
+
holder: params.orderProduct?._extend?.holder ?? existingBooking?.holder ?? existingBooking?.metadata?.holder,
|
|
2968
|
+
holderIds: params.orderProduct?._extend?.holder_id ?? params.orderProduct?._extend?.holder?.form_record ?? existingBooking?.metadata?.holder_id ?? existingBooking?.holder?.form_record
|
|
3099
2969
|
});
|
|
3100
2970
|
const transformed = this.buildUnifiedOrderProduct({
|
|
3101
2971
|
...params.orderProduct,
|
|
3102
2972
|
num: bookingCount,
|
|
3103
2973
|
quantity: bookingCount
|
|
3104
|
-
},
|
|
2974
|
+
}, 'unified_booking_sales', product);
|
|
3105
2975
|
if (!transformed) {
|
|
3106
|
-
throw new
|
|
2976
|
+
throw new _ResourcePlanner.AvailabilityError('INVALID_TARGET_FILTER', 'orderProduct 无法转换为订单行', {
|
|
3107
2977
|
orderProduct: params.orderProduct
|
|
3108
2978
|
});
|
|
3109
2979
|
}
|
|
3110
2980
|
transformed.num = bookingCount;
|
|
3111
2981
|
transformed.metadata = {
|
|
3112
|
-
...transformed.metadata || {},
|
|
2982
|
+
...(transformed.metadata || {}),
|
|
3113
2983
|
unified_booking_sales: true,
|
|
3114
2984
|
availability_candidate_key: refreshedCandidate.key,
|
|
3115
2985
|
availability_context_id: normalizedContextId,
|
|
3116
2986
|
availability_context_version: refreshedCandidate.availabilityContextVersion
|
|
3117
2987
|
};
|
|
3118
|
-
let productLineUid =
|
|
3119
|
-
let bookingUid =
|
|
2988
|
+
let productLineUid = '';
|
|
2989
|
+
let bookingUid = '';
|
|
3120
2990
|
let productLineUids = [];
|
|
3121
2991
|
let bookingUids = [];
|
|
3122
|
-
if (params.mode ===
|
|
2992
|
+
if (params.mode === 'edit' && editingLine && params.productLineUid) {
|
|
3123
2993
|
transformed.product_sku = transformed.product_sku ?? editingLine.product_sku;
|
|
3124
2994
|
transformed.product_bundle = transformed.product_bundle ?? editingLine.product_bundle;
|
|
3125
2995
|
transformed.booking_uid = existingBookingUid;
|
|
3126
2996
|
transformed.metadata = {
|
|
3127
|
-
...editingLine.metadata || {},
|
|
3128
|
-
...transformed.metadata || {},
|
|
2997
|
+
...(editingLine.metadata || {}),
|
|
2998
|
+
...(transformed.metadata || {}),
|
|
3129
2999
|
unique_identification_number: params.productLineUid,
|
|
3130
3000
|
booking_uid: existingBookingUid
|
|
3131
3001
|
};
|
|
@@ -3141,33 +3011,18 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
3141
3011
|
productLineUids = [productLineUid];
|
|
3142
3012
|
bookingUids = [bookingUid];
|
|
3143
3013
|
} else {
|
|
3144
|
-
const beforeProductLineUids = new Set(
|
|
3145
|
-
|
|
3146
|
-
);
|
|
3147
|
-
const beforeBookingUids = new Set(
|
|
3148
|
-
(((_r = this.getTempOrder()) == null ? void 0 : _r.bookings) || []).map((item) => getBookingUid(item)).filter(Boolean)
|
|
3149
|
-
);
|
|
3014
|
+
const beforeProductLineUids = new Set((this.getTempOrder()?.products || []).map(line => getProductLineUid(line)).filter(Boolean));
|
|
3015
|
+
const beforeBookingUids = new Set((this.getTempOrder()?.bookings || []).map(item => getBookingUid(item)).filter(Boolean));
|
|
3150
3016
|
const products = await this.addProductToOrder(transformed, booking);
|
|
3151
|
-
const addedLines = products.filter((line)
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
productLineUids = addedLines.map((line) => getProductLineUid(line)).filter(Boolean);
|
|
3156
|
-
const addedBookings = (((_s = this.getTempOrder()) == null ? void 0 : _s.bookings) || []).filter((item) => {
|
|
3157
|
-
var _a2;
|
|
3158
|
-
return ((_a2 = item.metadata) == null ? void 0 : _a2.availability_candidate_key) === refreshedCandidate.key && !beforeBookingUids.has(getBookingUid(item));
|
|
3159
|
-
});
|
|
3160
|
-
bookingUids = addedBookings.map((item) => getBookingUid(item)).filter(Boolean);
|
|
3017
|
+
const addedLines = products.filter(line => sameAvailabilityId(line.product_id ?? line.id, refreshedCandidate.productId) && line.metadata?.availability_candidate_key === refreshedCandidate.key && !beforeProductLineUids.has(getProductLineUid(line)));
|
|
3018
|
+
productLineUids = addedLines.map(line => getProductLineUid(line)).filter(Boolean);
|
|
3019
|
+
const addedBookings = (this.getTempOrder()?.bookings || []).filter(item => item.metadata?.availability_candidate_key === refreshedCandidate.key && !beforeBookingUids.has(getBookingUid(item)));
|
|
3020
|
+
bookingUids = addedBookings.map(item => getBookingUid(item)).filter(Boolean);
|
|
3161
3021
|
if (bookingUids.length === 0) {
|
|
3162
|
-
bookingUids = addedLines.map(
|
|
3163
|
-
var _a2;
|
|
3164
|
-
return String(
|
|
3165
|
-
line.booking_uid ?? ((_a2 = line.metadata) == null ? void 0 : _a2.booking_uid) ?? ""
|
|
3166
|
-
);
|
|
3167
|
-
}).filter(Boolean);
|
|
3022
|
+
bookingUids = addedLines.map(line => String(line.booking_uid ?? line.metadata?.booking_uid ?? '')).filter(Boolean);
|
|
3168
3023
|
}
|
|
3169
|
-
productLineUid = productLineUids[productLineUids.length - 1] ||
|
|
3170
|
-
bookingUid = bookingUids[bookingUids.length - 1] ||
|
|
3024
|
+
productLineUid = productLineUids[productLineUids.length - 1] || '';
|
|
3025
|
+
bookingUid = bookingUids[bookingUids.length - 1] || '';
|
|
3171
3026
|
}
|
|
3172
3027
|
this.invalidateAvailabilityContextsAfterCommit();
|
|
3173
3028
|
const nextContext = this.getAvailabilityContextEntry(normalizedContextId);
|
|
@@ -3182,28 +3037,12 @@ var UnifiedBookingSalesImpl = class extends import_BookingTicket.BookingTicket {
|
|
|
3182
3037
|
productLineUids,
|
|
3183
3038
|
bookingUids
|
|
3184
3039
|
};
|
|
3185
|
-
await this.core.effects.emit(
|
|
3186
|
-
`${this.name}:${import_types.UnifiedBookingSalesHooks.onAvailabilitySelectionCommitted}`,
|
|
3187
|
-
result
|
|
3188
|
-
);
|
|
3040
|
+
await this.core.effects.emit(`${this.name}:${_types.UnifiedBookingSalesHooks.onAvailabilitySelectionCommitted}`, result);
|
|
3189
3041
|
return result;
|
|
3190
3042
|
} finally {
|
|
3191
3043
|
await this.releaseAvailabilityCommitLock(normalizedContextId);
|
|
3192
3044
|
}
|
|
3193
3045
|
}
|
|
3194
|
-
}
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
0 && (module.exports = {
|
|
3198
|
-
AvailabilityError,
|
|
3199
|
-
UnifiedBookingSales,
|
|
3200
|
-
UnifiedBookingSalesImpl,
|
|
3201
|
-
buildAvailabilityLineIdentity,
|
|
3202
|
-
buildAvailabilityResourcesFromV2,
|
|
3203
|
-
buildBookingFromAvailabilityAssignment,
|
|
3204
|
-
buildBookingFromAvailabilityAssignments,
|
|
3205
|
-
buildPlannerDiscountResult,
|
|
3206
|
-
collectAvailabilityResourceIds,
|
|
3207
|
-
normalizeProductForAvailability,
|
|
3208
|
-
...require("./types")
|
|
3209
|
-
});
|
|
3046
|
+
}
|
|
3047
|
+
exports.UnifiedBookingSalesImpl = UnifiedBookingSalesImpl;
|
|
3048
|
+
const UnifiedBookingSales = exports.UnifiedBookingSales = UnifiedBookingSalesImpl;
|