@pisell/pisellos 2.2.198 → 2.2.199

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.
Files changed (42) hide show
  1. package/dist/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +7 -3
  2. package/dist/modules/Cart/utils/cartProduct.js +11 -1
  3. package/dist/modules/Cart/utils/changePrice.js +11 -11
  4. package/dist/modules/Order/index.js +155 -104
  5. package/dist/modules/Order/types.d.ts +1 -0
  6. package/dist/modules/Order/utils.d.ts +33 -2
  7. package/dist/modules/Order/utils.js +112 -55
  8. package/dist/modules/SalesSummary/utils.js +97 -63
  9. package/dist/server/index.d.ts +3 -0
  10. package/dist/server/index.js +294 -190
  11. package/dist/server/modules/order/index.d.ts +8 -0
  12. package/dist/server/modules/order/index.js +536 -317
  13. package/dist/server/modules/order/types.d.ts +2 -0
  14. package/dist/solution/BaseSales/index.js +2 -1
  15. package/dist/solution/BaseSales/types.d.ts +1 -0
  16. package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +19 -4
  17. package/dist/solution/BookingByStep/index.d.ts +1 -1
  18. package/dist/solution/ScanOrder/types.d.ts +1 -0
  19. package/dist/solution/ScanOrder/utils.js +26 -9
  20. package/dist/solution/VenueBooking/index.js +2 -1
  21. package/lib/model/strategy/adapter/promotion/index.js +49 -0
  22. package/lib/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +4 -1
  23. package/lib/modules/Cart/utils/cartProduct.js +11 -1
  24. package/lib/modules/Cart/utils/changePrice.js +12 -20
  25. package/lib/modules/Order/index.js +58 -6
  26. package/lib/modules/Order/types.d.ts +1 -0
  27. package/lib/modules/Order/utils.d.ts +33 -2
  28. package/lib/modules/Order/utils.js +44 -9
  29. package/lib/modules/SalesSummary/utils.js +41 -44
  30. package/lib/server/index.d.ts +3 -0
  31. package/lib/server/index.js +82 -3
  32. package/lib/server/modules/order/index.d.ts +8 -0
  33. package/lib/server/modules/order/index.js +106 -4
  34. package/lib/server/modules/order/types.d.ts +2 -0
  35. package/lib/solution/BaseSales/index.js +2 -1
  36. package/lib/solution/BaseSales/types.d.ts +1 -0
  37. package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +15 -1
  38. package/lib/solution/BookingByStep/index.d.ts +1 -1
  39. package/lib/solution/ScanOrder/types.d.ts +1 -0
  40. package/lib/solution/ScanOrder/utils.js +28 -10
  41. package/lib/solution/VenueBooking/index.js +2 -1
  42. package/package.json +1 -1
@@ -44,11 +44,14 @@ __export(utils_exports, {
44
44
  formatV1Product: () => formatV1Product,
45
45
  generateDuration: () => generateDuration,
46
46
  getAllDiscountList: () => getAllDiscountList,
47
+ getBundleSellingMagnitude: () => getBundleSellingMagnitude,
48
+ getBundleSignedUnit: () => getBundleSignedUnit,
47
49
  getOrderProductLineUid: () => getOrderProductLineUid,
48
50
  getProductSkuOptions: () => getProductSkuOptions,
49
51
  hasAssignedHolderId: () => hasAssignedHolderId,
50
52
  indexOrderProductsByUid: () => indexOrderProductsByUid,
51
53
  isEmptyOrderProductDisplayValue: () => isEmptyOrderProductDisplayValue,
54
+ isMarkdownBundle: () => isMarkdownBundle,
52
55
  isTempOrder: () => isTempOrder,
53
56
  mapPaymentItemToOrderPayment: () => mapPaymentItemToOrderPayment,
54
57
  mapPaymentItemsToOrderPayments: () => mapPaymentItemsToOrderPayments,
@@ -85,11 +88,10 @@ function composeLinePrice(params) {
85
88
  let total = new import_decimal.default(Number(mainPrice) || 0);
86
89
  if (Array.isArray(bundle)) {
87
90
  for (const item of bundle) {
88
- const rawPrice = useOriginalBundle ? (item == null ? void 0 : item.original_price) ?? (item == null ? void 0 : item.product_price) ?? (item == null ? void 0 : item.price) : (item == null ? void 0 : item.bundle_selling_price) ?? (item == null ? void 0 : item.price);
89
- const price = new import_decimal.default(Number(rawPrice) || 0);
91
+ const signedUnit = getBundleSignedUnit(item, { useOriginal: useOriginalBundle });
90
92
  const rawNum = (item == null ? void 0 : item.num) ?? (item == null ? void 0 : item.quantity) ?? 1;
91
93
  const num = new import_decimal.default(Number(rawNum) || 0);
92
- total = total.plus(price.times(num));
94
+ total = total.plus(signedUnit.times(num));
93
95
  }
94
96
  }
95
97
  return total.toDecimalPlaces(2).toFixed(2);
@@ -129,6 +131,33 @@ function sumOptionUnitPrice(options) {
129
131
  }
130
132
  return total;
131
133
  }
134
+ function isMarkdownBundle(bundle) {
135
+ if (!bundle || typeof bundle !== "object")
136
+ return false;
137
+ const ext = bundle.price_type_ext ?? bundle.custom_price_type_ext;
138
+ const type = bundle.price_type ?? bundle.custom_price_type;
139
+ return type === "markdown" && ext !== "product_price";
140
+ }
141
+ function getBundleSignedUnit(bundle, options) {
142
+ if (!bundle || typeof bundle !== "object")
143
+ return new import_decimal.default(0);
144
+ const useOriginal = (options == null ? void 0 : options.useOriginal) ?? false;
145
+ if (isMarkdownBundle(bundle)) {
146
+ const catalog = useOriginal ? bundle.original_price ?? bundle.product_price ?? bundle.custom_price ?? bundle.base_price ?? bundle.price : bundle.custom_price ?? bundle.product_price ?? bundle.original_price ?? bundle.base_price ?? bundle.price;
147
+ if (catalog !== void 0 && catalog !== null && catalog !== "") {
148
+ const raw = new import_decimal.default(Number(catalog) || 0);
149
+ if (raw.lt(0))
150
+ return raw;
151
+ return raw.minus(sumOptionUnitPrice(bundle.option)).negated();
152
+ }
153
+ return new import_decimal.default(Number(bundle.bundle_selling_price) || 0).abs().negated();
154
+ }
155
+ const rawPrice = useOriginal ? bundle.original_price ?? bundle.product_price ?? bundle.price : bundle.bundle_selling_price ?? bundle.price;
156
+ return new import_decimal.default(Number(rawPrice) || 0);
157
+ }
158
+ function getBundleSellingMagnitude(bundle) {
159
+ return getBundleSignedUnit(bundle).abs();
160
+ }
132
161
  function resolveRulesManualDiscountFlag(metadata) {
133
162
  if (!metadata)
134
163
  return void 0;
@@ -472,9 +501,8 @@ function formatSubmitBundleItems(bundle) {
472
501
  return bundle.map((b) => {
473
502
  const rawBundle = b && typeof b === "object" ? b : {};
474
503
  const existedMetadata = rawBundle.metadata && typeof rawBundle.metadata === "object" ? rawBundle.metadata : {};
475
- const sellingPrice = toMoneyString(
476
- rawBundle.bundle_selling_price ?? rawBundle.price
477
- );
504
+ const sellingPrice = getBundleSellingMagnitude(rawBundle).toFixed(2);
505
+ const paymentPrice = rawBundle.bundle_payment_price !== void 0 && rawBundle.bundle_payment_price !== null && rawBundle.bundle_payment_price !== "" ? toMoneyString(rawBundle.bundle_payment_price) : sellingPrice;
478
506
  const priceValue = rawBundle.price ?? rawBundle.custom_price ?? rawBundle.bundle_selling_price;
479
507
  const relationSurchargeIds = Array.isArray(rawBundle.relation_surcharge_ids) ? rawBundle.relation_surcharge_ids : Array.isArray(existedMetadata.relation_surcharge_ids) ? existedMetadata.relation_surcharge_ids : [];
480
508
  const surchargeFee = toMoneyString(
@@ -496,6 +524,7 @@ function formatSubmitBundleItems(bundle) {
496
524
  price_type: rawBundle.price_type ?? rawBundle.custom_price_type ?? "",
497
525
  price_type_ext: rawBundle.price_type_ext ?? rawBundle.custom_price_type_ext ?? "",
498
526
  bundle_selling_price: sellingPrice,
527
+ bundle_payment_price: paymentPrice,
499
528
  option: formatSubmitOptionItems(rawBundle.option),
500
529
  bundle_group_id: (rawBundle == null ? void 0 : rawBundle.bundle_group_id) ?? (rawBundle == null ? void 0 : rawBundle.group_id),
501
530
  bundle_id: (rawBundle == null ? void 0 : rawBundle.bundle_id) ?? (rawBundle == null ? void 0 : rawBundle.id),
@@ -532,6 +561,9 @@ function normalizeSubmitProduct(product) {
532
561
  const priceMetaKeys = [
533
562
  "main_product_original_price",
534
563
  "main_product_selling_price",
564
+ "main_product_attached_bundle_selling_price",
565
+ "main_product_attached_bundle_payment_price",
566
+ "average_discount_amount_rate",
535
567
  "source_product_price",
536
568
  "main_product_attached_bundle_surcharge_fee",
537
569
  "main_product_attached_bundle_tax_fee",
@@ -566,9 +598,9 @@ function normalizeSubmitProduct(product) {
566
598
  discount_list: normalizeOrderProductDiscountList(submitProduct.discount_list),
567
599
  product_bundle: formatSubmitBundleItems(submitProduct.product_bundle),
568
600
  metadata: cleanMetadata,
569
- // 出站兼容:后端消费 payment_price 字段,这里从 selling_price 直接派生。
570
- // 新语义下 selling_price 是 composite(含 option/bundle),payment_price 同语义。
571
- payment_price: submitProduct.selling_price
601
+ // 出站兼容:后端消费 payment_price 字段(行 composite 已减整单折扣均摊)。
602
+ // 由整单折扣均摊层产出;缺省(无折扣)时与 selling_price 同义。
603
+ payment_price: submitProduct.payment_price ?? submitProduct.selling_price
572
604
  };
573
605
  }
574
606
  var SUBMIT_BOOKING_METADATA_WHITELIST = [
@@ -953,11 +985,14 @@ function indexOrderProductsByUid(products) {
953
985
  formatV1Product,
954
986
  generateDuration,
955
987
  getAllDiscountList,
988
+ getBundleSellingMagnitude,
989
+ getBundleSignedUnit,
956
990
  getOrderProductLineUid,
957
991
  getProductSkuOptions,
958
992
  hasAssignedHolderId,
959
993
  indexOrderProductsByUid,
960
994
  isEmptyOrderProductDisplayValue,
995
+ isMarkdownBundle,
961
996
  isTempOrder,
962
997
  mapPaymentItemToOrderPayment,
963
998
  mapPaymentItemsToOrderPayments,
@@ -78,8 +78,8 @@ function getBundleUnitPrice(product, useOriginal = false) {
78
78
  const bundleItems = product.product_bundle || [];
79
79
  return bundleItems.reduce((sum, item) => {
80
80
  const quantity = getSafeNum(item == null ? void 0 : item.num);
81
- const price = useOriginal ? toDecimal((item == null ? void 0 : item.original_price) ?? (item == null ? void 0 : item.product_price) ?? (item == null ? void 0 : item.price)) : toDecimal((item == null ? void 0 : item.bundle_selling_price) ?? (item == null ? void 0 : item.price));
82
- return sum.plus(price.times(quantity));
81
+ const signedUnit = (0, import_utils2.getBundleSignedUnit)(item, { useOriginal });
82
+ return sum.plus(signedUnit.times(quantity));
83
83
  }, new import_decimal.default(0));
84
84
  }
85
85
  function getUnitPaymentTotal(product) {
@@ -607,20 +607,28 @@ function calculateSingleItemTax(params) {
607
607
  return new import_decimal.default(0);
608
608
  return price.dividedBy(divisor).times(rate);
609
609
  }
610
- function getMainProductPaymentTotal(product) {
610
+ function getMainAttachedBundleSellingTotal(product) {
611
611
  var _a, _b;
612
612
  const mainSelling = ((_a = product.metadata) == null ? void 0 : _a.main_product_selling_price) ?? ((_b = product.metadata) == null ? void 0 : _b.main_product_original_price) ?? 0;
613
613
  let total = toDecimal(mainSelling);
614
614
  const bundleItems = product.product_bundle || [];
615
615
  for (const bundleItem of bundleItems) {
616
616
  if (isBundleMarkupOrDiscount(bundleItem)) {
617
- const unit = toDecimal(bundleItem.bundle_selling_price ?? bundleItem.price ?? 0);
617
+ const unit = (0, import_utils2.getBundleSignedUnit)(bundleItem);
618
618
  const qty = getSafeNum(bundleItem.num ?? bundleItem.quantity);
619
619
  total = total.plus(unit.times(qty));
620
620
  }
621
621
  }
622
622
  return import_decimal.default.max(total, 0);
623
623
  }
624
+ function getMainProductPaymentTotal(product) {
625
+ var _a;
626
+ const attachedPayment = (_a = product.metadata) == null ? void 0 : _a.main_product_attached_bundle_payment_price;
627
+ if (attachedPayment !== void 0 && attachedPayment !== null && attachedPayment !== "") {
628
+ return import_decimal.default.max(toDecimal(attachedPayment), 0);
629
+ }
630
+ return getMainAttachedBundleSellingTotal(product);
631
+ }
624
632
  function getExplicitTaxRemainder(item) {
625
633
  var _a;
626
634
  const value = (item == null ? void 0 : item.tax_fee_rounding_remainder) ?? ((_a = item == null ? void 0 : item.metadata) == null ? void 0 : _a.tax_fee_rounding_remainder);
@@ -805,51 +813,44 @@ function calculateProductsTax(products, taxRate, isPriceIncludeTax) {
805
813
  originTax: totalOriginTax.toDecimalPlaces(2, import_decimal.default.ROUND_HALF_UP)
806
814
  };
807
815
  }
808
- function applyShopDiscountToSurchargeBase(products, productAmount, shopDiscountAmount) {
809
- if (productAmount.lte(0) || shopDiscountAmount.lte(0))
810
- return () => {
811
- };
812
- const effectiveDiscount = import_decimal.default.min(productAmount, shopDiscountAmount);
813
- if (effectiveDiscount.lte(0))
814
- return () => {
815
- };
816
- const snapshots = products.map((product) => {
817
- var _a;
818
- return {
819
- product,
820
- metadata: product.metadata,
821
- attachedBundleSellingPrice: (_a = product.metadata) == null ? void 0 : _a.main_product_attached_bundle_selling_price
822
- };
823
- });
816
+ function allocateShopDiscountToPayment(products, productAmount, shopDiscountAmount) {
817
+ const effectiveDiscount = productAmount.lte(0) ? new import_decimal.default(0) : import_decimal.default.max(import_decimal.default.min(productAmount, shopDiscountAmount), 0);
824
818
  let allocatedDiscount = new import_decimal.default(0);
825
819
  products.forEach((product, index) => {
826
820
  const quantity = new import_decimal.default(getSafeNum(product.num));
827
- const lineAmount = getUnitPaymentTotal(product).times(quantity);
828
- const lineDiscount = index === products.length - 1 ? effectiveDiscount.minus(allocatedDiscount) : toAmountFormat(lineAmount.div(productAmount).times(effectiveDiscount));
821
+ const sellingUnit = getUnitPaymentTotal(product);
822
+ const lineSellingTotal = sellingUnit.times(quantity);
823
+ const lineDiscount = effectiveDiscount.lte(0) ? new import_decimal.default(0) : index === products.length - 1 ? import_decimal.default.max(effectiveDiscount.minus(allocatedDiscount), 0) : toAmountFormat(lineSellingTotal.div(productAmount).times(effectiveDiscount));
829
824
  allocatedDiscount = allocatedDiscount.plus(lineDiscount);
830
825
  const unitDiscount = quantity.lte(0) ? new import_decimal.default(0) : lineDiscount.div(quantity);
831
- const currentBase = getMainSurchargeUnitAmount(product);
832
- const adjustedBase = import_decimal.default.max(currentBase.minus(unitDiscount), 0);
826
+ const paymentUnit = import_decimal.default.max(sellingUnit.minus(unitDiscount), 0);
827
+ const rate = sellingUnit.lte(0) ? new import_decimal.default(1) : toBcScale(paymentUnit.div(sellingUnit), 6);
828
+ const mainSellingTotal = getMainAttachedBundleSellingTotal(product);
829
+ const mainPaymentTotal = toAmountFormat(bcMul(mainSellingTotal, rate, 6));
833
830
  product.metadata = {
834
831
  ...product.metadata || {},
835
- main_product_attached_bundle_selling_price: adjustedBase.toFixed(2)
832
+ average_discount_amount_rate: rate.toString(),
833
+ main_product_attached_bundle_selling_price: mainSellingTotal.toFixed(2),
834
+ main_product_attached_bundle_payment_price: mainPaymentTotal.toFixed(2)
836
835
  };
837
- });
838
- return () => {
839
- for (const snapshot of snapshots) {
840
- if (!snapshot.metadata) {
841
- delete snapshot.product.metadata;
836
+ for (const bundle of product.product_bundle || []) {
837
+ if (isBundleOriginalPrice(bundle)) {
838
+ const bundleSelling = toDecimal(
839
+ bundle.bundle_selling_price ?? bundle.bundle_sum_price ?? bundle.price ?? 0
840
+ );
841
+ bundle.bundle_payment_price = toAmountFormat(bcMul(bundleSelling, rate, 6)).toFixed(2);
842
842
  continue;
843
843
  }
844
- snapshot.product.metadata = {
845
- ...snapshot.product.metadata,
846
- main_product_attached_bundle_selling_price: snapshot.attachedBundleSellingPrice
847
- };
848
- if (snapshot.attachedBundleSellingPrice === void 0) {
849
- delete snapshot.product.metadata.main_product_attached_bundle_selling_price;
844
+ if ((0, import_utils2.isMarkdownBundle)(bundle)) {
845
+ const magnitude = (0, import_utils2.getBundleSellingMagnitude)(bundle);
846
+ bundle.custom_price = bundle.custom_price ?? bundle.price;
847
+ bundle.price = magnitude.toFixed(2);
848
+ bundle.bundle_selling_price = magnitude.toFixed(2);
849
+ bundle.bundle_payment_price = toAmountFormat(bcMul(magnitude, rate, 6)).toFixed(2);
850
850
  }
851
851
  }
852
- };
852
+ product.payment_price = paymentUnit.toFixed(2);
853
+ });
853
854
  }
854
855
  function createEmptySalesSummary() {
855
856
  return {
@@ -907,12 +908,9 @@ function calculateSalesSummary(params) {
907
908
  var _a;
908
909
  return (_a = getPersistedMainSurchargeFee(product)) == null ? void 0 : _a.gt(0);
909
910
  });
910
- const restoreSurchargeBase = hasPersistedSurchargeSnapshot ? () => {
911
- } : applyShopDiscountToSurchargeBase(
912
- summaryProducts,
913
- productAmount,
914
- shopDiscountAmount
915
- );
911
+ if (!hasPersistedSurchargeSnapshot) {
912
+ allocateShopDiscountToPayment(summaryProducts, productAmount, shopDiscountAmount);
913
+ }
916
914
  const surchargeServiceItems = buildSurchargeServiceItems(summaryProducts);
917
915
  (0, import_utils.getSurcharge)(
918
916
  {
@@ -933,7 +931,6 @@ function calculateSalesSummary(params) {
933
931
  sourceItems: surchargeServiceItems,
934
932
  surchargeList
935
933
  });
936
- restoreSurchargeBase();
937
934
  const surchargeAmount = new import_decimal.default(
938
935
  (0, import_utils.getSurchargeAmount)(
939
936
  { bookingDetail: null, bookingId: void 0 },
@@ -287,6 +287,8 @@ declare class Server {
287
287
  private handleOrderSalesCheckout;
288
288
  private handleOrderCheckout;
289
289
  private handleOrderCheckoutSubmit;
290
+ private handlePendingSyncCheckoutOrder;
291
+ private buildPendingSyncCheckoutOrder;
290
292
  private handleOrderSalesDetail;
291
293
  /**
292
294
  * 解析 order_ids 入参,兼容 url query / data 字段、字符串(csv) / 数组两种形态。
@@ -320,6 +322,7 @@ declare class Server {
320
322
  private extractOrderDataFromCheckoutResponse;
321
323
  private normalizeCheckoutResponse;
322
324
  private extractBackendErrorResponse;
325
+ private isExplicitBackendErrorResponse;
323
326
  /**
324
327
  * 从 url 或路由 path 解析 pathname(不含 query,去掉末尾 /)
325
328
  */
@@ -1749,7 +1749,12 @@ var Server = class {
1749
1749
  });
1750
1750
  if (!((_a = this.app) == null ? void 0 : _a.request)) {
1751
1751
  this.logError(`${title}: app.request 不可用`);
1752
- return { code: 500, status: false, message: "app.request 不可用", data: null };
1752
+ return this.handlePendingSyncCheckoutOrder({
1753
+ backendPath,
1754
+ data,
1755
+ title,
1756
+ reason: "app.request 不可用"
1757
+ });
1753
1758
  }
1754
1759
  try {
1755
1760
  const response = await this.app.request.post(backendPath, data, {
@@ -1759,7 +1764,8 @@ var Server = class {
1759
1764
  });
1760
1765
  const fresh = this.extractOrderDataFromCheckoutResponse(response);
1761
1766
  if (fresh && this.order) {
1762
- await this.order.upsertOrdersFromRemote([fresh]);
1767
+ const syncedOrder = { ...fresh, need_sync: 0 };
1768
+ await this.order.upsertOrdersFromRemote([syncedOrder]);
1763
1769
  this.logInfo(`${title}: 订单已同步到本地`, {
1764
1770
  order_id: fresh.order_id,
1765
1771
  external_sale_number: fresh.external_sale_number,
@@ -1778,9 +1784,77 @@ var Server = class {
1778
1784
  const backendError = this.extractBackendErrorResponse(error);
1779
1785
  if (backendError)
1780
1786
  return backendError;
1787
+ return this.handlePendingSyncCheckoutOrder({
1788
+ backendPath,
1789
+ data,
1790
+ title,
1791
+ reason: errorMessage
1792
+ });
1793
+ }
1794
+ }
1795
+ async handlePendingSyncCheckoutOrder(params) {
1796
+ const { backendPath, data, title, reason } = params;
1797
+ const pendingOrder = this.buildPendingSyncCheckoutOrder(data);
1798
+ if (!pendingOrder) {
1799
+ this.logError(`${title}: checkout 失败且订单缺少本地存储标识`, {
1800
+ backendPath,
1801
+ reason
1802
+ });
1803
+ return {
1804
+ code: 500,
1805
+ status: false,
1806
+ message: "订单缺少本地存储标识,无法写入待同步队列",
1807
+ data: null
1808
+ };
1809
+ }
1810
+ if (!this.order || typeof this.order.upsertPendingSyncOrders !== "function") {
1811
+ this.logError(`${title}: Order 模块不支持本地待同步写入`, {
1812
+ backendPath,
1813
+ reason
1814
+ });
1815
+ return {
1816
+ code: 500,
1817
+ status: false,
1818
+ message: "Order 模块不支持本地待同步写入",
1819
+ data: null
1820
+ };
1821
+ }
1822
+ try {
1823
+ await this.order.upsertPendingSyncOrders([pendingOrder]);
1824
+ this.logWarning(`${title}: 后端未明确报错,订单已写入本地待同步`, {
1825
+ backendPath,
1826
+ reason,
1827
+ order_id: pendingOrder.order_id,
1828
+ external_sale_number: pendingOrder.external_sale_number,
1829
+ order_number: pendingOrder.order_number
1830
+ });
1831
+ return {
1832
+ code: 200,
1833
+ status: true,
1834
+ message: "",
1835
+ data: pendingOrder
1836
+ };
1837
+ } catch (error) {
1838
+ const errorMessage = error instanceof Error ? error.message : String(error);
1839
+ this.logError(`${title}: 写入本地待同步订单失败`, {
1840
+ backendPath,
1841
+ reason,
1842
+ error: errorMessage
1843
+ });
1781
1844
  return { code: 500, status: false, message: errorMessage, data: null };
1782
1845
  }
1783
1846
  }
1847
+ buildPendingSyncCheckoutOrder(data) {
1848
+ if (!data || typeof data !== "object")
1849
+ return null;
1850
+ const hasStorageKey = data.external_sale_number !== void 0 && data.external_sale_number !== null && data.external_sale_number !== "" ? true : data.order_id !== void 0 && data.order_id !== null && data.order_id !== "";
1851
+ if (!hasStorageKey)
1852
+ return null;
1853
+ return {
1854
+ ...data,
1855
+ need_sync: 1
1856
+ };
1857
+ }
1784
1858
  /**
1785
1859
  * 解析 order_ids 入参,兼容 url query / data 字段、字符串(csv) / 数组两种形态。
1786
1860
  * 自动 trim、去空、按字符串去重,输出归一化后的字符串数组。
@@ -1993,9 +2067,14 @@ var Server = class {
1993
2067
  error == null ? void 0 : error.body
1994
2068
  ];
1995
2069
  return candidates.find(
1996
- (candidate) => candidate && typeof candidate === "object"
2070
+ (candidate) => this.isExplicitBackendErrorResponse(candidate)
1997
2071
  ) || null;
1998
2072
  }
2073
+ isExplicitBackendErrorResponse(candidate) {
2074
+ if (!candidate || typeof candidate !== "object")
2075
+ return false;
2076
+ return candidate.code !== void 0 || candidate.status !== void 0 || typeof candidate.message === "string";
2077
+ }
1999
2078
  /**
2000
2079
  * 从 url 或路由 path 解析 pathname(不含 query,去掉末尾 /)
2001
2080
  */
@@ -92,6 +92,11 @@ export declare class OrderModule extends BaseModule implements Module {
92
92
  * 供 /update/localOrder 等在「本地尚无该单」时写入新建单。
93
93
  */
94
94
  upsertOrdersFromRemote(freshOrders: OrderData[]): Promise<void>;
95
+ /**
96
+ * 将本地待同步订单按 SQLite storage key 合并进 store 并落库。
97
+ * checkout 离线兜底可能没有 order_id,但通常会有 external_sale_number。
98
+ */
99
+ upsertPendingSyncOrders(pendingOrders: OrderData[]): Promise<void>;
95
100
  /**
96
101
  * 通过 SSE 按自定义 query 拉取订单(支持 select/with 精简字段)
97
102
  */
@@ -172,6 +177,9 @@ export declare class OrderModule extends BaseModule implements Module {
172
177
  private getStorageItem;
173
178
  private setStorageItem;
174
179
  private loadOrdersFromSQLite;
180
+ private isPendingSyncOrder;
181
+ private normalizeRemoteSyncedOrder;
182
+ private mergeRemoteSnapshotWithPendingOrders;
175
183
  /**
176
184
  * 串行执行订单 SQLite 写入任务,避免快照替换与增量 upsert 并发交错。
177
185
  *
@@ -44,6 +44,7 @@ var ORDER_SQLITE_DEDUPE_MS = 15e3;
44
44
  var ORDER_SILENT_REFRESH_MIN_INTERVAL_MS = 3e4;
45
45
  var ORDER_BUSINESS_WRITE_SOURCES = /* @__PURE__ */ new Set([
46
46
  "upsertOrdersFromRemote",
47
+ "upsertPendingSyncOrders",
47
48
  "overwriteExistingOrder"
48
49
  ]);
49
50
  var OrderModule = class extends import_BaseModule.BaseModule {
@@ -484,6 +485,53 @@ var OrderModule = class extends import_BaseModule.BaseModule {
484
485
  this.logInfo("upsertOrdersFromRemote-开始", { count: freshOrders.length });
485
486
  await this.mergeOrdersToStore(freshOrders, "upsertOrdersFromRemote");
486
487
  }
488
+ /**
489
+ * 将本地待同步订单按 SQLite storage key 合并进 store 并落库。
490
+ * checkout 离线兜底可能没有 order_id,但通常会有 external_sale_number。
491
+ */
492
+ async upsertPendingSyncOrders(pendingOrders) {
493
+ if (!(pendingOrders == null ? void 0 : pendingOrders.length)) {
494
+ this.logInfo("upsertPendingSyncOrders-订单列表为空", {});
495
+ return;
496
+ }
497
+ const pendingMap = /* @__PURE__ */ new Map();
498
+ for (const order of pendingOrders) {
499
+ const storageKey = this.getOrderStorageKey(order);
500
+ if (!storageKey)
501
+ continue;
502
+ pendingMap.set(storageKey, order);
503
+ }
504
+ if (pendingMap.size === 0) {
505
+ this.logError("upsertPendingSyncOrders-订单缺少可落库标识", {
506
+ count: pendingOrders.length
507
+ });
508
+ return;
509
+ }
510
+ this.logInfo("upsertPendingSyncOrders-开始", { count: pendingMap.size });
511
+ const patchedOrders = [...pendingMap.values()];
512
+ const mergeActions = {};
513
+ const updatedList = this.store.list.map((order) => {
514
+ const storageKey = this.getOrderStorageKey(order);
515
+ if (!storageKey || !pendingMap.has(storageKey))
516
+ return order;
517
+ const pendingOrder = pendingMap.get(storageKey);
518
+ pendingMap.delete(storageKey);
519
+ mergeActions[storageKey] = "update";
520
+ return pendingOrder;
521
+ });
522
+ for (const [storageKey, order] of pendingMap.entries()) {
523
+ mergeActions[storageKey] = "insert";
524
+ updatedList.push(order);
525
+ }
526
+ this.store.list = updatedList;
527
+ this.syncOrdersMap();
528
+ await this.patchOrdersInSQLite(patchedOrders, "upsertPendingSyncOrders", mergeActions);
529
+ this.logInfo("upsertPendingSyncOrders-结束", {
530
+ count: patchedOrders.length,
531
+ storeOrderCountAfter: this.store.list.length
532
+ });
533
+ this.core.effects.emit(import_types.OrderHooks.onOrdersChanged, this.store.list);
534
+ }
487
535
  /**
488
536
  * 通过 SSE 按自定义 query 拉取订单(支持 select/with 精简字段)
489
537
  */
@@ -778,12 +826,32 @@ var OrderModule = class extends import_BaseModule.BaseModule {
778
826
  const id = order == null ? void 0 : order.order_id;
779
827
  if (id === void 0 || id === null)
780
828
  continue;
781
- freshMap.set(this.getIdKey(id), order);
829
+ freshMap.set(this.getIdKey(id), this.normalizeRemoteSyncedOrder(order));
830
+ }
831
+ const freshStorageKeyMap = /* @__PURE__ */ new Map();
832
+ for (const order of freshMap.values()) {
833
+ const storageKey = this.getOrderStorageKey(order);
834
+ if (!storageKey)
835
+ continue;
836
+ freshStorageKeyMap.set(storageKey, order);
782
837
  }
783
838
  const uniqueFreshCount = freshMap.size;
784
839
  const patchedOrders = [...freshMap.values()];
785
840
  const mergeActions = {};
786
841
  const updatedList = this.store.list.map((order) => {
842
+ const storageKey = this.getOrderStorageKey(order);
843
+ if (storageKey && freshStorageKeyMap.has(storageKey)) {
844
+ const fresh2 = freshStorageKeyMap.get(storageKey);
845
+ freshStorageKeyMap.delete(storageKey);
846
+ const freshId = fresh2.order_id;
847
+ if (freshId !== void 0 && freshId !== null) {
848
+ freshMap.delete(this.getIdKey(freshId));
849
+ mergeActions[this.getIdKey(freshId)] = "update";
850
+ } else {
851
+ mergeActions[storageKey] = "update";
852
+ }
853
+ return fresh2;
854
+ }
787
855
  const id = order.order_id;
788
856
  if (id === void 0 || id === null)
789
857
  return order;
@@ -960,6 +1028,33 @@ var OrderModule = class extends import_BaseModule.BaseModule {
960
1028
  return [];
961
1029
  }
962
1030
  }
1031
+ isPendingSyncOrder(order) {
1032
+ return Number(order == null ? void 0 : order.need_sync) === 1;
1033
+ }
1034
+ normalizeRemoteSyncedOrder(order) {
1035
+ if (this.isPendingSyncOrder(order))
1036
+ return order;
1037
+ return {
1038
+ ...order,
1039
+ need_sync: 0
1040
+ };
1041
+ }
1042
+ mergeRemoteSnapshotWithPendingOrders(remoteOrders, existingOrders) {
1043
+ const merged = remoteOrders.map((order) => this.normalizeRemoteSyncedOrder(order));
1044
+ const remoteStorageKeys = new Set(
1045
+ merged.map((order) => this.getOrderStorageKey(order)).filter(Boolean)
1046
+ );
1047
+ for (const order of existingOrders) {
1048
+ if (!this.isPendingSyncOrder(order))
1049
+ continue;
1050
+ const storageKey = this.getOrderStorageKey(order);
1051
+ if (!storageKey || remoteStorageKeys.has(storageKey))
1052
+ continue;
1053
+ merged.push(order);
1054
+ remoteStorageKeys.add(storageKey);
1055
+ }
1056
+ return merged;
1057
+ }
963
1058
  /**
964
1059
  * 串行执行订单 SQLite 写入任务,避免快照替换与增量 upsert 并发交错。
965
1060
  *
@@ -1083,12 +1178,19 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1083
1178
  if (!this.dbManager) {
1084
1179
  return;
1085
1180
  }
1086
- const orderListSnapshot = (0, import_lodash_es.cloneDeep)(orderList);
1181
+ const remoteSnapshot = (0, import_lodash_es.cloneDeep)(orderList);
1087
1182
  try {
1088
1183
  await this.runInOrderSQLiteSaveQueue(async () => {
1184
+ const existingOrders = typeof this.dbManager.getAll === "function" ? await this.dbManager.getAll(INDEXDB_STORE_NAME) : [];
1185
+ const orderListSnapshot = this.mergeRemoteSnapshotWithPendingOrders(
1186
+ remoteSnapshot,
1187
+ existingOrders || []
1188
+ );
1089
1189
  this.logInfo("replaceOrdersSnapshotInSQLite-开始", {
1090
1190
  source,
1091
- count: orderListSnapshot.length
1191
+ count: orderListSnapshot.length,
1192
+ remoteCount: remoteSnapshot.length,
1193
+ preservedPendingCount: orderListSnapshot.length - remoteSnapshot.length
1092
1194
  });
1093
1195
  await this.dbManager.clear(INDEXDB_STORE_NAME);
1094
1196
  this.logInfo("replaceOrdersSnapshotInSQLite-clear完成", {
@@ -1107,7 +1209,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1107
1209
  } catch (error) {
1108
1210
  this.logError("全量保存订单到 SQLite 失败", {
1109
1211
  error: error instanceof Error ? error.message : String(error),
1110
- orderList: orderListSnapshot.length
1212
+ orderList: remoteSnapshot.length
1111
1213
  });
1112
1214
  }
1113
1215
  }
@@ -393,6 +393,8 @@ export interface OrderData {
393
393
  /** 本地辅助字段 */
394
394
  shop_id?: number;
395
395
  create_date?: string;
396
+ /** 本地待同步标记:1 表示需要后续同步到云端 */
397
+ need_sync?: 0 | 1 | number;
396
398
  /** 兼容:部分场景仍使用顶层 total_amount */
397
399
  total_amount?: OrderMoneyString | number;
398
400
  /** 扩展字段(物流、标签等);使用 any 以便筛选与数据源扩展 */
@@ -1301,7 +1301,7 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
1301
1301
  if (!((_b = tempOrder == null ? void 0 : tempOrder.products) == null ? void 0 : _b.length))
1302
1302
  return [];
1303
1303
  const products = (_c = tempOrder == null ? void 0 : tempOrder.products) == null ? void 0 : _c.filter((n) => n.product_id);
1304
- return products.map((product) => {
1304
+ const walletProducts = products.map((product) => {
1305
1305
  const metadata = product.metadata || {};
1306
1306
  return {
1307
1307
  product_id: product.product_id,
@@ -1345,6 +1345,7 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
1345
1345
  })
1346
1346
  };
1347
1347
  });
1348
+ return walletProducts;
1348
1349
  }
1349
1350
  async initWalletData(params) {
1350
1351
  if (!this.store.order)
@@ -126,6 +126,7 @@ export interface BaseSalesSummary {
126
126
  shipping_tax_fee: string;
127
127
  tax_fee: string;
128
128
  surcharge_fee: string;
129
+ surcharges?: any[];
129
130
  discount_amount: string;
130
131
  deposit_amount: string;
131
132
  deposit?: {
@@ -73,15 +73,29 @@ function normalizeBundleItems(bundleItems, preferPriceField = false) {
73
73
  const unitSelling = preferPriceField ? 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";
74
74
  const unitStr = toPriceString(unitSelling, "0.00");
75
75
  const num = toSafePositiveInt((bundleItem == null ? void 0 : bundleItem.num) ?? (bundleItem == null ? void 0 : bundleItem.quantity), 1);
76
- return {
76
+ const magnitudeSource = {
77
+ ...bundleItem,
78
+ option: normalizeOptionItems(bundleItem == null ? void 0 : bundleItem.option),
79
+ bundle_selling_price: unitStr,
80
+ // 计算 markdown 净额时,只传真实毛价;仅有 bundle_selling_price 时按历史净额兜底。
81
+ price: bundleItem.price ?? bundleItem.custom_price
82
+ };
83
+ const normalizedItem = {
77
84
  ...bundleItem,
78
85
  num,
79
86
  quantity: (bundleItem == null ? void 0 : bundleItem.quantity) ?? num,
87
+ // price 保留毛价(markdown 仍为正),由 price_type 决定符号。
80
88
  price: bundleItem.price ?? unitStr,
81
89
  bundle_selling_price: unitStr,
82
90
  original_price: bundleItem.original_price ?? bundleItem.product_price ?? bundleItem.price ?? unitStr,
83
91
  option: normalizeOptionItems(bundleItem == null ? void 0 : bundleItem.option)
84
92
  };
93
+ normalizedItem.bundle_selling_price = (0, import_utils.getBundleSellingMagnitude)(magnitudeSource).toFixed(2);
94
+ if ((normalizedItem.price_type ?? normalizedItem.custom_price_type) === "markdown" && (normalizedItem.price_type_ext === "" || normalizedItem.price_type_ext === void 0 || normalizedItem.price_type_ext === null)) {
95
+ normalizedItem.custom_price = normalizedItem.custom_price ?? normalizedItem.price;
96
+ normalizedItem.price = normalizedItem.bundle_selling_price;
97
+ }
98
+ return normalizedItem;
85
99
  });
86
100
  }
87
101
  function findVariantById(origin, variantId) {
@@ -311,7 +311,7 @@ export declare class BookingByStepImpl extends BaseModule implements Module {
311
311
  date: string;
312
312
  status: string;
313
313
  week: string;
314
- weekNum: 0 | 2 | 1 | 6 | 3 | 4 | 5;
314
+ weekNum: 0 | 1 | 2 | 3 | 5 | 4 | 6;
315
315
  }[]>;
316
316
  submitTimeSlot(timeSlots: TimeSliceItem): void;
317
317
  private getScheduleDataByIds;
@@ -115,6 +115,7 @@ export interface ScanOrderSummary {
115
115
  shipping_tax_fee: string;
116
116
  tax_fee: string;
117
117
  surcharge_fee: string;
118
+ surcharges: any[];
118
119
  discount_amount: string;
119
120
  deposit_amount: string;
120
121
  deposit?: {