@pisell/pisellos 0.0.499 → 0.0.501

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 (58) hide show
  1. package/dist/model/strategy/adapter/itemRule/adapter.d.ts +8 -0
  2. package/dist/model/strategy/adapter/itemRule/adapter.js +52 -8
  3. package/dist/model/strategy/adapter/itemRule/examples.d.ts +15 -0
  4. package/dist/model/strategy/adapter/itemRule/examples.js +68 -1
  5. package/dist/model/strategy/adapter/itemRule/index.d.ts +1 -1
  6. package/dist/model/strategy/adapter/itemRule/index.js +1 -1
  7. package/dist/model/strategy/adapter/itemRule/type.d.ts +20 -1
  8. package/dist/model/strategy/adapter/promotion/index.js +9 -0
  9. package/dist/modules/Order/index.d.ts +6 -0
  10. package/dist/modules/Order/index.js +60 -40
  11. package/dist/modules/Order/types.d.ts +4 -0
  12. package/dist/modules/Order/utils.d.ts +31 -0
  13. package/dist/modules/Order/utils.js +118 -13
  14. package/dist/modules/ProductList/index.js +2 -2
  15. package/dist/modules/Quotation/index.js +6 -3
  16. package/dist/modules/SalesSummary/types.d.ts +2 -1
  17. package/dist/modules/SalesSummary/utils.js +10 -10
  18. package/dist/modules/Schedule/utils.d.ts +1 -1
  19. package/dist/solution/BookingByStep/index.d.ts +2 -2
  20. package/dist/solution/ScanOrder/index.d.ts +7 -0
  21. package/dist/solution/ScanOrder/index.js +284 -67
  22. package/dist/solution/ScanOrder/types.d.ts +22 -5
  23. package/dist/solution/ScanOrder/utils.d.ts +26 -0
  24. package/dist/solution/ScanOrder/utils.js +191 -44
  25. package/dist/solution/VenueBooking/index.d.ts +14 -0
  26. package/dist/solution/VenueBooking/index.js +318 -144
  27. package/dist/solution/VenueBooking/utils/dateSummary.d.ts +1 -0
  28. package/dist/solution/VenueBooking/utils/dateSummary.js +6 -4
  29. package/dist/solution/VenueBooking/utils/slotMerge.js +1 -2
  30. package/lib/model/strategy/adapter/itemRule/adapter.d.ts +8 -0
  31. package/lib/model/strategy/adapter/itemRule/adapter.js +41 -2
  32. package/lib/model/strategy/adapter/itemRule/examples.d.ts +15 -0
  33. package/lib/model/strategy/adapter/itemRule/examples.js +47 -0
  34. package/lib/model/strategy/adapter/itemRule/index.d.ts +1 -1
  35. package/lib/model/strategy/adapter/itemRule/index.js +2 -0
  36. package/lib/model/strategy/adapter/itemRule/type.d.ts +20 -1
  37. package/lib/modules/Order/index.d.ts +6 -0
  38. package/lib/modules/Order/index.js +35 -37
  39. package/lib/modules/Order/types.d.ts +4 -0
  40. package/lib/modules/Order/utils.d.ts +31 -0
  41. package/lib/modules/Order/utils.js +97 -9
  42. package/lib/modules/ProductList/index.js +3 -2
  43. package/lib/modules/Quotation/index.js +3 -0
  44. package/lib/modules/SalesSummary/types.d.ts +2 -1
  45. package/lib/modules/SalesSummary/utils.js +2 -2
  46. package/lib/modules/Schedule/utils.d.ts +1 -1
  47. package/lib/solution/BookingByStep/index.d.ts +2 -2
  48. package/lib/solution/ScanOrder/index.d.ts +7 -0
  49. package/lib/solution/ScanOrder/index.js +162 -18
  50. package/lib/solution/ScanOrder/types.d.ts +22 -5
  51. package/lib/solution/ScanOrder/utils.d.ts +26 -0
  52. package/lib/solution/ScanOrder/utils.js +128 -19
  53. package/lib/solution/VenueBooking/index.d.ts +14 -0
  54. package/lib/solution/VenueBooking/index.js +156 -30
  55. package/lib/solution/VenueBooking/utils/dateSummary.d.ts +1 -0
  56. package/lib/solution/VenueBooking/utils/dateSummary.js +13 -4
  57. package/lib/solution/VenueBooking/utils/slotMerge.js +1 -2
  58. package/package.json +1 -1
@@ -7,4 +7,5 @@ export declare function buildDateRangeSummary(params: {
7
7
  rawResources: VenueResourceRawData[];
8
8
  resourceProductMap: Map<number | string, ResourceProductMapping>;
9
9
  quotationModule?: QuotationModule;
10
+ resolveConfig?: (date: string) => VenueBookingSlotConfig;
10
11
  }): VenueDateSummaryItem[];
@@ -13,7 +13,8 @@ export function buildDateRangeSummary(params) {
13
13
  config = params.config,
14
14
  rawResources = params.rawResources,
15
15
  resourceProductMap = params.resourceProductMap,
16
- quotationModule = params.quotationModule;
16
+ quotationModule = params.quotationModule,
17
+ resolveConfig = params.resolveConfig;
17
18
  var result = [];
18
19
  var productIds = quotationModule ? _toConsumableArray(new Set(_toConsumableArray(resourceProductMap.values()).map(function (m) {
19
20
  return m.productId;
@@ -22,9 +23,10 @@ export function buildDateRangeSummary(params) {
22
23
  var end = dayjs(endDate);
23
24
  var _loop = function _loop() {
24
25
  var date = cursor.format('YYYY-MM-DD');
26
+ var effectiveConfig = (resolveConfig === null || resolveConfig === void 0 ? void 0 : resolveConfig(date)) || config;
25
27
  var quotationPriceMap;
26
28
  if (quotationModule && productIds.length) {
27
- var timeLabels = generateTimeLabels(config);
29
+ var timeLabels = generateTimeLabels(effectiveConfig);
28
30
  var timePoints = timeLabels.map(function (label) {
29
31
  return "".concat(date, " ").concat(label);
30
32
  });
@@ -35,7 +37,7 @@ export function buildDateRangeSummary(params) {
35
37
  }
36
38
  var grid = buildTimeSlotGrid({
37
39
  date: date,
38
- config: config,
40
+ config: effectiveConfig,
39
41
  rawResources: rawResources,
40
42
  resourceProductMap: resourceProductMap,
41
43
  quotationPriceMap: quotationPriceMap
@@ -81,7 +83,7 @@ export function buildDateRangeSummary(params) {
81
83
  status = 'unavailable';
82
84
  } else if (availableSlots === 0) {
83
85
  status = 'sold_out';
84
- } else if ((totalSlots - availableSlots) / totalSlots >= config.fewLeftThreshold) {
86
+ } else if ((totalSlots - availableSlots) / totalSlots >= effectiveConfig.fewLeftThreshold) {
85
87
  status = 'few_left';
86
88
  } else {
87
89
  status = 'available';
@@ -131,8 +131,7 @@ export function buildVenueBookingEntry(params) {
131
131
  metadata: {
132
132
  unique_identification_number: bookingUuid,
133
133
  venue_booking: true,
134
- resource_id: resourceId,
135
- slot_count: group.slotCount
134
+ resource_id: resourceId
136
135
  }
137
136
  };
138
137
  }
@@ -50,6 +50,14 @@ export declare class ItemRuleAdapter implements BusinessAdapter {
50
50
  * 不适用或无配置时返回 null。
51
51
  */
52
52
  extractQuantityLimits(action: ActionEffect, businessData: ItemRuleBusinessData): QuantityLimitResult | null;
53
+ /**
54
+ * 计算当前提交剩余可选的最大数量。
55
+ *
56
+ * 仅在 scope='cumulative' 且存在 requiredMax 时生效:
57
+ * 用 requiredMax 减去 historicalItems 中目标商品的累计数量,保底 0。
58
+ * 其余场景返回 undefined,保持向后兼容。
59
+ */
60
+ private calculateRemainingMax;
53
61
  private processPrefillCart;
54
62
  /**
55
63
  * 根据 quantityFrom 计算预填数量
@@ -250,7 +250,7 @@ var ItemRuleAdapter = class {
250
250
  const config = action.config;
251
251
  if (!config)
252
252
  return null;
253
- const { scope, quantityRules, targetType, targets } = config;
253
+ const { scope, quantityRules, targetType, targets, validationMessage } = config;
254
254
  if (!this.shouldEvaluateForScope(scope, businessData.submissionIndex ?? 0)) {
255
255
  return null;
256
256
  }
@@ -283,17 +283,56 @@ var ItemRuleAdapter = class {
283
283
  }
284
284
  }
285
285
  }
286
+ const remainingMax = this.calculateRemainingMax(
287
+ scope,
288
+ requiredMax,
289
+ targets,
290
+ businessData
291
+ );
292
+ const formattedValidationMessage = this.formatValidationMessage(
293
+ validationMessage,
294
+ requiredMin,
295
+ requiredMax,
296
+ requiredExact
297
+ );
286
298
  return {
287
299
  ruleId: action.id,
288
300
  targetType,
289
301
  targets,
290
302
  requiredMin,
291
303
  requiredMax,
304
+ remainingMax,
292
305
  requiredExact,
293
306
  mustInclude,
294
- scope
307
+ scope,
308
+ validationMessage: formattedValidationMessage,
309
+ rawValidationMessage: validationMessage
295
310
  };
296
311
  }
312
+ /**
313
+ * 计算当前提交剩余可选的最大数量。
314
+ *
315
+ * 仅在 scope='cumulative' 且存在 requiredMax 时生效:
316
+ * 用 requiredMax 减去 historicalItems 中目标商品的累计数量,保底 0。
317
+ * 其余场景返回 undefined,保持向后兼容。
318
+ */
319
+ calculateRemainingMax(scope, requiredMax, targets, businessData) {
320
+ if (scope !== "cumulative" || typeof requiredMax !== "number") {
321
+ return void 0;
322
+ }
323
+ const historicalItems = businessData.historicalItems;
324
+ if (!(historicalItems == null ? void 0 : historicalItems.length)) {
325
+ return Math.max(0, requiredMax);
326
+ }
327
+ const targetIds = new Set(targets.map((t) => t.product_id));
328
+ let historicalQty = 0;
329
+ for (const item of historicalItems) {
330
+ if (targetIds.has(item.product_id)) {
331
+ historicalQty += item.quantity;
332
+ }
333
+ }
334
+ return Math.max(0, requiredMax - historicalQty);
335
+ }
297
336
  // ============================================
298
337
  // Prefill Cart 处理
299
338
  // ============================================
@@ -25,6 +25,21 @@ export declare const MAX_BUNS_PER_TABLE_STRATEGY: StrategyConfig;
25
25
  * - Prefill Cart: 自动添加默认锅底 x 1
26
26
  */
27
27
  export declare const HOTPOT_BASE_REQUIRED_STRATEGY: StrategyConfig;
28
+ /**
29
+ * 场景:4 款锅底里任选至少 1 款,不自动预填,不限制多选上限
30
+ *
31
+ * WHEN: Always active
32
+ * THEN:
33
+ * - Quantity Check: 4 个锅底商品合计数量 >= 1 (OR 组语义)
34
+ *
35
+ * 说明:
36
+ * - targets 里放多个 product_id 时,ItemRuleAdapter 会把它们视为 OR 组,
37
+ * 仅校验「合计数量」,不会要求每款各自满足 min。
38
+ * - ScanOrder 工具层 buildQuantityLimitIndex 会跳过多目标规则的
39
+ * 单品索引,避免 UI 把组级 min 误当作每张商品卡的减号下限,
40
+ * 锁死用户切换锅底的操作。
41
+ */
42
+ export declare const HOTPOT_BASE_PICK_ONE_STRATEGY: StrategyConfig;
28
43
  /**
29
44
  * ItemRuleEvaluator 简洁调用示例
30
45
  *
@@ -19,6 +19,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
19
19
  // src/model/strategy/adapter/itemRule/examples.ts
20
20
  var examples_exports = {};
21
21
  __export(examples_exports, {
22
+ HOTPOT_BASE_PICK_ONE_STRATEGY: () => HOTPOT_BASE_PICK_ONE_STRATEGY,
22
23
  HOTPOT_BASE_REQUIRED_STRATEGY: () => HOTPOT_BASE_REQUIRED_STRATEGY,
23
24
  MAX_BUNS_PER_TABLE_STRATEGY: () => MAX_BUNS_PER_TABLE_STRATEGY,
24
25
  MIN_CONDIMENT_PER_PERSON_STRATEGY: () => MIN_CONDIMENT_PER_PERSON_STRATEGY,
@@ -190,6 +191,51 @@ var HOTPOT_BASE_REQUIRED_STRATEGY = {
190
191
  }
191
192
  ]
192
193
  };
194
+ var HOTPOT_BASE_PICK_ONE_STRATEGY = {
195
+ metadata: {
196
+ id: "RULE_HOTPOT_BASE_PICK_ONE",
197
+ name: {
198
+ "zh-CN": "锅底必选(四选一)",
199
+ en: "Hotpot base required (pick any)"
200
+ },
201
+ type: "item_rule",
202
+ description: {
203
+ "zh-CN": "4 款锅底任选至少 1 份",
204
+ en: "Pick at least 1 hotpot base from the available options"
205
+ }
206
+ },
207
+ conditions: {
208
+ operator: "and",
209
+ rules: [],
210
+ actionIds: ["quantity_check_hotpot_base_group"]
211
+ },
212
+ actions: [
213
+ {
214
+ id: "quantity_check_hotpot_base_group",
215
+ type: import_type.ITEM_RULE_ACTION_TYPES.QUANTITY_CHECK,
216
+ value: null,
217
+ target: "cart",
218
+ priority: 10,
219
+ config: {
220
+ targetType: "product",
221
+ targets: [
222
+ { product_id: 30001 },
223
+ { product_id: 30002 },
224
+ { product_id: 30003 },
225
+ { product_id: 30004 }
226
+ ],
227
+ quantityRules: [
228
+ { comparison: "minimum", source: "fixed", value: 1 }
229
+ ],
230
+ scope: "first_submission_only",
231
+ validationMessage: {
232
+ "zh-CN": "请至少选择 {min} 款锅底",
233
+ en: "Please select at least {min} hotpot base"
234
+ }
235
+ }
236
+ }
237
+ ]
238
+ };
193
239
  function quickUseItemRuleEvaluator() {
194
240
  const evaluator = new import_evaluator.ItemRuleEvaluator();
195
241
  evaluator.setStrategyConfigs([
@@ -261,6 +307,7 @@ function runItemRuleEvaluatorDemo() {
261
307
  }
262
308
  // Annotate the CommonJS export names for ESM import in node:
263
309
  0 && (module.exports = {
310
+ HOTPOT_BASE_PICK_ONE_STRATEGY,
264
311
  HOTPOT_BASE_REQUIRED_STRATEGY,
265
312
  MAX_BUNS_PER_TABLE_STRATEGY,
266
313
  MIN_CONDIMENT_PER_PERSON_STRATEGY,
@@ -1,5 +1,5 @@
1
1
  export { ItemRuleEvaluator } from './evaluator';
2
2
  export { ItemRuleAdapter } from './adapter';
3
3
  export { default } from './adapter';
4
- export { MIN_CONDIMENT_PER_PERSON_STRATEGY, MAX_BUNS_PER_TABLE_STRATEGY, HOTPOT_BASE_REQUIRED_STRATEGY, quickUseItemRuleEvaluator, runItemRuleEvaluatorDemo, } from './examples';
4
+ export { MIN_CONDIMENT_PER_PERSON_STRATEGY, MAX_BUNS_PER_TABLE_STRATEGY, HOTPOT_BASE_REQUIRED_STRATEGY, HOTPOT_BASE_PICK_ONE_STRATEGY, quickUseItemRuleEvaluator, runItemRuleEvaluatorDemo, } from './examples';
5
5
  export * from './type';
@@ -30,6 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/model/strategy/adapter/itemRule/index.ts
31
31
  var itemRule_exports = {};
32
32
  __export(itemRule_exports, {
33
+ HOTPOT_BASE_PICK_ONE_STRATEGY: () => import_examples.HOTPOT_BASE_PICK_ONE_STRATEGY,
33
34
  HOTPOT_BASE_REQUIRED_STRATEGY: () => import_examples.HOTPOT_BASE_REQUIRED_STRATEGY,
34
35
  ItemRuleAdapter: () => import_adapter.ItemRuleAdapter,
35
36
  ItemRuleEvaluator: () => import_evaluator.ItemRuleEvaluator,
@@ -47,6 +48,7 @@ var import_examples = require("./examples");
47
48
  __reExport(itemRule_exports, require("./type"), module.exports);
48
49
  // Annotate the CommonJS export names for ESM import in node:
49
50
  0 && (module.exports = {
51
+ HOTPOT_BASE_PICK_ONE_STRATEGY,
50
52
  HOTPOT_BASE_REQUIRED_STRATEGY,
51
53
  ItemRuleAdapter,
52
54
  ItemRuleEvaluator,
@@ -180,14 +180,33 @@ export interface QuantityLimitResult {
180
180
  targets: TargetItem[];
181
181
  /** 要求的最小数量 */
182
182
  requiredMin?: number;
183
- /** 要求的最大数量 */
183
+ /** 要求的最大数量(规则配置的原始上限,用于 UI 文案展示) */
184
184
  requiredMax?: number;
185
+ /**
186
+ * 当前提交剩余可选的最大数量。
187
+ *
188
+ * 仅在 scope='cumulative' 且存在 requiredMax 时计算:
189
+ * remainingMax = max(0, requiredMax - historicalItems 中目标商品累计数量)
190
+ *
191
+ * 非 cumulative 场景不设置此字段;调用方用于限制加购上限时,
192
+ * 应优先读取本字段,未定义时回退到 requiredMax。
193
+ */
194
+ remainingMax?: number;
185
195
  /** 要求的精确数量 */
186
196
  requiredExact?: number;
187
197
  /** 是否必须包含 */
188
198
  mustInclude?: boolean;
189
199
  /** 作用范围 */
190
200
  scope: RuleScope;
201
+ /**
202
+ * 校验失败时的提示语(已做 {min}/{max}/{exact} 占位符替换)。
203
+ * 当 rawValidationMessage 为多语言对象时,后端不做 locale 选择,
204
+ * 默认按 en → Object.values()[0] 的顺序兜底,前端可再根据自身 locale
205
+ * 读取 rawValidationMessage 做精确匹配。
206
+ */
207
+ validationMessage?: string;
208
+ /** 原始多语言模板,前端可根据自身 locale 做二次选择 */
209
+ rawValidationMessage?: string | Record<string, string>;
191
210
  }
192
211
  /**
193
212
  * ItemRule 评估器的完整输出结果
@@ -76,6 +76,10 @@ export declare class OrderModule extends BaseModule implements Module, OrderModu
76
76
  removeProductFromOrder(identity: ScanOrderOrderProductIdentity): Promise<ScanOrderOrderProduct[]>;
77
77
  submitTempOrder<T = any>(params?: {
78
78
  cacheId?: string;
79
+ platform?: string;
80
+ businessCode?: string;
81
+ channel?: string;
82
+ type?: string;
79
83
  }): Promise<T>;
80
84
  createOrder(params: CommitOrderParams['query']): {
81
85
  type: "virtual" | "appointment_booking";
@@ -108,4 +112,6 @@ export declare class OrderModule extends BaseModule implements Module, OrderModu
108
112
  createOrderByCheckout(params: CheckoutOrderParams): Promise<any>;
109
113
  submitScanOrder<T = any>(params: SubmitScanOrderParams): Promise<T>;
110
114
  scanOrderMore<T = any>(params: ScanOrderMoreParams): Promise<T>;
115
+ getOrderInfoByRemote(order_id: number): Promise<any>;
116
+ getLastOrderInfo(): Record<string, any> | undefined;
111
117
  }
@@ -131,10 +131,24 @@ var OrderModule = class extends import_BaseModule.BaseModule {
131
131
  }
132
132
  // ─── Discount: 子模块注册 ───
133
133
  registerDiscountModules(options) {
134
+ let targetCacheData = {};
135
+ if (this.cacheId && this.window) {
136
+ const sessionData = this.window.sessionStorage.getItem(this.name);
137
+ if (sessionData) {
138
+ try {
139
+ const data = JSON.parse(sessionData);
140
+ targetCacheData = (data == null ? void 0 : data[this.cacheId]) || {};
141
+ } catch {
142
+ }
143
+ }
144
+ }
134
145
  const discount = new import_Discount.DiscountModule(`${this.name}_discount`);
135
146
  this.core.registerModule(discount, {
147
+ initialState: targetCacheData == null ? void 0 : targetCacheData[discount.name],
136
148
  otherParams: {
137
- fatherModule: this.name
149
+ fatherModule: this.name,
150
+ openCache: !!this.cacheId,
151
+ cacheId: this.cacheId
138
152
  }
139
153
  });
140
154
  this.store.discount = discount;
@@ -145,41 +159,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
145
159
  this.store.rules = rules;
146
160
  }
147
161
  createDefaultRulesHooks() {
148
- return {
149
- getProduct: (product) => {
150
- var _a, _b, _c, _d;
151
- return {
152
- id: product.product_id,
153
- _id: product.identity_key ? `${product.product_id}_${product.product_variant_id}_${product.identity_key}` : `${product.product_id}_${product.product_variant_id}`,
154
- price: product.selling_price,
155
- total: new import_decimal.default(product.payment_price || product.selling_price || 0).times(product.num || 1).toNumber(),
156
- origin_total: new import_decimal.default(product.original_price || product.selling_price || 0).times(product.num || 1).toNumber(),
157
- original_price: product.original_price,
158
- quantity: product.num || 1,
159
- num: product.num || 1,
160
- discount_list: product.discount_list || [],
161
- bundle: product.product_bundle || [],
162
- booking_id: ((_a = product._origin) == null ? void 0 : _a.booking_id) || null,
163
- isClient: false,
164
- isManualDiscount: ((_b = product._origin) == null ? void 0 : _b.isManualDiscount) || false,
165
- holder_id: (_c = product._origin) == null ? void 0 : _c.holder_id,
166
- startDate: (_d = product._origin) == null ? void 0 : _d.startDate
167
- };
168
- },
169
- setProduct: (product, values) => ({
170
- ...product,
171
- selling_price: values.price !== void 0 ? String(values.price) : product.selling_price,
172
- payment_price: values.total !== void 0 ? String(values.total) : product.payment_price,
173
- original_price: values.original_price !== void 0 ? String(values.original_price) : product.original_price,
174
- discount_list: values.discount_list ?? product.discount_list,
175
- num: values.quantity ?? product.num,
176
- product_bundle: values.bundle ?? product.product_bundle,
177
- metadata: {
178
- ...product.metadata || {},
179
- ...values.main_product_selling_price !== void 0 ? { main_product_selling_price: String(values.main_product_selling_price) } : {}
180
- }
181
- })
182
- };
162
+ return (0, import_utils.createDefaultOrderRulesHooks)();
183
163
  }
184
164
  // ─── Discount: 公共 API ───
185
165
  async loadDiscountConfig(params) {
@@ -476,7 +456,11 @@ var OrderModule = class extends import_BaseModule.BaseModule {
476
456
  const effectiveCacheId = (params == null ? void 0 : params.cacheId) ?? this.cacheId;
477
457
  const payload = (0, import_utils.buildSubmitPayload)({
478
458
  tempOrder,
479
- cacheId: effectiveCacheId
459
+ cacheId: effectiveCacheId,
460
+ platform: params == null ? void 0 : params.platform,
461
+ businessCode: params == null ? void 0 : params.businessCode,
462
+ channel: params == null ? void 0 : params.channel,
463
+ type: params == null ? void 0 : params.type
480
464
  });
481
465
  let result;
482
466
  if (tempOrder.order_id) {
@@ -719,6 +703,20 @@ var OrderModule = class extends import_BaseModule.BaseModule {
719
703
  };
720
704
  return this.request.put(fetchUrl, requestBody);
721
705
  }
706
+ // TODO 获取详情的接口
707
+ async getOrderInfoByRemote(order_id) {
708
+ const res = await this.request.get(`/order/sales/${order_id}`, {
709
+ with: ["products", "scheduleEvents"]
710
+ });
711
+ if (res.code === 200 && this.store.tempOrder) {
712
+ this.store.tempOrder.lastOrderInfo = res.data;
713
+ }
714
+ return res;
715
+ }
716
+ getLastOrderInfo() {
717
+ var _a, _b;
718
+ return (_b = (_a = this.store) == null ? void 0 : _a.tempOrder) == null ? void 0 : _b.lastOrderInfo;
719
+ }
722
720
  };
723
721
  // Annotate the CommonJS export names for ESM import in node:
724
722
  0 && (module.exports = {
@@ -212,6 +212,10 @@ export interface OrderModuleAPI {
212
212
  persistTempOrder: () => void;
213
213
  submitTempOrder: <T = any>(params?: {
214
214
  cacheId?: string;
215
+ platform?: string;
216
+ businessCode?: string;
217
+ channel?: string;
218
+ type?: string;
215
219
  }) => Promise<T>;
216
220
  loadDiscountConfig: (params: {
217
221
  customerId: number;
@@ -1,5 +1,25 @@
1
1
  import { CartItem } from "../Cart";
2
2
  import type { ScanOrderSubmitPayload, ScanOrderSubmitProduct, ScanOrderSummary, ScanOrderTempOrder } from '../../solution/ScanOrder/types';
3
+ import type { RulesParamsHooks } from '../Rules/types';
4
+ /**
5
+ * OrderModule 默认 Rules 钩子工厂。
6
+ *
7
+ * 价格语义约定(订单域):
8
+ * - `selling_price`:券后单品单价(实际成交单价)。初次加购 = `original_price`;券应用后由 Rules 引擎更新。
9
+ * - `original_price`:券前单品单价(店铺售价)。不承载后台划线价语义。
10
+ * - `payment_price`:已从内部字段移除;只在出站 payload 中由 `selling_price` 派生以兼容后端。
11
+ *
12
+ * Rules 钩子契约:
13
+ * - `getProduct.total` = `selling_price × num`(当前成交总价)
14
+ * - `getProduct.origin_total` = `original_price × num`(券前总价)
15
+ * - `setProduct` 写回 `selling_price` 的优先级:
16
+ * 1. `values.main_product_selling_price`(券应用分支,per-unit 券后主价)
17
+ * 2. `values.price`(还原分支 `restoredPrice`、good_pass 归零)
18
+ * 3. `values.total / num`(仅当 Rules 只给出总价时的兜底)
19
+ *
20
+ * 导出为纯函数方便单测直接驱动钩子,不依赖 OrderModule 实例。
21
+ */
22
+ export declare function createDefaultOrderRulesHooks(): RulesParamsHooks;
3
23
  /**
4
24
  * 通过 session 类商品的开始时间结束时间生成商品的时长
5
25
  * @param {CartItem} cartItem
@@ -25,6 +45,13 @@ export declare const getAllDiscountList: (cartItem: CartItem) => any;
25
45
  export declare function createUuidV4(): string;
26
46
  export declare function isTempOrder(data: any): data is ScanOrderTempOrder;
27
47
  export declare function formatDateTime(date: Date): string;
48
+ export declare function normalizeSubmitBooking<T extends {
49
+ metadata?: Record<string, any>;
50
+ }>(booking: T): T;
51
+ /** 提交用人数:有限且为正则向下取整,否则为 1 */
52
+ export declare function normalizeSubmitCollectPaxValue(value: unknown): number;
53
+ /** 从临时订单 metadata 解析合成 booking 的 collect_pax */
54
+ export declare function resolveSubmitCollectPax(tempOrder: ScanOrderTempOrder): number;
28
55
  export declare function createDefaultTempOrder(params: {
29
56
  now: string;
30
57
  summary?: ScanOrderSummary;
@@ -33,6 +60,10 @@ export declare function buildSubmitPayload(params: {
33
60
  tempOrder: ScanOrderTempOrder;
34
61
  cacheId?: string;
35
62
  now?: Date;
63
+ platform?: string;
64
+ businessCode?: string;
65
+ channel?: string;
66
+ type?: string;
36
67
  }): ScanOrderSubmitPayload;
37
68
  export declare function formatV1Product(products: ScanOrderSubmitProduct[]): {
38
69
  bundle: any[];
@@ -30,6 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  var utils_exports = {};
31
31
  __export(utils_exports, {
32
32
  buildSubmitPayload: () => buildSubmitPayload,
33
+ createDefaultOrderRulesHooks: () => createDefaultOrderRulesHooks,
33
34
  createDefaultTempOrder: () => createDefaultTempOrder,
34
35
  createEmptySummary: () => import_utils2.createEmptySummary,
35
36
  createUuidV4: () => createUuidV4,
@@ -38,12 +39,60 @@ __export(utils_exports, {
38
39
  generateDuration: () => generateDuration,
39
40
  getAllDiscountList: () => getAllDiscountList,
40
41
  isTempOrder: () => isTempOrder,
41
- mergeRelationForms: () => mergeRelationForms
42
+ mergeRelationForms: () => mergeRelationForms,
43
+ normalizeSubmitBooking: () => normalizeSubmitBooking,
44
+ normalizeSubmitCollectPaxValue: () => normalizeSubmitCollectPaxValue,
45
+ resolveSubmitCollectPax: () => resolveSubmitCollectPax
42
46
  });
43
47
  module.exports = __toCommonJS(utils_exports);
44
48
  var import_dayjs = __toESM(require("dayjs"));
49
+ var import_decimal = __toESM(require("decimal.js"));
45
50
  var import_utils = require("../../solution/ScanOrder/utils");
46
51
  var import_utils2 = require("../../solution/ScanOrder/utils");
52
+ function createDefaultOrderRulesHooks() {
53
+ const toUnitPriceString = (totalLike, num) => {
54
+ const effectiveNum = Number(num) > 0 ? Number(num) : 1;
55
+ return new import_decimal.default(Number(totalLike) || 0).dividedBy(effectiveNum).toDecimalPlaces(2).toString();
56
+ };
57
+ return {
58
+ getProduct: (product) => {
59
+ var _a, _b, _c, _d;
60
+ return {
61
+ id: product.product_id,
62
+ _id: product.identity_key ? `${product.product_id}_${product.product_variant_id}_${product.identity_key}` : `${product.product_id}_${product.product_variant_id}`,
63
+ price: product.selling_price,
64
+ total: new import_decimal.default(product.selling_price || 0).times(product.num || 1).toNumber(),
65
+ origin_total: new import_decimal.default(product.original_price || product.selling_price || 0).times(product.num || 1).toNumber(),
66
+ original_price: product.original_price,
67
+ quantity: product.num || 1,
68
+ num: product.num || 1,
69
+ discount_list: product.discount_list || [],
70
+ bundle: product.product_bundle || [],
71
+ booking_id: ((_a = product._origin) == null ? void 0 : _a.booking_id) || null,
72
+ isClient: false,
73
+ isManualDiscount: ((_b = product._origin) == null ? void 0 : _b.isManualDiscount) || false,
74
+ holder_id: (_c = product._origin) == null ? void 0 : _c.holder_id,
75
+ startDate: (_d = product._origin) == null ? void 0 : _d.startDate
76
+ };
77
+ },
78
+ setProduct: (product, values) => {
79
+ const nextNum = Number(values.quantity ?? product.num ?? 1) || 1;
80
+ const nextSellingPrice = values.main_product_selling_price !== void 0 ? String(values.main_product_selling_price) : values.price !== void 0 ? String(values.price) : values.total !== void 0 ? toUnitPriceString(values.total, nextNum) : product.selling_price;
81
+ return {
82
+ ...product,
83
+ selling_price: nextSellingPrice,
84
+ original_price: values.original_price !== void 0 ? String(values.original_price) : product.original_price,
85
+ discount_list: values.discount_list ?? product.discount_list,
86
+ num: values.quantity ?? product.num,
87
+ product_bundle: values.bundle ?? product.product_bundle,
88
+ metadata: {
89
+ ...product.metadata || {},
90
+ ...values.main_product_selling_price !== void 0 ? { main_product_selling_price: String(values.main_product_selling_price) } : {}
91
+ }
92
+ };
93
+ }
94
+ };
95
+ }
47
96
  var generateDuration = (cartItem) => {
48
97
  const startDate = (0, import_dayjs.default)(`${cartItem.start_date} ${cartItem.start_time}`);
49
98
  const endDate = (0, import_dayjs.default)(`${cartItem.end_date} ${cartItem.end_time}`);
@@ -129,6 +178,25 @@ function normalizeSubmitProduct(product) {
129
178
  product_option_item: submitProduct.product_option_item || [],
130
179
  discount_list: submitProduct.discount_list || [],
131
180
  product_bundle: submitProduct.product_bundle || [],
181
+ metadata: cleanMetadata,
182
+ // 出站兼容:后端仍消费 payment_price 字段,从 selling_price(券后单价)派生。
183
+ payment_price: submitProduct.selling_price
184
+ };
185
+ }
186
+ var SUBMIT_BOOKING_METADATA_WHITELIST = [
187
+ "unique_identification_number",
188
+ "collect_pax"
189
+ ];
190
+ function normalizeSubmitBooking(booking) {
191
+ const rawMetadata = booking.metadata || {};
192
+ const cleanMetadata = {};
193
+ for (const key of SUBMIT_BOOKING_METADATA_WHITELIST) {
194
+ if (rawMetadata[key] !== void 0) {
195
+ cleanMetadata[key] = rawMetadata[key];
196
+ }
197
+ }
198
+ return {
199
+ ...booking,
132
200
  metadata: cleanMetadata
133
201
  };
134
202
  }
@@ -154,6 +222,14 @@ function resolveTableOccupancyDuration(tempOrder) {
154
222
  }
155
223
  return DEFAULT_TABLE_OCCUPANCY_DURATION;
156
224
  }
225
+ function normalizeSubmitCollectPaxValue(value) {
226
+ const n = Number(value);
227
+ return Number.isFinite(n) && n > 0 ? Math.floor(n) : 1;
228
+ }
229
+ function resolveSubmitCollectPax(tempOrder) {
230
+ var _a;
231
+ return normalizeSubmitCollectPaxValue((_a = tempOrder.metadata) == null ? void 0 : _a.collect_pax);
232
+ }
157
233
  function createDefaultTempOrder(params) {
158
234
  const summary = params.summary || (0, import_utils.createEmptySummary)();
159
235
  return {
@@ -203,7 +279,15 @@ function createDefaultTempOrder(params) {
203
279
  };
204
280
  }
205
281
  function buildSubmitPayload(params) {
206
- const { tempOrder, cacheId, now = /* @__PURE__ */ new Date() } = params;
282
+ const {
283
+ tempOrder,
284
+ cacheId,
285
+ now = /* @__PURE__ */ new Date(),
286
+ platform,
287
+ businessCode,
288
+ channel,
289
+ type
290
+ } = params;
207
291
  const scheduleDate = tempOrder.schedule_date || tempOrder.created_at || formatDateTime(now);
208
292
  const summary = tempOrder.summary || (0, import_utils.createEmptySummary)();
209
293
  const relationId = tempOrder.relation_id;
@@ -222,7 +306,7 @@ function buildSubmitPayload(params) {
222
306
  duration: bookingDuration,
223
307
  metadata: {
224
308
  unique_identification_number: bookingUuid,
225
- collect_pax: 1
309
+ collect_pax: resolveSubmitCollectPax(tempOrder)
226
310
  },
227
311
  select_date: bookingStart.format("YYYY-MM-DD"),
228
312
  is_all: false,
@@ -238,12 +322,12 @@ function buildSubmitPayload(params) {
238
322
  const { created_at: _createdAt, summary: _summary, surcharges: _surcharges, ...tempOrderRest } = tempOrder;
239
323
  return {
240
324
  ...tempOrderRest,
241
- platform: normalizeSubmitPlatform(tempOrder.platform),
325
+ platform: normalizeSubmitPlatform(platform ?? tempOrder.platform),
242
326
  request_unique_idempotency_token: cacheId,
243
- type: tempOrder.type || "table-order",
244
- business_code: tempOrder.business_code || "table-order",
327
+ type: type ?? tempOrder.type ?? "table-order",
328
+ business_code: businessCode ?? tempOrder.business_code ?? "table-order",
245
329
  sales_channel: tempOrder.sales_channel || "my_pisel",
246
- order_sales_channel: tempOrder.order_sales_channel || "online_store",
330
+ order_sales_channel: channel ?? tempOrder.order_sales_channel ?? "online_store",
247
331
  status: tempOrder.status || "normal",
248
332
  payment_status: tempOrder.payment_status || "payment_processing",
249
333
  // shipping_status: tempOrder.shipping_status || 'unfulfilled',
@@ -255,7 +339,7 @@ function buildSubmitPayload(params) {
255
339
  surcharge_fee: summary.surcharge_fee || tempOrder.surcharge_fee || "0.00",
256
340
  note: tempOrder.note || "",
257
341
  schedule_date: scheduleDate,
258
- bookings,
342
+ bookings: bookings.map((booking) => normalizeSubmitBooking(booking)),
259
343
  payments: tempOrder.payments || [],
260
344
  // discount_list: tempOrder.discount_list || [],
261
345
  relation_forms: tempOrder.relation_forms || [],
@@ -290,6 +374,7 @@ function formatV1Product(products) {
290
374
  // Annotate the CommonJS export names for ESM import in node:
291
375
  0 && (module.exports = {
292
376
  buildSubmitPayload,
377
+ createDefaultOrderRulesHooks,
293
378
  createDefaultTempOrder,
294
379
  createEmptySummary,
295
380
  createUuidV4,
@@ -298,5 +383,8 @@ function formatV1Product(products) {
298
383
  generateDuration,
299
384
  getAllDiscountList,
300
385
  isTempOrder,
301
- mergeRelationForms
386
+ mergeRelationForms,
387
+ normalizeSubmitBooking,
388
+ normalizeSubmitCollectPaxValue,
389
+ resolveSubmitCollectPax
302
390
  });
@@ -61,7 +61,7 @@ var ProductList = class extends import_BaseModule.BaseModule {
61
61
  cacheId,
62
62
  with_schedule
63
63
  }, options) {
64
- var _a, _b;
64
+ var _a;
65
65
  let userPlugin = this.core.getPlugin("user");
66
66
  let customer_id = void 0;
67
67
  try {
@@ -97,7 +97,8 @@ var ProductList = class extends import_BaseModule.BaseModule {
97
97
  schedule_date,
98
98
  with_schedule,
99
99
  schedule_datetime,
100
- application_code: (_b = this.otherParams) == null ? void 0 : _b.channel,
100
+ // application_code: this.otherParams?.channel,
101
+ application_code: "online-store",
101
102
  is_eject: 1
102
103
  },
103
104
  { osServer: true, callback: options == null ? void 0 : options.callback, subscriberId: options == null ? void 0 : options.subscriberId, useCache: true, customToast: () => {