@pisell/pisellos 0.10.43 → 0.10.45
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 +4 -1
- package/dist/model/strategy/adapter/itemRule/adapter.js +135 -35
- package/dist/model/strategy/adapter/itemRule/evaluator.d.ts +8 -1
- package/dist/model/strategy/adapter/itemRule/evaluator.js +25 -1
- package/dist/model/strategy/adapter/itemRule/type.d.ts +44 -0
- package/dist/model/strategy/adapter/itemRule/type.js +19 -1
- package/dist/model/strategy/adapter/promotion/index.js +9 -0
- package/dist/model/strategy/adapter/walletPass/evaluator.d.ts +2 -0
- package/dist/model/strategy/adapter/walletPass/evaluator.js +4 -2
- package/dist/modules/Customer/index.js +2 -1
- package/dist/modules/Discount/availability.d.ts +7 -0
- package/dist/modules/Discount/availability.js +49 -0
- package/dist/modules/Discount/index.d.ts +1 -3
- package/dist/modules/Discount/index.js +12 -151
- package/dist/modules/Discount/types.d.ts +6 -0
- package/dist/modules/Order/index.d.ts +41 -9
- package/dist/modules/Order/index.js +1457 -872
- package/dist/modules/Order/salesNotes.d.ts +31 -0
- package/dist/modules/Order/salesNotes.js +492 -0
- package/dist/modules/Order/types.d.ts +105 -8
- package/dist/modules/Order/types.js +6 -1
- package/dist/modules/Order/utils.d.ts +4 -2
- package/dist/modules/Order/utils.js +224 -79
- package/dist/modules/Payment/cashRecommendationAlgorithm.d.ts +4 -5
- package/dist/modules/Payment/cashRecommendationAlgorithm.js +17 -367
- package/dist/modules/Product/index.d.ts +1 -1
- package/dist/modules/ProductList/index.d.ts +2 -1
- package/dist/modules/ProductList/index.js +100 -20
- package/dist/modules/ProductList/types.d.ts +10 -0
- package/dist/modules/Rules/index.js +50 -16
- package/dist/server/index.d.ts +7 -0
- package/dist/server/index.js +45 -3
- package/dist/server/modules/order/index.js +6 -0
- package/dist/server/modules/order/types.d.ts +9 -0
- package/dist/server/utils/small-ticket.d.ts +1 -0
- package/dist/server/utils/small-ticket.js +142 -14
- package/dist/solution/BaseSales/index.d.ts +15 -1
- package/dist/solution/BaseSales/index.js +1240 -877
- package/dist/solution/BaseSales/types.d.ts +2 -2
- package/dist/solution/BookingByStep/index.d.ts +1 -1
- package/dist/solution/BookingTicket/index.d.ts +1 -0
- package/dist/solution/BookingTicket/index.js +37 -16
- package/dist/solution/BookingTicket/utils/orderCustomer.js +3 -2
- package/dist/solution/RegisterAndLogin/config.d.ts +9 -3
- package/dist/solution/RegisterAndLogin/config.js +95 -40
- package/dist/solution/RegisterAndLogin/index.js +4 -1
- package/dist/solution/ScanOrder/index.d.ts +1 -0
- package/dist/solution/ScanOrder/index.js +31 -27
- package/dist/solution/ScanOrder/utils.d.ts +1 -0
- package/dist/solution/ScanOrder/utils.js +2 -0
- package/dist/solution/ShopDiscount/index.d.ts +1 -0
- package/dist/solution/ShopDiscount/index.js +59 -22
- package/dist/solution/ShopDiscount/utils.d.ts +4 -2
- package/dist/solution/ShopDiscount/utils.js +40 -21
- package/dist/solution/UnifiedBookingSales/index.d.ts +15 -2
- package/dist/solution/UnifiedBookingSales/index.js +777 -510
- package/dist/solution/UnifiedBookingSales/types.d.ts +14 -1
- package/dist/solution/VenueBooking/index.d.ts +1 -0
- package/dist/solution/VenueBooking/index.js +8 -4
- package/lib/model/strategy/adapter/itemRule/adapter.d.ts +4 -1
- package/lib/model/strategy/adapter/itemRule/adapter.js +73 -4
- package/lib/model/strategy/adapter/itemRule/evaluator.d.ts +8 -1
- package/lib/model/strategy/adapter/itemRule/evaluator.js +23 -1
- package/lib/model/strategy/adapter/itemRule/type.d.ts +44 -0
- package/lib/model/strategy/adapter/itemRule/type.js +19 -1
- package/lib/model/strategy/adapter/walletPass/evaluator.d.ts +2 -0
- package/lib/model/strategy/adapter/walletPass/evaluator.js +2 -0
- package/lib/modules/Customer/index.js +1 -0
- package/lib/modules/Discount/availability.d.ts +7 -0
- package/lib/modules/Discount/availability.js +43 -0
- package/lib/modules/Discount/index.d.ts +1 -3
- package/lib/modules/Discount/index.js +10 -131
- package/lib/modules/Discount/types.d.ts +6 -0
- package/lib/modules/Order/index.d.ts +41 -9
- package/lib/modules/Order/index.js +456 -69
- package/lib/modules/Order/salesNotes.d.ts +31 -0
- package/lib/modules/Order/salesNotes.js +382 -0
- package/lib/modules/Order/types.d.ts +105 -8
- package/lib/modules/Order/types.js +5 -1
- package/lib/modules/Order/utils.d.ts +4 -2
- package/lib/modules/Order/utils.js +134 -3
- package/lib/modules/Payment/cashRecommendationAlgorithm.d.ts +4 -5
- package/lib/modules/Payment/cashRecommendationAlgorithm.js +9 -354
- package/lib/modules/Product/index.d.ts +1 -1
- package/lib/modules/ProductList/index.d.ts +2 -1
- package/lib/modules/ProductList/index.js +47 -2
- package/lib/modules/ProductList/types.d.ts +10 -0
- package/lib/modules/Rules/index.js +42 -5
- package/lib/server/index.d.ts +7 -0
- package/lib/server/index.js +39 -3
- package/lib/server/modules/order/index.js +6 -0
- package/lib/server/modules/order/types.d.ts +9 -0
- package/lib/server/utils/small-ticket.d.ts +1 -0
- package/lib/server/utils/small-ticket.js +136 -12
- package/lib/solution/BaseSales/index.d.ts +15 -1
- package/lib/solution/BaseSales/index.js +192 -22
- package/lib/solution/BaseSales/types.d.ts +2 -2
- package/lib/solution/BookingByStep/index.d.ts +1 -1
- package/lib/solution/BookingTicket/index.d.ts +1 -0
- package/lib/solution/BookingTicket/index.js +23 -5
- package/lib/solution/BookingTicket/utils/orderCustomer.js +2 -1
- package/lib/solution/RegisterAndLogin/config.d.ts +9 -3
- package/lib/solution/RegisterAndLogin/config.js +49 -8
- package/lib/solution/RegisterAndLogin/index.js +4 -1
- package/lib/solution/ScanOrder/index.d.ts +1 -0
- package/lib/solution/ScanOrder/index.js +5 -1
- package/lib/solution/ScanOrder/utils.d.ts +1 -0
- package/lib/solution/ScanOrder/utils.js +1 -0
- package/lib/solution/ShopDiscount/index.d.ts +1 -0
- package/lib/solution/ShopDiscount/index.js +29 -3
- package/lib/solution/ShopDiscount/utils.d.ts +4 -2
- package/lib/solution/ShopDiscount/utils.js +39 -23
- package/lib/solution/UnifiedBookingSales/index.d.ts +15 -2
- package/lib/solution/UnifiedBookingSales/index.js +125 -1
- package/lib/solution/UnifiedBookingSales/types.d.ts +14 -1
- package/lib/solution/VenueBooking/index.d.ts +1 -0
- package/lib/solution/VenueBooking/index.js +5 -1
- package/package.json +1 -1
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import type { InvoiceLayoutConfig, InvoiceTemplateSettings, OpenDataConfig } from '../../modules/OpenData';
|
|
2
|
-
import type { ProductListLoadProductsParams } from '../../modules/ProductList';
|
|
2
|
+
import type { ProductListLoadProductsParams, ProductListStoreQueryParams } from '../../modules/ProductList';
|
|
3
|
+
import type { ProductData } from '../../modules/Product/types';
|
|
3
4
|
import type { LoadScheduleAvailableDateParams } from '../../modules/Schedule/types';
|
|
4
5
|
import type { AvailabilityDateRange, AvailabilityErrorCode, AvailabilityId, AvailabilityOccupancy, AvailabilityProduct, AvailabilityProjection, AvailabilityQuery, AvailabilityRequirementSelection, AvailabilityResource, AvailabilitySelectionValidationResult, DateAvailabilityQueryResult, GetProductTimeRangesRequest, ProductAvailabilityQueryResult, ProductTimeRange, ProductTimeRangeGroup, ResourceAvailabilityGroup, ResourceAvailabilityRangeGroup, TimeAvailabilityGroup, TimeAvailabilityItem } from '../../modules/ResourcePlanner';
|
|
5
6
|
import type { BookingTicketState } from '../BookingTicket/types';
|
|
7
|
+
import type { AllergyRequirement } from '../../model/strategy/adapter/itemRule';
|
|
8
|
+
import type { SalesNoteDraftInput } from '../../modules/Order/types';
|
|
9
|
+
export type { ProductListStoreQueryParams } from '../../modules/ProductList';
|
|
6
10
|
export type { AvailabilityAbsoluteRange, AvailabilityAssignment, AvailabilityCell, AvailabilityDateRange, AvailabilityDisplayRange, AvailabilityErrorCode, AvailabilityId, AvailabilityMode, AvailabilityOccupancy, AvailabilityProduct, AvailabilityProductKind, AvailabilityProjection, AvailabilityProjectionInput, AvailabilityProjectionMeta, AvailabilityQuery, AvailabilityQueryResult, AvailabilityRequirementGroup, AvailabilityRequirementGroupResult, AvailabilityRequirementSelection, AvailabilityResource, AvailabilityResourceOption, AvailabilityResourceType, AvailabilityResourceWindow, AvailabilityScheduleMode, AvailabilityScheduleRef, AvailabilityScheduleRule, AvailabilitySelectionValidationParams, AvailabilitySelectionValidationResult, AvailabilitySessionRange, AvailabilityStatus, DateAvailabilityGroup, DateAvailabilityItem, DateAvailabilityQuery, DateAvailabilityQueryResult, GetProductTimeRangesRequest, ProductAvailabilityGroup, ProductAvailabilityItem, ProductAvailabilityQuery, ProductAvailabilityQueryResult, ProductTimeRange, ProductTimeRangeGroup, QueryAvailabilityBaseRequest, ResourceAvailabilityGroup, ResourceAvailabilityQuery, ResourceAvailabilityQueryResult, ResourceAvailabilityRangeGroup, TimeAvailabilityGroup, TimeAvailabilityItem, TimeAvailabilityQuery, TimeAvailabilityQueryResult, } from '../../modules/ResourcePlanner';
|
|
7
11
|
export interface UnifiedBookingSalesState extends BookingTicketState {
|
|
8
12
|
}
|
|
@@ -24,6 +28,12 @@ export declare enum UnifiedBookingSalesHooks {
|
|
|
24
28
|
export interface UnifiedBookingSalesOtherParams extends Record<string, any> {
|
|
25
29
|
enableSessionStoragePersist?: boolean;
|
|
26
30
|
}
|
|
31
|
+
export type UnifiedBookingSalesAllergyRequirement = AllergyRequirement;
|
|
32
|
+
export interface UnifiedBookingSalesResolveProductAllergyRequirementParams {
|
|
33
|
+
productId: number | string;
|
|
34
|
+
productVariantId?: number | string;
|
|
35
|
+
channel?: string;
|
|
36
|
+
}
|
|
27
37
|
export interface UnifiedBookingSalesLoadOpenDataParams {
|
|
28
38
|
businessCode: string;
|
|
29
39
|
channel: string;
|
|
@@ -151,6 +161,8 @@ export interface ContextualCommitAvailabilitySelectionParams {
|
|
|
151
161
|
requirementSelections: AvailabilityRequirementSelection[];
|
|
152
162
|
partySize: number;
|
|
153
163
|
bookingCount?: number;
|
|
164
|
+
/** 新增商品行时与商品、预约在同一次 Order mutation 内写入的 Sales Notes。 */
|
|
165
|
+
salesNotes?: SalesNoteDraftInput[];
|
|
154
166
|
/** 编辑预约时,允许本次显式报价覆盖已落库订单行的历史价格。 */
|
|
155
167
|
allowBookingReprice?: boolean;
|
|
156
168
|
/** 编辑预约重报价后,允许订单模块重新应用已选编辑态折扣。 */
|
|
@@ -214,6 +226,7 @@ export interface UnifiedBookingSalesApi {
|
|
|
214
226
|
/** Date-first calendar availability backed by ScheduleModule. */
|
|
215
227
|
loadScheduleAvailableDate: (params: LoadScheduleAvailableDateParams) => Promise<unknown>;
|
|
216
228
|
loadProductsByScheduleDate: (params: UnifiedBookingSalesLoadProductsByScheduleDateParams) => Promise<unknown[]>;
|
|
229
|
+
queryProducts: (params?: ProductListStoreQueryParams) => Promise<ProductData[]>;
|
|
217
230
|
stageRetailProducts: (params: UnifiedBookingSalesStageRetailProductsParams) => Promise<Record<string, any> | null>;
|
|
218
231
|
addRetailProduct: (params: UnifiedBookingSalesAddRetailProductParams) => Promise<Record<string, any>[]>;
|
|
219
232
|
prepareAvailabilityProjection: (params: UnifiedBookingSalesPrepareAvailabilityProjectionParams) => Promise<AvailabilityContextRef>;
|
|
@@ -3140,7 +3140,11 @@ export var VenueBookingImpl = /*#__PURE__*/function (_BaseSalesImpl) {
|
|
|
3140
3140
|
}, {
|
|
3141
3141
|
key: "getItemRuleRuntimeConfig",
|
|
3142
3142
|
value: function getItemRuleRuntimeConfig() {
|
|
3143
|
-
|
|
3143
|
+
var _ref6, _runtimeConfig$channe, _runtimeConfig$custom, _this$otherParams21;
|
|
3144
|
+
var runtimeConfig = this.itemRuleRuntimeConfig || {};
|
|
3145
|
+
return _objectSpread(_objectSpread({}, runtimeConfig), {}, {
|
|
3146
|
+
channel: (_ref6 = (_runtimeConfig$channe = runtimeConfig.channel) !== null && _runtimeConfig$channe !== void 0 ? _runtimeConfig$channe : (_runtimeConfig$custom = runtimeConfig.custom) === null || _runtimeConfig$custom === void 0 ? void 0 : _runtimeConfig$custom.channel) !== null && _ref6 !== void 0 ? _ref6 : (_this$otherParams21 = this.otherParams) === null || _this$otherParams21 === void 0 ? void 0 : _this$otherParams21.channel
|
|
3147
|
+
});
|
|
3144
3148
|
}
|
|
3145
3149
|
}, {
|
|
3146
3150
|
key: "ensureItemRuleConfigsLoaded",
|
|
@@ -3806,14 +3810,14 @@ export var VenueBookingImpl = /*#__PURE__*/function (_BaseSalesImpl) {
|
|
|
3806
3810
|
key: "setOtherParams",
|
|
3807
3811
|
value: function () {
|
|
3808
3812
|
var _setOtherParams = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee46(params) {
|
|
3809
|
-
var
|
|
3810
|
-
|
|
3813
|
+
var _ref8,
|
|
3814
|
+
_ref8$cover,
|
|
3811
3815
|
cover,
|
|
3812
3816
|
_args46 = arguments;
|
|
3813
3817
|
return _regeneratorRuntime().wrap(function _callee46$(_context46) {
|
|
3814
3818
|
while (1) switch (_context46.prev = _context46.next) {
|
|
3815
3819
|
case 0:
|
|
3816
|
-
|
|
3820
|
+
_ref8 = _args46.length > 1 && _args46[1] !== undefined ? _args46[1] : {}, _ref8$cover = _ref8.cover, cover = _ref8$cover === void 0 ? false : _ref8$cover;
|
|
3817
3821
|
if (cover) {
|
|
3818
3822
|
this.otherParams = params;
|
|
3819
3823
|
} else {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { EvaluationResult, RuntimeContext, ActionEffect } from '../../type';
|
|
2
2
|
import type { BusinessAdapter } from '../type';
|
|
3
|
-
import { type ItemRuleBusinessData, type QuantityCheckResult, type PrefillItemResult, type QuantityLimitResult } from './type';
|
|
3
|
+
import { type ItemRuleBusinessData, type QuantityCheckResult, type PrefillItemResult, type QuantityLimitResult, type AllergyRequirement } from './type';
|
|
4
4
|
/**
|
|
5
5
|
* ItemRule 适配器
|
|
6
6
|
*
|
|
@@ -14,7 +14,10 @@ export declare class ItemRuleAdapter implements BusinessAdapter {
|
|
|
14
14
|
transformResult(result: EvaluationResult, businessData?: ItemRuleBusinessData): {
|
|
15
15
|
quantityChecks: QuantityCheckResult[];
|
|
16
16
|
prefillItems: PrefillItemResult[];
|
|
17
|
+
allergyRequirements: AllergyRequirement[];
|
|
17
18
|
};
|
|
19
|
+
private processAllergyCheck;
|
|
20
|
+
private normalizeAllergyDataSourceItems;
|
|
18
21
|
formatConfig(result: EvaluationResult, businessData?: ItemRuleBusinessData): {
|
|
19
22
|
result: EvaluationResult;
|
|
20
23
|
businessData?: ItemRuleBusinessData;
|
|
@@ -19,14 +19,17 @@ class ItemRuleAdapter {
|
|
|
19
19
|
pax,
|
|
20
20
|
cartItems,
|
|
21
21
|
serviceType,
|
|
22
|
+
channel,
|
|
22
23
|
submissionIndex,
|
|
23
24
|
custom
|
|
24
25
|
} = businessData;
|
|
26
|
+
const resolvedChannel = String(channel ?? custom?.channel ?? '').trim();
|
|
25
27
|
return {
|
|
26
28
|
entities: {
|
|
27
29
|
cartItems,
|
|
28
30
|
historicalItems: businessData.historicalItems || [],
|
|
29
|
-
pax
|
|
31
|
+
pax,
|
|
32
|
+
currentProduct: businessData.currentProduct
|
|
30
33
|
},
|
|
31
34
|
attributes: {
|
|
32
35
|
serviceType: serviceType || '',
|
|
@@ -36,7 +39,8 @@ class ItemRuleAdapter {
|
|
|
36
39
|
submissionIndex: submissionIndex ?? 0,
|
|
37
40
|
cartItemCount: cartItems.length,
|
|
38
41
|
cartTotalQuantity: cartItems.reduce((sum, item) => sum + item.quantity, 0),
|
|
39
|
-
...custom
|
|
42
|
+
...custom,
|
|
43
|
+
channel: resolvedChannel
|
|
40
44
|
},
|
|
41
45
|
metadata: {
|
|
42
46
|
timestamp: Date.now()
|
|
@@ -46,10 +50,12 @@ class ItemRuleAdapter {
|
|
|
46
50
|
transformResult(result, businessData) {
|
|
47
51
|
const quantityChecks = [];
|
|
48
52
|
const prefillItems = [];
|
|
53
|
+
const allergyRequirements = [];
|
|
49
54
|
if (!result.applicable || !businessData) {
|
|
50
55
|
return {
|
|
51
56
|
quantityChecks,
|
|
52
|
-
prefillItems
|
|
57
|
+
prefillItems,
|
|
58
|
+
allergyRequirements
|
|
53
59
|
};
|
|
54
60
|
}
|
|
55
61
|
for (const action of result.matchedActions) {
|
|
@@ -61,13 +67,76 @@ class ItemRuleAdapter {
|
|
|
61
67
|
} else if (action.type === _type.ITEM_RULE_ACTION_TYPES.PREFILL_CART) {
|
|
62
68
|
const items = this.processPrefillCart(action, businessData);
|
|
63
69
|
prefillItems.push(...items);
|
|
70
|
+
} else if (action.type === _type.ITEM_RULE_ACTION_TYPES.ALLERGY_CHECK) {
|
|
71
|
+
const requirement = this.processAllergyCheck(action, result, businessData);
|
|
72
|
+
if (requirement) allergyRequirements.push(requirement);
|
|
64
73
|
}
|
|
65
74
|
}
|
|
66
75
|
return {
|
|
67
76
|
quantityChecks,
|
|
68
|
-
prefillItems
|
|
77
|
+
prefillItems,
|
|
78
|
+
allergyRequirements
|
|
69
79
|
};
|
|
70
80
|
}
|
|
81
|
+
|
|
82
|
+
// ============================================
|
|
83
|
+
// Allergy Check 处理
|
|
84
|
+
// ============================================
|
|
85
|
+
|
|
86
|
+
processAllergyCheck(action, result, businessData) {
|
|
87
|
+
const config = action.config;
|
|
88
|
+
const currentProduct = businessData.currentProduct;
|
|
89
|
+
if (!config || config.targetType !== 'product' || config.promptScope !== 'once_per_order_product' || !Array.isArray(config.targets) || !currentProduct) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
const currentProductId = Number(currentProduct.product_id);
|
|
93
|
+
const currentVariantId = Number(currentProduct.product_variant_id ?? 0);
|
|
94
|
+
if (!Number.isFinite(currentProductId) || currentProductId <= 0) return null;
|
|
95
|
+
const productTargets = config.targets.filter(item => Number(item.product_id) === currentProductId);
|
|
96
|
+
const target = productTargets.find(item => currentVariantId > 0 && Number(item.product_variant_id ?? 0) === currentVariantId) ?? productTargets.find(item => Number(item.product_variant_id ?? 0) <= 0);
|
|
97
|
+
if (!target || target.dataSource?.type !== 'inline') return null;
|
|
98
|
+
const items = this.normalizeAllergyDataSourceItems(target.dataSource.items);
|
|
99
|
+
if (!items.length) return null;
|
|
100
|
+
return {
|
|
101
|
+
strategyId: result.config.metadata.id,
|
|
102
|
+
ruleId: action.id,
|
|
103
|
+
priority: action.priority ?? 0,
|
|
104
|
+
targetType: 'product',
|
|
105
|
+
product_id: currentProductId,
|
|
106
|
+
...(Number(target.product_variant_id ?? 0) > 0 ? {
|
|
107
|
+
product_variant_id: Number(target.product_variant_id)
|
|
108
|
+
} : {}),
|
|
109
|
+
dataSource: {
|
|
110
|
+
type: 'inline',
|
|
111
|
+
items
|
|
112
|
+
},
|
|
113
|
+
promptScope: config.promptScope,
|
|
114
|
+
allowNote: config.allowNote === true
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
normalizeAllergyDataSourceItems(items) {
|
|
118
|
+
if (!Array.isArray(items)) return [];
|
|
119
|
+
const uniqueIds = new Set();
|
|
120
|
+
const normalized = [];
|
|
121
|
+
for (const item of items) {
|
|
122
|
+
const id = String(item?.id ?? '').trim();
|
|
123
|
+
const rawLabel = item?.label;
|
|
124
|
+
const label = typeof rawLabel === 'string' ? rawLabel.trim() : Object.entries(rawLabel || {}).reduce((labels, [locale, value]) => {
|
|
125
|
+
if (typeof value === 'string' && value.trim().length > 0) {
|
|
126
|
+
labels[locale] = value.trim();
|
|
127
|
+
}
|
|
128
|
+
return labels;
|
|
129
|
+
}, {});
|
|
130
|
+
const hasLabel = typeof label === 'string' ? label.length > 0 : Object.keys(label).length > 0;
|
|
131
|
+
if (!id || !hasLabel || uniqueIds.has(id)) continue;
|
|
132
|
+
uniqueIds.add(id);
|
|
133
|
+
normalized.push({
|
|
134
|
+
id,
|
|
135
|
+
label
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
return normalized;
|
|
139
|
+
}
|
|
71
140
|
formatConfig(result, businessData) {
|
|
72
141
|
return {
|
|
73
142
|
result,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { StrategyConfig } from '../../type';
|
|
2
|
-
import { type ItemRuleBusinessData, type ItemRuleEvaluationResult, type QuantityCheckResult, type QuantityLimitResult, type PrefillItemResult } from './type';
|
|
2
|
+
import { type ItemRuleBusinessData, type ItemRuleEvaluationResult, type QuantityCheckResult, type QuantityLimitResult, type PrefillItemResult, type AllergyRequirement } from './type';
|
|
3
3
|
/**
|
|
4
4
|
* ItemRule 评估器
|
|
5
5
|
*
|
|
@@ -21,6 +21,13 @@ export declare class ItemRuleEvaluator {
|
|
|
21
21
|
* 完整评估:数量校验 + 预填购物车
|
|
22
22
|
*/
|
|
23
23
|
evaluate(businessData: ItemRuleBusinessData): ItemRuleEvaluationResult;
|
|
24
|
+
/**
|
|
25
|
+
* 获取当前商品优先级最高的过敏门禁要求。
|
|
26
|
+
*
|
|
27
|
+
* 不同商品或不同 Action 的数据源不会合并;相同优先级按
|
|
28
|
+
* strategyId + ruleId 稳定排序,保证多次评估结果一致。
|
|
29
|
+
*/
|
|
30
|
+
getAllergyRequirement(businessData: ItemRuleBusinessData): AllergyRequirement | null;
|
|
24
31
|
/**
|
|
25
32
|
* 获取当前配置中各商品的数量限制
|
|
26
33
|
*
|
|
@@ -43,6 +43,7 @@ class ItemRuleEvaluator {
|
|
|
43
43
|
const configs = businessData.strategyConfigs ? businessData.strategyConfigs.filter(c => c.metadata.type === 'item_rule') : this.strategyConfigs;
|
|
44
44
|
const allQuantityChecks = [];
|
|
45
45
|
const allPrefillItems = [];
|
|
46
|
+
const allAllergyRequirements = [];
|
|
46
47
|
const rawResults = [];
|
|
47
48
|
const context = this.adapter.prepareContext(businessData);
|
|
48
49
|
for (const config of configs) {
|
|
@@ -50,21 +51,42 @@ class ItemRuleEvaluator {
|
|
|
50
51
|
rawResults.push(evalResult);
|
|
51
52
|
const {
|
|
52
53
|
quantityChecks,
|
|
53
|
-
prefillItems
|
|
54
|
+
prefillItems,
|
|
55
|
+
allergyRequirements
|
|
54
56
|
} = this.adapter.transformResult(evalResult, businessData);
|
|
55
57
|
allQuantityChecks.push(...quantityChecks);
|
|
56
58
|
allPrefillItems.push(...prefillItems);
|
|
59
|
+
allAllergyRequirements.push(...allergyRequirements);
|
|
57
60
|
}
|
|
58
61
|
const failedChecks = allQuantityChecks.filter(c => !c.passed);
|
|
59
62
|
return {
|
|
60
63
|
quantityChecks: allQuantityChecks,
|
|
61
64
|
prefillItems: allPrefillItems,
|
|
65
|
+
allergyRequirements: allAllergyRequirements,
|
|
62
66
|
allPassed: failedChecks.length === 0,
|
|
63
67
|
failedChecks,
|
|
64
68
|
rawResults
|
|
65
69
|
};
|
|
66
70
|
}
|
|
67
71
|
|
|
72
|
+
/**
|
|
73
|
+
* 获取当前商品优先级最高的过敏门禁要求。
|
|
74
|
+
*
|
|
75
|
+
* 不同商品或不同 Action 的数据源不会合并;相同优先级按
|
|
76
|
+
* strategyId + ruleId 稳定排序,保证多次评估结果一致。
|
|
77
|
+
*/
|
|
78
|
+
getAllergyRequirement(businessData) {
|
|
79
|
+
const requirements = this.evaluate(businessData).allergyRequirements;
|
|
80
|
+
if (!requirements.length) return null;
|
|
81
|
+
return [...requirements].sort((left, right) => {
|
|
82
|
+
const priorityDiff = right.priority - left.priority;
|
|
83
|
+
if (priorityDiff !== 0) return priorityDiff;
|
|
84
|
+
const strategyDiff = left.strategyId.localeCompare(right.strategyId);
|
|
85
|
+
if (strategyDiff !== 0) return strategyDiff;
|
|
86
|
+
return left.ruleId.localeCompare(right.ruleId);
|
|
87
|
+
})[0];
|
|
88
|
+
}
|
|
89
|
+
|
|
68
90
|
/**
|
|
69
91
|
* 获取当前配置中各商品的数量限制
|
|
70
92
|
*
|
|
@@ -4,6 +4,8 @@ export declare const ITEM_RULE_ACTION_TYPES: {
|
|
|
4
4
|
readonly QUANTITY_CHECK: "QUANTITY_CHECK";
|
|
5
5
|
/** 预填购物车 */
|
|
6
6
|
readonly PREFILL_CART: "PREFILL_CART";
|
|
7
|
+
/** 加购前过敏信息确认 */
|
|
8
|
+
readonly ALLERGY_CHECK: "ALLERGY_CHECK";
|
|
7
9
|
};
|
|
8
10
|
export type ItemRuleActionType = (typeof ITEM_RULE_ACTION_TYPES)[keyof typeof ITEM_RULE_ACTION_TYPES];
|
|
9
11
|
export declare const ITEM_RULE_TEMPLATES: {
|
|
@@ -63,6 +65,42 @@ export interface TargetItem {
|
|
|
63
65
|
/** 变体 ID */
|
|
64
66
|
product_variant_id?: number;
|
|
65
67
|
}
|
|
68
|
+
/** 策略展示文案,可由调用方按当前 locale 解析。 */
|
|
69
|
+
export type ItemRuleLocalizedText = string | Record<string, string>;
|
|
70
|
+
/** 内联过敏源选项。稳定 id 用于保存选择,label 只负责展示。 */
|
|
71
|
+
export interface AllergyDataSourceItem {
|
|
72
|
+
id: string;
|
|
73
|
+
label: ItemRuleLocalizedText;
|
|
74
|
+
}
|
|
75
|
+
/** 当前仅支持随策略下发的内联过敏源。 */
|
|
76
|
+
export interface AllergyDataSource {
|
|
77
|
+
type: 'inline';
|
|
78
|
+
items: AllergyDataSourceItem[];
|
|
79
|
+
}
|
|
80
|
+
/** 每个目标商品独立持有自己的过敏源,禁止跨商品合并。 */
|
|
81
|
+
export interface AllergyTargetItem extends TargetItem {
|
|
82
|
+
dataSource: AllergyDataSource;
|
|
83
|
+
}
|
|
84
|
+
export type AllergyPromptScope = 'once_per_order_product';
|
|
85
|
+
/** ALLERGY_CHECK Action 的商品级配置。 */
|
|
86
|
+
export interface AllergyCheckConfig {
|
|
87
|
+
targetType: 'product';
|
|
88
|
+
targets: AllergyTargetItem[];
|
|
89
|
+
promptScope: AllergyPromptScope;
|
|
90
|
+
allowNote?: boolean;
|
|
91
|
+
}
|
|
92
|
+
/** 当前商品命中的单条过敏门禁要求。 */
|
|
93
|
+
export interface AllergyRequirement {
|
|
94
|
+
strategyId: string;
|
|
95
|
+
ruleId: string;
|
|
96
|
+
priority: number;
|
|
97
|
+
targetType: 'product';
|
|
98
|
+
product_id: number;
|
|
99
|
+
product_variant_id?: number;
|
|
100
|
+
dataSource: AllergyDataSource;
|
|
101
|
+
promptScope: AllergyPromptScope;
|
|
102
|
+
allowNote: boolean;
|
|
103
|
+
}
|
|
66
104
|
/**
|
|
67
105
|
* 预填商品项
|
|
68
106
|
*/
|
|
@@ -119,6 +157,10 @@ export interface ItemRuleBusinessData {
|
|
|
119
157
|
historicalItems?: CartItemSummary[];
|
|
120
158
|
/** 服务类型(如 'dine-in', 'takeaway') */
|
|
121
159
|
serviceType?: string;
|
|
160
|
+
/** 当前业务渠道(如 'kiosk', 'pos', 'online_store') */
|
|
161
|
+
channel?: string;
|
|
162
|
+
/** 当前正在加入购物车的商品;ALLERGY_CHECK 只读取该商品的 Target。 */
|
|
163
|
+
currentProduct?: Pick<TargetItem, 'product_id' | 'product_variant_id'>;
|
|
122
164
|
/** 当前提交次数(0=首次提交, >0=追加提交) */
|
|
123
165
|
submissionIndex?: number;
|
|
124
166
|
/** 策略配置列表(可选,如果不传则使用 setStrategyConfigs 设置的) */
|
|
@@ -216,6 +258,8 @@ export interface ItemRuleEvaluationResult {
|
|
|
216
258
|
quantityChecks: QuantityCheckResult[];
|
|
217
259
|
/** 需要预填的商品列表 */
|
|
218
260
|
prefillItems: PrefillItemResult[];
|
|
261
|
+
/** 当前商品命中的过敏门禁要求;不同 Action/商品不会合并。 */
|
|
262
|
+
allergyRequirements: AllergyRequirement[];
|
|
219
263
|
/** 所有校验是否全部通过 */
|
|
220
264
|
allPassed: boolean;
|
|
221
265
|
/** 未通过的校验列表 */
|
|
@@ -12,7 +12,9 @@ const ITEM_RULE_ACTION_TYPES = exports.ITEM_RULE_ACTION_TYPES = {
|
|
|
12
12
|
/** 数量校验 */
|
|
13
13
|
QUANTITY_CHECK: 'QUANTITY_CHECK',
|
|
14
14
|
/** 预填购物车 */
|
|
15
|
-
PREFILL_CART: 'PREFILL_CART'
|
|
15
|
+
PREFILL_CART: 'PREFILL_CART',
|
|
16
|
+
/** 加购前过敏信息确认 */
|
|
17
|
+
ALLERGY_CHECK: 'ALLERGY_CHECK'
|
|
16
18
|
};
|
|
17
19
|
// ============================================
|
|
18
20
|
// Rule Template 类型(创建规则时的起始模板)
|
|
@@ -56,6 +58,22 @@ const ITEM_RULE_TEMPLATES = exports.ITEM_RULE_TEMPLATES = {
|
|
|
56
58
|
|
|
57
59
|
/** 目标项 */
|
|
58
60
|
|
|
61
|
+
// ============================================
|
|
62
|
+
// Allergy Check 相关类型
|
|
63
|
+
// ============================================
|
|
64
|
+
|
|
65
|
+
/** 策略展示文案,可由调用方按当前 locale 解析。 */
|
|
66
|
+
|
|
67
|
+
/** 内联过敏源选项。稳定 id 用于保存选择,label 只负责展示。 */
|
|
68
|
+
|
|
69
|
+
/** 当前仅支持随策略下发的内联过敏源。 */
|
|
70
|
+
|
|
71
|
+
/** 每个目标商品独立持有自己的过敏源,禁止跨商品合并。 */
|
|
72
|
+
|
|
73
|
+
/** ALLERGY_CHECK Action 的商品级配置。 */
|
|
74
|
+
|
|
75
|
+
/** 当前商品命中的单条过敏门禁要求。 */
|
|
76
|
+
|
|
59
77
|
// ============================================
|
|
60
78
|
// Prefill Cart 相关类型
|
|
61
79
|
// ============================================
|
|
@@ -28,6 +28,8 @@ export declare class WalletPassEvaluator {
|
|
|
28
28
|
/** 检查代金券/折扣卡是否可用 并返回config */
|
|
29
29
|
checkVoucherAvailability(input: EvaluatorInput): {
|
|
30
30
|
isAvailable: boolean;
|
|
31
|
+
reason: string | undefined;
|
|
32
|
+
reasonCode: string | undefined;
|
|
31
33
|
config: {
|
|
32
34
|
[x: string]: any;
|
|
33
35
|
};
|
|
@@ -256,6 +256,8 @@ class WalletPassEvaluator {
|
|
|
256
256
|
const isAvailable = results[0]?.isApplicable || false;
|
|
257
257
|
return {
|
|
258
258
|
isAvailable,
|
|
259
|
+
reason: results[0]?.reason,
|
|
260
|
+
reasonCode: results[0]?.reasonCode,
|
|
259
261
|
config: {
|
|
260
262
|
...results[0]?.strategyResult?.config?.metadata?.custom
|
|
261
263
|
}
|
|
@@ -150,6 +150,7 @@ class CustomerModule extends _BaseModule.BaseModule {
|
|
|
150
150
|
with: ['latestWalletDetail.wallet', 'contactsInfo', 'formRecord'],
|
|
151
151
|
search_wallet_flag: 1,
|
|
152
152
|
search_wallet_pass_flag: 1,
|
|
153
|
+
filter_franchisor_permissions: ['use'],
|
|
153
154
|
...(search && {
|
|
154
155
|
search
|
|
155
156
|
}),
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Discount, DiscountFilterRejectKey } from './types';
|
|
2
|
+
/** 周期限制只影响当前使用;total_credits 才代表整张卡的次数耗尽。 */
|
|
3
|
+
export declare function getDiscountUsageLimitFailKey(discount: Discount): DiscountFilterRejectKey | null;
|
|
4
|
+
export declare function hasAvailableDiscountBalance(discount: Discount): boolean;
|
|
5
|
+
/** 按卡本身当前生命周期判断;预约时间或一次日程不匹配不代表已失效。 */
|
|
6
|
+
export declare function getDiscountLifecycleReason(discount: Discount): 'disabled' | 'exhausted' | 'expired' | undefined;
|
|
7
|
+
export declare function getDiscountUsageBlockReason(discount: Discount): string | undefined;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.getDiscountLifecycleReason = getDiscountLifecycleReason;
|
|
7
|
+
exports.getDiscountUsageBlockReason = getDiscountUsageBlockReason;
|
|
8
|
+
exports.getDiscountUsageLimitFailKey = getDiscountUsageLimitFailKey;
|
|
9
|
+
exports.hasAvailableDiscountBalance = hasAvailableDiscountBalance;
|
|
10
|
+
var _dayjs = _interopRequireDefault(require("dayjs"));
|
|
11
|
+
var _decimal = _interopRequireDefault(require("decimal.js"));
|
|
12
|
+
var _entitlementPass = require("./entitlementPass");
|
|
13
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
14
|
+
/** 周期限制只影响当前使用;total_credits 才代表整张卡的次数耗尽。 */
|
|
15
|
+
function getDiscountUsageLimitFailKey(discount) {
|
|
16
|
+
const value = discount.extension_data?.find(field => field.field_key === 'usage_credits')?.value;
|
|
17
|
+
if (!value) return null;
|
|
18
|
+
const limits = [['total_credits', discount.total_order_behavior_count], ['per_user_limit', discount.customer_order_behavior_count], ['max_per_day', discount.today_order_behavior_count], ['max_per_week', discount.week_order_behavior_count], ['max_per_month', discount.month_order_behavior_count]];
|
|
19
|
+
return limits.find(([key, count]) => Number(value[key]) > 0 && Number(count || 0) >= Number(value[key]))?.[0] || null;
|
|
20
|
+
}
|
|
21
|
+
function hasAvailableDiscountBalance(discount) {
|
|
22
|
+
if ((0, _entitlementPass.isEntitlementPass)(discount)) return (0, _entitlementPass.hasAvailableEntitlementUses)(discount);
|
|
23
|
+
return new _decimal.default(discount.par_value || 0).minus(discount.used_par_value || 0).greaterThan(0);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** 按卡本身当前生命周期判断;预约时间或一次日程不匹配不代表已失效。 */
|
|
27
|
+
function getDiscountLifecycleReason(discount) {
|
|
28
|
+
if (discount.limit_status && discount.limit_status !== 'enable') {
|
|
29
|
+
return 'disabled';
|
|
30
|
+
}
|
|
31
|
+
if (((0, _entitlementPass.isEntitlementPass)(discount) || discount.par_value != null) && !hasAvailableDiscountBalance(discount) || getDiscountUsageLimitFailKey(discount) === 'total_credits') {
|
|
32
|
+
return 'exhausted';
|
|
33
|
+
}
|
|
34
|
+
// 自定义日程沿用快照语义,不使用遗留固定有效期字段推断过期。
|
|
35
|
+
if (discount.metadata?.validity_type !== 'custom_schedule_validity' && discount.expire_time) {
|
|
36
|
+
const expiry = (0, _dayjs.default)(discount.expire_time);
|
|
37
|
+
if (expiry.isValid() && !(0, _dayjs.default)().isBefore(expiry)) return 'expired';
|
|
38
|
+
}
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
function getDiscountUsageBlockReason(discount) {
|
|
42
|
+
return getDiscountLifecycleReason(discount) || getDiscountUsageLimitFailKey(discount) || (discount.unified_available_status != null && String(discount.unified_available_status) !== '' && Number(discount.unified_available_status) === 0 ? 'server_unavailable' : undefined);
|
|
43
|
+
}
|
|
@@ -27,6 +27,7 @@ export declare class DiscountModule extends BaseModule implements Module, Discou
|
|
|
27
27
|
with_available_wallet_pass_flag?: 0 | 1;
|
|
28
28
|
with_virtual_currency?: 0 | 1;
|
|
29
29
|
with_entitlement_pass?: 0 | 1;
|
|
30
|
+
filter_order_wallet_pass_usage?: 0 | 1;
|
|
30
31
|
request_timezone: string;
|
|
31
32
|
order_id?: number | string;
|
|
32
33
|
}): Promise<DiscountPrepareConfigResult>;
|
|
@@ -37,10 +38,7 @@ export declare class DiscountModule extends BaseModule implements Module, Discou
|
|
|
37
38
|
}): Promise<DiscountWithReason>;
|
|
38
39
|
batchSearchByProductIds(productIds: number[]): Promise<any>;
|
|
39
40
|
filterEnabledDiscountList(discountList: Discount[]): Discount[];
|
|
40
|
-
private hasAvailableBalance;
|
|
41
|
-
private checkUsageCreditsLimit;
|
|
42
41
|
filterEnabledDiscountListWithReason(discountList: Discount[]): DiscountWithReason;
|
|
43
|
-
private checkUsageCreditsLimitWithFailKey;
|
|
44
42
|
uniqueByProductId(discountList: Discount[]): Discount[];
|
|
45
43
|
filterDiscountListByProductIds(discountList: Discount[], productIds: number[]): Discount[];
|
|
46
44
|
calcDiscountApplicableProductTotalPrice(discount: Discount): number | undefined;
|