@pisell/pisellos 0.0.567 → 0.0.569

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 (557) hide show
  1. package/dist/core/index.js +6 -9
  2. package/dist/model/strategy/adapter/promotion/evaluator.js +3 -2
  3. package/dist/model/strategy/adapter/walletPass/evaluator.js +0 -2
  4. package/dist/model/strategy/adapter/walletPass/example.js +3 -26
  5. package/dist/model/strategy/adapter/walletPass/utils.js +7 -7
  6. package/dist/model/strategy/strategy-example.js +5 -19
  7. package/dist/modules/BaseModule.d.ts +2 -2
  8. package/dist/modules/BaseModule.js +4 -4
  9. package/dist/modules/BookingContext/index.d.ts +48 -0
  10. package/dist/modules/BookingContext/index.js +566 -0
  11. package/dist/modules/BookingContext/types.d.ts +102 -0
  12. package/dist/modules/BookingContext/types.js +51 -0
  13. package/dist/modules/BookingContext/utils/buildCacheItemFromOrderLine.d.ts +16 -0
  14. package/dist/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +302 -0
  15. package/dist/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.d.ts +8 -0
  16. package/dist/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +149 -0
  17. package/dist/modules/BookingContext/utils/cacheItemToBookingInput.d.ts +76 -0
  18. package/dist/modules/BookingContext/utils/cacheItemToBookingInput.js +249 -0
  19. package/dist/modules/BookingContext/utils/flexible.d.ts +28 -0
  20. package/dist/modules/BookingContext/utils/flexible.js +56 -0
  21. package/dist/modules/BookingContext/utils/formatResourceList.d.ts +8 -0
  22. package/dist/modules/BookingContext/utils/formatResourceList.js +38 -0
  23. package/dist/modules/BookingContext/utils/index.d.ts +11 -0
  24. package/dist/modules/BookingContext/utils/index.js +11 -0
  25. package/dist/modules/BookingContext/utils/noResource.d.ts +13 -0
  26. package/dist/modules/BookingContext/utils/noResource.js +77 -0
  27. package/dist/modules/BookingContext/utils/orderLineDisplay.d.ts +1 -0
  28. package/dist/modules/BookingContext/utils/orderLineDisplay.js +36 -0
  29. package/dist/modules/BookingContext/utils/productExtend.d.ts +72 -0
  30. package/dist/modules/BookingContext/utils/productExtend.js +339 -0
  31. package/dist/modules/BookingContext/utils/resourceErrors.d.ts +41 -0
  32. package/dist/modules/BookingContext/utils/resourceErrors.js +74 -0
  33. package/dist/modules/BookingContext/utils/resourceSelection.d.ts +7 -0
  34. package/dist/modules/BookingContext/utils/resourceSelection.js +24 -0
  35. package/dist/modules/BookingContext/utils/resources.d.ts +80 -0
  36. package/dist/modules/BookingContext/utils/resources.js +486 -0
  37. package/dist/modules/BookingContext/utils/serviceTimes.d.ts +55 -0
  38. package/dist/modules/BookingContext/utils/serviceTimes.js +151 -0
  39. package/dist/modules/BookingContext/utils/timeSlices.d.ts +42 -0
  40. package/dist/modules/BookingContext/utils/timeSlices.js +100 -0
  41. package/dist/modules/Cart/index.js +0 -3
  42. package/dist/modules/Cart/utils/cartProduct.js +26 -6
  43. package/dist/modules/Cart/utils/changePrice.js +27 -20
  44. package/dist/modules/Customer/index.d.ts +20 -0
  45. package/dist/modules/Customer/index.js +172 -83
  46. package/dist/modules/Customer/types.d.ts +2 -0
  47. package/dist/modules/Date/index.js +1 -5
  48. package/dist/modules/Discount/index.d.ts +2 -1
  49. package/dist/modules/Discount/index.js +37 -12
  50. package/dist/modules/Discount/types.d.ts +1 -0
  51. package/dist/modules/OpenData/index.d.ts +8 -0
  52. package/dist/modules/OpenData/index.js +35 -17
  53. package/dist/modules/Order/index.d.ts +449 -24
  54. package/dist/modules/Order/index.js +5437 -608
  55. package/dist/modules/Order/types.d.ts +693 -88
  56. package/dist/modules/Order/types.js +68 -1
  57. package/dist/modules/Order/utils/bundleDiscountHydration.d.ts +5 -0
  58. package/dist/modules/Order/utils/bundleDiscountHydration.js +144 -0
  59. package/dist/modules/Order/utils/manualProductDiscount.d.ts +106 -0
  60. package/dist/modules/Order/utils/manualProductDiscount.js +210 -0
  61. package/dist/modules/Order/utils/orderCollectionIdentity.d.ts +53 -0
  62. package/dist/modules/Order/utils/orderCollectionIdentity.js +405 -0
  63. package/dist/modules/Order/utils.d.ts +167 -18
  64. package/dist/modules/Order/utils.js +960 -165
  65. package/dist/modules/Payment/index.d.ts +10 -9
  66. package/dist/modules/Payment/index.js +508 -874
  67. package/dist/modules/Payment/types.d.ts +40 -16
  68. package/dist/modules/Payment/utils.d.ts +1 -1
  69. package/dist/modules/Payment/utils.js +4 -3
  70. package/dist/modules/Payment/walletpass.js +48 -34
  71. package/dist/modules/Product/types.d.ts +29 -7
  72. package/dist/modules/ProductList/index.d.ts +17 -13
  73. package/dist/modules/ProductList/index.js +184 -74
  74. package/dist/modules/ProductList/types.d.ts +17 -0
  75. package/dist/modules/Quotation/index.d.ts +22 -2
  76. package/dist/modules/Quotation/index.js +108 -42
  77. package/dist/modules/Quotation/types.d.ts +19 -0
  78. package/dist/modules/ResourcePlanner/index.d.ts +24 -0
  79. package/dist/modules/ResourcePlanner/index.js +181 -0
  80. package/dist/modules/ResourcePlanner/planner.d.ts +14 -0
  81. package/dist/modules/ResourcePlanner/planner.js +1069 -0
  82. package/dist/modules/ResourcePlanner/types.d.ts +227 -0
  83. package/dist/modules/ResourcePlanner/types.js +1 -0
  84. package/dist/modules/Rules/index.d.ts +8 -10
  85. package/dist/modules/Rules/index.js +219 -114
  86. package/dist/modules/Rules/types.d.ts +14 -3
  87. package/dist/modules/SalesSummary/index.d.ts +23 -26
  88. package/dist/modules/SalesSummary/index.js +152 -46
  89. package/dist/modules/SalesSummary/types.d.ts +9 -1
  90. package/dist/modules/SalesSummary/utils.d.ts +3 -22
  91. package/dist/modules/SalesSummary/utils.js +803 -163
  92. package/dist/modules/Schedule/getDateIsInSchedule.js +14 -7
  93. package/dist/modules/Schedule/index.d.ts +18 -5
  94. package/dist/modules/Schedule/index.js +78 -24
  95. package/dist/modules/Schedule/types.d.ts +2 -0
  96. package/dist/modules/Schedule/utils.js +0 -1
  97. package/dist/modules/Step/index.js +0 -1
  98. package/dist/modules/Summary/index.js +4 -3
  99. package/dist/modules/SurchargeList/index.d.ts +18 -0
  100. package/dist/modules/SurchargeList/index.js +185 -0
  101. package/dist/modules/SurchargeList/types.d.ts +15 -0
  102. package/dist/modules/SurchargeList/types.js +1 -0
  103. package/dist/modules/index.d.ts +3 -0
  104. package/dist/modules/index.js +4 -1
  105. package/dist/plugins/app.d.ts +2 -0
  106. package/dist/plugins/request.d.ts +1 -0
  107. package/dist/plugins/request.js +1 -4
  108. package/dist/plugins/window.js +2 -6
  109. package/dist/server/index.d.ts +462 -8
  110. package/dist/server/index.js +6137 -714
  111. package/dist/server/modules/floor-plan/index.d.ts +39 -0
  112. package/dist/server/modules/floor-plan/index.js +595 -0
  113. package/dist/server/modules/floor-plan/types.d.ts +43 -0
  114. package/dist/server/modules/floor-plan/types.js +13 -0
  115. package/dist/server/modules/index.d.ts +7 -2
  116. package/dist/server/modules/index.js +6 -0
  117. package/dist/server/modules/menu/index.js +32 -41
  118. package/dist/server/modules/order/index.d.ts +207 -14
  119. package/dist/server/modules/order/index.js +2326 -379
  120. package/dist/server/modules/order/types.d.ts +76 -5
  121. package/dist/server/modules/order/types.js +14 -1
  122. package/dist/server/modules/order/utils/filterBookings.d.ts +7 -1
  123. package/dist/server/modules/order/utils/filterBookings.js +118 -11
  124. package/dist/server/modules/order/utils/filterOrders.js +242 -14
  125. package/dist/server/modules/payment/index.d.ts +28 -0
  126. package/dist/server/modules/payment/index.js +305 -0
  127. package/dist/server/modules/payment/types.d.ts +9 -0
  128. package/dist/server/modules/payment/types.js +1 -0
  129. package/dist/server/modules/products/index.d.ts +105 -14
  130. package/dist/server/modules/products/index.js +1397 -540
  131. package/dist/server/modules/products/types.d.ts +34 -1
  132. package/dist/server/modules/products/types.js +4 -0
  133. package/dist/server/modules/quotation/index.d.ts +39 -0
  134. package/dist/server/modules/quotation/index.js +289 -86
  135. package/dist/server/modules/resource/index.d.ts +2 -3
  136. package/dist/server/modules/resource/index.js +105 -84
  137. package/dist/server/modules/schedule/index.d.ts +6 -4
  138. package/dist/server/modules/schedule/index.js +137 -114
  139. package/dist/server/test.json +713 -0
  140. package/dist/server/types.d.ts +2 -0
  141. package/dist/server/utils/product.js +2 -3
  142. package/dist/server/utils/small-ticket.d.ts +71 -0
  143. package/dist/server/utils/small-ticket.js +963 -0
  144. package/dist/solution/BaseSales/index.d.ts +378 -0
  145. package/dist/solution/BaseSales/index.js +4006 -0
  146. package/dist/solution/BaseSales/types.d.ts +394 -0
  147. package/dist/solution/BaseSales/types.js +40 -0
  148. package/dist/solution/BaseSales/utils/cartPromotion.d.ts +287 -0
  149. package/dist/solution/BaseSales/utils/cartPromotion.js +1450 -0
  150. package/dist/solution/BaseSales/utils/parseSalesResponse.d.ts +144 -0
  151. package/dist/solution/BaseSales/utils/parseSalesResponse.js +157 -0
  152. package/dist/solution/BaseSales/utils/quotationPrice.d.ts +18 -0
  153. package/dist/solution/BaseSales/utils/quotationPrice.js +250 -0
  154. package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.d.ts +63 -0
  155. package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +218 -0
  156. package/dist/solution/BaseSales/utils.d.ts +40 -0
  157. package/dist/solution/BaseSales/utils.js +263 -0
  158. package/dist/solution/BookingByStep/index.js +7 -59
  159. package/dist/solution/BookingByStep/utils/capacity.js +1 -11
  160. package/dist/solution/BookingByStep/utils/resources.js +1 -3
  161. package/dist/solution/BookingByStep/utils/timeslots.js +2 -43
  162. package/dist/solution/BookingTicket/index.d.ts +393 -24
  163. package/dist/solution/BookingTicket/index.js +2506 -445
  164. package/dist/solution/BookingTicket/types.d.ts +191 -6
  165. package/dist/solution/BookingTicket/types.js +83 -10
  166. package/dist/solution/BookingTicket/utils/addProductDecision.d.ts +117 -0
  167. package/dist/solution/BookingTicket/utils/addProductDecision.js +346 -0
  168. package/dist/solution/BookingTicket/utils/addTimeAvailability.d.ts +35 -0
  169. package/dist/solution/BookingTicket/utils/addTimeAvailability.js +95 -0
  170. package/dist/solution/BookingTicket/utils/bookingStatus.d.ts +77 -0
  171. package/dist/solution/BookingTicket/utils/bookingStatus.js +136 -0
  172. package/dist/solution/BookingTicket/utils/cartView.d.ts +29 -0
  173. package/dist/solution/BookingTicket/utils/cartView.js +209 -0
  174. package/dist/solution/BookingTicket/utils/exampleData.d.ts +123 -0
  175. package/dist/solution/BookingTicket/utils/exampleData.js +183 -0
  176. package/dist/solution/BookingTicket/utils/orderCustomer.d.ts +4 -0
  177. package/dist/solution/BookingTicket/utils/orderCustomer.js +31 -0
  178. package/dist/solution/BookingTicket/utils/resolveBestAddTimePlan.d.ts +185 -0
  179. package/dist/solution/BookingTicket/utils/resolveBestAddTimePlan.js +438 -0
  180. package/dist/solution/BookingTicket/utils/scan/applyGlobalScan.d.ts +36 -0
  181. package/dist/solution/BookingTicket/utils/scan/applyGlobalScan.js +400 -0
  182. package/dist/solution/BookingTicket/utils/scan/cloudSearch.d.ts +4 -0
  183. package/dist/solution/BookingTicket/utils/scan/cloudSearch.js +41 -1
  184. package/dist/solution/BookingTicket/utils/scan/formatGlobalScan.d.ts +32 -0
  185. package/dist/solution/BookingTicket/utils/scan/formatGlobalScan.js +56 -0
  186. package/dist/solution/BookingTicket/utils/scan/handleScan.d.ts +3 -1
  187. package/dist/solution/BookingTicket/utils/scan/handleScan.js +36 -34
  188. package/dist/solution/BookingTicket/utils/scan/index.d.ts +5 -0
  189. package/dist/solution/BookingTicket/utils/scan/index.js +26 -18
  190. package/dist/solution/BookingTicket/utils/scan/scanProductValues.d.ts +21 -0
  191. package/dist/solution/BookingTicket/utils/scan/scanProductValues.js +150 -0
  192. package/dist/solution/BookingTicket/utils/sessionCatalogStale.d.ts +23 -0
  193. package/dist/solution/BookingTicket/utils/sessionCatalogStale.js +78 -0
  194. package/dist/solution/BuyTickets/index.js +9 -12
  195. package/dist/solution/Checkout/index.d.ts +1 -0
  196. package/dist/solution/Checkout/index.js +199 -277
  197. package/dist/solution/Checkout/utils/index.js +8 -20
  198. package/dist/solution/RegisterAndLogin/config.js +2 -10
  199. package/dist/solution/RegisterAndLogin/index.js +30 -76
  200. package/dist/solution/Sales/index.d.ts +50 -5
  201. package/dist/solution/Sales/index.js +484 -92
  202. package/dist/solution/Sales/types.d.ts +26 -2
  203. package/dist/solution/ScanOrder/index.d.ts +27 -21
  204. package/dist/solution/ScanOrder/index.js +509 -454
  205. package/dist/solution/ScanOrder/types.d.ts +33 -11
  206. package/dist/solution/ScanOrder/types.js +4 -5
  207. package/dist/solution/ScanOrder/utils.d.ts +4 -1
  208. package/dist/solution/ScanOrder/utils.js +109 -34
  209. package/dist/solution/ShopDiscount/index.js +6 -11
  210. package/dist/solution/UnifiedBookingSales/index.d.ts +183 -0
  211. package/dist/solution/UnifiedBookingSales/index.js +1871 -0
  212. package/dist/solution/UnifiedBookingSales/types.d.ts +143 -0
  213. package/dist/solution/UnifiedBookingSales/types.js +11 -0
  214. package/dist/solution/VenueBooking/index.d.ts +34 -20
  215. package/dist/solution/VenueBooking/index.js +630 -505
  216. package/dist/solution/VenueBooking/sales-section-4-annotated.json +0 -2
  217. package/dist/solution/VenueBooking/utils/slotMerge.d.ts +6 -0
  218. package/dist/solution/VenueBooking/utils/slotMerge.js +9 -3
  219. package/dist/solution/index.d.ts +2 -0
  220. package/dist/solution/index.js +3 -1
  221. package/dist/types/index.d.ts +11 -1
  222. package/dist/utils/task.js +15 -18
  223. package/dist/utils/watch.js +0 -1
  224. package/lib/apis/picoding.js +2 -0
  225. package/lib/core/index.js +130 -131
  226. package/lib/effects/index.js +46 -57
  227. package/lib/index.js +92 -53
  228. package/lib/model/index.js +14 -21
  229. package/lib/model/strategy/adapter/dataVariant/adapter.js +38 -50
  230. package/lib/model/strategy/adapter/dataVariant/evaluator.js +162 -236
  231. package/lib/model/strategy/adapter/dataVariant/examples.js +229 -237
  232. package/lib/model/strategy/adapter/dataVariant/index.js +39 -34
  233. package/lib/model/strategy/adapter/dataVariant/type.js +57 -28
  234. package/lib/model/strategy/adapter/index.js +100 -64
  235. package/lib/model/strategy/adapter/itemRule/adapter.js +156 -144
  236. package/lib/model/strategy/adapter/itemRule/evaluator.js +36 -48
  237. package/lib/model/strategy/adapter/itemRule/examples.js +295 -225
  238. package/lib/model/strategy/adapter/itemRule/index.js +83 -57
  239. package/lib/model/strategy/adapter/itemRule/type.js +97 -36
  240. package/lib/model/strategy/adapter/promotion/adapter.js +84 -63
  241. package/lib/model/strategy/adapter/promotion/evaluator.js +383 -232
  242. package/lib/model/strategy/adapter/promotion/examples.js +139 -159
  243. package/lib/model/strategy/adapter/promotion/index.js +1 -49
  244. package/lib/model/strategy/adapter/promotion/type.js +199 -35
  245. package/lib/model/strategy/adapter/type.js +4 -16
  246. package/lib/model/strategy/adapter/walletPass/evaluator.js +180 -152
  247. package/lib/model/strategy/adapter/walletPass/example.js +217 -190
  248. package/lib/model/strategy/adapter/walletPass/index.js +75 -51
  249. package/lib/model/strategy/adapter/walletPass/locales.js +36 -58
  250. package/lib/model/strategy/adapter/walletPass/type.js +4 -16
  251. package/lib/model/strategy/adapter/walletPass/utils.js +654 -402
  252. package/lib/model/strategy/engine.js +270 -168
  253. package/lib/model/strategy/index.js +49 -34
  254. package/lib/model/strategy/strategy-example.js +243 -239
  255. package/lib/model/strategy/type.js +100 -40
  256. package/lib/modules/Account/index.js +39 -53
  257. package/lib/modules/Account/types.js +20 -33
  258. package/lib/modules/AccountList/index.js +116 -154
  259. package/lib/modules/AccountList/types.js +30 -31
  260. package/lib/modules/AccountList/utils.js +18 -30
  261. package/lib/modules/BaseModule.d.ts +2 -2
  262. package/lib/modules/BaseModule.js +26 -49
  263. package/lib/modules/BookingContext/index.d.ts +48 -0
  264. package/lib/modules/BookingContext/index.js +390 -0
  265. package/lib/modules/BookingContext/types.d.ts +102 -0
  266. package/lib/modules/BookingContext/types.js +48 -0
  267. package/lib/modules/BookingContext/utils/buildCacheItemFromOrderLine.d.ts +16 -0
  268. package/lib/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +286 -0
  269. package/lib/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.d.ts +8 -0
  270. package/lib/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +148 -0
  271. package/lib/modules/BookingContext/utils/cacheItemToBookingInput.d.ts +76 -0
  272. package/lib/modules/BookingContext/utils/cacheItemToBookingInput.js +242 -0
  273. package/lib/modules/BookingContext/utils/flexible.d.ts +28 -0
  274. package/lib/modules/BookingContext/utils/flexible.js +63 -0
  275. package/lib/modules/BookingContext/utils/formatResourceList.d.ts +8 -0
  276. package/lib/modules/BookingContext/utils/formatResourceList.js +35 -0
  277. package/lib/modules/BookingContext/utils/index.d.ts +11 -0
  278. package/lib/modules/BookingContext/utils/index.js +126 -0
  279. package/lib/modules/BookingContext/utils/noResource.d.ts +13 -0
  280. package/lib/modules/BookingContext/utils/noResource.js +78 -0
  281. package/lib/modules/BookingContext/utils/orderLineDisplay.d.ts +1 -0
  282. package/lib/modules/BookingContext/utils/orderLineDisplay.js +29 -0
  283. package/lib/modules/BookingContext/utils/productExtend.d.ts +72 -0
  284. package/lib/modules/BookingContext/utils/productExtend.js +330 -0
  285. package/lib/modules/BookingContext/utils/resourceErrors.d.ts +41 -0
  286. package/lib/modules/BookingContext/utils/resourceErrors.js +80 -0
  287. package/lib/modules/BookingContext/utils/resourceSelection.d.ts +7 -0
  288. package/lib/modules/BookingContext/utils/resourceSelection.js +24 -0
  289. package/lib/modules/BookingContext/utils/resources.d.ts +80 -0
  290. package/lib/modules/BookingContext/utils/resources.js +419 -0
  291. package/lib/modules/BookingContext/utils/serviceTimes.d.ts +55 -0
  292. package/lib/modules/BookingContext/utils/serviceTimes.js +161 -0
  293. package/lib/modules/BookingContext/utils/timeSlices.d.ts +42 -0
  294. package/lib/modules/BookingContext/utils/timeSlices.js +114 -0
  295. package/lib/modules/Cart/index.js +170 -124
  296. package/lib/modules/Cart/types.js +46 -51
  297. package/lib/modules/Cart/utils/cartAccount.js +40 -39
  298. package/lib/modules/Cart/utils/cartDate.js +56 -61
  299. package/lib/modules/Cart/utils/cartDiscount.js +28 -33
  300. package/lib/modules/Cart/utils/cartNote.js +27 -32
  301. package/lib/modules/Cart/utils/cartProduct.js +331 -244
  302. package/lib/modules/Cart/utils/cartRelationForms.js +29 -35
  303. package/lib/modules/Cart/utils/cartResource.js +72 -78
  304. package/lib/modules/Cart/utils/changePrice.js +37 -67
  305. package/lib/modules/Cart/utils/index.js +106 -48
  306. package/lib/modules/Customer/constants.js +13 -34
  307. package/lib/modules/Customer/index.d.ts +20 -0
  308. package/lib/modules/Customer/index.js +270 -154
  309. package/lib/modules/Customer/types.d.ts +2 -0
  310. package/lib/modules/Customer/types.js +38 -35
  311. package/lib/modules/Date/index.js +116 -115
  312. package/lib/modules/Date/types.js +16 -28
  313. package/lib/modules/Date/utils.js +174 -123
  314. package/lib/modules/Discount/index.d.ts +2 -1
  315. package/lib/modules/Discount/index.js +133 -113
  316. package/lib/modules/Discount/types.d.ts +1 -0
  317. package/lib/modules/Discount/types.js +9 -31
  318. package/lib/modules/Guests/index.js +30 -56
  319. package/lib/modules/Guests/types.js +23 -33
  320. package/lib/modules/Holder/index.js +209 -189
  321. package/lib/modules/Holder/types.js +4 -16
  322. package/lib/modules/Holder/utils.js +20 -49
  323. package/lib/modules/OpenData/index.d.ts +8 -0
  324. package/lib/modules/OpenData/index.js +81 -74
  325. package/lib/modules/OpenData/types.js +4 -16
  326. package/lib/modules/OpenData/utils.js +44 -78
  327. package/lib/modules/Order/index.d.ts +449 -24
  328. package/lib/modules/Order/index.js +3834 -489
  329. package/lib/modules/Order/types.d.ts +693 -88
  330. package/lib/modules/Order/types.js +78 -33
  331. package/lib/modules/Order/utils/bundleDiscountHydration.d.ts +5 -0
  332. package/lib/modules/Order/utils/bundleDiscountHydration.js +106 -0
  333. package/lib/modules/Order/utils/manualProductDiscount.d.ts +106 -0
  334. package/lib/modules/Order/utils/manualProductDiscount.js +207 -0
  335. package/lib/modules/Order/utils/orderCollectionIdentity.d.ts +53 -0
  336. package/lib/modules/Order/utils/orderCollectionIdentity.js +353 -0
  337. package/lib/modules/Order/utils.d.ts +167 -18
  338. package/lib/modules/Order/utils.js +1247 -345
  339. package/lib/modules/Payment/cash.js +20 -33
  340. package/lib/modules/Payment/cashRecommendationAlgorithm.js +145 -76
  341. package/lib/modules/Payment/eftpos.js +16 -30
  342. package/lib/modules/Payment/index.d.ts +10 -9
  343. package/lib/modules/Payment/index.js +542 -502
  344. package/lib/modules/Payment/mx51.js +1 -0
  345. package/lib/modules/Payment/types.d.ts +40 -16
  346. package/lib/modules/Payment/types.js +216 -107
  347. package/lib/modules/Payment/utils.d.ts +1 -1
  348. package/lib/modules/Payment/utils.js +53 -49
  349. package/lib/modules/Payment/walletpass.js +231 -203
  350. package/lib/modules/Product/index.js +51 -46
  351. package/lib/modules/Product/types.d.ts +29 -7
  352. package/lib/modules/Product/types.js +4 -16
  353. package/lib/modules/Product/utils.js +32 -33
  354. package/lib/modules/ProductList/index.d.ts +17 -13
  355. package/lib/modules/ProductList/index.js +160 -118
  356. package/lib/modules/ProductList/types.d.ts +17 -0
  357. package/lib/modules/ProductList/types.js +9 -31
  358. package/lib/modules/Quotation/index.d.ts +22 -2
  359. package/lib/modules/Quotation/index.js +123 -89
  360. package/lib/modules/Quotation/types.d.ts +19 -0
  361. package/lib/modules/Quotation/types.js +4 -16
  362. package/lib/modules/Resource/index.js +27 -59
  363. package/lib/modules/Resource/types.js +22 -32
  364. package/lib/modules/Resource/utils.js +30 -38
  365. package/lib/modules/ResourcePlanner/index.d.ts +24 -0
  366. package/lib/modules/ResourcePlanner/index.js +147 -0
  367. package/lib/modules/ResourcePlanner/planner.d.ts +14 -0
  368. package/lib/modules/ResourcePlanner/planner.js +895 -0
  369. package/lib/modules/ResourcePlanner/types.d.ts +227 -0
  370. package/lib/modules/ResourcePlanner/types.js +5 -0
  371. package/lib/modules/Rules/index.d.ts +8 -10
  372. package/lib/modules/Rules/index.js +1286 -894
  373. package/lib/modules/Rules/types.d.ts +14 -3
  374. package/lib/modules/Rules/types.js +17 -40
  375. package/lib/modules/SalesSummary/index.d.ts +23 -26
  376. package/lib/modules/SalesSummary/index.js +130 -71
  377. package/lib/modules/SalesSummary/types.d.ts +9 -1
  378. package/lib/modules/SalesSummary/types.js +4 -16
  379. package/lib/modules/SalesSummary/utils.d.ts +3 -22
  380. package/lib/modules/SalesSummary/utils.js +738 -250
  381. package/lib/modules/ScanOrderLogger/index.js +59 -79
  382. package/lib/modules/ScanOrderLogger/providers/feishu.js +54 -78
  383. package/lib/modules/ScanOrderLogger/providers/grafana.js +13 -36
  384. package/lib/modules/ScanOrderLogger/types.js +4 -16
  385. package/lib/modules/Schedule/getDateIsInSchedule.js +345 -171
  386. package/lib/modules/Schedule/index.d.ts +18 -5
  387. package/lib/modules/Schedule/index.js +139 -114
  388. package/lib/modules/Schedule/type.js +4 -16
  389. package/lib/modules/Schedule/types.d.ts +2 -0
  390. package/lib/modules/Schedule/types.js +4 -16
  391. package/lib/modules/Schedule/utils.js +433 -291
  392. package/lib/modules/Step/index.js +40 -52
  393. package/lib/modules/Step/tyeps.js +4 -16
  394. package/lib/modules/Summary/index.js +66 -70
  395. package/lib/modules/Summary/types.js +4 -16
  396. package/lib/modules/Summary/utils.js +782 -429
  397. package/lib/modules/SurchargeList/index.d.ts +18 -0
  398. package/lib/modules/SurchargeList/index.js +95 -0
  399. package/lib/modules/SurchargeList/types.d.ts +15 -0
  400. package/lib/modules/SurchargeList/types.js +5 -0
  401. package/lib/modules/index.d.ts +3 -0
  402. package/lib/modules/index.js +245 -57
  403. package/lib/plugins/app.d.ts +2 -0
  404. package/lib/plugins/app.js +4 -16
  405. package/lib/plugins/index.js +36 -25
  406. package/lib/plugins/request.d.ts +1 -0
  407. package/lib/plugins/request.js +137 -126
  408. package/lib/plugins/shopStore.js +4 -16
  409. package/lib/plugins/user.js +4 -16
  410. package/lib/plugins/window.js +171 -179
  411. package/lib/server/index.d.ts +462 -8
  412. package/lib/server/index.js +4668 -713
  413. package/lib/server/modules/floor-plan/index.d.ts +39 -0
  414. package/lib/server/modules/floor-plan/index.js +306 -0
  415. package/lib/server/modules/floor-plan/types.d.ts +43 -0
  416. package/lib/server/modules/floor-plan/types.js +19 -0
  417. package/lib/server/modules/index.d.ts +7 -2
  418. package/lib/server/modules/index.js +106 -59
  419. package/lib/server/modules/menu/index.js +146 -122
  420. package/lib/server/modules/menu/types.js +18 -31
  421. package/lib/server/modules/order/index.d.ts +207 -14
  422. package/lib/server/modules/order/index.js +1463 -316
  423. package/lib/server/modules/order/types.d.ts +76 -5
  424. package/lib/server/modules/order/types.js +117 -32
  425. package/lib/server/modules/order/utils/filterBookings.d.ts +7 -1
  426. package/lib/server/modules/order/utils/filterBookings.js +288 -146
  427. package/lib/server/modules/order/utils/filterOrders.js +318 -64
  428. package/lib/server/modules/payment/index.d.ts +28 -0
  429. package/lib/server/modules/payment/index.js +168 -0
  430. package/lib/server/modules/payment/types.d.ts +9 -0
  431. package/lib/server/modules/payment/types.js +5 -0
  432. package/lib/server/modules/products/index.d.ts +105 -14
  433. package/lib/server/modules/products/index.js +969 -383
  434. package/lib/server/modules/products/types.d.ts +34 -1
  435. package/lib/server/modules/products/types.js +44 -34
  436. package/lib/server/modules/quotation/index.d.ts +39 -0
  437. package/lib/server/modules/quotation/index.js +220 -77
  438. package/lib/server/modules/quotation/types.js +21 -31
  439. package/lib/server/modules/resource/index.d.ts +2 -3
  440. package/lib/server/modules/resource/index.js +269 -214
  441. package/lib/server/modules/resource/types.js +42 -33
  442. package/lib/server/modules/schedule/index.d.ts +6 -4
  443. package/lib/server/modules/schedule/index.js +158 -132
  444. package/lib/server/modules/schedule/types.js +14 -21
  445. package/lib/server/modules/schedule/utils.js +334 -163
  446. package/lib/server/test.json +713 -0
  447. package/lib/server/types.d.ts +2 -0
  448. package/lib/server/types.js +4 -16
  449. package/lib/server/utils/index.js +25 -23
  450. package/lib/server/utils/product.js +197 -140
  451. package/lib/server/utils/schedule.js +34 -41
  452. package/lib/server/utils/small-ticket.d.ts +71 -0
  453. package/lib/server/utils/small-ticket.js +926 -0
  454. package/lib/server/utils/time.js +40 -49
  455. package/lib/solution/BaseSales/index.d.ts +378 -0
  456. package/lib/solution/BaseSales/index.js +2367 -0
  457. package/lib/solution/BaseSales/types.d.ts +394 -0
  458. package/lib/solution/BaseSales/types.js +47 -0
  459. package/lib/solution/BaseSales/utils/cartPromotion.d.ts +287 -0
  460. package/lib/solution/BaseSales/utils/cartPromotion.js +1155 -0
  461. package/lib/solution/BaseSales/utils/parseSalesResponse.d.ts +144 -0
  462. package/lib/solution/BaseSales/utils/parseSalesResponse.js +140 -0
  463. package/lib/solution/BaseSales/utils/quotationPrice.d.ts +18 -0
  464. package/lib/solution/BaseSales/utils/quotationPrice.js +231 -0
  465. package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.d.ts +63 -0
  466. package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +215 -0
  467. package/lib/solution/BaseSales/utils.d.ts +40 -0
  468. package/lib/solution/BaseSales/utils.js +259 -0
  469. package/lib/solution/BookingByStep/index.js +1530 -1343
  470. package/lib/solution/BookingByStep/types.js +40 -99
  471. package/lib/solution/BookingByStep/utils/capacity.js +192 -160
  472. package/lib/solution/BookingByStep/utils/products.js +42 -52
  473. package/lib/solution/BookingByStep/utils/resources.js +527 -330
  474. package/lib/solution/BookingByStep/utils/stock.js +65 -44
  475. package/lib/solution/BookingByStep/utils/timeslots.js +129 -131
  476. package/lib/solution/BookingTicket/index.d.ts +393 -24
  477. package/lib/solution/BookingTicket/index.js +1459 -160
  478. package/lib/solution/BookingTicket/types.d.ts +191 -6
  479. package/lib/solution/BookingTicket/types.js +101 -66
  480. package/lib/solution/BookingTicket/utils/addProductDecision.d.ts +117 -0
  481. package/lib/solution/BookingTicket/utils/addProductDecision.js +353 -0
  482. package/lib/solution/BookingTicket/utils/addTimeAvailability.d.ts +35 -0
  483. package/lib/solution/BookingTicket/utils/addTimeAvailability.js +94 -0
  484. package/lib/solution/BookingTicket/utils/bookingStatus.d.ts +77 -0
  485. package/lib/solution/BookingTicket/utils/bookingStatus.js +136 -0
  486. package/lib/solution/BookingTicket/utils/cartView.d.ts +29 -0
  487. package/lib/solution/BookingTicket/utils/cartView.js +200 -0
  488. package/lib/solution/BookingTicket/utils/exampleData.d.ts +123 -0
  489. package/lib/solution/BookingTicket/utils/exampleData.js +185 -0
  490. package/lib/solution/BookingTicket/utils/orderCustomer.d.ts +4 -0
  491. package/lib/solution/BookingTicket/utils/orderCustomer.js +35 -0
  492. package/lib/solution/BookingTicket/utils/resolveBestAddTimePlan.d.ts +185 -0
  493. package/lib/solution/BookingTicket/utils/resolveBestAddTimePlan.js +387 -0
  494. package/lib/solution/BookingTicket/utils/scan/applyGlobalScan.d.ts +36 -0
  495. package/lib/solution/BookingTicket/utils/scan/applyGlobalScan.js +220 -0
  496. package/lib/solution/BookingTicket/utils/scan/cloudSearch.d.ts +4 -0
  497. package/lib/solution/BookingTicket/utils/scan/cloudSearch.js +85 -80
  498. package/lib/solution/BookingTicket/utils/scan/formatGlobalScan.d.ts +32 -0
  499. package/lib/solution/BookingTicket/utils/scan/formatGlobalScan.js +64 -0
  500. package/lib/solution/BookingTicket/utils/scan/handleScan.d.ts +3 -1
  501. package/lib/solution/BookingTicket/utils/scan/handleScan.js +84 -80
  502. package/lib/solution/BookingTicket/utils/scan/index.d.ts +5 -0
  503. package/lib/solution/BookingTicket/utils/scan/index.js +103 -95
  504. package/lib/solution/BookingTicket/utils/scan/scanCache.js +90 -43
  505. package/lib/solution/BookingTicket/utils/scan/scanProductValues.d.ts +21 -0
  506. package/lib/solution/BookingTicket/utils/scan/scanProductValues.js +115 -0
  507. package/lib/solution/BookingTicket/utils/sessionCatalogStale.d.ts +23 -0
  508. package/lib/solution/BookingTicket/utils/sessionCatalogStale.js +83 -0
  509. package/lib/solution/BuyTickets/index.js +67 -70
  510. package/lib/solution/BuyTickets/types.js +22 -38
  511. package/lib/solution/Checkout/index.d.ts +1 -0
  512. package/lib/solution/Checkout/index.js +1461 -1169
  513. package/lib/solution/Checkout/types.js +91 -61
  514. package/lib/solution/Checkout/utils/index.js +228 -159
  515. package/lib/solution/PlaceOrder/index.js +55 -0
  516. package/lib/solution/RegisterAndLogin/config.js +495 -470
  517. package/lib/solution/RegisterAndLogin/index.js +633 -630
  518. package/lib/solution/RegisterAndLogin/types.js +189 -76
  519. package/lib/solution/RegisterAndLogin/utils.js +183 -125
  520. package/lib/solution/Sales/index.d.ts +50 -5
  521. package/lib/solution/Sales/index.js +495 -220
  522. package/lib/solution/Sales/types.d.ts +26 -2
  523. package/lib/solution/Sales/types.js +27 -33
  524. package/lib/solution/ScanOrder/index.d.ts +27 -21
  525. package/lib/solution/ScanOrder/index.js +908 -904
  526. package/lib/solution/ScanOrder/types.d.ts +33 -11
  527. package/lib/solution/ScanOrder/types.js +33 -34
  528. package/lib/solution/ScanOrder/utils.d.ts +4 -1
  529. package/lib/solution/ScanOrder/utils.js +457 -344
  530. package/lib/solution/ShopDiscount/index.js +230 -236
  531. package/lib/solution/ShopDiscount/types.js +15 -37
  532. package/lib/solution/ShopDiscount/utils.js +309 -172
  533. package/lib/solution/UnifiedBookingSales/index.d.ts +183 -0
  534. package/lib/solution/UnifiedBookingSales/index.js +1050 -0
  535. package/lib/solution/UnifiedBookingSales/types.d.ts +143 -0
  536. package/lib/solution/UnifiedBookingSales/types.js +14 -0
  537. package/lib/solution/VenueBooking/index.d.ts +34 -20
  538. package/lib/solution/VenueBooking/index.js +982 -950
  539. package/lib/solution/VenueBooking/sales-section-4-annotated.json +0 -2
  540. package/lib/solution/VenueBooking/types.js +20 -40
  541. package/lib/solution/VenueBooking/utils/dateSummary.js +22 -54
  542. package/lib/solution/VenueBooking/utils/resource.js +31 -54
  543. package/lib/solution/VenueBooking/utils/slotMerge.d.ts +6 -0
  544. package/lib/solution/VenueBooking/utils/slotMerge.js +96 -108
  545. package/lib/solution/VenueBooking/utils/smartPricing.js +112 -100
  546. package/lib/solution/VenueBooking/utils/timeSlot.js +133 -132
  547. package/lib/solution/VenueBooking/utils.js +130 -67
  548. package/lib/solution/index.d.ts +2 -0
  549. package/lib/solution/index.js +124 -37
  550. package/lib/store/createStore.js +32 -29
  551. package/lib/types/index.d.ts +11 -1
  552. package/lib/types/index.js +4 -16
  553. package/lib/utils/task.js +36 -36
  554. package/lib/utils/watch.js +81 -47
  555. package/package.json +2 -1
  556. package/dist/solution/Checkout/appointmentDemo.json +0 -1
  557. package/lib/solution/Checkout/appointmentDemo.json +0 -1
@@ -0,0 +1,2367 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ var _exportNames = {
7
+ BaseSalesImpl: true
8
+ };
9
+ exports.BaseSalesImpl = void 0;
10
+ var _BaseModule = require("../../modules/BaseModule");
11
+ var _types = require("./types");
12
+ Object.keys(_types).forEach(function (key) {
13
+ if (key === "default" || key === "__esModule") return;
14
+ if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
15
+ if (key in exports && exports[key] === _types[key]) return;
16
+ Object.defineProperty(exports, key, {
17
+ enumerable: true,
18
+ get: function () {
19
+ return _types[key];
20
+ }
21
+ });
22
+ });
23
+ var _Order = require("../../modules/Order");
24
+ var _decimal = _interopRequireDefault(require("decimal.js"));
25
+ var _lodashEs = require("lodash-es");
26
+ var _types2 = require("../BookingByStep/types");
27
+ var _utils = require("../../modules/Order/utils");
28
+ var _orderCollectionIdentity = require("../../modules/Order/utils/orderCollectionIdentity");
29
+ var _dayjs = _interopRequireDefault(require("dayjs"));
30
+ var _Quotation = require("../../modules/Quotation");
31
+ var _transformBaseProductToOrderProduct = require("./utils/transformBaseProductToOrderProduct");
32
+ var _quotationPrice = require("./utils/quotationPrice");
33
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
34
+ const SERVER_ORDER_CHANGED_EVENT = 'order:onOrdersChanged';
35
+ function isBaseSalesManualProductDiscountProduct(product) {
36
+ return product?.metadata?.is_manual_discount === true || product?.metadata?.is_manual_discount === 1 || (product?.discount_list || []).some(item => item?.type === 'product');
37
+ }
38
+ function getBaseSalesProductUid(product) {
39
+ const uid = product?.metadata?.unique_identification_number || product?.unique_identification_number;
40
+ if (uid === undefined || uid === null || uid === '') return null;
41
+ return String(uid);
42
+ }
43
+ function preserveManualProductDiscountProducts(previousProducts, nextProducts) {
44
+ const previousByUid = new Map();
45
+ (previousProducts || []).forEach((product, index) => {
46
+ const uid = getBaseSalesProductUid(product);
47
+ if (uid) previousByUid.set(uid, product);
48
+ previousByUid.set(`__index__:${index}`, product);
49
+ });
50
+ return (nextProducts || []).map((product, index) => {
51
+ const uid = getBaseSalesProductUid(product);
52
+ const previous = (uid ? previousByUid.get(uid) : undefined) || previousByUid.get(`__index__:${index}`);
53
+ return previous && isBaseSalesManualProductDiscountProduct(previous) ? previous : product;
54
+ });
55
+ }
56
+ function isBaseSalesHistoricalDiscountEntry(discount) {
57
+ return Boolean(discount?.isEditMode || discount?.isDisabled || discount?.order_discount_id != null);
58
+ }
59
+ const BASE_SALES_PERSISTENT_ID_FIELD_BY_KIND = {
60
+ product: 'order_detail_id',
61
+ booking: 'schedule_event_id',
62
+ payment: 'order_payment_id'
63
+ };
64
+ function getBaseSalesOrderCollectionKind(key) {
65
+ if (key === 'products') return 'product';
66
+ if (key === 'bookings') return 'booking';
67
+ if (key === 'payments' || key === 'payment') return 'payment';
68
+ return null;
69
+ }
70
+ function isSameBaseSalesOrderRecord(left, right) {
71
+ if (!left || !right || typeof left !== 'object' || typeof right !== 'object') return false;
72
+ const leftIdentities = new Set([left.order_id, left.id, left.external_sale_number, left.order_number].filter(value => value !== undefined && value !== null && value !== '').map(String));
73
+ if (leftIdentities.size === 0) return false;
74
+ return [right.order_id, right.id, right.external_sale_number, right.order_number].some(value => value !== undefined && value !== null && value !== '' && leftIdentities.has(String(value)));
75
+ }
76
+ function isBaseSalesBlankValue(value) {
77
+ return value === undefined || value === null || value === '';
78
+ }
79
+ function mergeBaseSalesProductSnapshotFields(currentItem, loadedItem) {
80
+ const mergedItem = (0, _lodashEs.cloneDeep)(loadedItem);
81
+ if (isBaseSalesBlankValue(mergedItem.product_cover) && !isBaseSalesBlankValue(currentItem?.product_cover)) {
82
+ mergedItem.product_cover = currentItem.product_cover;
83
+ }
84
+ const currentHolderConfig = currentItem?.metadata?.holder_config;
85
+ if ((mergedItem.metadata?.holder_config === undefined || mergedItem.metadata?.holder_config === null) && currentHolderConfig !== undefined && currentHolderConfig !== null) {
86
+ mergedItem.metadata = {
87
+ ...(mergedItem.metadata || {}),
88
+ holder_config: currentHolderConfig
89
+ };
90
+ }
91
+ return mergedItem;
92
+ }
93
+ function mergeBaseSalesCollectionItem(kind, currentItem, incomingItem, uidRemaps) {
94
+ const currentReferenceUid = (0, _orderCollectionIdentity.getOrderCollectionReferenceUid)(kind, currentItem);
95
+ const incomingReferenceUid = (0, _orderCollectionIdentity.getOrderCollectionReferenceUid)(kind, incomingItem);
96
+ const identityMergedItem = (0, _orderCollectionIdentity.mergeOrderCollectionItems)(kind, currentItem, incomingItem);
97
+ const mergedUid = (0, _orderCollectionIdentity.getOrderCollectionUid)(kind, identityMergedItem);
98
+ if (mergedUid) {
99
+ for (const from of new Set([currentReferenceUid, incomingReferenceUid])) {
100
+ if (!from || from === mergedUid) continue;
101
+ uidRemaps.push({
102
+ kind,
103
+ from,
104
+ to: mergedUid,
105
+ persistentId: (0, _orderCollectionIdentity.getOrderCollectionPersistentId)(kind, incomingItem) || (0, _orderCollectionIdentity.getOrderCollectionPersistentId)(kind, currentItem)
106
+ });
107
+ }
108
+ }
109
+ const mergedItem = mergedUid ? (0, _orderCollectionIdentity.setOrderCollectionUid)(kind, incomingItem, mergedUid) : (0, _lodashEs.cloneDeep)(incomingItem);
110
+ const persistentIdField = BASE_SALES_PERSISTENT_ID_FIELD_BY_KIND[kind];
111
+ if (isBaseSalesBlankValue(mergedItem[persistentIdField]) && !isBaseSalesBlankValue(identityMergedItem[persistentIdField])) {
112
+ mergedItem[persistentIdField] = identityMergedItem[persistentIdField];
113
+ }
114
+ return kind === 'product' ? mergeBaseSalesProductSnapshotFields(currentItem, mergedItem) : mergedItem;
115
+ }
116
+ function mergeBaseSalesOrderCollection(kind, currentValue, incomingValue, strategy, uidRemaps) {
117
+ const currentItems = (0, _orderCollectionIdentity.normalizeOrderCollection)(kind, currentValue, {
118
+ ensureUid: false
119
+ }).items;
120
+ const incomingItems = (0, _orderCollectionIdentity.normalizeOrderCollection)(kind, incomingValue, {
121
+ ensureUid: false
122
+ }).items;
123
+ if (strategy === 'authoritative-incoming') {
124
+ const result = (0, _lodashEs.cloneDeep)(incomingItems);
125
+ currentItems.forEach(currentItem => {
126
+ const incomingIndex = result.findIndex(incomingItem => (0, _orderCollectionIdentity.isSameOrderCollectionItem)(kind, currentItem, incomingItem));
127
+ if (incomingIndex !== -1) {
128
+ result[incomingIndex] = mergeBaseSalesCollectionItem(kind, currentItem, result[incomingIndex], uidRemaps);
129
+ return;
130
+ }
131
+
132
+ // 完整服务端快照负责已落库行;仅保留尚未提交的本地新行。
133
+ if (!(0, _orderCollectionIdentity.getOrderCollectionPersistentId)(kind, currentItem)) {
134
+ result.push((0, _lodashEs.cloneDeep)(currentItem));
135
+ }
136
+ });
137
+ return result;
138
+ }
139
+ const result = (0, _lodashEs.cloneDeep)(currentItems);
140
+ incomingItems.forEach(incomingItem => {
141
+ const currentIndex = result.findIndex(currentItem => (0, _orderCollectionIdentity.isSameOrderCollectionItem)(kind, currentItem, incomingItem));
142
+ if (currentIndex !== -1) {
143
+ result[currentIndex] = mergeBaseSalesCollectionItem(kind, result[currentIndex], incomingItem, uidRemaps);
144
+ return;
145
+ }
146
+ result.push((0, _lodashEs.cloneDeep)(incomingItem));
147
+ });
148
+ return result;
149
+ }
150
+ function normalizeBaseSalesRecordCollections(sourceRecord, options) {
151
+ const normalizedRecord = (0, _orderCollectionIdentity.normalizeOrderCollections)((0, _lodashEs.cloneDeep)(sourceRecord || {}), options).order;
152
+
153
+ // 兼容旧调用方偶尔使用的单数 payment 字段。
154
+ if (Array.isArray(normalizedRecord.payment)) {
155
+ normalizedRecord.payment = (0, _orderCollectionIdentity.normalizeOrderCollection)('payment', normalizedRecord.payment, options).items;
156
+ }
157
+ return normalizedRecord;
158
+ }
159
+ function mergeSalesDetailRecordWithTempOrder(currentTempOrder, loadedRecord, strategy) {
160
+ const normalizedLoadedRecord = normalizeBaseSalesRecordCollections(loadedRecord || {}, {
161
+ ensureUid: false
162
+ });
163
+ if (!currentTempOrder) {
164
+ return normalizeBaseSalesRecordCollections(normalizedLoadedRecord);
165
+ }
166
+ const normalizedCurrentTempOrder = normalizeBaseSalesRecordCollections(currentTempOrder, {
167
+ ensureUid: false
168
+ });
169
+ const uidRemaps = [];
170
+ const mergedRecord = (0, _lodashEs.mergeWith)(normalizedCurrentTempOrder, normalizedLoadedRecord, (currentValue, loadedValue, key) => {
171
+ const collectionKind = getBaseSalesOrderCollectionKind(key);
172
+ if (collectionKind && Array.isArray(loadedValue)) {
173
+ return mergeBaseSalesOrderCollection(collectionKind, Array.isArray(currentValue) ? currentValue : [], loadedValue, strategy, uidRemaps);
174
+ }
175
+ if (Array.isArray(currentValue) && Array.isArray(loadedValue)) {
176
+ return [...(0, _lodashEs.cloneDeep)(currentValue), ...(0, _lodashEs.cloneDeep)(loadedValue)];
177
+ }
178
+ if (Array.isArray(loadedValue)) return (0, _lodashEs.cloneDeep)(loadedValue);
179
+ if (Array.isArray(currentValue) && loadedValue === undefined) {
180
+ return (0, _lodashEs.cloneDeep)(currentValue);
181
+ }
182
+ return undefined;
183
+ });
184
+ delete mergedRecord.request_unique_idempotency_token;
185
+ return normalizeBaseSalesRecordCollections((0, _orderCollectionIdentity.applyOrderCollectionUidRemaps)(mergedRecord, uidRemaps));
186
+ }
187
+ class BaseSalesImpl extends _BaseModule.BaseModule {
188
+ defaultName = 'baseSales';
189
+ defaultVersion = '1.0.0';
190
+ isSolution = true;
191
+ initializeOptions = {};
192
+ logger; // LoggerManager 实例
193
+ appPlugin;
194
+ store = {
195
+ order: undefined
196
+ };
197
+ otherParams = {};
198
+ cacheId;
199
+ /**
200
+ * window / request 暴露为 public:现存外部工具(如 BookingTicket Scan)通过
201
+ * `solution.window` / `solution.request` 直接访问插件,保持原有契约不破坏。
202
+ */
203
+ window;
204
+ request;
205
+
206
+ // 当前缓存中已加载的最新 SalesDetail;只读 getter 走它对外暴露详情数据。
207
+ currentSalesDetail = null;
208
+ discountConfigCacheKey = null;
209
+ hasManualDiscountSelection = false;
210
+ reusedSharedSubModuleNames = new Set();
211
+ paymentMethodsCache = [];
212
+ queuedAutoPaymentSync = false;
213
+ autoPaymentSyncFlushing = false;
214
+ autoPaymentSyncTimer = null;
215
+ salesDetailLoadInFlightCount = 0;
216
+ salesDetailLoadSequence = 0;
217
+ salesDetailHydrateQueue = Promise.resolve();
218
+ serverOrderChangeUnsubscribe = null;
219
+ serverOrderChangeHydrateInFlight = null;
220
+ queuedServerOrderChanges = [];
221
+ serverOrderChangeSyncEnabled = false;
222
+ constructor(name, version) {
223
+ super(name, version);
224
+ }
225
+
226
+ /**
227
+ * 子类可覆盖此方法以追加/收敛要注册的子模块。
228
+ * 默认包含通用销售模块:products / order / salesSummary / schedule / payment / quotation。
229
+ */
230
+ getRegisteredModuleNames() {
231
+ return ['products', 'order', 'salesSummary', 'schedule', 'payment', 'quotation'];
232
+ }
233
+ getReusableSharedSubModule(moduleName) {
234
+ if (moduleName !== 'schedule' && moduleName !== 'quotation') return null;
235
+ return this.core?.getModule?.(moduleName) || null;
236
+ }
237
+ isScheduleListLoaded(scheduleModule) {
238
+ if (!scheduleModule) return false;
239
+ if (typeof scheduleModule.isScheduleListLoaded === 'function') {
240
+ return scheduleModule.isScheduleListLoaded();
241
+ }
242
+ if (typeof scheduleModule.getScheduleList === 'function') {
243
+ return Array.isArray(scheduleModule.getScheduleList());
244
+ }
245
+ return false;
246
+ }
247
+
248
+ /**
249
+ * 记录信息日志
250
+ */
251
+ logInfo(title, metadata) {
252
+ if (this.logger) {
253
+ const tempOrder = this.getTempOrder();
254
+ this.logger.addLog({
255
+ type: 'info',
256
+ title: `[BaseSales] ${tempOrder?.order_id} ${tempOrder?.external_sale_number} ${title}`,
257
+ metadata: metadata || {}
258
+ });
259
+ }
260
+ }
261
+
262
+ /**
263
+ * 记录警告日志
264
+ */
265
+ logWarning(title, metadata) {
266
+ if (this.logger) {
267
+ const tempOrder = this.getTempOrder();
268
+ this.logger.addLog({
269
+ type: 'warning',
270
+ title: `[BaseSales] ${tempOrder?.order_id} ${tempOrder?.external_sale_number} ${title}`,
271
+ metadata: metadata || {}
272
+ });
273
+ }
274
+ }
275
+
276
+ /**
277
+ * 记录错误日志
278
+ */
279
+ logError(title, metadata) {
280
+ if (this.logger) {
281
+ const tempOrder = this.getTempOrder();
282
+ this.logger.addLog({
283
+ type: 'error',
284
+ title: `[BaseSales] ${tempOrder?.order_id} ${tempOrder?.external_sale_number} ${title}`,
285
+ metadata: metadata || {}
286
+ });
287
+ }
288
+ }
289
+ isCurrentSalesOrderRecord(order) {
290
+ const currentTempOrder = this.store.order?.getTempOrder?.();
291
+ const currentRecords = [currentTempOrder, this.currentSalesDetail].filter(Boolean);
292
+ return currentRecords.some(record => isSameBaseSalesOrderRecord(record, order));
293
+ }
294
+ setServerOrderChangeSyncEnabled(enabled) {
295
+ this.serverOrderChangeSyncEnabled = enabled;
296
+ if (!enabled) this.queuedServerOrderChanges = [];
297
+ }
298
+ queueLatestServerOrderChange(order) {
299
+ const snapshot = (0, _lodashEs.cloneDeep)(order);
300
+ const existingIndex = this.queuedServerOrderChanges.findIndex(queued => isSameBaseSalesOrderRecord(queued, snapshot));
301
+ if (existingIndex >= 0) {
302
+ this.queuedServerOrderChanges[existingIndex] = snapshot;
303
+ return;
304
+ }
305
+ this.queuedServerOrderChanges.push(snapshot);
306
+ }
307
+ async drainQueuedServerOrderChanges() {
308
+ if (!this.serverOrderChangeSyncEnabled || this.salesDetailLoadInFlightCount > 0 || this.serverOrderChangeHydrateInFlight || this.queuedServerOrderChanges.length === 0) return;
309
+ const changedOrders = this.queuedServerOrderChanges;
310
+ this.queuedServerOrderChanges = [];
311
+ await this.syncCurrentSalesDetailFromServerOrderChange({
312
+ changedOrders
313
+ });
314
+ }
315
+ async syncCurrentSalesDetailFromServerOrderChange(payload) {
316
+ const changedOrders = Array.isArray(payload) ? payload : Array.isArray(payload?.changedOrders) ? payload.changedOrders : payload?.data && typeof payload.data === 'object' ? [payload.data] : [];
317
+ if (!this.serverOrderChangeSyncEnabled) return;
318
+
319
+ // 显式详情加载期间 current 可能仍是上一单,先按订单身份 latest-only 缓存;
320
+ // load 完成后再用新的 current 过滤,避免恰好到达的最新支付快照丢失。
321
+ if (this.salesDetailLoadInFlightCount > 0) {
322
+ changedOrders.forEach(order => this.queueLatestServerOrderChange(order));
323
+ return;
324
+ }
325
+ const changedOrder = changedOrders.find(order => this.isCurrentSalesOrderRecord(order));
326
+ if (!changedOrder) return;
327
+ if (this.serverOrderChangeHydrateInFlight) {
328
+ this.queueLatestServerOrderChange(changedOrder);
329
+ return;
330
+ }
331
+ if (!this.store.order) return;
332
+ this.serverOrderChangeHydrateInFlight = (async () => {
333
+ const currentTempOrder = this.store.order.getTempOrder?.();
334
+ const record = mergeSalesDetailRecordWithTempOrder(currentTempOrder, changedOrder, 'authoritative-incoming');
335
+ const sales = await this.store.order.hydrateTempOrderFromRecord(record, {
336
+ recalcOnHydrate: false
337
+ });
338
+ this.currentSalesDetail = sales;
339
+ await this.core.effects.emit(`${this.name}:onSalesOrderLoaded`, {
340
+ sales
341
+ });
342
+ })();
343
+ try {
344
+ await this.serverOrderChangeHydrateInFlight;
345
+ } finally {
346
+ this.serverOrderChangeHydrateInFlight = null;
347
+ await this.drainQueuedServerOrderChanges();
348
+ }
349
+ }
350
+ registerServerOrderChangeSync() {
351
+ this.serverOrderChangeUnsubscribe?.();
352
+ const effects = this.core?.effects;
353
+ if (!effects || typeof effects.on !== 'function') return;
354
+ this.serverOrderChangeUnsubscribe = effects.on(SERVER_ORDER_CHANGED_EVENT, payload => {
355
+ return this.syncCurrentSalesDetailFromServerOrderChange(payload).catch(error => {
356
+ this.logError('sync sales detail from server order change failed', {
357
+ error: error instanceof Error ? error.message : String(error)
358
+ });
359
+ });
360
+ });
361
+ }
362
+ async hydrateLatestLoadedSalesDetail(record, loadSequence) {
363
+ let hydratedSales = null;
364
+ let skippedBeforeHydrate = false;
365
+ const hydrateTask = async () => {
366
+ // 网络返回顺序可能倒置;只有当前最新显式加载可以进入共享 tempOrder。
367
+ if (loadSequence !== this.salesDetailLoadSequence) {
368
+ skippedBeforeHydrate = true;
369
+ return;
370
+ }
371
+ if (!this.store.order) throw new Error('order 模块未初始化');
372
+ const sales = await this.store.order.hydrateTempOrderFromRecord(record, {
373
+ recalcOnHydrate: false
374
+ });
375
+ hydratedSales = sales;
376
+
377
+ // hydrate 期间可能又发起了更新请求;后续任务已串行排队,将负责最终状态。
378
+ if (loadSequence !== this.salesDetailLoadSequence) return;
379
+ this.currentSalesDetail = sales;
380
+ await this.core.effects.emit(`${this.name}:onSalesOrderLoaded`, {
381
+ sales
382
+ });
383
+ };
384
+ const queuedTask = this.salesDetailHydrateQueue.then(hydrateTask, hydrateTask);
385
+ this.salesDetailHydrateQueue = queuedTask.then(() => undefined, () => undefined);
386
+ await queuedTask;
387
+ if (skippedBeforeHydrate) return record;
388
+ return hydratedSales || this.currentSalesDetail || record;
389
+ }
390
+ hydrateOrderPaymentNames(payments) {
391
+ if (!payments.length) return [];
392
+ if (!this.paymentMethodsCache.length) {
393
+ return payments.map(payment => ({
394
+ ...payment
395
+ }));
396
+ }
397
+ const paymentMethodById = new Map();
398
+ const paymentMethodByCode = new Map();
399
+ this.paymentMethodsCache.forEach(method => {
400
+ if (method?.id !== undefined && method?.id !== null) {
401
+ paymentMethodById.set(String(method.id), method);
402
+ }
403
+ if (method?.code) {
404
+ paymentMethodByCode.set(String(method.code), method);
405
+ }
406
+ });
407
+ return payments.map(payment => {
408
+ const nextPayment = {
409
+ ...payment
410
+ };
411
+ if (nextPayment.name) return nextPayment;
412
+ const paymentMethod = paymentMethodById.get(String(nextPayment.custom_payment_id)) || paymentMethodByCode.get(String(nextPayment.code));
413
+ if (!paymentMethod?.name) return nextPayment;
414
+ return {
415
+ ...nextPayment,
416
+ name: paymentMethod.name
417
+ };
418
+ });
419
+ }
420
+ get payment() {
421
+ return this.store.payment;
422
+ }
423
+ getCurrentOrderCustomerId() {
424
+ const tempOrder = this.store.order?.getTempOrder?.();
425
+ const customerId = tempOrder?.customer_id;
426
+ if (customerId === null || customerId === undefined) return undefined;
427
+ return customerId;
428
+ }
429
+ normalizePriceContextCustomerId(customerId) {
430
+ if (customerId === undefined || customerId === null || customerId === '') return null;
431
+ return String(customerId);
432
+ }
433
+ applyQuotationScheduleResolver(quotation) {
434
+ const scheduleModule = this.store.schedule;
435
+ if (!scheduleModule) return;
436
+ const scheduleById = new Map();
437
+ const scheduleList = typeof scheduleModule.getScheduleList === 'function' ? scheduleModule.getScheduleList() : [];
438
+ if (Array.isArray(scheduleList)) {
439
+ scheduleList.forEach(schedule => {
440
+ if (schedule?.id !== undefined && schedule?.id !== null) {
441
+ scheduleById.set(String(schedule.id), schedule);
442
+ }
443
+ });
444
+ }
445
+ quotation.setScheduleResolver(id => {
446
+ const scheduleId = String(id);
447
+ if (scheduleById.has(scheduleId)) return scheduleById.get(scheduleId);
448
+ const schedules = scheduleModule.getScheduleListByIds?.([id]) || scheduleModule.getScheduleByIds?.([id]) || [];
449
+ if (schedules[0]) scheduleById.set(scheduleId, schedules[0]);
450
+ return schedules[0];
451
+ });
452
+ }
453
+ async loadQuotationForPriceQuery(params) {
454
+ if (!params.quotation) return;
455
+ this.applyQuotationScheduleResolver(params.quotation);
456
+ await params.quotation.loadQuotations({
457
+ channel: params.channel || this.otherParams?.channel,
458
+ customer_id: params.customer_id
459
+ });
460
+ }
461
+ getPriceQueryProductId(product) {
462
+ const productId = Number(product?.product_id ?? product?.id);
463
+ if (!Number.isFinite(productId)) return null;
464
+ return productId;
465
+ }
466
+ getPriceQuerySchedule(params) {
467
+ const booking = params.booking || {};
468
+ if (typeof booking.start_date === 'string' && booking.start_date.trim()) {
469
+ const scheduleDate = booking.start_date.trim();
470
+ const startTime = typeof booking.start_time === 'string' && booking.start_time.trim() ? booking.start_time.trim() : '00:00:00';
471
+ const datetime = (0, _dayjs.default)(`${scheduleDate} ${startTime}`);
472
+ return {
473
+ schedule_date: scheduleDate,
474
+ schedule_datetime: datetime.isValid() ? datetime.format('YYYY-MM-DD HH:mm:ss') : `${scheduleDate} ${startTime}`
475
+ };
476
+ }
477
+ if (typeof params.datetime === 'string' && params.datetime.trim()) {
478
+ const datetime = (0, _dayjs.default)(params.datetime.trim());
479
+ if (datetime.isValid()) {
480
+ return {
481
+ schedule_date: datetime.format('YYYY-MM-DD'),
482
+ schedule_datetime: datetime.format('YYYY-MM-DD HH:mm:ss')
483
+ };
484
+ }
485
+ }
486
+ const now = (0, _dayjs.default)();
487
+ return {
488
+ schedule_date: now.format('YYYY-MM-DD'),
489
+ schedule_datetime: now.format('YYYY-MM-DD HH:mm:ss')
490
+ };
491
+ }
492
+ async loadProductsForPriceQuery(params) {
493
+ const ids = Array.from(new Set(params.ids.filter(id => Number.isFinite(id))));
494
+ const productsById = new Map();
495
+ if (!ids.length || !this.request) return productsById;
496
+ try {
497
+ const response = await this.request.post('/product/query', {
498
+ ids,
499
+ open_quotation: 1,
500
+ open_bundle: 1,
501
+ status: 'published',
502
+ num: 1,
503
+ skip: 1,
504
+ customer_id: params.customerId,
505
+ schedule_date: params.schedule.schedule_date,
506
+ schedule_datetime: params.schedule.schedule_datetime,
507
+ application_code: params.channel || this.otherParams?.channel
508
+ }, {
509
+ osServer: true,
510
+ customToast: () => {}
511
+ });
512
+ const list = response?.data?.list || response?.list || [];
513
+ if (!Array.isArray(list)) return productsById;
514
+ list.forEach(item => {
515
+ const productId = Number(item?.id ?? item?.product_id);
516
+ if (Number.isFinite(productId)) productsById.set(productId, item);
517
+ });
518
+ return productsById;
519
+ } catch (error) {
520
+ this.logWarning('loadProductsForPriceQuery: 商品批量重查失败,使用入参 fallback', {
521
+ ids,
522
+ error: error instanceof Error ? error.message : String(error)
523
+ });
524
+ return productsById;
525
+ }
526
+ }
527
+ async loadProductForPriceQuery(params, customerId) {
528
+ const product = params.product || {};
529
+ const productId = this.getPriceQueryProductId(product);
530
+ if (productId === null || !this.request) return null;
531
+ const schedule = this.getPriceQuerySchedule(params);
532
+ const productsById = await this.loadProductsForPriceQuery({
533
+ ids: [productId],
534
+ schedule,
535
+ customerId,
536
+ channel: params.channel
537
+ });
538
+ return productsById.get(productId) || null;
539
+ }
540
+ getAuthoritativeBundleItems(product) {
541
+ if (Array.isArray(product?.product_bundle)) return product.product_bundle;
542
+ const groups = Array.isArray(product?.bundle_group) ? product.bundle_group : Array.isArray(product?.bundleGroup) ? product.bundleGroup : [];
543
+ return groups.flatMap(group => {
544
+ const items = Array.isArray(group?.bundle_item) ? group.bundle_item : Array.isArray(group?.bundleItem) ? group.bundleItem : [];
545
+ return items.map(item => ({
546
+ ...item,
547
+ group_id: item?.group_id ?? group?.id,
548
+ bundle_group_id: item?.bundle_group_id ?? item?.group_id ?? group?.id
549
+ }));
550
+ });
551
+ }
552
+ findAuthoritativeBundleItem(bundle, candidates) {
553
+ const bundleId = bundle.bundle_id ?? bundle.id;
554
+ if (bundleId != null) {
555
+ const matchedById = candidates.find(item => String(item?.bundle_id ?? item?.id) === String(bundleId));
556
+ if (matchedById) return matchedById;
557
+ }
558
+ const productId = bundle.bundle_product_id ?? bundle._bundle_product_id;
559
+ const groupId = bundle.bundle_group_id ?? bundle.group_id;
560
+ return candidates.find(item => {
561
+ const itemProductId = item?.bundle_product_id ?? item?._bundle_product_id;
562
+ const itemGroupId = item?.bundle_group_id ?? item?.group_id;
563
+ if (productId == null || String(itemProductId) !== String(productId)) return false;
564
+ if (groupId == null) return true;
565
+ return String(itemGroupId) === String(groupId);
566
+ }) || null;
567
+ }
568
+ getAuthoritativeBundleUnitPrice(bundle) {
569
+ return bundle.price ?? bundle.bundle_selling_price ?? bundle.custom_price ?? bundle.product_price ?? bundle.base_price;
570
+ }
571
+ mergeProductForPriceQuery(product, authoritativeProduct) {
572
+ if (!authoritativeProduct) return product;
573
+ const selectedBundles = Array.isArray(product.product_bundle) ? product.product_bundle : [];
574
+ const authoritativeBundles = this.getAuthoritativeBundleItems(authoritativeProduct);
575
+ const productBundle = selectedBundles.map(bundle => {
576
+ const authoritativeBundle = this.findAuthoritativeBundleItem(bundle, authoritativeBundles);
577
+ const unitPrice = authoritativeBundle ? this.getAuthoritativeBundleUnitPrice(authoritativeBundle) : undefined;
578
+ if (unitPrice === undefined || unitPrice === null || unitPrice === '') return bundle;
579
+ return {
580
+ ...bundle,
581
+ price: unitPrice,
582
+ bundle_selling_price: unitPrice,
583
+ base_price: authoritativeBundle?.base_price ?? bundle.base_price,
584
+ product_price: authoritativeBundle?.product_price ?? bundle.product_price
585
+ };
586
+ });
587
+ return {
588
+ ...product,
589
+ ...authoritativeProduct,
590
+ id: product.id ?? authoritativeProduct.id,
591
+ product_id: product.product_id ?? authoritativeProduct.product_id ?? authoritativeProduct.id,
592
+ product_variant_id: product.product_variant_id ?? authoritativeProduct.product_variant_id ?? 0,
593
+ num: product.num ?? product.quantity ?? authoritativeProduct.num,
594
+ quantity: product.quantity ?? product.num ?? authoritativeProduct.quantity,
595
+ product_sku: (() => {
596
+ const authoritativeSku = (0, _utils.ensureProductSku)(authoritativeProduct);
597
+ const productSku = (0, _utils.ensureProductSku)(product);
598
+ const hasProductSku = product.product_sku && typeof product.product_sku === 'object';
599
+ return {
600
+ ...authoritativeSku,
601
+ ...productSku,
602
+ option: hasProductSku ? productSku.option : authoritativeSku.option
603
+ };
604
+ })(),
605
+ product_bundle: productBundle,
606
+ metadata: product.metadata ?? authoritativeProduct.metadata
607
+ };
608
+ }
609
+ getSubmitOrderSalesChannel() {
610
+ return this.otherParams?.channel;
611
+ }
612
+
613
+ /**
614
+ * 工厂入口:根据子模块名实例化对应模块。
615
+ * 默认使用 BookingByStep 公共 `createModule`。子类若需引入额外类型(如 customer),
616
+ * 可覆盖此方法并把未识别的 name 委托给 `super.createSubModule(name)`。
617
+ */
618
+ createSubModule(moduleName) {
619
+ if (moduleName === 'quotation') {
620
+ return new _Quotation.QuotationModule(`${this.name}_quotation`);
621
+ }
622
+ return (0, _types2.createModule)(moduleName, this.name);
623
+ }
624
+ getSubModuleHooks(moduleName) {
625
+ const optionHooks = this.initializeOptions.hooks || {};
626
+ const otherHooks = this.otherParams.hooks || {};
627
+ if (moduleName === 'order') {
628
+ const directOrderHooks = optionHooks;
629
+ return optionHooks.order || this.otherParams.order?.hooks || otherHooks.order || (directOrderHooks.onBeforeMergeProductToOrder ? directOrderHooks : undefined);
630
+ }
631
+ return optionHooks[moduleName] || this.otherParams[moduleName]?.hooks || otherHooks[moduleName];
632
+ }
633
+
634
+ /**
635
+ * 构造传给 BaseSales 子模块的完整运行态参数,保证初始化和后续同步语义一致。
636
+ *
637
+ * @example
638
+ * const params = this.buildSubModuleOtherParams();
639
+ * this.core.registerModule(orderModule, { otherParams: params });
640
+ */
641
+ buildSubModuleOtherParams() {
642
+ return {
643
+ ...this.otherParams,
644
+ salesSummaryModuleName: `${this.name}_salesSummary`,
645
+ fatherModule: this.name,
646
+ openCache: this.cacheId ? true : false,
647
+ cacheId: this.cacheId
648
+ };
649
+ }
650
+
651
+ /**
652
+ * 将父级 otherParams 的变更显式同步给已注册的子模块。
653
+ *
654
+ * @example
655
+ * await this.syncOtherParamsToSubModules({ channel: 'pos' });
656
+ */
657
+ async syncOtherParamsToSubModules(changedParams, {
658
+ cover = false
659
+ } = {}) {
660
+ const nextSubModuleOtherParams = this.buildSubModuleOtherParams();
661
+ await Promise.all(Object.entries(this.store).map(async ([moduleName, subModule]) => {
662
+ if (this.reusedSharedSubModuleNames.has(moduleName)) {
663
+ return;
664
+ }
665
+ const targetModule = subModule;
666
+ if (!targetModule || typeof targetModule.updateOtherParams !== 'function') {
667
+ return;
668
+ }
669
+ await targetModule.updateOtherParams(nextSubModuleOtherParams, {
670
+ changedParams,
671
+ cover
672
+ });
673
+ }));
674
+ }
675
+
676
+ /**
677
+ * 读取 sessionStorage[this.name][cacheId],作为子模块 initialState 的来源。
678
+ * sessionStorage 损坏时静默忽略,按空状态注册。
679
+ */
680
+ readSessionCacheData() {
681
+ if (!this.cacheId || !this.window) return {};
682
+ try {
683
+ const sessionData = this.window.sessionStorage.getItem(this.name);
684
+ if (!sessionData) return {};
685
+ const data = JSON.parse(sessionData);
686
+ return data && data[this.cacheId] || {};
687
+ } catch {
688
+ return {};
689
+ }
690
+ }
691
+ getAppData(key) {
692
+ const getData = this.appPlugin.getData?.();
693
+ if (typeof getData === 'function') return getData(key);
694
+ const app = this.appPlugin.getApp();
695
+ const coreData = app?.models?.getStore?.().getDataByModel?.('core', 'core');
696
+ return coreData?.[key];
697
+ }
698
+ syncAppDataToTempOrder(tempOrder) {
699
+ const isPriceIncludeTax = this.getAppData('is_price_include_tax');
700
+ const taxCountryCode = this.getAppData('tax_country_code');
701
+ const currencyCode = this.getAppData('shop_currency_code');
702
+ const currencySymbol = this.getAppData('shop_symbol');
703
+ let isChanged = false;
704
+ if (isPriceIncludeTax !== undefined) {
705
+ const nextIsPriceIncludeTax = Number(isPriceIncludeTax);
706
+ if ((nextIsPriceIncludeTax === 0 || nextIsPriceIncludeTax === 1) && tempOrder.is_price_include_tax !== nextIsPriceIncludeTax) {
707
+ tempOrder.is_price_include_tax = nextIsPriceIncludeTax;
708
+ isChanged = true;
709
+ }
710
+ }
711
+ if (taxCountryCode !== undefined) {
712
+ const nextTaxCountryCode = String(taxCountryCode || '');
713
+ if (tempOrder.tax_country_code !== nextTaxCountryCode) {
714
+ tempOrder.tax_country_code = nextTaxCountryCode;
715
+ isChanged = true;
716
+ }
717
+ }
718
+ if (currencyCode !== undefined) {
719
+ const nextCurrencyCode = String(currencyCode || '');
720
+ if (tempOrder.currency_code !== nextCurrencyCode) {
721
+ tempOrder.currency_code = nextCurrencyCode;
722
+ isChanged = true;
723
+ }
724
+ }
725
+ if (currencySymbol !== undefined) {
726
+ const nextCurrencySymbol = String(currencySymbol || '');
727
+ if (tempOrder.currency_symbol !== nextCurrencySymbol) {
728
+ tempOrder.currency_symbol = nextCurrencySymbol;
729
+ isChanged = true;
730
+ }
731
+ }
732
+ return isChanged;
733
+ }
734
+ async initialize(core, options = {}) {
735
+ this.core = core;
736
+ this.initializeOptions = options || {};
737
+ // this.store = { ...this.store, ...(options.store as Partial<BaseSalesState>) };
738
+ this.otherParams = options.otherParams || {};
739
+ this.cacheId = this.otherParams?.cacheId;
740
+ this.reusedSharedSubModuleNames.clear();
741
+ this.window = core.getPlugin('window');
742
+ this.request = core.getPlugin('request');
743
+
744
+ // 获取 logger 实例
745
+ this.appPlugin = core.getPlugin('app');
746
+ if (!this.appPlugin) {
747
+ // throw new Error('Checkout 解决方案需要 app 插件支持');
748
+ }
749
+ const app = this.appPlugin?.getApp?.();
750
+ this.logger = app?.logger;
751
+ const targetCacheData = this.readSessionCacheData();
752
+ const moduleNames = this.getRegisteredModuleNames();
753
+ moduleNames.forEach(step => {
754
+ const reusedModule = this.getReusableSharedSubModule(step);
755
+ if (reusedModule) {
756
+ this.store[step] = reusedModule;
757
+ this.reusedSharedSubModuleNames.add(step);
758
+ return;
759
+ }
760
+ const targetModule = this.createSubModule(step);
761
+ if (!targetModule) {
762
+ throw new Error(`模块 ${step} 不存在`);
763
+ }
764
+ const hooks = this.getSubModuleHooks(step);
765
+ this.store[step] = targetModule;
766
+ this.core.registerModule(targetModule, {
767
+ initialState: targetCacheData?.[targetModule.name] || {},
768
+ ...(hooks ? {
769
+ hooks
770
+ } : {}),
771
+ otherParams: this.buildSubModuleOtherParams()
772
+ });
773
+ });
774
+ if (this.store.schedule && !this.isScheduleListLoaded(this.store.schedule)) {
775
+ await this.store.schedule.loadAllSchedule();
776
+ }
777
+ if (this.store.order && typeof this.otherParams?.enableTempOrderPersist === 'boolean') {
778
+ this.store.order.setEnableTempOrderPersist(this.otherParams.enableTempOrderPersist);
779
+ }
780
+ await this.getPaymentMethodsAsync();
781
+ this.registerServerOrderChangeSync();
782
+ this.core.effects.emit(`${this.name}:onInited`, {});
783
+ }
784
+ async destroy() {
785
+ this.serverOrderChangeUnsubscribe?.();
786
+ this.serverOrderChangeUnsubscribe = null;
787
+ Object.keys(this.store).forEach(key => {
788
+ if (this.reusedSharedSubModuleNames.has(key)) return;
789
+ const sub = this.store[key];
790
+ if (sub && typeof sub.destroy === 'function') {
791
+ try {
792
+ sub.destroy();
793
+ } catch (error) {
794
+ console.warn(`[BaseSales] 子模块 ${key} 销毁失败`, error);
795
+ }
796
+ }
797
+ });
798
+ this.currentSalesDetail = null;
799
+ this.queuedServerOrderChanges = [];
800
+ this.salesDetailLoadSequence += 1;
801
+ await this.core.effects.emit(`${this.name}:onDestroy`, {});
802
+ super.destroy();
803
+ }
804
+
805
+ /**
806
+ * 加载销售订单到统一 tempOrder 工作区。
807
+ * 统一通过 OrderModule 的 sales detail lookup 加载订单详情。
808
+ *
809
+ * 子类可在 `super.loadSalesDetail` 之后基于返回的 `sales` 做业务侧装配
810
+ * (比如把 `sales.customer` 写入 CustomerModule、emit 业务 hook 等)。
811
+ */
812
+ async loadSalesDetail(orderIdOrParams) {
813
+ if (!this.store.order) throw new Error('order 模块未初始化');
814
+ const loadSequence = ++this.salesDetailLoadSequence;
815
+ this.salesDetailLoadInFlightCount += 1;
816
+ try {
817
+ const params = typeof orderIdOrParams === 'object' ? orderIdOrParams : {
818
+ orderId: orderIdOrParams
819
+ };
820
+ const externalSaleNumber = params.externalSaleNumber || params.external_sale_number;
821
+ const preloadedSalesDetail = params.preloadedSalesDetail;
822
+ const lookup = params.orderId ?? externalSaleNumber ?? params.orderNumber ?? preloadedSalesDetail?.order_id ?? preloadedSalesDetail?.external_sale_number ?? preloadedSalesDetail?.order_number;
823
+ if (!preloadedSalesDetail && (lookup === undefined || lookup === null || lookup === '')) {
824
+ throw new Error('加载销售详情需要 orderId、externalSaleNumber 或 orderNumber');
825
+ }
826
+ // GlobalSearch 等调用方已拿到完整详情时,直接进入查询成功后的 hydrate 流程,避免按 id 二次请求。
827
+ const loadedRecord = preloadedSalesDetail ?? (await this.store.order.getSalesOrderByLookup({
828
+ lookup,
829
+ forceRemote: params.forceRemote
830
+ }));
831
+ if (!preloadedSalesDetail && (!loadedRecord || loadedRecord.code !== 200)) {
832
+ const message = loadedRecord && (loadedRecord.message || loadedRecord.msg) || `加载销售详情失败: ${lookup}`;
833
+ throw new Error(message);
834
+ }
835
+ const remoteRecord = preloadedSalesDetail ?? loadedRecord.data;
836
+ const currentTempOrder = params.merge ? this.store.order.getTempOrder() : null;
837
+ const record = params.merge ? mergeSalesDetailRecordWithTempOrder(currentTempOrder, remoteRecord, 'preserve-local') : remoteRecord;
838
+ return await this.hydrateLatestLoadedSalesDetail(record, loadSequence);
839
+ } finally {
840
+ this.salesDetailLoadInFlightCount = Math.max(0, this.salesDetailLoadInFlightCount - 1);
841
+ if (this.salesDetailLoadInFlightCount === 0) {
842
+ try {
843
+ await this.drainQueuedServerOrderChanges();
844
+ } catch (error) {
845
+ this.logError('drain queued server order changes failed', {
846
+ error: error instanceof Error ? error.message : String(error)
847
+ });
848
+ }
849
+ }
850
+ }
851
+ }
852
+
853
+ /** 获取当前已加载的销售详情(只读快照)。未加载过则返回 null。 */
854
+ getSalesOrder() {
855
+ return this.currentSalesDetail;
856
+ }
857
+
858
+ /** 获取销售详情顶部业务字段(订单号/状态/金额等通用字段集合)。 */
859
+ getOrderHeader() {
860
+ if (!this.currentSalesDetail) return null;
861
+ return this.currentSalesDetail.header || this.currentSalesDetail;
862
+ }
863
+
864
+ /** 获取所有 booking(含 root + children),按服务端原始顺序输出。 */
865
+ getBookings() {
866
+ return this.currentSalesDetail?.bookings || [];
867
+ }
868
+
869
+ /** 获取根 booking(is_next === 0 / is_root === 1),多个时取第一个。 */
870
+ getRootBooking() {
871
+ if (!this.currentSalesDetail) return null;
872
+ if (this.currentSalesDetail.rootBooking) return this.currentSalesDetail.rootBooking;
873
+ const bookings = this.currentSalesDetail.bookings || [];
874
+ return bookings.find(booking => {
875
+ const parentId = booking?.parent_id;
876
+ return parentId === 0 || parentId === null || parentId === undefined;
877
+ }) || null;
878
+ }
879
+
880
+ /** 按 product_uid 反查 booking 列表(用于商品行 ↔ booking 映射)。 */
881
+ getChildBookingsByProductUid(productUid) {
882
+ if (!this.currentSalesDetail || !productUid) return [];
883
+ const indexed = this.currentSalesDetail.bookingsByProductUid?.[productUid];
884
+ if (indexed) return indexed;
885
+ return (this.currentSalesDetail.bookings || []).filter(booking => {
886
+ const uid = booking?.product_uid || booking?.metadata?.product_uid;
887
+ return String(uid || '') === String(productUid);
888
+ });
889
+ }
890
+
891
+ /** 获取支付列表(已支付/退款等明细)。 */
892
+ getPayments() {
893
+ return this.currentSalesDetail?.payments || [];
894
+ }
895
+
896
+ /** 获取附加费用(运费/服务费/税费等)。 */
897
+ getSurcharges() {
898
+ return this.currentSalesDetail?.surcharges || [];
899
+ }
900
+ getTempOrder() {
901
+ const result = this.store.order?.getTempOrder() || null;
902
+ return result;
903
+ }
904
+ async replaceTempOrder(tempOrder) {
905
+ if (!this.store.order) throw new Error('order 模块未初始化');
906
+ const nextTempOrder = await this.store.order.replaceTempOrder(tempOrder);
907
+ await this.effectsEmit('onTempOrderReplaced', {
908
+ tempOrder: nextTempOrder
909
+ });
910
+ return nextTempOrder;
911
+ }
912
+ getOrderIdentity() {
913
+ if (!this.store.order) return null;
914
+ return this.store.order.getOrderIdentity();
915
+ }
916
+ getOrderSnapshot() {
917
+ if (!this.store.order) return null;
918
+ return this.store.order.getOrderSnapshot();
919
+ }
920
+ getOrderSyncState() {
921
+ if (!this.store.order) return 'local';
922
+ return this.store.order.getOrderSyncState();
923
+ }
924
+ getOrderAmountSnapshot() {
925
+ if (!this.store.order) return null;
926
+ return this.store.order.getOrderAmountSnapshot();
927
+ }
928
+ getTempOrderNote() {
929
+ if (!this.store.order) return '';
930
+ return this.store.order.getTempOrderNote();
931
+ }
932
+ updateTempOrderNote(note, sync = false) {
933
+ try {
934
+ if (!this.store.order) throw new Error('order 模块未初始化');
935
+ const result = this.store.order.updateTempOrderNote(note, sync);
936
+ return result;
937
+ } catch (error) {
938
+ throw error;
939
+ }
940
+ }
941
+ updateRemoteOrderNote(orderId, note) {
942
+ if (!this.store.order) throw new Error('order 模块未初始化');
943
+ this.store.order.syncTempOrderNoteToRemote(orderId, note);
944
+ }
945
+ updateTempOrderShopDiscount(amount) {
946
+ if (!this.store.order) throw new Error('order 模块未初始化');
947
+ return this.store.order.updateTempOrderShopDiscount(amount);
948
+ }
949
+ updateTempOrderDepositAmount(amount) {
950
+ if (!this.store.order) throw new Error('order 模块未初始化');
951
+ return this.store.order.updateTempOrderDepositAmount(amount);
952
+ }
953
+ updateTempOrderContactsInfo(contactsInfo) {
954
+ if (!this.store.order) throw new Error('order 模块未初始化');
955
+ return this.store.order.updateTempOrderContactsInfo(contactsInfo);
956
+ }
957
+
958
+ /** @deprecated OrderModule 不再负责 tempOrder localStorage 持久化。 */
959
+ setEnableTempOrderPersist(enabled) {
960
+ if (!this.store.order) throw new Error('order 模块未初始化');
961
+ this.store.order.setEnableTempOrderPersist(enabled);
962
+ }
963
+
964
+ /** 是否允许 saveDraft 写入 IndexedDB 草稿。 */
965
+ isTempOrderPersistEnabled() {
966
+ if (!this.store.order) return false;
967
+ return this.store.order.isTempOrderPersistEnabled();
968
+ }
969
+ ensureTempOrder() {
970
+ if (!this.store.order) throw new Error('order 模块未初始化');
971
+ return this.store.order.ensureTempOrder();
972
+ }
973
+
974
+ // 主动创建临时订单;若已存在则直接返回已有订单
975
+ async addNewOrder() {
976
+ try {
977
+ if (!this.store.order) throw new Error('order 模块未初始化');
978
+ const tempOrder = await this.store.order.addNewOrder();
979
+ if (this.syncAppDataToTempOrder(tempOrder) && this.isTempOrderPersistEnabled()) {
980
+ this.store.order.persistTempOrder();
981
+ await this.store.order.saveDraft();
982
+ }
983
+ return tempOrder;
984
+ } catch (error) {
985
+ throw error;
986
+ }
987
+ }
988
+ async saveDraft() {
989
+ if (!this.store.order) throw new Error('order 模块未初始化');
990
+ return this.store.order.saveDraft();
991
+ }
992
+
993
+ // 重置 tempOrder 与与上一单强相关的运行态(resource/entryPaxNumber/enabledReservationRuleProducts),
994
+ // 适用于从支付页通过 react-router 跳回继续下单时清理残留数据
995
+ async restoreOrder() {
996
+ try {
997
+ if (!this.store.order) {
998
+ throw new Error('scanOrder 解决方案需要 order 模块支持');
999
+ }
1000
+ // this.store.resource = null;
1001
+ const tempOrder = this.store.order.restoreOrder();
1002
+ this.currentSalesDetail = null;
1003
+ this.discountConfigCacheKey = null;
1004
+ this.hasManualDiscountSelection = false;
1005
+ await this.effectsEmit('onTempOrderReplaced', {
1006
+ tempOrder
1007
+ });
1008
+ if (this.syncAppDataToTempOrder(tempOrder) && this.isTempOrderPersistEnabled()) {
1009
+ this.store.order.persistTempOrder();
1010
+ await this.store.order.saveDraft();
1011
+ }
1012
+ return tempOrder;
1013
+ } catch (error) {
1014
+ throw error;
1015
+ }
1016
+ }
1017
+
1018
+ /**
1019
+ * 仅清空 tempOrder 购物车行(products / bookings / discount_list)。
1020
+ * 同步清空内存中的 salesDetail 商品/预约视图,避免编辑态仍读到旧 bookings。
1021
+ */
1022
+ async clearOrderCartLines() {
1023
+ try {
1024
+ if (!this.store.order) throw new Error('order 模块未初始化');
1025
+ const tempOrder = await this.store.order.clearOrderCartLines();
1026
+ if (this.currentSalesDetail) {
1027
+ this.currentSalesDetail = {
1028
+ ...this.currentSalesDetail,
1029
+ products: [],
1030
+ bookings: [],
1031
+ rootBooking: null,
1032
+ bookingsByProductUid: {},
1033
+ discount_list: []
1034
+ };
1035
+ }
1036
+ await this.effectsEmit('onTempOrderReplaced', {
1037
+ tempOrder
1038
+ });
1039
+ if (this.syncAppDataToTempOrder(tempOrder) && this.isTempOrderPersistEnabled()) {
1040
+ this.store.order.persistTempOrder();
1041
+ await this.store.order.saveDraft();
1042
+ }
1043
+ return tempOrder;
1044
+ } catch (error) {
1045
+ throw error;
1046
+ }
1047
+ }
1048
+ getOrderProducts() {
1049
+ if (!this.store.order) throw new Error('order 模块未初始化');
1050
+ const products = this.store.order.getOrderProducts();
1051
+ return products;
1052
+ }
1053
+ async getSummary() {
1054
+ try {
1055
+ if (!this.store.order) throw new Error('order 模块未初始化');
1056
+ const summary = await this.store.order.getOrderSummary();
1057
+ return summary;
1058
+ } catch (error) {
1059
+ throw error;
1060
+ }
1061
+ }
1062
+ getHistoricalProductDiscountList(products) {
1063
+ const historyDiscountList = [];
1064
+ products.forEach(item => {
1065
+ if (!this.isPersistedOrderProduct(item)) return;
1066
+ (item.discount_list || []).forEach(discount => {
1067
+ const discountId = discount.discount?.resource_id || discount.id;
1068
+ if (!discountId || !['good_pass', 'discount_card'].includes(discount.type)) return;
1069
+ const quantity = item.quantity || item.num || item.product_quantity || 1;
1070
+ const savedAmount = new _decimal.default(discount.amount || 0).times(quantity).plus(discount?.metadata?.product_discount_difference || 0);
1071
+ const index = historyDiscountList.findIndex(n => n.id === discountId);
1072
+ if (index !== -1) {
1073
+ historyDiscountList[index] = {
1074
+ ...historyDiscountList[index],
1075
+ amount: new _decimal.default(historyDiscountList[index].amount || 0).plus(discount.amount || 0).toNumber(),
1076
+ savedAmount: savedAmount.plus(historyDiscountList[index].savedAmount || 0).toNumber()
1077
+ };
1078
+ return;
1079
+ }
1080
+ historyDiscountList.push({
1081
+ ...discount,
1082
+ id: discountId,
1083
+ tag: discount.tag || discount.type,
1084
+ name: discount.name || discount.discount?.title?.auto,
1085
+ format_title: discount.discount?.title || discount.format_title,
1086
+ isEditMode: true,
1087
+ limited_relation_product_data: {},
1088
+ savedAmount: savedAmount.toNumber(),
1089
+ isAvailable: true,
1090
+ isDisabled: true,
1091
+ isSelected: true,
1092
+ product_id: discount.discount?.product_id || discount.product_id
1093
+ });
1094
+ });
1095
+ });
1096
+ return historyDiscountList;
1097
+ }
1098
+ isPersistedOrderProduct(product) {
1099
+ const orderDetailId = product.order_detail_id ?? product.orderDetailId;
1100
+ if (orderDetailId !== undefined && orderDetailId !== null && orderDetailId !== '') {
1101
+ return true;
1102
+ }
1103
+ const bookingId = product.booking_id ?? product.bookingId;
1104
+ return bookingId !== undefined && bookingId !== null && bookingId !== '';
1105
+ }
1106
+ mergeHistoricalDiscountList(discountList, products) {
1107
+ const historyDiscountList = this.getHistoricalProductDiscountList(products);
1108
+ const historyIds = new Set(historyDiscountList.map(discount => discount.id));
1109
+ return [...historyDiscountList, ...(discountList || []).filter(discount => {
1110
+ if (!historyIds.has(discount.id)) return true;
1111
+ return !isBaseSalesHistoricalDiscountEntry(discount);
1112
+ })];
1113
+ }
1114
+ shouldSkipAutoApplyFetchedDiscountsInEditMode(params) {
1115
+ if (params.discountAction !== 'edit') return false;
1116
+ if (!params.products.length) return false;
1117
+ const hasDraftProduct = params.products.some(product => !this.isPersistedOrderProduct(product));
1118
+ if (hasDraftProduct) return false;
1119
+ const hasSelectedDiscount = params.currentDiscountList.some(discount => discount.isSelected || discount.isEditMode || discount.isManualSelect);
1120
+ if (hasSelectedDiscount) return false;
1121
+ return !params.nextDiscountList.some(discount => discount.isSelected || discount.isEditMode || discount.isManualSelect);
1122
+ }
1123
+ mergeCurrentDiscountSelectionState(discountList, currentDiscountList) {
1124
+ if (!currentDiscountList.length) return discountList;
1125
+ const currentDiscountMap = new Map();
1126
+ currentDiscountList.forEach(discount => {
1127
+ currentDiscountMap.set(`${discount.id}:${isBaseSalesHistoricalDiscountEntry(discount) ? 'historical' : 'runtime'}`, discount);
1128
+ });
1129
+ return discountList.map(discount => {
1130
+ const currentDiscount = currentDiscountMap.get(`${discount.id}:${isBaseSalesHistoricalDiscountEntry(discount) ? 'historical' : 'runtime'}`);
1131
+ if (!currentDiscount) return discount;
1132
+ if (currentDiscount.isManualSelect) {
1133
+ return {
1134
+ ...discount,
1135
+ isSelected: false,
1136
+ isManualSelect: true
1137
+ };
1138
+ }
1139
+ if (currentDiscount.isSelected) {
1140
+ return {
1141
+ ...discount,
1142
+ isSelected: true,
1143
+ isManualSelect: false
1144
+ };
1145
+ }
1146
+ return discount;
1147
+ });
1148
+ }
1149
+ async loadDiscountConfig(params) {
1150
+ if (!this.store.order) throw new Error('order 模块未初始化');
1151
+ const tempOrder = this.store.order.ensureTempOrder();
1152
+ const orderStore = this.store.order.store || {};
1153
+ const discountModule = orderStore.discount;
1154
+ const rulesModule = orderStore.rules;
1155
+ const orderId = tempOrder.order_id || this.getOrderIdentity?.()?.orderId;
1156
+ const customerId = Number(params?.customerId || tempOrder.customer_id || tempOrder.customer?.id || tempOrder._extend?.customerSnapshot?.id || tempOrder._extend?.customerSnapshot?.customer_id || 0);
1157
+ const currentDiscountList = discountModule?.getDiscountList?.() || [];
1158
+ const originalDiscountList = discountModule?.getOriginalDiscountList?.() || [];
1159
+ const hasManualDiscountSelection = this.hasManualDiscountSelection || currentDiscountList.some(discount => discount.isManualSelect);
1160
+ const discountAction = params?.action || (orderId ? 'edit' : 'create');
1161
+ const discountConfigCacheKey = [customerId, discountAction, Number(orderId) || 0].join(':');
1162
+ let preparedDiscountList = [];
1163
+ if (customerId && customerId !== 1) {
1164
+ if (this.discountConfigCacheKey === discountConfigCacheKey) {
1165
+ preparedDiscountList = originalDiscountList;
1166
+ } else {
1167
+ preparedDiscountList = await this.store.order.loadDiscountConfig({
1168
+ customerId,
1169
+ action: discountAction,
1170
+ orderId: Number(orderId) || undefined,
1171
+ apply: false
1172
+ });
1173
+ this.discountConfigCacheKey = discountConfigCacheKey;
1174
+ await discountModule?.setOriginalDiscountList?.(preparedDiscountList);
1175
+ }
1176
+ }
1177
+ if (hasManualDiscountSelection && currentDiscountList.length) {
1178
+ return {
1179
+ productList: tempOrder.products || [],
1180
+ discountList: currentDiscountList,
1181
+ availableWalletIds: this.getAvailableWalletIds()
1182
+ };
1183
+ }
1184
+ let nextDiscountList = this.mergeHistoricalDiscountList(preparedDiscountList || discountModule?.getDiscountList?.() || [], tempOrder.products || []);
1185
+ nextDiscountList = this.mergeCurrentDiscountSelectionState(nextDiscountList, currentDiscountList);
1186
+ await discountModule?.setDiscountList(nextDiscountList);
1187
+ const shouldSkipAutoApplyFetchedDiscounts = this.shouldSkipAutoApplyFetchedDiscountsInEditMode({
1188
+ discountAction,
1189
+ products: tempOrder.products || [],
1190
+ currentDiscountList,
1191
+ nextDiscountList
1192
+ });
1193
+ if (shouldSkipAutoApplyFetchedDiscounts) {
1194
+ const summary = await this.store.order.recalculateSummary({
1195
+ createIfMissing: true
1196
+ });
1197
+ this.store.order.persistTempOrder();
1198
+ await this.effectsEmit('onDiscountApplied', {
1199
+ productList: tempOrder.products || [],
1200
+ discountList: nextDiscountList,
1201
+ selectedDiscountList: tempOrder.discount_list || [],
1202
+ tempOrder
1203
+ });
1204
+ return {
1205
+ productList: tempOrder.products || [],
1206
+ discountList: nextDiscountList,
1207
+ availableWalletIds: this.getAvailableWalletIds()
1208
+ };
1209
+ }
1210
+ if (rulesModule) {
1211
+ const orderTotalAmount = Number(orderStore.summary?.total_amount || 0);
1212
+ const result = rulesModule.calcDiscount({
1213
+ productList: tempOrder.products,
1214
+ discountList: nextDiscountList,
1215
+ holders: [],
1216
+ isFormSubject: product => (0, _utils.resolveIsFormSubject)(tempOrder, product),
1217
+ orderTotalAmount
1218
+ }) || {
1219
+ productList: tempOrder.products,
1220
+ discountList: nextDiscountList
1221
+ };
1222
+ if (result.productList) {
1223
+ tempOrder.products = preserveManualProductDiscountProducts(tempOrder.products, result.productList);
1224
+ }
1225
+ for (const product of tempOrder.products || []) {
1226
+ const productUid = product.metadata?.unique_identification_number;
1227
+ const runtimeOrigin = productUid ? tempOrder._extend?.productsByUid?.[productUid]?.origin : undefined;
1228
+ if (runtimeOrigin?.isManualDiscount || isBaseSalesManualProductDiscountProduct(product)) continue;
1229
+ const totalPerUnitDiscount = (product.discount_list || []).reduce((sum, pd) => sum + Number(pd.amount || 0), 0);
1230
+ const optionSum = (0, _utils.sumOptionUnitPrice)((0, _utils.getProductSkuOptions)(product));
1231
+ const sourcePrice = product.metadata?.source_product_price ?? (product.metadata?.main_product_original_price != null ? new _decimal.default(Number(product.metadata.main_product_original_price) || 0).minus(optionSum).toFixed(2) : product.original_price ?? '0');
1232
+ const newSourceSellingPrice = new _decimal.default(Number(sourcePrice) || 0).minus(totalPerUnitDiscount).toDecimalPlaces(2).toString();
1233
+ const newMainSellingPrice = new _decimal.default(Number(newSourceSellingPrice) || 0).plus(optionSum).toDecimalPlaces(2).toFixed(2);
1234
+ if (product.metadata) {
1235
+ product.metadata.main_product_selling_price = newMainSellingPrice;
1236
+ product.metadata.price_schema_version = 2;
1237
+ }
1238
+ product.selling_price = (0, _utils.composeLinePrice)({
1239
+ mainPrice: newMainSellingPrice,
1240
+ bundle: product.product_bundle
1241
+ });
1242
+ }
1243
+ if (result.discountList) {
1244
+ nextDiscountList = this.mergeHistoricalDiscountList(result.discountList, tempOrder.products || []);
1245
+ _Order.OrderModule.populateSavedAmounts(tempOrder.products, nextDiscountList);
1246
+ await discountModule?.setDiscountList(nextDiscountList);
1247
+ tempOrder.discount_list = nextDiscountList.filter(discount => discount.isSelected);
1248
+ }
1249
+ }
1250
+ const summary = await this.store.order.recalculateSummary({
1251
+ createIfMissing: true
1252
+ });
1253
+ this.store.order.persistTempOrder();
1254
+ await this.effectsEmit('onDiscountApplied', {
1255
+ productList: tempOrder.products || [],
1256
+ discountList: nextDiscountList,
1257
+ selectedDiscountList: tempOrder.discount_list || [],
1258
+ tempOrder
1259
+ });
1260
+ return {
1261
+ productList: tempOrder.products || [],
1262
+ discountList: nextDiscountList,
1263
+ availableWalletIds: this.getAvailableWalletIds()
1264
+ };
1265
+ }
1266
+ getAvailableWalletIds() {
1267
+ if (!this.store.order?.getAvailableWalletIds) return [];
1268
+ return this.store.order.getAvailableWalletIds();
1269
+ }
1270
+ async bestDiscount(cb) {
1271
+ if (!this.store.order) throw new Error('order 模块未初始化');
1272
+ const tempOrder = this.store.order.ensureTempOrder();
1273
+ const orderStore = this.store.order.store || {};
1274
+ const discountModule = orderStore.discount;
1275
+ const rulesModule = orderStore.rules;
1276
+ const originalDiscountList = discountModule?.getOriginalDiscountList?.() || this.getDiscountList();
1277
+ const currentDiscountList = discountModule?.getDiscountList?.() || [];
1278
+ let nextDiscountList = this.mergeHistoricalDiscountList(originalDiscountList || [], tempOrder.products || []);
1279
+ let result = {
1280
+ productList: tempOrder.products || [],
1281
+ discountList: nextDiscountList
1282
+ };
1283
+ await discountModule?.setDiscountList(nextDiscountList);
1284
+ if (rulesModule) {
1285
+ const orderTotalAmount = Number(orderStore.summary?.total_amount || 0);
1286
+ result = rulesModule.calcDiscount({
1287
+ productList: tempOrder.products,
1288
+ discountList: nextDiscountList,
1289
+ holders: [],
1290
+ isFormSubject: product => (0, _utils.resolveIsFormSubject)(tempOrder, product),
1291
+ orderTotalAmount
1292
+ }) || result;
1293
+ if (result.productList) {
1294
+ tempOrder.products = preserveManualProductDiscountProducts(tempOrder.products, result.productList);
1295
+ }
1296
+ if (result.discountList) {
1297
+ nextDiscountList = this.mergeHistoricalDiscountList(result.discountList, tempOrder.products || []);
1298
+ _Order.OrderModule.populateSavedAmounts(tempOrder.products, nextDiscountList);
1299
+ await discountModule?.setDiscountList(nextDiscountList);
1300
+ tempOrder.discount_list = nextDiscountList.filter(discount => discount.isSelected);
1301
+ result = {
1302
+ productList: tempOrder.products,
1303
+ discountList: nextDiscountList
1304
+ };
1305
+ }
1306
+ }
1307
+ await this.store.order.recalculateSummary({
1308
+ createIfMissing: true
1309
+ });
1310
+ this.store.order.persistTempOrder();
1311
+ await this.effectsEmit('onDiscountApplied', {
1312
+ productList: tempOrder.products || [],
1313
+ discountList: nextDiscountList,
1314
+ selectedDiscountList: tempOrder.discount_list || [],
1315
+ tempOrder
1316
+ });
1317
+ cb?.(result);
1318
+ return result;
1319
+ }
1320
+ getDiscountList() {
1321
+ if (!this.store.order) return [];
1322
+ return this.store.order.getDiscountList();
1323
+ }
1324
+
1325
+ /** 读取 Discount 模块原始券列表(未经过 bookingTime 等过滤)。 */
1326
+ getOriginalDiscountList() {
1327
+ if (!this.store.order) return [];
1328
+ const discountModule = this.store.order.store?.discount;
1329
+ return discountModule?.getOriginalDiscountList?.() || [];
1330
+ }
1331
+
1332
+ /**
1333
+ * 同步写入 Discount 模块的 discountList / originalDiscountList。
1334
+ * 供 Holder 补全等场景在 OS store 层更新 machinecode holder 字段。
1335
+ */
1336
+ async replaceDiscountStoreLists(params) {
1337
+ if (!this.store.order) throw new Error('order 模块未初始化');
1338
+ const discountModule = this.store.order.store?.discount;
1339
+ if (!discountModule) return;
1340
+ if (params.originalDiscountList) {
1341
+ await discountModule.setOriginalDiscountList(params.originalDiscountList);
1342
+ }
1343
+ await discountModule.setDiscountList(params.discountList);
1344
+ }
1345
+ async scanPromotionCode(code, customerId) {
1346
+ try {
1347
+ if (!this.store.order) throw new Error('order 模块未初始化');
1348
+ const raw = await this.store.order.scanCode(code, customerId);
1349
+ if (raw.isAvailable) {
1350
+ await this.store.order.recalculateSummary({
1351
+ createIfMissing: true
1352
+ });
1353
+ this.store.order.persistTempOrder();
1354
+ const tempOrder = this.store.order.ensureTempOrder();
1355
+ await this.effectsEmit('onDiscountApplied', {
1356
+ productList: tempOrder.products || [],
1357
+ discountList: this.store.order.getDiscountList(),
1358
+ selectedDiscountList: tempOrder.discount_list || [],
1359
+ tempOrder
1360
+ });
1361
+ }
1362
+ return {
1363
+ isAvailable: raw.isAvailable,
1364
+ type: raw.type,
1365
+ unavailableReason: raw.unavailableReason,
1366
+ scannedDiscountList: raw.scannedDiscountList
1367
+ };
1368
+ } catch (error) {
1369
+ throw error;
1370
+ }
1371
+ }
1372
+
1373
+ // TODO 需要优化,这段逻辑太长了,里面应该有些历史遗留问题
1374
+ async setDiscountSelected(params) {
1375
+ try {
1376
+ if (!this.store.order) throw new Error('order 模块未初始化');
1377
+ const list = this.store.order.getDiscountList();
1378
+ const updated = list.map(d => d.id === params.discountId && !isBaseSalesHistoricalDiscountEntry(d) ? {
1379
+ ...d,
1380
+ isSelected: params.isSelected,
1381
+ isManualSelect: !params.isSelected
1382
+ } : d);
1383
+ this.hasManualDiscountSelection = true;
1384
+ const tempOrder = this.store.order.ensureTempOrder();
1385
+ const orderStore = this.store.order.store || {};
1386
+ const discountModule = orderStore.discount;
1387
+ const rulesModule = orderStore.rules;
1388
+ let nextDiscountList = updated;
1389
+ await discountModule?.setDiscountList(updated);
1390
+ if (rulesModule) {
1391
+ const orderTotalAmount = Number(orderStore.summary?.total_amount || 0);
1392
+ const result = rulesModule.calcDiscount({
1393
+ productList: tempOrder.products,
1394
+ discountList: updated,
1395
+ holders: [],
1396
+ isFormSubject: product => (0, _utils.resolveIsFormSubject)(tempOrder, product),
1397
+ orderTotalAmount
1398
+ }, {
1399
+ discountId: params.discountId,
1400
+ isSelected: params.isSelected
1401
+ }) || {
1402
+ productList: tempOrder.products,
1403
+ discountList: updated
1404
+ };
1405
+ if (result?.productList) {
1406
+ tempOrder.products = preserveManualProductDiscountProducts(tempOrder.products, result.productList);
1407
+ }
1408
+ if (result?.discountList) {
1409
+ nextDiscountList = result.discountList;
1410
+ if (!params.isSelected) {
1411
+ const beforeSelectedIds = new Set(updated.filter(d => d.isSelected).map(d => d.id));
1412
+ for (const d of nextDiscountList) {
1413
+ if (d.isSelected && !beforeSelectedIds.has(d.id)) {
1414
+ d.isSelected = false;
1415
+ d.isManualSelect = true;
1416
+ d.appliedProductDetails = [];
1417
+ }
1418
+ }
1419
+ }
1420
+ }
1421
+ }
1422
+ const selectedResourceIds = new Set(nextDiscountList.filter(d => d.isSelected).map(d => d.id));
1423
+ const filterSelectedDiscountDetails = discountList => (discountList || []).filter(pd => {
1424
+ const rid = pd.discount?.resource_id ?? pd.id;
1425
+ return rid != null && selectedResourceIds.has(rid);
1426
+ });
1427
+ for (const product of tempOrder.products) {
1428
+ const productUid = product.metadata?.unique_identification_number;
1429
+ const runtimeOrigin = productUid ? tempOrder._extend?.productsByUid?.[productUid]?.origin : undefined;
1430
+ if (runtimeOrigin?.isManualDiscount || isBaseSalesManualProductDiscountProduct(product)) continue;
1431
+ product.discount_list = filterSelectedDiscountDetails(product.discount_list);
1432
+ if (Array.isArray(product.product_bundle)) {
1433
+ product.product_bundle = product.product_bundle.map(bundle => {
1434
+ const nextBundleDiscountList = filterSelectedDiscountDetails(bundle?.discount_list);
1435
+ const shouldRestoreBundlePrice = nextBundleDiscountList.length === 0;
1436
+ const restoredBundlePrice = bundle?.original_price ?? bundle?.product_price ?? bundle?.price;
1437
+ return {
1438
+ ...bundle,
1439
+ ...(shouldRestoreBundlePrice ? {
1440
+ price: restoredBundlePrice,
1441
+ bundle_selling_price: restoredBundlePrice
1442
+ } : {}),
1443
+ discount_list: nextBundleDiscountList
1444
+ };
1445
+ });
1446
+ }
1447
+
1448
+ // 券作用于 source(不含 option、不含 bundle);算出新 source 折后价后补回 options
1449
+ // 写入含 option 的 main_product_selling_price,再合成 composite 回写 selling_price。
1450
+ const totalPerUnitDiscount = (product.discount_list || []).reduce((sum, pd) => sum + (pd.amount || 0), 0);
1451
+ // TODO 这块逻辑需要拿掉
1452
+ const optionSum = (0, _utils.sumOptionUnitPrice)((0, _utils.getProductSkuOptions)(product));
1453
+ const sourcePrice = product.metadata?.source_product_price ?? (product.metadata?.main_product_original_price != null ? new _decimal.default(Number(product.metadata.main_product_original_price) || 0).minus(optionSum).toFixed(2) : product.original_price ?? '0');
1454
+ const newSourceSellingPrice = new _decimal.default(Number(sourcePrice) || 0).minus(totalPerUnitDiscount).toDecimalPlaces(2).toString();
1455
+ const newMainSellingPrice = new _decimal.default(Number(newSourceSellingPrice) || 0).plus(optionSum).toDecimalPlaces(2).toFixed(2);
1456
+ if (product.metadata) {
1457
+ product.metadata.main_product_selling_price = newMainSellingPrice;
1458
+ product.metadata.price_schema_version = 2;
1459
+ }
1460
+ product.selling_price = (0, _utils.composeLinePrice)({
1461
+ mainPrice: newMainSellingPrice,
1462
+ bundle: product.product_bundle
1463
+ });
1464
+ }
1465
+ _Order.OrderModule.populateSavedAmounts(tempOrder.products, nextDiscountList);
1466
+ await discountModule?.setDiscountList(nextDiscountList);
1467
+ tempOrder.discount_list = (nextDiscountList || []).filter(d => d.isSelected);
1468
+ await this.store.order.recalculateSummary({
1469
+ createIfMissing: true
1470
+ });
1471
+ this.store.order.persistTempOrder();
1472
+ this.effectsEmit('onDiscountApplied', {
1473
+ productList: tempOrder.products,
1474
+ discountList: nextDiscountList,
1475
+ selectedDiscountList: tempOrder.discount_list,
1476
+ tempOrder
1477
+ });
1478
+ } catch (error) {
1479
+ throw error;
1480
+ }
1481
+ }
1482
+ async applyDiscountCalculationResult(result) {
1483
+ try {
1484
+ if (!this.store.order) throw new Error('order 模块未初始化');
1485
+ if (!result) return;
1486
+ const tempOrder = this.store.order.ensureTempOrder();
1487
+ const orderStore = this.store.order.store || {};
1488
+ const discountModule = orderStore.discount;
1489
+ if (result.productList) {
1490
+ tempOrder.products = preserveManualProductDiscountProducts(tempOrder.products, result.productList);
1491
+ }
1492
+ if (result.discountList) {
1493
+ _Order.OrderModule.populateSavedAmounts(tempOrder.products, result.discountList);
1494
+ await discountModule?.setDiscountList(result.discountList);
1495
+ tempOrder.discount_list = result.discountList.filter(discount => discount.isSelected);
1496
+ }
1497
+ await this.store.order.recalculateSummary({
1498
+ createIfMissing: true
1499
+ });
1500
+ this.store.order.persistTempOrder();
1501
+ await this.effectsEmit('onDiscountApplied', {
1502
+ productList: tempOrder.products || [],
1503
+ discountList: result.discountList || [],
1504
+ selectedDiscountList: tempOrder.discount_list || [],
1505
+ tempOrder
1506
+ });
1507
+ } catch (error) {
1508
+ throw error;
1509
+ }
1510
+ }
1511
+ async submitScanOrder(params) {
1512
+ // Deprecated compatibility alias. New BaseSales callers should use submitSalesOrder.
1513
+ return this.submitSalesOrder(params);
1514
+ }
1515
+
1516
+ /**
1517
+ * 提交当前 Sales 订单。
1518
+ *
1519
+ * PaymentModal 场景通过 smallTicketDataFlag=1 请求 B 端小票数据;
1520
+ * payments 会交给 OrderModule 统一映射为 Sales 协议支付项。
1521
+ */
1522
+ async submitSalesOrder(params) {
1523
+ if (!this.store.order) {
1524
+ throw new Error('BaseSales 解决方案需要 order 模块支持');
1525
+ }
1526
+ // this.store.order.persistTempOrder();
1527
+ const inferredPaymentStatus = this.getSubmitPaymentStatus(params?.paymentStatus);
1528
+ const smallTicketDataFlag = params?.smallTicketDataFlag ?? this.getDefaultCheckoutSmallTicketDataFlag();
1529
+ const submitParams = {
1530
+ cacheId: this.cacheId,
1531
+ platform: this.otherParams?.platform,
1532
+ businessCode: this.otherParams?.businessCode || this.otherParams?.business_code,
1533
+ channel: this.getSubmitOrderSalesChannel(),
1534
+ type: this.otherParams?.type,
1535
+ request_unique_idempotency_token: this.store.order.generateIdempotencyToken(),
1536
+ ...(params?.payments !== undefined ? {
1537
+ payments: params.payments
1538
+ } : {}),
1539
+ ...(inferredPaymentStatus !== undefined ? {
1540
+ paymentStatus: inferredPaymentStatus
1541
+ } : {}),
1542
+ ...(smallTicketDataFlag !== undefined ? {
1543
+ smallTicketDataFlag
1544
+ } : {}),
1545
+ ...(params?.confirmPendingVoucherPayments !== undefined ? {
1546
+ confirmPendingVoucherPayments: params.confirmPendingVoucherPayments
1547
+ } : {}),
1548
+ ...(params?.enhancePayload !== undefined ? {
1549
+ enhancePayload: params.enhancePayload
1550
+ } : {})
1551
+ };
1552
+ return this.shouldUseCheckoutSyncTask() ? this.store.order.submitTempOrder(submitParams) : this.store.order.submitTempOrderAsync(submitParams);
1553
+ }
1554
+ async submitTempOrderAsync(params) {
1555
+ if (!this.store.order) {
1556
+ throw new Error('BaseSales 解决方案需要 order 模块支持');
1557
+ }
1558
+ const inferredPaymentStatus = this.getSubmitPaymentStatus(params?.paymentStatus);
1559
+ const smallTicketDataFlag = params?.smallTicketDataFlag ?? this.getDefaultCheckoutSmallTicketDataFlag();
1560
+ const submitParams = {
1561
+ cacheId: this.cacheId,
1562
+ platform: this.otherParams?.platform,
1563
+ businessCode: this.otherParams?.businessCode || this.otherParams?.business_code,
1564
+ channel: this.getSubmitOrderSalesChannel(),
1565
+ type: this.otherParams?.type,
1566
+ request_unique_idempotency_token: this.store.order.generateIdempotencyToken(),
1567
+ ...(params?.payments !== undefined ? {
1568
+ payments: params.payments
1569
+ } : {}),
1570
+ ...(inferredPaymentStatus !== undefined ? {
1571
+ paymentStatus: inferredPaymentStatus
1572
+ } : {}),
1573
+ ...(smallTicketDataFlag !== undefined ? {
1574
+ smallTicketDataFlag
1575
+ } : {}),
1576
+ ...(params?.confirmPendingVoucherPayments !== undefined ? {
1577
+ confirmPendingVoucherPayments: params.confirmPendingVoucherPayments
1578
+ } : {}),
1579
+ ...(params?.enhancePayload !== undefined ? {
1580
+ enhancePayload: params.enhancePayload
1581
+ } : {})
1582
+ };
1583
+ return this.store.order.submitTempOrderAsync(submitParams);
1584
+ }
1585
+
1586
+ /**
1587
+ * 默认保持 WebPOS 的本地待同步订单模式;需要云端确认后才算成功的渠道可覆盖此策略。
1588
+ * otherParams.checkoutSyncTaskEnabled 优先,便于同一 solution 按入口恢复旧行为。
1589
+ */
1590
+ shouldUseCheckoutSyncTask() {
1591
+ const configured = this.otherParams?.checkoutSyncTaskEnabled;
1592
+ return typeof configured === 'boolean' ? configured : true;
1593
+ }
1594
+
1595
+ /** 可由特定 solution 为真实 checkout 补充默认收据数据请求。 */
1596
+ getDefaultCheckoutSmallTicketDataFlag() {
1597
+ return undefined;
1598
+ }
1599
+ async printLocalOrderReceipt(lookup) {
1600
+ if (!this.store.order) {
1601
+ throw new Error('BaseSales 解决方案需要 order 模块支持');
1602
+ }
1603
+ const hasLookup = lookup !== undefined && lookup !== null && lookup !== '';
1604
+ const lookupPayload = typeof lookup === 'number' ? {
1605
+ order_id: lookup
1606
+ } : {
1607
+ external_sale_number: lookup
1608
+ };
1609
+ const context = {
1610
+ platform: this.otherParams?.platform,
1611
+ businessCode: this.otherParams?.businessCode || this.otherParams?.business_code,
1612
+ channel: this.getSubmitOrderSalesChannel(),
1613
+ type: this.otherParams?.type
1614
+ };
1615
+ const payload = hasLookup ? {
1616
+ ...lookupPayload,
1617
+ lookup_order_from_db: 1,
1618
+ small_ticket_data_flag: 1,
1619
+ platform: context.platform,
1620
+ business_code: context.businessCode,
1621
+ order_sales_channel: context.channel,
1622
+ type: context.type
1623
+ } : this.store.order.buildCurrentSubmitPayloadForLocalPrint({
1624
+ cacheId: this.cacheId,
1625
+ platform: context.platform,
1626
+ businessCode: context.businessCode,
1627
+ channel: context.channel,
1628
+ type: context.type
1629
+ });
1630
+ const response = await this.request.post('/local/print-order', payload, {
1631
+ osServer: true,
1632
+ customToast: () => {}
1633
+ });
1634
+ if (!hasLookup) {
1635
+ await this.store.order.applyLocalPrintOrderNumbers?.(response?.data?.order);
1636
+ }
1637
+ return response;
1638
+ }
1639
+ getSubmitPaymentStatus(paymentStatus) {
1640
+ if (paymentStatus !== undefined) return paymentStatus;
1641
+ const amountSnapshot = this.store.order?.getOrderAmountSnapshot?.();
1642
+ if (!amountSnapshot) return undefined;
1643
+ try {
1644
+ return new _decimal.default(amountSnapshot.amountGap || 0).lte(0) ? 'paid' : undefined;
1645
+ } catch {
1646
+ return undefined;
1647
+ }
1648
+ }
1649
+ async syncPaymentsToOrder(params) {
1650
+ if (!this.store.order) throw new Error('order 模块未初始化');
1651
+ const businessCode = params.businessCode ?? this.otherParams?.businessCode ?? this.otherParams?.business_code;
1652
+ const channel = params.channel ?? this.getSubmitOrderSalesChannel();
1653
+ const syncParams = {
1654
+ ...params,
1655
+ checkoutSyncTaskEnabled: this.shouldUseCheckoutSyncTask(),
1656
+ ...(businessCode !== undefined ? {
1657
+ businessCode
1658
+ } : {}),
1659
+ ...(channel !== undefined ? {
1660
+ channel
1661
+ } : {})
1662
+ };
1663
+ const syncState = this.store.order.getOrderSyncState();
1664
+ if (params.submitWhenPaid && syncState === 'submitting') {
1665
+ this.queueLatestAutoPaymentSync();
1666
+ return this.store.order.syncPaymentsToOrder(syncParams);
1667
+ }
1668
+ const payments = params.payments || [];
1669
+ await this.effectsEmit(_types.BaseSalesHookNames.onPaymentSyncStart, {
1670
+ payments,
1671
+ params: syncParams,
1672
+ amountSnapshot: this.store.order.getOrderAmountSnapshot(),
1673
+ orderSnapshot: this.store.order.getOrderSnapshot()
1674
+ });
1675
+ try {
1676
+ const syncResult = await this.store.order.syncPaymentsToOrder(syncParams);
1677
+ const latestPayments = this.store.order.getOrderPayments();
1678
+ const amountSnapshot = this.store.order.getOrderAmountSnapshot();
1679
+ const orderSnapshot = this.store.order.getOrderSnapshot();
1680
+ const syncPayload = {
1681
+ ok: true,
1682
+ syncResult,
1683
+ payments: latestPayments,
1684
+ params: syncParams,
1685
+ amountSnapshot,
1686
+ orderSnapshot,
1687
+ isFullyPaid: syncResult.isFullyPaid,
1688
+ isOrderFullyPaid: syncResult.isOrderFullyPaid,
1689
+ isDepositFullyPaid: syncResult.isDepositFullyPaid,
1690
+ paymentStage: syncResult.paymentStage,
1691
+ depositAmount: syncResult.depositAmount,
1692
+ orderExpectedAmount: syncResult.orderExpectedAmount
1693
+ };
1694
+ await this.effectsEmit(_types.BaseSalesHookNames.onPaymentSyncEnd, syncPayload);
1695
+ if (syncResult.isDepositFullyPaid && !syncResult.isOrderFullyPaid) {
1696
+ await this.effectsEmit(_types.BaseSalesHookNames.onDepositPaymentSyncEnd, syncPayload);
1697
+ }
1698
+ this.scheduleQueuedAutoPaymentSyncFlush(0);
1699
+ return syncResult;
1700
+ } catch (error) {
1701
+ await this.effectsEmit(_types.BaseSalesHookNames.onPaymentSyncEnd, {
1702
+ ok: false,
1703
+ error,
1704
+ payments: this.store.order.getOrderPayments(),
1705
+ params: syncParams,
1706
+ amountSnapshot: this.store.order.getOrderAmountSnapshot(),
1707
+ orderSnapshot: this.store.order.getOrderSnapshot()
1708
+ });
1709
+ this.scheduleQueuedAutoPaymentSyncFlush(0);
1710
+ throw error;
1711
+ }
1712
+ }
1713
+
1714
+ /** 读取当前 BaseSales 订单上下文中的 Sales 协议支付项。 */
1715
+ getOrderPayments() {
1716
+ if (!this.store.order) return [];
1717
+ const payments = this.store.order.getOrderPayments();
1718
+ return this.hydrateOrderPaymentNames(payments);
1719
+ }
1720
+
1721
+ /** 覆盖当前订单支付项,内部由 OrderModule 清洗为 Sales 协议字段。 */
1722
+ setOrderPayments(payments) {
1723
+ if (!this.store.order) throw new Error('order 模块未初始化');
1724
+ return this.store.order.setOrderPayments(payments);
1725
+ }
1726
+ getPaymentStatusForSync(payments) {
1727
+ const order = this.store.order;
1728
+ if (!order) throw new Error('order 模块未初始化');
1729
+ const completion = order.getPaymentCompletion(payments);
1730
+ return completion.isOrderFullyPaid ? 'paid' : 'partially_paid';
1731
+ }
1732
+ queueLatestAutoPaymentSync() {
1733
+ this.queuedAutoPaymentSync = true;
1734
+ this.scheduleQueuedAutoPaymentSyncFlush();
1735
+ }
1736
+ scheduleQueuedAutoPaymentSyncFlush(delay = 100) {
1737
+ if (!this.queuedAutoPaymentSync) return;
1738
+ if (this.autoPaymentSyncTimer) return;
1739
+ this.autoPaymentSyncTimer = setTimeout(() => {
1740
+ this.autoPaymentSyncTimer = null;
1741
+ void this.flushQueuedAutoPaymentSync();
1742
+ }, delay);
1743
+ }
1744
+ async flushQueuedAutoPaymentSync() {
1745
+ if (!this.store.order) return;
1746
+ if (!this.queuedAutoPaymentSync || this.autoPaymentSyncFlushing) return;
1747
+ if (this.store.order.getOrderSyncState() === 'submitting') {
1748
+ this.scheduleQueuedAutoPaymentSyncFlush();
1749
+ return;
1750
+ }
1751
+ this.autoPaymentSyncFlushing = true;
1752
+ try {
1753
+ while (this.queuedAutoPaymentSync) {
1754
+ this.queuedAutoPaymentSync = false;
1755
+ const payments = this.store.order.getOrderPayments();
1756
+ if (!payments.length) continue;
1757
+ await this.syncPaymentsToOrder({
1758
+ payments,
1759
+ paymentStatus: this.getPaymentStatusForSync(payments),
1760
+ submitWhenPaid: true,
1761
+ smallTicketDataFlag: 1
1762
+ });
1763
+ }
1764
+ } catch (error) {
1765
+ this.logError('queued auto sync payments failed', {
1766
+ error: error instanceof Error ? error.message : String(error)
1767
+ });
1768
+ } finally {
1769
+ this.autoPaymentSyncFlushing = false;
1770
+ if (this.queuedAutoPaymentSync) {
1771
+ this.scheduleQueuedAutoPaymentSyncFlush(0);
1772
+ }
1773
+ }
1774
+ }
1775
+
1776
+ /** 追加单个支付项到当前订单。 */
1777
+ addOrderPayment(payment) {
1778
+ if (!this.store.order) throw new Error('order 模块未初始化');
1779
+ const paymentRecord = payment;
1780
+ const paymentAmount = new _decimal.default(paymentRecord.amount || 0);
1781
+ if (paymentAmount.isZero()) {
1782
+ this.logWarning('addOrderPayment: 支付金额为 0,跳过添加支付项', {
1783
+ payment: paymentRecord
1784
+ });
1785
+ return this.store.order.getOrderPayments();
1786
+ }
1787
+ const metadata = paymentRecord.metadata && typeof paymentRecord.metadata === 'object' ? {
1788
+ ...paymentRecord.metadata
1789
+ } : {};
1790
+ const shopWalletPassId = this.otherParams?.shop_wallet_pass_id;
1791
+ if (shopWalletPassId !== undefined && shopWalletPassId !== null) {
1792
+ metadata.shop_wallet_pass_id = shopWalletPassId;
1793
+ }
1794
+ if (!metadata.unique_payment_number) {
1795
+ metadata.unique_payment_number = (0, _utils.createUuidV4)();
1796
+ }
1797
+ const paymentTimestamp = (0, _dayjs.default)().format('YYYY-MM-DD HH:mm:ss');
1798
+ const paymentTime = paymentRecord.payment_time ?? paymentTimestamp;
1799
+ const createdAt = paymentRecord.created_at ?? paymentTimestamp;
1800
+ const serviceCharge = paymentRecord.service_charge && typeof paymentRecord.service_charge === 'object' ? paymentRecord.service_charge : null;
1801
+ const calculatedServiceFee = serviceCharge ? new _decimal.default(paymentRecord.origin_amount ?? paymentRecord.amount ?? 0).times(serviceCharge.percentage || 0).plus(serviceCharge.amount || 0).toDecimalPlaces(2).toFixed(2) : undefined;
1802
+ const payments = this.store.order.addOrderPayment({
1803
+ ...paymentRecord,
1804
+ ...(calculatedServiceFee !== undefined ? {
1805
+ service_fee: calculatedServiceFee
1806
+ } : {}),
1807
+ metadata,
1808
+ payment_time: paymentTime,
1809
+ created_at: createdAt
1810
+ });
1811
+ const amountSnapshot = this.store.order.getOrderAmountSnapshot();
1812
+ const syncState = this.store.order.getOrderSyncState();
1813
+ if (amountSnapshot) {
1814
+ if (syncState === 'submitting') {
1815
+ this.queueLatestAutoPaymentSync();
1816
+ return payments;
1817
+ }
1818
+ void this.syncPaymentsToOrder({
1819
+ payments,
1820
+ paymentStatus: this.getPaymentStatusForSync(payments),
1821
+ submitWhenPaid: true,
1822
+ smallTicketDataFlag: 1
1823
+ }).catch(error => {
1824
+ this.logError('auto sync payments failed', {
1825
+ error: error instanceof Error ? error.message : String(error)
1826
+ });
1827
+ });
1828
+ }
1829
+ return payments;
1830
+ }
1831
+
1832
+ /** 更新当前订单中的指定支付项。 */
1833
+ updateOrderPayment(paymentIdentity, updates) {
1834
+ if (!this.store.order) throw new Error('order 模块未初始化');
1835
+ return this.store.order.updateOrderPayment(paymentIdentity, updates);
1836
+ }
1837
+
1838
+ /** 删除当前订单中的指定支付项。 */
1839
+ deleteOrderPayment(paymentIdentity) {
1840
+ if (!this.store.order) throw new Error('order 模块未初始化');
1841
+ return this.store.order.deleteOrderPayment(paymentIdentity);
1842
+ }
1843
+
1844
+ /** 覆盖 wallet pass 支付项,同时保留普通支付项。 */
1845
+ updateVoucherOrderPayments(payments, options) {
1846
+ if (!this.store.order) throw new Error('order 模块未初始化');
1847
+ return this.store.order.updateVoucherOrderPayments(payments, options);
1848
+ }
1849
+ confirmPendingVoucherPayments() {
1850
+ if (!this.store.order) throw new Error('order 模块未初始化');
1851
+ return this.store.order.confirmPendingVoucherPayments();
1852
+ }
1853
+ discardPendingVoucherPayments() {
1854
+ if (!this.store.order) throw new Error('order 模块未初始化');
1855
+ return this.store.order.discardPendingVoucherPayments();
1856
+ }
1857
+ getWalletProductList() {
1858
+ const tempOrder = this.store.order?.getTempOrder();
1859
+ if (!tempOrder?.products?.length) return [];
1860
+ // 过滤出有 product_id的商品来,如果没有代表他是自定义商品,不需要参与 wallet
1861
+ // debugger
1862
+ // const products = tempOrder?.products?.filter(n => n.product_id)
1863
+
1864
+ const walletProducts = tempOrder?.products.map(product => {
1865
+ const metadata = product.metadata || {};
1866
+ const rawHolderId = product.holder_id ?? metadata.holder_id;
1867
+ const holderIdValue = Array.isArray(rawHolderId) ? rawHolderId[0] : rawHolderId;
1868
+ const holder_id = Number.isInteger(Number(holderIdValue)) ? Number(holderIdValue) : undefined;
1869
+ return {
1870
+ product_id: product.product_id || 0,
1871
+ product_variant_id: String(product.product_variant_id ?? ''),
1872
+ quantity: product.num || 1,
1873
+ is_price_include_tax: tempOrder.is_price_include_tax,
1874
+ is_charge_tax: product.is_charge_tax ?? 0,
1875
+ tax_fee: product.tax_fee,
1876
+ selling_price: Number(product.selling_price || 0),
1877
+ discount_list: product.discount_list || [],
1878
+ holder_id,
1879
+ original_price: product.original_price,
1880
+ main_product_original_price: metadata.main_product_original_price ?? product.original_price,
1881
+ main_product_selling_price: metadata.main_product_selling_price ?? product.selling_price,
1882
+ metadata: {
1883
+ main_product_attached_bundle_tax_fee: metadata.main_product_attached_bundle_tax_fee,
1884
+ main_product_attached_bundle_surcharge_fee: metadata.main_product_attached_bundle_surcharge_fee,
1885
+ surcharge_rounding_remainder: metadata.surcharge_rounding_remainder
1886
+ },
1887
+ product_bundle: (product.product_bundle || []).map(bundle => {
1888
+ const bundleMetadata = bundle.metadata || {};
1889
+ return {
1890
+ is_price_include_tax: tempOrder.is_price_include_tax,
1891
+ // 套餐子项在 tempOrder 上常为 id(如 1718),prepare/deduction 协议要求 bundle_id
1892
+ bundle_id: bundle.bundle_id ?? bundle.id,
1893
+ bundle_product_id: bundle.bundle_product_id ?? bundle._bundle_product_id,
1894
+ bundle_group_id: bundle.bundle_group_id ?? bundle.group_id,
1895
+ bundle_variant_id: bundle.bundle_variant_id,
1896
+ price_type: bundle.price_type,
1897
+ price_type_ext: bundle.price_type_ext,
1898
+ bundle_sum_price: bundle.bundle_sum_price,
1899
+ bundle_selling_price: bundle.bundle_selling_price,
1900
+ num: bundle.num,
1901
+ is_charge_tax: bundle.is_charge_tax,
1902
+ tax_fee: bundle.tax_fee,
1903
+ metadata: {
1904
+ surcharge_fee: bundleMetadata.surcharge_fee,
1905
+ surcharge_rounding_remainder: bundleMetadata.surcharge_rounding_remainder
1906
+ }
1907
+ };
1908
+ })
1909
+ };
1910
+ });
1911
+ return walletProducts;
1912
+ }
1913
+ async initWalletData(params) {
1914
+ if (!this.store.order) throw new Error('order 模块未初始化');
1915
+ if (!this.store.payment) throw new Error('payment 模块未初始化');
1916
+ const snapshot = this.store.order.getOrderSnapshot();
1917
+ const tempOrder = snapshot?.tempOrder;
1918
+ const amount = snapshot?.amount;
1919
+ if (!tempOrder || !amount) {
1920
+ return {
1921
+ walletRecommendList: [],
1922
+ userIdentificationCodes: [],
1923
+ transformList: [],
1924
+ noApplicableVoucher: [],
1925
+ products: []
1926
+ };
1927
+ }
1928
+ const walletBusinessData = {
1929
+ customer_id: tempOrder.customer_id ? Number(tempOrder.customer_id) : undefined,
1930
+ holder: tempOrder.holder,
1931
+ amountInfo: {
1932
+ totalAmount: amount.totalAmount,
1933
+ subTotal: amount.summary.product_amount || amount.summary.product_expect_amount || amount.totalAmount,
1934
+ depositAmount: amount.depositAmount,
1935
+ isDeposit: tempOrder.is_deposit
1936
+ },
1937
+ products: this.getWalletProductList(),
1938
+ order_wait_pay_amount: params?.order_wait_pay_amount ?? Number(amount.amountGap || 0),
1939
+ is_price_include_tax: tempOrder.is_price_include_tax ?? this.otherParams.is_price_include_tax ?? 1,
1940
+ ...(snapshot.identity.orderId ? {
1941
+ payment_order_id: String(snapshot.identity.orderId)
1942
+ } : {})
1943
+ };
1944
+ const result = await this.store.payment.wallet.initializeWalletDataFromBusinessAsync(walletBusinessData);
1945
+ await this.core.effects.emit(`${this.name}:onWalletDataInitialized`, {
1946
+ orderId: snapshot.identity.orderId,
1947
+ customerId: walletBusinessData.customer_id,
1948
+ walletBusinessData: {
1949
+ customer_id: walletBusinessData.customer_id,
1950
+ amountInfo: walletBusinessData.amountInfo,
1951
+ order_wait_pay_amount: walletBusinessData.order_wait_pay_amount
1952
+ },
1953
+ timestamp: Date.now()
1954
+ });
1955
+ return result;
1956
+ }
1957
+
1958
+ /**
1959
+ * 更新当前订单客户字段。
1960
+ *
1961
+ * 这是 PaymentModal 客户切换的订单事实入口;外部客户模块同步应由
1962
+ * 调用方通过 onCustomerChange 回调完成。
1963
+ */
1964
+ updateOrderCustomer(customer) {
1965
+ if (!this.store.order) throw new Error('order 模块未初始化');
1966
+ const result = this.store.order.updateTempOrderCustomer(customer);
1967
+ this.store.order.recalculateSummary({
1968
+ createIfMissing: true
1969
+ });
1970
+ this.store.order.persistTempOrder();
1971
+ return result;
1972
+ }
1973
+
1974
+ /** 获取可用支付方式列表,供 PaymentModal 渲染支付选项。 */
1975
+ async getPaymentMethodsAsync() {
1976
+ // if (this.store.payment) {
1977
+ // return this.store.payment.getPayMethodListAsync();
1978
+ // }
1979
+ try {
1980
+ const response = await this.request.get('/pay/custom-payment/all', {
1981
+ filterPaymentMethods: true
1982
+ }, {
1983
+ osServer: true
1984
+ });
1985
+ const paymentMethods = response?.data || [];
1986
+ this.paymentMethodsCache = Array.isArray(paymentMethods) ? [...paymentMethods] : [];
1987
+ return this.getPaymentMethods();
1988
+ } catch (error) {
1989
+ this.logWarning('getPaymentMethodsAsync: 获取支付方式列表失败,使用缓存列表', {
1990
+ error: error instanceof Error ? error.message : String(error)
1991
+ });
1992
+ return this.getPaymentMethods();
1993
+ }
1994
+ }
1995
+
1996
+ /** 同步读取初始化时缓存的支付方式列表。 */
1997
+ getPaymentMethods() {
1998
+ return [...this.paymentMethodsCache];
1999
+ }
2000
+
2001
+ /** 轻量舍入能力,供现金支付 UI 计算 rounding_amount。 */
2002
+ roundAmount(params) {
2003
+ const amount = new _decimal.default(params.amount || 0);
2004
+ const interval = new _decimal.default(params.interval || 0.05);
2005
+ const rule = params.rule || 'standard_rounding';
2006
+ const quotient = amount.div(interval);
2007
+ let roundedQuotient;
2008
+ if (rule === 'always_up') {
2009
+ roundedQuotient = quotient.ceil();
2010
+ } else if (rule === 'always_down') {
2011
+ roundedQuotient = quotient.floor();
2012
+ } else if (rule === 'standard_down') {
2013
+ roundedQuotient = quotient.toDecimalPlaces(0, _decimal.default.ROUND_HALF_DOWN);
2014
+ } else {
2015
+ roundedQuotient = quotient.toDecimalPlaces(0, _decimal.default.ROUND_HALF_UP);
2016
+ }
2017
+ const roundedAmount = roundedQuotient.times(interval).toDecimalPlaces(2);
2018
+ return {
2019
+ originalAmount: amount.toFixed(2),
2020
+ roundedAmount: roundedAmount.toFixed(2),
2021
+ roundingDifference: roundedAmount.minus(amount).toFixed(2)
2022
+ };
2023
+ }
2024
+
2025
+ /**
2026
+ * 发送支付链接。
2027
+ *
2028
+ * 如果没有 orderIds 或显式 submitBeforeSend,会先提交当前本地订单到云端;
2029
+ * 只有成功拿到远端 order_id 后才调用 OrderModule 发送邮件。
2030
+ */
2031
+ async sendCustomerPayLink(params) {
2032
+ if (!this.store.order) throw new Error('order 模块未初始化');
2033
+ let orderIds = params.order_ids || params.orderIds || [];
2034
+ if (!orderIds.length || params.submitBeforeSend) {
2035
+ const submitResult = await this.submitSalesOrder({
2036
+ smallTicketDataFlag: 1
2037
+ });
2038
+ const orderId = submitResult?.data?.order_id ?? submitResult?.order_id ?? submitResult?.data?.id ?? submitResult?.id;
2039
+ if (!orderId) {
2040
+ throw new Error('本地订单提交云端失败,无法发送支付链接');
2041
+ }
2042
+ orderIds = [orderId];
2043
+ }
2044
+ return this.store.order.sendCustomerPayLink({
2045
+ emails: params.emails,
2046
+ notify_action: params.notify_action,
2047
+ order_ids: orderIds
2048
+ });
2049
+ }
2050
+ transformBaseProductToOrderProduct(params) {
2051
+ return (0, _transformBaseProductToOrderProduct.transformBaseProductToOrderProduct)(params);
2052
+ }
2053
+ async calculateProductBookingPrice(params) {
2054
+ const quotation = this.store.quotation;
2055
+ const customerId = params.customer_id ?? this.getCurrentOrderCustomerId();
2056
+ const authoritativeProduct = await this.loadProductForPriceQuery(params, customerId);
2057
+ const product = this.mergeProductForPriceQuery(params.product, authoritativeProduct);
2058
+ await this.loadQuotationForPriceQuery({
2059
+ quotation,
2060
+ customer_id: customerId,
2061
+ channel: params.channel
2062
+ });
2063
+ return (0, _quotationPrice.calculateBaseSalesProductBookingPrice)({
2064
+ ...params,
2065
+ product,
2066
+ fallback_price: authoritativeProduct ? undefined : params.fallback_price,
2067
+ customer_id: customerId,
2068
+ quotation
2069
+ });
2070
+ }
2071
+ getQuotationCustomerScopeInfo() {
2072
+ const quotation = this.store.quotation;
2073
+ if (quotation && typeof quotation.getQuotationCustomerScopeInfo === 'function') {
2074
+ return quotation.getQuotationCustomerScopeInfo();
2075
+ }
2076
+ const list = typeof quotation?.getQuotationList === 'function' ? quotation.getQuotationList() : [];
2077
+ const customerById = new Map();
2078
+ const quotationScopes = (Array.isArray(list) ? list : []).filter(item => Array.isArray(item?.customer) && item.customer.length > 0).map(item => {
2079
+ const localSeen = new Set();
2080
+ const customers = item.customer.filter(customer => {
2081
+ const customerId = this.normalizePriceContextCustomerId(customer?.id);
2082
+ if (!customerId || localSeen.has(customerId)) return false;
2083
+ localSeen.add(customerId);
2084
+ if (!customerById.has(customerId)) customerById.set(customerId, customer);
2085
+ return true;
2086
+ });
2087
+ return {
2088
+ quotationId: item.id,
2089
+ quotationName: item.name,
2090
+ customerIds: customers.map(customer => String(customer.id)),
2091
+ customers
2092
+ };
2093
+ });
2094
+ return {
2095
+ quotationCount: Array.isArray(list) ? list.length : 0,
2096
+ hasCustomerScopedQuotations: quotationScopes.length > 0,
2097
+ allQuotationsCustomerScoped: Array.isArray(list) && list.length > 0 && quotationScopes.length === list.length,
2098
+ customerIds: Array.from(customerById.keys()),
2099
+ customers: Array.from(customerById.values()),
2100
+ quotationScopes
2101
+ };
2102
+ }
2103
+ shouldRecalculateProductBookingPricesForContextChange(params) {
2104
+ const scopeInfo = this.getQuotationCustomerScopeInfo();
2105
+ const scopedCustomerIds = new Set(scopeInfo.customerIds.map(id => String(id)));
2106
+ const previousCustomerId = this.normalizePriceContextCustomerId(params.previousCustomerId);
2107
+ const nextCustomerId = this.normalizePriceContextCustomerId(params.nextCustomerId);
2108
+ const previousInScope = previousCustomerId ? scopedCustomerIds.has(previousCustomerId) : false;
2109
+ const nextInScope = nextCustomerId ? scopedCustomerIds.has(nextCustomerId) : false;
2110
+ let shouldRecalculate = false;
2111
+ if (scopeInfo.hasCustomerScopedQuotations) {
2112
+ if (previousInScope) {
2113
+ shouldRecalculate = true;
2114
+ } else if (!previousCustomerId && !nextInScope) {
2115
+ shouldRecalculate = false;
2116
+ } else {
2117
+ shouldRecalculate = nextInScope;
2118
+ }
2119
+ }
2120
+ return shouldRecalculate;
2121
+ }
2122
+ async calculateProductBookingPrices(paramsList) {
2123
+ if (!paramsList.length) return [];
2124
+ const quotation = this.store.quotation;
2125
+ const customerId = paramsList[0]?.customer_id ?? this.getCurrentOrderCustomerId();
2126
+ const productMapsByGroup = new Map();
2127
+ const groups = new Map();
2128
+ paramsList.forEach(params => {
2129
+ const product = params.product || {};
2130
+ const productId = this.getPriceQueryProductId(product);
2131
+ if (productId === null) return;
2132
+ const schedule = this.getPriceQuerySchedule(params);
2133
+ const channel = params.channel || this.otherParams?.channel;
2134
+ const groupKey = [schedule.schedule_date, schedule.schedule_datetime, channel || ''].join('|');
2135
+ const group = groups.get(groupKey) || {
2136
+ ids: new Set(),
2137
+ schedule,
2138
+ channel
2139
+ };
2140
+ group.ids.add(productId);
2141
+ groups.set(groupKey, group);
2142
+ });
2143
+ await Promise.all(Array.from(groups.entries()).map(async ([groupKey, group]) => {
2144
+ const productsById = await this.loadProductsForPriceQuery({
2145
+ ids: Array.from(group.ids),
2146
+ schedule: group.schedule,
2147
+ customerId,
2148
+ channel: group.channel
2149
+ });
2150
+ productMapsByGroup.set(groupKey, productsById);
2151
+ }));
2152
+ await this.loadQuotationForPriceQuery({
2153
+ quotation,
2154
+ customer_id: customerId,
2155
+ channel: paramsList[0]?.channel
2156
+ });
2157
+ return paramsList.map(params => {
2158
+ const productInput = params.product || {};
2159
+ const productId = this.getPriceQueryProductId(productInput);
2160
+ const schedule = this.getPriceQuerySchedule(params);
2161
+ const channel = params.channel || this.otherParams?.channel;
2162
+ const groupKey = [schedule.schedule_date, schedule.schedule_datetime, channel || ''].join('|');
2163
+ const authoritativeProduct = productId === null ? null : productMapsByGroup.get(groupKey)?.get(productId) || null;
2164
+ const product = this.mergeProductForPriceQuery(productInput, authoritativeProduct);
2165
+ return (0, _quotationPrice.calculateBaseSalesProductBookingPrice)({
2166
+ ...params,
2167
+ product,
2168
+ fallback_price: authoritativeProduct ? undefined : params.fallback_price,
2169
+ customer_id: customerId,
2170
+ quotation
2171
+ });
2172
+ });
2173
+ }
2174
+ async addProductToOrder(product, booking) {
2175
+ try {
2176
+ if (!this.store.order) throw new Error('order 模块未初始化');
2177
+ const products = await this.store.order.addProductToOrder(product, booking);
2178
+ // // 需要叠加
2179
+ // if (this.hooks.onAddProductOverlay) {
2180
+ // const res = this.hooks.onAddProductOverlay(是否叠加, product)
2181
+ // if (!res) {
2182
+ // 单独添加商品
2183
+ // } else {
2184
+ // 叠加
2185
+ // }
2186
+ // } else {
2187
+ // 叠加
2188
+ // }
2189
+ return products;
2190
+ } catch (error) {
2191
+ throw error;
2192
+ }
2193
+ }
2194
+ async updateOrderProduct(params) {
2195
+ try {
2196
+ if (!this.store.order) throw new Error('order 模块未初始化');
2197
+ const products = await this.store.order.updateOrderProduct(params);
2198
+ return products;
2199
+ } catch (error) {
2200
+ throw error;
2201
+ }
2202
+ }
2203
+ async updateOrderProducts(paramsList) {
2204
+ try {
2205
+ if (!this.store.order) throw new Error('order 模块未初始化');
2206
+ const products = await this.store.order.updateOrderProducts(paramsList);
2207
+ return products;
2208
+ } catch (error) {
2209
+ throw error;
2210
+ }
2211
+ }
2212
+ async updateOrderProductQuantity(params) {
2213
+ try {
2214
+ if (!this.store.order) throw new Error('order 模块未初始化');
2215
+ const products = await this.store.order.updateOrderProductQuantity(params);
2216
+ return products;
2217
+ } catch (error) {
2218
+ throw error;
2219
+ }
2220
+ }
2221
+ updateOrderBooking(params) {
2222
+ try {
2223
+ if (!this.store.order) throw new Error('order 模块未初始化');
2224
+ return this.store.order.updateOrderBooking(params);
2225
+ } catch (error) {
2226
+ throw error;
2227
+ }
2228
+ }
2229
+
2230
+ /**
2231
+ * 设置单行商品备注(与整单 `updateTempOrderNote` 区分)。
2232
+ * 多行同 SKU 时必须传入与删除/更新一致的 unique_identification_number。
2233
+ */
2234
+ async setOrderProductLineNote(identity, note) {
2235
+ try {
2236
+ const params = {
2237
+ product_id: identity.product_id,
2238
+ product_variant_id: identity.product_variant_id,
2239
+ updates: {
2240
+ note: String(note || '')
2241
+ }
2242
+ };
2243
+ if (identity.unique_identification_number !== undefined) {
2244
+ params.unique_identification_number = identity.unique_identification_number;
2245
+ }
2246
+ if (identity.product_sku !== undefined) {
2247
+ params.product_sku = identity.product_sku;
2248
+ }
2249
+ if (identity.product_bundle !== undefined) params.product_bundle = identity.product_bundle;
2250
+ const products = await this.updateOrderProduct(params);
2251
+ return products;
2252
+ } catch (error) {
2253
+ throw error;
2254
+ }
2255
+ }
2256
+
2257
+ /**
2258
+ * 批量删除购物车行,末尾仅一次促销重算。
2259
+ *
2260
+ * @example
2261
+ * await baseSales.removeProductsFromOrder([identityA, identityB]);
2262
+ */
2263
+ async removeProductsFromOrder(identities) {
2264
+ try {
2265
+ if (!this.store.order) throw new Error('order 模块未初始化');
2266
+ return await this.store.order.removeProductsFromOrder(identities);
2267
+ } catch (error) {
2268
+ throw error;
2269
+ }
2270
+ }
2271
+ async removeProductFromOrder(identity) {
2272
+ return this.removeProductsFromOrder([identity]);
2273
+ }
2274
+
2275
+ // ─── 促销/赠品 透传(迁移自 booking usePromotion) ────────────────
2276
+
2277
+ /**
2278
+ * 注入促销评估器到底层 OrderModule。
2279
+ *
2280
+ * @example
2281
+ * solution.setPromotionEvaluator(appHelper.utils.promotionEvaluator);
2282
+ */
2283
+ setPromotionEvaluator(evaluator) {
2284
+ if (!this.store.order) throw new Error('order 模块未初始化');
2285
+ this.store.order.setPromotionEvaluator(evaluator);
2286
+ }
2287
+
2288
+ /**
2289
+ * 注入赠品选择 resolver。UI 层(SalesSdk)通过 bridge 提供。
2290
+ *
2291
+ * @example
2292
+ * solution.setGiftSelectResolver(async (ctx) => bridge.request(ctx));
2293
+ */
2294
+ setGiftSelectResolver(resolver) {
2295
+ if (!this.store.order) throw new Error('order 模块未初始化');
2296
+ this.store.order.setGiftSelectResolver(resolver);
2297
+ }
2298
+
2299
+ /**
2300
+ * 为商品目录追加促销标签,供 SalesSdkProductProvider 等商品列表入口复用。
2301
+ *
2302
+ * @example
2303
+ * const products = solution.appendPromotionTags(catalogProducts);
2304
+ */
2305
+ appendPromotionTags(products) {
2306
+ if (!this.store.order) throw new Error('order 模块未初始化');
2307
+ return this.store.order.appendPromotionTags(products);
2308
+ }
2309
+
2310
+ /**
2311
+ * 主动触发一次促销应用(一般写路径会自动触发,少数场景如客户切换后可手动调)。
2312
+ */
2313
+ async applyPromotion() {
2314
+ if (!this.store.order) throw new Error('order 模块未初始化');
2315
+ await this.store.order.applyPromotion();
2316
+ }
2317
+
2318
+ /** 最近一次促销计算输出的未满足提示。 */
2319
+ getUnfulfilledPromotions() {
2320
+ return this.store.order?.getUnfulfilledPromotions() || [];
2321
+ }
2322
+
2323
+ /** 最近一次促销计算输出的赠品操作 diff。 */
2324
+ getLastGiftActions() {
2325
+ return this.store.order?.getLastGiftActions() || null;
2326
+ }
2327
+
2328
+ // 获取商品列表
2329
+ // TODO 需要跟 webpos 内的对齐一下
2330
+ async getProductList() {
2331
+ // 可以直接通过配置里的 menu 读取
2332
+ const menu_list_ids = this.otherParams?.dineInConfig?.['menu.associated_menus'].map(n => Number(n.value)) || [];
2333
+ try {
2334
+ const res = await this.store.products?.loadProducts({
2335
+ menu_list_ids,
2336
+ cacheId: this.cacheId,
2337
+ schedule_date: (0, _dayjs.default)().format('YYYY-MM-DD'),
2338
+ schedule_datetime: (0, _dayjs.default)().format('YYYY-MM-DD HH:mm:ss')
2339
+ });
2340
+ return res;
2341
+ } catch (error) {
2342
+ throw error;
2343
+ }
2344
+ }
2345
+ getOtherParams() {
2346
+ return this.otherParams;
2347
+ }
2348
+ async setOtherParams(params, {
2349
+ cover = false
2350
+ } = {}) {
2351
+ if (cover) {
2352
+ this.otherParams = {
2353
+ ...params
2354
+ };
2355
+ } else {
2356
+ this.otherParams = {
2357
+ ...this.otherParams,
2358
+ ...params
2359
+ };
2360
+ }
2361
+ this.cacheId = this.otherParams?.cacheId;
2362
+ await this.syncOtherParamsToSubModules(params, {
2363
+ cover
2364
+ });
2365
+ }
2366
+ }
2367
+ exports.BaseSalesImpl = BaseSalesImpl;