@pisell/pisellos 0.0.501 → 0.0.503

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 (43) hide show
  1. package/dist/model/strategy/adapter/promotion/index.js +0 -9
  2. package/dist/modules/Order/index.d.ts +4 -0
  3. package/dist/modules/Order/index.js +23 -3
  4. package/dist/modules/Order/types.d.ts +9 -1
  5. package/dist/modules/Order/utils.d.ts +8 -1
  6. package/dist/modules/Order/utils.js +28 -12
  7. package/dist/solution/BookingByStep/index.d.ts +2 -2
  8. package/dist/solution/ScanOrder/index.d.ts +15 -0
  9. package/dist/solution/ScanOrder/index.js +653 -375
  10. package/dist/solution/ScanOrder/types.d.ts +9 -3
  11. package/dist/solution/VenueBooking/index.d.ts +28 -5
  12. package/dist/solution/VenueBooking/index.js +472 -234
  13. package/dist/solution/VenueBooking/types.d.ts +23 -0
  14. package/dist/solution/VenueBooking/utils/dateSummary.d.ts +1 -1
  15. package/dist/solution/VenueBooking/utils/dateSummary.js +1 -1
  16. package/dist/solution/VenueBooking/utils/resource.d.ts +11 -1
  17. package/dist/solution/VenueBooking/utils/resource.js +57 -21
  18. package/dist/solution/VenueBooking/utils/slotMerge.d.ts +5 -0
  19. package/dist/solution/VenueBooking/utils/slotMerge.js +33 -12
  20. package/dist/solution/VenueBooking/utils/timeSlot.d.ts +1 -1
  21. package/dist/solution/VenueBooking/utils/timeSlot.js +259 -62
  22. package/lib/model/strategy/adapter/promotion/index.js +49 -0
  23. package/lib/modules/Order/index.d.ts +4 -0
  24. package/lib/modules/Order/index.js +21 -8
  25. package/lib/modules/Order/types.d.ts +9 -1
  26. package/lib/modules/Order/utils.d.ts +8 -1
  27. package/lib/modules/Order/utils.js +23 -13
  28. package/lib/solution/BookingByStep/index.d.ts +2 -2
  29. package/lib/solution/ScanOrder/index.d.ts +15 -0
  30. package/lib/solution/ScanOrder/index.js +218 -10
  31. package/lib/solution/ScanOrder/types.d.ts +9 -3
  32. package/lib/solution/VenueBooking/index.d.ts +28 -5
  33. package/lib/solution/VenueBooking/index.js +204 -58
  34. package/lib/solution/VenueBooking/types.d.ts +23 -0
  35. package/lib/solution/VenueBooking/utils/dateSummary.d.ts +1 -1
  36. package/lib/solution/VenueBooking/utils/dateSummary.js +1 -1
  37. package/lib/solution/VenueBooking/utils/resource.d.ts +11 -1
  38. package/lib/solution/VenueBooking/utils/resource.js +15 -4
  39. package/lib/solution/VenueBooking/utils/slotMerge.d.ts +5 -0
  40. package/lib/solution/VenueBooking/utils/slotMerge.js +29 -12
  41. package/lib/solution/VenueBooking/utils/timeSlot.d.ts +1 -1
  42. package/lib/solution/VenueBooking/utils/timeSlot.js +182 -43
  43. package/package.json +1 -1
@@ -172,6 +172,9 @@ function normalizeSubmitProduct(product) {
172
172
  if (rawMetadata.price_breakdown) {
173
173
  cleanMetadata.price_breakdown = rawMetadata.price_breakdown;
174
174
  }
175
+ if (rawMetadata.is_rule !== void 0) {
176
+ cleanMetadata.is_rule = rawMetadata.is_rule;
177
+ }
175
178
  return {
176
179
  ...submitProduct,
177
180
  ...bookingUid ? { booking_uid: bookingUid } : {},
@@ -286,7 +289,8 @@ function buildSubmitPayload(params) {
286
289
  platform,
287
290
  businessCode,
288
291
  channel,
289
- type
292
+ type,
293
+ enhance
290
294
  } = params;
291
295
  const scheduleDate = tempOrder.schedule_date || tempOrder.created_at || formatDateTime(now);
292
296
  const summary = tempOrder.summary || (0, import_utils.createEmptySummary)();
@@ -297,8 +301,8 @@ function buildSubmitPayload(params) {
297
301
  const bookingDuration = resolveTableOccupancyDuration(tempOrder);
298
302
  const bookingEnd = bookingStart.add(bookingDuration, "minute");
299
303
  const bookings = relationId && tableFormId ? [{
300
- relation_id: relationId,
301
- form_id: tableFormId,
304
+ relation_id: 0,
305
+ form_id: 0,
302
306
  start_time: bookingStart.format("HH:mm"),
303
307
  start_date: bookingStart.format("YYYY-MM-DD"),
304
308
  end_time: bookingEnd.format("HH:mm"),
@@ -310,17 +314,17 @@ function buildSubmitPayload(params) {
310
314
  },
311
315
  select_date: bookingStart.format("YYYY-MM-DD"),
312
316
  is_all: false,
313
- "like_status": "common",
314
- "schedule_id": 0,
315
- "relation_type": "form",
316
- "number": 1
317
+ like_status: "common",
318
+ schedule_id: 0,
319
+ relation_type: "",
320
+ number: 1
317
321
  }] : tempOrder.bookings || [];
318
322
  const formRecordIds = relationId && tableFormId ? [{
319
323
  form_id: tableFormId,
320
- form_record_ids: [relationId]
324
+ form_record_id: relationId
321
325
  }] : void 0;
322
326
  const { created_at: _createdAt, summary: _summary, surcharges: _surcharges, ...tempOrderRest } = tempOrder;
323
- return {
327
+ const payload = {
324
328
  ...tempOrderRest,
325
329
  platform: normalizeSubmitPlatform(platform ?? tempOrder.platform),
326
330
  request_unique_idempotency_token: cacheId,
@@ -348,13 +352,19 @@ function buildSubmitPayload(params) {
348
352
  contacts_info: tempOrder.contacts_info || [],
349
353
  // holder: tempOrder.holder || null,
350
354
  // summary,
351
- metadata: {
352
- ...tempOrder.metadata
353
- },
355
+ metadata: (() => {
356
+ const {
357
+ collect_pax: _collectPax,
358
+ table_occupancy_duration: _tableOccupancyDuration,
359
+ ...rest
360
+ } = tempOrder.metadata || {};
361
+ return { ...rest };
362
+ })(),
354
363
  products: (tempOrder.products || []).map(
355
364
  (product) => normalizeSubmitProduct(product)
356
365
  )
357
366
  };
367
+ return enhance ? enhance(payload, { tempOrder, bookingUuid, now }) : payload;
358
368
  }
359
369
  function formatV1Product(products) {
360
370
  return products.map((product) => {
@@ -364,7 +374,7 @@ function formatV1Product(products) {
364
374
  option: product.product_option_item,
365
375
  product_id: product.product_id,
366
376
  product_variant_id: product.product_variant_id,
367
- quantity: product.num,
377
+ num: product.num,
368
378
  rowKey: product.product_id,
369
379
  session: null,
370
380
  unique: createUuidV4()
@@ -311,7 +311,7 @@ export declare class BookingByStepImpl extends BaseModule implements Module {
311
311
  date: string;
312
312
  status: string;
313
313
  week: string;
314
- weekNum: 0 | 5 | 1 | 4 | 2 | 3 | 6;
314
+ weekNum: 0 | 1 | 4 | 3 | 2 | 5 | 6;
315
315
  }[]>;
316
316
  submitTimeSlot(timeSlots: TimeSliceItem): void;
317
317
  private getScheduleDataByIds;
@@ -330,7 +330,7 @@ export declare class BookingByStepImpl extends BaseModule implements Module {
330
330
  count: number;
331
331
  left: number;
332
332
  summaryCount: number;
333
- status: "sold_out" | "lots_of_space" | "filling_up_fast";
333
+ status: "lots_of_space" | "filling_up_fast" | "sold_out";
334
334
  }[];
335
335
  /**
336
336
  * 找到多个资源的公共可用时间段
@@ -52,10 +52,17 @@ export declare class ScanOrderImpl extends BaseModule implements Module {
52
52
  };
53
53
  getTempOrder(): import("./types").ScanOrderTempOrder | null;
54
54
  updateTempOrderNote(note: string): string;
55
+ setPickupReferenceMode(mode: 'counter_pickup' | 'table_service'): {
56
+ service_type: 'dine_in';
57
+ pickup_reference_mode: 'counter_pickup' | 'table_service';
58
+ };
59
+ setPickupRef(buzzer: string): string;
55
60
  private ensureTempOrder;
56
61
  addNewOrder(): Promise<import("./types").ScanOrderTempOrder>;
62
+ restoreOrder(): Promise<import("./types").ScanOrderTempOrder>;
57
63
  getOrderProducts(): ScanOrderOrderProduct[];
58
64
  getSummary(): Promise<import("./types").ScanOrderSummary>;
65
+ private buildSubmitPayloadEnhancer;
59
66
  submitScanOrder<T = any>(): Promise<T>;
60
67
  addProductToOrder(product: Partial<ScanOrderOrderProduct> & ScanOrderOrderProductIdentity): Promise<ScanOrderOrderProduct[]>;
61
68
  updateProductInOrder(params: {
@@ -94,4 +101,12 @@ export declare class ScanOrderImpl extends BaseModule implements Module {
94
101
  }): Promise<void>;
95
102
  setEntryPaxNumber(number: number): Promise<void>;
96
103
  getEntryPaxNumber(): number | null;
104
+ getFulfillmentModes(): {
105
+ enablePickup: boolean;
106
+ enableTableService: boolean;
107
+ };
108
+ checkManualPickupRef(): {
109
+ enabled: boolean;
110
+ manualInputType?: string;
111
+ };
97
112
  }
@@ -38,6 +38,8 @@ var import_types = require("./types");
38
38
  var import_utils = require("./utils");
39
39
  var import_types2 = require("../BookingByStep/types");
40
40
  var import_ProductList = require("../../modules/ProductList");
41
+ var import_Schedule = require("../../modules/Schedule");
42
+ var import_getDateIsInSchedule = require("../../modules/Schedule/getDateIsInSchedule");
41
43
  var import_utils2 = require("../../modules/Order/utils");
42
44
  var import_dayjs = __toESM(require("dayjs"));
43
45
  var import_itemRule = require("../../model/strategy/adapter/itemRule");
@@ -226,11 +228,26 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
226
228
  if (this.store.scanOrderLogger) {
227
229
  this.store.scanOrderLogger.setContext(this.getScanOrderLoggerContext());
228
230
  }
231
+ const scheduleModule = new import_Schedule.ScheduleModule(`${this.name}_schedule`);
232
+ this.store.schedule = scheduleModule;
233
+ this.core.registerModule(scheduleModule, {
234
+ otherParams: {
235
+ ...this.otherParams,
236
+ fatherModule: this.name
237
+ }
238
+ });
229
239
  console.log("[ScanOrder] 初始化开始");
230
240
  try {
231
241
  await ((_e = this.store.order) == null ? void 0 : _e.recalculateSummary({ createIfMissing: false }));
232
242
  (_f = this.store.order) == null ? void 0 : _f.persistTempOrder();
233
243
  await this.loadRuntimeConfigs();
244
+ if (this.store.schedule) {
245
+ try {
246
+ await this.store.schedule.loadAllSchedule();
247
+ } catch (scheduleError) {
248
+ console.warn("[ScanOrder] loadAllSchedule 失败,operating_hours 判定将跳过", scheduleError);
249
+ }
250
+ }
234
251
  await this.refreshItemRuleQuantityLimits();
235
252
  this.store.status = "ready";
236
253
  console.log("[ScanOrder] 初始化完成");
@@ -368,6 +385,50 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
368
385
  throw error;
369
386
  }
370
387
  }
388
+ // 存储 UI 层选择的取餐方式到 tempOrder.metadata.shop_service_data
389
+ // service_type 在扫码点餐场景固定为 dine_in
390
+ setPickupReferenceMode(mode) {
391
+ this.logMethodStart("setPickupReferenceMode", { mode });
392
+ try {
393
+ if (!this.store.order)
394
+ throw new Error("order 模块未初始化");
395
+ const tempOrder = this.store.order.ensureTempOrder();
396
+ tempOrder.metadata = {
397
+ ...tempOrder.metadata || {},
398
+ shop_service_data: {
399
+ ...(tempOrder.metadata || {}).shop_service_data || {},
400
+ service_type: "dine_in",
401
+ pickup_reference_mode: mode
402
+ }
403
+ };
404
+ this.store.order.persistTempOrder();
405
+ this.logMethodSuccess("setPickupReferenceMode", {
406
+ mode
407
+ });
408
+ return tempOrder.metadata.shop_service_data;
409
+ } catch (error) {
410
+ this.logMethodError("setPickupReferenceMode", error, { mode });
411
+ throw error;
412
+ }
413
+ }
414
+ // 存储用户自定义取餐标识到 tempOrder.buzzer
415
+ setPickupRef(buzzer) {
416
+ this.logMethodStart("setPickupRef", {
417
+ buzzerLength: String(buzzer || "").length
418
+ });
419
+ try {
420
+ if (!this.store.order)
421
+ throw new Error("order 模块未初始化");
422
+ const result = this.store.order.updateTempOrderBuzzer(buzzer);
423
+ this.logMethodSuccess("setPickupRef", {
424
+ buzzerLength: result.length
425
+ });
426
+ return result;
427
+ } catch (error) {
428
+ this.logMethodError("setPickupRef", error);
429
+ throw error;
430
+ }
431
+ }
371
432
  ensureTempOrder() {
372
433
  if (!this.store.order)
373
434
  throw new Error("order 模块未初始化");
@@ -389,6 +450,25 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
389
450
  throw error;
390
451
  }
391
452
  }
453
+ // 重置 tempOrder 与与上一单强相关的运行态(resource/entryPaxNumber/enabledReservationRuleProducts),
454
+ // 适用于从支付页通过 react-router 跳回继续下单时清理残留数据
455
+ async restoreOrder() {
456
+ this.logMethodStart("restoreOrder");
457
+ try {
458
+ if (!this.store.order) {
459
+ throw new Error("scanOrder 解决方案需要 order 模块支持");
460
+ }
461
+ this.store.resource = null;
462
+ this.store.entryPaxNumber = 1;
463
+ this.enabledReservationRuleProducts = [];
464
+ const tempOrder = this.store.order.restoreOrder();
465
+ this.logMethodSuccess("restoreOrder");
466
+ return tempOrder;
467
+ } catch (error) {
468
+ this.logMethodError("restoreOrder", error);
469
+ throw error;
470
+ }
471
+ }
392
472
  getOrderProducts() {
393
473
  this.logMethodStart("getOrderProducts");
394
474
  if (!this.store.order)
@@ -414,8 +494,71 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
414
494
  throw error;
415
495
  }
416
496
  }
497
+ // ScanOrder 提交 payload enhancer:
498
+ // - 给所有 booking 注入 appointment_status: 'started'(扫码点餐语义)
499
+ // - 给第一条 booking 补 resources 与 product_uid(仅当存在 resource / rule product)
500
+ // - 追加一条 is_rule=true 的 rule product,与 booking 互相关联
501
+ buildSubmitPayloadEnhancer() {
502
+ const ruleProduct = this.enabledReservationRuleProducts[0];
503
+ const resourceState = this.store.resource;
504
+ return (payload, { bookingUuid, tempOrder }) => {
505
+ var _a;
506
+ const resourceId = tempOrder.resource_id ?? (resourceState == null ? void 0 : resourceState.relationId);
507
+ const pickOriginal = (value) => {
508
+ if (value && typeof value === "object" && !Array.isArray(value)) {
509
+ const original = value.original;
510
+ return typeof original === "string" && original.length > 0 ? original : void 0;
511
+ }
512
+ return void 0;
513
+ };
514
+ const mainField = pickOriginal(tempOrder.table_number) ?? pickOriginal((_a = resourceState == null ? void 0 : resourceState.table_form_record) == null ? void 0 : _a.name) ?? "";
515
+ const resourceEntry = resourceState && resourceId ? {
516
+ relation_type: "form",
517
+ like_status: "common",
518
+ id: Number(resourceId),
519
+ main_field: mainField,
520
+ form_id: resourceState.tableFormId,
521
+ relation_id: resourceState.relationId ?? resourceId,
522
+ capacity: 1,
523
+ metadata: {}
524
+ } : void 0;
525
+ const ruleProductUid = ruleProduct ? (0, import_utils2.createUuidV4)() : void 0;
526
+ const nextBookings = (payload.bookings || []).map((booking, idx) => ({
527
+ ...booking,
528
+ appointment_status: "started",
529
+ ...idx === 0 && resourceEntry ? { resources: [resourceEntry] } : {},
530
+ ...idx === 0 && ruleProductUid ? { product_uid: ruleProductUid } : {}
531
+ }));
532
+ const nextProducts = [...payload.products || []];
533
+ if (ruleProduct && ruleProductUid) {
534
+ const sellingPrice = String(ruleProduct.price ?? "0.00");
535
+ const originalPrice = String(
536
+ ruleProduct.original_price ?? ruleProduct.price ?? "0.00"
537
+ );
538
+ nextProducts.push({
539
+ product_id: ruleProduct.id,
540
+ product_variant_id: 0,
541
+ num: 1,
542
+ selling_price: sellingPrice,
543
+ original_price: originalPrice,
544
+ payment_price: sellingPrice,
545
+ is_charge_tax: ruleProduct.is_gst ?? 0,
546
+ product_option_item: [],
547
+ discount_list: [],
548
+ product_bundle: [],
549
+ booking_uid: bookingUuid,
550
+ metadata: {
551
+ is_rule: true,
552
+ unique_identification_number: ruleProductUid,
553
+ booking_uid: bookingUuid
554
+ }
555
+ });
556
+ }
557
+ return { ...payload, bookings: nextBookings, products: nextProducts };
558
+ };
559
+ }
417
560
  async submitScanOrder() {
418
- var _a, _b, _c, _d, _e;
561
+ var _a, _b, _c, _d, _e, _f, _g;
419
562
  this.logMethodStart("submitScanOrder");
420
563
  try {
421
564
  await this.validateBeforeSubmitByItemRule();
@@ -426,17 +569,26 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
426
569
  this.store.entryPaxNumber = pax;
427
570
  const tempOrderForSubmit = this.store.order.ensureTempOrder();
428
571
  tempOrderForSubmit.metadata = { ...tempOrderForSubmit.metadata, collect_pax: pax };
572
+ tempOrderForSubmit.delivery_type = "shop_service";
573
+ const resourceTableName = (_b = (_a = this.store.resource) == null ? void 0 : _a.table_form_record) == null ? void 0 : _b.name;
574
+ if (resourceTableName && typeof resourceTableName === "object") {
575
+ tempOrderForSubmit.table_number = resourceTableName;
576
+ } else {
577
+ delete tempOrderForSubmit.table_number;
578
+ }
429
579
  this.store.order.persistTempOrder();
580
+ const enhancePayload = this.buildSubmitPayloadEnhancer();
430
581
  const result = await this.store.order.submitTempOrder({
431
582
  cacheId: this.cacheId,
432
- platform: (_a = this.otherParams) == null ? void 0 : _a.platform,
433
- businessCode: (_b = this.otherParams) == null ? void 0 : _b.businessCode,
434
- channel: (_c = this.otherParams) == null ? void 0 : _c.channel,
435
- type: (_d = this.otherParams) == null ? void 0 : _d.type
583
+ platform: (_c = this.otherParams) == null ? void 0 : _c.platform,
584
+ businessCode: (_d = this.otherParams) == null ? void 0 : _d.businessCode,
585
+ channel: (_e = this.otherParams) == null ? void 0 : _e.channel,
586
+ type: (_f = this.otherParams) == null ? void 0 : _f.type,
587
+ enhancePayload
436
588
  });
437
589
  const tempOrder = this.store.order.getTempOrder();
438
590
  this.logMethodSuccess("submitScanOrder", {
439
- productCount: ((_e = tempOrder == null ? void 0 : tempOrder.products) == null ? void 0 : _e.length) || 0
591
+ productCount: ((_g = tempOrder == null ? void 0 : tempOrder.products) == null ? void 0 : _g.length) || 0
440
592
  });
441
593
  return result;
442
594
  } catch (error) {
@@ -707,6 +859,7 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
707
859
  );
708
860
  tempOrder.products.push(
709
861
  (0, import_utils.normalizeOrderProduct)({
862
+ ...sourceItem,
710
863
  product_id: productId,
711
864
  product_variant_id: productVariantId,
712
865
  num: targetQuantity,
@@ -752,6 +905,7 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
752
905
  }
753
906
  }
754
907
  if (hasChanges) {
908
+ this.store.order.applyDiscount();
755
909
  await this.store.order.recalculateSummary({ createIfMissing: true });
756
910
  this.store.order.persistTempOrder();
757
911
  }
@@ -969,19 +1123,39 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
969
1123
  const dineInConfig = (openData == null ? void 0 : openData.data) || {};
970
1124
  this.otherParams.dineInConfig = dineInConfig;
971
1125
  await this.syncItemRuleConfigsFromDineInConfig(dineInConfig);
972
- const shopClosedInfo = () => ({
1126
+ const closedBehaviorValue = dineInConfig == null ? void 0 : dineInConfig["availability.closed_behavior"];
1127
+ const closedMessage = (dineInConfig == null ? void 0 : dineInConfig["availability.closed_message"]) || (dineInConfig == null ? void 0 : dineInConfig["availability.message"]) || (dineInConfig == null ? void 0 : dineInConfig["basic.closed_message"]);
1128
+ const basicUnavailableMessage = (dineInConfig == null ? void 0 : dineInConfig["basic.unavailable_message"]) || closedMessage;
1129
+ const pauseMessage = (dineInConfig == null ? void 0 : dineInConfig["availability.pause_message"]) || closedMessage;
1130
+ const makeShopClosed = (errorTips, closed_behavior) => ({
973
1131
  mode: "shop_closed",
974
1132
  order_id: void 0,
975
1133
  relation_id: void 0,
976
1134
  table_form_id: void 0,
977
1135
  deskmate_valid: false,
978
- errorTips: (dineInConfig == null ? void 0 : dineInConfig["availability.pause_message"]) || (dineInConfig == null ? void 0 : dineInConfig["availability.closed_message"]) || (dineInConfig == null ? void 0 : dineInConfig["availability.message"]) || (dineInConfig == null ? void 0 : dineInConfig["basic.closed_message"])
1136
+ errorTips,
1137
+ closed_behavior
979
1138
  });
1139
+ if ((dineInConfig == null ? void 0 : dineInConfig["basic.enable"]) === false) {
1140
+ return makeShopClosed(basicUnavailableMessage);
1141
+ }
980
1142
  if ((0, import_utils.toBoolean)(dineInConfig == null ? void 0 : dineInConfig["availability.paused"])) {
981
1143
  if ((dineInConfig == null ? void 0 : dineInConfig["availability.pause_behavior"]) === "hide_all") {
982
- return shopClosedInfo();
1144
+ return makeShopClosed(pauseMessage);
1145
+ }
1146
+ }
1147
+ const operatingHourIds = Array.isArray(dineInConfig == null ? void 0 : dineInConfig["availability.operating_hours"]) ? dineInConfig["availability.operating_hours"].map((id) => Number(id)).filter((id) => Number.isFinite(id) && id > 0) : [];
1148
+ let outsideOperatingHours = false;
1149
+ if (operatingHourIds.length && this.store.schedule) {
1150
+ const scheduleList = this.store.schedule.getScheduleListByIds(operatingHourIds);
1151
+ if (scheduleList.length) {
1152
+ const now = (0, import_dayjs.default)().format("YYYY-MM-DD HH:mm:ss");
1153
+ outsideOperatingHours = !(0, import_getDateIsInSchedule.getDateIsInSchedule)(now, scheduleList);
983
1154
  }
984
1155
  }
1156
+ if (outsideOperatingHours && closedBehaviorValue !== "show_menu_disabled") {
1157
+ return makeShopClosed(closedMessage, closedBehaviorValue);
1158
+ }
985
1159
  const config = await this.fetchTableConfigByResourceId(resourceId);
986
1160
  const resourceState = this.normalizeResourceState(config, hasOrderId);
987
1161
  this.store.resource = resourceState;
@@ -1000,7 +1174,7 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
1000
1174
  tempOrder.table_form_id = resourceState.tableFormId;
1001
1175
  tempOrder.resource_id = resourceId;
1002
1176
  const reservationLinkIds = (0, import_utils.collectLinkProductIdsFromReservationRules)(
1003
- dineInConfig["reservation.enabled_reservation_rules"]
1177
+ dineInConfig["fulfillment.enabled_resource_rules"]
1004
1178
  );
1005
1179
  if (reservationLinkIds.length === 0) {
1006
1180
  this.enabledReservationRuleProducts = [];
@@ -1115,6 +1289,11 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
1115
1289
  submissionIndex: hasOrderId ? 1 : 0,
1116
1290
  historicalItems
1117
1291
  });
1292
+ if (outsideOperatingHours && closedBehaviorValue === "show_menu_disabled") {
1293
+ availabilityInfo.mode = "submit_disabled";
1294
+ availabilityInfo.errorTips = closedMessage;
1295
+ availabilityInfo.closed_behavior = closedBehaviorValue;
1296
+ }
1118
1297
  this.logMethodSuccess("checkResourceAvailable", {
1119
1298
  resourceId,
1120
1299
  mode: availabilityInfo.mode,
@@ -1207,6 +1386,35 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
1207
1386
  getEntryPaxNumber() {
1208
1387
  return this.store.entryPaxNumber;
1209
1388
  }
1389
+ // 读取取餐/送餐开关
1390
+ // 依赖 checkResourceAvailable 已缓存的 dineInConfig
1391
+ getFulfillmentModes() {
1392
+ var _a;
1393
+ this.logMethodStart("getFulfillmentModes");
1394
+ const dineInConfig = ((_a = this.otherParams) == null ? void 0 : _a.dineInConfig) || {};
1395
+ const result = {
1396
+ enablePickup: Boolean(dineInConfig["fulfillment.enable_pickup"]),
1397
+ enableTableService: Boolean(dineInConfig["fulfillment.enable_table_service"])
1398
+ };
1399
+ this.logMethodSuccess("getFulfillmentModes", result);
1400
+ return result;
1401
+ }
1402
+ // 判定后台是否开启客户自定义取餐标识
1403
+ // 依赖 checkResourceAvailable 已缓存的 dineInConfig
1404
+ checkManualPickupRef() {
1405
+ var _a;
1406
+ this.logMethodStart("checkManualPickupRef");
1407
+ const dineInConfig = ((_a = this.otherParams) == null ? void 0 : _a.dineInConfig) || {};
1408
+ const refMode = dineInConfig["fulfillment.fulfillment_ref_mode"];
1409
+ const manualInputType = dineInConfig["fulfillment.manual_input_type"];
1410
+ const enabled = refMode === "manual_input";
1411
+ const result = enabled ? { enabled: true, manualInputType } : { enabled: false };
1412
+ this.logMethodSuccess("checkManualPickupRef", {
1413
+ enabled,
1414
+ manualInputType: enabled ? manualInputType : void 0
1415
+ });
1416
+ return result;
1417
+ }
1210
1418
  };
1211
1419
  // Annotate the CommonJS export names for ESM import in node:
1212
1420
  0 && (module.exports = {
@@ -1,4 +1,4 @@
1
- import { OrderModule, ProductList, SalesSummaryModule, ScanOrderLogInput, ScanOrderLoggerModule, ScanOrderLoggerProviderConfig, ScanOrderLoggerProviderType } from '../../modules';
1
+ import { OrderModule, ProductList, SalesSummaryModule, ScanOrderLogInput, ScanOrderLoggerModule, ScanOrderLoggerProviderConfig, ScanOrderLoggerProviderType, ScheduleModule } from '../../modules';
2
2
  import type { QuantityCheckResult, QuantityLimitResult } from '../../model/strategy/adapter/itemRule';
3
3
  /**
4
4
  * 扫码下单流程 hook
@@ -114,6 +114,9 @@ export interface ScanOrderTempOrder {
114
114
  shop_discount: string;
115
115
  surcharge_fee: string;
116
116
  note: string;
117
+ buzzer?: string;
118
+ delivery_type?: string;
119
+ table_number?: Record<string, any>;
117
120
  schedule_date: string;
118
121
  created_at: string;
119
122
  products: ScanOrderOrderProduct[];
@@ -134,11 +137,11 @@ export interface ScanOrderSubmitPayload extends Omit<ScanOrderTempOrder, 'platfo
134
137
  request_unique_idempotency_token?: string;
135
138
  form_record_ids?: Array<{
136
139
  form_id: number | string;
137
- form_record_ids: Array<number | string>;
140
+ form_record_id: number | string;
138
141
  }>;
139
142
  products: ScanOrderSubmitProduct[];
140
143
  }
141
- export type ScanOrderAvailabilityMode = 'idle' | 'shop_closed' | 'resource_block' | 'resource_busy' | 'additional_order_with_code' | 'additional_order';
144
+ export type ScanOrderAvailabilityMode = 'idle' | 'shop_closed' | 'submit_disabled' | 'resource_block' | 'resource_busy' | 'additional_order_with_code' | 'additional_order';
142
145
  export interface ScanOrderTableFormRecord {
143
146
  policy?: string | null;
144
147
  partyroom_booking?: string | null;
@@ -151,6 +154,8 @@ export interface ScanOrderAvailabilityInfo {
151
154
  table_form_id?: string;
152
155
  deskmate_valid?: boolean;
153
156
  errorTips?: string;
157
+ /** 透传 `availability.closed_behavior`,便于 UI 识别拦截类型(如 show_menu_disabled) */
158
+ closed_behavior?: string;
154
159
  /** `/order/dining/table/config` 返回的 `table_form_record` 原样透出 */
155
160
  table_form_record?: ScanOrderTableFormRecord | null;
156
161
  policy?: string | null;
@@ -212,6 +217,7 @@ export interface ScanOrderState {
212
217
  order?: OrderModule;
213
218
  salesSummary?: SalesSummaryModule;
214
219
  scanOrderLogger?: ScanOrderLoggerModule;
220
+ schedule?: ScheduleModule;
215
221
  itemRuleQuantityLimits: QuantityLimitResult[];
216
222
  cartValidation: {
217
223
  passed: boolean | null;
@@ -92,25 +92,48 @@ export declare class VenueBookingImpl extends BaseModule implements Module {
92
92
  /**
93
93
  * 切换单个时段的选中状态(选中/取消)。
94
94
  * 内部自动处理连续时段的合并与拆分,订单是唯一真相源。
95
+ *
96
+ * slot.productId 指定当前操作针对的是该 resourceId 下的哪一个商品。
97
+ * 同一资源下不同 productId 之间互相隔离,不会相互合并。
95
98
  */
96
99
  toggleSlot(slot: VenueSlotSelection): Promise<ScanOrderOrderProduct[]>;
97
100
  /**
98
101
  * 获取某资源当前选中的所有独立时段(从订单中解析)。
102
+ * 不传 productId 时返回该资源下所有商品的选中时段;传了则精确匹配。
99
103
  */
100
- getSelectedSlotsForResource(resourceId: number | string): VenueSlotSelection[];
104
+ getSelectedSlotsForResource(resourceId: number | string, productId?: number): VenueSlotSelection[];
105
+ /** getSelectedSlotsForResource 的 (resourceId, productId) 精确版,内部使用。 */
106
+ private getSelectedSlotsForResourceProduct;
101
107
  /**
102
108
  * 判断某个时段是否已选中。
109
+ * 不传 productId 时:只要该资源下任一商品在 startTime 被选中即返回 true;传了则精确匹配。
103
110
  */
104
- isSlotSelected(resourceId: number | string, startTime: string): boolean;
111
+ isSlotSelected(resourceId: number | string, startTime: string, productId?: number): boolean;
105
112
  /**
106
- * 获取所有已选时段(按资源分组)。
113
+ * 判断指定 (resourceId, productId, startTime) 格子是否应因其它已选项而被禁用。
114
+ * 规则:
115
+ * 1) 同一 resourceId 下若已选了另一个 productId 的同 startTime → 禁用
116
+ * 2) 当前 resource 是组合资源:若其任一 child resource 在 startTime 被选中 → 禁用
117
+ * 3) 当前 resource 是某些组合资源的 child:若该组合资源在 startTime 被选中 → 禁用
118
+ * 4) 两个组合资源的 child 集合有交集,且对方在该 startTime 被选中 → 禁用
119
+ */
120
+ isSlotDisabledBySelection(params: {
121
+ resourceId: number | string;
122
+ productId: number;
123
+ startTime: string;
124
+ }): boolean;
125
+ /**
126
+ * 获取所有已选时段(按资源分组)。每个 slot 上带 productId,便于 UI 做 per-product 判定。
107
127
  */
108
128
  getAllSelectedSlots(): Map<number | string, VenueSlotSelection[]>;
109
129
  /**
110
- * 对指定资源的订单商品进行 reconcile:
130
+ * 对指定 (resourceId, productId) 的订单商品进行 reconcile:
111
131
  * 清除旧商品 → 合并连续时段 → 重新写入。
132
+ * 同一场地下不同商品互不干扰,各自单独 reconcile。
112
133
  */
113
- private reconcileOrderForResource;
134
+ private reconcileOrderForResourceProduct;
135
+ /** 给定一个父 rawResource,返回其 combined_resource.resource_ids 对应的子 rawResource 列表。 */
136
+ private getCombinedChildRawResources;
114
137
  setSlotConfig(config: Partial<VenueBookingSlotConfig>): void;
115
138
  getSlotConfig(): VenueBookingSlotConfig;
116
139
  loadSchedules(): Promise<void>;