@pisell/pisellos 2.2.308 → 2.2.310
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/model/strategy/adapter/walletPass/evaluator.d.ts +2 -0
- package/dist/model/strategy/adapter/walletPass/evaluator.js +4 -2
- package/dist/modules/BookingContext/index.js +4 -4
- package/dist/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +58 -19
- package/dist/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +24 -12
- package/dist/modules/Customer/index.js +6 -3
- package/dist/modules/Customer/types.d.ts +2 -0
- package/dist/modules/Discount/availability.d.ts +7 -0
- package/dist/modules/Discount/availability.js +49 -0
- package/dist/modules/Discount/entitlementPass.d.ts +34 -0
- package/dist/modules/Discount/entitlementPass.js +147 -0
- package/dist/modules/Discount/index.d.ts +2 -2
- package/dist/modules/Discount/index.js +37 -151
- package/dist/modules/Discount/types.d.ts +36 -5
- package/dist/modules/OpenData/index.d.ts +1 -1
- package/dist/modules/OpenData/index.js +21 -12
- package/dist/modules/Order/index.d.ts +33 -4
- package/dist/modules/Order/index.js +830 -411
- package/dist/modules/Order/types.d.ts +17 -0
- package/dist/modules/Order/utils/orderCollectionIdentity.js +16 -12
- package/dist/modules/Order/utils/weighedProduct.d.ts +12 -0
- package/dist/modules/Order/utils/weighedProduct.js +94 -0
- package/dist/modules/Order/utils.d.ts +14 -2
- package/dist/modules/Order/utils.js +280 -79
- package/dist/modules/Payment/index.d.ts +0 -5
- package/dist/modules/Payment/index.js +35 -85
- package/dist/modules/Payment/types.d.ts +21 -1
- package/dist/modules/Payment/types.js +2 -0
- package/dist/modules/Payment/walletpass.d.ts +1 -1
- package/dist/modules/Payment/walletpass.js +80 -47
- package/dist/modules/ProductList/index.js +18 -5
- package/dist/modules/Rules/index.d.ts +2 -2
- package/dist/modules/Rules/index.js +484 -188
- package/dist/modules/Rules/types.d.ts +6 -0
- package/dist/server/index.js +8 -5
- package/dist/server/modules/order/index.js +6 -0
- package/dist/server/modules/order/types.d.ts +12 -0
- package/dist/server/modules/payment/index.d.ts +25 -6
- package/dist/server/modules/payment/index.js +660 -133
- package/dist/server/utils/small-ticket.d.ts +8 -5
- package/dist/server/utils/small-ticket.js +1269 -199
- package/dist/solution/BaseSales/index.d.ts +11 -2
- package/dist/solution/BaseSales/index.js +1463 -1173
- package/dist/solution/BaseSales/types.d.ts +7 -3
- package/dist/solution/BaseSales/utils/cartPromotion.js +141 -126
- package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +40 -25
- package/dist/solution/BaseSales/utils.js +3 -2
- package/dist/solution/BookingTicket/index.d.ts +11 -2
- package/dist/solution/BookingTicket/index.js +663 -542
- package/dist/solution/BookingTicket/types.d.ts +5 -1
- package/dist/solution/BookingTicket/utils/scan/applyGlobalScan.d.ts +2 -0
- package/dist/solution/BookingTicket/utils/scan/applyGlobalScan.js +338 -147
- package/dist/solution/BookingTicket/utils/scan/cloudSearch.js +8 -4
- package/dist/solution/BookingTicket/utils/scan/formatGlobalScan.d.ts +6 -0
- package/dist/solution/BookingTicket/utils/scan/formatGlobalScan.js +19 -6
- package/dist/solution/ScanOrder/utils.js +3 -2
- package/dist/solution/ShopDiscount/index.d.ts +2 -0
- package/dist/solution/ShopDiscount/index.js +99 -42
- package/dist/solution/ShopDiscount/utils.d.ts +4 -2
- package/dist/solution/ShopDiscount/utils.js +43 -23
- package/dist/solution/UnifiedBookingSales/index.d.ts +2 -2
- package/dist/solution/UnifiedBookingSales/index.js +5 -5
- package/dist/utils/os-private-payload.d.ts +5 -0
- package/dist/utils/os-private-payload.js +17 -0
- package/lib/model/strategy/adapter/walletPass/evaluator.d.ts +2 -0
- package/lib/model/strategy/adapter/walletPass/evaluator.js +2 -0
- package/lib/modules/BookingContext/index.js +3 -3
- package/lib/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +35 -6
- package/lib/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +13 -6
- package/lib/modules/Customer/index.js +5 -1
- package/lib/modules/Customer/types.d.ts +2 -0
- package/lib/modules/Discount/availability.d.ts +7 -0
- package/lib/modules/Discount/availability.js +43 -0
- package/lib/modules/Discount/entitlementPass.d.ts +34 -0
- package/lib/modules/Discount/entitlementPass.js +141 -0
- package/lib/modules/Discount/index.d.ts +2 -2
- package/lib/modules/Discount/index.js +23 -129
- package/lib/modules/Discount/types.d.ts +36 -5
- package/lib/modules/OpenData/index.d.ts +1 -1
- package/lib/modules/OpenData/index.js +11 -2
- package/lib/modules/Order/index.d.ts +33 -4
- package/lib/modules/Order/index.js +411 -81
- package/lib/modules/Order/types.d.ts +17 -0
- package/lib/modules/Order/utils/orderCollectionIdentity.js +14 -11
- package/lib/modules/Order/utils/weighedProduct.d.ts +12 -0
- package/lib/modules/Order/utils/weighedProduct.js +89 -0
- package/lib/modules/Order/utils.d.ts +14 -2
- package/lib/modules/Order/utils.js +189 -5
- package/lib/modules/Payment/index.d.ts +0 -5
- package/lib/modules/Payment/index.js +3 -28
- package/lib/modules/Payment/types.d.ts +21 -1
- package/lib/modules/Payment/types.js +1 -0
- package/lib/modules/Payment/walletpass.d.ts +1 -1
- package/lib/modules/Payment/walletpass.js +29 -9
- package/lib/modules/ProductList/index.js +14 -1
- package/lib/modules/Rules/index.d.ts +2 -2
- package/lib/modules/Rules/index.js +361 -112
- package/lib/modules/Rules/types.d.ts +6 -0
- package/lib/server/index.js +8 -5
- package/lib/server/modules/order/index.js +6 -0
- package/lib/server/modules/order/types.d.ts +12 -0
- package/lib/server/modules/payment/index.d.ts +25 -6
- package/lib/server/modules/payment/index.js +350 -78
- package/lib/server/utils/small-ticket.d.ts +8 -5
- package/lib/server/utils/small-ticket.js +1009 -121
- package/lib/solution/BaseSales/index.d.ts +11 -2
- package/lib/solution/BaseSales/index.js +164 -30
- package/lib/solution/BaseSales/types.d.ts +7 -3
- package/lib/solution/BaseSales/utils/cartPromotion.js +12 -4
- package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +24 -9
- package/lib/solution/BaseSales/utils.js +2 -1
- package/lib/solution/BookingTicket/index.d.ts +11 -2
- package/lib/solution/BookingTicket/index.js +103 -41
- package/lib/solution/BookingTicket/types.d.ts +5 -1
- package/lib/solution/BookingTicket/utils/scan/applyGlobalScan.d.ts +2 -0
- package/lib/solution/BookingTicket/utils/scan/applyGlobalScan.js +174 -42
- package/lib/solution/BookingTicket/utils/scan/cloudSearch.js +8 -4
- package/lib/solution/BookingTicket/utils/scan/formatGlobalScan.d.ts +6 -0
- package/lib/solution/BookingTicket/utils/scan/formatGlobalScan.js +11 -3
- package/lib/solution/ScanOrder/utils.js +2 -1
- package/lib/solution/ShopDiscount/index.d.ts +2 -0
- package/lib/solution/ShopDiscount/index.js +55 -9
- package/lib/solution/ShopDiscount/utils.d.ts +4 -2
- package/lib/solution/ShopDiscount/utils.js +42 -25
- package/lib/solution/UnifiedBookingSales/index.d.ts +2 -2
- package/lib/solution/UnifiedBookingSales/index.js +5 -5
- package/lib/utils/os-private-payload.d.ts +5 -0
- package/lib/utils/os-private-payload.js +22 -0
- package/package.json +1 -1
|
@@ -5,10 +5,21 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.applyGlobalScan = applyGlobalScan;
|
|
7
7
|
var _addProductDecision = require("../addProductDecision");
|
|
8
|
-
var _cartView = require("../cartView");
|
|
9
8
|
var _scanProductValues = require("./scanProductValues");
|
|
10
9
|
/** applyGlobalScan 所需的 BookingTicket 运行时能力 */
|
|
11
10
|
|
|
11
|
+
function getRealCustomerId(customer) {
|
|
12
|
+
const customerId = Number(customer?.id ?? customer?.customer_id);
|
|
13
|
+
return Number.isInteger(customerId) && customerId > 1 ? customerId : undefined;
|
|
14
|
+
}
|
|
15
|
+
function getWalletAssetCustomerId(asset) {
|
|
16
|
+
const customerId = Number(asset?.customer_id ?? asset?.holder?.customer_id);
|
|
17
|
+
return Number.isInteger(customerId) && customerId > 1 ? customerId : undefined;
|
|
18
|
+
}
|
|
19
|
+
function getErrorMessage(error) {
|
|
20
|
+
return error instanceof Error ? error.message : String(error || '');
|
|
21
|
+
}
|
|
22
|
+
|
|
12
23
|
/**
|
|
13
24
|
* 处理单条扫码商品的加车决策(含弹窗链)。
|
|
14
25
|
*/
|
|
@@ -93,17 +104,81 @@ async function applyGlobalScan(host, formatted, bridge) {
|
|
|
93
104
|
const {
|
|
94
105
|
searchType,
|
|
95
106
|
data,
|
|
96
|
-
scanCode
|
|
107
|
+
scanCode,
|
|
108
|
+
customerId: formattedCustomerId,
|
|
109
|
+
isCrossShopWallet,
|
|
110
|
+
isWalletPassUnavailableAtStore
|
|
97
111
|
} = formatted;
|
|
98
112
|
if (searchType === 'wallet') {
|
|
99
|
-
|
|
113
|
+
const currentCustomer = host.getOrderCustomerSnapshot();
|
|
114
|
+
const currentCustomerId = getRealCustomerId(currentCustomer);
|
|
115
|
+
let customer = data;
|
|
116
|
+
if (isCrossShopWallet) {
|
|
117
|
+
const sourceCustomerId = getRealCustomerId({
|
|
118
|
+
id: formattedCustomerId
|
|
119
|
+
});
|
|
120
|
+
if (sourceCustomerId === undefined || !host.resolveCustomerById) {
|
|
121
|
+
bridge?.onNotify?.('fail', 'pisell2.text.scan-global-failed');
|
|
122
|
+
return {
|
|
123
|
+
status: 'failed',
|
|
124
|
+
reason: 'invalid_data'
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
try {
|
|
128
|
+
customer = await host.resolveCustomerById(sourceCustomerId);
|
|
129
|
+
} catch (error) {
|
|
130
|
+
bridge?.onNotify?.('fail', getErrorMessage(error) || 'pisell2.text.scan-global-failed');
|
|
131
|
+
return {
|
|
132
|
+
status: 'failed',
|
|
133
|
+
reason: 'invalid_data'
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
const customerId = getRealCustomerId(customer) ?? (!isCrossShopWallet ? getRealCustomerId({
|
|
138
|
+
id: formattedCustomerId
|
|
139
|
+
}) : undefined);
|
|
140
|
+
if (!customer || customerId === undefined) {
|
|
100
141
|
bridge?.onNotify?.('fail', 'pisell2.text.scan-global-failed');
|
|
101
142
|
return {
|
|
102
143
|
status: 'failed',
|
|
103
144
|
reason: 'invalid_data'
|
|
104
145
|
};
|
|
105
146
|
}
|
|
106
|
-
|
|
147
|
+
const orderCustomer = customer.id === undefined || customer.id === null ? {
|
|
148
|
+
...customer,
|
|
149
|
+
id: customerId
|
|
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
|
+
}
|
|
181
|
+
host.setOrderCustomer(orderCustomer);
|
|
107
182
|
bridge?.onNotify?.('success', 'pisell2.text.add-client-success');
|
|
108
183
|
return {
|
|
109
184
|
status: 'success',
|
|
@@ -111,8 +186,70 @@ async function applyGlobalScan(host, formatted, bridge) {
|
|
|
111
186
|
};
|
|
112
187
|
}
|
|
113
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
|
+
}
|
|
114
196
|
const snapshot = host.getOrderCustomerSnapshot();
|
|
115
|
-
|
|
197
|
+
let activeCustomer = snapshot;
|
|
198
|
+
const bindWalletPassCustomer = async asset => {
|
|
199
|
+
if (getRealCustomerId(activeCustomer) !== undefined) {
|
|
200
|
+
return {
|
|
201
|
+
resolveFailed: false,
|
|
202
|
+
customerBound: false
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
const assetCustomerId = getWalletAssetCustomerId(asset) ?? getRealCustomerId({
|
|
206
|
+
id: formattedCustomerId
|
|
207
|
+
});
|
|
208
|
+
let customer = null;
|
|
209
|
+
if (assetCustomerId !== undefined) {
|
|
210
|
+
if (!host.resolveCustomerById) {
|
|
211
|
+
bridge?.onNotify?.('fail', 'pisell2.text.scan-global-failed');
|
|
212
|
+
return {
|
|
213
|
+
resolveFailed: true,
|
|
214
|
+
customerBound: false
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
try {
|
|
218
|
+
customer = await host.resolveCustomerById(assetCustomerId);
|
|
219
|
+
} catch (error) {
|
|
220
|
+
console.warn('[GlobalScan] Wallet asset customer lookup failed', error);
|
|
221
|
+
bridge?.onNotify?.('fail', getErrorMessage(error) || 'pisell2.text.scan-global-failed');
|
|
222
|
+
return {
|
|
223
|
+
resolveFailed: true,
|
|
224
|
+
customerBound: false
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
const customerId = getRealCustomerId(customer);
|
|
229
|
+
if (!customer || customerId === undefined) {
|
|
230
|
+
if (assetCustomerId !== undefined) {
|
|
231
|
+
bridge?.onNotify?.('fail', 'pisell2.text.scan-global-failed');
|
|
232
|
+
}
|
|
233
|
+
return {
|
|
234
|
+
resolveFailed: assetCustomerId !== undefined,
|
|
235
|
+
customerBound: false
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
const orderCustomer = customer.id === undefined || customer.id === null ? {
|
|
239
|
+
...customer,
|
|
240
|
+
id: customerId
|
|
241
|
+
} : customer;
|
|
242
|
+
host.setOrderCustomer(orderCustomer);
|
|
243
|
+
activeCustomer = orderCustomer;
|
|
244
|
+
await host.refreshWalletAssets?.({
|
|
245
|
+
preserveSelection: false,
|
|
246
|
+
retainSearchResultCodes: [scanCode]
|
|
247
|
+
});
|
|
248
|
+
return {
|
|
249
|
+
resolveFailed: false,
|
|
250
|
+
customerBound: true
|
|
251
|
+
};
|
|
252
|
+
};
|
|
116
253
|
const trySelectWalletAsset = async () => {
|
|
117
254
|
if (!host.scanWalletAsset) return null;
|
|
118
255
|
const walletResult = await host.scanWalletAsset(scanCode).catch(error => {
|
|
@@ -123,61 +260,56 @@ async function applyGlobalScan(host, formatted, bridge) {
|
|
|
123
260
|
if (walletResult.type !== 'normalCode' || !walletResult.asset) {
|
|
124
261
|
return null;
|
|
125
262
|
}
|
|
263
|
+
const bindResult = await bindWalletPassCustomer(walletResult.asset);
|
|
126
264
|
if (walletResult.selected) {
|
|
127
|
-
|
|
265
|
+
if (!bindResult.resolveFailed) {
|
|
266
|
+
bridge?.onNotify?.('success');
|
|
267
|
+
}
|
|
128
268
|
return {
|
|
129
269
|
status: 'success',
|
|
130
270
|
kind: 'promotion'
|
|
131
271
|
};
|
|
132
272
|
}
|
|
133
273
|
// 已识别为 Wallet 支付资产但当前不可用:保留禁用展示,不回退优惠码。
|
|
134
|
-
|
|
274
|
+
if (!bindResult.resolveFailed) {
|
|
275
|
+
bridge?.onNotify?.('fail', 'pisell2.text.goodpass.code-invalid-cart');
|
|
276
|
+
}
|
|
135
277
|
return {
|
|
136
278
|
status: 'failed',
|
|
137
279
|
reason: 'invalid_data'
|
|
138
280
|
};
|
|
139
281
|
};
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
reason: 'no_bridge'
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
const result = await bridge.applyPromotionCode(scanCode);
|
|
151
|
-
if (result?.isAccepted ?? result?.isAvailable) {
|
|
152
|
-
return {
|
|
153
|
-
status: 'success',
|
|
154
|
-
kind: 'promotion'
|
|
155
|
-
};
|
|
156
|
-
}
|
|
282
|
+
|
|
283
|
+
// batch-search 可能返回来源门店客户;先映射到当前门店,确保 Wallet
|
|
284
|
+
// 支付资产查询使用本店客户上下文。
|
|
285
|
+
const bindResult = await bindWalletPassCustomer();
|
|
286
|
+
const walletSelection = await trySelectWalletAsset();
|
|
287
|
+
if (walletSelection) return walletSelection;
|
|
288
|
+
if (!bridge?.applyPromotionCode) {
|
|
157
289
|
return {
|
|
158
290
|
status: 'failed',
|
|
159
|
-
reason: '
|
|
291
|
+
reason: 'no_bridge'
|
|
160
292
|
};
|
|
161
293
|
}
|
|
162
|
-
|
|
163
|
-
await
|
|
164
|
-
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
294
|
+
const promotionCustomerId = getRealCustomerId(activeCustomer);
|
|
295
|
+
const promotionResult = bindResult.customerBound ? await bridge.applyPromotionCode(scanCode, promotionCustomerId, {
|
|
296
|
+
suppressFailureNotification: true
|
|
297
|
+
}) : await bridge.applyPromotionCode(scanCode, promotionCustomerId);
|
|
298
|
+
if (promotionResult?.isAccepted ?? promotionResult?.isAvailable) {
|
|
299
|
+
return {
|
|
300
|
+
status: 'success',
|
|
301
|
+
kind: 'promotion'
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
if (bindResult.customerBound) {
|
|
305
|
+
return {
|
|
306
|
+
status: 'success',
|
|
307
|
+
kind: 'customer'
|
|
308
|
+
};
|
|
176
309
|
}
|
|
177
|
-
bridge?.onNotify?.('success', 'pisell2.text.add-client-success');
|
|
178
310
|
return {
|
|
179
|
-
status: '
|
|
180
|
-
|
|
311
|
+
status: 'failed',
|
|
312
|
+
reason: 'invalid_data'
|
|
181
313
|
};
|
|
182
314
|
}
|
|
183
315
|
if (searchType === 'product' || searchType === 'local_product') {
|
|
@@ -14,7 +14,8 @@ const searchWalletPass = async (request, code) => {
|
|
|
14
14
|
// 翻译识别码名称 0:不翻译 1:翻译
|
|
15
15
|
relation_product: 1,
|
|
16
16
|
// 组装关联商品: 0不处理 1处理
|
|
17
|
-
|
|
17
|
+
with_franchisor_data: 1,
|
|
18
|
+
with: ['customer', 'franchiseeWalletPass:shop_id,source_shop_id,product_id,use'],
|
|
18
19
|
tags: ['point_card',
|
|
19
20
|
// 积分卡
|
|
20
21
|
'gift_card',
|
|
@@ -25,6 +26,8 @@ const searchWalletPass = async (request, code) => {
|
|
|
25
26
|
// 折扣卡
|
|
26
27
|
'good_pass',
|
|
27
28
|
// 商品券
|
|
29
|
+
'entitlement_pass',
|
|
30
|
+
// 月卡 / 年卡 / 多次使用卡
|
|
28
31
|
'session_ticket',
|
|
29
32
|
// 场次票
|
|
30
33
|
'appointment_ticket' // 预约类门票
|
|
@@ -51,17 +54,18 @@ exports.searchWalletPass = searchWalletPass;
|
|
|
51
54
|
const searchWallet = async (request, code) => {
|
|
52
55
|
const params = {
|
|
53
56
|
code,
|
|
54
|
-
with_customer: 1
|
|
57
|
+
with_customer: 1,
|
|
58
|
+
with_franchisor_data: 1
|
|
55
59
|
};
|
|
56
60
|
try {
|
|
57
61
|
const res = await request?.post('/wallet/detail/search', params);
|
|
58
|
-
if (res?.code
|
|
62
|
+
if (res?.status === true && res?.code === 200 && !!res?.data) {
|
|
59
63
|
return {
|
|
60
64
|
searchType: 'wallet',
|
|
61
65
|
response: res
|
|
62
66
|
};
|
|
63
67
|
}
|
|
64
|
-
throw new Error('没有找到对应的识别码');
|
|
68
|
+
throw new Error(res?.message || '没有找到对应的识别码');
|
|
65
69
|
} catch (error) {
|
|
66
70
|
throw error;
|
|
67
71
|
}
|
|
@@ -6,6 +6,12 @@ export interface GlobalScanFormattedResult {
|
|
|
6
6
|
searchType: GlobalScanSearchType;
|
|
7
7
|
data: any;
|
|
8
8
|
scanCode: string;
|
|
9
|
+
/** Wallet / Wallet Pass 来源客户 ID;跨店时必须先解析为当前店客户。 */
|
|
10
|
+
customerId?: string | number;
|
|
11
|
+
/** 仅 Wallet 返回;true 时禁止直接使用响应内的来源客户。 */
|
|
12
|
+
isCrossShopWallet?: boolean;
|
|
13
|
+
/** 总店 Wallet Pass 未共享给当前子店。 */
|
|
14
|
+
isWalletPassUnavailableAtStore?: boolean;
|
|
9
15
|
}
|
|
10
16
|
interface RawScanSearchResult {
|
|
11
17
|
searchType?: GlobalScanSearchType;
|
|
@@ -36,14 +36,22 @@ function formatGlobalScanResult(raw, scanCode) {
|
|
|
36
36
|
return {
|
|
37
37
|
searchType,
|
|
38
38
|
data: resultData?.customer,
|
|
39
|
-
scanCode
|
|
39
|
+
scanCode,
|
|
40
|
+
customerId: resultData?.customer_id,
|
|
41
|
+
isCrossShopWallet: resultData?.is_cross_shop_wallet === true || Number(resultData?.is_cross_shop_wallet) === 1
|
|
40
42
|
};
|
|
41
43
|
}
|
|
42
44
|
if (searchType === 'walletPass') {
|
|
45
|
+
const firstResult = resultData?.[0];
|
|
46
|
+
const isWalletPassUnavailableAtStore = firstResult?.is_franchisor_pass === 1 && firstResult?.franchisee_wallet_pass?.use !== 'enabled';
|
|
43
47
|
return {
|
|
44
48
|
searchType,
|
|
45
|
-
data:
|
|
46
|
-
scanCode
|
|
49
|
+
data: firstResult?.customer,
|
|
50
|
+
scanCode,
|
|
51
|
+
customerId: firstResult?.customer_id ?? firstResult?.holder?.customer_id ?? firstResult?.customer?.id ?? firstResult?.customer?.customer_id,
|
|
52
|
+
...(isWalletPassUnavailableAtStore ? {
|
|
53
|
+
isWalletPassUnavailableAtStore: true
|
|
54
|
+
} : {})
|
|
47
55
|
};
|
|
48
56
|
}
|
|
49
57
|
if (searchType === 'product' || searchType === 'local_product') {
|
|
@@ -34,6 +34,7 @@ exports.toNonNegativeInt = toNonNegativeInt;
|
|
|
34
34
|
exports.toNonNegativeNumber = toNonNegativeNumber;
|
|
35
35
|
exports.toPositiveString = toPositiveString;
|
|
36
36
|
exports.toPriceString = toPriceString;
|
|
37
|
+
var _weighedProduct = require("../../modules/Order/utils/weighedProduct");
|
|
37
38
|
var _dayjs = _interopRequireDefault(require("dayjs"));
|
|
38
39
|
var _decimal = _interopRequireDefault(require("decimal.js"));
|
|
39
40
|
var _utils = require("../../modules/Order/utils");
|
|
@@ -612,7 +613,7 @@ function normalizeOrderProduct(product) {
|
|
|
612
613
|
});
|
|
613
614
|
const composedOriginalPrice = (0, _utils.composeLinePrice)({
|
|
614
615
|
mainPrice: mainOriginalStr,
|
|
615
|
-
bundle: normalizedBundle,
|
|
616
|
+
bundle: Number(productSku.open_sold_weight) === 1 && (0, _manualProductDiscount.hasManualProductDiscountFlag)(metadata) ? (0, _weighedProduct.withOriginalBundleOptionPrices)(normalizedBundle) : normalizedBundle,
|
|
616
617
|
useOriginalBundle: true
|
|
617
618
|
});
|
|
618
619
|
const finalOriginalPrice = (0, _utils.resolveManualOverrideLineOriginalPrice)({
|
|
@@ -43,6 +43,7 @@ export declare class ShopDiscountImpl extends BaseModule implements Module {
|
|
|
43
43
|
batchSearchByProductIds(productIds: number[]): Promise<Discount[]>;
|
|
44
44
|
scanCode(code: string, customerId?: number): Promise<{
|
|
45
45
|
isAvailable: boolean;
|
|
46
|
+
isAccepted?: boolean;
|
|
46
47
|
productList: Record<string, any>[];
|
|
47
48
|
discountList: Discount[];
|
|
48
49
|
type: "server" | "clientCalc";
|
|
@@ -62,6 +63,7 @@ export declare class ShopDiscountImpl extends BaseModule implements Module {
|
|
|
62
63
|
action?: 'create' | 'edit';
|
|
63
64
|
with_good_pass?: 0 | 1;
|
|
64
65
|
with_discount_card?: 0 | 1;
|
|
66
|
+
with_entitlement_pass?: 0 | 1;
|
|
65
67
|
}): Promise<void>;
|
|
66
68
|
}
|
|
67
69
|
export default ShopDiscountImpl;
|
|
@@ -7,8 +7,10 @@ exports.default = exports.ShopDiscountImpl = void 0;
|
|
|
7
7
|
var _BaseModule = require("../../modules/BaseModule");
|
|
8
8
|
var _Discount = require("../../modules/Discount");
|
|
9
9
|
var _Rules = require("../../modules/Rules");
|
|
10
|
+
var _entitlementPass = require("../../modules/Discount/entitlementPass");
|
|
10
11
|
var _utils = require("./utils");
|
|
11
12
|
var _types = require("../../modules/Rules/types");
|
|
13
|
+
var _availability = require("../../modules/Discount/availability");
|
|
12
14
|
var _decimal = _interopRequireDefault(require("decimal.js"));
|
|
13
15
|
var _lodashEs = require("lodash-es");
|
|
14
16
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
@@ -125,7 +127,8 @@ class ShopDiscountImpl extends _BaseModule.BaseModule {
|
|
|
125
127
|
customerId: customer.id,
|
|
126
128
|
action: 'create',
|
|
127
129
|
with_good_pass: 1,
|
|
128
|
-
with_discount_card: 1
|
|
130
|
+
with_discount_card: 1,
|
|
131
|
+
with_entitlement_pass: 1
|
|
129
132
|
});
|
|
130
133
|
});
|
|
131
134
|
}
|
|
@@ -347,6 +350,7 @@ class ShopDiscountImpl extends _BaseModule.BaseModule {
|
|
|
347
350
|
const {
|
|
348
351
|
productList: newProductList,
|
|
349
352
|
discountList: newDiscountList,
|
|
353
|
+
evaluatedDiscountList,
|
|
350
354
|
isAvailable,
|
|
351
355
|
unavailableReason
|
|
352
356
|
} = rulesModule.isDiscountListAvailable({
|
|
@@ -361,6 +365,7 @@ class ShopDiscountImpl extends _BaseModule.BaseModule {
|
|
|
361
365
|
productList: this.store.productList || [],
|
|
362
366
|
discountList: this.getDiscountList()
|
|
363
367
|
};
|
|
368
|
+
let isAccepted = isAvailable;
|
|
364
369
|
if (isAvailable) {
|
|
365
370
|
this.setDiscountList(newDiscountList || []);
|
|
366
371
|
this.store.originalDiscountList = newDiscountList || [];
|
|
@@ -368,12 +373,34 @@ class ShopDiscountImpl extends _BaseModule.BaseModule {
|
|
|
368
373
|
if (this.isWalkIn() && resultDiscountList.length && this.options.otherParams?.platform === 'shop' && !((resultDiscountList[0].customer_id || 1) === 1)) {
|
|
369
374
|
await this.getCustomerWallet(resultDiscountList[0].customer_id);
|
|
370
375
|
}
|
|
376
|
+
} else {
|
|
377
|
+
const scannedIds = new Set(withScanList.map(discount => String(discount.id)));
|
|
378
|
+
const retainedScannedDiscounts = (evaluatedDiscountList || withScanList).filter(discount => scannedIds.has(String(discount.id)) && !(0, _availability.getDiscountLifecycleReason)(discount));
|
|
379
|
+
if (retainedScannedDiscounts.length) {
|
|
380
|
+
const retainedDiscounts = new Map(this.getDiscountList().map(discount => [String(discount.id), discount]));
|
|
381
|
+
retainedScannedDiscounts.forEach(discount => {
|
|
382
|
+
retainedDiscounts.set(String(discount.id), {
|
|
383
|
+
...discount,
|
|
384
|
+
isScan: true,
|
|
385
|
+
isAvailable: false,
|
|
386
|
+
isSelected: false,
|
|
387
|
+
reasonCode: discount.reasonCode || 'product_not_applicable',
|
|
388
|
+
appliedProductDetails: [],
|
|
389
|
+
savedAmount: 0
|
|
390
|
+
});
|
|
391
|
+
});
|
|
392
|
+
this.setDiscountList([...retainedDiscounts.values()]);
|
|
393
|
+
this.store.originalDiscountList = this.getDiscountList();
|
|
394
|
+
await this.store.discount?.setOriginalDiscountList(this.getDiscountList());
|
|
395
|
+
isAccepted = true;
|
|
396
|
+
}
|
|
371
397
|
}
|
|
372
398
|
return {
|
|
373
399
|
type: "clientCalc",
|
|
374
400
|
isAvailable: isAvailable || false,
|
|
401
|
+
isAccepted: Boolean(isAccepted),
|
|
375
402
|
productList: newProductList || this.store.productList || [],
|
|
376
|
-
discountList:
|
|
403
|
+
discountList: this.getDiscountList(),
|
|
377
404
|
unavailableReason
|
|
378
405
|
};
|
|
379
406
|
} catch (error) {
|
|
@@ -416,7 +443,7 @@ class ShopDiscountImpl extends _BaseModule.BaseModule {
|
|
|
416
443
|
if (item.booking_id) {
|
|
417
444
|
const product = this.hooks?.getProduct(item);
|
|
418
445
|
(item.discount_list || []).forEach(discount => {
|
|
419
|
-
if (discount.id && ['good_pass', 'discount_card'].includes(discount.type)) {
|
|
446
|
+
if (discount.id && ['entitlement_pass', 'good_pass', 'discount_card'].includes(discount.type)) {
|
|
420
447
|
const index = editModeDiscountList.findIndex(n => n.id === (discount.discount?.resource_id || discount.id));
|
|
421
448
|
if (index !== -1) {
|
|
422
449
|
editModeDiscountList[index] = {
|
|
@@ -522,6 +549,8 @@ class ShopDiscountImpl extends _BaseModule.BaseModule {
|
|
|
522
549
|
with_good_pass: 1,
|
|
523
550
|
with_discount_card: 1,
|
|
524
551
|
with_wallet_pass_holder: 1,
|
|
552
|
+
with_entitlement_pass: 1,
|
|
553
|
+
filter_order_wallet_pass_usage: 0,
|
|
525
554
|
request_timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
526
555
|
...(orderId ? {
|
|
527
556
|
order_id: orderId
|
|
@@ -530,17 +559,34 @@ class ShopDiscountImpl extends _BaseModule.BaseModule {
|
|
|
530
559
|
const goodPassList = prepareConfigResult?.discountList || [];
|
|
531
560
|
const scanDiscount = this.getDiscountList()?.filter(item => item.isScan);
|
|
532
561
|
|
|
533
|
-
//
|
|
534
|
-
|
|
535
|
-
const
|
|
536
|
-
const
|
|
562
|
+
// 扫码 Entitlement Pass 与客户列表重复时保留扫码选择状态,但刷新实时余额。
|
|
563
|
+
// 其他券继续沿用原来的“保留扫码结果、剔除列表重复项”行为。
|
|
564
|
+
const scanDiscountById = new Map(scanDiscount.map(discount => [String(discount.id), discount]));
|
|
565
|
+
const refreshedScanEntitlementIds = new Set();
|
|
566
|
+
const newGoodPassList = goodPassList.flatMap(discount => {
|
|
567
|
+
const key = String(discount.id);
|
|
568
|
+
const scannedDiscount = scanDiscountById.get(key);
|
|
569
|
+
if (!scannedDiscount) return [discount];
|
|
570
|
+
if ((0, _entitlementPass.isEntitlementPass)(scannedDiscount) && (0, _entitlementPass.isEntitlementPass)(discount)) {
|
|
571
|
+
refreshedScanEntitlementIds.add(key);
|
|
572
|
+
return [{
|
|
573
|
+
...discount,
|
|
574
|
+
isScan: true,
|
|
575
|
+
isSelected: scannedDiscount.isSelected ?? discount.isSelected,
|
|
576
|
+
isManualSelect: scannedDiscount.isManualSelect ?? discount.isManualSelect
|
|
577
|
+
}];
|
|
578
|
+
}
|
|
579
|
+
return [];
|
|
580
|
+
});
|
|
581
|
+
const retainedScanDiscounts = scanDiscount.filter(discount => !refreshedScanEntitlementIds.has(String(discount.id)));
|
|
582
|
+
const newDiscountList = [...retainedScanDiscounts, ...newGoodPassList];
|
|
537
583
|
|
|
538
584
|
// 存储原始优惠券列表
|
|
539
585
|
this.store.originalDiscountList = newDiscountList;
|
|
540
586
|
await this.store.discount?.setOriginalDiscountList(newDiscountList);
|
|
541
587
|
|
|
542
|
-
//
|
|
543
|
-
const filteredDiscountList =
|
|
588
|
+
// 日程限制交给 Rules 计算可用状态,展示列表保留当前不可用的卡券。
|
|
589
|
+
const filteredDiscountList = newDiscountList;
|
|
544
590
|
this.store.filteredDiscountList = filteredDiscountList;
|
|
545
591
|
this.setDiscountList(filteredDiscountList || []);
|
|
546
592
|
if (this.store.productList?.length) {
|
|
@@ -59,12 +59,14 @@ export interface ScheduleItem {
|
|
|
59
59
|
}
|
|
60
60
|
export declare const getDateIsInSchedule: (dateTime: string, scheduleList: ScheduleItem[]) => boolean;
|
|
61
61
|
/**
|
|
62
|
-
*
|
|
62
|
+
* 根据商品使用时间过滤优惠券列表(预约开始时间或普通商品的当前时间)
|
|
63
63
|
* @param discountList 优惠券列表
|
|
64
|
-
* @param bookingTime
|
|
64
|
+
* @param bookingTime 使用时间 (格式: YYYY-MM-DD HH:mm:ss),未确定时不提前过滤
|
|
65
65
|
* @returns 过滤后的优惠券列表
|
|
66
66
|
*/
|
|
67
67
|
export declare const filterDiscountListByBookingTime: (discountList: Discount[], bookingTime: string | null) => Discount[];
|
|
68
|
+
/** 与抵扣计算共用时间判断,区分未开始、有效期不匹配和日程限制。 */
|
|
69
|
+
export declare const getDiscountTimeUnavailableReason: (discount: Discount, useTime: string) => 'not_started' | 'time_limit' | 'outside_schedule' | undefined;
|
|
68
70
|
/**
|
|
69
71
|
* 判断是否是订单级别的固定金额折扣卡
|
|
70
72
|
* @param discount 折扣
|
|
@@ -3,10 +3,15 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.uniqueById = exports.isOrderLevelFixedAmountDiscount = exports.isNormalProductByDurationSchedule = exports.isAllNormalProduct = exports.getDiscountListAmountTotal = exports.getDiscountListAmount = exports.getDiscountAmount = exports.getDateIsInSchedule = exports.filterDiscountListByBookingTime = exports.calculateOrderLevelDiscountAllocation = void 0;
|
|
6
|
+
exports.uniqueById = exports.isOrderLevelFixedAmountDiscount = exports.isNormalProductByDurationSchedule = exports.isAllNormalProduct = exports.getDiscountTimeUnavailableReason = exports.getDiscountListAmountTotal = exports.getDiscountListAmount = exports.getDiscountAmount = exports.getDateIsInSchedule = exports.filterDiscountListByBookingTime = exports.calculateOrderLevelDiscountAllocation = void 0;
|
|
7
7
|
var _decimal = _interopRequireDefault(require("decimal.js"));
|
|
8
8
|
var _dayjs = _interopRequireDefault(require("dayjs"));
|
|
9
|
+
var _isSameOrAfter = _interopRequireDefault(require("dayjs/plugin/isSameOrAfter"));
|
|
10
|
+
var _isSameOrBefore = _interopRequireDefault(require("dayjs/plugin/isSameOrBefore"));
|
|
11
|
+
var _entitlementPass = require("../../modules/Discount/entitlementPass");
|
|
9
12
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
13
|
+
_dayjs.default.extend(_isSameOrAfter.default);
|
|
14
|
+
_dayjs.default.extend(_isSameOrBefore.default);
|
|
10
15
|
const uniqueById = (arr, key = 'id') => {
|
|
11
16
|
const seen = new Set();
|
|
12
17
|
return arr.filter(item => !seen.has(item[key]) && seen.add(item[key]));
|
|
@@ -35,8 +40,8 @@ const isAllNormalProduct = items => {
|
|
|
35
40
|
*/
|
|
36
41
|
exports.isAllNormalProduct = isAllNormalProduct;
|
|
37
42
|
const getDiscountAmount = (discount, total, price) => {
|
|
38
|
-
//
|
|
39
|
-
if (
|
|
43
|
+
// 商品券和 Entitlement Pass 都是全额抵扣。
|
|
44
|
+
if ((0, _entitlementPass.isFullCoveragePass)(discount)) {
|
|
40
45
|
return new _decimal.default(price).minus(new _decimal.default(price || 0)).toNumber();
|
|
41
46
|
}
|
|
42
47
|
|
|
@@ -376,41 +381,53 @@ const isInWeeklyRepeatForDate = (targetDate, startDate, repeatRule) => {
|
|
|
376
381
|
};
|
|
377
382
|
|
|
378
383
|
/**
|
|
379
|
-
*
|
|
384
|
+
* 根据商品使用时间过滤优惠券列表(预约开始时间或普通商品的当前时间)
|
|
380
385
|
* @param discountList 优惠券列表
|
|
381
|
-
* @param bookingTime
|
|
386
|
+
* @param bookingTime 使用时间 (格式: YYYY-MM-DD HH:mm:ss),未确定时不提前过滤
|
|
382
387
|
* @returns 过滤后的优惠券列表
|
|
383
388
|
*/
|
|
384
389
|
const filterDiscountListByBookingTime = (discountList, bookingTime) => {
|
|
385
|
-
if (!bookingTime)
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
390
|
+
if (!bookingTime) return discountList;
|
|
391
|
+
return discountList.filter(discount => !getDiscountTimeUnavailableReason(discount, bookingTime));
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
/** 与抵扣计算共用时间判断,区分未开始、有效期不匹配和日程限制。 */
|
|
395
|
+
exports.filterDiscountListByBookingTime = filterDiscountListByBookingTime;
|
|
396
|
+
const getDiscountTimeUnavailableReason = (discount, useTime) => {
|
|
397
|
+
const useTimeDayjs = (0, _dayjs.default)(useTime);
|
|
398
|
+
if (!useTimeDayjs.isValid()) return 'time_limit';
|
|
399
|
+
try {
|
|
389
400
|
if (discount?.metadata?.validity_type === "custom_schedule_validity") {
|
|
390
|
-
return getDateIsInSchedule(
|
|
401
|
+
return getDateIsInSchedule(useTime, discount.custom_schedule_snapshot?.data || []) ? undefined : 'outside_schedule';
|
|
391
402
|
}
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
// 判断预约时间是否在过期时间之前(expire_time 为空时为长期有效)
|
|
401
|
-
const isBeforeExpire = discount.expire_time ? bookingTimeDayjs.isSameOrBefore((0, _dayjs.default)(discount.expire_time)) : true;
|
|
402
|
-
return isAfterStart && isBeforeExpire;
|
|
403
|
-
} catch (error) {
|
|
404
|
-
return true;
|
|
403
|
+
const isHasStart = !!discount.start_date && !!discount.start_time;
|
|
404
|
+
const startDate = (0, _dayjs.default)(`${discount.start_date} ${discount.start_time}`);
|
|
405
|
+
|
|
406
|
+
// 开始时刻包含在有效期内。
|
|
407
|
+
if (isHasStart) {
|
|
408
|
+
if (!startDate.isValid()) return 'time_limit';
|
|
409
|
+
if (useTimeDayjs.isBefore(startDate)) return 'not_started';
|
|
405
410
|
}
|
|
406
|
-
|
|
411
|
+
|
|
412
|
+
// 到期时刻不再可用;expire_time 为空时为长期有效。
|
|
413
|
+
const expireTime = discount.expire_time ? (0, _dayjs.default)(discount.expire_time) : null;
|
|
414
|
+
const isBeforeExpire = expireTime ? expireTime.isValid() && useTimeDayjs.isBefore(expireTime) : true;
|
|
415
|
+
if (!isBeforeExpire) return 'time_limit';
|
|
416
|
+
const validity = discount.extension_data?.find(field => field.field_key === 'valid_times')?.value;
|
|
417
|
+
if (validity?.restrict_use_by_schedule === true && !getDateIsInSchedule(useTime, discount.custom_schedule_snapshot?.data || [])) {
|
|
418
|
+
return 'outside_schedule';
|
|
419
|
+
}
|
|
420
|
+
return undefined;
|
|
421
|
+
} catch (error) {
|
|
422
|
+
return 'time_limit';
|
|
423
|
+
}
|
|
407
424
|
};
|
|
408
425
|
/**
|
|
409
426
|
* 判断是否是订单级别的固定金额折扣卡
|
|
410
427
|
* @param discount 折扣
|
|
411
428
|
* @returns 是否是订单级别的固定金额折扣卡
|
|
412
429
|
*/
|
|
413
|
-
exports.
|
|
430
|
+
exports.getDiscountTimeUnavailableReason = getDiscountTimeUnavailableReason;
|
|
414
431
|
const isOrderLevelFixedAmountDiscount = discount => {
|
|
415
432
|
return discount.tag === 'product_discount_card' && discount?.metadata?.discount_card_type === 'fixed_amount' && discount?.metadata?.discount_calculation_mode === 'order_level';
|
|
416
433
|
};
|
|
@@ -127,8 +127,8 @@ export declare class UnifiedBookingSalesImpl extends BookingTicket {
|
|
|
127
127
|
*/
|
|
128
128
|
clearSessionStorageOrderSnapshot(): void;
|
|
129
129
|
/**
|
|
130
|
-
* H5/Kiosk
|
|
131
|
-
*
|
|
130
|
+
* PC/H5/Kiosk 结账默认直接等待云端 checkout;POS/WebPOS 仍保持本地待同步。
|
|
131
|
+
* 显式传 checkoutSyncTaskEnabled: true 时仍可回退到本地同步模式。
|
|
132
132
|
*/
|
|
133
133
|
protected shouldUseCheckoutSyncTask(): boolean;
|
|
134
134
|
/** UnifiedBookingSales 的真实结账默认请求收据数据,供云端确认后再本地打印。 */
|