@pisell/pisellos 2.2.302 → 2.2.303
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/model/strategy/adapter/dataVariant/evaluator.d.ts +18 -2
- package/dist/model/strategy/adapter/dataVariant/evaluator.js +287 -101
- package/dist/model/strategy/adapter/dataVariant/type.d.ts +25 -0
- package/dist/model/strategy/adapter/itemRule/adapter.d.ts +4 -1
- package/dist/model/strategy/adapter/itemRule/adapter.js +135 -35
- package/dist/model/strategy/adapter/itemRule/evaluator.d.ts +8 -1
- package/dist/model/strategy/adapter/itemRule/evaluator.js +25 -1
- package/dist/model/strategy/adapter/itemRule/type.d.ts +44 -0
- package/dist/model/strategy/adapter/itemRule/type.js +19 -1
- package/dist/model/strategy/adapter/promotion/index.js +0 -9
- package/dist/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +10 -7
- package/dist/modules/Discount/index.d.ts +2 -0
- package/dist/modules/Discount/index.js +41 -5
- package/dist/modules/Discount/types.d.ts +21 -0
- package/dist/modules/OpenData/index.d.ts +15 -2
- package/dist/modules/OpenData/index.js +307 -19
- package/dist/modules/OpenData/types.d.ts +55 -0
- package/dist/modules/OpenData/types.js +7 -1
- package/dist/modules/OpenData/utils.d.ts +21 -1
- package/dist/modules/OpenData/utils.js +138 -0
- package/dist/modules/Order/index.d.ts +50 -7
- package/dist/modules/Order/index.js +1690 -815
- package/dist/modules/Order/salesNotes.d.ts +31 -0
- package/dist/modules/Order/salesNotes.js +492 -0
- package/dist/modules/Order/types.d.ts +117 -10
- package/dist/modules/Order/types.js +6 -1
- package/dist/modules/Order/utils/virtualSettlement.d.ts +63 -0
- package/dist/modules/Order/utils/virtualSettlement.js +284 -0
- package/dist/modules/Order/utils.d.ts +5 -1
- package/dist/modules/Order/utils.js +138 -33
- package/dist/modules/Payment/walletpass.js +20 -21
- package/dist/modules/Product/types.d.ts +23 -0
- package/dist/modules/ProductList/index.d.ts +2 -1
- package/dist/modules/ProductList/index.js +100 -20
- package/dist/modules/ProductList/types.d.ts +10 -0
- package/dist/modules/Rules/index.d.ts +5 -0
- package/dist/modules/Rules/index.js +30 -14
- package/dist/modules/SalesSummary/index.d.ts +1 -0
- package/dist/modules/SalesSummary/index.js +4 -3
- package/dist/modules/SalesSummary/types.d.ts +1 -0
- package/dist/modules/SalesSummary/utils.d.ts +1 -0
- package/dist/modules/SalesSummary/utils.js +21 -1
- package/dist/plugins/request.d.ts +2 -1
- package/dist/plugins/request.js +4 -2
- package/dist/server/index.d.ts +21 -0
- package/dist/server/index.js +1774 -1285
- package/dist/server/modules/order/types.d.ts +50 -0
- package/dist/server/modules/order/types.js +2 -0
- package/dist/server/utils/small-ticket.d.ts +4 -1
- package/dist/server/utils/small-ticket.js +461 -96
- package/dist/solution/BaseSales/index.d.ts +24 -3
- package/dist/solution/BaseSales/index.js +1484 -873
- package/dist/solution/BaseSales/utils/parseSalesResponse.d.ts +2 -0
- package/dist/solution/BaseSales/utils/parseSalesResponse.js +1 -1
- package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.d.ts +1 -0
- package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +72 -9
- package/dist/solution/BookingByStep/index.d.ts +1 -1
- package/dist/solution/BookingTicket/index.d.ts +22 -5
- package/dist/solution/BookingTicket/index.js +558 -443
- package/dist/solution/BookingTicket/utils/addProductDecision.js +2 -1
- package/dist/solution/BookingTicket/utils/orderCustomer.js +3 -2
- package/dist/solution/RegisterAndLogin/config.d.ts +9 -3
- package/dist/solution/RegisterAndLogin/config.js +95 -40
- package/dist/solution/RegisterAndLogin/index.js +4 -1
- package/dist/solution/Sales/index.d.ts +16 -1
- package/dist/solution/Sales/index.js +338 -63
- package/dist/solution/Sales/types.d.ts +10 -0
- package/dist/solution/ScanOrder/index.d.ts +1 -0
- package/dist/solution/ScanOrder/index.js +31 -27
- package/dist/solution/ScanOrder/utils.d.ts +1 -0
- package/dist/solution/ScanOrder/utils.js +2 -0
- package/dist/solution/UnifiedBookingSales/index.d.ts +20 -6
- package/dist/solution/UnifiedBookingSales/index.js +874 -527
- package/dist/solution/UnifiedBookingSales/types.d.ts +21 -2
- package/dist/solution/VenueBooking/index.d.ts +1 -0
- package/dist/solution/VenueBooking/index.js +8 -4
- package/lib/model/strategy/adapter/dataVariant/evaluator.d.ts +18 -2
- package/lib/model/strategy/adapter/dataVariant/evaluator.js +137 -6
- package/lib/model/strategy/adapter/dataVariant/type.d.ts +25 -0
- package/lib/model/strategy/adapter/itemRule/adapter.d.ts +4 -1
- package/lib/model/strategy/adapter/itemRule/adapter.js +73 -4
- package/lib/model/strategy/adapter/itemRule/evaluator.d.ts +8 -1
- package/lib/model/strategy/adapter/itemRule/evaluator.js +23 -1
- package/lib/model/strategy/adapter/itemRule/type.d.ts +44 -0
- package/lib/model/strategy/adapter/itemRule/type.js +19 -1
- package/lib/model/strategy/adapter/promotion/index.js +1 -46
- package/lib/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +4 -0
- package/lib/modules/Discount/index.d.ts +2 -0
- package/lib/modules/Discount/index.js +34 -2
- package/lib/modules/Discount/types.d.ts +21 -0
- package/lib/modules/OpenData/index.d.ts +15 -2
- package/lib/modules/OpenData/index.js +230 -3
- package/lib/modules/OpenData/types.d.ts +55 -0
- package/lib/modules/OpenData/types.js +9 -1
- package/lib/modules/OpenData/utils.d.ts +21 -1
- package/lib/modules/OpenData/utils.js +126 -0
- package/lib/modules/Order/index.d.ts +50 -7
- package/lib/modules/Order/index.js +704 -64
- package/lib/modules/Order/salesNotes.d.ts +31 -0
- package/lib/modules/Order/salesNotes.js +382 -0
- package/lib/modules/Order/types.d.ts +117 -10
- package/lib/modules/Order/types.js +5 -1
- package/lib/modules/Order/utils/virtualSettlement.d.ts +63 -0
- package/lib/modules/Order/utils/virtualSettlement.js +262 -0
- package/lib/modules/Order/utils.d.ts +5 -1
- package/lib/modules/Order/utils.js +127 -21
- package/lib/modules/Payment/walletpass.js +14 -11
- package/lib/modules/Product/types.d.ts +23 -0
- package/lib/modules/ProductList/index.d.ts +2 -1
- package/lib/modules/ProductList/index.js +47 -2
- package/lib/modules/ProductList/types.d.ts +10 -0
- package/lib/modules/Rules/index.d.ts +5 -0
- package/lib/modules/Rules/index.js +22 -12
- package/lib/modules/SalesSummary/index.d.ts +1 -0
- package/lib/modules/SalesSummary/index.js +4 -2
- package/lib/modules/SalesSummary/types.d.ts +1 -0
- package/lib/modules/SalesSummary/utils.d.ts +1 -0
- package/lib/modules/SalesSummary/utils.js +17 -1
- package/lib/plugins/request.d.ts +2 -1
- package/lib/plugins/request.js +3 -2
- package/lib/server/index.d.ts +21 -0
- package/lib/server/index.js +492 -108
- package/lib/server/modules/order/types.d.ts +50 -0
- package/lib/server/modules/order/types.js +1 -0
- package/lib/server/utils/small-ticket.d.ts +4 -1
- package/lib/server/utils/small-ticket.js +427 -72
- package/lib/solution/BaseSales/index.d.ts +24 -3
- package/lib/solution/BaseSales/index.js +433 -30
- package/lib/solution/BaseSales/utils/parseSalesResponse.d.ts +2 -0
- package/lib/solution/BaseSales/utils/parseSalesResponse.js +1 -1
- package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.d.ts +1 -0
- package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +38 -0
- package/lib/solution/BookingByStep/index.d.ts +1 -1
- package/lib/solution/BookingTicket/index.d.ts +22 -5
- package/lib/solution/BookingTicket/index.js +60 -15
- package/lib/solution/BookingTicket/utils/addProductDecision.js +1 -0
- package/lib/solution/BookingTicket/utils/orderCustomer.js +2 -1
- package/lib/solution/RegisterAndLogin/config.d.ts +9 -3
- package/lib/solution/RegisterAndLogin/config.js +49 -8
- package/lib/solution/RegisterAndLogin/index.js +4 -1
- package/lib/solution/Sales/index.d.ts +16 -1
- package/lib/solution/Sales/index.js +168 -1
- package/lib/solution/Sales/types.d.ts +10 -0
- package/lib/solution/ScanOrder/index.d.ts +1 -0
- package/lib/solution/ScanOrder/index.js +5 -1
- package/lib/solution/ScanOrder/utils.d.ts +1 -0
- package/lib/solution/ScanOrder/utils.js +1 -0
- package/lib/solution/UnifiedBookingSales/index.d.ts +20 -6
- package/lib/solution/UnifiedBookingSales/index.js +169 -16
- package/lib/solution/UnifiedBookingSales/types.d.ts +21 -2
- package/lib/solution/VenueBooking/index.d.ts +1 -0
- package/lib/solution/VenueBooking/index.js +5 -1
- package/package.json +1 -1
package/lib/server/index.js
CHANGED
|
@@ -27,6 +27,9 @@ var _filterBookings = require("./modules/order/utils/filterBookings");
|
|
|
27
27
|
var _smallTicket = require("./utils/small-ticket");
|
|
28
28
|
var _BaseSales = require("../solution/BaseSales");
|
|
29
29
|
var _paymentUtils = require("../modules/Order/payment-utils");
|
|
30
|
+
var _salesNotes = require("../modules/Order/salesNotes");
|
|
31
|
+
var _orderCollectionIdentity = require("../modules/Order/utils/orderCollectionIdentity");
|
|
32
|
+
var _OpenData = require("../modules/OpenData");
|
|
30
33
|
var _modules = require("./modules");
|
|
31
34
|
Object.keys(_modules).forEach(function (key) {
|
|
32
35
|
if (key === "default" || key === "__esModule") return;
|
|
@@ -41,6 +44,8 @@ Object.keys(_modules).forEach(function (key) {
|
|
|
41
44
|
});
|
|
42
45
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
43
46
|
const OS_FULFILLMENT_REF_MODE_FIELD = '_os_fulfillment_ref_mode';
|
|
47
|
+
const OS_INVOICE_LAYOUT_CONFIG_FIELD = '_os_invoice_layout_config';
|
|
48
|
+
const OS_INVOICE_TEMPLATE_SETTINGS_FIELD = '_os_invoice_template_settings';
|
|
44
49
|
|
|
45
50
|
// 重新导出类型供外部使用
|
|
46
51
|
|
|
@@ -572,7 +577,16 @@ class Server {
|
|
|
572
577
|
async handleSyncSalesTask(ctx) {
|
|
573
578
|
const payload = ctx.task?.payload || {};
|
|
574
579
|
const backendPath = payload.backendPath;
|
|
575
|
-
const
|
|
580
|
+
const extractedInvoice = this.extractInvoiceLayoutConfig(payload.data, {
|
|
581
|
+
includePublicInvoice: true
|
|
582
|
+
});
|
|
583
|
+
const extractedTemplateSettings = this.extractInvoiceTemplateSettings(extractedInvoice.data, {
|
|
584
|
+
includePublicTemplateSettings: true
|
|
585
|
+
});
|
|
586
|
+
const data = extractedTemplateSettings.data;
|
|
587
|
+
const rawInvoiceLayoutConfig = payload.invoice ?? extractedInvoice.invoiceLayoutConfig;
|
|
588
|
+
const invoiceLayoutConfig = rawInvoiceLayoutConfig === undefined || rawInvoiceLayoutConfig === null ? undefined : this.normalizePrintInvoiceLayoutConfig(rawInvoiceLayoutConfig);
|
|
589
|
+
const invoiceTemplateSettings = this.normalizePrintInvoiceTemplateSettings(payload.template_settings ?? extractedTemplateSettings.invoiceTemplateSettings);
|
|
576
590
|
const title = payload.title || 'sync_sales';
|
|
577
591
|
if (!backendPath || !data) {
|
|
578
592
|
return this.taskFail('INVALID_PAYLOAD', '缺少 backendPath 或 data');
|
|
@@ -588,7 +602,9 @@ class Server {
|
|
|
588
602
|
title,
|
|
589
603
|
externalSaleNumber: payload.external_sale_number,
|
|
590
604
|
deviceId: ctx.task?.device_id,
|
|
591
|
-
persistCheckoutDataWithResponse: false
|
|
605
|
+
persistCheckoutDataWithResponse: false,
|
|
606
|
+
invoiceLayoutConfig,
|
|
607
|
+
invoiceTemplateSettings
|
|
592
608
|
});
|
|
593
609
|
if (syncResult.rejected) {
|
|
594
610
|
this.logError(`${title}: sync_sales 后端明确失败`, {
|
|
@@ -627,7 +643,7 @@ class Server {
|
|
|
627
643
|
} = params;
|
|
628
644
|
const normalizedData = await this.normalizeCheckoutSubmitData(params.data, title);
|
|
629
645
|
const numberedData = await this.ensureCheckoutOrderNumbers(normalizedData, title);
|
|
630
|
-
const checkoutData = this.applyCheckoutFulfillmentBuzzer(numberedData, title);
|
|
646
|
+
const checkoutData = this.normalizeCheckoutCollections(this.applyCheckoutFulfillmentBuzzer(numberedData, title));
|
|
631
647
|
const remoteCheckoutData = this.buildRemoteCheckoutData(checkoutData, title);
|
|
632
648
|
const deviceHeaders = await this.buildCheckoutDeviceHeaders(title);
|
|
633
649
|
if (!this.app?.request?.post) {
|
|
@@ -675,7 +691,9 @@ class Server {
|
|
|
675
691
|
checkoutData,
|
|
676
692
|
syncedOrder: printableSyncedOrder,
|
|
677
693
|
response,
|
|
678
|
-
deviceId: params.deviceId
|
|
694
|
+
deviceId: params.deviceId,
|
|
695
|
+
invoiceLayoutConfig: params.invoiceLayoutConfig,
|
|
696
|
+
invoiceTemplateSettings: params.invoiceTemplateSettings
|
|
679
697
|
});
|
|
680
698
|
}
|
|
681
699
|
return {
|
|
@@ -701,6 +719,7 @@ class Server {
|
|
|
701
719
|
if (!next || typeof next !== 'object') return next;
|
|
702
720
|
const record = next;
|
|
703
721
|
delete record[OS_FULFILLMENT_REF_MODE_FIELD];
|
|
722
|
+
delete record[OS_INVOICE_LAYOUT_CONFIG_FIELD];
|
|
704
723
|
this.applyKioskCheckoutOperator(record);
|
|
705
724
|
if (!Array.isArray(record.payments)) return next;
|
|
706
725
|
const originalPayments = record.payments;
|
|
@@ -1302,6 +1321,10 @@ class Server {
|
|
|
1302
1321
|
method: 'post',
|
|
1303
1322
|
path: '/shop/local/print-order',
|
|
1304
1323
|
handler: this.handleLocalPrintOrder.bind(this)
|
|
1324
|
+
}, {
|
|
1325
|
+
method: 'post',
|
|
1326
|
+
path: '/shop/local/order-receipt-snapshot',
|
|
1327
|
+
handler: this.handleLocalOrderReceiptSnapshot.bind(this)
|
|
1305
1328
|
}, {
|
|
1306
1329
|
method: 'post',
|
|
1307
1330
|
path: '/shop/local/getLocalPayment',
|
|
@@ -2151,10 +2174,12 @@ class Server {
|
|
|
2151
2174
|
|
|
2152
2175
|
/**
|
|
2153
2176
|
* 预约查询是否走本地:
|
|
2177
|
+
* - 日历周/月/资源视图通过 compact_fields 显式标识,始终走远端;
|
|
2154
2178
|
* - 当 sales_time_between 的开始和结束都不是今天时,走远端;
|
|
2155
2179
|
* - 其余情况走本地。
|
|
2156
2180
|
*/
|
|
2157
2181
|
shouldUseLocalBookingQuery(data) {
|
|
2182
|
+
if (data?.compact_fields === true) return false;
|
|
2158
2183
|
const range = data?.sales_time_between;
|
|
2159
2184
|
if (!Array.isArray(range) || range.length < 2) return true;
|
|
2160
2185
|
return !this.isRangeBothNotToday(range[0], range[1]);
|
|
@@ -3426,6 +3451,80 @@ class Server {
|
|
|
3426
3451
|
isLocalCheckoutOrderId(orderId) {
|
|
3427
3452
|
return typeof orderId === 'string' && orderId.startsWith('LOCAL_');
|
|
3428
3453
|
}
|
|
3454
|
+
normalizePrintInvoiceLayoutConfig(value) {
|
|
3455
|
+
if (value === undefined || value === null) {
|
|
3456
|
+
return (0, _OpenData.getDefaultInvoiceLayoutConfig)();
|
|
3457
|
+
}
|
|
3458
|
+
let parsed = value;
|
|
3459
|
+
if (typeof parsed === 'string') {
|
|
3460
|
+
try {
|
|
3461
|
+
parsed = JSON.parse(parsed);
|
|
3462
|
+
} catch {
|
|
3463
|
+
return (0, _OpenData.getDefaultInvoiceLayoutConfig)();
|
|
3464
|
+
}
|
|
3465
|
+
}
|
|
3466
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
3467
|
+
return (0, _OpenData.getDefaultInvoiceLayoutConfig)();
|
|
3468
|
+
}
|
|
3469
|
+
const record = parsed;
|
|
3470
|
+
if (!Array.isArray(record.module)) {
|
|
3471
|
+
return (0, _OpenData.getDefaultInvoiceLayoutConfig)();
|
|
3472
|
+
}
|
|
3473
|
+
return {
|
|
3474
|
+
...(typeof record.locale === 'string' ? {
|
|
3475
|
+
locale: record.locale
|
|
3476
|
+
} : {}),
|
|
3477
|
+
module: record.module.map(moduleConfig => moduleConfig && typeof moduleConfig === 'object' && !Array.isArray(moduleConfig) ? {
|
|
3478
|
+
...moduleConfig
|
|
3479
|
+
} : moduleConfig)
|
|
3480
|
+
};
|
|
3481
|
+
}
|
|
3482
|
+
extractInvoiceLayoutConfig(data, options) {
|
|
3483
|
+
if (!data || typeof data !== 'object' || Array.isArray(data)) {
|
|
3484
|
+
return {
|
|
3485
|
+
data
|
|
3486
|
+
};
|
|
3487
|
+
}
|
|
3488
|
+
const record = data;
|
|
3489
|
+
const rawInvoiceLayoutConfig = options?.includePublicInvoice ? record.invoice ?? record[OS_INVOICE_LAYOUT_CONFIG_FIELD] : record[OS_INVOICE_LAYOUT_CONFIG_FIELD];
|
|
3490
|
+
const next = {
|
|
3491
|
+
...record
|
|
3492
|
+
};
|
|
3493
|
+
delete next[OS_INVOICE_LAYOUT_CONFIG_FIELD];
|
|
3494
|
+
if (options?.includePublicInvoice) delete next.invoice;
|
|
3495
|
+
return {
|
|
3496
|
+
data: next,
|
|
3497
|
+
...(rawInvoiceLayoutConfig !== undefined && rawInvoiceLayoutConfig !== null ? {
|
|
3498
|
+
invoiceLayoutConfig: this.normalizePrintInvoiceLayoutConfig(rawInvoiceLayoutConfig)
|
|
3499
|
+
} : {})
|
|
3500
|
+
};
|
|
3501
|
+
}
|
|
3502
|
+
normalizePrintInvoiceTemplateSettings(value) {
|
|
3503
|
+
if (value === undefined || value === null) {
|
|
3504
|
+
return (0, _OpenData.getDefaultInvoiceTemplateSettings)();
|
|
3505
|
+
}
|
|
3506
|
+
return (0, _OpenData.normalizeRawInvoiceTemplateSettings)(value);
|
|
3507
|
+
}
|
|
3508
|
+
extractInvoiceTemplateSettings(data, options) {
|
|
3509
|
+
if (!data || typeof data !== 'object' || Array.isArray(data)) {
|
|
3510
|
+
return {
|
|
3511
|
+
data
|
|
3512
|
+
};
|
|
3513
|
+
}
|
|
3514
|
+
const record = data;
|
|
3515
|
+
const rawInvoiceTemplateSettings = options?.includePublicTemplateSettings ? record.template_settings ?? record[OS_INVOICE_TEMPLATE_SETTINGS_FIELD] : record[OS_INVOICE_TEMPLATE_SETTINGS_FIELD];
|
|
3516
|
+
const next = {
|
|
3517
|
+
...record
|
|
3518
|
+
};
|
|
3519
|
+
delete next[OS_INVOICE_TEMPLATE_SETTINGS_FIELD];
|
|
3520
|
+
if (options?.includePublicTemplateSettings) delete next.template_settings;
|
|
3521
|
+
return {
|
|
3522
|
+
data: next,
|
|
3523
|
+
...(rawInvoiceTemplateSettings !== undefined && rawInvoiceTemplateSettings !== null ? {
|
|
3524
|
+
invoiceTemplateSettings: this.normalizePrintInvoiceTemplateSettings(rawInvoiceTemplateSettings)
|
|
3525
|
+
} : {})
|
|
3526
|
+
};
|
|
3527
|
+
}
|
|
3429
3528
|
applyCheckoutFulfillmentBuzzer(data, title) {
|
|
3430
3529
|
if (!data || typeof data !== 'object') return data;
|
|
3431
3530
|
const next = {
|
|
@@ -3587,14 +3686,16 @@ class Server {
|
|
|
3587
3686
|
};
|
|
3588
3687
|
const needsShopOrderNumber = next.shop_order_number === undefined || next.shop_order_number === null || next.shop_order_number === '';
|
|
3589
3688
|
const needsShopFullOrderNumber = next.shop_full_order_number === undefined || next.shop_full_order_number === null || next.shop_full_order_number === '';
|
|
3590
|
-
if (!needsShopOrderNumber && !needsShopFullOrderNumber)
|
|
3689
|
+
if (!needsShopOrderNumber && !needsShopFullOrderNumber) {
|
|
3690
|
+
return this.finalizeCheckoutSalesNotes(next, title);
|
|
3691
|
+
}
|
|
3591
3692
|
const idGenerator = this.getIdGeneratorPlugin();
|
|
3592
3693
|
if (!idGenerator) {
|
|
3593
3694
|
this.logWarning(`${title}: idGenerator 插件不可用,无法生成订单号`, {
|
|
3594
3695
|
needsShopOrderNumber,
|
|
3595
3696
|
needsShopFullOrderNumber
|
|
3596
3697
|
});
|
|
3597
|
-
return next;
|
|
3698
|
+
return this.finalizeCheckoutSalesNotes(next, title);
|
|
3598
3699
|
}
|
|
3599
3700
|
try {
|
|
3600
3701
|
if (needsShopOrderNumber && idGenerator.generateShopOrderNumber) {
|
|
@@ -3616,7 +3717,38 @@ class Server {
|
|
|
3616
3717
|
error: error instanceof Error ? error.message : String(error)
|
|
3617
3718
|
});
|
|
3618
3719
|
}
|
|
3619
|
-
return next;
|
|
3720
|
+
return this.finalizeCheckoutSalesNotes(next, title);
|
|
3721
|
+
}
|
|
3722
|
+
|
|
3723
|
+
/**
|
|
3724
|
+
* Notes 允许在本地草稿阶段暂存空订单目标;checkout 的统一出口负责用最终
|
|
3725
|
+
* shop_order_number 完成绑定并校验,保证后端请求、pending、设备任务和打印
|
|
3726
|
+
* 都使用同一份完整快照。
|
|
3727
|
+
*/
|
|
3728
|
+
finalizeCheckoutSalesNotes(data, title) {
|
|
3729
|
+
if (!data || typeof data !== 'object') return data;
|
|
3730
|
+
if (!Object.prototype.hasOwnProperty.call(data, 'notes')) return data;
|
|
3731
|
+
if (data.notes === undefined || data.notes === null) return data;
|
|
3732
|
+
if (!Array.isArray(data.notes)) {
|
|
3733
|
+
throw new Error('[Order Notes] checkout notes 必须为数组');
|
|
3734
|
+
}
|
|
3735
|
+
if (data.notes.length === 0) return data;
|
|
3736
|
+
const productTargetUuids = (Array.isArray(data.products) ? data.products : []).map(product => product?.metadata?.unique_identification_number ?? product?.unique_identification_number).filter(targetUuid => targetUuid !== undefined && targetUuid !== null && String(targetUuid).trim().length > 0).map(targetUuid => String(targetUuid));
|
|
3737
|
+
const shopOrderNumber = String(data.shop_order_number ?? '').trim();
|
|
3738
|
+
const notes = (0, _salesNotes.finalizeSalesNotesForCheckout)({
|
|
3739
|
+
notes: data.notes,
|
|
3740
|
+
shopOrderNumber,
|
|
3741
|
+
productTargetUuids
|
|
3742
|
+
});
|
|
3743
|
+
this.logInfo(`${title}: checkout Notes 已绑定订单号`, {
|
|
3744
|
+
shop_order_number: shopOrderNumber,
|
|
3745
|
+
note_count: notes.length
|
|
3746
|
+
});
|
|
3747
|
+
return {
|
|
3748
|
+
...data,
|
|
3749
|
+
shop_order_number: shopOrderNumber,
|
|
3750
|
+
notes
|
|
3751
|
+
};
|
|
3620
3752
|
}
|
|
3621
3753
|
async dispatchCheckoutSyncTask(params) {
|
|
3622
3754
|
this.registerDeviceTaskActions();
|
|
@@ -3634,6 +3766,8 @@ class Server {
|
|
|
3634
3766
|
payload: {
|
|
3635
3767
|
backendPath: params.backendPath,
|
|
3636
3768
|
data: params.data,
|
|
3769
|
+
invoice: this.normalizePrintInvoiceLayoutConfig(params.invoiceLayoutConfig),
|
|
3770
|
+
template_settings: this.normalizePrintInvoiceTemplateSettings(params.invoiceTemplateSettings),
|
|
3637
3771
|
external_sale_number: params.data?.external_sale_number,
|
|
3638
3772
|
title: params.title
|
|
3639
3773
|
},
|
|
@@ -3669,6 +3803,7 @@ class Server {
|
|
|
3669
3803
|
device_id: params.deviceId ?? deviceId,
|
|
3670
3804
|
payload: {
|
|
3671
3805
|
type: params.isManualPrint ? '0' : '99',
|
|
3806
|
+
template_settings: this.normalizePrintInvoiceTemplateSettings(params.invoiceTemplateSettings),
|
|
3672
3807
|
data: params.receiptOnly ? {
|
|
3673
3808
|
order_id: printIdentity,
|
|
3674
3809
|
small_ticket_data: params.syncedOrder?.payment_info?.small_ticket_data,
|
|
@@ -3712,7 +3847,8 @@ class Server {
|
|
|
3712
3847
|
};
|
|
3713
3848
|
}
|
|
3714
3849
|
const printableOrder = await this.withLocalSmallTicketData(params.order, {
|
|
3715
|
-
requireFullyPaid: params.requireFullyPaid
|
|
3850
|
+
requireFullyPaid: params.requireFullyPaid,
|
|
3851
|
+
invoiceTemplateSettings: params.invoiceTemplateSettings
|
|
3716
3852
|
});
|
|
3717
3853
|
if (!(0, _smallTicket.hasSmallTicketData)(printableOrder.payment_info?.small_ticket_data)) {
|
|
3718
3854
|
return {
|
|
@@ -3730,7 +3866,9 @@ class Server {
|
|
|
3730
3866
|
syncedOrder: printableOrder,
|
|
3731
3867
|
response: params.response,
|
|
3732
3868
|
receiptOnly: true,
|
|
3733
|
-
isManualPrint: params.isManualPrint
|
|
3869
|
+
isManualPrint: params.isManualPrint,
|
|
3870
|
+
invoiceLayoutConfig: params.invoiceLayoutConfig,
|
|
3871
|
+
invoiceTemplateSettings: params.invoiceTemplateSettings
|
|
3734
3872
|
});
|
|
3735
3873
|
return {
|
|
3736
3874
|
printed: true,
|
|
@@ -3750,11 +3888,35 @@ class Server {
|
|
|
3750
3888
|
}
|
|
3751
3889
|
return undefined;
|
|
3752
3890
|
}
|
|
3891
|
+
mergeLocalPrintOrderInput(localOrder, inputOrder) {
|
|
3892
|
+
const definedInput = Object.fromEntries(Object.entries(inputOrder || {}).filter(([, value]) => value !== undefined));
|
|
3893
|
+
return {
|
|
3894
|
+
...localOrder,
|
|
3895
|
+
...definedInput,
|
|
3896
|
+
order_id: localOrder.order_id ?? definedInput.order_id ?? definedInput.orderId,
|
|
3897
|
+
external_sale_number: localOrder.external_sale_number ?? definedInput.external_sale_number ?? definedInput.externalSaleNumber
|
|
3898
|
+
};
|
|
3899
|
+
}
|
|
3753
3900
|
handleLocalPrintOrder = async ({
|
|
3754
3901
|
data
|
|
3755
3902
|
}) => {
|
|
3756
3903
|
try {
|
|
3757
|
-
const
|
|
3904
|
+
const extractedRequest = this.extractInvoiceLayoutConfig(data, {
|
|
3905
|
+
includePublicInvoice: true
|
|
3906
|
+
});
|
|
3907
|
+
const extractedRequestTemplateSettings = this.extractInvoiceTemplateSettings(extractedRequest.data, {
|
|
3908
|
+
includePublicTemplateSettings: true
|
|
3909
|
+
});
|
|
3910
|
+
const requestOrder = extractedRequestTemplateSettings.data?.order;
|
|
3911
|
+
const nestedOrder = requestOrder && typeof requestOrder === 'object' ? this.extractInvoiceLayoutConfig(requestOrder, {
|
|
3912
|
+
includePublicInvoice: true
|
|
3913
|
+
}) : null;
|
|
3914
|
+
const nestedOrderTemplateSettings = nestedOrder ? this.extractInvoiceTemplateSettings(nestedOrder.data, {
|
|
3915
|
+
includePublicTemplateSettings: true
|
|
3916
|
+
}) : null;
|
|
3917
|
+
const inputOrder = nestedOrderTemplateSettings?.data ?? extractedRequestTemplateSettings.data;
|
|
3918
|
+
const invoiceLayoutConfig = extractedRequest.invoiceLayoutConfig ?? nestedOrder?.invoiceLayoutConfig;
|
|
3919
|
+
const invoiceTemplateSettings = this.normalizePrintInvoiceTemplateSettings(extractedRequestTemplateSettings.invoiceTemplateSettings ?? nestedOrderTemplateSettings?.invoiceTemplateSettings);
|
|
3758
3920
|
if (!inputOrder || typeof inputOrder !== 'object') {
|
|
3759
3921
|
return {
|
|
3760
3922
|
code: 400,
|
|
@@ -3766,13 +3928,13 @@ class Server {
|
|
|
3766
3928
|
}
|
|
3767
3929
|
};
|
|
3768
3930
|
}
|
|
3769
|
-
const shouldLookup = this.shouldLookupLocalPrintOrder(inputOrder) || this.shouldLookupLocalPrintOrder(data);
|
|
3931
|
+
const shouldLookup = this.shouldLookupLocalPrintOrder(inputOrder) || this.shouldLookupLocalPrintOrder(extractedRequest.data);
|
|
3770
3932
|
this.logInfo('handleLocalPrintOrder: 是否需要查找本地订单', {
|
|
3771
3933
|
shouldLookup,
|
|
3772
3934
|
inputOrder,
|
|
3773
3935
|
data
|
|
3774
3936
|
});
|
|
3775
|
-
const lookup = shouldLookup ? this.getLocalPrintOrderLookup(inputOrder) ?? this.getLocalPrintOrderLookup(data) : undefined;
|
|
3937
|
+
const lookup = shouldLookup ? this.getLocalPrintOrderLookup(inputOrder) ?? this.getLocalPrintOrderLookup(extractedRequest.data) : undefined;
|
|
3776
3938
|
let sourceOrder = inputOrder;
|
|
3777
3939
|
if (shouldLookup) {
|
|
3778
3940
|
if (lookup === undefined) {
|
|
@@ -3797,24 +3959,25 @@ class Server {
|
|
|
3797
3959
|
}
|
|
3798
3960
|
};
|
|
3799
3961
|
}
|
|
3800
|
-
|
|
3962
|
+
let localOrder = await this.order.getLocalOrderByLookup(lookup);
|
|
3801
3963
|
if (!localOrder) {
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3964
|
+
const remoteResult = await this.fetchAndPersistSalesOrderByLookup(lookup, {
|
|
3965
|
+
with: ['all']
|
|
3966
|
+
}, 'handleLocalPrintOrder');
|
|
3967
|
+
if (!remoteResult.order) {
|
|
3968
|
+
return {
|
|
3969
|
+
code: remoteResult.code,
|
|
3970
|
+
status: false,
|
|
3971
|
+
message: remoteResult.message,
|
|
3972
|
+
data: {
|
|
3973
|
+
printed: false,
|
|
3974
|
+
order: null
|
|
3975
|
+
}
|
|
3976
|
+
};
|
|
3977
|
+
}
|
|
3978
|
+
localOrder = remoteResult.order;
|
|
3811
3979
|
}
|
|
3812
|
-
sourceOrder =
|
|
3813
|
-
...localOrder,
|
|
3814
|
-
...inputOrder,
|
|
3815
|
-
order_id: localOrder.order_id ?? inputOrder.order_id ?? inputOrder.orderId,
|
|
3816
|
-
external_sale_number: localOrder.external_sale_number ?? inputOrder.external_sale_number ?? inputOrder.externalSaleNumber
|
|
3817
|
-
};
|
|
3980
|
+
sourceOrder = this.mergeLocalPrintOrderInput(localOrder, inputOrder);
|
|
3818
3981
|
}
|
|
3819
3982
|
const checkoutData = await this.ensureCheckoutOrderNumbers({
|
|
3820
3983
|
...sourceOrder,
|
|
@@ -3833,7 +3996,9 @@ class Server {
|
|
|
3833
3996
|
order: printableOrder,
|
|
3834
3997
|
response: null,
|
|
3835
3998
|
requireFullyPaid: false,
|
|
3836
|
-
isManualPrint: true
|
|
3999
|
+
isManualPrint: true,
|
|
4000
|
+
invoiceLayoutConfig,
|
|
4001
|
+
invoiceTemplateSettings
|
|
3837
4002
|
});
|
|
3838
4003
|
return {
|
|
3839
4004
|
code: 200,
|
|
@@ -3860,6 +4025,99 @@ class Server {
|
|
|
3860
4025
|
};
|
|
3861
4026
|
}
|
|
3862
4027
|
};
|
|
4028
|
+
normalizeRequestedReceiptLocales(value) {
|
|
4029
|
+
if (!Array.isArray(value)) return [];
|
|
4030
|
+
return Array.from(new Set(value.filter(locale => typeof locale === 'string').map(locale => locale.trim()).filter(Boolean)));
|
|
4031
|
+
}
|
|
4032
|
+
handleLocalOrderReceiptSnapshot = async ({
|
|
4033
|
+
data
|
|
4034
|
+
}) => {
|
|
4035
|
+
const input = data && typeof data === 'object' && !Array.isArray(data) ? data : {};
|
|
4036
|
+
const lookup = this.getLocalPrintOrderLookup(input);
|
|
4037
|
+
if (lookup === undefined) {
|
|
4038
|
+
return {
|
|
4039
|
+
code: 400,
|
|
4040
|
+
status: false,
|
|
4041
|
+
message: '订单 lookup 缺失',
|
|
4042
|
+
data: {
|
|
4043
|
+
small_ticket_data: null
|
|
4044
|
+
}
|
|
4045
|
+
};
|
|
4046
|
+
}
|
|
4047
|
+
if (!this.order?.getLocalOrderByLookup) {
|
|
4048
|
+
return {
|
|
4049
|
+
code: 500,
|
|
4050
|
+
status: false,
|
|
4051
|
+
message: 'Order 模块未注册',
|
|
4052
|
+
data: {
|
|
4053
|
+
small_ticket_data: null
|
|
4054
|
+
}
|
|
4055
|
+
};
|
|
4056
|
+
}
|
|
4057
|
+
try {
|
|
4058
|
+
let sourceOrder = await this.order.getLocalOrderByLookup(lookup);
|
|
4059
|
+
if (!sourceOrder) {
|
|
4060
|
+
const remoteResult = await this.fetchAndPersistSalesOrderByLookup(lookup, {
|
|
4061
|
+
with: ['all']
|
|
4062
|
+
}, 'handleLocalOrderReceiptSnapshot');
|
|
4063
|
+
if (!remoteResult.order) {
|
|
4064
|
+
return {
|
|
4065
|
+
code: remoteResult.code,
|
|
4066
|
+
status: false,
|
|
4067
|
+
message: remoteResult.message,
|
|
4068
|
+
data: {
|
|
4069
|
+
small_ticket_data: null
|
|
4070
|
+
}
|
|
4071
|
+
};
|
|
4072
|
+
}
|
|
4073
|
+
sourceOrder = remoteResult.order;
|
|
4074
|
+
}
|
|
4075
|
+
const paymentInfo = {
|
|
4076
|
+
...(sourceOrder.payment_info || {})
|
|
4077
|
+
};
|
|
4078
|
+
delete paymentInfo.small_ticket_data;
|
|
4079
|
+
const rebuiltOrder = await this.withLocalSmallTicketData({
|
|
4080
|
+
...sourceOrder,
|
|
4081
|
+
small_ticket_data_flag: 1,
|
|
4082
|
+
payment_info: paymentInfo
|
|
4083
|
+
}, {
|
|
4084
|
+
locales: this.normalizeRequestedReceiptLocales(input.locales)
|
|
4085
|
+
});
|
|
4086
|
+
const smallTicketData = rebuiltOrder.payment_info?.small_ticket_data;
|
|
4087
|
+
if (!(0, _smallTicket.hasSmallTicketData)(smallTicketData)) {
|
|
4088
|
+
return {
|
|
4089
|
+
code: 500,
|
|
4090
|
+
status: false,
|
|
4091
|
+
message: '本地小票 Snapshot 生成失败',
|
|
4092
|
+
data: {
|
|
4093
|
+
small_ticket_data: null
|
|
4094
|
+
}
|
|
4095
|
+
};
|
|
4096
|
+
}
|
|
4097
|
+
return {
|
|
4098
|
+
code: 200,
|
|
4099
|
+
status: true,
|
|
4100
|
+
message: '',
|
|
4101
|
+
data: {
|
|
4102
|
+
small_ticket_data: smallTicketData
|
|
4103
|
+
}
|
|
4104
|
+
};
|
|
4105
|
+
} catch (error) {
|
|
4106
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4107
|
+
this.logError('handleLocalOrderReceiptSnapshot: 生成失败', {
|
|
4108
|
+
lookup,
|
|
4109
|
+
error: message
|
|
4110
|
+
});
|
|
4111
|
+
return {
|
|
4112
|
+
code: 500,
|
|
4113
|
+
status: false,
|
|
4114
|
+
message,
|
|
4115
|
+
data: {
|
|
4116
|
+
small_ticket_data: null
|
|
4117
|
+
}
|
|
4118
|
+
};
|
|
4119
|
+
}
|
|
4120
|
+
};
|
|
3863
4121
|
handleMachinecodeBatchMeta = async ({
|
|
3864
4122
|
data
|
|
3865
4123
|
}) => {
|
|
@@ -4044,9 +4302,17 @@ class Server {
|
|
|
4044
4302
|
async handleOrderCheckoutSubmit(params) {
|
|
4045
4303
|
const {
|
|
4046
4304
|
backendPath,
|
|
4047
|
-
data,
|
|
4048
4305
|
title
|
|
4049
4306
|
} = params;
|
|
4307
|
+
const extractedInvoice = this.extractInvoiceLayoutConfig(params.data, {
|
|
4308
|
+
includePublicInvoice: true
|
|
4309
|
+
});
|
|
4310
|
+
const extractedTemplateSettings = this.extractInvoiceTemplateSettings(extractedInvoice.data, {
|
|
4311
|
+
includePublicTemplateSettings: true
|
|
4312
|
+
});
|
|
4313
|
+
const data = extractedTemplateSettings.data;
|
|
4314
|
+
const invoiceLayoutConfig = extractedInvoice.invoiceLayoutConfig;
|
|
4315
|
+
const invoiceTemplateSettings = this.normalizePrintInvoiceTemplateSettings(extractedTemplateSettings.invoiceTemplateSettings);
|
|
4050
4316
|
this.logInfo(`${title}: 开始处理`, {
|
|
4051
4317
|
backendPath,
|
|
4052
4318
|
order_id: data?.order_id,
|
|
@@ -4057,17 +4323,20 @@ class Server {
|
|
|
4057
4323
|
return this.handleSyncCheckoutSubmit({
|
|
4058
4324
|
backendPath,
|
|
4059
4325
|
data,
|
|
4060
|
-
title
|
|
4326
|
+
title,
|
|
4327
|
+
invoiceLayoutConfig,
|
|
4328
|
+
invoiceTemplateSettings
|
|
4061
4329
|
});
|
|
4062
4330
|
}
|
|
4063
4331
|
const normalizedData = await this.normalizeCheckoutSubmitData(data, title);
|
|
4064
4332
|
const numberedData = await this.ensureCheckoutOrderNumbers(normalizedData, title);
|
|
4065
|
-
const checkoutData = this.applyCheckoutFulfillmentBuzzer(numberedData, title);
|
|
4333
|
+
const checkoutData = this.normalizeCheckoutCollections(this.applyCheckoutFulfillmentBuzzer(numberedData, title));
|
|
4066
4334
|
const pendingResult = await this.handlePendingSyncCheckoutOrder({
|
|
4067
4335
|
backendPath,
|
|
4068
4336
|
data: checkoutData,
|
|
4069
4337
|
title,
|
|
4070
|
-
reason: 'checkout 已转入设备任务同步'
|
|
4338
|
+
reason: 'checkout 已转入设备任务同步',
|
|
4339
|
+
invoiceTemplateSettings
|
|
4071
4340
|
});
|
|
4072
4341
|
if (pendingResult?.status === true) {
|
|
4073
4342
|
const pendingOrder = pendingResult.data;
|
|
@@ -4076,13 +4345,17 @@ class Server {
|
|
|
4076
4345
|
checkoutData,
|
|
4077
4346
|
order: pendingOrder,
|
|
4078
4347
|
response: pendingResult,
|
|
4079
|
-
requireFullyPaid: true
|
|
4348
|
+
requireFullyPaid: true,
|
|
4349
|
+
invoiceLayoutConfig,
|
|
4350
|
+
invoiceTemplateSettings
|
|
4080
4351
|
});
|
|
4081
4352
|
}
|
|
4082
4353
|
await this.dispatchCheckoutSyncTask({
|
|
4083
4354
|
backendPath,
|
|
4084
4355
|
data: checkoutData,
|
|
4085
|
-
title
|
|
4356
|
+
title,
|
|
4357
|
+
invoiceLayoutConfig,
|
|
4358
|
+
invoiceTemplateSettings
|
|
4086
4359
|
});
|
|
4087
4360
|
}
|
|
4088
4361
|
if (!pendingResult.data.order_id) {
|
|
@@ -4100,7 +4373,9 @@ class Server {
|
|
|
4100
4373
|
const {
|
|
4101
4374
|
backendPath,
|
|
4102
4375
|
data,
|
|
4103
|
-
title
|
|
4376
|
+
title,
|
|
4377
|
+
invoiceLayoutConfig,
|
|
4378
|
+
invoiceTemplateSettings
|
|
4104
4379
|
} = params;
|
|
4105
4380
|
if (!this.app?.request?.post) {
|
|
4106
4381
|
this.logError(`${title}: sync checkout app.request 不可用`);
|
|
@@ -4116,7 +4391,9 @@ class Server {
|
|
|
4116
4391
|
backendPath,
|
|
4117
4392
|
data,
|
|
4118
4393
|
title,
|
|
4119
|
-
persistCheckoutDataWithResponse: true
|
|
4394
|
+
persistCheckoutDataWithResponse: true,
|
|
4395
|
+
invoiceLayoutConfig,
|
|
4396
|
+
invoiceTemplateSettings
|
|
4120
4397
|
});
|
|
4121
4398
|
if (syncResult.rejected) {
|
|
4122
4399
|
const normalized = this.normalizeCheckoutResponse(syncResult.errorResponse);
|
|
@@ -4134,7 +4411,9 @@ class Server {
|
|
|
4134
4411
|
checkoutData: syncResult.checkoutData,
|
|
4135
4412
|
order: syncedOrder,
|
|
4136
4413
|
response: syncResult.normalizedResponse,
|
|
4137
|
-
requireFullyPaid: true
|
|
4414
|
+
requireFullyPaid: true,
|
|
4415
|
+
invoiceLayoutConfig,
|
|
4416
|
+
invoiceTemplateSettings
|
|
4138
4417
|
// print_all 的 type=0 是收据打印;不要沿用同步成功后的券/手环打印 type=99。
|
|
4139
4418
|
});
|
|
4140
4419
|
}
|
|
@@ -4166,9 +4445,16 @@ class Server {
|
|
|
4166
4445
|
}
|
|
4167
4446
|
async handleOrderDraftSubmit(params) {
|
|
4168
4447
|
const {
|
|
4169
|
-
data,
|
|
4170
4448
|
title
|
|
4171
4449
|
} = params;
|
|
4450
|
+
const extractedInvoice = this.extractInvoiceLayoutConfig(params.data, {
|
|
4451
|
+
includePublicInvoice: true
|
|
4452
|
+
});
|
|
4453
|
+
const {
|
|
4454
|
+
data
|
|
4455
|
+
} = this.extractInvoiceTemplateSettings(extractedInvoice.data, {
|
|
4456
|
+
includePublicTemplateSettings: true
|
|
4457
|
+
});
|
|
4172
4458
|
this.logInfo(`${title}: 开始处理`, {
|
|
4173
4459
|
order_id: data?.order_id,
|
|
4174
4460
|
external_sale_number: data?.external_sale_number,
|
|
@@ -4229,9 +4515,10 @@ class Server {
|
|
|
4229
4515
|
backendPath,
|
|
4230
4516
|
data,
|
|
4231
4517
|
title,
|
|
4232
|
-
reason
|
|
4518
|
+
reason,
|
|
4519
|
+
invoiceTemplateSettings
|
|
4233
4520
|
} = params;
|
|
4234
|
-
const pendingOrder = await this.buildPendingSyncCheckoutOrder(data);
|
|
4521
|
+
const pendingOrder = await this.buildPendingSyncCheckoutOrder(data, invoiceTemplateSettings);
|
|
4235
4522
|
if (!pendingOrder) {
|
|
4236
4523
|
this.logError(`${title}: checkout 失败且订单缺少本地存储标识`, {
|
|
4237
4524
|
backendPath,
|
|
@@ -4297,23 +4584,39 @@ class Server {
|
|
|
4297
4584
|
};
|
|
4298
4585
|
}
|
|
4299
4586
|
}
|
|
4300
|
-
async buildPendingSyncCheckoutOrder(data) {
|
|
4587
|
+
async buildPendingSyncCheckoutOrder(data, invoiceTemplateSettings) {
|
|
4301
4588
|
if (!data || typeof data !== 'object') return null;
|
|
4302
4589
|
const hasStorageKey = data.external_sale_number !== undefined && data.external_sale_number !== null && data.external_sale_number !== '' ? true : data.order_id !== undefined && data.order_id !== null && data.order_id !== '';
|
|
4303
4590
|
if (!hasStorageKey) return null;
|
|
4304
|
-
const pendingOrder = {
|
|
4591
|
+
const pendingOrder = this.normalizeCheckoutCollections({
|
|
4305
4592
|
...data,
|
|
4306
4593
|
need_sync: 1,
|
|
4307
4594
|
is_draft_order: 0
|
|
4308
|
-
};
|
|
4595
|
+
});
|
|
4309
4596
|
const productMap = await this.buildSmallTicketProductMap(pendingOrder);
|
|
4310
4597
|
const orderWithProductTitles = this.withPendingSyncProductTitles(pendingOrder, productMap);
|
|
4311
4598
|
return this.withLocalSmallTicketData(orderWithProductTitles, {
|
|
4312
4599
|
requireFullyPaid: true,
|
|
4313
|
-
productMap
|
|
4600
|
+
productMap,
|
|
4601
|
+
invoiceTemplateSettings
|
|
4314
4602
|
});
|
|
4315
4603
|
}
|
|
4316
4604
|
|
|
4605
|
+
/**
|
|
4606
|
+
* Checkout 与本地小票必须消费同一份规范化子项快照。
|
|
4607
|
+
* 商品/预约只折叠持久化 ID 或协议 UID 相同的行;支付额外兼容 payment_number。
|
|
4608
|
+
*/
|
|
4609
|
+
normalizeCheckoutCollections(data) {
|
|
4610
|
+
if (!data || typeof data !== 'object' || Array.isArray(data)) return data;
|
|
4611
|
+
const normalized = (0, _orderCollectionIdentity.normalizeOrderCollections)(data, {
|
|
4612
|
+
ensureUid: false
|
|
4613
|
+
}).order;
|
|
4614
|
+
if (Array.isArray(normalized.payments)) {
|
|
4615
|
+
normalized.payments = (0, _paymentUtils.mergeOrderPaymentListsByIdentity)([], normalized.payments);
|
|
4616
|
+
}
|
|
4617
|
+
return normalized;
|
|
4618
|
+
}
|
|
4619
|
+
|
|
4317
4620
|
/**
|
|
4318
4621
|
* Android 结账自动小票由当前设备设置控制;其它平台保持原有行为。
|
|
4319
4622
|
*/
|
|
@@ -4631,11 +4934,27 @@ class Server {
|
|
|
4631
4934
|
if (!this.shouldBuildSmallTicketData(order)) return order;
|
|
4632
4935
|
if (options?.requireFullyPaid && !this.isSalesOrderFullyPaid(order)) return order;
|
|
4633
4936
|
try {
|
|
4634
|
-
const
|
|
4937
|
+
const normalizedCollections = (0, _orderCollectionIdentity.normalizeOrderCollections)(order, {
|
|
4938
|
+
ensureUid: false
|
|
4939
|
+
});
|
|
4940
|
+
const normalizedOrder = normalizedCollections.order;
|
|
4941
|
+
const collapsedDuplicateCounts = {
|
|
4942
|
+
products: normalizedCollections.stats.product.collapsedDuplicateCount,
|
|
4943
|
+
bookings: normalizedCollections.stats.booking.collapsedDuplicateCount,
|
|
4944
|
+
payments: normalizedCollections.stats.payment.collapsedDuplicateCount
|
|
4945
|
+
};
|
|
4946
|
+
if (Object.values(collapsedDuplicateCounts).some(count => count > 0)) {
|
|
4947
|
+
this.logWarning('withLocalSmallTicketData: 折叠重复订单子项', {
|
|
4948
|
+
order_identifier: normalizedOrder.order_id ?? normalizedOrder.external_sale_number ?? normalizedOrder.shop_order_number ?? null,
|
|
4949
|
+
collapsed_duplicate_counts: collapsedDuplicateCounts
|
|
4950
|
+
});
|
|
4951
|
+
}
|
|
4952
|
+
const enrichedOrder = await this.enrichPaymentNamesByCode(normalizedOrder);
|
|
4635
4953
|
const productMap = options?.productMap ?? (await this.buildSmallTicketProductMap(enrichedOrder));
|
|
4636
4954
|
const smallTicketData = (0, _smallTicket.buildSmallTicketData)({
|
|
4637
4955
|
order: enrichedOrder,
|
|
4638
4956
|
shopInfo: this.getSmallTicketShopInfo(),
|
|
4957
|
+
locales: [...(0, _OpenData.resolveInvoiceReceiptLocales)(options?.invoiceTemplateSettings), ...(options?.locales || [])],
|
|
4639
4958
|
productMap
|
|
4640
4959
|
});
|
|
4641
4960
|
return {
|
|
@@ -4652,6 +4971,122 @@ class Server {
|
|
|
4652
4971
|
return order;
|
|
4653
4972
|
}
|
|
4654
4973
|
}
|
|
4974
|
+
normalizeRemoteSalesOrderErrorCode(value) {
|
|
4975
|
+
if (value === undefined || value === null || value === '') return 500;
|
|
4976
|
+
const code = typeof value === 'number' ? value : Number(value);
|
|
4977
|
+
return Number.isFinite(code) ? code : 500;
|
|
4978
|
+
}
|
|
4979
|
+
async fetchAndPersistSalesOrderByLookup(lookup, data, title) {
|
|
4980
|
+
if (!this.order?.getLocalOrderByLookup || !this.order?.upsertOrdersFromRemote) {
|
|
4981
|
+
this.logError(`${title}: Order 模块未注册`);
|
|
4982
|
+
return {
|
|
4983
|
+
code: 500,
|
|
4984
|
+
message: 'Order 模块未注册',
|
|
4985
|
+
order: null
|
|
4986
|
+
};
|
|
4987
|
+
}
|
|
4988
|
+
if (!this.app?.request?.get) {
|
|
4989
|
+
this.logError(`${title}: app.request 不可用`);
|
|
4990
|
+
return {
|
|
4991
|
+
code: 500,
|
|
4992
|
+
message: 'app.request 不可用',
|
|
4993
|
+
order: null
|
|
4994
|
+
};
|
|
4995
|
+
}
|
|
4996
|
+
const backendPath = this.buildOrderSalesDetailBackendPath(String(lookup), data);
|
|
4997
|
+
try {
|
|
4998
|
+
const response = await this.app.request.get(backendPath, {
|
|
4999
|
+
lookup_mode: 'multi'
|
|
5000
|
+
}, {
|
|
5001
|
+
isShopApi: true,
|
|
5002
|
+
customToast: () => {}
|
|
5003
|
+
});
|
|
5004
|
+
if (this.isCheckoutResponseRejected(response)) {
|
|
5005
|
+
const responseStatusCode = typeof response?.status === 'number' ? response.status : undefined;
|
|
5006
|
+
const code = this.normalizeRemoteSalesOrderErrorCode(response?.code ?? responseStatusCode);
|
|
5007
|
+
const message = response?.message || '后端拒绝订单查询';
|
|
5008
|
+
this.logInfo(`${title}: 后端拒绝订单查询`, {
|
|
5009
|
+
lookup,
|
|
5010
|
+
backendPath,
|
|
5011
|
+
code,
|
|
5012
|
+
message
|
|
5013
|
+
});
|
|
5014
|
+
return {
|
|
5015
|
+
code,
|
|
5016
|
+
message,
|
|
5017
|
+
order: null
|
|
5018
|
+
};
|
|
5019
|
+
}
|
|
5020
|
+
const fresh = this.extractOrderDataFromCheckoutResponse(response);
|
|
5021
|
+
if (!fresh || fresh.order_id === undefined || fresh.order_id === null || String(fresh.order_id).trim() === '') {
|
|
5022
|
+
this.logInfo(`${title}: 后端返回订单为空`, {
|
|
5023
|
+
lookup,
|
|
5024
|
+
backendPath
|
|
5025
|
+
});
|
|
5026
|
+
return {
|
|
5027
|
+
code: 500,
|
|
5028
|
+
message: '后端返回订单为空',
|
|
5029
|
+
order: null
|
|
5030
|
+
};
|
|
5031
|
+
}
|
|
5032
|
+
await this.order.upsertOrdersFromRemote([fresh]);
|
|
5033
|
+
let savedOrder = await this.order.getLocalOrderByLookup(lookup);
|
|
5034
|
+
if (!savedOrder && fresh.order_id !== undefined && fresh.order_id !== null && String(fresh.order_id) !== String(lookup)) {
|
|
5035
|
+
savedOrder = await this.order.getLocalOrderByLookup(fresh.order_id);
|
|
5036
|
+
}
|
|
5037
|
+
if (!savedOrder) {
|
|
5038
|
+
this.logError(`${title}: 订单写入后回读失败`, {
|
|
5039
|
+
lookup,
|
|
5040
|
+
remote_order_id: fresh.order_id
|
|
5041
|
+
});
|
|
5042
|
+
return {
|
|
5043
|
+
code: 500,
|
|
5044
|
+
message: '订单写入后回读失败',
|
|
5045
|
+
order: null
|
|
5046
|
+
};
|
|
5047
|
+
}
|
|
5048
|
+
this.logInfo(`${title}: 远端订单已同步到本地`, {
|
|
5049
|
+
lookup,
|
|
5050
|
+
remote_order_id: fresh.order_id
|
|
5051
|
+
});
|
|
5052
|
+
return {
|
|
5053
|
+
code: 200,
|
|
5054
|
+
message: '',
|
|
5055
|
+
order: savedOrder
|
|
5056
|
+
};
|
|
5057
|
+
} catch (error) {
|
|
5058
|
+
const backendError = this.extractBackendErrorResponse(error);
|
|
5059
|
+
const errorRecord = error;
|
|
5060
|
+
const responseStatusCode = typeof errorRecord?.response?.status === 'number' ? errorRecord.response.status : undefined;
|
|
5061
|
+
if (backendError || responseStatusCode !== undefined) {
|
|
5062
|
+
const backendStatusCode = typeof backendError?.status === 'number' ? backendError.status : undefined;
|
|
5063
|
+
const code = this.normalizeRemoteSalesOrderErrorCode(backendError?.code ?? backendStatusCode ?? responseStatusCode);
|
|
5064
|
+
const message = backendError?.message || this.getUnknownErrorMessage(error);
|
|
5065
|
+
this.logInfo(`${title}: 后端订单查询失败`, {
|
|
5066
|
+
lookup,
|
|
5067
|
+
backendPath,
|
|
5068
|
+
code,
|
|
5069
|
+
message
|
|
5070
|
+
});
|
|
5071
|
+
return {
|
|
5072
|
+
code,
|
|
5073
|
+
message,
|
|
5074
|
+
order: null
|
|
5075
|
+
};
|
|
5076
|
+
}
|
|
5077
|
+
const message = this.getUnknownErrorMessage(error);
|
|
5078
|
+
this.logInfo(`${title}: 请求失败`, {
|
|
5079
|
+
lookup,
|
|
5080
|
+
backendPath,
|
|
5081
|
+
error: message
|
|
5082
|
+
});
|
|
5083
|
+
return {
|
|
5084
|
+
code: 500,
|
|
5085
|
+
message,
|
|
5086
|
+
order: null
|
|
5087
|
+
};
|
|
5088
|
+
}
|
|
5089
|
+
}
|
|
4655
5090
|
handleOrderSalesDetail = async ({
|
|
4656
5091
|
url,
|
|
4657
5092
|
data,
|
|
@@ -4700,73 +5135,22 @@ class Server {
|
|
|
4700
5135
|
};
|
|
4701
5136
|
}
|
|
4702
5137
|
}
|
|
4703
|
-
|
|
4704
|
-
|
|
5138
|
+
const remoteResult = await this.fetchAndPersistSalesOrderByLookup(lookup, data, 'handleOrderSalesDetail');
|
|
5139
|
+
if (!remoteResult.order) {
|
|
4705
5140
|
return {
|
|
4706
|
-
code:
|
|
5141
|
+
code: remoteResult.code,
|
|
4707
5142
|
status: false,
|
|
4708
|
-
message:
|
|
4709
|
-
data: null
|
|
4710
|
-
};
|
|
4711
|
-
}
|
|
4712
|
-
const backendPath = this.buildOrderSalesDetailBackendPath(lookup, data);
|
|
4713
|
-
try {
|
|
4714
|
-
const response = await this.app.request.get(backendPath, {
|
|
4715
|
-
lookup_mode: 'multi'
|
|
4716
|
-
}, {
|
|
4717
|
-
isShopApi: true,
|
|
4718
|
-
customToast: () => {}
|
|
4719
|
-
});
|
|
4720
|
-
const fresh = response?.data ?? response;
|
|
4721
|
-
if (!fresh || typeof fresh !== 'object' || fresh.order_id == null) {
|
|
4722
|
-
this.logInfo('handleOrderSalesDetail: 后端返回订单为空', {
|
|
4723
|
-
lookup,
|
|
4724
|
-
backendPath
|
|
4725
|
-
});
|
|
4726
|
-
return {
|
|
4727
|
-
code: 500,
|
|
4728
|
-
status: false,
|
|
4729
|
-
message: '后端返回订单为空',
|
|
4730
|
-
data: null
|
|
4731
|
-
};
|
|
4732
|
-
}
|
|
4733
|
-
await this.order.upsertOrdersFromRemote([fresh]);
|
|
4734
|
-
const savedOrder = await this.order.getLocalOrderByLookup(lookup);
|
|
4735
|
-
if (!savedOrder) {
|
|
4736
|
-
this.logError('handleOrderSalesDetail: 订单写入后回读失败', {
|
|
4737
|
-
lookup
|
|
4738
|
-
});
|
|
4739
|
-
return {
|
|
4740
|
-
code: 500,
|
|
4741
|
-
status: false,
|
|
4742
|
-
message: '订单写入后回读失败',
|
|
4743
|
-
data: null
|
|
4744
|
-
};
|
|
4745
|
-
}
|
|
4746
|
-
this.logInfo('handleOrderSalesDetail: 远端订单已同步到本地', {
|
|
4747
|
-
lookup
|
|
4748
|
-
});
|
|
4749
|
-
const enrichedOrder = await this.withSalesDetailProductSnapshots(savedOrder);
|
|
4750
|
-
return {
|
|
4751
|
-
code: 200,
|
|
4752
|
-
status: true,
|
|
4753
|
-
message: '',
|
|
4754
|
-
data: enrichedOrder
|
|
4755
|
-
};
|
|
4756
|
-
} catch (error) {
|
|
4757
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4758
|
-
this.logInfo('handleOrderSalesDetail: 请求失败', {
|
|
4759
|
-
lookup,
|
|
4760
|
-
backendPath,
|
|
4761
|
-
error: errorMessage
|
|
4762
|
-
});
|
|
4763
|
-
return {
|
|
4764
|
-
code: 500,
|
|
4765
|
-
status: false,
|
|
4766
|
-
message: errorMessage,
|
|
5143
|
+
message: remoteResult.message,
|
|
4767
5144
|
data: null
|
|
4768
5145
|
};
|
|
4769
5146
|
}
|
|
5147
|
+
const enrichedOrder = await this.withSalesDetailProductSnapshots(remoteResult.order);
|
|
5148
|
+
return {
|
|
5149
|
+
code: 200,
|
|
5150
|
+
status: true,
|
|
5151
|
+
message: '',
|
|
5152
|
+
data: enrichedOrder
|
|
5153
|
+
};
|
|
4770
5154
|
};
|
|
4771
5155
|
|
|
4772
5156
|
/**
|