@pisell/pisellos 2.1.129 → 2.1.131

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 (54) hide show
  1. package/dist/model/strategy/adapter/promotion/index.js +9 -0
  2. package/dist/modules/Order/index.d.ts +7 -6
  3. package/dist/modules/Order/index.js +137 -42
  4. package/dist/modules/Order/types.d.ts +32 -6
  5. package/dist/modules/Order/types.js +2 -0
  6. package/dist/modules/Order/utils.d.ts +73 -11
  7. package/dist/modules/Order/utils.js +304 -52
  8. package/dist/modules/SalesSummary/utils.js +33 -68
  9. package/dist/modules/ScanOrderLogger/providers/feishu.js +168 -60
  10. package/dist/modules/ScanOrderLogger/types.d.ts +6 -0
  11. package/dist/modules/Summary/utils.js +6 -21
  12. package/dist/solution/ScanOrder/index.d.ts +57 -8
  13. package/dist/solution/ScanOrder/index.js +1531 -583
  14. package/dist/solution/ScanOrder/types.d.ts +86 -26
  15. package/dist/solution/ScanOrder/types.js +20 -1
  16. package/dist/solution/ScanOrder/utils.d.ts +53 -5
  17. package/dist/solution/ScanOrder/utils.js +257 -37
  18. package/dist/solution/VenueBooking/index.d.ts +30 -10
  19. package/dist/solution/VenueBooking/index.js +460 -217
  20. package/dist/solution/VenueBooking/types.d.ts +23 -0
  21. package/dist/solution/VenueBooking/utils/dateSummary.d.ts +1 -1
  22. package/dist/solution/VenueBooking/utils/dateSummary.js +1 -1
  23. package/dist/solution/VenueBooking/utils/resource.d.ts +11 -1
  24. package/dist/solution/VenueBooking/utils/resource.js +57 -21
  25. package/dist/solution/VenueBooking/utils/slotMerge.d.ts +5 -0
  26. package/dist/solution/VenueBooking/utils/slotMerge.js +33 -12
  27. package/dist/solution/VenueBooking/utils/timeSlot.d.ts +1 -1
  28. package/dist/solution/VenueBooking/utils/timeSlot.js +259 -62
  29. package/lib/modules/Order/index.d.ts +7 -6
  30. package/lib/modules/Order/index.js +123 -31
  31. package/lib/modules/Order/types.d.ts +32 -6
  32. package/lib/modules/Order/utils.d.ts +73 -11
  33. package/lib/modules/Order/utils.js +203 -28
  34. package/lib/modules/SalesSummary/utils.js +13 -47
  35. package/lib/modules/ScanOrderLogger/providers/feishu.js +100 -34
  36. package/lib/modules/ScanOrderLogger/types.d.ts +6 -0
  37. package/lib/modules/Summary/utils.js +4 -18
  38. package/lib/solution/ScanOrder/index.d.ts +57 -8
  39. package/lib/solution/ScanOrder/index.js +713 -117
  40. package/lib/solution/ScanOrder/types.d.ts +86 -26
  41. package/lib/solution/ScanOrder/utils.d.ts +53 -5
  42. package/lib/solution/ScanOrder/utils.js +186 -19
  43. package/lib/solution/VenueBooking/index.d.ts +30 -10
  44. package/lib/solution/VenueBooking/index.js +206 -51
  45. package/lib/solution/VenueBooking/types.d.ts +23 -0
  46. package/lib/solution/VenueBooking/utils/dateSummary.d.ts +1 -1
  47. package/lib/solution/VenueBooking/utils/dateSummary.js +1 -1
  48. package/lib/solution/VenueBooking/utils/resource.d.ts +11 -1
  49. package/lib/solution/VenueBooking/utils/resource.js +15 -4
  50. package/lib/solution/VenueBooking/utils/slotMerge.d.ts +5 -0
  51. package/lib/solution/VenueBooking/utils/slotMerge.js +29 -12
  52. package/lib/solution/VenueBooking/utils/timeSlot.d.ts +1 -1
  53. package/lib/solution/VenueBooking/utils/timeSlot.js +182 -43
  54. package/package.json +1 -1
@@ -35,14 +35,20 @@ __export(ScanOrder_exports, {
35
35
  module.exports = __toCommonJS(ScanOrder_exports);
36
36
  var import_BaseModule = require("../../modules/BaseModule");
37
37
  var import_types = require("./types");
38
+ var import_Order = require("../../modules/Order");
39
+ var import_types2 = require("../../modules/Account/types");
40
+ var import_types3 = require("../RegisterAndLogin/types");
41
+ var import_decimal = __toESM(require("decimal.js"));
38
42
  var import_utils = require("./utils");
39
- var import_types2 = require("../BookingByStep/types");
43
+ var import_types4 = require("../BookingByStep/types");
40
44
  var import_ProductList = require("../../modules/ProductList");
45
+ var import_Schedule = require("../../modules/Schedule");
46
+ var import_getDateIsInSchedule = require("../../modules/Schedule/getDateIsInSchedule");
41
47
  var import_utils2 = require("../../modules/Order/utils");
42
48
  var import_dayjs = __toESM(require("dayjs"));
43
49
  var import_itemRule = require("../../model/strategy/adapter/itemRule");
44
50
  __reExport(ScanOrder_exports, require("./types"), module.exports);
45
- var ScanOrderImpl = class extends import_BaseModule.BaseModule {
51
+ var _ScanOrderImpl = class extends import_BaseModule.BaseModule {
46
52
  constructor(name, version) {
47
53
  super(name, version);
48
54
  this.defaultName = "scanOrder";
@@ -72,6 +78,9 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
72
78
  this.itemRuleRuntimeConfig = {};
73
79
  /** 最近一次 checkResourceAvailable 从预约规则 link 拉取的商品快照 */
74
80
  this.enabledReservationRuleProducts = [];
81
+ this.loginEffectDisposers = [];
82
+ this.customerLoginRefreshInFlight = null;
83
+ this.customerLoginRefreshIdInFlight = null;
75
84
  }
76
85
  getScanOrderLoggerContext() {
77
86
  return {
@@ -82,11 +91,11 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
82
91
  }
83
92
  serializeError(error) {
84
93
  if (error instanceof Error) {
85
- return {
94
+ return JSON.stringify({
86
95
  name: error.name,
87
96
  message: error.message,
88
97
  stack: error.stack
89
- };
98
+ });
90
99
  }
91
100
  return {
92
101
  message: String(error)
@@ -155,6 +164,119 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
155
164
  }
156
165
  });
157
166
  }
167
+ normalizeCustomerId(value) {
168
+ const customerId = Number(value);
169
+ if (!Number.isFinite(customerId) || customerId <= 0)
170
+ return null;
171
+ return customerId;
172
+ }
173
+ resolveCustomerIdFromLoginPayload(payload) {
174
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
175
+ const candidates = [
176
+ payload == null ? void 0 : payload.customerId,
177
+ payload == null ? void 0 : payload.customer_id,
178
+ payload == null ? void 0 : payload.id,
179
+ (_a = payload == null ? void 0 : payload.user) == null ? void 0 : _a.customerId,
180
+ (_b = payload == null ? void 0 : payload.user) == null ? void 0 : _b.customer_id,
181
+ (_c = payload == null ? void 0 : payload.user) == null ? void 0 : _c.id,
182
+ (_e = (_d = payload == null ? void 0 : payload.user) == null ? void 0 : _d._origin) == null ? void 0 : _e.customer_id,
183
+ (_f = payload == null ? void 0 : payload.account) == null ? void 0 : _f.customerId,
184
+ (_g = payload == null ? void 0 : payload.account) == null ? void 0 : _g.customer_id,
185
+ (_h = payload == null ? void 0 : payload.account) == null ? void 0 : _h.id,
186
+ (_i = payload == null ? void 0 : payload._origin) == null ? void 0 : _i.customer_id
187
+ ];
188
+ for (const candidate of candidates) {
189
+ const customerId = this.normalizeCustomerId(candidate);
190
+ if (customerId)
191
+ return customerId;
192
+ }
193
+ return null;
194
+ }
195
+ clearLoginEffectListeners() {
196
+ for (const dispose of this.loginEffectDisposers) {
197
+ dispose();
198
+ }
199
+ this.loginEffectDisposers = [];
200
+ }
201
+ registerLoginEffect(event, callback) {
202
+ var _a;
203
+ const effects = (_a = this.core) == null ? void 0 : _a.effects;
204
+ if (!(effects == null ? void 0 : effects.on))
205
+ return;
206
+ effects.on(event, callback);
207
+ this.loginEffectDisposers.push(() => {
208
+ if (typeof effects.off === "function") {
209
+ effects.off(event, callback);
210
+ }
211
+ });
212
+ }
213
+ registerCustomerLoginListeners() {
214
+ this.clearLoginEffectListeners();
215
+ const createHandleLogin = () => async (payload) => {
216
+ const customerId = this.resolveCustomerIdFromLoginPayload(payload);
217
+ if (!customerId)
218
+ return;
219
+ await this.refreshOrderMarketingAfterLogin({ customerId });
220
+ };
221
+ this.registerLoginEffect(
222
+ _ScanOrderImpl.PISELL1_LOGIN_SUCCESS,
223
+ createHandleLogin()
224
+ );
225
+ this.registerLoginEffect(import_types2.AccountHooks.OnLogin, createHandleLogin());
226
+ this.registerLoginEffect(
227
+ import_types3.RegisterAndLoginHooks.onLoginSuccess,
228
+ createHandleLogin()
229
+ );
230
+ }
231
+ async refreshOrderMarketingAfterLogin(params) {
232
+ if (!this.store.order)
233
+ throw new Error("order 模块未初始化");
234
+ if (this.customerLoginRefreshInFlight) {
235
+ if (this.customerLoginRefreshIdInFlight === params.customerId) {
236
+ await this.customerLoginRefreshInFlight;
237
+ return;
238
+ }
239
+ await this.customerLoginRefreshInFlight;
240
+ }
241
+ this.customerLoginRefreshIdInFlight = params.customerId;
242
+ const refreshTask = (async () => {
243
+ await this.store.order.loadDiscountConfig({
244
+ customerId: params.customerId
245
+ });
246
+ await this.store.order.recalculateSummary({ createIfMissing: true });
247
+ this.store.order.persistTempOrder();
248
+ await this.refreshItemRuleQuantityLimits();
249
+ await this.refreshCartValidationPassed();
250
+ })();
251
+ this.customerLoginRefreshInFlight = refreshTask;
252
+ try {
253
+ await refreshTask;
254
+ } catch (error) {
255
+ throw error;
256
+ } finally {
257
+ if (this.customerLoginRefreshInFlight === refreshTask) {
258
+ this.customerLoginRefreshInFlight = null;
259
+ this.customerLoginRefreshIdInFlight = null;
260
+ }
261
+ }
262
+ }
263
+ /** 与 `otherParams.cacheId` 一致,供宿主在 URL 变化时判断是否需要重新注册模块 */
264
+ getCacheId() {
265
+ return this.cacheId;
266
+ }
267
+ async destroyRegisteredChildModules() {
268
+ const modules = [
269
+ this.store.schedule,
270
+ this.store.salesSummary,
271
+ this.store.order,
272
+ this.store.products,
273
+ this.store.scanOrderLogger
274
+ ];
275
+ for (const mod of modules) {
276
+ if (mod && typeof mod.destroy === "function")
277
+ await Promise.resolve(mod.destroy());
278
+ }
279
+ }
158
280
  async initialize(core, options = {}) {
159
281
  var _a, _b, _c, _d, _e, _f;
160
282
  this.logMethodStart("initialize");
@@ -193,7 +315,7 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
193
315
  ];
194
316
  moduleArr.forEach((step) => {
195
317
  var _a2, _b2, _c2, _d2, _e2, _f2;
196
- const targetModule = (0, import_types2.createModule)(step, this.name);
318
+ const targetModule = (0, import_types4.createModule)(step, this.name);
197
319
  if (targetModule) {
198
320
  this.store[step] = targetModule;
199
321
  const initialState = step === "salesSummary" ? {
@@ -202,7 +324,7 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
202
324
  const loggerProvider = ((_c2 = this.otherParams) == null ? void 0 : _c2.scanOrderLoggerProvider) || "feishu";
203
325
  const loggerConfig = ((_d2 = this.otherParams) == null ? void 0 : _d2.scanOrderLoggerConfig) || {
204
326
  feishu: {
205
- // webhook: 'https://open.feishu.cn/open-apis/bot/v2/hook/216b3fe6-af98-424e-8706-f0471241a7ed',
327
+ webhook: "https://open.feishu.cn/open-apis/bot/v2/hook/216b3fe6-af98-424e-8706-f0471241a7ed"
206
328
  }
207
329
  };
208
330
  this.core.registerModule(targetModule, {
@@ -226,11 +348,27 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
226
348
  if (this.store.scanOrderLogger) {
227
349
  this.store.scanOrderLogger.setContext(this.getScanOrderLoggerContext());
228
350
  }
351
+ const scheduleModule = new import_Schedule.ScheduleModule(`${this.name}_schedule`);
352
+ this.store.schedule = scheduleModule;
353
+ this.core.registerModule(scheduleModule, {
354
+ otherParams: {
355
+ ...this.otherParams,
356
+ fatherModule: this.name
357
+ }
358
+ });
359
+ this.registerCustomerLoginListeners();
229
360
  console.log("[ScanOrder] 初始化开始");
230
361
  try {
231
362
  await ((_e = this.store.order) == null ? void 0 : _e.recalculateSummary({ createIfMissing: false }));
232
363
  (_f = this.store.order) == null ? void 0 : _f.persistTempOrder();
233
364
  await this.loadRuntimeConfigs();
365
+ if (this.store.schedule) {
366
+ try {
367
+ await this.store.schedule.loadAllSchedule();
368
+ } catch (scheduleError) {
369
+ console.warn("[ScanOrder] loadAllSchedule 失败,operating_hours 判定将跳过", scheduleError);
370
+ }
371
+ }
234
372
  await this.refreshItemRuleQuantityLimits();
235
373
  this.store.status = "ready";
236
374
  console.log("[ScanOrder] 初始化完成");
@@ -252,7 +390,10 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
252
390
  }
253
391
  async destroy() {
254
392
  this.logMethodStart("destroy");
393
+ this.clearLoginEffectListeners();
255
394
  await this.core.effects.emit(import_types.ScanOrderHooks.onDestroy, {});
395
+ await this.destroyRegisteredChildModules();
396
+ super.destroy();
256
397
  console.log("[ScanOrder] 已销毁");
257
398
  this.logMethodSuccess("destroy");
258
399
  }
@@ -368,6 +509,50 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
368
509
  throw error;
369
510
  }
370
511
  }
512
+ // 存储 UI 层选择的取餐方式到 tempOrder.metadata.shop_service_data
513
+ // service_type 在扫码点餐场景固定为 dine_in
514
+ setPickupReferenceMode(mode) {
515
+ this.logMethodStart("setPickupReferenceMode", { mode });
516
+ try {
517
+ if (!this.store.order)
518
+ throw new Error("order 模块未初始化");
519
+ const tempOrder = this.store.order.ensureTempOrder();
520
+ tempOrder.metadata = {
521
+ ...tempOrder.metadata || {},
522
+ shop_service_data: {
523
+ ...(tempOrder.metadata || {}).shop_service_data || {},
524
+ service_type: "dine_in",
525
+ pickup_reference_mode: mode
526
+ }
527
+ };
528
+ this.store.order.persistTempOrder();
529
+ this.logMethodSuccess("setPickupReferenceMode", {
530
+ mode
531
+ });
532
+ return tempOrder.metadata.shop_service_data;
533
+ } catch (error) {
534
+ this.logMethodError("setPickupReferenceMode", error, { mode });
535
+ throw error;
536
+ }
537
+ }
538
+ // 存储用户自定义取餐标识到 tempOrder.buzzer
539
+ setPickupRef(buzzer) {
540
+ this.logMethodStart("setPickupRef", {
541
+ buzzerLength: String(buzzer || "").length
542
+ });
543
+ try {
544
+ if (!this.store.order)
545
+ throw new Error("order 模块未初始化");
546
+ const result = this.store.order.updateTempOrderBuzzer(buzzer);
547
+ this.logMethodSuccess("setPickupRef", {
548
+ buzzerLength: result.length
549
+ });
550
+ return result;
551
+ } catch (error) {
552
+ this.logMethodError("setPickupRef", error);
553
+ throw error;
554
+ }
555
+ }
371
556
  ensureTempOrder() {
372
557
  if (!this.store.order)
373
558
  throw new Error("order 模块未初始化");
@@ -389,6 +574,25 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
389
574
  throw error;
390
575
  }
391
576
  }
577
+ // 重置 tempOrder 与与上一单强相关的运行态(resource/entryPaxNumber/enabledReservationRuleProducts),
578
+ // 适用于从支付页通过 react-router 跳回继续下单时清理残留数据
579
+ async restoreOrder() {
580
+ this.logMethodStart("restoreOrder");
581
+ try {
582
+ if (!this.store.order) {
583
+ throw new Error("scanOrder 解决方案需要 order 模块支持");
584
+ }
585
+ this.store.resource = null;
586
+ this.store.entryPaxNumber = 1;
587
+ this.enabledReservationRuleProducts = [];
588
+ const tempOrder = this.store.order.restoreOrder();
589
+ this.logMethodSuccess("restoreOrder");
590
+ return tempOrder;
591
+ } catch (error) {
592
+ this.logMethodError("restoreOrder", error);
593
+ throw error;
594
+ }
595
+ }
392
596
  getOrderProducts() {
393
597
  this.logMethodStart("getOrderProducts");
394
598
  if (!this.store.order)
@@ -414,8 +618,229 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
414
618
  throw error;
415
619
  }
416
620
  }
417
- async submitScanOrder() {
621
+ getDiscountList() {
622
+ if (!this.store.order)
623
+ return [];
624
+ return this.store.order.getDiscountList();
625
+ }
626
+ async scanCode(code, customerId) {
627
+ this.logMethodStart("scanCode", { code });
628
+ try {
629
+ if (!this.store.order)
630
+ throw new Error("order 模块未初始化");
631
+ const raw = await this.store.order.scanCode(code, customerId);
632
+ if (raw.isAvailable) {
633
+ await this.store.order.recalculateSummary({ createIfMissing: true });
634
+ this.store.order.persistTempOrder();
635
+ }
636
+ this.logMethodSuccess("scanCode", { isAvailable: raw.isAvailable });
637
+ return {
638
+ isAvailable: raw.isAvailable,
639
+ type: raw.type,
640
+ unavailableReason: raw.unavailableReason
641
+ };
642
+ } catch (error) {
643
+ this.logMethodError("scanCode", error);
644
+ throw error;
645
+ }
646
+ }
647
+ async setDiscountSelected(params) {
418
648
  var _a, _b, _c, _d, _e;
649
+ this.logMethodStart("setDiscountSelected", params);
650
+ try {
651
+ if (!this.store.order)
652
+ throw new Error("order 模块未初始化");
653
+ const list = this.store.order.getDiscountList();
654
+ const updated = list.map(
655
+ (d) => d.id === params.discountId ? {
656
+ ...d,
657
+ isSelected: params.isSelected,
658
+ isManualSelect: !params.isSelected
659
+ } : d
660
+ );
661
+ const tempOrder = this.store.order.ensureTempOrder();
662
+ const orderStore = this.store.order.store || {};
663
+ const discountModule = orderStore.discount;
664
+ const rulesModule = orderStore.rules;
665
+ const holders = ((_a = tempOrder.holder) == null ? void 0 : _a.form_record_id) ? [{ form_record_id: tempOrder.holder.form_record_id }] : [];
666
+ let nextDiscountList = updated;
667
+ await (discountModule == null ? void 0 : discountModule.setDiscountList(updated));
668
+ if (rulesModule) {
669
+ const result = rulesModule.calcDiscount(
670
+ {
671
+ productList: tempOrder.products,
672
+ discountList: updated,
673
+ holders,
674
+ isFormSubject: !!((_b = tempOrder.holder) == null ? void 0 : _b.type) && tempOrder.holder.type === "form"
675
+ },
676
+ {
677
+ discountId: params.discountId,
678
+ isSelected: params.isSelected
679
+ }
680
+ ) || { productList: tempOrder.products, discountList: updated };
681
+ if (result == null ? void 0 : result.productList) {
682
+ tempOrder.products = result.productList;
683
+ }
684
+ if (result == null ? void 0 : result.discountList) {
685
+ nextDiscountList = result.discountList;
686
+ if (!params.isSelected) {
687
+ const beforeSelectedIds = new Set(
688
+ updated.filter((d) => d.isSelected).map((d) => d.id)
689
+ );
690
+ for (const d of nextDiscountList) {
691
+ if (d.isSelected && !beforeSelectedIds.has(d.id)) {
692
+ d.isSelected = false;
693
+ }
694
+ }
695
+ }
696
+ }
697
+ }
698
+ const selectedResourceIds = new Set(
699
+ nextDiscountList.filter((d) => d.isSelected).map((d) => d.id)
700
+ );
701
+ for (const product of tempOrder.products) {
702
+ if ((_c = product._origin) == null ? void 0 : _c.isManualDiscount)
703
+ continue;
704
+ product.discount_list = (product.discount_list || []).filter((pd) => {
705
+ var _a2;
706
+ const rid = ((_a2 = pd.discount) == null ? void 0 : _a2.resource_id) ?? pd.id;
707
+ return rid != null && selectedResourceIds.has(rid);
708
+ });
709
+ const totalPerUnitDiscount = (product.discount_list || []).reduce(
710
+ (sum, pd) => sum + (pd.amount || 0),
711
+ 0
712
+ );
713
+ const optionSum = (0, import_utils2.sumOptionUnitPrice)(product.product_option_item);
714
+ const sourcePrice = ((_d = product.metadata) == null ? void 0 : _d.source_product_price) ?? (((_e = product.metadata) == null ? void 0 : _e.main_product_original_price) != null ? new import_decimal.default(Number(product.metadata.main_product_original_price) || 0).minus(optionSum).toFixed(2) : product.original_price ?? "0");
715
+ const newSourceSellingPrice = new import_decimal.default(Number(sourcePrice) || 0).minus(totalPerUnitDiscount).toDecimalPlaces(2).toString();
716
+ const newMainSellingPrice = new import_decimal.default(Number(newSourceSellingPrice) || 0).plus(optionSum).toDecimalPlaces(2).toFixed(2);
717
+ if (product.metadata) {
718
+ product.metadata.main_product_selling_price = newMainSellingPrice;
719
+ product.metadata.price_schema_version = 2;
720
+ }
721
+ product.selling_price = (0, import_utils2.composeLinePrice)({
722
+ mainPrice: newMainSellingPrice,
723
+ bundle: product.product_bundle
724
+ });
725
+ }
726
+ import_Order.OrderModule.populateSavedAmounts(tempOrder.products, nextDiscountList);
727
+ await (discountModule == null ? void 0 : discountModule.setDiscountList(nextDiscountList));
728
+ tempOrder.discount_list = (nextDiscountList || []).filter((d) => d.isSelected);
729
+ await this.store.order.recalculateSummary({ createIfMissing: true });
730
+ this.store.order.persistTempOrder();
731
+ this.logMethodSuccess("setDiscountSelected", params);
732
+ } catch (error) {
733
+ this.logMethodError("setDiscountSelected", error);
734
+ throw error;
735
+ }
736
+ }
737
+ async onCustomerLogin(params) {
738
+ this.logMethodStart("onCustomerLogin", { customerId: params.customerId });
739
+ try {
740
+ await this.refreshOrderMarketingAfterLogin({
741
+ customerId: params.customerId
742
+ });
743
+ this.logMethodSuccess("onCustomerLogin", { customerId: params.customerId });
744
+ } catch (error) {
745
+ this.logMethodError("onCustomerLogin", error);
746
+ throw error;
747
+ }
748
+ }
749
+ // ScanOrder 提交 payload enhancer:
750
+ // - 给所有 booking 注入 appointment_status: 'started'(扫码点餐语义)
751
+ // - 给所有 booking 的 metadata 注入 resource_select_type(来自预约规则商品的 resource.type)
752
+ // - 给第一条 booking 补 resources 与 product_uid(仅当存在 resource / rule product)
753
+ // - 追加一条 is_rule=true 的 rule product,与 booking 互相关联
754
+ buildSubmitPayloadEnhancer() {
755
+ const ruleProduct = this.enabledReservationRuleProducts[0];
756
+ const resourceState = this.store.resource;
757
+ const resourceSelectType = resourceState == null ? void 0 : resourceState.resourceSelectType;
758
+ const resolveResourceCapacity = () => {
759
+ var _a;
760
+ if (resourceSelectType === "single") {
761
+ const raw = (_a = resourceState == null ? void 0 : resourceState.table_form_record) == null ? void 0 : _a.capacity;
762
+ const num = Number(raw);
763
+ if (Number.isFinite(num) && num > 0)
764
+ return num;
765
+ return 1;
766
+ }
767
+ return 1;
768
+ };
769
+ return (payload, { bookingUuid, tempOrder }) => {
770
+ var _a, _b;
771
+ const resourceId = tempOrder.resource_id ?? (resourceState == null ? void 0 : resourceState.relationId);
772
+ const pickOriginal = (value) => {
773
+ if (value && typeof value === "object" && !Array.isArray(value)) {
774
+ const original = value.original;
775
+ return typeof original === "string" && original.length > 0 ? original : void 0;
776
+ }
777
+ return void 0;
778
+ };
779
+ const mainField = pickOriginal(tempOrder.table_number) ?? pickOriginal((_a = resourceState == null ? void 0 : resourceState.table_form_record) == null ? void 0 : _a.name) ?? "";
780
+ const resourceEntry = resourceState && resourceId ? {
781
+ relation_type: "form",
782
+ like_status: "common",
783
+ id: Number(resourceId),
784
+ main_field: mainField,
785
+ form_id: resourceState.tableFormId,
786
+ relation_id: resourceState.relationId ?? resourceId,
787
+ capacity: resolveResourceCapacity(),
788
+ metadata: {}
789
+ } : void 0;
790
+ const ruleProductUid = ruleProduct ? (0, import_utils2.createUuidV4)() : void 0;
791
+ const rawCollectPax = (_b = tempOrder.metadata) == null ? void 0 : _b.collect_pax;
792
+ const hasCollectPaxForCapacity = rawCollectPax !== null && rawCollectPax !== void 0 && rawCollectPax !== "";
793
+ const bookingCapacityValue = hasCollectPaxForCapacity ? (0, import_utils2.normalizeSubmitCollectPaxValue)(rawCollectPax) : resolveResourceCapacity();
794
+ const bookingCapacityDimensionId = (0, import_utils.pickFirstCustomCapacityDimensionId)(this.enabledReservationRuleProducts) ?? 0;
795
+ const nextBookings = (payload.bookings || []).map((booking, idx) => ({
796
+ ...booking,
797
+ appointment_status: "started",
798
+ metadata: {
799
+ ...booking.metadata || {},
800
+ ...resourceSelectType ? { resource_select_type: resourceSelectType } : {},
801
+ ...resourceSelectType ? {
802
+ capacity: [
803
+ {
804
+ id: bookingCapacityDimensionId,
805
+ value: bookingCapacityValue,
806
+ name: ""
807
+ }
808
+ ]
809
+ } : {}
810
+ },
811
+ ...idx === 0 && resourceEntry ? { resources: [resourceEntry] } : {},
812
+ ...idx === 0 && ruleProductUid ? { product_uid: ruleProductUid } : {}
813
+ }));
814
+ const nextProducts = [...payload.products || []];
815
+ if (ruleProduct && ruleProductUid && !tempOrder.order_id) {
816
+ const sellingPrice = String(ruleProduct.price ?? "0.00");
817
+ const originalPrice = String(
818
+ ruleProduct.original_price ?? ruleProduct.price ?? "0.00"
819
+ );
820
+ nextProducts.push({
821
+ product_id: ruleProduct.id,
822
+ product_variant_id: 0,
823
+ num: 1,
824
+ selling_price: sellingPrice,
825
+ original_price: originalPrice,
826
+ payment_price: sellingPrice,
827
+ is_charge_tax: ruleProduct.is_gst ?? 0,
828
+ product_option_item: [],
829
+ discount_list: [],
830
+ product_bundle: [],
831
+ booking_uid: bookingUuid,
832
+ metadata: {
833
+ is_rule: true,
834
+ unique_identification_number: ruleProductUid,
835
+ booking_uid: bookingUuid
836
+ }
837
+ });
838
+ }
839
+ return { ...payload, bookings: nextBookings, products: nextProducts };
840
+ };
841
+ }
842
+ async submitScanOrder() {
843
+ var _a, _b, _c, _d, _e, _f, _g;
419
844
  this.logMethodStart("submitScanOrder");
420
845
  try {
421
846
  await this.validateBeforeSubmitByItemRule();
@@ -426,17 +851,26 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
426
851
  this.store.entryPaxNumber = pax;
427
852
  const tempOrderForSubmit = this.store.order.ensureTempOrder();
428
853
  tempOrderForSubmit.metadata = { ...tempOrderForSubmit.metadata, collect_pax: pax };
854
+ tempOrderForSubmit.delivery_type = "shop_service";
855
+ const resourceTableName = (_b = (_a = this.store.resource) == null ? void 0 : _a.table_form_record) == null ? void 0 : _b.name;
856
+ if (resourceTableName && typeof resourceTableName === "object") {
857
+ tempOrderForSubmit.table_number = resourceTableName;
858
+ } else {
859
+ delete tempOrderForSubmit.table_number;
860
+ }
429
861
  this.store.order.persistTempOrder();
862
+ const enhancePayload = this.buildSubmitPayloadEnhancer();
430
863
  const result = await this.store.order.submitTempOrder({
431
864
  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
865
+ platform: (_c = this.otherParams) == null ? void 0 : _c.platform,
866
+ businessCode: (_d = this.otherParams) == null ? void 0 : _d.businessCode,
867
+ channel: (_e = this.otherParams) == null ? void 0 : _e.channel,
868
+ type: (_f = this.otherParams) == null ? void 0 : _f.type,
869
+ enhancePayload
436
870
  });
437
871
  const tempOrder = this.store.order.getTempOrder();
438
872
  this.logMethodSuccess("submitScanOrder", {
439
- productCount: ((_e = tempOrder == null ? void 0 : tempOrder.products) == null ? void 0 : _e.length) || 0
873
+ productCount: ((_g = tempOrder == null ? void 0 : tempOrder.products) == null ? void 0 : _g.length) || 0
440
874
  });
441
875
  return result;
442
876
  } catch (error) {
@@ -484,6 +918,36 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
484
918
  throw error;
485
919
  }
486
920
  }
921
+ /**
922
+ * 设置单行商品备注(与整单 `updateTempOrderNote` 区分)。
923
+ * 多行同 SKU 时必须传入与删除/更新一致的 identity(如 `identity_key`)。
924
+ */
925
+ async setOrderProductLineNote(identity, note) {
926
+ this.logMethodStart("setOrderProductLineNote", {
927
+ product_id: identity.product_id,
928
+ product_variant_id: identity.product_variant_id
929
+ });
930
+ try {
931
+ const params = {
932
+ product_id: identity.product_id,
933
+ product_variant_id: identity.product_variant_id,
934
+ updates: { note: String(note || "") }
935
+ };
936
+ if (identity.identity_key !== void 0)
937
+ params.identity_key = identity.identity_key;
938
+ if (identity.product_option_item !== void 0) {
939
+ params.product_option_item = identity.product_option_item;
940
+ }
941
+ if (identity.product_bundle !== void 0)
942
+ params.product_bundle = identity.product_bundle;
943
+ const products = await this.updateProductInOrder(params);
944
+ this.logMethodSuccess("setOrderProductLineNote", { productCount: products.length });
945
+ return products;
946
+ } catch (error) {
947
+ this.logMethodError("setOrderProductLineNote", error);
948
+ throw error;
949
+ }
950
+ }
487
951
  async removeProductFromOrder(identity) {
488
952
  this.logMethodStart("removeProductFromOrder", {
489
953
  product_id: identity.product_id,
@@ -857,20 +1321,22 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
857
1321
  strategyCount: Array.isArray(this.itemRuleRuntimeConfig.strategyConfigs) ? this.itemRuleRuntimeConfig.strategyConfigs.length : 0
858
1322
  });
859
1323
  }
860
- normalizeResourceState(config, hasOrderId) {
861
- var _a, _b;
862
- const orderNumberPrefix = Array.isArray(config == null ? void 0 : config.order_number_prefix) ? (config == null ? void 0 : config.order_number_prefix) || [] : [];
863
- const tableMaxNumber = Number((config == null ? void 0 : config.table_max_number) || 1);
864
- const orderCount = (0, import_utils.toNonNegativeNumber)(config == null ? void 0 : config.order_count);
865
- const currentOrderId = (0, import_utils.toPositiveString)(config == null ? void 0 : config.order_id);
866
- const lastOrderId = (0, import_utils.toPositiveString)(config == null ? void 0 : config.last_order_id);
867
- const relationId = (0, import_utils.toPositiveString)(config == null ? void 0 : config.relation_id);
868
- const tableFormId = (0, import_utils.toPositiveString)(config == null ? void 0 : config.table_form_id);
869
- const allowSnack = ((_b = (_a = this.otherParams) == null ? void 0 : _a.dineInConfig) == null ? void 0 : _b["workflow.allow_add_items"]) || false;
1324
+ normalizeResourceState(detail, resourceSelectType, hasOrderId) {
1325
+ var _a, _b, _c, _d;
1326
+ const currentOrderId = (0, import_utils.toPositiveString)(detail == null ? void 0 : detail.order_id);
1327
+ const lastOrderId = (0, import_utils.toPositiveString)(detail == null ? void 0 : detail.last_order_id);
1328
+ const relationId = (0, import_utils.toPositiveString)(detail == null ? void 0 : detail.form_record_id);
1329
+ const tableFormId = (0, import_utils.toPositiveString)(detail == null ? void 0 : detail.form_id);
1330
+ const formRecord = (detail == null ? void 0 : detail.form_record) ?? null;
1331
+ const allowSnack = ((_b = (_a = this.otherParams) == null ? void 0 : _a.dineInConfig) == null ? void 0 : _b["sale.allow_add_items"]) || false;
870
1332
  const deskmateValid = false;
871
- const isExclusive = tableMaxNumber > 0 ? tableMaxNumber <= 1 : false;
872
- const isFull = tableMaxNumber > 0 ? orderCount >= tableMaxNumber : false;
873
- const isBlock = tableMaxNumber === -1;
1333
+ const isExclusive = resourceSelectType === "single";
1334
+ const isFull = (0, import_utils.computeResourceIsFull)({
1335
+ resourceSelectType,
1336
+ lastOrderId,
1337
+ capacityList: ((_d = (_c = detail == null ? void 0 : detail.resource_capacity) == null ? void 0 : _c[0]) == null ? void 0 : _d.capacity_list) ?? [],
1338
+ capacity: formRecord == null ? void 0 : formRecord.capacity
1339
+ });
874
1340
  let availabilityInfo = {
875
1341
  mode: "idle",
876
1342
  deskmate_valid: deskmateValid
@@ -894,33 +1360,19 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
894
1360
  };
895
1361
  }
896
1362
  } else if (lastOrderId) {
897
- availabilityInfo = allowSnack ? {
898
- mode: deskmateValid ? "additional_order_with_code" : "additional_order",
899
- order_id: lastOrderId,
900
- relation_id: relationId,
901
- table_form_id: tableFormId,
902
- deskmate_valid: deskmateValid
903
- } : {
904
- mode: "resource_busy",
905
- order_id: lastOrderId,
906
- relation_id: relationId,
907
- table_form_id: tableFormId,
908
- deskmate_valid: deskmateValid
909
- };
910
- }
911
- if (isBlock) {
912
- availabilityInfo = {
913
- mode: "resource_block",
914
- order_id: lastOrderId,
915
- relation_id: relationId,
916
- table_form_id: tableFormId,
917
- deskmate_valid: deskmateValid
918
- };
1363
+ const canFallthroughToIdle = resourceSelectType === "multiple" && !isFull;
1364
+ if (!canFallthroughToIdle) {
1365
+ availabilityInfo = {
1366
+ mode: "resource_busy",
1367
+ order_id: "0",
1368
+ relation_id: relationId,
1369
+ table_form_id: tableFormId,
1370
+ deskmate_valid: deskmateValid
1371
+ };
1372
+ }
919
1373
  }
920
1374
  return {
921
1375
  ...availabilityInfo,
922
- tableMaxNumber,
923
- orderCount,
924
1376
  currentOrderId,
925
1377
  lastOrderId,
926
1378
  relationId,
@@ -929,27 +1381,46 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
929
1381
  deskmateValid,
930
1382
  isExclusive,
931
1383
  isFull,
932
- orderNumberPrefix,
933
- raw: config,
934
- table_form_record: (config == null ? void 0 : config.table_form_record) ?? null
1384
+ resourceSelectType,
1385
+ raw: detail,
1386
+ table_form_record: formRecord
935
1387
  };
936
1388
  }
937
- async fetchTableConfigByResourceId(resourceId) {
938
- const tableResourceId = Number(resourceId);
939
- if (!Number.isFinite(tableResourceId) || tableResourceId <= 0) {
1389
+ // 从首个预约规则商品的 product_resource.resources 中,按 form_id 匹配返回 type
1390
+ resolveResourceSelectType(formId) {
1391
+ var _a;
1392
+ const firstProduct = this.enabledReservationRuleProducts[0];
1393
+ const resources = ((_a = firstProduct == null ? void 0 : firstProduct.product_resource) == null ? void 0 : _a.resources) || [];
1394
+ const numericFormId = Number(formId);
1395
+ if (!Number.isFinite(numericFormId) || numericFormId <= 0)
1396
+ return void 0;
1397
+ const matched = resources.find((r) => Number(r == null ? void 0 : r.id) === numericFormId);
1398
+ return matched == null ? void 0 : matched.type;
1399
+ }
1400
+ async fetchResourceOccupyDetailByResourceId(resourceId) {
1401
+ var _a, _b, _c, _d;
1402
+ const formRecordId = Number(resourceId);
1403
+ if (!Number.isFinite(formRecordId) || formRecordId <= 0) {
940
1404
  throw new Error(`[ScanOrder] 非法桌台 resourceId: ${resourceId}`);
941
1405
  }
1406
+ const shopId = (_b = (_a = this.otherParams) == null ? void 0 : _a.getStateData) == null ? void 0 : _b.call(_a, "shop_id");
1407
+ if (!shopId) {
1408
+ throw new Error("[ScanOrder] 无法获取 shop_id");
1409
+ }
942
1410
  const response = await this.request.get(
943
- "/order/dining/table/config",
1411
+ "/order/resource/occupy-detail",
944
1412
  {
945
- table_resource_id: tableResourceId,
946
- with_table_form_info: 1
1413
+ shop_id: shopId,
1414
+ "form_record_ids[]": formRecordId,
1415
+ with_resource_capacity_info: 1,
1416
+ with_resource_order_info: 1,
1417
+ with_resource_form_info: 1
947
1418
  }
948
1419
  );
949
1420
  if (!(response == null ? void 0 : response.status)) {
950
- throw new Error((response == null ? void 0 : response.message) || "获取桌台配置失败");
1421
+ throw new Error((response == null ? void 0 : response.message) || "获取资源占用详情失败");
951
1422
  }
952
- return (response == null ? void 0 : response.data) || null;
1423
+ return ((_d = (_c = response == null ? void 0 : response.data) == null ? void 0 : _c.occupy_details) == null ? void 0 : _d[0]) ?? null;
953
1424
  }
954
1425
  // 检测当前链接是否可用
955
1426
  // 通过 resource_id + 店铺配置
@@ -971,46 +1442,48 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
971
1442
  const dineInConfig = (openData == null ? void 0 : openData.data) || {};
972
1443
  this.otherParams.dineInConfig = dineInConfig;
973
1444
  await this.syncItemRuleConfigsFromDineInConfig(dineInConfig);
974
- const shopClosedInfo = () => ({
1445
+ const closedBehaviorValue = dineInConfig == null ? void 0 : dineInConfig["availability.closed_behavior"];
1446
+ 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"]);
1447
+ const basicUnavailableMessage = (dineInConfig == null ? void 0 : dineInConfig["basic.unavailable_message"]) || closedMessage;
1448
+ const pauseMessage = (dineInConfig == null ? void 0 : dineInConfig["availability.pause_message"]) || closedMessage;
1449
+ const makeShopClosed = (errorTips, closed_behavior) => ({
975
1450
  mode: "shop_closed",
976
1451
  order_id: void 0,
977
1452
  relation_id: void 0,
978
1453
  table_form_id: void 0,
979
1454
  deskmate_valid: false,
980
- 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"])
1455
+ errorTips,
1456
+ closed_behavior
981
1457
  });
1458
+ if ((dineInConfig == null ? void 0 : dineInConfig["basic.enable"]) === false) {
1459
+ return makeShopClosed(basicUnavailableMessage);
1460
+ }
982
1461
  if ((0, import_utils.toBoolean)(dineInConfig == null ? void 0 : dineInConfig["availability.paused"])) {
983
1462
  if ((dineInConfig == null ? void 0 : dineInConfig["availability.pause_behavior"]) === "hide_all") {
984
- return shopClosedInfo();
1463
+ return makeShopClosed(pauseMessage);
985
1464
  }
986
1465
  }
987
- const config = await this.fetchTableConfigByResourceId(resourceId);
988
- const resourceState = this.normalizeResourceState(config, hasOrderId);
989
- this.store.resource = resourceState;
990
- const availabilityInfo = {
991
- mode: resourceState.mode,
992
- order_id: resourceState.order_id,
993
- relation_id: resourceState.relationId,
994
- table_form_id: resourceState.tableFormId,
995
- deskmate_valid: resourceState.deskmate_valid,
996
- table_form_record: resourceState.table_form_record,
997
- policy: (_c = config == null ? void 0 : config.table_form_record) == null ? void 0 : _c.policy,
998
- partyroom_booking: (_d = config == null ? void 0 : config.table_form_record) == null ? void 0 : _d.partyroom_booking
999
- };
1466
+ 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) : [];
1467
+ let outsideOperatingHours = false;
1468
+ if (operatingHourIds.length && this.store.schedule) {
1469
+ const scheduleList = this.store.schedule.getScheduleListByIds(operatingHourIds);
1470
+ if (scheduleList.length) {
1471
+ const now = (0, import_dayjs.default)().format("YYYY-MM-DD HH:mm:ss");
1472
+ outsideOperatingHours = !(0, import_getDateIsInSchedule.getDateIsInSchedule)(now, scheduleList);
1473
+ }
1474
+ }
1475
+ if (outsideOperatingHours && closedBehaviorValue !== "show_menu_disabled") {
1476
+ return makeShopClosed(closedMessage, closedBehaviorValue);
1477
+ }
1000
1478
  const tempOrder = this.ensureTempOrder();
1001
- tempOrder.relation_id = resourceId || ((_e = this.otherParams) == null ? void 0 : _e.relation_id);
1002
- tempOrder.table_form_id = resourceState.tableFormId;
1003
- tempOrder.resource_id = resourceId;
1004
1479
  const reservationLinkIds = (0, import_utils.collectLinkProductIdsFromReservationRules)(
1005
- dineInConfig["reservation.enabled_reservation_rules"]
1480
+ dineInConfig["fulfillment.enabled_resource_rules"]
1006
1481
  );
1482
+ let pendingRequestEntryPax;
1483
+ let pendingRequestPaxMin;
1484
+ let pendingRequestPaxMax;
1007
1485
  if (reservationLinkIds.length === 0) {
1008
1486
  this.enabledReservationRuleProducts = [];
1009
- if (this.store.resource) {
1010
- delete this.store.resource.requestEntryPax;
1011
- delete this.store.resource.requestPaxMin;
1012
- delete this.store.resource.requestPaxMax;
1013
- }
1014
1487
  } else {
1015
1488
  tempOrder.metadata = { ...tempOrder.metadata || {} };
1016
1489
  delete tempOrder.metadata.table_occupancy_duration;
@@ -1042,38 +1515,15 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
1042
1515
  tempOrder.metadata.table_occupancy_duration = occupancyMinutes;
1043
1516
  }
1044
1517
  if ((0, import_utils.hasCustomCapacityProduct)(loaded)) {
1045
- availabilityInfo.requestEntryPax = 1;
1046
- if (this.store.resource)
1047
- this.store.resource.requestEntryPax = 1;
1048
- delete availabilityInfo.requestPaxMin;
1049
- delete availabilityInfo.requestPaxMax;
1050
- if (this.store.resource) {
1051
- delete this.store.resource.requestPaxMin;
1052
- delete this.store.resource.requestPaxMax;
1053
- }
1518
+ pendingRequestEntryPax = 1;
1054
1519
  const paxBounds = (0, import_utils.pickFirstCustomCapacityPaxBounds)(loaded);
1055
- if ((paxBounds == null ? void 0 : paxBounds.min) !== void 0) {
1056
- availabilityInfo.requestPaxMin = paxBounds.min;
1057
- if (this.store.resource)
1058
- this.store.resource.requestPaxMin = paxBounds.min;
1059
- }
1060
- if ((paxBounds == null ? void 0 : paxBounds.max) !== void 0) {
1061
- availabilityInfo.requestPaxMax = paxBounds.max;
1062
- if (this.store.resource)
1063
- this.store.resource.requestPaxMax = paxBounds.max;
1064
- }
1065
- } else if (this.store.resource) {
1066
- delete this.store.resource.requestEntryPax;
1067
- delete this.store.resource.requestPaxMin;
1068
- delete this.store.resource.requestPaxMax;
1520
+ if ((paxBounds == null ? void 0 : paxBounds.min) !== void 0)
1521
+ pendingRequestPaxMin = paxBounds.min;
1522
+ if ((paxBounds == null ? void 0 : paxBounds.max) !== void 0)
1523
+ pendingRequestPaxMax = paxBounds.max;
1069
1524
  }
1070
1525
  } else {
1071
1526
  this.enabledReservationRuleProducts = [];
1072
- if (this.store.resource) {
1073
- delete this.store.resource.requestEntryPax;
1074
- delete this.store.resource.requestPaxMin;
1075
- delete this.store.resource.requestPaxMax;
1076
- }
1077
1527
  void this.addScanOrderLog({
1078
1528
  level: "error",
1079
1529
  title: "[ScanOrder] enabled_reservation_rules product query failed",
@@ -1084,6 +1534,41 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
1084
1534
  });
1085
1535
  }
1086
1536
  }
1537
+ const occupyDetail = await this.fetchResourceOccupyDetailByResourceId(resourceId);
1538
+ const resourceSelectType = this.resolveResourceSelectType(
1539
+ (0, import_utils.toPositiveString)(occupyDetail == null ? void 0 : occupyDetail.form_id)
1540
+ );
1541
+ const resourceState = this.normalizeResourceState(
1542
+ occupyDetail,
1543
+ resourceSelectType,
1544
+ hasOrderId
1545
+ );
1546
+ this.store.resource = resourceState;
1547
+ if (pendingRequestEntryPax !== void 0) {
1548
+ this.store.resource.requestEntryPax = pendingRequestEntryPax;
1549
+ }
1550
+ if (pendingRequestPaxMin !== void 0) {
1551
+ this.store.resource.requestPaxMin = pendingRequestPaxMin;
1552
+ }
1553
+ if (pendingRequestPaxMax !== void 0) {
1554
+ this.store.resource.requestPaxMax = pendingRequestPaxMax;
1555
+ }
1556
+ const availabilityInfo = {
1557
+ mode: resourceState.mode,
1558
+ order_id: resourceState.order_id,
1559
+ relation_id: resourceState.relationId,
1560
+ table_form_id: resourceState.tableFormId,
1561
+ deskmate_valid: resourceState.deskmate_valid,
1562
+ table_form_record: resourceState.table_form_record,
1563
+ policy: (_c = occupyDetail == null ? void 0 : occupyDetail.form_record) == null ? void 0 : _c.policy,
1564
+ partyroom_booking: (_d = occupyDetail == null ? void 0 : occupyDetail.form_record) == null ? void 0 : _d.partyroom_booking,
1565
+ ...this.store.resource.requestEntryPax !== void 0 ? { requestEntryPax: this.store.resource.requestEntryPax } : {},
1566
+ ...this.store.resource.requestPaxMin !== void 0 ? { requestPaxMin: this.store.resource.requestPaxMin } : {},
1567
+ ...this.store.resource.requestPaxMax !== void 0 ? { requestPaxMax: this.store.resource.requestPaxMax } : {}
1568
+ };
1569
+ tempOrder.relation_id = resourceId || ((_e = this.otherParams) == null ? void 0 : _e.relation_id);
1570
+ tempOrder.table_form_id = resourceState.tableFormId;
1571
+ tempOrder.resource_id = resourceId;
1087
1572
  (_f = this.store.order) == null ? void 0 : _f.persistTempOrder();
1088
1573
  if (availabilityInfo.mode === "idle") {
1089
1574
  await this.addNewOrder();
@@ -1117,6 +1602,11 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
1117
1602
  submissionIndex: hasOrderId ? 1 : 0,
1118
1603
  historicalItems
1119
1604
  });
1605
+ if (outsideOperatingHours && closedBehaviorValue === "show_menu_disabled") {
1606
+ availabilityInfo.mode = "submit_disabled";
1607
+ availabilityInfo.errorTips = closedMessage;
1608
+ availabilityInfo.closed_behavior = closedBehaviorValue;
1609
+ }
1120
1610
  this.logMethodSuccess("checkResourceAvailable", {
1121
1611
  resourceId,
1122
1612
  mode: availabilityInfo.mode,
@@ -1126,14 +1616,13 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
1126
1616
  persistedRelationId: tempOrder.relation_id,
1127
1617
  persistedResourceId: tempOrder.resource_id,
1128
1618
  deskmateValid: availabilityInfo.deskmate_valid,
1129
- orderCount: resourceState.orderCount,
1130
- tableMaxNumber: resourceState.tableMaxNumber,
1619
+ resourceSelectType: resourceState.resourceSelectType,
1131
1620
  isExclusive: resourceState.isExclusive,
1132
1621
  isFull: resourceState.isFull
1133
1622
  });
1134
1623
  return availabilityInfo;
1135
1624
  } catch (error) {
1136
- this.logMethodError("checkResourceAvailable", error, {
1625
+ this.logMethodError("checkResourceAvailable", error.message, {
1137
1626
  resourceId
1138
1627
  });
1139
1628
  throw error;
@@ -1176,7 +1665,7 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
1176
1665
  });
1177
1666
  return formattedRes;
1178
1667
  } catch (error) {
1179
- this.logMethodError("getProductList", error);
1668
+ this.logMethodError("getProductList", error.message);
1180
1669
  throw error;
1181
1670
  }
1182
1671
  }
@@ -1190,6 +1679,69 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
1190
1679
  this.otherParams = { ...this.otherParams, ...params };
1191
1680
  }
1192
1681
  }
1682
+ getUIStateBucketKey() {
1683
+ if (!this.cacheId)
1684
+ return null;
1685
+ return `${_ScanOrderImpl.UI_STATE_KEY_PREFIX}${this.cacheId}`;
1686
+ }
1687
+ readUIStateBucket() {
1688
+ var _a;
1689
+ const key = this.getUIStateBucketKey();
1690
+ if (!key || !((_a = this.window) == null ? void 0 : _a.localStorage))
1691
+ return {};
1692
+ try {
1693
+ const raw = this.window.localStorage.getItem(key) || "{}";
1694
+ const parsed = JSON.parse(raw);
1695
+ return parsed && typeof parsed === "object" ? parsed : {};
1696
+ } catch {
1697
+ return {};
1698
+ }
1699
+ }
1700
+ writeUIStateBucket(bucket) {
1701
+ var _a;
1702
+ const key = this.getUIStateBucketKey();
1703
+ if (!key || !((_a = this.window) == null ? void 0 : _a.localStorage))
1704
+ return;
1705
+ try {
1706
+ this.window.localStorage.setItem(key, JSON.stringify(bucket));
1707
+ } catch (error) {
1708
+ console.warn("[ScanOrder] writeUIStateBucket failed", error);
1709
+ }
1710
+ }
1711
+ setUIState(key, value) {
1712
+ if (!this.getUIStateBucketKey())
1713
+ return;
1714
+ const bucket = this.readUIStateBucket();
1715
+ bucket[key] = value;
1716
+ this.writeUIStateBucket(bucket);
1717
+ }
1718
+ getUIState(key) {
1719
+ if (!this.getUIStateBucketKey())
1720
+ return void 0;
1721
+ const bucket = this.readUIStateBucket();
1722
+ return bucket[key];
1723
+ }
1724
+ deleteUIState(key) {
1725
+ if (!this.getUIStateBucketKey())
1726
+ return;
1727
+ const bucket = this.readUIStateBucket();
1728
+ if (key in bucket) {
1729
+ delete bucket[key];
1730
+ this.writeUIStateBucket(bucket);
1731
+ }
1732
+ }
1733
+ // 整桶清空(用于扫新桌、提交成功、restoreOrder 等场景)
1734
+ clearUIState() {
1735
+ var _a;
1736
+ const key = this.getUIStateBucketKey();
1737
+ if (!key || !((_a = this.window) == null ? void 0 : _a.localStorage))
1738
+ return;
1739
+ try {
1740
+ this.window.localStorage.removeItem(key);
1741
+ } catch (error) {
1742
+ console.warn("[ScanOrder] clearUIState failed", error);
1743
+ }
1744
+ }
1193
1745
  async setEntryPaxNumber(number) {
1194
1746
  const pax = (0, import_utils2.normalizeSubmitCollectPaxValue)(number);
1195
1747
  this.store.entryPaxNumber = pax;
@@ -1209,7 +1761,51 @@ var ScanOrderImpl = class extends import_BaseModule.BaseModule {
1209
1761
  getEntryPaxNumber() {
1210
1762
  return this.store.entryPaxNumber;
1211
1763
  }
1764
+ // 读取取餐/送餐开关
1765
+ // 依赖 checkResourceAvailable 已缓存的 dineInConfig
1766
+ getFulfillmentModes() {
1767
+ var _a;
1768
+ this.logMethodStart("getFulfillmentModes");
1769
+ const dineInConfig = ((_a = this.otherParams) == null ? void 0 : _a.dineInConfig) || {};
1770
+ const result = {
1771
+ enablePickup: Boolean(dineInConfig["fulfillment.enable_pickup"]),
1772
+ enableTableService: Boolean(dineInConfig["fulfillment.enable_table_service"])
1773
+ };
1774
+ this.logMethodSuccess("getFulfillmentModes", result);
1775
+ return result;
1776
+ }
1777
+ // 判定后台是否开启客户自定义取餐标识
1778
+ // 依赖 checkResourceAvailable 已缓存的 dineInConfig
1779
+ checkManualPickupRef() {
1780
+ var _a;
1781
+ this.logMethodStart("checkManualPickupRef");
1782
+ const dineInConfig = ((_a = this.otherParams) == null ? void 0 : _a.dineInConfig) || {};
1783
+ const refMode = dineInConfig["fulfillment.fulfillment_ref_mode"];
1784
+ const manualInputType = dineInConfig["fulfillment.manual_input_type"];
1785
+ const enabled = refMode === "manual_input";
1786
+ const result = enabled ? { enabled: true, manualInputType } : { enabled: false };
1787
+ this.logMethodSuccess("checkManualPickupRef", {
1788
+ enabled,
1789
+ manualInputType: enabled ? manualInputType : void 0
1790
+ });
1791
+ return result;
1792
+ }
1212
1793
  };
1794
+ var ScanOrderImpl = _ScanOrderImpl;
1795
+ ScanOrderImpl.PISELL1_LOGIN_SUCCESS = "pisell1.login.success";
1796
+ // ─── UI 状态缓存(按 cacheId 分桶,localStorage) ───
1797
+ //
1798
+ // 用于物料层持久化 UI 层面的轻量状态(如当前步骤、gate 标记、登录回跳意图等)。
1799
+ // 桶键:pisell.scanOrder.uiState:<cacheId>;内部以 JSON object 形式存储多个字段。
1800
+ // 无 cacheId 时所有方法自动降级为 no-op,上层不用判空。
1801
+ //
1802
+ // 之所以用 localStorage 而不是 sessionStorage:
1803
+ // 1. 与 Order 模块 openCache 下的 tempOrder 同栈,购物车在 / UIState 在,行为对齐;
1804
+ // 2. pisell1.login 整页 OAuth 跳转在 H5 壳 / iOS Safari 场景下可能丢 sessionStorage,
1805
+ // 导致登录回跳后 gate 标记失效、pax 弹窗重开、落点错误;localStorage 稳定不受影响。
1806
+ // 3. 失效由上层显式控制:提交成功 clearStepCache / entryContext 不一致 clearUIState /
1807
+ // pendingStep 登录回调 deleteUIState,足够约束生命周期。
1808
+ ScanOrderImpl.UI_STATE_KEY_PREFIX = "pisell.scanOrder.uiState:";
1213
1809
  // Annotate the CommonJS export names for ESM import in node:
1214
1810
  0 && (module.exports = {
1215
1811
  ScanOrderImpl,