@pisell/pisellos 2.2.268 → 2.2.269

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 (120) 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 +9 -0
  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/BookingContext/utils/buildCacheItemFromOrderLine.js +12 -10
  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.d.ts +28 -8
  17. package/dist/modules/Order/index.js +813 -498
  18. package/dist/modules/Order/payment-utils.d.ts +26 -0
  19. package/dist/modules/Order/payment-utils.js +225 -0
  20. package/dist/modules/Order/types.d.ts +35 -1
  21. package/dist/modules/Order/utils/orderCollectionIdentity.js +7 -2
  22. package/dist/modules/Order/utils.js +10 -6
  23. package/dist/modules/Payment/index.d.ts +2 -0
  24. package/dist/modules/Payment/index.js +78 -49
  25. package/dist/modules/Payment/types.d.ts +104 -25
  26. package/dist/modules/Payment/types.js +17 -0
  27. package/dist/modules/Payment/walletpass.d.ts +38 -1
  28. package/dist/modules/Payment/walletpass.js +917 -114
  29. package/dist/modules/Rules/index.d.ts +3 -0
  30. package/dist/modules/Rules/index.js +146 -106
  31. package/dist/modules/SalesSummary/utils.js +7 -1
  32. package/dist/server/index.d.ts +16 -0
  33. package/dist/server/index.js +1940 -1043
  34. package/dist/server/modules/index.d.ts +1 -1
  35. package/dist/server/modules/order/index.d.ts +70 -4
  36. package/dist/server/modules/order/index.js +1816 -728
  37. package/dist/server/modules/order/types.d.ts +25 -3
  38. package/dist/server/modules/order/types.js +7 -1
  39. package/dist/solution/BaseSales/index.d.ts +115 -6
  40. package/dist/solution/BaseSales/index.js +1847 -478
  41. package/dist/solution/BaseSales/types.d.ts +55 -1
  42. package/dist/solution/BaseSales/types.js +2 -1
  43. package/dist/solution/BaseSales/utils/cartPromotion.d.ts +3 -1
  44. package/dist/solution/BaseSales/utils/cartPromotion.js +90 -31
  45. package/dist/solution/BaseSales/utils/parseSalesResponse.d.ts +6 -1
  46. package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +6 -3
  47. package/dist/solution/BookingByStep/index.d.ts +1 -1
  48. package/dist/solution/BookingTicket/index.d.ts +9 -17
  49. package/dist/solution/BookingTicket/index.js +138 -52
  50. package/dist/solution/BookingTicket/types.d.ts +1 -0
  51. package/dist/solution/BookingTicket/utils/scan/applyGlobalScan.d.ts +8 -0
  52. package/dist/solution/BookingTicket/utils/scan/applyGlobalScan.js +173 -83
  53. package/dist/solution/Sales/index.d.ts +2 -0
  54. package/dist/solution/Sales/index.js +26 -4
  55. package/dist/solution/ScanOrder/index.js +31 -20
  56. package/dist/solution/VenueBooking/index.d.ts +1 -0
  57. package/dist/solution/VenueBooking/index.js +30 -19
  58. package/dist/types/index.d.ts +1 -0
  59. package/dist/utils/payment-number.d.ts +9 -0
  60. package/dist/utils/payment-number.js +69 -0
  61. package/lib/core/index.d.ts +2 -0
  62. package/lib/core/index.js +4 -0
  63. package/lib/model/strategy/adapter/promotion/adapter.d.ts +4 -0
  64. package/lib/model/strategy/adapter/promotion/adapter.js +20 -0
  65. package/lib/model/strategy/adapter/promotion/evaluator.d.ts +17 -0
  66. package/lib/model/strategy/adapter/promotion/evaluator.js +235 -0
  67. package/lib/model/strategy/adapter/promotion/examples.d.ts +86 -0
  68. package/lib/model/strategy/adapter/promotion/examples.js +91 -0
  69. package/lib/model/strategy/adapter/promotion/index.d.ts +1 -1
  70. package/lib/model/strategy/adapter/promotion/type.d.ts +90 -2
  71. package/lib/model/strategy/adapter/promotion/type.js +2 -0
  72. package/lib/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +3 -1
  73. package/lib/modules/OpenData/index.d.ts +2 -0
  74. package/lib/modules/OpenData/index.js +5 -0
  75. package/lib/modules/Order/index.d.ts +28 -8
  76. package/lib/modules/Order/index.js +236 -71
  77. package/lib/modules/Order/payment-utils.d.ts +26 -0
  78. package/lib/modules/Order/payment-utils.js +224 -0
  79. package/lib/modules/Order/types.d.ts +35 -1
  80. package/lib/modules/Order/utils/orderCollectionIdentity.js +3 -0
  81. package/lib/modules/Order/utils.js +11 -4
  82. package/lib/modules/Payment/index.d.ts +2 -0
  83. package/lib/modules/Payment/index.js +33 -12
  84. package/lib/modules/Payment/types.d.ts +104 -25
  85. package/lib/modules/Payment/types.js +1 -0
  86. package/lib/modules/Payment/walletpass.d.ts +38 -1
  87. package/lib/modules/Payment/walletpass.js +730 -21
  88. package/lib/modules/Rules/index.d.ts +3 -0
  89. package/lib/modules/Rules/index.js +54 -21
  90. package/lib/modules/SalesSummary/utils.js +5 -1
  91. package/lib/server/index.d.ts +16 -0
  92. package/lib/server/index.js +670 -18
  93. package/lib/server/modules/index.d.ts +1 -1
  94. package/lib/server/modules/order/index.d.ts +70 -4
  95. package/lib/server/modules/order/index.js +818 -69
  96. package/lib/server/modules/order/types.d.ts +25 -3
  97. package/lib/server/modules/order/types.js +1 -0
  98. package/lib/solution/BaseSales/index.d.ts +115 -6
  99. package/lib/solution/BaseSales/index.js +896 -32
  100. package/lib/solution/BaseSales/types.d.ts +55 -1
  101. package/lib/solution/BaseSales/types.js +2 -1
  102. package/lib/solution/BaseSales/utils/cartPromotion.d.ts +3 -1
  103. package/lib/solution/BaseSales/utils/cartPromotion.js +63 -12
  104. package/lib/solution/BaseSales/utils/parseSalesResponse.d.ts +6 -1
  105. package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +3 -2
  106. package/lib/solution/BookingByStep/index.d.ts +1 -1
  107. package/lib/solution/BookingTicket/index.d.ts +9 -17
  108. package/lib/solution/BookingTicket/index.js +63 -9
  109. package/lib/solution/BookingTicket/types.d.ts +1 -0
  110. package/lib/solution/BookingTicket/utils/scan/applyGlobalScan.d.ts +8 -0
  111. package/lib/solution/BookingTicket/utils/scan/applyGlobalScan.js +37 -5
  112. package/lib/solution/Sales/index.d.ts +2 -0
  113. package/lib/solution/Sales/index.js +20 -6
  114. package/lib/solution/ScanOrder/index.js +8 -0
  115. package/lib/solution/VenueBooking/index.d.ts +1 -0
  116. package/lib/solution/VenueBooking/index.js +8 -0
  117. package/lib/types/index.d.ts +1 -0
  118. package/lib/utils/payment-number.d.ts +9 -0
  119. package/lib/utils/payment-number.js +64 -0
  120. package/package.json +1 -1
@@ -37,6 +37,8 @@ var import_types = require("./types");
37
37
  var import_utils = require("./utils");
38
38
  var import_utils2 = require("../Product/utils");
39
39
  var import_dayjs = __toESM(require("dayjs"));
40
+ var import_payment_utils = require("./payment-utils");
41
+ var import_payment_number = require("../../utils/payment-number");
40
42
  var import_cartPromotion = require("../../solution/BaseSales/utils/cartPromotion");
41
43
  var import_utils3 = require("../../solution/ScanOrder/utils");
42
44
  var import_manualProductDiscount = require("./utils/manualProductDiscount");
@@ -68,6 +70,24 @@ function isManualProductDiscountProduct(product) {
68
70
  }
69
71
  var ORDER_PAYMENT_STATUS_PAID = "paid";
70
72
  var ORDER_PAYMENT_STATUS_PENDING = "payment_pending";
73
+ var CUSTOMER_PROMOTION_DISCOUNT_TAGS = /* @__PURE__ */ new Set([
74
+ "good_pass",
75
+ "product_discount_card",
76
+ "discount_card"
77
+ ]);
78
+ function isCustomerPromotionDiscount(discount) {
79
+ return CUSTOMER_PROMOTION_DISCOUNT_TAGS.has(
80
+ String((discount == null ? void 0 : discount.tag) || (discount == null ? void 0 : discount.type) || "").toLowerCase()
81
+ );
82
+ }
83
+ function markScannedDiscounts(discountList, scannedDiscountList) {
84
+ const scannedIds = new Set(
85
+ scannedDiscountList.map((discount) => discount == null ? void 0 : discount.id).filter((id) => id !== void 0 && id !== null).map(String)
86
+ );
87
+ return discountList.map(
88
+ (discount) => scannedIds.has(String(discount == null ? void 0 : discount.id)) ? { ...discount, isScan: true } : discount
89
+ );
90
+ }
71
91
  function isVoucherPaymentRecord(payment) {
72
92
  return (payment == null ? void 0 : payment.voucher_id) !== void 0 && Number(payment.voucher_id) !== 0;
73
93
  }
@@ -83,6 +103,11 @@ function isPaidPaymentRecord(payment) {
83
103
  function isCommittedVoucherPaymentRecord(payment) {
84
104
  return isVoucherPaymentRecord(payment) && (payment == null ? void 0 : payment.status) !== "voided" && (hasSyncedOrderPaymentRecord(payment) || isPaidPaymentRecord(payment));
85
105
  }
106
+ function getVoucherPaymentLifecycleKey(payment) {
107
+ if (!isVoucherPaymentRecord(payment))
108
+ return null;
109
+ return `${String(payment.voucher_id)}::${String(payment.status || "")}`;
110
+ }
86
111
  var OrderModule = class extends import_BaseModule.BaseModule {
87
112
  constructor(name, version) {
88
113
  super(name || "order", version);
@@ -115,6 +140,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
115
140
  const type = (item == null ? void 0 : item.type) ?? (item == null ? void 0 : item.tag);
116
141
  if (!type || type === "product")
117
142
  continue;
143
+ const isDiscountCard = type === "discount_card" || type === "product_discount_card";
118
144
  const resourceId = ((_a = item == null ? void 0 : item.discount) == null ? void 0 : _a.resource_id) ?? (item == null ? void 0 : item.resource_id) ?? (item == null ? void 0 : item.discount_id) ?? (item == null ? void 0 : item.order_discount_id);
119
145
  if (resourceId === void 0 || resourceId === null || resourceId === "")
120
146
  continue;
@@ -137,7 +163,9 @@ var OrderModule = class extends import_BaseModule.BaseModule {
137
163
  if (existed) {
138
164
  existed.amount = Number(existed.amount || 0) + amount;
139
165
  existed.used_par_value = String(Number(existed.used_par_value || 0) + amount);
140
- existed.balance = String(Number(existed.balance || 0) + amount);
166
+ if (!isDiscountCard) {
167
+ existed.balance = String(Number(existed.balance || 0) + amount);
168
+ }
141
169
  const productIds = Array.isArray((_f = existed.limited_relation_product_data) == null ? void 0 : _f.product_ids) ? existed.limited_relation_product_data.product_ids : [];
142
170
  if ((product == null ? void 0 : product.product_id) !== void 0 && (product == null ? void 0 : product.product_id) !== null && !productIds.includes(product.product_id)) {
143
171
  existed.limited_relation_product_data = {
@@ -174,7 +202,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
174
202
  filter: 0,
175
203
  exclude_product_ids: []
176
204
  },
177
- balance: String((item == null ? void 0 : item.amount) ?? "0"),
205
+ balance: isDiscountCard ? void 0 : String((item == null ? void 0 : item.amount) ?? "0"),
178
206
  format_title: title,
179
207
  product: {
180
208
  id: (product == null ? void 0 : product.product_id) ?? 0,
@@ -313,11 +341,11 @@ var OrderModule = class extends import_BaseModule.BaseModule {
313
341
  this.store.availableWalletIds = [];
314
342
  }
315
343
  this.request = this.core.getPlugin("request");
316
- const appPlugin = this.core.getPlugin("app");
317
- if (!appPlugin) {
344
+ this.appPlugin = this.core.getPlugin("app");
345
+ if (!this.appPlugin) {
318
346
  throw new Error("Order 模块需要 app 插件支持");
319
347
  }
320
- const app = appPlugin.getApp();
348
+ const app = this.appPlugin.getApp();
321
349
  this.logger = app.logger;
322
350
  this.window = this.core.getPlugin("window");
323
351
  const otherParams = options.otherParams || {};
@@ -521,7 +549,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
521
549
  });
522
550
  }
523
551
  async scanCode(code, customerId) {
524
- var _a, _b, _c, _d;
552
+ var _a, _b, _c, _d, _e;
525
553
  const resultDiscountWithReason = await ((_a = this.store.discount) == null ? void 0 : _a.batchSearch(code, { customerId })) || { discountList: [], unavailableReasonKey: null };
526
554
  const { discountList: resultDiscountList, unavailableReasonKey } = resultDiscountWithReason;
527
555
  const rulesModule = this.store.rules;
@@ -529,6 +557,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
529
557
  return {
530
558
  type: "clientCalc",
531
559
  isAvailable: false,
560
+ isAccepted: false,
532
561
  discountList: this.getDiscountList(),
533
562
  scannedDiscountList: resultDiscountList,
534
563
  unavailableReason: import_types2.UnavailableReason.Unknown
@@ -538,6 +567,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
538
567
  return {
539
568
  type: "server",
540
569
  isAvailable: false,
570
+ isAccepted: false,
541
571
  discountList: this.getDiscountList(),
542
572
  scannedDiscountList: resultDiscountList,
543
573
  unavailableReasonKey
@@ -553,13 +583,19 @@ var OrderModule = class extends import_BaseModule.BaseModule {
553
583
  return {
554
584
  type: "clientCalc",
555
585
  isAvailable: true,
586
+ isAccepted: true,
556
587
  discountList: this.getDiscountList(),
557
588
  scannedDiscountList: resultDiscountList
558
589
  };
559
590
  }
560
591
  const tempOrder = this.store.tempOrder;
561
592
  const holders = ((_b = tempOrder == null ? void 0 : tempOrder.holder) == null ? void 0 : _b.form_record) || [];
562
- const { isAvailable, discountList: newDiscountList, unavailableReason } = rulesModule.isDiscountListAvailable({
593
+ const {
594
+ isAvailable,
595
+ discountList: newDiscountList,
596
+ evaluatedDiscountList,
597
+ unavailableReason
598
+ } = rulesModule.isDiscountListAvailable({
563
599
  productList: (tempOrder == null ? void 0 : tempOrder.products) || [],
564
600
  oldDiscountList: this.getDiscountList(),
565
601
  newDiscountList: withScanList,
@@ -567,15 +603,25 @@ var OrderModule = class extends import_BaseModule.BaseModule {
567
603
  holders,
568
604
  isFormSubject: (product) => (0, import_utils.resolveIsFormSubject)(tempOrder, product)
569
605
  }) || { isAvailable: false, discountList: this.getDiscountList() };
570
- if (isAvailable && newDiscountList) {
571
- (_d = this.store.discount) == null ? void 0 : _d.setDiscountList(newDiscountList);
606
+ const shouldRetainForLater = !isAvailable && unavailableReason === import_types2.UnavailableReason.ProductNotApplicable && withScanList.some(isCustomerPromotionDiscount);
607
+ const normalizedNewDiscountList = newDiscountList ? markScannedDiscounts(newDiscountList, withScanList) : void 0;
608
+ const retainedDiscountList = evaluatedDiscountList ? markScannedDiscounts(evaluatedDiscountList, withScanList) : normalizedNewDiscountList;
609
+ let resolvedDiscountList = normalizedNewDiscountList || this.getDiscountList();
610
+ if (isAvailable && normalizedNewDiscountList) {
611
+ await ((_d = this.store.discount) == null ? void 0 : _d.setDiscountList(normalizedNewDiscountList));
572
612
  this.applyDiscount();
573
613
  await this.syncScannedDiscountsToOriginalDiscountList(this.getDiscountList());
614
+ resolvedDiscountList = this.getDiscountList();
615
+ } else if (shouldRetainForLater && retainedDiscountList) {
616
+ await ((_e = this.store.discount) == null ? void 0 : _e.setDiscountList(retainedDiscountList));
617
+ await this.syncScannedDiscountsToOriginalDiscountList(this.getDiscountList());
618
+ resolvedDiscountList = this.getDiscountList();
574
619
  }
575
620
  return {
576
621
  type: "clientCalc",
577
622
  isAvailable: isAvailable || false,
578
- discountList: newDiscountList || this.getDiscountList(),
623
+ isAccepted: Boolean(isAvailable || shouldRetainForLater),
624
+ discountList: resolvedDiscountList,
579
625
  scannedDiscountList: resultDiscountList,
580
626
  unavailableReason
581
627
  };
@@ -1053,17 +1099,40 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1053
1099
  const depositPaidAmount = this.calculateDepositPaidAmount(tempOrder.payments || []);
1054
1100
  return depositPaidAmount.lt(depositAmount) ? "deposit" : "normal";
1055
1101
  }
1102
+ getPaymentNumberAppData(key) {
1103
+ var _a, _b, _c, _d, _e;
1104
+ const appPlugin = this.appPlugin;
1105
+ if (!appPlugin)
1106
+ return void 0;
1107
+ const getData = (_a = appPlugin.getData) == null ? void 0 : _a.call(appPlugin);
1108
+ if (typeof getData === "function")
1109
+ return getData(key);
1110
+ const app = appPlugin.getApp();
1111
+ const coreData = (_e = (_c = (_b = app == null ? void 0 : app.models) == null ? void 0 : _b.getStore) == null ? void 0 : (_d = _c.call(_b)).getDataByModel) == null ? void 0 : _e.call(_d, "core", "core");
1112
+ return coreData == null ? void 0 : coreData[key];
1113
+ }
1114
+ getPaymentNumberDevicePrefixSync() {
1115
+ return (0, import_payment_number.resolvePaymentNumberDevicePrefixFromDeviceId)(
1116
+ (key) => this.getPaymentNumberAppData(key)
1117
+ );
1118
+ }
1119
+ getPaymentNumberDevicePrefixAsync() {
1120
+ return (0, import_payment_number.resolvePaymentNumberDevicePrefix)(
1121
+ (key) => this.getPaymentNumberAppData(key)
1122
+ );
1123
+ }
1056
1124
  normalizePaymentSource(payment, options) {
1057
1125
  const paymentRecord = payment;
1058
1126
  const metadata = paymentRecord.metadata && typeof paymentRecord.metadata === "object" ? { ...paymentRecord.metadata } : {};
1059
- if (!metadata.unique_payment_number) {
1060
- metadata.unique_payment_number = (0, import_utils.createUuidV4)();
1061
- }
1062
- const normalized = {
1063
- ...paymentRecord,
1064
- metadata,
1065
- origin_amount: paymentRecord.origin_amount ?? paymentRecord.amount ?? "0.00"
1066
- };
1127
+ const normalized = (0, import_payment_utils.ensureOrderPaymentNumber)(
1128
+ {
1129
+ ...paymentRecord,
1130
+ metadata,
1131
+ origin_amount: paymentRecord.origin_amount ?? paymentRecord.amount ?? "0.00"
1132
+ },
1133
+ (options == null ? void 0 : options.devicePrefix) || this.getPaymentNumberDevicePrefixSync(),
1134
+ import_utils.createUuidV4
1135
+ );
1067
1136
  if (paymentRecord.status !== void 0) {
1068
1137
  normalized.status = paymentRecord.status;
1069
1138
  } else if ((options == null ? void 0 : options.defaultPaid) !== false) {
@@ -2484,17 +2553,36 @@ var OrderModule = class extends import_BaseModule.BaseModule {
2484
2553
  * 用一组支付来源覆盖当前订单支付项。
2485
2554
  *
2486
2555
  * 入参允许来自 PaymentModule 的 PaymentItem,内部统一映射为 Sales 协议:
2487
- * uuid -> metadata.unique_payment_number,id -> custom_payment_id,
2556
+ * 新支付来源的 uuid 会作为 payment_number 的唯一号段,id -> custom_payment_id,
2488
2557
  * isSynced 等运行态字段不会进入 tempOrder.payments。
2489
2558
  */
2490
2559
  setOrderPayments(payments) {
2491
2560
  const tempOrder = this.ensureTempOrder();
2492
- tempOrder.payments = (0, import_utils.mapPaymentItemsToOrderPayments)(payments);
2561
+ tempOrder.payments = (0, import_utils.mapPaymentItemsToOrderPayments)(
2562
+ (payments || []).map((payment) => this.normalizePaymentSource(payment, { defaultPaid: false }))
2563
+ );
2493
2564
  this.persistTempOrder();
2494
2565
  return tempOrder.payments;
2495
2566
  }
2496
- /** 追加单个支付项,并立即持久化当前 tempOrder。 */
2567
+ /** 同步兼容入口:按调用当下的 device_id 或 LOCAL 生成支付编号。 */
2497
2568
  addOrderPayment(payment) {
2569
+ const hasPaymentNumber = String(
2570
+ payment.payment_number || ""
2571
+ ).trim() !== "";
2572
+ return this.addOrderPaymentWithDevicePrefix(
2573
+ payment,
2574
+ hasPaymentNumber ? "LOCAL" : this.getPaymentNumberDevicePrefixSync()
2575
+ );
2576
+ }
2577
+ /** 异步入口:创建支付项时读取最新 IoT 设备短号。 */
2578
+ async addOrderPaymentAsync(payment) {
2579
+ const hasPaymentNumber = String(
2580
+ payment.payment_number || ""
2581
+ ).trim() !== "";
2582
+ const devicePrefix = hasPaymentNumber ? "LOCAL" : await this.getPaymentNumberDevicePrefixAsync();
2583
+ return this.addOrderPaymentWithDevicePrefix(payment, devicePrefix);
2584
+ }
2585
+ addOrderPaymentWithDevicePrefix(payment, devicePrefix) {
2498
2586
  this.logInfo("addOrderPayment", {
2499
2587
  payment
2500
2588
  });
@@ -2507,7 +2595,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
2507
2595
  type: orderPaymentType,
2508
2596
  order_payment_type: orderPaymentType
2509
2597
  },
2510
- { defaultPaid: true }
2598
+ { defaultPaid: true, devicePrefix }
2511
2599
  )
2512
2600
  ]);
2513
2601
  tempOrder.payments = [...tempOrder.payments || [], ...mapped];
@@ -2520,57 +2608,55 @@ var OrderModule = class extends import_BaseModule.BaseModule {
2520
2608
  return tempOrder.payments;
2521
2609
  }
2522
2610
  /**
2523
- * 按支付唯一号、后端支付 ID、自定义支付方式 ID 或旧 uuid 更新支付项。
2524
- *
2525
- * 该方法服务 PaymentModal 的 void / 编辑场景;更新 metadata 时会做浅合并,
2526
- * 避免覆盖 unique_payment_number、现金实付/找零等已有元数据。
2611
+ * 默认按支付唯一号精确更新;仅显式指定 legacy 时兼容历史支付标识。
2612
+ * metadata 与 transactions 会分别合并,避免覆盖支付会话号和已有交易记录。
2527
2613
  */
2528
- updateOrderPayment(paymentIdentity, updates) {
2614
+ async updateOrderPayment(paymentIdentity, updates, options = {}) {
2529
2615
  const tempOrder = this.ensureTempOrder();
2530
2616
  const identity = String(paymentIdentity);
2531
- tempOrder.payments = (tempOrder.payments || []).map((payment) => {
2532
- var _a;
2533
- const uniquePaymentNumber = (_a = payment == null ? void 0 : payment.metadata) == null ? void 0 : _a.unique_payment_number;
2534
- const orderPaymentId = payment == null ? void 0 : payment.order_payment_id;
2535
- const customPaymentId = payment == null ? void 0 : payment.custom_payment_id;
2536
- const uuid = payment == null ? void 0 : payment.uuid;
2537
- const matched = [
2538
- uniquePaymentNumber,
2539
- orderPaymentId,
2540
- customPaymentId,
2541
- uuid
2542
- ].some((value) => value !== void 0 && String(value) === identity);
2543
- if (!matched)
2617
+ const matchMode = options.matchBy === "compat" || options.matchBy === "legacy" ? "compat" : "payment_number";
2618
+ const matchedPayment = (0, import_payment_utils.resolveOrderPaymentIdentity)(
2619
+ tempOrder.payments || [],
2620
+ identity,
2621
+ matchMode
2622
+ );
2623
+ if (!matchedPayment) {
2624
+ throw new Error(`未找到支付项: ${identity}`);
2625
+ }
2626
+ let updatedPayment = null;
2627
+ tempOrder.payments = (tempOrder.payments || []).map((payment, index) => {
2628
+ if (index !== matchedPayment.index)
2544
2629
  return payment;
2545
- return {
2546
- ...payment,
2630
+ const nextPayment = (0, import_payment_utils.mergeOrderPaymentRecord)(payment, {
2547
2631
  ...updates,
2548
- metadata: {
2549
- ...payment.metadata || {},
2550
- ...updates.metadata || {}
2551
- }
2552
- };
2632
+ updated_at: updates.updated_at || (0, import_dayjs.default)().format("YYYY-MM-DD HH:mm:ss")
2633
+ });
2634
+ updatedPayment = nextPayment;
2635
+ return nextPayment;
2553
2636
  });
2554
2637
  this.persistTempOrder();
2555
- return tempOrder.payments;
2638
+ const summary = await this.recalculateSummary({ createIfMissing: true });
2639
+ this.persistTempOrder();
2640
+ const payments = tempOrder.payments;
2641
+ return {
2642
+ updated: true,
2643
+ payment: payments[matchedPayment.index] || updatedPayment,
2644
+ payments,
2645
+ summary
2646
+ };
2556
2647
  }
2557
2648
  /** 从当前订单支付项中移除指定支付项。 */
2558
2649
  deleteOrderPayment(paymentIdentity) {
2559
2650
  const tempOrder = this.ensureTempOrder();
2560
2651
  const identity = String(paymentIdentity);
2561
- tempOrder.payments = (tempOrder.payments || []).filter((payment) => {
2562
- var _a;
2563
- const uniquePaymentNumber = (_a = payment == null ? void 0 : payment.metadata) == null ? void 0 : _a.unique_payment_number;
2564
- const orderPaymentId = payment == null ? void 0 : payment.order_payment_id;
2565
- const customPaymentId = payment == null ? void 0 : payment.custom_payment_id;
2566
- const uuid = payment == null ? void 0 : payment.uuid;
2567
- return ![
2568
- uniquePaymentNumber,
2569
- orderPaymentId,
2570
- customPaymentId,
2571
- uuid
2572
- ].some((value) => value !== void 0 && String(value) === identity);
2573
- });
2652
+ const matchedPayment = (0, import_payment_utils.resolveOrderPaymentIdentity)(
2653
+ tempOrder.payments || [],
2654
+ identity,
2655
+ "compat"
2656
+ );
2657
+ if (!matchedPayment)
2658
+ return tempOrder.payments;
2659
+ tempOrder.payments = (tempOrder.payments || []).filter((_, index) => index !== matchedPayment.index);
2574
2660
  this.persistTempOrder();
2575
2661
  return tempOrder.payments;
2576
2662
  }
@@ -2679,7 +2765,10 @@ var OrderModule = class extends import_BaseModule.BaseModule {
2679
2765
  payments.map(
2680
2766
  (payment) => this.normalizePaymentSource(
2681
2767
  normalizeVoucherPaymentStatus(withVoucherPaymentType(payment)),
2682
- { defaultPaid: false }
2768
+ {
2769
+ defaultPaid: false,
2770
+ devicePrefix: options == null ? void 0 : options.devicePrefix
2771
+ }
2683
2772
  )
2684
2773
  )
2685
2774
  ).filter((payment) => {
@@ -2701,14 +2790,15 @@ var OrderModule = class extends import_BaseModule.BaseModule {
2701
2790
  const committedVoucherPaymentIds = new Set(
2702
2791
  committedVoucherPayments.filter((payment) => hasSyncedOrderPaymentRecord(payment)).map((payment) => String(payment.order_payment_id))
2703
2792
  );
2704
- const committedVoucherIds = new Set(
2705
- committedVoucherPayments.map((payment) => String(payment.voucher_id))
2793
+ const committedVoucherLifecycleKeys = new Set(
2794
+ committedVoucherPayments.map((payment) => getVoucherPaymentLifecycleKey(payment)).filter((key) => key !== null)
2706
2795
  );
2707
2796
  const replaceableVouchers = mappedVouchers.filter((payment) => {
2708
2797
  if (hasSyncedOrderPaymentRecord(payment) && committedVoucherPaymentIds.has(String(payment.order_payment_id))) {
2709
2798
  return false;
2710
2799
  }
2711
- if (payment.voucher_id !== void 0 && committedVoucherIds.has(String(payment.voucher_id))) {
2800
+ const lifecycleKey = getVoucherPaymentLifecycleKey(payment);
2801
+ if (lifecycleKey && committedVoucherLifecycleKeys.has(lifecycleKey)) {
2712
2802
  return false;
2713
2803
  }
2714
2804
  return true;
@@ -2730,6 +2820,14 @@ var OrderModule = class extends import_BaseModule.BaseModule {
2730
2820
  });
2731
2821
  return tempOrder.payments;
2732
2822
  }
2823
+ async updateVoucherOrderPaymentsAsync(payments, options) {
2824
+ const needsPaymentNumber = (payments || []).some((payment) => String(payment.payment_number || "").trim() === "");
2825
+ const devicePrefix = needsPaymentNumber ? await this.getPaymentNumberDevicePrefixAsync() : "LOCAL";
2826
+ return this.updateVoucherOrderPayments(payments, {
2827
+ ...options,
2828
+ devicePrefix
2829
+ });
2830
+ }
2733
2831
  async shouldMergeProductToOrder(params) {
2734
2832
  var _a;
2735
2833
  const onBeforeMergeProductToOrder = (_a = this.orderHooks) == null ? void 0 : _a.onBeforeMergeProductToOrder;
@@ -3627,6 +3725,38 @@ var OrderModule = class extends import_BaseModule.BaseModule {
3627
3725
  await this.saveDraft();
3628
3726
  return tempOrder;
3629
3727
  }
3728
+ async saveTempOrderAsDraft(params) {
3729
+ var _a, _b, _c, _d, _e;
3730
+ const tempOrder = this.ensureTempOrder();
3731
+ this.ensureExternalSaleNumber(tempOrder);
3732
+ this.persistTempOrder();
3733
+ const latestSummary = await this.recalculateSummary({ createIfMissing: true }) || this.store.summary || (0, import_utils.createEmptySummary)();
3734
+ const payload = (0, import_utils.buildSubmitPayload)({
3735
+ tempOrder,
3736
+ cacheId: (params == null ? void 0 : params.cacheId) ?? this.cacheId,
3737
+ platform: (params == null ? void 0 : params.platform) || ((_a = this.otherParams) == null ? void 0 : _a.platform),
3738
+ businessCode: (params == null ? void 0 : params.businessCode) || ((_b = this.otherParams) == null ? void 0 : _b.businessCode) || ((_c = this.otherParams) == null ? void 0 : _c.business_code) || tempOrder.business_code,
3739
+ channel: params == null ? void 0 : params.channel,
3740
+ type: (params == null ? void 0 : params.type) ?? ((_d = this.otherParams) == null ? void 0 : _d.type),
3741
+ summary: latestSummary,
3742
+ enhance: params == null ? void 0 : params.enhancePayload
3743
+ });
3744
+ if (!payload.created_at) {
3745
+ payload.created_at = (0, import_dayjs.default)().format("YYYY-MM-DD HH:mm:ss");
3746
+ }
3747
+ payload.need_sync = 0;
3748
+ payload.is_draft_order = 1;
3749
+ this.logInfo("saveTempOrderAsDraft calling sales draft", {
3750
+ orderId: payload.order_id,
3751
+ externalSaleNumber: payload.external_sale_number,
3752
+ productsCount: ((_e = payload.products) == null ? void 0 : _e.length) || 0
3753
+ });
3754
+ return this.request.post("/order/sales/draft", payload, {
3755
+ osServer: true,
3756
+ customToast: () => {
3757
+ }
3758
+ });
3759
+ }
3630
3760
  /**
3631
3761
  * 提交当前 Sales tempOrder。
3632
3762
  *
@@ -3827,8 +3957,13 @@ var OrderModule = class extends import_BaseModule.BaseModule {
3827
3957
  }
3828
3958
  async syncPaymentsToOrder(params) {
3829
3959
  const tempOrder = this.ensureTempOrder();
3960
+ const needsPaymentNumber = (params.payments || []).some((payment) => String(payment.payment_number || "").trim() === "");
3961
+ const devicePrefix = needsPaymentNumber ? await this.getPaymentNumberDevicePrefixAsync() : "LOCAL";
3830
3962
  const mappedPayments = (0, import_utils.mapPaymentItemsToOrderPayments)(
3831
- params.payments || []
3963
+ (params.payments || []).map((payment) => this.normalizePaymentSource(payment, {
3964
+ defaultPaid: false,
3965
+ devicePrefix
3966
+ }))
3832
3967
  );
3833
3968
  const shouldConfirmPendingVoucherPayments = params.confirmPendingVoucherPayments ?? params.submitWhenPaid === true;
3834
3969
  const effectivePayments = shouldConfirmPendingVoucherPayments ? this.confirmPendingVoucherPaymentsInList(mappedPayments) : params.confirmPendingVoucherPayments === false ? this.discardPendingVoucherPaymentsFromList(mappedPayments) : mappedPayments;
@@ -4158,7 +4293,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
4158
4293
  params.forceRemote = false;
4159
4294
  }
4160
4295
  const res = await this.request.get(`/order/sales/${encodeURIComponent(lookup)}`, {
4161
- with: ["products", "bookings", "payments", "customer", "summary", "contacts_info"],
4296
+ with: ["products", "bookings", "payments", "customer", "summary", "contacts_info", "refunds"],
4162
4297
  ...params.forceRemote ? { forceRemote: true } : {}
4163
4298
  }, { osServer: true });
4164
4299
  if (res.code === 200) {
@@ -4204,6 +4339,12 @@ var OrderModule = class extends import_BaseModule.BaseModule {
4204
4339
  }
4205
4340
  }
4206
4341
  const nextTempOrder = this.normalizeTempOrderForRuntime(raw, { makeEditMark: true });
4342
+ const sourceLastOrderInfo = raw.lastOrderInfo || raw;
4343
+ const isDraftOrder = Number(nextTempOrder.is_draft_order || 0) === 1;
4344
+ const syntheticIdentityOptions = {
4345
+ isDraftOrder,
4346
+ externalSaleNumber: nextTempOrder.external_sale_number
4347
+ };
4207
4348
  const rawSummary = raw.summary && typeof raw.summary === "object" ? raw.summary : {};
4208
4349
  const summarySurcharges = Array.isArray(rawSummary.surcharges) ? rawSummary.surcharges.map((s) => ({ ...s || {} })) : (0, import_lodash_es.cloneDeep)(nextTempOrder.surcharges || []);
4209
4350
  this.store.tempOrder = nextTempOrder;
@@ -4224,7 +4365,14 @@ var OrderModule = class extends import_BaseModule.BaseModule {
4224
4365
  productFingerprint: this.buildOrderProductsFingerprint(nextTempOrder.products || [])
4225
4366
  }
4226
4367
  };
4227
- this.store.lastOrderInfo = sourceRaw.lastOrderInfo || sourceRaw;
4368
+ this.store.lastOrderInfo = this.withoutSyntheticDraftOrderIdForRuntime(
4369
+ sourceLastOrderInfo,
4370
+ syntheticIdentityOptions
4371
+ );
4372
+ const hadSyntheticDraftOrderId = this.isSyntheticDraftOrderIdForRuntime(raw, syntheticIdentityOptions) || this.isSyntheticDraftOrderIdForRuntime(sourceLastOrderInfo, syntheticIdentityOptions);
4373
+ if (hadSyntheticDraftOrderId && this.store.syncState !== "submitting") {
4374
+ this.store.syncState = "local";
4375
+ }
4228
4376
  const hydratedEditDiscounts = this.collectHydratedEditDiscounts(nextTempOrder.products);
4229
4377
  const currentDiscounts = typeof ((_a = this.store.discount) == null ? void 0 : _a.getDiscountList) === "function" ? this.store.discount.getDiscountList() || [] : [];
4230
4378
  const currentScanDiscounts = currentDiscounts.filter((discount) => discount == null ? void 0 : discount.isScan);
@@ -4337,12 +4485,13 @@ var OrderModule = class extends import_BaseModule.BaseModule {
4337
4485
  }
4338
4486
  normalizeTempOrderForRuntime(raw, options) {
4339
4487
  var _a, _b;
4488
+ const runtimeRaw = this.withoutSyntheticDraftOrderIdForRuntime(raw);
4340
4489
  const nextTempOrder = {
4341
4490
  ...this.createDefaultTempOrderInstance(),
4342
- ...raw || {}
4491
+ ...runtimeRaw || {}
4343
4492
  };
4344
- if ((nextTempOrder.order_id === void 0 || nextTempOrder.order_id === null) && (raw == null ? void 0 : raw.id) !== void 0 && (raw == null ? void 0 : raw.id) !== null) {
4345
- nextTempOrder.order_id = raw.id;
4493
+ if ((nextTempOrder.order_id === void 0 || nextTempOrder.order_id === null) && (runtimeRaw == null ? void 0 : runtimeRaw.id) !== void 0 && (runtimeRaw == null ? void 0 : runtimeRaw.id) !== null) {
4494
+ nextTempOrder.order_id = runtimeRaw.id;
4346
4495
  }
4347
4496
  delete nextTempOrder.summary;
4348
4497
  delete nextTempOrder.lastOrderInfo;
@@ -4410,6 +4559,22 @@ var OrderModule = class extends import_BaseModule.BaseModule {
4410
4559
  nextTempOrder.vouchers = raw.vouchers || [];
4411
4560
  return nextTempOrder;
4412
4561
  }
4562
+ isSyntheticDraftOrderIdForRuntime(record, options) {
4563
+ const isDraftOrder = (options == null ? void 0 : options.isDraftOrder) ?? Number((record == null ? void 0 : record.is_draft_order) || 0) === 1;
4564
+ const externalSaleNumber = (record == null ? void 0 : record.external_sale_number) ?? (options == null ? void 0 : options.externalSaleNumber);
4565
+ const orderId = record == null ? void 0 : record.order_id;
4566
+ return Boolean(
4567
+ isDraftOrder && orderId !== void 0 && orderId !== null && orderId !== "" && externalSaleNumber !== void 0 && externalSaleNumber !== null && externalSaleNumber !== "" && String(orderId) === String(externalSaleNumber)
4568
+ );
4569
+ }
4570
+ withoutSyntheticDraftOrderIdForRuntime(record, options) {
4571
+ if (!this.isSyntheticDraftOrderIdForRuntime(record, options))
4572
+ return record;
4573
+ return {
4574
+ ...record,
4575
+ order_id: null
4576
+ };
4577
+ }
4413
4578
  hydrateLinkedBookingIdentity(tempOrder) {
4414
4579
  const bookingUidByProductUid = /* @__PURE__ */ new Map();
4415
4580
  const productByUid = /* @__PURE__ */ new Map();
@@ -0,0 +1,26 @@
1
+ export type OrderPaymentTransactionStatus = 'success' | 'fail';
2
+ export interface OrderPaymentTransaction {
3
+ number: string;
4
+ status: OrderPaymentTransactionStatus;
5
+ }
6
+ export type OrderPaymentIdentityType = 'order_payment_id' | 'payment_number' | 'unique_payment_number';
7
+ export interface OrderPaymentIdentityMatch<T> {
8
+ index: number;
9
+ payment: T;
10
+ matchedBy: OrderPaymentIdentityType;
11
+ }
12
+ export declare function getOrderPaymentIdentityKeys(payment: unknown): string[];
13
+ export declare function isPendingOrderPayment(payment: unknown): boolean;
14
+ export declare function isIdentifiedPendingOrderPayment(payment: unknown): boolean;
15
+ export declare function hasOrderPaymentIdentityOverlap(left: unknown, right: unknown): boolean;
16
+ export declare function resolveOrderPaymentIdentity<T extends Record<string, any>>(payments: T[] | null | undefined, identity: string | number, mode?: 'payment_number' | 'compat'): OrderPaymentIdentityMatch<T> | null;
17
+ export declare function ensureOrderPaymentNumber<T extends Record<string, any>>(payment: T, devicePrefix: string, createPaymentNumber: () => string): T & {
18
+ payment_number: string;
19
+ };
20
+ export declare function mergeOrderPaymentTransactions(current: unknown, incoming: unknown): OrderPaymentTransaction[];
21
+ export declare function mergeOrderPaymentRecord<T extends Record<string, any>>(payment: T, updates: Record<string, any>): T;
22
+ export declare function mergeOrderPaymentSnapshot<T extends Record<string, any>>(current: T, incoming: T): T;
23
+ export declare function mergeOrderPaymentListsByIdentity<T extends Record<string, any>>(existing: T[] | null | undefined, incoming: T[] | null | undefined, options?: {
24
+ preserveUnmatchedExisting?: boolean;
25
+ preserveUnmatchedExistingWhen?: (payment: T) => boolean;
26
+ }): T[];