@pisell/pisellos 2.1.125 → 2.1.127
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.
- package/dist/model/strategy/adapter/itemRule/adapter.d.ts +8 -0
- package/dist/model/strategy/adapter/itemRule/adapter.js +52 -8
- package/dist/model/strategy/adapter/itemRule/examples.d.ts +15 -0
- package/dist/model/strategy/adapter/itemRule/examples.js +68 -1
- package/dist/model/strategy/adapter/itemRule/index.d.ts +1 -1
- package/dist/model/strategy/adapter/itemRule/index.js +1 -1
- package/dist/model/strategy/adapter/itemRule/type.d.ts +20 -1
- package/dist/modules/Order/index.d.ts +1 -1
- package/dist/modules/Order/index.js +6 -40
- package/dist/modules/Order/utils.d.ts +24 -0
- package/dist/modules/Order/utils.js +87 -11
- package/dist/modules/SalesSummary/types.d.ts +2 -1
- package/dist/modules/SalesSummary/utils.js +10 -10
- package/dist/modules/Schedule/utils.d.ts +1 -1
- package/dist/solution/ScanOrder/index.d.ts +5 -0
- package/dist/solution/ScanOrder/index.js +205 -64
- package/dist/solution/ScanOrder/types.d.ts +19 -5
- package/dist/solution/ScanOrder/utils.d.ts +15 -0
- package/dist/solution/ScanOrder/utils.js +142 -62
- package/dist/solution/VenueBooking/index.d.ts +4 -0
- package/dist/solution/VenueBooking/index.js +219 -141
- package/dist/solution/VenueBooking/utils/dateSummary.d.ts +1 -0
- package/dist/solution/VenueBooking/utils/dateSummary.js +6 -4
- package/lib/model/strategy/adapter/itemRule/adapter.d.ts +8 -0
- package/lib/model/strategy/adapter/itemRule/adapter.js +41 -2
- package/lib/model/strategy/adapter/itemRule/examples.d.ts +15 -0
- package/lib/model/strategy/adapter/itemRule/examples.js +47 -0
- package/lib/model/strategy/adapter/itemRule/index.d.ts +1 -1
- package/lib/model/strategy/adapter/itemRule/index.js +2 -0
- package/lib/model/strategy/adapter/itemRule/type.d.ts +20 -1
- package/lib/model/strategy/adapter/promotion/index.js +49 -0
- package/lib/modules/Order/index.d.ts +1 -1
- package/lib/modules/Order/index.js +4 -37
- package/lib/modules/Order/utils.d.ts +24 -0
- package/lib/modules/Order/utils.js +65 -4
- package/lib/modules/SalesSummary/types.d.ts +2 -1
- package/lib/modules/SalesSummary/utils.js +2 -2
- package/lib/modules/Schedule/utils.d.ts +1 -1
- package/lib/solution/ScanOrder/index.d.ts +5 -0
- package/lib/solution/ScanOrder/index.js +94 -18
- package/lib/solution/ScanOrder/types.d.ts +19 -5
- package/lib/solution/ScanOrder/utils.d.ts +15 -0
- package/lib/solution/ScanOrder/utils.js +86 -19
- package/lib/solution/VenueBooking/index.d.ts +4 -0
- package/lib/solution/VenueBooking/index.js +85 -25
- package/lib/solution/VenueBooking/utils/dateSummary.d.ts +1 -0
- package/lib/solution/VenueBooking/utils/dateSummary.js +13 -4
- 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(
|
|
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:
|
|
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 >=
|
|
86
|
+
} else if ((totalSlots - availableSlots) / totalSlots >= effectiveConfig.fewLeftThreshold) {
|
|
85
87
|
status = 'few_left';
|
|
86
88
|
} else {
|
|
87
89
|
status = 'available';
|
|
@@ -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 评估器的完整输出结果
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
20
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
21
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
22
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
23
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
24
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
25
|
+
mod
|
|
26
|
+
));
|
|
27
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
|
+
|
|
29
|
+
// src/model/strategy/adapter/promotion/index.ts
|
|
30
|
+
var promotion_exports = {};
|
|
31
|
+
__export(promotion_exports, {
|
|
32
|
+
BUY_X_GET_Y_FREE_STRATEGY: () => import_examples.BUY_X_GET_Y_FREE_STRATEGY,
|
|
33
|
+
PromotionAdapter: () => import_adapter.PromotionAdapter,
|
|
34
|
+
PromotionEvaluator: () => import_evaluator.PromotionEvaluator,
|
|
35
|
+
X_ITEMS_FOR_Y_PRICE_STRATEGY: () => import_examples.X_ITEMS_FOR_Y_PRICE_STRATEGY,
|
|
36
|
+
default: () => import_adapter2.default
|
|
37
|
+
});
|
|
38
|
+
module.exports = __toCommonJS(promotion_exports);
|
|
39
|
+
var import_evaluator = require("./evaluator");
|
|
40
|
+
var import_adapter = require("./adapter");
|
|
41
|
+
var import_adapter2 = __toESM(require("./adapter"));
|
|
42
|
+
var import_examples = require("./examples");
|
|
43
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
44
|
+
0 && (module.exports = {
|
|
45
|
+
BUY_X_GET_Y_FREE_STRATEGY,
|
|
46
|
+
PromotionAdapter,
|
|
47
|
+
PromotionEvaluator,
|
|
48
|
+
X_ITEMS_FOR_Y_PRICE_STRATEGY
|
|
49
|
+
});
|
|
@@ -112,6 +112,6 @@ export declare class OrderModule extends BaseModule implements Module, OrderModu
|
|
|
112
112
|
createOrderByCheckout(params: CheckoutOrderParams): Promise<any>;
|
|
113
113
|
submitScanOrder<T = any>(params: SubmitScanOrderParams): Promise<T>;
|
|
114
114
|
scanOrderMore<T = any>(params: ScanOrderMoreParams): Promise<T>;
|
|
115
|
-
getOrderInfoByRemote(order_id: number): Promise<
|
|
115
|
+
getOrderInfoByRemote(order_id: number): Promise<any>;
|
|
116
116
|
getLastOrderInfo(): Record<string, any> | undefined;
|
|
117
117
|
}
|
|
@@ -158,41 +158,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
158
158
|
this.store.rules = rules;
|
|
159
159
|
}
|
|
160
160
|
createDefaultRulesHooks() {
|
|
161
|
-
return
|
|
162
|
-
getProduct: (product) => {
|
|
163
|
-
var _a, _b, _c, _d;
|
|
164
|
-
return {
|
|
165
|
-
id: product.product_id,
|
|
166
|
-
_id: product.identity_key ? `${product.product_id}_${product.product_variant_id}_${product.identity_key}` : `${product.product_id}_${product.product_variant_id}`,
|
|
167
|
-
price: product.selling_price,
|
|
168
|
-
total: new import_decimal.default(product.payment_price || product.selling_price || 0).times(product.num || 1).toNumber(),
|
|
169
|
-
origin_total: new import_decimal.default(product.original_price || product.selling_price || 0).times(product.num || 1).toNumber(),
|
|
170
|
-
original_price: product.original_price,
|
|
171
|
-
quantity: product.num || 1,
|
|
172
|
-
num: product.num || 1,
|
|
173
|
-
discount_list: product.discount_list || [],
|
|
174
|
-
bundle: product.product_bundle || [],
|
|
175
|
-
booking_id: ((_a = product._origin) == null ? void 0 : _a.booking_id) || null,
|
|
176
|
-
isClient: false,
|
|
177
|
-
isManualDiscount: ((_b = product._origin) == null ? void 0 : _b.isManualDiscount) || false,
|
|
178
|
-
holder_id: (_c = product._origin) == null ? void 0 : _c.holder_id,
|
|
179
|
-
startDate: (_d = product._origin) == null ? void 0 : _d.startDate
|
|
180
|
-
};
|
|
181
|
-
},
|
|
182
|
-
setProduct: (product, values) => ({
|
|
183
|
-
...product,
|
|
184
|
-
selling_price: values.price !== void 0 ? String(values.price) : product.selling_price,
|
|
185
|
-
payment_price: values.total !== void 0 ? String(values.total) : product.payment_price,
|
|
186
|
-
original_price: values.original_price !== void 0 ? String(values.original_price) : product.original_price,
|
|
187
|
-
discount_list: values.discount_list ?? product.discount_list,
|
|
188
|
-
num: values.quantity ?? product.num,
|
|
189
|
-
product_bundle: values.bundle ?? product.product_bundle,
|
|
190
|
-
metadata: {
|
|
191
|
-
...product.metadata || {},
|
|
192
|
-
...values.main_product_selling_price !== void 0 ? { main_product_selling_price: String(values.main_product_selling_price) } : {}
|
|
193
|
-
}
|
|
194
|
-
})
|
|
195
|
-
};
|
|
161
|
+
return (0, import_utils.createDefaultOrderRulesHooks)();
|
|
196
162
|
}
|
|
197
163
|
// ─── Discount: 公共 API ───
|
|
198
164
|
async loadDiscountConfig(params) {
|
|
@@ -737,9 +703,10 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
737
703
|
const res = await this.request.get(`/order/sales/${order_id}`, {
|
|
738
704
|
with: ["products", "scheduleEvents"]
|
|
739
705
|
});
|
|
740
|
-
if (res.
|
|
741
|
-
this.store.tempOrder.lastOrderInfo = res.data
|
|
706
|
+
if (res.code === 200 && this.store.tempOrder) {
|
|
707
|
+
this.store.tempOrder.lastOrderInfo = res.data;
|
|
742
708
|
}
|
|
709
|
+
return res;
|
|
743
710
|
}
|
|
744
711
|
getLastOrderInfo() {
|
|
745
712
|
var _a, _b;
|
|
@@ -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
|
|
@@ -28,6 +48,10 @@ export declare function formatDateTime(date: Date): string;
|
|
|
28
48
|
export declare function normalizeSubmitBooking<T extends {
|
|
29
49
|
metadata?: Record<string, any>;
|
|
30
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;
|
|
31
55
|
export declare function createDefaultTempOrder(params: {
|
|
32
56
|
now: string;
|
|
33
57
|
summary?: ScanOrderSummary;
|
|
@@ -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,
|
|
@@ -39,12 +40,59 @@ __export(utils_exports, {
|
|
|
39
40
|
getAllDiscountList: () => getAllDiscountList,
|
|
40
41
|
isTempOrder: () => isTempOrder,
|
|
41
42
|
mergeRelationForms: () => mergeRelationForms,
|
|
42
|
-
normalizeSubmitBooking: () => normalizeSubmitBooking
|
|
43
|
+
normalizeSubmitBooking: () => normalizeSubmitBooking,
|
|
44
|
+
normalizeSubmitCollectPaxValue: () => normalizeSubmitCollectPaxValue,
|
|
45
|
+
resolveSubmitCollectPax: () => resolveSubmitCollectPax
|
|
43
46
|
});
|
|
44
47
|
module.exports = __toCommonJS(utils_exports);
|
|
45
48
|
var import_dayjs = __toESM(require("dayjs"));
|
|
49
|
+
var import_decimal = __toESM(require("decimal.js"));
|
|
46
50
|
var import_utils = require("../../solution/ScanOrder/utils");
|
|
47
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
|
+
}
|
|
48
96
|
var generateDuration = (cartItem) => {
|
|
49
97
|
const startDate = (0, import_dayjs.default)(`${cartItem.start_date} ${cartItem.start_time}`);
|
|
50
98
|
const endDate = (0, import_dayjs.default)(`${cartItem.end_date} ${cartItem.end_time}`);
|
|
@@ -130,7 +178,9 @@ function normalizeSubmitProduct(product) {
|
|
|
130
178
|
product_option_item: submitProduct.product_option_item || [],
|
|
131
179
|
discount_list: submitProduct.discount_list || [],
|
|
132
180
|
product_bundle: submitProduct.product_bundle || [],
|
|
133
|
-
metadata: cleanMetadata
|
|
181
|
+
metadata: cleanMetadata,
|
|
182
|
+
// 出站兼容:后端仍消费 payment_price 字段,从 selling_price(券后单价)派生。
|
|
183
|
+
payment_price: submitProduct.selling_price
|
|
134
184
|
};
|
|
135
185
|
}
|
|
136
186
|
var SUBMIT_BOOKING_METADATA_WHITELIST = [
|
|
@@ -172,6 +222,14 @@ function resolveTableOccupancyDuration(tempOrder) {
|
|
|
172
222
|
}
|
|
173
223
|
return DEFAULT_TABLE_OCCUPANCY_DURATION;
|
|
174
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
|
+
}
|
|
175
233
|
function createDefaultTempOrder(params) {
|
|
176
234
|
const summary = params.summary || (0, import_utils.createEmptySummary)();
|
|
177
235
|
return {
|
|
@@ -248,7 +306,7 @@ function buildSubmitPayload(params) {
|
|
|
248
306
|
duration: bookingDuration,
|
|
249
307
|
metadata: {
|
|
250
308
|
unique_identification_number: bookingUuid,
|
|
251
|
-
collect_pax:
|
|
309
|
+
collect_pax: resolveSubmitCollectPax(tempOrder)
|
|
252
310
|
},
|
|
253
311
|
select_date: bookingStart.format("YYYY-MM-DD"),
|
|
254
312
|
is_all: false,
|
|
@@ -316,6 +374,7 @@ function formatV1Product(products) {
|
|
|
316
374
|
// Annotate the CommonJS export names for ESM import in node:
|
|
317
375
|
0 && (module.exports = {
|
|
318
376
|
buildSubmitPayload,
|
|
377
|
+
createDefaultOrderRulesHooks,
|
|
319
378
|
createDefaultTempOrder,
|
|
320
379
|
createEmptySummary,
|
|
321
380
|
createUuidV4,
|
|
@@ -325,5 +384,7 @@ function formatV1Product(products) {
|
|
|
325
384
|
getAllDiscountList,
|
|
326
385
|
isTempOrder,
|
|
327
386
|
mergeRelationForms,
|
|
328
|
-
normalizeSubmitBooking
|
|
387
|
+
normalizeSubmitBooking,
|
|
388
|
+
normalizeSubmitCollectPaxValue,
|
|
389
|
+
resolveSubmitCollectPax
|
|
329
390
|
});
|
|
@@ -2,9 +2,10 @@ export interface SalesSummaryProduct {
|
|
|
2
2
|
product_id: number | null;
|
|
3
3
|
product_variant_id: number;
|
|
4
4
|
num: number;
|
|
5
|
+
/** 券后单品单价(订单域成交单价),由 OrderModule 维护。 */
|
|
5
6
|
selling_price: string;
|
|
7
|
+
/** 券前单品单价(店铺售价)。 */
|
|
6
8
|
original_price: string;
|
|
7
|
-
payment_price: string;
|
|
8
9
|
tax_fee: string;
|
|
9
10
|
is_charge_tax?: number;
|
|
10
11
|
product_option_item?: any[];
|
|
@@ -82,7 +82,7 @@ function getBundleUnitPrice(product, useOriginal = false) {
|
|
|
82
82
|
}
|
|
83
83
|
function getUnitPaymentTotal(product) {
|
|
84
84
|
const variantPrice = getVariantPrice(product);
|
|
85
|
-
const basePrice = variantPrice || toDecimal(product.selling_price
|
|
85
|
+
const basePrice = variantPrice || toDecimal(product.selling_price);
|
|
86
86
|
return basePrice.plus(getOptionUnitPrice(product)).plus(getBundleUnitPrice(product));
|
|
87
87
|
}
|
|
88
88
|
function getUnitOriginalTotal(product) {
|
|
@@ -231,7 +231,7 @@ function calculateSingleItemTax(params) {
|
|
|
231
231
|
function getMainProductPaymentTotal(product) {
|
|
232
232
|
var _a, _b, _c, _d;
|
|
233
233
|
const variantPrice = getVariantPrice(product);
|
|
234
|
-
let total = variantPrice || toDecimal(((_a = product.metadata) == null ? void 0 : _a.main_product_selling_price) ?? product.selling_price
|
|
234
|
+
let total = variantPrice || toDecimal(((_a = product.metadata) == null ? void 0 : _a.main_product_selling_price) ?? product.selling_price);
|
|
235
235
|
const mainDiscountList = ((_c = (_b = product._origin) == null ? void 0 : _b.product) == null ? void 0 : _c.discount_list) || ((_d = product._origin) == null ? void 0 : _d.discount_list) || [];
|
|
236
236
|
const mainDiscountAmount = getDiscountListAmount(
|
|
237
237
|
mainDiscountList.filter((d) => {
|
|
@@ -46,7 +46,7 @@ export declare const formatScheduleResult: (schedule: ScheduleItem) => {
|
|
|
46
46
|
};
|
|
47
47
|
export declare const calcScheduleDateRange: (schedule: ScheduleItem) => any[];
|
|
48
48
|
export declare const calcCalendarDataBySchedules: (schedules: ScheduleItem[]) => unknown[];
|
|
49
|
-
export declare const calcMinTimeMaxTimeBySchedules: (schedules: ScheduleItem[], scheduleAllMap?: ScheduleAllMap, selectDate?: string) =>
|
|
49
|
+
export declare const calcMinTimeMaxTimeBySchedules: (schedules: ScheduleItem[], scheduleAllMap?: ScheduleAllMap, selectDate?: string) => ScheduleAllMap;
|
|
50
50
|
/**
|
|
51
51
|
* @title: 格式化日程数据
|
|
52
52
|
* @description:
|
|
@@ -89,4 +89,9 @@ export declare class ScanOrderImpl extends BaseModule implements Module {
|
|
|
89
89
|
}>;
|
|
90
90
|
getProductList(): Promise<any>;
|
|
91
91
|
getOtherParams(): Record<string, any>;
|
|
92
|
+
setOtherParams(params: Record<string, any>, { cover }?: {
|
|
93
|
+
cover?: boolean;
|
|
94
|
+
}): Promise<void>;
|
|
95
|
+
setEntryPaxNumber(number: number): Promise<void>;
|
|
96
|
+
getEntryPaxNumber(): number | null;
|
|
92
97
|
}
|