@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.
@@ -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: "appointment_booking" | "virtual";
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?: string;
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
- ...(isPresentId(financials.amountGap) ? {
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
- ...(isPresentId(basePrice) ? {
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
- ...(Array.isArray(product.combinations) && product.combinations.length ? {
352
- combinations: normalizeExistingCombinations(product.combinations, locale)
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: toNumber(option.num ?? option.quantity, 1)
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: toNumber(bundle.num ?? bundle.quantity ?? 1, 1),
542
- price: formatBundlePrice(bundle, currencySymbol),
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 nestedCombinations = Array.isArray(combination.combinations) ? normalizeExistingCombinations(combination.combinations, locale) : undefined;
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() {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "private": false,
3
3
  "name": "@pisell/pisellos",
4
- "version": "0.10.32",
4
+ "version": "0.10.33",
5
5
  "description": "一个可扩展的前端模块化SDK框架,支持插件系统",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",