@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.
- package/dist/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +7 -3
- package/dist/modules/Cart/utils/cartProduct.js +11 -1
- package/dist/modules/Cart/utils/changePrice.js +11 -11
- package/dist/modules/Order/index.js +155 -104
- package/dist/modules/Order/types.d.ts +1 -0
- package/dist/modules/Order/utils.d.ts +33 -2
- package/dist/modules/Order/utils.js +112 -55
- package/dist/modules/SalesSummary/utils.js +97 -63
- package/dist/server/index.d.ts +3 -0
- package/dist/server/index.js +294 -190
- package/dist/server/modules/order/index.d.ts +8 -0
- package/dist/server/modules/order/index.js +536 -317
- package/dist/server/modules/order/types.d.ts +2 -0
- package/dist/solution/BaseSales/index.js +2 -1
- package/dist/solution/BaseSales/types.d.ts +1 -0
- package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +19 -4
- package/dist/solution/BookingByStep/index.d.ts +1 -1
- package/dist/solution/ScanOrder/types.d.ts +1 -0
- package/dist/solution/ScanOrder/utils.js +26 -9
- package/dist/solution/VenueBooking/index.js +2 -1
- package/lib/model/strategy/adapter/promotion/index.js +49 -0
- package/lib/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +4 -1
- package/lib/modules/Cart/utils/cartProduct.js +11 -1
- package/lib/modules/Cart/utils/changePrice.js +12 -20
- package/lib/modules/Order/index.js +58 -6
- package/lib/modules/Order/types.d.ts +1 -0
- package/lib/modules/Order/utils.d.ts +33 -2
- package/lib/modules/Order/utils.js +44 -9
- package/lib/modules/SalesSummary/utils.js +41 -44
- package/lib/server/index.d.ts +3 -0
- package/lib/server/index.js +82 -3
- package/lib/server/modules/order/index.d.ts +8 -0
- package/lib/server/modules/order/index.js +106 -4
- package/lib/server/modules/order/types.d.ts +2 -0
- package/lib/solution/BaseSales/index.js +2 -1
- package/lib/solution/BaseSales/types.d.ts +1 -0
- package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +15 -1
- package/lib/solution/BookingByStep/index.d.ts +1 -1
- package/lib/solution/ScanOrder/types.d.ts +1 -0
- package/lib/solution/ScanOrder/utils.js +28 -10
- package/lib/solution/VenueBooking/index.js +2 -1
- 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
|
|
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(
|
|
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 =
|
|
476
|
-
|
|
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
|
|
570
|
-
//
|
|
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
|
|
82
|
-
return sum.plus(
|
|
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
|
|
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 =
|
|
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
|
|
809
|
-
|
|
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
|
|
828
|
-
const
|
|
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
|
|
832
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
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
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
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
|
-
|
|
911
|
-
|
|
912
|
-
|
|
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 },
|
package/lib/server/index.d.ts
CHANGED
|
@@ -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
|
*/
|
package/lib/server/index.js
CHANGED
|
@@ -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 {
|
|
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
|
-
|
|
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
|
|
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
|
|
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:
|
|
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
|
-
|
|
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)
|
|
@@ -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
|
-
|
|
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 |
|
|
314
|
+
weekNum: 0 | 1 | 2 | 3 | 5 | 4 | 6;
|
|
315
315
|
}[]>;
|
|
316
316
|
submitTimeSlot(timeSlots: TimeSliceItem): void;
|
|
317
317
|
private getScheduleDataByIds;
|