@pisell/pisellos 0.0.507 → 0.0.509

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.
@@ -235,3 +235,9 @@ export interface ScanOrderLoggerRuntimeConfig {
235
235
  }
236
236
  export interface ScanOrderAddLogParams extends ScanOrderLogInput {
237
237
  }
238
+ /** ScanOrder.scanCode 对外的轻量结果(不含 discountList,UI 在 resolve 后调 getDiscountList) */
239
+ export interface ScanOrderScanCodeResult {
240
+ isAvailable: boolean;
241
+ type?: 'server' | string;
242
+ unavailableReason?: 'time_limit' | string;
243
+ }
@@ -17,4 +17,6 @@ export var ScanOrderHooks = /*#__PURE__*/function (ScanOrderHooks) {
17
17
 
18
18
  /** `resource_capacity[i].capacity_list[j]` */
19
19
 
20
- /** `/order/resource/occupy-detail` 单条 `occupy_details[i]` */
20
+ /** `/order/resource/occupy-detail` 单条 `occupy_details[i]` */
21
+
22
+ /** ScanOrder.scanCode 对外的轻量结果(不含 discountList,UI 在 resolve 后调 getDiscountList) */
@@ -129,3 +129,8 @@ export declare function pickFirstCustomCapacityPaxBounds(products: ProductData[]
129
129
  min?: number;
130
130
  max?: number;
131
131
  } | undefined;
132
+ /**
133
+ * 第一个 `capacity.type === 'custom'` 的商品,取其 `custom[0].id`(提交 booking metadata.capacity 维度 id)。
134
+ * 无匹配时返回 `undefined`,调用方应回退为 `0`。
135
+ */
136
+ export declare function pickFirstCustomCapacityDimensionId(products: ProductData[]): string | number | undefined;
@@ -580,4 +580,36 @@ export function pickFirstCustomCapacityPaxBounds(products) {
580
580
  _iterator9.f();
581
581
  }
582
582
  return undefined;
583
+ }
584
+
585
+ /**
586
+ * 第一个 `capacity.type === 'custom'` 的商品,取其 `custom[0].id`(提交 booking metadata.capacity 维度 id)。
587
+ * 无匹配时返回 `undefined`,调用方应回退为 `0`。
588
+ */
589
+ export function pickFirstCustomCapacityDimensionId(products) {
590
+ var _iterator10 = _createForOfIteratorHelper(products),
591
+ _step10;
592
+ try {
593
+ for (_iterator10.s(); !(_step10 = _iterator10.n()).done;) {
594
+ var p = _step10.value;
595
+ var cap = p === null || p === void 0 ? void 0 : p.capacity;
596
+ if (!cap || cap.type !== 'custom') continue;
597
+ if (!Array.isArray(cap.custom) || cap.custom.length === 0) continue;
598
+ var row = cap.custom[0];
599
+ if (!row || _typeof(row) !== 'object') continue;
600
+ var id = row.id;
601
+ if (id === null || id === undefined || id === '') continue;
602
+ if (typeof id === 'number' && Number.isFinite(id)) return id;
603
+ if (typeof id === 'string') {
604
+ var trimmed = id.trim();
605
+ if (!trimmed) continue;
606
+ return trimmed;
607
+ }
608
+ }
609
+ } catch (err) {
610
+ _iterator10.e(err);
611
+ } finally {
612
+ _iterator10.f();
613
+ }
614
+ return undefined;
583
615
  }
@@ -86,7 +86,7 @@ export declare class OrderModule extends BaseModule implements Module, OrderModu
86
86
  enhancePayload?: SubmitPayloadEnhancer;
87
87
  }): Promise<T>;
88
88
  createOrder(params: CommitOrderParams['query']): {
89
- type: "appointment_booking" | "virtual";
89
+ type: "virtual" | "appointment_booking";
90
90
  platform: string;
91
91
  sales_channel: string;
92
92
  order_sales_channel: string;
@@ -477,7 +477,9 @@ var OrderModule = class extends import_BaseModule.BaseModule {
477
477
  });
478
478
  let result;
479
479
  if (tempOrder.order_id) {
480
- const products = (0, import_utils.formatV1Product)(payload.products);
480
+ let products = (0, import_utils.formatV1Product)(
481
+ (0, import_utils.filterProductsForScanOrderMore)(payload.products)
482
+ );
481
483
  const moreResult = await this.scanOrderMore({
482
484
  query: {
483
485
  order_id: tempOrder.order_id,
@@ -72,6 +72,8 @@ export declare function buildSubmitPayload(params: {
72
72
  type?: string;
73
73
  enhance?: SubmitPayloadEnhancer;
74
74
  }): ScanOrderSubmitPayload;
75
+ /** 加单(scanOrderMore)不应提交 booking 关联的虚拟规则商品行 */
76
+ export declare function filterProductsForScanOrderMore(products: ScanOrderSubmitProduct[]): ScanOrderSubmitProduct[];
75
77
  export declare function formatV1Product(products: ScanOrderSubmitProduct[]): {
76
78
  bundle: any[];
77
79
  key: number | null;
@@ -34,6 +34,7 @@ __export(utils_exports, {
34
34
  createDefaultTempOrder: () => createDefaultTempOrder,
35
35
  createEmptySummary: () => import_utils2.createEmptySummary,
36
36
  createUuidV4: () => createUuidV4,
37
+ filterProductsForScanOrderMore: () => filterProductsForScanOrderMore,
37
38
  formatDateTime: () => formatDateTime,
38
39
  formatV1Product: () => formatV1Product,
39
40
  generateDuration: () => generateDuration,
@@ -366,6 +367,12 @@ function buildSubmitPayload(params) {
366
367
  };
367
368
  return enhance ? enhance(payload, { tempOrder, bookingUuid, now }) : payload;
368
369
  }
370
+ function filterProductsForScanOrderMore(products) {
371
+ return (products || []).filter((p) => {
372
+ var _a;
373
+ return ((_a = p.metadata) == null ? void 0 : _a.is_rule) !== true;
374
+ });
375
+ }
369
376
  function formatV1Product(products) {
370
377
  return products.map((product) => {
371
378
  return {
@@ -388,6 +395,7 @@ function formatV1Product(products) {
388
395
  createDefaultTempOrder,
389
396
  createEmptySummary,
390
397
  createUuidV4,
398
+ filterProductsForScanOrderMore,
391
399
  formatDateTime,
392
400
  formatV1Product,
393
401
  generateDuration,
@@ -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 | 2 | 1 | 3 | 5 | 4 | 6;
314
+ weekNum: 0 | 1 | 2 | 3 | 4 | 5 | 6;
315
315
  }[]>;
316
316
  submitTimeSlot(timeSlots: TimeSliceItem): void;
317
317
  private getScheduleDataByIds;
@@ -131,7 +131,7 @@ export declare class BookingTicketImpl extends BaseModule implements Module {
131
131
  * 获取当前的客户搜索条件
132
132
  * @returns 当前搜索条件
133
133
  */
134
- getCurrentCustomerSearchParams(): Omit<import("../../modules").ShopGetCustomerListParams, "skip" | "num">;
134
+ getCurrentCustomerSearchParams(): Omit<import("../../modules").ShopGetCustomerListParams, "num" | "skip">;
135
135
  /**
136
136
  * 获取客户列表状态(包含滚动加载相关状态)
137
137
  * @returns 客户状态
@@ -1,6 +1,7 @@
1
1
  import { Module, ModuleOptions, PisellCore } from '../../types';
2
2
  import { BaseModule } from '../../modules/BaseModule';
3
- import { ScanOrderAddLogParams, ScanOrderAvailabilityInfo, ScanOrderOrderProduct, ScanOrderOrderProductIdentity } from './types';
3
+ import { ScanOrderAddLogParams, ScanOrderAvailabilityInfo, ScanOrderOrderProduct, ScanOrderOrderProductIdentity, ScanOrderScanCodeResult } from './types';
4
+ import type { Discount } from '../../modules/Discount/types';
4
5
  import { type CartItemSummary, type PaxInfo, type QuantityCheckResult, type QuantityLimitResult } from '../../model/strategy/adapter/itemRule';
5
6
  import type { StrategyConfig } from '../../model/strategy/type';
6
7
  export * from './types';
@@ -29,6 +30,10 @@ export declare class ScanOrderImpl extends BaseModule implements Module {
29
30
  private itemRuleRuntimeConfig;
30
31
  /** 最近一次 checkResourceAvailable 从预约规则 link 拉取的商品快照 */
31
32
  private enabledReservationRuleProducts;
33
+ private loginEffectDisposers;
34
+ private customerLoginRefreshInFlight;
35
+ private customerLoginRefreshIdInFlight;
36
+ private static readonly PISELL1_LOGIN_SUCCESS;
32
37
  private getScanOrderLoggerContext;
33
38
  private serializeError;
34
39
  private addScanOrderLog;
@@ -36,6 +41,12 @@ export declare class ScanOrderImpl extends BaseModule implements Module {
36
41
  private logMethodSuccess;
37
42
  private logMethodError;
38
43
  addLog(params: ScanOrderAddLogParams): Promise<void>;
44
+ private normalizeCustomerId;
45
+ private resolveCustomerIdFromLoginPayload;
46
+ private clearLoginEffectListeners;
47
+ private registerLoginEffect;
48
+ private registerCustomerLoginListeners;
49
+ private refreshOrderMarketingAfterLogin;
39
50
  constructor(name?: string, version?: string);
40
51
  initialize(core: PisellCore, options?: ModuleOptions): Promise<void>;
41
52
  destroy(): Promise<void>;
@@ -62,6 +73,15 @@ export declare class ScanOrderImpl extends BaseModule implements Module {
62
73
  restoreOrder(): Promise<import("./types").ScanOrderTempOrder>;
63
74
  getOrderProducts(): ScanOrderOrderProduct[];
64
75
  getSummary(): Promise<import("./types").ScanOrderSummary>;
76
+ getDiscountList(): Discount[];
77
+ scanCode(code: string, customerId?: number): Promise<ScanOrderScanCodeResult>;
78
+ setDiscountSelected(params: {
79
+ discountId: number;
80
+ isSelected: boolean;
81
+ }): Promise<void>;
82
+ onCustomerLogin(params: {
83
+ customerId: number;
84
+ }): Promise<void>;
65
85
  private buildSubmitPayloadEnhancer;
66
86
  submitScanOrder<T = any>(): Promise<T>;
67
87
  addProductToOrder(product: Partial<ScanOrderOrderProduct> & ScanOrderOrderProductIdentity): Promise<ScanOrderOrderProduct[]>;
@@ -35,8 +35,12 @@ __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");
41
45
  var import_Schedule = require("../../modules/Schedule");
42
46
  var import_getDateIsInSchedule = require("../../modules/Schedule/getDateIsInSchedule");
@@ -74,6 +78,9 @@ var _ScanOrderImpl = class extends import_BaseModule.BaseModule {
74
78
  this.itemRuleRuntimeConfig = {};
75
79
  /** 最近一次 checkResourceAvailable 从预约规则 link 拉取的商品快照 */
76
80
  this.enabledReservationRuleProducts = [];
81
+ this.loginEffectDisposers = [];
82
+ this.customerLoginRefreshInFlight = null;
83
+ this.customerLoginRefreshIdInFlight = null;
77
84
  }
78
85
  getScanOrderLoggerContext() {
79
86
  return {
@@ -157,6 +164,102 @@ var _ScanOrderImpl = class extends import_BaseModule.BaseModule {
157
164
  }
158
165
  });
159
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
+ }
160
263
  async initialize(core, options = {}) {
161
264
  var _a, _b, _c, _d, _e, _f;
162
265
  this.logMethodStart("initialize");
@@ -195,7 +298,7 @@ var _ScanOrderImpl = class extends import_BaseModule.BaseModule {
195
298
  ];
196
299
  moduleArr.forEach((step) => {
197
300
  var _a2, _b2, _c2, _d2, _e2, _f2;
198
- const targetModule = (0, import_types2.createModule)(step, this.name);
301
+ const targetModule = (0, import_types4.createModule)(step, this.name);
199
302
  if (targetModule) {
200
303
  this.store[step] = targetModule;
201
304
  const initialState = step === "salesSummary" ? {
@@ -236,6 +339,7 @@ var _ScanOrderImpl = class extends import_BaseModule.BaseModule {
236
339
  fatherModule: this.name
237
340
  }
238
341
  });
342
+ this.registerCustomerLoginListeners();
239
343
  console.log("[ScanOrder] 初始化开始");
240
344
  try {
241
345
  await ((_e = this.store.order) == null ? void 0 : _e.recalculateSummary({ createIfMissing: false }));
@@ -269,6 +373,7 @@ var _ScanOrderImpl = class extends import_BaseModule.BaseModule {
269
373
  }
270
374
  async destroy() {
271
375
  this.logMethodStart("destroy");
376
+ this.clearLoginEffectListeners();
272
377
  await this.core.effects.emit(import_types.ScanOrderHooks.onDestroy, {});
273
378
  console.log("[ScanOrder] 已销毁");
274
379
  this.logMethodSuccess("destroy");
@@ -494,6 +599,127 @@ var _ScanOrderImpl = class extends import_BaseModule.BaseModule {
494
599
  throw error;
495
600
  }
496
601
  }
602
+ getDiscountList() {
603
+ if (!this.store.order)
604
+ return [];
605
+ return this.store.order.getDiscountList();
606
+ }
607
+ async scanCode(code, customerId) {
608
+ this.logMethodStart("scanCode", { code });
609
+ try {
610
+ if (!this.store.order)
611
+ throw new Error("order 模块未初始化");
612
+ const raw = await this.store.order.scanCode(code, customerId);
613
+ if (raw.isAvailable) {
614
+ await this.store.order.recalculateSummary({ createIfMissing: true });
615
+ this.store.order.persistTempOrder();
616
+ }
617
+ this.logMethodSuccess("scanCode", { isAvailable: raw.isAvailable });
618
+ return {
619
+ isAvailable: raw.isAvailable,
620
+ type: raw.type,
621
+ unavailableReason: raw.unavailableReason
622
+ };
623
+ } catch (error) {
624
+ this.logMethodError("scanCode", error);
625
+ throw error;
626
+ }
627
+ }
628
+ async setDiscountSelected(params) {
629
+ var _a, _b, _c;
630
+ this.logMethodStart("setDiscountSelected", params);
631
+ try {
632
+ if (!this.store.order)
633
+ throw new Error("order 模块未初始化");
634
+ const list = this.store.order.getDiscountList();
635
+ const updated = list.map(
636
+ (d) => d.id === params.discountId ? {
637
+ ...d,
638
+ isSelected: params.isSelected,
639
+ isManualSelect: !params.isSelected
640
+ } : d
641
+ );
642
+ const tempOrder = this.store.order.ensureTempOrder();
643
+ const orderStore = this.store.order.store || {};
644
+ const discountModule = orderStore.discount;
645
+ const rulesModule = orderStore.rules;
646
+ const holders = ((_a = tempOrder.holder) == null ? void 0 : _a.form_record_id) ? [{ form_record_id: tempOrder.holder.form_record_id }] : [];
647
+ let nextDiscountList = updated;
648
+ await (discountModule == null ? void 0 : discountModule.setDiscountList(updated));
649
+ if (rulesModule) {
650
+ const result = rulesModule.calcDiscount(
651
+ {
652
+ productList: tempOrder.products,
653
+ discountList: updated,
654
+ holders,
655
+ isFormSubject: !!((_b = tempOrder.holder) == null ? void 0 : _b.type) && tempOrder.holder.type === "form"
656
+ },
657
+ {
658
+ discountId: params.discountId,
659
+ isSelected: params.isSelected
660
+ }
661
+ ) || { productList: tempOrder.products, discountList: updated };
662
+ if (result == null ? void 0 : result.productList) {
663
+ tempOrder.products = result.productList;
664
+ }
665
+ if (result == null ? void 0 : result.discountList) {
666
+ nextDiscountList = result.discountList;
667
+ if (!params.isSelected) {
668
+ const beforeSelectedIds = new Set(
669
+ updated.filter((d) => d.isSelected).map((d) => d.id)
670
+ );
671
+ for (const d of nextDiscountList) {
672
+ if (d.isSelected && !beforeSelectedIds.has(d.id)) {
673
+ d.isSelected = false;
674
+ }
675
+ }
676
+ }
677
+ }
678
+ }
679
+ const selectedResourceIds = new Set(
680
+ nextDiscountList.filter((d) => d.isSelected).map((d) => d.id)
681
+ );
682
+ for (const product of tempOrder.products) {
683
+ if ((_c = product._origin) == null ? void 0 : _c.isManualDiscount)
684
+ continue;
685
+ product.discount_list = (product.discount_list || []).filter((pd) => {
686
+ var _a2;
687
+ const rid = ((_a2 = pd.discount) == null ? void 0 : _a2.resource_id) ?? pd.id;
688
+ return rid != null && selectedResourceIds.has(rid);
689
+ });
690
+ const totalPerUnitDiscount = (product.discount_list || []).reduce(
691
+ (sum, pd) => sum + (pd.amount || 0),
692
+ 0
693
+ );
694
+ const newSellingPrice = new import_decimal.default(product.original_price || 0).minus(totalPerUnitDiscount).toDecimalPlaces(2).toString();
695
+ product.selling_price = newSellingPrice;
696
+ if (product.metadata) {
697
+ product.metadata.main_product_selling_price = newSellingPrice;
698
+ }
699
+ }
700
+ import_Order.OrderModule.populateSavedAmounts(tempOrder.products, nextDiscountList);
701
+ await (discountModule == null ? void 0 : discountModule.setDiscountList(nextDiscountList));
702
+ tempOrder.discount_list = (nextDiscountList || []).filter((d) => d.isSelected);
703
+ await this.store.order.recalculateSummary({ createIfMissing: true });
704
+ this.store.order.persistTempOrder();
705
+ this.logMethodSuccess("setDiscountSelected", params);
706
+ } catch (error) {
707
+ this.logMethodError("setDiscountSelected", error);
708
+ throw error;
709
+ }
710
+ }
711
+ async onCustomerLogin(params) {
712
+ this.logMethodStart("onCustomerLogin", { customerId: params.customerId });
713
+ try {
714
+ await this.refreshOrderMarketingAfterLogin({
715
+ customerId: params.customerId
716
+ });
717
+ this.logMethodSuccess("onCustomerLogin", { customerId: params.customerId });
718
+ } catch (error) {
719
+ this.logMethodError("onCustomerLogin", error);
720
+ throw error;
721
+ }
722
+ }
497
723
  // ScanOrder 提交 payload enhancer:
498
724
  // - 给所有 booking 注入 appointment_status: 'started'(扫码点餐语义)
499
725
  // - 给所有 booking 的 metadata 注入 resource_select_type(来自预约规则商品的 resource.type)
@@ -515,7 +741,7 @@ var _ScanOrderImpl = class extends import_BaseModule.BaseModule {
515
741
  return 1;
516
742
  };
517
743
  return (payload, { bookingUuid, tempOrder }) => {
518
- var _a;
744
+ var _a, _b;
519
745
  const resourceId = tempOrder.resource_id ?? (resourceState == null ? void 0 : resourceState.relationId);
520
746
  const pickOriginal = (value) => {
521
747
  if (value && typeof value === "object" && !Array.isArray(value)) {
@@ -536,20 +762,31 @@ var _ScanOrderImpl = class extends import_BaseModule.BaseModule {
536
762
  metadata: {}
537
763
  } : void 0;
538
764
  const ruleProductUid = ruleProduct ? (0, import_utils2.createUuidV4)() : void 0;
539
- const bookingCapacityValue = resolveResourceCapacity();
765
+ const rawCollectPax = (_b = tempOrder.metadata) == null ? void 0 : _b.collect_pax;
766
+ const hasCollectPaxForCapacity = rawCollectPax !== null && rawCollectPax !== void 0 && rawCollectPax !== "";
767
+ const bookingCapacityValue = hasCollectPaxForCapacity ? (0, import_utils2.normalizeSubmitCollectPaxValue)(rawCollectPax) : resolveResourceCapacity();
768
+ const bookingCapacityDimensionId = (0, import_utils.pickFirstCustomCapacityDimensionId)(this.enabledReservationRuleProducts) ?? 0;
540
769
  const nextBookings = (payload.bookings || []).map((booking, idx) => ({
541
770
  ...booking,
542
771
  appointment_status: "started",
543
772
  metadata: {
544
773
  ...booking.metadata || {},
545
774
  ...resourceSelectType ? { resource_select_type: resourceSelectType } : {},
546
- ...resourceSelectType ? { capacity: [{ id: 0, value: bookingCapacityValue, name: "" }] } : {}
775
+ ...resourceSelectType ? {
776
+ capacity: [
777
+ {
778
+ id: bookingCapacityDimensionId,
779
+ value: bookingCapacityValue,
780
+ name: ""
781
+ }
782
+ ]
783
+ } : {}
547
784
  },
548
785
  ...idx === 0 && resourceEntry ? { resources: [resourceEntry] } : {},
549
786
  ...idx === 0 && ruleProductUid ? { product_uid: ruleProductUid } : {}
550
787
  }));
551
788
  const nextProducts = [...payload.products || []];
552
- if (ruleProduct && ruleProductUid) {
789
+ if (ruleProduct && ruleProductUid && !tempOrder.order_id) {
553
790
  const sellingPrice = String(ruleProduct.price ?? "0.00");
554
791
  const originalPrice = String(
555
792
  ruleProduct.original_price ?? ruleProduct.price ?? "0.00"
@@ -1035,7 +1272,7 @@ var _ScanOrderImpl = class extends import_BaseModule.BaseModule {
1035
1272
  const relationId = (0, import_utils.toPositiveString)(detail == null ? void 0 : detail.form_record_id);
1036
1273
  const tableFormId = (0, import_utils.toPositiveString)(detail == null ? void 0 : detail.form_id);
1037
1274
  const formRecord = (detail == null ? void 0 : detail.form_record) ?? null;
1038
- const allowSnack = ((_b = (_a = this.otherParams) == null ? void 0 : _a.dineInConfig) == null ? void 0 : _b["workflow.allow_add_items"]) || false;
1275
+ const allowSnack = ((_b = (_a = this.otherParams) == null ? void 0 : _a.dineInConfig) == null ? void 0 : _b["sale.allow_add_items"]) || false;
1039
1276
  const deskmateValid = false;
1040
1277
  const isExclusive = resourceSelectType === "single";
1041
1278
  const isFull = (0, import_utils.computeResourceIsFull)({
@@ -1394,10 +1631,10 @@ var _ScanOrderImpl = class extends import_BaseModule.BaseModule {
1394
1631
  readUIStateBucket() {
1395
1632
  var _a;
1396
1633
  const key = this.getUIStateBucketKey();
1397
- if (!key || !((_a = this.window) == null ? void 0 : _a.sessionStorage))
1634
+ if (!key || !((_a = this.window) == null ? void 0 : _a.localStorage))
1398
1635
  return {};
1399
1636
  try {
1400
- const raw = this.window.sessionStorage.getItem(key) || "{}";
1637
+ const raw = this.window.localStorage.getItem(key) || "{}";
1401
1638
  const parsed = JSON.parse(raw);
1402
1639
  return parsed && typeof parsed === "object" ? parsed : {};
1403
1640
  } catch {
@@ -1407,10 +1644,10 @@ var _ScanOrderImpl = class extends import_BaseModule.BaseModule {
1407
1644
  writeUIStateBucket(bucket) {
1408
1645
  var _a;
1409
1646
  const key = this.getUIStateBucketKey();
1410
- if (!key || !((_a = this.window) == null ? void 0 : _a.sessionStorage))
1647
+ if (!key || !((_a = this.window) == null ? void 0 : _a.localStorage))
1411
1648
  return;
1412
1649
  try {
1413
- this.window.sessionStorage.setItem(key, JSON.stringify(bucket));
1650
+ this.window.localStorage.setItem(key, JSON.stringify(bucket));
1414
1651
  } catch (error) {
1415
1652
  console.warn("[ScanOrder] writeUIStateBucket failed", error);
1416
1653
  }
@@ -1441,10 +1678,10 @@ var _ScanOrderImpl = class extends import_BaseModule.BaseModule {
1441
1678
  clearUIState() {
1442
1679
  var _a;
1443
1680
  const key = this.getUIStateBucketKey();
1444
- if (!key || !((_a = this.window) == null ? void 0 : _a.sessionStorage))
1681
+ if (!key || !((_a = this.window) == null ? void 0 : _a.localStorage))
1445
1682
  return;
1446
1683
  try {
1447
- this.window.sessionStorage.removeItem(key);
1684
+ this.window.localStorage.removeItem(key);
1448
1685
  } catch (error) {
1449
1686
  console.warn("[ScanOrder] clearUIState failed", error);
1450
1687
  }
@@ -1499,11 +1736,19 @@ var _ScanOrderImpl = class extends import_BaseModule.BaseModule {
1499
1736
  }
1500
1737
  };
1501
1738
  var ScanOrderImpl = _ScanOrderImpl;
1502
- // ─── UI 状态缓存(按 cacheId 分桶,sessionStorage) ───
1739
+ ScanOrderImpl.PISELL1_LOGIN_SUCCESS = "pisell1.login.success";
1740
+ // ─── UI 状态缓存(按 cacheId 分桶,localStorage) ───
1503
1741
  //
1504
1742
  // 用于物料层持久化 UI 层面的轻量状态(如当前步骤、gate 标记、登录回跳意图等)。
1505
1743
  // 桶键:pisell.scanOrder.uiState:<cacheId>;内部以 JSON object 形式存储多个字段。
1506
1744
  // 无 cacheId 时所有方法自动降级为 no-op,上层不用判空。
1745
+ //
1746
+ // 之所以用 localStorage 而不是 sessionStorage:
1747
+ // 1. 与 Order 模块 openCache 下的 tempOrder 同栈,购物车在 / UIState 在,行为对齐;
1748
+ // 2. pisell1.login 整页 OAuth 跳转在 H5 壳 / iOS Safari 场景下可能丢 sessionStorage,
1749
+ // 导致登录回跳后 gate 标记失效、pax 弹窗重开、落点错误;localStorage 稳定不受影响。
1750
+ // 3. 失效由上层显式控制:提交成功 clearStepCache / entryContext 不一致 clearUIState /
1751
+ // pendingStep 登录回调 deleteUIState,足够约束生命周期。
1507
1752
  ScanOrderImpl.UI_STATE_KEY_PREFIX = "pisell.scanOrder.uiState:";
1508
1753
  // Annotate the CommonJS export names for ESM import in node:
1509
1754
  0 && (module.exports = {
@@ -235,3 +235,9 @@ export interface ScanOrderLoggerRuntimeConfig {
235
235
  }
236
236
  export interface ScanOrderAddLogParams extends ScanOrderLogInput {
237
237
  }
238
+ /** ScanOrder.scanCode 对外的轻量结果(不含 discountList,UI 在 resolve 后调 getDiscountList) */
239
+ export interface ScanOrderScanCodeResult {
240
+ isAvailable: boolean;
241
+ type?: 'server' | string;
242
+ unavailableReason?: 'time_limit' | string;
243
+ }
@@ -129,3 +129,8 @@ export declare function pickFirstCustomCapacityPaxBounds(products: ProductData[]
129
129
  min?: number;
130
130
  max?: number;
131
131
  } | undefined;
132
+ /**
133
+ * 第一个 `capacity.type === 'custom'` 的商品,取其 `custom[0].id`(提交 booking metadata.capacity 维度 id)。
134
+ * 无匹配时返回 `undefined`,调用方应回退为 `0`。
135
+ */
136
+ export declare function pickFirstCustomCapacityDimensionId(products: ProductData[]): string | number | undefined;
@@ -47,6 +47,7 @@ __export(utils_exports, {
47
47
  normalizeEnabledItemRuleIds: () => normalizeEnabledItemRuleIds,
48
48
  normalizeItemRuleStrategies: () => normalizeItemRuleStrategies,
49
49
  normalizeOrderProduct: () => normalizeOrderProduct,
50
+ pickFirstCustomCapacityDimensionId: () => pickFirstCustomCapacityDimensionId,
50
51
  pickFirstCustomCapacityPaxBounds: () => pickFirstCustomCapacityPaxBounds,
51
52
  pickFirstDurationMinutesFromProducts: () => pickFirstDurationMinutesFromProducts,
52
53
  resolveSkuMatchedQuantityLimits: () => resolveSkuMatchedQuantityLimits,
@@ -474,6 +475,30 @@ function pickFirstCustomCapacityPaxBounds(products) {
474
475
  }
475
476
  return void 0;
476
477
  }
478
+ function pickFirstCustomCapacityDimensionId(products) {
479
+ for (const p of products) {
480
+ const cap = p == null ? void 0 : p.capacity;
481
+ if (!cap || cap.type !== "custom")
482
+ continue;
483
+ if (!Array.isArray(cap.custom) || cap.custom.length === 0)
484
+ continue;
485
+ const row = cap.custom[0];
486
+ if (!row || typeof row !== "object")
487
+ continue;
488
+ const id = row.id;
489
+ if (id === null || id === void 0 || id === "")
490
+ continue;
491
+ if (typeof id === "number" && Number.isFinite(id))
492
+ return id;
493
+ if (typeof id === "string") {
494
+ const trimmed = id.trim();
495
+ if (!trimmed)
496
+ continue;
497
+ return trimmed;
498
+ }
499
+ }
500
+ return void 0;
501
+ }
477
502
  // Annotate the CommonJS export names for ESM import in node:
478
503
  0 && (module.exports = {
479
504
  aggregateItemRuleLimit,
@@ -494,6 +519,7 @@ function pickFirstCustomCapacityPaxBounds(products) {
494
519
  normalizeEnabledItemRuleIds,
495
520
  normalizeItemRuleStrategies,
496
521
  normalizeOrderProduct,
522
+ pickFirstCustomCapacityDimensionId,
497
523
  pickFirstCustomCapacityPaxBounds,
498
524
  pickFirstDurationMinutesFromProducts,
499
525
  resolveSkuMatchedQuantityLimits,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "private": false,
3
3
  "name": "@pisell/pisellos",
4
- "version": "0.0.507",
4
+ "version": "0.0.509",
5
5
  "description": "一个可扩展的前端模块化SDK框架,支持插件系统",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",