@pisell/pisellos 2.2.274 → 2.2.276

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/dist/core/index.d.ts +2 -0
  2. package/dist/core/index.js +7 -0
  3. package/dist/model/strategy/adapter/promotion/adapter.d.ts +4 -0
  4. package/dist/model/strategy/adapter/promotion/adapter.js +23 -0
  5. package/dist/model/strategy/adapter/promotion/evaluator.d.ts +17 -0
  6. package/dist/model/strategy/adapter/promotion/evaluator.js +279 -6
  7. package/dist/model/strategy/adapter/promotion/examples.d.ts +86 -0
  8. package/dist/model/strategy/adapter/promotion/examples.js +99 -0
  9. package/dist/model/strategy/adapter/promotion/index.d.ts +1 -1
  10. package/dist/model/strategy/adapter/promotion/index.js +1 -1
  11. package/dist/model/strategy/adapter/promotion/type.d.ts +90 -2
  12. package/dist/model/strategy/adapter/promotion/type.js +45 -0
  13. package/dist/modules/OpenData/index.d.ts +2 -0
  14. package/dist/modules/OpenData/index.js +8 -0
  15. package/dist/modules/Order/index.d.ts +28 -8
  16. package/dist/modules/Order/index.js +907 -588
  17. package/dist/modules/Order/payment-utils.d.ts +26 -0
  18. package/dist/modules/Order/payment-utils.js +225 -0
  19. package/dist/modules/Order/types.d.ts +35 -1
  20. package/dist/modules/Order/utils/orderCollectionIdentity.js +7 -2
  21. package/dist/modules/Order/utils.js +10 -6
  22. package/dist/modules/Payment/index.d.ts +2 -0
  23. package/dist/modules/Payment/index.js +78 -49
  24. package/dist/modules/Payment/types.d.ts +104 -25
  25. package/dist/modules/Payment/types.js +17 -0
  26. package/dist/modules/Payment/walletpass.d.ts +38 -1
  27. package/dist/modules/Payment/walletpass.js +917 -114
  28. package/dist/modules/Rules/index.d.ts +3 -0
  29. package/dist/modules/Rules/index.js +100 -70
  30. package/dist/server/index.d.ts +25 -0
  31. package/dist/server/index.js +1993 -1061
  32. package/dist/server/modules/index.d.ts +1 -1
  33. package/dist/server/modules/order/index.d.ts +70 -4
  34. package/dist/server/modules/order/index.js +1816 -728
  35. package/dist/server/modules/order/types.d.ts +25 -3
  36. package/dist/server/modules/order/types.js +7 -1
  37. package/dist/solution/BaseSales/index.d.ts +104 -5
  38. package/dist/solution/BaseSales/index.js +1767 -436
  39. package/dist/solution/BaseSales/types.d.ts +55 -1
  40. package/dist/solution/BaseSales/types.js +2 -1
  41. package/dist/solution/BaseSales/utils/cartPromotion.d.ts +3 -1
  42. package/dist/solution/BaseSales/utils/cartPromotion.js +94 -32
  43. package/dist/solution/BaseSales/utils/parseSalesResponse.d.ts +6 -1
  44. package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +7 -4
  45. package/dist/solution/BookingByStep/index.d.ts +1 -1
  46. package/dist/solution/BookingTicket/index.d.ts +21 -5
  47. package/dist/solution/BookingTicket/index.js +139 -56
  48. package/dist/solution/BookingTicket/types.d.ts +1 -0
  49. package/dist/solution/BookingTicket/utils/scan/applyGlobalScan.d.ts +8 -0
  50. package/dist/solution/BookingTicket/utils/scan/applyGlobalScan.js +173 -83
  51. package/dist/solution/Sales/index.d.ts +2 -0
  52. package/dist/solution/Sales/index.js +26 -4
  53. package/dist/solution/ScanOrder/index.js +31 -20
  54. package/dist/solution/VenueBooking/index.d.ts +1 -0
  55. package/dist/solution/VenueBooking/index.js +30 -19
  56. package/dist/types/index.d.ts +1 -0
  57. package/dist/utils/payment-number.d.ts +9 -0
  58. package/dist/utils/payment-number.js +69 -0
  59. package/lib/core/index.d.ts +2 -0
  60. package/lib/core/index.js +4 -0
  61. package/lib/model/strategy/adapter/promotion/adapter.d.ts +4 -0
  62. package/lib/model/strategy/adapter/promotion/adapter.js +20 -0
  63. package/lib/model/strategy/adapter/promotion/evaluator.d.ts +17 -0
  64. package/lib/model/strategy/adapter/promotion/evaluator.js +235 -0
  65. package/lib/model/strategy/adapter/promotion/examples.d.ts +86 -0
  66. package/lib/model/strategy/adapter/promotion/examples.js +91 -0
  67. package/lib/model/strategy/adapter/promotion/index.d.ts +1 -1
  68. package/lib/model/strategy/adapter/promotion/index.js +2 -0
  69. package/lib/model/strategy/adapter/promotion/type.d.ts +90 -2
  70. package/lib/model/strategy/adapter/promotion/type.js +2 -0
  71. package/lib/modules/OpenData/index.d.ts +2 -0
  72. package/lib/modules/OpenData/index.js +5 -0
  73. package/lib/modules/Order/index.d.ts +28 -8
  74. package/lib/modules/Order/index.js +252 -84
  75. package/lib/modules/Order/payment-utils.d.ts +26 -0
  76. package/lib/modules/Order/payment-utils.js +224 -0
  77. package/lib/modules/Order/types.d.ts +35 -1
  78. package/lib/modules/Order/utils/orderCollectionIdentity.js +3 -0
  79. package/lib/modules/Order/utils.js +11 -4
  80. package/lib/modules/Payment/index.d.ts +2 -0
  81. package/lib/modules/Payment/index.js +33 -12
  82. package/lib/modules/Payment/types.d.ts +104 -25
  83. package/lib/modules/Payment/types.js +1 -0
  84. package/lib/modules/Payment/walletpass.d.ts +38 -1
  85. package/lib/modules/Payment/walletpass.js +730 -21
  86. package/lib/modules/Rules/index.d.ts +3 -0
  87. package/lib/modules/Rules/index.js +38 -9
  88. package/lib/server/index.d.ts +25 -0
  89. package/lib/server/index.js +726 -31
  90. package/lib/server/modules/index.d.ts +1 -1
  91. package/lib/server/modules/order/index.d.ts +70 -4
  92. package/lib/server/modules/order/index.js +818 -69
  93. package/lib/server/modules/order/types.d.ts +25 -3
  94. package/lib/server/modules/order/types.js +1 -0
  95. package/lib/solution/BaseSales/index.d.ts +104 -5
  96. package/lib/solution/BaseSales/index.js +858 -27
  97. package/lib/solution/BaseSales/types.d.ts +55 -1
  98. package/lib/solution/BaseSales/types.js +2 -1
  99. package/lib/solution/BaseSales/utils/cartPromotion.d.ts +3 -1
  100. package/lib/solution/BaseSales/utils/cartPromotion.js +66 -12
  101. package/lib/solution/BaseSales/utils/parseSalesResponse.d.ts +6 -1
  102. package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +4 -3
  103. package/lib/solution/BookingByStep/index.d.ts +1 -1
  104. package/lib/solution/BookingTicket/index.d.ts +21 -5
  105. package/lib/solution/BookingTicket/index.js +64 -13
  106. package/lib/solution/BookingTicket/types.d.ts +1 -0
  107. package/lib/solution/BookingTicket/utils/scan/applyGlobalScan.d.ts +8 -0
  108. package/lib/solution/BookingTicket/utils/scan/applyGlobalScan.js +37 -5
  109. package/lib/solution/Sales/index.d.ts +2 -0
  110. package/lib/solution/Sales/index.js +20 -6
  111. package/lib/solution/ScanOrder/index.js +8 -0
  112. package/lib/solution/VenueBooking/index.d.ts +1 -0
  113. package/lib/solution/VenueBooking/index.js +8 -0
  114. package/lib/types/index.d.ts +1 -0
  115. package/lib/utils/payment-number.d.ts +9 -0
  116. package/lib/utils/payment-number.js +64 -0
  117. package/package.json +1 -1
@@ -34,6 +34,8 @@ __export(server_exports, {
34
34
  });
35
35
  module.exports = __toCommonJS(server_exports);
36
36
  var import_dayjs = __toESM(require("dayjs"));
37
+ var import_decimal = __toESM(require("decimal.js"));
38
+ var import_lodash_es = require("lodash-es");
37
39
  var import_products = require("./modules/products");
38
40
  var import_menu = require("./modules/menu");
39
41
  var import_quotation = require("./modules/quotation");
@@ -43,6 +45,7 @@ var import_floor_plan = require("./modules/floor-plan");
43
45
  var import_types = require("./modules/floor-plan/types");
44
46
  var import_payment = require("./modules/payment");
45
47
  var import_schedule2 = require("./utils/schedule");
48
+ var import_time = require("./utils/time");
46
49
  var import_types2 = require("./modules/products/types");
47
50
  var import_product = require("./utils/product");
48
51
  var import_order = require("./modules/order");
@@ -50,6 +53,8 @@ var import_types3 = require("./modules/order/types");
50
53
  var import_filterOrders = require("./modules/order/utils/filterOrders");
51
54
  var import_filterBookings = require("./modules/order/utils/filterBookings");
52
55
  var import_small_ticket = require("./utils/small-ticket");
56
+ var import_BaseSales = require("../solution/BaseSales");
57
+ var import_payment_utils = require("../modules/Order/payment-utils");
53
58
  __reExport(server_exports, require("./modules"), module.exports);
54
59
  var PRODUCT_TITLE_SNAPSHOT_KEYS = [
55
60
  "en",
@@ -61,6 +66,7 @@ var PRODUCT_TITLE_SNAPSHOT_KEYS = [
61
66
  "kitchen",
62
67
  "original"
63
68
  ];
69
+ var AUTO_PRINT_STORAGE_KEY = "auto_print";
64
70
  var Server = class {
65
71
  constructor(core) {
66
72
  this.quotationAvailableCache = /* @__PURE__ */ new Map();
@@ -87,6 +93,7 @@ var Server = class {
87
93
  this.BOOKING_REMOTE_CACHE_MAX_ENTRIES = 80;
88
94
  this.salesSearchSubscribers = /* @__PURE__ */ new Map();
89
95
  this.deviceTaskActionsRegistered = false;
96
+ this.localPaymentUpdateQueues = /* @__PURE__ */ new Map();
90
97
  this.floorPlanQuerySubscribers = /* @__PURE__ */ new Map();
91
98
  // 模块注册表 - 定义所有可用的模块配置
92
99
  this.moduleRegistry = {
@@ -241,7 +248,11 @@ var Server = class {
241
248
  strategy_context
242
249
  });
243
250
  if (subscriberId && typeof callback === "function") {
244
- this.syncProductQueryScheduleTimers(subscriberId, strategy_context);
251
+ this.syncProductQueryScheduleTimers({
252
+ subscriberId,
253
+ menu_list_ids,
254
+ strategy_context
255
+ });
245
256
  }
246
257
  return result;
247
258
  };
@@ -575,6 +586,128 @@ var Server = class {
575
586
  title: "handleOrderSalesCheckout"
576
587
  });
577
588
  };
589
+ this.handleOrderSalesDraft = async ({ data }) => {
590
+ return this.handleOrderDraftSubmit({
591
+ data,
592
+ title: "handleOrderSalesDraft"
593
+ });
594
+ };
595
+ this.handleGetLocalPayment = async ({ data }) => {
596
+ const saleId = data == null ? void 0 : data.sale_id;
597
+ const paymentNumber = data == null ? void 0 : data.payment_number;
598
+ const operationId = this.createLocalPaymentOperationId("get");
599
+ const requestContext = {
600
+ operation_id: operationId,
601
+ sale_id: saleId ?? null,
602
+ payment_number: paymentNumber ?? null
603
+ };
604
+ this.logInfo("handleGetLocalPayment: 请求进入", requestContext);
605
+ if (saleId === void 0 || saleId === null || saleId === "" || paymentNumber === void 0 || paymentNumber === null || paymentNumber === "") {
606
+ this.logWarning("handleGetLocalPayment: 参数校验失败", {
607
+ ...requestContext,
608
+ missing_fields: [
609
+ ...saleId === void 0 || saleId === null || saleId === "" ? ["sale_id"] : [],
610
+ ...paymentNumber === void 0 || paymentNumber === null || paymentNumber === "" ? ["payment_number"] : []
611
+ ]
612
+ });
613
+ return {
614
+ code: 400,
615
+ status: false,
616
+ message: "sale_id 和 payment_number 必填",
617
+ data: null
618
+ };
619
+ }
620
+ if (!this.order) {
621
+ this.logError("handleGetLocalPayment: Order 模块未注册", requestContext);
622
+ return { code: 500, status: false, message: "Order 模块未注册", data: null };
623
+ }
624
+ const startedAt = Date.now();
625
+ try {
626
+ this.logInfo("handleGetLocalPayment: 开始查询本地支付项", requestContext);
627
+ const found = await this.order.getLocalPayment({
628
+ saleId,
629
+ paymentNumber,
630
+ operationId
631
+ });
632
+ if (!found) {
633
+ this.logWarning("handleGetLocalPayment: 本地支付项不存在", {
634
+ ...requestContext,
635
+ duration_ms: Date.now() - startedAt
636
+ });
637
+ return { code: 404, status: false, message: "本地支付项不存在", data: null };
638
+ }
639
+ this.logInfo("handleGetLocalPayment: 查询命中", {
640
+ ...requestContext,
641
+ duration_ms: Date.now() - startedAt,
642
+ matched_by: found.matchedBy,
643
+ payment_index: found.paymentIndex,
644
+ ...this.buildLocalPaymentOrderLogContext(found.order),
645
+ payment: found.payment
646
+ });
647
+ return { code: 200, status: true, message: "", data: found.payment };
648
+ } catch (error) {
649
+ this.logError("handleGetLocalPayment: 查询异常", {
650
+ ...requestContext,
651
+ duration_ms: Date.now() - startedAt,
652
+ error: error instanceof Error ? error.message : String(error),
653
+ error_detail: this.normalizeUnknownError(error)
654
+ });
655
+ throw error;
656
+ }
657
+ };
658
+ this.handleUpdateLocalPayment = async ({ data }) => {
659
+ const saleId = data == null ? void 0 : data.sale_id;
660
+ const paymentNumber = data == null ? void 0 : data.payment_number;
661
+ const number = data == null ? void 0 : data.number;
662
+ const status = data == null ? void 0 : data.status;
663
+ const serviceCharge = data == null ? void 0 : data.service_charge;
664
+ const cardReaderSurcharge = data == null ? void 0 : data.card_reader_surcharge;
665
+ const hasSurchargeInput = serviceCharge !== void 0 || cardReaderSurcharge !== void 0;
666
+ const invalidSurchargeFields = this.getInvalidLocalPaymentSurchargeFields(
667
+ serviceCharge,
668
+ cardReaderSurcharge
669
+ );
670
+ const operationId = this.createLocalPaymentOperationId("update");
671
+ const requestContext = {
672
+ operation_id: operationId,
673
+ sale_id: saleId ?? null,
674
+ payment_number: paymentNumber ?? null,
675
+ transaction_number: number ?? null,
676
+ requested_status: status ?? null,
677
+ service_charge: serviceCharge ?? null,
678
+ card_reader_surcharge: cardReaderSurcharge ?? null
679
+ };
680
+ this.logInfo("handleUpdateLocalPayment: 请求进入", requestContext);
681
+ if (saleId === void 0 || saleId === null || saleId === "" || paymentNumber === void 0 || paymentNumber === null || paymentNumber === "" || number === void 0 || number === null || number === "" || status !== "success" && status !== "fail" || invalidSurchargeFields.length > 0) {
682
+ this.logWarning("handleUpdateLocalPayment: 参数校验失败", {
683
+ ...requestContext,
684
+ missing_or_invalid_fields: [
685
+ ...saleId === void 0 || saleId === null || saleId === "" ? ["sale_id"] : [],
686
+ ...paymentNumber === void 0 || paymentNumber === null || paymentNumber === "" ? ["payment_number"] : [],
687
+ ...number === void 0 || number === null || number === "" ? ["number"] : [],
688
+ ...status !== "success" && status !== "fail" ? ["status"] : [],
689
+ ...invalidSurchargeFields
690
+ ]
691
+ });
692
+ return {
693
+ code: 400,
694
+ status: false,
695
+ message: invalidSurchargeFields.length > 0 ? `手续费参数非法: ${invalidSurchargeFields.join(", ")}` : "sale_id、payment_number、number 必填,status 仅支持 success/fail",
696
+ data: null
697
+ };
698
+ }
699
+ const queueKey = `${String(saleId)}:${String(paymentNumber)}`;
700
+ return this.runLocalPaymentUpdateInQueue(queueKey, () => this.processLocalPaymentUpdate({
701
+ operationId,
702
+ saleId,
703
+ paymentNumber,
704
+ number: String(number),
705
+ status,
706
+ serviceCharge,
707
+ cardReaderSurcharge,
708
+ hasSurchargeInput
709
+ }), requestContext);
710
+ };
578
711
  this.handleOrderCheckout = async ({ data }) => {
579
712
  return this.handleOrderCheckoutSubmit({
580
713
  backendPath: "/order/checkout",
@@ -946,7 +1079,12 @@ var Server = class {
946
1079
  };
947
1080
  }
948
1081
  try {
949
- const menuList = this.menu.getMenuByIds(menu_list_ids);
1082
+ const {
1083
+ menuList,
1084
+ scheduleIds,
1085
+ scheduleList,
1086
+ timePoints
1087
+ } = this.getMenuScheduleTimePoints(menu_list_ids);
950
1088
  console.log(`[Server] 找到 ${menuList.length} 个餐牌`);
951
1089
  this.logInfo("handleGetScheduleTimePoints: 获取到餐牌列表", {
952
1090
  requestedCount: menu_list_ids.length,
@@ -954,7 +1092,6 @@ var Server = class {
954
1092
  menu_list_ids,
955
1093
  menuList
956
1094
  });
957
- const scheduleIds = (0, import_schedule2.extractScheduleIdsFromMenus)(menuList);
958
1095
  console.log(`[Server] 提取到 ${scheduleIds.length} 个日程ID:`, scheduleIds);
959
1096
  this.logInfo("handleGetScheduleTimePoints: 提取到日程 IDs", {
960
1097
  scheduleCount: scheduleIds.length
@@ -968,13 +1105,11 @@ var Server = class {
968
1105
  status: true
969
1106
  };
970
1107
  }
971
- const scheduleList = this.schedule.getScheduleByIds(scheduleIds);
972
1108
  console.log(`[Server] 找到 ${scheduleList.length} 个日程`);
973
1109
  this.logInfo("handleGetScheduleTimePoints: 获取到日程详情", {
974
1110
  requestedCount: scheduleIds.length,
975
1111
  foundCount: scheduleList.length
976
1112
  });
977
- const timePoints = (0, import_schedule2.extractTimePointsFromSchedules)(scheduleList);
978
1113
  console.log(`[Server] 提取到 ${timePoints.length} 个时间点:`, timePoints);
979
1114
  this.logInfo("handleGetScheduleTimePoints 处理完成", {
980
1115
  menuListIdsCount: menu_list_ids.length,
@@ -1356,7 +1491,7 @@ var Server = class {
1356
1491
  const { backendPath, title } = params;
1357
1492
  const normalizedData = await this.normalizeCheckoutSubmitData(params.data, title);
1358
1493
  const checkoutData = await this.ensureCheckoutOrderNumbers(normalizedData, title);
1359
- const remoteCheckoutData = this.omitCheckoutSyncMode(checkoutData);
1494
+ const remoteCheckoutData = this.buildRemoteCheckoutData(checkoutData, title);
1360
1495
  if (!((_b = (_a = this.app) == null ? void 0 : _a.request) == null ? void 0 : _b.post)) {
1361
1496
  throw new Error("app.request 不可用");
1362
1497
  }
@@ -1372,20 +1507,20 @@ var Server = class {
1372
1507
  errorResponse: response,
1373
1508
  response,
1374
1509
  normalizedResponse: this.normalizeCheckoutResponse(response),
1375
- checkoutData: remoteCheckoutData
1510
+ checkoutData
1376
1511
  };
1377
1512
  }
1378
1513
  const fresh = this.extractOrderDataFromCheckoutResponse(response);
1379
1514
  const externalSaleNumber = this.getCheckoutExternalSaleNumber(
1380
- remoteCheckoutData,
1515
+ checkoutData,
1381
1516
  {
1382
1517
  external_sale_number: params.externalSaleNumber,
1383
- data: remoteCheckoutData
1518
+ data: checkoutData
1384
1519
  }
1385
1520
  );
1386
1521
  const syncedOrder = await this.persistSyncedCheckoutOrder({
1387
1522
  backendPath,
1388
- checkoutData: remoteCheckoutData,
1523
+ checkoutData,
1389
1524
  externalSaleNumber,
1390
1525
  fresh,
1391
1526
  title,
@@ -1396,11 +1531,11 @@ var Server = class {
1396
1531
  checkoutResponseOrder: fresh
1397
1532
  }) : void 0;
1398
1533
  if (printableSyncedOrder && this.shouldPrintSyncedOrder({
1399
- checkoutData: remoteCheckoutData,
1534
+ checkoutData,
1400
1535
  syncedOrder: printableSyncedOrder
1401
1536
  })) {
1402
1537
  await this.dispatchPrintOtherReceiptTask({
1403
- checkoutData: remoteCheckoutData,
1538
+ checkoutData,
1404
1539
  syncedOrder: printableSyncedOrder,
1405
1540
  response,
1406
1541
  deviceId: params.deviceId
@@ -1410,7 +1545,7 @@ var Server = class {
1410
1545
  rejected: false,
1411
1546
  response,
1412
1547
  normalizedResponse: this.normalizeCheckoutResponse(response),
1413
- checkoutData: remoteCheckoutData,
1548
+ checkoutData,
1414
1549
  syncedOrder,
1415
1550
  fresh
1416
1551
  };
@@ -1423,6 +1558,54 @@ var Server = class {
1423
1558
  delete next.syncMode;
1424
1559
  return next;
1425
1560
  }
1561
+ buildRemoteCheckoutData(data, title) {
1562
+ const next = (0, import_lodash_es.cloneDeep)(this.omitCheckoutSyncMode(data));
1563
+ if (!next || typeof next !== "object")
1564
+ return next;
1565
+ const record = next;
1566
+ if (!Array.isArray(record.payments))
1567
+ return next;
1568
+ const originalPayments = record.payments;
1569
+ const filteredPayments = originalPayments.filter((payment) => !(0, import_payment_utils.isPendingOrderPayment)(payment));
1570
+ const removedPayments = originalPayments.filter((payment) => (0, import_payment_utils.isPendingOrderPayment)(payment));
1571
+ record.payments = filteredPayments;
1572
+ this.logInfo(`${title}: checkout 云端支付项已过滤`, {
1573
+ external_sale_number: record.external_sale_number,
1574
+ original_payment_count: originalPayments.length,
1575
+ remote_payment_count: filteredPayments.length,
1576
+ filtered_pending_count: removedPayments.length,
1577
+ filtered_payment_identities: removedPayments.map((payment) => (0, import_payment_utils.getOrderPaymentIdentityKeys)(payment))
1578
+ });
1579
+ return next;
1580
+ }
1581
+ mergeCheckoutSyncPayments(checkoutData, fresh, title) {
1582
+ const localPayments = Array.isArray(checkoutData == null ? void 0 : checkoutData.payments) ? checkoutData.payments : [];
1583
+ const freshRecord = fresh;
1584
+ if (!Array.isArray(freshRecord.payments)) {
1585
+ return localPayments.length > 0 ? { ...freshRecord, payments: (0, import_lodash_es.cloneDeep)(localPayments) } : freshRecord;
1586
+ }
1587
+ const freshPayments = freshRecord.payments;
1588
+ const mergedPayments = (0, import_payment_utils.mergeOrderPaymentListsByIdentity)(
1589
+ localPayments,
1590
+ freshPayments,
1591
+ { preserveUnmatchedExistingWhen: import_payment_utils.isIdentifiedPendingOrderPayment }
1592
+ );
1593
+ const remoteIdentityKeys = new Set(
1594
+ freshPayments.flatMap((payment) => (0, import_payment_utils.getOrderPaymentIdentityKeys)(payment))
1595
+ );
1596
+ const preservedPending = localPayments.filter((payment) => (0, import_payment_utils.isIdentifiedPendingOrderPayment)(payment) && !(0, import_payment_utils.getOrderPaymentIdentityKeys)(payment).some((key) => remoteIdentityKeys.has(key)));
1597
+ if (preservedPending.length > 0) {
1598
+ this.logInfo(`${title}: checkout 响应合并保留本地 pending 支付项`, {
1599
+ external_sale_number: checkoutData == null ? void 0 : checkoutData.external_sale_number,
1600
+ preserved_pending_count: preservedPending.length,
1601
+ preserved_payment_identities: preservedPending.map((payment) => (0, import_payment_utils.getOrderPaymentIdentityKeys)(payment))
1602
+ });
1603
+ }
1604
+ return {
1605
+ ...freshRecord,
1606
+ payments: mergedPayments
1607
+ };
1608
+ }
1426
1609
  async persistSyncedCheckoutOrder(params) {
1427
1610
  const {
1428
1611
  backendPath,
@@ -1437,15 +1620,17 @@ var Server = class {
1437
1620
  if (this.order && fresh && (shouldMergeRemoteOrder || persistCheckoutDataWithResponse)) {
1438
1621
  syncedOrder = persistCheckoutDataWithResponse ? {
1439
1622
  ...checkoutData,
1440
- ...fresh,
1623
+ ...this.mergeCheckoutSyncPayments(checkoutData, fresh, title),
1441
1624
  external_sale_number: fresh.external_sale_number ?? externalSaleNumber,
1442
- need_sync: 0
1625
+ need_sync: 0,
1626
+ is_draft_order: 0
1443
1627
  } : {
1444
- ...fresh,
1628
+ ...this.mergeCheckoutSyncPayments(checkoutData, fresh, title),
1445
1629
  external_sale_number: fresh.external_sale_number ?? externalSaleNumber,
1446
- need_sync: 0
1630
+ need_sync: 0,
1631
+ is_draft_order: 0
1447
1632
  };
1448
- await this.order.upsertOrdersFromRemote([syncedOrder]);
1633
+ await this.order.upsertOrdersFromRemote([syncedOrder], "checkoutSync");
1449
1634
  this.logInfo(`${title}: sync_sales 完整订单已同步到本地`, {
1450
1635
  order_id: fresh.order_id,
1451
1636
  external_sale_number: syncedOrder.external_sale_number,
@@ -1463,7 +1648,8 @@ var Server = class {
1463
1648
  ...checkoutData,
1464
1649
  ...patch,
1465
1650
  external_sale_number: externalSaleNumber,
1466
- need_sync: 0
1651
+ need_sync: 0,
1652
+ is_draft_order: 0
1467
1653
  };
1468
1654
  this.logInfo(`${title}: sync_sales 已按 external_sale_number 标记本地订单`, {
1469
1655
  order_id: (syncedOrder == null ? void 0 : syncedOrder.order_id) ?? null,
@@ -1843,6 +2029,11 @@ var Server = class {
1843
2029
  path: "/shop/order/sales/checkout",
1844
2030
  handler: this.handleOrderSalesCheckout.bind(this)
1845
2031
  },
2032
+ {
2033
+ method: "post",
2034
+ path: "/shop/order/sales/draft",
2035
+ handler: this.handleOrderSalesDraft.bind(this)
2036
+ },
1846
2037
  {
1847
2038
  method: "post",
1848
2039
  path: "/order/checkout",
@@ -1853,6 +2044,16 @@ var Server = class {
1853
2044
  path: "/shop/local/print-order",
1854
2045
  handler: this.handleLocalPrintOrder.bind(this)
1855
2046
  },
2047
+ {
2048
+ method: "post",
2049
+ path: "/shop/local/getLocalPayment",
2050
+ handler: this.handleGetLocalPayment.bind(this)
2051
+ },
2052
+ {
2053
+ method: "post",
2054
+ path: "/shop/local/updateLocalPayment",
2055
+ handler: this.handleUpdateLocalPayment.bind(this)
2056
+ },
1856
2057
  {
1857
2058
  method: "post",
1858
2059
  path: "/shop/machinecode/batch",
@@ -2070,16 +2271,44 @@ var Server = class {
2070
2271
  });
2071
2272
  }
2072
2273
  }
2274
+ /**
2275
+ * 根据餐牌 ID 获取其绑定日程中的全部时间点。
2276
+ * 商品查询定时器与 schedule-time-points 路由共用此逻辑。
2277
+ */
2278
+ getMenuScheduleTimePoints(menuListIds) {
2279
+ var _a, _b;
2280
+ const menuList = ((_a = this.menu) == null ? void 0 : _a.getMenuByIds(menuListIds)) ?? [];
2281
+ const scheduleIds = (0, import_schedule2.extractScheduleIdsFromMenus)(menuList);
2282
+ const scheduleList = scheduleIds.length > 0 ? ((_b = this.schedule) == null ? void 0 : _b.getScheduleByIds(scheduleIds)) ?? [] : [];
2283
+ const timePoints = (0, import_schedule2.extractTimePointsFromSchedules)(scheduleList);
2284
+ return {
2285
+ menuList,
2286
+ scheduleIds,
2287
+ scheduleList,
2288
+ timePoints
2289
+ };
2290
+ }
2073
2291
  /**
2074
2292
  * query 完成后为 subscriber 同步 schedule 定时器(先 clear 再 schedule)。
2075
2293
  */
2076
- syncProductQueryScheduleTimers(subscriberId, strategy_context) {
2294
+ syncProductQueryScheduleTimers({
2295
+ subscriberId,
2296
+ menu_list_ids,
2297
+ strategy_context
2298
+ }) {
2077
2299
  var _a, _b;
2078
2300
  this.clearSubscriberScheduleTimers(subscriberId);
2079
2301
  if (!this.shouldScheduleProductQueryReload(strategy_context)) {
2080
2302
  return;
2081
2303
  }
2082
- const timePoints = ((_b = (_a = this.products) == null ? void 0 : _a.getLastScheduleTimePoints) == null ? void 0 : _b.call(_a)) ?? [];
2304
+ const productTimePoints = ((_b = (_a = this.products) == null ? void 0 : _a.getLastScheduleTimePoints) == null ? void 0 : _b.call(_a)) ?? [];
2305
+ const menuListIds = Array.isArray(menu_list_ids) ? Array.from(new Set(
2306
+ menu_list_ids.map((id) => Number(id)).filter((id) => Number.isFinite(id))
2307
+ )) : [];
2308
+ const menuTimePoints = this.getMenuScheduleTimePoints(menuListIds).timePoints;
2309
+ const timePoints = (0, import_time.sortTimePoints)(
2310
+ Array.from(/* @__PURE__ */ new Set([...productTimePoints, ...menuTimePoints]))
2311
+ );
2083
2312
  if (timePoints.length > 0) {
2084
2313
  this.scheduleSubscriberTimePointReloads(subscriberId, timePoints);
2085
2314
  }
@@ -2694,6 +2923,405 @@ var Server = class {
2694
2923
  };
2695
2924
  }
2696
2925
  }
2926
+ createLocalPaymentOperationId(action) {
2927
+ return `local_payment_${action}_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
2928
+ }
2929
+ buildLocalPaymentOrderLogContext(order) {
2930
+ if (!order)
2931
+ return {};
2932
+ return {
2933
+ order_id: order.order_id ?? null,
2934
+ external_sale_number: order.external_sale_number ?? null,
2935
+ order_number: order.order_number ?? null,
2936
+ shop_order_number: order.shop_order_number ?? null,
2937
+ shop_full_order_number: order.shop_full_order_number ?? null,
2938
+ order_status: order.status ?? null,
2939
+ payment_status: order.payment_status ?? null,
2940
+ is_draft_order: Number(order.is_draft_order || 0),
2941
+ need_sync: Number(order.need_sync || 0),
2942
+ platform: order.platform ?? null,
2943
+ business_code: order.business_code ?? null,
2944
+ order_sales_channel: order.order_sales_channel ?? null,
2945
+ sales_channel: order.sales_channel ?? null,
2946
+ order_type: order.type ?? null,
2947
+ payments_count: Array.isArray(order.payments) ? order.payments.length : 0
2948
+ };
2949
+ }
2950
+ isValidLocalPaymentDecimal(value) {
2951
+ if (typeof value !== "string" && typeof value !== "number")
2952
+ return false;
2953
+ if (typeof value === "string" && value.trim() === "")
2954
+ return false;
2955
+ try {
2956
+ return new import_decimal.default(value).isFinite();
2957
+ } catch {
2958
+ return false;
2959
+ }
2960
+ }
2961
+ getInvalidLocalPaymentSurchargeFields(serviceCharge, cardReaderSurcharge) {
2962
+ const invalidFields = [];
2963
+ if (serviceCharge !== void 0) {
2964
+ if (serviceCharge === null || typeof serviceCharge !== "object" || Array.isArray(serviceCharge)) {
2965
+ invalidFields.push("service_charge");
2966
+ } else {
2967
+ const input = serviceCharge;
2968
+ if (input.fixed !== void 0 && !this.isValidLocalPaymentDecimal(input.fixed)) {
2969
+ invalidFields.push("service_charge.fixed");
2970
+ }
2971
+ if (input.percentage !== void 0 && !this.isValidLocalPaymentDecimal(input.percentage)) {
2972
+ invalidFields.push("service_charge.percentage");
2973
+ }
2974
+ }
2975
+ }
2976
+ if (cardReaderSurcharge !== void 0 && !this.isValidLocalPaymentDecimal(cardReaderSurcharge)) {
2977
+ invalidFields.push("card_reader_surcharge");
2978
+ }
2979
+ return invalidFields;
2980
+ }
2981
+ buildLocalPaymentSurchargeUpdate(params) {
2982
+ var _a, _b, _c;
2983
+ const amount = new import_decimal.default(((_a = params.serviceCharge) == null ? void 0 : _a.fixed) || 0).plus(params.cardReaderSurcharge || 0).toFixed(2);
2984
+ const serviceFee = new import_decimal.default(params.paymentAmount || 0).times(((_b = params.serviceCharge) == null ? void 0 : _b.percentage) || 0).plus(amount).toDecimalPlaces(2).toFixed(2);
2985
+ return {
2986
+ service_charge: {
2987
+ amount,
2988
+ ...((_c = params.serviceCharge) == null ? void 0 : _c.percentage) !== void 0 ? { percentage: params.serviceCharge.percentage } : {}
2989
+ },
2990
+ service_fee: serviceFee
2991
+ };
2992
+ }
2993
+ async runLocalPaymentUpdateInQueue(key, task, context) {
2994
+ var _a;
2995
+ const queuedAt = Date.now();
2996
+ const hasPreviousTask = this.localPaymentUpdateQueues.has(key);
2997
+ const previous = this.localPaymentUpdateQueues.get(key) || Promise.resolve();
2998
+ this.logInfo("handleUpdateLocalPayment: 串行队列入队", {
2999
+ ...context,
3000
+ queue_key: key,
3001
+ waiting_for_previous: hasPreviousTask,
3002
+ active_queue_count: this.localPaymentUpdateQueues.size
3003
+ });
3004
+ const current = previous.catch(() => void 0).then(async () => {
3005
+ this.logInfo("handleUpdateLocalPayment: 串行队列开始执行", {
3006
+ ...context,
3007
+ queue_key: key,
3008
+ queue_wait_ms: Date.now() - queuedAt
3009
+ });
3010
+ return task();
3011
+ });
3012
+ const tail = current.then(() => void 0, () => void 0);
3013
+ this.localPaymentUpdateQueues.set(key, tail);
3014
+ try {
3015
+ const result = await current;
3016
+ this.logInfo("handleUpdateLocalPayment: 串行队列执行完成", {
3017
+ ...context,
3018
+ queue_key: key,
3019
+ total_duration_ms: Date.now() - queuedAt,
3020
+ result_code: result == null ? void 0 : result.code,
3021
+ result_status: result == null ? void 0 : result.status,
3022
+ payment: (_a = result == null ? void 0 : result.data) == null ? void 0 : _a.payment
3023
+ });
3024
+ return result;
3025
+ } catch (error) {
3026
+ this.logError("handleUpdateLocalPayment: 串行队列执行失败", {
3027
+ ...context,
3028
+ queue_key: key,
3029
+ total_duration_ms: Date.now() - queuedAt,
3030
+ error: error instanceof Error ? error.message : String(error),
3031
+ error_detail: this.normalizeUnknownError(error)
3032
+ });
3033
+ throw error;
3034
+ } finally {
3035
+ let released = false;
3036
+ if (this.localPaymentUpdateQueues.get(key) === tail) {
3037
+ this.localPaymentUpdateQueues.delete(key);
3038
+ released = true;
3039
+ }
3040
+ this.logInfo("handleUpdateLocalPayment: 串行队列释放", {
3041
+ ...context,
3042
+ queue_key: key,
3043
+ released,
3044
+ active_queue_count: this.localPaymentUpdateQueues.size
3045
+ });
3046
+ }
3047
+ }
3048
+ async processLocalPaymentUpdate(params) {
3049
+ var _a, _b, _c;
3050
+ const requestContext = {
3051
+ operation_id: params.operationId,
3052
+ sale_id: params.saleId,
3053
+ payment_number: params.paymentNumber,
3054
+ transaction_number: params.number,
3055
+ requested_status: params.status,
3056
+ service_charge: params.serviceCharge ?? null,
3057
+ card_reader_surcharge: params.cardReaderSurcharge ?? null
3058
+ };
3059
+ this.logInfo("processLocalPaymentUpdate: 开始处理", requestContext);
3060
+ if (!this.order) {
3061
+ this.logError("processLocalPaymentUpdate: Order 模块未注册", requestContext);
3062
+ return { code: 500, status: false, message: "Order 模块未注册", data: null };
3063
+ }
3064
+ this.logInfo("processLocalPaymentUpdate: 开始查询本地支付项", requestContext);
3065
+ const found = await this.order.getLocalPayment({
3066
+ saleId: params.saleId,
3067
+ paymentNumber: params.paymentNumber,
3068
+ operationId: params.operationId
3069
+ });
3070
+ if (!found) {
3071
+ this.logWarning("processLocalPaymentUpdate: 本地支付项不存在", requestContext);
3072
+ return { code: 404, status: false, message: "本地支付项不存在", data: null };
3073
+ }
3074
+ const surchargeUpdate = params.status === "success" && params.hasSurchargeInput ? this.buildLocalPaymentSurchargeUpdate({
3075
+ paymentAmount: (_a = found.payment) == null ? void 0 : _a.amount,
3076
+ serviceCharge: params.serviceCharge,
3077
+ cardReaderSurcharge: params.cardReaderSurcharge
3078
+ }) : void 0;
3079
+ const incomingPaymentUpdate = params.status === "success" ? {
3080
+ status: "paid",
3081
+ ...surchargeUpdate || {},
3082
+ metadata: {
3083
+ unique_payment_number: params.number,
3084
+ transactions: [{ number: params.number, status: "success" }]
3085
+ }
3086
+ } : {
3087
+ metadata: {
3088
+ transactions: [{ number: params.number, status: "fail" }]
3089
+ }
3090
+ };
3091
+ const transactions = Array.isArray((_c = (_b = found.payment) == null ? void 0 : _b.metadata) == null ? void 0 : _c.transactions) ? found.payment.metadata.transactions : [];
3092
+ const existingTransaction = transactions.find((item) => String((item == null ? void 0 : item.number) || "") === params.number);
3093
+ const hasRecordedSuccess = transactions.some((item) => (item == null ? void 0 : item.status) === "success");
3094
+ const orderIsDraft = Number(found.order.is_draft_order || 0) === 1;
3095
+ const foundContext = {
3096
+ ...requestContext,
3097
+ matched_by: found.matchedBy,
3098
+ payment_index: found.paymentIndex,
3099
+ ...this.buildLocalPaymentOrderLogContext(found.order),
3100
+ payment: found.payment,
3101
+ incoming_payment_update: incomingPaymentUpdate,
3102
+ existing_transaction: existingTransaction || null,
3103
+ has_recorded_success: hasRecordedSuccess
3104
+ };
3105
+ this.logInfo("processLocalPaymentUpdate: 查询命中", foundContext);
3106
+ if (params.status === "fail" && (found.payment.status === "paid" || hasRecordedSuccess)) {
3107
+ this.logInfo("processLocalPaymentUpdate: 幂等返回", {
3108
+ ...foundContext,
3109
+ idempotent_reason: "ignore_failure_after_success"
3110
+ });
3111
+ return {
3112
+ code: 200,
3113
+ status: true,
3114
+ message: "",
3115
+ data: { payment: found.payment, order: found.order, idempotent: true }
3116
+ };
3117
+ }
3118
+ const alreadyRecordedFailure = params.status === "fail" && (existingTransaction == null ? void 0 : existingTransaction.status) === "fail";
3119
+ if (alreadyRecordedFailure) {
3120
+ this.logInfo("processLocalPaymentUpdate: 幂等返回", {
3121
+ ...foundContext,
3122
+ idempotent_reason: "failure_already_recorded"
3123
+ });
3124
+ return {
3125
+ code: 200,
3126
+ status: true,
3127
+ message: "",
3128
+ data: { payment: found.payment, order: found.order, idempotent: true }
3129
+ };
3130
+ }
3131
+ if (params.status === "success" && (existingTransaction == null ? void 0 : existingTransaction.status) === "success" && found.payment.status === "paid" && !orderIsDraft) {
3132
+ this.logInfo("processLocalPaymentUpdate: 幂等返回", {
3133
+ ...foundContext,
3134
+ idempotent_reason: "success_already_committed"
3135
+ });
3136
+ return {
3137
+ code: 200,
3138
+ status: true,
3139
+ message: "",
3140
+ data: { payment: found.payment, order: found.order, idempotent: true }
3141
+ };
3142
+ }
3143
+ if (params.status === "success") {
3144
+ this.logInfo("processLocalPaymentUpdate: 进入 success 恢复分支", {
3145
+ ...foundContext,
3146
+ force_submit_if_paid: found.payment.status === "paid" && orderIsDraft
3147
+ });
3148
+ return this.processSuccessfulLocalPayment({
3149
+ ...params,
3150
+ order: found.order,
3151
+ payment: found.payment,
3152
+ matchBy: found.matchedBy === "payment_number" ? "payment_number" : "compat",
3153
+ forceSubmitIfPaid: found.payment.status === "paid" && orderIsDraft,
3154
+ paymentUpdate: incomingPaymentUpdate
3155
+ });
3156
+ }
3157
+ this.logInfo("processLocalPaymentUpdate: 进入 fail 本地更新分支", {
3158
+ ...foundContext,
3159
+ incoming_payment_update: incomingPaymentUpdate
3160
+ });
3161
+ let updated;
3162
+ try {
3163
+ updated = await this.order.updateLocalPayment({
3164
+ saleId: params.saleId,
3165
+ paymentNumber: params.paymentNumber,
3166
+ operationId: params.operationId,
3167
+ updates: incomingPaymentUpdate
3168
+ });
3169
+ } catch (error) {
3170
+ this.logError("processLocalPaymentUpdate: fail 本地更新失败", {
3171
+ ...foundContext,
3172
+ incoming_payment_update: incomingPaymentUpdate,
3173
+ error: error instanceof Error ? error.message : String(error),
3174
+ error_detail: this.normalizeUnknownError(error)
3175
+ });
3176
+ throw error;
3177
+ }
3178
+ if (!updated) {
3179
+ this.logWarning("processLocalPaymentUpdate: fail 本地更新未命中", foundContext);
3180
+ return { code: 404, status: false, message: "本地支付项不存在", data: null };
3181
+ }
3182
+ this.logInfo("processLocalPaymentUpdate: fail 本地更新完成", {
3183
+ ...requestContext,
3184
+ ...this.buildLocalPaymentOrderLogContext(updated.order),
3185
+ previous_payment: updated.previousPayment,
3186
+ payment: updated.payment
3187
+ });
3188
+ return {
3189
+ code: 200,
3190
+ status: true,
3191
+ message: "",
3192
+ data: {
3193
+ local_only: true,
3194
+ payment: updated.payment,
3195
+ order: updated.order
3196
+ }
3197
+ };
3198
+ }
3199
+ async processSuccessfulLocalPayment(params) {
3200
+ var _a, _b, _c, _d, _e;
3201
+ const moduleName = `local_payment_recovery_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
3202
+ const baseSales = new import_BaseSales.BaseSalesImpl(moduleName);
3203
+ const startedAt = Date.now();
3204
+ const recoveryContext = {
3205
+ operation_id: params.operationId,
3206
+ sale_id: params.saleId,
3207
+ payment_number: params.paymentNumber,
3208
+ transaction_number: params.number,
3209
+ requested_status: "success",
3210
+ match_by: params.matchBy,
3211
+ force_submit_if_paid: params.forceSubmitIfPaid,
3212
+ incoming_payment_update: params.paymentUpdate,
3213
+ base_sales_module_name: moduleName,
3214
+ ...this.buildLocalPaymentOrderLogContext(params.order),
3215
+ payment: params.payment
3216
+ };
3217
+ this.logInfo("processSuccessfulLocalPayment: 创建临时 BaseSales", recoveryContext);
3218
+ try {
3219
+ const order = this.sanitizeLocalRecoveryOrder(params.order);
3220
+ this.logInfo("processSuccessfulLocalPayment: 本地订单清洗完成", {
3221
+ ...recoveryContext,
3222
+ sanitized_order_id: order.order_id ?? null,
3223
+ removed_synthetic_order_id: params.order.order_id != null && order.order_id == null
3224
+ });
3225
+ await baseSales.initialize(this.core, {
3226
+ otherParams: {
3227
+ platform: order.platform,
3228
+ businessCode: order.business_code,
3229
+ business_code: order.business_code,
3230
+ channel: order.order_sales_channel || order.sales_channel,
3231
+ type: order.type,
3232
+ enableTempOrderPersist: false
3233
+ }
3234
+ });
3235
+ this.logInfo("processSuccessfulLocalPayment: BaseSales 初始化完成", recoveryContext);
3236
+ await baseSales.replaceTempOrder(order);
3237
+ this.logInfo("processSuccessfulLocalPayment: 本地订单 tempOrder 恢复完成", recoveryContext);
3238
+ this.logInfo("processSuccessfulLocalPayment: 开始更新支付项并提交", {
3239
+ ...recoveryContext,
3240
+ incoming_payment_update: params.paymentUpdate
3241
+ });
3242
+ const result = await baseSales.updateOrderPayment(
3243
+ params.paymentNumber,
3244
+ params.paymentUpdate,
3245
+ {
3246
+ submitWhenPaid: true,
3247
+ matchBy: params.matchBy,
3248
+ forceSubmitIfPaid: params.forceSubmitIfPaid,
3249
+ applyUpdatesWhenPaid: params.forceSubmitIfPaid,
3250
+ smallTicketDataFlag: 1
3251
+ }
3252
+ );
3253
+ this.logInfo("processSuccessfulLocalPayment: 支付恢复完成", {
3254
+ ...recoveryContext,
3255
+ duration_ms: Date.now() - startedAt,
3256
+ payment: result.payment,
3257
+ payments_count: ((_a = result.payments) == null ? void 0 : _a.length) || 0,
3258
+ summary: result.summary,
3259
+ sync_result: result.syncResult ? {
3260
+ is_fully_paid: result.syncResult.isFullyPaid,
3261
+ is_order_fully_paid: result.syncResult.isOrderFullyPaid,
3262
+ is_deposit_fully_paid: result.syncResult.isDepositFullyPaid,
3263
+ payment_stage: result.syncResult.paymentStage,
3264
+ deposit_amount: result.syncResult.depositAmount,
3265
+ order_expected_amount: result.syncResult.orderExpectedAmount,
3266
+ submit_result_code: (_b = result.syncResult.submitResult) == null ? void 0 : _b.code,
3267
+ submit_result_status: (_c = result.syncResult.submitResult) == null ? void 0 : _c.status
3268
+ } : null,
3269
+ draft_result: result.draftResult ? {
3270
+ code: (_d = result.draftResult) == null ? void 0 : _d.code,
3271
+ status: (_e = result.draftResult) == null ? void 0 : _e.status
3272
+ } : null,
3273
+ draft_error: result.draftError instanceof Error ? result.draftError.message : result.draftError
3274
+ });
3275
+ return {
3276
+ code: 200,
3277
+ status: true,
3278
+ message: "",
3279
+ data: {
3280
+ payment: result.payment,
3281
+ payments: result.payments,
3282
+ summary: result.summary,
3283
+ sync_result: result.syncResult
3284
+ }
3285
+ };
3286
+ } catch (error) {
3287
+ const message = error instanceof Error ? error.message : String(error);
3288
+ this.logError("processSuccessfulLocalPayment: 支付恢复失败", {
3289
+ ...recoveryContext,
3290
+ duration_ms: Date.now() - startedAt,
3291
+ error: message,
3292
+ error_detail: this.normalizeUnknownError(error)
3293
+ });
3294
+ return { code: 500, status: false, message, data: null };
3295
+ } finally {
3296
+ try {
3297
+ await baseSales.destroy();
3298
+ this.logInfo("processSuccessfulLocalPayment: 临时 BaseSales 销毁完成", {
3299
+ ...recoveryContext,
3300
+ duration_ms: Date.now() - startedAt
3301
+ });
3302
+ } catch (error) {
3303
+ this.logError("processSuccessfulLocalPayment: 临时 BaseSales 销毁失败", {
3304
+ ...recoveryContext,
3305
+ duration_ms: Date.now() - startedAt,
3306
+ error: error instanceof Error ? error.message : String(error),
3307
+ error_detail: this.normalizeUnknownError(error)
3308
+ });
3309
+ }
3310
+ }
3311
+ }
3312
+ sanitizeLocalRecoveryOrder(order) {
3313
+ const next = {
3314
+ ...order,
3315
+ country_calling_code: String(order.country_calling_code ?? ""),
3316
+ phone: String(order.phone ?? ""),
3317
+ email: String(order.email ?? "")
3318
+ };
3319
+ const externalSaleNumber = next.external_sale_number;
3320
+ if (Number(next.is_draft_order || 0) === 1 && next.order_id !== void 0 && externalSaleNumber !== void 0 && String(next.order_id) === String(externalSaleNumber)) {
3321
+ delete next.order_id;
3322
+ }
3323
+ return next;
3324
+ }
2697
3325
  getDeviceTaskPlugin() {
2698
3326
  var _a, _b;
2699
3327
  return ((_b = (_a = this.app) == null ? void 0 : _a.getPlugin) == null ? void 0 : _b.call(_a, "deviceTask")) || null;
@@ -3106,7 +3734,6 @@ var Server = class {
3106
3734
  });
3107
3735
  }
3108
3736
  async handleOrderCheckoutSubmit(params) {
3109
- var _a;
3110
3737
  const { backendPath, data, title } = params;
3111
3738
  this.logInfo(`${title}: 开始处理`, {
3112
3739
  backendPath,
@@ -3131,15 +3758,14 @@ var Server = class {
3131
3758
  });
3132
3759
  if ((pendingResult == null ? void 0 : pendingResult.status) === true) {
3133
3760
  const pendingOrder = pendingResult.data;
3134
- const platform = (_a = this.core.context) == null ? void 0 : _a.getPlatform();
3135
- const isAndroid = platform === "android";
3136
- if (pendingOrder && this.shouldBuildSmallTicketData(checkoutData) && this.isSalesOrderFullyPaid(pendingOrder) && // TODO 这里先加个兼容,如果是安卓,则需要 type=virtual 才走打印,如果是苹果则无限制
3137
- (isAndroid ? checkoutData.type === "virtual" : true)) {
3761
+ if (pendingOrder && this.shouldBuildSmallTicketData(checkoutData) && this.isSalesOrderFullyPaid(pendingOrder) && this.shouldAutoPrintCheckoutReceipt()) {
3138
3762
  await this.dispatchLocalReceiptPrint({
3139
3763
  checkoutData,
3140
3764
  order: pendingOrder,
3141
3765
  response: pendingResult,
3142
- requireFullyPaid: true
3766
+ requireFullyPaid: true,
3767
+ // 自动小票固定使用 print_all 的收据类型 0,不随订单 type 改成券/手环类型 99。
3768
+ isManualPrint: true
3143
3769
  });
3144
3770
  }
3145
3771
  await this.dispatchCheckoutSyncTask({
@@ -3216,6 +3842,59 @@ var Server = class {
3216
3842
  };
3217
3843
  }
3218
3844
  }
3845
+ async handleOrderDraftSubmit(params) {
3846
+ var _a, _b, _c;
3847
+ const { data, title } = params;
3848
+ this.logInfo(`${title}: 开始处理`, {
3849
+ order_id: data == null ? void 0 : data.order_id,
3850
+ external_sale_number: data == null ? void 0 : data.external_sale_number,
3851
+ order_number: data == null ? void 0 : data.order_number
3852
+ });
3853
+ const normalizedData = await this.normalizeCheckoutSubmitData(data, title);
3854
+ const createdAt = normalizedData.created_at || normalizedData.create_date || (0, import_dayjs.default)().format("YYYY-MM-DD HH:mm:ss");
3855
+ const draftOrder = {
3856
+ ...normalizedData,
3857
+ external_sale_number: normalizedData.external_sale_number || `LOCAL_DRAFT_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`,
3858
+ created_at: createdAt,
3859
+ schedule_date: normalizedData.schedule_date || createdAt,
3860
+ need_sync: 0,
3861
+ is_draft_order: 1
3862
+ };
3863
+ if (!this.order || typeof this.order.upsertLocalDraftOrders !== "function") {
3864
+ this.logError(`${title}: Order 模块不支持本地草稿写入`);
3865
+ return {
3866
+ code: 500,
3867
+ status: false,
3868
+ message: "Order 模块不支持本地草稿写入",
3869
+ data: null
3870
+ };
3871
+ }
3872
+ try {
3873
+ await this.order.upsertLocalDraftOrders([draftOrder]);
3874
+ this.logInfo(`${title}: 草稿订单已写入本地`, {
3875
+ order_id: draftOrder.order_id,
3876
+ external_sale_number: draftOrder.external_sale_number
3877
+ });
3878
+ return {
3879
+ code: 200,
3880
+ status: true,
3881
+ message: "",
3882
+ data: {
3883
+ ...draftOrder,
3884
+ amount_gap: (_a = draftOrder == null ? void 0 : draftOrder.summary) == null ? void 0 : _a.amount_gap,
3885
+ expect_amount: (_b = draftOrder == null ? void 0 : draftOrder.summary) == null ? void 0 : _b.expect_amount,
3886
+ total_amount: (_c = draftOrder == null ? void 0 : draftOrder.summary) == null ? void 0 : _c.total_amount,
3887
+ payment_status: draftOrder == null ? void 0 : draftOrder.payment_status
3888
+ }
3889
+ };
3890
+ } catch (error) {
3891
+ const errorMessage = error instanceof Error ? error.message : String(error);
3892
+ this.logError(`${title}: 写入本地草稿订单失败`, {
3893
+ error: errorMessage
3894
+ });
3895
+ return { code: 500, status: false, message: errorMessage, data: null };
3896
+ }
3897
+ }
3219
3898
  async handlePendingSyncCheckoutOrder(params) {
3220
3899
  var _a, _b, _c, _d;
3221
3900
  const { backendPath, data, title, reason } = params;
@@ -3291,7 +3970,8 @@ var Server = class {
3291
3970
  return null;
3292
3971
  const pendingOrder = {
3293
3972
  ...data,
3294
- need_sync: 1
3973
+ need_sync: 1,
3974
+ is_draft_order: 0
3295
3975
  };
3296
3976
  const productMap = await this.buildSmallTicketProductMap(pendingOrder);
3297
3977
  const orderWithProductTitles = this.withPendingSyncProductTitles(
@@ -3303,6 +3983,15 @@ var Server = class {
3303
3983
  { requireFullyPaid: true, productMap }
3304
3984
  );
3305
3985
  }
3986
+ /**
3987
+ * Android 结账自动小票由当前设备设置控制;其它平台保持原有行为。
3988
+ */
3989
+ shouldAutoPrintCheckoutReceipt() {
3990
+ var _a, _b, _c, _d, _e;
3991
+ if (((_b = (_a = this.core.context) == null ? void 0 : _a.getPlatform) == null ? void 0 : _b.call(_a)) !== "android")
3992
+ return true;
3993
+ return ((_e = (_d = (_c = this.app) == null ? void 0 : _c.storage) == null ? void 0 : _d.getStorage) == null ? void 0 : _e.call(_d, AUTO_PRINT_STORAGE_KEY)) === "true";
3994
+ }
3306
3995
  shouldBuildSmallTicketData(order) {
3307
3996
  if (!order || typeof order !== "object")
3308
3997
  return false;
@@ -3344,7 +4033,7 @@ var Server = class {
3344
4033
  return 0;
3345
4034
  return payments.reduce((sum, payment) => {
3346
4035
  const status = String((payment == null ? void 0 : payment.status) || "").toLowerCase();
3347
- if (status === "voided" || status === "failed" || status === "cancelled")
4036
+ if (status !== "paid")
3348
4037
  return sum;
3349
4038
  return sum + this.toAmountNumber((payment == null ? void 0 : payment.amount) ?? (payment == null ? void 0 : payment.origin_amount));
3350
4039
  }, 0);
@@ -3911,9 +4600,11 @@ var Server = class {
3911
4600
  return value === void 0 ? void 0 : String(value);
3912
4601
  }
3913
4602
  buildCheckoutSyncSuccessPatch(fresh) {
4603
+ const patch = {
4604
+ is_draft_order: 0
4605
+ };
3914
4606
  if (!fresh)
3915
- return {};
3916
- const patch = {};
4607
+ return patch;
3917
4608
  if (fresh.order_id !== void 0 && fresh.order_id !== null)
3918
4609
  patch.order_id = fresh.order_id;
3919
4610
  if (fresh.order_number !== void 0 && fresh.order_number !== null) {
@@ -4194,6 +4885,10 @@ var Server = class {
4194
4885
  const rawList = this.order.getOrders();
4195
4886
  this.logInfo("computeOrderQueryResult: 本地订单数量", { rawCount: rawList.length });
4196
4887
  const result = (0, import_filterOrders.filterOrders)(rawList, data);
4888
+ result.list.forEach((order) => {
4889
+ var _a;
4890
+ order.payments = (_a = order.payments) == null ? void 0 : _a.filter((payment) => !(0, import_payment_utils.isPendingOrderPayment)(payment));
4891
+ });
4197
4892
  this.logInfo("computeOrderQueryResult: 过滤结果", {
4198
4893
  rawCount: rawList.length,
4199
4894
  filteredCount: result.count,