@pisell/pisellos 0.0.568 → 0.0.570

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 (455) hide show
  1. package/dist/effects/index.d.ts +2 -2
  2. package/dist/model/strategy/adapter/dataVariant/type.d.ts +3 -3
  3. package/dist/model/strategy/adapter/itemRule/type.d.ts +6 -6
  4. package/dist/model/strategy/adapter/promotion/evaluator.js +3 -2
  5. package/dist/model/strategy/adapter/promotion/type.d.ts +5 -5
  6. package/dist/model/strategy/adapter/walletPass/utils.js +7 -4
  7. package/dist/model/strategy/type.d.ts +1 -1
  8. package/dist/modules/BaseModule.d.ts +2 -2
  9. package/dist/modules/BaseModule.js +4 -4
  10. package/dist/modules/BookingContext/index.d.ts +48 -0
  11. package/dist/modules/BookingContext/index.js +566 -0
  12. package/dist/modules/BookingContext/types.d.ts +102 -0
  13. package/dist/modules/BookingContext/types.js +51 -0
  14. package/dist/modules/BookingContext/utils/buildCacheItemFromOrderLine.d.ts +16 -0
  15. package/dist/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +302 -0
  16. package/dist/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.d.ts +8 -0
  17. package/dist/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +149 -0
  18. package/dist/modules/BookingContext/utils/cacheItemToBookingInput.d.ts +76 -0
  19. package/dist/modules/BookingContext/utils/cacheItemToBookingInput.js +249 -0
  20. package/dist/modules/BookingContext/utils/flexible.d.ts +28 -0
  21. package/dist/modules/BookingContext/utils/flexible.js +56 -0
  22. package/dist/modules/BookingContext/utils/formatResourceList.d.ts +8 -0
  23. package/dist/modules/BookingContext/utils/formatResourceList.js +38 -0
  24. package/dist/modules/BookingContext/utils/index.d.ts +11 -0
  25. package/dist/modules/BookingContext/utils/index.js +11 -0
  26. package/dist/modules/BookingContext/utils/noResource.d.ts +13 -0
  27. package/dist/modules/BookingContext/utils/noResource.js +77 -0
  28. package/dist/modules/BookingContext/utils/orderLineDisplay.d.ts +1 -0
  29. package/dist/modules/BookingContext/utils/orderLineDisplay.js +36 -0
  30. package/dist/modules/BookingContext/utils/productExtend.d.ts +72 -0
  31. package/dist/modules/BookingContext/utils/productExtend.js +339 -0
  32. package/dist/modules/BookingContext/utils/resourceErrors.d.ts +41 -0
  33. package/dist/modules/BookingContext/utils/resourceErrors.js +74 -0
  34. package/dist/modules/BookingContext/utils/resourceSelection.d.ts +7 -0
  35. package/dist/modules/BookingContext/utils/resourceSelection.js +24 -0
  36. package/dist/modules/BookingContext/utils/resources.d.ts +80 -0
  37. package/dist/modules/BookingContext/utils/resources.js +486 -0
  38. package/dist/modules/BookingContext/utils/serviceTimes.d.ts +55 -0
  39. package/dist/modules/BookingContext/utils/serviceTimes.js +151 -0
  40. package/dist/modules/BookingContext/utils/timeSlices.d.ts +42 -0
  41. package/dist/modules/BookingContext/utils/timeSlices.js +100 -0
  42. package/dist/modules/Cart/utils/cartProduct.js +26 -5
  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 +3 -1
  47. package/dist/modules/Discount/index.d.ts +2 -1
  48. package/dist/modules/Discount/index.js +37 -9
  49. package/dist/modules/Discount/types.d.ts +2 -1
  50. package/dist/modules/Holder/types.d.ts +1 -1
  51. package/dist/modules/OpenData/index.d.ts +8 -0
  52. package/dist/modules/OpenData/index.js +36 -16
  53. package/dist/modules/OpenData/types.d.ts +1 -1
  54. package/dist/modules/Order/index.d.ts +448 -23
  55. package/dist/modules/Order/index.js +5417 -561
  56. package/dist/modules/Order/types.d.ts +693 -88
  57. package/dist/modules/Order/types.js +68 -1
  58. package/dist/modules/Order/utils/bundleDiscountHydration.d.ts +5 -0
  59. package/dist/modules/Order/utils/bundleDiscountHydration.js +144 -0
  60. package/dist/modules/Order/utils/manualProductDiscount.d.ts +106 -0
  61. package/dist/modules/Order/utils/manualProductDiscount.js +210 -0
  62. package/dist/modules/Order/utils/orderCollectionIdentity.d.ts +53 -0
  63. package/dist/modules/Order/utils/orderCollectionIdentity.js +405 -0
  64. package/dist/modules/Order/utils.d.ts +167 -18
  65. package/dist/modules/Order/utils.js +960 -165
  66. package/dist/modules/Payment/index.d.ts +10 -9
  67. package/dist/modules/Payment/index.js +465 -703
  68. package/dist/modules/Payment/types.d.ts +47 -23
  69. package/dist/modules/Payment/utils.d.ts +1 -1
  70. package/dist/modules/Payment/utils.js +4 -3
  71. package/dist/modules/Payment/walletpass.js +44 -29
  72. package/dist/modules/Product/index.d.ts +1 -1
  73. package/dist/modules/Product/types.d.ts +29 -7
  74. package/dist/modules/ProductList/index.d.ts +17 -13
  75. package/dist/modules/ProductList/index.js +184 -74
  76. package/dist/modules/ProductList/types.d.ts +17 -0
  77. package/dist/modules/Quotation/index.d.ts +22 -2
  78. package/dist/modules/Quotation/index.js +108 -42
  79. package/dist/modules/Quotation/types.d.ts +19 -0
  80. package/dist/modules/ResourcePlanner/index.d.ts +24 -0
  81. package/dist/modules/ResourcePlanner/index.js +181 -0
  82. package/dist/modules/ResourcePlanner/planner.d.ts +14 -0
  83. package/dist/modules/ResourcePlanner/planner.js +1069 -0
  84. package/dist/modules/ResourcePlanner/types.d.ts +227 -0
  85. package/dist/modules/ResourcePlanner/types.js +1 -0
  86. package/dist/modules/Rules/index.d.ts +8 -10
  87. package/dist/modules/Rules/index.js +222 -109
  88. package/dist/modules/Rules/types.d.ts +15 -4
  89. package/dist/modules/SalesSummary/index.d.ts +23 -26
  90. package/dist/modules/SalesSummary/index.js +152 -46
  91. package/dist/modules/SalesSummary/types.d.ts +9 -1
  92. package/dist/modules/SalesSummary/utils.d.ts +3 -22
  93. package/dist/modules/SalesSummary/utils.js +803 -163
  94. package/dist/modules/ScanOrderLogger/types.d.ts +2 -2
  95. package/dist/modules/Schedule/getDateIsInSchedule.d.ts +1 -1
  96. package/dist/modules/Schedule/getDateIsInSchedule.js +14 -7
  97. package/dist/modules/Schedule/index.d.ts +18 -5
  98. package/dist/modules/Schedule/index.js +78 -24
  99. package/dist/modules/Schedule/type.d.ts +7 -7
  100. package/dist/modules/Schedule/types.d.ts +11 -9
  101. package/dist/modules/Summary/index.js +4 -3
  102. package/dist/modules/SurchargeList/index.d.ts +18 -0
  103. package/dist/modules/SurchargeList/index.js +185 -0
  104. package/dist/modules/SurchargeList/types.d.ts +15 -0
  105. package/dist/modules/SurchargeList/types.js +1 -0
  106. package/dist/modules/index.d.ts +3 -0
  107. package/dist/modules/index.js +4 -1
  108. package/dist/plugins/app.d.ts +2 -0
  109. package/dist/plugins/request.d.ts +3 -2
  110. package/dist/server/index.d.ts +462 -8
  111. package/dist/server/index.js +6112 -656
  112. package/dist/server/modules/floor-plan/index.d.ts +39 -0
  113. package/dist/server/modules/floor-plan/index.js +595 -0
  114. package/dist/server/modules/floor-plan/types.d.ts +43 -0
  115. package/dist/server/modules/floor-plan/types.js +13 -0
  116. package/dist/server/modules/index.d.ts +7 -2
  117. package/dist/server/modules/index.js +6 -0
  118. package/dist/server/modules/menu/types.d.ts +1 -1
  119. package/dist/server/modules/order/index.d.ts +207 -14
  120. package/dist/server/modules/order/index.js +2326 -379
  121. package/dist/server/modules/order/types.d.ts +91 -20
  122. package/dist/server/modules/order/types.js +14 -1
  123. package/dist/server/modules/order/utils/filterBookings.d.ts +7 -1
  124. package/dist/server/modules/order/utils/filterBookings.js +118 -11
  125. package/dist/server/modules/order/utils/filterOrders.js +242 -14
  126. package/dist/server/modules/payment/index.d.ts +28 -0
  127. package/dist/server/modules/payment/index.js +305 -0
  128. package/dist/server/modules/payment/types.d.ts +9 -0
  129. package/dist/server/modules/payment/types.js +1 -0
  130. package/dist/server/modules/products/index.d.ts +105 -14
  131. package/dist/server/modules/products/index.js +1393 -516
  132. package/dist/server/modules/products/types.d.ts +35 -2
  133. package/dist/server/modules/products/types.js +4 -0
  134. package/dist/server/modules/quotation/index.d.ts +39 -0
  135. package/dist/server/modules/quotation/index.js +278 -66
  136. package/dist/server/modules/resource/index.d.ts +2 -3
  137. package/dist/server/modules/resource/index.js +108 -86
  138. package/dist/server/modules/resource/types.d.ts +1 -1
  139. package/dist/server/modules/schedule/index.d.ts +6 -4
  140. package/dist/server/modules/schedule/index.js +116 -85
  141. package/dist/server/modules/schedule/utils.d.ts +1 -1
  142. package/dist/server/test.json +713 -0
  143. package/dist/server/types.d.ts +6 -4
  144. package/dist/server/utils/product.js +2 -2
  145. package/dist/server/utils/small-ticket.d.ts +71 -0
  146. package/dist/server/utils/small-ticket.js +963 -0
  147. package/dist/solution/BaseSales/index.d.ts +378 -0
  148. package/dist/solution/BaseSales/index.js +4006 -0
  149. package/dist/solution/BaseSales/types.d.ts +394 -0
  150. package/dist/solution/BaseSales/types.js +40 -0
  151. package/dist/solution/BaseSales/utils/cartPromotion.d.ts +287 -0
  152. package/dist/solution/BaseSales/utils/cartPromotion.js +1450 -0
  153. package/dist/solution/BaseSales/utils/parseSalesResponse.d.ts +144 -0
  154. package/dist/solution/BaseSales/utils/parseSalesResponse.js +157 -0
  155. package/dist/solution/BaseSales/utils/quotationPrice.d.ts +18 -0
  156. package/dist/solution/BaseSales/utils/quotationPrice.js +250 -0
  157. package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.d.ts +63 -0
  158. package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +218 -0
  159. package/dist/solution/BaseSales/utils.d.ts +40 -0
  160. package/dist/solution/BaseSales/utils.js +263 -0
  161. package/dist/solution/BookingByStep/index.d.ts +2 -2
  162. package/dist/solution/BookingByStep/utils/resources.d.ts +1 -1
  163. package/dist/solution/BookingTicket/index.d.ts +382 -25
  164. package/dist/solution/BookingTicket/index.js +2506 -443
  165. package/dist/solution/BookingTicket/types.d.ts +191 -6
  166. package/dist/solution/BookingTicket/types.js +83 -10
  167. package/dist/solution/BookingTicket/utils/addProductDecision.d.ts +117 -0
  168. package/dist/solution/BookingTicket/utils/addProductDecision.js +346 -0
  169. package/dist/solution/BookingTicket/utils/addTimeAvailability.d.ts +35 -0
  170. package/dist/solution/BookingTicket/utils/addTimeAvailability.js +95 -0
  171. package/dist/solution/BookingTicket/utils/bookingStatus.d.ts +77 -0
  172. package/dist/solution/BookingTicket/utils/bookingStatus.js +136 -0
  173. package/dist/solution/BookingTicket/utils/cartView.d.ts +29 -0
  174. package/dist/solution/BookingTicket/utils/cartView.js +209 -0
  175. package/dist/solution/BookingTicket/utils/exampleData.d.ts +123 -0
  176. package/dist/solution/BookingTicket/utils/exampleData.js +183 -0
  177. package/dist/solution/BookingTicket/utils/orderCustomer.d.ts +4 -0
  178. package/dist/solution/BookingTicket/utils/orderCustomer.js +31 -0
  179. package/dist/solution/BookingTicket/utils/resolveBestAddTimePlan.d.ts +185 -0
  180. package/dist/solution/BookingTicket/utils/resolveBestAddTimePlan.js +438 -0
  181. package/dist/solution/BookingTicket/utils/scan/applyGlobalScan.d.ts +36 -0
  182. package/dist/solution/BookingTicket/utils/scan/applyGlobalScan.js +400 -0
  183. package/dist/solution/BookingTicket/utils/scan/cloudSearch.d.ts +4 -0
  184. package/dist/solution/BookingTicket/utils/scan/cloudSearch.js +41 -1
  185. package/dist/solution/BookingTicket/utils/scan/formatGlobalScan.d.ts +32 -0
  186. package/dist/solution/BookingTicket/utils/scan/formatGlobalScan.js +56 -0
  187. package/dist/solution/BookingTicket/utils/scan/handleScan.d.ts +3 -1
  188. package/dist/solution/BookingTicket/utils/scan/handleScan.js +11 -3
  189. package/dist/solution/BookingTicket/utils/scan/index.d.ts +5 -0
  190. package/dist/solution/BookingTicket/utils/scan/index.js +20 -8
  191. package/dist/solution/BookingTicket/utils/scan/scanProductValues.d.ts +21 -0
  192. package/dist/solution/BookingTicket/utils/scan/scanProductValues.js +150 -0
  193. package/dist/solution/BookingTicket/utils/sessionCatalogStale.d.ts +23 -0
  194. package/dist/solution/BookingTicket/utils/sessionCatalogStale.js +78 -0
  195. package/dist/solution/Checkout/index.d.ts +1 -0
  196. package/dist/solution/Checkout/index.js +25 -34
  197. package/dist/solution/Checkout/utils/index.js +4 -4
  198. package/dist/solution/RegisterAndLogin/config.js +2 -10
  199. package/dist/solution/Sales/index.d.ts +50 -5
  200. package/dist/solution/Sales/index.js +484 -92
  201. package/dist/solution/Sales/types.d.ts +29 -5
  202. package/dist/solution/ScanOrder/index.d.ts +27 -21
  203. package/dist/solution/ScanOrder/index.js +499 -434
  204. package/dist/solution/ScanOrder/types.d.ts +38 -16
  205. package/dist/solution/ScanOrder/types.js +4 -5
  206. package/dist/solution/ScanOrder/utils.d.ts +4 -1
  207. package/dist/solution/ScanOrder/utils.js +109 -34
  208. package/dist/solution/ShopDiscount/index.js +4 -4
  209. package/dist/solution/ShopDiscount/types.d.ts +1 -1
  210. package/dist/solution/ShopDiscount/utils.d.ts +2 -2
  211. package/dist/solution/UnifiedBookingSales/index.d.ts +183 -0
  212. package/dist/solution/UnifiedBookingSales/index.js +1871 -0
  213. package/dist/solution/UnifiedBookingSales/types.d.ts +143 -0
  214. package/dist/solution/UnifiedBookingSales/types.js +11 -0
  215. package/dist/solution/VenueBooking/index.d.ts +29 -20
  216. package/dist/solution/VenueBooking/index.js +301 -213
  217. package/dist/solution/VenueBooking/sales-section-4-annotated.json +0 -2
  218. package/dist/solution/VenueBooking/types.d.ts +1 -1
  219. package/dist/solution/VenueBooking/utils/slotMerge.d.ts +6 -0
  220. package/dist/solution/VenueBooking/utils/slotMerge.js +9 -3
  221. package/dist/solution/index.d.ts +2 -0
  222. package/dist/solution/index.js +3 -1
  223. package/dist/types/index.d.ts +12 -2
  224. package/dist/utils/task.d.ts +2 -2
  225. package/dist/utils/watch.d.ts +2 -2
  226. package/lib/effects/index.d.ts +2 -2
  227. package/lib/model/strategy/adapter/dataVariant/type.d.ts +3 -3
  228. package/lib/model/strategy/adapter/itemRule/type.d.ts +6 -6
  229. package/lib/model/strategy/adapter/promotion/evaluator.js +3 -2
  230. package/lib/model/strategy/adapter/promotion/type.d.ts +5 -5
  231. package/lib/model/strategy/adapter/walletPass/utils.js +6 -4
  232. package/lib/model/strategy/type.d.ts +1 -1
  233. package/lib/modules/BaseModule.d.ts +2 -2
  234. package/lib/modules/BaseModule.js +4 -4
  235. package/lib/modules/BookingContext/index.d.ts +48 -0
  236. package/lib/modules/BookingContext/index.js +390 -0
  237. package/lib/modules/BookingContext/types.d.ts +102 -0
  238. package/lib/modules/BookingContext/types.js +48 -0
  239. package/lib/modules/BookingContext/utils/buildCacheItemFromOrderLine.d.ts +16 -0
  240. package/lib/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +286 -0
  241. package/lib/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.d.ts +8 -0
  242. package/lib/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +148 -0
  243. package/lib/modules/BookingContext/utils/cacheItemToBookingInput.d.ts +76 -0
  244. package/lib/modules/BookingContext/utils/cacheItemToBookingInput.js +242 -0
  245. package/lib/modules/BookingContext/utils/flexible.d.ts +28 -0
  246. package/lib/modules/BookingContext/utils/flexible.js +63 -0
  247. package/lib/modules/BookingContext/utils/formatResourceList.d.ts +8 -0
  248. package/lib/modules/BookingContext/utils/formatResourceList.js +35 -0
  249. package/lib/modules/BookingContext/utils/index.d.ts +11 -0
  250. package/lib/modules/BookingContext/utils/index.js +126 -0
  251. package/lib/modules/BookingContext/utils/noResource.d.ts +13 -0
  252. package/lib/modules/BookingContext/utils/noResource.js +78 -0
  253. package/lib/modules/BookingContext/utils/orderLineDisplay.d.ts +1 -0
  254. package/lib/modules/BookingContext/utils/orderLineDisplay.js +29 -0
  255. package/lib/modules/BookingContext/utils/productExtend.d.ts +72 -0
  256. package/lib/modules/BookingContext/utils/productExtend.js +330 -0
  257. package/lib/modules/BookingContext/utils/resourceErrors.d.ts +41 -0
  258. package/lib/modules/BookingContext/utils/resourceErrors.js +80 -0
  259. package/lib/modules/BookingContext/utils/resourceSelection.d.ts +7 -0
  260. package/lib/modules/BookingContext/utils/resourceSelection.js +24 -0
  261. package/lib/modules/BookingContext/utils/resources.d.ts +80 -0
  262. package/lib/modules/BookingContext/utils/resources.js +419 -0
  263. package/lib/modules/BookingContext/utils/serviceTimes.d.ts +55 -0
  264. package/lib/modules/BookingContext/utils/serviceTimes.js +161 -0
  265. package/lib/modules/BookingContext/utils/timeSlices.d.ts +42 -0
  266. package/lib/modules/BookingContext/utils/timeSlices.js +114 -0
  267. package/lib/modules/Cart/utils/cartProduct.js +27 -5
  268. package/lib/modules/Cart/utils/changePrice.js +20 -14
  269. package/lib/modules/Customer/index.d.ts +20 -0
  270. package/lib/modules/Customer/index.js +63 -10
  271. package/lib/modules/Customer/types.d.ts +3 -1
  272. package/lib/modules/Discount/index.d.ts +2 -1
  273. package/lib/modules/Discount/index.js +18 -2
  274. package/lib/modules/Discount/types.d.ts +2 -1
  275. package/lib/modules/Holder/types.d.ts +1 -1
  276. package/lib/modules/OpenData/index.d.ts +8 -0
  277. package/lib/modules/OpenData/index.js +21 -3
  278. package/lib/modules/OpenData/types.d.ts +1 -1
  279. package/lib/modules/Order/index.d.ts +448 -23
  280. package/lib/modules/Order/index.js +3641 -308
  281. package/lib/modules/Order/types.d.ts +693 -88
  282. package/lib/modules/Order/types.js +55 -1
  283. package/lib/modules/Order/utils/bundleDiscountHydration.d.ts +5 -0
  284. package/lib/modules/Order/utils/bundleDiscountHydration.js +106 -0
  285. package/lib/modules/Order/utils/manualProductDiscount.d.ts +106 -0
  286. package/lib/modules/Order/utils/manualProductDiscount.js +207 -0
  287. package/lib/modules/Order/utils/orderCollectionIdentity.d.ts +53 -0
  288. package/lib/modules/Order/utils/orderCollectionIdentity.js +353 -0
  289. package/lib/modules/Order/utils.d.ts +167 -18
  290. package/lib/modules/Order/utils.js +974 -138
  291. package/lib/modules/Payment/index.d.ts +10 -9
  292. package/lib/modules/Payment/index.js +30 -124
  293. package/lib/modules/Payment/types.d.ts +47 -23
  294. package/lib/modules/Payment/utils.d.ts +1 -1
  295. package/lib/modules/Payment/utils.js +4 -3
  296. package/lib/modules/Payment/walletpass.js +16 -6
  297. package/lib/modules/Product/index.d.ts +1 -1
  298. package/lib/modules/Product/types.d.ts +29 -7
  299. package/lib/modules/ProductList/index.d.ts +17 -13
  300. package/lib/modules/ProductList/index.js +92 -51
  301. package/lib/modules/ProductList/types.d.ts +17 -0
  302. package/lib/modules/Quotation/index.d.ts +22 -2
  303. package/lib/modules/Quotation/index.js +76 -20
  304. package/lib/modules/Quotation/types.d.ts +19 -0
  305. package/lib/modules/ResourcePlanner/index.d.ts +24 -0
  306. package/lib/modules/ResourcePlanner/index.js +147 -0
  307. package/lib/modules/ResourcePlanner/planner.d.ts +14 -0
  308. package/lib/modules/ResourcePlanner/planner.js +895 -0
  309. package/lib/modules/ResourcePlanner/types.d.ts +227 -0
  310. package/lib/modules/ResourcePlanner/types.js +5 -0
  311. package/lib/modules/Rules/index.d.ts +8 -10
  312. package/lib/modules/Rules/index.js +153 -61
  313. package/lib/modules/Rules/types.d.ts +15 -4
  314. package/lib/modules/SalesSummary/index.d.ts +23 -26
  315. package/lib/modules/SalesSummary/index.js +84 -16
  316. package/lib/modules/SalesSummary/types.d.ts +9 -1
  317. package/lib/modules/SalesSummary/utils.d.ts +3 -22
  318. package/lib/modules/SalesSummary/utils.js +616 -111
  319. package/lib/modules/ScanOrderLogger/types.d.ts +2 -2
  320. package/lib/modules/Schedule/getDateIsInSchedule.d.ts +1 -1
  321. package/lib/modules/Schedule/getDateIsInSchedule.js +14 -7
  322. package/lib/modules/Schedule/index.d.ts +18 -5
  323. package/lib/modules/Schedule/index.js +49 -14
  324. package/lib/modules/Schedule/type.d.ts +7 -7
  325. package/lib/modules/Schedule/types.d.ts +11 -9
  326. package/lib/modules/Summary/index.js +2 -2
  327. package/lib/modules/SurchargeList/index.d.ts +18 -0
  328. package/lib/modules/SurchargeList/index.js +95 -0
  329. package/lib/modules/SurchargeList/types.d.ts +15 -0
  330. package/lib/modules/SurchargeList/types.js +5 -0
  331. package/lib/modules/index.d.ts +3 -0
  332. package/lib/modules/index.js +33 -0
  333. package/lib/plugins/app.d.ts +2 -0
  334. package/lib/plugins/request.d.ts +3 -2
  335. package/lib/server/index.d.ts +462 -8
  336. package/lib/server/index.js +3901 -205
  337. package/lib/server/modules/floor-plan/index.d.ts +39 -0
  338. package/lib/server/modules/floor-plan/index.js +306 -0
  339. package/lib/server/modules/floor-plan/types.d.ts +43 -0
  340. package/lib/server/modules/floor-plan/types.js +19 -0
  341. package/lib/server/modules/index.d.ts +7 -2
  342. package/lib/server/modules/index.js +21 -0
  343. package/lib/server/modules/menu/types.d.ts +1 -1
  344. package/lib/server/modules/order/index.d.ts +207 -14
  345. package/lib/server/modules/order/index.js +1324 -141
  346. package/lib/server/modules/order/types.d.ts +91 -20
  347. package/lib/server/modules/order/types.js +11 -1
  348. package/lib/server/modules/order/utils/filterBookings.d.ts +7 -1
  349. package/lib/server/modules/order/utils/filterBookings.js +119 -11
  350. package/lib/server/modules/order/utils/filterOrders.js +238 -13
  351. package/lib/server/modules/payment/index.d.ts +28 -0
  352. package/lib/server/modules/payment/index.js +168 -0
  353. package/lib/server/modules/payment/types.d.ts +9 -0
  354. package/lib/server/modules/payment/types.js +5 -0
  355. package/lib/server/modules/products/index.d.ts +105 -14
  356. package/lib/server/modules/products/index.js +588 -97
  357. package/lib/server/modules/products/types.d.ts +35 -2
  358. package/lib/server/modules/products/types.js +3 -0
  359. package/lib/server/modules/quotation/index.d.ts +39 -0
  360. package/lib/server/modules/quotation/index.js +145 -1
  361. package/lib/server/modules/resource/index.d.ts +2 -3
  362. package/lib/server/modules/resource/index.js +71 -45
  363. package/lib/server/modules/resource/types.d.ts +1 -1
  364. package/lib/server/modules/schedule/index.d.ts +6 -4
  365. package/lib/server/modules/schedule/index.js +55 -44
  366. package/lib/server/modules/schedule/utils.d.ts +1 -1
  367. package/lib/server/test.json +713 -0
  368. package/lib/server/types.d.ts +6 -4
  369. package/lib/server/utils/product.js +2 -2
  370. package/lib/server/utils/small-ticket.d.ts +71 -0
  371. package/lib/server/utils/small-ticket.js +926 -0
  372. package/lib/solution/BaseSales/index.d.ts +378 -0
  373. package/lib/solution/BaseSales/index.js +2367 -0
  374. package/lib/solution/BaseSales/types.d.ts +394 -0
  375. package/lib/solution/BaseSales/types.js +47 -0
  376. package/lib/solution/BaseSales/utils/cartPromotion.d.ts +287 -0
  377. package/lib/solution/BaseSales/utils/cartPromotion.js +1155 -0
  378. package/lib/solution/BaseSales/utils/parseSalesResponse.d.ts +144 -0
  379. package/lib/solution/BaseSales/utils/parseSalesResponse.js +140 -0
  380. package/lib/solution/BaseSales/utils/quotationPrice.d.ts +18 -0
  381. package/lib/solution/BaseSales/utils/quotationPrice.js +231 -0
  382. package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.d.ts +63 -0
  383. package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +215 -0
  384. package/lib/solution/BaseSales/utils.d.ts +40 -0
  385. package/lib/solution/BaseSales/utils.js +259 -0
  386. package/lib/solution/BookingByStep/index.d.ts +2 -2
  387. package/lib/solution/BookingByStep/utils/resources.d.ts +1 -1
  388. package/lib/solution/BookingTicket/index.d.ts +382 -25
  389. package/lib/solution/BookingTicket/index.js +1350 -76
  390. package/lib/solution/BookingTicket/types.d.ts +191 -6
  391. package/lib/solution/BookingTicket/types.js +71 -10
  392. package/lib/solution/BookingTicket/utils/addProductDecision.d.ts +117 -0
  393. package/lib/solution/BookingTicket/utils/addProductDecision.js +353 -0
  394. package/lib/solution/BookingTicket/utils/addTimeAvailability.d.ts +35 -0
  395. package/lib/solution/BookingTicket/utils/addTimeAvailability.js +94 -0
  396. package/lib/solution/BookingTicket/utils/bookingStatus.d.ts +77 -0
  397. package/lib/solution/BookingTicket/utils/bookingStatus.js +136 -0
  398. package/lib/solution/BookingTicket/utils/cartView.d.ts +29 -0
  399. package/lib/solution/BookingTicket/utils/cartView.js +200 -0
  400. package/lib/solution/BookingTicket/utils/exampleData.d.ts +123 -0
  401. package/lib/solution/BookingTicket/utils/exampleData.js +185 -0
  402. package/lib/solution/BookingTicket/utils/orderCustomer.d.ts +4 -0
  403. package/lib/solution/BookingTicket/utils/orderCustomer.js +35 -0
  404. package/lib/solution/BookingTicket/utils/resolveBestAddTimePlan.d.ts +185 -0
  405. package/lib/solution/BookingTicket/utils/resolveBestAddTimePlan.js +387 -0
  406. package/lib/solution/BookingTicket/utils/scan/applyGlobalScan.d.ts +36 -0
  407. package/lib/solution/BookingTicket/utils/scan/applyGlobalScan.js +220 -0
  408. package/lib/solution/BookingTicket/utils/scan/cloudSearch.d.ts +4 -0
  409. package/lib/solution/BookingTicket/utils/scan/cloudSearch.js +24 -3
  410. package/lib/solution/BookingTicket/utils/scan/formatGlobalScan.d.ts +32 -0
  411. package/lib/solution/BookingTicket/utils/scan/formatGlobalScan.js +64 -0
  412. package/lib/solution/BookingTicket/utils/scan/handleScan.d.ts +3 -1
  413. package/lib/solution/BookingTicket/utils/scan/handleScan.js +4 -2
  414. package/lib/solution/BookingTicket/utils/scan/index.d.ts +5 -0
  415. package/lib/solution/BookingTicket/utils/scan/index.js +14 -2
  416. package/lib/solution/BookingTicket/utils/scan/scanProductValues.d.ts +21 -0
  417. package/lib/solution/BookingTicket/utils/scan/scanProductValues.js +115 -0
  418. package/lib/solution/BookingTicket/utils/sessionCatalogStale.d.ts +23 -0
  419. package/lib/solution/BookingTicket/utils/sessionCatalogStale.js +83 -0
  420. package/lib/solution/Checkout/index.d.ts +1 -0
  421. package/lib/solution/Checkout/index.js +18 -19
  422. package/lib/solution/Checkout/utils/index.js +4 -4
  423. package/lib/solution/RegisterAndLogin/config.js +2 -10
  424. package/lib/solution/Sales/index.d.ts +50 -5
  425. package/lib/solution/Sales/index.js +296 -39
  426. package/lib/solution/Sales/types.d.ts +29 -5
  427. package/lib/solution/ScanOrder/index.d.ts +27 -21
  428. package/lib/solution/ScanOrder/index.js +144 -103
  429. package/lib/solution/ScanOrder/types.d.ts +38 -16
  430. package/lib/solution/ScanOrder/types.js +4 -5
  431. package/lib/solution/ScanOrder/utils.d.ts +4 -1
  432. package/lib/solution/ScanOrder/utils.js +98 -26
  433. package/lib/solution/ShopDiscount/index.js +3 -3
  434. package/lib/solution/ShopDiscount/types.d.ts +1 -1
  435. package/lib/solution/ShopDiscount/utils.d.ts +2 -2
  436. package/lib/solution/UnifiedBookingSales/index.d.ts +183 -0
  437. package/lib/solution/UnifiedBookingSales/index.js +1050 -0
  438. package/lib/solution/UnifiedBookingSales/types.d.ts +143 -0
  439. package/lib/solution/UnifiedBookingSales/types.js +14 -0
  440. package/lib/solution/VenueBooking/index.d.ts +29 -20
  441. package/lib/solution/VenueBooking/index.js +169 -94
  442. package/lib/solution/VenueBooking/sales-section-4-annotated.json +0 -2
  443. package/lib/solution/VenueBooking/types.d.ts +1 -1
  444. package/lib/solution/VenueBooking/utils/slotMerge.d.ts +6 -0
  445. package/lib/solution/VenueBooking/utils/slotMerge.js +9 -3
  446. package/lib/solution/index.d.ts +2 -0
  447. package/lib/solution/index.js +22 -0
  448. package/lib/types/index.d.ts +12 -2
  449. package/lib/utils/task.d.ts +2 -2
  450. package/lib/utils/watch.d.ts +2 -2
  451. package/package.json +18 -19
  452. package/dist/model/strategy/adapter/promotion/index.d.ts +0 -4
  453. package/dist/solution/Checkout/appointmentDemo.json +0 -1
  454. package/lib/model/strategy/adapter/promotion/index.d.ts +0 -4
  455. package/lib/solution/Checkout/appointmentDemo.json +0 -1
@@ -8,7 +8,7 @@ var _exportNames = {
8
8
  BookingTicket: true
9
9
  };
10
10
  exports.BookingTicketImpl = exports.BookingTicket = void 0;
11
- var _BaseModule = require("../../modules/BaseModule");
11
+ var _dayjs = _interopRequireDefault(require("dayjs"));
12
12
  var _types = require("./types");
13
13
  Object.keys(_types).forEach(function (key) {
14
14
  if (key === "default" || key === "__esModule") return;
@@ -21,69 +21,551 @@ Object.keys(_types).forEach(function (key) {
21
21
  }
22
22
  });
23
23
  });
24
+ var _BookingContext = require("../../modules/BookingContext");
25
+ var _BaseSales = require("../BaseSales");
24
26
  var _scan = _interopRequireDefault(require("./utils/scan"));
27
+ var _orderCustomer = require("./utils/orderCustomer");
25
28
  var _handleScan = require("./utils/scan/handleScan");
26
29
  var _scanCache = _interopRequireDefault(require("./utils/scan/scanCache"));
30
+ var _formatGlobalScan = require("./utils/scan/formatGlobalScan");
31
+ var _applyGlobalScan = require("./utils/scan/applyGlobalScan");
32
+ var _cartView = require("./utils/cartView");
33
+ var _addProductDecision = require("./utils/addProductDecision");
34
+ var _buildCacheItemFromOrderLine = require("../../modules/BookingContext/utils/buildCacheItemFromOrderLine");
35
+ var _sessionCatalogStale = require("./utils/sessionCatalogStale");
36
+ var _buildNormalProductCacheItemFromOrderLine = require("../../modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine");
37
+ var _orderLineDisplay = require("../../modules/BookingContext/utils/orderLineDisplay");
38
+ var _bookingStatus = require("./utils/bookingStatus");
39
+ var _resolveBestAddTimePlan = require("./utils/resolveBestAddTimePlan");
40
+ var _utils = require("../../modules/Order/utils");
27
41
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
28
- class BookingTicketImpl extends _BaseModule.BaseModule {
42
+ const OPEN_DATA_SECTION_CODES = ['sale', 'reservation', 'fulfillment', 'menu', 'workflow'];
43
+ const OPEN_DATA_CACHE_TTL = 5 * 60 * 1000;
44
+
45
+ /**
46
+ * 读取已有 booking 的协议唯一值,用于加时商品行绑定原预约。
47
+ *
48
+ * @example
49
+ * const bookingUid = resolveBookingUidForAddTime(booking);
50
+ */
51
+ function resolveBookingUidForAddTime(booking) {
52
+ const uid = booking?.metadata?.unique_identification_number;
53
+ return uid === undefined || uid === null ? '' : String(uid);
54
+ }
55
+
56
+ /**
57
+ * 规范化加车数量,避免非法值进入 OrderModule。
58
+ *
59
+ * @example
60
+ * const quantity = normalizeAddTimeQuantity(product.num);
61
+ */
62
+ function normalizeAddTimeQuantity(value) {
63
+ const parsed = Math.floor(Number(value));
64
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : 1;
65
+ }
66
+
67
+ /**
68
+ * 解析加时时长,优先使用显式入参,其次回退商品字段。
69
+ *
70
+ * @example
71
+ * const minutes = resolveAddTimeScheduleMinutes(input.product_add_schedule_time, product);
72
+ */
73
+ function resolveAddTimeScheduleMinutes(explicitValue, product) {
74
+ const raw = explicitValue ?? product?.product_add_schedule_time;
75
+ if (raw === undefined || raw === null || raw === '') return undefined;
76
+ const parsed = Number(raw);
77
+ return Number.isFinite(parsed) ? parsed : undefined;
78
+ }
79
+
80
+ /**
81
+ * 计算加时商品行价格字段,保持 `price` 与 `selling_price` 输入口径一致。
82
+ *
83
+ * @example
84
+ * const price = resolveAddTimeProductPrice(input.price, product);
85
+ */
86
+ function resolveAddTimeProductPrice(explicitValue, product) {
87
+ return explicitValue ?? product?.price ?? product?.selling_price ?? product?.base_price;
88
+ }
89
+
90
+ /**
91
+ * 规范化加时覆盖分钟数。
92
+ *
93
+ * @example
94
+ * const minutes = normalizeCoveredMinutes(input.coveredMinutes);
95
+ */
96
+ function normalizeCoveredMinutes(value) {
97
+ const parsed = Math.ceil(Number(value));
98
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : 0;
99
+ }
100
+
101
+ /**
102
+ * 计算 booking 加时后的结束时间与时长。
103
+ *
104
+ * @example
105
+ * const updates = buildAddTimeBookingTimePatch(booking, 30);
106
+ */
107
+ function buildAddTimeBookingTimePatch(booking, coveredMinutes) {
108
+ if (coveredMinutes <= 0) return null;
109
+ const endDate = booking?.end_date;
110
+ const endTime = booking?.end_time;
111
+ if (!endDate || !endTime) {
112
+ throw new Error('[BookingTicket] addAddTimeProductsToBooking: booking.end_date/end_time 缺失');
113
+ }
114
+ const currentEnd = (0, _dayjs.default)(`${endDate} ${endTime}`);
115
+ if (!currentEnd.isValid()) {
116
+ throw new Error('[BookingTicket] addAddTimeProductsToBooking: booking.end_date/end_time 无效');
117
+ }
118
+ const nextEnd = currentEnd.add(coveredMinutes, 'minute');
119
+ const currentDuration = Number(booking?.duration);
120
+ let duration = Number.isFinite(currentDuration) ? currentDuration + coveredMinutes : coveredMinutes;
121
+ if (booking?.start_date && booking?.start_time) {
122
+ const start = (0, _dayjs.default)(`${booking.start_date} ${booking.start_time}`);
123
+ if (start.isValid()) {
124
+ const diffMinutes = nextEnd.diff(start, 'minute');
125
+ duration = diffMinutes > 0 ? diffMinutes : duration;
126
+ }
127
+ }
128
+ return {
129
+ end_date: nextEnd.format('YYYY-MM-DD'),
130
+ end_time: nextEnd.format('HH:mm'),
131
+ duration
132
+ };
133
+ }
134
+ function withProductOptionString(cacheItem) {
135
+ const productOptionString = (0, _orderLineDisplay.buildProductOptionStringFromOrderLine)(cacheItem);
136
+ if (!productOptionString) return cacheItem;
137
+ return {
138
+ ...cacheItem,
139
+ product_option_string: cacheItem?.product_option_string || productOptionString,
140
+ _extend: {
141
+ ...(cacheItem?._extend || {}),
142
+ product_option_string: cacheItem?._extend?.product_option_string || productOptionString
143
+ }
144
+ };
145
+ }
146
+ class BookingTicketImpl extends _BaseSales.BaseSalesImpl {
29
147
  defaultName = 'bookingTicket';
30
148
  defaultVersion = '1.0.0';
31
- isSolution = true;
32
- request;
33
- window;
34
- shopStore;
35
- store;
36
- otherParams = {};
37
- cacheId;
38
- platform; // 平台类型,用于判断是 PC / H5
39
149
 
150
+ /**
151
+ * 子类覆盖 store 类型为 BookingTicket 子模块清单(在父类基础上追加 customer)。
152
+ * 父类按 `getRegisteredModuleNames` 注入成员到该 store。
153
+ */
154
+ store = {};
155
+ platform;
40
156
  scan;
41
- async initialize(core, options) {
42
- this.core = core;
43
- this.store = options.store || {};
44
- this.otherParams = options.otherParams || {};
45
- // 获取依赖的插件
46
- this.request = core.getPlugin('request');
47
- this.window = core.getPlugin('window');
48
- this.shopStore = core.getPlugin('shopStore');
157
+ productCatalog = [];
158
+ addTimeProductsCatalog = [];
159
+ orderCustomerDiscountRefreshInFlight = null;
160
+ orderCustomerDiscountRefreshCustomerId = null;
161
+ loadOpenDataConfigInFlight = null;
162
+
163
+ /**
164
+ * 在 BaseSales 默认模块清单(products/order/salesSummary/schedule)基础上追加 customer + bookingContext。
165
+ * bookingContext 是 SalesSdk 加车下沉所需的「预约 UI 上下文」(bookingConfig / resourcesOrigin / date)。
166
+ */
167
+ getRegisteredModuleNames() {
168
+ return [...super.getRegisteredModuleNames(), 'customer', 'bookingContext', 'openData'];
169
+ }
170
+ getSubmitOrderSalesChannel() {
171
+ const channel = this.core?.context?.getChannel?.();
172
+ return !channel || channel === 'system' ? 'pos' : channel;
173
+ }
174
+
175
+ /**
176
+ * 业务侧子模块工厂:BookingTicket 自带的 createBookingTicketModule 优先匹配;
177
+ * 未识别的模块名(如 BookingByStep 公共类型)回退到父类工厂。
178
+ */
179
+ createSubModule(moduleName) {
180
+ const localModule = (0, _types.createBookingTicketModule)(moduleName, this.name);
181
+ if (localModule) return localModule;
182
+ return super.createSubModule(moduleName);
183
+ }
184
+ async initialize(core, options = {}) {
185
+ this.scan = new _scan.default(this, `BOOKING_TICKET_SCAN:${this.name}`);
186
+ await super.initialize(core, options);
49
187
  if (!this.request) {
50
188
  throw new Error('bookingTicket解决方案需要 request 插件支持');
51
189
  }
52
- if (!this.shopStore) {
53
- throw new Error('bookingTicket解决方案需要 shopStore 插件支持');
190
+ this.platform = this.otherParams?.platform;
191
+ await this.configureIdGeneratorFromOpenData();
192
+ }
193
+ getBookingTicketBusinessCode() {
194
+ const businessCode = this.otherParams?.businessCode ?? this.otherParams?.business_code;
195
+ if (businessCode === undefined || businessCode === null) return null;
196
+ const normalized = String(businessCode).trim();
197
+ return normalized || null;
198
+ }
199
+ getIdGeneratorPlugin() {
200
+ const appPlugin = this.core.getPlugin('app');
201
+ const app = appPlugin?.getApp?.();
202
+ return app?.getPlugin?.('idGenerator') || null;
203
+ }
204
+ async loadOpenDataConfig() {
205
+ if (!this.store.openData) throw new Error('openData 模块未初始化');
206
+ const businessCode = this.getBookingTicketBusinessCode();
207
+ if (!businessCode) {
208
+ console.warn('[BookingTicket] businessCode 缺失,跳过 OpenData 配置加载,当前 module name: ', this.name);
209
+ return null;
210
+ }
211
+ const lastFetchedAt = this.store.openData.getLastFetchedAt();
212
+ const cachedData = this.store.openData.getOpenData();
213
+ if (cachedData && lastFetchedAt && Date.now() - lastFetchedAt < OPEN_DATA_CACHE_TTL) {
214
+ this.otherParams.openData = cachedData;
215
+ return cachedData;
216
+ }
217
+ if (this.loadOpenDataConfigInFlight) return await this.loadOpenDataConfigInFlight;
218
+ const channel = this.getSubmitOrderSalesChannel();
219
+ this.loadOpenDataConfigInFlight = this.store.openData.fetchOpenData({
220
+ scope: 'board',
221
+ target: `${businessCode}+${channel}`,
222
+ // target: `dine_in+pos`,
223
+ section_code: [...OPEN_DATA_SECTION_CODES]
224
+ });
225
+ try {
226
+ const openDataConfig = await this.loadOpenDataConfigInFlight;
227
+ this.otherParams.openData = openDataConfig;
228
+ return openDataConfig;
229
+ } finally {
230
+ this.loadOpenDataConfigInFlight = null;
231
+ }
232
+ }
233
+ async getOpenData() {
234
+ return this.store.openData?.getOpenData();
235
+ }
236
+ async getShortNumberOrDeviceId() {
237
+ const getAsyncIotDeviceInfo = this.getAppData('async_iot_device_info');
238
+ if (getAsyncIotDeviceInfo) {
239
+ const res = await getAsyncIotDeviceInfo?.();
240
+ if (res?.short_number) {
241
+ return res?.short_number;
242
+ }
243
+ }
244
+ return String(this.getAppData('device_id') || 0).slice(-2);
245
+ }
246
+ async configureIdGeneratorFromOpenData() {
247
+ let openDataConfig = null;
248
+ try {
249
+ openDataConfig = await this.loadOpenDataConfig();
250
+ } catch (error) {
251
+ console.warn('[BookingTicket] OpenData 配置加载失败,跳过 idGenerator 配置', error);
252
+ return;
54
253
  }
55
254
 
56
- // 如果有传 cacheid,默认需要收集上面这些模块里用户操作选中的数据
57
- let targetCacheData = {};
58
- this.cacheId = this.otherParams?.cacheId;
59
- this.platform = this.otherParams?.platform;
60
- this.scan = new _scan.default(this, 'BOOKING_TICKET_SCAN');
61
- if (this.otherParams?.cacheId) {
62
- const sessionData = this.window.sessionStorage.getItem(this.name);
63
- if (sessionData) {
64
- const data = JSON.parse(sessionData);
65
- targetCacheData = data[this.otherParams.cacheId];
255
+ // if (!openDataConfig) return;
256
+
257
+ const idGenerator = this.getIdGeneratorPlugin();
258
+ if (!idGenerator?.configure) {
259
+ console.warn('[BookingTicket] idGenerator 插件不可用,跳过配置');
260
+ return;
261
+ }
262
+ const receiptSequenceLength = this.normalizePositiveInteger(openDataConfig?.['sale.short_number_digits'], 5);
263
+ const receiptSequenceStart = this.normalizePositiveInteger(openDataConfig?.['sale.short_number_start_number'], 1);
264
+ const prefix = this.normalizeIdPrefix(openDataConfig?.['sale.short_number_prefix'], '');
265
+ const shopOrderPrefix = this.normalizeIdPrefix(openDataConfig?.['sale.sale_number_prefix'], '');
266
+ const resetReceiptSequenceDaily = typeof openDataConfig?.['sale.short_number_daily_reset'] === 'boolean' ? openDataConfig?.['sale.short_number_daily_reset'] : false;
267
+ const operatingDayBoundary = this.getAppData('operating_day_boundary');
268
+ const deviceId = await this.getShortNumberOrDeviceId();
269
+ const businessCode = this.getBookingTicketBusinessCode();
270
+ if (!businessCode) {
271
+ console.warn('[BookingTicket] businessCode 缺失,跳过 idGenerator 配置');
272
+ return;
273
+ }
274
+ try {
275
+ await idGenerator.configure({
276
+ biz: businessCode,
277
+ config: {
278
+ prefix,
279
+ deviceCode: deviceId,
280
+ // TODO 现在 picoding 没这个配置的 key
281
+ padReceiptSequence: false,
282
+ receiptSequenceLength,
283
+ resetReceiptSequenceDaily,
284
+ receiptSequenceResetTime: operatingDayBoundary.time,
285
+ receiptSequenceStart,
286
+ shopOrderPrefix
287
+ }
288
+ });
289
+ } catch (error) {
290
+ console.warn('[BookingTicket] idGenerator 配置失败', error);
291
+ }
292
+ }
293
+ normalizePositiveInteger(value, fallback) {
294
+ const numeric = Number(value);
295
+ if (!Number.isInteger(numeric) || numeric <= 0) return fallback;
296
+ return numeric;
297
+ }
298
+ normalizeIdPrefix(value, fallback) {
299
+ if (value === undefined || value === null) return fallback;
300
+ const normalized = String(value).trim().toUpperCase();
301
+ return normalized || fallback;
302
+ }
303
+
304
+ /**
305
+ * 加载销售详情,并在父类基础上把 customer 信息装配到 CustomerModule。
306
+ *
307
+ * 业务上 ticketBooking 进入「编辑/查看」时需要 active customer 状态,因此从
308
+ * sales.customer / sales.header / tempOrder 顶层字段中拼装 ICustomer 后调 setSelectedCustomer,
309
+ * 让现有 setActiveCustomer / getActiveCustomer 调用方零成本拿到详情态客户。
310
+ */
311
+ async loadSalesDetail(orderIdOrParams) {
312
+ const sales = await super.loadSalesDetail(orderIdOrParams);
313
+ const customerModule = this.store.customer;
314
+ const detailCustomer = sales?.customer;
315
+ const orderCustomerSnapshot = this.store.order.getOrderCustomerSnapshot();
316
+ const protocolCustomerId = sales?.customer_id;
317
+ const protocolCustomer = protocolCustomerId !== undefined && protocolCustomerId !== null ? {
318
+ id: protocolCustomerId,
319
+ name: String(sales?.customer_name || ''),
320
+ phone: sales?.phone,
321
+ email: sales?.email,
322
+ country_calling_code: sales?.country_calling_code
323
+ } : null;
324
+ const selectedCustomer = detailCustomer ?? orderCustomerSnapshot ?? protocolCustomer;
325
+ if (customerModule && selectedCustomer) {
326
+ const {
327
+ id: detailCustomerId,
328
+ ...detailCustomerRest
329
+ } = selectedCustomer ?? {};
330
+ const rawId = detailCustomerId ?? '';
331
+ const customer = {
332
+ ...detailCustomerRest,
333
+ id: rawId == null ? '' : rawId,
334
+ name: selectedCustomer?.name ?? '',
335
+ phone: selectedCustomer?.phone,
336
+ email: selectedCustomer?.email,
337
+ country_calling_code: selectedCustomer?.country_calling_code
338
+ };
339
+ customerModule.setSelectedCustomer(customer);
340
+ this.setOrderCustomer(customer);
341
+ // await this.store.order.saveDraft();
342
+ }
343
+ return sales;
344
+ }
345
+ async scanPromotionCode(code, customerId) {
346
+ if (!this.store.order) throw new Error('order 模块未初始化');
347
+ const raw = await this.store.order.scanCode(code, customerId);
348
+ const scannedList = raw.scannedDiscountList || [];
349
+ const extractedCustomerId = this.getDiscountCustomerId(scannedList);
350
+ const hasOrderCustomer = Boolean(this.store.order.getOrderCustomer());
351
+
352
+ // 缺 Holder 等场景 isAvailable=false,但仍需把 Pass 归属客户带入订单,供 Holder 弹窗使用。
353
+ if (!hasOrderCustomer && extractedCustomerId) {
354
+ await this.hydrateOrderCustomerFromScanDiscounts(scannedList);
355
+ }
356
+ if (raw.isAvailable) {
357
+ await this.store.order.recalculateSummary({
358
+ createIfMissing: true
359
+ });
360
+ this.store.order.persistTempOrder();
361
+ const tempOrder = this.store.order.ensureTempOrder();
362
+ await this.effectsEmit('onDiscountApplied', {
363
+ productList: tempOrder.products || [],
364
+ discountList: this.store.order.getDiscountList(),
365
+ selectedDiscountList: tempOrder.discount_list || [],
366
+ tempOrder
367
+ });
368
+ }
369
+ return {
370
+ isAvailable: raw.isAvailable,
371
+ type: raw.type,
372
+ unavailableReason: raw.unavailableReason,
373
+ scannedDiscountList: raw.scannedDiscountList
374
+ };
375
+ }
376
+ async hydrateOrderCustomerFromScanDiscounts(discounts) {
377
+ const currentOrderCustomer = this.store.order.getOrderCustomer();
378
+ if (currentOrderCustomer) return;
379
+ const customerId = this.getDiscountCustomerId(discounts);
380
+ if (!customerId) return;
381
+ const customer = await this.resolveCustomerByDiscountCustomerId(customerId);
382
+ if (!customer) return;
383
+ this.store.customer.setSelectedCustomer(customer);
384
+ this.setOrderCustomer(customer);
385
+ }
386
+ getDiscountCustomerId(discounts) {
387
+ for (const item of discounts) {
388
+ const topLevelId = Number(item.customer_id);
389
+ if (Number.isFinite(topLevelId) && topLevelId > 0) {
390
+ return topLevelId;
66
391
  }
67
392
  }
68
- const moduleArr = ['cart', 'products', 'accountList', 'customer', 'order'];
69
- moduleArr.forEach(step => {
70
- const targetModule = (0, _types.createModule)(step, this.name);
71
- if (targetModule) {
72
- this.store[step] = targetModule;
73
- this.core.registerModule(targetModule, {
74
- initialState: targetCacheData?.[targetModule.name],
75
- otherParams: {
76
- ...this.otherParams,
77
- fatherModule: this.name,
78
- openCache: this.otherParams?.cacheId ? true : false,
79
- cacheId: this.otherParams?.cacheId
80
- }
81
- });
82
- } else {
83
- throw new Error(`模块 ${step} 不存在`);
393
+ return null;
394
+ }
395
+ async resolveCustomerByDiscountCustomerId(customerId) {
396
+ const localCustomers = this.store.customer.getCustomers();
397
+ const localCustomer = this.findCustomerById(localCustomers, customerId);
398
+ const normalizedLocalCustomer = localCustomer ? this.normalizeCustomer(localCustomer) : null;
399
+ if (normalizedLocalCustomer && this.hasUsableCustomerDetails(normalizedLocalCustomer)) {
400
+ return normalizedLocalCustomer;
401
+ }
402
+ const result = await this.store.customer.queryCustomerList({
403
+ ids: [customerId],
404
+ skip: 1,
405
+ num: 1
406
+ });
407
+ const remoteCustomer = this.findCustomerById(result?.list || [], customerId) || result?.list?.[0] || null;
408
+ return remoteCustomer ? this.normalizeCustomer(remoteCustomer) : null;
409
+ }
410
+ findCustomerById(customers = [], customerId) {
411
+ return customers.find(customer => {
412
+ const rawId = this.getRawCustomerId(customer);
413
+ return rawId !== null && String(rawId) === String(customerId);
414
+ }) || null;
415
+ }
416
+ normalizeCustomer(customer) {
417
+ const raw = customer;
418
+ const rawId = this.getRawCustomerId(customer);
419
+ const name = customer.name || raw.display_name || raw.nickname || raw.customer_name || [raw.first_name, raw.last_name].filter(Boolean).join(' ') || '';
420
+ return {
421
+ ...customer,
422
+ id: rawId ?? customer.id,
423
+ name,
424
+ phone: customer.phone ?? raw.phone ?? raw.mobile ?? '',
425
+ email: customer.email ?? raw.email ?? '',
426
+ country_calling_code: customer.country_calling_code ?? raw.country_calling_code ?? ''
427
+ };
428
+ }
429
+ getRawCustomerId(customer) {
430
+ const raw = customer;
431
+ return customer.id ?? raw.customer_id ?? raw.customerId ?? null;
432
+ }
433
+ hasUsableCustomerDetails(customer) {
434
+ return !!(customer.name || customer.phone || customer.email || customer.display_name || customer.customer_name);
435
+ }
436
+
437
+ /**
438
+ * 更新当前预约的 appointment_status。
439
+ *
440
+ * 数据源:从 OrderModule.tempOrder 内部读取,调用方无需传 orderId / scheduleId。
441
+ * schedule id 解析优先级:
442
+ * 1. `tempOrder.metadata.appointment_booking_parent_schedule_id`
443
+ * 2. `tempOrder.bookings` 中 `parent_id === 0` 的 `schedule_event_id`
444
+ *
445
+ * 流程:先乐观更新本地 bookings 的 appointment_status,再调 PUT 接口,
446
+ * 接口成功后强制刷新销售详情;任一步失败都回滚本地状态并抛错。
447
+ *
448
+ * @param status 目标状态(如 'confirmed')
449
+ * @returns 接口返回的 data
450
+ */
451
+ async setBookingStatus(status) {
452
+ const tempOrder = this.store.order?.getTempOrder?.();
453
+ if (!tempOrder) {
454
+ throw new Error('setBookingStatus: tempOrder 未加载');
455
+ }
456
+ const orderId = tempOrder.order_id;
457
+ if (orderId === undefined || orderId === null) {
458
+ throw new Error('setBookingStatus: tempOrder.order_id 缺失');
459
+ }
460
+ const scheduleId = (0, _bookingStatus.resolveScheduleId)(tempOrder);
461
+ if (scheduleId === null) {
462
+ throw new Error('setBookingStatus: 无法解析 schedule id(metadata 与 root booking 均缺失)');
463
+ }
464
+ const bookings = Array.isArray(tempOrder.bookings) ? tempOrder.bookings : [];
465
+ const previous = (0, _bookingStatus.applyBookingsStatus)(bookings, status);
466
+ try {
467
+ const res = await this.request.put(`/schedule/booking/appointment-status/${scheduleId}`, {
468
+ appointment_status: status
469
+ });
470
+ await this.refreshSalesDetail();
471
+ this.core.effects.emit(`${this.name}:onBookingStatusChange`, {
472
+ orderId,
473
+ status
474
+ });
475
+ return res?.data ?? res;
476
+ } catch (error) {
477
+ (0, _bookingStatus.restoreBookingsStatus)(bookings, previous);
478
+ throw error;
479
+ }
480
+ }
481
+
482
+ /**
483
+ * 子预约状态机转移(POST /schedule/booking-transition/{schedule_event_id})。
484
+ *
485
+ * 与 `setBookingStatus`(父预约 PUT appointment-status)不同,本方法按子预约
486
+ * `schedule_event_id` 调用状态转移接口,body 为 `{ action }`。
487
+ *
488
+ * 流程:能解析出目标状态时先乐观更新对应子预约的 `appointment_status`,
489
+ * 再调 POST;成功后强制刷新销售详情;失败则回滚该子预约本地状态并抛错。
490
+ * `reschedule` 暂未启用状态矩阵,跳过乐观更新仅调接口。
491
+ *
492
+ * @param params.schedule_event_id 子预约 schedule_event_id
493
+ * @param params.action 转移动作:confirm | reject | arrive | start | complete | cancel | no_show | reschedule
494
+ * @returns 接口返回的 data
495
+ *
496
+ * @example
497
+ * await bookingTicket.transitionChildBooking({
498
+ * schedule_event_id: 301,
499
+ * action: 'confirm',
500
+ * });
501
+ */
502
+ async transitionChildBooking(params) {
503
+ const {
504
+ schedule_event_id,
505
+ action
506
+ } = params;
507
+ if (schedule_event_id === undefined || schedule_event_id === null || schedule_event_id === '') {
508
+ throw new Error('transitionChildBooking: schedule_event_id 不能为空');
509
+ }
510
+ if (!action) {
511
+ throw new Error('transitionChildBooking: action 不能为空');
512
+ }
513
+ const tempOrder = this.store.order?.getTempOrder?.();
514
+ if (!tempOrder) {
515
+ throw new Error('transitionChildBooking: tempOrder 未加载');
516
+ }
517
+ const orderId = tempOrder.order_id;
518
+ if (orderId === undefined || orderId === null) {
519
+ throw new Error('transitionChildBooking: tempOrder.order_id 缺失');
520
+ }
521
+ const bookings = Array.isArray(tempOrder.bookings) ? tempOrder.bookings : [];
522
+ const targetStatus = (0, _bookingStatus.resolveStatusAfterTransition)(action);
523
+ const previous = targetStatus !== null ? (0, _bookingStatus.applyChildBookingStatus)(bookings, schedule_event_id, targetStatus) : undefined;
524
+ try {
525
+ const res = await this.request.post(`/schedule/booking-transition/${schedule_event_id}`, {
526
+ action
527
+ });
528
+ await this.refreshSalesDetail();
529
+ this.core.effects.emit(`${this.name}:onChildBookingStatusChange`, {
530
+ orderId,
531
+ schedule_event_id,
532
+ action,
533
+ status: targetStatus
534
+ });
535
+ return res?.data ?? res;
536
+ } catch (error) {
537
+ if (previous !== undefined) {
538
+ (0, _bookingStatus.restoreChildBookingStatus)(bookings, schedule_event_id, previous);
84
539
  }
540
+ throw error;
541
+ }
542
+ }
543
+
544
+ /**
545
+ * 基于当前 tempOrder.order_id 强制从远端重新拉取销售详情。
546
+ *
547
+ * 适用场景:在 BookingTicket 内执行了「修改服务端订单状态」的动作(如改预约状态、
548
+ * 改下单客户、改商品行等)后,统一刷新本地销售详情,保证 UI 与服务端一致。
549
+ *
550
+ * 约束:调用前必须已有 tempOrder 且 order_id 存在,否则视为非法调用并抛错。
551
+ *
552
+ * @returns loadSalesDetail 的返回值
553
+ */
554
+ async refreshSalesDetail(params = {}) {
555
+ const tempOrder = this.store.order?.getTempOrder?.();
556
+ if (!tempOrder) {
557
+ throw new Error('refreshSalesDetail: tempOrder 未加载');
558
+ }
559
+ debugger;
560
+ const orderId = tempOrder.order_id || tempOrder.external_sale_number;
561
+ if (orderId === undefined || orderId === null) {
562
+ return {};
563
+ }
564
+ return this.loadSalesDetail({
565
+ orderId: orderId,
566
+ // merge: true
567
+ forceRemote: params.forceRemote
85
568
  });
86
- this.core.effects.emit(`${this.name}:onInited`, {});
87
569
  }
88
570
 
89
571
  /**
@@ -98,6 +580,13 @@ class BookingTicketImpl extends _BaseModule.BaseModule {
98
580
  menu_list_ids,
99
581
  schedule_datetime
100
582
  } = params;
583
+
584
+ // 优先写入 schedule_datetime(含时分),供加购 presetStartTime 与 TimeBar 对齐;
585
+ // requiresDetail 弹窗仍通过 formatBookingDateForUI 只取日历日 YYYY-MM-DD。
586
+ const bookingDate = schedule_datetime || schedule_date || '';
587
+ if (bookingDate) {
588
+ this.setBookingDate(bookingDate);
589
+ }
101
590
  try {
102
591
  const result = await this.store.products.loadProducts({
103
592
  with_count: ['bundleGroup', 'optionGroup'],
@@ -105,6 +594,7 @@ class BookingTicketImpl extends _BaseModule.BaseModule {
105
594
  ...params,
106
595
  cacheId: this.cacheId
107
596
  }, options);
597
+ this.productCatalog = Array.isArray(result) ? [...result] : [];
108
598
  this.core.effects.emit(`${this.name}:onProductsLoaded`, result);
109
599
  return result;
110
600
  } catch (error) {
@@ -113,6 +603,72 @@ class BookingTicketImpl extends _BaseModule.BaseModule {
113
603
  }
114
604
  }
115
605
 
606
+ /**
607
+ * 获取加时商品列表。
608
+ *
609
+ * @example
610
+ * const products = await bookingTicket.getAddTimeProducts({ schedule_date: '2026-06-16' });
611
+ */
612
+ async getAddTimeProducts(params = {}) {
613
+ const result = await this.store.products.getAddTimeProducts({
614
+ ...params,
615
+ cacheId: this.cacheId
616
+ });
617
+ this.addTimeProductsCatalog = Array.isArray(result) ? [...result] : [];
618
+ return result;
619
+ }
620
+
621
+ /**
622
+ * 只读查询单商品详情:走 ProductList 报价查询,但不写 productCatalog、
623
+ * 不 emit onProductsLoaded、不修改 BookingDate。
624
+ *
625
+ * 供「列表已 loadProducts、仅需补 product_resource / capacity」等场景使用,
626
+ * 避免 loadProductDetail 覆盖左侧商品列表。
627
+ *
628
+ * @example
629
+ * const detail = await bookingTicket.queryProductDetail({
630
+ * product_id: 2,
631
+ * schedule_date: '2030-01-15',
632
+ * schedule_datetime: '2030-01-15 10:00:00',
633
+ * });
634
+ */
635
+ async queryProductDetail(params, options) {
636
+ const {
637
+ product_id,
638
+ ...queryParams
639
+ } = params;
640
+ try {
641
+ const result = await this.store.products.loadProducts({
642
+ with_count: ['bundleGroup', 'optionGroup'],
643
+ with_schedule: 1,
644
+ ...queryParams,
645
+ product_ids: [product_id],
646
+ cacheId: this.cacheId
647
+ }, options);
648
+ const list = Array.isArray(result) ? result : [];
649
+ return list.find(product => Number(product.id) === Number(product_id));
650
+ } catch (error) {
651
+ console.error('Failed to query product detail:', error);
652
+ throw error;
653
+ }
654
+ }
655
+
656
+ /**
657
+ * 按预约时间加载单个商品详情。
658
+ * 价格仍由 server products 的报价单桥接统一计算,避免 solution 实例重复请求报价单。
659
+ */
660
+ async loadProductDetail(params, options) {
661
+ const {
662
+ product_id,
663
+ ...queryParams
664
+ } = params;
665
+ const products = await this.loadProducts({
666
+ ...queryParams,
667
+ product_ids: [product_id]
668
+ }, options);
669
+ return products.find(product => Number(product.id) === Number(product_id));
670
+ }
671
+
116
672
  /**
117
673
  * 取消商品查询订阅
118
674
  * @param subscriberId 订阅时传入的 subscriberId
@@ -136,6 +692,57 @@ class BookingTicketImpl extends _BaseModule.BaseModule {
136
692
  return this.store.products.getProducts();
137
693
  }
138
694
 
695
+ /**
696
+ * 获取当前已加载的商品池 UI 视图。
697
+ *
698
+ * ProductList.getProducts 是异步 API;BookingTicket 维护最近一次 loadProducts 结果,
699
+ * 供 UI 同步 getter 使用。未加载时返回空列表。
700
+ */
701
+ getProductCatalog() {
702
+ return {
703
+ products: [...this.productCatalog]
704
+ };
705
+ }
706
+
707
+ /**
708
+ * 获取购物车 UI 视图。
709
+ *
710
+ * tempOrder 仍是 checkout 协议对象;商品行直接透传 products,bookings 附带关联商品。
711
+ */
712
+ getCart() {
713
+ const tempOrder = this.store.order?.getTempOrder() ?? null;
714
+ const salesDetail = this.getSalesOrder();
715
+ const bookings = salesDetail?.bookings ?? (tempOrder?.bookings || []);
716
+ let operatingDayBoundary;
717
+ try {
718
+ operatingDayBoundary = this.getAppData('operating_day_boundary');
719
+ } catch {
720
+ operatingDayBoundary = undefined;
721
+ }
722
+ const shopOpeningHours = operatingDayBoundary?.type === 'start_time' ? '23:59' : operatingDayBoundary?.time;
723
+ return (0, _cartView.buildCartView)(tempOrder?.products || [], bookings, {
724
+ addTimeProducts: this.addTimeProductsCatalog,
725
+ bookingConfig: this.getBookingConfig(),
726
+ productsByUid: tempOrder?._extend?.productsByUid,
727
+ shopOpeningHours
728
+ });
729
+ }
730
+
731
+ /**
732
+ * 获取当前下单客户(与 OrderModule.tempOrder snapshot 一致)。
733
+ */
734
+ getCustomer() {
735
+ return this.getOrderCustomerSnapshot();
736
+ }
737
+
738
+ /**
739
+ * 通过 ids 获取商品列表(不加载到模块中)
740
+ * @returns 商品列表
741
+ */
742
+ async getProductByIds(ids) {
743
+ return this.store.products.getProductByIds(ids);
744
+ }
745
+
139
746
  /**
140
747
  * 获取日程时间段点
141
748
  * @param params 参数
@@ -230,6 +837,154 @@ class BookingTicketImpl extends _BaseModule.BaseModule {
230
837
  this.store.customer.addCustomerToFirst(customer);
231
838
  }
232
839
 
840
+ // ─── 下单客户(基于 OrderModule.tempOrder,单一真源)───
841
+ // 与老的 setActiveCustomer/getActiveCustomer 解耦:
842
+ // - 老路:写入 CustomerModule.selectedCustomer(保留兼容,存量调用方不动)
843
+ // - 新路:写入 tempOrder 的 customer 协议字段 + _extend.customerSnapshot
844
+ // 新业务(如 SalesSdk)应优先使用本组方法。
845
+
846
+ /**
847
+ * 设置当前下单客户。
848
+ *
849
+ * @param customer 完整客户对象;null 表示清空
850
+ *
851
+ * 内部将 customer 格式化为 tempOrder 协议字段后写入 OrderModule,
852
+ * 并以 customer 作为 snapshot 保存富字段。
853
+ *
854
+ * 同时透传 OrderModule 内部 emit 的 `order:onOrderCustomerChange` 之外,
855
+ * 在 BookingTicket 命名空间也 emit 一次,方便上层统一从 Solution 订阅。
856
+ */
857
+ setOrderCustomer(customer) {
858
+ if (!customer) {
859
+ this.clearOrderCustomer();
860
+ return;
861
+ }
862
+ const previousCustomerId = this.store.order.getOrderCustomer()?.customer_id;
863
+ const snapshotBeforeSet = this.store.order.getOrderCustomerSnapshot();
864
+ const patch = (0, _orderCustomer.formatOrderCustomerPatch)(customer);
865
+ const customerUnchanged = Number(previousCustomerId || 0) === Number(patch.customer_id || 0);
866
+ if (!customerUnchanged) {
867
+ this.discountConfigCacheKey = null;
868
+ this.hasManualDiscountSelection = false;
869
+ }
870
+ this.store.order.setOrderCustomer(patch, customer);
871
+ const snapshotAfterOrderSet = this.store.order.getOrderCustomerSnapshot();
872
+ if (customerUnchanged) {
873
+ // hydrate 后 customer_id 已存在但 _extend.customerSnapshot 仍为空时,
874
+ // 须通知 SalesSdk 刷新 selected(否则会一直停在 onSalesOrderLoaded 时的 null)。
875
+ const snapshotReplenished = !snapshotBeforeSet && Boolean(snapshotAfterOrderSet);
876
+ if (snapshotReplenished) {
877
+ const next = this.buildOrderCustomerPayload();
878
+ this.core.effects.emit(`${this.name}:onOrderCustomerChange`, next);
879
+ }
880
+ return;
881
+ }
882
+ const next = this.buildOrderCustomerPayload();
883
+ this.core.effects.emit(`${this.name}:onOrderCustomerChange`, next);
884
+ this.refreshDiscountConfigAfterOrderCustomerChange(patch.customer_id);
885
+ // 客户切换可能改变可用促销/赠品;触发一次重算(recursion-safe)。
886
+ void this.store.order.applyPromotion().catch(error => {
887
+ console.error('[BookingTicket] applyPromotion after customer change failed', error);
888
+ });
889
+ }
890
+
891
+ /**
892
+ * 读取 tempOrder 上的下单客户协议字段;customer_id 缺失时返回 null。
893
+ */
894
+ getOrderCustomer() {
895
+ return this.store.order.getOrderCustomer();
896
+ }
897
+
898
+ /**
899
+ * 读取 tempOrder._extend.customerSnapshot 中的完整客户对象。
900
+ */
901
+ getOrderCustomerSnapshot() {
902
+ return this.store.order.getOrderCustomerSnapshot();
903
+ }
904
+
905
+ /**
906
+ * 清空下单客户(协议字段 + snapshot)。
907
+ */
908
+ clearOrderCustomer() {
909
+ this.store.order.clearOrderCustomer();
910
+ this.discountConfigCacheKey = null;
911
+ this.hasManualDiscountSelection = false;
912
+ const orderStore = this.store.order.store || {};
913
+ const discountModule = orderStore.discount;
914
+ void discountModule?.setOriginalDiscountList?.([]);
915
+ void discountModule?.setDiscountList?.([]);
916
+ this.store.order.applyDiscount();
917
+ void this.store.order.recalculateSummary({
918
+ createIfMissing: true
919
+ }).then(() => {
920
+ const tempOrder = this.store.order.ensureTempOrder();
921
+ this.store.order.persistTempOrder();
922
+ return this.effectsEmit('onDiscountApplied', {
923
+ productList: tempOrder.products || [],
924
+ discountList: discountModule?.getDiscountList?.() || [],
925
+ selectedDiscountList: tempOrder.discount_list || [],
926
+ tempOrder
927
+ });
928
+ }).catch(error => {
929
+ console.error('Failed to recalculate summary after clearing customer:', error);
930
+ });
931
+ this.core.effects.emit(`${this.name}:onOrderCustomerChange`, null);
932
+ }
933
+
934
+ /**
935
+ * 重置 tempOrder,并同步清空下单客户运行态。
936
+ *
937
+ * OrderModule.restoreOrder 会重建空单(customer_id 已为 null),但不会:
938
+ * - emit BookingTicket `onOrderCustomerChange`
939
+ * - 重置 CustomerModule.selectedCustomer(老路 getActiveCustomer)
940
+ *
941
+ * 因此在 super.restoreOrder 之后显式清客户,保证 SalesSdk / ticketBooking 订阅方与 tempOrder 一致。
942
+ */
943
+ async restoreOrder() {
944
+ const tempOrder = await super.restoreOrder();
945
+ this.clearOrderCustomer();
946
+ this.store.customer?.setSelectedCustomer(null);
947
+ return tempOrder;
948
+ }
949
+
950
+ /** 内部:基于 OrderModule 当前状态构造一份 OrderCustomerChangePayload。 */
951
+ buildOrderCustomerPayload() {
952
+ const patch = this.store.order.getOrderCustomer();
953
+ if (!patch) return null;
954
+ return {
955
+ patch,
956
+ snapshot: this.store.order.getOrderCustomerSnapshot()
957
+ };
958
+ }
959
+ refreshDiscountConfigAfterOrderCustomerChange(customerId) {
960
+ if (!customerId || customerId === 1) return;
961
+ if (this.orderCustomerDiscountRefreshInFlight && this.orderCustomerDiscountRefreshCustomerId === customerId) {
962
+ return;
963
+ }
964
+ const previousRefresh = this.orderCustomerDiscountRefreshInFlight;
965
+ const refreshTask = (async () => {
966
+ if (previousRefresh) {
967
+ await previousRefresh.catch(() => undefined);
968
+ }
969
+ const currentCustomer = this.store.order.getOrderCustomer();
970
+ if (!currentCustomer || Number(currentCustomer.customer_id) !== customerId) {
971
+ return;
972
+ }
973
+ await this.loadDiscountConfig({
974
+ customerId
975
+ });
976
+ })();
977
+ this.orderCustomerDiscountRefreshInFlight = refreshTask;
978
+ this.orderCustomerDiscountRefreshCustomerId = customerId;
979
+ refreshTask.catch(error => {
980
+ console.error('Failed to load discount config after customer change:', error);
981
+ }).finally(() => {
982
+ if (this.orderCustomerDiscountRefreshInFlight !== refreshTask) return;
983
+ this.orderCustomerDiscountRefreshInFlight = null;
984
+ this.orderCustomerDiscountRefreshCustomerId = null;
985
+ });
986
+ }
987
+
233
988
  /**
234
989
  * 获取客户分页信息
235
990
  * @returns 分页信息
@@ -332,7 +1087,7 @@ class BookingTicketImpl extends _BaseModule.BaseModule {
332
1087
  */
333
1088
  scanGlobalListener(callback) {
334
1089
  const localSearch = v => this.store.products.findProductsByCodeOrBarcode(v);
335
- const scanCallback = (0, _handleScan.handleGlobalScan)(this.request, localSearch);
1090
+ const scanCallback = (0, _handleScan.handleGlobalScan)(this.request, localSearch, this.getSubmitOrderSalesChannel());
336
1091
  const safeCallback = d => {
337
1092
  try {
338
1093
  callback(d);
@@ -425,45 +1180,564 @@ class BookingTicketImpl extends _BaseModule.BaseModule {
425
1180
  }
426
1181
 
427
1182
  /**
428
- * 设置其他参数
429
- * @param params 参数
430
- * @param options 选项
1183
+ * 获取其他参数
1184
+ * @returns 其他参数
431
1185
  */
432
- async setOtherParams(params, {
433
- cover = false
434
- } = {}) {
435
- if (cover) {
436
- this.otherParams = params;
437
- } else {
438
- this.otherParams = {
439
- ...this.otherParams,
440
- ...params
1186
+ async getOtherParams() {
1187
+ return this.otherParams;
1188
+ }
1189
+
1190
+ // ─── BookingContext API(透传给 BookingContextModule) ──────────────────────
1191
+ // SalesSdkProvider 挂载时默认调 initBookingContext;也可由 UI 显式 set* 灌入已有数据。
1192
+
1193
+ /** 拉取 board config 并写入 BookingContext。 */
1194
+ loadBookingConfig(params) {
1195
+ return this.store.bookingContext.loadBookingConfig(params);
1196
+ }
1197
+
1198
+ /** 拉取资源全集并写入 BookingContext。 */
1199
+ loadBookingResources(params) {
1200
+ return this.store.bookingContext.loadResources(params);
1201
+ }
1202
+
1203
+ /** 初始化 BookingContext(date + config + resources)。 */
1204
+ initBookingContext(params) {
1205
+ return this.store.bookingContext.initBookingContext(params);
1206
+ }
1207
+
1208
+ /** 当前 store 是否已具备与入参等价的 config / resources(无需再请求接口)。 */
1209
+ isBookingContextReady(params) {
1210
+ return this.store.bookingContext.isBookingContextReady(params);
1211
+ }
1212
+
1213
+ /** 写入 booking config(来源:`/core/board/management/config`)。 */
1214
+ setBookingConfig(config) {
1215
+ this.store.bookingContext.setBookingConfig(config);
1216
+ }
1217
+
1218
+ /** 读取当前 booking config。 */
1219
+ getBookingConfig() {
1220
+ return this.store.bookingContext.getBookingConfig();
1221
+ }
1222
+
1223
+ /** 写入资源全集(来源:`/form/schedule`),同步派生 resourcesOriginMap。 */
1224
+ setResources(resources) {
1225
+ this.store.bookingContext.setResources(resources);
1226
+ }
1227
+
1228
+ /** 读取资源全集(浅克隆)。 */
1229
+ getResourcesOrigin() {
1230
+ return this.store.bookingContext.getResourcesOrigin();
1231
+ }
1232
+
1233
+ /** 读取资源全集 map(id → resource)。 */
1234
+ getResourcesOriginMap() {
1235
+ return this.store.bookingContext.getResourcesOriginMap();
1236
+ }
1237
+
1238
+ /** 写入当前选择的日期;支持 dayjs / Date / string。 */
1239
+ setBookingDate(date) {
1240
+ this.store.bookingContext.setDate(date);
1241
+ }
1242
+
1243
+ /** 读取当前选择的日期(统一 string)。 */
1244
+ getBookingDate() {
1245
+ return this.store.bookingContext.getDate();
1246
+ }
1247
+
1248
+ /** 读取 BookingContext 完整 store(调试 / 测试 / 透传给 SDK 使用)。 */
1249
+ getBookingContextState() {
1250
+ return this.store.bookingContext.getState();
1251
+ }
1252
+
1253
+ /** 清空 BookingContext(切租户 / destroy 场景)。 */
1254
+ clearBookingContext() {
1255
+ this.store.bookingContext.clear();
1256
+ }
1257
+
1258
+ /**
1259
+ * 构造 BookingContext 计算上下文(供 getProductExtend / getResourceByIds 等读 API 复用)。
1260
+ */
1261
+ buildBookingCalcContext(extra) {
1262
+ const state = this.store.bookingContext.getState();
1263
+ const date = extra?.date ?? state.date;
1264
+ return {
1265
+ bookingConfig: state.bookingConfig,
1266
+ resourcesOrigin: state.resourcesOrigin,
1267
+ resourcesOriginMap: state.resourcesOriginMap,
1268
+ date,
1269
+ presetStartTime: extra?.presetStartTime ?? (0, _BookingContext.derivePresetStartTimeFromDate)(date),
1270
+ ...(extra || {})
1271
+ };
1272
+ }
1273
+
1274
+ /**
1275
+ * 取商品当前可选资源列表(透传 OS BookingContext utils#getResourceByIds)。
1276
+ * `extraResources` 用于把拖入未绑定的资源也展示出来。
1277
+ */
1278
+ getResourcesForProduct(product, options) {
1279
+ const ctx = this.buildBookingCalcContext();
1280
+ return (0, _BookingContext.getResourceByIds)(ctx.resourcesOriginMap || {}, product, {
1281
+ resourceTab: ctx.bookingConfig?.config?.resource_tab,
1282
+ resourcesOrigin: ctx.resourcesOrigin,
1283
+ extraResources: options?.extraResources || []
1284
+ });
1285
+ }
1286
+
1287
+ /**
1288
+ * 拼装 cacheItem 的 _extend / _data(资源 / 容量 / timeObj),与 info2 `getProductExtend` 等价。
1289
+ */
1290
+ getProductExtend(cacheItem, extra) {
1291
+ const preparedCacheItem = withProductOptionString(cacheItem);
1292
+ const result = (0, _BookingContext.getProductExtend)({
1293
+ cacheItem: preparedCacheItem,
1294
+ ctx: this.buildBookingCalcContext(extra)
1295
+ });
1296
+ return result;
1297
+ }
1298
+
1299
+ /**
1300
+ * 取资源不可用原因列表(结构化 enum + params,i18n 文案由 UI 渲染)。
1301
+ */
1302
+ getResourceErrors(resource, cacheItem) {
1303
+ return (0, _BookingContext.getResourceErrors)(resource, cacheItem);
1304
+ }
1305
+
1306
+ // ─── addProduct 决策 / 转换 API(SDK 加车下沉所需) ─────────────────────────
1307
+ // 这一组方法是 SalesSdkCartContext.addProduct / confirmDetail 的 OS 后端。
1308
+ // 调用方传入 ProductData,OS 自取 bookingContext / customer / date 等上下文,
1309
+ // 输出 (a) autoClose 决策结果或 (b) `{ product, booking }` 协议入参。
1310
+
1311
+ /**
1312
+ * session 商品 catalog 日期与 TimeBar 不一致时写业务日志。
1313
+ *
1314
+ * @example
1315
+ * this.logSessionCatalogDateMismatch(product, ctx.date);
1316
+ */
1317
+ logSessionCatalogDateMismatch(item, bookingDate) {
1318
+ const logger = this.logger;
1319
+ if (!logger?.addLog) return;
1320
+ const tempOrder = this.getTempOrder?.();
1321
+ const staleInfo = (0, _sessionCatalogStale.buildSessionCatalogStaleInfo)(item, bookingDate);
1322
+ logger.addLog({
1323
+ type: 'warning',
1324
+ title: `[BookingTicket] session catalog date mismatch, reload products`,
1325
+ metadata: {
1326
+ order_id: tempOrder?.order_id ?? null,
1327
+ external_sale_number: tempOrder?.external_sale_number ?? null,
1328
+ ...staleInfo,
1329
+ cartDetailValueSession: item?.cartDetailValue?.session ?? null
1330
+ }
1331
+ });
1332
+ }
1333
+
1334
+ /**
1335
+ * 加车前:session 商品 catalog 日历日与 bookingDate 不一致时中断加购,交由 SDK reload products。
1336
+ */
1337
+ resolveSessionCatalogBeforeAdd(item, ctx) {
1338
+ if (!(0, _sessionCatalogStale.isSessionCatalogDateStale)(item, ctx.date)) {
1339
+ return null;
1340
+ }
1341
+ this.logSessionCatalogDateMismatch(item, ctx.date);
1342
+ return {
1343
+ action: 'reloadCatalog'
1344
+ };
1345
+ }
1346
+
1347
+ /**
1348
+ * 拼装 addProductDecision 通用上下文(从 store 自取)。
1349
+ */
1350
+ buildAddProductCtx(extra) {
1351
+ const bookingContextState = this.store.bookingContext.getState();
1352
+ const date = extra?.date ?? bookingContextState.date;
1353
+ const orderCustomerId = this.getOrderCustomerSnapshot()?.id ?? this.store.order.getTempOrder()?.customer_id ?? undefined;
1354
+ return {
1355
+ bookingConfig: bookingContextState.bookingConfig,
1356
+ resourcesOrigin: bookingContextState.resourcesOrigin,
1357
+ resourcesOriginMap: bookingContextState.resourcesOriginMap,
1358
+ date,
1359
+ presetStartTime: extra?.presetStartTime ?? (0, _BookingContext.derivePresetStartTimeFromDate)(date),
1360
+ customerId: orderCustomerId,
1361
+ ...(extra || {})
1362
+ };
1363
+ }
1364
+
1365
+ /**
1366
+ * 加车两阶段主决策(规格弹窗 / 资源编辑 / 直接加车)。
1367
+ * 与 ticketBooking `handleSelectProduct` + `handleBooking4Service` 等价。
1368
+ */
1369
+ decideAddProduct(item, options) {
1370
+ const ctx = this.buildAddProductCtx(options);
1371
+ const staleDecision = this.resolveSessionCatalogBeforeAdd(item, ctx);
1372
+ if (staleDecision) {
1373
+ return staleDecision;
1374
+ }
1375
+ return (0, _addProductDecision.decideAddProduct)(item, ctx);
1376
+ }
1377
+
1378
+ /** 规格弹窗 callback 后的第二段决策。 */
1379
+ decideAfterDetail(cacheItem, options) {
1380
+ return (0, _addProductDecision.decideAfterDetail)(cacheItem, this.buildAddProductCtx(options));
1381
+ }
1382
+
1383
+ /**
1384
+ * @deprecated 请用 `decideAddProduct`;`autoClose=true` 仅当 action=add。
1385
+ */
1386
+ decideAutoClose(item, options) {
1387
+ return (0, _addProductDecision.decideAutoClose)(item, this.buildAddProductCtx(options));
1388
+ }
1389
+
1390
+ /** 替代 info2 `getIsEject`:商品是否需要弹窗(0/1)。 */
1391
+ getIsEject(item, type = 'select') {
1392
+ return (0, _addProductDecision.getIsEject)(item, type);
1393
+ }
1394
+
1395
+ /** 仅 session 商品(无 variant/option/package)→ true。 */
1396
+ getIsOnlySession(item) {
1397
+ return (0, _addProductDecision.getIsOnlySession)(item);
1398
+ }
1399
+
1400
+ /**
1401
+ * 拼装 requiresDetail payload(给 SDK 直接 return 给 UI 用)。
1402
+ */
1403
+ buildRequiresDetailPayload(item, options) {
1404
+ const ctx = this.buildAddProductCtx(options);
1405
+ return (0, _addProductDecision.buildRequiresDetailPayload)(item, ctx);
1406
+ }
1407
+
1408
+ /**
1409
+ * 把 detail 弹窗回调结果(e, extension_type, detail)转换为 `(product, booking)` 入参。
1410
+ * 内部会先拼 cacheItem,再走 cacheItemToBookingInput 严格协议对齐。
1411
+ */
1412
+ transformDetailToProductAndBooking(input) {
1413
+ const {
1414
+ item,
1415
+ cacheItem: preparedCacheItem,
1416
+ detailResult,
1417
+ options
1418
+ } = input;
1419
+ const ctx = this.buildAddProductCtx(options);
1420
+ const cacheItem = preparedCacheItem || (detailResult ? (0, _addProductDecision.buildCacheItemFromDetail)(item, detailResult, ctx) : (0, _addProductDecision.buildCacheItemFromCartDetail)(item, ctx));
1421
+ return (0, _addProductDecision.transformDetailToProductAndBooking)(cacheItem, ctx);
1422
+ }
1423
+
1424
+ /**
1425
+ * 从已加车的 order line + booking 反查资源编辑抽屉所需的 cacheItem。
1426
+ * 供 SalesSdk「编辑资源」等场景使用;字段语义与 buildCacheItemFromDetail 产出对齐。
1427
+ */
1428
+ buildCacheItemFromOrderLine(input) {
1429
+ return (0, _buildCacheItemFromOrderLine.buildCacheItemFromOrderLine)(input);
1430
+ }
1431
+
1432
+ /**
1433
+ * 从已加车的普通商品 order line 反查 SkuDetailModal edit 所需的 cacheItem。
1434
+ */
1435
+ buildNormalProductCacheItemFromOrderLine(input) {
1436
+ return (0, _buildNormalProductCacheItemFromOrderLine.buildNormalProductCacheItemFromOrderLine)(input);
1437
+ }
1438
+
1439
+ /** 文档别名:与 OrderModule.updateOrderProduct 一致。 */
1440
+ async updateProductInOrder(params) {
1441
+ return this.updateOrderProduct(params);
1442
+ }
1443
+
1444
+ /**
1445
+ * 全局扫码 pubsub 入口:解析码值 → 搜索 → 业务分发(客户 / 加车 / 开单)。
1446
+ * UI 层在 pubsub 订阅回调里注入 `bridge`(uiHosts / action / cart.confirm*)。
1447
+ *
1448
+ * @example
1449
+ * ```ts
1450
+ * await bookingTicket.handleGlobalScanCode(payload.data, {
1451
+ * openProductDetail: uiHosts.openProductDetail,
1452
+ * confirmDetail: cart.confirmDetail,
1453
+ * openOrder: ({ order_id }) => action({ type: 'pisell1.handleOpenCachedBigSale', data: { order_id } }),
1454
+ * });
1455
+ * ```
1456
+ */
1457
+ async handleGlobalScanCode(code, bridge) {
1458
+ const trimmed = typeof code === 'string' ? code.trim() : '';
1459
+ if (!trimmed) {
1460
+ bridge?.onNotify?.('fail', 'pisell2.text.scan-global-failed');
1461
+ return {
1462
+ status: 'failed',
1463
+ reason: 'empty_code'
441
1464
  };
442
1465
  }
1466
+ try {
1467
+ const localSearch = v => this.store.products.findProductsByCodeOrBarcode(v);
1468
+ const scanCallback = (0, _handleScan.handleGlobalScan)(this.request, localSearch, this.getSubmitOrderSalesChannel());
1469
+ const raw = await scanCallback({
1470
+ type: 'nativeScanner',
1471
+ value: trimmed
1472
+ });
1473
+ const formatted = (0, _formatGlobalScan.formatGlobalScanResult)(raw, trimmed);
1474
+ if (!formatted) {
1475
+ bridge?.onNotify?.('fail', 'pisell2.text.scan-global-failed');
1476
+ return {
1477
+ status: 'failed',
1478
+ reason: 'not_found'
1479
+ };
1480
+ }
1481
+ const result = await (0, _applyGlobalScan.applyGlobalScan)({
1482
+ setOrderCustomer: customer => this.setOrderCustomer(customer),
1483
+ getOrderCustomerSnapshot: () => this.getOrderCustomerSnapshot(),
1484
+ getBusinessCode: () => this.otherParams?.businessCode,
1485
+ buildAddProductCtx: extra => this.buildAddProductCtx(extra),
1486
+ decideAddProduct: (item, options) => this.decideAddProduct(item, options),
1487
+ decideAfterDetail: (cacheItem, options) => this.decideAfterDetail(cacheItem, options),
1488
+ transformDetailToProductAndBooking: input => this.transformDetailToProductAndBooking(input),
1489
+ addProductToOrder: (product, booking) => this.addProductToOrder(product, booking)
1490
+ }, formatted, bridge);
1491
+ bridge?.refresh?.();
1492
+ return result;
1493
+ } catch (error) {
1494
+ console.error('[BookingTicket] handleGlobalScanCode failed', error);
1495
+ bridge?.onNotify?.('fail', 'pisell2.text.scan-global-failed');
1496
+ return {
1497
+ status: 'failed',
1498
+ reason: 'not_found'
1499
+ };
1500
+ }
1501
+ }
1502
+ resolveBestAddTimePlan(addTimeProducts, targetMinutes) {
1503
+ return (0, _resolveBestAddTimePlan.resolveBestAddTimePlan)(addTimeProducts, targetMinutes);
443
1504
  }
444
1505
 
445
1506
  /**
446
- * 获取其他参数
447
- * @returns 其他参数
1507
+ * 构造一条绑定到已有 booking 的加时商品行。
1508
+ *
1509
+ * @example
1510
+ * const line = this.buildAddTimeOrderLine({ product, product_add_schedule_time: 30 }, 'booking-1');
448
1511
  */
449
- async getOtherParams() {
450
- return this.otherParams;
1512
+ buildAddTimeOrderLine(input, bookingUid) {
1513
+ const product = input?.product;
1514
+ if (!product || typeof product !== 'object') {
1515
+ throw new Error('[BookingTicket] addAddTimeProductToBooking: product 不能为空');
1516
+ }
1517
+ const quantity = normalizeAddTimeQuantity(input.num ?? product.num);
1518
+ const price = resolveAddTimeProductPrice(input.price, product);
1519
+ const addScheduleTime = resolveAddTimeScheduleMinutes(input.product_add_schedule_time, product);
1520
+ const payload = {
1521
+ id: product.id ?? product.product_id,
1522
+ product_id: product.product_id ?? product.id,
1523
+ product_variant_id: product.product_variant_id ?? product.variant_id ?? 0,
1524
+ variant_id: product.variant_id ?? product.product_variant_id ?? 0,
1525
+ quantity,
1526
+ num: quantity,
1527
+ price,
1528
+ selling_price: price,
1529
+ original_price: product.original_price ?? price,
1530
+ line_total: price,
1531
+ total: price,
1532
+ product_sku: product.product_sku,
1533
+ option: product.option ?? product.options ?? product.product_sku?.option ?? [],
1534
+ bundle: product.bundle ?? product.bundles ?? product.product_bundle ?? []
1535
+ };
1536
+ const orderLine = this.transformBaseProductToOrderProduct({
1537
+ payload,
1538
+ fallbackProductId: product.id ?? product.product_id,
1539
+ sourceProduct: product,
1540
+ sourceItem: product
1541
+ });
1542
+ if (!orderLine) {
1543
+ throw new Error('[BookingTicket] addAddTimeProductToBooking: product transform failed');
1544
+ }
1545
+ const metadata = {
1546
+ ...(orderLine.metadata || {}),
1547
+ ...(addScheduleTime !== undefined ? {
1548
+ product_add_schedule_time: addScheduleTime
1549
+ } : {}),
1550
+ booking_uid: bookingUid
1551
+ };
1552
+ return {
1553
+ ...orderLine,
1554
+ booking_uid: bookingUid,
1555
+ metadata
1556
+ };
1557
+ }
1558
+
1559
+ /**
1560
+ * 将加时商品作为独立商品行绑定到已有 booking。
1561
+ *
1562
+ * 与预约商品加车不同,本方法不会把当前 booking 作为 `addProductToOrder` 第二参数传入,
1563
+ * 避免 OrderModule 新建 booking 或重写已有 booking.product_uid。
1564
+ *
1565
+ * @example
1566
+ * await bookingTicket.addAddTimeProductToBooking({
1567
+ * booking,
1568
+ * product,
1569
+ * num: 1,
1570
+ * price: '10.00',
1571
+ * product_add_schedule_time: 30,
1572
+ * });
1573
+ */
1574
+ async addAddTimeProductToBooking(input) {
1575
+ return this.addAddTimeProductsToBooking({
1576
+ booking: input.booking,
1577
+ products: [input],
1578
+ coveredMinutes: input.coveredMinutes,
1579
+ bookingPatch: input.bookingPatch
1580
+ });
1581
+ }
1582
+
1583
+ /**
1584
+ * 批量将加时商品作为独立商品行绑定到已有 booking,并只触发一次购物车重算。
1585
+ *
1586
+ * @example
1587
+ * await bookingTicket.addAddTimeProductsToBooking({
1588
+ * booking,
1589
+ * products: [{ product, product_add_schedule_time: 30 }],
1590
+ * });
1591
+ */
1592
+ async addAddTimeProductsToBooking(input) {
1593
+ const bookingUid = resolveBookingUidForAddTime(input?.booking);
1594
+ if (!bookingUid) {
1595
+ throw new Error('[BookingTicket] addAddTimeProductToBooking: booking.metadata.unique_identification_number 缺失');
1596
+ }
1597
+ const orderLines = (input.products || []).map(line => ({
1598
+ product: this.buildAddTimeOrderLine(line, bookingUid)
1599
+ }));
1600
+ if (!this.store.order) throw new Error('order 模块未初始化');
1601
+ const coveredMinutes = normalizeCoveredMinutes(input.coveredMinutes);
1602
+ const bookingTimePatch = input.bookingPatch ?? buildAddTimeBookingTimePatch(input.booking, coveredMinutes);
1603
+ if (bookingTimePatch) {
1604
+ this.store.order.updateOrderBooking({
1605
+ unique_identification_number: bookingUid,
1606
+ updates: bookingTimePatch
1607
+ });
1608
+ }
1609
+ if (typeof this.store.order.addProductsToOrder === 'function') {
1610
+ return this.store.order.addProductsToOrder(orderLines, {
1611
+ skipEditDiscountConfigRefresh: true,
1612
+ skipDiscountRecalculation: true
1613
+ });
1614
+ }
1615
+ const result = [];
1616
+ for (const line of orderLines) {
1617
+ result.splice(0, result.length, ...(await this.addProductToOrder(line.product)));
1618
+ }
1619
+ return result;
1620
+ }
1621
+ async setOtherParams(params, {
1622
+ cover = false
1623
+ } = {}) {
1624
+ super.setOtherParams(params, {
1625
+ cover
1626
+ });
1627
+ if (params?.businessCode) {
1628
+ await this.configureIdGeneratorFromOpenData();
1629
+ }
1630
+ }
1631
+ getDeviceTaskPlugin() {
1632
+ const app = this.core.getPlugin('app');
1633
+ return app?.getApp()?.getPlugin('deviceTask') || null;
1634
+ }
1635
+
1636
+ // 打印手环,单个或批量都可以
1637
+ async handlePrintWristband(params) {
1638
+ const deviceTask = this.getDeviceTaskPlugin();
1639
+ if (!deviceTask?.dispatch) {
1640
+ this.logger.addLog({
1641
+ type: 'info',
1642
+ title: '[BookingTicket] handlePrintWristband: deviceTask.dispatch 不可用',
1643
+ metadata: {
1644
+ voucher_ids: Array.isArray(params.voucher_ids) ? params.voucher_ids : [params.voucher_ids]
1645
+ }
1646
+ });
1647
+ return;
1648
+ }
1649
+ this.logger.addLog({
1650
+ type: 'info',
1651
+ title: '[BookingTicket] handlePrintWristband',
1652
+ metadata: {
1653
+ voucher_ids: Array.isArray(params.voucher_ids) ? params.voucher_ids : [params.voucher_ids]
1654
+ }
1655
+ });
1656
+ const deviceId = await this.getShortNumberOrDeviceId();
1657
+ const idempotencyKey = (0, _utils.createUuidV4)();
1658
+ deviceTask.dispatch({
1659
+ action: 'print_all',
1660
+ device_id: deviceId,
1661
+ payload: {
1662
+ type: 'WRISTBAND',
1663
+ data: {
1664
+ order_id: this.getOrderIdentity()?.orderId,
1665
+ voucher_ids: Array.isArray(params.voucher_ids) ? params.voucher_ids : [params.voucher_ids]
1666
+ }
1667
+ },
1668
+ idempotency_key: idempotencyKey,
1669
+ name: 'PRINT_WRISTBAND',
1670
+ source: 'bookingTicket',
1671
+ source_type: 'handlePrintWristband',
1672
+ source_id: idempotencyKey
1673
+ });
1674
+ }
1675
+
1676
+ // 触发核销
1677
+ async handleRedeem(params) {
1678
+ const voucherIds = Array.isArray(params.voucher_ids) ? params.voucher_ids : [];
1679
+ const status = params.is_checkin ? 'used' : 'unused';
1680
+ this.logger.addLog({
1681
+ type: 'info',
1682
+ title: '[BookingTicket] handleRedeem',
1683
+ metadata: {
1684
+ voucher_ids: voucherIds,
1685
+ status
1686
+ }
1687
+ });
1688
+ const voucherIdSet = new Set(voucherIds);
1689
+ const tempOrder = this.store.order?.getTempOrder?.();
1690
+ const vouchers = tempOrder?.vouchers || this.store.order?.getVouchers() || [];
1691
+ let hasUpdatedVoucher = false;
1692
+ const nextVouchers = vouchers.map(voucher => {
1693
+ if (!voucherIdSet.has(voucher.voucher_id)) return voucher;
1694
+ hasUpdatedVoucher = true;
1695
+ return {
1696
+ ...voucher,
1697
+ status
1698
+ };
1699
+ });
1700
+ if (tempOrder && hasUpdatedVoucher) {
1701
+ await this.replaceTempOrder({
1702
+ ...tempOrder,
1703
+ vouchers: nextVouchers
1704
+ });
1705
+ }
1706
+ this.core.effects.emit(`${this.name}:onVouchersChange`, {});
1707
+ if (voucherIds.length === 0) {
1708
+ return {
1709
+ code: 200,
1710
+ status: true,
1711
+ message: '',
1712
+ data: []
1713
+ };
1714
+ }
1715
+ const identity = this.getOrderIdentity();
1716
+ const externalSaleNumber = tempOrder?.external_sale_number || identity?.externalSaleNumber || undefined;
1717
+ const orderId = tempOrder?.order_id ?? identity?.orderId ?? undefined;
1718
+ const orderLookup = externalSaleNumber || orderId || identity?.orderNumber || identity?.shopOrderNumber || identity?.shopFullOrderNumber || undefined;
1719
+ return this.request.post('/machinecode/batch', {
1720
+ ids: voucherIds,
1721
+ status,
1722
+ order_lookup: orderLookup,
1723
+ order_id: orderId,
1724
+ external_sale_number: externalSaleNumber
1725
+ }, {
1726
+ osServer: true
1727
+ });
451
1728
  }
452
1729
 
453
1730
  /**
454
- * 销毁模块
1731
+ * 销毁模块:先调用父类(销毁所有 store 内子模块 + emit destroy + super.destroy()),
1732
+ * 再清理 BookingTicket 自有资源(scan 监听 + scan 内存缓存)。
455
1733
  */
456
- destroy() {
457
- this.store.cart?.destroy();
458
- this.store.products?.destroy();
459
- this.store.accountList?.destroy();
460
- this.store.customer?.destroy();
461
- this.store.order?.destroy();
462
- this.core.effects.offByModuleDestroy(this.name);
463
- this.core.unregisterModule(this);
464
- // 清除所有的监听
465
- this.scan.removeAllListeners();
466
- // 清除scan数据内存缓存
1734
+ async destroy() {
1735
+ await super.destroy();
1736
+ try {
1737
+ this.scan?.removeAllListeners();
1738
+ } catch (error) {
1739
+ console.warn('[BookingTicket] scan removeAllListeners 失败', error);
1740
+ }
467
1741
  _scanCache.default.clear();
468
1742
  }
469
1743
  }