@pisell/pisellos 2.2.174 → 2.2.176

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.
@@ -13,7 +13,106 @@ export declare enum OrderHooks {
13
13
  OnOrderUpdate = "order:onOrderUpdate",
14
14
  OnOrderCancel = "order:onOrderCancel",
15
15
  OnOrderStatusChange = "order:onOrderStatusChange",
16
- OnOrderCustomerChange = "order:onOrderCustomerChange"
16
+ OnOrderCustomerChange = "order:onOrderCustomerChange",
17
+ OnPromotionApplied = "order:onPromotionApplied"
18
+ }
19
+ /**
20
+ * 促销评估器协议(OS 与 PromotionEvaluator 之间的桥接接口)。
21
+ *
22
+ * 与 `pisell_os/src/model/strategy/adapter/promotion/evaluator.ts` 中的
23
+ * `PromotionEvaluator` 实例结构保持兼容。SalesSdkProvider 会从 `appHelper.utils.promotionEvaluator`
24
+ * 读取实例并通过 `setPromotionEvaluator` 注入到 OrderModule。
25
+ *
26
+ * 这里只声明 OrderModule 需要调用的子集,避免反向依赖具体 evaluator 实现。
27
+ */
28
+ export interface OrderPromotionEvaluator {
29
+ evaluateCartWithPricing(input: {
30
+ products: any[];
31
+ channel?: string;
32
+ }): any;
33
+ getProductsApplicableStrategies(input: {
34
+ products: any[];
35
+ channel?: string;
36
+ }, matchVariant?: boolean): any[];
37
+ }
38
+ /** 赠品解析回调 context(OS → SDK 的请求体) */
39
+ export interface OrderGiftSelectContext {
40
+ strategyId: string;
41
+ strategyName: string | Record<string, string>;
42
+ giftCount: number;
43
+ giftOptions: Array<{
44
+ product_id: number;
45
+ product_variant_id: number;
46
+ }>;
47
+ /** 触发此赠品的主商品 metadata.unique_identification_number 列表 */
48
+ sourceProductIds: string[];
49
+ }
50
+ /**
51
+ * 赠品解析回调签名(SDK → OS 的返回值)。
52
+ *
53
+ * - 单选项:SDK 内部自动构造商品,不弹窗
54
+ * - 多选项:SDK 弹窗等用户确认后构造
55
+ * - 用户取消:抛错或返回空数组(OS 视为放弃本次添加)
56
+ *
57
+ * 返回的 OrderProduct 必须是完整的购物车行(含 metadata._giftInfo 标记),OS 会直接 push 入 tempOrder.products。
58
+ */
59
+ export type OrderGiftSelectResolver = (ctx: OrderGiftSelectContext) => Promise<Partial<OrderProduct>[] | null>;
60
+ /** 未满足的促销策略(购物车底部 alert 渲染源) */
61
+ export interface OrderUnfulfilledPromotion {
62
+ strategyId: string;
63
+ strategyName: string | Record<string, string>;
64
+ actionType: string;
65
+ needQuantity: number;
66
+ currentQuantity: number;
67
+ requiredQuantity: number;
68
+ eligibleProducts: Array<{
69
+ product_id: number;
70
+ product_variant_id: number;
71
+ }>;
72
+ strategyMetadata?: any;
73
+ display?: {
74
+ text: string | Record<string, string>;
75
+ type: string;
76
+ };
77
+ }
78
+ /** 上一次 applyPromotion 计算出的赠品操作(仅供调试/读取使用) */
79
+ export interface OrderLastGiftActions {
80
+ toAdd: Array<{
81
+ strategyId: string;
82
+ strategyName: string | Record<string, string>;
83
+ giftCount: number;
84
+ giftOptions: Array<{
85
+ product_id: number;
86
+ product_variant_id: number;
87
+ }>;
88
+ sourceProductIds: string[];
89
+ }>;
90
+ toReduce: Array<{
91
+ strategyId: string;
92
+ items: Array<{
93
+ uid: string;
94
+ currentQuantity: number;
95
+ newQuantity: number;
96
+ }>;
97
+ }>;
98
+ toRemove: string[];
99
+ }
100
+ /** onPromotionApplied 事件 payload */
101
+ export interface OrderPromotionAppliedPayload {
102
+ unfulfilledPromotions: OrderUnfulfilledPromotion[];
103
+ giftActions: OrderLastGiftActions;
104
+ /** evaluator 返回的赠品摘要 */
105
+ gifts: Array<{
106
+ strategyId: string;
107
+ strategyName: string | Record<string, string>;
108
+ giftCount: number;
109
+ giftOptions: Array<{
110
+ product_id: number;
111
+ product_variant_id: number;
112
+ }>;
113
+ }>;
114
+ /** 处理后的商品列表 */
115
+ products: OrderProduct[];
17
116
  }
18
117
  /**
19
118
  * tempOrder 上的下单客户协议字段视图。
@@ -561,6 +660,46 @@ export interface OrderModuleAPI {
561
660
  }) => Promise<Discount[]>;
562
661
  getDiscountList: () => Discount[];
563
662
  applyDiscount: () => void;
663
+ /**
664
+ * 注入促销评估器。
665
+ *
666
+ * SalesSdkProvider 在初始化 bookingTicket 后会自动从 `appHelper.utils.promotionEvaluator`
667
+ * 读取实例并调用本方法注入。host 透明,业务 UI 不感知。
668
+ *
669
+ * @example
670
+ * order.setPromotionEvaluator(appHelper.utils.promotionEvaluator);
671
+ */
672
+ setPromotionEvaluator: (evaluator: OrderPromotionEvaluator | null) => void;
673
+ /**
674
+ * 注入赠品选择 resolver。OS 在 applyPromotion 内部当需要新增赠品时会 await 调用 resolver。
675
+ *
676
+ * 缺省时 OS 会 `console.warn` 并跳过新增(既有赠品的 reduce/remove 仍会执行)。
677
+ *
678
+ * @example
679
+ * order.setGiftSelectResolver((ctx) => giftSelectBridge.request(ctx));
680
+ */
681
+ setGiftSelectResolver: (resolver: OrderGiftSelectResolver | null) => void;
682
+ /**
683
+ * 为商品目录追加促销标签。
684
+ *
685
+ * @example
686
+ * const taggedProducts = order.appendPromotionTags(products);
687
+ */
688
+ appendPromotionTags: <T extends Record<string, any>>(products: T[]) => T[];
689
+ /**
690
+ * 应用购物车促销(评估 → 差异化赠品 → 写回 tempOrder.products → emit onPromotionApplied)。
691
+ *
692
+ * 自动在 addProductToOrder / updateOrderProductQuantity / removeProductFromOrder /
693
+ * clearOrderCartLines / setOrderCustomer 等写路径末尾触发,业务一般无需手动调用。
694
+ *
695
+ * 多选项赠品依赖 giftSelectResolver;如未注入则跳过且 console.warn。
696
+ * 内部以 `_isApplyingPromotion` flag 防递归。
697
+ */
698
+ applyPromotion: () => Promise<void>;
699
+ /** 读取上次 applyPromotion 产出的未满足促销提示 */
700
+ getUnfulfilledPromotions: () => OrderUnfulfilledPromotion[];
701
+ /** 读取上次 applyPromotion 产出的赠品操作 diff(toAdd / toReduce / toRemove) */
702
+ getLastGiftActions: () => OrderLastGiftActions | null;
564
703
  /**
565
704
  * 取消订单
566
705
  * @param params 取消订单参数
@@ -4,9 +4,38 @@ export var OrderHooks = /*#__PURE__*/function (OrderHooks) {
4
4
  OrderHooks["OnOrderCancel"] = "order:onOrderCancel";
5
5
  OrderHooks["OnOrderStatusChange"] = "order:onOrderStatusChange";
6
6
  OrderHooks["OnOrderCustomerChange"] = "order:onOrderCustomerChange";
7
+ OrderHooks["OnPromotionApplied"] = "order:onPromotionApplied";
7
8
  return OrderHooks;
8
9
  }({});
9
10
 
11
+ /**
12
+ * 促销评估器协议(OS 与 PromotionEvaluator 之间的桥接接口)。
13
+ *
14
+ * 与 `pisell_os/src/model/strategy/adapter/promotion/evaluator.ts` 中的
15
+ * `PromotionEvaluator` 实例结构保持兼容。SalesSdkProvider 会从 `appHelper.utils.promotionEvaluator`
16
+ * 读取实例并通过 `setPromotionEvaluator` 注入到 OrderModule。
17
+ *
18
+ * 这里只声明 OrderModule 需要调用的子集,避免反向依赖具体 evaluator 实现。
19
+ */
20
+
21
+ /** 赠品解析回调 context(OS → SDK 的请求体) */
22
+
23
+ /**
24
+ * 赠品解析回调签名(SDK → OS 的返回值)。
25
+ *
26
+ * - 单选项:SDK 内部自动构造商品,不弹窗
27
+ * - 多选项:SDK 弹窗等用户确认后构造
28
+ * - 用户取消:抛错或返回空数组(OS 视为放弃本次添加)
29
+ *
30
+ * 返回的 OrderProduct 必须是完整的购物车行(含 metadata._giftInfo 标记),OS 会直接 push 入 tempOrder.products。
31
+ */
32
+
33
+ /** 未满足的促销策略(购物车底部 alert 渲染源) */
34
+
35
+ /** 上一次 applyPromotion 计算出的赠品操作(仅供调试/读取使用) */
36
+
37
+ /** onPromotionApplied 事件 payload */
38
+
10
39
  /**
11
40
  * tempOrder 上的下单客户协议字段视图。
12
41
  * 这些字段会随订单提交到后端。
@@ -85,6 +85,17 @@ export declare function resolveManualOverrideLineOriginalPrice(params: {
85
85
  /** 已有行级 original_price(origin 被裁剪时兜底划线价) */
86
86
  lineOriginalPrice?: string | number | null;
87
87
  }, fallbackCompositeOriginal: string): string;
88
+ /**
89
+ * 计算商品行「每单位有效折扣」:discount_list 合计 + 被 Rules 剥离但仍 inPromotion 的差额。
90
+ *
91
+ * Rules.calcDiscount 会把 type=promotion 从 discount_list 去掉,但 metadata._promotion 仍保留;
92
+ * applyProductDiscountPrices 必须回读 _promotion,否则抬价/降价都会在第二步被 source 原价覆盖。
93
+ *
94
+ * @example
95
+ * resolveEffectivePerUnitDiscount({ metadata: { _promotion: { inPromotion: true, originalPrice: 1, finalPrice: 5 } }, discount_list: [] });
96
+ * // => -4
97
+ */
98
+ export declare function resolveEffectivePerUnitDiscount(product: Record<string, any>): number;
88
99
  export declare function createDefaultOrderRulesHooks(): RulesParamsHooks;
89
100
  /**
90
101
  * 通过 session 类商品的开始时间结束时间生成商品的时长
@@ -197,6 +197,44 @@ export function resolveManualOverrideLineOriginalPrice(params, fallbackComposite
197
197
  }
198
198
  return fallbackCompositeOriginal;
199
199
  }
200
+
201
+ /**
202
+ * 计算商品行「每单位有效折扣」:discount_list 合计 + 被 Rules 剥离但仍 inPromotion 的差额。
203
+ *
204
+ * Rules.calcDiscount 会把 type=promotion 从 discount_list 去掉,但 metadata._promotion 仍保留;
205
+ * applyProductDiscountPrices 必须回读 _promotion,否则抬价/降价都会在第二步被 source 原价覆盖。
206
+ *
207
+ * @example
208
+ * resolveEffectivePerUnitDiscount({ metadata: { _promotion: { inPromotion: true, originalPrice: 1, finalPrice: 5 } }, discount_list: [] });
209
+ * // => -4
210
+ */
211
+ export function resolveEffectivePerUnitDiscount(product) {
212
+ var discountList = Array.isArray(product === null || product === void 0 ? void 0 : product.discount_list) ? product.discount_list : [];
213
+ var fromList = discountList.reduce(function (sum, discount) {
214
+ return sum + Number((discount === null || discount === void 0 ? void 0 : discount.amount) || 0);
215
+ }, 0);
216
+ var hasPromoDiscountItem = discountList.some(function (item) {
217
+ return (item === null || item === void 0 ? void 0 : item.type) === 'promotion';
218
+ });
219
+ if (hasPromoDiscountItem) return fromList;
220
+ var metadata = (product === null || product === void 0 ? void 0 : product.metadata) || {};
221
+ var optionSum = sumOptionUnitPrice(product === null || product === void 0 ? void 0 : product.product_option_item).toNumber();
222
+
223
+ // applyPromotion 写价后、Rules 剥离 promotion discount 的兜底
224
+ if (metadata.source_product_price != null && metadata.main_product_selling_price != null) {
225
+ var source = Number(metadata.source_product_price);
226
+ var selling = Number(metadata.main_product_selling_price) - optionSum;
227
+ if (Number.isFinite(source) && Number.isFinite(selling) && source !== selling) {
228
+ return fromList + (source - selling);
229
+ }
230
+ }
231
+ var promo = metadata._promotion;
232
+ if ((promo === null || promo === void 0 ? void 0 : promo.inPromotion) !== true) return fromList;
233
+ var originalPrice = Number(promo.originalPrice);
234
+ var finalPrice = Number(promo.finalPrice);
235
+ if (!Number.isFinite(originalPrice) || !Number.isFinite(finalPrice)) return fromList;
236
+ return fromList + (originalPrice - finalPrice);
237
+ }
200
238
  export function createDefaultOrderRulesHooks() {
201
239
  var toUnitPriceString = function toUnitPriceString(totalLike, num) {
202
240
  var effectiveNum = Number(num) > 0 ? Number(num) : 1;
@@ -204,7 +242,7 @@ export function createDefaultOrderRulesHooks() {
204
242
  };
205
243
  return {
206
244
  getProduct: function getProduct(product) {
207
- var _product$metadata, _product$metadata2, _product$metadata3, _product$metadata4, _product$metadata5;
245
+ var _product$metadata, _product$metadata2, _product$metadata3, _product$metadata4, _product$metadata5, _metadataAny$_promoti;
208
246
  // source_product_price 是权威源;兜底链:source → mainSelling − options(反推)→ 行 selling_price
209
247
  var metadataAny = product.metadata || {};
210
248
  var optionSum = sumOptionUnitPrice(product.product_option_item);
@@ -249,7 +287,10 @@ export function createDefaultOrderRulesHooks() {
249
287
  // 无券时会把 bundle 价还原成 original_price(手动改价套餐会从 20 回到 30)。
250
288
  isManualDiscount: resolveRulesManualDiscountFlag(metadataAny),
251
289
  holder_id: (_product$metadata3 = product.metadata) === null || _product$metadata3 === void 0 ? void 0 : _product$metadata3.holder_id,
252
- startDate: ((_product$metadata4 = product.metadata) === null || _product$metadata4 === void 0 ? void 0 : _product$metadata4.start_date) || ((_product$metadata5 = product.metadata) === null || _product$metadata5 === void 0 ? void 0 : _product$metadata5.startDate)
290
+ startDate: ((_product$metadata4 = product.metadata) === null || _product$metadata4 === void 0 ? void 0 : _product$metadata4.start_date) || ((_product$metadata5 = product.metadata) === null || _product$metadata5 === void 0 ? void 0 : _product$metadata5.startDate),
291
+ main_product_selling_price: metadataAny.main_product_selling_price !== undefined ? new Decimal(Number(metadataAny.main_product_selling_price) || 0).minus(optionSum).toDecimalPlaces(2).toString() : undefined,
292
+ inPromotion: ((_metadataAny$_promoti = metadataAny._promotion) === null || _metadataAny$_promoti === void 0 ? void 0 : _metadataAny$_promoti.inPromotion) === true,
293
+ _promotion: metadataAny._promotion
253
294
  };
254
295
  },
255
296
  setProduct: function setProduct(product, values) {
@@ -458,8 +458,8 @@ export function calculateSalesSummary(params) {
458
458
  }, new Decimal(0));
459
459
  }
460
460
  var shopDiscountAmount = Decimal.max(new Decimal(Number(shopDiscount) || 0), 0);
461
- var expectAmount = Decimal.max(0, productAmount.plus(surchargeAmount).minus(shopDiscountAmount));
462
- var totalAmount = isPriceIncludeTax === 1 ? expectAmount : expectAmount.plus(productTaxFee);
461
+ var preTaxExpectAmount = Decimal.max(0, productAmount.plus(surchargeAmount).minus(shopDiscountAmount));
462
+ var totalAmount = isPriceIncludeTax === 1 ? preTaxExpectAmount : preTaxExpectAmount.plus(productTaxFee);
463
463
  var deposit = calculateProductsDeposit(products);
464
464
  return {
465
465
  product_quantity: productQuantity,
@@ -475,7 +475,7 @@ export function calculateSalesSummary(params) {
475
475
  discount_amount: toFixed2(Decimal.max(productOriginalAmount.minus(productAmount), 0).plus(shopDiscountAmount)),
476
476
  deposit_amount: deposit ? deposit.total : '0.00',
477
477
  deposit: deposit,
478
- expect_amount: toFixed2(expectAmount),
478
+ expect_amount: toFixed2(totalAmount),
479
479
  total_amount: toFixed2(totalAmount),
480
480
  total_refund_amount: '0.00',
481
481
  customer_paid_amount: '0.00',
@@ -168,16 +168,17 @@ var Server = /*#__PURE__*/function () {
168
168
  */
169
169
  _defineProperty(this, "handleProductQuery", /*#__PURE__*/function () {
170
170
  var _ref2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee(_ref) {
171
- var url, method, data, config, menu_list_ids, schedule_datetime, schedule_date, customer_id, _ref3, callback, subscriberId;
171
+ var url, method, data, config, menu_list_ids, schedule_datetime, schedule_date, customer_id, ids, _ref3, callback, subscriberId;
172
172
  return _regeneratorRuntime().wrap(function _callee$(_context) {
173
173
  while (1) switch (_context.prev = _context.next) {
174
174
  case 0:
175
175
  url = _ref.url, method = _ref.method, data = _ref.data, config = _ref.config;
176
176
  console.log('[Server] handleProductQuery:', url, method, data, config);
177
- menu_list_ids = data.menu_list_ids, schedule_datetime = data.schedule_datetime, schedule_date = data.schedule_date, customer_id = data.customer_id;
177
+ menu_list_ids = data.menu_list_ids, schedule_datetime = data.schedule_datetime, schedule_date = data.schedule_date, customer_id = data.customer_id, ids = data.ids;
178
178
  _ref3 = config || {}, callback = _ref3.callback, subscriberId = _ref3.subscriberId;
179
179
  _this.logInfo('handleProductQuery: 开始处理商品查询请求', {
180
180
  menu_list_ids: menu_list_ids,
181
+ ids: ids,
181
182
  schedule_datetime: schedule_datetime,
182
183
  schedule_date: schedule_date,
183
184
  customer_id: customer_id
@@ -189,6 +190,7 @@ var Server = /*#__PURE__*/function () {
189
190
  callback: callback,
190
191
  context: {
191
192
  menu_list_ids: menu_list_ids,
193
+ ids: ids,
192
194
  schedule_date: schedule_date,
193
195
  schedule_datetime: schedule_datetime,
194
196
  customer_id: customer_id
@@ -201,6 +203,7 @@ var Server = /*#__PURE__*/function () {
201
203
  }
202
204
  return _context.abrupt("return", _this.computeProductQueryResult({
203
205
  menu_list_ids: menu_list_ids,
206
+ ids: ids,
204
207
  schedule_date: schedule_date,
205
208
  schedule_datetime: schedule_datetime,
206
209
  customer_id: customer_id
@@ -3307,14 +3310,20 @@ var Server = /*#__PURE__*/function () {
3307
3310
  var _computeProductQueryResult = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee29(context, options) {
3308
3311
  var _menu_list_ids$length3,
3309
3312
  _this6 = this;
3310
- var tTotal, menu_list_ids, schedule_date, schedule_datetime, customer_id, product_id, _published$find, pid, _tPrice, allProductsWithPrice, published, item, activeMenuList, tMenu, menuList, tFilter, productScope, scopedProductIds, tPrice, filteredProducts, tStatus, beforeStatusCount, tSort;
3313
+ var tTotal, menu_list_ids, ids, schedule_date, schedule_datetime, customer_id, product_id, queryIds, uniqueIds, _tPrice, productsWithPrice, _filteredProducts, _published$find, pid, _tPrice2, allProductsWithPrice, published, item, activeMenuList, tMenu, menuList, tFilter, productScope, scopedProductIds, tPrice, filteredProducts, tStatus, beforeStatusCount, tSort;
3311
3314
  return _regeneratorRuntime().wrap(function _callee29$(_context29) {
3312
3315
  while (1) switch (_context29.prev = _context29.next) {
3313
3316
  case 0:
3314
3317
  tTotal = performance.now();
3315
- menu_list_ids = context.menu_list_ids, schedule_date = context.schedule_date, schedule_datetime = context.schedule_datetime, customer_id = context.customer_id, product_id = context.product_id;
3318
+ menu_list_ids = context.menu_list_ids, ids = context.ids, schedule_date = context.schedule_date, schedule_datetime = context.schedule_datetime, customer_id = context.customer_id, product_id = context.product_id;
3319
+ queryIds = Array.isArray(ids) ? ids.map(function (id) {
3320
+ return Number(id);
3321
+ }).filter(function (id) {
3322
+ return Number.isFinite(id);
3323
+ }) : [];
3316
3324
  this.logInfo('computeProductQueryResult 开始', {
3317
3325
  menuListIdsCount: (_menu_list_ids$length3 = menu_list_ids === null || menu_list_ids === void 0 ? void 0 : menu_list_ids.length) !== null && _menu_list_ids$length3 !== void 0 ? _menu_list_ids$length3 : 0,
3326
+ ids: queryIds,
3318
3327
  schedule_datetime: schedule_datetime,
3319
3328
  schedule_date: schedule_date,
3320
3329
  customer_id: customer_id,
@@ -3322,7 +3331,7 @@ var Server = /*#__PURE__*/function () {
3322
3331
  changedIds: options === null || options === void 0 ? void 0 : options.changedIds
3323
3332
  });
3324
3333
  if (this.products) {
3325
- _context29.next = 6;
3334
+ _context29.next = 7;
3326
3335
  break;
3327
3336
  }
3328
3337
  this.logError('computeProductQueryResult: Products 模块未注册');
@@ -3333,22 +3342,67 @@ var Server = /*#__PURE__*/function () {
3333
3342
  count: 0
3334
3343
  }
3335
3344
  });
3336
- case 6:
3337
- if (!(product_id != null && Number.isFinite(Number(product_id)))) {
3345
+ case 7:
3346
+ if (!(queryIds.length > 0)) {
3338
3347
  _context29.next = 18;
3339
3348
  break;
3340
3349
  }
3341
- pid = Number(product_id);
3350
+ uniqueIds = Array.from(new Set(queryIds));
3342
3351
  _tPrice = performance.now();
3343
- _context29.next = 11;
3352
+ _context29.next = 12;
3353
+ return this.products.getProductsWithPrice(schedule_date, {
3354
+ scheduleModule: this.getSchedule(),
3355
+ schedule_datetime: schedule_datetime,
3356
+ customer_id: customer_id
3357
+ }, {
3358
+ changedIds: options === null || options === void 0 ? void 0 : options.changedIds,
3359
+ productIds: uniqueIds
3360
+ });
3361
+ case 12:
3362
+ productsWithPrice = _context29.sent;
3363
+ perfMark('computeQuery.getProductsWithPrice(ids)', performance.now() - _tPrice, {
3364
+ count: productsWithPrice.length,
3365
+ ids: uniqueIds
3366
+ });
3367
+ _filteredProducts = productsWithPrice.filter(function (p) {
3368
+ return uniqueIds.includes(Number(p === null || p === void 0 ? void 0 : p.id));
3369
+ }).filter(function (p) {
3370
+ return ((p === null || p === void 0 ? void 0 : p.status) || 'published') === 'published';
3371
+ });
3372
+ perfMark('computeProductQueryResult', performance.now() - tTotal, {
3373
+ mode: 'ids',
3374
+ ids: uniqueIds,
3375
+ count: _filteredProducts.length
3376
+ });
3377
+ this.logInfo('computeProductQueryResult 完成(ids)', {
3378
+ ids: uniqueIds,
3379
+ count: _filteredProducts.length
3380
+ });
3381
+ return _context29.abrupt("return", {
3382
+ code: 200,
3383
+ data: {
3384
+ list: _filteredProducts,
3385
+ count: _filteredProducts.length
3386
+ },
3387
+ message: '',
3388
+ status: true
3389
+ });
3390
+ case 18:
3391
+ if (!(product_id != null && Number.isFinite(Number(product_id)))) {
3392
+ _context29.next = 30;
3393
+ break;
3394
+ }
3395
+ pid = Number(product_id);
3396
+ _tPrice2 = performance.now();
3397
+ _context29.next = 23;
3344
3398
  return this.products.getProductsWithPrice(schedule_date, {
3345
3399
  scheduleModule: this.getSchedule()
3346
3400
  }, {
3347
3401
  changedIds: options === null || options === void 0 ? void 0 : options.changedIds
3348
3402
  });
3349
- case 11:
3403
+ case 23:
3350
3404
  allProductsWithPrice = _context29.sent;
3351
- perfMark('computeQuery.getProductsWithPrice(single)', performance.now() - _tPrice, {
3405
+ perfMark('computeQuery.getProductsWithPrice(single)', performance.now() - _tPrice2, {
3352
3406
  count: allProductsWithPrice.length,
3353
3407
  productId: pid
3354
3408
  });
@@ -3373,9 +3427,9 @@ var Server = /*#__PURE__*/function () {
3373
3427
  message: item ? '' : '商品不存在或未发布',
3374
3428
  status: true
3375
3429
  });
3376
- case 18:
3430
+ case 30:
3377
3431
  if (this.menu) {
3378
- _context29.next = 21;
3432
+ _context29.next = 33;
3379
3433
  break;
3380
3434
  }
3381
3435
  this.logError('computeProductQueryResult: Menu 模块未注册');
@@ -3386,9 +3440,9 @@ var Server = /*#__PURE__*/function () {
3386
3440
  count: 0
3387
3441
  }
3388
3442
  });
3389
- case 21:
3443
+ case 33:
3390
3444
  if (this.schedule) {
3391
- _context29.next = 24;
3445
+ _context29.next = 36;
3392
3446
  break;
3393
3447
  }
3394
3448
  this.logError('computeProductQueryResult: Schedule 模块未注册');
@@ -3399,7 +3453,7 @@ var Server = /*#__PURE__*/function () {
3399
3453
  count: 0
3400
3454
  }
3401
3455
  });
3402
- case 24:
3456
+ case 36:
3403
3457
  activeMenuList = [];
3404
3458
  if (menu_list_ids && Array.isArray(menu_list_ids) && menu_list_ids.length > 0) {
3405
3459
  tMenu = performance.now();
@@ -3423,10 +3477,10 @@ var Server = /*#__PURE__*/function () {
3423
3477
  });
3424
3478
  tPrice = performance.now();
3425
3479
  if (!(scopedProductIds.length > 0)) {
3426
- _context29.next = 37;
3480
+ _context29.next = 49;
3427
3481
  break;
3428
3482
  }
3429
- _context29.next = 34;
3483
+ _context29.next = 46;
3430
3484
  return this.products.getProductsWithPrice(schedule_date, {
3431
3485
  scheduleModule: this.getSchedule(),
3432
3486
  schedule_datetime: schedule_datetime,
@@ -3435,13 +3489,13 @@ var Server = /*#__PURE__*/function () {
3435
3489
  changedIds: options === null || options === void 0 ? void 0 : options.changedIds,
3436
3490
  productIds: productScope.isAllProducts ? undefined : scopedProductIds
3437
3491
  });
3438
- case 34:
3492
+ case 46:
3439
3493
  _context29.t0 = _context29.sent;
3440
- _context29.next = 38;
3494
+ _context29.next = 50;
3441
3495
  break;
3442
- case 37:
3496
+ case 49:
3443
3497
  _context29.t0 = [];
3444
- case 38:
3498
+ case 50:
3445
3499
  filteredProducts = _context29.t0;
3446
3500
  perfMark('computeQuery.getProductsWithPrice', performance.now() - tPrice, {
3447
3501
  count: filteredProducts.length,
@@ -3484,7 +3538,7 @@ var Server = /*#__PURE__*/function () {
3484
3538
  message: '',
3485
3539
  status: true
3486
3540
  });
3487
- case 50:
3541
+ case 62:
3488
3542
  case "end":
3489
3543
  return _context29.stop();
3490
3544
  }
@@ -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, BaseSalesProductBookingPriceResult, BaseSalesScanCodeResult } from './types';
4
4
  import { OrderModule } from '../../modules/Order';
5
- import type { AddProductBookingInput, LoadSalesDetailParams, OrderPaymentData, OrderPaymentSource, SendCustomerPayLinkParams, SyncPaymentsToOrderParams, SyncPaymentsToOrderResult, UpdateTempOrderCustomerInput, UpdateOrderBookingParams, UpdateOrderProductParams, OrderTempOrder } from '../../modules/Order/types';
5
+ import type { AddProductBookingInput, LoadSalesDetailParams, OrderPaymentData, OrderPaymentSource, SendCustomerPayLinkParams, SyncPaymentsToOrderParams, SyncPaymentsToOrderResult, UpdateTempOrderCustomerInput, UpdateOrderBookingParams, UpdateOrderProductParams, OrderTempOrder, OrderPromotionEvaluator, OrderGiftSelectResolver, OrderUnfulfilledPromotion, OrderLastGiftActions } 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';
@@ -63,6 +63,13 @@ export declare class BaseSalesImpl extends BaseModule implements Module {
63
63
  private getCurrentOrderCustomerId;
64
64
  private applyQuotationScheduleResolver;
65
65
  private loadQuotationForPriceQuery;
66
+ private getPriceQueryProductId;
67
+ private getPriceQuerySchedule;
68
+ private loadProductForPriceQuery;
69
+ private getAuthoritativeBundleItems;
70
+ private findAuthoritativeBundleItem;
71
+ private getAuthoritativeBundleUnitPrice;
72
+ private mergeProductForPriceQuery;
66
73
  protected getSubmitOrderSalesChannel(): string | undefined;
67
74
  /**
68
75
  * 工厂入口:根据子模块名实例化对应模块。
@@ -237,6 +244,35 @@ export declare class BaseSalesImpl extends BaseModule implements Module {
237
244
  */
238
245
  setOrderProductLineNote(identity: BaseSalesOrderProductIdentity, note: string): Promise<import("../../modules/Order/types").OrderProduct[]>;
239
246
  removeProductFromOrder(identity: BaseSalesOrderProductIdentity): Promise<import("../../modules/Order/types").OrderProduct[]>;
247
+ /**
248
+ * 注入促销评估器到底层 OrderModule。
249
+ *
250
+ * @example
251
+ * solution.setPromotionEvaluator(appHelper.utils.promotionEvaluator);
252
+ */
253
+ setPromotionEvaluator(evaluator: OrderPromotionEvaluator | null): void;
254
+ /**
255
+ * 注入赠品选择 resolver。UI 层(SalesSdk)通过 bridge 提供。
256
+ *
257
+ * @example
258
+ * solution.setGiftSelectResolver(async (ctx) => bridge.request(ctx));
259
+ */
260
+ setGiftSelectResolver(resolver: OrderGiftSelectResolver | null): void;
261
+ /**
262
+ * 为商品目录追加促销标签,供 SalesSdkProductProvider 等商品列表入口复用。
263
+ *
264
+ * @example
265
+ * const products = solution.appendPromotionTags(catalogProducts);
266
+ */
267
+ appendPromotionTags<T extends Record<string, any>>(products: T[]): T[];
268
+ /**
269
+ * 主动触发一次促销应用(一般写路径会自动触发,少数场景如客户切换后可手动调)。
270
+ */
271
+ applyPromotion(): Promise<void>;
272
+ /** 最近一次促销计算输出的未满足提示。 */
273
+ getUnfulfilledPromotions(): OrderUnfulfilledPromotion[];
274
+ /** 最近一次促销计算输出的赠品操作 diff。 */
275
+ getLastGiftActions(): OrderLastGiftActions | null;
240
276
  getProductList(): Promise<any>;
241
277
  getOtherParams(): Record<string, any>;
242
278
  setOtherParams(params: Record<string, any>, { cover }?: {