@pisell/pisellos 2.2.314 → 2.2.316
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/index.d.ts +2 -0
- package/dist/index.js +3 -1
- package/dist/model/strategy/adapter/itemRule/adapter.d.ts +3 -1
- package/dist/model/strategy/adapter/itemRule/adapter.js +74 -7
- package/dist/model/strategy/adapter/itemRule/evaluator.d.ts +3 -1
- package/dist/model/strategy/adapter/itemRule/evaluator.js +13 -0
- package/dist/model/strategy/adapter/itemRule/type.d.ts +40 -1
- package/dist/model/strategy/adapter/itemRule/type.js +10 -0
- package/dist/model/strategy/adapter/promotion/evaluator.js +89 -104
- package/dist/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.d.ts +3 -1
- package/dist/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +28 -4
- package/dist/modules/BookingContext/utils/productExtend.d.ts +7 -0
- package/dist/modules/BookingContext/utils/productExtend.js +62 -18
- package/dist/modules/BookingContext/utils/resources.js +1 -1
- package/dist/modules/BusinessAvailability/index.d.ts +30 -0
- package/dist/modules/BusinessAvailability/index.js +204 -0
- package/dist/modules/Cart/index.js +4 -3
- package/dist/modules/Cart/types.d.ts +2 -0
- package/dist/modules/Cart/utils/cartProduct.d.ts +3 -1
- package/dist/modules/Cart/utils/cartProduct.js +59 -32
- package/dist/modules/FulfillmentTiming/index.d.ts +11 -0
- package/dist/modules/FulfillmentTiming/index.js +407 -0
- package/dist/modules/FulfillmentTiming/types.d.ts +67 -0
- package/dist/modules/FulfillmentTiming/types.js +1 -0
- package/dist/modules/Holder/utils.d.ts +2 -0
- package/dist/modules/Holder/utils.js +32 -2
- package/dist/modules/OpenData/contactInfo.d.ts +20 -0
- package/dist/modules/OpenData/contactInfo.js +70 -0
- package/dist/modules/OpenData/index.d.ts +1 -0
- package/dist/modules/OpenData/index.js +1 -0
- package/dist/modules/OpenData/types.d.ts +23 -0
- package/dist/modules/Order/holderRelations.d.ts +14 -0
- package/dist/modules/Order/holderRelations.js +154 -0
- package/dist/modules/Order/index.d.ts +16 -4
- package/dist/modules/Order/index.js +926 -511
- package/dist/modules/Order/localEftposState.d.ts +13 -0
- package/dist/modules/Order/localEftposState.js +39 -0
- package/dist/modules/Order/pickupTiming.d.ts +46 -0
- package/dist/modules/Order/pickupTiming.js +53 -0
- package/dist/modules/Order/types.d.ts +49 -3
- package/dist/modules/Order/types.js +2 -0
- package/dist/modules/Order/utils/discountProductLineIdentity.d.ts +1 -1
- package/dist/modules/Order/utils/discountProductLineIdentity.js +142 -7
- package/dist/modules/Order/utils/orderCollectionIdentity.js +24 -10
- package/dist/modules/Order/utils.d.ts +2 -1
- package/dist/modules/Order/utils.js +61 -17
- package/dist/modules/Payment/index.js +9 -10
- package/dist/modules/Product/types.d.ts +11 -0
- package/dist/modules/ProductList/index.js +1 -1
- package/dist/modules/ResourcePlanner/planner.d.ts +2 -0
- package/dist/modules/ResourcePlanner/planner.js +332 -23
- package/dist/modules/ResourcePlanner/types.d.ts +29 -0
- package/dist/modules/Rules/index.js +62 -61
- package/dist/modules/Schedule/index.d.ts +8 -0
- package/dist/modules/Schedule/index.js +141 -39
- package/dist/modules/index.d.ts +1 -0
- package/dist/modules/index.js +1 -0
- package/dist/plugins/request.d.ts +7 -0
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +1557 -1322
- package/dist/server/modules/form/index.d.ts +19 -0
- package/dist/server/modules/form/index.js +205 -0
- package/dist/server/modules/form/types.d.ts +13 -0
- package/dist/server/modules/form/types.js +3 -0
- package/dist/server/modules/index.d.ts +3 -0
- package/dist/server/modules/index.js +4 -0
- package/dist/server/modules/order/index.d.ts +19 -0
- package/dist/server/modules/order/index.js +1071 -774
- package/dist/server/modules/order/types.d.ts +4 -0
- package/dist/server/modules/products/index.d.ts +28 -7
- package/dist/server/modules/products/index.js +1297 -770
- package/dist/server/modules/products/replication.d.ts +8 -0
- package/dist/server/modules/products/replication.js +69 -0
- package/dist/server/modules/products/types.d.ts +12 -1
- package/dist/server/modules/products/types.js +4 -0
- package/dist/server/test.json +1 -1
- package/dist/server/utils/small-ticket.d.ts +1 -85
- package/dist/server/utils/small-ticket.js +2 -2542
- package/dist/shared/payment/loadPaymentMethods.d.ts +9 -0
- package/dist/shared/payment/loadPaymentMethods.js +66 -0
- package/dist/shared/receipt/customerInvoiceLanguages.d.ts +8 -0
- package/dist/shared/receipt/customerInvoiceLanguages.js +60 -0
- package/dist/shared/receipt/loadCustomerInvoiceLayout.d.ts +27 -0
- package/dist/shared/receipt/loadCustomerInvoiceLayout.js +184 -0
- package/dist/shared/receipt/small-ticket.d.ts +96 -0
- package/dist/shared/receipt/small-ticket.js +2763 -0
- package/dist/shared/receipt/templateVisibility.d.ts +26 -0
- package/dist/shared/receipt/templateVisibility.js +68 -0
- package/dist/shared/sales/loadSalesOrderSnapshot.d.ts +9 -0
- package/dist/shared/sales/loadSalesOrderSnapshot.js +21 -0
- package/dist/solution/BaseSales/fulfillmentItemRules.d.ts +27 -0
- package/dist/solution/BaseSales/fulfillmentItemRules.js +750 -0
- package/dist/solution/BaseSales/index.d.ts +43 -16
- package/dist/solution/BaseSales/index.js +2028 -1247
- package/dist/solution/BaseSales/pickupTiming.d.ts +47 -0
- package/dist/solution/BaseSales/pickupTiming.js +616 -0
- package/dist/solution/BaseSales/types.d.ts +12 -0
- package/dist/solution/BaseSales/utils/parseSalesResponse.d.ts +3 -1
- package/dist/solution/BaseSales/utils/parseSalesResponse.js +10 -3
- package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +14 -4
- package/dist/solution/BookingByStep/index.d.ts +4 -2
- package/dist/solution/BookingByStep/index.js +8 -3
- package/dist/solution/BookingTicket/index.d.ts +23 -3
- package/dist/solution/BookingTicket/index.js +867 -507
- package/dist/solution/BookingTicket/utils/addProductDecision.d.ts +0 -1
- package/dist/solution/BookingTicket/utils/addProductDecision.js +1 -1
- package/dist/solution/BookingTicket/utils/cartView.js +3 -1
- package/dist/solution/OrderInvoicePreview/index.d.ts +66 -0
- package/dist/solution/OrderInvoicePreview/index.js +340 -0
- package/dist/solution/RegisterAndLogin/config.js +54 -11
- package/dist/solution/RegisterAndLogin/index.d.ts +2 -0
- package/dist/solution/RegisterAndLogin/index.js +1 -1
- package/dist/solution/RegisterAndLogin/utils.d.ts +12 -4
- package/dist/solution/RegisterAndLogin/utils.js +36 -16
- package/dist/solution/ScanOrder/index.js +65 -31
- package/dist/solution/ScanOrder/types.d.ts +3 -0
- package/dist/solution/UnifiedBookingSales/businessAvailability.d.ts +27 -0
- package/dist/solution/UnifiedBookingSales/businessAvailability.js +292 -0
- package/dist/solution/UnifiedBookingSales/index.d.ts +36 -4
- package/dist/solution/UnifiedBookingSales/index.js +1715 -906
- package/dist/solution/UnifiedBookingSales/types.d.ts +16 -2
- package/dist/solution/VenueBooking/index.d.ts +15 -2
- package/dist/solution/VenueBooking/index.js +1062 -785
- package/dist/solution/VenueBooking/types.d.ts +2 -1
- package/dist/solution/VenueBooking/types.js +1 -0
- package/dist/solution/VenueBooking/utils/dateSummary.d.ts +1 -0
- package/dist/solution/VenueBooking/utils/dateSummary.js +7 -4
- package/dist/solution/VenueBooking/utils/smartPricing.d.ts +71 -0
- package/dist/solution/VenueBooking/utils/smartPricing.js +273 -0
- package/dist/solution/VenueBooking/utils/timeSlot.d.ts +1 -0
- package/dist/solution/VenueBooking/utils/timeSlot.js +6 -3
- package/dist/solution/index.d.ts +1 -0
- package/dist/solution/index.js +2 -1
- package/lib/index.d.ts +2 -0
- package/lib/index.js +21 -1
- package/lib/model/strategy/adapter/itemRule/adapter.d.ts +3 -1
- package/lib/model/strategy/adapter/itemRule/adapter.js +54 -4
- package/lib/model/strategy/adapter/itemRule/evaluator.d.ts +3 -1
- package/lib/model/strategy/adapter/itemRule/evaluator.js +9 -0
- package/lib/model/strategy/adapter/itemRule/type.d.ts +40 -1
- package/lib/model/strategy/adapter/itemRule/type.js +10 -0
- package/lib/model/strategy/adapter/promotion/evaluator.js +9 -14
- package/lib/model/strategy/adapter/promotion/index.js +46 -1
- package/lib/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.d.ts +3 -1
- package/lib/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +17 -1
- package/lib/modules/BookingContext/utils/productExtend.d.ts +7 -0
- package/lib/modules/BookingContext/utils/productExtend.js +45 -2
- package/lib/modules/BookingContext/utils/resources.js +1 -1
- package/lib/modules/BusinessAvailability/index.d.ts +30 -0
- package/lib/modules/BusinessAvailability/index.js +146 -0
- package/lib/modules/Cart/index.js +4 -2
- package/lib/modules/Cart/types.d.ts +2 -0
- package/lib/modules/Cart/utils/cartProduct.d.ts +3 -1
- package/lib/modules/Cart/utils/cartProduct.js +39 -20
- package/lib/modules/FulfillmentTiming/index.d.ts +11 -0
- package/lib/modules/FulfillmentTiming/index.js +359 -0
- package/lib/modules/FulfillmentTiming/types.d.ts +67 -0
- package/lib/modules/FulfillmentTiming/types.js +5 -0
- package/lib/modules/Holder/utils.d.ts +2 -0
- package/lib/modules/Holder/utils.js +27 -1
- package/lib/modules/OpenData/contactInfo.d.ts +20 -0
- package/lib/modules/OpenData/contactInfo.js +56 -0
- package/lib/modules/OpenData/index.d.ts +1 -0
- package/lib/modules/OpenData/index.js +12 -0
- package/lib/modules/OpenData/types.d.ts +23 -0
- package/lib/modules/Order/holderRelations.d.ts +14 -0
- package/lib/modules/Order/holderRelations.js +98 -0
- package/lib/modules/Order/index.d.ts +16 -4
- package/lib/modules/Order/index.js +284 -50
- package/lib/modules/Order/localEftposState.d.ts +13 -0
- package/lib/modules/Order/localEftposState.js +44 -0
- package/lib/modules/Order/pickupTiming.d.ts +46 -0
- package/lib/modules/Order/pickupTiming.js +61 -0
- package/lib/modules/Order/types.d.ts +49 -3
- package/lib/modules/Order/types.js +1 -0
- package/lib/modules/Order/utils/discountProductLineIdentity.d.ts +1 -1
- package/lib/modules/Order/utils/discountProductLineIdentity.js +51 -2
- package/lib/modules/Order/utils/orderCollectionIdentity.js +5 -0
- package/lib/modules/Order/utils.d.ts +2 -1
- package/lib/modules/Order/utils.js +49 -11
- package/lib/modules/Payment/index.js +3 -4
- package/lib/modules/Product/types.d.ts +11 -0
- package/lib/modules/ProductList/index.js +1 -1
- package/lib/modules/ResourcePlanner/planner.d.ts +2 -0
- package/lib/modules/ResourcePlanner/planner.js +252 -8
- package/lib/modules/ResourcePlanner/types.d.ts +29 -0
- package/lib/modules/Rules/index.js +2 -1
- package/lib/modules/Schedule/index.d.ts +8 -0
- package/lib/modules/Schedule/index.js +68 -12
- package/lib/modules/index.d.ts +1 -0
- package/lib/modules/index.js +11 -0
- package/lib/plugins/request.d.ts +7 -0
- package/lib/server/index.d.ts +1 -0
- package/lib/server/index.js +166 -39
- package/lib/server/modules/form/index.d.ts +19 -0
- package/lib/server/modules/form/index.js +73 -0
- package/lib/server/modules/form/types.d.ts +13 -0
- package/lib/server/modules/form/types.js +9 -0
- package/lib/server/modules/index.d.ts +3 -0
- package/lib/server/modules/index.js +15 -1
- package/lib/server/modules/order/index.d.ts +19 -0
- package/lib/server/modules/order/index.js +160 -7
- package/lib/server/modules/order/types.d.ts +4 -0
- package/lib/server/modules/products/index.d.ts +28 -7
- package/lib/server/modules/products/index.js +226 -63
- package/lib/server/modules/products/replication.d.ts +8 -0
- package/lib/server/modules/products/replication.js +30 -0
- package/lib/server/modules/products/types.d.ts +12 -1
- package/lib/server/modules/products/types.js +3 -0
- package/lib/server/test.json +1 -1
- package/lib/server/utils/small-ticket.d.ts +1 -85
- package/lib/server/utils/small-ticket.js +9 -2305
- package/lib/shared/payment/loadPaymentMethods.d.ts +9 -0
- package/lib/shared/payment/loadPaymentMethods.js +40 -0
- package/lib/shared/receipt/customerInvoiceLanguages.d.ts +8 -0
- package/lib/shared/receipt/customerInvoiceLanguages.js +58 -0
- package/lib/shared/receipt/loadCustomerInvoiceLayout.d.ts +27 -0
- package/lib/shared/receipt/loadCustomerInvoiceLayout.js +124 -0
- package/lib/shared/receipt/small-ticket.d.ts +96 -0
- package/lib/shared/receipt/small-ticket.js +2555 -0
- package/lib/shared/receipt/templateVisibility.d.ts +26 -0
- package/lib/shared/receipt/templateVisibility.js +67 -0
- package/lib/shared/sales/loadSalesOrderSnapshot.d.ts +9 -0
- package/lib/shared/sales/loadSalesOrderSnapshot.js +22 -0
- package/lib/solution/BaseSales/fulfillmentItemRules.d.ts +27 -0
- package/lib/solution/BaseSales/fulfillmentItemRules.js +259 -0
- package/lib/solution/BaseSales/index.d.ts +43 -16
- package/lib/solution/BaseSales/index.js +401 -33
- package/lib/solution/BaseSales/pickupTiming.d.ts +47 -0
- package/lib/solution/BaseSales/pickupTiming.js +290 -0
- package/lib/solution/BaseSales/types.d.ts +12 -0
- package/lib/solution/BaseSales/utils/parseSalesResponse.d.ts +3 -1
- package/lib/solution/BaseSales/utils/parseSalesResponse.js +7 -0
- package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +16 -8
- package/lib/solution/BookingByStep/index.d.ts +4 -2
- package/lib/solution/BookingByStep/index.js +7 -3
- package/lib/solution/BookingTicket/index.d.ts +23 -3
- package/lib/solution/BookingTicket/index.js +197 -9
- package/lib/solution/BookingTicket/utils/addProductDecision.d.ts +0 -1
- package/lib/solution/BookingTicket/utils/addProductDecision.js +1 -1
- package/lib/solution/BookingTicket/utils/cartView.js +3 -1
- package/lib/solution/OrderInvoicePreview/index.d.ts +66 -0
- package/lib/solution/OrderInvoicePreview/index.js +246 -0
- package/lib/solution/RegisterAndLogin/config.js +57 -11
- package/lib/solution/RegisterAndLogin/index.d.ts +2 -0
- package/lib/solution/RegisterAndLogin/index.js +9 -1
- package/lib/solution/RegisterAndLogin/utils.d.ts +12 -4
- package/lib/solution/RegisterAndLogin/utils.js +24 -17
- package/lib/solution/ScanOrder/index.js +59 -20
- package/lib/solution/ScanOrder/types.d.ts +3 -0
- package/lib/solution/UnifiedBookingSales/businessAvailability.d.ts +27 -0
- package/lib/solution/UnifiedBookingSales/businessAvailability.js +138 -0
- package/lib/solution/UnifiedBookingSales/index.d.ts +36 -4
- package/lib/solution/UnifiedBookingSales/index.js +562 -75
- package/lib/solution/UnifiedBookingSales/types.d.ts +16 -2
- package/lib/solution/VenueBooking/index.d.ts +15 -2
- package/lib/solution/VenueBooking/index.js +243 -96
- package/lib/solution/VenueBooking/types.d.ts +2 -1
- package/lib/solution/VenueBooking/types.js +1 -0
- package/lib/solution/VenueBooking/utils/dateSummary.d.ts +1 -0
- package/lib/solution/VenueBooking/utils/dateSummary.js +7 -4
- package/lib/solution/VenueBooking/utils/smartPricing.d.ts +71 -0
- package/lib/solution/VenueBooking/utils/smartPricing.js +224 -0
- package/lib/solution/VenueBooking/utils/timeSlot.d.ts +1 -0
- package/lib/solution/VenueBooking/utils/timeSlot.js +5 -2
- package/lib/solution/index.d.ts +1 -0
- package/lib/solution/index.js +11 -0
- package/package.json +1 -1
|
@@ -9,6 +9,7 @@ var _exportNames = {
|
|
|
9
9
|
};
|
|
10
10
|
exports.PaymentModule = void 0;
|
|
11
11
|
exports.generateRequestUniqueId = generateRequestUniqueId;
|
|
12
|
+
var _loadPaymentMethods = require("../../shared/payment/loadPaymentMethods");
|
|
12
13
|
var _BaseModule = require("../BaseModule");
|
|
13
14
|
var _types = require("./types");
|
|
14
15
|
Object.keys(_types).forEach(function (key) {
|
|
@@ -282,12 +283,10 @@ class PaymentModule extends _BaseModule.BaseModule {
|
|
|
282
283
|
async getPayMethodListAsync() {
|
|
283
284
|
this.logInfo('Starting getPayMethodListAsync');
|
|
284
285
|
try {
|
|
285
|
-
const
|
|
286
|
+
const payMethods = await (0, _loadPaymentMethods.loadPaymentMethods)(this.request, {
|
|
287
|
+
transport: 'os-server',
|
|
286
288
|
filterPaymentMethods: true
|
|
287
|
-
}, {
|
|
288
|
-
osServer: true
|
|
289
289
|
});
|
|
290
|
-
const payMethods = response?.data || response || [];
|
|
291
290
|
this.logInfo('getPayMethodListAsync completed successfully', {
|
|
292
291
|
payMethods
|
|
293
292
|
});
|
|
@@ -218,6 +218,17 @@ export interface ProductData {
|
|
|
218
218
|
bundle?: any[];
|
|
219
219
|
/** 商品选项组成 */
|
|
220
220
|
options?: any[];
|
|
221
|
+
/** 单规格组;用于由已选 option 反查规格项名称、价格等商品快照字段。 */
|
|
222
|
+
option_group?: Array<{
|
|
223
|
+
id: number | string;
|
|
224
|
+
option_item?: Array<{
|
|
225
|
+
id: number | string;
|
|
226
|
+
name?: string | Record<string, any>;
|
|
227
|
+
title?: string | Record<string, any>;
|
|
228
|
+
[key: string]: any;
|
|
229
|
+
}>;
|
|
230
|
+
[key: string]: any;
|
|
231
|
+
}>;
|
|
221
232
|
/** 商品原信息 */
|
|
222
233
|
origin?: any;
|
|
223
234
|
/** 容量 */
|
|
@@ -125,7 +125,7 @@ class ProductList extends _BaseModule.BaseModule {
|
|
|
125
125
|
online_store: 'online-store',
|
|
126
126
|
scan_to_order: 'online-store',
|
|
127
127
|
'scan-to-order': 'online-store',
|
|
128
|
-
scan_to_order_2: '
|
|
128
|
+
scan_to_order_2: 'online-store',
|
|
129
129
|
scan_to_order_3: 'scantoorder3'
|
|
130
130
|
};
|
|
131
131
|
const rawApplicationCode = this.otherParams?.channel;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { AvailabilityProjection, AvailabilityProjectionInput, AvailabilityQuery, AvailabilityQueryResult, AvailabilitySelectionValidationParams, AvailabilitySelectionValidationResult, GetProductTimeRangesRequest, GetProductTimeRangesRuntimeOptions, ProductTimeRangeGroup } from './types';
|
|
2
2
|
export declare function createAvailabilityProjection(input: AvailabilityProjectionInput): AvailabilityProjection;
|
|
3
|
+
/** Resolve the label without changing the real calendar timestamp. */
|
|
4
|
+
export declare function getDurationBusinessDate(projection: AvailabilityProjection, startAt: string): string;
|
|
3
5
|
export declare function getProductTimeRanges(projection: AvailabilityProjection, request?: GetProductTimeRangesRequest, runtimeOptions?: GetProductTimeRangesRuntimeOptions): ProductTimeRangeGroup[];
|
|
4
6
|
export declare function queryAvailabilityProjection(projection: AvailabilityProjection, request: AvailabilityQuery, runtimeOptions?: GetProductTimeRangesRuntimeOptions): AvailabilityQueryResult;
|
|
5
7
|
export declare function validateAvailabilitySelection(projection: AvailabilityProjection, params: AvailabilitySelectionValidationParams, runtimeOptions?: GetProductTimeRangesRuntimeOptions): AvailabilitySelectionValidationResult;
|
|
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.createAvailabilityProjection = createAvailabilityProjection;
|
|
7
|
+
exports.getDurationBusinessDate = getDurationBusinessDate;
|
|
7
8
|
exports.getProductTimeRanges = getProductTimeRanges;
|
|
8
9
|
exports.queryAvailabilityProjection = queryAvailabilityProjection;
|
|
9
10
|
exports.validateAvailabilitySelection = validateAvailabilitySelection;
|
|
@@ -162,7 +163,11 @@ function resolveCutOffPolicyIssue(policy) {
|
|
|
162
163
|
return null;
|
|
163
164
|
}
|
|
164
165
|
function addLeadWindow(base, unit, unitType, _timezoneName) {
|
|
165
|
-
if (unitType === 'days')
|
|
166
|
+
if (unitType === 'days') {
|
|
167
|
+
const leadDate = (0, _localClock.addLocalCalendarDays)(base, unit);
|
|
168
|
+
// Positive day lead times start at midnight; zero days keeps the current-time boundary.
|
|
169
|
+
return Math.trunc(unit) > 0 ? (0, _localClock.localDateToScalarStart)((0, _localClock.formatLocalDate)(leadDate)) : leadDate;
|
|
170
|
+
}
|
|
166
171
|
if (unitType === 'hours') return (0, _localClock.addLocalHours)(base, unit);
|
|
167
172
|
return (0, _localClock.addLocalMinutes)(base, unit);
|
|
168
173
|
}
|
|
@@ -250,6 +255,18 @@ function evaluateCandidateTimeGate(params) {
|
|
|
250
255
|
};
|
|
251
256
|
}
|
|
252
257
|
const policy = product.cutOffPolicy;
|
|
258
|
+
// Current duration items can be shown outside business hours. Their real
|
|
259
|
+
// interval must remain unavailable, including products with no resource groups.
|
|
260
|
+
if (evaluationContext.useDurationTimeGrid && product.kind === 'duration' && !projection.meta.defaults.operatingHours.windows.some(window => {
|
|
261
|
+
const range = normalizeAbsoluteRange(window);
|
|
262
|
+
return range && contains(range, candidateRange);
|
|
263
|
+
})) {
|
|
264
|
+
return {
|
|
265
|
+
status: 'unavailable',
|
|
266
|
+
reasonCodes: ['outside_operating_hours'],
|
|
267
|
+
conflictCode: 'booking_cutoff'
|
|
268
|
+
};
|
|
269
|
+
}
|
|
253
270
|
if (!policy) return null;
|
|
254
271
|
if (resolveCutOffPolicyIssue(policy)) {
|
|
255
272
|
return {
|
|
@@ -286,8 +303,8 @@ function evaluateCandidateTimeGate(params) {
|
|
|
286
303
|
// otherwise a minute refresh turns it into 0 capacity and incorrectly
|
|
287
304
|
// advances a create flow to the next slot. The earlier `past` gate still
|
|
288
305
|
// rejects the anchor once its booking range has ended.
|
|
289
|
-
const isActivePosDurationAnchor = evaluationContext.useExactDurationCandidateMinute && product.kind === 'duration' && !product.isFlexibleDuration && evaluationContext.durationCandidateAnchorAtMs === candidateRange.start;
|
|
290
|
-
const usesMinutePrecision = product.isFlexibleDuration || evaluationContext.useExactDurationCandidateMinute && product.kind === 'duration';
|
|
306
|
+
const isActivePosDurationAnchor = evaluationContext.useExactDurationCandidateMinute && !evaluationContext.enforceSaleCutoff && product.kind === 'duration' && !product.isFlexibleDuration && evaluationContext.durationCandidateAnchorAtMs === candidateRange.start;
|
|
307
|
+
const usesMinutePrecision = product.isFlexibleDuration || evaluationContext.useDurationTimeGrid || evaluationContext.useExactDurationCandidateMinute && product.kind === 'duration';
|
|
291
308
|
const hasNotReachedStart = isActivePosDurationAnchor || (usesMinutePrecision ? floorToMinute(now) <= candidateRange.start : now < candidateRange.start);
|
|
292
309
|
if (hasNotReachedStart) return null;
|
|
293
310
|
return {
|
|
@@ -489,11 +506,34 @@ function normalizeOperatingHours(params) {
|
|
|
489
506
|
}));
|
|
490
507
|
const windows = normalizeWindows(sourceWindows);
|
|
491
508
|
const excludedWindows = normalizeWindows(params.input?.excludedWindows || []);
|
|
509
|
+
// Keep original dates and opening anchors separately: the availability
|
|
510
|
+
// windows below may be clipped at midnight or merged without segment identity.
|
|
511
|
+
const periodMap = new Map();
|
|
512
|
+
const originalPeriods = params.input?.businessPeriods || [...sourceWindows, ...(params.input?.excludedWindows || [])].map(window => ({
|
|
513
|
+
businessDate: window.businessDate || window.startAt.slice(0, 10),
|
|
514
|
+
startAt: window.openingAnchorAt || window.startAt,
|
|
515
|
+
endAt: window.endAt
|
|
516
|
+
}));
|
|
517
|
+
originalPeriods.forEach(period => {
|
|
518
|
+
const range = normalizeAbsoluteRange(period);
|
|
519
|
+
if (!range) return;
|
|
520
|
+
const previous = periodMap.get(period.businessDate);
|
|
521
|
+
periodMap.set(period.businessDate, {
|
|
522
|
+
start: previous ? Math.min(previous.start, range.start) : range.start,
|
|
523
|
+
end: previous ? Math.max(previous.end, range.end) : range.end
|
|
524
|
+
});
|
|
525
|
+
});
|
|
526
|
+
const businessPeriods = Array.from(periodMap, ([businessDate, range]) => ({
|
|
527
|
+
businessDate,
|
|
528
|
+
startAt: formatDateTime(range.start, params.timezoneName, params.fixedOffsetMinutes),
|
|
529
|
+
endAt: formatDateTime(range.end, params.timezoneName, params.fixedOffsetMinutes)
|
|
530
|
+
})).sort((left, right) => left.startAt.localeCompare(right.startAt));
|
|
492
531
|
return {
|
|
493
532
|
source: params.input?.source || params.fallbackSource,
|
|
494
533
|
scheduleIds: uniqById(params.input?.scheduleIds || []),
|
|
495
534
|
missingScheduleIds: uniqById(params.input?.missingScheduleIds || []),
|
|
496
535
|
windows,
|
|
536
|
+
businessPeriods,
|
|
497
537
|
...(excludedWindows.length > 0 ? {
|
|
498
538
|
excludedWindows
|
|
499
539
|
} : {})
|
|
@@ -992,6 +1032,162 @@ function createDurationTimeRangeWithEnd(product, start, bookingEnd, projection,
|
|
|
992
1032
|
scheduleRefs: []
|
|
993
1033
|
};
|
|
994
1034
|
}
|
|
1035
|
+
|
|
1036
|
+
/** Resolve the label without changing the real calendar timestamp. */
|
|
1037
|
+
function getDurationBusinessDate(projection, startAt) {
|
|
1038
|
+
const start = toDateTime(startAt);
|
|
1039
|
+
const periods = projection.meta.defaults.operatingHours.businessPeriods || [];
|
|
1040
|
+
const period = [...periods].reverse().find(item => {
|
|
1041
|
+
const range = normalizeAbsoluteRange(item);
|
|
1042
|
+
return range && range.start <= start && start < range.end;
|
|
1043
|
+
});
|
|
1044
|
+
return period?.businessDate || startAt.slice(0, 10);
|
|
1045
|
+
}
|
|
1046
|
+
function getAlignedDurationRanges(params) {
|
|
1047
|
+
const {
|
|
1048
|
+
projection,
|
|
1049
|
+
product,
|
|
1050
|
+
request,
|
|
1051
|
+
runtimeOptions,
|
|
1052
|
+
dateRange,
|
|
1053
|
+
operatingRanges,
|
|
1054
|
+
baseRanges,
|
|
1055
|
+
resourceWindowIndex,
|
|
1056
|
+
fixedOffsetMinutes
|
|
1057
|
+
} = params;
|
|
1058
|
+
const step = projection.meta.defaults.slotStepMinutes;
|
|
1059
|
+
const now = floorToMinute(toDateTime(runtimeOptions.evaluatedAt || projection.meta.durationCandidateAnchorAt));
|
|
1060
|
+
if (!Number.isFinite(now)) throw availabilityError('INVALID_TARGET_FILTER', 'evaluatedAt 必须是有效时间');
|
|
1061
|
+
const nowAt = formatDateTime(now, projection.meta.timezone, fixedOffsetMinutes);
|
|
1062
|
+
const currentBusinessDate = getDurationBusinessDate(projection, nowAt);
|
|
1063
|
+
const inRequestedDays = date => date >= dateRange.startDate && date <= dateRange.endDate;
|
|
1064
|
+
const periods = (projection.meta.defaults.operatingHours.businessPeriods || []).filter(period => inRequestedDays(period.businessDate));
|
|
1065
|
+
// A prepared month includes tomorrow's windows too; the single-day UI must
|
|
1066
|
+
// receive the selected business cycle's end, not tomorrow's closing time.
|
|
1067
|
+
const operatingEndAt = dateRange.startDate === dateRange.endDate && periods.length ? periods.reduce((latest, period) => period.endAt > latest ? period.endAt : latest, periods[0].endAt) : undefined;
|
|
1068
|
+
const ranges = [];
|
|
1069
|
+
const duration = resolveRequestedDurationMinutes(product, request);
|
|
1070
|
+
const addRange = (start, end, businessDate) => {
|
|
1071
|
+
if (end <= start) return;
|
|
1072
|
+
const candidate = createDurationTimeRangeWithEnd(product, start, end, projection, fixedOffsetMinutes);
|
|
1073
|
+
candidate.businessDate = businessDate || getDurationBusinessDate(projection, candidate.startAt);
|
|
1074
|
+
if (inRequestedDays(candidate.businessDate)) ranges.push(candidate);
|
|
1075
|
+
};
|
|
1076
|
+
// A flexible end comes from resources that actually cover this exact start,
|
|
1077
|
+
// never from the next future window. Keep alternative real ends separate.
|
|
1078
|
+
const flexibleEnds = start => {
|
|
1079
|
+
const operating = operatingRanges.find(range => range.start <= start && start < range.end);
|
|
1080
|
+
if (!operating) return [];
|
|
1081
|
+
const businessDate = getDurationBusinessDate(projection, formatDateTime(start, projection.meta.timezone, fixedOffsetMinutes));
|
|
1082
|
+
const period = projection.meta.defaults.operatingHours.businessPeriods?.find(item => item.businessDate === businessDate);
|
|
1083
|
+
const operatingEnd = Math.min(operating.end, period ? toDateTime(period.endAt) : operating.end);
|
|
1084
|
+
const requiredGroups = (product.requirementGroups || []).filter(group => group.required !== false);
|
|
1085
|
+
const groups = requiredGroups.map(group => projection.resources.filter(resource => matchesRequirementGroup(group, resource, product, request.partySize)).filter(resource => !request.resourceIds?.length || request.resourceIds.some(id => sameId(id, resource.id))).flatMap(resource => (resourceWindowIndex.get(String(resource.id)) || []).filter(range => range.start <= start && start < range.end).map(range => Math.min(range.end, operatingEnd))));
|
|
1086
|
+
if (groups.some(ends => ends.length === 0)) return [];
|
|
1087
|
+
if (!groups.length) return [operatingEnd];
|
|
1088
|
+
return Array.from(new Set(groups.flat())).filter(end => groups.every(ends => ends.some(availableEnd => availableEnd >= end))).sort((left, right) => left - right);
|
|
1089
|
+
};
|
|
1090
|
+
if (product.isFlexibleDuration) {
|
|
1091
|
+
// Preserve the existing continuous-window UI, with the business-day label.
|
|
1092
|
+
periods.forEach(period => {
|
|
1093
|
+
const periodRange = normalizeAbsoluteRange(period);
|
|
1094
|
+
baseRanges.forEach(range => {
|
|
1095
|
+
const start = Math.max(range.start, periodRange.start, now);
|
|
1096
|
+
const end = Math.min(range.end, periodRange.end);
|
|
1097
|
+
if (start < end) addRange(start, end, period.businessDate);
|
|
1098
|
+
});
|
|
1099
|
+
});
|
|
1100
|
+
} else {
|
|
1101
|
+
periods.forEach(period => {
|
|
1102
|
+
const periodRange = normalizeAbsoluteRange(period);
|
|
1103
|
+
const anchor = 60 % step === 0 ? toDateStart(period.businessDate) : periodRange.start;
|
|
1104
|
+
baseRanges.forEach(range => {
|
|
1105
|
+
const lower = Math.max(range.start, periodRange.start, now);
|
|
1106
|
+
const upper = Math.min(range.end, periodRange.end);
|
|
1107
|
+
let cursor = anchor + Math.ceil((lower - anchor) / (step * 60000)) * step * 60000;
|
|
1108
|
+
for (; cursor + duration * 60000 <= upper; cursor += step * 60000) {
|
|
1109
|
+
addRange(cursor, cursor + duration * 60000, period.businessDate);
|
|
1110
|
+
}
|
|
1111
|
+
});
|
|
1112
|
+
});
|
|
1113
|
+
}
|
|
1114
|
+
let unavailableCurrentTime;
|
|
1115
|
+
if (inRequestedDays(currentBusinessDate)) {
|
|
1116
|
+
if (product.isFlexibleDuration) {
|
|
1117
|
+
const ends = flexibleEnds(now);
|
|
1118
|
+
ends.forEach(end => addRange(now, end, currentBusinessDate));
|
|
1119
|
+
if (!ends.length) {
|
|
1120
|
+
unavailableCurrentTime = {
|
|
1121
|
+
productId: product.id,
|
|
1122
|
+
startAt: nowAt,
|
|
1123
|
+
date: formatDate(now),
|
|
1124
|
+
businessDate: currentBusinessDate,
|
|
1125
|
+
startTime: formatTime(now),
|
|
1126
|
+
status: 'unavailable',
|
|
1127
|
+
reasonCodes: [operatingRanges.some(range => range.start <= now && now < range.end) ? 'outside_resource_window' : 'outside_operating_hours']
|
|
1128
|
+
};
|
|
1129
|
+
}
|
|
1130
|
+
} else {
|
|
1131
|
+
// Even outside a working window the fixed product has a real end. Query
|
|
1132
|
+
// reports unavailable; POS manual capabilities remain a separate policy.
|
|
1133
|
+
addRange(now, now + duration * 60000, currentBusinessDate);
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
const rememberedAt = request.rememberedStartAtByProductId?.[String(product.id)];
|
|
1137
|
+
if (rememberedAt !== undefined) {
|
|
1138
|
+
const start = toDateTime(rememberedAt);
|
|
1139
|
+
if (!Number.isFinite(start)) throw availabilityError('INVALID_TARGET_FILTER', '记忆开始时间必须是完整日期时间');
|
|
1140
|
+
if (product.isFlexibleDuration) {
|
|
1141
|
+
flexibleEnds(start).forEach(end => addRange(start, end));
|
|
1142
|
+
} else {
|
|
1143
|
+
addRange(start, start + duration * 60000);
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
const editingAt = request.editingStartAtByProductId?.[String(product.id)];
|
|
1147
|
+
if (editingAt !== undefined) {
|
|
1148
|
+
const start = toDateTime(editingAt);
|
|
1149
|
+
const editingDuration = Number(request.durationMinutesByProductId?.[String(product.id)]);
|
|
1150
|
+
if (!Number.isFinite(start) || !Number.isInteger(editingDuration) || editingDuration <= 0) {
|
|
1151
|
+
throw availabilityError('INVALID_TARGET_FILTER', '编辑时间必须包含完整开始日期时间和明确的正整数时长');
|
|
1152
|
+
}
|
|
1153
|
+
// Editing restores the original interval, including its actual overnight
|
|
1154
|
+
// date. It neither adopts the current grid nor recomputes a flexible end.
|
|
1155
|
+
if (request.allowOutsideOperatingHoursForRequestedStartTimes || operatingRanges.some(range => range.start <= start && start < range.end)) {
|
|
1156
|
+
addRange(start, addMinutesZoned(start, editingDuration, projection.meta.timezone));
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
// Explicit custom input keeps its existing start + requested duration
|
|
1161
|
+
// semantics. Remembered input above must never use this default-30 path.
|
|
1162
|
+
if (dateRange.startDate === dateRange.endDate) {
|
|
1163
|
+
resolveRequestedDurationStartTimes(product, request).forEach(startTime => {
|
|
1164
|
+
const sameDayStart = toDateTimeInDate(dateRange.startDate, startTime);
|
|
1165
|
+
const period = periods.find(item => item.businessDate === dateRange.startDate);
|
|
1166
|
+
const periodRange = period ? normalizeAbsoluteRange(period) : null;
|
|
1167
|
+
const nextDayStart = (0, _localClock.addLocalCalendarDays)(sameDayStart, 1);
|
|
1168
|
+
// HH:mm is entered within a selected business day, whose early hours
|
|
1169
|
+
// may belong to the next calendar day. Do not move other out-of-hours
|
|
1170
|
+
// manual input to tomorrow merely because the business closes overnight.
|
|
1171
|
+
const start = periodRange && !(periodRange.start <= sameDayStart && sameDayStart < periodRange.end) && periodRange.start <= nextDayStart && nextDayStart < periodRange.end ? nextDayStart : sameDayStart;
|
|
1172
|
+
if (request.allowOutsideOperatingHoursForRequestedStartTimes || operatingRanges.some(range => range.start <= start && start < range.end)) {
|
|
1173
|
+
addRange(start, start + duration * 60000);
|
|
1174
|
+
}
|
|
1175
|
+
});
|
|
1176
|
+
}
|
|
1177
|
+
return {
|
|
1178
|
+
productId: product.id,
|
|
1179
|
+
title: product.title,
|
|
1180
|
+
kind: product.kind,
|
|
1181
|
+
slotStepMinutes: step,
|
|
1182
|
+
...(operatingEndAt ? {
|
|
1183
|
+
operatingEndAt
|
|
1184
|
+
} : {}),
|
|
1185
|
+
ranges: dedupeProductRanges(ranges),
|
|
1186
|
+
...(unavailableCurrentTime ? {
|
|
1187
|
+
unavailableCurrentTime
|
|
1188
|
+
} : {})
|
|
1189
|
+
};
|
|
1190
|
+
}
|
|
995
1191
|
function getProductTimeRanges(projection, request = {}, runtimeOptions = {}) {
|
|
996
1192
|
const normalizedPartySize = normalizePartySize(request.partySize);
|
|
997
1193
|
const dateRange = resolveProjectionDateRange(projection, request.dateRange);
|
|
@@ -1044,6 +1240,19 @@ function getProductTimeRanges(projection, request = {}, runtimeOptions = {}) {
|
|
|
1044
1240
|
includeExcludedWindows: allowExcludedScheduleDateOverride
|
|
1045
1241
|
})).reduce((current, ranges) => current === null ? ranges : intersectRangeLists(current, ranges), null) || [] : null;
|
|
1046
1242
|
const baseRanges = resourceRanges === null ? operatingRanges : intersectRangeLists(resourceRanges, operatingRanges);
|
|
1243
|
+
if (runtimeOptions.useDurationTimeGrid) {
|
|
1244
|
+
return getAlignedDurationRanges({
|
|
1245
|
+
projection,
|
|
1246
|
+
product,
|
|
1247
|
+
request,
|
|
1248
|
+
runtimeOptions,
|
|
1249
|
+
dateRange,
|
|
1250
|
+
operatingRanges,
|
|
1251
|
+
baseRanges,
|
|
1252
|
+
resourceWindowIndex,
|
|
1253
|
+
fixedOffsetMinutes
|
|
1254
|
+
});
|
|
1255
|
+
}
|
|
1047
1256
|
const durationMinutes = resolveRequestedDurationMinutes(product, request);
|
|
1048
1257
|
const flexibleStartAt = (() => {
|
|
1049
1258
|
if (!product.isFlexibleDuration) return undefined;
|
|
@@ -1265,7 +1474,9 @@ function buildEvaluationContext(params) {
|
|
|
1265
1474
|
selectionsByGroupKey,
|
|
1266
1475
|
useExactDurationCandidateMinute: params.useExactDurationCandidateMinute === true,
|
|
1267
1476
|
durationCandidateAnchorAtMs: Number.isFinite(durationCandidateAnchorAt) ? floorToMinute(durationCandidateAnchorAt) : null,
|
|
1268
|
-
allowExcludedScheduleDateOverride: params.allowExcludedScheduleDateOverride === true
|
|
1477
|
+
allowExcludedScheduleDateOverride: params.allowExcludedScheduleDateOverride === true,
|
|
1478
|
+
useDurationTimeGrid: params.useDurationTimeGrid === true,
|
|
1479
|
+
enforceSaleCutoff: params.enforceSaleCutoff === true
|
|
1269
1480
|
};
|
|
1270
1481
|
}
|
|
1271
1482
|
function summarizeStatuses(statuses) {
|
|
@@ -1672,6 +1883,8 @@ function queryAvailabilityProjection(projection, request, runtimeOptions = {}) {
|
|
|
1672
1883
|
projection,
|
|
1673
1884
|
evaluatedAt,
|
|
1674
1885
|
resourceIds: request.resourceIds,
|
|
1886
|
+
useDurationTimeGrid: runtimeOptions.useDurationTimeGrid,
|
|
1887
|
+
enforceSaleCutoff: runtimeOptions.enforceSaleCutoff,
|
|
1675
1888
|
useExactDurationCandidateMinute: runtimeOptions.useExactDurationCandidateMinute,
|
|
1676
1889
|
allowExcludedScheduleDateOverride: runtimeOptions.allowExcludedScheduleDateOverride
|
|
1677
1890
|
});
|
|
@@ -1748,6 +1961,8 @@ function queryAvailabilityProjection(projection, request, runtimeOptions = {}) {
|
|
|
1748
1961
|
partySize: normalizedPartySize
|
|
1749
1962
|
}, {
|
|
1750
1963
|
evaluatedAt,
|
|
1964
|
+
useDurationTimeGrid: runtimeOptions.useDurationTimeGrid,
|
|
1965
|
+
enforceSaleCutoff: runtimeOptions.enforceSaleCutoff,
|
|
1751
1966
|
useExactDurationCandidateMinute: runtimeOptions.useExactDurationCandidateMinute,
|
|
1752
1967
|
allowExcludedScheduleDateOverride: runtimeOptions.allowExcludedScheduleDateOverride
|
|
1753
1968
|
});
|
|
@@ -1772,7 +1987,8 @@ function queryAvailabilityProjection(projection, request, runtimeOptions = {}) {
|
|
|
1772
1987
|
candidateRangeCache,
|
|
1773
1988
|
evaluationContext
|
|
1774
1989
|
});
|
|
1775
|
-
const
|
|
1990
|
+
const candidateDate = runtimeOptions.useDurationTimeGrid ? candidate.businessDate || candidate.date : candidate.date;
|
|
1991
|
+
const aggregate = byDate.get(candidateDate) || {
|
|
1776
1992
|
availableCount: 0,
|
|
1777
1993
|
totalCount: 0,
|
|
1778
1994
|
maxPartySize: 0,
|
|
@@ -1790,8 +2006,24 @@ function queryAvailabilityProjection(projection, request, runtimeOptions = {}) {
|
|
|
1790
2006
|
aggregate.maxCapacity += evaluated.maxCapacity;
|
|
1791
2007
|
pushStatusAggregate(aggregate.statusAggregate, evaluated.status);
|
|
1792
2008
|
evaluated.reasonCodes.forEach(reasonCode => aggregate.reasonCodeSet.add(reasonCode));
|
|
1793
|
-
byDate.set(
|
|
2009
|
+
byDate.set(candidateDate, aggregate);
|
|
1794
2010
|
});
|
|
2011
|
+
if (group.unavailableCurrentTime) {
|
|
2012
|
+
const current = group.unavailableCurrentTime;
|
|
2013
|
+
const aggregate = byDate.get(current.businessDate) || {
|
|
2014
|
+
availableCount: 0,
|
|
2015
|
+
totalCount: 0,
|
|
2016
|
+
maxPartySize: 0,
|
|
2017
|
+
remainingCapacity: 0,
|
|
2018
|
+
maxCapacity: 0,
|
|
2019
|
+
reasonCodeSet: new Set(),
|
|
2020
|
+
statusAggregate: createStatusAggregate()
|
|
2021
|
+
};
|
|
2022
|
+
aggregate.totalCount += 1;
|
|
2023
|
+
pushStatusAggregate(aggregate.statusAggregate, 'unavailable');
|
|
2024
|
+
current.reasonCodes.forEach(reason => aggregate.reasonCodeSet.add(reason));
|
|
2025
|
+
byDate.set(current.businessDate, aggregate);
|
|
2026
|
+
}
|
|
1795
2027
|
const dates = enumerateDates(dateRange.startDate, dateRange.endDate).map(date => {
|
|
1796
2028
|
const aggregate = byDate.get(date);
|
|
1797
2029
|
return {
|
|
@@ -1849,10 +2081,20 @@ function queryAvailabilityProjection(projection, request, runtimeOptions = {}) {
|
|
|
1849
2081
|
maxPartySize = Math.max(maxPartySize, evaluated.maxPartySize);
|
|
1850
2082
|
pushStatusAggregate(productStatusAggregate, evaluated.status);
|
|
1851
2083
|
evaluated.reasonCodes.forEach(reasonCode => productReasonCodeSet.add(reasonCode));
|
|
1852
|
-
const
|
|
2084
|
+
const candidateDate = runtimeOptions.useDurationTimeGrid ? candidate.businessDate || candidate.date : candidate.date;
|
|
2085
|
+
const dateAggregate = dateStatusMap.get(candidateDate) || createStatusAggregate();
|
|
1853
2086
|
pushStatusAggregate(dateAggregate, evaluated.status);
|
|
1854
|
-
dateStatusMap.set(
|
|
2087
|
+
dateStatusMap.set(candidateDate, dateAggregate);
|
|
1855
2088
|
});
|
|
2089
|
+
if (group.unavailableCurrentTime) {
|
|
2090
|
+
const current = group.unavailableCurrentTime;
|
|
2091
|
+
totalTimeCount += 1;
|
|
2092
|
+
pushStatusAggregate(productStatusAggregate, 'unavailable');
|
|
2093
|
+
current.reasonCodes.forEach(reason => productReasonCodeSet.add(reason));
|
|
2094
|
+
const dateAggregate = dateStatusMap.get(current.businessDate) || createStatusAggregate();
|
|
2095
|
+
pushStatusAggregate(dateAggregate, 'unavailable');
|
|
2096
|
+
dateStatusMap.set(current.businessDate, dateAggregate);
|
|
2097
|
+
}
|
|
1856
2098
|
const availableDateCount = Array.from(dateStatusMap.values()).filter(aggregate => {
|
|
1857
2099
|
const status = summarizeStatusAggregate(aggregate);
|
|
1858
2100
|
return status === 'available' || status === 'limited';
|
|
@@ -1921,6 +2163,8 @@ function validateAvailabilitySelection(projection, params, runtimeOptions = {})
|
|
|
1921
2163
|
projection,
|
|
1922
2164
|
evaluatedAt,
|
|
1923
2165
|
requirementSelections: params.requirementSelections,
|
|
2166
|
+
useDurationTimeGrid: runtimeOptions.useDurationTimeGrid,
|
|
2167
|
+
enforceSaleCutoff: runtimeOptions.enforceSaleCutoff,
|
|
1924
2168
|
useExactDurationCandidateMinute: runtimeOptions.useExactDurationCandidateMinute,
|
|
1925
2169
|
allowExcludedScheduleDateOverride: runtimeOptions.allowExcludedScheduleDateOverride
|
|
1926
2170
|
})
|
|
@@ -92,6 +92,9 @@ export interface AvailabilityProduct {
|
|
|
92
92
|
raw?: Record<string, any>;
|
|
93
93
|
}
|
|
94
94
|
export interface AvailabilityResourceWindow extends AvailabilityAbsoluteRange {
|
|
95
|
+
/** Original business day and opening anchor, before coverage clipping. */
|
|
96
|
+
businessDate?: string;
|
|
97
|
+
openingAnchorAt?: string;
|
|
95
98
|
source?: string;
|
|
96
99
|
raw?: Record<string, any>;
|
|
97
100
|
}
|
|
@@ -101,10 +104,15 @@ export interface AvailabilityOperatingHoursSnapshot {
|
|
|
101
104
|
scheduleIds: AvailabilityId[];
|
|
102
105
|
missingScheduleIds: AvailabilityId[];
|
|
103
106
|
windows: AvailabilityResourceWindow[];
|
|
107
|
+
/** Complete business cycles; lunch gaps remain in windows, not in this envelope. */
|
|
108
|
+
businessPeriods?: AvailabilityBusinessPeriod[];
|
|
104
109
|
/** Schedule windows omitted only because their date is excluded. POS may use
|
|
105
110
|
* these to surface an unavailable, manually selectable candidate. */
|
|
106
111
|
excludedWindows?: AvailabilityResourceWindow[];
|
|
107
112
|
}
|
|
113
|
+
export interface AvailabilityBusinessPeriod extends AvailabilityAbsoluteRange {
|
|
114
|
+
businessDate: string;
|
|
115
|
+
}
|
|
108
116
|
export interface AvailabilityResource {
|
|
109
117
|
id: AvailabilityId;
|
|
110
118
|
formId?: AvailabilityId;
|
|
@@ -212,6 +220,17 @@ export interface ProductTimeRange extends AvailabilityDisplayRange {
|
|
|
212
220
|
bookingEndAt: string;
|
|
213
221
|
scheduleRefs: AvailabilityScheduleRef[];
|
|
214
222
|
primaryScheduleRef?: AvailabilityScheduleRef;
|
|
223
|
+
/** Business-day label. date/startAt keep the actual calendar date. */
|
|
224
|
+
businessDate?: string;
|
|
225
|
+
}
|
|
226
|
+
export interface UnavailableCurrentTime {
|
|
227
|
+
productId: AvailabilityId;
|
|
228
|
+
startAt: string;
|
|
229
|
+
date: string;
|
|
230
|
+
businessDate: string;
|
|
231
|
+
startTime: string;
|
|
232
|
+
status: 'unavailable';
|
|
233
|
+
reasonCodes: string[];
|
|
215
234
|
}
|
|
216
235
|
export interface ProductTimeRangeGroup {
|
|
217
236
|
productId: AvailabilityId;
|
|
@@ -221,6 +240,8 @@ export interface ProductTimeRangeGroup {
|
|
|
221
240
|
/** 单日查询对应营业窗口的最终结束时刻,不受资源排班是否为空影响。 */
|
|
222
241
|
operatingEndAt?: string;
|
|
223
242
|
ranges: ProductTimeRange[];
|
|
243
|
+
/** A display-only current item when flexible duration has no valid end. Never submit it. */
|
|
244
|
+
unavailableCurrentTime?: UnavailableCurrentTime;
|
|
224
245
|
}
|
|
225
246
|
export interface AvailabilityAssignment extends AvailabilityAbsoluteRange {
|
|
226
247
|
productId: AvailabilityId;
|
|
@@ -242,6 +263,10 @@ export interface GetProductTimeRangesRequest {
|
|
|
242
263
|
partySize?: number;
|
|
243
264
|
durationMinutesByProductId?: Record<string, number>;
|
|
244
265
|
durationStartTimesByProductId?: Record<string, string[]>;
|
|
266
|
+
/** Restore only the exact remembered start; recompute the end from current product/windows. */
|
|
267
|
+
rememberedStartAtByProductId?: Record<string, string>;
|
|
268
|
+
/** Edit only: preserve the full actual start plus an explicit durationMinutesByProductId. */
|
|
269
|
+
editingStartAtByProductId?: Record<string, string>;
|
|
245
270
|
/**
|
|
246
271
|
* POS 手工输入时间的兼容开关:只为明确请求的 start time 补候选,
|
|
247
272
|
* 不改变常规候选列表仍按营业时间生成的规则。
|
|
@@ -250,6 +275,10 @@ export interface GetProductTimeRangesRequest {
|
|
|
250
275
|
}
|
|
251
276
|
export interface GetProductTimeRangesRuntimeOptions {
|
|
252
277
|
evaluatedAt?: string;
|
|
278
|
+
/** Enabled by the POS/Kiosk facade only; other consumers retain their existing grid. */
|
|
279
|
+
useDurationTimeGrid?: boolean;
|
|
280
|
+
/** Automatic remembered selection must not use POS opening-minute cutoff exceptions. */
|
|
281
|
+
enforceSaleCutoff?: boolean;
|
|
253
282
|
/**
|
|
254
283
|
* Internal POS capability: fixed-duration candidates start from the stable
|
|
255
284
|
* context anchor's current minute instead of rounding up to ten minutes, and
|
|
@@ -8,6 +8,7 @@ var _BaseModule = require("../BaseModule");
|
|
|
8
8
|
var _types = require("./types");
|
|
9
9
|
var _utils = require("../../solution/ShopDiscount/utils");
|
|
10
10
|
var _utils2 = require("../Cart/utils");
|
|
11
|
+
var _utils3 = require("../Holder/utils");
|
|
11
12
|
var _decimal = _interopRequireDefault(require("decimal.js"));
|
|
12
13
|
var _lodashEs = require("lodash-es");
|
|
13
14
|
var _dayjs = _interopRequireDefault(require("dayjs"));
|
|
@@ -59,7 +60,7 @@ class RulesModule extends _BaseModule.BaseModule {
|
|
|
59
60
|
if (discount.holder?.holder_type !== 'form') return true;
|
|
60
61
|
|
|
61
62
|
// getProduct 会裁剪商品字段,从原始商品读取配置,套餐子项沿用父商品。
|
|
62
|
-
const holderConfig =
|
|
63
|
+
const holderConfig = (0, _utils3.resolveProductHolderConfig)(holderSource);
|
|
63
64
|
const holderStatus = holderConfig?.status;
|
|
64
65
|
const holderEnabled = holderStatus === true || ['enable', '1'].includes(String(holderStatus).trim().toLowerCase());
|
|
65
66
|
if (!holderEnabled || holderConfig?.resource_id === undefined || holderConfig?.resource_id === null || holderConfig?.resource_id === '') {
|
|
@@ -10,6 +10,10 @@ export declare class ScheduleModule extends BaseModule implements Module, Schedu
|
|
|
10
10
|
private openCache;
|
|
11
11
|
private fatherModule;
|
|
12
12
|
private hasLoadedScheduleList;
|
|
13
|
+
private scheduleListPromise;
|
|
14
|
+
private scheduleListRequestVersion;
|
|
15
|
+
private scheduleCatalogScope;
|
|
16
|
+
private otherParams;
|
|
13
17
|
constructor(name?: string, version?: string);
|
|
14
18
|
initialize(core: PisellCore, options: ModuleOptions): Promise<void>;
|
|
15
19
|
/**
|
|
@@ -19,6 +23,9 @@ export declare class ScheduleModule extends BaseModule implements Module, Schedu
|
|
|
19
23
|
* scheduleModule.updateOtherParams({ openCache: true, cacheId: 'cache-2' });
|
|
20
24
|
*/
|
|
21
25
|
updateOtherParams(params: Record<string, any>): void;
|
|
26
|
+
private syncScheduleCatalogScope;
|
|
27
|
+
private resetScheduleCatalog;
|
|
28
|
+
destroy(): void;
|
|
22
29
|
/**
|
|
23
30
|
* 加载当前店铺下所有 schedule
|
|
24
31
|
*
|
|
@@ -27,6 +34,7 @@ export declare class ScheduleModule extends BaseModule implements Module, Schedu
|
|
|
27
34
|
loadAllSchedule(options?: {
|
|
28
35
|
useCache?: boolean;
|
|
29
36
|
}): Promise<void>;
|
|
37
|
+
private loadScheduleCatalog;
|
|
30
38
|
setScheduleList(list: ScheduleItem[]): void;
|
|
31
39
|
private syncScheduleMap;
|
|
32
40
|
getScheduleList(): ScheduleItem[];
|