@pisell/pisellos 2.1.129 → 2.1.131
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/promotion/index.js +9 -0
- package/dist/modules/Order/index.d.ts +7 -6
- package/dist/modules/Order/index.js +137 -42
- package/dist/modules/Order/types.d.ts +32 -6
- package/dist/modules/Order/types.js +2 -0
- package/dist/modules/Order/utils.d.ts +73 -11
- package/dist/modules/Order/utils.js +304 -52
- package/dist/modules/SalesSummary/utils.js +33 -68
- package/dist/modules/ScanOrderLogger/providers/feishu.js +168 -60
- package/dist/modules/ScanOrderLogger/types.d.ts +6 -0
- package/dist/modules/Summary/utils.js +6 -21
- package/dist/solution/ScanOrder/index.d.ts +57 -8
- package/dist/solution/ScanOrder/index.js +1531 -583
- package/dist/solution/ScanOrder/types.d.ts +86 -26
- package/dist/solution/ScanOrder/types.js +20 -1
- package/dist/solution/ScanOrder/utils.d.ts +53 -5
- package/dist/solution/ScanOrder/utils.js +257 -37
- package/dist/solution/VenueBooking/index.d.ts +30 -10
- package/dist/solution/VenueBooking/index.js +460 -217
- package/dist/solution/VenueBooking/types.d.ts +23 -0
- package/dist/solution/VenueBooking/utils/dateSummary.d.ts +1 -1
- package/dist/solution/VenueBooking/utils/dateSummary.js +1 -1
- package/dist/solution/VenueBooking/utils/resource.d.ts +11 -1
- package/dist/solution/VenueBooking/utils/resource.js +57 -21
- package/dist/solution/VenueBooking/utils/slotMerge.d.ts +5 -0
- package/dist/solution/VenueBooking/utils/slotMerge.js +33 -12
- package/dist/solution/VenueBooking/utils/timeSlot.d.ts +1 -1
- package/dist/solution/VenueBooking/utils/timeSlot.js +259 -62
- package/lib/modules/Order/index.d.ts +7 -6
- package/lib/modules/Order/index.js +123 -31
- package/lib/modules/Order/types.d.ts +32 -6
- package/lib/modules/Order/utils.d.ts +73 -11
- package/lib/modules/Order/utils.js +203 -28
- package/lib/modules/SalesSummary/utils.js +13 -47
- package/lib/modules/ScanOrderLogger/providers/feishu.js +100 -34
- package/lib/modules/ScanOrderLogger/types.d.ts +6 -0
- package/lib/modules/Summary/utils.js +4 -18
- package/lib/solution/ScanOrder/index.d.ts +57 -8
- package/lib/solution/ScanOrder/index.js +713 -117
- package/lib/solution/ScanOrder/types.d.ts +86 -26
- package/lib/solution/ScanOrder/utils.d.ts +53 -5
- package/lib/solution/ScanOrder/utils.js +186 -19
- package/lib/solution/VenueBooking/index.d.ts +30 -10
- package/lib/solution/VenueBooking/index.js +206 -51
- package/lib/solution/VenueBooking/types.d.ts +23 -0
- package/lib/solution/VenueBooking/utils/dateSummary.d.ts +1 -1
- package/lib/solution/VenueBooking/utils/dateSummary.js +1 -1
- package/lib/solution/VenueBooking/utils/resource.d.ts +11 -1
- package/lib/solution/VenueBooking/utils/resource.js +15 -4
- package/lib/solution/VenueBooking/utils/slotMerge.d.ts +5 -0
- package/lib/solution/VenueBooking/utils/slotMerge.js +29 -12
- package/lib/solution/VenueBooking/utils/timeSlot.d.ts +1 -1
- package/lib/solution/VenueBooking/utils/timeSlot.js +182 -43
- package/package.json +1 -1
|
@@ -3,6 +3,7 @@ import type { ScanOrderTempOrder, ScanOrderOrderProduct, ScanOrderOrderProductId
|
|
|
3
3
|
import type { DiscountModule } from '../Discount';
|
|
4
4
|
import type { RulesModule } from '../Rules';
|
|
5
5
|
import type { Discount } from '../Discount/types';
|
|
6
|
+
import type { SubmitPayloadEnhancer } from './utils';
|
|
6
7
|
export declare enum OrderHooks {
|
|
7
8
|
OnOrderCreate = "order:onOrderCreate",
|
|
8
9
|
OnOrderUpdate = "order:onOrderUpdate",
|
|
@@ -15,6 +16,15 @@ export interface OrderState {
|
|
|
15
16
|
discount: DiscountModule | null;
|
|
16
17
|
rules: RulesModule | null;
|
|
17
18
|
}
|
|
19
|
+
/** 更新购物车行:仅传 SKU 时命中同 SKU 第一行;多行同 SKU 须传 identity_key / 选项指纹等 */
|
|
20
|
+
export interface UpdateProductInOrderParams {
|
|
21
|
+
product_id: number | null;
|
|
22
|
+
product_variant_id: number;
|
|
23
|
+
updates: Partial<ScanOrderOrderProduct>;
|
|
24
|
+
identity_key?: string;
|
|
25
|
+
product_option_item?: any[];
|
|
26
|
+
product_bundle?: any[];
|
|
27
|
+
}
|
|
18
28
|
/**
|
|
19
29
|
* 订单信息
|
|
20
30
|
*/
|
|
@@ -34,14 +44,27 @@ export interface SubmitScanOrderProduct {
|
|
|
34
44
|
num: number;
|
|
35
45
|
product_variant_id: number;
|
|
36
46
|
product_option_item: any[];
|
|
47
|
+
/**
|
|
48
|
+
* 行 composite 券后单价。
|
|
49
|
+
* 公式:`metadata.main_product_selling_price` + Σ(bundle_selling_price × num)。
|
|
50
|
+
* 主商品(含 option、含主商品折扣)的单价存在 `metadata.main_product_selling_price`。
|
|
51
|
+
*/
|
|
37
52
|
selling_price: string;
|
|
53
|
+
/**
|
|
54
|
+
* 行 composite 券前单价。
|
|
55
|
+
* 公式:`metadata.main_product_original_price` + Σ(bundle 原价 × num)。
|
|
56
|
+
* 主商品(含 option、不含折扣)的单价存在 `metadata.main_product_original_price`。
|
|
57
|
+
*/
|
|
38
58
|
original_price: string;
|
|
59
|
+
/** 出站兼容字段,直接由 `selling_price` 派生,语义同 composite。 */
|
|
39
60
|
payment_price: string;
|
|
40
61
|
tax_fee: string;
|
|
41
62
|
is_charge_tax: number;
|
|
42
63
|
discount_list: any[];
|
|
43
64
|
product_bundle: any[];
|
|
44
65
|
metadata: Record<string, any>;
|
|
66
|
+
/** 商品行备注 */
|
|
67
|
+
note?: string;
|
|
45
68
|
}
|
|
46
69
|
export interface SubmitScanOrderSummary {
|
|
47
70
|
product_quantity: number;
|
|
@@ -68,10 +91,12 @@ export interface SubmitScanOrderBooking {
|
|
|
68
91
|
start_time?: string;
|
|
69
92
|
end_time?: string;
|
|
70
93
|
metadata?: Record<string, any>;
|
|
94
|
+
resources?: Array<Record<string, any>>;
|
|
95
|
+
product_uid?: string;
|
|
71
96
|
}
|
|
72
97
|
export interface SubmitScanOrderFormRecord {
|
|
73
98
|
form_id: number | string;
|
|
74
|
-
|
|
99
|
+
form_record_id: number | string;
|
|
75
100
|
}
|
|
76
101
|
export interface SubmitScanOrderParams {
|
|
77
102
|
url?: string;
|
|
@@ -107,6 +132,8 @@ export interface SubmitScanOrderParams {
|
|
|
107
132
|
shop_discount: string;
|
|
108
133
|
surcharge_fee: string;
|
|
109
134
|
note: string;
|
|
135
|
+
delivery_type?: string;
|
|
136
|
+
table_number?: Record<string, any>;
|
|
110
137
|
schedule_date: string;
|
|
111
138
|
created_at?: string;
|
|
112
139
|
products: SubmitScanOrderProduct[];
|
|
@@ -195,19 +222,17 @@ export interface OrderModuleAPI {
|
|
|
195
222
|
getTempOrder: () => ScanOrderTempOrder | null;
|
|
196
223
|
ensureTempOrder: () => ScanOrderTempOrder;
|
|
197
224
|
addNewOrder: () => Promise<ScanOrderTempOrder>;
|
|
225
|
+
restoreOrder: () => ScanOrderTempOrder;
|
|
198
226
|
getOrderProducts: () => ScanOrderOrderProduct[];
|
|
199
227
|
getScanOrderSummary: () => Promise<ScanOrderSummary>;
|
|
200
228
|
recalculateSummary: (options?: {
|
|
201
229
|
createIfMissing?: boolean;
|
|
202
230
|
}) => Promise<ScanOrderSummary | null>;
|
|
203
231
|
updateTempOrderNote: (note: string) => string;
|
|
232
|
+
updateTempOrderBuzzer: (buzzer: string) => string;
|
|
204
233
|
updateTempOrderContactsInfo: (contactsInfo: any[]) => any[];
|
|
205
234
|
addProductToOrder: (product: Partial<ScanOrderOrderProduct> & ScanOrderOrderProductIdentity) => Promise<ScanOrderOrderProduct[]>;
|
|
206
|
-
updateProductInOrder: (params:
|
|
207
|
-
product_id: number | null;
|
|
208
|
-
product_variant_id: number;
|
|
209
|
-
updates: Partial<ScanOrderOrderProduct>;
|
|
210
|
-
}) => Promise<ScanOrderOrderProduct[]>;
|
|
235
|
+
updateProductInOrder: (params: UpdateProductInOrderParams) => Promise<ScanOrderOrderProduct[]>;
|
|
211
236
|
removeProductFromOrder: (identity: ScanOrderOrderProductIdentity) => Promise<ScanOrderOrderProduct[]>;
|
|
212
237
|
persistTempOrder: () => void;
|
|
213
238
|
submitTempOrder: <T = any>(params?: {
|
|
@@ -216,6 +241,7 @@ export interface OrderModuleAPI {
|
|
|
216
241
|
businessCode?: string;
|
|
217
242
|
channel?: string;
|
|
218
243
|
type?: string;
|
|
244
|
+
enhancePayload?: SubmitPayloadEnhancer;
|
|
219
245
|
}) => Promise<T>;
|
|
220
246
|
loadDiscountConfig: (params: {
|
|
221
247
|
customerId: number;
|
|
@@ -1,21 +1,66 @@
|
|
|
1
|
+
import Decimal from 'decimal.js';
|
|
1
2
|
import { CartItem } from "../Cart";
|
|
2
3
|
import type { ScanOrderSubmitPayload, ScanOrderSubmitProduct, ScanOrderSummary, ScanOrderTempOrder } from '../../solution/ScanOrder/types';
|
|
3
4
|
import type { RulesParamsHooks } from '../Rules/types';
|
|
5
|
+
/**
|
|
6
|
+
* 把"含 option 的主商品单价"与 bundle 合成"单行 composite 单价"。
|
|
7
|
+
*
|
|
8
|
+
* 新语义 v2 约定:
|
|
9
|
+
* - `mainPrice` 必须是**已经包含 option 的主商品单价**(即 `metadata.main_product_selling_price`
|
|
10
|
+
* 或 `metadata.main_product_original_price`),option 价格不由本函数叠加。
|
|
11
|
+
* - 套餐价:Σ((bundle.bundle_selling_price ?? bundle.price) × (bundle.num ?? bundle.quantity ?? 1))
|
|
12
|
+
* 当 `useOriginalBundle=true` 时,改用 `bundle.original_price ?? bundle.product_price ?? bundle.price`。
|
|
13
|
+
* - 返回值:保留 2 位小数的字符串,方便直接写回 `selling_price` / `original_price` 字段。
|
|
14
|
+
*
|
|
15
|
+
* 被 `normalizeOrderProduct`、Rules setProduct 钩子、各 Solution 的 setDiscountSelected 共用,
|
|
16
|
+
* 保证合成逻辑单点来源。
|
|
17
|
+
*/
|
|
18
|
+
export declare function composeLinePrice(params: {
|
|
19
|
+
mainPrice: string | number | null | undefined;
|
|
20
|
+
bundle?: Array<{
|
|
21
|
+
bundle_selling_price?: any;
|
|
22
|
+
price?: any;
|
|
23
|
+
num?: any;
|
|
24
|
+
quantity?: any;
|
|
25
|
+
original_price?: any;
|
|
26
|
+
product_price?: any;
|
|
27
|
+
}> | null;
|
|
28
|
+
useOriginalBundle?: boolean;
|
|
29
|
+
}): string;
|
|
30
|
+
/**
|
|
31
|
+
* 计算 option 单价合计:Σ(option.price × option.num)。
|
|
32
|
+
* 在新语义 v2 下,main_product_original_price = source_product_price + 本函数结果。
|
|
33
|
+
*/
|
|
34
|
+
export declare function sumOptionUnitPrice(options?: Array<{
|
|
35
|
+
price?: any;
|
|
36
|
+
num?: any;
|
|
37
|
+
}> | null): Decimal;
|
|
4
38
|
/**
|
|
5
39
|
* OrderModule 默认 Rules 钩子工厂。
|
|
6
40
|
*
|
|
7
|
-
*
|
|
8
|
-
* - `selling_price
|
|
9
|
-
*
|
|
10
|
-
* - `
|
|
41
|
+
* 价格语义约定(订单域,v2 composite 口径):
|
|
42
|
+
* - `selling_price`:券后 **行 composite 单价**
|
|
43
|
+
* = `metadata.main_product_selling_price` + Σ(bundle_selling_price × num)。
|
|
44
|
+
* - `original_price`:券前 **行 composite 单价**
|
|
45
|
+
* = `metadata.main_product_original_price` + Σ(bundle 原价 × num)。
|
|
46
|
+
* - `metadata.source_product_price`:主商品/variant 基础价(已应用报价单),不含 option、
|
|
47
|
+
* 不含折扣。**权威源**。
|
|
48
|
+
* - `metadata.main_product_original_price` = source + Σ(option.price × option.num)(含 option、不含折扣)。
|
|
49
|
+
* - `metadata.main_product_selling_price` = main_original − 主商品券 per-unit amount(含 option、含折扣)。
|
|
50
|
+
* - `payment_price`:出站 payload 中由 `selling_price` 派生以兼容后端(composite)。
|
|
11
51
|
*
|
|
12
|
-
* Rules
|
|
13
|
-
* - `getProduct.
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
52
|
+
* Rules 钩子契约(hook_adapt 方案,Rules 内部保持不变):
|
|
53
|
+
* - `getProduct.price` / `getProduct.original_price` 回传 **source-level 主价**
|
|
54
|
+
* (`metadata.source_product_price`)。Rules 内部 `getProductTotalPrice` /
|
|
55
|
+
* `getProductOriginTotalPrice` 会自行叠加 option + bundle,喂 source 可避免 option 双加。
|
|
56
|
+
* - `getProduct.total` = `selling_price × num`(行 composite × num),与 Rules 内部产出的
|
|
57
|
+
* total(source + option + bundle)对齐。
|
|
58
|
+
* - `setProduct` 反向流程:把 Rules 返回的 source-level `main_product_selling_price` / `price`
|
|
59
|
+
* 加回 Σoptions,重新合成含 option 的 main_product_* 并派生 composite。
|
|
60
|
+
* 优先级:
|
|
61
|
+
* 1. `values.main_product_selling_price`(券应用分支,per-unit 券后 source-level 主价)
|
|
62
|
+
* 2. `values.price`(还原分支 `restoredPrice`、good_pass 归零)
|
|
63
|
+
* 3. `values.total / num`(仅当 Rules 只给出总价时的兜底)
|
|
19
64
|
*
|
|
20
65
|
* 导出为纯函数方便单测直接驱动钩子,不依赖 OrderModule 实例。
|
|
21
66
|
*/
|
|
@@ -56,6 +101,12 @@ export declare function createDefaultTempOrder(params: {
|
|
|
56
101
|
now: string;
|
|
57
102
|
summary?: ScanOrderSummary;
|
|
58
103
|
}): ScanOrderTempOrder;
|
|
104
|
+
export interface SubmitPayloadEnhancerContext {
|
|
105
|
+
tempOrder: ScanOrderTempOrder;
|
|
106
|
+
bookingUuid: string;
|
|
107
|
+
now: Date;
|
|
108
|
+
}
|
|
109
|
+
export type SubmitPayloadEnhancer = (payload: ScanOrderSubmitPayload, ctx: SubmitPayloadEnhancerContext) => ScanOrderSubmitPayload;
|
|
59
110
|
export declare function buildSubmitPayload(params: {
|
|
60
111
|
tempOrder: ScanOrderTempOrder;
|
|
61
112
|
cacheId?: string;
|
|
@@ -64,7 +115,17 @@ export declare function buildSubmitPayload(params: {
|
|
|
64
115
|
businessCode?: string;
|
|
65
116
|
channel?: string;
|
|
66
117
|
type?: string;
|
|
118
|
+
enhance?: SubmitPayloadEnhancer;
|
|
67
119
|
}): ScanOrderSubmitPayload;
|
|
120
|
+
/** 加单(scanOrderMore)不应提交 booking 关联的虚拟规则商品行 */
|
|
121
|
+
export declare function filterProductsForScanOrderMore(products: ScanOrderSubmitProduct[]): ScanOrderSubmitProduct[];
|
|
122
|
+
/**
|
|
123
|
+
* 历史 V1 加餐结构映射器。
|
|
124
|
+
*
|
|
125
|
+
* @deprecated 加餐(`PUT /order/order/product/:id`)已改为与正常 checkout 共用
|
|
126
|
+
* `ScanOrderSubmitProduct` 新结构(见 `OrderModule.submitTempOrder` 的加餐分支)。
|
|
127
|
+
* 本函数仅保留以兼容历史接入方,未来会移除。新代码请勿再调用。
|
|
128
|
+
*/
|
|
68
129
|
export declare function formatV1Product(products: ScanOrderSubmitProduct[]): {
|
|
69
130
|
bundle: any[];
|
|
70
131
|
key: number | null;
|
|
@@ -72,6 +133,7 @@ export declare function formatV1Product(products: ScanOrderSubmitProduct[]): {
|
|
|
72
133
|
product_id: number | null;
|
|
73
134
|
product_variant_id: number;
|
|
74
135
|
num: number;
|
|
136
|
+
note: string;
|
|
75
137
|
rowKey: number | null;
|
|
76
138
|
session: null;
|
|
77
139
|
unique: string;
|
|
@@ -30,10 +30,12 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
var utils_exports = {};
|
|
31
31
|
__export(utils_exports, {
|
|
32
32
|
buildSubmitPayload: () => buildSubmitPayload,
|
|
33
|
+
composeLinePrice: () => composeLinePrice,
|
|
33
34
|
createDefaultOrderRulesHooks: () => createDefaultOrderRulesHooks,
|
|
34
35
|
createDefaultTempOrder: () => createDefaultTempOrder,
|
|
35
36
|
createEmptySummary: () => import_utils2.createEmptySummary,
|
|
36
37
|
createUuidV4: () => createUuidV4,
|
|
38
|
+
filterProductsForScanOrderMore: () => filterProductsForScanOrderMore,
|
|
37
39
|
formatDateTime: () => formatDateTime,
|
|
38
40
|
formatV1Product: () => formatV1Product,
|
|
39
41
|
generateDuration: () => generateDuration,
|
|
@@ -42,13 +44,39 @@ __export(utils_exports, {
|
|
|
42
44
|
mergeRelationForms: () => mergeRelationForms,
|
|
43
45
|
normalizeSubmitBooking: () => normalizeSubmitBooking,
|
|
44
46
|
normalizeSubmitCollectPaxValue: () => normalizeSubmitCollectPaxValue,
|
|
45
|
-
resolveSubmitCollectPax: () => resolveSubmitCollectPax
|
|
47
|
+
resolveSubmitCollectPax: () => resolveSubmitCollectPax,
|
|
48
|
+
sumOptionUnitPrice: () => sumOptionUnitPrice
|
|
46
49
|
});
|
|
47
50
|
module.exports = __toCommonJS(utils_exports);
|
|
48
51
|
var import_dayjs = __toESM(require("dayjs"));
|
|
49
52
|
var import_decimal = __toESM(require("decimal.js"));
|
|
50
53
|
var import_utils = require("../../solution/ScanOrder/utils");
|
|
51
54
|
var import_utils2 = require("../../solution/ScanOrder/utils");
|
|
55
|
+
function composeLinePrice(params) {
|
|
56
|
+
const { mainPrice, bundle, useOriginalBundle } = params;
|
|
57
|
+
let total = new import_decimal.default(Number(mainPrice) || 0);
|
|
58
|
+
if (Array.isArray(bundle)) {
|
|
59
|
+
for (const item of bundle) {
|
|
60
|
+
const rawPrice = useOriginalBundle ? (item == null ? void 0 : item.original_price) ?? (item == null ? void 0 : item.product_price) ?? (item == null ? void 0 : item.price) : (item == null ? void 0 : item.bundle_selling_price) ?? (item == null ? void 0 : item.price);
|
|
61
|
+
const price = new import_decimal.default(Number(rawPrice) || 0);
|
|
62
|
+
const rawNum = (item == null ? void 0 : item.num) ?? (item == null ? void 0 : item.quantity) ?? 1;
|
|
63
|
+
const num = new import_decimal.default(Number(rawNum) || 0);
|
|
64
|
+
total = total.plus(price.times(num));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return total.toDecimalPlaces(2).toFixed(2);
|
|
68
|
+
}
|
|
69
|
+
function sumOptionUnitPrice(options) {
|
|
70
|
+
let total = new import_decimal.default(0);
|
|
71
|
+
if (!Array.isArray(options))
|
|
72
|
+
return total;
|
|
73
|
+
for (const opt of options) {
|
|
74
|
+
const price = new import_decimal.default(Number(opt == null ? void 0 : opt.price) || 0);
|
|
75
|
+
const num = new import_decimal.default(Number(opt == null ? void 0 : opt.num) || 0);
|
|
76
|
+
total = total.plus(price.times(num));
|
|
77
|
+
}
|
|
78
|
+
return total;
|
|
79
|
+
}
|
|
52
80
|
function createDefaultOrderRulesHooks() {
|
|
53
81
|
const toUnitPriceString = (totalLike, num) => {
|
|
54
82
|
const effectiveNum = Number(num) > 0 ? Number(num) : 1;
|
|
@@ -57,13 +85,47 @@ function createDefaultOrderRulesHooks() {
|
|
|
57
85
|
return {
|
|
58
86
|
getProduct: (product) => {
|
|
59
87
|
var _a, _b, _c, _d;
|
|
88
|
+
const metadataAny = product.metadata || {};
|
|
89
|
+
const optionSum = sumOptionUnitPrice(product.product_option_item);
|
|
90
|
+
let sourcePrice;
|
|
91
|
+
if (metadataAny.source_product_price !== void 0) {
|
|
92
|
+
sourcePrice = String(metadataAny.source_product_price);
|
|
93
|
+
} else if (metadataAny.main_product_selling_price !== void 0) {
|
|
94
|
+
sourcePrice = new import_decimal.default(
|
|
95
|
+
Number(metadataAny.main_product_selling_price) || 0
|
|
96
|
+
).minus(optionSum).toDecimalPlaces(2).toString();
|
|
97
|
+
} else {
|
|
98
|
+
sourcePrice = String(product.selling_price ?? 0);
|
|
99
|
+
}
|
|
100
|
+
let sourceOriginalPrice;
|
|
101
|
+
if (metadataAny.source_product_price !== void 0) {
|
|
102
|
+
sourceOriginalPrice = String(metadataAny.source_product_price);
|
|
103
|
+
} else if (metadataAny.main_product_original_price !== void 0) {
|
|
104
|
+
sourceOriginalPrice = new import_decimal.default(
|
|
105
|
+
Number(metadataAny.main_product_original_price) || 0
|
|
106
|
+
).minus(optionSum).toDecimalPlaces(2).toString();
|
|
107
|
+
} else {
|
|
108
|
+
sourceOriginalPrice = sourcePrice;
|
|
109
|
+
}
|
|
60
110
|
return {
|
|
61
111
|
id: product.product_id,
|
|
62
|
-
_id
|
|
63
|
-
|
|
112
|
+
// Rules 引擎用 _id 作为 processedProductsMap 键;必须与购物车行级 identity 一致,
|
|
113
|
+
// 否则同 SKU 不同小料会在 calcDiscount 重组时互相覆盖。
|
|
114
|
+
// 不透明 identity 契约下,normalizeOrderProduct / restoreTempOrderFromStorage 已保证 identity_key 必存在;
|
|
115
|
+
// 留 fingerprint 分支仅作 Rules 单测里直接传裸 product 时的兜底。
|
|
116
|
+
_id: product.identity_key ? `${product.product_id}_${product.product_variant_id}_${product.identity_key}` : `${product.product_id}_${product.product_variant_id}_${(0, import_utils.buildProductLineFingerprint)(
|
|
117
|
+
product.product_option_item,
|
|
118
|
+
product.product_bundle
|
|
119
|
+
)}`,
|
|
120
|
+
// Rules 内部 getProductTotalPrice / getProductOriginTotalPrice 会各自再叠加 bundle + option,
|
|
121
|
+
// 所以这里必须喂 source-level(不含 option、不含 bundle、不含折扣)单价,避免双加 option。
|
|
122
|
+
price: sourcePrice,
|
|
123
|
+
// total / origin_total 使用 composite × num,跟 Rules 内部产出的 total 对齐(含 option + bundle)。
|
|
64
124
|
total: new import_decimal.default(product.selling_price || 0).times(product.num || 1).toNumber(),
|
|
65
|
-
origin_total: new import_decimal.default(
|
|
66
|
-
|
|
125
|
+
origin_total: new import_decimal.default(
|
|
126
|
+
product.original_price || product.selling_price || 0
|
|
127
|
+
).times(product.num || 1).toNumber(),
|
|
128
|
+
original_price: sourceOriginalPrice,
|
|
67
129
|
quantity: product.num || 1,
|
|
68
130
|
num: product.num || 1,
|
|
69
131
|
discount_list: product.discount_list || [],
|
|
@@ -77,17 +139,37 @@ function createDefaultOrderRulesHooks() {
|
|
|
77
139
|
},
|
|
78
140
|
setProduct: (product, values) => {
|
|
79
141
|
const nextNum = Number(values.quantity ?? product.num ?? 1) || 1;
|
|
80
|
-
const
|
|
142
|
+
const metadataAny = product.metadata || {};
|
|
143
|
+
const existedSource = metadataAny.source_product_price ?? product.selling_price ?? "0";
|
|
144
|
+
const nextOptions = product.product_option_item;
|
|
145
|
+
const optionSum = sumOptionUnitPrice(nextOptions);
|
|
146
|
+
const nextSourceSellingPrice = 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) : String(existedSource);
|
|
147
|
+
const nextSourceOriginalPrice = values.original_price !== void 0 ? String(values.original_price) : String(existedSource);
|
|
148
|
+
const nextMainSellingPrice = new import_decimal.default(Number(nextSourceSellingPrice) || 0).plus(optionSum).toDecimalPlaces(2).toFixed(2);
|
|
149
|
+
const nextMainOriginalPrice = new import_decimal.default(Number(nextSourceOriginalPrice) || 0).plus(optionSum).toDecimalPlaces(2).toFixed(2);
|
|
150
|
+
const nextBundle = values.bundle ?? product.product_bundle;
|
|
151
|
+
const composedSellingPrice = composeLinePrice({
|
|
152
|
+
mainPrice: nextMainSellingPrice,
|
|
153
|
+
bundle: nextBundle
|
|
154
|
+
});
|
|
155
|
+
const composedOriginalPrice = composeLinePrice({
|
|
156
|
+
mainPrice: nextMainOriginalPrice,
|
|
157
|
+
bundle: nextBundle,
|
|
158
|
+
useOriginalBundle: true
|
|
159
|
+
});
|
|
81
160
|
return {
|
|
82
161
|
...product,
|
|
83
|
-
selling_price:
|
|
84
|
-
original_price:
|
|
162
|
+
selling_price: composedSellingPrice,
|
|
163
|
+
original_price: composedOriginalPrice,
|
|
85
164
|
discount_list: values.discount_list ?? product.discount_list,
|
|
86
165
|
num: values.quantity ?? product.num,
|
|
87
|
-
product_bundle:
|
|
166
|
+
product_bundle: nextBundle,
|
|
88
167
|
metadata: {
|
|
89
|
-
...
|
|
90
|
-
|
|
168
|
+
...metadataAny,
|
|
169
|
+
source_product_price: nextSourceSellingPrice,
|
|
170
|
+
main_product_selling_price: nextMainSellingPrice,
|
|
171
|
+
main_product_original_price: nextMainOriginalPrice,
|
|
172
|
+
price_schema_version: 2
|
|
91
173
|
}
|
|
92
174
|
};
|
|
93
175
|
}
|
|
@@ -151,6 +233,77 @@ function formatDateTime(date) {
|
|
|
151
233
|
function normalizeSubmitPlatform(platform) {
|
|
152
234
|
return (platform == null ? void 0 : platform.toLowerCase()) === "pc" ? "PC" : "H5";
|
|
153
235
|
}
|
|
236
|
+
function formatSubmitOptionItems(options) {
|
|
237
|
+
if (!Array.isArray(options))
|
|
238
|
+
return [];
|
|
239
|
+
return options.map((d) => ({
|
|
240
|
+
num: d == null ? void 0 : d.num,
|
|
241
|
+
option_group_item_id: (d == null ? void 0 : d.option_group_item_id) ?? (d == null ? void 0 : d.product_option_item_id),
|
|
242
|
+
option_group_id: d == null ? void 0 : d.option_group_id
|
|
243
|
+
}));
|
|
244
|
+
}
|
|
245
|
+
function toBundleNumber(value, fallback = 0) {
|
|
246
|
+
const parsed = Number(value);
|
|
247
|
+
return Number.isFinite(parsed) ? parsed : fallback;
|
|
248
|
+
}
|
|
249
|
+
function toBundleCustomPriceString(value) {
|
|
250
|
+
const parsed = Number(value);
|
|
251
|
+
if (Number.isFinite(parsed))
|
|
252
|
+
return parsed.toFixed(2);
|
|
253
|
+
if (value === null || value === void 0 || value === "")
|
|
254
|
+
return "0.00";
|
|
255
|
+
return String(value);
|
|
256
|
+
}
|
|
257
|
+
function formatSubmitBundleItems(bundle) {
|
|
258
|
+
if (!Array.isArray(bundle))
|
|
259
|
+
return [];
|
|
260
|
+
return bundle.map((b) => {
|
|
261
|
+
const rawBundle = b && typeof b === "object" ? b : {};
|
|
262
|
+
const existedMetadata = rawBundle.metadata && typeof rawBundle.metadata === "object" ? rawBundle.metadata : {};
|
|
263
|
+
const sellingPriceNum = toBundleNumber(
|
|
264
|
+
rawBundle.bundle_selling_price ?? rawBundle.price,
|
|
265
|
+
0
|
|
266
|
+
);
|
|
267
|
+
const priceNum = toBundleNumber(rawBundle.price, sellingPriceNum);
|
|
268
|
+
const priceType = rawBundle.price_type ?? "";
|
|
269
|
+
const customPriceStr = toBundleCustomPriceString(
|
|
270
|
+
rawBundle.custom_price ?? rawBundle.bundle_selling_price ?? rawBundle.price
|
|
271
|
+
);
|
|
272
|
+
const relationSurchargeIds = Array.isArray(rawBundle.relation_surcharge_ids) ? rawBundle.relation_surcharge_ids : Array.isArray(existedMetadata.relation_surcharge_ids) ? existedMetadata.relation_surcharge_ids : [];
|
|
273
|
+
const surchargeFee = toBundleNumber(
|
|
274
|
+
rawBundle.surcharge_fee ?? existedMetadata.surcharge_fee,
|
|
275
|
+
0
|
|
276
|
+
);
|
|
277
|
+
const productDiscountDifference = toBundleNumber(
|
|
278
|
+
existedMetadata.product_discount_difference,
|
|
279
|
+
0
|
|
280
|
+
);
|
|
281
|
+
return {
|
|
282
|
+
...rawBundle,
|
|
283
|
+
is_charge_tax: rawBundle.is_charge_tax ?? 0,
|
|
284
|
+
tax_fee: toBundleNumber(rawBundle.tax_fee, 0),
|
|
285
|
+
bundle_variant_id: rawBundle.bundle_variant_id ?? 0,
|
|
286
|
+
num: toBundleNumber(rawBundle.num, 1),
|
|
287
|
+
extension_id: rawBundle.extension_id ?? 0,
|
|
288
|
+
extension_type: rawBundle.extension_type ?? "normal",
|
|
289
|
+
price: priceNum,
|
|
290
|
+
price_type: priceType,
|
|
291
|
+
price_type_ext: rawBundle.price_type_ext ?? "",
|
|
292
|
+
custom_price: customPriceStr,
|
|
293
|
+
custom_price_type: rawBundle.custom_price_type ?? priceType ?? "",
|
|
294
|
+
bundle_selling_price: sellingPriceNum,
|
|
295
|
+
option: formatSubmitOptionItems(rawBundle.option),
|
|
296
|
+
bundle_group_id: rawBundle == null ? void 0 : rawBundle.group_id,
|
|
297
|
+
bundle_id: rawBundle == null ? void 0 : rawBundle.id,
|
|
298
|
+
metadata: {
|
|
299
|
+
...existedMetadata,
|
|
300
|
+
surcharge_fee: surchargeFee,
|
|
301
|
+
relation_surcharge_ids: relationSurchargeIds,
|
|
302
|
+
product_discount_difference: productDiscountDifference
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
});
|
|
306
|
+
}
|
|
154
307
|
function normalizeSubmitProduct(product) {
|
|
155
308
|
const { _origin, identity_key, ...submitProduct } = product;
|
|
156
309
|
const rawMetadata = submitProduct.metadata || {};
|
|
@@ -162,7 +315,8 @@ function normalizeSubmitProduct(product) {
|
|
|
162
315
|
const priceMetaKeys = [
|
|
163
316
|
"main_product_original_price",
|
|
164
317
|
"main_product_selling_price",
|
|
165
|
-
"source_product_price"
|
|
318
|
+
"source_product_price",
|
|
319
|
+
"price_schema_version"
|
|
166
320
|
];
|
|
167
321
|
for (const key of priceMetaKeys) {
|
|
168
322
|
if (rawMetadata[key] !== void 0) {
|
|
@@ -172,14 +326,18 @@ function normalizeSubmitProduct(product) {
|
|
|
172
326
|
if (rawMetadata.price_breakdown) {
|
|
173
327
|
cleanMetadata.price_breakdown = rawMetadata.price_breakdown;
|
|
174
328
|
}
|
|
329
|
+
if (rawMetadata.is_rule !== void 0) {
|
|
330
|
+
cleanMetadata.is_rule = rawMetadata.is_rule;
|
|
331
|
+
}
|
|
175
332
|
return {
|
|
176
333
|
...submitProduct,
|
|
177
334
|
...bookingUid ? { booking_uid: bookingUid } : {},
|
|
178
|
-
product_option_item: submitProduct.product_option_item
|
|
335
|
+
product_option_item: formatSubmitOptionItems(submitProduct.product_option_item),
|
|
179
336
|
discount_list: submitProduct.discount_list || [],
|
|
180
|
-
product_bundle: submitProduct.product_bundle
|
|
337
|
+
product_bundle: formatSubmitBundleItems(submitProduct.product_bundle),
|
|
181
338
|
metadata: cleanMetadata,
|
|
182
|
-
//
|
|
339
|
+
// 出站兼容:后端消费 payment_price 字段,这里从 selling_price 直接派生。
|
|
340
|
+
// 新语义下 selling_price 是 composite(含 option/bundle),payment_price 同语义。
|
|
183
341
|
payment_price: submitProduct.selling_price
|
|
184
342
|
};
|
|
185
343
|
}
|
|
@@ -286,7 +444,8 @@ function buildSubmitPayload(params) {
|
|
|
286
444
|
platform,
|
|
287
445
|
businessCode,
|
|
288
446
|
channel,
|
|
289
|
-
type
|
|
447
|
+
type,
|
|
448
|
+
enhance
|
|
290
449
|
} = params;
|
|
291
450
|
const scheduleDate = tempOrder.schedule_date || tempOrder.created_at || formatDateTime(now);
|
|
292
451
|
const summary = tempOrder.summary || (0, import_utils.createEmptySummary)();
|
|
@@ -297,8 +456,8 @@ function buildSubmitPayload(params) {
|
|
|
297
456
|
const bookingDuration = resolveTableOccupancyDuration(tempOrder);
|
|
298
457
|
const bookingEnd = bookingStart.add(bookingDuration, "minute");
|
|
299
458
|
const bookings = relationId && tableFormId ? [{
|
|
300
|
-
relation_id:
|
|
301
|
-
form_id:
|
|
459
|
+
relation_id: 0,
|
|
460
|
+
form_id: 0,
|
|
302
461
|
start_time: bookingStart.format("HH:mm"),
|
|
303
462
|
start_date: bookingStart.format("YYYY-MM-DD"),
|
|
304
463
|
end_time: bookingEnd.format("HH:mm"),
|
|
@@ -310,17 +469,17 @@ function buildSubmitPayload(params) {
|
|
|
310
469
|
},
|
|
311
470
|
select_date: bookingStart.format("YYYY-MM-DD"),
|
|
312
471
|
is_all: false,
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
472
|
+
like_status: "common",
|
|
473
|
+
schedule_id: 0,
|
|
474
|
+
relation_type: "",
|
|
475
|
+
number: 1
|
|
317
476
|
}] : tempOrder.bookings || [];
|
|
318
477
|
const formRecordIds = relationId && tableFormId ? [{
|
|
319
478
|
form_id: tableFormId,
|
|
320
|
-
|
|
479
|
+
form_record_id: relationId
|
|
321
480
|
}] : void 0;
|
|
322
481
|
const { created_at: _createdAt, summary: _summary, surcharges: _surcharges, ...tempOrderRest } = tempOrder;
|
|
323
|
-
|
|
482
|
+
const payload = {
|
|
324
483
|
...tempOrderRest,
|
|
325
484
|
platform: normalizeSubmitPlatform(platform ?? tempOrder.platform),
|
|
326
485
|
request_unique_idempotency_token: cacheId,
|
|
@@ -348,13 +507,25 @@ function buildSubmitPayload(params) {
|
|
|
348
507
|
contacts_info: tempOrder.contacts_info || [],
|
|
349
508
|
// holder: tempOrder.holder || null,
|
|
350
509
|
// summary,
|
|
351
|
-
metadata: {
|
|
352
|
-
|
|
353
|
-
|
|
510
|
+
metadata: (() => {
|
|
511
|
+
const {
|
|
512
|
+
collect_pax: _collectPax,
|
|
513
|
+
table_occupancy_duration: _tableOccupancyDuration,
|
|
514
|
+
...rest
|
|
515
|
+
} = tempOrder.metadata || {};
|
|
516
|
+
return { ...rest };
|
|
517
|
+
})(),
|
|
354
518
|
products: (tempOrder.products || []).map(
|
|
355
519
|
(product) => normalizeSubmitProduct(product)
|
|
356
520
|
)
|
|
357
521
|
};
|
|
522
|
+
return enhance ? enhance(payload, { tempOrder, bookingUuid, now }) : payload;
|
|
523
|
+
}
|
|
524
|
+
function filterProductsForScanOrderMore(products) {
|
|
525
|
+
return (products || []).filter((p) => {
|
|
526
|
+
var _a;
|
|
527
|
+
return ((_a = p.metadata) == null ? void 0 : _a.is_rule) !== true;
|
|
528
|
+
});
|
|
358
529
|
}
|
|
359
530
|
function formatV1Product(products) {
|
|
360
531
|
return products.map((product) => {
|
|
@@ -365,6 +536,7 @@ function formatV1Product(products) {
|
|
|
365
536
|
product_id: product.product_id,
|
|
366
537
|
product_variant_id: product.product_variant_id,
|
|
367
538
|
num: product.num,
|
|
539
|
+
note: String(product.note ?? ""),
|
|
368
540
|
rowKey: product.product_id,
|
|
369
541
|
session: null,
|
|
370
542
|
unique: createUuidV4()
|
|
@@ -374,10 +546,12 @@ function formatV1Product(products) {
|
|
|
374
546
|
// Annotate the CommonJS export names for ESM import in node:
|
|
375
547
|
0 && (module.exports = {
|
|
376
548
|
buildSubmitPayload,
|
|
549
|
+
composeLinePrice,
|
|
377
550
|
createDefaultOrderRulesHooks,
|
|
378
551
|
createDefaultTempOrder,
|
|
379
552
|
createEmptySummary,
|
|
380
553
|
createUuidV4,
|
|
554
|
+
filterProductsForScanOrderMore,
|
|
381
555
|
formatDateTime,
|
|
382
556
|
formatV1Product,
|
|
383
557
|
generateDuration,
|
|
@@ -386,5 +560,6 @@ function formatV1Product(products) {
|
|
|
386
560
|
mergeRelationForms,
|
|
387
561
|
normalizeSubmitBooking,
|
|
388
562
|
normalizeSubmitCollectPaxValue,
|
|
389
|
-
resolveSubmitCollectPax
|
|
563
|
+
resolveSubmitCollectPax,
|
|
564
|
+
sumOptionUnitPrice
|
|
390
565
|
});
|
|
@@ -48,30 +48,6 @@ function getSafeNum(num) {
|
|
|
48
48
|
return 1;
|
|
49
49
|
return Math.floor(num);
|
|
50
50
|
}
|
|
51
|
-
function getVariantPrice(product) {
|
|
52
|
-
var _a, _b;
|
|
53
|
-
const variantId = Number(product.product_variant_id || 0);
|
|
54
|
-
if (!variantId)
|
|
55
|
-
return null;
|
|
56
|
-
const metadataVariantList = (_b = (_a = product.metadata) == null ? void 0 : _a.origin) == null ? void 0 : _b.variant;
|
|
57
|
-
if (Array.isArray(metadataVariantList)) {
|
|
58
|
-
const variant = metadataVariantList.find(
|
|
59
|
-
(item) => Number(item.id) === variantId
|
|
60
|
-
);
|
|
61
|
-
if ((variant == null ? void 0 : variant.price) !== void 0 && (variant == null ? void 0 : variant.price) !== null) {
|
|
62
|
-
return toDecimal(variant.price);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
return null;
|
|
66
|
-
}
|
|
67
|
-
function getOptionUnitPrice(product) {
|
|
68
|
-
const optionItems = product.product_option_item || [];
|
|
69
|
-
return optionItems.reduce((sum, item) => {
|
|
70
|
-
const quantity = getSafeNum(item == null ? void 0 : item.num);
|
|
71
|
-
const itemPrice = toDecimal(item == null ? void 0 : item.price);
|
|
72
|
-
return sum.plus(itemPrice.times(quantity));
|
|
73
|
-
}, new import_decimal.default(0));
|
|
74
|
-
}
|
|
75
51
|
function getBundleUnitPrice(product, useOriginal = false) {
|
|
76
52
|
const bundleItems = product.product_bundle || [];
|
|
77
53
|
return bundleItems.reduce((sum, item) => {
|
|
@@ -81,13 +57,18 @@ function getBundleUnitPrice(product, useOriginal = false) {
|
|
|
81
57
|
}, new import_decimal.default(0));
|
|
82
58
|
}
|
|
83
59
|
function getUnitPaymentTotal(product) {
|
|
84
|
-
|
|
85
|
-
const
|
|
86
|
-
|
|
60
|
+
var _a, _b;
|
|
61
|
+
const mainSelling = (_a = product.metadata) == null ? void 0 : _a.main_product_selling_price;
|
|
62
|
+
const mainOriginal = (_b = product.metadata) == null ? void 0 : _b.main_product_original_price;
|
|
63
|
+
const basePrice = mainSelling !== void 0 ? toDecimal(mainSelling) : mainOriginal !== void 0 ? toDecimal(mainOriginal) : toDecimal(product.selling_price);
|
|
64
|
+
return basePrice.plus(getBundleUnitPrice(product));
|
|
87
65
|
}
|
|
88
66
|
function getUnitOriginalTotal(product) {
|
|
89
|
-
|
|
90
|
-
|
|
67
|
+
var _a, _b;
|
|
68
|
+
const mainOriginal = (_a = product.metadata) == null ? void 0 : _a.main_product_original_price;
|
|
69
|
+
const mainSelling = (_b = product.metadata) == null ? void 0 : _b.main_product_selling_price;
|
|
70
|
+
const basePrice = mainOriginal !== void 0 ? toDecimal(mainOriginal) : mainSelling !== void 0 ? toDecimal(mainSelling) : toDecimal(product.original_price || product.selling_price);
|
|
71
|
+
return basePrice.plus(getBundleUnitPrice(product, true));
|
|
91
72
|
}
|
|
92
73
|
function buildSurchargeServiceItems(products) {
|
|
93
74
|
return products.map((product) => {
|
|
@@ -207,12 +188,6 @@ function isBundleMarkupOrDiscount(item) {
|
|
|
207
188
|
const isDiscount = (item == null ? void 0 : item.price_type) === "markdown" && ((item == null ? void 0 : item.price_type_ext) === "" || !(item == null ? void 0 : item.price_type_ext));
|
|
208
189
|
return isMarkup || isDiscount;
|
|
209
190
|
}
|
|
210
|
-
function getDiscountListAmount(discountList) {
|
|
211
|
-
return (discountList || []).reduce(
|
|
212
|
-
(total, d) => total.plus(toDecimal(d.amount)),
|
|
213
|
-
new import_decimal.default(0)
|
|
214
|
-
);
|
|
215
|
-
}
|
|
216
191
|
function calculateSingleItemTax(params) {
|
|
217
192
|
const { price, taxRate, isPriceIncludeTax, isChargeTax } = params;
|
|
218
193
|
if (price.lte(0))
|
|
@@ -229,18 +204,9 @@ function calculateSingleItemTax(params) {
|
|
|
229
204
|
return price.dividedBy(divisor).times(rate);
|
|
230
205
|
}
|
|
231
206
|
function getMainProductPaymentTotal(product) {
|
|
232
|
-
var _a, _b
|
|
233
|
-
const
|
|
234
|
-
let total =
|
|
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
|
-
const mainDiscountAmount = getDiscountListAmount(
|
|
237
|
-
mainDiscountList.filter((d) => {
|
|
238
|
-
var _a2;
|
|
239
|
-
return !((_a2 = d == null ? void 0 : d.metadata) == null ? void 0 : _a2.custom_product_bundle_map_id);
|
|
240
|
-
})
|
|
241
|
-
);
|
|
242
|
-
total = total.minus(mainDiscountAmount);
|
|
243
|
-
total = total.plus(getOptionUnitPrice(product));
|
|
207
|
+
var _a, _b;
|
|
208
|
+
const mainSelling = ((_a = product.metadata) == null ? void 0 : _a.main_product_selling_price) ?? ((_b = product.metadata) == null ? void 0 : _b.main_product_original_price) ?? 0;
|
|
209
|
+
let total = toDecimal(mainSelling);
|
|
244
210
|
const bundleItems = product.product_bundle || [];
|
|
245
211
|
for (const bundleItem of bundleItems) {
|
|
246
212
|
if (isBundleMarkupOrDiscount(bundleItem)) {
|