@pisell/pisellos 0.0.509 → 0.0.511
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 +3 -6
- package/dist/modules/Order/index.js +114 -57
- package/dist/modules/Order/types.d.ts +12 -5
- package/dist/modules/Order/types.js +2 -0
- package/dist/modules/Order/utils.d.ts +1 -0
- package/dist/modules/Order/utils.js +33 -4
- package/dist/solution/ScanOrder/index.d.ts +7 -5
- package/dist/solution/ScanOrder/index.js +327 -274
- package/dist/solution/ScanOrder/types.d.ts +17 -0
- package/dist/solution/ScanOrder/types.js +13 -0
- package/dist/solution/ScanOrder/utils.d.ts +18 -3
- package/dist/solution/ScanOrder/utils.js +80 -13
- package/dist/solution/VenueBooking/index.d.ts +2 -5
- package/lib/model/strategy/adapter/promotion/index.js +49 -0
- package/lib/modules/Order/index.d.ts +3 -6
- package/lib/modules/Order/index.js +90 -26
- package/lib/modules/Order/types.d.ts +12 -5
- package/lib/modules/Order/utils.d.ts +1 -0
- package/lib/modules/Order/utils.js +28 -3
- package/lib/solution/ScanOrder/index.d.ts +7 -5
- package/lib/solution/ScanOrder/index.js +30 -0
- package/lib/solution/ScanOrder/types.d.ts +17 -0
- package/lib/solution/ScanOrder/utils.d.ts +18 -3
- package/lib/solution/ScanOrder/utils.js +58 -5
- package/lib/solution/VenueBooking/index.d.ts +2 -5
- package/package.json +1 -1
|
@@ -31,6 +31,9 @@ export interface ScanOrderOrderProductIdentity {
|
|
|
31
31
|
product_id: number | null;
|
|
32
32
|
product_variant_id: number;
|
|
33
33
|
identity_key?: string;
|
|
34
|
+
/** 参与合并/匹配;与 remove 通配语义见 isSkuOnlyDeleteIdentity */
|
|
35
|
+
product_option_item?: any[];
|
|
36
|
+
product_bundle?: any[];
|
|
34
37
|
}
|
|
35
38
|
export interface ScanOrderOrderProduct extends ScanOrderOrderProductIdentity {
|
|
36
39
|
order_detail_id: number | null;
|
|
@@ -45,8 +48,22 @@ export interface ScanOrderOrderProduct extends ScanOrderOrderProductIdentity {
|
|
|
45
48
|
discount_list: any[];
|
|
46
49
|
product_bundle: any[];
|
|
47
50
|
metadata: Record<string, any>;
|
|
51
|
+
/** 商品行备注(如顾客对单品的特殊要求) */
|
|
52
|
+
note?: string;
|
|
48
53
|
_origin?: Record<string, any>;
|
|
49
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* 出站 payload 版本的商品行。
|
|
57
|
+
*
|
|
58
|
+
* 与 tempOrder 内部 `ScanOrderOrderProduct` 的字段差异:
|
|
59
|
+
* - `product_option_item[*]`:内部为 `{ product_option_item_id, option_group_id, num, price, ... }`,
|
|
60
|
+
* 出站由 `normalizeSubmitProduct` 重命名为 `{ option_group_item_id, option_group_id, num }`
|
|
61
|
+
* (后端 checkout 协议;丢弃 `price` 等运行时辅助字段)。
|
|
62
|
+
* - `product_bundle[*].option[*]`:同上重命名规则。
|
|
63
|
+
*
|
|
64
|
+
* 运行时(UI 显示、加购合并、指纹、持久化)全部消费内部字段名 `product_option_item_id`,
|
|
65
|
+
* 仅在 SDK 提交边界做一次出站映射,不影响 opaque identity 契约。
|
|
66
|
+
*/
|
|
50
67
|
export interface ScanOrderSubmitProduct extends Omit<ScanOrderOrderProduct, '_origin' | 'identity_key'> {
|
|
51
68
|
/**
|
|
52
69
|
* 出站兼容字段:SDK 内部不再消费 payment_price,
|
|
@@ -82,9 +82,24 @@ export declare function buildItemRuleBusinessData(params: {
|
|
|
82
82
|
}): ItemRuleBusinessData;
|
|
83
83
|
export declare function attachItemRuleLimitsToTopLevelProducts<T>(productList: T, limits: QuantityLimitResult[]): T;
|
|
84
84
|
/**
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
|
|
85
|
+
* 将选项行 / 套餐行归一成稳定 JSON,用于「同 SKU 是否合并」判断。
|
|
86
|
+
* 数组元素先按主键排序再序列化,避免顺序差异导致误判为不同行。
|
|
87
|
+
*/
|
|
88
|
+
export declare function buildProductLineFingerprint(productOptionItem?: unknown, productBundle?: unknown): string;
|
|
89
|
+
/**
|
|
90
|
+
* removeProductFromOrder 仅传 SKU 时的通配 identity(对象上未声明选项键)。
|
|
91
|
+
* 与显式 `product_option_item: []` 区分:后者只匹配「无选项」行。
|
|
92
|
+
*/
|
|
93
|
+
export declare function isSkuOnlyDeleteIdentity(x: ScanOrderOrderProductIdentity): boolean;
|
|
94
|
+
/**
|
|
95
|
+
* 判断两个商品 identity 是否匹配(不透明 identity 契约)。
|
|
96
|
+
*
|
|
97
|
+
* 调用约定:始终 `isIdentityMatch(line, callerIdentity)`。
|
|
98
|
+
* - 若 SKU(product_id + product_variant_id)不一致 → 不匹配。
|
|
99
|
+
* - **双方都带** `identity_key` → 严格字符串相等比较(不再猜测合成 key / metadata 桥接)。
|
|
100
|
+
* 这是 opaque identity 契约的标准路径:UI 删改时透传 SDK 回灌的 identity_key。
|
|
101
|
+
* - 否则(即至少一侧未声明 identity_key)→ 进入「SKU 通配 / 显式空选项 / 指纹」回退路径,
|
|
102
|
+
* 忽略线侧 identity_key,按内容语义匹配。这给「行已 opaque、调用方却想按 SKU 通配 / 选项指纹删除」留兼容入口。
|
|
88
103
|
*/
|
|
89
104
|
export declare function isIdentityMatch(a: ScanOrderOrderProductIdentity, b: ScanOrderOrderProductIdentity): boolean;
|
|
90
105
|
/**
|
|
@@ -33,6 +33,7 @@ __export(utils_exports, {
|
|
|
33
33
|
attachItemRuleLimitsToTopLevelProducts: () => attachItemRuleLimitsToTopLevelProducts,
|
|
34
34
|
buildItemRuleBusinessData: () => buildItemRuleBusinessData,
|
|
35
35
|
buildProductKey: () => buildProductKey,
|
|
36
|
+
buildProductLineFingerprint: () => buildProductLineFingerprint,
|
|
36
37
|
buildQuantityLimitIndex: () => buildQuantityLimitIndex,
|
|
37
38
|
collectLinkProductIdsFromReservationRules: () => collectLinkProductIdsFromReservationRules,
|
|
38
39
|
computeResourceIsFull: () => computeResourceIsFull,
|
|
@@ -44,6 +45,7 @@ __export(utils_exports, {
|
|
|
44
45
|
getTopLevelVariantId: () => getTopLevelVariantId,
|
|
45
46
|
hasCustomCapacityProduct: () => hasCustomCapacityProduct,
|
|
46
47
|
isIdentityMatch: () => isIdentityMatch,
|
|
48
|
+
isSkuOnlyDeleteIdentity: () => isSkuOnlyDeleteIdentity,
|
|
47
49
|
normalizeEnabledItemRuleIds: () => normalizeEnabledItemRuleIds,
|
|
48
50
|
normalizeItemRuleStrategies: () => normalizeItemRuleStrategies,
|
|
49
51
|
normalizeOrderProduct: () => normalizeOrderProduct,
|
|
@@ -59,6 +61,7 @@ __export(utils_exports, {
|
|
|
59
61
|
});
|
|
60
62
|
module.exports = __toCommonJS(utils_exports);
|
|
61
63
|
var import_dayjs = __toESM(require("dayjs"));
|
|
64
|
+
var import_utils = require("../../modules/Order/utils");
|
|
62
65
|
function createEmptySummary() {
|
|
63
66
|
return {
|
|
64
67
|
product_quantity: 0,
|
|
@@ -339,12 +342,58 @@ function attachItemRuleLimitsToTopLevelProducts(productList, limits) {
|
|
|
339
342
|
}
|
|
340
343
|
return productList;
|
|
341
344
|
}
|
|
345
|
+
function buildProductLineFingerprint(productOptionItem, productBundle) {
|
|
346
|
+
const optArr = Array.isArray(productOptionItem) ? productOptionItem : [];
|
|
347
|
+
const normalizedOpts = optArr.map((item) => ({
|
|
348
|
+
product_option_item_id: Number(item == null ? void 0 : item.product_option_item_id) || 0,
|
|
349
|
+
option_group_id: Number(item == null ? void 0 : item.option_group_id) || 0,
|
|
350
|
+
num: typeof (item == null ? void 0 : item.num) === "number" && !Number.isNaN(item.num) ? Math.max(1, Math.floor(item.num)) : 1,
|
|
351
|
+
price: String((item == null ? void 0 : item.price) ?? "")
|
|
352
|
+
})).sort((p, q) => {
|
|
353
|
+
if (p.product_option_item_id !== q.product_option_item_id) {
|
|
354
|
+
return p.product_option_item_id - q.product_option_item_id;
|
|
355
|
+
}
|
|
356
|
+
if (p.option_group_id !== q.option_group_id)
|
|
357
|
+
return p.option_group_id - q.option_group_id;
|
|
358
|
+
if (p.num !== q.num)
|
|
359
|
+
return p.num - q.num;
|
|
360
|
+
return p.price.localeCompare(q.price);
|
|
361
|
+
});
|
|
362
|
+
const bundleArr = Array.isArray(productBundle) ? productBundle : [];
|
|
363
|
+
const normalizedBundles = bundleArr.map((item) => ({
|
|
364
|
+
bundle_id: Number((item == null ? void 0 : item.bundle_id) ?? (item == null ? void 0 : item.id) ?? 0) || 0,
|
|
365
|
+
product_id: Number(item == null ? void 0 : item.product_id) || 0,
|
|
366
|
+
num: typeof (item == null ? void 0 : item.num) === "number" && !Number.isNaN(item.num) ? Math.max(1, Math.floor(item.num)) : 1
|
|
367
|
+
})).sort((p, q) => {
|
|
368
|
+
if (p.bundle_id !== q.bundle_id)
|
|
369
|
+
return p.bundle_id - q.bundle_id;
|
|
370
|
+
if (p.product_id !== q.product_id)
|
|
371
|
+
return p.product_id - q.product_id;
|
|
372
|
+
return p.num - q.num;
|
|
373
|
+
});
|
|
374
|
+
return JSON.stringify([normalizedOpts, normalizedBundles]);
|
|
375
|
+
}
|
|
376
|
+
function isSkuOnlyDeleteIdentity(x) {
|
|
377
|
+
if (x.identity_key)
|
|
378
|
+
return false;
|
|
379
|
+
return !("product_option_item" in x) && !("product_bundle" in x);
|
|
380
|
+
}
|
|
381
|
+
function fingerprintForIdentityWithOptionKeys(x) {
|
|
382
|
+
const row = x;
|
|
383
|
+
const opts = "product_option_item" in row ? Array.isArray(row.product_option_item) ? row.product_option_item : [] : [];
|
|
384
|
+
const buds = "product_bundle" in row ? Array.isArray(row.product_bundle) ? row.product_bundle : [] : [];
|
|
385
|
+
return buildProductLineFingerprint(opts, buds);
|
|
386
|
+
}
|
|
342
387
|
function isIdentityMatch(a, b) {
|
|
343
388
|
if (a.product_id !== b.product_id || a.product_variant_id !== b.product_variant_id)
|
|
344
389
|
return false;
|
|
345
|
-
|
|
390
|
+
const aHasKey = typeof a.identity_key === "string" && a.identity_key.length > 0;
|
|
391
|
+
const bHasKey = typeof b.identity_key === "string" && b.identity_key.length > 0;
|
|
392
|
+
if (aHasKey && bHasKey)
|
|
393
|
+
return a.identity_key === b.identity_key;
|
|
394
|
+
if (isSkuOnlyDeleteIdentity(a) || isSkuOnlyDeleteIdentity(b))
|
|
346
395
|
return true;
|
|
347
|
-
return a
|
|
396
|
+
return fingerprintForIdentityWithOptionKeys(a) === fingerprintForIdentityWithOptionKeys(b);
|
|
348
397
|
}
|
|
349
398
|
function getProductIdentityIndex(products, identity) {
|
|
350
399
|
return products.findIndex((item) => isIdentityMatch(item, identity));
|
|
@@ -352,8 +401,9 @@ function getProductIdentityIndex(products, identity) {
|
|
|
352
401
|
function normalizeOrderProduct(product) {
|
|
353
402
|
var _a, _b;
|
|
354
403
|
const metadata = { ...product.metadata || {} };
|
|
355
|
-
|
|
356
|
-
|
|
404
|
+
const resolvedIdentityKey = product.identity_key && String(product.identity_key).length > 0 ? String(product.identity_key) : (0, import_utils.createUuidV4)();
|
|
405
|
+
if (!metadata.unique_identification_number) {
|
|
406
|
+
metadata.unique_identification_number = resolvedIdentityKey;
|
|
357
407
|
}
|
|
358
408
|
const resolvedSellingPrice = product.selling_price || "0.00";
|
|
359
409
|
const resolvedOriginalPrice = product.original_price || resolvedSellingPrice;
|
|
@@ -376,7 +426,7 @@ function normalizeOrderProduct(product) {
|
|
|
376
426
|
product_id: product.product_id,
|
|
377
427
|
num: getSafeProductNum(product.num),
|
|
378
428
|
product_variant_id: product.product_variant_id,
|
|
379
|
-
identity_key:
|
|
429
|
+
identity_key: resolvedIdentityKey,
|
|
380
430
|
product_option_item: product.product_option_item || [],
|
|
381
431
|
selling_price: resolvedSellingPrice,
|
|
382
432
|
original_price: resolvedOriginalPrice,
|
|
@@ -385,6 +435,7 @@ function normalizeOrderProduct(product) {
|
|
|
385
435
|
discount_list: product.discount_list || [],
|
|
386
436
|
product_bundle: normalizedBundle,
|
|
387
437
|
metadata,
|
|
438
|
+
note: product.note != null ? String(product.note) : "",
|
|
388
439
|
_origin: product._origin
|
|
389
440
|
};
|
|
390
441
|
}
|
|
@@ -505,6 +556,7 @@ function pickFirstCustomCapacityDimensionId(products) {
|
|
|
505
556
|
attachItemRuleLimitsToTopLevelProducts,
|
|
506
557
|
buildItemRuleBusinessData,
|
|
507
558
|
buildProductKey,
|
|
559
|
+
buildProductLineFingerprint,
|
|
508
560
|
buildQuantityLimitIndex,
|
|
509
561
|
collectLinkProductIdsFromReservationRules,
|
|
510
562
|
computeResourceIsFull,
|
|
@@ -516,6 +568,7 @@ function pickFirstCustomCapacityDimensionId(products) {
|
|
|
516
568
|
getTopLevelVariantId,
|
|
517
569
|
hasCustomCapacityProduct,
|
|
518
570
|
isIdentityMatch,
|
|
571
|
+
isSkuOnlyDeleteIdentity,
|
|
519
572
|
normalizeEnabledItemRuleIds,
|
|
520
573
|
normalizeItemRuleStrategies,
|
|
521
574
|
normalizeOrderProduct,
|
|
@@ -2,6 +2,7 @@ import { Module, ModuleOptions, PisellCore } from '../../types';
|
|
|
2
2
|
import { BaseModule } from '../../modules/BaseModule';
|
|
3
3
|
import { VenueBookingAddLogParams, VenueBookingSlotConfig, VenueDateSummaryItem, VenueSlotSelection, VenueTimeSlotGrid } from './types';
|
|
4
4
|
import type { ScanOrderOrderProduct, ScanOrderOrderProductIdentity } from '../ScanOrder/types';
|
|
5
|
+
import type { UpdateProductInOrderParams } from '../../modules/Order/types';
|
|
5
6
|
import type { OpenDataAvailabilityResult } from '../../modules/OpenData';
|
|
6
7
|
import type { ProductData } from '../../modules/Product/types';
|
|
7
8
|
import { type CartItemSummary, type PaxInfo, type QuantityCheckResult, type QuantityLimitResult } from '../../model/strategy/adapter/itemRule';
|
|
@@ -165,11 +166,7 @@ export declare class VenueBookingImpl extends BaseModule implements Module {
|
|
|
165
166
|
getSummary(): Promise<import("./types").ScanOrderSummary>;
|
|
166
167
|
submitOrder<T = any>(): Promise<T>;
|
|
167
168
|
addProductToOrder(product: Partial<ScanOrderOrderProduct> & ScanOrderOrderProductIdentity): Promise<ScanOrderOrderProduct[]>;
|
|
168
|
-
updateProductInOrder(params:
|
|
169
|
-
product_id: number | null;
|
|
170
|
-
product_variant_id: number;
|
|
171
|
-
updates: Partial<ScanOrderOrderProduct>;
|
|
172
|
-
}): Promise<ScanOrderOrderProduct[]>;
|
|
169
|
+
updateProductInOrder(params: UpdateProductInOrderParams): Promise<ScanOrderOrderProduct[]>;
|
|
173
170
|
removeProductFromOrder(identity: ScanOrderOrderProductIdentity): Promise<ScanOrderOrderProduct[]>;
|
|
174
171
|
getProductList(): Promise<ProductData[]>;
|
|
175
172
|
private loadOpenDataConfig;
|