@pisell/pisellos 2.2.314 → 2.2.316

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 (268) hide show
  1. package/dist/index.d.ts +2 -0
  2. package/dist/index.js +3 -1
  3. package/dist/model/strategy/adapter/itemRule/adapter.d.ts +3 -1
  4. package/dist/model/strategy/adapter/itemRule/adapter.js +74 -7
  5. package/dist/model/strategy/adapter/itemRule/evaluator.d.ts +3 -1
  6. package/dist/model/strategy/adapter/itemRule/evaluator.js +13 -0
  7. package/dist/model/strategy/adapter/itemRule/type.d.ts +40 -1
  8. package/dist/model/strategy/adapter/itemRule/type.js +10 -0
  9. package/dist/model/strategy/adapter/promotion/evaluator.js +89 -104
  10. package/dist/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.d.ts +3 -1
  11. package/dist/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +28 -4
  12. package/dist/modules/BookingContext/utils/productExtend.d.ts +7 -0
  13. package/dist/modules/BookingContext/utils/productExtend.js +62 -18
  14. package/dist/modules/BookingContext/utils/resources.js +1 -1
  15. package/dist/modules/BusinessAvailability/index.d.ts +30 -0
  16. package/dist/modules/BusinessAvailability/index.js +204 -0
  17. package/dist/modules/Cart/index.js +4 -3
  18. package/dist/modules/Cart/types.d.ts +2 -0
  19. package/dist/modules/Cart/utils/cartProduct.d.ts +3 -1
  20. package/dist/modules/Cart/utils/cartProduct.js +59 -32
  21. package/dist/modules/FulfillmentTiming/index.d.ts +11 -0
  22. package/dist/modules/FulfillmentTiming/index.js +407 -0
  23. package/dist/modules/FulfillmentTiming/types.d.ts +67 -0
  24. package/dist/modules/FulfillmentTiming/types.js +1 -0
  25. package/dist/modules/Holder/utils.d.ts +2 -0
  26. package/dist/modules/Holder/utils.js +32 -2
  27. package/dist/modules/OpenData/contactInfo.d.ts +20 -0
  28. package/dist/modules/OpenData/contactInfo.js +70 -0
  29. package/dist/modules/OpenData/index.d.ts +1 -0
  30. package/dist/modules/OpenData/index.js +1 -0
  31. package/dist/modules/OpenData/types.d.ts +23 -0
  32. package/dist/modules/Order/holderRelations.d.ts +14 -0
  33. package/dist/modules/Order/holderRelations.js +154 -0
  34. package/dist/modules/Order/index.d.ts +16 -4
  35. package/dist/modules/Order/index.js +926 -511
  36. package/dist/modules/Order/localEftposState.d.ts +13 -0
  37. package/dist/modules/Order/localEftposState.js +39 -0
  38. package/dist/modules/Order/pickupTiming.d.ts +46 -0
  39. package/dist/modules/Order/pickupTiming.js +53 -0
  40. package/dist/modules/Order/types.d.ts +49 -3
  41. package/dist/modules/Order/types.js +2 -0
  42. package/dist/modules/Order/utils/discountProductLineIdentity.d.ts +1 -1
  43. package/dist/modules/Order/utils/discountProductLineIdentity.js +142 -7
  44. package/dist/modules/Order/utils/orderCollectionIdentity.js +24 -10
  45. package/dist/modules/Order/utils.d.ts +2 -1
  46. package/dist/modules/Order/utils.js +61 -17
  47. package/dist/modules/Payment/index.js +9 -10
  48. package/dist/modules/Product/types.d.ts +11 -0
  49. package/dist/modules/ProductList/index.js +1 -1
  50. package/dist/modules/ResourcePlanner/planner.d.ts +2 -0
  51. package/dist/modules/ResourcePlanner/planner.js +332 -23
  52. package/dist/modules/ResourcePlanner/types.d.ts +29 -0
  53. package/dist/modules/Rules/index.js +62 -61
  54. package/dist/modules/Schedule/index.d.ts +8 -0
  55. package/dist/modules/Schedule/index.js +141 -39
  56. package/dist/modules/index.d.ts +1 -0
  57. package/dist/modules/index.js +1 -0
  58. package/dist/plugins/request.d.ts +7 -0
  59. package/dist/server/index.d.ts +1 -0
  60. package/dist/server/index.js +1557 -1322
  61. package/dist/server/modules/form/index.d.ts +19 -0
  62. package/dist/server/modules/form/index.js +205 -0
  63. package/dist/server/modules/form/types.d.ts +13 -0
  64. package/dist/server/modules/form/types.js +3 -0
  65. package/dist/server/modules/index.d.ts +3 -0
  66. package/dist/server/modules/index.js +4 -0
  67. package/dist/server/modules/order/index.d.ts +19 -0
  68. package/dist/server/modules/order/index.js +1071 -774
  69. package/dist/server/modules/order/types.d.ts +4 -0
  70. package/dist/server/modules/products/index.d.ts +28 -7
  71. package/dist/server/modules/products/index.js +1297 -770
  72. package/dist/server/modules/products/replication.d.ts +8 -0
  73. package/dist/server/modules/products/replication.js +69 -0
  74. package/dist/server/modules/products/types.d.ts +12 -1
  75. package/dist/server/modules/products/types.js +4 -0
  76. package/dist/server/test.json +1 -1
  77. package/dist/server/utils/small-ticket.d.ts +1 -85
  78. package/dist/server/utils/small-ticket.js +2 -2542
  79. package/dist/shared/payment/loadPaymentMethods.d.ts +9 -0
  80. package/dist/shared/payment/loadPaymentMethods.js +66 -0
  81. package/dist/shared/receipt/customerInvoiceLanguages.d.ts +8 -0
  82. package/dist/shared/receipt/customerInvoiceLanguages.js +60 -0
  83. package/dist/shared/receipt/loadCustomerInvoiceLayout.d.ts +27 -0
  84. package/dist/shared/receipt/loadCustomerInvoiceLayout.js +184 -0
  85. package/dist/shared/receipt/small-ticket.d.ts +96 -0
  86. package/dist/shared/receipt/small-ticket.js +2763 -0
  87. package/dist/shared/receipt/templateVisibility.d.ts +26 -0
  88. package/dist/shared/receipt/templateVisibility.js +68 -0
  89. package/dist/shared/sales/loadSalesOrderSnapshot.d.ts +9 -0
  90. package/dist/shared/sales/loadSalesOrderSnapshot.js +21 -0
  91. package/dist/solution/BaseSales/fulfillmentItemRules.d.ts +27 -0
  92. package/dist/solution/BaseSales/fulfillmentItemRules.js +750 -0
  93. package/dist/solution/BaseSales/index.d.ts +43 -16
  94. package/dist/solution/BaseSales/index.js +2028 -1247
  95. package/dist/solution/BaseSales/pickupTiming.d.ts +47 -0
  96. package/dist/solution/BaseSales/pickupTiming.js +616 -0
  97. package/dist/solution/BaseSales/types.d.ts +12 -0
  98. package/dist/solution/BaseSales/utils/parseSalesResponse.d.ts +3 -1
  99. package/dist/solution/BaseSales/utils/parseSalesResponse.js +10 -3
  100. package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +14 -4
  101. package/dist/solution/BookingByStep/index.d.ts +4 -2
  102. package/dist/solution/BookingByStep/index.js +8 -3
  103. package/dist/solution/BookingTicket/index.d.ts +23 -3
  104. package/dist/solution/BookingTicket/index.js +867 -507
  105. package/dist/solution/BookingTicket/utils/addProductDecision.d.ts +0 -1
  106. package/dist/solution/BookingTicket/utils/addProductDecision.js +1 -1
  107. package/dist/solution/BookingTicket/utils/cartView.js +3 -1
  108. package/dist/solution/OrderInvoicePreview/index.d.ts +66 -0
  109. package/dist/solution/OrderInvoicePreview/index.js +340 -0
  110. package/dist/solution/RegisterAndLogin/config.js +54 -11
  111. package/dist/solution/RegisterAndLogin/index.d.ts +2 -0
  112. package/dist/solution/RegisterAndLogin/index.js +1 -1
  113. package/dist/solution/RegisterAndLogin/utils.d.ts +12 -4
  114. package/dist/solution/RegisterAndLogin/utils.js +36 -16
  115. package/dist/solution/ScanOrder/index.js +65 -31
  116. package/dist/solution/ScanOrder/types.d.ts +3 -0
  117. package/dist/solution/UnifiedBookingSales/businessAvailability.d.ts +27 -0
  118. package/dist/solution/UnifiedBookingSales/businessAvailability.js +292 -0
  119. package/dist/solution/UnifiedBookingSales/index.d.ts +36 -4
  120. package/dist/solution/UnifiedBookingSales/index.js +1715 -906
  121. package/dist/solution/UnifiedBookingSales/types.d.ts +16 -2
  122. package/dist/solution/VenueBooking/index.d.ts +15 -2
  123. package/dist/solution/VenueBooking/index.js +1062 -785
  124. package/dist/solution/VenueBooking/types.d.ts +2 -1
  125. package/dist/solution/VenueBooking/types.js +1 -0
  126. package/dist/solution/VenueBooking/utils/dateSummary.d.ts +1 -0
  127. package/dist/solution/VenueBooking/utils/dateSummary.js +7 -4
  128. package/dist/solution/VenueBooking/utils/smartPricing.d.ts +71 -0
  129. package/dist/solution/VenueBooking/utils/smartPricing.js +273 -0
  130. package/dist/solution/VenueBooking/utils/timeSlot.d.ts +1 -0
  131. package/dist/solution/VenueBooking/utils/timeSlot.js +6 -3
  132. package/dist/solution/index.d.ts +1 -0
  133. package/dist/solution/index.js +2 -1
  134. package/lib/index.d.ts +2 -0
  135. package/lib/index.js +21 -1
  136. package/lib/model/strategy/adapter/itemRule/adapter.d.ts +3 -1
  137. package/lib/model/strategy/adapter/itemRule/adapter.js +54 -4
  138. package/lib/model/strategy/adapter/itemRule/evaluator.d.ts +3 -1
  139. package/lib/model/strategy/adapter/itemRule/evaluator.js +9 -0
  140. package/lib/model/strategy/adapter/itemRule/type.d.ts +40 -1
  141. package/lib/model/strategy/adapter/itemRule/type.js +10 -0
  142. package/lib/model/strategy/adapter/promotion/evaluator.js +9 -14
  143. package/lib/model/strategy/adapter/promotion/index.js +46 -1
  144. package/lib/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.d.ts +3 -1
  145. package/lib/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +17 -1
  146. package/lib/modules/BookingContext/utils/productExtend.d.ts +7 -0
  147. package/lib/modules/BookingContext/utils/productExtend.js +45 -2
  148. package/lib/modules/BookingContext/utils/resources.js +1 -1
  149. package/lib/modules/BusinessAvailability/index.d.ts +30 -0
  150. package/lib/modules/BusinessAvailability/index.js +146 -0
  151. package/lib/modules/Cart/index.js +4 -2
  152. package/lib/modules/Cart/types.d.ts +2 -0
  153. package/lib/modules/Cart/utils/cartProduct.d.ts +3 -1
  154. package/lib/modules/Cart/utils/cartProduct.js +39 -20
  155. package/lib/modules/FulfillmentTiming/index.d.ts +11 -0
  156. package/lib/modules/FulfillmentTiming/index.js +359 -0
  157. package/lib/modules/FulfillmentTiming/types.d.ts +67 -0
  158. package/lib/modules/FulfillmentTiming/types.js +5 -0
  159. package/lib/modules/Holder/utils.d.ts +2 -0
  160. package/lib/modules/Holder/utils.js +27 -1
  161. package/lib/modules/OpenData/contactInfo.d.ts +20 -0
  162. package/lib/modules/OpenData/contactInfo.js +56 -0
  163. package/lib/modules/OpenData/index.d.ts +1 -0
  164. package/lib/modules/OpenData/index.js +12 -0
  165. package/lib/modules/OpenData/types.d.ts +23 -0
  166. package/lib/modules/Order/holderRelations.d.ts +14 -0
  167. package/lib/modules/Order/holderRelations.js +98 -0
  168. package/lib/modules/Order/index.d.ts +16 -4
  169. package/lib/modules/Order/index.js +284 -50
  170. package/lib/modules/Order/localEftposState.d.ts +13 -0
  171. package/lib/modules/Order/localEftposState.js +44 -0
  172. package/lib/modules/Order/pickupTiming.d.ts +46 -0
  173. package/lib/modules/Order/pickupTiming.js +61 -0
  174. package/lib/modules/Order/types.d.ts +49 -3
  175. package/lib/modules/Order/types.js +1 -0
  176. package/lib/modules/Order/utils/discountProductLineIdentity.d.ts +1 -1
  177. package/lib/modules/Order/utils/discountProductLineIdentity.js +51 -2
  178. package/lib/modules/Order/utils/orderCollectionIdentity.js +5 -0
  179. package/lib/modules/Order/utils.d.ts +2 -1
  180. package/lib/modules/Order/utils.js +49 -11
  181. package/lib/modules/Payment/index.js +3 -4
  182. package/lib/modules/Product/types.d.ts +11 -0
  183. package/lib/modules/ProductList/index.js +1 -1
  184. package/lib/modules/ResourcePlanner/planner.d.ts +2 -0
  185. package/lib/modules/ResourcePlanner/planner.js +252 -8
  186. package/lib/modules/ResourcePlanner/types.d.ts +29 -0
  187. package/lib/modules/Rules/index.js +2 -1
  188. package/lib/modules/Schedule/index.d.ts +8 -0
  189. package/lib/modules/Schedule/index.js +68 -12
  190. package/lib/modules/index.d.ts +1 -0
  191. package/lib/modules/index.js +11 -0
  192. package/lib/plugins/request.d.ts +7 -0
  193. package/lib/server/index.d.ts +1 -0
  194. package/lib/server/index.js +166 -39
  195. package/lib/server/modules/form/index.d.ts +19 -0
  196. package/lib/server/modules/form/index.js +73 -0
  197. package/lib/server/modules/form/types.d.ts +13 -0
  198. package/lib/server/modules/form/types.js +9 -0
  199. package/lib/server/modules/index.d.ts +3 -0
  200. package/lib/server/modules/index.js +15 -1
  201. package/lib/server/modules/order/index.d.ts +19 -0
  202. package/lib/server/modules/order/index.js +160 -7
  203. package/lib/server/modules/order/types.d.ts +4 -0
  204. package/lib/server/modules/products/index.d.ts +28 -7
  205. package/lib/server/modules/products/index.js +226 -63
  206. package/lib/server/modules/products/replication.d.ts +8 -0
  207. package/lib/server/modules/products/replication.js +30 -0
  208. package/lib/server/modules/products/types.d.ts +12 -1
  209. package/lib/server/modules/products/types.js +3 -0
  210. package/lib/server/test.json +1 -1
  211. package/lib/server/utils/small-ticket.d.ts +1 -85
  212. package/lib/server/utils/small-ticket.js +9 -2305
  213. package/lib/shared/payment/loadPaymentMethods.d.ts +9 -0
  214. package/lib/shared/payment/loadPaymentMethods.js +40 -0
  215. package/lib/shared/receipt/customerInvoiceLanguages.d.ts +8 -0
  216. package/lib/shared/receipt/customerInvoiceLanguages.js +58 -0
  217. package/lib/shared/receipt/loadCustomerInvoiceLayout.d.ts +27 -0
  218. package/lib/shared/receipt/loadCustomerInvoiceLayout.js +124 -0
  219. package/lib/shared/receipt/small-ticket.d.ts +96 -0
  220. package/lib/shared/receipt/small-ticket.js +2555 -0
  221. package/lib/shared/receipt/templateVisibility.d.ts +26 -0
  222. package/lib/shared/receipt/templateVisibility.js +67 -0
  223. package/lib/shared/sales/loadSalesOrderSnapshot.d.ts +9 -0
  224. package/lib/shared/sales/loadSalesOrderSnapshot.js +22 -0
  225. package/lib/solution/BaseSales/fulfillmentItemRules.d.ts +27 -0
  226. package/lib/solution/BaseSales/fulfillmentItemRules.js +259 -0
  227. package/lib/solution/BaseSales/index.d.ts +43 -16
  228. package/lib/solution/BaseSales/index.js +401 -33
  229. package/lib/solution/BaseSales/pickupTiming.d.ts +47 -0
  230. package/lib/solution/BaseSales/pickupTiming.js +290 -0
  231. package/lib/solution/BaseSales/types.d.ts +12 -0
  232. package/lib/solution/BaseSales/utils/parseSalesResponse.d.ts +3 -1
  233. package/lib/solution/BaseSales/utils/parseSalesResponse.js +7 -0
  234. package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +16 -8
  235. package/lib/solution/BookingByStep/index.d.ts +4 -2
  236. package/lib/solution/BookingByStep/index.js +7 -3
  237. package/lib/solution/BookingTicket/index.d.ts +23 -3
  238. package/lib/solution/BookingTicket/index.js +197 -9
  239. package/lib/solution/BookingTicket/utils/addProductDecision.d.ts +0 -1
  240. package/lib/solution/BookingTicket/utils/addProductDecision.js +1 -1
  241. package/lib/solution/BookingTicket/utils/cartView.js +3 -1
  242. package/lib/solution/OrderInvoicePreview/index.d.ts +66 -0
  243. package/lib/solution/OrderInvoicePreview/index.js +246 -0
  244. package/lib/solution/RegisterAndLogin/config.js +57 -11
  245. package/lib/solution/RegisterAndLogin/index.d.ts +2 -0
  246. package/lib/solution/RegisterAndLogin/index.js +9 -1
  247. package/lib/solution/RegisterAndLogin/utils.d.ts +12 -4
  248. package/lib/solution/RegisterAndLogin/utils.js +24 -17
  249. package/lib/solution/ScanOrder/index.js +59 -20
  250. package/lib/solution/ScanOrder/types.d.ts +3 -0
  251. package/lib/solution/UnifiedBookingSales/businessAvailability.d.ts +27 -0
  252. package/lib/solution/UnifiedBookingSales/businessAvailability.js +138 -0
  253. package/lib/solution/UnifiedBookingSales/index.d.ts +36 -4
  254. package/lib/solution/UnifiedBookingSales/index.js +562 -75
  255. package/lib/solution/UnifiedBookingSales/types.d.ts +16 -2
  256. package/lib/solution/VenueBooking/index.d.ts +15 -2
  257. package/lib/solution/VenueBooking/index.js +243 -96
  258. package/lib/solution/VenueBooking/types.d.ts +2 -1
  259. package/lib/solution/VenueBooking/types.js +1 -0
  260. package/lib/solution/VenueBooking/utils/dateSummary.d.ts +1 -0
  261. package/lib/solution/VenueBooking/utils/dateSummary.js +7 -4
  262. package/lib/solution/VenueBooking/utils/smartPricing.d.ts +71 -0
  263. package/lib/solution/VenueBooking/utils/smartPricing.js +224 -0
  264. package/lib/solution/VenueBooking/utils/timeSlot.d.ts +1 -0
  265. package/lib/solution/VenueBooking/utils/timeSlot.js +5 -2
  266. package/lib/solution/index.d.ts +1 -0
  267. package/lib/solution/index.js +11 -0
  268. package/package.json +1 -1
@@ -4,11 +4,21 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.OrderModule = void 0;
7
+ Object.defineProperty(exports, "isFulfillmentTimingBooking", {
8
+ enumerable: true,
9
+ get: function () {
10
+ return _pickupTiming.isFulfillmentTimingBooking;
11
+ }
12
+ });
13
+ var _loadSalesOrderSnapshot = require("../../shared/sales/loadSalesOrderSnapshot");
14
+ var _localEftposState = require("./localEftposState");
7
15
  var _BaseModule = require("../BaseModule");
8
16
  var _types = require("./types");
9
17
  var _utils = require("./utils");
10
18
  var _utils2 = require("../Product/utils");
19
+ var _holderRelations = require("./holderRelations");
11
20
  var _weighedProduct = require("./utils/weighedProduct");
21
+ var _pickupTiming = require("./pickupTiming");
12
22
  var _dayjs = _interopRequireDefault(require("dayjs"));
13
23
  var _availability = require("../Discount/availability");
14
24
  var _osPrivatePayload = require("../../utils/os-private-payload");
@@ -433,6 +443,29 @@ class OrderModule extends _BaseModule.BaseModule {
433
443
  this.otherParams = otherParams;
434
444
  }
435
445
 
446
+ /** Internal intent boundary shared with asynchronous scanner/detail flows. */
447
+ createCartIntentCommitGuard(action, tempOrder = this.store.tempOrder) {
448
+ const hook = this.orderHooks?.beforeCartIntentCommit;
449
+ if (!hook) return () => {};
450
+ const getScope = this.orderHooks?.getCartIntentScope;
451
+ const scope = getScope?.();
452
+ const otherParams = this.otherParams;
453
+ let committed = false;
454
+ return () => {
455
+ if (this.store.tempOrder !== tempOrder || this.otherParams !== otherParams || this.orderHooks?.beforeCartIntentCommit !== hook || this.orderHooks?.getCartIntentScope !== getScope || getScope?.() !== scope) {
456
+ throw new Error('[Order] Cart intent belongs to a previous order or business');
457
+ }
458
+ // Derived gifts, discounts and persistence belong to the accepted intent,
459
+ // but an asynchronous continuation must never target a different cart.
460
+ if (committed) return;
461
+ hook({
462
+ action,
463
+ tempOrder
464
+ });
465
+ committed = true;
466
+ };
467
+ }
468
+
436
469
  /** PC/H5 属于在线店铺;其它或缺失平台统一沿用 POS 行为。 */
437
470
  isOnlineStorePlatform() {
438
471
  const platform = String(this.otherParams?.platform || '').trim().toLowerCase();
@@ -511,7 +544,7 @@ class OrderModule extends _BaseModule.BaseModule {
511
544
  this.store.rules = rules;
512
545
  }
513
546
  createDefaultRulesHooks() {
514
- return (0, _utils.createDefaultOrderRulesHooks)(() => this.store.tempOrder?.bookings || []);
547
+ return (0, _utils.createDefaultOrderRulesHooks)(() => this.store.tempOrder?.bookings || [], () => this.store.tempOrder?.holder_relations);
515
548
  }
516
549
 
517
550
  /**
@@ -661,6 +694,7 @@ class OrderModule extends _BaseModule.BaseModule {
661
694
  });
662
695
  }
663
696
  async scanCode(code, customerId) {
697
+ const beforeCommit = this.createCartIntentCommitGuard('scan_promotion');
664
698
  if (this.isActiveVirtualSettlementOrder(this.store.tempOrder)) {
665
699
  return {
666
700
  type: 'clientCalc',
@@ -708,6 +742,7 @@ class OrderModule extends _BaseModule.BaseModule {
708
742
  }));
709
743
  const currentSelected = this.getDiscountList().filter(n => n.isSelected);
710
744
  if (currentSelected.length && currentSelected.some(n => withScanList.some(m => m.id === n.id))) {
745
+ beforeCommit();
711
746
  await this.syncScannedDiscountsToOriginalDiscountList(this.getDiscountList());
712
747
  return {
713
748
  type: 'clientCalc',
@@ -741,6 +776,7 @@ class OrderModule extends _BaseModule.BaseModule {
741
776
  const normalizedNewDiscountList = newDiscountList ? markScannedDiscounts(newDiscountList, withScanList) : undefined;
742
777
  let resolvedDiscountList = normalizedNewDiscountList || this.getDiscountList();
743
778
  if (isAvailable && normalizedNewDiscountList) {
779
+ beforeCommit();
744
780
  await this.store.discount?.setDiscountList(normalizedNewDiscountList);
745
781
  // 实际写回商品时沿用扫码试算的优先级,避免旧商品行的选卡覆盖本次扫码。
746
782
  this.applyDiscount({
@@ -749,6 +785,7 @@ class OrderModule extends _BaseModule.BaseModule {
749
785
  await this.syncScannedDiscountsToOriginalDiscountList(this.getDiscountList());
750
786
  resolvedDiscountList = this.getDiscountList();
751
787
  } else if (shouldRetainForLater) {
788
+ beforeCommit();
752
789
  // 只合并本次识别的卡券;不把试算产生的其他选券状态写回订单。
753
790
  const retainedDiscounts = new Map(this.getDiscountList().map(discount => [String(discount.id), discount]));
754
791
  evaluatedScannedDiscounts.forEach(discount => {
@@ -821,7 +858,7 @@ class OrderModule extends _BaseModule.BaseModule {
821
858
  discount_list: (0, _utils.normalizeOrderProductDiscountList)((0, _manualProductDiscount.syncManualProductDiscountProductNum)(mergedProduct.discount_list, mergedProduct.num))
822
859
  };
823
860
  }));
824
- (0, _discountProductLineIdentity.ensureUniqueProductLineUids)(tempOrder);
861
+ (0, _discountProductLineIdentity.ensureUniqueProductLineUids)(tempOrder, discountList);
825
862
  (0, _discountProductLineIdentity.consolidateDiscountFreeProductLines)(tempOrder);
826
863
  }
827
864
  if (result?.discountList) {
@@ -878,7 +915,8 @@ class OrderModule extends _BaseModule.BaseModule {
878
915
  * - 不主动调用 applyDiscount / recalculateSummary,调用方负责跟进;
879
916
  * - 返回的 payload 只能在 applyDiscount(商品写路径还需 summary/persist)完成后发布。
880
917
  */
881
- async evaluatePromotion() {
918
+ async evaluatePromotion(options) {
919
+ if (options?.isCurrentOrder && !options.isCurrentOrder()) return null;
882
920
  if (this.isApplyingPromotion || this.isEmittingPromotionApplied) {
883
921
  return null;
884
922
  }
@@ -939,6 +977,10 @@ class OrderModule extends _BaseModule.BaseModule {
939
977
  continue;
940
978
  }
941
979
  }
980
+ const giftMutationBaseline = options?.isCurrentOrder ? (0, _lodashEs.cloneDeep)({
981
+ products: tempOrder.products,
982
+ bookings: tempOrder.bookings
983
+ }) : null;
942
984
  try {
943
985
  const giftResult = await this.giftSelectResolver({
944
986
  strategyId: addAction.strategyId,
@@ -947,6 +989,11 @@ class OrderModule extends _BaseModule.BaseModule {
947
989
  giftOptions: addAction.giftOptions,
948
990
  sourceProductIds: addAction.sourceProductIds
949
991
  });
992
+ if (options?.isCurrentOrder && !options.isCurrentOrder()) return null;
993
+ if (options?.isCurrentOrder && !(0, _lodashEs.isEqual)({
994
+ products: tempOrder.products,
995
+ bookings: tempOrder.bookings
996
+ }, giftMutationBaseline)) return null;
950
997
  const giftProducts = Array.isArray(giftResult) ? giftResult : giftResult?.products;
951
998
  const giftBookings = Array.isArray(giftResult) ? [] : giftResult?.bookings || [];
952
999
  if (Array.isArray(giftProducts) && giftProducts.length > 0) {
@@ -967,12 +1014,18 @@ class OrderModule extends _BaseModule.BaseModule {
967
1014
  }
968
1015
  }
969
1016
  } catch (error) {
1017
+ if (options?.isCurrentOrder && !options.isCurrentOrder()) return null;
1018
+ if (options?.isCurrentOrder && !(0, _lodashEs.isEqual)({
1019
+ products: tempOrder.products,
1020
+ bookings: tempOrder.bookings
1021
+ }, giftMutationBaseline)) return null;
970
1022
  // 用户取消 / 选择失败 → 视为放弃本次新增
971
1023
  console.warn('[OrderModule] giftSelectResolver rejected, skip add gift', addAction.strategyId, error);
972
1024
  }
973
1025
  }
974
1026
  }
975
1027
  }
1028
+ if (options?.isCurrentOrder && !options.isCurrentOrder()) return null;
976
1029
  tempOrder.products = result.products;
977
1030
  this.lastUnfulfilledPromotions = result.unfulfilledPromotions;
978
1031
  this.lastGiftActions = result.giftActions;
@@ -1066,7 +1119,9 @@ class OrderModule extends _BaseModule.BaseModule {
1066
1119
  * sessionStorage 的 cacheId 分桶。写入失败由 BaseModule 静默降级。
1067
1120
  */
1068
1121
  persistTempOrder() {
1069
- if (!this.sessionStoragePersistEnabled || !this.store.tempOrder) return;
1122
+ if (!this.store.tempOrder) return;
1123
+ (0, _holderRelations.ensureHolderRelationUuids)(this.store.tempOrder);
1124
+ if (!this.sessionStoragePersistEnabled) return;
1070
1125
  this.checkSaveCache({
1071
1126
  cacheId: this.cacheId,
1072
1127
  fatherModule: this.fatherModule,
@@ -1775,7 +1830,10 @@ class OrderModule extends _BaseModule.BaseModule {
1775
1830
  source_product_price: this.normalizeFingerprintMoney(metadata.source_product_price),
1776
1831
  main_product_selling_price: this.normalizeFingerprintMoney(metadata.main_product_selling_price),
1777
1832
  main_product_original_price: this.normalizeFingerprintMoney(metadata.main_product_original_price),
1778
- product_discount_difference: this.normalizeFingerprintMoney(metadata.product_discount_difference)
1833
+ product_discount_difference: this.normalizeFingerprintMoney(metadata.product_discount_difference),
1834
+ ...(!(0, _holderRelations.hasProductBooking)(product) ? {
1835
+ holder_relations: this.normalizeFingerprintValue((0, _holderRelations.getProductHolderRelations)(this.store.tempOrder || {}, product))
1836
+ } : {})
1779
1837
  },
1780
1838
  product_sku: this.normalizeFingerprintValue({
1781
1839
  code: sku.code ?? '',
@@ -1811,7 +1869,7 @@ class OrderModule extends _BaseModule.BaseModule {
1811
1869
  items.sort((left, right) => JSON.stringify(left).localeCompare(JSON.stringify(right)));
1812
1870
  return JSON.stringify(items);
1813
1871
  }
1814
- buildProductStructuralFingerprintItem(product) {
1872
+ buildProductStructuralFingerprintItem(product, order = this.store.tempOrder || {}) {
1815
1873
  const sku = product?.product_sku || {};
1816
1874
  const metadata = product?.metadata || {};
1817
1875
  const discountTypes = (product?.discount_list || []).map(discount => discount?.type ?? discount?.tag);
@@ -1826,7 +1884,10 @@ class OrderModule extends _BaseModule.BaseModule {
1826
1884
  unique_identification_number: metadata.unique_identification_number ?? product?.unique_identification_number ?? '',
1827
1885
  price_override: metadata.price_override ?? '',
1828
1886
  is_manual_discount: metadata.is_manual_discount ?? '',
1829
- product_discount_reason: metadata.product_discount_reason ?? ''
1887
+ product_discount_reason: metadata.product_discount_reason ?? '',
1888
+ ...(!(0, _holderRelations.hasProductBooking)(product) ? {
1889
+ holder_relations: this.normalizeFingerprintValue((0, _holderRelations.getProductHolderRelations)(order, product))
1890
+ } : {})
1830
1891
  },
1831
1892
  product_sku: this.normalizeFingerprintValue({
1832
1893
  code: sku.code ?? '',
@@ -1864,8 +1925,8 @@ class OrderModule extends _BaseModule.BaseModule {
1864
1925
  })))
1865
1926
  };
1866
1927
  }
1867
- buildOrderProductsStructuralFingerprint(products) {
1868
- const items = (products || []).map(product => this.buildProductStructuralFingerprintItem(product));
1928
+ buildOrderProductsStructuralFingerprint(products, order = this.store.tempOrder || {}) {
1929
+ const items = (products || []).map(product => this.buildProductStructuralFingerprintItem(product, order));
1869
1930
  items.sort((left, right) => JSON.stringify(left).localeCompare(JSON.stringify(right)));
1870
1931
  return JSON.stringify(items);
1871
1932
  }
@@ -1932,7 +1993,7 @@ class OrderModule extends _BaseModule.BaseModule {
1932
1993
  const manualDepositOverride = this.getManualDepositOverride(tempOrder);
1933
1994
  if (currentFingerprint !== snapshot.productFingerprint) {
1934
1995
  const currentStructuralFingerprint = this.buildOrderProductsStructuralFingerprint(tempOrder.products);
1935
- const snapshotStructuralFingerprint = this.buildOrderProductsStructuralFingerprint(Array.isArray(snapshot.products) ? snapshot.products : []);
1996
+ const snapshotStructuralFingerprint = this.buildOrderProductsStructuralFingerprint(Array.isArray(snapshot.products) ? snapshot.products : [], snapshot);
1936
1997
  if (currentStructuralFingerprint !== snapshotStructuralFingerprint) return null;
1937
1998
  }
1938
1999
  let summary = {
@@ -2153,9 +2214,16 @@ class OrderModule extends _BaseModule.BaseModule {
2153
2214
  product.metadata = metadata;
2154
2215
  return String(uid);
2155
2216
  }
2156
- sanitizeOrderProductForTempOrder(product) {
2217
+ sanitizeOrderProductForTempOrder(product, deferHolderCleanup = false) {
2157
2218
  if (product.metadata && typeof product.metadata === 'object') {
2158
2219
  delete product.metadata.origin;
2220
+ if (!deferHolderCleanup && !(0, _holderRelations.hasProductBooking)(product)) {
2221
+ delete product.metadata.holder;
2222
+ delete product.metadata.holder_id;
2223
+ }
2224
+ }
2225
+ if (product._extend?.holder_relations !== undefined) {
2226
+ delete product._extend.holder_relations;
2159
2227
  }
2160
2228
  return product;
2161
2229
  }
@@ -2275,6 +2343,7 @@ class OrderModule extends _BaseModule.BaseModule {
2275
2343
  const bookingByBookingUid = new Map();
2276
2344
  bookings.forEach(booking => {
2277
2345
  if (!booking || typeof booking !== 'object') return;
2346
+ if ((0, _pickupTiming.isFulfillmentTimingBooking)(booking)) return;
2278
2347
  const productUid = booking.product_uid || booking.metadata?.product_uid;
2279
2348
  if (productUid !== undefined && productUid !== null && String(productUid) !== '') {
2280
2349
  bookingByProductUid.set(String(productUid), booking);
@@ -2325,6 +2394,7 @@ class OrderModule extends _BaseModule.BaseModule {
2325
2394
  let baseHolder = null;
2326
2395
  const seen = new Set();
2327
2396
  for (const booking of tempOrder.bookings || []) {
2397
+ if ((0, _pickupTiming.isFulfillmentTimingBooking)(booking)) continue;
2328
2398
  const holder = booking?.holder && typeof booking.holder === 'object' ? booking.holder : null;
2329
2399
  if (!baseHolder && holder) {
2330
2400
  baseHolder = holder;
@@ -2365,7 +2435,7 @@ class OrderModule extends _BaseModule.BaseModule {
2365
2435
  * this.syncTempOrderHolderFromBookings(tempOrder);
2366
2436
  */
2367
2437
  syncTempOrderHolderFromBookings(tempOrder) {
2368
- const bookings = tempOrder.bookings || [];
2438
+ const bookings = (tempOrder.bookings || []).filter(booking => !(0, _pickupTiming.isFulfillmentTimingBooking)(booking));
2369
2439
  const hasHolderSource = bookings.some(booking => {
2370
2440
  return booking?.holder !== undefined || booking?.metadata?.holder_id !== undefined;
2371
2441
  });
@@ -2390,6 +2460,7 @@ class OrderModule extends _BaseModule.BaseModule {
2390
2460
  const bookingUid = product?.booking_uid || product?.metadata?.booking_uid;
2391
2461
  if (!productUid && !bookingUid) return;
2392
2462
  tempOrder.bookings = (tempOrder.bookings || []).filter(booking => {
2463
+ if ((0, _pickupTiming.isFulfillmentTimingBooking)(booking)) return true;
2393
2464
  const currentBookingUid = this.getBookingUniqueIdentificationNumber(booking);
2394
2465
  if (bookingUid && currentBookingUid === String(bookingUid)) return false;
2395
2466
  if (productUid && String(booking?.product_uid || '') === String(productUid)) return false;
@@ -2431,6 +2502,7 @@ class OrderModule extends _BaseModule.BaseModule {
2431
2502
  return shouldUseManualDepositOverride ? nextSummary : this.applyLastOrderDepositSnapshot(tempOrder, nextSummary);
2432
2503
  }
2433
2504
  async recalculateSummary(options) {
2505
+ if (options?.isCurrentOrder && !options.isCurrentOrder()) return null;
2434
2506
  const tempOrder = options?.createIfMissing ? this.ensureTempOrder() : this.store.tempOrder;
2435
2507
  if (!tempOrder) return null;
2436
2508
  this.sanitizeTempOrderProducts(tempOrder);
@@ -2440,7 +2512,7 @@ class OrderModule extends _BaseModule.BaseModule {
2440
2512
  const snapshotSummary = shouldReuseOriginalSnapshot ? this.restoreOriginalSnapshotIfProductsUnchanged(tempOrder, originalSnapshot) : null;
2441
2513
  if (snapshotSummary) return snapshotSummary;
2442
2514
  const shouldFullRecalculateFromSnapshot = Boolean(originalSnapshot);
2443
- const summaryProducts = shouldFullRecalculateFromSnapshot ? (0, _lodashEs.cloneDeep)(tempOrder.products || []) : tempOrder.products;
2515
+ const summaryProducts = shouldFullRecalculateFromSnapshot || options?.isCurrentOrder ? (0, _lodashEs.cloneDeep)(tempOrder.products || []) : tempOrder.products;
2444
2516
  if (shouldFullRecalculateFromSnapshot) {
2445
2517
  this.clearPersistedAmountFieldsForFullRecalc(summaryProducts || []);
2446
2518
  }
@@ -2478,6 +2550,10 @@ class OrderModule extends _BaseModule.BaseModule {
2478
2550
  });
2479
2551
  return this.store.summary;
2480
2552
  }
2553
+ const summaryMutationBaseline = options?.isCurrentOrder ? (0, _lodashEs.cloneDeep)({
2554
+ tempOrder,
2555
+ summary: this.store.summary
2556
+ }) : null;
2481
2557
  const salesSummaryResult = await salesSummary.getSummary({
2482
2558
  products: summaryProducts,
2483
2559
  isPriceIncludeTax: tempOrder.is_price_include_tax,
@@ -2487,7 +2563,12 @@ class OrderModule extends _BaseModule.BaseModule {
2487
2563
  channel: this.otherParams.channel
2488
2564
  } : {})
2489
2565
  });
2490
- if (shouldFullRecalculateFromSnapshot) {
2566
+ if (options?.isCurrentOrder && !options.isCurrentOrder()) return null;
2567
+ if (options?.isCurrentOrder && !(0, _lodashEs.isEqual)({
2568
+ tempOrder,
2569
+ summary: this.store.summary
2570
+ }, summaryMutationBaseline)) return null;
2571
+ if (shouldFullRecalculateFromSnapshot || options?.isCurrentOrder) {
2491
2572
  tempOrder.products = summaryProducts;
2492
2573
  }
2493
2574
  this.syncSummaryProductMetadata(tempOrder.products || []);
@@ -2803,10 +2884,33 @@ class OrderModule extends _BaseModule.BaseModule {
2803
2884
  });
2804
2885
  }
2805
2886
  updateTempOrderServiceType(serviceType) {
2887
+ if (serviceType !== 'pick_up') {
2888
+ const tempOrder = this.ensureTempOrder();
2889
+ tempOrder.bookings = (tempOrder.bookings || []).filter(booking => !(0, _pickupTiming.isFulfillmentTimingBooking)(booking));
2890
+ }
2806
2891
  return this.updateTempOrderShopServiceData({
2807
2892
  service_type: serviceType
2808
2893
  });
2809
2894
  }
2895
+ getPickupTimingBooking() {
2896
+ return (this.store.tempOrder?.bookings || []).find(_pickupTiming.isFulfillmentTimingBooking) || null;
2897
+ }
2898
+ setPickupTimingBooking(input) {
2899
+ const tempOrder = this.ensureTempOrder();
2900
+ const bookings = tempOrder.bookings || [];
2901
+ const firstIndex = bookings.findIndex(_pickupTiming.isFulfillmentTimingBooking);
2902
+ const nextBooking = (0, _pickupTiming.buildPickupTimingBooking)(input, bookings[firstIndex]);
2903
+ // Keep the first row's identity and position; repair duplicate marked rows only.
2904
+ tempOrder.bookings = firstIndex === -1 ? [...bookings, nextBooking] : bookings.flatMap((booking, index) => index === firstIndex ? [nextBooking] : (0, _pickupTiming.isFulfillmentTimingBooking)(booking) ? [] : [booking]);
2905
+ this.notifyTempOrderChanged();
2906
+ return nextBooking;
2907
+ }
2908
+ clearPickupTimingBooking() {
2909
+ const tempOrder = this.store.tempOrder;
2910
+ if (!tempOrder?.bookings?.some(_pickupTiming.isFulfillmentTimingBooking)) return;
2911
+ tempOrder.bookings = tempOrder.bookings.filter(booking => !(0, _pickupTiming.isFulfillmentTimingBooking)(booking));
2912
+ this.notifyTempOrderChanged();
2913
+ }
2810
2914
  updateTempOrderContactsInfo(contactsInfo) {
2811
2915
  const tempOrder = this.ensureTempOrder();
2812
2916
  tempOrder.contacts_info = contactsInfo && typeof contactsInfo === 'object' && !Array.isArray(contactsInfo) ? contactsInfo : null;
@@ -2918,6 +3022,7 @@ class OrderModule extends _BaseModule.BaseModule {
2918
3022
  const nextCustomerId = tempOrder.customer_id ?? null;
2919
3023
  if (String(previousCustomerId ?? '') !== String(nextCustomerId ?? '')) {
2920
3024
  this.clearCustomerWalletState(tempOrder);
3025
+ (0, _holderRelations.removeProductHolderRelations)(tempOrder, tempOrder.products.filter(product => !(0, _holderRelations.hasProductBooking)(product)));
2921
3026
  }
2922
3027
  this.persistTempOrder();
2923
3028
  return {
@@ -3086,6 +3191,18 @@ class OrderModule extends _BaseModule.BaseModule {
3086
3191
  const matchMode = options.matchBy === 'compat' || options.matchBy === 'legacy' ? 'compat' : 'payment_number';
3087
3192
  const matchedPayment = (0, _paymentUtils.resolveOrderPaymentIdentity)(tempOrder.payments || [], identity, matchMode);
3088
3193
  if (!matchedPayment) {
3194
+ try {
3195
+ void Promise.resolve(this.logger?.addLog?.({
3196
+ type: 'warning',
3197
+ title: '[PaymentTrace] payment-missing',
3198
+ metadata: {
3199
+ moduleName: this.name,
3200
+ saleId: tempOrder.external_sale_number,
3201
+ paymentNumber: identity,
3202
+ currentPaymentNumbers: (tempOrder.payments || []).map(p => p.payment_number)
3203
+ }
3204
+ })).catch(() => {});
3205
+ } catch {/* 日志失败不影响原异常。 */}
3089
3206
  throw new Error(`未找到支付项: ${identity}`);
3090
3207
  }
3091
3208
  let updatedPayment = null;
@@ -3393,20 +3510,30 @@ class OrderModule extends _BaseModule.BaseModule {
3393
3510
  * await this.finalizeProductOrderMutation(tempOrder);
3394
3511
  */
3395
3512
  async finalizeProductOrderMutation(tempOrder, options) {
3513
+ const isCurrentOrder = options?.isCurrentOrder;
3514
+ if (isCurrentOrder && !isCurrentOrder()) return;
3396
3515
  this.syncTempOrderHolderFromBookings(tempOrder);
3397
3516
  this.prepareVirtualSettlementTempOrder(tempOrder);
3398
3517
  const isVirtualSettlement = this.isActiveVirtualSettlementOrder(tempOrder);
3399
3518
  if (!options?.skipEditDiscountConfigRefresh && !isVirtualSettlement) {
3400
3519
  await this.ensureEditDiscountConfigForProductChange(tempOrder);
3401
3520
  }
3402
- const promotionPayload = !options?.skipDiscountRecalculation && !isVirtualSettlement ? await this.evaluatePromotion() : null;
3521
+ if (isCurrentOrder && !isCurrentOrder()) return;
3522
+ const promotionPayload = !options?.skipDiscountRecalculation && !isVirtualSettlement ? await this.evaluatePromotion(isCurrentOrder ? {
3523
+ isCurrentOrder
3524
+ } : undefined) : null;
3525
+ if (isCurrentOrder && !isCurrentOrder()) return;
3403
3526
  if (!options?.skipDiscountRecalculation && !isVirtualSettlement) {
3404
3527
  this.applyDiscount();
3405
3528
  }
3406
3529
  this.sanitizeTempOrderProducts(tempOrder);
3407
- await this.recalculateSummary({
3408
- createIfMissing: true
3530
+ const summary = await this.recalculateSummary({
3531
+ createIfMissing: true,
3532
+ ...(isCurrentOrder ? {
3533
+ isCurrentOrder
3534
+ } : {})
3409
3535
  });
3536
+ if (isCurrentOrder && (!isCurrentOrder() || !summary)) return;
3410
3537
  this.persistTempOrder();
3411
3538
  this.emitPromotionApplied(promotionPayload);
3412
3539
  }
@@ -3429,7 +3556,6 @@ class OrderModule extends _BaseModule.BaseModule {
3429
3556
  booking
3430
3557
  }) : null;
3431
3558
  const productToAdd = linked?.product || product;
3432
- this.assertAndLockProductSettlement(tempOrder, productToAdd);
3433
3559
  const disableMerge = options?.disableMerge === true;
3434
3560
  const incomingFingerprint = disableMerge ? '' : (0, _utils3.buildProductLineFingerprint)((0, _utils.getProductSkuOptions)(productToAdd), productToAdd.product_bundle);
3435
3561
  const normalizedIncoming = this.normalizeVirtualSettlementProductIfEnabled((0, _utils.mergeOrderProductDisplayFields)(productToAdd, (0, _utils3.normalizeOrderProduct)(productToAdd)));
@@ -3460,10 +3586,13 @@ class OrderModule extends _BaseModule.BaseModule {
3460
3586
  const hasIncomingProductNote = this.hasOrderProductLineNote(normalizedIncoming);
3461
3587
  const hasIncomingManualProductDiscount = isManualProductDiscountProduct(normalizedIncoming);
3462
3588
  const hasIncomingBookingUid = this.hasLinkedBookingUid(productToAdd) || this.hasLinkedBookingUid(normalizedIncoming);
3589
+ const holderRelationsPatch = !hasIncomingBookingUid ? productToAdd._extend?.holder_relations : undefined;
3590
+ const hasIncomingHolder = Array.isArray(holderRelationsPatch) && holderRelationsPatch.length > 0;
3463
3591
  const isIncomingCustomItem = productToAdd.metadata?.origin?.isCustomItem === true || productToAdd._origin?.isCustomItem === true;
3464
- const shouldForceNewLine = disableMerge || !!booking || hasIncomingGoodPass || isIncomingPersistedEntitlementPass || hasIncomingBookingUid || isIncomingCustomItem || isWeighingProduct;
3592
+ const shouldForceNewLine = disableMerge || !!booking || hasIncomingGoodPass || isIncomingPersistedEntitlementPass || hasIncomingBookingUid || hasIncomingHolder || isIncomingCustomItem || isWeighingProduct;
3465
3593
  const matchedIndex = shouldForceNewLine ? -1 : tempOrder.products.findIndex(item => {
3466
3594
  if (this.hasGoodPassDiscount(item)) return false;
3595
+ if ((0, _holderRelations.getProductHolderRelations)(tempOrder, item).length > 0 || (0, _utils.hasAssignedHolderId)(item.metadata?.holder_id)) return false;
3467
3596
  const isRuntimeEntitlementPass = this.isRuntimeEntitlementAppend(item, tempOrder);
3468
3597
  if (isIncomingEntitlementAppend !== isRuntimeEntitlementPass) {
3469
3598
  return false;
@@ -3491,6 +3620,8 @@ class OrderModule extends _BaseModule.BaseModule {
3491
3620
  const matchedProduct = matchedIndex === -1 ? null : tempOrder.products[matchedIndex];
3492
3621
  const existedFingerprint = matchedProduct ? (0, _utils3.buildProductLineFingerprint)((0, _utils.getProductSkuOptions)(matchedProduct), matchedProduct.product_bundle) : '';
3493
3622
  const commitProductLine = shouldMerge => {
3623
+ options?.beforeCommit?.();
3624
+ this.assertAndLockProductSettlement(tempOrder, productToAdd);
3494
3625
  let committedProduct;
3495
3626
  if (matchedIndex === -1 || !matchedProduct || !shouldMerge) {
3496
3627
  this.captureProductRuntime(tempOrder, productToAdd, normalizedIncoming);
@@ -3552,6 +3683,7 @@ class OrderModule extends _BaseModule.BaseModule {
3552
3683
  if (linked) {
3553
3684
  tempOrder.bookings = [...(tempOrder.bookings || []), linked.booking];
3554
3685
  }
3686
+ (0, _holderRelations.replaceProductHolderRelations)(tempOrder, committedProduct, holderRelationsPatch);
3555
3687
  return committedProduct;
3556
3688
  };
3557
3689
  if (!matchedProduct) {
@@ -3642,7 +3774,16 @@ class OrderModule extends _BaseModule.BaseModule {
3642
3774
  * // → tempOrder.products 新增 3 行,每行 num=1,bookings 3 条
3643
3775
  */
3644
3776
  async addProductToOrder(product, booking, options) {
3645
- const tempOrder = this.ensureTempOrder();
3777
+ const availabilityCommitGuard = options?.availabilityCommitGuard;
3778
+ availabilityCommitGuard?.();
3779
+ const liveTempOrder = this.ensureTempOrder();
3780
+ const beforeCommit = this.createCartIntentCommitGuard('add_product', liveTempOrder);
3781
+ // 自动预约在草稿上准备完整批次,避免拆票期间暴露半成品,或在失败时
3782
+ // 用整单快照回滚而抹掉其他入口的并发修改。人工路径保持原有写入行为。
3783
+ const tempOrder = availabilityCommitGuard ? (0, _lodashEs.cloneDeep)(liveTempOrder) : liveTempOrder;
3784
+ const guardedMutationFields = ['products', 'bookings', 'notes', '_extend', 'metadata', 'currency_code', 'currency_symbol', 'currency_format'];
3785
+ const readGuardedMutationFields = order => Object.fromEntries(guardedMutationFields.map(key => [key, order[key]]));
3786
+ const guardedMutationBaseline = availabilityCommitGuard ? (0, _lodashEs.cloneDeep)(readGuardedMutationFields(liveTempOrder)) : null;
3646
3787
  const salesNoteDrafts = (options?.salesNotes || []).map(draft => ({
3647
3788
  note_type: String(draft.note_type || '').trim(),
3648
3789
  text: {
@@ -3652,6 +3793,7 @@ class OrderModule extends _BaseModule.BaseModule {
3652
3793
  }));
3653
3794
  const mutationSnapshot = salesNoteDrafts.length > 0 ? {
3654
3795
  products: (0, _lodashEs.cloneDeep)(tempOrder.products || []),
3796
+ holder_relations: (0, _lodashEs.cloneDeep)(tempOrder.holder_relations),
3655
3797
  bookings: (0, _lodashEs.cloneDeep)(tempOrder.bookings || []),
3656
3798
  notes: (0, _lodashEs.cloneDeep)(tempOrder.notes || []),
3657
3799
  extend: (0, _lodashEs.cloneDeep)(tempOrder._extend || {})
@@ -3676,6 +3818,7 @@ class OrderModule extends _BaseModule.BaseModule {
3676
3818
  const rollbackProductAndNotesMutation = () => {
3677
3819
  if (!mutationSnapshot) return;
3678
3820
  tempOrder.products = mutationSnapshot.products;
3821
+ if (mutationSnapshot.holder_relations === undefined) delete tempOrder.holder_relations;else tempOrder.holder_relations = mutationSnapshot.holder_relations;
3679
3822
  tempOrder.bookings = mutationSnapshot.bookings;
3680
3823
  tempOrder.notes = mutationSnapshot.notes;
3681
3824
  tempOrder._extend = mutationSnapshot.extend;
@@ -3685,7 +3828,7 @@ class OrderModule extends _BaseModule.BaseModule {
3685
3828
  const uid = line.metadata?.unique_identification_number || line.unique_identification_number;
3686
3829
  return uid !== undefined && uid !== null && String(uid) === String(insertAfterProductUid);
3687
3830
  }) : -1;
3688
- let insertAtIndex = anchorIndex >= 0 ? anchorIndex + 1 : undefined;
3831
+ let insertAtIndex = options?.insertAtStart ? 0 : anchorIndex >= 0 ? anchorIndex + 1 : undefined;
3689
3832
  try {
3690
3833
  if (booking) {
3691
3834
  const productRecord = product;
@@ -3713,35 +3856,76 @@ class OrderModule extends _BaseModule.BaseModule {
3713
3856
  }
3714
3857
  const committedProduct = await this.appendProductLineToTempOrder(tempOrder, splitLine.product, splitLine.booking, {
3715
3858
  insertAtIndex,
3716
- disableMerge: options?.disableMerge
3859
+ disableMerge: options?.disableMerge,
3860
+ beforeCommit
3717
3861
  });
3862
+ availabilityCommitGuard?.();
3718
3863
  attachSalesNoteDrafts(committedProduct);
3719
3864
  if (insertAtIndex !== undefined) insertAtIndex += 1;
3720
3865
  }
3721
3866
  } else {
3722
3867
  const committedProduct = await this.appendProductLineToTempOrder(tempOrder, product, (0, _lodashEs.cloneDeep)(booking), {
3723
3868
  insertAtIndex,
3724
- disableMerge: options?.disableMerge
3869
+ disableMerge: options?.disableMerge,
3870
+ beforeCommit
3725
3871
  });
3872
+ availabilityCommitGuard?.();
3726
3873
  attachSalesNoteDrafts(committedProduct);
3727
3874
  }
3728
3875
  } else {
3729
3876
  const committedProduct = await this.appendProductLineToTempOrder(tempOrder, product, undefined, {
3730
3877
  insertAtIndex,
3731
- disableMerge: options?.disableMerge
3878
+ disableMerge: options?.disableMerge,
3879
+ beforeCommit
3732
3880
  });
3881
+ availabilityCommitGuard?.();
3733
3882
  attachSalesNoteDrafts(committedProduct);
3734
3883
  }
3884
+ if (availabilityCommitGuard) {
3885
+ availabilityCommitGuard();
3886
+ beforeCommit();
3887
+ // 调用方的结构 revision 可能不包括备注、价格或结算元数据。
3888
+ // 本次会覆盖的字段也要保持原值,不能让草稿覆盖并发编辑。
3889
+ if (!(0, _lodashEs.isEqual)(readGuardedMutationFields(liveTempOrder), guardedMutationBaseline)) {
3890
+ throw Object.assign(new Error('预约加购期间订单已变化,请重新选择'), {
3891
+ code: 'AVAILABILITY_SELECTION_STALE'
3892
+ });
3893
+ }
3894
+ // 从最后一次门禁到真正新增之间不再 await。只发布本次新增会改写的
3895
+ // 字段;其余订单字段继续使用 live 对象,外部持有的订单引用也不变。
3896
+ Object.assign(liveTempOrder, readGuardedMutationFields(tempOrder));
3897
+ }
3735
3898
  } catch (error) {
3736
3899
  rollbackProductAndNotesMutation();
3737
3900
  throw error;
3738
3901
  }
3739
- await this.finalizeProductOrderMutation(tempOrder);
3902
+ // 发布后预约已加购;后续促销/summary 沿用既有收尾,不用自身新增后的
3903
+ // revision 再执行提交 CAS,也不会把已加购预约因收尾耗时而撤回。
3904
+ if (availabilityCommitGuard) {
3905
+ const committedIdentity = [liveTempOrder.order_id, liveTempOrder.external_sale_number];
3906
+ const isCurrentOrder = () => this.store.tempOrder === liveTempOrder && liveTempOrder.order_id === committedIdentity[0] && liveTempOrder.external_sale_number === committedIdentity[1] && (options?.availabilityCommitTargetGuard?.() ?? true);
3907
+ try {
3908
+ // 新增已发布,即使后续报价/促销收尾失败,也不能向调用方报告未加购。
3909
+ // 先保存已提交行;异步结果只允许写回仍属于本次提交的订单。
3910
+ if (isCurrentOrder()) this.persistTempOrder();
3911
+ await this.finalizeProductOrderMutation(liveTempOrder, {
3912
+ isCurrentOrder
3913
+ });
3914
+ } catch (error) {
3915
+ this.logWarning('addProductToOrder committed; finalization failed', {
3916
+ product_id: product.product_id,
3917
+ committed: true,
3918
+ error: error instanceof Error ? error.message : String(error)
3919
+ });
3920
+ }
3921
+ } else {
3922
+ await this.finalizeProductOrderMutation(liveTempOrder);
3923
+ }
3740
3924
  this.logInfo('addProductToOrder success', {
3741
3925
  product_id: product.product_id,
3742
3926
  with_booking: !!booking
3743
3927
  });
3744
- return tempOrder.products;
3928
+ return liveTempOrder.products;
3745
3929
  }
3746
3930
 
3747
3931
  /**
@@ -3756,6 +3940,7 @@ class OrderModule extends _BaseModule.BaseModule {
3756
3940
  async addProductsToOrder(items, options) {
3757
3941
  const tempOrder = this.ensureTempOrder();
3758
3942
  if (!Array.isArray(items) || items.length === 0) return tempOrder.products;
3943
+ const beforeCommit = this.createCartIntentCommitGuard('add_product', tempOrder);
3759
3944
  for (const item of items || []) {
3760
3945
  const {
3761
3946
  product,
@@ -3768,13 +3953,17 @@ class OrderModule extends _BaseModule.BaseModule {
3768
3953
  if (splitCount > 1) {
3769
3954
  for (let i = 0; i < splitCount; i += 1) {
3770
3955
  const splitLine = this.createSplitBookingLine(productRecord, booking, i, splitCount);
3771
- const appendResult = this.appendProductLineToTempOrder(tempOrder, splitLine.product, splitLine.booking);
3956
+ const appendResult = this.appendProductLineToTempOrder(tempOrder, splitLine.product, splitLine.booking, {
3957
+ beforeCommit
3958
+ });
3772
3959
  if (appendResult) await appendResult;
3773
3960
  }
3774
3961
  continue;
3775
3962
  }
3776
3963
  }
3777
- const appendResult = this.appendProductLineToTempOrder(tempOrder, product, booking);
3964
+ const appendResult = this.appendProductLineToTempOrder(tempOrder, product, booking, {
3965
+ beforeCommit
3966
+ });
3778
3967
  if (appendResult) await appendResult;
3779
3968
  }
3780
3969
  await this.finalizeProductOrderMutation(tempOrder, options);
@@ -3859,6 +4048,16 @@ class OrderModule extends _BaseModule.BaseModule {
3859
4048
  };
3860
4049
  });
3861
4050
  }
4051
+ isRestrictedCartProductUpdate(tempOrder, params) {
4052
+ if (!this.orderHooks?.beforeCartIntentCommit) return false;
4053
+ if (params.booking || Object.keys(params.updates || {}).some(key => key !== 'num')) {
4054
+ return true;
4055
+ }
4056
+ if (params.updates?.num === undefined) return false;
4057
+ const uid = params.unique_identification_number;
4058
+ const product = uid !== undefined ? tempOrder.products.find(item => String(item.metadata?.unique_identification_number || item.unique_identification_number || '') === String(uid)) : tempOrder.products[(0, _utils3.getProductIdentityIndex)(tempOrder.products, params)];
4059
+ return !product || (0, _utils3.getSafeProductNum)(params.updates.num) > product.num;
4060
+ }
3862
4061
  applyOrderProductUpdateToTempOrder(tempOrder, params, reapplyWeighedDiscountProductUids) {
3863
4062
  const {
3864
4063
  product_id,
@@ -4104,12 +4303,16 @@ class OrderModule extends _BaseModule.BaseModule {
4104
4303
  if (uid) reapplyWeighedDiscountProductUids?.push(String(uid));
4105
4304
  }
4106
4305
  tempOrder.products[productIndex] = normalizedProduct;
4306
+ (0, _holderRelations.replaceProductHolderRelations)(tempOrder, normalizedProduct, updates._extend?.holder_relations);
4107
4307
  this.syncTempOrderHolderFromBookings(tempOrder);
4108
4308
  this.captureProductRuntime(tempOrder, updates, tempOrder.products[productIndex], tempOrder.products[productIndex].metadata?.unique_identification_number);
4109
4309
  return tempOrder.products[productIndex].metadata?.unique_identification_number;
4110
4310
  }
4111
4311
  async updateOrderProduct(params) {
4112
4312
  const tempOrder = this.ensureTempOrder();
4313
+ if (this.isRestrictedCartProductUpdate(tempOrder, params)) {
4314
+ this.createCartIntentCommitGuard('update_product', tempOrder)();
4315
+ }
4113
4316
  const reapplyWeighedDiscountProductUids = [];
4114
4317
  const updatedProductUid = this.applyOrderProductUpdateToTempOrder(tempOrder, params, reapplyWeighedDiscountProductUids);
4115
4318
  const promotionPayload = await this.evaluatePromotion();
@@ -4133,6 +4336,9 @@ class OrderModule extends _BaseModule.BaseModule {
4133
4336
  if (!paramsList.length) {
4134
4337
  return tempOrder.products;
4135
4338
  }
4339
+ if (paramsList.some(params => this.isRestrictedCartProductUpdate(tempOrder, params))) {
4340
+ this.createCartIntentCommitGuard('update_product', tempOrder)();
4341
+ }
4136
4342
  const reapplyWeighedDiscountProductUids = [];
4137
4343
  const reapplyBookingDiscountProductUids = paramsList.reduce((acc, params) => {
4138
4344
  const updatedProductUid = this.applyOrderProductUpdateToTempOrder(tempOrder, params, reapplyWeighedDiscountProductUids);
@@ -4151,7 +4357,9 @@ class OrderModule extends _BaseModule.BaseModule {
4151
4357
  });
4152
4358
  return tempOrder.products;
4153
4359
  }
4154
- async updateOrderProductQuantity(params) {
4360
+ async updateOrderProductQuantity(params, options) {
4361
+ const isCurrentOrder = options?.isCurrentOrder;
4362
+ if (isCurrentOrder && !isCurrentOrder()) return this.store.tempOrder?.products || [];
4155
4363
  const {
4156
4364
  product_id,
4157
4365
  product_variant_id,
@@ -4217,19 +4425,30 @@ class OrderModule extends _BaseModule.BaseModule {
4217
4425
  throw new _virtualSettlement.VirtualCurrencyBalanceError(balance.toFixed(2), requiredAmount.toFixed(2));
4218
4426
  }
4219
4427
  }
4428
+ if (normalizedProduct.num > targetProduct.num) {
4429
+ this.createCartIntentCommitGuard('increase_quantity', tempOrder)();
4430
+ }
4431
+ if (isCurrentOrder && !isCurrentOrder()) return tempOrder.products;
4220
4432
  tempOrder.products[productIndex] = normalizedProduct;
4221
- this.prepareVirtualSettlementTempOrder(tempOrder);
4222
- const isVirtualSettlement = this.isActiveVirtualSettlementOrder(tempOrder);
4223
- const promotionPayload = !isVirtualSettlement ? await this.evaluatePromotion() : null;
4224
- if (!isVirtualSettlement) {
4225
- this.applyDiscount();
4433
+ if (isCurrentOrder) {
4434
+ await this.finalizeProductOrderMutation(tempOrder, {
4435
+ skipEditDiscountConfigRefresh: true,
4436
+ isCurrentOrder
4437
+ });
4438
+ } else {
4439
+ this.prepareVirtualSettlementTempOrder(tempOrder);
4440
+ const isVirtualSettlement = this.isActiveVirtualSettlementOrder(tempOrder);
4441
+ const promotionPayload = !isVirtualSettlement ? await this.evaluatePromotion() : null;
4442
+ if (!isVirtualSettlement) {
4443
+ this.applyDiscount();
4444
+ }
4445
+ this.sanitizeTempOrderProducts(tempOrder);
4446
+ await this.recalculateSummary({
4447
+ createIfMissing: true
4448
+ });
4449
+ this.persistTempOrder();
4450
+ this.emitPromotionApplied(promotionPayload);
4226
4451
  }
4227
- this.sanitizeTempOrderProducts(tempOrder);
4228
- await this.recalculateSummary({
4229
- createIfMissing: true
4230
- });
4231
- this.persistTempOrder();
4232
- this.emitPromotionApplied(promotionPayload);
4233
4452
  this.logInfo('updateOrderProductQuantity success', {
4234
4453
  params
4235
4454
  });
@@ -4294,6 +4513,8 @@ class OrderModule extends _BaseModule.BaseModule {
4294
4513
  * await order.removeProductsFromOrder([identityA, identityB]);
4295
4514
  */
4296
4515
  async removeProductsFromOrder(identities, options) {
4516
+ const isCurrentOrder = options?.isCurrentOrder;
4517
+ if (isCurrentOrder && !isCurrentOrder()) return this.store.tempOrder?.products || [];
4297
4518
  const tempOrder = this.ensureTempOrder();
4298
4519
  if (!identities.length) {
4299
4520
  return tempOrder.products;
@@ -4360,8 +4581,16 @@ class OrderModule extends _BaseModule.BaseModule {
4360
4581
  tempOrder.products = nextProductIndex >= 0 ? [...productsWithoutRemaining.slice(0, nextProductIndex), remainingProduct, ...productsWithoutRemaining.slice(nextProductIndex)] : [...productsWithoutRemaining, remainingProduct];
4361
4582
  }
4362
4583
  }
4584
+ (0, _holderRelations.removeProductHolderRelations)(tempOrder, productsBeforeRemove.filter(product => !tempOrder.products.includes(product)));
4363
4585
  this.removeSalesNotesForMissingProductLines(tempOrder);
4364
- await this.finalizeAfterProductsMutation(tempOrder);
4586
+ if (isCurrentOrder) {
4587
+ await this.finalizeProductOrderMutation(tempOrder, {
4588
+ skipEditDiscountConfigRefresh: true,
4589
+ isCurrentOrder
4590
+ });
4591
+ } else {
4592
+ await this.finalizeAfterProductsMutation(tempOrder);
4593
+ }
4365
4594
  this.logInfo('removeProductsFromOrder success', {
4366
4595
  identities
4367
4596
  });
@@ -4378,6 +4607,7 @@ class OrderModule extends _BaseModule.BaseModule {
4378
4607
  async clearOrderCartLines() {
4379
4608
  const tempOrder = this.ensureTempOrder();
4380
4609
  this.ensureSalesNotesLoadedForMutation(tempOrder);
4610
+ (0, _holderRelations.removeProductHolderRelations)(tempOrder, tempOrder.products);
4381
4611
  tempOrder.products = [];
4382
4612
  tempOrder.bookings = [];
4383
4613
  (0, _virtualSettlement.releaseTempOrderSettlementState)(tempOrder);
@@ -4407,6 +4637,7 @@ class OrderModule extends _BaseModule.BaseModule {
4407
4637
  async clearOrderForSettlementSwitch() {
4408
4638
  const tempOrder = this.ensureTempOrder();
4409
4639
  this.ensureSalesNotesLoadedForMutation(tempOrder);
4640
+ (0, _holderRelations.removeProductHolderRelations)(tempOrder, tempOrder.products);
4410
4641
  tempOrder.products = [];
4411
4642
  tempOrder.bookings = [];
4412
4643
  this.removeSalesNotesForMissingProductLines(tempOrder);
@@ -4525,6 +4756,7 @@ class OrderModule extends _BaseModule.BaseModule {
4525
4756
  }
4526
4757
  async runSubmitTempOrder(params) {
4527
4758
  const tempOrder = this.ensureTempOrder();
4759
+ if ((0, _localEftposState.requiresLocalPaymentRefresh)(tempOrder)) throw new Error(_localEftposState.PAYMENT_REFRESH_MESSAGE);
4528
4760
  this.prepareVirtualSettlementTempOrder(tempOrder);
4529
4761
  const isOnlineStorePlatform = this.isOnlineStorePlatform();
4530
4762
  const shouldConfirmPendingVoucherPayments = params?.confirmPendingVoucherPayments !== false;
@@ -4999,7 +5231,7 @@ class OrderModule extends _BaseModule.BaseModule {
4999
5231
  query
5000
5232
  } = params;
5001
5233
  const fetchUrl = url || '/order/sales/checkout';
5002
- const requestQuery = this.isSalesCheckoutHandledByOsServer(fetchUrl) ? query : (0, _osPrivatePayload.stripOsPrivatePayloadFields)(query);
5234
+ const requestQuery = this.isSalesCheckoutHandledByOsServer(fetchUrl) ? query : (0, _localEftposState.stripLocalEftposState)((0, _osPrivatePayload.stripOsPrivatePayloadFields)(query));
5003
5235
  return this.request.post(fetchUrl, requestQuery, {
5004
5236
  osServer: true,
5005
5237
  customToast: () => {}
@@ -5044,13 +5276,10 @@ class OrderModule extends _BaseModule.BaseModule {
5044
5276
  if (lookup.startsWith('LOCAL_')) {
5045
5277
  params.forceRemote = false;
5046
5278
  }
5047
- const res = await this.request.get(`/order/sales/${encodeURIComponent(lookup)}`, {
5048
- with: ['products', 'bookings', 'payments', 'customer', 'summary', 'contacts_info', 'order_meta_list', 'refunds', 'notes'],
5049
- ...(params.forceRemote ? {
5050
- forceRemote: true
5051
- } : {})
5052
- }, {
5053
- osServer: true
5279
+ const res = await (0, _loadSalesOrderSnapshot.loadSalesOrderSnapshot)(this.request, {
5280
+ lookup,
5281
+ transport: 'os-server',
5282
+ forceRemote: params.forceRemote
5054
5283
  });
5055
5284
  if (res.code === 200) {
5056
5285
  this.store.lastOrderInfo = res.data;
@@ -5152,6 +5381,7 @@ class OrderModule extends _BaseModule.BaseModule {
5152
5381
  originalSalesSnapshot: {
5153
5382
  summary: (0, _lodashEs.cloneDeep)(this.store.summary),
5154
5383
  products: (0, _lodashEs.cloneDeep)(nextTempOrder.products || []),
5384
+ holder_relations: (0, _lodashEs.cloneDeep)(nextTempOrder.holder_relations),
5155
5385
  bookings: (0, _lodashEs.cloneDeep)(nextTempOrder.bookings || []),
5156
5386
  payments: (0, _lodashEs.cloneDeep)(nextTempOrder.payments || []),
5157
5387
  surcharges: (0, _lodashEs.cloneDeep)(nextTempOrder.surcharges || []),
@@ -5304,6 +5534,9 @@ class OrderModule extends _BaseModule.BaseModule {
5304
5534
  nextTempOrder.metadata = raw.metadata && typeof raw.metadata === 'object' ? {
5305
5535
  ...raw.metadata
5306
5536
  } : {};
5537
+ const holderRelations = (0, _holderRelations.restoreLegacyHolderRelations)(raw);
5538
+ if (holderRelations === undefined) delete nextTempOrder.holder_relations;else nextTempOrder.holder_relations = (0, _lodashEs.cloneDeep)(holderRelations);
5539
+ (0, _holderRelations.ensureHolderRelationUuids)(nextTempOrder);
5307
5540
  nextTempOrder.holder = raw.holder && typeof raw.holder === 'object' ? {
5308
5541
  ...raw.holder
5309
5542
  } : null;
@@ -5381,6 +5614,7 @@ class OrderModule extends _BaseModule.BaseModule {
5381
5614
  }
5382
5615
  tempOrderExtend.productsByUid = productsByUid;
5383
5616
  this.hydrateLinkedBookingIdentity(nextTempOrder);
5617
+ nextTempOrder.products.forEach(product => this.sanitizeOrderProductForTempOrder(product));
5384
5618
  if (options?.ensureIdentity !== false) {
5385
5619
  this.ensureExternalSaleNumber(nextTempOrder);
5386
5620
  }
@@ -5541,7 +5775,7 @@ class OrderModule extends _BaseModule.BaseModule {
5541
5775
  };
5542
5776
  if (result.metadata) delete result.metadata.price_schema_version;
5543
5777
  const restored = (0, _bundleDiscountHydration.restoreHydratedBundleDiscounts)(result, _utils.normalizeOrderProductDiscountList);
5544
- return this.sanitizeOrderProductForTempOrder(this.normalizeVirtualSettlementProductIfEnabled(restored));
5778
+ return this.sanitizeOrderProductForTempOrder(this.normalizeVirtualSettlementProductIfEnabled(restored), true);
5545
5779
  }
5546
5780
  getLastOrderInfo() {
5547
5781
  return this.store?.lastOrderInfo;