@pisell/pisellos 2.3.53 → 2.3.55

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 (149) hide show
  1. package/dist/model/strategy/adapter/promotion/index.js +9 -0
  2. package/dist/modules/Discount/index.d.ts +1 -0
  3. package/dist/modules/Discount/index.js +40 -11
  4. package/dist/modules/Discount/types.d.ts +1 -0
  5. package/dist/modules/Holder/index.d.ts +48 -0
  6. package/dist/modules/Holder/index.js +640 -0
  7. package/dist/modules/Holder/types.d.ts +123 -0
  8. package/dist/modules/Holder/types.js +1 -0
  9. package/dist/modules/Holder/utils.d.ts +13 -0
  10. package/dist/modules/Holder/utils.js +34 -0
  11. package/dist/modules/OpenData/index.js +2 -2
  12. package/dist/modules/Order/index.d.ts +4 -2
  13. package/dist/modules/Order/index.js +66 -34
  14. package/dist/modules/Order/types.d.ts +9 -0
  15. package/dist/modules/Order/utils.d.ts +10 -0
  16. package/dist/modules/Order/utils.js +35 -3
  17. package/dist/modules/Product/types.d.ts +1 -0
  18. package/dist/modules/ProductList/index.js +34 -15
  19. package/dist/modules/Quotation/index.d.ts +1 -1
  20. package/dist/modules/Quotation/index.js +2 -2
  21. package/dist/modules/ResourcePlanner/index.d.ts +24 -0
  22. package/dist/modules/ResourcePlanner/index.js +181 -0
  23. package/dist/modules/ResourcePlanner/planner.d.ts +14 -0
  24. package/dist/modules/ResourcePlanner/planner.js +1069 -0
  25. package/dist/modules/ResourcePlanner/types.d.ts +227 -0
  26. package/dist/modules/ResourcePlanner/types.js +1 -0
  27. package/dist/modules/Rules/index.js +47 -13
  28. package/dist/modules/SalesSummary/index.js +12 -13
  29. package/dist/modules/ScanOrderLogger/index.d.ts +2 -0
  30. package/dist/modules/ScanOrderLogger/index.js +15 -2
  31. package/dist/modules/ScanOrderLogger/providers/feishu.js +45 -27
  32. package/dist/modules/ScanOrderLogger/types.d.ts +1 -0
  33. package/dist/modules/Schedule/index.d.ts +3 -1
  34. package/dist/modules/Schedule/index.js +21 -16
  35. package/dist/modules/Summary/utils.js +38 -13
  36. package/dist/modules/index.d.ts +2 -0
  37. package/dist/modules/index.js +3 -1
  38. package/dist/plugins/window.d.ts +1 -0
  39. package/dist/server/index.d.ts +14 -0
  40. package/dist/server/index.js +907 -793
  41. package/dist/solution/BaseSales/index.d.ts +7 -0
  42. package/dist/solution/BaseSales/index.js +64 -42
  43. package/dist/solution/BaseSales/utils/cartPromotion.js +4 -1
  44. package/dist/solution/BookingByStep/index.d.ts +17 -2
  45. package/dist/solution/BookingByStep/index.js +294 -215
  46. package/dist/solution/BookingByStep/types.d.ts +2 -1
  47. package/dist/solution/BookingByStep/types.js +3 -1
  48. package/dist/solution/BookingByStep/utils/capacity.js +17 -1
  49. package/dist/solution/BookingByStep/utils/timeslots.d.ts +3 -1
  50. package/dist/solution/BookingByStep/utils/timeslots.js +19 -12
  51. package/dist/solution/BookingTicket/index.d.ts +14 -2
  52. package/dist/solution/BookingTicket/index.js +2 -2
  53. package/dist/solution/BookingTicket/utils/scan/handleScan.d.ts +3 -1
  54. package/dist/solution/BookingTicket/utils/scan/handleScan.js +10 -2
  55. package/dist/solution/ScanOrder/index.d.ts +26 -14
  56. package/dist/solution/ScanOrder/index.js +1142 -739
  57. package/dist/solution/ScanOrder/types.d.ts +21 -1
  58. package/dist/solution/ScanOrder/utils.d.ts +8 -0
  59. package/dist/solution/ScanOrder/utils.js +66 -25
  60. package/dist/solution/ShopDiscount/index.d.ts +1 -0
  61. package/dist/solution/ShopDiscount/index.js +125 -87
  62. package/dist/solution/UnifiedBookingSales/index.d.ts +179 -0
  63. package/dist/solution/UnifiedBookingSales/index.js +1892 -0
  64. package/dist/solution/UnifiedBookingSales/types.d.ts +143 -0
  65. package/dist/solution/UnifiedBookingSales/types.js +11 -0
  66. package/dist/solution/VenueBooking/index.d.ts +39 -11
  67. package/dist/solution/VenueBooking/index.js +1167 -841
  68. package/dist/solution/VenueBooking/types.d.ts +3 -0
  69. package/dist/solution/VenueBooking/utils/resource.js +1 -0
  70. package/dist/solution/VenueBooking/utils/slotMerge.d.ts +4 -4
  71. package/dist/solution/VenueBooking/utils/slotMerge.js +10 -5
  72. package/dist/solution/index.d.ts +1 -0
  73. package/dist/solution/index.js +2 -1
  74. package/dist/types/index.d.ts +1 -0
  75. package/lib/model/strategy/adapter/promotion/index.js +51 -0
  76. package/lib/modules/Discount/index.d.ts +1 -0
  77. package/lib/modules/Discount/index.js +9 -0
  78. package/lib/modules/Discount/types.d.ts +1 -0
  79. package/lib/modules/Holder/index.d.ts +48 -0
  80. package/lib/modules/Holder/index.js +402 -0
  81. package/lib/modules/Holder/types.d.ts +123 -0
  82. package/lib/modules/Holder/types.js +17 -0
  83. package/lib/modules/Holder/utils.d.ts +13 -0
  84. package/lib/modules/Holder/utils.js +71 -0
  85. package/lib/modules/OpenData/index.js +1 -1
  86. package/lib/modules/Order/index.d.ts +4 -2
  87. package/lib/modules/Order/index.js +30 -13
  88. package/lib/modules/Order/types.d.ts +9 -0
  89. package/lib/modules/Order/utils.d.ts +10 -0
  90. package/lib/modules/Order/utils.js +36 -5
  91. package/lib/modules/Product/types.d.ts +1 -0
  92. package/lib/modules/ProductList/index.js +48 -31
  93. package/lib/modules/Quotation/index.d.ts +1 -1
  94. package/lib/modules/Quotation/index.js +2 -2
  95. package/lib/modules/ResourcePlanner/index.d.ts +24 -0
  96. package/lib/modules/ResourcePlanner/index.js +148 -0
  97. package/lib/modules/ResourcePlanner/planner.d.ts +14 -0
  98. package/lib/modules/ResourcePlanner/planner.js +933 -0
  99. package/lib/modules/ResourcePlanner/types.d.ts +227 -0
  100. package/lib/modules/ResourcePlanner/types.js +17 -0
  101. package/lib/modules/Rules/index.js +33 -16
  102. package/lib/modules/SalesSummary/index.js +6 -7
  103. package/lib/modules/ScanOrderLogger/index.d.ts +2 -0
  104. package/lib/modules/ScanOrderLogger/index.js +13 -1
  105. package/lib/modules/ScanOrderLogger/providers/feishu.js +15 -6
  106. package/lib/modules/ScanOrderLogger/types.d.ts +1 -0
  107. package/lib/modules/Schedule/index.d.ts +3 -1
  108. package/lib/modules/Schedule/index.js +10 -8
  109. package/lib/modules/Summary/utils.js +21 -1
  110. package/lib/modules/index.d.ts +2 -0
  111. package/lib/modules/index.js +5 -1
  112. package/lib/plugins/window.d.ts +1 -0
  113. package/lib/server/index.d.ts +14 -0
  114. package/lib/server/index.js +72 -15
  115. package/lib/solution/BaseSales/index.d.ts +7 -0
  116. package/lib/solution/BaseSales/index.js +24 -8
  117. package/lib/solution/BaseSales/utils/cartPromotion.js +3 -0
  118. package/lib/solution/BookingByStep/index.d.ts +17 -2
  119. package/lib/solution/BookingByStep/index.js +60 -18
  120. package/lib/solution/BookingByStep/types.d.ts +2 -1
  121. package/lib/solution/BookingByStep/types.js +5 -0
  122. package/lib/solution/BookingByStep/utils/capacity.js +20 -1
  123. package/lib/solution/BookingByStep/utils/timeslots.d.ts +3 -1
  124. package/lib/solution/BookingByStep/utils/timeslots.js +19 -11
  125. package/lib/solution/BookingTicket/index.d.ts +14 -2
  126. package/lib/solution/BookingTicket/index.js +10 -2
  127. package/lib/solution/BookingTicket/utils/scan/handleScan.d.ts +3 -1
  128. package/lib/solution/BookingTicket/utils/scan/handleScan.js +2 -2
  129. package/lib/solution/ScanOrder/index.d.ts +26 -14
  130. package/lib/solution/ScanOrder/index.js +449 -182
  131. package/lib/solution/ScanOrder/types.d.ts +21 -1
  132. package/lib/solution/ScanOrder/utils.d.ts +8 -0
  133. package/lib/solution/ScanOrder/utils.js +31 -0
  134. package/lib/solution/ShopDiscount/index.d.ts +1 -0
  135. package/lib/solution/ShopDiscount/index.js +14 -0
  136. package/lib/solution/UnifiedBookingSales/index.d.ts +179 -0
  137. package/lib/solution/UnifiedBookingSales/index.js +1085 -0
  138. package/lib/solution/UnifiedBookingSales/types.d.ts +143 -0
  139. package/lib/solution/UnifiedBookingSales/types.js +33 -0
  140. package/lib/solution/VenueBooking/index.d.ts +39 -11
  141. package/lib/solution/VenueBooking/index.js +322 -110
  142. package/lib/solution/VenueBooking/types.d.ts +3 -0
  143. package/lib/solution/VenueBooking/utils/resource.js +1 -0
  144. package/lib/solution/VenueBooking/utils/slotMerge.d.ts +4 -4
  145. package/lib/solution/VenueBooking/utils/slotMerge.js +6 -4
  146. package/lib/solution/index.d.ts +1 -0
  147. package/lib/solution/index.js +3 -1
  148. package/lib/types/index.d.ts +1 -0
  149. package/package.json +1 -1
@@ -49,6 +49,10 @@ declare class Server {
49
49
  private productQuerySubscribers;
50
50
  /** subscriberId → 智能定价变价时间点定时器句柄(与 subscriber 分离存储,便于 clear) */
51
51
  private productQueryScheduleTimers;
52
+ /** 餐牌筛选为空时才触发的菜单/日程缓存自愈,避免每次商品查询都请求远端。 */
53
+ private menuScheduleRefreshInFlight;
54
+ private lastMenuScheduleRefreshAt;
55
+ private readonly MENU_SCHEDULE_REFRESH_COOLDOWN_MS;
52
56
  private orderQuerySubscribers;
53
57
  private bookingQuerySubscribers;
54
58
  private bookingRemoteQuerySubscribers;
@@ -262,6 +266,16 @@ declare class Server {
262
266
  * 存储订阅者信息,便于数据变更时推送最新结果
263
267
  */
264
268
  private handleProductQuery;
269
+ /**
270
+ * ProductList callers such as standalone retail pages do not always have a selected schedule.
271
+ * In that case query against the current local time so menu availability can still be evaluated.
272
+ */
273
+ private resolveProductQueryScheduleContext;
274
+ /**
275
+ * OpenData 的关联餐牌可能已更新,而 server_menu/server_schedule 仍命中本地持久化缓存。
276
+ * 仅在当前筛选没有任何生效餐牌时刷新一次,并由调用方重新计算筛选结果。
277
+ */
278
+ private refreshMenuScheduleCacheAfterEmptyResult;
265
279
  /**
266
280
  * 按商品 id 查询单条(GET /shop/product/query/:productId)
267
281
  * schedule_date 必填:query 或 data;schedule_datetime 可选。不走订阅。
@@ -82,6 +82,10 @@ var Server = class {
82
82
  this.productQuerySubscribers = /* @__PURE__ */ new Map();
83
83
  /** subscriberId → 智能定价变价时间点定时器句柄(与 subscriber 分离存储,便于 clear) */
84
84
  this.productQueryScheduleTimers = /* @__PURE__ */ new Map();
85
+ /** 餐牌筛选为空时才触发的菜单/日程缓存自愈,避免每次商品查询都请求远端。 */
86
+ this.menuScheduleRefreshInFlight = null;
87
+ this.lastMenuScheduleRefreshAt = 0;
88
+ this.MENU_SCHEDULE_REFRESH_COOLDOWN_MS = 30 * 1e3;
85
89
  // ---- 订单 / 预约列表查询订阅者 ----
86
90
  this.orderQuerySubscribers = /* @__PURE__ */ new Map();
87
91
  this.bookingQuerySubscribers = /* @__PURE__ */ new Map();
@@ -215,7 +219,11 @@ var Server = class {
215
219
  */
216
220
  this.handleProductQuery = async ({ url, method, data, config }) => {
217
221
  console.log("[Server] handleProductQuery:", url, method, data, config);
218
- const { menu_list_ids, schedule_datetime, schedule_date, customer_id, ids, extension_type, strategy_context } = data;
222
+ const { menu_list_ids, customer_id, ids, extension_type, strategy_context } = data;
223
+ const { schedule_date, schedule_datetime } = this.resolveProductQueryScheduleContext({
224
+ schedule_date: data.schedule_date,
225
+ schedule_datetime: data.schedule_datetime
226
+ });
219
227
  const { callback, subscriberId } = config || {};
220
228
  this.logInfo("handleProductQuery: 开始处理商品查询请求", {
221
229
  menu_list_ids,
@@ -2211,15 +2219,12 @@ var Server = class {
2211
2219
  return;
2212
2220
  const scheduleDate = subscriber.context.schedule_date || (0, import_dayjs.default)().format("YYYY-MM-DD");
2213
2221
  const timerIds = [];
2214
- for (const timePoint of timePoints) {
2215
- const delayMs = this.computeScheduleTimePointDelayMs(scheduleDate, timePoint);
2216
- if (delayMs < 0)
2217
- continue;
2218
- const timerId = setTimeout(() => {
2219
- void this.onScheduleTimePointTimerFire(subscriberId, timerId);
2220
- }, delayMs);
2221
- timerIds.push(timerId);
2222
- }
2222
+ setInterval(() => {
2223
+ const currentTime = (0, import_dayjs.default)().format("hh:mm");
2224
+ if (timePoints.includes(currentTime)) {
2225
+ this.onScheduleTimePointTimerFire(subscriberId);
2226
+ }
2227
+ }, 6e4);
2223
2228
  if (timerIds.length > 0) {
2224
2229
  this.productQueryScheduleTimers.set(subscriberId, timerIds);
2225
2230
  subscriber.scheduleTimerIds = timerIds;
@@ -2234,7 +2239,7 @@ var Server = class {
2234
2239
  * schedule 变价时间点定时器触发:刷新 schedule_datetime 后重算并 callback。
2235
2240
  */
2236
2241
  async onScheduleTimePointTimerFire(subscriberId, timerId) {
2237
- this.removeScheduleTimerRef(subscriberId, timerId);
2242
+ timerId && this.removeScheduleTimerRef(subscriberId, timerId);
2238
2243
  if (!this.productQuerySubscribers.has(subscriberId))
2239
2244
  return;
2240
2245
  const subscriber = this.productQuerySubscribers.get(subscriberId);
@@ -2277,6 +2282,43 @@ var Server = class {
2277
2282
  this.scheduleSubscriberTimePointReloads(subscriberId, timePoints);
2278
2283
  }
2279
2284
  }
2285
+ /**
2286
+ * ProductList callers such as standalone retail pages do not always have a selected schedule.
2287
+ * In that case query against the current local time so menu availability can still be evaluated.
2288
+ */
2289
+ resolveProductQueryScheduleContext(params) {
2290
+ const now = (0, import_dayjs.default)();
2291
+ const schedule_datetime = typeof params.schedule_datetime === "string" && params.schedule_datetime.trim() ? params.schedule_datetime : now.format("YYYY-MM-DD HH:mm:ss");
2292
+ const schedule_date = typeof params.schedule_date === "string" && params.schedule_date.trim() ? params.schedule_date : (0, import_dayjs.default)(schedule_datetime).isValid() ? (0, import_dayjs.default)(schedule_datetime).format("YYYY-MM-DD") : now.format("YYYY-MM-DD");
2293
+ return { schedule_date, schedule_datetime };
2294
+ }
2295
+ /**
2296
+ * OpenData 的关联餐牌可能已更新,而 server_menu/server_schedule 仍命中本地持久化缓存。
2297
+ * 仅在当前筛选没有任何生效餐牌时刷新一次,并由调用方重新计算筛选结果。
2298
+ */
2299
+ async refreshMenuScheduleCacheAfterEmptyResult() {
2300
+ if (!this.menu || !this.schedule)
2301
+ return false;
2302
+ const now = Date.now();
2303
+ if (now - this.lastMenuScheduleRefreshAt < this.MENU_SCHEDULE_REFRESH_COOLDOWN_MS) {
2304
+ return false;
2305
+ }
2306
+ if (!this.menuScheduleRefreshInFlight) {
2307
+ this.lastMenuScheduleRefreshAt = now;
2308
+ this.menuScheduleRefreshInFlight = Promise.all([
2309
+ this.menu.loadMenuList(),
2310
+ this.schedule.loadAllSchedule()
2311
+ ]).then(() => void 0).catch((error) => {
2312
+ this.logWarning("刷新菜单/日程缓存失败,继续使用现有缓存", {
2313
+ error: error instanceof Error ? error.message : String(error)
2314
+ });
2315
+ }).finally(() => {
2316
+ this.menuScheduleRefreshInFlight = null;
2317
+ });
2318
+ }
2319
+ await this.menuScheduleRefreshInFlight;
2320
+ return true;
2321
+ }
2280
2322
  /**
2281
2323
  * 执行 sales 搜索并登记可见订单号。
2282
2324
  * 搜索参数完全透传后端,OS 不做过滤、筛选、排序。
@@ -3779,10 +3821,22 @@ var Server = class {
3779
3821
  data: (normalized == null ? void 0 : normalized.data) ?? null
3780
3822
  };
3781
3823
  }
3782
- return this.withSyncedOrderIdInCheckoutResponse(
3824
+ const response = this.withSyncedOrderIdInCheckoutResponse(
3783
3825
  syncResult.normalizedResponse,
3784
3826
  syncResult.syncedOrder
3785
3827
  );
3828
+ const syncedOrder = syncResult.syncedOrder;
3829
+ if (syncedOrder && this.shouldBuildSmallTicketData(syncResult.checkoutData) && this.isSalesOrderFullyPaid(syncedOrder)) {
3830
+ await this.dispatchLocalReceiptPrint({
3831
+ checkoutData: syncResult.checkoutData,
3832
+ order: syncedOrder,
3833
+ response: syncResult.normalizedResponse,
3834
+ requireFullyPaid: true,
3835
+ // print_all 的 type=0 是收据打印;不要沿用同步成功后的券/手环打印 type=99。
3836
+ isManualPrint: true
3837
+ });
3838
+ }
3839
+ return response;
3786
3840
  } catch (error) {
3787
3841
  const backendError = this.extractBackendErrorResponse(error);
3788
3842
  if (backendError) {
@@ -5072,13 +5126,16 @@ var Server = class {
5072
5126
  let activeMenuList = [];
5073
5127
  if (menu_list_ids && Array.isArray(menu_list_ids) && menu_list_ids.length > 0) {
5074
5128
  const tMenu = performance.now();
5075
- const menuList = this.menu.getMenuByIds(menu_list_ids);
5076
- activeMenuList = menuList.filter((menu) => {
5129
+ const getActiveMenus = () => this.menu.getMenuByIds(menu_list_ids).filter((menu) => {
5077
5130
  var _a;
5078
5131
  return ((_a = this.schedule) == null ? void 0 : _a.getDateIsInSchedule(schedule_datetime, menu.schedule)) || false;
5079
5132
  });
5133
+ activeMenuList = getActiveMenus();
5134
+ if (!activeMenuList.length && await this.refreshMenuScheduleCacheAfterEmptyResult()) {
5135
+ activeMenuList = getActiveMenus();
5136
+ }
5080
5137
  (0, import_product.perfMark)("computeQuery.filterActiveMenu", performance.now() - tMenu, {
5081
- totalMenu: menuList.length,
5138
+ totalMenu: menu_list_ids.length,
5082
5139
  activeMenu: activeMenuList.length
5083
5140
  });
5084
5141
  }
@@ -290,6 +290,13 @@ export declare class BaseSalesImpl extends BaseModule implements Module {
290
290
  confirmPendingVoucherPayments?: boolean;
291
291
  enhancePayload?: SubmitPayloadEnhancer;
292
292
  }): Promise<T>;
293
+ /**
294
+ * 默认保持 WebPOS 的本地待同步订单模式;需要云端确认后才算成功的渠道可覆盖此策略。
295
+ * otherParams.checkoutSyncTaskEnabled 优先,便于同一 solution 按入口恢复旧行为。
296
+ */
297
+ protected shouldUseCheckoutSyncTask(): boolean;
298
+ /** 可由特定 solution 为真实 checkout 补充默认收据数据请求。 */
299
+ protected getDefaultCheckoutSmallTicketDataFlag(): number | undefined;
293
300
  printLocalOrderReceipt<T = any>(lookup?: BaseSalesPrintLocalOrderReceiptParams): Promise<T>;
294
301
  private getSubmitPaymentStatus;
295
302
  syncPaymentsToOrder<T = any>(params: SyncPaymentsToOrderParams): Promise<SyncPaymentsToOrderResult<T>>;
@@ -1041,7 +1041,7 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
1041
1041
  return isChanged;
1042
1042
  }
1043
1043
  async initialize(core, options = {}) {
1044
- var _a, _b;
1044
+ var _a, _b, _c, _d;
1045
1045
  this.core = core;
1046
1046
  this.initializeOptions = options || {};
1047
1047
  this.paymentNumberDevicePrefix = null;
@@ -1052,10 +1052,9 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
1052
1052
  this.request = core.getPlugin("request");
1053
1053
  this.appPlugin = core.getPlugin("app");
1054
1054
  if (!this.appPlugin) {
1055
- throw new Error("Checkout 解决方案需要 app 插件支持");
1056
1055
  }
1057
- const app = this.appPlugin.getApp();
1058
- this.logger = app.logger;
1056
+ const app = (_c = (_b = this.appPlugin) == null ? void 0 : _b.getApp) == null ? void 0 : _c.call(_b);
1057
+ this.logger = app == null ? void 0 : app.logger;
1059
1058
  const targetCacheData = this.readSessionCacheData();
1060
1059
  const moduleNames = this.getRegisteredModuleNames();
1061
1060
  moduleNames.forEach((step) => {
@@ -1080,7 +1079,7 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
1080
1079
  if (this.store.schedule && !this.isScheduleListLoaded(this.store.schedule)) {
1081
1080
  await this.store.schedule.loadAllSchedule();
1082
1081
  }
1083
- if (this.store.order && typeof ((_b = this.otherParams) == null ? void 0 : _b.enableTempOrderPersist) === "boolean") {
1082
+ if (this.store.order && typeof ((_d = this.otherParams) == null ? void 0 : _d.enableTempOrderPersist) === "boolean") {
1084
1083
  this.store.order.setEnableTempOrderPersist(
1085
1084
  this.otherParams.enableTempOrderPersist
1086
1085
  );
@@ -1934,6 +1933,7 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
1934
1933
  throw new Error("BaseSales 解决方案需要 order 模块支持");
1935
1934
  }
1936
1935
  const inferredPaymentStatus = this.getSubmitPaymentStatus(params == null ? void 0 : params.paymentStatus);
1936
+ const smallTicketDataFlag = (params == null ? void 0 : params.smallTicketDataFlag) ?? this.getDefaultCheckoutSmallTicketDataFlag();
1937
1937
  const submitParams = {
1938
1938
  cacheId: this.cacheId,
1939
1939
  platform: (_a = this.otherParams) == null ? void 0 : _a.platform,
@@ -1943,11 +1943,11 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
1943
1943
  request_unique_idempotency_token: this.store.order.generateIdempotencyToken(),
1944
1944
  ...(params == null ? void 0 : params.payments) !== void 0 ? { payments: params.payments } : {},
1945
1945
  ...inferredPaymentStatus !== void 0 ? { paymentStatus: inferredPaymentStatus } : {},
1946
- ...(params == null ? void 0 : params.smallTicketDataFlag) !== void 0 ? { smallTicketDataFlag: params.smallTicketDataFlag } : {},
1946
+ ...smallTicketDataFlag !== void 0 ? { smallTicketDataFlag } : {},
1947
1947
  ...(params == null ? void 0 : params.confirmPendingVoucherPayments) !== void 0 ? { confirmPendingVoucherPayments: params.confirmPendingVoucherPayments } : {},
1948
1948
  ...(params == null ? void 0 : params.enhancePayload) !== void 0 ? { enhancePayload: params.enhancePayload } : {}
1949
1949
  };
1950
- return this.store.order.submitTempOrder(submitParams);
1950
+ return this.shouldUseCheckoutSyncTask() ? this.store.order.submitTempOrder(submitParams) : this.store.order.submitTempOrderAsync(submitParams);
1951
1951
  }
1952
1952
  async saveSalesOrderDraft(params) {
1953
1953
  var _a, _b, _c, _d;
@@ -1969,20 +1969,35 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
1969
1969
  throw new Error("BaseSales 解决方案需要 order 模块支持");
1970
1970
  }
1971
1971
  const inferredPaymentStatus = this.getSubmitPaymentStatus(params == null ? void 0 : params.paymentStatus);
1972
+ const smallTicketDataFlag = (params == null ? void 0 : params.smallTicketDataFlag) ?? this.getDefaultCheckoutSmallTicketDataFlag();
1972
1973
  const submitParams = {
1973
1974
  cacheId: this.cacheId,
1974
1975
  platform: (_a = this.otherParams) == null ? void 0 : _a.platform,
1975
1976
  businessCode: ((_b = this.otherParams) == null ? void 0 : _b.businessCode) || ((_c = this.otherParams) == null ? void 0 : _c.business_code),
1976
1977
  channel: this.getSubmitOrderSalesChannel(),
1977
1978
  type: (_d = this.otherParams) == null ? void 0 : _d.type,
1979
+ request_unique_idempotency_token: this.store.order.generateIdempotencyToken(),
1978
1980
  ...(params == null ? void 0 : params.payments) !== void 0 ? { payments: params.payments } : {},
1979
1981
  ...inferredPaymentStatus !== void 0 ? { paymentStatus: inferredPaymentStatus } : {},
1980
- ...(params == null ? void 0 : params.smallTicketDataFlag) !== void 0 ? { smallTicketDataFlag: params.smallTicketDataFlag } : {},
1982
+ ...smallTicketDataFlag !== void 0 ? { smallTicketDataFlag } : {},
1981
1983
  ...(params == null ? void 0 : params.confirmPendingVoucherPayments) !== void 0 ? { confirmPendingVoucherPayments: params.confirmPendingVoucherPayments } : {},
1982
1984
  ...(params == null ? void 0 : params.enhancePayload) !== void 0 ? { enhancePayload: params.enhancePayload } : {}
1983
1985
  };
1984
1986
  return this.store.order.submitTempOrderAsync(submitParams);
1985
1987
  }
1988
+ /**
1989
+ * 默认保持 WebPOS 的本地待同步订单模式;需要云端确认后才算成功的渠道可覆盖此策略。
1990
+ * otherParams.checkoutSyncTaskEnabled 优先,便于同一 solution 按入口恢复旧行为。
1991
+ */
1992
+ shouldUseCheckoutSyncTask() {
1993
+ var _a;
1994
+ const configured = (_a = this.otherParams) == null ? void 0 : _a.checkoutSyncTaskEnabled;
1995
+ return typeof configured === "boolean" ? configured : true;
1996
+ }
1997
+ /** 可由特定 solution 为真实 checkout 补充默认收据数据请求。 */
1998
+ getDefaultCheckoutSmallTicketDataFlag() {
1999
+ return void 0;
2000
+ }
1986
2001
  async printLocalOrderReceipt(lookup) {
1987
2002
  var _a, _b, _c, _d, _e, _f, _g;
1988
2003
  if (!this.store.order) {
@@ -2042,6 +2057,7 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
2042
2057
  const channel = params.channel ?? this.getSubmitOrderSalesChannel();
2043
2058
  const syncParams = {
2044
2059
  ...params,
2060
+ checkoutSyncTaskEnabled: this.shouldUseCheckoutSyncTask(),
2045
2061
  ...businessCode !== void 0 ? { businessCode } : {},
2046
2062
  ...channel !== void 0 ? { channel } : {}
2047
2063
  };
@@ -368,6 +368,9 @@ function getProductLineNoteSignature(product) {
368
368
  }
369
369
  function getPromotionLineMergeBoundary(product) {
370
370
  var _a, _b;
371
+ if ((product == null ? void 0 : product.product_id) === void 0 || (product == null ? void 0 : product.product_id) === null) {
372
+ return `custom:${getOrderProductUid(product)}`;
373
+ }
371
374
  if (Number(((_a = product == null ? void 0 : product.product_sku) == null ? void 0 : _a.open_sold_weight) || 0) === 1) {
372
375
  return `weighing:${getOrderProductUid(product)}`;
373
376
  }
@@ -10,6 +10,7 @@ import { ITime } from '../../modules/Date/types';
10
10
  import dayjs from 'dayjs';
11
11
  import { LoadScheduleAvailableDateParams } from '../../modules/Schedule/types';
12
12
  import { IHolder, IFetchHolderAccountsParams } from '../../modules/AccountList/types';
13
+ import { IAppendFormRecordParams } from '../../modules/Holder/types';
13
14
  export declare class BookingByStepImpl extends BaseModule implements Module {
14
15
  protected defaultName: string;
15
16
  protected defaultVersion: string;
@@ -126,6 +127,20 @@ export declare class BookingByStepImpl extends BaseModule implements Module {
126
127
  * @param params
127
128
  */
128
129
  fetchHolderAccountsAsync(params: IFetchHolderAccountsParams): Promise<void>;
130
+ /**
131
+ * 获取 holder 表单 map
132
+ */
133
+ getHolderFormMap(): import("../../modules").HolderFormMap;
134
+ /**
135
+ * 登录后或外部触发时,按当前 holderMap 批量刷新所有表单的 records
136
+ */
137
+ refreshAllHolderFormRecords(params?: {
138
+ customer_id?: number;
139
+ }): Promise<import("../../modules").HolderFormMap>;
140
+ /**
141
+ * 添加表单记录
142
+ */
143
+ appendFormRecord(params: IAppendFormRecordParams): import("../../modules").IFormRecord;
129
144
  setDateRange(dateRange: ITime[]): Promise<void>;
130
145
  clearDateRange(): void;
131
146
  getDateRange(): Promise<ITime[]>;
@@ -302,7 +317,7 @@ export declare class BookingByStepImpl extends BaseModule implements Module {
302
317
  autoSelectAllProductResources(resources_code: string, timeSlots?: TimeSliceItem): {
303
318
  errorList: any[];
304
319
  };
305
- getTimeSlotByAllResources(resources_code: string): any[];
320
+ getTimeSlotByAllResources(resources_code: string, split?: number): any[];
306
321
  getTimeSlotByAllResourcesForDate({ resources_code, startDate, endDate }: {
307
322
  resources_code: string;
308
323
  startDate: string;
@@ -311,7 +326,7 @@ export declare class BookingByStepImpl extends BaseModule implements Module {
311
326
  date: string;
312
327
  status: string;
313
328
  week: string;
314
- weekNum: 0 | 1 | 2 | 3 | 4 | 5 | 6;
329
+ weekNum: 0 | 2 | 1 | 6 | 4 | 5 | 3;
315
330
  }[]>;
316
331
  submitTimeSlot(timeSlots: TimeSliceItem): void;
317
332
  private getScheduleDataByIds;
@@ -89,6 +89,7 @@ var BookingByStepImpl = class extends import_BaseModule.BaseModule {
89
89
  }
90
90
  const moduleArr = [
91
91
  "accountList",
92
+ "holder",
92
93
  "cart",
93
94
  "schedule",
94
95
  "summary",
@@ -379,6 +380,27 @@ var BookingByStepImpl = class extends import_BaseModule.BaseModule {
379
380
  }
380
381
  return this.store.accountList.fetchHolderAccounts(params);
381
382
  }
383
+ /**
384
+ * 获取 holder 表单 map
385
+ */
386
+ getHolderFormMap() {
387
+ return this.store.holder.getHolderFormMap();
388
+ }
389
+ /**
390
+ * 登录后或外部触发时,按当前 holderMap 批量刷新所有表单的 records
391
+ */
392
+ async refreshAllHolderFormRecords(params) {
393
+ return this.store.holder.refreshAllFormRecords({
394
+ customer_id: params == null ? void 0 : params.customer_id,
395
+ useCache: false
396
+ });
397
+ }
398
+ /**
399
+ * 添加表单记录
400
+ */
401
+ appendFormRecord(params) {
402
+ return this.store.holder.appendFormRecord(params);
403
+ }
382
404
  // 设置日期范围,注入到日期模块中
383
405
  async setDateRange(dateRange) {
384
406
  this.store.date.setDateRange(dateRange);
@@ -486,6 +508,14 @@ var BookingByStepImpl = class extends import_BaseModule.BaseModule {
486
508
  date = (normalProductCartItem == null ? void 0 : normalProductCartItem.start_date) || "";
487
509
  }
488
510
  this.updateQuotationPriceAndCart(date);
511
+ try {
512
+ const customerId = Number(accountInfo.id);
513
+ await this.refreshAllHolderFormRecords({
514
+ customer_id: customerId
515
+ });
516
+ } catch (error) {
517
+ console.error("[BookingByStep] 登录后刷新 holder 表单失败", error);
518
+ }
489
519
  }
490
520
  } else {
491
521
  throw new Error(`没有找到${accountId}账户`);
@@ -512,10 +542,11 @@ var BookingByStepImpl = class extends import_BaseModule.BaseModule {
512
542
  }
513
543
  // 购物车提交订单
514
544
  async submitOrder(extraData) {
545
+ var _a;
515
546
  const cartItems = this.store.cart.getItems();
516
547
  const newCartItems = (0, import_lodash_es.cloneDeep)(cartItems);
517
548
  newCartItems.forEach((item) => {
518
- var _a;
549
+ var _a2;
519
550
  if (item._origin.resources && item._origin.resources.length) {
520
551
  item._origin.resources = item._origin.resources.map((n) => {
521
552
  const newResourcesItem = (0, import_lodash_es.cloneDeep)(n);
@@ -525,7 +556,7 @@ var BookingByStepImpl = class extends import_BaseModule.BaseModule {
525
556
  return { ...newResourcesItem };
526
557
  });
527
558
  const { currentCapacity, formatCapacity } = (0, import_capacity.getCapacityInfoByCartItem)(item);
528
- if (!((_a = item._origin) == null ? void 0 : _a.metadata)) {
559
+ if (!((_a2 = item._origin) == null ? void 0 : _a2.metadata)) {
529
560
  item._origin.metadata = {};
530
561
  }
531
562
  item._origin.metadata.capacity = formatCapacity;
@@ -543,6 +574,7 @@ var BookingByStepImpl = class extends import_BaseModule.BaseModule {
543
574
  cartItems: newCartItems,
544
575
  type,
545
576
  platform: this.platform,
577
+ business_code: (_a = this.otherParams) == null ? void 0 : _a.business_code,
546
578
  extraData
547
579
  }
548
580
  });
@@ -628,6 +660,15 @@ var BookingByStepImpl = class extends import_BaseModule.BaseModule {
628
660
  account = activeAccount;
629
661
  }
630
662
  }
663
+ try {
664
+ this.store.holder.ensureFormByProduct({
665
+ product: productData,
666
+ shop_id: (productData == null ? void 0 : productData.shop_id) || "",
667
+ useCache: true
668
+ });
669
+ } catch (error) {
670
+ console.error("[BookingByStep] 加载 holder 表单失败", error);
671
+ }
631
672
  const flag = this.store.cart.mergeCartItemByRowKey({
632
673
  rowKey,
633
674
  quantity,
@@ -1150,7 +1191,7 @@ var BookingByStepImpl = class extends import_BaseModule.BaseModule {
1150
1191
  }
1151
1192
  const totalCapacity = (countMap[n.id] || []).reduce(
1152
1193
  (sum, item) => {
1153
- const hasOverlap = !(0, import_dayjs.default)(item.time.start_at).isAfter((0, import_dayjs.default)(timeSlots.start_at)) && !(0, import_dayjs.default)(item.time.end_at).isBefore((0, import_dayjs.default)(timeSlots.end_at));
1194
+ const hasOverlap = (0, import_dayjs.default)(item.time.start_at).isBefore((0, import_dayjs.default)(timeSlots.end_at)) && (0, import_dayjs.default)(item.time.end_at).isAfter((0, import_dayjs.default)(timeSlots.start_at));
1154
1195
  return hasOverlap ? sum + item.pax : sum;
1155
1196
  },
1156
1197
  0
@@ -1232,9 +1273,10 @@ var BookingByStepImpl = class extends import_BaseModule.BaseModule {
1232
1273
  }
1233
1274
  );
1234
1275
  cartItems2.forEach((item, index) => {
1235
- var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
1276
+ var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
1236
1277
  const { currentCapacity } = (0, import_capacity.getCapacityInfoByCartItem)(item);
1237
- if (item._origin.start_time && !(timeSlots == null ? void 0 : timeSlots.start_time)) {
1278
+ const hasOriginTimeSlot = Boolean(item._origin.start_time) && Boolean(item._origin.end_time) && !(timeSlots == null ? void 0 : timeSlots.start_time);
1279
+ if (hasOriginTimeSlot) {
1238
1280
  recordTimeSlots = {
1239
1281
  start_time: item._origin.start_time,
1240
1282
  end_time: item._origin.end_time,
@@ -1246,13 +1288,11 @@ var BookingByStepImpl = class extends import_BaseModule.BaseModule {
1246
1288
  const currentResourceConfig = (_c = (_b = (_a2 = item._productOrigin) == null ? void 0 : _a2.product_resource) == null ? void 0 : _b.resources) == null ? void 0 : _c.find(
1247
1289
  (n) => n.code === resources_code
1248
1290
  );
1249
- if (index !== 0 && recordTimeSlots && (currentResourceConfig == null ? void 0 : currentResourceConfig.type) === "single" && ((_d = item._productOrigin) == null ? void 0 : _d.duration) && !hasFlexibleProduct) {
1250
- let start_at = (0, import_dayjs.default)(recordTimeSlots.end_time).add(
1251
- ((_f = (_e = item._productOrigin) == null ? void 0 : _e.duration) == null ? void 0 : _f.value) || 10,
1252
- "minutes"
1253
- );
1291
+ if (index !== 0 && recordTimeSlots && (currentResourceConfig == null ? void 0 : currentResourceConfig.type) === "single" && ((_d = item._productOrigin) == null ? void 0 : _d.duration) && !hasFlexibleProduct && !hasOriginTimeSlot) {
1292
+ const recordEndAt = recordTimeSlots.end_at ? (0, import_dayjs.default)(recordTimeSlots.end_at) : (0, import_dayjs.default)(`${item.start_date} ${recordTimeSlots.end_time}`);
1293
+ let start_at = recordEndAt;
1254
1294
  let end_at = start_at.add(
1255
- ((_h = (_g = item._productOrigin) == null ? void 0 : _g.duration) == null ? void 0 : _h.value) || 10,
1295
+ ((_f = (_e = item._productOrigin) == null ? void 0 : _e.duration) == null ? void 0 : _f.value) || 10,
1256
1296
  "minutes"
1257
1297
  );
1258
1298
  recordTimeSlots = {
@@ -1306,7 +1346,7 @@ var BookingByStepImpl = class extends import_BaseModule.BaseModule {
1306
1346
  const allCartItems = (0, import_lodash_es.cloneDeep)(this.store.cart.getItems());
1307
1347
  let selectedResources = [];
1308
1348
  const resources2 = (0, import_lodash_es.cloneDeep)(
1309
- ((_j = (_i = item._productOrigin) == null ? void 0 : _i.product_resource) == null ? void 0 : _j.resources) || []
1349
+ ((_h = (_g = item._productOrigin) == null ? void 0 : _g.product_resource) == null ? void 0 : _h.resources) || []
1310
1350
  );
1311
1351
  const currentResourcesRenderList = [];
1312
1352
  resources2.forEach((n) => {
@@ -1354,11 +1394,11 @@ var BookingByStepImpl = class extends import_BaseModule.BaseModule {
1354
1394
  return recordCount >= currentCapacity;
1355
1395
  });
1356
1396
  });
1357
- const targetRenderList = (_k = productResources.find(
1397
+ const targetRenderList = (_i = productResources.find(
1358
1398
  (n) => n.code === resources_code
1359
- )) == null ? void 0 : _k.renderList;
1399
+ )) == null ? void 0 : _i.renderList;
1360
1400
  if (targetRenderList && targetRenderList.length > 0) {
1361
- if ((_l = item._origin.resources) == null ? void 0 : _l.some(
1401
+ if ((_j = item._origin.resources) == null ? void 0 : _j.some(
1362
1402
  (n) => n.form_id === targetRenderList[0].form_id
1363
1403
  )) {
1364
1404
  return;
@@ -1366,7 +1406,9 @@ var BookingByStepImpl = class extends import_BaseModule.BaseModule {
1366
1406
  const fastestResource = (0, import_timeslots.findFastestAvailableResource)({
1367
1407
  resources: targetRenderList,
1368
1408
  currentCapacity,
1369
- countMap: selectResourcesMap
1409
+ countMap: selectResourcesMap,
1410
+ targetDate: (_k = dateRange == null ? void 0 : dateRange[0]) == null ? void 0 : _k.date,
1411
+ durationMinutes: ((_l = item == null ? void 0 : item.duration) == null ? void 0 : _l.value) || ((_n = (_m = item == null ? void 0 : item._productOrigin) == null ? void 0 : _m.duration) == null ? void 0 : _n.value) || 10
1370
1412
  });
1371
1413
  const targetResource = fastestResource || targetRenderList[0];
1372
1414
  targetResource.capacity = currentCapacity;
@@ -1403,7 +1445,7 @@ var BookingByStepImpl = class extends import_BaseModule.BaseModule {
1403
1445
  return { errorList };
1404
1446
  }
1405
1447
  // 从购物车中获取已经分配好第一步资源的所有时间片
1406
- getTimeSlotByAllResources(resources_code) {
1448
+ getTimeSlotByAllResources(resources_code, split = 10) {
1407
1449
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
1408
1450
  let dateRange = this.store.date.getDateRange();
1409
1451
  const resources = [];
@@ -1561,7 +1603,7 @@ var BookingByStepImpl = class extends import_BaseModule.BaseModule {
1561
1603
  resourcesMap,
1562
1604
  duration,
1563
1605
  currentDate: dateRange[0].date,
1564
- split: 10,
1606
+ split,
1565
1607
  resourcesUseableMap,
1566
1608
  capacity: maxCapacity,
1567
1609
  cut_off_time: maxCutOffTime,
@@ -1,4 +1,4 @@
1
- import { ProductList, CartModule, ProductData, AccountModule, AccountListModule, DateModule, GuestListModule, OrderModule, PaymentModule, ResourceListModule, StepModule, SummaryModule, SalesSummaryModule, ScheduleModule, ScanOrderLoggerModule } from '../../modules';
1
+ import { ProductList, CartModule, ProductData, AccountModule, AccountListModule, DateModule, GuestListModule, OrderModule, PaymentModule, ResourceListModule, StepModule, SummaryModule, SalesSummaryModule, ScheduleModule, ScanOrderLoggerModule, HolderModule } from '../../modules';
2
2
  export interface BookingByStepState {
3
3
  cart: CartModule;
4
4
  summary: SummaryModule;
@@ -16,6 +16,7 @@ export interface BookingByStepState {
16
16
  schedule: ScheduleModule;
17
17
  salesSummary?: SalesSummaryModule;
18
18
  scanOrderLogger?: ScanOrderLoggerModule;
19
+ holder: HolderModule;
19
20
  }
20
21
  export declare function createModule<T extends keyof BookingByStepState>(moduleName: T, solutionName: string, name?: string, version?: string): BookingByStepState[T];
21
22
  export declare enum BookingByStepHooks {
@@ -81,6 +81,11 @@ function createModule(moduleName, solutionName, name, version) {
81
81
  `${solutionName}_${name || moduleName}`,
82
82
  version
83
83
  );
84
+ case "holder":
85
+ return new import_modules.HolderModule(
86
+ `${solutionName}_${name || moduleName}`,
87
+ version
88
+ );
84
89
  default:
85
90
  throw new Error(`Unknown module type: ${moduleName}`);
86
91
  }
@@ -49,8 +49,27 @@ var formatDefaultCapacitys = ({
49
49
  const id = d.bundle_product_id;
50
50
  const variantId = d.bundle_variant_id;
51
51
  const item = ((capacity == null ? void 0 : capacity.package) || []).find(
52
- (item2) => item2.product_id === id || item2.product_id === variantId
52
+ (item2) => item2.product_id == id || item2.product_id == variantId
53
53
  );
54
+ if (!item && (window == null ? void 0 : window.sendWarningLog)) {
55
+ window.sendWarningLog({
56
+ title: "未找到套餐 capacity 配置",
57
+ content: [
58
+ {
59
+ key: "product_id",
60
+ value: id
61
+ },
62
+ {
63
+ key: "variant_id",
64
+ value: variantId
65
+ },
66
+ {
67
+ key: "package",
68
+ value: JSON.stringify((capacity == null ? void 0 : capacity.package) || [])
69
+ }
70
+ ]
71
+ });
72
+ }
54
73
  return {
55
74
  id,
56
75
  value: item ? d.num || 0 : 0,
@@ -19,10 +19,12 @@ export declare function calculateResourceAvailableTime({ resource, timeSlots, cu
19
19
  * @param countMap 已预约数量映射
20
20
  * @returns 最快可用的资源
21
21
  */
22
- export declare function findFastestAvailableResource({ resources, currentCapacity, countMap, }: {
22
+ export declare function findFastestAvailableResource({ resources, currentCapacity, countMap, targetDate, durationMinutes, }: {
23
23
  resources: ResourceItem[];
24
24
  currentCapacity?: number;
25
25
  countMap?: Record<number, number>;
26
+ targetDate?: string | Dayjs;
27
+ durationMinutes?: number;
26
28
  }): ResourceItem | null;
27
29
  /**
28
30
  * 给定一个时间列表,通过开始和结束时间过滤出符合条件的时间段