@pisell/pisellos 2.3.36 → 2.3.38

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 (90) 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/type.d.ts +90 -2
  11. package/dist/model/strategy/adapter/promotion/type.js +45 -0
  12. package/dist/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +5 -3
  13. package/dist/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +6 -3
  14. package/dist/modules/OpenData/index.d.ts +2 -0
  15. package/dist/modules/OpenData/index.js +8 -0
  16. package/dist/modules/Order/index.js +21 -14
  17. package/dist/modules/Order/utils.js +2 -1
  18. package/dist/modules/Payment/index.js +1 -1
  19. package/dist/modules/Payment/types.d.ts +73 -4
  20. package/dist/modules/Payment/types.js +18 -3
  21. package/dist/modules/Payment/walletpass.d.ts +25 -1
  22. package/dist/modules/Payment/walletpass.js +707 -157
  23. package/dist/modules/Rules/index.d.ts +1 -0
  24. package/dist/modules/Rules/index.js +75 -49
  25. package/dist/server/index.js +28 -24
  26. package/dist/server/modules/order/index.d.ts +5 -0
  27. package/dist/server/modules/order/index.js +52 -31
  28. package/dist/solution/BaseSales/index.d.ts +77 -11
  29. package/dist/solution/BaseSales/index.js +1476 -434
  30. package/dist/solution/BaseSales/types.d.ts +52 -0
  31. package/dist/solution/BaseSales/types.js +2 -1
  32. package/dist/solution/BaseSales/utils/cartPromotion.d.ts +3 -1
  33. package/dist/solution/BaseSales/utils/cartPromotion.js +90 -31
  34. package/dist/solution/BookingByStep/index.d.ts +1 -1
  35. package/dist/solution/BookingTicket/index.d.ts +7 -3
  36. package/dist/solution/BookingTicket/index.js +130 -48
  37. package/dist/solution/BookingTicket/utils/addProductDecision.js +6 -0
  38. package/dist/solution/BookingTicket/utils/scan/applyGlobalScan.d.ts +8 -0
  39. package/dist/solution/BookingTicket/utils/scan/applyGlobalScan.js +172 -82
  40. package/dist/solution/BookingTicket/utils/weighingProductSku.d.ts +5 -0
  41. package/dist/solution/BookingTicket/utils/weighingProductSku.js +45 -0
  42. package/dist/solution/ScanOrder/index.js +31 -20
  43. package/dist/solution/VenueBooking/index.js +30 -19
  44. package/dist/types/index.d.ts +1 -0
  45. package/lib/core/index.d.ts +2 -0
  46. package/lib/core/index.js +4 -0
  47. package/lib/model/strategy/adapter/promotion/adapter.d.ts +4 -0
  48. package/lib/model/strategy/adapter/promotion/adapter.js +20 -0
  49. package/lib/model/strategy/adapter/promotion/evaluator.d.ts +17 -0
  50. package/lib/model/strategy/adapter/promotion/evaluator.js +235 -0
  51. package/lib/model/strategy/adapter/promotion/examples.d.ts +86 -0
  52. package/lib/model/strategy/adapter/promotion/examples.js +91 -0
  53. package/lib/model/strategy/adapter/promotion/index.d.ts +1 -1
  54. package/lib/model/strategy/adapter/promotion/index.js +2 -0
  55. package/lib/model/strategy/adapter/promotion/type.d.ts +90 -2
  56. package/lib/model/strategy/adapter/promotion/type.js +2 -0
  57. package/lib/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +2 -1
  58. package/lib/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +2 -1
  59. package/lib/modules/OpenData/index.d.ts +2 -0
  60. package/lib/modules/OpenData/index.js +5 -0
  61. package/lib/modules/Order/index.js +10 -4
  62. package/lib/modules/Order/utils.js +6 -1
  63. package/lib/modules/Payment/index.js +1 -1
  64. package/lib/modules/Payment/types.d.ts +73 -4
  65. package/lib/modules/Payment/types.js +4 -3
  66. package/lib/modules/Payment/walletpass.d.ts +25 -1
  67. package/lib/modules/Payment/walletpass.js +470 -21
  68. package/lib/modules/Rules/index.d.ts +1 -0
  69. package/lib/modules/Rules/index.js +30 -7
  70. package/lib/server/index.js +11 -8
  71. package/lib/server/modules/order/index.d.ts +5 -0
  72. package/lib/server/modules/order/index.js +24 -2
  73. package/lib/solution/BaseSales/index.d.ts +77 -11
  74. package/lib/solution/BaseSales/index.js +699 -41
  75. package/lib/solution/BaseSales/types.d.ts +52 -0
  76. package/lib/solution/BaseSales/types.js +2 -1
  77. package/lib/solution/BaseSales/utils/cartPromotion.d.ts +3 -1
  78. package/lib/solution/BaseSales/utils/cartPromotion.js +63 -12
  79. package/lib/solution/BookingByStep/index.d.ts +1 -1
  80. package/lib/solution/BookingTicket/index.d.ts +7 -3
  81. package/lib/solution/BookingTicket/index.js +60 -8
  82. package/lib/solution/BookingTicket/utils/addProductDecision.js +4 -0
  83. package/lib/solution/BookingTicket/utils/scan/applyGlobalScan.d.ts +8 -0
  84. package/lib/solution/BookingTicket/utils/scan/applyGlobalScan.js +36 -4
  85. package/lib/solution/BookingTicket/utils/weighingProductSku.d.ts +5 -0
  86. package/lib/solution/BookingTicket/utils/weighingProductSku.js +63 -0
  87. package/lib/solution/ScanOrder/index.js +8 -0
  88. package/lib/solution/VenueBooking/index.js +8 -0
  89. package/lib/types/index.d.ts +1 -0
  90. package/package.json +2 -2
@@ -49,6 +49,88 @@ var import_Quotation = require("../../modules/Quotation");
49
49
  var import_transformBaseProductToOrderProduct = require("./utils/transformBaseProductToOrderProduct");
50
50
  var import_quotationPrice = require("./utils/quotationPrice");
51
51
  var SERVER_ORDER_CHANGED_EVENT = "order:onOrdersChanged";
52
+ var WALLET_PAYMENT_CODE_BY_TAG = {
53
+ product_voucher: "PRODUCTVOUCHER",
54
+ gift_card: "GIFTCARD",
55
+ point_card: "POINTCARD"
56
+ };
57
+ function getSharedWalletAssetId(asset) {
58
+ return (asset == null ? void 0 : asset.id) ?? (asset == null ? void 0 : asset.voucher_id) ?? (asset == null ? void 0 : asset.code);
59
+ }
60
+ function getSharedWalletAssetAmount(asset) {
61
+ const value = (asset == null ? void 0 : asset.edit_current_amount) ?? (asset == null ? void 0 : asset._available_max_amount) ?? ((asset == null ? void 0 : asset.tag) === "point_card" ? asset == null ? void 0 : asset.recommended_usage_amount : asset == null ? void 0 : asset.available_max_amount) ?? (asset == null ? void 0 : asset.actualDeduction) ?? (asset == null ? void 0 : asset.amount) ?? 0;
62
+ const amount = Number(value);
63
+ return Number.isFinite(amount) && amount > 0 ? amount : 0;
64
+ }
65
+ function isRejectedSalesSubmitResult(result) {
66
+ var _a, _b;
67
+ const candidates = [
68
+ result,
69
+ result == null ? void 0 : result.data,
70
+ (_a = result == null ? void 0 : result.response) == null ? void 0 : _a.data,
71
+ (_b = result == null ? void 0 : result.response) == null ? void 0 : _b.body
72
+ ];
73
+ return candidates.some((candidate) => {
74
+ if (!candidate || typeof candidate !== "object")
75
+ return false;
76
+ const responseCode = Number(candidate.code ?? candidate.statusCode);
77
+ if (Number.isFinite(responseCode) && responseCode >= 400)
78
+ return true;
79
+ if (candidate.status === false || candidate.success === false || candidate.result === false) {
80
+ return true;
81
+ }
82
+ return ["failed", "error"].includes(
83
+ String(candidate.status || "").toLowerCase()
84
+ );
85
+ });
86
+ }
87
+ function getSalesSubmitErrorMessage(result) {
88
+ var _a, _b;
89
+ return (result == null ? void 0 : result.message) || (result == null ? void 0 : result.msg) || ((_a = result == null ? void 0 : result.data) == null ? void 0 : _a.message) || ((_b = result == null ? void 0 : result.data) == null ? void 0 : _b.msg) || "Wallet 支付确认失败";
90
+ }
91
+ function toWalletFundValue(value) {
92
+ try {
93
+ return new import_decimal.default(value || 0).toDecimalPlaces(2).toFixed(2);
94
+ } catch {
95
+ return "0.00";
96
+ }
97
+ }
98
+ function subtractWalletFundValue(left, right) {
99
+ try {
100
+ return new import_decimal.default(left || 0).minus(right || 0).toDecimalPlaces(2).toFixed(2);
101
+ } catch {
102
+ return "0.00";
103
+ }
104
+ }
105
+ function getSharedWalletPassUseValue(params) {
106
+ const { asset, amount, paymentMethod } = params;
107
+ const configuredValue = Number(asset == null ? void 0 : asset.wallet_pass_use_value);
108
+ if (Number.isFinite(configuredValue) && configuredValue > 0) {
109
+ return configuredValue;
110
+ }
111
+ const isPointCard = (asset == null ? void 0 : asset.tag) === "point_card" || paymentMethod.code === "POINTCARD";
112
+ const usageUnit = asset == null ? void 0 : asset.wallet_pass_usage_unit;
113
+ if (isPointCard && (usageUnit == null ? void 0 : usageUnit.points) && (usageUnit == null ? void 0 : usageUnit.price)) {
114
+ return new import_decimal.default(amount).div(usageUnit.price).mul(usageUnit.points).toDecimalPlaces(2, import_decimal.default.ROUND_DOWN).toNumber();
115
+ }
116
+ return amount;
117
+ }
118
+ function buildSharedWalletFundRecord(params) {
119
+ const { asset, amount, paymentMethod } = params;
120
+ const isPointCard = (asset == null ? void 0 : asset.tag) === "point_card" || paymentMethod.code === "POINTCARD";
121
+ const usingBeforeValue = (asset == null ? void 0 : asset.balance_par_value) ?? (asset == null ? void 0 : asset.balance) ?? (asset == null ? void 0 : asset.using_before_value) ?? 0;
122
+ const walletPassUseValue = getSharedWalletPassUseValue(params);
123
+ const usingValue = isPointCard ? walletPassUseValue : amount;
124
+ const walletPassName = (asset == null ? void 0 : asset.wallet_pass_name) || (asset == null ? void 0 : asset.format_title) || (asset == null ? void 0 : asset.product_name) || (asset == null ? void 0 : asset.name) || paymentMethod.name;
125
+ return {
126
+ voucher_id: (asset == null ? void 0 : asset.voucher_id) ?? (asset == null ? void 0 : asset.id),
127
+ wallet_pass_name: walletPassName && typeof walletPassName === "object" ? walletPassName : { default: walletPassName || "" },
128
+ code: (asset == null ? void 0 : asset.code) || (asset == null ? void 0 : asset.tag),
129
+ encoded: asset == null ? void 0 : asset.encoded,
130
+ using_before_value: toWalletFundValue(usingBeforeValue),
131
+ using_after_value: subtractWalletFundValue(usingBeforeValue, usingValue)
132
+ };
133
+ }
52
134
  function isBaseSalesManualProductDiscountProduct(product) {
53
135
  var _a, _b;
54
136
  return ((_a = product == null ? void 0 : product.metadata) == null ? void 0 : _a.is_manual_discount) === true || ((_b = product == null ? void 0 : product.metadata) == null ? void 0 : _b.is_manual_discount) === 1 || ((product == null ? void 0 : product.discount_list) || []).some((item) => (item == null ? void 0 : item.type) === "product");
@@ -309,6 +391,18 @@ function mergeSalesDetailRecordWithTempOrder(currentTempOrder, loadedRecord, str
309
391
  (0, import_orderCollectionIdentity.applyOrderCollectionUidRemaps)(mergedRecord, uidRemaps)
310
392
  );
311
393
  }
394
+ function filterPendingPaymentsFromLoadedSalesDetail(sourceRecord) {
395
+ if (!sourceRecord || typeof sourceRecord !== "object")
396
+ return sourceRecord;
397
+ const record = { ...sourceRecord };
398
+ if (Array.isArray(sourceRecord.payments)) {
399
+ record.payments = sourceRecord.payments.filter((payment) => !(0, import_payment_utils.isPendingOrderPayment)(payment));
400
+ }
401
+ if (Array.isArray(sourceRecord.payment)) {
402
+ record.payment = sourceRecord.payment.filter((payment) => !(0, import_payment_utils.isPendingOrderPayment)(payment));
403
+ }
404
+ return record;
405
+ }
312
406
  var BaseSalesImpl = class extends import_BaseModule.BaseModule {
313
407
  constructor(name, version) {
314
408
  super(name, version);
@@ -320,6 +414,11 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
320
414
  order: void 0
321
415
  };
322
416
  this.otherParams = {};
417
+ /**
418
+ * 由已在初始化阶段解析过设备短号的业务解决方案写入。
419
+ * 未设置时,支付编号仍按原逻辑实时读取设备信息。
420
+ */
421
+ this.paymentNumberDevicePrefix = null;
323
422
  // 当前缓存中已加载的最新 SalesDetail;只读 getter 走它对外暴露详情数据。
324
423
  this.currentSalesDetail = null;
325
424
  this.discountConfigCacheKey = null;
@@ -331,6 +430,7 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
331
430
  this.autoPaymentSyncTimer = null;
332
431
  this.salesDetailLoadInFlightCount = 0;
333
432
  this.salesDetailLoadSequence = 0;
433
+ this.walletAssetsRefreshSequence = 0;
334
434
  this.salesDetailHydrateQueue = Promise.resolve();
335
435
  this.serverOrderChangeUnsubscribe = null;
336
436
  this.serverOrderChangeHydrateInFlight = null;
@@ -850,6 +950,14 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
850
950
  const coreData = (_f = (_d = (_c = app == null ? void 0 : app.models) == null ? void 0 : _c.getStore) == null ? void 0 : (_e = _d.call(_c)).getDataByModel) == null ? void 0 : _f.call(_e, "core", "core");
851
951
  return coreData == null ? void 0 : coreData[key];
852
952
  }
953
+ /**
954
+ * 复用业务解决方案初始化时已解析的支付设备短号。
955
+ * 空值会清除缓存,使后续支付恢复实时读取设备信息的兼容路径。
956
+ */
957
+ setPaymentNumberDevicePrefix(prefix) {
958
+ const normalized = String(prefix ?? "").trim();
959
+ this.paymentNumberDevicePrefix = normalized || null;
960
+ }
853
961
  getPaymentNumberDevicePrefixSync() {
854
962
  if (!this.appPlugin)
855
963
  return "LOCAL";
@@ -858,6 +966,9 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
858
966
  getPaymentNumberDevicePrefixAsync() {
859
967
  if (!this.appPlugin)
860
968
  return Promise.resolve("LOCAL");
969
+ if (this.paymentNumberDevicePrefix) {
970
+ return Promise.resolve(this.paymentNumberDevicePrefix);
971
+ }
861
972
  return (0, import_payment_number.resolvePaymentNumberDevicePrefix)((key) => this.getAppData(key));
862
973
  }
863
974
  syncAppDataToTempOrder(tempOrder) {
@@ -900,6 +1011,7 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
900
1011
  var _a, _b;
901
1012
  this.core = core;
902
1013
  this.initializeOptions = options || {};
1014
+ this.paymentNumberDevicePrefix = null;
903
1015
  this.otherParams = options.otherParams || {};
904
1016
  this.cacheId = (_a = this.otherParams) == null ? void 0 : _a.cacheId;
905
1017
  this.reusedSharedSubModuleNames.clear();
@@ -961,8 +1073,10 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
961
1073
  }
962
1074
  });
963
1075
  this.currentSalesDetail = null;
1076
+ this.paymentNumberDevicePrefix = null;
964
1077
  this.queuedServerOrderChanges = [];
965
1078
  this.salesDetailLoadSequence += 1;
1079
+ this.walletAssetsRefreshSequence += 1;
966
1080
  await this.core.effects.emit(`${this.name}:onDestroy`, {});
967
1081
  super.destroy();
968
1082
  }
@@ -994,13 +1108,16 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
994
1108
  const message = loadedRecord && (loadedRecord.message || loadedRecord.msg) || `加载销售详情失败: ${lookup}`;
995
1109
  throw new Error(message);
996
1110
  }
997
- const remoteRecord = preloadedSalesDetail ?? loadedRecord.data;
1111
+ const remoteRecord = filterPendingPaymentsFromLoadedSalesDetail(
1112
+ preloadedSalesDetail ?? loadedRecord.data
1113
+ );
998
1114
  const currentTempOrder = params.merge ? this.store.order.getTempOrder() : null;
999
- const record = params.merge ? mergeSalesDetailRecordWithTempOrder(
1115
+ const mergedRecord = params.merge ? mergeSalesDetailRecordWithTempOrder(
1000
1116
  currentTempOrder,
1001
1117
  remoteRecord,
1002
1118
  "preserve-local"
1003
1119
  ) : remoteRecord;
1120
+ const record = filterPendingPaymentsFromLoadedSalesDetail(mergedRecord);
1004
1121
  return await this.hydrateLatestLoadedSalesDetail(record, loadSequence);
1005
1122
  } finally {
1006
1123
  this.salesDetailLoadInFlightCount = Math.max(
@@ -1176,6 +1293,7 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
1176
1293
  // 重置 tempOrder 与与上一单强相关的运行态(resource/entryPaxNumber/enabledReservationRuleProducts),
1177
1294
  // 适用于从支付页通过 react-router 跳回继续下单时清理残留数据
1178
1295
  async restoreOrder() {
1296
+ var _a;
1179
1297
  try {
1180
1298
  if (!this.store.order) {
1181
1299
  throw new Error("scanOrder 解决方案需要 order 模块支持");
@@ -1184,6 +1302,12 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
1184
1302
  this.currentSalesDetail = null;
1185
1303
  this.discountConfigCacheKey = null;
1186
1304
  this.hasManualDiscountSelection = false;
1305
+ const wallet = (_a = this.store.payment) == null ? void 0 : _a.wallet;
1306
+ if (wallet) {
1307
+ this.walletAssetsRefreshSequence += 1;
1308
+ wallet.clearAllCache();
1309
+ await this.publishWalletAssetsState(wallet.getWalletAssetsState());
1310
+ }
1187
1311
  await this.effectsEmit("onTempOrderReplaced", { tempOrder });
1188
1312
  if (this.syncAppDataToTempOrder(tempOrder) && this.isTempOrderPersistEnabled()) {
1189
1313
  this.store.order.persistTempOrder();
@@ -1799,7 +1923,7 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
1799
1923
  return this.store.order.saveTempOrderAsDraft({
1800
1924
  cacheId: this.cacheId,
1801
1925
  platform: (params == null ? void 0 : params.platform) ?? ((_a = this.otherParams) == null ? void 0 : _a.platform),
1802
- businessCode: (params == null ? void 0 : params.businessCode) ?? ((_b = this.otherParams) == null ? void 0 : _b.businessCode) ?? ((_c = this.otherParams) == null ? void 0 : _c.business_code),
1926
+ businessCode: (params == null ? void 0 : params.businessCode) || ((_b = this.otherParams) == null ? void 0 : _b.businessCode) || ((_c = this.otherParams) == null ? void 0 : _c.business_code),
1803
1927
  channel: (params == null ? void 0 : params.channel) ?? this.getSubmitOrderSalesChannel(),
1804
1928
  type: (params == null ? void 0 : params.type) ?? ((_d = this.otherParams) == null ? void 0 : _d.type),
1805
1929
  ...(params == null ? void 0 : params.enhancePayload) !== void 0 ? { enhancePayload: params.enhancePayload } : {}
@@ -2017,7 +2141,10 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
2017
2141
  hasPaymentNumber ? "LOCAL" : this.getPaymentNumberDevicePrefixSync()
2018
2142
  );
2019
2143
  }
2020
- /** 创建新支付项时读取最新设备短号,不缓存运行时设备信息。 */
2144
+ /**
2145
+ * 创建新支付项时优先使用业务解决方案初始化阶段缓存的设备短号;
2146
+ * 未缓存的通用 BaseSales 保持实时读取设备信息的兼容语义。
2147
+ */
2021
2148
  async addOrderPaymentAsync(payment) {
2022
2149
  const paymentRecord = payment;
2023
2150
  const hasPaymentNumber = String(paymentRecord.payment_number || "").trim() !== "";
@@ -2170,7 +2297,16 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
2170
2297
  confirmPendingVoucherPayments() {
2171
2298
  if (!this.store.order)
2172
2299
  throw new Error("order 模块未初始化");
2173
- return this.store.order.confirmPendingVoucherPayments();
2300
+ const payments = this.store.order.confirmPendingVoucherPayments();
2301
+ if (this.store.payment) {
2302
+ const committed = this.getCommittedWalletAssets();
2303
+ const state = this.store.payment.wallet.setCommittedWalletAssets(
2304
+ committed.ids,
2305
+ committed.assets
2306
+ );
2307
+ void this.publishWalletAssetsState(state);
2308
+ }
2309
+ return payments;
2174
2310
  }
2175
2311
  discardPendingVoucherPayments() {
2176
2312
  if (!this.store.order)
@@ -2231,24 +2367,14 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
2231
2367
  });
2232
2368
  return walletProducts;
2233
2369
  }
2234
- async initWalletData(params) {
2235
- if (!this.store.order)
2236
- throw new Error("order 模块未初始化");
2237
- if (!this.store.payment)
2238
- throw new Error("payment 模块未初始化");
2239
- const snapshot = this.store.order.getOrderSnapshot();
2370
+ buildWalletInitBusinessData(params) {
2371
+ var _a;
2372
+ const snapshot = (_a = this.store.order) == null ? void 0 : _a.getOrderSnapshot();
2240
2373
  const tempOrder = snapshot == null ? void 0 : snapshot.tempOrder;
2241
2374
  const amount = snapshot == null ? void 0 : snapshot.amount;
2242
- if (!tempOrder || !amount) {
2243
- return {
2244
- walletRecommendList: [],
2245
- userIdentificationCodes: [],
2246
- transformList: [],
2247
- noApplicableVoucher: [],
2248
- products: []
2249
- };
2250
- }
2251
- const walletBusinessData = {
2375
+ if (!tempOrder || !amount)
2376
+ return null;
2377
+ return {
2252
2378
  customer_id: tempOrder.customer_id ? Number(tempOrder.customer_id) : void 0,
2253
2379
  holder: tempOrder.holder,
2254
2380
  amountInfo: {
@@ -2262,11 +2388,29 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
2262
2388
  is_price_include_tax: tempOrder.is_price_include_tax ?? this.otherParams.is_price_include_tax ?? 1,
2263
2389
  ...snapshot.identity.orderId ? { payment_order_id: String(snapshot.identity.orderId) } : {}
2264
2390
  };
2391
+ }
2392
+ async initWalletData(params) {
2393
+ var _a;
2394
+ if (!this.store.order)
2395
+ throw new Error("order 模块未初始化");
2396
+ if (!this.store.payment)
2397
+ throw new Error("payment 模块未初始化");
2398
+ const snapshot = this.store.order.getOrderSnapshot();
2399
+ const walletBusinessData = this.buildWalletInitBusinessData(params);
2400
+ if (!walletBusinessData) {
2401
+ return {
2402
+ walletRecommendList: [],
2403
+ userIdentificationCodes: [],
2404
+ transformList: [],
2405
+ noApplicableVoucher: [],
2406
+ products: []
2407
+ };
2408
+ }
2265
2409
  const result = await this.store.payment.wallet.initializeWalletDataFromBusinessAsync(
2266
2410
  walletBusinessData
2267
2411
  );
2268
2412
  await this.core.effects.emit(`${this.name}:onWalletDataInitialized`, {
2269
- orderId: snapshot.identity.orderId,
2413
+ orderId: (_a = snapshot == null ? void 0 : snapshot.identity) == null ? void 0 : _a.orderId,
2270
2414
  customerId: walletBusinessData.customer_id,
2271
2415
  walletBusinessData: {
2272
2416
  customer_id: walletBusinessData.customer_id,
@@ -2277,6 +2421,202 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
2277
2421
  });
2278
2422
  return result;
2279
2423
  }
2424
+ getCommittedWalletAssets() {
2425
+ var _a, _b;
2426
+ const payments = ((_b = (_a = this.store.order) == null ? void 0 : _a.getOrderPayments) == null ? void 0 : _b.call(_a)) || [];
2427
+ const committed = payments.filter((payment) => {
2428
+ const voucherId = payment == null ? void 0 : payment.voucher_id;
2429
+ if (voucherId === void 0 || voucherId === null || voucherId === 0) {
2430
+ return false;
2431
+ }
2432
+ return (payment == null ? void 0 : payment.status) === "paid" || (payment == null ? void 0 : payment.isSynced) === true || (payment == null ? void 0 : payment.order_payment_id) !== void 0 && (payment == null ? void 0 : payment.order_payment_id) !== null && (payment == null ? void 0 : payment.order_payment_id) !== "";
2433
+ });
2434
+ return {
2435
+ ids: committed.map((payment) => payment.voucher_id),
2436
+ assets: committed.map((payment) => {
2437
+ var _a2;
2438
+ const fundRecord = (payment == null ? void 0 : payment.fund_record) || {};
2439
+ const walletPassName = fundRecord.wallet_pass_name;
2440
+ const title = walletPassName && typeof walletPassName === "object" ? walletPassName.default || Object.values(walletPassName)[0] : walletPassName;
2441
+ const code = String((payment == null ? void 0 : payment.code) || "").toUpperCase();
2442
+ const tag = ((_a2 = Object.entries(WALLET_PAYMENT_CODE_BY_TAG).find(
2443
+ ([, paymentCode]) => paymentCode === code
2444
+ )) == null ? void 0 : _a2[0]) || fundRecord.tag;
2445
+ return {
2446
+ id: payment.voucher_id,
2447
+ voucher_id: payment.voucher_id,
2448
+ code: fundRecord.code,
2449
+ tag,
2450
+ product_name: title || payment.name,
2451
+ name: title || payment.name,
2452
+ amount: Number(payment.amount || 0),
2453
+ _available_max_amount: Number(payment.amount || 0),
2454
+ unified_available_status: 1,
2455
+ _unified_available_status: 1,
2456
+ _committed_payment: payment
2457
+ };
2458
+ })
2459
+ };
2460
+ }
2461
+ async publishWalletAssetsState(state) {
2462
+ await this.effectsEmit(import_types.BaseSalesHookNames.onWalletAssetsStateChanged, {
2463
+ state
2464
+ });
2465
+ return state;
2466
+ }
2467
+ async syncSelectedWalletAssets(state) {
2468
+ if (!this.store.order || !this.store.payment) {
2469
+ throw new Error("订单或支付模块未初始化");
2470
+ }
2471
+ let paymentMethods = this.getPaymentMethods();
2472
+ if (!paymentMethods.length && state.selectedItems.length) {
2473
+ paymentMethods = await this.getPaymentMethodsAsync();
2474
+ }
2475
+ const methodByCode = new Map(
2476
+ paymentMethods.map((method) => [String(method.code || "").toUpperCase(), method])
2477
+ );
2478
+ const preparePayments = this.store.payment.wallet.formatWalletPassList2PreparePayments(
2479
+ state.selectedItems
2480
+ );
2481
+ const selectedAssetById = new Map(
2482
+ state.selectedItems.map((asset) => [
2483
+ String(getSharedWalletAssetId(asset)),
2484
+ asset
2485
+ ])
2486
+ );
2487
+ const voucherPayments = preparePayments.flatMap((item) => {
2488
+ const asset = selectedAssetById.get(String(item.voucher_id));
2489
+ const paymentCode = WALLET_PAYMENT_CODE_BY_TAG[String(item.tag || (asset == null ? void 0 : asset.tag) || "")] || String(item.tag || (asset == null ? void 0 : asset.tag) || "").toUpperCase();
2490
+ const paymentMethod = methodByCode.get(paymentCode);
2491
+ const formattedAmount = Number(item.amount);
2492
+ const amount = Number.isFinite(formattedAmount) && formattedAmount > 0 ? formattedAmount : getSharedWalletAssetAmount(asset);
2493
+ if (!asset || !paymentMethod || !Number.isFinite(amount) || amount <= 0) {
2494
+ return [];
2495
+ }
2496
+ const walletPassUseValue = getSharedWalletPassUseValue({
2497
+ asset,
2498
+ amount,
2499
+ paymentMethod
2500
+ });
2501
+ return [
2502
+ {
2503
+ amount: amount.toFixed(2),
2504
+ code: paymentMethod.code,
2505
+ custom_payment_id: paymentMethod.id,
2506
+ name: paymentMethod.name,
2507
+ type: paymentMethod.type,
2508
+ voucher_id: item.voucher_id,
2509
+ wallet_pass_usage_unit: item.wallet_pass_usage_unit,
2510
+ wallet_pass_use_value: walletPassUseValue,
2511
+ fund_record: buildSharedWalletFundRecord({
2512
+ asset,
2513
+ amount,
2514
+ paymentMethod
2515
+ })
2516
+ }
2517
+ ];
2518
+ });
2519
+ const payments = this.updateVoucherOrderPayments(voucherPayments, {
2520
+ status: "payment_pending"
2521
+ });
2522
+ await this.store.order.recalculateSummary({ createIfMissing: true });
2523
+ return payments;
2524
+ }
2525
+ /** 读取购物车与 Shared Checkout 共用的钱包资产状态。 */
2526
+ getWalletAssetsState() {
2527
+ if (!this.store.payment)
2528
+ throw new Error("payment 模块未初始化");
2529
+ return this.store.payment.wallet.getWalletAssetsState();
2530
+ }
2531
+ /** 导出 Shared Checkout 跨运行时恢复所需的钱包状态与搜索缓存。 */
2532
+ exportWalletAssetsSnapshot() {
2533
+ if (!this.store.payment)
2534
+ throw new Error("payment 模块未初始化");
2535
+ return this.store.payment.wallet.exportWalletAssetsSnapshot();
2536
+ }
2537
+ /**
2538
+ * 恢复 Shared Checkout 钱包快照,并把选择重新同步为订单 pending payment。
2539
+ */
2540
+ async importWalletAssetsSnapshot(snapshot) {
2541
+ if (!this.store.payment)
2542
+ throw new Error("payment 模块未初始化");
2543
+ const state = this.store.payment.wallet.importWalletAssetsSnapshot(snapshot);
2544
+ await this.syncSelectedWalletAssets(state);
2545
+ return this.publishWalletAssetsState(state);
2546
+ }
2547
+ /**
2548
+ * 基于当前订单快照刷新钱包资产。商品变化保留并重验选择;客户变化强制清空。
2549
+ */
2550
+ async refreshWalletAssets(params) {
2551
+ if (!this.store.order)
2552
+ throw new Error("order 模块未初始化");
2553
+ if (!this.store.payment)
2554
+ throw new Error("payment 模块未初始化");
2555
+ const refreshSequence = ++this.walletAssetsRefreshSequence;
2556
+ const wallet = this.store.payment.wallet;
2557
+ const businessData = this.buildWalletInitBusinessData(params);
2558
+ if (!businessData) {
2559
+ wallet.clearAllCache();
2560
+ const emptyState = wallet.getWalletAssetsState();
2561
+ this.updateVoucherOrderPayments([], { status: "payment_pending" });
2562
+ return this.publishWalletAssetsState(emptyState);
2563
+ }
2564
+ const previousState = wallet.getWalletAssetsState();
2565
+ const hasPreviousWalletContext = previousState.status !== "idle" || previousState.items.length > 0 || previousState.selectedIds.length > 0 || previousState.customerId !== void 0;
2566
+ const customerChanged = hasPreviousWalletContext && String(previousState.customerId ?? "") !== String(businessData.customer_id ?? "");
2567
+ if (customerChanged) {
2568
+ const clearedState = wallet.clearWalletAssetSelection();
2569
+ this.updateVoucherOrderPayments([], { status: "payment_pending" });
2570
+ await this.publishWalletAssetsState(clearedState);
2571
+ }
2572
+ const refreshTask = wallet.refreshWalletAssetsFromBusinessAsync(
2573
+ businessData,
2574
+ {
2575
+ preserveSelection: (params == null ? void 0 : params.preserveSelection) !== false && !customerChanged
2576
+ }
2577
+ );
2578
+ await this.publishWalletAssetsState(wallet.getWalletAssetsState());
2579
+ let state = await refreshTask;
2580
+ if (refreshSequence !== this.walletAssetsRefreshSequence) {
2581
+ return wallet.getWalletAssetsState();
2582
+ }
2583
+ if (state.status !== "error") {
2584
+ const committed = this.getCommittedWalletAssets();
2585
+ state = wallet.setCommittedWalletAssets(committed.ids, committed.assets);
2586
+ }
2587
+ await this.syncSelectedWalletAssets(state);
2588
+ return this.publishWalletAssetsState(state);
2589
+ }
2590
+ /** 选择或取消单张钱包资产,并立即同步 pending wallet payments。 */
2591
+ async selectWalletAsset(params) {
2592
+ if (!this.store.payment)
2593
+ throw new Error("payment 模块未初始化");
2594
+ const state = this.store.payment.wallet.selectWalletAsset(params.assetId, {
2595
+ selected: params.selected,
2596
+ source: params.source
2597
+ });
2598
+ await this.syncSelectedWalletAssets(state);
2599
+ return this.publishWalletAssetsState(state);
2600
+ }
2601
+ /** 扫码精确查券;仅可用资产会被自动选中。 */
2602
+ async scanWalletAsset(code) {
2603
+ if (!this.store.payment)
2604
+ throw new Error("payment 模块未初始化");
2605
+ const result = await this.store.payment.wallet.scanWalletAssetAsync(code);
2606
+ if (result.type === "normalCode") {
2607
+ await this.syncSelectedWalletAssets(result.state);
2608
+ await this.publishWalletAssetsState(result.state);
2609
+ }
2610
+ return result;
2611
+ }
2612
+ /** 清空全部未确认钱包选择,已支付钱包项由 Order 模块继续保留。 */
2613
+ async clearWalletAssetSelection() {
2614
+ if (!this.store.payment)
2615
+ throw new Error("payment 模块未初始化");
2616
+ const state = this.store.payment.wallet.clearWalletAssetSelection();
2617
+ await this.syncSelectedWalletAssets(state);
2618
+ return this.publishWalletAssetsState(state);
2619
+ }
2280
2620
  /**
2281
2621
  * 更新当前订单客户字段。
2282
2622
  *
@@ -2313,28 +2653,346 @@ var BaseSalesImpl = class extends import_BaseModule.BaseModule {
2313
2653
  getPaymentMethods() {
2314
2654
  return [...this.paymentMethodsCache];
2315
2655
  }
2316
- /** 轻量舍入能力,供现金支付 UI 计算 rounding_amount。 */
2317
- roundAmount(params) {
2318
- const amount = new import_decimal.default(params.amount || 0);
2319
- const interval = new import_decimal.default(params.interval || 0.05);
2320
- const rule = params.rule || "standard_rounding";
2321
- const quotient = amount.div(interval);
2322
- let roundedQuotient;
2323
- if (rule === "always_up") {
2324
- roundedQuotient = quotient.ceil();
2325
- } else if (rule === "always_down") {
2326
- roundedQuotient = quotient.floor();
2327
- } else if (rule === "standard_down") {
2328
- roundedQuotient = quotient.toDecimalPlaces(0, import_decimal.default.ROUND_HALF_DOWN);
2329
- } else {
2330
- roundedQuotient = quotient.toDecimalPlaces(0, import_decimal.default.ROUND_HALF_UP);
2656
+ findCashPaymentMethod(paymentMethods) {
2657
+ return paymentMethods.find((method) => {
2658
+ const identifiers = [method == null ? void 0 : method.code, method == null ? void 0 : method.type, method == null ? void 0 : method.tag].map(
2659
+ (value) => String(value || "").toUpperCase()
2660
+ );
2661
+ return identifiers.includes("CASHMANUAL") && (method == null ? void 0 : method.enabled) !== false && (method == null ? void 0 : method.enable) !== false;
2662
+ });
2663
+ }
2664
+ getPendingWalletPaymentAmount(payments = ((_b) => (_b = ((_a) => (_a = this.store.order) == null ? void 0 : _a.getOrderPayments)()) == null ? void 0 : _b.call(_a))() || []) {
2665
+ return payments.reduce((total, payment) => {
2666
+ if ((payment == null ? void 0 : payment.status) !== "payment_pending" || (payment == null ? void 0 : payment.voucher_id) === void 0 || Number(payment.voucher_id) === 0) {
2667
+ return total;
2668
+ }
2669
+ try {
2670
+ return total.plus(payment.amount || 0).plus(payment.rounding_amount || 0);
2671
+ } catch {
2672
+ return total;
2673
+ }
2674
+ }, new import_decimal.default(0));
2675
+ }
2676
+ /** 读取现金 action 所需的支付方式、待付金额和快捷金额。 */
2677
+ async getCashPaymentConfig() {
2678
+ var _a, _b, _c, _d, _e, _f;
2679
+ if (!this.store.payment)
2680
+ throw new Error("payment 模块未初始化");
2681
+ let paymentMethods = this.getPaymentMethods();
2682
+ let paymentMethod = this.findCashPaymentMethod(paymentMethods);
2683
+ if (!paymentMethod) {
2684
+ paymentMethods = await this.getPaymentMethodsAsync();
2685
+ paymentMethod = this.findCashPaymentMethod(paymentMethods);
2331
2686
  }
2332
- const roundedAmount = roundedQuotient.times(interval).toDecimalPlaces(2);
2687
+ const amountSnapshot = (_b = (_a = this.store.order) == null ? void 0 : _a.getOrderAmountSnapshot) == null ? void 0 : _b.call(_a);
2688
+ const pendingWalletAmount = this.getPendingWalletPaymentAmount();
2689
+ const amountDue = import_decimal.default.max(
2690
+ new import_decimal.default((amountSnapshot == null ? void 0 : amountSnapshot.amountGap) || 0).minus(pendingWalletAmount),
2691
+ 0
2692
+ ).toDecimalPlaces(2).toNumber();
2693
+ const currency = ((_e = (_d = (_c = this.store.order) == null ? void 0 : _c.getTempOrder) == null ? void 0 : _d.call(_c)) == null ? void 0 : _e.currency_code) || ((_f = this.otherParams) == null ? void 0 : _f.currency) || "USD";
2694
+ const recommendedAmounts = amountDue > 0 ? this.store.payment.cash.getRecommendedAmount(amountDue, currency) : [];
2333
2695
  return {
2334
- originalAmount: amount.toFixed(2),
2335
- roundedAmount: roundedAmount.toFixed(2),
2336
- roundingDifference: roundedAmount.minus(amount).toFixed(2)
2696
+ available: Boolean(paymentMethod && amountDue > 0),
2697
+ paymentMethod,
2698
+ amountDue,
2699
+ recommendedAmounts
2700
+ };
2701
+ }
2702
+ /**
2703
+ * 确认足额覆盖订单的 pending Wallet 支付并提交订单。
2704
+ *
2705
+ * Order 模块在构造提交参数时将 pending voucher 转为 paid;只有提交成功后
2706
+ * 才同步 Wallet committed 状态。失败时恢复调用前 payments,允许用户重试。
2707
+ */
2708
+ async confirmWalletPayment() {
2709
+ var _a, _b, _c, _d, _e;
2710
+ if (!this.store.order)
2711
+ throw new Error("order 模块未初始化");
2712
+ const beforePayments = (0, import_lodash_es.cloneDeep)(this.store.order.getOrderPayments());
2713
+ const pendingWalletAmount = this.getPendingWalletPaymentAmount(beforePayments);
2714
+ if (pendingWalletAmount.lte(0)) {
2715
+ throw new Error("当前订单没有待确认的 Wallet 支付");
2716
+ }
2717
+ const amountSnapshot = (_b = (_a = this.store.order).getOrderAmountSnapshot) == null ? void 0 : _b.call(_a);
2718
+ const effectiveAmountDue = import_decimal.default.max(
2719
+ new import_decimal.default((amountSnapshot == null ? void 0 : amountSnapshot.amountGap) || 0).minus(pendingWalletAmount),
2720
+ 0
2721
+ );
2722
+ if (effectiveAmountDue.gt(0.01)) {
2723
+ throw new Error("Wallet 支付尚未覆盖全部待支付金额");
2724
+ }
2725
+ const beforePaymentStatus = (_c = this.store.order.getTempOrder()) == null ? void 0 : _c.payment_status;
2726
+ let submitResult;
2727
+ try {
2728
+ submitResult = await this.submitSalesOrder({
2729
+ smallTicketDataFlag: 1,
2730
+ confirmPendingVoucherPayments: true
2731
+ });
2732
+ if (isRejectedSalesSubmitResult(submitResult)) {
2733
+ throw new Error(getSalesSubmitErrorMessage(submitResult));
2734
+ }
2735
+ } catch (error) {
2736
+ this.store.order.setOrderPayments(beforePayments);
2737
+ const restoredTempOrder = this.store.order.getTempOrder();
2738
+ if (restoredTempOrder && beforePaymentStatus !== void 0) {
2739
+ restoredTempOrder.payment_status = beforePaymentStatus;
2740
+ this.store.order.persistTempOrder();
2741
+ }
2742
+ await this.store.order.recalculateSummary({ createIfMissing: true });
2743
+ throw error;
2744
+ }
2745
+ if (this.store.payment) {
2746
+ try {
2747
+ const committed = this.getCommittedWalletAssets();
2748
+ const walletState = this.store.payment.wallet.setCommittedWalletAssets(
2749
+ committed.ids,
2750
+ committed.assets
2751
+ );
2752
+ await this.publishWalletAssetsState(walletState);
2753
+ } catch (error) {
2754
+ this.logWarning("confirmWalletPayment: Wallet committed 状态同步失败", {
2755
+ error: error instanceof Error ? error.message : String(error)
2756
+ });
2757
+ }
2758
+ }
2759
+ const latestAmountSnapshot = (_e = (_d = this.store.order).getOrderAmountSnapshot) == null ? void 0 : _e.call(_d);
2760
+ return {
2761
+ submitResult,
2762
+ payments: this.store.order.getOrderPayments(),
2763
+ isOrderFullyPaid: new import_decimal.default(
2764
+ (latestAmountSnapshot == null ? void 0 : latestAmountSnapshot.amountGap) || 0
2765
+ ).lte(0)
2766
+ };
2767
+ }
2768
+ /**
2769
+ * 购物车现金直付:写入现金记录并等待支付同步完成。
2770
+ * 同步失败会恢复调用前 payments(包含 Wallet pending 状态)。
2771
+ */
2772
+ async payCash(params) {
2773
+ var _a, _b, _c, _d, _e;
2774
+ if (!this.store.order)
2775
+ throw new Error("order 模块未初始化");
2776
+ const amount = Number(params.amount || 0);
2777
+ if (!Number.isFinite(amount) || amount <= 0) {
2778
+ throw new Error("现金支付金额必须大于 0");
2779
+ }
2780
+ const config = await this.getCashPaymentConfig();
2781
+ const paymentMethod = config.paymentMethod;
2782
+ if (!paymentMethod)
2783
+ throw new Error("未找到可用的 CASHMANUAL 支付方式");
2784
+ if (config.amountDue <= 0)
2785
+ throw new Error("当前订单没有待支付金额");
2786
+ const roundingAmount = Number(params.roundingAmount || 0);
2787
+ const effectivePaymentAmount = new import_decimal.default(amount).minus(roundingAmount);
2788
+ if (effectivePaymentAmount.gt(new import_decimal.default(config.amountDue).plus(0.01))) {
2789
+ throw new Error("现金支付金额超过当前待支付金额");
2790
+ }
2791
+ const beforePayments = (0, import_lodash_es.cloneDeep)(this.store.order.getOrderPayments());
2792
+ const beforePaymentStatus = (_a = this.store.order.getTempOrder()) == null ? void 0 : _a.payment_status;
2793
+ const uniquePaymentNumber = (0, import_utils.createUuidV4)();
2794
+ const paymentTimestamp = (0, import_dayjs.default)().format("YYYY-MM-DD HH:mm:ss");
2795
+ const actualPaidAmount = Number(params.actualPaidAmount ?? amount);
2796
+ const changeAmount = Number(params.changeAmount || 0);
2797
+ if (!Number.isFinite(actualPaidAmount) || actualPaidAmount + 0.01 < amount) {
2798
+ throw new Error("顾客实收现金不能小于支付金额");
2799
+ }
2800
+ (_e = (_d = (_c = (_b = this.window) == null ? void 0 : _b.interaction) == null ? void 0 : _c.utils) == null ? void 0 : _d.postMessageToApp) == null ? void 0 : _e.call(_d, {
2801
+ module: "till",
2802
+ key: "open_till",
2803
+ data: { type: "cash" }
2804
+ });
2805
+ const payments = this.store.order.addOrderPayment({
2806
+ amount: amount.toFixed(2),
2807
+ code: paymentMethod.code,
2808
+ custom_payment_id: paymentMethod.id,
2809
+ name: paymentMethod.name,
2810
+ custom_payment_type: paymentMethod.type,
2811
+ voucher_id: 0,
2812
+ rounding_amount: roundingAmount.toFixed(2),
2813
+ metadata: {
2814
+ unique_payment_number: uniquePaymentNumber,
2815
+ actual_paid_amount: Number.isFinite(actualPaidAmount) ? actualPaidAmount : amount,
2816
+ change_given_amount: Number.isFinite(changeAmount) ? changeAmount : 0
2817
+ },
2818
+ payment_time: paymentTimestamp,
2819
+ created_at: paymentTimestamp
2820
+ });
2821
+ const payment = payments.find(
2822
+ (item) => {
2823
+ var _a2;
2824
+ return ((_a2 = item == null ? void 0 : item.metadata) == null ? void 0 : _a2.unique_payment_number) === uniquePaymentNumber;
2825
+ }
2826
+ );
2827
+ try {
2828
+ const syncResult = await this.syncPaymentsToOrder({
2829
+ payments,
2830
+ submitWhenPaid: true,
2831
+ smallTicketDataFlag: 1,
2832
+ confirmPendingVoucherPayments: true
2833
+ });
2834
+ if (this.store.payment) {
2835
+ const committed = this.getCommittedWalletAssets();
2836
+ const walletState = this.store.payment.wallet.setCommittedWalletAssets(
2837
+ committed.ids,
2838
+ committed.assets
2839
+ );
2840
+ await this.publishWalletAssetsState(walletState);
2841
+ }
2842
+ return {
2843
+ payment: payment || {},
2844
+ payments: this.store.order.getOrderPayments(),
2845
+ syncResult,
2846
+ isOrderFullyPaid: syncResult.isOrderFullyPaid
2847
+ };
2848
+ } catch (error) {
2849
+ this.store.order.setOrderPayments(beforePayments);
2850
+ const restoredTempOrder = this.store.order.getTempOrder();
2851
+ if (restoredTempOrder && beforePaymentStatus !== void 0) {
2852
+ restoredTempOrder.payment_status = beforePaymentStatus;
2853
+ this.store.order.persistTempOrder();
2854
+ }
2855
+ await this.store.order.recalculateSummary({ createIfMissing: true });
2856
+ throw error;
2857
+ }
2858
+ }
2859
+ /**
2860
+ * 提交一次已由支付设备确认成功的非 Wallet 支付。
2861
+ *
2862
+ * 与 addOrderPayment 不同,本入口不会启动后台 fire-and-forget 同步;调用方会等待
2863
+ * 本次 payment、当前 pending Wallet 以及订单提交共同完成。任何同步失败都会恢复
2864
+ * 调用前的 payments 与 payment_status,便于 UI 安全解锁并重试。
2865
+ */
2866
+ async commitPaymentMethodPayment(params) {
2867
+ var _a, _b, _c, _d;
2868
+ if (!this.store.order)
2869
+ throw new Error("order 模块未初始化");
2870
+ const amount = Number(params.amount || 0);
2871
+ if (!Number.isFinite(amount) || amount <= 0) {
2872
+ throw new Error("支付金额必须大于 0");
2873
+ }
2874
+ let paymentMethods = this.getPaymentMethods();
2875
+ const findPaymentMethod = () => {
2876
+ const methodCode = String(params.paymentMethodCode || "").toUpperCase();
2877
+ return paymentMethods.find((method) => {
2878
+ const idMatched = params.paymentMethodId !== void 0 && String(method.id) === String(params.paymentMethodId);
2879
+ const codeMatched = Boolean(methodCode) && String(method.code || "").toUpperCase() === methodCode;
2880
+ return (idMatched || codeMatched) && method.enabled !== false && method.enable !== false;
2881
+ });
2337
2882
  };
2883
+ let paymentMethod = findPaymentMethod();
2884
+ if (!paymentMethod) {
2885
+ paymentMethods = await this.getPaymentMethodsAsync();
2886
+ paymentMethod = findPaymentMethod();
2887
+ }
2888
+ if (!paymentMethod) {
2889
+ throw new Error(
2890
+ `未找到可用的支付方式:${params.paymentMethodCode || params.paymentMethodId || ""}`
2891
+ );
2892
+ }
2893
+ const amountSnapshot = (_b = (_a = this.store.order).getOrderAmountSnapshot) == null ? void 0 : _b.call(_a);
2894
+ const pendingWalletAmount = this.getPendingWalletPaymentAmount();
2895
+ const amountDue = import_decimal.default.max(
2896
+ new import_decimal.default((amountSnapshot == null ? void 0 : amountSnapshot.amountGap) || 0).minus(pendingWalletAmount),
2897
+ 0
2898
+ );
2899
+ if (amountDue.lte(0))
2900
+ throw new Error("当前订单没有待支付金额");
2901
+ if (new import_decimal.default(amount).gt(amountDue.plus(0.01))) {
2902
+ throw new Error("支付金额超过当前待支付金额");
2903
+ }
2904
+ const beforePayments = (0, import_lodash_es.cloneDeep)(this.store.order.getOrderPayments());
2905
+ const beforePaymentStatus = (_c = this.store.order.getTempOrder()) == null ? void 0 : _c.payment_status;
2906
+ const paymentTimestamp = (0, import_dayjs.default)().format("YYYY-MM-DD HH:mm:ss");
2907
+ const metadata = {
2908
+ ...params.metadata || {},
2909
+ unique_payment_number: ((_d = params.metadata) == null ? void 0 : _d.unique_payment_number) || (0, import_utils.createUuidV4)()
2910
+ };
2911
+ const serviceCharge = params.serviceCharge ? {
2912
+ amount: String(params.serviceCharge.amount || 0),
2913
+ percentage: String(params.serviceCharge.percentage || 0)
2914
+ } : void 0;
2915
+ const serviceFee = serviceCharge ? new import_decimal.default(params.originAmount ?? amount).times(serviceCharge.percentage || 0).plus(serviceCharge.amount || 0).toDecimalPlaces(2).toFixed(2) : void 0;
2916
+ const payments = this.store.order.addOrderPayment({
2917
+ ...params.paymentData || {},
2918
+ amount: amount.toFixed(2),
2919
+ ...params.originAmount !== void 0 ? { origin_amount: Number(params.originAmount).toFixed(2) } : {},
2920
+ code: paymentMethod.code,
2921
+ custom_payment_id: paymentMethod.id,
2922
+ name: paymentMethod.name,
2923
+ custom_payment_type: paymentMethod.type,
2924
+ voucher_id: 0,
2925
+ ...serviceCharge ? { service_charge: serviceCharge } : {},
2926
+ ...serviceFee !== void 0 ? { service_fee: serviceFee } : {},
2927
+ metadata,
2928
+ payment_time: paymentTimestamp,
2929
+ created_at: paymentTimestamp
2930
+ });
2931
+ const payment = payments.find(
2932
+ (item) => {
2933
+ var _a2;
2934
+ return ((_a2 = item == null ? void 0 : item.metadata) == null ? void 0 : _a2.unique_payment_number) === metadata.unique_payment_number;
2935
+ }
2936
+ );
2937
+ try {
2938
+ const syncResult = await this.syncPaymentsToOrder({
2939
+ payments,
2940
+ submitWhenPaid: true,
2941
+ smallTicketDataFlag: 1,
2942
+ confirmPendingVoucherPayments: true
2943
+ });
2944
+ if (this.store.payment) {
2945
+ const committed = this.getCommittedWalletAssets();
2946
+ const walletState = this.store.payment.wallet.setCommittedWalletAssets(
2947
+ committed.ids,
2948
+ committed.assets
2949
+ );
2950
+ await this.publishWalletAssetsState(walletState);
2951
+ }
2952
+ return {
2953
+ payment: payment || {},
2954
+ payments: this.store.order.getOrderPayments(),
2955
+ syncResult,
2956
+ isOrderFullyPaid: syncResult.isOrderFullyPaid
2957
+ };
2958
+ } catch (error) {
2959
+ this.store.order.setOrderPayments(beforePayments);
2960
+ const restoredTempOrder = this.store.order.getTempOrder();
2961
+ if (restoredTempOrder && beforePaymentStatus !== void 0) {
2962
+ restoredTempOrder.payment_status = beforePaymentStatus;
2963
+ this.store.order.persistTempOrder();
2964
+ }
2965
+ await this.store.order.recalculateSummary({ createIfMissing: true });
2966
+ throw error;
2967
+ }
2968
+ }
2969
+ /**
2970
+ * 按当前店铺的现金舍入配置计算金额,供现金支付 UI 使用。
2971
+ *
2972
+ * 舍入开关取自 CASHMANUAL 支付方式;舍入规则取自
2973
+ * otherParams.order_rounding_setting,金额算法统一由 PaymentModule 维护。
2974
+ */
2975
+ async roundAmount(params) {
2976
+ var _a, _b, _c;
2977
+ const amount = params.amount;
2978
+ const cashManualPayment = this.paymentMethodsCache.find(
2979
+ (paymentMethod) => paymentMethod.code === "CASHMANUAL"
2980
+ );
2981
+ if (!((_a = cashManualPayment == null ? void 0 : cashManualPayment.metadata) == null ? void 0 : _a.order_rounding_switch)) {
2982
+ return {
2983
+ originalAmount: amount.toString(),
2984
+ roundedAmount: amount.toString(),
2985
+ roundingDifference: "0.00"
2986
+ };
2987
+ }
2988
+ if (!this.payment) {
2989
+ throw new Error("payment 模块未初始化");
2990
+ }
2991
+ return this.payment.roundAmountAsync(
2992
+ amount,
2993
+ (_b = this.otherParams.order_rounding_setting) == null ? void 0 : _b.interval,
2994
+ (_c = this.otherParams.order_rounding_setting) == null ? void 0 : _c.type
2995
+ );
2338
2996
  }
2339
2997
  /**
2340
2998
  * 发送支付链接。