@pisell/pisellos 0.10.54 → 0.10.56
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/modules/BusinessAvailability/index.d.ts +30 -0
- package/dist/modules/BusinessAvailability/index.js +204 -0
- package/dist/modules/OpenData/types.d.ts +2 -0
- package/dist/modules/Order/index.d.ts +4 -1
- package/dist/modules/Order/index.js +315 -241
- package/dist/modules/Order/types.d.ts +8 -0
- package/dist/plugins/request.d.ts +4 -0
- package/dist/shared/receipt/loadCustomerInvoiceLayout.d.ts +22 -0
- package/dist/shared/receipt/loadCustomerInvoiceLayout.js +142 -0
- package/dist/solution/BaseSales/index.d.ts +4 -15
- package/dist/solution/BaseSales/index.js +131 -86
- package/dist/solution/BaseSales/types.d.ts +11 -0
- package/dist/solution/BookingByStep/index.d.ts +1 -1
- package/dist/solution/BookingTicket/index.d.ts +1 -1
- package/dist/solution/BookingTicket/index.js +41 -13
- package/dist/solution/OrderInvoicePreview/index.js +6 -6
- package/dist/solution/UnifiedBookingSales/businessAvailability.d.ts +27 -0
- package/dist/solution/UnifiedBookingSales/businessAvailability.js +292 -0
- package/dist/solution/UnifiedBookingSales/index.d.ts +11 -0
- package/dist/solution/UnifiedBookingSales/index.js +154 -75
- package/dist/solution/UnifiedBookingSales/types.d.ts +2 -0
- package/lib/model/strategy/adapter/promotion/index.js +1 -46
- package/lib/modules/BusinessAvailability/index.d.ts +30 -0
- package/lib/modules/BusinessAvailability/index.js +146 -0
- package/lib/modules/OpenData/types.d.ts +2 -0
- package/lib/modules/Order/index.d.ts +4 -1
- package/lib/modules/Order/index.js +62 -6
- package/lib/modules/Order/types.d.ts +8 -0
- package/lib/plugins/request.d.ts +4 -0
- package/lib/shared/receipt/loadCustomerInvoiceLayout.d.ts +22 -0
- package/lib/shared/receipt/loadCustomerInvoiceLayout.js +86 -0
- package/lib/solution/BaseSales/index.d.ts +4 -15
- package/lib/solution/BaseSales/index.js +45 -24
- package/lib/solution/BaseSales/types.d.ts +11 -0
- package/lib/solution/BookingByStep/index.d.ts +1 -1
- package/lib/solution/BookingTicket/index.d.ts +1 -1
- package/lib/solution/BookingTicket/index.js +37 -4
- package/lib/solution/OrderInvoicePreview/index.js +2 -4
- package/lib/solution/UnifiedBookingSales/businessAvailability.d.ts +27 -0
- package/lib/solution/UnifiedBookingSales/businessAvailability.js +138 -0
- package/lib/solution/UnifiedBookingSales/index.d.ts +11 -0
- package/lib/solution/UnifiedBookingSales/index.js +54 -0
- package/lib/solution/UnifiedBookingSales/types.d.ts +2 -0
- package/package.json +1 -1
|
@@ -2432,7 +2432,7 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
|
|
|
2432
2432
|
// Deprecated compatibility alias. New BaseSales callers should use submitSalesOrder.
|
|
2433
2433
|
return this.submitSalesOrder(params);
|
|
2434
2434
|
}
|
|
2435
|
-
|
|
2435
|
+
buildVirtualSettlementOrderData() {
|
|
2436
2436
|
if (!this.store.order) return null;
|
|
2437
2437
|
const tempOrder = this.store.order.getTempOrder();
|
|
2438
2438
|
if (!this.isActiveVirtualSettlementOrder(tempOrder)) return null;
|
|
@@ -2441,6 +2441,21 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
|
|
|
2441
2441
|
if (!snapshot || !Number.isFinite(currencyProductId) || currencyProductId <= 0) {
|
|
2442
2442
|
throw new Error('Virtual currency settlement configuration is invalid.');
|
|
2443
2443
|
}
|
|
2444
|
+
return {
|
|
2445
|
+
snapshot,
|
|
2446
|
+
currencyProductId,
|
|
2447
|
+
currencyCode: String(tempOrder?.currency_code || ''),
|
|
2448
|
+
currencySymbol: String(tempOrder?.currency_symbol || ''),
|
|
2449
|
+
currencyFormat: String(tempOrder?.currency_format || 'symbol_first')
|
|
2450
|
+
};
|
|
2451
|
+
}
|
|
2452
|
+
async buildVirtualSettlementSubmitData() {
|
|
2453
|
+
const settlement = this.buildVirtualSettlementOrderData();
|
|
2454
|
+
if (!settlement || !this.store.order) return null;
|
|
2455
|
+
const tempOrder = this.store.order.getTempOrder();
|
|
2456
|
+
const {
|
|
2457
|
+
currencyProductId
|
|
2458
|
+
} = settlement;
|
|
2444
2459
|
const summary = await this.store.order.recalculateSummary({
|
|
2445
2460
|
createIfMissing: true
|
|
2446
2461
|
});
|
|
@@ -2455,17 +2470,10 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
|
|
|
2455
2470
|
});
|
|
2456
2471
|
}
|
|
2457
2472
|
const asset = this.store.order.getVirtualCurrencyList().find(item => Number(item?.product_id) === currencyProductId);
|
|
2458
|
-
const currencyCode = String(tempOrder?.currency_code || '');
|
|
2459
|
-
const currencySymbol = String(tempOrder?.currency_symbol || '');
|
|
2460
|
-
const currencyFormat = String(tempOrder?.currency_format || 'symbol_first');
|
|
2461
2473
|
if (amount.lte(0)) {
|
|
2462
2474
|
return {
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
currencyProductId,
|
|
2466
|
-
currencyCode,
|
|
2467
|
-
currencySymbol,
|
|
2468
|
-
currencyFormat
|
|
2475
|
+
...settlement,
|
|
2476
|
+
payments: []
|
|
2469
2477
|
};
|
|
2470
2478
|
}
|
|
2471
2479
|
if (!asset) {
|
|
@@ -2519,12 +2527,8 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
|
|
|
2519
2527
|
};
|
|
2520
2528
|
}
|
|
2521
2529
|
return {
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
currencyProductId,
|
|
2525
|
-
currencyCode,
|
|
2526
|
-
currencySymbol,
|
|
2527
|
-
currencyFormat
|
|
2530
|
+
...settlement,
|
|
2531
|
+
payments: [payment]
|
|
2528
2532
|
};
|
|
2529
2533
|
}
|
|
2530
2534
|
|
|
@@ -2541,9 +2545,11 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
|
|
|
2541
2545
|
if (this.isPickupTimingEnabled()) await this.preparePickupTimingForCheckout({
|
|
2542
2546
|
freeze: true
|
|
2543
2547
|
});
|
|
2544
|
-
const
|
|
2548
|
+
const isDeferredPayment = params?.paymentExecution === 'deferred';
|
|
2549
|
+
const virtualPaymentSettlement = isDeferredPayment ? null : await this.buildVirtualSettlementSubmitData();
|
|
2550
|
+
const virtualSettlement = isDeferredPayment ? this.buildVirtualSettlementOrderData() : virtualPaymentSettlement;
|
|
2545
2551
|
// this.store.order.persistTempOrder();
|
|
2546
|
-
const inferredPaymentStatus = this.getSubmitPaymentStatus(params?.paymentStatus);
|
|
2552
|
+
const inferredPaymentStatus = isDeferredPayment ? undefined : this.getSubmitPaymentStatus(params?.paymentStatus);
|
|
2547
2553
|
const smallTicketDataFlag = params?.smallTicketDataFlag ?? this.getDefaultCheckoutSmallTicketDataFlag();
|
|
2548
2554
|
const virtualSettlementEnhancePayload = virtualSettlement ? (payload, ctx) => {
|
|
2549
2555
|
const callerPayload = params?.enhancePayload ? params.enhancePayload(payload, ctx) : payload;
|
|
@@ -2564,7 +2570,20 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
|
|
|
2564
2570
|
currency_format: virtualSettlement.currencyFormat
|
|
2565
2571
|
};
|
|
2566
2572
|
} : params?.enhancePayload;
|
|
2567
|
-
const
|
|
2573
|
+
const configuredEnhancePayload = this.buildSubmitPayloadEnhancerWithOpenDataConfig(virtualSettlementEnhancePayload);
|
|
2574
|
+
// Apply this last so neither a stale draft status nor a caller enhancer can
|
|
2575
|
+
// turn deferred submission into payment. The live wallet selection is untouched.
|
|
2576
|
+
const enhancePayload = isDeferredPayment ? (payload, ctx) => {
|
|
2577
|
+
const enhancedPayload = configuredEnhancePayload ? configuredEnhancePayload(payload, ctx) : payload;
|
|
2578
|
+
const {
|
|
2579
|
+
payment_status: _paymentStatus,
|
|
2580
|
+
...orderPayload
|
|
2581
|
+
} = enhancedPayload;
|
|
2582
|
+
return {
|
|
2583
|
+
...orderPayload,
|
|
2584
|
+
payments: []
|
|
2585
|
+
};
|
|
2586
|
+
} : configuredEnhancePayload;
|
|
2568
2587
|
const submitParams = {
|
|
2569
2588
|
cacheId: this.cacheId,
|
|
2570
2589
|
platform: this.otherParams?.platform,
|
|
@@ -2572,8 +2591,10 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
|
|
|
2572
2591
|
channel: this.getSubmitOrderSalesChannel(),
|
|
2573
2592
|
type: this.otherParams?.type,
|
|
2574
2593
|
request_unique_idempotency_token: this.store.order.generateIdempotencyToken(),
|
|
2575
|
-
...(
|
|
2576
|
-
payments:
|
|
2594
|
+
...(isDeferredPayment ? {
|
|
2595
|
+
payments: []
|
|
2596
|
+
} : virtualPaymentSettlement ? {
|
|
2597
|
+
payments: virtualPaymentSettlement.payments
|
|
2577
2598
|
} : params?.payments !== undefined ? {
|
|
2578
2599
|
payments: params.payments
|
|
2579
2600
|
} : {}),
|
|
@@ -2583,7 +2604,7 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
|
|
|
2583
2604
|
...(smallTicketDataFlag !== undefined ? {
|
|
2584
2605
|
smallTicketDataFlag
|
|
2585
2606
|
} : {}),
|
|
2586
|
-
...(virtualSettlement ? {
|
|
2607
|
+
...(isDeferredPayment || virtualSettlement ? {
|
|
2587
2608
|
confirmPendingVoucherPayments: false
|
|
2588
2609
|
} : params?.confirmPendingVoucherPayments !== undefined ? {
|
|
2589
2610
|
confirmPendingVoucherPayments: params.confirmPendingVoucherPayments
|
|
@@ -3921,7 +3942,7 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
|
|
|
3921
3942
|
async addProductToOrder(product, booking, options) {
|
|
3922
3943
|
try {
|
|
3923
3944
|
if (!this.store.order) throw new Error('order 模块未初始化');
|
|
3924
|
-
const products = await this.store.order.addProductToOrder(product, booking, options);
|
|
3945
|
+
const products = options === undefined ? await this.store.order.addProductToOrder(product, booking) : await this.store.order.addProductToOrder(product, booking, options);
|
|
3925
3946
|
// // 需要叠加
|
|
3926
3947
|
// if (this.hooks.onAddProductOverlay) {
|
|
3927
3948
|
// const res = this.hooks.onAddProductOverlay(是否叠加, product)
|
|
@@ -4010,7 +4031,7 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
|
|
|
4010
4031
|
async removeProductsFromOrder(identities, options) {
|
|
4011
4032
|
try {
|
|
4012
4033
|
if (!this.store.order) throw new Error('order 模块未初始化');
|
|
4013
|
-
return await this.store.order.removeProductsFromOrder(identities, options);
|
|
4034
|
+
return options === undefined ? await this.store.order.removeProductsFromOrder(identities) : await this.store.order.removeProductsFromOrder(identities, options);
|
|
4014
4035
|
} catch (error) {
|
|
4015
4036
|
throw error;
|
|
4016
4037
|
}
|
|
@@ -4,6 +4,8 @@ import type { ScanOrderLogInput as BaseSalesLogInput, ScanOrderLoggerProviderCon
|
|
|
4
4
|
import type { QuantityCheckResult, QuantityLimitResult } from '../../model/strategy/adapter/itemRule';
|
|
5
5
|
import type { QuotationCustomerScopeInfo } from '../../modules/Quotation/types';
|
|
6
6
|
import type { PaymentMethod } from '../../modules/Payment/types';
|
|
7
|
+
import type { OrderPaymentSource } from '../../modules/Order/types';
|
|
8
|
+
import type { SubmitPayloadEnhancer } from '../../modules/Order/utils';
|
|
7
9
|
/**
|
|
8
10
|
* BaseSales 流程 hook 后缀。
|
|
9
11
|
* 完整事件名应由当前模块名动态拼接,避免多个实例共享固定事件 key。
|
|
@@ -23,6 +25,15 @@ export type BaseSalesHookName = typeof BaseSalesHookNames[keyof typeof BaseSales
|
|
|
23
25
|
export declare function createBaseSalesHook(moduleName: string, hookName: BaseSalesHookName): string;
|
|
24
26
|
export type BaseSalesStatus = 'idle' | 'initializing' | 'ready' | 'error';
|
|
25
27
|
export type BaseSalesPaymentStatus = 'paid' | 'partially_paid' | 'unpaid' | 'payment_processing' | string;
|
|
28
|
+
export interface BaseSalesSubmitSalesOrderParams {
|
|
29
|
+
payments?: OrderPaymentSource[];
|
|
30
|
+
paymentStatus?: BaseSalesPaymentStatus;
|
|
31
|
+
smallTicketDataFlag?: number;
|
|
32
|
+
confirmPendingVoucherPayments?: boolean;
|
|
33
|
+
enhancePayload?: SubmitPayloadEnhancer;
|
|
34
|
+
/** Create the order without executing payment; payment status comes from the server. */
|
|
35
|
+
paymentExecution?: 'deferred';
|
|
36
|
+
}
|
|
26
37
|
export interface BaseSalesCashPaymentConfig {
|
|
27
38
|
available: boolean;
|
|
28
39
|
paymentMethod?: PaymentMethod;
|
|
@@ -326,7 +326,7 @@ export declare class BookingByStepImpl extends BaseModule implements Module {
|
|
|
326
326
|
date: string;
|
|
327
327
|
status: string;
|
|
328
328
|
week: string;
|
|
329
|
-
weekNum: 0 |
|
|
329
|
+
weekNum: 0 | 2 | 1 | 6 | 4 | 5 | 3;
|
|
330
330
|
}[]>;
|
|
331
331
|
submitTimeSlot(timeSlots: TimeSliceItem): void;
|
|
332
332
|
private getScheduleDataByIds;
|
|
@@ -345,7 +345,7 @@ export declare class BookingTicketImpl extends BaseSalesImpl implements Module {
|
|
|
345
345
|
* 获取当前的客户搜索条件
|
|
346
346
|
* @returns 当前搜索条件
|
|
347
347
|
*/
|
|
348
|
-
getCurrentCustomerSearchParams(): Omit<import("../../modules").ShopGetCustomerListParams, "
|
|
348
|
+
getCurrentCustomerSearchParams(): Omit<import("../../modules").ShopGetCustomerListParams, "num" | "skip">;
|
|
349
349
|
/**
|
|
350
350
|
* 获取客户列表状态(包含滚动加载相关状态)
|
|
351
351
|
* @returns 客户状态
|
|
@@ -1672,6 +1672,30 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
|
|
|
1672
1672
|
};
|
|
1673
1673
|
}
|
|
1674
1674
|
try {
|
|
1675
|
+
// Capture before lookup/UI awaits, so a result cannot land in a new cart.
|
|
1676
|
+
// Unscoped POS Order modules return a no-op guard.
|
|
1677
|
+
const beforeScanCommit = this.store.order.createCartIntentCommitGuard('add_product');
|
|
1678
|
+
const beforeScanDialog = this.store.order.createCartIntentCommitGuard('add_product');
|
|
1679
|
+
const guardCallback = callback => callback ? (...args) => {
|
|
1680
|
+
beforeScanCommit();
|
|
1681
|
+
return callback(...args);
|
|
1682
|
+
} : undefined;
|
|
1683
|
+
const guardedBridge = bridge ? {
|
|
1684
|
+
...bridge,
|
|
1685
|
+
confirmDetail: guardCallback(bridge.confirmDetail),
|
|
1686
|
+
confirmBookingEdit: guardCallback(bridge.confirmBookingEdit),
|
|
1687
|
+
applyPromotionCode: guardCallback(bridge.applyPromotionCode),
|
|
1688
|
+
requestCustomerSwitch: guardCallback(bridge.requestCustomerSwitch),
|
|
1689
|
+
openOrder: guardCallback(bridge.openOrder),
|
|
1690
|
+
openProductDetail: bridge.openProductDetail ? payload => {
|
|
1691
|
+
beforeScanDialog();
|
|
1692
|
+
return bridge.openProductDetail(payload);
|
|
1693
|
+
} : undefined,
|
|
1694
|
+
openBookingEdit: bridge.openBookingEdit ? payload => {
|
|
1695
|
+
beforeScanDialog();
|
|
1696
|
+
return bridge.openBookingEdit(payload);
|
|
1697
|
+
} : undefined
|
|
1698
|
+
} : undefined;
|
|
1675
1699
|
const localSearch = v => this.store.products.findProductsByCodeOrBarcode(v);
|
|
1676
1700
|
const scanCallback = (0, _handleScan.handleGlobalScan)(this.request, localSearch, this.getSubmitOrderSalesChannel());
|
|
1677
1701
|
const raw = await scanCallback({
|
|
@@ -1687,7 +1711,10 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
|
|
|
1687
1711
|
};
|
|
1688
1712
|
}
|
|
1689
1713
|
const result = await (0, _applyGlobalScan.applyGlobalScan)({
|
|
1690
|
-
setOrderCustomer: customer =>
|
|
1714
|
+
setOrderCustomer: customer => {
|
|
1715
|
+
beforeScanCommit();
|
|
1716
|
+
this.setOrderCustomer(customer);
|
|
1717
|
+
},
|
|
1691
1718
|
getOrderCustomerSnapshot: () => this.getOrderCustomerSnapshot(),
|
|
1692
1719
|
resolveCustomerById: customerId => this.resolveCustomerById(customerId),
|
|
1693
1720
|
getBusinessCode: () => this.otherParams?.businessCode,
|
|
@@ -1695,10 +1722,16 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
|
|
|
1695
1722
|
decideAddProduct: (item, options) => this.decideAddProduct(item, options),
|
|
1696
1723
|
decideAfterDetail: (cacheItem, options) => this.decideAfterDetail(cacheItem, options),
|
|
1697
1724
|
transformDetailToProductAndBooking: input => this.transformDetailToProductAndBooking(input),
|
|
1698
|
-
addProductToOrder: (product, booking) =>
|
|
1725
|
+
addProductToOrder: (product, booking) => {
|
|
1726
|
+
beforeScanCommit();
|
|
1727
|
+
return this.addProductToOrder(product, booking);
|
|
1728
|
+
},
|
|
1699
1729
|
refreshWalletAssets: params => this.refreshWalletAssets(params),
|
|
1700
|
-
scanWalletAsset: scanCode =>
|
|
1701
|
-
|
|
1730
|
+
scanWalletAsset: scanCode => {
|
|
1731
|
+
beforeScanCommit();
|
|
1732
|
+
return this.scanWalletAsset(scanCode);
|
|
1733
|
+
}
|
|
1734
|
+
}, formatted, guardedBridge);
|
|
1702
1735
|
bridge?.refresh?.();
|
|
1703
1736
|
return result;
|
|
1704
1737
|
} catch (error) {
|
|
@@ -5,7 +5,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.OrderInvoicePreviewImpl = void 0;
|
|
7
7
|
var _BaseModule = require("../../modules/BaseModule");
|
|
8
|
-
var
|
|
8
|
+
var _loadCustomerInvoiceLayout = require("../../shared/receipt/loadCustomerInvoiceLayout");
|
|
9
9
|
var _smallTicket = require("../../shared/receipt/small-ticket");
|
|
10
10
|
var _loadSalesOrderSnapshot = require("../../shared/sales/loadSalesOrderSnapshot");
|
|
11
11
|
var _loadPaymentMethods = require("../../shared/payment/loadPaymentMethods");
|
|
@@ -124,10 +124,8 @@ class OrderInvoicePreviewImpl extends _BaseModule.BaseModule {
|
|
|
124
124
|
error: null
|
|
125
125
|
});
|
|
126
126
|
const task = (async () => {
|
|
127
|
-
const [snapshot, context] = await Promise.all([this.loadSource(input), this.loadContext(input)]);
|
|
127
|
+
const [snapshot, context, layout] = await Promise.all([this.loadSource(input), this.loadContext(input), (0, _loadCustomerInvoiceLayout.loadCustomerInvoiceLayout)(this.request, input)]);
|
|
128
128
|
const source = (0, _loadPaymentMethods.withPaymentMethodNames)(snapshot, context.methods);
|
|
129
|
-
// Customer templates will use a dedicated backend API. Do not request NocoBase.
|
|
130
|
-
const layout = (0, _OpenData.getDefaultInvoiceLayoutConfig)();
|
|
131
129
|
const getData = this.core.getPlugin('app')?.getData?.();
|
|
132
130
|
if (getData?.('shop_id') != null && String(getData('shop_id')) !== String(input.shopId)) throw new Error('Invoice shop changed');
|
|
133
131
|
const coreData = getData?.('core');
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type BusinessAvailability } from '../../modules/BusinessAvailability';
|
|
2
|
+
import type { ScheduleModule } from '../../modules/Schedule';
|
|
3
|
+
interface BusinessAvailabilityHost {
|
|
4
|
+
loadOpenData: () => Promise<Record<string, unknown> | null>;
|
|
5
|
+
getSchedule: () => ScheduleModule | undefined;
|
|
6
|
+
getTimezone: () => string;
|
|
7
|
+
getScope: () => string;
|
|
8
|
+
now?: () => Date;
|
|
9
|
+
}
|
|
10
|
+
/** Read successful configuration once per business runtime; evaluate the clock on demand. */
|
|
11
|
+
export declare class BusinessAvailabilityController {
|
|
12
|
+
private readonly host;
|
|
13
|
+
private runtime?;
|
|
14
|
+
constructor(host: BusinessAvailabilityHost);
|
|
15
|
+
invalidate(): void;
|
|
16
|
+
private now;
|
|
17
|
+
private getRuntime;
|
|
18
|
+
private assertCurrent;
|
|
19
|
+
private loadConfig;
|
|
20
|
+
private loadSchedules;
|
|
21
|
+
private error;
|
|
22
|
+
/** Synchronous final cart-commit check; deliberately never starts a load. */
|
|
23
|
+
getCachedAvailability(): BusinessAvailability;
|
|
24
|
+
getAvailability(): Promise<BusinessAvailability>;
|
|
25
|
+
assertAvailable(): void;
|
|
26
|
+
}
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.BusinessAvailabilityController = void 0;
|
|
7
|
+
var _lodashEs = require("lodash-es");
|
|
8
|
+
var _BusinessAvailability = require("../../modules/BusinessAvailability");
|
|
9
|
+
async function withTimeout(request) {
|
|
10
|
+
let timer;
|
|
11
|
+
try {
|
|
12
|
+
return await Promise.race([request, new Promise((_, reject) => {
|
|
13
|
+
timer = setTimeout(() => reject(new Error('Unable to load business settings. Please try again.')), 30_000);
|
|
14
|
+
})]);
|
|
15
|
+
} finally {
|
|
16
|
+
if (timer !== undefined) clearTimeout(timer);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Read successful configuration once per business runtime; evaluate the clock on demand. */
|
|
21
|
+
class BusinessAvailabilityController {
|
|
22
|
+
runtime;
|
|
23
|
+
constructor(host) {
|
|
24
|
+
this.host = host;
|
|
25
|
+
}
|
|
26
|
+
invalidate() {
|
|
27
|
+
this.runtime = undefined;
|
|
28
|
+
}
|
|
29
|
+
now() {
|
|
30
|
+
return this.host.now?.() ?? new Date();
|
|
31
|
+
}
|
|
32
|
+
getRuntime() {
|
|
33
|
+
const scope = this.host.getScope();
|
|
34
|
+
if (this.runtime?.scope !== scope) this.runtime = {
|
|
35
|
+
scope
|
|
36
|
+
};
|
|
37
|
+
return this.runtime;
|
|
38
|
+
}
|
|
39
|
+
assertCurrent(runtime) {
|
|
40
|
+
if (this.runtime !== runtime || runtime.scope !== this.host.getScope()) {
|
|
41
|
+
throw new Error('Store settings changed. Please try again.');
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async loadConfig(runtime) {
|
|
45
|
+
if (runtime.config) return runtime.config;
|
|
46
|
+
if (!runtime.configRequest) {
|
|
47
|
+
runtime.configRequest = (async () => {
|
|
48
|
+
const data = await withTimeout(this.host.loadOpenData());
|
|
49
|
+
this.assertCurrent(runtime);
|
|
50
|
+
if (!data) throw new Error('Business settings are unavailable. Please try again.');
|
|
51
|
+
const config = (0, _lodashEs.cloneDeep)((0, _BusinessAvailability.normalizeBusinessAvailabilityConfig)(data));
|
|
52
|
+
runtime.config = config;
|
|
53
|
+
return config;
|
|
54
|
+
})();
|
|
55
|
+
}
|
|
56
|
+
const request = runtime.configRequest;
|
|
57
|
+
try {
|
|
58
|
+
return await request;
|
|
59
|
+
} finally {
|
|
60
|
+
if (runtime.configRequest === request) runtime.configRequest = undefined;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
async loadSchedules(runtime) {
|
|
64
|
+
if (runtime.schedules) return runtime.schedules;
|
|
65
|
+
if (!runtime.scheduleRequest) {
|
|
66
|
+
runtime.scheduleRequest = (async () => {
|
|
67
|
+
const schedule = this.host.getSchedule();
|
|
68
|
+
if (!schedule) throw new Error('Business operating-hours schedule is unavailable. Please try again.');
|
|
69
|
+
await withTimeout(schedule.loadAllSchedule({
|
|
70
|
+
useCache: true
|
|
71
|
+
}));
|
|
72
|
+
this.assertCurrent(runtime);
|
|
73
|
+
const schedules = (0, _lodashEs.cloneDeep)(schedule.getScheduleListByIds(runtime.config.scheduleIds) || []);
|
|
74
|
+
runtime.schedules = schedules;
|
|
75
|
+
return schedules;
|
|
76
|
+
})();
|
|
77
|
+
}
|
|
78
|
+
const request = runtime.scheduleRequest;
|
|
79
|
+
try {
|
|
80
|
+
return await request;
|
|
81
|
+
} finally {
|
|
82
|
+
if (runtime.scheduleRequest === request) runtime.scheduleRequest = undefined;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
error(reason, runtime) {
|
|
86
|
+
return {
|
|
87
|
+
status: 'error',
|
|
88
|
+
behavior: 'hide_all',
|
|
89
|
+
message: null,
|
|
90
|
+
nextChangeAt: this.now().getTime() + 60_000,
|
|
91
|
+
requirePaymentOnSubmit: runtime?.config?.requirePaymentOnSubmit ?? true,
|
|
92
|
+
error: reason instanceof Error ? reason.message : 'Business settings are unavailable. Please try again.'
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Synchronous final cart-commit check; deliberately never starts a load. */
|
|
97
|
+
getCachedAvailability() {
|
|
98
|
+
const runtime = this.getRuntime();
|
|
99
|
+
try {
|
|
100
|
+
this.assertCurrent(runtime);
|
|
101
|
+
if (!runtime.config) throw new Error('Business settings are still loading. Please try again.');
|
|
102
|
+
return (0, _BusinessAvailability.computeBusinessAvailability)({
|
|
103
|
+
config: runtime.config,
|
|
104
|
+
schedules: runtime.schedules || [],
|
|
105
|
+
timezone: this.host.getTimezone(),
|
|
106
|
+
now: this.now()
|
|
107
|
+
});
|
|
108
|
+
} catch (reason) {
|
|
109
|
+
return this.error(reason, runtime);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
async getAvailability() {
|
|
113
|
+
const runtime = this.getRuntime();
|
|
114
|
+
try {
|
|
115
|
+
const config = await this.loadConfig(runtime);
|
|
116
|
+
this.assertCurrent(runtime);
|
|
117
|
+
if (!config.paused && !config.invalidScheduleIds && config.scheduleIds.length) {
|
|
118
|
+
await this.loadSchedules(runtime);
|
|
119
|
+
}
|
|
120
|
+
this.assertCurrent(runtime);
|
|
121
|
+
return this.getCachedAvailability();
|
|
122
|
+
} catch (reason) {
|
|
123
|
+
return this.error(reason, runtime);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
assertAvailable() {
|
|
127
|
+
const availability = this.getCachedAvailability();
|
|
128
|
+
if (availability.status !== 'open') {
|
|
129
|
+
const error = new Error(availability.error || (availability.status === 'paused' ? 'Ordering is temporarily paused.' : 'Ordering is currently closed.'));
|
|
130
|
+
Object.assign(error, {
|
|
131
|
+
code: 'BUSINESS_UNAVAILABLE',
|
|
132
|
+
businessAvailability: availability
|
|
133
|
+
});
|
|
134
|
+
throw error;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
exports.BusinessAvailabilityController = BusinessAvailabilityController;
|
|
@@ -5,9 +5,11 @@ import type { LoadScheduleAvailableDateParams, ScheduleItem } from '../../module
|
|
|
5
5
|
import type { ProductListLoadProductsParams, ProductListStoreQueryParams } from '../../modules/ProductList';
|
|
6
6
|
import type { AvailabilityDateRange, AvailabilityOccupancy, AvailabilityProduct, AvailabilityResource, GetProductTimeRangesRequest } from '../../modules/ResourcePlanner';
|
|
7
7
|
import type { AvailabilityContextRef, ContextualAvailabilityQuery, ContextualAvailabilityQueryResult, ContextualCommitAvailabilitySelectionParams, ContextualProductTimeRangeGroup, UnifiedBookingSalesAddRetailProductParams, UnifiedBookingSalesApplyPlannerDiscountsParams, UnifiedBookingSalesCommitAvailabilitySelectionResult, UnifiedBookingSalesDiscountResult, UnifiedBookingSalesLoadProductsByScheduleDateParams, UnifiedBookingSalesLoadOpenDataParams, UnifiedBookingSalesPrepareAvailabilityProjectionParams, UnifiedBookingSalesPolicyResponse, UnifiedBookingSalesResolveProductAllergyRequirementParams, UnifiedBookingSalesStageRetailProductsParams, UnifiedBookingSalesState } from './types';
|
|
8
|
+
import type { BusinessAvailability } from '../../modules/BusinessAvailability';
|
|
8
9
|
import { BookingTicket } from '../BookingTicket';
|
|
9
10
|
import type { AllergyRequirement } from '../../model/strategy/adapter/itemRule';
|
|
10
11
|
export * from './types';
|
|
12
|
+
export type { BusinessAvailability, BusinessClosedBehavior } from '../../modules/BusinessAvailability';
|
|
11
13
|
export { AvailabilityError } from '../../modules/ResourcePlanner';
|
|
12
14
|
/**
|
|
13
15
|
* Converts Rules/Order discount internals into a small UI-facing diagnostic. The result keeps
|
|
@@ -60,6 +62,7 @@ export declare class UnifiedBookingSalesImpl extends BookingTicket {
|
|
|
60
62
|
protected defaultVersion: string;
|
|
61
63
|
protected store: UnifiedBookingSalesState;
|
|
62
64
|
protected pickupTimingEnabled: boolean;
|
|
65
|
+
private businessAvailabilityController?;
|
|
63
66
|
private runtimeOpenDataScope;
|
|
64
67
|
private runtimeOpenDataSnapshot;
|
|
65
68
|
private runtimeOpenDataModule;
|
|
@@ -86,6 +89,14 @@ export declare class UnifiedBookingSalesImpl extends BookingTicket {
|
|
|
86
89
|
private allergyItemRuleConfigsPromise;
|
|
87
90
|
initialize(core: PisellCore, options?: ModuleOptions): Promise<void>;
|
|
88
91
|
protected getSubmitOrderSalesChannel(): string;
|
|
92
|
+
/** Only the explicit Kiosk Sales consumer entry enables this policy; POS stays unchanged. */
|
|
93
|
+
protected isBusinessAvailabilityEnabled(): boolean;
|
|
94
|
+
protected getBusinessAvailabilityScope(): string;
|
|
95
|
+
private getBusinessAvailabilityController;
|
|
96
|
+
getBusinessAvailability(): Promise<BusinessAvailability>;
|
|
97
|
+
/** Internal final-write assertion; does not fetch OpenData or Schedule. */
|
|
98
|
+
assertBusinessAvailability(): void;
|
|
99
|
+
protected getSubModuleHooks(moduleName: string): any;
|
|
89
100
|
private createAvailabilityFacadeError;
|
|
90
101
|
private getAvailabilityScopeDescriptor;
|
|
91
102
|
private normalizeAvailabilityIdList;
|
|
@@ -49,6 +49,7 @@ Object.keys(_types).forEach(function (key) {
|
|
|
49
49
|
});
|
|
50
50
|
});
|
|
51
51
|
var _remoteOccupancyCache = require("./remoteOccupancyCache");
|
|
52
|
+
var _businessAvailability = require("./businessAvailability");
|
|
52
53
|
var _BookingTicket = require("../BookingTicket");
|
|
53
54
|
var _localClock = require("../../modules/ResourcePlanner/localClock");
|
|
54
55
|
var _platform = require("../../utils/platform");
|
|
@@ -1199,6 +1200,7 @@ class UnifiedBookingSalesImpl extends _BookingTicket.BookingTicket {
|
|
|
1199
1200
|
defaultVersion = '1.0.0';
|
|
1200
1201
|
store = {};
|
|
1201
1202
|
pickupTimingEnabled = true;
|
|
1203
|
+
businessAvailabilityController;
|
|
1202
1204
|
runtimeOpenDataScope = null;
|
|
1203
1205
|
runtimeOpenDataSnapshot = null;
|
|
1204
1206
|
runtimeOpenDataModule = null;
|
|
@@ -1241,6 +1243,57 @@ class UnifiedBookingSalesImpl extends _BookingTicket.BookingTicket {
|
|
|
1241
1243
|
const channel = String(this.otherParams?.channel ?? '').trim();
|
|
1242
1244
|
return channel || super.getSubmitOrderSalesChannel();
|
|
1243
1245
|
}
|
|
1246
|
+
|
|
1247
|
+
/** Only the explicit Kiosk Sales consumer entry enables this policy; POS stays unchanged. */
|
|
1248
|
+
isBusinessAvailabilityEnabled() {
|
|
1249
|
+
return this.otherParams?.enableBusinessAvailability === true && ['pc', 'h5', 'kiosk'].includes((0, _platform.normalizeRuntimePlatform)(this.otherParams?.platform));
|
|
1250
|
+
}
|
|
1251
|
+
getBusinessAvailabilityScope() {
|
|
1252
|
+
return `${this.getRuntimeOpenDataScope()}:${this.runtimeOpenDataGeneration}`;
|
|
1253
|
+
}
|
|
1254
|
+
getBusinessAvailabilityController() {
|
|
1255
|
+
if (!this.businessAvailabilityController) {
|
|
1256
|
+
this.businessAvailabilityController = new _businessAvailability.BusinessAvailabilityController({
|
|
1257
|
+
loadOpenData: () => this.loadOpenDataConfig(),
|
|
1258
|
+
getSchedule: () => this.store.schedule,
|
|
1259
|
+
getTimezone: () => String(this.otherParams?.businessTimezone || this.getPickupTimingContextData('timezone') || ''),
|
|
1260
|
+
getScope: () => this.getBusinessAvailabilityScope()
|
|
1261
|
+
});
|
|
1262
|
+
}
|
|
1263
|
+
return this.businessAvailabilityController;
|
|
1264
|
+
}
|
|
1265
|
+
getBusinessAvailability() {
|
|
1266
|
+
return this.getBusinessAvailabilityController().getAvailability();
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
/** Internal final-write assertion; does not fetch OpenData or Schedule. */
|
|
1270
|
+
assertBusinessAvailability() {
|
|
1271
|
+
if (this.isBusinessAvailabilityEnabled()) this.getBusinessAvailabilityController().assertAvailable();
|
|
1272
|
+
}
|
|
1273
|
+
getSubModuleHooks(moduleName) {
|
|
1274
|
+
const hooks = super.getSubModuleHooks(moduleName);
|
|
1275
|
+
if (moduleName !== 'order' || !this.isBusinessAvailabilityEnabled()) return hooks;
|
|
1276
|
+
let lastScope;
|
|
1277
|
+
let lastExistingScope;
|
|
1278
|
+
let scopeToken = {};
|
|
1279
|
+
return {
|
|
1280
|
+
...hooks,
|
|
1281
|
+
getCartIntentScope: () => {
|
|
1282
|
+
const scope = this.getBusinessAvailabilityScope();
|
|
1283
|
+
const existingScope = hooks?.getCartIntentScope?.();
|
|
1284
|
+
if (scope !== lastScope || !Object.is(existingScope, lastExistingScope)) {
|
|
1285
|
+
lastScope = scope;
|
|
1286
|
+
lastExistingScope = existingScope;
|
|
1287
|
+
scopeToken = {};
|
|
1288
|
+
}
|
|
1289
|
+
return scopeToken;
|
|
1290
|
+
},
|
|
1291
|
+
beforeCartIntentCommit: context => {
|
|
1292
|
+
this.assertBusinessAvailability();
|
|
1293
|
+
hooks?.beforeCartIntentCommit?.(context);
|
|
1294
|
+
}
|
|
1295
|
+
};
|
|
1296
|
+
}
|
|
1244
1297
|
createAvailabilityFacadeError(code, message, details) {
|
|
1245
1298
|
return new _ResourcePlanner.AvailabilityError(code, message, details);
|
|
1246
1299
|
}
|
|
@@ -1714,6 +1767,7 @@ class UnifiedBookingSalesImpl extends _BookingTicket.BookingTicket {
|
|
|
1714
1767
|
});
|
|
1715
1768
|
}
|
|
1716
1769
|
resetRuntimeOpenData() {
|
|
1770
|
+
this.businessAvailabilityController?.invalidate();
|
|
1717
1771
|
this.runtimeOpenDataGeneration += 1;
|
|
1718
1772
|
this.runtimeOpenDataScope = null;
|
|
1719
1773
|
this.runtimeOpenDataSnapshot = null;
|
|
@@ -216,6 +216,8 @@ export interface UnifiedBookingSalesDiscountResult {
|
|
|
216
216
|
summaryAfter?: unknown;
|
|
217
217
|
}
|
|
218
218
|
export interface UnifiedBookingSalesApi {
|
|
219
|
+
/** Uses cached business settings and evaluates the current store-local clock. */
|
|
220
|
+
getBusinessAvailability: () => Promise<import('../../modules/BusinessAvailability').BusinessAvailability>;
|
|
219
221
|
loadOpenData: (params: UnifiedBookingSalesLoadOpenDataParams) => Promise<OpenDataConfig>;
|
|
220
222
|
/** Returns the latest OpenData payload cached by this solution instance. */
|
|
221
223
|
getOpenData: () => Promise<OpenDataConfig | null>;
|