@pisell/pisellos 2.3.94 → 2.3.96

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 (88) hide show
  1. package/dist/model/strategy/adapter/dataVariant/evaluator.js +13 -2
  2. package/dist/model/strategy/adapter/itemRule/adapter.d.ts +4 -1
  3. package/dist/model/strategy/adapter/itemRule/adapter.js +135 -35
  4. package/dist/model/strategy/adapter/itemRule/evaluator.d.ts +8 -1
  5. package/dist/model/strategy/adapter/itemRule/evaluator.js +25 -1
  6. package/dist/model/strategy/adapter/itemRule/type.d.ts +44 -0
  7. package/dist/model/strategy/adapter/itemRule/type.js +19 -1
  8. package/dist/model/strategy/adapter/promotion/index.js +9 -0
  9. package/dist/modules/Order/index.d.ts +20 -3
  10. package/dist/modules/Order/index.js +931 -508
  11. package/dist/modules/Order/salesNotes.d.ts +31 -0
  12. package/dist/modules/Order/salesNotes.js +492 -0
  13. package/dist/modules/Order/types.d.ts +91 -5
  14. package/dist/modules/Order/types.js +5 -0
  15. package/dist/modules/Order/utils.d.ts +12 -0
  16. package/dist/modules/Order/utils.js +75 -26
  17. package/dist/modules/ProductList/index.d.ts +2 -1
  18. package/dist/modules/ProductList/index.js +100 -20
  19. package/dist/modules/ProductList/types.d.ts +10 -0
  20. package/dist/modules/SalesSummary/index.js +6 -4
  21. package/dist/server/index.d.ts +6 -0
  22. package/dist/server/index.js +57 -11
  23. package/dist/server/modules/products/index.d.ts +2 -2
  24. package/dist/server/modules/products/index.js +10 -9
  25. package/dist/server/utils/product.d.ts +1 -1
  26. package/dist/server/utils/product.js +186 -22
  27. package/dist/server/utils/small-ticket.js +23 -2
  28. package/dist/solution/BaseSales/index.d.ts +9 -1
  29. package/dist/solution/BaseSales/index.js +894 -706
  30. package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +3 -2
  31. package/dist/solution/BookingByStep/index.d.ts +2 -2
  32. package/dist/solution/BookingTicket/utils/orderCustomer.js +3 -2
  33. package/dist/solution/RegisterAndLogin/config.d.ts +9 -3
  34. package/dist/solution/RegisterAndLogin/config.js +95 -40
  35. package/dist/solution/RegisterAndLogin/index.js +4 -1
  36. package/dist/solution/ScanOrder/index.d.ts +1 -0
  37. package/dist/solution/ScanOrder/index.js +31 -27
  38. package/dist/solution/ScanOrder/utils.d.ts +1 -0
  39. package/dist/solution/ScanOrder/utils.js +2 -0
  40. package/dist/solution/UnifiedBookingSales/index.d.ts +15 -2
  41. package/dist/solution/UnifiedBookingSales/index.js +751 -483
  42. package/dist/solution/UnifiedBookingSales/types.d.ts +14 -1
  43. package/dist/solution/VenueBooking/index.d.ts +1 -0
  44. package/dist/solution/VenueBooking/index.js +8 -4
  45. package/lib/model/strategy/adapter/dataVariant/evaluator.js +12 -2
  46. package/lib/model/strategy/adapter/itemRule/adapter.d.ts +4 -1
  47. package/lib/model/strategy/adapter/itemRule/adapter.js +73 -4
  48. package/lib/model/strategy/adapter/itemRule/evaluator.d.ts +8 -1
  49. package/lib/model/strategy/adapter/itemRule/evaluator.js +23 -1
  50. package/lib/model/strategy/adapter/itemRule/type.d.ts +44 -0
  51. package/lib/model/strategy/adapter/itemRule/type.js +19 -1
  52. package/lib/modules/Order/index.d.ts +20 -3
  53. package/lib/modules/Order/index.js +358 -42
  54. package/lib/modules/Order/salesNotes.d.ts +31 -0
  55. package/lib/modules/Order/salesNotes.js +382 -0
  56. package/lib/modules/Order/types.d.ts +91 -5
  57. package/lib/modules/Order/types.js +4 -0
  58. package/lib/modules/Order/utils.d.ts +12 -0
  59. package/lib/modules/Order/utils.js +68 -16
  60. package/lib/modules/ProductList/index.d.ts +2 -1
  61. package/lib/modules/ProductList/index.js +47 -2
  62. package/lib/modules/ProductList/types.d.ts +10 -0
  63. package/lib/modules/SalesSummary/index.js +3 -1
  64. package/lib/server/index.d.ts +6 -0
  65. package/lib/server/index.js +47 -9
  66. package/lib/server/modules/products/index.d.ts +2 -2
  67. package/lib/server/modules/products/index.js +5 -4
  68. package/lib/server/utils/product.d.ts +1 -1
  69. package/lib/server/utils/product.js +163 -3
  70. package/lib/server/utils/small-ticket.js +22 -1
  71. package/lib/solution/BaseSales/index.d.ts +9 -1
  72. package/lib/solution/BaseSales/index.js +105 -1
  73. package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +3 -2
  74. package/lib/solution/BookingByStep/index.d.ts +2 -2
  75. package/lib/solution/BookingTicket/utils/orderCustomer.js +2 -1
  76. package/lib/solution/RegisterAndLogin/config.d.ts +9 -3
  77. package/lib/solution/RegisterAndLogin/config.js +49 -8
  78. package/lib/solution/RegisterAndLogin/index.js +4 -1
  79. package/lib/solution/ScanOrder/index.d.ts +1 -0
  80. package/lib/solution/ScanOrder/index.js +5 -1
  81. package/lib/solution/ScanOrder/utils.d.ts +1 -0
  82. package/lib/solution/ScanOrder/utils.js +1 -0
  83. package/lib/solution/UnifiedBookingSales/index.d.ts +15 -2
  84. package/lib/solution/UnifiedBookingSales/index.js +125 -1
  85. package/lib/solution/UnifiedBookingSales/types.d.ts +14 -1
  86. package/lib/solution/VenueBooking/index.d.ts +1 -0
  87. package/lib/solution/VenueBooking/index.js +5 -1
  88. package/package.json +1 -1
@@ -32,6 +32,7 @@ const LABELS = {
32
32
  orderDiscount: 'Order discount',
33
33
  manualDiscount: 'Manual discount',
34
34
  payProcessingFee: 'Pay processing fee',
35
+ surchargeIncluded: 'Surcharge(Included)',
35
36
  rounding: 'Rounding',
36
37
  gross: 'Gross',
37
38
  tare: 'Tare'
@@ -58,6 +59,7 @@ const LABELS = {
58
59
  orderDiscount: '整单折扣',
59
60
  manualDiscount: '手动折扣',
60
61
  payProcessingFee: '支付手续费',
62
+ surchargeIncluded: '手续费(已含)',
61
63
  rounding: '抹零',
62
64
  gross: '重量',
63
65
  tare: '去皮'
@@ -84,6 +86,7 @@ const LABELS = {
84
86
  orderDiscount: '整單折扣',
85
87
  manualDiscount: '手動折扣',
86
88
  payProcessingFee: '支付手續費',
89
+ surchargeIncluded: '手續費(已含)',
87
90
  rounding: '抹零',
88
91
  gross: '重量',
89
92
  tare: '去皮'
@@ -730,10 +733,17 @@ function buildResources(order, locale) {
730
733
  function buildPaymentData(params) {
731
734
  const payments = normalizeArray(params.order.payments);
732
735
  return payments.filter(payment => isPrintablePayment(payment)).map(payment => {
736
+ const serviceFee = getPaymentServiceFee(payment);
733
737
  const items = [{
734
738
  item: paymentMethodName(payment, params.locale),
735
- value: formatMoney(payment.amount ?? payment.origin_amount, params.currencySymbol)
739
+ value: formatMoney(resolveReceiptPaymentAmount(payment, serviceFee), params.currencySymbol)
736
740
  }];
741
+ if (serviceFee.gt(0)) {
742
+ items.push({
743
+ item: label(params.locale, 'surchargeIncluded'),
744
+ value: formatMoney(serviceFee, params.currencySymbol)
745
+ });
746
+ }
737
747
  const fundRecord = getFundRecord(payment);
738
748
  if (fundRecord) {
739
749
  items.push(...buildFundRecordPaymentItems({
@@ -758,6 +768,17 @@ function buildPaymentData(params) {
758
768
  return items;
759
769
  });
760
770
  }
771
+ function getPaymentServiceFee(payment) {
772
+ return new _decimal.default(toMoneyNumber(payment.service_fee));
773
+ }
774
+ function resolveReceiptPaymentAmount(payment, serviceFee) {
775
+ const amount = new _decimal.default(toMoneyNumber(payment.amount ?? payment.origin_amount));
776
+ const hasAmount = payment.amount !== undefined && payment.amount !== null && payment.amount !== '';
777
+ const hasOriginAmount = payment.origin_amount !== undefined && payment.origin_amount !== null && payment.origin_amount !== '';
778
+ if (!hasAmount || !hasOriginAmount || serviceFee.lte(0)) return amount;
779
+ const originAmount = new _decimal.default(toMoneyNumber(payment.origin_amount));
780
+ return amount.eq(originAmount) ? amount.plus(serviceFee) : amount;
781
+ }
761
782
  function buildFundRecordPaymentItems(params) {
762
783
  const {
763
784
  payment,
@@ -2,7 +2,7 @@ import { Module, ModuleOptions, PisellCore } from '../../types';
2
2
  import { BaseModule } from '../../modules/BaseModule';
3
3
  import { BaseSalesOrderProduct, BaseSalesOrderProductIdentity, BaseSalesPaymentStatus, BaseSalesUpdateOrderProductQuantityParams, BaseSalesCalculateProductBookingPriceParams, BaseSalesPriceRecalculationContextChange, BaseSalesProductBookingPriceResult, BaseSalesQuotationCustomerScopeInfo, BaseSalesScanCodeResult, BaseSalesCashPaymentConfig, BaseSalesPayCashParams, BaseSalesPayCashResult, BaseSalesCommitPaymentMethodParams, BaseSalesCommitPaymentMethodResult, BaseSalesConfirmWalletPaymentResult } from './types';
4
4
  import { OrderModule } from '../../modules/Order';
5
- import type { AddProductBookingInput, AddProductToOrderOptions, LoadSalesDetailParams, OrderPaymentData, OrderPaymentUpdateOptions, OrderPaymentUpdateResult, OrderPaymentSource, SendCustomerPayLinkParams, SyncPaymentsToOrderParams, SyncPaymentsToOrderResult, UpdateTempOrderCustomerInput, UpdateOrderBookingParams, UpdateOrderProductParams, OrderTempOrder, OrderSummary, OrderPromotionEvaluator, OrderGiftSelectResolver, OrderUnfulfilledPromotion, OrderLastGiftActions, RemoveProductsFromOrderOptions, ReplaceTempOrderOptions } from '../../modules/Order/types';
5
+ import type { AddProductBookingInput, AddProductToOrderOptions, LoadSalesDetailParams, OrderPaymentData, OrderPaymentUpdateOptions, OrderPaymentUpdateResult, OrderPaymentSource, SendCustomerPayLinkParams, SyncPaymentsToOrderParams, SyncPaymentsToOrderResult, UpdateTempOrderCustomerInput, UpdateOrderBookingParams, UpdateOrderProductParams, OrderTempOrder, OrderSummary, OrderPromotionEvaluator, OrderGiftSelectResolver, OrderUnfulfilledPromotion, OrderLastGiftActions, RemoveProductsFromOrderOptions, ReplaceTempOrderOptions, SalesNote, SalesNotesMutationSnapshot, SetSalesNoteInput, PickupReferenceMode, ShopServiceData, ShopServiceType } from '../../modules/Order/types';
6
6
  import type { SubmitPayloadEnhancer } from '../../modules/Order/utils';
7
7
  import type { Discount } from '../../modules/Discount/types';
8
8
  import { RequestPlugin, WindowPlugin } from '../../plugins';
@@ -225,12 +225,20 @@ export declare class BaseSalesImpl extends BaseModule implements Module {
225
225
  getOrderSyncState(): import("../../modules/Order/types").OrderSyncState;
226
226
  getOrderAmountSnapshot(): import("../../modules/Order/types").OrderAmountSnapshot | null;
227
227
  getTempOrderNote(): string;
228
+ getSalesNotes(): SalesNote[];
229
+ createSalesNotesMutationSnapshot(): SalesNotesMutationSnapshot;
230
+ restoreSalesNotesMutationSnapshot(snapshot: SalesNotesMutationSnapshot): void;
231
+ setSalesNote(input: SetSalesNoteInput): Promise<SalesNote>;
232
+ removeSalesNote(uuid: string): Promise<void>;
233
+ clearSalesNotes(): Promise<void>;
228
234
  updateTempOrderNote(note: string): string;
229
235
  updateTempOrderNote(note: string, sync: true): Promise<string>;
230
236
  updateTempOrderNote(note: string, sync: boolean): string | Promise<string>;
231
237
  updateRemoteOrderNote(orderId: number | string, note: string): void;
232
238
  updateTempOrderShopDiscount(amount: string | number): string;
233
239
  updateTempOrderDepositAmount(amount: string | number): OrderSummary;
240
+ setPickupReferenceMode(mode: PickupReferenceMode): ShopServiceData;
241
+ setServiceType(serviceType: ShopServiceType): ShopServiceData;
234
242
  updateTempOrderContactsInfo(contactsInfo: Record<string, any> | null): Record<string, any> | null;
235
243
  /** @deprecated OrderModule 不再负责 tempOrder localStorage 持久化。 */
236
244
  setEnableTempOrderPersist(enabled: boolean): void;
@@ -283,7 +283,56 @@ function mergeBaseSalesUniqueArray(currentValue, loadedValue, metadataKey) {
283
283
  loadedValue.forEach(appendOrReplaceByUid);
284
284
  return result;
285
285
  }
286
+
287
+ // checkout / realtime snapshots may reorder protocol arrays or append persistence fields.
288
+ // Compare only the Sales Note protocol fields so a matching remote echo can acknowledge dirty state.
289
+ function normalizeBaseSalesNotesForComparison(value, options) {
290
+ if (!Array.isArray(value)) return null;
291
+ const unresolvedOrderTargetUuid = String(options?.unresolvedOrderTargetUuid ?? '').trim();
292
+ return value.map(note => {
293
+ if (!note || typeof note !== 'object' || Array.isArray(note)) {
294
+ return (0, _lodashEs.cloneDeep)(note);
295
+ }
296
+ const noteRecord = note;
297
+ const relations = Array.isArray(noteRecord.note_relations) ? noteRecord.note_relations.map(relation => {
298
+ if (!relation || typeof relation !== 'object' || Array.isArray(relation)) {
299
+ return (0, _lodashEs.cloneDeep)(relation);
300
+ }
301
+ const shouldResolveOrderTarget = relation.is_primary === 0 && relation.relation_type === 'related_to' && relation.target_type === 'order' && String(relation.target_uuid ?? '').trim() === '' && unresolvedOrderTargetUuid.length > 0;
302
+ return {
303
+ uuid: relation.uuid,
304
+ relation_type: relation.relation_type,
305
+ target_type: relation.target_type,
306
+ target_uuid: shouldResolveOrderTarget ? unresolvedOrderTargetUuid : relation.target_uuid,
307
+ is_primary: relation.is_primary
308
+ };
309
+ }).sort((left, right) => String(left?.uuid ?? '').localeCompare(String(right?.uuid ?? ''))) : (0, _lodashEs.cloneDeep)(noteRecord.note_relations);
310
+ return {
311
+ uuid: noteRecord.uuid,
312
+ note_type: noteRecord.note_type,
313
+ content: {
314
+ text: (0, _lodashEs.cloneDeep)(noteRecord.content?.text)
315
+ },
316
+ importance: noteRecord.importance,
317
+ note_relations: relations
318
+ };
319
+ }).sort((left, right) => String(left?.uuid ?? '').localeCompare(String(right?.uuid ?? '')));
320
+ }
321
+ function areBaseSalesNotesEquivalent(currentValue, loadedValue, options) {
322
+ const currentNotes = normalizeBaseSalesNotesForComparison(currentValue, {
323
+ unresolvedOrderTargetUuid: options?.currentOrderTargetUuid
324
+ });
325
+ const loadedNotes = normalizeBaseSalesNotesForComparison(loadedValue);
326
+ return currentNotes !== null && loadedNotes !== null && (0, _lodashEs.isEqual)(currentNotes, loadedNotes);
327
+ }
286
328
  function mergeSalesDetailRecordWithTempOrder(currentTempOrder, loadedRecord, strategy) {
329
+ const loadedHasSalesNotes = Object.prototype.hasOwnProperty.call(loadedRecord || {}, 'notes');
330
+ const hasDirtyLocalSalesNotes = currentTempOrder?._extend?.salesNotesState?.dirty === true;
331
+ const incomingSalesNotesMatchLocal = loadedHasSalesNotes && areBaseSalesNotesEquivalent(currentTempOrder?.notes, loadedRecord.notes, {
332
+ currentOrderTargetUuid: loadedRecord?.shop_order_number || currentTempOrder?.shop_order_number
333
+ });
334
+ const shouldAwaitAuthoritativeSalesNotes = strategy === 'authoritative-incoming' && hasDirtyLocalSalesNotes && (Number(loadedRecord?.need_sync) === 1 || !incomingSalesNotesMatchLocal);
335
+ const preserveDirtyLocalSalesNotes = hasDirtyLocalSalesNotes && (strategy === 'preserve-local' || shouldAwaitAuthoritativeSalesNotes);
287
336
  const normalizedLoadedRecord = normalizeBaseSalesRecordCollections(loadedRecord || {}, {
288
337
  ensureUid: false
289
338
  });
@@ -295,6 +344,12 @@ function mergeSalesDetailRecordWithTempOrder(currentTempOrder, loadedRecord, str
295
344
  });
296
345
  const uidRemaps = [];
297
346
  const mergedRecord = (0, _lodashEs.mergeWith)(normalizedCurrentTempOrder, normalizedLoadedRecord, (currentValue, loadedValue, key) => {
347
+ if (key === 'notes') {
348
+ if (preserveDirtyLocalSalesNotes && Array.isArray(currentValue)) {
349
+ return (0, _lodashEs.cloneDeep)(currentValue);
350
+ }
351
+ if (Array.isArray(loadedValue)) return (0, _lodashEs.cloneDeep)(loadedValue);
352
+ }
298
353
  const collectionKind = getBaseSalesOrderCollectionKind(key);
299
354
  if (collectionKind && Array.isArray(loadedValue)) {
300
355
  return mergeBaseSalesOrderCollection(collectionKind, Array.isArray(currentValue) ? currentValue : [], loadedValue, strategy, uidRemaps);
@@ -318,7 +373,24 @@ function mergeSalesDetailRecordWithTempOrder(currentTempOrder, loadedRecord, str
318
373
  return undefined;
319
374
  });
320
375
  delete mergedRecord.request_unique_idempotency_token;
321
- return normalizeBaseSalesRecordCollections((0, _orderCollectionIdentity.applyOrderCollectionUidRemaps)(mergedRecord, uidRemaps));
376
+ const normalizedMergedRecord = normalizeBaseSalesRecordCollections((0, _orderCollectionIdentity.applyOrderCollectionUidRemaps)(mergedRecord, uidRemaps));
377
+ normalizedMergedRecord._extend = {
378
+ ...(normalizedMergedRecord._extend || {}),
379
+ salesNotesState: preserveDirtyLocalSalesNotes ? {
380
+ loaded: true,
381
+ dirty: true
382
+ } : loadedHasSalesNotes ? {
383
+ loaded: true,
384
+ dirty: normalizedLoadedRecord._extend?.salesNotesState?.dirty === true
385
+ } : {
386
+ loaded: false,
387
+ dirty: false
388
+ }
389
+ };
390
+ if (!loadedHasSalesNotes && !preserveDirtyLocalSalesNotes) {
391
+ delete normalizedMergedRecord.notes;
392
+ }
393
+ return normalizedMergedRecord;
322
394
  }
323
395
  function filterPendingPaymentsFromLoadedSalesDetail(sourceRecord) {
324
396
  if (!sourceRecord || typeof sourceRecord !== 'object') return sourceRecord;
@@ -1426,6 +1498,30 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
1426
1498
  if (!this.store.order) return '';
1427
1499
  return this.store.order.getTempOrderNote();
1428
1500
  }
1501
+ getSalesNotes() {
1502
+ if (!this.store.order) return [];
1503
+ return this.store.order.getSalesNotes();
1504
+ }
1505
+ createSalesNotesMutationSnapshot() {
1506
+ if (!this.store.order) throw new Error('order 模块未初始化');
1507
+ return this.store.order.createSalesNotesMutationSnapshot();
1508
+ }
1509
+ restoreSalesNotesMutationSnapshot(snapshot) {
1510
+ if (!this.store.order) throw new Error('order 模块未初始化');
1511
+ this.store.order.restoreSalesNotesMutationSnapshot(snapshot);
1512
+ }
1513
+ async setSalesNote(input) {
1514
+ if (!this.store.order) throw new Error('order 模块未初始化');
1515
+ return this.store.order.setSalesNote(input);
1516
+ }
1517
+ async removeSalesNote(uuid) {
1518
+ if (!this.store.order) throw new Error('order 模块未初始化');
1519
+ await this.store.order.removeSalesNote(uuid);
1520
+ }
1521
+ async clearSalesNotes() {
1522
+ if (!this.store.order) throw new Error('order 模块未初始化');
1523
+ await this.store.order.clearSalesNotes();
1524
+ }
1429
1525
  updateTempOrderNote(note, sync = false) {
1430
1526
  try {
1431
1527
  if (!this.store.order) throw new Error('order 模块未初始化');
@@ -1447,6 +1543,14 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
1447
1543
  if (!this.store.order) throw new Error('order 模块未初始化');
1448
1544
  return this.store.order.updateTempOrderDepositAmount(amount);
1449
1545
  }
1546
+ setPickupReferenceMode(mode) {
1547
+ if (!this.store.order) throw new Error('order 模块未初始化');
1548
+ return this.store.order.updateTempOrderPickupReferenceMode(mode);
1549
+ }
1550
+ setServiceType(serviceType) {
1551
+ if (!this.store.order) throw new Error('order 模块未初始化');
1552
+ return this.store.order.updateTempOrderServiceType(serviceType);
1553
+ }
1450
1554
  updateTempOrderContactsInfo(contactsInfo) {
1451
1555
  if (!this.store.order) throw new Error('order 模块未初始化');
1452
1556
  return this.store.order.updateTempOrderContactsInfo(contactsInfo);
@@ -191,9 +191,10 @@ function transformBaseProductToOrderProduct(params) {
191
191
  metadata.unique_identification_number = String(uniqueIdentificationNumber);
192
192
  }
193
193
  // 从源头补充商品多语言标题
194
+ const productTitleI18n = sourceProduct?.title_i18n || {};
194
195
  const productTitle = {
195
- ...(sourceProduct?.title_i18n || {}),
196
- original: sourceProduct?.original_title || sourceProduct?.title || ''
196
+ ...productTitleI18n,
197
+ original: productTitleI18n.original || sourceProduct?.original_title || sourceProduct?.title || ''
197
198
  };
198
199
  return {
199
200
  product_id: productId,
@@ -326,7 +326,7 @@ export declare class BookingByStepImpl extends BaseModule implements Module {
326
326
  date: string;
327
327
  status: string;
328
328
  week: string;
329
- weekNum: 0 | 1 | 2 | 5 | 4 | 3 | 6;
329
+ weekNum: 0 | 2 | 1 | 3 | 4 | 5 | 6;
330
330
  }[]>;
331
331
  submitTimeSlot(timeSlots: TimeSliceItem): void;
332
332
  private getScheduleDataByIds;
@@ -345,7 +345,7 @@ export declare class BookingByStepImpl extends BaseModule implements Module {
345
345
  count: number;
346
346
  left: number;
347
347
  summaryCount: number;
348
- status: "sold_out" | "lots_of_space" | "filling_up_fast";
348
+ status: "lots_of_space" | "filling_up_fast" | "sold_out";
349
349
  }[];
350
350
  /**
351
351
  * 找到多个资源的公共可用时间段
@@ -23,7 +23,8 @@ function resolveCustomerDisplayName(customer) {
23
23
 
24
24
  /** 将 ICustomer 格式化为写入 tempOrder 的协议字段。 */
25
25
  function formatOrderCustomerPatch(customer) {
26
- const rawId = customer.id;
26
+ const c = customer;
27
+ const rawId = c.customer_id ?? c.customerId ?? customer.id;
27
28
  const numericId = typeof rawId === 'number' ? rawId : rawId !== undefined && rawId !== null && rawId !== '' ? Number(rawId) : null;
28
29
  return {
29
30
  customer_id: Number.isFinite(numericId) ? numericId : null,
@@ -10,7 +10,11 @@ export interface ApiConfig {
10
10
  method: 'GET' | 'POST' | 'PUT' | 'DELETE';
11
11
  transformParams?: (params: any) => any;
12
12
  transformResponse?: (response: any) => any;
13
- options?: Record<string, any>;
13
+ options?: Record<string, any> | ((params: any) => Record<string, any>);
14
+ }
15
+ export interface ApiCallerOptions {
16
+ baseUrl?: string;
17
+ shopDomain?: string;
14
18
  }
15
19
  /**
16
20
  * 渠道配置接口
@@ -65,12 +69,14 @@ export declare function getChannelConfig(channel: string): ChannelConfig;
65
69
  export declare class ApiCaller {
66
70
  private request;
67
71
  private config;
72
+ private options;
68
73
  constructor(request: {
69
74
  get: <T = any>(url: string, data?: any, options?: any) => Promise<T>;
70
75
  post: <T = any>(url: string, data?: any, options?: any) => Promise<T>;
71
76
  put: <T = any>(url: string, data?: any, options?: any) => Promise<T>;
72
77
  delete: <T = any>(url: string, options?: any) => Promise<T>;
73
- }, config: ChannelConfig);
78
+ }, config: ChannelConfig, options?: ApiCallerOptions);
79
+ private resolveUrl;
74
80
  /**
75
81
  * 执行 API 调用
76
82
  */
@@ -84,4 +90,4 @@ export declare function createApiCaller(request: {
84
90
  post: <T = any>(url: string, data?: any, options?: any) => Promise<T>;
85
91
  put: <T = any>(url: string, data?: any, options?: any) => Promise<T>;
86
92
  delete: <T = any>(url: string, options?: any) => Promise<T>;
87
- }, channel: string): ApiCaller;
93
+ }, channel: string, options?: ApiCallerOptions): ApiCaller;
@@ -15,6 +15,8 @@ exports.getChannelConfig = getChannelConfig;
15
15
  * API 配置接口
16
16
  */
17
17
 
18
+ const h5CustomerAuthApiNames = new Set(['sendEmailVerificationCode', 'sendEmailLoginCode', 'sendSmsRegisterCode', 'sendEmailRegisterLink', 'verifyEmailRegistrationLink', 'emailPasswordLogin', 'emailCodeRegister', 'phoneCodeRegister', 'resendEmailRegisterLink', 'checkEmailLinkCode', 'sendSmsLoginCode', 'phoneCodeLogin', 'guestLogin', 'checkEmailExists', 'checkEmailCode', 'checkMobileCode', 'phonePasswordLogin', 'sendPasswordResetEmail', 'sendPasswordResetSms', 'sendResetPasswordLink', 'checkResetPasswordCode', 'resetPasswordByCode', 'resetPasswordByEmail', 'resetPasswordByPhone', 'verifyCode', 'register', 'login', 'oauthLogin', 'facebookLogin', 'appleLogin']);
19
+
18
20
  /**
19
21
  * 渠道配置接口
20
22
  */
@@ -278,6 +280,31 @@ const onlineStoreConfig = {
278
280
  })
279
281
  }
280
282
  };
283
+ function withH5AuthPrefix(config) {
284
+ const nextConfig = {};
285
+ Object.entries(config).forEach(([key, apiConfig]) => {
286
+ if (!apiConfig.url.startsWith('/auth/')) {
287
+ nextConfig[key] = apiConfig;
288
+ return;
289
+ }
290
+ nextConfig[key] = {
291
+ ...apiConfig,
292
+ url: apiConfig.url.startsWith('/h5/') ? apiConfig.url : `/h5${apiConfig.url}`,
293
+ options: params => {
294
+ const {
295
+ withCredentials,
296
+ ...baseOptions
297
+ } = (typeof apiConfig.options === 'function' ? apiConfig.options(params) : apiConfig.options) || {};
298
+ return {
299
+ ...baseOptions,
300
+ prefix: false
301
+ };
302
+ }
303
+ };
304
+ });
305
+ return nextConfig;
306
+ }
307
+ const h5OnlineStoreConfig = withH5AuthPrefix(onlineStoreConfig);
281
308
 
282
309
  /**
283
310
  * 默认配置(通用配置)
@@ -1066,6 +1093,7 @@ const shopWebposConfig = {
1066
1093
  */
1067
1094
  const channelConfigMap = exports.channelConfigMap = {
1068
1095
  'online-store': onlineStoreConfig,
1096
+ 'online-store-h5': h5OnlineStoreConfig,
1069
1097
  'default': defaultConfig,
1070
1098
  'webpos': webposConfig,
1071
1099
  'shop.webpos': shopWebposConfig
@@ -1083,9 +1111,16 @@ function getChannelConfig(channel) {
1083
1111
  * 负责根据配置执行 API 调用
1084
1112
  */
1085
1113
  class ApiCaller {
1086
- constructor(request, config) {
1114
+ constructor(request, config, options = {}) {
1087
1115
  this.request = request;
1088
1116
  this.config = config;
1117
+ this.options = options;
1118
+ }
1119
+ resolveUrl(url) {
1120
+ if (!this.options.baseUrl || !url.startsWith('/h5/auth/')) {
1121
+ return url;
1122
+ }
1123
+ return `${this.options.baseUrl.replace(/\/$/, '')}${url}`;
1089
1124
  }
1090
1125
 
1091
1126
  /**
@@ -1111,6 +1146,12 @@ class ApiCaller {
1111
1146
  ...options,
1112
1147
  ...additionalOptions
1113
1148
  };
1149
+ if (apiConfig.url.startsWith('/h5/auth/') && h5CustomerAuthApiNames.has(apiName)) {
1150
+ requestOptions.h5CustomerAuth = true;
1151
+ if (this.options.shopDomain) {
1152
+ requestOptions.h5AuthShopDomain = this.options.shopDomain;
1153
+ }
1154
+ }
1114
1155
 
1115
1156
  // 处理动态 headers
1116
1157
  if (requestOptions.headers && typeof requestOptions.headers === 'function') {
@@ -1123,19 +1164,19 @@ class ApiCaller {
1123
1164
  switch (apiConfig.method) {
1124
1165
  case 'GET':
1125
1166
  if (transformedParams && Object.keys(transformedParams).length > 0) {
1126
- response = await this.request.get(apiConfig.url, transformedParams, requestOptions);
1167
+ response = await this.request.get(this.resolveUrl(apiConfig.url), transformedParams, requestOptions);
1127
1168
  } else {
1128
- response = await this.request.get(apiConfig.url, undefined, requestOptions);
1169
+ response = await this.request.get(this.resolveUrl(apiConfig.url), undefined, requestOptions);
1129
1170
  }
1130
1171
  break;
1131
1172
  case 'POST':
1132
- response = await this.request.post(apiConfig.url, transformedParams, requestOptions);
1173
+ response = await this.request.post(this.resolveUrl(apiConfig.url), transformedParams, requestOptions);
1133
1174
  break;
1134
1175
  case 'PUT':
1135
- response = await this.request.put(apiConfig.url, transformedParams, requestOptions);
1176
+ response = await this.request.put(this.resolveUrl(apiConfig.url), transformedParams, requestOptions);
1136
1177
  break;
1137
1178
  case 'DELETE':
1138
- response = await this.request.delete(apiConfig.url, requestOptions);
1179
+ response = await this.request.delete(this.resolveUrl(apiConfig.url), requestOptions);
1139
1180
  break;
1140
1181
  default:
1141
1182
  throw new Error(`不支持的 HTTP 方法: ${apiConfig.method}`);
@@ -1150,7 +1191,7 @@ class ApiCaller {
1150
1191
  * 创建 API 调用器
1151
1192
  */
1152
1193
  exports.ApiCaller = ApiCaller;
1153
- function createApiCaller(request, channel) {
1194
+ function createApiCaller(request, channel, options) {
1154
1195
  const config = getChannelConfig(channel);
1155
- return new ApiCaller(request, config);
1196
+ return new ApiCaller(request, config, options);
1156
1197
  }
@@ -227,7 +227,10 @@ class RegisterAndLoginImpl extends _BaseModule.BaseModule {
227
227
  }
228
228
 
229
229
  // 创建 API 调用器
230
- this.apiCaller = (0, _config.createApiCaller)(this.request, this.channel);
230
+ this.apiCaller = (0, _config.createApiCaller)(this.request, this.channel, {
231
+ baseUrl: this.otherParams.h5AuthBaseUrl,
232
+ shopDomain: this.otherParams.h5AuthShopDomain
233
+ });
231
234
 
232
235
  // 尝试从本地存储恢复登录状态
233
236
  await this.restoreLoginState();
@@ -13,6 +13,7 @@ interface ScanOrderItemRuleRuntimeConfig {
13
13
  pax?: Partial<PaxInfo>;
14
14
  historicalItems?: CartItemSummary[];
15
15
  serviceType?: string;
16
+ channel?: string;
16
17
  submissionIndex?: number;
17
18
  custom?: Record<string, any>;
18
19
  }
@@ -1200,7 +1200,11 @@ class ScanOrderImpl extends _BaseSales.BaseSalesImpl {
1200
1200
  return sourceMap;
1201
1201
  }
1202
1202
  getItemRuleRuntimeConfig() {
1203
- return this.itemRuleRuntimeConfig || {};
1203
+ const runtimeConfig = this.itemRuleRuntimeConfig || {};
1204
+ return {
1205
+ ...runtimeConfig,
1206
+ channel: runtimeConfig.channel ?? runtimeConfig.custom?.channel ?? this.otherParams?.channel
1207
+ };
1204
1208
  }
1205
1209
  async ensureItemRuleConfigsLoaded() {
1206
1210
  if (this.itemRuleConfigs.length > 0) return this.itemRuleConfigs;
@@ -78,6 +78,7 @@ export declare function buildItemRuleBusinessData(params: {
78
78
  pax?: Partial<PaxInfo>;
79
79
  historicalItems?: CartItemSummary[];
80
80
  serviceType?: string;
81
+ channel?: string;
81
82
  submissionIndex?: number;
82
83
  custom?: Record<string, any>;
83
84
  };
@@ -333,6 +333,7 @@ function buildItemRuleBusinessData(params) {
333
333
  cartItems,
334
334
  historicalItems,
335
335
  serviceType: runtimeConfig.serviceType || 'dine-in',
336
+ channel: runtimeConfig.channel ?? runtimeConfig.custom?.channel,
336
337
  submissionIndex: typeof runtimeConfig.submissionIndex === 'number' ? toNonNegativeInt(runtimeConfig.submissionIndex) : tempOrder.order_id ? 1 : 0,
337
338
  custom: runtimeConfig.custom || {},
338
339
  strategyConfigs: itemRuleConfigs
@@ -2,10 +2,11 @@ import type { Module, ModuleOptions, PisellCore } from '../../types';
2
2
  import type { ProductData } from '../../modules';
3
3
  import type { OpenDataConfig } from '../../modules/OpenData';
4
4
  import type { LoadScheduleAvailableDateParams, ScheduleItem } from '../../modules/Schedule/types';
5
- import type { ProductListLoadProductsParams } from '../../modules/ProductList';
5
+ import type { ProductListLoadProductsParams, ProductListStoreQueryParams } from '../../modules/ProductList';
6
6
  import type { AvailabilityDateRange, AvailabilityOccupancy, AvailabilityProduct, AvailabilityResource, GetProductTimeRangesRequest } from '../../modules/ResourcePlanner';
7
- import type { AvailabilityContextRef, ContextualAvailabilityQuery, ContextualAvailabilityQueryResult, ContextualCommitAvailabilitySelectionParams, ContextualProductTimeRangeGroup, UnifiedBookingSalesAddRetailProductParams, UnifiedBookingSalesApplyPlannerDiscountsParams, UnifiedBookingSalesCommitAvailabilitySelectionResult, UnifiedBookingSalesDiscountResult, UnifiedBookingSalesLoadProductsByScheduleDateParams, UnifiedBookingSalesLoadOpenDataParams, UnifiedBookingSalesPrepareAvailabilityProjectionParams, UnifiedBookingSalesPolicyResponse, UnifiedBookingSalesStageRetailProductsParams, UnifiedBookingSalesState } from './types';
7
+ import type { AvailabilityContextRef, ContextualAvailabilityQuery, ContextualAvailabilityQueryResult, ContextualCommitAvailabilitySelectionParams, ContextualProductTimeRangeGroup, UnifiedBookingSalesAddRetailProductParams, UnifiedBookingSalesApplyPlannerDiscountsParams, UnifiedBookingSalesCommitAvailabilitySelectionResult, UnifiedBookingSalesDiscountResult, UnifiedBookingSalesLoadProductsByScheduleDateParams, UnifiedBookingSalesLoadOpenDataParams, UnifiedBookingSalesPrepareAvailabilityProjectionParams, UnifiedBookingSalesPolicyResponse, UnifiedBookingSalesResolveProductAllergyRequirementParams, UnifiedBookingSalesStageRetailProductsParams, UnifiedBookingSalesState } from './types';
8
8
  import { BookingTicket } from '../BookingTicket';
9
+ import type { AllergyRequirement } from '../../model/strategy/adapter/itemRule';
9
10
  export * from './types';
10
11
  export { AvailabilityError } from '../../modules/ResourcePlanner';
11
12
  /**
@@ -76,6 +77,9 @@ export declare class UnifiedBookingSalesImpl extends BookingTicket {
76
77
  private availabilityRemoteResourceQuerySequence;
77
78
  private availabilityRemoteResourceEpoch;
78
79
  private availabilityCommitLocks;
80
+ private allergyItemRuleEvaluator;
81
+ private allergyItemRuleConfigs;
82
+ private allergyItemRuleConfigsPromise;
79
83
  initialize(core: PisellCore, options?: ModuleOptions): Promise<void>;
80
84
  protected getSubmitOrderSalesChannel(): string;
81
85
  private createAvailabilityFacadeError;
@@ -147,6 +151,14 @@ export declare class UnifiedBookingSalesImpl extends BookingTicket {
147
151
  */
148
152
  loadOpenData(params: UnifiedBookingSalesLoadOpenDataParams): Promise<OpenDataConfig>;
149
153
  getOpenData(): Promise<OpenDataConfig | null>;
154
+ private resetAllergyItemRuleState;
155
+ private fetchAllergyItemRuleConfigs;
156
+ private ensureAllergyItemRuleConfigsLoaded;
157
+ /**
158
+ * 评估当前渠道与商品是否命中 ALLERGY_CHECK。
159
+ * 只返回优先级最高 Action 中当前商品 Target 的 dataSource,不跨商品或 Action 合并。
160
+ */
161
+ resolveProductAllergyRequirement(params: UnifiedBookingSalesResolveProductAllergyRequirementParams): Promise<AllergyRequirement | null>;
150
162
  /**
151
163
  * Load a resource policy without depending on the legacy appointmentBooking solution.
152
164
  */
@@ -159,6 +171,7 @@ export declare class UnifiedBookingSalesImpl extends BookingTicket {
159
171
  callback?: (result: any) => void;
160
172
  subscriberId?: string;
161
173
  }): Promise<ProductData[]>;
174
+ queryProducts(params?: ProductListStoreQueryParams): Promise<ProductData[]>;
162
175
  /** Date-first entry point. It delegates protocol details and cache ownership to ScheduleModule. */
163
176
  loadScheduleAvailableDate(params: LoadScheduleAvailableDateParams): Promise<import("../../modules/Date/types").ITime[]>;
164
177
  /**