@pisell/pisellos 0.10.42 → 0.10.44
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 +0 -9
- package/dist/modules/Order/index.d.ts +40 -8
- package/dist/modules/Order/index.js +1438 -878
- 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 +3 -1
- package/dist/modules/Order/utils.js +191 -71
- package/dist/modules/Payment/cashRecommendationAlgorithm.d.ts +4 -5
- package/dist/modules/Payment/cashRecommendationAlgorithm.js +17 -367
- 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 +2 -1
- 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/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/types.d.ts +5 -1
- package/dist/solution/BookingTicket/utils/orderCustomer.js +3 -2
- package/dist/solution/BookingTicket/utils/scan/applyGlobalScan.js +171 -96
- package/dist/solution/BookingTicket/utils/scan/cloudSearch.js +1 -1
- package/dist/solution/BookingTicket/utils/scan/formatGlobalScan.d.ts +2 -0
- package/dist/solution/BookingTicket/utils/scan/formatGlobalScan.js +12 -3
- 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/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/promotion/index.js +1 -46
- package/lib/modules/Order/index.d.ts +40 -8
- package/lib/modules/Order/index.js +434 -61
- 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 +3 -1
- package/lib/modules/Order/utils.js +114 -1
- package/lib/modules/Payment/cashRecommendationAlgorithm.d.ts +4 -5
- package/lib/modules/Payment/cashRecommendationAlgorithm.js +9 -354
- 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 +2 -1
- 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/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/types.d.ts +5 -1
- package/lib/solution/BookingTicket/utils/orderCustomer.js +2 -1
- package/lib/solution/BookingTicket/utils/scan/applyGlobalScan.js +65 -18
- package/lib/solution/BookingTicket/utils/scan/cloudSearch.js +1 -1
- package/lib/solution/BookingTicket/utils/scan/formatGlobalScan.d.ts +2 -0
- package/lib/solution/BookingTicket/utils/scan/formatGlobalScan.js +5 -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/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
|
@@ -106,16 +106,12 @@ async function applyGlobalScan(host, formatted, bridge) {
|
|
|
106
106
|
data,
|
|
107
107
|
scanCode,
|
|
108
108
|
customerId: formattedCustomerId,
|
|
109
|
-
isCrossShopWallet
|
|
109
|
+
isCrossShopWallet,
|
|
110
|
+
isWalletPassUnavailableAtStore
|
|
110
111
|
} = formatted;
|
|
111
112
|
if (searchType === 'wallet') {
|
|
112
113
|
const currentCustomer = host.getOrderCustomerSnapshot();
|
|
113
|
-
|
|
114
|
-
return {
|
|
115
|
-
status: 'success',
|
|
116
|
-
kind: 'customer'
|
|
117
|
-
};
|
|
118
|
-
}
|
|
114
|
+
const currentCustomerId = getRealCustomerId(currentCustomer);
|
|
119
115
|
let customer = data;
|
|
120
116
|
if (isCrossShopWallet) {
|
|
121
117
|
const sourceCustomerId = getRealCustomerId({
|
|
@@ -152,6 +148,36 @@ async function applyGlobalScan(host, formatted, bridge) {
|
|
|
152
148
|
...customer,
|
|
153
149
|
id: customerId
|
|
154
150
|
} : customer;
|
|
151
|
+
if (currentCustomerId === customerId) {
|
|
152
|
+
return {
|
|
153
|
+
status: 'success',
|
|
154
|
+
kind: 'customer'
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
if (currentCustomerId !== undefined) {
|
|
158
|
+
// 未接入新桥接的宿主保持原行为:已有客户时不直接覆盖。
|
|
159
|
+
if (!bridge?.requestCustomerSwitch) {
|
|
160
|
+
return {
|
|
161
|
+
status: 'success',
|
|
162
|
+
kind: 'customer'
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
try {
|
|
166
|
+
const switched = await bridge.requestCustomerSwitch(orderCustomer);
|
|
167
|
+
return switched ? {
|
|
168
|
+
status: 'success',
|
|
169
|
+
kind: 'customer'
|
|
170
|
+
} : {
|
|
171
|
+
status: 'cancelled'
|
|
172
|
+
};
|
|
173
|
+
} catch (error) {
|
|
174
|
+
bridge.onNotify?.('fail', getErrorMessage(error) || 'pisell2.text.scan-global-failed');
|
|
175
|
+
return {
|
|
176
|
+
status: 'failed',
|
|
177
|
+
reason: 'invalid_data'
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
}
|
|
155
181
|
host.setOrderCustomer(orderCustomer);
|
|
156
182
|
bridge?.onNotify?.('success', 'pisell2.text.add-client-success');
|
|
157
183
|
return {
|
|
@@ -160,13 +186,20 @@ async function applyGlobalScan(host, formatted, bridge) {
|
|
|
160
186
|
};
|
|
161
187
|
}
|
|
162
188
|
if (searchType === 'walletPass') {
|
|
189
|
+
if (isWalletPassUnavailableAtStore) {
|
|
190
|
+
bridge?.onNotify?.('fail', 'pisell2.text.wallet-pass-not-available-at-store');
|
|
191
|
+
return {
|
|
192
|
+
status: 'failed',
|
|
193
|
+
reason: 'invalid_data'
|
|
194
|
+
};
|
|
195
|
+
}
|
|
163
196
|
const snapshot = host.getOrderCustomerSnapshot();
|
|
164
|
-
const currentCustomerIsWalkIn = getRealCustomerId(snapshot) === undefined;
|
|
165
197
|
let activeCustomer = snapshot;
|
|
166
198
|
const bindWalletPassCustomer = async asset => {
|
|
167
|
-
if (
|
|
199
|
+
if (getRealCustomerId(activeCustomer) !== undefined) {
|
|
168
200
|
return {
|
|
169
|
-
resolveFailed: false
|
|
201
|
+
resolveFailed: false,
|
|
202
|
+
customerBound: false
|
|
170
203
|
};
|
|
171
204
|
}
|
|
172
205
|
const assetCustomerId = getWalletAssetCustomerId(asset) ?? getRealCustomerId({
|
|
@@ -177,7 +210,8 @@ async function applyGlobalScan(host, formatted, bridge) {
|
|
|
177
210
|
if (!host.resolveCustomerById) {
|
|
178
211
|
bridge?.onNotify?.('fail', 'pisell2.text.scan-global-failed');
|
|
179
212
|
return {
|
|
180
|
-
resolveFailed: true
|
|
213
|
+
resolveFailed: true,
|
|
214
|
+
customerBound: false
|
|
181
215
|
};
|
|
182
216
|
}
|
|
183
217
|
try {
|
|
@@ -186,7 +220,8 @@ async function applyGlobalScan(host, formatted, bridge) {
|
|
|
186
220
|
console.warn('[GlobalScan] Wallet asset customer lookup failed', error);
|
|
187
221
|
bridge?.onNotify?.('fail', getErrorMessage(error) || 'pisell2.text.scan-global-failed');
|
|
188
222
|
return {
|
|
189
|
-
resolveFailed: true
|
|
223
|
+
resolveFailed: true,
|
|
224
|
+
customerBound: false
|
|
190
225
|
};
|
|
191
226
|
}
|
|
192
227
|
}
|
|
@@ -196,7 +231,8 @@ async function applyGlobalScan(host, formatted, bridge) {
|
|
|
196
231
|
bridge?.onNotify?.('fail', 'pisell2.text.scan-global-failed');
|
|
197
232
|
}
|
|
198
233
|
return {
|
|
199
|
-
resolveFailed: assetCustomerId !== undefined
|
|
234
|
+
resolveFailed: assetCustomerId !== undefined,
|
|
235
|
+
customerBound: false
|
|
200
236
|
};
|
|
201
237
|
}
|
|
202
238
|
const orderCustomer = customer.id === undefined || customer.id === null ? {
|
|
@@ -210,7 +246,8 @@ async function applyGlobalScan(host, formatted, bridge) {
|
|
|
210
246
|
retainSearchResultCodes: [scanCode]
|
|
211
247
|
});
|
|
212
248
|
return {
|
|
213
|
-
resolveFailed: false
|
|
249
|
+
resolveFailed: false,
|
|
250
|
+
customerBound: true
|
|
214
251
|
};
|
|
215
252
|
};
|
|
216
253
|
const trySelectWalletAsset = async () => {
|
|
@@ -242,24 +279,34 @@ async function applyGlobalScan(host, formatted, bridge) {
|
|
|
242
279
|
reason: 'invalid_data'
|
|
243
280
|
};
|
|
244
281
|
};
|
|
282
|
+
|
|
283
|
+
// batch-search 可能返回来源门店客户;先映射到当前门店,确保 Wallet
|
|
284
|
+
// 支付资产查询使用本店客户上下文。
|
|
285
|
+
const bindResult = await bindWalletPassCustomer();
|
|
245
286
|
const walletSelection = await trySelectWalletAsset();
|
|
246
287
|
if (walletSelection) return walletSelection;
|
|
247
|
-
|
|
248
|
-
// 非支付类 Wallet Pass 仍可能由 batch-search 返回所属客户。
|
|
249
|
-
await bindWalletPassCustomer();
|
|
250
288
|
if (!bridge?.applyPromotionCode) {
|
|
251
289
|
return {
|
|
252
290
|
status: 'failed',
|
|
253
291
|
reason: 'no_bridge'
|
|
254
292
|
};
|
|
255
293
|
}
|
|
256
|
-
const
|
|
294
|
+
const promotionCustomerId = getRealCustomerId(activeCustomer);
|
|
295
|
+
const promotionResult = bindResult.customerBound ? await bridge.applyPromotionCode(scanCode, promotionCustomerId, {
|
|
296
|
+
suppressFailureNotification: true
|
|
297
|
+
}) : await bridge.applyPromotionCode(scanCode, promotionCustomerId);
|
|
257
298
|
if (promotionResult?.isAccepted ?? promotionResult?.isAvailable) {
|
|
258
299
|
return {
|
|
259
300
|
status: 'success',
|
|
260
301
|
kind: 'promotion'
|
|
261
302
|
};
|
|
262
303
|
}
|
|
304
|
+
if (bindResult.customerBound) {
|
|
305
|
+
return {
|
|
306
|
+
status: 'success',
|
|
307
|
+
kind: 'customer'
|
|
308
|
+
};
|
|
309
|
+
}
|
|
263
310
|
return {
|
|
264
311
|
status: 'failed',
|
|
265
312
|
reason: 'invalid_data'
|
|
@@ -15,7 +15,7 @@ const searchWalletPass = async (request, code) => {
|
|
|
15
15
|
relation_product: 1,
|
|
16
16
|
// 组装关联商品: 0不处理 1处理
|
|
17
17
|
with_franchisor_data: 1,
|
|
18
|
-
with: ['customer'],
|
|
18
|
+
with: ['customer', 'franchiseeWalletPass:shop_id,source_shop_id,product_id,use'],
|
|
19
19
|
tags: ['point_card',
|
|
20
20
|
// 积分卡
|
|
21
21
|
'gift_card',
|
|
@@ -10,6 +10,8 @@ export interface GlobalScanFormattedResult {
|
|
|
10
10
|
customerId?: string | number;
|
|
11
11
|
/** 仅 Wallet 返回;true 时禁止直接使用响应内的来源客户。 */
|
|
12
12
|
isCrossShopWallet?: boolean;
|
|
13
|
+
/** 总店 Wallet Pass 未共享给当前子店。 */
|
|
14
|
+
isWalletPassUnavailableAtStore?: boolean;
|
|
13
15
|
}
|
|
14
16
|
interface RawScanSearchResult {
|
|
15
17
|
searchType?: GlobalScanSearchType;
|
|
@@ -43,11 +43,15 @@ function formatGlobalScanResult(raw, scanCode) {
|
|
|
43
43
|
}
|
|
44
44
|
if (searchType === 'walletPass') {
|
|
45
45
|
const firstResult = resultData?.[0];
|
|
46
|
+
const isWalletPassUnavailableAtStore = firstResult?.is_franchisor_pass === 1 && firstResult?.franchisee_wallet_pass?.use !== 'enabled';
|
|
46
47
|
return {
|
|
47
48
|
searchType,
|
|
48
49
|
data: firstResult?.customer,
|
|
49
50
|
scanCode,
|
|
50
|
-
customerId: firstResult?.customer_id ?? firstResult?.holder?.customer_id ?? firstResult?.customer?.id ?? firstResult?.customer?.customer_id
|
|
51
|
+
customerId: firstResult?.customer_id ?? firstResult?.holder?.customer_id ?? firstResult?.customer?.id ?? firstResult?.customer?.customer_id,
|
|
52
|
+
...(isWalletPassUnavailableAtStore ? {
|
|
53
|
+
isWalletPassUnavailableAtStore: true
|
|
54
|
+
} : {})
|
|
51
55
|
};
|
|
52
56
|
}
|
|
53
57
|
if (searchType === 'product' || searchType === 'local_product') {
|
|
@@ -10,7 +10,11 @@ export interface ApiConfig {
|
|
|
10
10
|
method: 'GET' | 'POST' | 'PUT' | 'DELETE';
|
|
11
11
|
transformParams?: (params: any) => any;
|
|
12
12
|
transformResponse?: (response: any) => any;
|
|
13
|
-
options?: Record<string, any
|
|
13
|
+
options?: Record<string, any> | ((params: any) => Record<string, any>);
|
|
14
|
+
}
|
|
15
|
+
export interface ApiCallerOptions {
|
|
16
|
+
baseUrl?: string;
|
|
17
|
+
shopDomain?: string;
|
|
14
18
|
}
|
|
15
19
|
/**
|
|
16
20
|
* 渠道配置接口
|
|
@@ -65,12 +69,14 @@ export declare function getChannelConfig(channel: string): ChannelConfig;
|
|
|
65
69
|
export declare class ApiCaller {
|
|
66
70
|
private request;
|
|
67
71
|
private config;
|
|
72
|
+
private options;
|
|
68
73
|
constructor(request: {
|
|
69
74
|
get: <T = any>(url: string, data?: any, options?: any) => Promise<T>;
|
|
70
75
|
post: <T = any>(url: string, data?: any, options?: any) => Promise<T>;
|
|
71
76
|
put: <T = any>(url: string, data?: any, options?: any) => Promise<T>;
|
|
72
77
|
delete: <T = any>(url: string, options?: any) => Promise<T>;
|
|
73
|
-
}, config: ChannelConfig);
|
|
78
|
+
}, config: ChannelConfig, options?: ApiCallerOptions);
|
|
79
|
+
private resolveUrl;
|
|
74
80
|
/**
|
|
75
81
|
* 执行 API 调用
|
|
76
82
|
*/
|
|
@@ -84,4 +90,4 @@ export declare function createApiCaller(request: {
|
|
|
84
90
|
post: <T = any>(url: string, data?: any, options?: any) => Promise<T>;
|
|
85
91
|
put: <T = any>(url: string, data?: any, options?: any) => Promise<T>;
|
|
86
92
|
delete: <T = any>(url: string, options?: any) => Promise<T>;
|
|
87
|
-
}, channel: string): ApiCaller;
|
|
93
|
+
}, channel: string, options?: ApiCallerOptions): ApiCaller;
|
|
@@ -15,6 +15,8 @@ exports.getChannelConfig = getChannelConfig;
|
|
|
15
15
|
* API 配置接口
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
+
const h5CustomerAuthApiNames = new Set(['sendEmailVerificationCode', 'sendEmailLoginCode', 'sendSmsRegisterCode', 'sendEmailRegisterLink', 'verifyEmailRegistrationLink', 'emailPasswordLogin', 'emailCodeRegister', 'phoneCodeRegister', 'resendEmailRegisterLink', 'checkEmailLinkCode', 'sendSmsLoginCode', 'phoneCodeLogin', 'guestLogin', 'checkEmailExists', 'checkEmailCode', 'checkMobileCode', 'phonePasswordLogin', 'sendPasswordResetEmail', 'sendPasswordResetSms', 'sendResetPasswordLink', 'checkResetPasswordCode', 'resetPasswordByCode', 'resetPasswordByEmail', 'resetPasswordByPhone', 'verifyCode', 'register', 'login', 'oauthLogin', 'facebookLogin', 'appleLogin']);
|
|
19
|
+
|
|
18
20
|
/**
|
|
19
21
|
* 渠道配置接口
|
|
20
22
|
*/
|
|
@@ -278,6 +280,31 @@ const onlineStoreConfig = {
|
|
|
278
280
|
})
|
|
279
281
|
}
|
|
280
282
|
};
|
|
283
|
+
function withH5AuthPrefix(config) {
|
|
284
|
+
const nextConfig = {};
|
|
285
|
+
Object.entries(config).forEach(([key, apiConfig]) => {
|
|
286
|
+
if (!apiConfig.url.startsWith('/auth/')) {
|
|
287
|
+
nextConfig[key] = apiConfig;
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
nextConfig[key] = {
|
|
291
|
+
...apiConfig,
|
|
292
|
+
url: apiConfig.url.startsWith('/h5/') ? apiConfig.url : `/h5${apiConfig.url}`,
|
|
293
|
+
options: params => {
|
|
294
|
+
const {
|
|
295
|
+
withCredentials,
|
|
296
|
+
...baseOptions
|
|
297
|
+
} = (typeof apiConfig.options === 'function' ? apiConfig.options(params) : apiConfig.options) || {};
|
|
298
|
+
return {
|
|
299
|
+
...baseOptions,
|
|
300
|
+
prefix: false
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
});
|
|
305
|
+
return nextConfig;
|
|
306
|
+
}
|
|
307
|
+
const h5OnlineStoreConfig = withH5AuthPrefix(onlineStoreConfig);
|
|
281
308
|
|
|
282
309
|
/**
|
|
283
310
|
* 默认配置(通用配置)
|
|
@@ -1066,6 +1093,7 @@ const shopWebposConfig = {
|
|
|
1066
1093
|
*/
|
|
1067
1094
|
const channelConfigMap = exports.channelConfigMap = {
|
|
1068
1095
|
'online-store': onlineStoreConfig,
|
|
1096
|
+
'online-store-h5': h5OnlineStoreConfig,
|
|
1069
1097
|
'default': defaultConfig,
|
|
1070
1098
|
'webpos': webposConfig,
|
|
1071
1099
|
'shop.webpos': shopWebposConfig
|
|
@@ -1083,9 +1111,16 @@ function getChannelConfig(channel) {
|
|
|
1083
1111
|
* 负责根据配置执行 API 调用
|
|
1084
1112
|
*/
|
|
1085
1113
|
class ApiCaller {
|
|
1086
|
-
constructor(request, config) {
|
|
1114
|
+
constructor(request, config, options = {}) {
|
|
1087
1115
|
this.request = request;
|
|
1088
1116
|
this.config = config;
|
|
1117
|
+
this.options = options;
|
|
1118
|
+
}
|
|
1119
|
+
resolveUrl(url) {
|
|
1120
|
+
if (!this.options.baseUrl || !url.startsWith('/h5/auth/')) {
|
|
1121
|
+
return url;
|
|
1122
|
+
}
|
|
1123
|
+
return `${this.options.baseUrl.replace(/\/$/, '')}${url}`;
|
|
1089
1124
|
}
|
|
1090
1125
|
|
|
1091
1126
|
/**
|
|
@@ -1111,6 +1146,12 @@ class ApiCaller {
|
|
|
1111
1146
|
...options,
|
|
1112
1147
|
...additionalOptions
|
|
1113
1148
|
};
|
|
1149
|
+
if (apiConfig.url.startsWith('/h5/auth/') && h5CustomerAuthApiNames.has(apiName)) {
|
|
1150
|
+
requestOptions.h5CustomerAuth = true;
|
|
1151
|
+
if (this.options.shopDomain) {
|
|
1152
|
+
requestOptions.h5AuthShopDomain = this.options.shopDomain;
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1114
1155
|
|
|
1115
1156
|
// 处理动态 headers
|
|
1116
1157
|
if (requestOptions.headers && typeof requestOptions.headers === 'function') {
|
|
@@ -1123,19 +1164,19 @@ class ApiCaller {
|
|
|
1123
1164
|
switch (apiConfig.method) {
|
|
1124
1165
|
case 'GET':
|
|
1125
1166
|
if (transformedParams && Object.keys(transformedParams).length > 0) {
|
|
1126
|
-
response = await this.request.get(apiConfig.url, transformedParams, requestOptions);
|
|
1167
|
+
response = await this.request.get(this.resolveUrl(apiConfig.url), transformedParams, requestOptions);
|
|
1127
1168
|
} else {
|
|
1128
|
-
response = await this.request.get(apiConfig.url, undefined, requestOptions);
|
|
1169
|
+
response = await this.request.get(this.resolveUrl(apiConfig.url), undefined, requestOptions);
|
|
1129
1170
|
}
|
|
1130
1171
|
break;
|
|
1131
1172
|
case 'POST':
|
|
1132
|
-
response = await this.request.post(apiConfig.url, transformedParams, requestOptions);
|
|
1173
|
+
response = await this.request.post(this.resolveUrl(apiConfig.url), transformedParams, requestOptions);
|
|
1133
1174
|
break;
|
|
1134
1175
|
case 'PUT':
|
|
1135
|
-
response = await this.request.put(apiConfig.url, transformedParams, requestOptions);
|
|
1176
|
+
response = await this.request.put(this.resolveUrl(apiConfig.url), transformedParams, requestOptions);
|
|
1136
1177
|
break;
|
|
1137
1178
|
case 'DELETE':
|
|
1138
|
-
response = await this.request.delete(apiConfig.url, requestOptions);
|
|
1179
|
+
response = await this.request.delete(this.resolveUrl(apiConfig.url), requestOptions);
|
|
1139
1180
|
break;
|
|
1140
1181
|
default:
|
|
1141
1182
|
throw new Error(`不支持的 HTTP 方法: ${apiConfig.method}`);
|
|
@@ -1150,7 +1191,7 @@ class ApiCaller {
|
|
|
1150
1191
|
* 创建 API 调用器
|
|
1151
1192
|
*/
|
|
1152
1193
|
exports.ApiCaller = ApiCaller;
|
|
1153
|
-
function createApiCaller(request, channel) {
|
|
1194
|
+
function createApiCaller(request, channel, options) {
|
|
1154
1195
|
const config = getChannelConfig(channel);
|
|
1155
|
-
return new ApiCaller(request, config);
|
|
1196
|
+
return new ApiCaller(request, config, options);
|
|
1156
1197
|
}
|
|
@@ -227,7 +227,10 @@ class RegisterAndLoginImpl extends _BaseModule.BaseModule {
|
|
|
227
227
|
}
|
|
228
228
|
|
|
229
229
|
// 创建 API 调用器
|
|
230
|
-
this.apiCaller = (0, _config.createApiCaller)(this.request, this.channel
|
|
230
|
+
this.apiCaller = (0, _config.createApiCaller)(this.request, this.channel, {
|
|
231
|
+
baseUrl: this.otherParams.h5AuthBaseUrl,
|
|
232
|
+
shopDomain: this.otherParams.h5AuthShopDomain
|
|
233
|
+
});
|
|
231
234
|
|
|
232
235
|
// 尝试从本地存储恢复登录状态
|
|
233
236
|
await this.restoreLoginState();
|
|
@@ -1200,7 +1200,11 @@ class ScanOrderImpl extends _BaseSales.BaseSalesImpl {
|
|
|
1200
1200
|
return sourceMap;
|
|
1201
1201
|
}
|
|
1202
1202
|
getItemRuleRuntimeConfig() {
|
|
1203
|
-
|
|
1203
|
+
const runtimeConfig = this.itemRuleRuntimeConfig || {};
|
|
1204
|
+
return {
|
|
1205
|
+
...runtimeConfig,
|
|
1206
|
+
channel: runtimeConfig.channel ?? runtimeConfig.custom?.channel ?? this.otherParams?.channel
|
|
1207
|
+
};
|
|
1204
1208
|
}
|
|
1205
1209
|
async ensureItemRuleConfigsLoaded() {
|
|
1206
1210
|
if (this.itemRuleConfigs.length > 0) return this.itemRuleConfigs;
|
|
@@ -333,6 +333,7 @@ function buildItemRuleBusinessData(params) {
|
|
|
333
333
|
cartItems,
|
|
334
334
|
historicalItems,
|
|
335
335
|
serviceType: runtimeConfig.serviceType || 'dine-in',
|
|
336
|
+
channel: runtimeConfig.channel ?? runtimeConfig.custom?.channel,
|
|
336
337
|
submissionIndex: typeof runtimeConfig.submissionIndex === 'number' ? toNonNegativeInt(runtimeConfig.submissionIndex) : tempOrder.order_id ? 1 : 0,
|
|
337
338
|
custom: runtimeConfig.custom || {},
|
|
338
339
|
strategyConfigs: itemRuleConfigs
|
|
@@ -2,10 +2,11 @@ import type { Module, ModuleOptions, PisellCore } from '../../types';
|
|
|
2
2
|
import type { ProductData } from '../../modules';
|
|
3
3
|
import { type InvoiceLayoutConfig, type InvoiceTemplateSettings, type OpenDataConfig } from '../../modules/OpenData';
|
|
4
4
|
import type { LoadScheduleAvailableDateParams, ScheduleItem } from '../../modules/Schedule/types';
|
|
5
|
-
import type { ProductListLoadProductsParams } from '../../modules/ProductList';
|
|
5
|
+
import type { ProductListLoadProductsParams, ProductListStoreQueryParams } from '../../modules/ProductList';
|
|
6
6
|
import type { AvailabilityDateRange, AvailabilityOccupancy, AvailabilityProduct, AvailabilityResource, GetProductTimeRangesRequest } from '../../modules/ResourcePlanner';
|
|
7
|
-
import type { AvailabilityContextRef, ContextualAvailabilityQuery, ContextualAvailabilityQueryResult, ContextualCommitAvailabilitySelectionParams, ContextualProductTimeRangeGroup, UnifiedBookingSalesAddRetailProductParams, UnifiedBookingSalesApplyPlannerDiscountsParams, UnifiedBookingSalesCommitAvailabilitySelectionResult, UnifiedBookingSalesDiscountResult, UnifiedBookingSalesLoadProductsByScheduleDateParams, UnifiedBookingSalesLoadOpenDataParams, UnifiedBookingSalesPrepareAvailabilityProjectionParams, UnifiedBookingSalesPolicyResponse, UnifiedBookingSalesStageRetailProductsParams, UnifiedBookingSalesState } from './types';
|
|
7
|
+
import type { AvailabilityContextRef, ContextualAvailabilityQuery, ContextualAvailabilityQueryResult, ContextualCommitAvailabilitySelectionParams, ContextualProductTimeRangeGroup, UnifiedBookingSalesAddRetailProductParams, UnifiedBookingSalesApplyPlannerDiscountsParams, UnifiedBookingSalesCommitAvailabilitySelectionResult, UnifiedBookingSalesDiscountResult, UnifiedBookingSalesLoadProductsByScheduleDateParams, UnifiedBookingSalesLoadOpenDataParams, UnifiedBookingSalesPrepareAvailabilityProjectionParams, UnifiedBookingSalesPolicyResponse, UnifiedBookingSalesResolveProductAllergyRequirementParams, UnifiedBookingSalesStageRetailProductsParams, UnifiedBookingSalesState } from './types';
|
|
8
8
|
import { BookingTicket } from '../BookingTicket';
|
|
9
|
+
import type { AllergyRequirement } from '../../model/strategy/adapter/itemRule';
|
|
9
10
|
export * from './types';
|
|
10
11
|
export { AvailabilityError } from '../../modules/ResourcePlanner';
|
|
11
12
|
/**
|
|
@@ -73,6 +74,9 @@ export declare class UnifiedBookingSalesImpl extends BookingTicket {
|
|
|
73
74
|
private availabilityRemoteResourceQuerySequence;
|
|
74
75
|
private availabilityRemoteResourceEpoch;
|
|
75
76
|
private availabilityCommitLocks;
|
|
77
|
+
private allergyItemRuleEvaluator;
|
|
78
|
+
private allergyItemRuleConfigs;
|
|
79
|
+
private allergyItemRuleConfigsPromise;
|
|
76
80
|
initialize(core: PisellCore, options?: ModuleOptions): Promise<void>;
|
|
77
81
|
protected getSubmitOrderSalesChannel(): string;
|
|
78
82
|
private createAvailabilityFacadeError;
|
|
@@ -144,6 +148,14 @@ export declare class UnifiedBookingSalesImpl extends BookingTicket {
|
|
|
144
148
|
*/
|
|
145
149
|
loadOpenData(params: UnifiedBookingSalesLoadOpenDataParams): Promise<OpenDataConfig>;
|
|
146
150
|
getOpenData(): Promise<OpenDataConfig | null>;
|
|
151
|
+
private resetAllergyItemRuleState;
|
|
152
|
+
private fetchAllergyItemRuleConfigs;
|
|
153
|
+
private ensureAllergyItemRuleConfigsLoaded;
|
|
154
|
+
/**
|
|
155
|
+
* 评估当前渠道与商品是否命中 ALLERGY_CHECK。
|
|
156
|
+
* 只返回优先级最高 Action 中当前商品 Target 的 dataSource,不跨商品或 Action 合并。
|
|
157
|
+
*/
|
|
158
|
+
resolveProductAllergyRequirement(params: UnifiedBookingSalesResolveProductAllergyRequirementParams): Promise<AllergyRequirement | null>;
|
|
147
159
|
clearOpenDataCache(): void;
|
|
148
160
|
getInvoiceLayoutConfig(): Promise<InvoiceLayoutConfig>;
|
|
149
161
|
getInvoiceTemplateSettings(): Promise<InvoiceTemplateSettings>;
|
|
@@ -160,6 +172,7 @@ export declare class UnifiedBookingSalesImpl extends BookingTicket {
|
|
|
160
172
|
callback?: (result: any) => void;
|
|
161
173
|
subscriberId?: string;
|
|
162
174
|
}): Promise<ProductData[]>;
|
|
175
|
+
queryProducts(params?: ProductListStoreQueryParams): Promise<ProductData[]>;
|
|
163
176
|
/** Date-first entry point. It delegates protocol details and cache ownership to ScheduleModule. */
|
|
164
177
|
loadScheduleAvailableDate(params: LoadScheduleAvailableDateParams): Promise<import("../../modules/Date/types").ITime[]>;
|
|
165
178
|
/**
|
|
@@ -52,6 +52,7 @@ var _remoteOccupancyCache = require("./remoteOccupancyCache");
|
|
|
52
52
|
var _BookingTicket = require("../BookingTicket");
|
|
53
53
|
var _localClock = require("../../modules/ResourcePlanner/localClock");
|
|
54
54
|
var _platform = require("../../utils/platform");
|
|
55
|
+
var _itemRule = require("../../model/strategy/adapter/itemRule");
|
|
55
56
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
56
57
|
const OPEN_DATA_SECTION_CODES = ['sale', 'reservation', 'fulfillment', 'menu', 'workflow', 'basic', 'checkout', 'availability', 'documents'];
|
|
57
58
|
const OPEN_DATA_CACHE_TTL = 5 * 60 * 1000;
|
|
@@ -84,6 +85,22 @@ function getCandidateStartTime(candidate) {
|
|
|
84
85
|
return undefined;
|
|
85
86
|
}
|
|
86
87
|
}
|
|
88
|
+
function normalizePositiveIntegerList(value) {
|
|
89
|
+
if (!Array.isArray(value)) return [];
|
|
90
|
+
return Array.from(new Set(value.map(item => normalizePositiveInteger(item)).filter(item => item !== undefined)));
|
|
91
|
+
}
|
|
92
|
+
function toConfiguredBoolean(value) {
|
|
93
|
+
if (typeof value === 'boolean') return value;
|
|
94
|
+
if (typeof value === 'number') return value !== 0;
|
|
95
|
+
if (typeof value === 'string') {
|
|
96
|
+
return ['1', 'true', 'yes', 'y', 'on'].includes(value.trim().toLowerCase());
|
|
97
|
+
}
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
function normalizeItemRuleStrategyConfigs(value) {
|
|
101
|
+
if (!Array.isArray(value)) return [];
|
|
102
|
+
return value.filter(config => Boolean(config && typeof config === 'object' && config.metadata?.type === 'item_rule'));
|
|
103
|
+
}
|
|
87
104
|
function isRecord(value) {
|
|
88
105
|
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
89
106
|
}
|
|
@@ -1197,11 +1214,15 @@ class UnifiedBookingSalesImpl extends _BookingTicket.BookingTicket {
|
|
|
1197
1214
|
availabilityRemoteResourceQuerySequence = 0;
|
|
1198
1215
|
availabilityRemoteResourceEpoch = 0;
|
|
1199
1216
|
availabilityCommitLocks = new Set();
|
|
1217
|
+
allergyItemRuleEvaluator = new _itemRule.ItemRuleEvaluator();
|
|
1218
|
+
allergyItemRuleConfigs = [];
|
|
1219
|
+
allergyItemRuleConfigsPromise = null;
|
|
1200
1220
|
async initialize(core, options = {}) {
|
|
1201
1221
|
this.availabilityScheduleCatalog = null;
|
|
1202
1222
|
this.openDataTarget = null;
|
|
1203
1223
|
this.availabilityRuntimeOffsetMinutes = (0, _localClock.captureRuntimeOffsetMinutes)();
|
|
1204
1224
|
this.clearAvailabilityRemoteResourceState();
|
|
1225
|
+
this.resetAllergyItemRuleState();
|
|
1205
1226
|
await super.initialize(core, options);
|
|
1206
1227
|
const openDataBusinessCode = String(this.otherParams?.businessCode ?? this.otherParams?.business_code ?? '').trim();
|
|
1207
1228
|
const openDataChannel = String(this.otherParams?.channel ?? '').trim();
|
|
@@ -1346,6 +1367,7 @@ class UnifiedBookingSalesImpl extends _BookingTicket.BookingTicket {
|
|
|
1346
1367
|
cover = false
|
|
1347
1368
|
} = {}) {
|
|
1348
1369
|
const previousScope = this.getAvailabilityScopeDescriptor(this.otherParams || {});
|
|
1370
|
+
const previousItemRuleConfig = this.otherParams?.itemRule;
|
|
1349
1371
|
const explicitlySetsOpenData = Object.prototype.hasOwnProperty.call(params, 'openData');
|
|
1350
1372
|
await super.setOtherParams(params, {
|
|
1351
1373
|
cover
|
|
@@ -1358,11 +1380,15 @@ class UnifiedBookingSalesImpl extends _BookingTicket.BookingTicket {
|
|
|
1358
1380
|
if (shouldResetAvailabilityState) {
|
|
1359
1381
|
await this.resetAvailabilityProjectionState('set_other_params_reset');
|
|
1360
1382
|
}
|
|
1383
|
+
if (shouldResetAvailabilityState || explicitlySetsOpenData || previousItemRuleConfig !== this.otherParams?.itemRule) {
|
|
1384
|
+
this.resetAllergyItemRuleState();
|
|
1385
|
+
}
|
|
1361
1386
|
}
|
|
1362
1387
|
async destroy() {
|
|
1363
1388
|
this.availabilityScheduleCatalog = null;
|
|
1364
1389
|
this.availabilityRuntimeOffsetMinutes = null;
|
|
1365
1390
|
await this.resetAvailabilityProjectionState('destroy');
|
|
1391
|
+
this.resetAllergyItemRuleState();
|
|
1366
1392
|
await super.destroy();
|
|
1367
1393
|
}
|
|
1368
1394
|
createAvailabilityContextId() {
|
|
@@ -1721,6 +1747,98 @@ class UnifiedBookingSalesImpl extends _BookingTicket.BookingTicket {
|
|
|
1721
1747
|
channel
|
|
1722
1748
|
});
|
|
1723
1749
|
}
|
|
1750
|
+
resetAllergyItemRuleState() {
|
|
1751
|
+
this.allergyItemRuleConfigs = [];
|
|
1752
|
+
this.allergyItemRuleConfigsPromise = null;
|
|
1753
|
+
this.allergyItemRuleEvaluator.setStrategyConfigs([]);
|
|
1754
|
+
}
|
|
1755
|
+
async fetchAllergyItemRuleConfigs(strategyModelIds) {
|
|
1756
|
+
if (!strategyModelIds.length) return [];
|
|
1757
|
+
try {
|
|
1758
|
+
const response = await this.request.get('/promotion', {
|
|
1759
|
+
skip: 1,
|
|
1760
|
+
num: 99,
|
|
1761
|
+
status: 'active',
|
|
1762
|
+
ids: strategyModelIds
|
|
1763
|
+
});
|
|
1764
|
+
if (response?.code !== 200 || !Array.isArray(response?.data?.list)) {
|
|
1765
|
+
throw new Error('[UnifiedBookingSales] 商品过敏策略加载失败');
|
|
1766
|
+
}
|
|
1767
|
+
const list = response.data.list;
|
|
1768
|
+
return normalizeItemRuleStrategyConfigs(list.map(item => item?.metadata));
|
|
1769
|
+
} catch (error) {
|
|
1770
|
+
// this.logMethodError('fetchAllergyItemRuleConfigs', error, {
|
|
1771
|
+
// strategyModelIds,
|
|
1772
|
+
// });
|
|
1773
|
+
throw error;
|
|
1774
|
+
}
|
|
1775
|
+
}
|
|
1776
|
+
async ensureAllergyItemRuleConfigsLoaded() {
|
|
1777
|
+
if (this.allergyItemRuleConfigs.length > 0) {
|
|
1778
|
+
return this.allergyItemRuleConfigs;
|
|
1779
|
+
}
|
|
1780
|
+
if (this.allergyItemRuleConfigsPromise) {
|
|
1781
|
+
return this.allergyItemRuleConfigsPromise;
|
|
1782
|
+
}
|
|
1783
|
+
this.allergyItemRuleConfigsPromise = (async () => {
|
|
1784
|
+
const staticConfigs = normalizeItemRuleStrategyConfigs(this.otherParams?.itemRule?.strategyConfigs);
|
|
1785
|
+
if (staticConfigs.length > 0) {
|
|
1786
|
+
this.allergyItemRuleConfigs = staticConfigs;
|
|
1787
|
+
this.allergyItemRuleEvaluator.setStrategyConfigs(staticConfigs);
|
|
1788
|
+
return staticConfigs;
|
|
1789
|
+
}
|
|
1790
|
+
const openData = await this.getOpenData();
|
|
1791
|
+
const enabled = toConfiguredBoolean(openData?.['sale.enable_item_rules']);
|
|
1792
|
+
const strategyModelIds = normalizePositiveIntegerList(openData?.['sale.enabled_item_rules']);
|
|
1793
|
+
if (!enabled || !strategyModelIds.length) {
|
|
1794
|
+
this.allergyItemRuleEvaluator.setStrategyConfigs([]);
|
|
1795
|
+
return [];
|
|
1796
|
+
}
|
|
1797
|
+
const loaded = await this.fetchAllergyItemRuleConfigs(strategyModelIds);
|
|
1798
|
+
this.allergyItemRuleConfigs = loaded;
|
|
1799
|
+
this.allergyItemRuleEvaluator.setStrategyConfigs(loaded);
|
|
1800
|
+
return loaded;
|
|
1801
|
+
})();
|
|
1802
|
+
return this.allergyItemRuleConfigsPromise;
|
|
1803
|
+
}
|
|
1804
|
+
|
|
1805
|
+
/**
|
|
1806
|
+
* 评估当前渠道与商品是否命中 ALLERGY_CHECK。
|
|
1807
|
+
* 只返回优先级最高 Action 中当前商品 Target 的 dataSource,不跨商品或 Action 合并。
|
|
1808
|
+
*/
|
|
1809
|
+
async resolveProductAllergyRequirement(params) {
|
|
1810
|
+
const productId = normalizePositiveInteger(params.productId);
|
|
1811
|
+
if (!productId) return null;
|
|
1812
|
+
const strategyConfigs = await this.ensureAllergyItemRuleConfigsLoaded();
|
|
1813
|
+
if (!strategyConfigs.length) return null;
|
|
1814
|
+
const productVariantId = normalizePositiveInteger(params.productVariantId);
|
|
1815
|
+
const runtimeConfig = this.otherParams?.itemRule || {};
|
|
1816
|
+
const runtimePax = runtimeConfig.pax || {};
|
|
1817
|
+
const cartItems = (this.getOrderProducts?.() || []).map(item => ({
|
|
1818
|
+
product_id: Number(item?.product_id ?? item?.id),
|
|
1819
|
+
product_variant_id: Number(item?.product_variant_id ?? 0) || undefined,
|
|
1820
|
+
quantity: Number(item?.num ?? item?.quantity ?? 0)
|
|
1821
|
+
})).filter(item => Number.isFinite(item.product_id) && item.product_id > 0 && Number.isFinite(item.quantity) && item.quantity > 0);
|
|
1822
|
+
return this.allergyItemRuleEvaluator.getAllergyRequirement({
|
|
1823
|
+
pax: {
|
|
1824
|
+
total: Number(runtimePax.total ?? 0) || 0,
|
|
1825
|
+
adult: Number(runtimePax.adult ?? 0) || 0,
|
|
1826
|
+
child: Number(runtimePax.child ?? 0) || 0
|
|
1827
|
+
},
|
|
1828
|
+
cartItems,
|
|
1829
|
+
serviceType: String(runtimeConfig.serviceType ?? this.otherParams?.serviceType ?? ''),
|
|
1830
|
+
channel: String(params.channel ?? this.otherParams?.channel ?? this.otherParams?.platform ?? ''),
|
|
1831
|
+
currentProduct: {
|
|
1832
|
+
product_id: productId,
|
|
1833
|
+
...(productVariantId ? {
|
|
1834
|
+
product_variant_id: productVariantId
|
|
1835
|
+
} : {})
|
|
1836
|
+
},
|
|
1837
|
+
submissionIndex: Number(runtimeConfig.submissionIndex ?? 0) || 0,
|
|
1838
|
+
strategyConfigs,
|
|
1839
|
+
custom: runtimeConfig.custom
|
|
1840
|
+
});
|
|
1841
|
+
}
|
|
1724
1842
|
clearOpenDataCache() {
|
|
1725
1843
|
this.openDataTarget = null;
|
|
1726
1844
|
this.loadOpenDataInFlight = null;
|
|
@@ -1783,6 +1901,10 @@ class UnifiedBookingSalesImpl extends _BookingTicket.BookingTicket {
|
|
|
1783
1901
|
await this.core.effects.emit(`${this.name}:onProductsLoaded`, result);
|
|
1784
1902
|
return result;
|
|
1785
1903
|
}
|
|
1904
|
+
async queryProducts(params = {}) {
|
|
1905
|
+
if (!this.store.products) throw new Error('[UnifiedBookingSales] products 模块未初始化');
|
|
1906
|
+
return this.store.products.queryProducts(params);
|
|
1907
|
+
}
|
|
1786
1908
|
|
|
1787
1909
|
/** Date-first entry point. It delegates protocol details and cache ownership to ScheduleModule. */
|
|
1788
1910
|
async loadScheduleAvailableDate(params) {
|
|
@@ -3333,7 +3455,9 @@ class UnifiedBookingSalesImpl extends _BookingTicket.BookingTicket {
|
|
|
3333
3455
|
} else {
|
|
3334
3456
|
const beforeProductLineUids = new Set((this.getTempOrder()?.products || []).map(line => getProductLineUid(line)).filter(Boolean));
|
|
3335
3457
|
const beforeBookingUids = new Set((this.getTempOrder()?.bookings || []).map(item => getBookingUid(item)).filter(Boolean));
|
|
3336
|
-
const products = await this.addProductToOrder(transformed, booking
|
|
3458
|
+
const products = await this.addProductToOrder(transformed, booking, params.salesNotes?.length ? {
|
|
3459
|
+
salesNotes: params.salesNotes
|
|
3460
|
+
} : undefined);
|
|
3337
3461
|
const addedLines = products.filter(line => sameAvailabilityId(line.product_id ?? line.id, refreshedCandidate.productId) && line.metadata?.availability_candidate_key === refreshedCandidate.key && !beforeProductLineUids.has(getProductLineUid(line)));
|
|
3338
3462
|
productLineUids = addedLines.map(line => getProductLineUid(line)).filter(Boolean);
|
|
3339
3463
|
const addedBookings = (this.getTempOrder()?.bookings || []).filter(item => item.metadata?.availability_candidate_key === refreshedCandidate.key && !beforeBookingUids.has(getBookingUid(item)));
|