@pisell/pisellos 2.2.198 → 2.2.200
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/BookingTicket/index.d.ts +23 -0
- package/dist/solution/BookingTicket/index.js +291 -196
- package/dist/solution/BookingTicket/utils/bookingStatus.d.ts +40 -0
- package/dist/solution/BookingTicket/utils/bookingStatus.js +70 -0
- 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/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/BookingTicket/index.d.ts +23 -0
- package/lib/solution/BookingTicket/index.js +60 -0
- package/lib/solution/BookingTicket/utils/bookingStatus.d.ts +40 -0
- package/lib/solution/BookingTicket/utils/bookingStatus.js +44 -2
- 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
|
@@ -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) {
|
|
@@ -12,6 +12,8 @@ import { AddProductDecideContext, AddProductRequiresDetailPayload } from './util
|
|
|
12
12
|
import { type BuildCacheItemFromOrderLineInput } from '../../modules/BookingContext/utils/buildCacheItemFromOrderLine';
|
|
13
13
|
import { type BuildNormalProductCacheItemFromOrderLineInput } from '../../modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine';
|
|
14
14
|
import type { AddProductBookingInput, OrderProduct, OrderProductIdentity, UpdateOrderProductParams } from '../../modules/Order/types';
|
|
15
|
+
import { type TransitionChildBookingParams } from './utils/bookingStatus';
|
|
16
|
+
export type { BookingTransitionAction, TransitionChildBookingParams, } from './utils/bookingStatus';
|
|
15
17
|
export declare class BookingTicketImpl extends BaseSalesImpl implements Module {
|
|
16
18
|
protected defaultName: string;
|
|
17
19
|
protected defaultVersion: string;
|
|
@@ -68,6 +70,27 @@ export declare class BookingTicketImpl extends BaseSalesImpl implements Module {
|
|
|
68
70
|
* @returns 接口返回的 data
|
|
69
71
|
*/
|
|
70
72
|
setBookingStatus(status: string): Promise<any>;
|
|
73
|
+
/**
|
|
74
|
+
* 子预约状态机转移(POST /schedule/booking-transition/{schedule_event_id})。
|
|
75
|
+
*
|
|
76
|
+
* 与 `setBookingStatus`(父预约 PUT appointment-status)不同,本方法按子预约
|
|
77
|
+
* `schedule_event_id` 调用状态转移接口,body 为 `{ action }`。
|
|
78
|
+
*
|
|
79
|
+
* 流程:能解析出目标状态时先乐观更新对应子预约的 `appointment_status`,
|
|
80
|
+
* 再调 POST;成功后强制刷新销售详情;失败则回滚该子预约本地状态并抛错。
|
|
81
|
+
* `reschedule` 暂未启用状态矩阵,跳过乐观更新仅调接口。
|
|
82
|
+
*
|
|
83
|
+
* @param params.schedule_event_id 子预约 schedule_event_id
|
|
84
|
+
* @param params.action 转移动作:confirm | reject | arrive | start | complete | cancel | no_show | reschedule
|
|
85
|
+
* @returns 接口返回的 data
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* await bookingTicket.transitionChildBooking({
|
|
89
|
+
* schedule_event_id: 301,
|
|
90
|
+
* action: 'confirm',
|
|
91
|
+
* });
|
|
92
|
+
*/
|
|
93
|
+
transitionChildBooking(params: TransitionChildBookingParams): Promise<any>;
|
|
71
94
|
/**
|
|
72
95
|
* 基于当前 tempOrder.order_id 强制从远端重新拉取销售详情。
|
|
73
96
|
*
|
|
@@ -274,6 +274,66 @@ var BookingTicketImpl = class extends import_BaseSales.BaseSalesImpl {
|
|
|
274
274
|
throw error;
|
|
275
275
|
}
|
|
276
276
|
}
|
|
277
|
+
/**
|
|
278
|
+
* 子预约状态机转移(POST /schedule/booking-transition/{schedule_event_id})。
|
|
279
|
+
*
|
|
280
|
+
* 与 `setBookingStatus`(父预约 PUT appointment-status)不同,本方法按子预约
|
|
281
|
+
* `schedule_event_id` 调用状态转移接口,body 为 `{ action }`。
|
|
282
|
+
*
|
|
283
|
+
* 流程:能解析出目标状态时先乐观更新对应子预约的 `appointment_status`,
|
|
284
|
+
* 再调 POST;成功后强制刷新销售详情;失败则回滚该子预约本地状态并抛错。
|
|
285
|
+
* `reschedule` 暂未启用状态矩阵,跳过乐观更新仅调接口。
|
|
286
|
+
*
|
|
287
|
+
* @param params.schedule_event_id 子预约 schedule_event_id
|
|
288
|
+
* @param params.action 转移动作:confirm | reject | arrive | start | complete | cancel | no_show | reschedule
|
|
289
|
+
* @returns 接口返回的 data
|
|
290
|
+
*
|
|
291
|
+
* @example
|
|
292
|
+
* await bookingTicket.transitionChildBooking({
|
|
293
|
+
* schedule_event_id: 301,
|
|
294
|
+
* action: 'confirm',
|
|
295
|
+
* });
|
|
296
|
+
*/
|
|
297
|
+
async transitionChildBooking(params) {
|
|
298
|
+
var _a, _b;
|
|
299
|
+
const { schedule_event_id, action } = params;
|
|
300
|
+
if (schedule_event_id === void 0 || schedule_event_id === null || schedule_event_id === "") {
|
|
301
|
+
throw new Error("transitionChildBooking: schedule_event_id 不能为空");
|
|
302
|
+
}
|
|
303
|
+
if (!action) {
|
|
304
|
+
throw new Error("transitionChildBooking: action 不能为空");
|
|
305
|
+
}
|
|
306
|
+
const tempOrder = (_b = (_a = this.store.order) == null ? void 0 : _a.getTempOrder) == null ? void 0 : _b.call(_a);
|
|
307
|
+
if (!tempOrder) {
|
|
308
|
+
throw new Error("transitionChildBooking: tempOrder 未加载");
|
|
309
|
+
}
|
|
310
|
+
const orderId = tempOrder.order_id;
|
|
311
|
+
if (orderId === void 0 || orderId === null) {
|
|
312
|
+
throw new Error("transitionChildBooking: tempOrder.order_id 缺失");
|
|
313
|
+
}
|
|
314
|
+
const bookings = Array.isArray(tempOrder.bookings) ? tempOrder.bookings : [];
|
|
315
|
+
const targetStatus = (0, import_bookingStatus.resolveStatusAfterTransition)(action);
|
|
316
|
+
const previous = targetStatus !== null ? (0, import_bookingStatus.applyChildBookingStatus)(bookings, schedule_event_id, targetStatus) : void 0;
|
|
317
|
+
try {
|
|
318
|
+
const res = await this.request.post(
|
|
319
|
+
`/schedule/booking-transition/${schedule_event_id}`,
|
|
320
|
+
{ action }
|
|
321
|
+
);
|
|
322
|
+
await this.refreshSalesDetail();
|
|
323
|
+
this.core.effects.emit(`${this.name}:onChildBookingStatusChange`, {
|
|
324
|
+
orderId,
|
|
325
|
+
schedule_event_id,
|
|
326
|
+
action,
|
|
327
|
+
status: targetStatus
|
|
328
|
+
});
|
|
329
|
+
return (res == null ? void 0 : res.data) ?? res;
|
|
330
|
+
} catch (error) {
|
|
331
|
+
if (previous !== void 0) {
|
|
332
|
+
(0, import_bookingStatus.restoreChildBookingStatus)(bookings, schedule_event_id, previous);
|
|
333
|
+
}
|
|
334
|
+
throw error;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
277
337
|
/**
|
|
278
338
|
* 基于当前 tempOrder.order_id 强制从远端重新拉取销售详情。
|
|
279
339
|
*
|