@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.
Files changed (44) hide show
  1. package/dist/modules/BusinessAvailability/index.d.ts +30 -0
  2. package/dist/modules/BusinessAvailability/index.js +204 -0
  3. package/dist/modules/OpenData/types.d.ts +2 -0
  4. package/dist/modules/Order/index.d.ts +4 -1
  5. package/dist/modules/Order/index.js +315 -241
  6. package/dist/modules/Order/types.d.ts +8 -0
  7. package/dist/plugins/request.d.ts +4 -0
  8. package/dist/shared/receipt/loadCustomerInvoiceLayout.d.ts +22 -0
  9. package/dist/shared/receipt/loadCustomerInvoiceLayout.js +142 -0
  10. package/dist/solution/BaseSales/index.d.ts +4 -15
  11. package/dist/solution/BaseSales/index.js +131 -86
  12. package/dist/solution/BaseSales/types.d.ts +11 -0
  13. package/dist/solution/BookingByStep/index.d.ts +1 -1
  14. package/dist/solution/BookingTicket/index.d.ts +1 -1
  15. package/dist/solution/BookingTicket/index.js +41 -13
  16. package/dist/solution/OrderInvoicePreview/index.js +6 -6
  17. package/dist/solution/UnifiedBookingSales/businessAvailability.d.ts +27 -0
  18. package/dist/solution/UnifiedBookingSales/businessAvailability.js +292 -0
  19. package/dist/solution/UnifiedBookingSales/index.d.ts +11 -0
  20. package/dist/solution/UnifiedBookingSales/index.js +154 -75
  21. package/dist/solution/UnifiedBookingSales/types.d.ts +2 -0
  22. package/lib/model/strategy/adapter/promotion/index.js +1 -46
  23. package/lib/modules/BusinessAvailability/index.d.ts +30 -0
  24. package/lib/modules/BusinessAvailability/index.js +146 -0
  25. package/lib/modules/OpenData/types.d.ts +2 -0
  26. package/lib/modules/Order/index.d.ts +4 -1
  27. package/lib/modules/Order/index.js +62 -6
  28. package/lib/modules/Order/types.d.ts +8 -0
  29. package/lib/plugins/request.d.ts +4 -0
  30. package/lib/shared/receipt/loadCustomerInvoiceLayout.d.ts +22 -0
  31. package/lib/shared/receipt/loadCustomerInvoiceLayout.js +86 -0
  32. package/lib/solution/BaseSales/index.d.ts +4 -15
  33. package/lib/solution/BaseSales/index.js +45 -24
  34. package/lib/solution/BaseSales/types.d.ts +11 -0
  35. package/lib/solution/BookingByStep/index.d.ts +1 -1
  36. package/lib/solution/BookingTicket/index.d.ts +1 -1
  37. package/lib/solution/BookingTicket/index.js +37 -4
  38. package/lib/solution/OrderInvoicePreview/index.js +2 -4
  39. package/lib/solution/UnifiedBookingSales/businessAvailability.d.ts +27 -0
  40. package/lib/solution/UnifiedBookingSales/businessAvailability.js +138 -0
  41. package/lib/solution/UnifiedBookingSales/index.d.ts +11 -0
  42. package/lib/solution/UnifiedBookingSales/index.js +54 -0
  43. package/lib/solution/UnifiedBookingSales/types.d.ts +2 -0
  44. 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
- async buildVirtualSettlementSubmitData() {
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
- payments: [],
2464
- snapshot,
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
- payments: [payment],
2523
- snapshot,
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 virtualSettlement = await this.buildVirtualSettlementSubmitData();
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 enhancePayload = this.buildSubmitPayloadEnhancerWithOpenDataConfig(virtualSettlementEnhancePayload);
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
- ...(virtualSettlement ? {
2576
- payments: virtualSettlement.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 | 1 | 2 | 5 | 6 | 3 | 4;
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, "skip" | "num">;
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 => this.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) => this.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 => this.scanWalletAsset(scanCode)
1701
- }, formatted, bridge);
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 _OpenData = require("../../modules/OpenData");
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>;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "private": false,
3
3
  "name": "@pisell/pisellos",
4
- "version": "0.10.54",
4
+ "version": "0.10.56",
5
5
  "description": "一个可扩展的前端模块化SDK框架,支持插件系统",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",