@pisell/pisellos 2.2.273 → 2.2.275

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 (74) hide show
  1. package/dist/model/strategy/adapter/promotion/index.js +9 -0
  2. package/dist/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +12 -10
  3. package/dist/modules/Order/index.d.ts +7 -6
  4. package/dist/modules/Order/index.js +217 -115
  5. package/dist/modules/Order/types.d.ts +23 -3
  6. package/dist/modules/Order/types.js +11 -0
  7. package/dist/modules/Payment/index.js +1 -1
  8. package/dist/modules/Payment/types.d.ts +81 -4
  9. package/dist/modules/Payment/types.js +18 -3
  10. package/dist/modules/Payment/walletpass.d.ts +38 -1
  11. package/dist/modules/Payment/walletpass.js +917 -114
  12. package/dist/modules/Rules/index.d.ts +2 -0
  13. package/dist/modules/Rules/index.js +78 -64
  14. package/dist/modules/SalesSummary/utils.js +7 -1
  15. package/dist/server/index.d.ts +4 -0
  16. package/dist/server/index.js +27 -18
  17. package/dist/server/modules/index.d.ts +1 -1
  18. package/dist/server/modules/order/index.d.ts +16 -0
  19. package/dist/server/modules/order/index.js +964 -776
  20. package/dist/server/modules/order/types.d.ts +14 -0
  21. package/dist/server/modules/order/types.js +6 -0
  22. package/dist/solution/BaseSales/index.d.ts +80 -15
  23. package/dist/solution/BaseSales/index.js +1498 -444
  24. package/dist/solution/BaseSales/types.d.ts +55 -1
  25. package/dist/solution/BaseSales/types.js +2 -1
  26. package/dist/solution/BaseSales/utils/cartPromotion.js +8 -3
  27. package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +6 -3
  28. package/dist/solution/BookingByStep/index.d.ts +1 -1
  29. package/dist/solution/BookingTicket/index.d.ts +1 -1
  30. package/dist/solution/BookingTicket/index.js +12 -3
  31. package/dist/solution/BookingTicket/types.d.ts +4 -0
  32. package/dist/solution/BookingTicket/utils/cartView.js +44 -32
  33. package/dist/solution/BookingTicket/utils/scan/applyGlobalScan.d.ts +8 -0
  34. package/dist/solution/BookingTicket/utils/scan/applyGlobalScan.js +173 -83
  35. package/dist/solution/Sales/index.d.ts +1 -0
  36. package/dist/solution/Sales/index.js +16 -2
  37. package/dist/solution/VenueBooking/index.d.ts +1 -0
  38. package/lib/model/strategy/adapter/promotion/index.js +0 -51
  39. package/lib/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +3 -1
  40. package/lib/modules/Order/index.d.ts +7 -6
  41. package/lib/modules/Order/index.js +127 -20
  42. package/lib/modules/Order/types.d.ts +23 -3
  43. package/lib/modules/Payment/index.js +1 -1
  44. package/lib/modules/Payment/types.d.ts +81 -4
  45. package/lib/modules/Payment/types.js +4 -3
  46. package/lib/modules/Payment/walletpass.d.ts +38 -1
  47. package/lib/modules/Payment/walletpass.js +730 -21
  48. package/lib/modules/Rules/index.d.ts +2 -0
  49. package/lib/modules/Rules/index.js +24 -14
  50. package/lib/modules/SalesSummary/utils.js +5 -1
  51. package/lib/server/index.d.ts +4 -0
  52. package/lib/server/index.js +14 -6
  53. package/lib/server/modules/index.d.ts +1 -1
  54. package/lib/server/modules/order/index.d.ts +16 -0
  55. package/lib/server/modules/order/index.js +140 -4
  56. package/lib/server/modules/order/types.d.ts +14 -0
  57. package/lib/server/modules/order/types.js +1 -0
  58. package/lib/solution/BaseSales/index.d.ts +80 -15
  59. package/lib/solution/BaseSales/index.js +712 -48
  60. package/lib/solution/BaseSales/types.d.ts +55 -1
  61. package/lib/solution/BaseSales/types.js +2 -1
  62. package/lib/solution/BaseSales/utils/cartPromotion.js +8 -2
  63. package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +3 -2
  64. package/lib/solution/BookingByStep/index.d.ts +1 -1
  65. package/lib/solution/BookingTicket/index.d.ts +1 -1
  66. package/lib/solution/BookingTicket/index.js +4 -1
  67. package/lib/solution/BookingTicket/types.d.ts +4 -0
  68. package/lib/solution/BookingTicket/utils/cartView.js +43 -30
  69. package/lib/solution/BookingTicket/utils/scan/applyGlobalScan.d.ts +8 -0
  70. package/lib/solution/BookingTicket/utils/scan/applyGlobalScan.js +37 -5
  71. package/lib/solution/Sales/index.d.ts +1 -0
  72. package/lib/solution/Sales/index.js +15 -3
  73. package/lib/solution/VenueBooking/index.d.ts +1 -0
  74. package/package.json +1 -1
@@ -70,6 +70,24 @@ function isManualProductDiscountProduct(product) {
70
70
  }
71
71
  var ORDER_PAYMENT_STATUS_PAID = "paid";
72
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
+ }
73
91
  function isVoucherPaymentRecord(payment) {
74
92
  return (payment == null ? void 0 : payment.voucher_id) !== void 0 && Number(payment.voucher_id) !== 0;
75
93
  }
@@ -122,6 +140,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
122
140
  const type = (item == null ? void 0 : item.type) ?? (item == null ? void 0 : item.tag);
123
141
  if (!type || type === "product")
124
142
  continue;
143
+ const isDiscountCard = type === "discount_card" || type === "product_discount_card";
125
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);
126
145
  if (resourceId === void 0 || resourceId === null || resourceId === "")
127
146
  continue;
@@ -144,7 +163,9 @@ var OrderModule = class extends import_BaseModule.BaseModule {
144
163
  if (existed) {
145
164
  existed.amount = Number(existed.amount || 0) + amount;
146
165
  existed.used_par_value = String(Number(existed.used_par_value || 0) + amount);
147
- existed.balance = String(Number(existed.balance || 0) + amount);
166
+ if (!isDiscountCard) {
167
+ existed.balance = String(Number(existed.balance || 0) + amount);
168
+ }
148
169
  const productIds = Array.isArray((_f = existed.limited_relation_product_data) == null ? void 0 : _f.product_ids) ? existed.limited_relation_product_data.product_ids : [];
149
170
  if ((product == null ? void 0 : product.product_id) !== void 0 && (product == null ? void 0 : product.product_id) !== null && !productIds.includes(product.product_id)) {
150
171
  existed.limited_relation_product_data = {
@@ -181,7 +202,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
181
202
  filter: 0,
182
203
  exclude_product_ids: []
183
204
  },
184
- balance: String((item == null ? void 0 : item.amount) ?? "0"),
205
+ balance: isDiscountCard ? void 0 : String((item == null ? void 0 : item.amount) ?? "0"),
185
206
  format_title: title,
186
207
  product: {
187
208
  id: (product == null ? void 0 : product.product_id) ?? 0,
@@ -528,7 +549,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
528
549
  });
529
550
  }
530
551
  async scanCode(code, customerId) {
531
- var _a, _b, _c, _d;
552
+ var _a, _b, _c, _d, _e;
532
553
  const resultDiscountWithReason = await ((_a = this.store.discount) == null ? void 0 : _a.batchSearch(code, { customerId })) || { discountList: [], unavailableReasonKey: null };
533
554
  const { discountList: resultDiscountList, unavailableReasonKey } = resultDiscountWithReason;
534
555
  const rulesModule = this.store.rules;
@@ -536,6 +557,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
536
557
  return {
537
558
  type: "clientCalc",
538
559
  isAvailable: false,
560
+ isAccepted: false,
539
561
  discountList: this.getDiscountList(),
540
562
  scannedDiscountList: resultDiscountList,
541
563
  unavailableReason: import_types2.UnavailableReason.Unknown
@@ -545,6 +567,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
545
567
  return {
546
568
  type: "server",
547
569
  isAvailable: false,
570
+ isAccepted: false,
548
571
  discountList: this.getDiscountList(),
549
572
  scannedDiscountList: resultDiscountList,
550
573
  unavailableReasonKey
@@ -560,13 +583,19 @@ var OrderModule = class extends import_BaseModule.BaseModule {
560
583
  return {
561
584
  type: "clientCalc",
562
585
  isAvailable: true,
586
+ isAccepted: true,
563
587
  discountList: this.getDiscountList(),
564
588
  scannedDiscountList: resultDiscountList
565
589
  };
566
590
  }
567
591
  const tempOrder = this.store.tempOrder;
568
592
  const holders = ((_b = tempOrder == null ? void 0 : tempOrder.holder) == null ? void 0 : _b.form_record) || [];
569
- const { isAvailable, discountList: newDiscountList, unavailableReason } = rulesModule.isDiscountListAvailable({
593
+ const {
594
+ isAvailable,
595
+ discountList: newDiscountList,
596
+ evaluatedDiscountList,
597
+ unavailableReason
598
+ } = rulesModule.isDiscountListAvailable({
570
599
  productList: (tempOrder == null ? void 0 : tempOrder.products) || [],
571
600
  oldDiscountList: this.getDiscountList(),
572
601
  newDiscountList: withScanList,
@@ -574,15 +603,25 @@ var OrderModule = class extends import_BaseModule.BaseModule {
574
603
  holders,
575
604
  isFormSubject: (product) => (0, import_utils.resolveIsFormSubject)(tempOrder, product)
576
605
  }) || { isAvailable: false, discountList: this.getDiscountList() };
577
- if (isAvailable && newDiscountList) {
578
- (_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));
579
612
  this.applyDiscount();
580
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();
581
619
  }
582
620
  return {
583
621
  type: "clientCalc",
584
622
  isAvailable: isAvailable || false,
585
- discountList: newDiscountList || this.getDiscountList(),
623
+ isAccepted: Boolean(isAvailable || shouldRetainForLater),
624
+ discountList: resolvedDiscountList,
586
625
  scannedDiscountList: resultDiscountList,
587
626
  unavailableReason
588
627
  };
@@ -2848,8 +2887,8 @@ var OrderModule = class extends import_BaseModule.BaseModule {
2848
2887
  * @example
2849
2888
  * await this.appendProductLineToTempOrder(tempOrder, { product_id: 1, num: 1 }, booking);
2850
2889
  */
2851
- async appendProductLineToTempOrder(tempOrder, product, booking) {
2852
- var _a, _b;
2890
+ async appendProductLineToTempOrder(tempOrder, product, booking, options) {
2891
+ var _a, _b, _c, _d, _e, _f, _g;
2853
2892
  const linked = booking ? this.createLinkedProductAndBooking({ product, booking }) : null;
2854
2893
  const productToAdd = (linked == null ? void 0 : linked.product) || product;
2855
2894
  const incomingFingerprint = (0, import_utils3.buildProductLineFingerprint)(
@@ -2860,7 +2899,12 @@ var OrderModule = class extends import_BaseModule.BaseModule {
2860
2899
  productToAdd,
2861
2900
  (0, import_utils3.normalizeOrderProduct)(productToAdd)
2862
2901
  );
2863
- const incomingBookingUid = productToAdd.booking_uid ?? ((_a = productToAdd.metadata) == null ? void 0 : _a.booking_uid);
2902
+ const isWeighingProduct = Number(((_a = normalizedIncoming.product_sku) == null ? void 0 : _a.open_sold_weight) || 0) === 1;
2903
+ const netWeight = Number((_b = normalizedIncoming.product_sku) == null ? void 0 : _b.net_weight);
2904
+ if (isWeighingProduct && (!Number.isFinite(netWeight) || netWeight <= 0)) {
2905
+ throw new Error("[Order] 称重商品净重必须大于 0");
2906
+ }
2907
+ const incomingBookingUid = productToAdd.booking_uid ?? ((_c = productToAdd.metadata) == null ? void 0 : _c.booking_uid);
2864
2908
  if (incomingBookingUid !== void 0 && incomingBookingUid !== null && String(incomingBookingUid) !== "") {
2865
2909
  normalizedIncoming.booking_uid = String(incomingBookingUid);
2866
2910
  normalizedIncoming.metadata = {
@@ -2876,7 +2920,8 @@ var OrderModule = class extends import_BaseModule.BaseModule {
2876
2920
  );
2877
2921
  const hasIncomingProductNote = this.hasOrderProductLineNote(normalizedIncoming);
2878
2922
  const hasIncomingBookingUid = this.hasLinkedBookingUid(productToAdd) || this.hasLinkedBookingUid(normalizedIncoming);
2879
- const shouldForceNewLine = !!booking || hasIncomingProductNote || hasIncomingBookingUid;
2923
+ const isIncomingCustomItem = ((_e = (_d = productToAdd.metadata) == null ? void 0 : _d.origin) == null ? void 0 : _e.isCustomItem) === true || ((_f = productToAdd._origin) == null ? void 0 : _f.isCustomItem) === true;
2924
+ const shouldForceNewLine = !!booking || hasIncomingProductNote || hasIncomingBookingUid || isIncomingCustomItem || isWeighingProduct;
2880
2925
  const matchedIndex = hasIncomingGoodPass || shouldForceNewLine ? -1 : tempOrder.products.findIndex((item) => {
2881
2926
  var _a2;
2882
2927
  if (this.hasGoodPassDiscount(item))
@@ -2925,10 +2970,19 @@ var OrderModule = class extends import_BaseModule.BaseModule {
2925
2970
  booking_uid: linked.bookingUid
2926
2971
  };
2927
2972
  }
2928
- tempOrder.products = [...tempOrder.products, normalizedIncoming];
2973
+ const insertAtIndex = options == null ? void 0 : options.insertAtIndex;
2974
+ if (Number.isInteger(insertAtIndex) && Number(insertAtIndex) >= 0 && Number(insertAtIndex) < tempOrder.products.length) {
2975
+ tempOrder.products = [
2976
+ ...tempOrder.products.slice(0, insertAtIndex),
2977
+ normalizedIncoming,
2978
+ ...tempOrder.products.slice(insertAtIndex)
2979
+ ];
2980
+ } else {
2981
+ tempOrder.products = [...tempOrder.products, normalizedIncoming];
2982
+ }
2929
2983
  } else {
2930
2984
  const targetProduct = matchedProduct;
2931
- const targetUid = (_b = targetProduct.metadata) == null ? void 0 : _b.unique_identification_number;
2985
+ const targetUid = (_g = targetProduct.metadata) == null ? void 0 : _g.unique_identification_number;
2932
2986
  const normalizedProduct = (0, import_utils.mergeOrderProductDisplayFields)(
2933
2987
  targetProduct,
2934
2988
  (0, import_utils3.normalizeOrderProduct)({
@@ -2984,6 +3038,14 @@ var OrderModule = class extends import_BaseModule.BaseModule {
2984
3038
  tempOrder.products[matchedIndex],
2985
3039
  targetUid
2986
3040
  );
3041
+ if (matchedIndex !== tempOrder.products.length - 1) {
3042
+ const mergedProduct = tempOrder.products[matchedIndex];
3043
+ tempOrder.products = [
3044
+ ...tempOrder.products.slice(0, matchedIndex),
3045
+ ...tempOrder.products.slice(matchedIndex + 1),
3046
+ mergedProduct
3047
+ ];
3048
+ }
2987
3049
  }
2988
3050
  if (linked) {
2989
3051
  tempOrder.bookings = [...tempOrder.bookings || [], linked.booking];
@@ -2996,8 +3058,15 @@ var OrderModule = class extends import_BaseModule.BaseModule {
2996
3058
  * await order.addProductToOrder({ product_id: 1, num: 3 }, { start_time: '10:00' });
2997
3059
  * // → tempOrder.products 新增 3 行,每行 num=1,bookings 3 条
2998
3060
  */
2999
- async addProductToOrder(product, booking) {
3061
+ async addProductToOrder(product, booking, options) {
3000
3062
  const tempOrder = this.ensureTempOrder();
3063
+ const insertAfterProductUid = options == null ? void 0 : options.insertAfterProductUid;
3064
+ const anchorIndex = booking && insertAfterProductUid ? tempOrder.products.findIndex((line) => {
3065
+ var _a;
3066
+ const uid = ((_a = line.metadata) == null ? void 0 : _a.unique_identification_number) || line.unique_identification_number;
3067
+ return uid !== void 0 && uid !== null && String(uid) === String(insertAfterProductUid);
3068
+ }) : -1;
3069
+ let insertAtIndex = anchorIndex >= 0 ? anchorIndex + 1 : void 0;
3001
3070
  if (booking) {
3002
3071
  const productRecord = product;
3003
3072
  const splitCount = (0, import_utils3.getSafeProductNum)(
@@ -3011,14 +3080,22 @@ var OrderModule = class extends import_BaseModule.BaseModule {
3011
3080
  await this.appendProductLineToTempOrder(
3012
3081
  tempOrder,
3013
3082
  singleLine,
3014
- (0, import_lodash_es.cloneDeep)(booking)
3083
+ (0, import_lodash_es.cloneDeep)(booking),
3084
+ { insertAtIndex }
3015
3085
  );
3086
+ if (insertAtIndex !== void 0)
3087
+ insertAtIndex += 1;
3016
3088
  }
3017
3089
  await this.finalizeProductOrderMutation(tempOrder);
3018
3090
  return tempOrder.products;
3019
3091
  }
3020
3092
  }
3021
- await this.appendProductLineToTempOrder(tempOrder, product, booking);
3093
+ await this.appendProductLineToTempOrder(
3094
+ tempOrder,
3095
+ product,
3096
+ booking,
3097
+ { insertAtIndex }
3098
+ );
3022
3099
  await this.finalizeProductOrderMutation(tempOrder);
3023
3100
  this.logInfo("addProductToOrder success", {
3024
3101
  product_id: product.product_id,
@@ -3499,7 +3576,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
3499
3576
  * @example
3500
3577
  * await order.removeProductsFromOrder([identityA, identityB]);
3501
3578
  */
3502
- async removeProductsFromOrder(identities) {
3579
+ async removeProductsFromOrder(identities, options) {
3503
3580
  var _a, _b, _c, _d;
3504
3581
  const tempOrder = this.ensureTempOrder();
3505
3582
  if (!identities.length) {
@@ -3507,6 +3584,13 @@ var OrderModule = class extends import_BaseModule.BaseModule {
3507
3584
  }
3508
3585
  const productsBeforeRemove = tempOrder.products || [];
3509
3586
  const bookingsBeforeRemove = tempOrder.bookings || [];
3587
+ const preserveDisplayPosition = options == null ? void 0 : options.preserveDisplayPosition;
3588
+ const anchorIndex = preserveDisplayPosition ? productsBeforeRemove.findIndex((item) => {
3589
+ var _a2;
3590
+ const uid = ((_a2 = item.metadata) == null ? void 0 : _a2.unique_identification_number) || item.unique_identification_number;
3591
+ return uid !== void 0 && uid !== null && String(uid) === String(preserveDisplayPosition.anchorProductUid);
3592
+ }) : -1;
3593
+ const productsAfterAnchor = anchorIndex >= 0 ? productsBeforeRemove.slice(anchorIndex + 1) : [];
3510
3594
  const matchedProducts = productsBeforeRemove.filter((item) => {
3511
3595
  return identities.some((identity) => (0, import_utils3.isIdentityMatch)(item, identity));
3512
3596
  });
@@ -3547,14 +3631,37 @@ var OrderModule = class extends import_BaseModule.BaseModule {
3547
3631
  return !(isAddTimeProduct && lineBookingUid !== void 0 && lineBookingUid !== null && linkedBookingUidsToRemove.has(String(lineBookingUid)));
3548
3632
  });
3549
3633
  }
3634
+ if (preserveDisplayPosition && anchorIndex >= 0) {
3635
+ const remainingProductIndex = tempOrder.products.findIndex((item) => {
3636
+ var _a2;
3637
+ const uid = ((_a2 = item.metadata) == null ? void 0 : _a2.unique_identification_number) || item.unique_identification_number;
3638
+ return uid !== void 0 && uid !== null && String(uid) === String(preserveDisplayPosition.remainingProductUid);
3639
+ });
3640
+ if (remainingProductIndex >= 0) {
3641
+ const remainingProduct = tempOrder.products[remainingProductIndex];
3642
+ const nextProduct = productsAfterAnchor.find(
3643
+ (item) => tempOrder.products.includes(item)
3644
+ );
3645
+ const productsWithoutRemaining = [
3646
+ ...tempOrder.products.slice(0, remainingProductIndex),
3647
+ ...tempOrder.products.slice(remainingProductIndex + 1)
3648
+ ];
3649
+ const nextProductIndex = nextProduct ? productsWithoutRemaining.indexOf(nextProduct) : -1;
3650
+ tempOrder.products = nextProductIndex >= 0 ? [
3651
+ ...productsWithoutRemaining.slice(0, nextProductIndex),
3652
+ remainingProduct,
3653
+ ...productsWithoutRemaining.slice(nextProductIndex)
3654
+ ] : [...productsWithoutRemaining, remainingProduct];
3655
+ }
3656
+ }
3550
3657
  await this.finalizeAfterProductsMutation(tempOrder);
3551
3658
  this.logInfo("removeProductsFromOrder success", {
3552
3659
  identities
3553
3660
  });
3554
3661
  return tempOrder.products;
3555
3662
  }
3556
- async removeProductFromOrder(identity) {
3557
- return this.removeProductsFromOrder([identity]);
3663
+ async removeProductFromOrder(identity, options) {
3664
+ return this.removeProductsFromOrder([identity], options);
3558
3665
  }
3559
3666
  /**
3560
3667
  * 仅清空购物车行(products / bookings),不重置整单。
@@ -4187,7 +4294,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
4187
4294
  params.forceRemote = false;
4188
4295
  }
4189
4296
  const res = await this.request.get(`/order/sales/${encodeURIComponent(lookup)}`, {
4190
- with: ["products", "bookings", "payments", "customer", "summary", "contacts_info"],
4297
+ with: ["products", "bookings", "payments", "customer", "summary", "contacts_info", "refunds"],
4191
4298
  ...params.forceRemote ? { forceRemote: true } : {}
4192
4299
  }, { osServer: true });
4193
4300
  if (res.code === 200) {
@@ -165,6 +165,8 @@ export interface OrderCustomerChangePayload {
165
165
  }
166
166
  export interface OrderScanCodeResult {
167
167
  isAvailable: boolean;
168
+ /** 卡券已加入当前订单折扣列表,但不一定能立即应用。 */
169
+ isAccepted?: boolean;
168
170
  discountList: Discount[];
169
171
  scannedDiscountList: Discount[];
170
172
  type: 'server' | 'clientCalc';
@@ -319,6 +321,24 @@ export interface ReplaceTempOrderOptions {
319
321
  saveDraft?: boolean;
320
322
  }
321
323
  export type AddProductBookingInput = Record<string, any>;
324
+ /**
325
+ * 添加商品行时的顺序控制。
326
+ * 默认追加到 products 尾部;预约数量同步可插入到既有预约组的最新行之后,
327
+ * 避免步进器修改数量改变该组展示位置。
328
+ */
329
+ export interface AddProductToOrderOptions {
330
+ insertAfterProductUid?: string;
331
+ }
332
+ /**
333
+ * 删除商品后的可选位置保持策略。
334
+ * 预约合并行减量时仍删除最新真实行,再把剩余代表行移动到被删除代表行原位置。
335
+ */
336
+ export interface RemoveProductsFromOrderOptions {
337
+ preserveDisplayPosition?: {
338
+ remainingProductUid: string;
339
+ anchorProductUid: string;
340
+ };
341
+ }
322
342
  /** 更新购物车行:仅传 SKU 时命中同 SKU 第一行;多行同 SKU 须传 unique_identification_number / 选项指纹等 */
323
343
  export interface UpdateOrderProductParams {
324
344
  product_id: number | null;
@@ -713,15 +733,15 @@ export interface OrderModuleAPI {
713
733
  updateTempOrderCustomer: (customer: UpdateTempOrderCustomerInput) => OrderSnapshot['customer'];
714
734
  updateTempOrderBuzzer: (buzzer: string) => string;
715
735
  updateTempOrderContactsInfo: (contactsInfo: Record<string, any> | null) => Record<string, any> | null;
716
- addProductToOrder: (product: Partial<OrderProduct> & OrderProductIdentity, booking?: AddProductBookingInput) => Promise<OrderProduct[]>;
736
+ addProductToOrder: (product: Partial<OrderProduct> & OrderProductIdentity, booking?: AddProductBookingInput, options?: AddProductToOrderOptions) => Promise<OrderProduct[]>;
717
737
  updateOrderProduct: (params: UpdateOrderProductParams) => Promise<OrderProduct[]>;
718
738
  /** 批量更新购物车行,末尾仅触发一次促销重算与 summary 刷新 */
719
739
  updateOrderProducts: (paramsList: UpdateOrderProductParams[]) => Promise<OrderProduct[]>;
720
740
  updateOrderProductQuantity: (params: UpdateOrderProductQuantityParams) => Promise<OrderProduct[]>;
721
741
  updateOrderBooking: (params: UpdateOrderBookingParams) => any[];
722
- removeProductFromOrder: (identity: OrderProductIdentity) => Promise<OrderProduct[]>;
742
+ removeProductFromOrder: (identity: OrderProductIdentity, options?: RemoveProductsFromOrderOptions) => Promise<OrderProduct[]>;
723
743
  /** 批量删除购物车行,末尾仅触发一次促销重算与 summary 刷新 */
724
- removeProductsFromOrder: (identities: OrderProductIdentity[]) => Promise<OrderProduct[]>;
744
+ removeProductsFromOrder: (identities: OrderProductIdentity[], options?: RemoveProductsFromOrderOptions) => Promise<OrderProduct[]>;
725
745
  /** @deprecated no-op;OrderModule 不再负责 tempOrder localStorage 持久化。 */
726
746
  persistTempOrder: () => void;
727
747
  /**
@@ -1088,7 +1088,7 @@ var PaymentModule = class extends import_BaseModule.BaseModule {
1088
1088
  roundedValue = this.standardRound(baseValue, true);
1089
1089
  break;
1090
1090
  case import_types.RoundingRule.StandardDown:
1091
- case "standard_down":
1091
+ case "standard_rounding_midpoint_down":
1092
1092
  roundedValue = this.standardRound(baseValue, false);
1093
1093
  break;
1094
1094
  case import_types.RoundingRule.AlwaysUp:
@@ -40,11 +40,11 @@ export declare enum RoundingRule {
40
40
  /** 标准舍入(中点向上) */
41
41
  Standard = "standard_rounding",
42
42
  /** 标准舍入(中点向下) */
43
- StandardDown = "standard_down",
43
+ StandardDown = "standard_rounding_midpoint_down",
44
44
  /** 总是向上舍入 */
45
- AlwaysUp = "always_up",
45
+ AlwaysUp = "round_up",
46
46
  /** 总是向下舍入 */
47
- AlwaysDown = "always_down"
47
+ AlwaysDown = "round_down"
48
48
  }
49
49
  /**
50
50
  * 舍入结果
@@ -340,6 +340,8 @@ export interface CashPayment {
340
340
  processCashPayment: (amount: number, orderUuid: string) => Promise<void>;
341
341
  /** 获取现金余额 */
342
342
  getCashBalance: () => Promise<number>;
343
+ /** 根据币种面额生成现金快捷金额。 */
344
+ getRecommendedAmount: (money: number, currency: string) => number[];
343
345
  }
344
346
  /**
345
347
  * Eftpos支付接口
@@ -365,6 +367,62 @@ export interface WalletStoredInitData {
365
367
  noApplicableVoucher: any[];
366
368
  products: any[];
367
369
  }
370
+ export type WalletAssetId = string | number;
371
+ export type WalletAssetSelectionSource = 'manual' | 'scan';
372
+ export type WalletAssetsStatus = 'idle' | 'loading' | 'ready' | 'error';
373
+ /**
374
+ * 购物车与结账共享的钱包资产状态。
375
+ *
376
+ * 原始券字段完整保留在 items / selectedItems 中,Payment 只补充统一的
377
+ * 可用性、选择和加载状态,不在此层引入 UI ViewModel。
378
+ */
379
+ export interface WalletAssetsState {
380
+ status: WalletAssetsStatus;
381
+ items: any[];
382
+ selectedIds: WalletAssetId[];
383
+ selectedItems: any[];
384
+ committedIds: WalletAssetId[];
385
+ selectionSources: Record<string, WalletAssetSelectionSource>;
386
+ totalSelectedAmount: number;
387
+ customerId?: string | number;
388
+ error: string | null;
389
+ revision: number;
390
+ }
391
+ /**
392
+ * Shared Wallet Assets 的可序列化运行时快照。
393
+ *
394
+ * state 供购物车 / Checkout 直接恢复展示与选择;searchResults 和
395
+ * calculationContext 用于商品变化后继续重验扫码搜索过的 Wallet。
396
+ */
397
+ export interface WalletAssetsSnapshot {
398
+ state: WalletAssetsState;
399
+ searchResults: SearchIdentificationCodeItem[];
400
+ /** 已搜索但尚未满足订单条件、等待首次可用后自动选中的 Wallet Asset code。 */
401
+ pendingAutoSelectSearchCodes?: string[];
402
+ walletParams: WalletDeductionRecommendParams | null;
403
+ calculationContext: {
404
+ orderTotalAmount: number;
405
+ products: any[];
406
+ };
407
+ }
408
+ export interface WalletAssetsBehaviorOptions {
409
+ /** 搜索到 Wallet Asset 后,当前或后续首次可用时是否自动选中。 */
410
+ autoSelectAfterSearch?: boolean;
411
+ }
412
+ export interface RefreshWalletAssetsOptions {
413
+ /** 商品/金额变化默认保留并重验选择;客户变化由实现层强制清空。 */
414
+ preserveSelection?: boolean;
415
+ }
416
+ export interface SelectWalletAssetOptions {
417
+ selected: boolean;
418
+ source?: WalletAssetSelectionSource;
419
+ }
420
+ export interface ScanWalletAssetResult {
421
+ type: SearchIdentificationCodeResult['type'];
422
+ asset?: any;
423
+ selected: boolean;
424
+ state: WalletAssetsState;
425
+ }
368
426
  export interface WalletPassPayment {
369
427
  /** 生成钱包API默认参数 */
370
428
  generateWalletParams: (businessData: WalletInitBusinessData) => WalletDeductionRecommendParams;
@@ -416,6 +474,24 @@ export interface WalletPassPayment {
416
474
  processWalletPayment: (amount: number, orderUuid: string, voucherId?: string) => Promise<void>;
417
475
  /** 获取钱包余额 */
418
476
  getWalletBalance: (voucherId: string) => Promise<number>;
477
+ /** 刷新购物车/结账共享的钱包资产,刷新本身不会默认选券。 */
478
+ refreshWalletAssetsFromBusinessAsync: (businessData: WalletInitBusinessData, options?: RefreshWalletAssetsOptions) => Promise<WalletAssetsState>;
479
+ /** 获取共享钱包资产状态快照。 */
480
+ getWalletAssetsState: () => WalletAssetsState;
481
+ /** 配置仅影响 Wallet Assets 的交互行为。 */
482
+ configureWalletAssetsBehavior: (options: WalletAssetsBehaviorOptions) => void;
483
+ /** 导出供 Shared Checkout 跨运行时传递的 Wallet Assets 快照。 */
484
+ exportWalletAssetsSnapshot: () => WalletAssetsSnapshot;
485
+ /** 恢复 Shared Checkout 传回的 Wallet Assets 快照。 */
486
+ importWalletAssetsSnapshot: (snapshot: WalletAssetsSnapshot) => WalletAssetsState;
487
+ /** 设置单张钱包资产的选择状态并重新计算其实际抵扣额。 */
488
+ selectWalletAsset: (assetId: WalletAssetId, options: SelectWalletAssetOptions) => WalletAssetsState;
489
+ /** 精确搜索钱包资产;仅当前券可用时自动选择。 */
490
+ scanWalletAssetAsync: (code: string) => Promise<ScanWalletAssetResult>;
491
+ /** 清空未确认的钱包资产选择。 */
492
+ clearWalletAssetSelection: () => WalletAssetsState;
493
+ /** 同步订单中已经支付、不可撤销的钱包资产。 */
494
+ setCommittedWalletAssets: (committedIds: WalletAssetId[], committedAssets?: any[]) => WalletAssetsState;
419
495
  }
420
496
  /**
421
497
  * 支付模块API接口
@@ -608,7 +684,8 @@ export declare enum WalletPassHooks {
608
684
  OnSearchIdentificationCodeCompleted = "wallet:onSearchIdentificationCodeCompleted",
609
685
  OnWalletInitializationStarted = "wallet:onWalletInitializationStarted",
610
686
  OnWalletInitializationCompleted = "wallet:onWalletInitializationCompleted",
611
- OnWalletInitializationFailed = "wallet:onWalletInitializationFailed"
687
+ OnWalletInitializationFailed = "wallet:onWalletInitializationFailed",
688
+ OnWalletAssetsStateChanged = "wallet:onWalletAssetsStateChanged"
612
689
  }
613
690
  /**
614
691
  * 支付钩子事件(统一接口)
@@ -51,9 +51,9 @@ var TaskRunStatus = /* @__PURE__ */ ((TaskRunStatus2) => {
51
51
  })(TaskRunStatus || {});
52
52
  var RoundingRule = /* @__PURE__ */ ((RoundingRule2) => {
53
53
  RoundingRule2["Standard"] = "standard_rounding";
54
- RoundingRule2["StandardDown"] = "standard_down";
55
- RoundingRule2["AlwaysUp"] = "always_up";
56
- RoundingRule2["AlwaysDown"] = "always_down";
54
+ RoundingRule2["StandardDown"] = "standard_rounding_midpoint_down";
55
+ RoundingRule2["AlwaysUp"] = "round_up";
56
+ RoundingRule2["AlwaysDown"] = "round_down";
57
57
  return RoundingRule2;
58
58
  })(RoundingRule || {});
59
59
  var RoundingInterval = /* @__PURE__ */ ((RoundingInterval2) => {
@@ -73,6 +73,7 @@ var WalletPassHooks = /* @__PURE__ */ ((WalletPassHooks2) => {
73
73
  WalletPassHooks2["OnWalletInitializationStarted"] = "wallet:onWalletInitializationStarted";
74
74
  WalletPassHooks2["OnWalletInitializationCompleted"] = "wallet:onWalletInitializationCompleted";
75
75
  WalletPassHooks2["OnWalletInitializationFailed"] = "wallet:onWalletInitializationFailed";
76
+ WalletPassHooks2["OnWalletAssetsStateChanged"] = "wallet:onWalletAssetsStateChanged";
76
77
  return WalletPassHooks2;
77
78
  })(WalletPassHooks || {});
78
79
  var PaymentHooks = /* @__PURE__ */ ((PaymentHooks2) => {
@@ -1,4 +1,4 @@
1
- import { WalletPassPayment, WalletDeductionRecommendParams, WalletRecommendItem, UserIdentificationCodeParams, UserIdentificationCodeItem, SearchIdentificationCodeParams, SearchIdentificationCodeItem, SearchIdentificationCodeResult, WalletInitBusinessData } from './types';
1
+ import { WalletPassPayment, WalletDeductionRecommendParams, WalletRecommendItem, UserIdentificationCodeParams, UserIdentificationCodeItem, SearchIdentificationCodeParams, SearchIdentificationCodeItem, SearchIdentificationCodeResult, WalletInitBusinessData, WalletAssetId, WalletAssetsBehaviorOptions, WalletAssetsSnapshot, WalletAssetsState, RefreshWalletAssetsOptions, SelectWalletAssetOptions, ScanWalletAssetResult } from './types';
2
2
  import type { PaymentModule } from './index';
3
3
  /**
4
4
  * 钱包支付实现
@@ -10,12 +10,30 @@ export declare class WalletPassPaymentImpl implements WalletPassPayment {
10
10
  private searchResults;
11
11
  private walletParams;
12
12
  private walletInitData;
13
+ private walletAssetsState;
14
+ private walletAssetsRequestSequence;
15
+ private walletAssetsBehavior;
16
+ private pendingAutoSelectSearchCodes;
17
+ private walletAssetsCalculationContext;
13
18
  constructor(paymentModule: PaymentModule);
19
+ configureWalletAssetsBehavior(options: WalletAssetsBehaviorOptions): void;
14
20
  /**
15
21
  * 发送事件的辅助方法
16
22
  * 支持使用WalletPassHooks或PaymentHooks
17
23
  */
18
24
  private emitEvent;
25
+ private publishWalletAssetsState;
26
+ private recalculateWalletAssets;
27
+ /** 获取当前缓存中所有支付类 Wallet 资产的扫码 code。 */
28
+ private getSearchedWalletAssetCodes;
29
+ /**
30
+ * 用订单重算接口返回的数据更新扫码 Wallet 缓存。
31
+ * 接口暂时缺失某个扫码 code 时继续保留旧值,避免列表闪空。
32
+ */
33
+ private syncSearchedWalletAssets;
34
+ private isPendingAutoSelectAsset;
35
+ private clearPendingAutoSelectForAsset;
36
+ private queuePendingAutoSelectForAssets;
19
37
  /**
20
38
  * 生成钱包API默认参数
21
39
  * 根据业务数据生成标准的钱包API参数,并存储在模块中
@@ -57,6 +75,11 @@ export declare class WalletPassPaymentImpl implements WalletPassPayment {
57
75
  noApplicableVoucher: any[];
58
76
  products: any[];
59
77
  }>;
78
+ /**
79
+ * 商品或订单金额变化后,使用聚合接口一次重算已选与剩余 WalletPass。
80
+ * Walk-In 不发送 customer_id;会员才携带真实 customer_id。
81
+ */
82
+ private aggregateRecalculateWalletAssetsAsync;
60
83
  getWalletPassRecommendListAsync(params: WalletDeductionRecommendParams): Promise<WalletRecommendItem[]>;
61
84
  formatWalletPassList2PreparePayments(list: WalletRecommendItem[]): {
62
85
  voucher_id: number;
@@ -73,6 +96,20 @@ export declare class WalletPassPaymentImpl implements WalletPassPayment {
73
96
  searchIdentificationCodeAsync(params: SearchIdentificationCodeParams, config?: {
74
97
  noCache?: boolean;
75
98
  }): Promise<SearchIdentificationCodeResult>;
99
+ /**
100
+ * 刷新购物车与结账共享的钱包资产。
101
+ *
102
+ * 与 legacy initializeWalletDataFromBusinessAsync 不同,这里维护选择状态;
103
+ * 推荐结果只作为可用性/金额计算输入,不会自动成为 selectedIds。
104
+ */
105
+ refreshWalletAssetsFromBusinessAsync(businessData: WalletInitBusinessData, options?: RefreshWalletAssetsOptions): Promise<WalletAssetsState>;
106
+ getWalletAssetsState(): WalletAssetsState;
107
+ exportWalletAssetsSnapshot(): WalletAssetsSnapshot;
108
+ importWalletAssetsSnapshot(snapshot: WalletAssetsSnapshot): WalletAssetsState;
109
+ selectWalletAsset(assetId: WalletAssetId, options: SelectWalletAssetOptions): WalletAssetsState;
110
+ scanWalletAssetAsync(code: string): Promise<ScanWalletAssetResult>;
111
+ clearWalletAssetSelection(): WalletAssetsState;
112
+ setCommittedWalletAssets(committedIds: WalletAssetId[], committedAssets?: any[]): WalletAssetsState;
76
113
  processWalletPayment(amount: number, orderUuid: string, voucherId?: string): Promise<void>;
77
114
  getWalletBalance(voucherId: string): Promise<number>;
78
115
  /**