@pisell/pisellos 0.10.32 → 0.10.33
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 +0 -9
- package/dist/modules/OpenData/index.d.ts +1 -1
- package/dist/modules/OpenData/index.js +19 -11
- package/dist/modules/Order/index.d.ts +1 -1
- package/dist/server/utils/small-ticket.d.ts +5 -1
- package/dist/server/utils/small-ticket.js +145 -80
- package/dist/solution/BookingTicket/index.d.ts +6 -0
- package/dist/solution/BookingTicket/index.js +548 -485
- package/lib/modules/OpenData/index.d.ts +1 -1
- package/lib/modules/OpenData/index.js +9 -1
- package/lib/modules/Order/index.d.ts +1 -1
- package/lib/server/utils/small-ticket.d.ts +5 -1
- package/lib/server/utils/small-ticket.js +77 -17
- package/lib/solution/BookingTicket/index.d.ts +6 -0
- package/lib/solution/BookingTicket/index.js +32 -3
- package/package.json +1 -1
|
@@ -33,7 +33,7 @@ export declare class OpenDataModule extends BaseModule implements Module {
|
|
|
33
33
|
getOpenData(): OpenDataConfig | null;
|
|
34
34
|
getInvoiceLayoutConfig(): InvoiceLayoutConfig;
|
|
35
35
|
getInvoiceTemplateSettings(): InvoiceTemplateSettings;
|
|
36
|
-
ensureInvoiceTemplateSettings(target: string): Promise<InvoiceTemplateSettings>;
|
|
36
|
+
ensureInvoiceTemplateSettings(target: string, openDataConfig?: OpenDataConfig): Promise<InvoiceTemplateSettings>;
|
|
37
37
|
private commitInvoiceTemplateSettings;
|
|
38
38
|
private resolveInvoiceTemplateSettings;
|
|
39
39
|
private buildInvoiceTemplateCacheKey;
|
|
@@ -211,8 +211,16 @@ class OpenDataModule extends _BaseModule.BaseModule {
|
|
|
211
211
|
}
|
|
212
212
|
return (0, _utils.getDefaultInvoiceTemplateSettings)();
|
|
213
213
|
}
|
|
214
|
-
async ensureInvoiceTemplateSettings(target) {
|
|
214
|
+
async ensureInvoiceTemplateSettings(target, openDataConfig) {
|
|
215
215
|
const normalizedTarget = String(target || '').trim();
|
|
216
|
+
if (openDataConfig && openDataConfig !== this.store.data) {
|
|
217
|
+
// 部分解决方案会在初始化参数中直接收到已经拉取完成的 OpenData。
|
|
218
|
+
// 将它纳入模块状态后再解析 Document,避免为了 Invoice 重复请求 setting,
|
|
219
|
+
// 同时让后续同步 getter 能校验同一份 document id 与 target。
|
|
220
|
+
this.fetchRevision += 1;
|
|
221
|
+
this.store.data = openDataConfig;
|
|
222
|
+
this.store.lastFetchedAt = Date.now();
|
|
223
|
+
}
|
|
216
224
|
const config = this.store.data;
|
|
217
225
|
if (!normalizedTarget || !config) {
|
|
218
226
|
return (0, _utils.getDefaultInvoiceTemplateSettings)();
|
|
@@ -535,7 +535,7 @@ export declare class OrderModule extends BaseModule implements Module, OrderModu
|
|
|
535
535
|
generateIdempotencyToken(): string;
|
|
536
536
|
syncPaymentsToOrder<T = any>(params: SyncPaymentsToOrderParams): Promise<SyncPaymentsToOrderResult<T>>;
|
|
537
537
|
createOrder(params: CommitOrderParams['query']): {
|
|
538
|
-
type: "
|
|
538
|
+
type: "virtual" | "appointment_booking";
|
|
539
539
|
platform: string;
|
|
540
540
|
sales_channel: string;
|
|
541
541
|
order_sales_channel: string;
|
|
@@ -33,9 +33,12 @@ export interface SmallTicketProduct {
|
|
|
33
33
|
product_title: string;
|
|
34
34
|
product_quantity: number;
|
|
35
35
|
selling_price: string;
|
|
36
|
+
total_selling_price: string;
|
|
36
37
|
original_price: string;
|
|
37
|
-
base_price
|
|
38
|
+
base_price: string;
|
|
38
39
|
total_original_price: string;
|
|
40
|
+
options_total_price?: string;
|
|
41
|
+
package_items_total_price?: string;
|
|
39
42
|
extension_list: any[];
|
|
40
43
|
product_uid?: string | number;
|
|
41
44
|
booking_uid?: string | number;
|
|
@@ -44,6 +47,7 @@ export interface SmallTicketProduct {
|
|
|
44
47
|
name: string;
|
|
45
48
|
num?: number;
|
|
46
49
|
price?: string;
|
|
50
|
+
total_price: string;
|
|
47
51
|
value?: string;
|
|
48
52
|
}>;
|
|
49
53
|
variant?: string;
|
|
@@ -269,9 +269,7 @@ function buildBaseData(params) {
|
|
|
269
269
|
payment_status: financials.paymentStatus,
|
|
270
270
|
total_amount: formatMoney(financials.totalAmount, currencySymbol),
|
|
271
271
|
has_pay: formatMoney(financials.netPaymentApplied, currencySymbol),
|
|
272
|
-
|
|
273
|
-
amount_gap: formatMoney(financials.amountGap, currencySymbol)
|
|
274
|
-
} : {}),
|
|
272
|
+
amount_gap: formatCalculatedMoney(financials.amountGap ?? 0, currencySymbol),
|
|
275
273
|
is_repeat: 0,
|
|
276
274
|
buzzer: stringify(order.buzzer || order.pickup_number || order.metadata?.buzzer),
|
|
277
275
|
order_no: orderNumber,
|
|
@@ -323,15 +321,21 @@ function buildProducts(order, locale, productMap) {
|
|
|
323
321
|
});
|
|
324
322
|
const productNote = resolveProductNote(product, locale);
|
|
325
323
|
const basePrice = resolveProductBasePrice(product);
|
|
324
|
+
const resolvedCombinations = Array.isArray(product.combinations) && product.combinations.length ? normalizeExistingCombinations(product.combinations, locale, currencySymbol) : combinations;
|
|
326
325
|
return {
|
|
327
326
|
product_title: productTitle,
|
|
328
327
|
product_quantity: quantity,
|
|
329
328
|
selling_price: formatMoney(sellingPrice, currencySymbol),
|
|
329
|
+
total_selling_price: formatCalculatedMoney(new _decimal.default(toMoneyNumber(sellingPrice)).mul(quantity), currencySymbol),
|
|
330
330
|
original_price: formatMoney(originalPrice, currencySymbol),
|
|
331
|
-
|
|
332
|
-
base_price: formatMoney(basePrice, currencySymbol)
|
|
333
|
-
} : {}),
|
|
331
|
+
base_price: formatCalculatedMoney(basePrice ?? 0, currencySymbol),
|
|
334
332
|
total_original_price: formatMoney(new _decimal.default(toMoneyNumber(originalPrice)).mul(quantity), currencySymbol),
|
|
333
|
+
...(options.length ? {
|
|
334
|
+
options_total_price: sumNormalizedOptionTotals(options, currencySymbol)
|
|
335
|
+
} : {}),
|
|
336
|
+
...(resolvedCombinations.length ? {
|
|
337
|
+
package_items_total_price: sumCombinationTotals(resolvedCombinations, currencySymbol)
|
|
338
|
+
} : {}),
|
|
335
339
|
extension_list: extensionList,
|
|
336
340
|
...(isPresentId(productUid) ? {
|
|
337
341
|
product_uid: productUid
|
|
@@ -348,10 +352,8 @@ function buildProducts(order, locale, productMap) {
|
|
|
348
352
|
...(resolvedVariant ? {
|
|
349
353
|
variant: resolvedVariant
|
|
350
354
|
} : {}),
|
|
351
|
-
...(
|
|
352
|
-
combinations:
|
|
353
|
-
} : combinations.length ? {
|
|
354
|
-
combinations
|
|
355
|
+
...(resolvedCombinations.length ? {
|
|
356
|
+
combinations: resolvedCombinations
|
|
355
357
|
} : {})
|
|
356
358
|
};
|
|
357
359
|
});
|
|
@@ -491,6 +493,7 @@ function normalizeProductOptions(params) {
|
|
|
491
493
|
const rawOptions = Array.isArray(params.options) ? params.options : Array.isArray(product.options) ? product.options : Array.isArray(product.product_sku?.option) ? product.product_sku.option : [];
|
|
492
494
|
return rawOptions.map(option => {
|
|
493
495
|
const price = option.original_price ?? option.add_price ?? option.price;
|
|
496
|
+
const quantity = toNumber(option.num ?? option.quantity ?? 1, 1);
|
|
494
497
|
const value = option.value ?? option.option ?? option.item;
|
|
495
498
|
const name = getProductTitleSource(option, locale) || resolveOptionTitleFromParentProduct({
|
|
496
499
|
option,
|
|
@@ -500,11 +503,12 @@ function normalizeProductOptions(params) {
|
|
|
500
503
|
return {
|
|
501
504
|
name,
|
|
502
505
|
...(option.num !== undefined || option.quantity !== undefined ? {
|
|
503
|
-
num:
|
|
506
|
+
num: quantity
|
|
504
507
|
} : {}),
|
|
505
508
|
...(price !== undefined && price !== null && price !== '' ? {
|
|
506
509
|
price: formatMoney(price, currencySymbol)
|
|
507
510
|
} : {}),
|
|
511
|
+
total_price: formatCalculatedMoney(new _decimal.default(toMoneyNumber(price ?? 0)).mul(quantity), currencySymbol),
|
|
508
512
|
...(value !== undefined && value !== null && value !== '' ? {
|
|
509
513
|
value: getLocalizedValue(value, locale)
|
|
510
514
|
} : {})
|
|
@@ -535,12 +539,25 @@ function normalizeProductCombinations(params) {
|
|
|
535
539
|
parentProduct
|
|
536
540
|
});
|
|
537
541
|
const productNote = resolveProductNote(bundle, locale);
|
|
538
|
-
const nestedCombinations = Array.isArray(bundle.combinations) ? normalizeExistingCombinations(bundle.combinations, locale) : [];
|
|
542
|
+
const nestedCombinations = Array.isArray(bundle.combinations) ? normalizeExistingCombinations(bundle.combinations, locale, currencySymbol) : [];
|
|
543
|
+
const quantity = toNumber(bundle.num ?? bundle.quantity ?? 1, 1);
|
|
544
|
+
const formattedPrice = formatBundlePrice(bundle, currencySymbol);
|
|
545
|
+
const optionsTotal = sumNormalizedOptionTotals(combinationsOptions, currencySymbol);
|
|
546
|
+
const explicitBasePrice = firstPresentValue(bundle.base_price, bundle.metadata?.source_product_price);
|
|
547
|
+
const basePrice = isPresentId(explicitBasePrice) ? explicitBasePrice : 0;
|
|
539
548
|
return {
|
|
540
549
|
product_title: baseTitle,
|
|
541
|
-
product_quantity:
|
|
542
|
-
price:
|
|
550
|
+
product_quantity: quantity,
|
|
551
|
+
price: formattedPrice,
|
|
552
|
+
total_selling_price: formatCalculatedMoney(new _decimal.default(toMoneyNumber(formattedPrice)).mul(quantity), currencySymbol),
|
|
553
|
+
base_price: formatCalculatedMoney(basePrice, currencySymbol),
|
|
543
554
|
combinations_options: combinationsOptions,
|
|
555
|
+
...(combinationsOptions.length ? {
|
|
556
|
+
options_total_price: optionsTotal
|
|
557
|
+
} : {}),
|
|
558
|
+
...(nestedCombinations.length ? {
|
|
559
|
+
package_items_total_price: sumCombinationTotals(nestedCombinations, currencySymbol)
|
|
560
|
+
} : {}),
|
|
544
561
|
...(productNote ? {
|
|
545
562
|
product_note: productNote
|
|
546
563
|
} : {}),
|
|
@@ -550,29 +567,65 @@ function normalizeProductCombinations(params) {
|
|
|
550
567
|
};
|
|
551
568
|
});
|
|
552
569
|
}
|
|
553
|
-
function normalizeExistingCombinations(combinations, locale) {
|
|
570
|
+
function normalizeExistingCombinations(combinations, locale, currencySymbol) {
|
|
554
571
|
return combinations.map(combination => {
|
|
555
572
|
if (!combination || typeof combination !== 'object' || Array.isArray(combination)) {
|
|
556
573
|
return combination;
|
|
557
574
|
}
|
|
558
575
|
const productNote = resolveProductNote(combination, locale);
|
|
559
|
-
const
|
|
576
|
+
const rawOptions = Array.isArray(combination.combinations_options) ? combination.combinations_options : Array.isArray(combination.options) ? combination.options : [];
|
|
577
|
+
const normalizedOptions = normalizeProductOptions({
|
|
578
|
+
product: {},
|
|
579
|
+
locale,
|
|
580
|
+
currencySymbol,
|
|
581
|
+
options: rawOptions
|
|
582
|
+
});
|
|
583
|
+
const nestedCombinations = Array.isArray(combination.combinations) ? normalizeExistingCombinations(combination.combinations, locale, currencySymbol) : [];
|
|
560
584
|
const snapshot = {
|
|
561
585
|
...combination
|
|
562
586
|
};
|
|
563
587
|
delete snapshot.product_note;
|
|
564
588
|
delete snapshot.note;
|
|
589
|
+
delete snapshot.combinations;
|
|
590
|
+
const quantity = toNumber(combination.product_quantity ?? combination.num ?? combination.quantity ?? 1, 1);
|
|
591
|
+
const unitPrice = firstPresentValue(combination.selling_price, combination.price, combination.original_price) ?? 0;
|
|
592
|
+
const optionsAmount = sumMoneyValues(normalizedOptions.map(option => option.total_price));
|
|
593
|
+
const explicitBasePrice = firstPresentValue(combination.base_price);
|
|
594
|
+
const basePrice = isPresentId(explicitBasePrice) ? explicitBasePrice : 0;
|
|
565
595
|
return {
|
|
566
596
|
...snapshot,
|
|
597
|
+
product_quantity: quantity,
|
|
598
|
+
total_selling_price: formatCalculatedMoney(new _decimal.default(toMoneyNumber(unitPrice)).mul(quantity), currencySymbol),
|
|
599
|
+
base_price: formatCalculatedMoney(basePrice, currencySymbol),
|
|
600
|
+
...(Array.isArray(combination.combinations_options) ? {
|
|
601
|
+
combinations_options: normalizedOptions
|
|
602
|
+
} : Array.isArray(combination.options) ? {
|
|
603
|
+
options: normalizedOptions
|
|
604
|
+
} : {}),
|
|
605
|
+
...(normalizedOptions.length ? {
|
|
606
|
+
options_total_price: formatCalculatedMoney(optionsAmount, currencySymbol)
|
|
607
|
+
} : {}),
|
|
608
|
+
...(nestedCombinations.length ? {
|
|
609
|
+
package_items_total_price: sumCombinationTotals(nestedCombinations, currencySymbol)
|
|
610
|
+
} : {}),
|
|
567
611
|
...(productNote ? {
|
|
568
612
|
product_note: productNote
|
|
569
613
|
} : {}),
|
|
570
|
-
...(nestedCombinations ? {
|
|
614
|
+
...(nestedCombinations.length ? {
|
|
571
615
|
combinations: nestedCombinations
|
|
572
616
|
} : {})
|
|
573
617
|
};
|
|
574
618
|
});
|
|
575
619
|
}
|
|
620
|
+
function sumMoneyValues(values) {
|
|
621
|
+
return values.reduce((sum, value) => sum.plus(toMoneyNumber(value)), new _decimal.default(0));
|
|
622
|
+
}
|
|
623
|
+
function sumNormalizedOptionTotals(options, currencySymbol) {
|
|
624
|
+
return formatCalculatedMoney(sumMoneyValues(options.map(option => option.total_price)), currencySymbol);
|
|
625
|
+
}
|
|
626
|
+
function sumCombinationTotals(combinations, currencySymbol) {
|
|
627
|
+
return formatCalculatedMoney(sumMoneyValues(combinations.map(combination => combination.total_selling_price)), currencySymbol);
|
|
628
|
+
}
|
|
576
629
|
function resolveProductNote(product, locale) {
|
|
577
630
|
const productNote = getLocalizedValue(product.product_note, locale).trim();
|
|
578
631
|
if (productNote) return productNote;
|
|
@@ -819,12 +872,14 @@ function buildFees(params) {
|
|
|
819
872
|
const productExpectAmount = firstPresentValue(summary.product_expect_amount, summary.product_amount, order.product_expect_amount);
|
|
820
873
|
const productOriginalAmount = firstPresentValue(summary.product_original_amount, order.product_original_amount) ?? calculateProductOriginalAmount(order);
|
|
821
874
|
const totalSavingsAmount = new _decimal.default(toMoneyNumber(productOriginalAmount)).minus(toMoneyNumber(productExpectAmount)).plus(toMoneyNumber(discountAmount));
|
|
875
|
+
const itemDiscountAmount = new _decimal.default(toMoneyNumber(productExpectAmount ?? 0)).minus(toMoneyNumber(productOriginalAmount));
|
|
822
876
|
return {
|
|
823
877
|
fees,
|
|
824
878
|
countFee: {
|
|
825
879
|
total_amount: formatMoney(firstPresentValue(summary.total_amount, order.total_amount) ?? 0, currencySymbol),
|
|
826
880
|
original_item_amount: formatMoney(productOriginalAmount, currencySymbol),
|
|
827
881
|
product_expect_amount: formatMoney(productExpectAmount ?? 0, currencySymbol),
|
|
882
|
+
item_discount_amount: formatCalculatedMoney(itemDiscountAmount, currencySymbol),
|
|
828
883
|
expect_amount: formatMoney(financials.totalAmount, currencySymbol),
|
|
829
884
|
product_quantity: toNumber(firstPresentValue(summary.product_quantity, products.reduce((sum, product) => sum + product.product_quantity, 0)), 0),
|
|
830
885
|
has_pay: formatMoney(financials.orderPaidAmount, currencySymbol),
|
|
@@ -1449,6 +1504,11 @@ function formatMoney(value, currencySymbol) {
|
|
|
1449
1504
|
const amount = new _decimal.default(toMoneyNumber(value));
|
|
1450
1505
|
return `${currencySymbol}${amount.toFixed(2)}`;
|
|
1451
1506
|
}
|
|
1507
|
+
function formatCalculatedMoney(value, currencySymbol) {
|
|
1508
|
+
const amount = new _decimal.default(toMoneyNumber(value));
|
|
1509
|
+
const sign = amount.isNegative() ? '-' : '';
|
|
1510
|
+
return `${sign}${currencySymbol}${amount.abs().toFixed(2)}`;
|
|
1511
|
+
}
|
|
1452
1512
|
function formatNegativeMoney(value, currencySymbol) {
|
|
1453
1513
|
const amount = new _decimal.default(toMoneyNumber(value)).abs();
|
|
1454
1514
|
return `-${currencySymbol}${amount.toFixed(2)}`;
|
|
@@ -45,6 +45,12 @@ export declare class BookingTicketImpl extends BaseSalesImpl implements Module {
|
|
|
45
45
|
*/
|
|
46
46
|
protected createSubModule(moduleName: string): Module | null;
|
|
47
47
|
initialize(core: PisellCore, options?: ModuleOptions): Promise<void>;
|
|
48
|
+
private getInjectedOpenDataConfig;
|
|
49
|
+
/**
|
|
50
|
+
* 在解决方案对外 ready 前准备 Invoice Document;失败只降级模板,不阻断销售。
|
|
51
|
+
* 已由页面取得的 OpenData 直接交给模块接管,没有注入数据时再走 target-scoped 加载。
|
|
52
|
+
*/
|
|
53
|
+
private prepareInvoiceTemplateAtSolutionReady;
|
|
48
54
|
protected getBookingTicketBusinessCode(): string | null;
|
|
49
55
|
private getIdGeneratorPlugin;
|
|
50
56
|
private loadOpenDataConfig;
|
|
@@ -193,10 +193,37 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
|
|
|
193
193
|
throw new Error('bookingTicket解决方案需要 request 插件支持');
|
|
194
194
|
}
|
|
195
195
|
this.platform = this.otherParams?.platform;
|
|
196
|
+
await this.prepareInvoiceTemplateAtSolutionReady(this.getInjectedOpenDataConfig());
|
|
196
197
|
await this.configureIdGeneratorFromOpenData();
|
|
197
198
|
}
|
|
199
|
+
getInjectedOpenDataConfig() {
|
|
200
|
+
const openData = this.otherParams?.openData;
|
|
201
|
+
return openData && typeof openData === 'object' && !Array.isArray(openData) ? openData : undefined;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* 在解决方案对外 ready 前准备 Invoice Document;失败只降级模板,不阻断销售。
|
|
206
|
+
* 已由页面取得的 OpenData 直接交给模块接管,没有注入数据时再走 target-scoped 加载。
|
|
207
|
+
*/
|
|
208
|
+
async prepareInvoiceTemplateAtSolutionReady(injectedOpenData) {
|
|
209
|
+
const businessCode = this.getBookingTicketBusinessCode();
|
|
210
|
+
if (!businessCode || !this.store.openData) return;
|
|
211
|
+
const channel = this.getSubmitOrderSalesChannel();
|
|
212
|
+
const target = `${businessCode}+${channel}`;
|
|
213
|
+
try {
|
|
214
|
+
if (injectedOpenData && typeof this.store.openData.ensureInvoiceTemplateSettings === 'function') {
|
|
215
|
+
this.openDataTarget = target;
|
|
216
|
+
await this.store.openData.ensureInvoiceTemplateSettings(target, injectedOpenData);
|
|
217
|
+
this.otherParams.openData = injectedOpenData;
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
await this.loadOpenDataConfig();
|
|
221
|
+
} catch (error) {
|
|
222
|
+
console.warn('[BookingTicket] Invoice 模板预加载失败,使用默认配置', error);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
198
225
|
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];
|
|
226
|
+
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
227
|
for (const candidate of candidates) {
|
|
201
228
|
if (candidate === undefined || candidate === null) continue;
|
|
202
229
|
const normalized = String(candidate).trim();
|
|
@@ -1784,10 +1811,12 @@ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
|
|
|
1784
1811
|
await super.setOtherParams(params, {
|
|
1785
1812
|
cover
|
|
1786
1813
|
});
|
|
1814
|
+
const invoiceContextChanged = params?.businessCode !== undefined || params?.business_code !== undefined || params?.channel !== undefined || params?.platform !== undefined || Object.prototype.hasOwnProperty.call(params, 'openData');
|
|
1815
|
+
if (invoiceContextChanged) {
|
|
1816
|
+
await this.prepareInvoiceTemplateAtSolutionReady(Object.prototype.hasOwnProperty.call(params, 'openData') ? this.getInjectedOpenDataConfig() : undefined);
|
|
1817
|
+
}
|
|
1787
1818
|
if (params?.businessCode !== undefined || params?.business_code !== undefined) {
|
|
1788
1819
|
await this.configureIdGeneratorFromOpenData();
|
|
1789
|
-
} else if (params?.channel !== undefined) {
|
|
1790
|
-
await this.loadOpenDataConfig();
|
|
1791
1820
|
}
|
|
1792
1821
|
}
|
|
1793
1822
|
getDeviceTaskPlugin() {
|