@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
|
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.transformBaseProductToOrderProduct = transformBaseProductToOrderProduct;
|
|
7
|
+
var _weighedProduct = require("../../../modules/Order/utils/weighedProduct");
|
|
7
8
|
var _utils = require("../../../modules/Order/utils");
|
|
8
9
|
var _manualProductDiscount = require("../../../modules/Order/utils/manualProductDiscount");
|
|
9
10
|
function toNumber(value, fallback = 0) {
|
|
@@ -65,11 +66,11 @@ function appendDataVariantIds(base, ...values) {
|
|
|
65
66
|
}
|
|
66
67
|
return Array.isArray(base) || result.length ? result : undefined;
|
|
67
68
|
}
|
|
68
|
-
function normalizeOptionItems(optionItems) {
|
|
69
|
+
function normalizeOptionItems(optionItems, useOriginalPrice = false) {
|
|
69
70
|
if (!Array.isArray(optionItems)) return [];
|
|
70
71
|
return optionItems.map(optionItem => {
|
|
71
72
|
const optionGroupItemId = toNumber(optionItem?.option_group_item_id ?? optionItem?.id);
|
|
72
|
-
const price = optionItem?.price ?? optionItem?.add_price ?? 0;
|
|
73
|
+
const price = (useOriginalPrice ? optionItem?.original_price : undefined) ?? optionItem?.price ?? optionItem?.add_price ?? 0;
|
|
73
74
|
return {
|
|
74
75
|
...optionItem,
|
|
75
76
|
option_group_item_id: optionGroupItemId,
|
|
@@ -80,10 +81,12 @@ function normalizeOptionItems(optionItems) {
|
|
|
80
81
|
};
|
|
81
82
|
});
|
|
82
83
|
}
|
|
83
|
-
function normalizeBundleItems(bundleItems, preferPriceField = false) {
|
|
84
|
+
function normalizeBundleItems(bundleItems, preferPriceField = false, preferBundleSellingPrice = false) {
|
|
84
85
|
if (!Array.isArray(bundleItems)) return [];
|
|
85
86
|
return bundleItems.map(bundleItem => {
|
|
86
|
-
|
|
87
|
+
// Weighed manual prices distribute the bundle base and nested options separately.
|
|
88
|
+
// Its explicit selling amount already includes those options.
|
|
89
|
+
const unitSelling = preferPriceField && !preferBundleSellingPrice ? bundleItem.price ?? bundleItem.bundle_selling_price ?? bundleItem.custom_price ?? bundleItem.product_price ?? '0' : bundleItem.bundle_selling_price ?? bundleItem.price ?? bundleItem.custom_price ?? bundleItem.product_price ?? '0';
|
|
87
90
|
const unitStr = toPriceString(unitSelling, '0.00');
|
|
88
91
|
const num = toSafePositiveInt(bundleItem?.num ?? bundleItem?.quantity, 1);
|
|
89
92
|
const magnitudeSource = {
|
|
@@ -147,19 +150,25 @@ function transformBaseProductToOrderProduct(params) {
|
|
|
147
150
|
const productionCode = normalizeProductionCode(Object.prototype.hasOwnProperty.call(payload, 'production_code') ? payload.production_code : productVariantId > 0 ? matchedVariant?.production_code : origin?.production_code ?? sourceProduct?.production_code);
|
|
148
151
|
const hasPriceOverride = payload.price_override !== undefined && payload.price_override !== null && payload.price_override !== '';
|
|
149
152
|
// 手动改价后 payload.price 是分摊后的主商品售价,不能再作为券前 source。
|
|
150
|
-
//
|
|
153
|
+
// 称重商品使用改价前净重金额;其它商品从商品/variant 快照读取目录价。
|
|
154
|
+
// 售价仍由 line_total - bundle 推导。
|
|
151
155
|
const catalogSourcePrice = matchedVariant?.price ?? matchedVariant?.base_price ?? origin?.price ?? origin?.base_price ?? sourceProduct?.price ?? sourceProduct?.selling_price ?? sourceProduct?.base_price;
|
|
152
|
-
const
|
|
156
|
+
const weighedSourcePrice = (0, _weighedProduct.resolveWeighedSourcePrice)(payload.product_sku ?? origin.product_sku, payload._extend?.weighing ?? origin._extend?.weighing);
|
|
157
|
+
const sourceProductPrice = toPriceString(weighedSourcePrice ?? (hasPriceOverride ? catalogSourcePrice ?? payload.price ?? payload.selling_price : payload.price ?? payload.selling_price ?? catalogSourcePrice), '0.00');
|
|
153
158
|
const explicitLineTotal = payload.line_total ?? payload.total ?? payload._extend?.total;
|
|
154
159
|
const hasExplicitLineTotal = Number.isFinite(Number(explicitLineTotal));
|
|
155
160
|
const lineCompositeTotal = toPriceString(explicitLineTotal, sourceProductPrice);
|
|
156
|
-
const productOptionItem = (0, _utils.normalizeProductSkuOptions)(normalizeOptionItems(payload.product_sku?.option ?? payload.option ?? payload.options ?? payload.product_option_item));
|
|
157
|
-
const productBundle = normalizeBundleItems(payload.bundle ?? payload.product_bundle, hasPriceOverride);
|
|
161
|
+
const productOptionItem = (0, _utils.normalizeProductSkuOptions)(normalizeOptionItems(payload.product_sku?.option ?? payload.option ?? payload.options ?? payload.product_option_item, hasPriceOverride || weighedSourcePrice !== undefined));
|
|
162
|
+
const productBundle = normalizeBundleItems(payload.bundle ?? payload.product_bundle, hasPriceOverride, hasPriceOverride && weighedSourcePrice !== undefined);
|
|
158
163
|
// `payload.unique` 是业务侧商品/票务标识,可能在连续加同一商品时重复;
|
|
159
164
|
// 行级唯一值只接受显式协议字段,缺省交给 OrderModule 生成。
|
|
160
165
|
const uniqueIdentificationNumber = payload.unique_identification_number;
|
|
161
166
|
const resolvedQuantity = toSafePositiveInt(payload.quantity ?? payload.num, 1);
|
|
162
|
-
const originTotal = (0,
|
|
167
|
+
const originTotal = weighedSourcePrice !== undefined ? Number((0, _utils.composeLinePrice)({
|
|
168
|
+
mainPrice: Number(weighedSourcePrice) + (0, _utils.sumOptionUnitPrice)(productOptionItem).toNumber(),
|
|
169
|
+
bundle: hasPriceOverride ? (0, _weighedProduct.withOriginalBundleOptionPrices)(productBundle) : productBundle,
|
|
170
|
+
useOriginalBundle: true
|
|
171
|
+
})) : (0, _manualProductDiscount.resolveManualDiscountOriginTotal)({
|
|
163
172
|
originTotal: payload.origin_total,
|
|
164
173
|
extendOriginTotal: payload._extend?.origin_total,
|
|
165
174
|
originExtendOriginTotal: origin?._extend?.origin_total,
|
|
@@ -260,6 +269,12 @@ function transformBaseProductToOrderProduct(params) {
|
|
|
260
269
|
product_cover: firstNonBlank(matchedVariant?.cover, sourceProduct?.cover),
|
|
261
270
|
_origin: {
|
|
262
271
|
...(sourceProduct || {}),
|
|
272
|
+
...(weighedSourcePrice !== undefined && hasPriceOverride ? {
|
|
273
|
+
_extend: {
|
|
274
|
+
...sourceProduct?._extend,
|
|
275
|
+
origin_total: originTotal
|
|
276
|
+
}
|
|
277
|
+
} : {}),
|
|
263
278
|
callbackData: payload
|
|
264
279
|
}
|
|
265
280
|
};
|
|
@@ -8,6 +8,7 @@ exports.createEmptySummary = createEmptySummary;
|
|
|
8
8
|
exports.getSafeProductNum = getSafeProductNum;
|
|
9
9
|
exports.isSkuOnlyDeleteIdentity = isSkuOnlyDeleteIdentity;
|
|
10
10
|
exports.normalizeOrderProduct = normalizeOrderProduct;
|
|
11
|
+
var _weighedProduct = require("../../modules/Order/utils/weighedProduct");
|
|
11
12
|
var _decimal = _interopRequireDefault(require("decimal.js"));
|
|
12
13
|
var _utils = require("../../modules/Order/utils");
|
|
13
14
|
var _manualProductDiscount = require("../../modules/Order/utils/manualProductDiscount");
|
|
@@ -237,7 +238,7 @@ function normalizeOrderProduct(product) {
|
|
|
237
238
|
});
|
|
238
239
|
const composedOriginalPrice = (0, _utils.composeLinePrice)({
|
|
239
240
|
mainPrice: mainOriginalStr,
|
|
240
|
-
bundle: normalizedBundle,
|
|
241
|
+
bundle: Number(productSku.open_sold_weight) === 1 && (0, _manualProductDiscount.hasManualProductDiscountFlag)(metadata) ? (0, _weighedProduct.withOriginalBundleOptionPrices)(normalizedBundle) : normalizedBundle,
|
|
241
242
|
useOriginalBundle: true
|
|
242
243
|
});
|
|
243
244
|
const finalOriginalPrice = (0, _utils.resolveManualOverrideLineOriginalPrice)({
|
|
@@ -8,6 +8,7 @@ import { BaseSalesImpl } from '../BaseSales';
|
|
|
8
8
|
import type { BaseSalesScanCodeResult } from '../BaseSales/types';
|
|
9
9
|
import type { ISalesDetail } from '../BaseSales/utils/parseSalesResponse';
|
|
10
10
|
import type { LoadSalesDetailParams } from '../../modules/Order/types';
|
|
11
|
+
import type { GlobalScanSearchType } from './utils/scan/formatGlobalScan';
|
|
11
12
|
import type { AddAddTimeProductToBookingInput, AddAddTimeProductsToBookingInput, GlobalScanHandleResult, GlobalScanHostBridge } from './types';
|
|
12
13
|
import { AddProductDecideContext, AddProductRequiresDetailPayload } from './utils/addProductDecision';
|
|
13
14
|
import { type BuildCacheItemFromOrderLineInput } from '../../modules/BookingContext/utils/buildCacheItemFromOrderLine';
|
|
@@ -29,7 +30,9 @@ export declare class BookingTicketImpl extends BaseSalesImpl implements Module {
|
|
|
29
30
|
private addTimeProductsCatalog;
|
|
30
31
|
private orderCustomerDiscountRefreshInFlight;
|
|
31
32
|
private orderCustomerDiscountRefreshCustomerId;
|
|
33
|
+
private orderCustomerDiscountRefreshTarget;
|
|
32
34
|
private orderCustomerPromotionRefreshInFlight;
|
|
35
|
+
private readonly customerResolveRequests;
|
|
33
36
|
protected loadOpenDataInFlight: Promise<OpenDataConfig> | null;
|
|
34
37
|
protected loadOpenDataInFlightTarget: string | null;
|
|
35
38
|
protected openDataTarget: string | null;
|
|
@@ -45,6 +48,12 @@ export declare class BookingTicketImpl extends BaseSalesImpl implements Module {
|
|
|
45
48
|
*/
|
|
46
49
|
protected createSubModule(moduleName: string): Module | null;
|
|
47
50
|
initialize(core: PisellCore, options?: ModuleOptions): Promise<void>;
|
|
51
|
+
private getInjectedOpenDataConfig;
|
|
52
|
+
/**
|
|
53
|
+
* 在解决方案对外 ready 前准备 Invoice Document;失败只降级模板,不阻断销售。
|
|
54
|
+
* 已由页面取得的 OpenData 直接交给模块接管,没有注入数据时再走 target-scoped 加载。
|
|
55
|
+
*/
|
|
56
|
+
private prepareInvoiceTemplateAtSolutionReady;
|
|
48
57
|
protected getBookingTicketBusinessCode(): string | null;
|
|
49
58
|
private getIdGeneratorPlugin;
|
|
50
59
|
private loadOpenDataConfig;
|
|
@@ -72,10 +81,8 @@ export declare class BookingTicketImpl extends BaseSalesImpl implements Module {
|
|
|
72
81
|
resolveCustomerById(customerId: string | number): Promise<ICustomer | null>;
|
|
73
82
|
/** 兼容既有优惠码扫码逻辑。 */
|
|
74
83
|
private resolveCustomerByDiscountCustomerId;
|
|
75
|
-
private findCustomerById;
|
|
76
84
|
private normalizeCustomer;
|
|
77
85
|
private getRawCustomerId;
|
|
78
|
-
private hasUsableCustomerDetails;
|
|
79
86
|
/**
|
|
80
87
|
* 更新当前预约的 appointment_status。
|
|
81
88
|
*
|
|
@@ -531,6 +538,8 @@ export declare class BookingTicketImpl extends BaseSalesImpl implements Module {
|
|
|
531
538
|
* ```
|
|
532
539
|
*/
|
|
533
540
|
handleGlobalScanCode(code: string, bridge?: GlobalScanHostBridge): Promise<GlobalScanHandleResult>;
|
|
541
|
+
/** 查询码类型;复用原有识别和缓存,不写订单、不打开选择 UI。 */
|
|
542
|
+
getGlobalScanCodeType(code: string): Promise<GlobalScanSearchType | null>;
|
|
534
543
|
resolveBestAddTimePlan(addTimeProducts: any[], targetMinutes: number): any;
|
|
535
544
|
/**
|
|
536
545
|
* 构造一条绑定到已有 booking 的加时商品行。
|
|
@@ -160,7 +160,9 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
|
|
|
160
160
|
addTimeProductsCatalog = [];
|
|
161
161
|
orderCustomerDiscountRefreshInFlight = null;
|
|
162
162
|
orderCustomerDiscountRefreshCustomerId = null;
|
|
163
|
+
orderCustomerDiscountRefreshTarget = null;
|
|
163
164
|
orderCustomerPromotionRefreshInFlight = null;
|
|
165
|
+
customerResolveRequests = new Map();
|
|
164
166
|
loadOpenDataInFlight = null;
|
|
165
167
|
loadOpenDataInFlightTarget = null;
|
|
166
168
|
openDataTarget = null;
|
|
@@ -193,10 +195,37 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
|
|
|
193
195
|
throw new Error('bookingTicket解决方案需要 request 插件支持');
|
|
194
196
|
}
|
|
195
197
|
this.platform = this.otherParams?.platform;
|
|
198
|
+
await this.prepareInvoiceTemplateAtSolutionReady(this.getInjectedOpenDataConfig());
|
|
196
199
|
await this.configureIdGeneratorFromOpenData();
|
|
197
200
|
}
|
|
201
|
+
getInjectedOpenDataConfig() {
|
|
202
|
+
const openData = this.otherParams?.openData;
|
|
203
|
+
return openData && typeof openData === 'object' && !Array.isArray(openData) ? openData : undefined;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* 在解决方案对外 ready 前准备 Invoice Document;失败只降级模板,不阻断销售。
|
|
208
|
+
* 已由页面取得的 OpenData 直接交给模块接管,没有注入数据时再走 target-scoped 加载。
|
|
209
|
+
*/
|
|
210
|
+
async prepareInvoiceTemplateAtSolutionReady(injectedOpenData) {
|
|
211
|
+
const businessCode = this.getBookingTicketBusinessCode();
|
|
212
|
+
if (!businessCode || !this.store.openData) return;
|
|
213
|
+
const channel = this.getSubmitOrderSalesChannel();
|
|
214
|
+
const target = `${businessCode}+${channel}`;
|
|
215
|
+
try {
|
|
216
|
+
if (injectedOpenData && typeof this.store.openData.ensureInvoiceTemplateSettings === 'function') {
|
|
217
|
+
this.openDataTarget = target;
|
|
218
|
+
await this.store.openData.ensureInvoiceTemplateSettings(target, injectedOpenData);
|
|
219
|
+
this.otherParams.openData = injectedOpenData;
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
await this.loadOpenDataConfig();
|
|
223
|
+
} catch (error) {
|
|
224
|
+
console.warn('[BookingTicket] Invoice 模板预加载失败,使用默认配置', error);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
198
227
|
getBookingTicketBusinessCode() {
|
|
199
|
-
const candidates = [this.otherParams?.businessCode, this.otherParams?.business_code, this.getOrderHeader()?.business_code, this.store.order?.getTempOrder()?.business_code, this.getSalesOrder()?.raw?.business_code];
|
|
228
|
+
const candidates = [this.otherParams?.businessCode, this.otherParams?.business_code, this.getOrderHeader()?.business_code, typeof this.store.order?.getTempOrder === 'function' ? this.store.order.getTempOrder()?.business_code : undefined, this.getSalesOrder()?.raw?.business_code];
|
|
200
229
|
for (const candidate of candidates) {
|
|
201
230
|
if (candidate === undefined || candidate === null) continue;
|
|
202
231
|
const normalized = String(candidate).trim();
|
|
@@ -415,7 +444,15 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
|
|
|
415
444
|
|
|
416
445
|
// 缺 Holder 等场景 isAvailable=false,但仍需把 Pass 归属客户带入订单,供 Holder 弹窗使用。
|
|
417
446
|
if (!hasOrderCustomer && extractedCustomerId && !isUserPlatform) {
|
|
418
|
-
|
|
447
|
+
try {
|
|
448
|
+
await this.hydrateOrderCustomerFromScanDiscounts(scannedList);
|
|
449
|
+
} catch (error) {
|
|
450
|
+
// 客户映射失败不能阻断原有 Pass 的可用性、展示或优惠处理。
|
|
451
|
+
console.warn('[BookingTicket] scan discount customer resolve failed', {
|
|
452
|
+
customerId: extractedCustomerId,
|
|
453
|
+
error
|
|
454
|
+
});
|
|
455
|
+
}
|
|
419
456
|
}
|
|
420
457
|
if (raw.isAvailable) {
|
|
421
458
|
await this.store.order.recalculateSummary({
|
|
@@ -466,43 +503,39 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
|
|
|
466
503
|
return null;
|
|
467
504
|
}
|
|
468
505
|
async resolveCustomerById(customerId) {
|
|
469
|
-
const
|
|
470
|
-
const
|
|
471
|
-
|
|
472
|
-
if (normalizedLocalCustomer && this.hasUsableCustomerDetails(normalizedLocalCustomer)) {
|
|
473
|
-
return normalizedLocalCustomer;
|
|
474
|
-
}
|
|
475
|
-
const result = await this.store.customer.queryCustomerList({
|
|
476
|
-
ids: [customerId],
|
|
477
|
-
skip: 1,
|
|
478
|
-
num: 1
|
|
479
|
-
});
|
|
480
|
-
const remoteCustomer = this.findCustomerById(result?.list || [], customerId) || result?.list?.[0] || null;
|
|
481
|
-
if (remoteCustomer) {
|
|
482
|
-
return this.normalizeCustomer(remoteCustomer);
|
|
483
|
-
}
|
|
484
|
-
try {
|
|
485
|
-
const detailCustomer = await this.store.customer.queryCustomerDetail(customerId);
|
|
486
|
-
return detailCustomer ? this.normalizeCustomer(detailCustomer) : null;
|
|
487
|
-
} catch (error) {
|
|
488
|
-
console.warn('[BookingTicket] resolveCustomerById detail request failed', {
|
|
489
|
-
customerId,
|
|
490
|
-
error
|
|
491
|
-
});
|
|
506
|
+
const rawCustomerId = String(customerId ?? '').trim();
|
|
507
|
+
const numericCustomerId = Number(rawCustomerId);
|
|
508
|
+
if (!rawCustomerId || !Number.isInteger(numericCustomerId) || numericCustomerId <= 1) {
|
|
492
509
|
return null;
|
|
493
510
|
}
|
|
511
|
+
const normalizedCustomerId = String(numericCustomerId);
|
|
512
|
+
const existingRequest = this.customerResolveRequests.get(normalizedCustomerId);
|
|
513
|
+
if (existingRequest) return existingRequest;
|
|
514
|
+
const request = this.request.post(`/customer/resolve/${encodeURIComponent(normalizedCustomerId)}`, {
|
|
515
|
+
with_franchisor_data: 1,
|
|
516
|
+
with: ['latestWalletDetail.wallet', 'contactsInfo', 'formRecord']
|
|
517
|
+
}).then(result => {
|
|
518
|
+
const customer = result?.data;
|
|
519
|
+
const resolvedCustomerId = Number(customer && typeof customer === 'object' && !Array.isArray(customer) ? this.getRawCustomerId(customer) : NaN);
|
|
520
|
+
if (result?.status !== true || result?.code !== 200 || !customer || typeof customer !== 'object' || Array.isArray(customer) || !Number.isInteger(resolvedCustomerId) || resolvedCustomerId <= 1) {
|
|
521
|
+
const error = new Error(result?.message || 'Customer resolve failed');
|
|
522
|
+
error.code = result?.code;
|
|
523
|
+
throw error;
|
|
524
|
+
}
|
|
525
|
+
return this.normalizeCustomer(customer);
|
|
526
|
+
}).finally(() => {
|
|
527
|
+
if (this.customerResolveRequests.get(normalizedCustomerId) === request) {
|
|
528
|
+
this.customerResolveRequests.delete(normalizedCustomerId);
|
|
529
|
+
}
|
|
530
|
+
});
|
|
531
|
+
this.customerResolveRequests.set(normalizedCustomerId, request);
|
|
532
|
+
return request;
|
|
494
533
|
}
|
|
495
534
|
|
|
496
535
|
/** 兼容既有优惠码扫码逻辑。 */
|
|
497
536
|
resolveCustomerByDiscountCustomerId(customerId) {
|
|
498
537
|
return this.resolveCustomerById(customerId);
|
|
499
538
|
}
|
|
500
|
-
findCustomerById(customers = [], customerId) {
|
|
501
|
-
return customers.find(customer => {
|
|
502
|
-
const rawId = this.getRawCustomerId(customer);
|
|
503
|
-
return rawId !== null && String(rawId) === String(customerId);
|
|
504
|
-
}) || null;
|
|
505
|
-
}
|
|
506
539
|
normalizeCustomer(customer) {
|
|
507
540
|
const raw = customer;
|
|
508
541
|
const rawId = this.getRawCustomerId(customer);
|
|
@@ -520,9 +553,6 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
|
|
|
520
553
|
const raw = customer;
|
|
521
554
|
return customer.id ?? raw.customer_id ?? raw.customerId ?? null;
|
|
522
555
|
}
|
|
523
|
-
hasUsableCustomerDetails(customer) {
|
|
524
|
-
return !!(customer.name || customer.phone || customer.email || customer.display_name || customer.customer_name);
|
|
525
|
-
}
|
|
526
556
|
|
|
527
557
|
/**
|
|
528
558
|
* 更新当前预约的 appointment_status。
|
|
@@ -1094,7 +1124,13 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
|
|
|
1094
1124
|
}
|
|
1095
1125
|
refreshDiscountConfigAfterOrderCustomerChange(customerId) {
|
|
1096
1126
|
if (!customerId || customerId === 1) return Promise.resolve();
|
|
1097
|
-
|
|
1127
|
+
const tempOrder = this.store.order.getTempOrder?.();
|
|
1128
|
+
const orderIdentity = this.getOrderIdentity?.();
|
|
1129
|
+
const orderId = tempOrder?.order_id || orderIdentity?.orderId;
|
|
1130
|
+
const action = !orderId || String(orderId).startsWith('local_order') ? 'create' : 'edit';
|
|
1131
|
+
const orderTarget = orderId ? `order:${String(orderId)}` : `external:${String(tempOrder?.external_sale_number || orderIdentity?.externalSaleNumber || orderIdentity?.localId || 'new')}`;
|
|
1132
|
+
const refreshTarget = `${customerId}:${action}:${orderTarget}`;
|
|
1133
|
+
if (this.orderCustomerDiscountRefreshInFlight && this.orderCustomerDiscountRefreshTarget === refreshTarget) {
|
|
1098
1134
|
return this.orderCustomerDiscountRefreshInFlight;
|
|
1099
1135
|
}
|
|
1100
1136
|
const previousRefresh = this.orderCustomerDiscountRefreshInFlight;
|
|
@@ -1106,22 +1142,33 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
|
|
|
1106
1142
|
if (!currentCustomer || Number(currentCustomer.customer_id) !== customerId) {
|
|
1107
1143
|
return;
|
|
1108
1144
|
}
|
|
1109
|
-
const
|
|
1110
|
-
const
|
|
1111
|
-
const
|
|
1145
|
+
const currentTempOrder = this.store.order.getTempOrder?.();
|
|
1146
|
+
const currentOrderIdentity = this.getOrderIdentity?.();
|
|
1147
|
+
const currentOrderId = currentTempOrder?.order_id || currentOrderIdentity?.orderId;
|
|
1148
|
+
const currentAction = !currentOrderId || String(currentOrderId).startsWith('local_order') ? 'create' : 'edit';
|
|
1149
|
+
const currentOrderTarget = currentOrderId ? `order:${String(currentOrderId)}` : `external:${String(currentTempOrder?.external_sale_number || currentOrderIdentity?.externalSaleNumber || currentOrderIdentity?.localId || 'new')}`;
|
|
1150
|
+
if (`${customerId}:${currentAction}:${currentOrderTarget}` !== refreshTarget) {
|
|
1151
|
+
return;
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
// 上一个订单的迟到请求可能在客户切换后重新写入 BaseSales cache key;
|
|
1155
|
+
// 客户变更触发的目标订单刷新必须绕过该缓存。
|
|
1112
1156
|
await this.loadDiscountConfig({
|
|
1113
1157
|
customerId,
|
|
1114
|
-
action
|
|
1158
|
+
action,
|
|
1159
|
+
force: true
|
|
1115
1160
|
});
|
|
1116
1161
|
})();
|
|
1117
1162
|
this.orderCustomerDiscountRefreshInFlight = refreshTask;
|
|
1118
1163
|
this.orderCustomerDiscountRefreshCustomerId = customerId;
|
|
1164
|
+
this.orderCustomerDiscountRefreshTarget = refreshTarget;
|
|
1119
1165
|
refreshTask.catch(error => {
|
|
1120
1166
|
console.error('Failed to load discount config after customer change:', error);
|
|
1121
1167
|
}).finally(() => {
|
|
1122
1168
|
if (this.orderCustomerDiscountRefreshInFlight !== refreshTask) return;
|
|
1123
1169
|
this.orderCustomerDiscountRefreshInFlight = null;
|
|
1124
1170
|
this.orderCustomerDiscountRefreshCustomerId = null;
|
|
1171
|
+
this.orderCustomerDiscountRefreshTarget = null;
|
|
1125
1172
|
});
|
|
1126
1173
|
return refreshTask;
|
|
1127
1174
|
}
|
|
@@ -1642,6 +1689,7 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
|
|
|
1642
1689
|
const result = await (0, _applyGlobalScan.applyGlobalScan)({
|
|
1643
1690
|
setOrderCustomer: customer => this.setOrderCustomer(customer),
|
|
1644
1691
|
getOrderCustomerSnapshot: () => this.getOrderCustomerSnapshot(),
|
|
1692
|
+
resolveCustomerById: customerId => this.resolveCustomerById(customerId),
|
|
1645
1693
|
getBusinessCode: () => this.otherParams?.businessCode,
|
|
1646
1694
|
buildAddProductCtx: extra => this.buildAddProductCtx(extra),
|
|
1647
1695
|
decideAddProduct: (item, options) => this.decideAddProduct(item, options),
|
|
@@ -1662,6 +1710,18 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
|
|
|
1662
1710
|
};
|
|
1663
1711
|
}
|
|
1664
1712
|
}
|
|
1713
|
+
|
|
1714
|
+
/** 查询码类型;复用原有识别和缓存,不写订单、不打开选择 UI。 */
|
|
1715
|
+
async getGlobalScanCodeType(code) {
|
|
1716
|
+
const trimmed = typeof code === 'string' ? code.trim() : '';
|
|
1717
|
+
if (!trimmed) return null;
|
|
1718
|
+
const scan = (0, _handleScan.handleGlobalScan)(this.request, value => this.store.products.findProductsByCodeOrBarcode(value), this.getSubmitOrderSalesChannel());
|
|
1719
|
+
const raw = await scan({
|
|
1720
|
+
type: 'nativeScanner',
|
|
1721
|
+
value: trimmed
|
|
1722
|
+
});
|
|
1723
|
+
return (0, _formatGlobalScan.formatGlobalScanResult)(raw, trimmed)?.searchType ?? null;
|
|
1724
|
+
}
|
|
1665
1725
|
resolveBestAddTimePlan(addTimeProducts, targetMinutes) {
|
|
1666
1726
|
return (0, _resolveBestAddTimePlan.resolveBestAddTimePlan)(addTimeProducts, targetMinutes);
|
|
1667
1727
|
}
|
|
@@ -1784,10 +1844,12 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
|
|
|
1784
1844
|
await super.setOtherParams(params, {
|
|
1785
1845
|
cover
|
|
1786
1846
|
});
|
|
1847
|
+
const invoiceContextChanged = params?.businessCode !== undefined || params?.business_code !== undefined || params?.channel !== undefined || params?.platform !== undefined || Object.prototype.hasOwnProperty.call(params, 'openData');
|
|
1848
|
+
if (invoiceContextChanged) {
|
|
1849
|
+
await this.prepareInvoiceTemplateAtSolutionReady(Object.prototype.hasOwnProperty.call(params, 'openData') ? this.getInjectedOpenDataConfig() : undefined);
|
|
1850
|
+
}
|
|
1787
1851
|
if (params?.businessCode !== undefined || params?.business_code !== undefined) {
|
|
1788
1852
|
await this.configureIdGeneratorFromOpenData();
|
|
1789
|
-
} else if (params?.channel !== undefined) {
|
|
1790
|
-
await this.loadOpenDataConfig();
|
|
1791
1853
|
}
|
|
1792
1854
|
}
|
|
1793
1855
|
getDeviceTaskPlugin() {
|
|
@@ -230,12 +230,16 @@ export interface GlobalScanHostBridge {
|
|
|
230
230
|
/**
|
|
231
231
|
* walk-in 扫 walletPass:直接核销优惠码(对齐 SaleDetail Voucher.handleApplyCode / cart.scanCode)。
|
|
232
232
|
*/
|
|
233
|
-
applyPromotionCode?: (code: string, customerId?: number
|
|
233
|
+
applyPromotionCode?: (code: string, customerId?: number, options?: {
|
|
234
|
+
suppressFailureNotification?: boolean;
|
|
235
|
+
}) => Promise<{
|
|
234
236
|
isAvailable?: boolean;
|
|
235
237
|
isAccepted?: boolean;
|
|
236
238
|
type?: string;
|
|
237
239
|
unavailableReason?: string;
|
|
238
240
|
}>;
|
|
241
|
+
/** 当前订单已有其他客户时,由宿主复用客户组件完成确认及切换。 */
|
|
242
|
+
requestCustomerSwitch?: (customer: ICustomer) => Promise<boolean | null>;
|
|
239
243
|
onNotify?: (type: 'success' | 'fail' | 'info', messageKey?: string) => void;
|
|
240
244
|
refresh?: () => void;
|
|
241
245
|
}
|
|
@@ -5,6 +5,7 @@ import { GlobalScanFormattedResult } from './formatGlobalScan';
|
|
|
5
5
|
export interface GlobalScanApplyHost {
|
|
6
6
|
setOrderCustomer(customer: ICustomer | null): void;
|
|
7
7
|
getOrderCustomerSnapshot(): ICustomer | null;
|
|
8
|
+
resolveCustomerById?(customerId: string | number): Promise<ICustomer | null>;
|
|
8
9
|
getBusinessCode(): string | undefined;
|
|
9
10
|
buildAddProductCtx(extra?: Record<string, any>): Record<string, any>;
|
|
10
11
|
decideAddProduct(item: any, options?: Record<string, any>): AddProductDecision;
|
|
@@ -26,6 +27,7 @@ export interface GlobalScanApplyHost {
|
|
|
26
27
|
addProductToOrder(product: any, booking?: any): Promise<any>;
|
|
27
28
|
refreshWalletAssets?(params?: {
|
|
28
29
|
preserveSelection?: boolean;
|
|
30
|
+
retainSearchResultCodes?: string[];
|
|
29
31
|
}): Promise<any>;
|
|
30
32
|
scanWalletAsset?(code: string): Promise<{
|
|
31
33
|
type: 'walletCode' | 'normalCode';
|