@pisell/pisellos 0.10.43 → 0.10.45

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 (118) hide show
  1. package/dist/model/strategy/adapter/itemRule/adapter.d.ts +4 -1
  2. package/dist/model/strategy/adapter/itemRule/adapter.js +135 -35
  3. package/dist/model/strategy/adapter/itemRule/evaluator.d.ts +8 -1
  4. package/dist/model/strategy/adapter/itemRule/evaluator.js +25 -1
  5. package/dist/model/strategy/adapter/itemRule/type.d.ts +44 -0
  6. package/dist/model/strategy/adapter/itemRule/type.js +19 -1
  7. package/dist/model/strategy/adapter/promotion/index.js +9 -0
  8. package/dist/model/strategy/adapter/walletPass/evaluator.d.ts +2 -0
  9. package/dist/model/strategy/adapter/walletPass/evaluator.js +4 -2
  10. package/dist/modules/Customer/index.js +2 -1
  11. package/dist/modules/Discount/availability.d.ts +7 -0
  12. package/dist/modules/Discount/availability.js +49 -0
  13. package/dist/modules/Discount/index.d.ts +1 -3
  14. package/dist/modules/Discount/index.js +12 -151
  15. package/dist/modules/Discount/types.d.ts +6 -0
  16. package/dist/modules/Order/index.d.ts +41 -9
  17. package/dist/modules/Order/index.js +1457 -872
  18. package/dist/modules/Order/salesNotes.d.ts +31 -0
  19. package/dist/modules/Order/salesNotes.js +492 -0
  20. package/dist/modules/Order/types.d.ts +105 -8
  21. package/dist/modules/Order/types.js +6 -1
  22. package/dist/modules/Order/utils.d.ts +4 -2
  23. package/dist/modules/Order/utils.js +224 -79
  24. package/dist/modules/Payment/cashRecommendationAlgorithm.d.ts +4 -5
  25. package/dist/modules/Payment/cashRecommendationAlgorithm.js +17 -367
  26. package/dist/modules/Product/index.d.ts +1 -1
  27. package/dist/modules/ProductList/index.d.ts +2 -1
  28. package/dist/modules/ProductList/index.js +100 -20
  29. package/dist/modules/ProductList/types.d.ts +10 -0
  30. package/dist/modules/Rules/index.js +50 -16
  31. package/dist/server/index.d.ts +7 -0
  32. package/dist/server/index.js +45 -3
  33. package/dist/server/modules/order/index.js +6 -0
  34. package/dist/server/modules/order/types.d.ts +9 -0
  35. package/dist/server/utils/small-ticket.d.ts +1 -0
  36. package/dist/server/utils/small-ticket.js +142 -14
  37. package/dist/solution/BaseSales/index.d.ts +15 -1
  38. package/dist/solution/BaseSales/index.js +1240 -877
  39. package/dist/solution/BaseSales/types.d.ts +2 -2
  40. package/dist/solution/BookingByStep/index.d.ts +1 -1
  41. package/dist/solution/BookingTicket/index.d.ts +1 -0
  42. package/dist/solution/BookingTicket/index.js +37 -16
  43. package/dist/solution/BookingTicket/utils/orderCustomer.js +3 -2
  44. package/dist/solution/RegisterAndLogin/config.d.ts +9 -3
  45. package/dist/solution/RegisterAndLogin/config.js +95 -40
  46. package/dist/solution/RegisterAndLogin/index.js +4 -1
  47. package/dist/solution/ScanOrder/index.d.ts +1 -0
  48. package/dist/solution/ScanOrder/index.js +31 -27
  49. package/dist/solution/ScanOrder/utils.d.ts +1 -0
  50. package/dist/solution/ScanOrder/utils.js +2 -0
  51. package/dist/solution/ShopDiscount/index.d.ts +1 -0
  52. package/dist/solution/ShopDiscount/index.js +59 -22
  53. package/dist/solution/ShopDiscount/utils.d.ts +4 -2
  54. package/dist/solution/ShopDiscount/utils.js +40 -21
  55. package/dist/solution/UnifiedBookingSales/index.d.ts +15 -2
  56. package/dist/solution/UnifiedBookingSales/index.js +777 -510
  57. package/dist/solution/UnifiedBookingSales/types.d.ts +14 -1
  58. package/dist/solution/VenueBooking/index.d.ts +1 -0
  59. package/dist/solution/VenueBooking/index.js +8 -4
  60. package/lib/model/strategy/adapter/itemRule/adapter.d.ts +4 -1
  61. package/lib/model/strategy/adapter/itemRule/adapter.js +73 -4
  62. package/lib/model/strategy/adapter/itemRule/evaluator.d.ts +8 -1
  63. package/lib/model/strategy/adapter/itemRule/evaluator.js +23 -1
  64. package/lib/model/strategy/adapter/itemRule/type.d.ts +44 -0
  65. package/lib/model/strategy/adapter/itemRule/type.js +19 -1
  66. package/lib/model/strategy/adapter/walletPass/evaluator.d.ts +2 -0
  67. package/lib/model/strategy/adapter/walletPass/evaluator.js +2 -0
  68. package/lib/modules/Customer/index.js +1 -0
  69. package/lib/modules/Discount/availability.d.ts +7 -0
  70. package/lib/modules/Discount/availability.js +43 -0
  71. package/lib/modules/Discount/index.d.ts +1 -3
  72. package/lib/modules/Discount/index.js +10 -131
  73. package/lib/modules/Discount/types.d.ts +6 -0
  74. package/lib/modules/Order/index.d.ts +41 -9
  75. package/lib/modules/Order/index.js +456 -69
  76. package/lib/modules/Order/salesNotes.d.ts +31 -0
  77. package/lib/modules/Order/salesNotes.js +382 -0
  78. package/lib/modules/Order/types.d.ts +105 -8
  79. package/lib/modules/Order/types.js +5 -1
  80. package/lib/modules/Order/utils.d.ts +4 -2
  81. package/lib/modules/Order/utils.js +134 -3
  82. package/lib/modules/Payment/cashRecommendationAlgorithm.d.ts +4 -5
  83. package/lib/modules/Payment/cashRecommendationAlgorithm.js +9 -354
  84. package/lib/modules/Product/index.d.ts +1 -1
  85. package/lib/modules/ProductList/index.d.ts +2 -1
  86. package/lib/modules/ProductList/index.js +47 -2
  87. package/lib/modules/ProductList/types.d.ts +10 -0
  88. package/lib/modules/Rules/index.js +42 -5
  89. package/lib/server/index.d.ts +7 -0
  90. package/lib/server/index.js +39 -3
  91. package/lib/server/modules/order/index.js +6 -0
  92. package/lib/server/modules/order/types.d.ts +9 -0
  93. package/lib/server/utils/small-ticket.d.ts +1 -0
  94. package/lib/server/utils/small-ticket.js +136 -12
  95. package/lib/solution/BaseSales/index.d.ts +15 -1
  96. package/lib/solution/BaseSales/index.js +192 -22
  97. package/lib/solution/BaseSales/types.d.ts +2 -2
  98. package/lib/solution/BookingByStep/index.d.ts +1 -1
  99. package/lib/solution/BookingTicket/index.d.ts +1 -0
  100. package/lib/solution/BookingTicket/index.js +23 -5
  101. package/lib/solution/BookingTicket/utils/orderCustomer.js +2 -1
  102. package/lib/solution/RegisterAndLogin/config.d.ts +9 -3
  103. package/lib/solution/RegisterAndLogin/config.js +49 -8
  104. package/lib/solution/RegisterAndLogin/index.js +4 -1
  105. package/lib/solution/ScanOrder/index.d.ts +1 -0
  106. package/lib/solution/ScanOrder/index.js +5 -1
  107. package/lib/solution/ScanOrder/utils.d.ts +1 -0
  108. package/lib/solution/ScanOrder/utils.js +1 -0
  109. package/lib/solution/ShopDiscount/index.d.ts +1 -0
  110. package/lib/solution/ShopDiscount/index.js +29 -3
  111. package/lib/solution/ShopDiscount/utils.d.ts +4 -2
  112. package/lib/solution/ShopDiscount/utils.js +39 -23
  113. package/lib/solution/UnifiedBookingSales/index.d.ts +15 -2
  114. package/lib/solution/UnifiedBookingSales/index.js +125 -1
  115. package/lib/solution/UnifiedBookingSales/types.d.ts +14 -1
  116. package/lib/solution/VenueBooking/index.d.ts +1 -0
  117. package/lib/solution/VenueBooking/index.js +5 -1
  118. package/package.json +1 -1
@@ -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
@@ -43,6 +43,7 @@ export declare class ShopDiscountImpl extends BaseModule implements Module {
43
43
  batchSearchByProductIds(productIds: number[]): Promise<Discount[]>;
44
44
  scanCode(code: string, customerId?: number): Promise<{
45
45
  isAvailable: boolean;
46
+ isAccepted?: boolean;
46
47
  productList: Record<string, any>[];
47
48
  discountList: Discount[];
48
49
  type: "server" | "clientCalc";
@@ -10,6 +10,7 @@ var _Rules = require("../../modules/Rules");
10
10
  var _entitlementPass = require("../../modules/Discount/entitlementPass");
11
11
  var _utils = require("./utils");
12
12
  var _types = require("../../modules/Rules/types");
13
+ var _availability = require("../../modules/Discount/availability");
13
14
  var _decimal = _interopRequireDefault(require("decimal.js"));
14
15
  var _lodashEs = require("lodash-es");
15
16
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
@@ -349,6 +350,7 @@ class ShopDiscountImpl extends _BaseModule.BaseModule {
349
350
  const {
350
351
  productList: newProductList,
351
352
  discountList: newDiscountList,
353
+ evaluatedDiscountList,
352
354
  isAvailable,
353
355
  unavailableReason
354
356
  } = rulesModule.isDiscountListAvailable({
@@ -363,6 +365,7 @@ class ShopDiscountImpl extends _BaseModule.BaseModule {
363
365
  productList: this.store.productList || [],
364
366
  discountList: this.getDiscountList()
365
367
  };
368
+ let isAccepted = isAvailable;
366
369
  if (isAvailable) {
367
370
  this.setDiscountList(newDiscountList || []);
368
371
  this.store.originalDiscountList = newDiscountList || [];
@@ -370,12 +373,34 @@ class ShopDiscountImpl extends _BaseModule.BaseModule {
370
373
  if (this.isWalkIn() && resultDiscountList.length && this.options.otherParams?.platform === 'shop' && !((resultDiscountList[0].customer_id || 1) === 1)) {
371
374
  await this.getCustomerWallet(resultDiscountList[0].customer_id);
372
375
  }
376
+ } else {
377
+ const scannedIds = new Set(withScanList.map(discount => String(discount.id)));
378
+ const retainedScannedDiscounts = (evaluatedDiscountList || withScanList).filter(discount => scannedIds.has(String(discount.id)) && !(0, _availability.getDiscountLifecycleReason)(discount));
379
+ if (retainedScannedDiscounts.length) {
380
+ const retainedDiscounts = new Map(this.getDiscountList().map(discount => [String(discount.id), discount]));
381
+ retainedScannedDiscounts.forEach(discount => {
382
+ retainedDiscounts.set(String(discount.id), {
383
+ ...discount,
384
+ isScan: true,
385
+ isAvailable: false,
386
+ isSelected: false,
387
+ reasonCode: discount.reasonCode || 'product_not_applicable',
388
+ appliedProductDetails: [],
389
+ savedAmount: 0
390
+ });
391
+ });
392
+ this.setDiscountList([...retainedDiscounts.values()]);
393
+ this.store.originalDiscountList = this.getDiscountList();
394
+ await this.store.discount?.setOriginalDiscountList(this.getDiscountList());
395
+ isAccepted = true;
396
+ }
373
397
  }
374
398
  return {
375
399
  type: "clientCalc",
376
400
  isAvailable: isAvailable || false,
401
+ isAccepted: Boolean(isAccepted),
377
402
  productList: newProductList || this.store.productList || [],
378
- discountList: newDiscountList || this.getDiscountList(),
403
+ discountList: this.getDiscountList(),
379
404
  unavailableReason
380
405
  };
381
406
  } catch (error) {
@@ -525,6 +550,7 @@ class ShopDiscountImpl extends _BaseModule.BaseModule {
525
550
  with_discount_card: 1,
526
551
  with_wallet_pass_holder: 1,
527
552
  with_entitlement_pass: 1,
553
+ filter_order_wallet_pass_usage: 0,
528
554
  request_timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
529
555
  ...(orderId ? {
530
556
  order_id: orderId
@@ -559,8 +585,8 @@ class ShopDiscountImpl extends _BaseModule.BaseModule {
559
585
  this.store.originalDiscountList = newDiscountList;
560
586
  await this.store.discount?.setOriginalDiscountList(newDiscountList);
561
587
 
562
- // 根据当前预约时间过滤优惠券列表
563
- const filteredDiscountList = this.filterDiscountListByBookingTime(newDiscountList, this.store.currentBookingTime);
588
+ // 日程限制交给 Rules 计算可用状态,展示列表保留当前不可用的卡券。
589
+ const filteredDiscountList = newDiscountList;
564
590
  this.store.filteredDiscountList = filteredDiscountList;
565
591
  this.setDiscountList(filteredDiscountList || []);
566
592
  if (this.store.productList?.length) {
@@ -59,12 +59,14 @@ export interface ScheduleItem {
59
59
  }
60
60
  export declare const getDateIsInSchedule: (dateTime: string, scheduleList: ScheduleItem[]) => boolean;
61
61
  /**
62
- * 根据预约时间过滤优惠券列表
62
+ * 根据商品使用时间过滤优惠券列表(预约开始时间或普通商品的当前时间)
63
63
  * @param discountList 优惠券列表
64
- * @param bookingTime 预约时间 (格式: YYYY-MM-DD HH:mm:ss)
64
+ * @param bookingTime 使用时间 (格式: YYYY-MM-DD HH:mm:ss),未确定时不提前过滤
65
65
  * @returns 过滤后的优惠券列表
66
66
  */
67
67
  export declare const filterDiscountListByBookingTime: (discountList: Discount[], bookingTime: string | null) => Discount[];
68
+ /** 与抵扣计算共用时间判断,区分未开始、有效期不匹配和日程限制。 */
69
+ export declare const getDiscountTimeUnavailableReason: (discount: Discount, useTime: string) => 'not_started' | 'time_limit' | 'outside_schedule' | undefined;
68
70
  /**
69
71
  * 判断是否是订单级别的固定金额折扣卡
70
72
  * @param discount 折扣
@@ -3,11 +3,15 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.uniqueById = exports.isOrderLevelFixedAmountDiscount = exports.isNormalProductByDurationSchedule = exports.isAllNormalProduct = exports.getDiscountListAmountTotal = exports.getDiscountListAmount = exports.getDiscountAmount = exports.getDateIsInSchedule = exports.filterDiscountListByBookingTime = exports.calculateOrderLevelDiscountAllocation = void 0;
6
+ exports.uniqueById = exports.isOrderLevelFixedAmountDiscount = exports.isNormalProductByDurationSchedule = exports.isAllNormalProduct = exports.getDiscountTimeUnavailableReason = exports.getDiscountListAmountTotal = exports.getDiscountListAmount = exports.getDiscountAmount = exports.getDateIsInSchedule = exports.filterDiscountListByBookingTime = exports.calculateOrderLevelDiscountAllocation = void 0;
7
7
  var _decimal = _interopRequireDefault(require("decimal.js"));
8
8
  var _dayjs = _interopRequireDefault(require("dayjs"));
9
+ var _isSameOrAfter = _interopRequireDefault(require("dayjs/plugin/isSameOrAfter"));
10
+ var _isSameOrBefore = _interopRequireDefault(require("dayjs/plugin/isSameOrBefore"));
9
11
  var _entitlementPass = require("../../modules/Discount/entitlementPass");
10
12
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
13
+ _dayjs.default.extend(_isSameOrAfter.default);
14
+ _dayjs.default.extend(_isSameOrBefore.default);
11
15
  const uniqueById = (arr, key = 'id') => {
12
16
  const seen = new Set();
13
17
  return arr.filter(item => !seen.has(item[key]) && seen.add(item[key]));
@@ -377,41 +381,53 @@ const isInWeeklyRepeatForDate = (targetDate, startDate, repeatRule) => {
377
381
  };
378
382
 
379
383
  /**
380
- * 根据预约时间过滤优惠券列表
384
+ * 根据商品使用时间过滤优惠券列表(预约开始时间或普通商品的当前时间)
381
385
  * @param discountList 优惠券列表
382
- * @param bookingTime 预约时间 (格式: YYYY-MM-DD HH:mm:ss)
386
+ * @param bookingTime 使用时间 (格式: YYYY-MM-DD HH:mm:ss),未确定时不提前过滤
383
387
  * @returns 过滤后的优惠券列表
384
388
  */
385
389
  const filterDiscountListByBookingTime = (discountList, bookingTime) => {
386
- if (!bookingTime) {
387
- return discountList;
388
- }
389
- return discountList.filter(discount => {
390
+ if (!bookingTime) return discountList;
391
+ return discountList.filter(discount => !getDiscountTimeUnavailableReason(discount, bookingTime));
392
+ };
393
+
394
+ /** 与抵扣计算共用时间判断,区分未开始、有效期不匹配和日程限制。 */
395
+ exports.filterDiscountListByBookingTime = filterDiscountListByBookingTime;
396
+ const getDiscountTimeUnavailableReason = (discount, useTime) => {
397
+ const useTimeDayjs = (0, _dayjs.default)(useTime);
398
+ if (!useTimeDayjs.isValid()) return 'time_limit';
399
+ try {
390
400
  if (discount?.metadata?.validity_type === "custom_schedule_validity") {
391
- return getDateIsInSchedule(bookingTime, discount.custom_schedule_snapshot?.data || []);
401
+ return getDateIsInSchedule(useTime, discount.custom_schedule_snapshot?.data || []) ? undefined : 'outside_schedule';
392
402
  }
393
- try {
394
- const isHasStart = !!discount.start_date && !!discount.start_time;
395
- const startDate = (0, _dayjs.default)(`${discount.start_date} ${discount.start_time}`);
396
- const bookingTimeDayjs = (0, _dayjs.default)(bookingTime);
397
-
398
- // 判断预约时间是否在开始时间之后
399
- const isAfterStart = isHasStart ? bookingTimeDayjs.isSameOrAfter(startDate) : true;
400
-
401
- // 判断预约时间是否在过期时间之前(expire_time 为空时为长期有效)
402
- const isBeforeExpire = discount.expire_time ? bookingTimeDayjs.isSameOrBefore((0, _dayjs.default)(discount.expire_time)) : true;
403
- return isAfterStart && isBeforeExpire;
404
- } catch (error) {
405
- return true;
403
+ const isHasStart = !!discount.start_date && !!discount.start_time;
404
+ const startDate = (0, _dayjs.default)(`${discount.start_date} ${discount.start_time}`);
405
+
406
+ // 开始时刻包含在有效期内。
407
+ if (isHasStart) {
408
+ if (!startDate.isValid()) return 'time_limit';
409
+ if (useTimeDayjs.isBefore(startDate)) return 'not_started';
406
410
  }
407
- });
411
+
412
+ // 到期时刻不再可用;expire_time 为空时为长期有效。
413
+ const expireTime = discount.expire_time ? (0, _dayjs.default)(discount.expire_time) : null;
414
+ const isBeforeExpire = expireTime ? expireTime.isValid() && useTimeDayjs.isBefore(expireTime) : true;
415
+ if (!isBeforeExpire) return 'time_limit';
416
+ const validity = discount.extension_data?.find(field => field.field_key === 'valid_times')?.value;
417
+ if (validity?.restrict_use_by_schedule === true && !getDateIsInSchedule(useTime, discount.custom_schedule_snapshot?.data || [])) {
418
+ return 'outside_schedule';
419
+ }
420
+ return undefined;
421
+ } catch (error) {
422
+ return 'time_limit';
423
+ }
408
424
  };
409
425
  /**
410
426
  * 判断是否是订单级别的固定金额折扣卡
411
427
  * @param discount 折扣
412
428
  * @returns 是否是订单级别的固定金额折扣卡
413
429
  */
414
- exports.filterDiscountListByBookingTime = filterDiscountListByBookingTime;
430
+ exports.getDiscountTimeUnavailableReason = getDiscountTimeUnavailableReason;
415
431
  const isOrderLevelFixedAmountDiscount = discount => {
416
432
  return discount.tag === 'product_discount_card' && discount?.metadata?.discount_card_type === 'fixed_amount' && discount?.metadata?.discount_calculation_mode === 'order_level';
417
433
  };
@@ -2,10 +2,11 @@ import type { Module, ModuleOptions, PisellCore } from '../../types';
2
2
  import type { ProductData } from '../../modules';
3
3
  import { type InvoiceLayoutConfig, type InvoiceTemplateSettings, 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
  /**
@@ -73,6 +74,9 @@ export declare class UnifiedBookingSalesImpl extends BookingTicket {
73
74
  private availabilityRemoteResourceQuerySequence;
74
75
  private availabilityRemoteResourceEpoch;
75
76
  private availabilityCommitLocks;
77
+ private allergyItemRuleEvaluator;
78
+ private allergyItemRuleConfigs;
79
+ private allergyItemRuleConfigsPromise;
76
80
  initialize(core: PisellCore, options?: ModuleOptions): Promise<void>;
77
81
  protected getSubmitOrderSalesChannel(): string;
78
82
  private createAvailabilityFacadeError;
@@ -144,6 +148,14 @@ export declare class UnifiedBookingSalesImpl extends BookingTicket {
144
148
  */
145
149
  loadOpenData(params: UnifiedBookingSalesLoadOpenDataParams): Promise<OpenDataConfig>;
146
150
  getOpenData(): Promise<OpenDataConfig | null>;
151
+ private resetAllergyItemRuleState;
152
+ private fetchAllergyItemRuleConfigs;
153
+ private ensureAllergyItemRuleConfigsLoaded;
154
+ /**
155
+ * 评估当前渠道与商品是否命中 ALLERGY_CHECK。
156
+ * 只返回优先级最高 Action 中当前商品 Target 的 dataSource,不跨商品或 Action 合并。
157
+ */
158
+ resolveProductAllergyRequirement(params: UnifiedBookingSalesResolveProductAllergyRequirementParams): Promise<AllergyRequirement | null>;
147
159
  clearOpenDataCache(): void;
148
160
  getInvoiceLayoutConfig(): Promise<InvoiceLayoutConfig>;
149
161
  getInvoiceTemplateSettings(): Promise<InvoiceTemplateSettings>;
@@ -160,6 +172,7 @@ export declare class UnifiedBookingSalesImpl extends BookingTicket {
160
172
  callback?: (result: any) => void;
161
173
  subscriberId?: string;
162
174
  }): Promise<ProductData[]>;
175
+ queryProducts(params?: ProductListStoreQueryParams): Promise<ProductData[]>;
163
176
  /** Date-first entry point. It delegates protocol details and cache ownership to ScheduleModule. */
164
177
  loadScheduleAvailableDate(params: LoadScheduleAvailableDateParams): Promise<import("../../modules/Date/types").ITime[]>;
165
178
  /**
@@ -52,6 +52,7 @@ var _remoteOccupancyCache = require("./remoteOccupancyCache");
52
52
  var _BookingTicket = require("../BookingTicket");
53
53
  var _localClock = require("../../modules/ResourcePlanner/localClock");
54
54
  var _platform = require("../../utils/platform");
55
+ var _itemRule = require("../../model/strategy/adapter/itemRule");
55
56
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
56
57
  const OPEN_DATA_SECTION_CODES = ['sale', 'reservation', 'fulfillment', 'menu', 'workflow', 'basic', 'checkout', 'availability', 'documents'];
57
58
  const OPEN_DATA_CACHE_TTL = 5 * 60 * 1000;
@@ -84,6 +85,22 @@ function getCandidateStartTime(candidate) {
84
85
  return undefined;
85
86
  }
86
87
  }
88
+ function normalizePositiveIntegerList(value) {
89
+ if (!Array.isArray(value)) return [];
90
+ return Array.from(new Set(value.map(item => normalizePositiveInteger(item)).filter(item => item !== undefined)));
91
+ }
92
+ function toConfiguredBoolean(value) {
93
+ if (typeof value === 'boolean') return value;
94
+ if (typeof value === 'number') return value !== 0;
95
+ if (typeof value === 'string') {
96
+ return ['1', 'true', 'yes', 'y', 'on'].includes(value.trim().toLowerCase());
97
+ }
98
+ return false;
99
+ }
100
+ function normalizeItemRuleStrategyConfigs(value) {
101
+ if (!Array.isArray(value)) return [];
102
+ return value.filter(config => Boolean(config && typeof config === 'object' && config.metadata?.type === 'item_rule'));
103
+ }
87
104
  function isRecord(value) {
88
105
  return !!value && typeof value === 'object' && !Array.isArray(value);
89
106
  }
@@ -1197,11 +1214,15 @@ class UnifiedBookingSalesImpl extends _BookingTicket.BookingTicket {
1197
1214
  availabilityRemoteResourceQuerySequence = 0;
1198
1215
  availabilityRemoteResourceEpoch = 0;
1199
1216
  availabilityCommitLocks = new Set();
1217
+ allergyItemRuleEvaluator = new _itemRule.ItemRuleEvaluator();
1218
+ allergyItemRuleConfigs = [];
1219
+ allergyItemRuleConfigsPromise = null;
1200
1220
  async initialize(core, options = {}) {
1201
1221
  this.availabilityScheduleCatalog = null;
1202
1222
  this.openDataTarget = null;
1203
1223
  this.availabilityRuntimeOffsetMinutes = (0, _localClock.captureRuntimeOffsetMinutes)();
1204
1224
  this.clearAvailabilityRemoteResourceState();
1225
+ this.resetAllergyItemRuleState();
1205
1226
  await super.initialize(core, options);
1206
1227
  const openDataBusinessCode = String(this.otherParams?.businessCode ?? this.otherParams?.business_code ?? '').trim();
1207
1228
  const openDataChannel = String(this.otherParams?.channel ?? '').trim();
@@ -1346,6 +1367,7 @@ class UnifiedBookingSalesImpl extends _BookingTicket.BookingTicket {
1346
1367
  cover = false
1347
1368
  } = {}) {
1348
1369
  const previousScope = this.getAvailabilityScopeDescriptor(this.otherParams || {});
1370
+ const previousItemRuleConfig = this.otherParams?.itemRule;
1349
1371
  const explicitlySetsOpenData = Object.prototype.hasOwnProperty.call(params, 'openData');
1350
1372
  await super.setOtherParams(params, {
1351
1373
  cover
@@ -1358,11 +1380,15 @@ class UnifiedBookingSalesImpl extends _BookingTicket.BookingTicket {
1358
1380
  if (shouldResetAvailabilityState) {
1359
1381
  await this.resetAvailabilityProjectionState('set_other_params_reset');
1360
1382
  }
1383
+ if (shouldResetAvailabilityState || explicitlySetsOpenData || previousItemRuleConfig !== this.otherParams?.itemRule) {
1384
+ this.resetAllergyItemRuleState();
1385
+ }
1361
1386
  }
1362
1387
  async destroy() {
1363
1388
  this.availabilityScheduleCatalog = null;
1364
1389
  this.availabilityRuntimeOffsetMinutes = null;
1365
1390
  await this.resetAvailabilityProjectionState('destroy');
1391
+ this.resetAllergyItemRuleState();
1366
1392
  await super.destroy();
1367
1393
  }
1368
1394
  createAvailabilityContextId() {
@@ -1721,6 +1747,98 @@ class UnifiedBookingSalesImpl extends _BookingTicket.BookingTicket {
1721
1747
  channel
1722
1748
  });
1723
1749
  }
1750
+ resetAllergyItemRuleState() {
1751
+ this.allergyItemRuleConfigs = [];
1752
+ this.allergyItemRuleConfigsPromise = null;
1753
+ this.allergyItemRuleEvaluator.setStrategyConfigs([]);
1754
+ }
1755
+ async fetchAllergyItemRuleConfigs(strategyModelIds) {
1756
+ if (!strategyModelIds.length) return [];
1757
+ try {
1758
+ const response = await this.request.get('/promotion', {
1759
+ skip: 1,
1760
+ num: 99,
1761
+ status: 'active',
1762
+ ids: strategyModelIds
1763
+ });
1764
+ if (response?.code !== 200 || !Array.isArray(response?.data?.list)) {
1765
+ throw new Error('[UnifiedBookingSales] 商品过敏策略加载失败');
1766
+ }
1767
+ const list = response.data.list;
1768
+ return normalizeItemRuleStrategyConfigs(list.map(item => item?.metadata));
1769
+ } catch (error) {
1770
+ // this.logMethodError('fetchAllergyItemRuleConfigs', error, {
1771
+ // strategyModelIds,
1772
+ // });
1773
+ throw error;
1774
+ }
1775
+ }
1776
+ async ensureAllergyItemRuleConfigsLoaded() {
1777
+ if (this.allergyItemRuleConfigs.length > 0) {
1778
+ return this.allergyItemRuleConfigs;
1779
+ }
1780
+ if (this.allergyItemRuleConfigsPromise) {
1781
+ return this.allergyItemRuleConfigsPromise;
1782
+ }
1783
+ this.allergyItemRuleConfigsPromise = (async () => {
1784
+ const staticConfigs = normalizeItemRuleStrategyConfigs(this.otherParams?.itemRule?.strategyConfigs);
1785
+ if (staticConfigs.length > 0) {
1786
+ this.allergyItemRuleConfigs = staticConfigs;
1787
+ this.allergyItemRuleEvaluator.setStrategyConfigs(staticConfigs);
1788
+ return staticConfigs;
1789
+ }
1790
+ const openData = await this.getOpenData();
1791
+ const enabled = toConfiguredBoolean(openData?.['sale.enable_item_rules']);
1792
+ const strategyModelIds = normalizePositiveIntegerList(openData?.['sale.enabled_item_rules']);
1793
+ if (!enabled || !strategyModelIds.length) {
1794
+ this.allergyItemRuleEvaluator.setStrategyConfigs([]);
1795
+ return [];
1796
+ }
1797
+ const loaded = await this.fetchAllergyItemRuleConfigs(strategyModelIds);
1798
+ this.allergyItemRuleConfigs = loaded;
1799
+ this.allergyItemRuleEvaluator.setStrategyConfigs(loaded);
1800
+ return loaded;
1801
+ })();
1802
+ return this.allergyItemRuleConfigsPromise;
1803
+ }
1804
+
1805
+ /**
1806
+ * 评估当前渠道与商品是否命中 ALLERGY_CHECK。
1807
+ * 只返回优先级最高 Action 中当前商品 Target 的 dataSource,不跨商品或 Action 合并。
1808
+ */
1809
+ async resolveProductAllergyRequirement(params) {
1810
+ const productId = normalizePositiveInteger(params.productId);
1811
+ if (!productId) return null;
1812
+ const strategyConfigs = await this.ensureAllergyItemRuleConfigsLoaded();
1813
+ if (!strategyConfigs.length) return null;
1814
+ const productVariantId = normalizePositiveInteger(params.productVariantId);
1815
+ const runtimeConfig = this.otherParams?.itemRule || {};
1816
+ const runtimePax = runtimeConfig.pax || {};
1817
+ const cartItems = (this.getOrderProducts?.() || []).map(item => ({
1818
+ product_id: Number(item?.product_id ?? item?.id),
1819
+ product_variant_id: Number(item?.product_variant_id ?? 0) || undefined,
1820
+ quantity: Number(item?.num ?? item?.quantity ?? 0)
1821
+ })).filter(item => Number.isFinite(item.product_id) && item.product_id > 0 && Number.isFinite(item.quantity) && item.quantity > 0);
1822
+ return this.allergyItemRuleEvaluator.getAllergyRequirement({
1823
+ pax: {
1824
+ total: Number(runtimePax.total ?? 0) || 0,
1825
+ adult: Number(runtimePax.adult ?? 0) || 0,
1826
+ child: Number(runtimePax.child ?? 0) || 0
1827
+ },
1828
+ cartItems,
1829
+ serviceType: String(runtimeConfig.serviceType ?? this.otherParams?.serviceType ?? ''),
1830
+ channel: String(params.channel ?? this.otherParams?.channel ?? this.otherParams?.platform ?? ''),
1831
+ currentProduct: {
1832
+ product_id: productId,
1833
+ ...(productVariantId ? {
1834
+ product_variant_id: productVariantId
1835
+ } : {})
1836
+ },
1837
+ submissionIndex: Number(runtimeConfig.submissionIndex ?? 0) || 0,
1838
+ strategyConfigs,
1839
+ custom: runtimeConfig.custom
1840
+ });
1841
+ }
1724
1842
  clearOpenDataCache() {
1725
1843
  this.openDataTarget = null;
1726
1844
  this.loadOpenDataInFlight = null;
@@ -1783,6 +1901,10 @@ class UnifiedBookingSalesImpl extends _BookingTicket.BookingTicket {
1783
1901
  await this.core.effects.emit(`${this.name}:onProductsLoaded`, result);
1784
1902
  return result;
1785
1903
  }
1904
+ async queryProducts(params = {}) {
1905
+ if (!this.store.products) throw new Error('[UnifiedBookingSales] products 模块未初始化');
1906
+ return this.store.products.queryProducts(params);
1907
+ }
1786
1908
 
1787
1909
  /** Date-first entry point. It delegates protocol details and cache ownership to ScheduleModule. */
1788
1910
  async loadScheduleAvailableDate(params) {
@@ -3333,7 +3455,9 @@ class UnifiedBookingSalesImpl extends _BookingTicket.BookingTicket {
3333
3455
  } else {
3334
3456
  const beforeProductLineUids = new Set((this.getTempOrder()?.products || []).map(line => getProductLineUid(line)).filter(Boolean));
3335
3457
  const beforeBookingUids = new Set((this.getTempOrder()?.bookings || []).map(item => getBookingUid(item)).filter(Boolean));
3336
- const products = await this.addProductToOrder(transformed, booking);
3458
+ const products = await this.addProductToOrder(transformed, booking, params.salesNotes?.length ? {
3459
+ salesNotes: params.salesNotes
3460
+ } : undefined);
3337
3461
  const addedLines = products.filter(line => sameAvailabilityId(line.product_id ?? line.id, refreshedCandidate.productId) && line.metadata?.availability_candidate_key === refreshedCandidate.key && !beforeProductLineUids.has(getProductLineUid(line)));
3338
3462
  productLineUids = addedLines.map(line => getProductLineUid(line)).filter(Boolean);
3339
3463
  const addedBookings = (this.getTempOrder()?.bookings || []).filter(item => item.metadata?.availability_candidate_key === refreshedCandidate.key && !beforeBookingUids.has(getBookingUid(item)));
@@ -1,8 +1,12 @@
1
1
  import type { InvoiceLayoutConfig, InvoiceTemplateSettings, OpenDataConfig } from '../../modules/OpenData';
2
- import type { ProductListLoadProductsParams } from '../../modules/ProductList';
2
+ import type { ProductListLoadProductsParams, ProductListStoreQueryParams } from '../../modules/ProductList';
3
+ import type { ProductData } from '../../modules/Product/types';
3
4
  import type { LoadScheduleAvailableDateParams } from '../../modules/Schedule/types';
4
5
  import type { AvailabilityDateRange, AvailabilityErrorCode, AvailabilityId, AvailabilityOccupancy, AvailabilityProduct, AvailabilityProjection, AvailabilityQuery, AvailabilityRequirementSelection, AvailabilityResource, AvailabilitySelectionValidationResult, DateAvailabilityQueryResult, GetProductTimeRangesRequest, ProductAvailabilityQueryResult, ProductTimeRange, ProductTimeRangeGroup, ResourceAvailabilityGroup, ResourceAvailabilityRangeGroup, TimeAvailabilityGroup, TimeAvailabilityItem } from '../../modules/ResourcePlanner';
5
6
  import type { BookingTicketState } from '../BookingTicket/types';
7
+ import type { AllergyRequirement } from '../../model/strategy/adapter/itemRule';
8
+ import type { SalesNoteDraftInput } from '../../modules/Order/types';
9
+ export type { ProductListStoreQueryParams } from '../../modules/ProductList';
6
10
  export type { AvailabilityAbsoluteRange, AvailabilityAssignment, AvailabilityCell, AvailabilityDateRange, AvailabilityDisplayRange, AvailabilityErrorCode, AvailabilityId, AvailabilityMode, AvailabilityOccupancy, AvailabilityProduct, AvailabilityProductKind, AvailabilityProjection, AvailabilityProjectionInput, AvailabilityProjectionMeta, AvailabilityQuery, AvailabilityQueryResult, AvailabilityRequirementGroup, AvailabilityRequirementGroupResult, AvailabilityRequirementSelection, AvailabilityResource, AvailabilityResourceOption, AvailabilityResourceType, AvailabilityResourceWindow, AvailabilityScheduleMode, AvailabilityScheduleRef, AvailabilityScheduleRule, AvailabilitySelectionValidationParams, AvailabilitySelectionValidationResult, AvailabilitySessionRange, AvailabilityStatus, DateAvailabilityGroup, DateAvailabilityItem, DateAvailabilityQuery, DateAvailabilityQueryResult, GetProductTimeRangesRequest, ProductAvailabilityGroup, ProductAvailabilityItem, ProductAvailabilityQuery, ProductAvailabilityQueryResult, ProductTimeRange, ProductTimeRangeGroup, QueryAvailabilityBaseRequest, ResourceAvailabilityGroup, ResourceAvailabilityQuery, ResourceAvailabilityQueryResult, ResourceAvailabilityRangeGroup, TimeAvailabilityGroup, TimeAvailabilityItem, TimeAvailabilityQuery, TimeAvailabilityQueryResult, } from '../../modules/ResourcePlanner';
7
11
  export interface UnifiedBookingSalesState extends BookingTicketState {
8
12
  }
@@ -24,6 +28,12 @@ export declare enum UnifiedBookingSalesHooks {
24
28
  export interface UnifiedBookingSalesOtherParams extends Record<string, any> {
25
29
  enableSessionStoragePersist?: boolean;
26
30
  }
31
+ export type UnifiedBookingSalesAllergyRequirement = AllergyRequirement;
32
+ export interface UnifiedBookingSalesResolveProductAllergyRequirementParams {
33
+ productId: number | string;
34
+ productVariantId?: number | string;
35
+ channel?: string;
36
+ }
27
37
  export interface UnifiedBookingSalesLoadOpenDataParams {
28
38
  businessCode: string;
29
39
  channel: string;
@@ -151,6 +161,8 @@ export interface ContextualCommitAvailabilitySelectionParams {
151
161
  requirementSelections: AvailabilityRequirementSelection[];
152
162
  partySize: number;
153
163
  bookingCount?: number;
164
+ /** 新增商品行时与商品、预约在同一次 Order mutation 内写入的 Sales Notes。 */
165
+ salesNotes?: SalesNoteDraftInput[];
154
166
  /** 编辑预约时,允许本次显式报价覆盖已落库订单行的历史价格。 */
155
167
  allowBookingReprice?: boolean;
156
168
  /** 编辑预约重报价后,允许订单模块重新应用已选编辑态折扣。 */
@@ -214,6 +226,7 @@ export interface UnifiedBookingSalesApi {
214
226
  /** Date-first calendar availability backed by ScheduleModule. */
215
227
  loadScheduleAvailableDate: (params: LoadScheduleAvailableDateParams) => Promise<unknown>;
216
228
  loadProductsByScheduleDate: (params: UnifiedBookingSalesLoadProductsByScheduleDateParams) => Promise<unknown[]>;
229
+ queryProducts: (params?: ProductListStoreQueryParams) => Promise<ProductData[]>;
217
230
  stageRetailProducts: (params: UnifiedBookingSalesStageRetailProductsParams) => Promise<Record<string, any> | null>;
218
231
  addRetailProduct: (params: UnifiedBookingSalesAddRetailProductParams) => Promise<Record<string, any>[]>;
219
232
  prepareAvailabilityProjection: (params: UnifiedBookingSalesPrepareAvailabilityProjectionParams) => Promise<AvailabilityContextRef>;