@pisell/pisellos 0.0.568 → 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 (413) hide show
  1. package/dist/model/strategy/adapter/promotion/evaluator.js +3 -2
  2. package/dist/model/strategy/adapter/walletPass/utils.js +7 -4
  3. package/dist/modules/BaseModule.d.ts +2 -2
  4. package/dist/modules/BaseModule.js +4 -4
  5. package/dist/modules/BookingContext/index.d.ts +48 -0
  6. package/dist/modules/BookingContext/index.js +566 -0
  7. package/dist/modules/BookingContext/types.d.ts +102 -0
  8. package/dist/modules/BookingContext/types.js +51 -0
  9. package/dist/modules/BookingContext/utils/buildCacheItemFromOrderLine.d.ts +16 -0
  10. package/dist/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +302 -0
  11. package/dist/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.d.ts +8 -0
  12. package/dist/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +149 -0
  13. package/dist/modules/BookingContext/utils/cacheItemToBookingInput.d.ts +76 -0
  14. package/dist/modules/BookingContext/utils/cacheItemToBookingInput.js +249 -0
  15. package/dist/modules/BookingContext/utils/flexible.d.ts +28 -0
  16. package/dist/modules/BookingContext/utils/flexible.js +56 -0
  17. package/dist/modules/BookingContext/utils/formatResourceList.d.ts +8 -0
  18. package/dist/modules/BookingContext/utils/formatResourceList.js +38 -0
  19. package/dist/modules/BookingContext/utils/index.d.ts +11 -0
  20. package/dist/modules/BookingContext/utils/index.js +11 -0
  21. package/dist/modules/BookingContext/utils/noResource.d.ts +13 -0
  22. package/dist/modules/BookingContext/utils/noResource.js +77 -0
  23. package/dist/modules/BookingContext/utils/orderLineDisplay.d.ts +1 -0
  24. package/dist/modules/BookingContext/utils/orderLineDisplay.js +36 -0
  25. package/dist/modules/BookingContext/utils/productExtend.d.ts +72 -0
  26. package/dist/modules/BookingContext/utils/productExtend.js +339 -0
  27. package/dist/modules/BookingContext/utils/resourceErrors.d.ts +41 -0
  28. package/dist/modules/BookingContext/utils/resourceErrors.js +74 -0
  29. package/dist/modules/BookingContext/utils/resourceSelection.d.ts +7 -0
  30. package/dist/modules/BookingContext/utils/resourceSelection.js +24 -0
  31. package/dist/modules/BookingContext/utils/resources.d.ts +80 -0
  32. package/dist/modules/BookingContext/utils/resources.js +486 -0
  33. package/dist/modules/BookingContext/utils/serviceTimes.d.ts +55 -0
  34. package/dist/modules/BookingContext/utils/serviceTimes.js +151 -0
  35. package/dist/modules/BookingContext/utils/timeSlices.d.ts +42 -0
  36. package/dist/modules/BookingContext/utils/timeSlices.js +100 -0
  37. package/dist/modules/Cart/utils/cartProduct.js +26 -5
  38. package/dist/modules/Cart/utils/changePrice.js +27 -20
  39. package/dist/modules/Customer/index.d.ts +20 -0
  40. package/dist/modules/Customer/index.js +172 -83
  41. package/dist/modules/Customer/types.d.ts +2 -0
  42. package/dist/modules/Discount/index.d.ts +2 -1
  43. package/dist/modules/Discount/index.js +37 -9
  44. package/dist/modules/Discount/types.d.ts +1 -0
  45. package/dist/modules/OpenData/index.d.ts +8 -0
  46. package/dist/modules/OpenData/index.js +36 -16
  47. package/dist/modules/Order/index.d.ts +448 -23
  48. package/dist/modules/Order/index.js +5417 -561
  49. package/dist/modules/Order/types.d.ts +693 -88
  50. package/dist/modules/Order/types.js +68 -1
  51. package/dist/modules/Order/utils/bundleDiscountHydration.d.ts +5 -0
  52. package/dist/modules/Order/utils/bundleDiscountHydration.js +144 -0
  53. package/dist/modules/Order/utils/manualProductDiscount.d.ts +106 -0
  54. package/dist/modules/Order/utils/manualProductDiscount.js +210 -0
  55. package/dist/modules/Order/utils/orderCollectionIdentity.d.ts +53 -0
  56. package/dist/modules/Order/utils/orderCollectionIdentity.js +405 -0
  57. package/dist/modules/Order/utils.d.ts +167 -18
  58. package/dist/modules/Order/utils.js +960 -165
  59. package/dist/modules/Payment/index.d.ts +10 -9
  60. package/dist/modules/Payment/index.js +465 -703
  61. package/dist/modules/Payment/types.d.ts +40 -16
  62. package/dist/modules/Payment/utils.d.ts +1 -1
  63. package/dist/modules/Payment/utils.js +4 -3
  64. package/dist/modules/Payment/walletpass.js +44 -29
  65. package/dist/modules/Product/types.d.ts +29 -7
  66. package/dist/modules/ProductList/index.d.ts +17 -13
  67. package/dist/modules/ProductList/index.js +184 -74
  68. package/dist/modules/ProductList/types.d.ts +17 -0
  69. package/dist/modules/Quotation/index.d.ts +22 -2
  70. package/dist/modules/Quotation/index.js +108 -42
  71. package/dist/modules/Quotation/types.d.ts +19 -0
  72. package/dist/modules/ResourcePlanner/index.d.ts +24 -0
  73. package/dist/modules/ResourcePlanner/index.js +181 -0
  74. package/dist/modules/ResourcePlanner/planner.d.ts +14 -0
  75. package/dist/modules/ResourcePlanner/planner.js +1069 -0
  76. package/dist/modules/ResourcePlanner/types.d.ts +227 -0
  77. package/dist/modules/ResourcePlanner/types.js +1 -0
  78. package/dist/modules/Rules/index.d.ts +8 -10
  79. package/dist/modules/Rules/index.js +221 -109
  80. package/dist/modules/Rules/types.d.ts +14 -3
  81. package/dist/modules/SalesSummary/index.d.ts +23 -26
  82. package/dist/modules/SalesSummary/index.js +152 -46
  83. package/dist/modules/SalesSummary/types.d.ts +9 -1
  84. package/dist/modules/SalesSummary/utils.d.ts +3 -22
  85. package/dist/modules/SalesSummary/utils.js +803 -163
  86. package/dist/modules/Schedule/getDateIsInSchedule.js +14 -7
  87. package/dist/modules/Schedule/index.d.ts +18 -5
  88. package/dist/modules/Schedule/index.js +78 -24
  89. package/dist/modules/Schedule/types.d.ts +2 -0
  90. package/dist/modules/Summary/index.js +4 -3
  91. package/dist/modules/SurchargeList/index.d.ts +18 -0
  92. package/dist/modules/SurchargeList/index.js +185 -0
  93. package/dist/modules/SurchargeList/types.d.ts +15 -0
  94. package/dist/modules/SurchargeList/types.js +1 -0
  95. package/dist/modules/index.d.ts +3 -0
  96. package/dist/modules/index.js +4 -1
  97. package/dist/plugins/app.d.ts +2 -0
  98. package/dist/plugins/request.d.ts +1 -0
  99. package/dist/server/index.d.ts +462 -8
  100. package/dist/server/index.js +6112 -656
  101. package/dist/server/modules/floor-plan/index.d.ts +39 -0
  102. package/dist/server/modules/floor-plan/index.js +595 -0
  103. package/dist/server/modules/floor-plan/types.d.ts +43 -0
  104. package/dist/server/modules/floor-plan/types.js +13 -0
  105. package/dist/server/modules/index.d.ts +7 -2
  106. package/dist/server/modules/index.js +6 -0
  107. package/dist/server/modules/order/index.d.ts +207 -14
  108. package/dist/server/modules/order/index.js +2326 -379
  109. package/dist/server/modules/order/types.d.ts +76 -5
  110. package/dist/server/modules/order/types.js +14 -1
  111. package/dist/server/modules/order/utils/filterBookings.d.ts +7 -1
  112. package/dist/server/modules/order/utils/filterBookings.js +118 -11
  113. package/dist/server/modules/order/utils/filterOrders.js +242 -14
  114. package/dist/server/modules/payment/index.d.ts +28 -0
  115. package/dist/server/modules/payment/index.js +305 -0
  116. package/dist/server/modules/payment/types.d.ts +9 -0
  117. package/dist/server/modules/payment/types.js +1 -0
  118. package/dist/server/modules/products/index.d.ts +105 -14
  119. package/dist/server/modules/products/index.js +1393 -516
  120. package/dist/server/modules/products/types.d.ts +34 -1
  121. package/dist/server/modules/products/types.js +4 -0
  122. package/dist/server/modules/quotation/index.d.ts +39 -0
  123. package/dist/server/modules/quotation/index.js +278 -66
  124. package/dist/server/modules/resource/index.d.ts +2 -3
  125. package/dist/server/modules/resource/index.js +108 -86
  126. package/dist/server/modules/schedule/index.d.ts +6 -4
  127. package/dist/server/modules/schedule/index.js +116 -85
  128. package/dist/server/test.json +713 -0
  129. package/dist/server/types.d.ts +2 -0
  130. package/dist/server/utils/product.js +2 -2
  131. package/dist/server/utils/small-ticket.d.ts +71 -0
  132. package/dist/server/utils/small-ticket.js +963 -0
  133. package/dist/solution/BaseSales/index.d.ts +378 -0
  134. package/dist/solution/BaseSales/index.js +4006 -0
  135. package/dist/solution/BaseSales/types.d.ts +394 -0
  136. package/dist/solution/BaseSales/types.js +40 -0
  137. package/dist/solution/BaseSales/utils/cartPromotion.d.ts +287 -0
  138. package/dist/solution/BaseSales/utils/cartPromotion.js +1450 -0
  139. package/dist/solution/BaseSales/utils/parseSalesResponse.d.ts +144 -0
  140. package/dist/solution/BaseSales/utils/parseSalesResponse.js +157 -0
  141. package/dist/solution/BaseSales/utils/quotationPrice.d.ts +18 -0
  142. package/dist/solution/BaseSales/utils/quotationPrice.js +250 -0
  143. package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.d.ts +63 -0
  144. package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +218 -0
  145. package/dist/solution/BaseSales/utils.d.ts +40 -0
  146. package/dist/solution/BaseSales/utils.js +263 -0
  147. package/dist/solution/BookingByStep/index.d.ts +1 -1
  148. package/dist/solution/BookingTicket/index.d.ts +394 -25
  149. package/dist/solution/BookingTicket/index.js +2506 -443
  150. package/dist/solution/BookingTicket/types.d.ts +191 -6
  151. package/dist/solution/BookingTicket/types.js +83 -10
  152. package/dist/solution/BookingTicket/utils/addProductDecision.d.ts +117 -0
  153. package/dist/solution/BookingTicket/utils/addProductDecision.js +346 -0
  154. package/dist/solution/BookingTicket/utils/addTimeAvailability.d.ts +35 -0
  155. package/dist/solution/BookingTicket/utils/addTimeAvailability.js +95 -0
  156. package/dist/solution/BookingTicket/utils/bookingStatus.d.ts +77 -0
  157. package/dist/solution/BookingTicket/utils/bookingStatus.js +136 -0
  158. package/dist/solution/BookingTicket/utils/cartView.d.ts +29 -0
  159. package/dist/solution/BookingTicket/utils/cartView.js +209 -0
  160. package/dist/solution/BookingTicket/utils/exampleData.d.ts +123 -0
  161. package/dist/solution/BookingTicket/utils/exampleData.js +183 -0
  162. package/dist/solution/BookingTicket/utils/orderCustomer.d.ts +4 -0
  163. package/dist/solution/BookingTicket/utils/orderCustomer.js +31 -0
  164. package/dist/solution/BookingTicket/utils/resolveBestAddTimePlan.d.ts +185 -0
  165. package/dist/solution/BookingTicket/utils/resolveBestAddTimePlan.js +438 -0
  166. package/dist/solution/BookingTicket/utils/scan/applyGlobalScan.d.ts +36 -0
  167. package/dist/solution/BookingTicket/utils/scan/applyGlobalScan.js +400 -0
  168. package/dist/solution/BookingTicket/utils/scan/cloudSearch.d.ts +4 -0
  169. package/dist/solution/BookingTicket/utils/scan/cloudSearch.js +41 -1
  170. package/dist/solution/BookingTicket/utils/scan/formatGlobalScan.d.ts +32 -0
  171. package/dist/solution/BookingTicket/utils/scan/formatGlobalScan.js +56 -0
  172. package/dist/solution/BookingTicket/utils/scan/handleScan.d.ts +3 -1
  173. package/dist/solution/BookingTicket/utils/scan/handleScan.js +11 -3
  174. package/dist/solution/BookingTicket/utils/scan/index.d.ts +5 -0
  175. package/dist/solution/BookingTicket/utils/scan/index.js +20 -8
  176. package/dist/solution/BookingTicket/utils/scan/scanProductValues.d.ts +21 -0
  177. package/dist/solution/BookingTicket/utils/scan/scanProductValues.js +150 -0
  178. package/dist/solution/BookingTicket/utils/sessionCatalogStale.d.ts +23 -0
  179. package/dist/solution/BookingTicket/utils/sessionCatalogStale.js +78 -0
  180. package/dist/solution/Checkout/index.d.ts +1 -0
  181. package/dist/solution/Checkout/index.js +25 -34
  182. package/dist/solution/Checkout/utils/index.js +4 -4
  183. package/dist/solution/RegisterAndLogin/config.js +2 -10
  184. package/dist/solution/Sales/index.d.ts +50 -5
  185. package/dist/solution/Sales/index.js +484 -92
  186. package/dist/solution/Sales/types.d.ts +26 -2
  187. package/dist/solution/ScanOrder/index.d.ts +27 -21
  188. package/dist/solution/ScanOrder/index.js +499 -434
  189. package/dist/solution/ScanOrder/types.d.ts +33 -11
  190. package/dist/solution/ScanOrder/types.js +4 -5
  191. package/dist/solution/ScanOrder/utils.d.ts +4 -1
  192. package/dist/solution/ScanOrder/utils.js +109 -34
  193. package/dist/solution/ShopDiscount/index.js +4 -4
  194. package/dist/solution/UnifiedBookingSales/index.d.ts +183 -0
  195. package/dist/solution/UnifiedBookingSales/index.js +1871 -0
  196. package/dist/solution/UnifiedBookingSales/types.d.ts +143 -0
  197. package/dist/solution/UnifiedBookingSales/types.js +11 -0
  198. package/dist/solution/VenueBooking/index.d.ts +29 -20
  199. package/dist/solution/VenueBooking/index.js +301 -213
  200. package/dist/solution/VenueBooking/sales-section-4-annotated.json +0 -2
  201. package/dist/solution/VenueBooking/utils/slotMerge.d.ts +6 -0
  202. package/dist/solution/VenueBooking/utils/slotMerge.js +9 -3
  203. package/dist/solution/index.d.ts +2 -0
  204. package/dist/solution/index.js +3 -1
  205. package/dist/types/index.d.ts +11 -1
  206. package/lib/model/strategy/adapter/promotion/evaluator.js +3 -2
  207. package/lib/model/strategy/adapter/walletPass/utils.js +6 -4
  208. package/lib/modules/BaseModule.d.ts +2 -2
  209. package/lib/modules/BaseModule.js +4 -4
  210. package/lib/modules/BookingContext/index.d.ts +48 -0
  211. package/lib/modules/BookingContext/index.js +390 -0
  212. package/lib/modules/BookingContext/types.d.ts +102 -0
  213. package/lib/modules/BookingContext/types.js +48 -0
  214. package/lib/modules/BookingContext/utils/buildCacheItemFromOrderLine.d.ts +16 -0
  215. package/lib/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +286 -0
  216. package/lib/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.d.ts +8 -0
  217. package/lib/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +148 -0
  218. package/lib/modules/BookingContext/utils/cacheItemToBookingInput.d.ts +76 -0
  219. package/lib/modules/BookingContext/utils/cacheItemToBookingInput.js +242 -0
  220. package/lib/modules/BookingContext/utils/flexible.d.ts +28 -0
  221. package/lib/modules/BookingContext/utils/flexible.js +63 -0
  222. package/lib/modules/BookingContext/utils/formatResourceList.d.ts +8 -0
  223. package/lib/modules/BookingContext/utils/formatResourceList.js +35 -0
  224. package/lib/modules/BookingContext/utils/index.d.ts +11 -0
  225. package/lib/modules/BookingContext/utils/index.js +126 -0
  226. package/lib/modules/BookingContext/utils/noResource.d.ts +13 -0
  227. package/lib/modules/BookingContext/utils/noResource.js +78 -0
  228. package/lib/modules/BookingContext/utils/orderLineDisplay.d.ts +1 -0
  229. package/lib/modules/BookingContext/utils/orderLineDisplay.js +29 -0
  230. package/lib/modules/BookingContext/utils/productExtend.d.ts +72 -0
  231. package/lib/modules/BookingContext/utils/productExtend.js +330 -0
  232. package/lib/modules/BookingContext/utils/resourceErrors.d.ts +41 -0
  233. package/lib/modules/BookingContext/utils/resourceErrors.js +80 -0
  234. package/lib/modules/BookingContext/utils/resourceSelection.d.ts +7 -0
  235. package/lib/modules/BookingContext/utils/resourceSelection.js +24 -0
  236. package/lib/modules/BookingContext/utils/resources.d.ts +80 -0
  237. package/lib/modules/BookingContext/utils/resources.js +419 -0
  238. package/lib/modules/BookingContext/utils/serviceTimes.d.ts +55 -0
  239. package/lib/modules/BookingContext/utils/serviceTimes.js +161 -0
  240. package/lib/modules/BookingContext/utils/timeSlices.d.ts +42 -0
  241. package/lib/modules/BookingContext/utils/timeSlices.js +114 -0
  242. package/lib/modules/Cart/utils/cartProduct.js +27 -5
  243. package/lib/modules/Cart/utils/changePrice.js +20 -14
  244. package/lib/modules/Customer/index.d.ts +20 -0
  245. package/lib/modules/Customer/index.js +63 -10
  246. package/lib/modules/Customer/types.d.ts +2 -0
  247. package/lib/modules/Discount/index.d.ts +2 -1
  248. package/lib/modules/Discount/index.js +18 -2
  249. package/lib/modules/Discount/types.d.ts +1 -0
  250. package/lib/modules/OpenData/index.d.ts +8 -0
  251. package/lib/modules/OpenData/index.js +21 -3
  252. package/lib/modules/Order/index.d.ts +448 -23
  253. package/lib/modules/Order/index.js +3641 -308
  254. package/lib/modules/Order/types.d.ts +693 -88
  255. package/lib/modules/Order/types.js +55 -1
  256. package/lib/modules/Order/utils/bundleDiscountHydration.d.ts +5 -0
  257. package/lib/modules/Order/utils/bundleDiscountHydration.js +106 -0
  258. package/lib/modules/Order/utils/manualProductDiscount.d.ts +106 -0
  259. package/lib/modules/Order/utils/manualProductDiscount.js +207 -0
  260. package/lib/modules/Order/utils/orderCollectionIdentity.d.ts +53 -0
  261. package/lib/modules/Order/utils/orderCollectionIdentity.js +353 -0
  262. package/lib/modules/Order/utils.d.ts +167 -18
  263. package/lib/modules/Order/utils.js +974 -138
  264. package/lib/modules/Payment/index.d.ts +10 -9
  265. package/lib/modules/Payment/index.js +30 -124
  266. package/lib/modules/Payment/types.d.ts +40 -16
  267. package/lib/modules/Payment/utils.d.ts +1 -1
  268. package/lib/modules/Payment/utils.js +4 -3
  269. package/lib/modules/Payment/walletpass.js +16 -6
  270. package/lib/modules/Product/types.d.ts +29 -7
  271. package/lib/modules/ProductList/index.d.ts +17 -13
  272. package/lib/modules/ProductList/index.js +92 -51
  273. package/lib/modules/ProductList/types.d.ts +17 -0
  274. package/lib/modules/Quotation/index.d.ts +22 -2
  275. package/lib/modules/Quotation/index.js +76 -20
  276. package/lib/modules/Quotation/types.d.ts +19 -0
  277. package/lib/modules/ResourcePlanner/index.d.ts +24 -0
  278. package/lib/modules/ResourcePlanner/index.js +147 -0
  279. package/lib/modules/ResourcePlanner/planner.d.ts +14 -0
  280. package/lib/modules/ResourcePlanner/planner.js +895 -0
  281. package/lib/modules/ResourcePlanner/types.d.ts +227 -0
  282. package/lib/modules/ResourcePlanner/types.js +5 -0
  283. package/lib/modules/Rules/index.d.ts +8 -10
  284. package/lib/modules/Rules/index.js +152 -60
  285. package/lib/modules/Rules/types.d.ts +14 -3
  286. package/lib/modules/SalesSummary/index.d.ts +23 -26
  287. package/lib/modules/SalesSummary/index.js +84 -16
  288. package/lib/modules/SalesSummary/types.d.ts +9 -1
  289. package/lib/modules/SalesSummary/utils.d.ts +3 -22
  290. package/lib/modules/SalesSummary/utils.js +616 -111
  291. package/lib/modules/Schedule/getDateIsInSchedule.js +14 -7
  292. package/lib/modules/Schedule/index.d.ts +18 -5
  293. package/lib/modules/Schedule/index.js +49 -14
  294. package/lib/modules/Schedule/types.d.ts +2 -0
  295. package/lib/modules/Summary/index.js +2 -2
  296. package/lib/modules/SurchargeList/index.d.ts +18 -0
  297. package/lib/modules/SurchargeList/index.js +95 -0
  298. package/lib/modules/SurchargeList/types.d.ts +15 -0
  299. package/lib/modules/SurchargeList/types.js +5 -0
  300. package/lib/modules/index.d.ts +3 -0
  301. package/lib/modules/index.js +33 -0
  302. package/lib/plugins/app.d.ts +2 -0
  303. package/lib/plugins/request.d.ts +1 -0
  304. package/lib/server/index.d.ts +462 -8
  305. package/lib/server/index.js +3901 -205
  306. package/lib/server/modules/floor-plan/index.d.ts +39 -0
  307. package/lib/server/modules/floor-plan/index.js +306 -0
  308. package/lib/server/modules/floor-plan/types.d.ts +43 -0
  309. package/lib/server/modules/floor-plan/types.js +19 -0
  310. package/lib/server/modules/index.d.ts +7 -2
  311. package/lib/server/modules/index.js +21 -0
  312. package/lib/server/modules/order/index.d.ts +207 -14
  313. package/lib/server/modules/order/index.js +1324 -141
  314. package/lib/server/modules/order/types.d.ts +76 -5
  315. package/lib/server/modules/order/types.js +11 -1
  316. package/lib/server/modules/order/utils/filterBookings.d.ts +7 -1
  317. package/lib/server/modules/order/utils/filterBookings.js +119 -11
  318. package/lib/server/modules/order/utils/filterOrders.js +238 -13
  319. package/lib/server/modules/payment/index.d.ts +28 -0
  320. package/lib/server/modules/payment/index.js +168 -0
  321. package/lib/server/modules/payment/types.d.ts +9 -0
  322. package/lib/server/modules/payment/types.js +5 -0
  323. package/lib/server/modules/products/index.d.ts +105 -14
  324. package/lib/server/modules/products/index.js +588 -97
  325. package/lib/server/modules/products/types.d.ts +34 -1
  326. package/lib/server/modules/products/types.js +3 -0
  327. package/lib/server/modules/quotation/index.d.ts +39 -0
  328. package/lib/server/modules/quotation/index.js +145 -1
  329. package/lib/server/modules/resource/index.d.ts +2 -3
  330. package/lib/server/modules/resource/index.js +71 -45
  331. package/lib/server/modules/schedule/index.d.ts +6 -4
  332. package/lib/server/modules/schedule/index.js +55 -44
  333. package/lib/server/test.json +713 -0
  334. package/lib/server/types.d.ts +2 -0
  335. package/lib/server/utils/product.js +2 -2
  336. package/lib/server/utils/small-ticket.d.ts +71 -0
  337. package/lib/server/utils/small-ticket.js +926 -0
  338. package/lib/solution/BaseSales/index.d.ts +378 -0
  339. package/lib/solution/BaseSales/index.js +2367 -0
  340. package/lib/solution/BaseSales/types.d.ts +394 -0
  341. package/lib/solution/BaseSales/types.js +47 -0
  342. package/lib/solution/BaseSales/utils/cartPromotion.d.ts +287 -0
  343. package/lib/solution/BaseSales/utils/cartPromotion.js +1155 -0
  344. package/lib/solution/BaseSales/utils/parseSalesResponse.d.ts +144 -0
  345. package/lib/solution/BaseSales/utils/parseSalesResponse.js +140 -0
  346. package/lib/solution/BaseSales/utils/quotationPrice.d.ts +18 -0
  347. package/lib/solution/BaseSales/utils/quotationPrice.js +231 -0
  348. package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.d.ts +63 -0
  349. package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +215 -0
  350. package/lib/solution/BaseSales/utils.d.ts +40 -0
  351. package/lib/solution/BaseSales/utils.js +259 -0
  352. package/lib/solution/BookingByStep/index.d.ts +1 -1
  353. package/lib/solution/BookingTicket/index.d.ts +394 -25
  354. package/lib/solution/BookingTicket/index.js +1350 -76
  355. package/lib/solution/BookingTicket/types.d.ts +191 -6
  356. package/lib/solution/BookingTicket/types.js +71 -10
  357. package/lib/solution/BookingTicket/utils/addProductDecision.d.ts +117 -0
  358. package/lib/solution/BookingTicket/utils/addProductDecision.js +353 -0
  359. package/lib/solution/BookingTicket/utils/addTimeAvailability.d.ts +35 -0
  360. package/lib/solution/BookingTicket/utils/addTimeAvailability.js +94 -0
  361. package/lib/solution/BookingTicket/utils/bookingStatus.d.ts +77 -0
  362. package/lib/solution/BookingTicket/utils/bookingStatus.js +136 -0
  363. package/lib/solution/BookingTicket/utils/cartView.d.ts +29 -0
  364. package/lib/solution/BookingTicket/utils/cartView.js +200 -0
  365. package/lib/solution/BookingTicket/utils/exampleData.d.ts +123 -0
  366. package/lib/solution/BookingTicket/utils/exampleData.js +185 -0
  367. package/lib/solution/BookingTicket/utils/orderCustomer.d.ts +4 -0
  368. package/lib/solution/BookingTicket/utils/orderCustomer.js +35 -0
  369. package/lib/solution/BookingTicket/utils/resolveBestAddTimePlan.d.ts +185 -0
  370. package/lib/solution/BookingTicket/utils/resolveBestAddTimePlan.js +387 -0
  371. package/lib/solution/BookingTicket/utils/scan/applyGlobalScan.d.ts +36 -0
  372. package/lib/solution/BookingTicket/utils/scan/applyGlobalScan.js +220 -0
  373. package/lib/solution/BookingTicket/utils/scan/cloudSearch.d.ts +4 -0
  374. package/lib/solution/BookingTicket/utils/scan/cloudSearch.js +24 -3
  375. package/lib/solution/BookingTicket/utils/scan/formatGlobalScan.d.ts +32 -0
  376. package/lib/solution/BookingTicket/utils/scan/formatGlobalScan.js +64 -0
  377. package/lib/solution/BookingTicket/utils/scan/handleScan.d.ts +3 -1
  378. package/lib/solution/BookingTicket/utils/scan/handleScan.js +4 -2
  379. package/lib/solution/BookingTicket/utils/scan/index.d.ts +5 -0
  380. package/lib/solution/BookingTicket/utils/scan/index.js +14 -2
  381. package/lib/solution/BookingTicket/utils/scan/scanProductValues.d.ts +21 -0
  382. package/lib/solution/BookingTicket/utils/scan/scanProductValues.js +115 -0
  383. package/lib/solution/BookingTicket/utils/sessionCatalogStale.d.ts +23 -0
  384. package/lib/solution/BookingTicket/utils/sessionCatalogStale.js +83 -0
  385. package/lib/solution/Checkout/index.d.ts +1 -0
  386. package/lib/solution/Checkout/index.js +18 -19
  387. package/lib/solution/Checkout/utils/index.js +4 -4
  388. package/lib/solution/RegisterAndLogin/config.js +2 -10
  389. package/lib/solution/Sales/index.d.ts +50 -5
  390. package/lib/solution/Sales/index.js +296 -39
  391. package/lib/solution/Sales/types.d.ts +26 -2
  392. package/lib/solution/ScanOrder/index.d.ts +27 -21
  393. package/lib/solution/ScanOrder/index.js +144 -103
  394. package/lib/solution/ScanOrder/types.d.ts +33 -11
  395. package/lib/solution/ScanOrder/types.js +4 -5
  396. package/lib/solution/ScanOrder/utils.d.ts +4 -1
  397. package/lib/solution/ScanOrder/utils.js +98 -26
  398. package/lib/solution/ShopDiscount/index.js +3 -3
  399. package/lib/solution/UnifiedBookingSales/index.d.ts +183 -0
  400. package/lib/solution/UnifiedBookingSales/index.js +1050 -0
  401. package/lib/solution/UnifiedBookingSales/types.d.ts +143 -0
  402. package/lib/solution/UnifiedBookingSales/types.js +14 -0
  403. package/lib/solution/VenueBooking/index.d.ts +29 -20
  404. package/lib/solution/VenueBooking/index.js +169 -94
  405. package/lib/solution/VenueBooking/sales-section-4-annotated.json +0 -2
  406. package/lib/solution/VenueBooking/utils/slotMerge.d.ts +6 -0
  407. package/lib/solution/VenueBooking/utils/slotMerge.js +9 -3
  408. package/lib/solution/index.d.ts +2 -0
  409. package/lib/solution/index.js +22 -0
  410. package/lib/types/index.d.ts +11 -1
  411. package/package.json +1 -1
  412. package/dist/solution/Checkout/appointmentDemo.json +0 -1
  413. package/lib/solution/Checkout/appointmentDemo.json +0 -1
@@ -3,7 +3,15 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
+ Object.defineProperty(exports, "buildManualProductDiscountItem", {
7
+ enumerable: true,
8
+ get: function () {
9
+ return _manualProductDiscount.buildManualProductDiscountItem;
10
+ }
11
+ });
6
12
  exports.buildSubmitPayload = buildSubmitPayload;
13
+ exports.calculateOrderProductsDeposit = calculateOrderProductsDeposit;
14
+ exports.clearTempOrderHolderAssignments = clearTempOrderHolderAssignments;
7
15
  exports.composeLinePrice = composeLinePrice;
8
16
  exports.createDefaultOrderRulesHooks = createDefaultOrderRulesHooks;
9
17
  exports.createDefaultTempOrder = createDefaultTempOrder;
@@ -13,22 +21,111 @@ Object.defineProperty(exports, "createEmptySummary", {
13
21
  return _utils2.createEmptySummary;
14
22
  }
15
23
  });
16
- exports.createUuidV4 = createUuidV4;
24
+ Object.defineProperty(exports, "createUuidV4", {
25
+ enumerable: true,
26
+ get: function () {
27
+ return _orderCollectionIdentity.createUuidV4;
28
+ }
29
+ });
17
30
  exports.ensureCartItemOriginHolder = ensureCartItemOriginHolder;
18
- exports.filterProductsForScanOrderMore = filterProductsForScanOrderMore;
31
+ Object.defineProperty(exports, "ensureManualProductDiscountList", {
32
+ enumerable: true,
33
+ get: function () {
34
+ return _manualProductDiscount.ensureManualProductDiscountList;
35
+ }
36
+ });
37
+ exports.ensureProductSku = ensureProductSku;
38
+ Object.defineProperty(exports, "findManualProductDiscountItem", {
39
+ enumerable: true,
40
+ get: function () {
41
+ return _manualProductDiscount.findManualProductDiscountItem;
42
+ }
43
+ });
19
44
  exports.formatDateTime = formatDateTime;
20
45
  exports.formatV1Product = formatV1Product;
21
46
  exports.getAllDiscountList = exports.generateDuration = void 0;
47
+ exports.getBundleSellingMagnitude = getBundleSellingMagnitude;
48
+ exports.getBundleSignedUnit = getBundleSignedUnit;
49
+ exports.getOrderProductLineUid = getOrderProductLineUid;
50
+ exports.getProductSkuOptions = getProductSkuOptions;
51
+ exports.getRuntimeProductOrigin = getRuntimeProductOrigin;
52
+ exports.hasAssignedHolderId = hasAssignedHolderId;
53
+ exports.indexOrderProductsByUid = indexOrderProductsByUid;
54
+ exports.isEmptyOrderProductDisplayValue = isEmptyOrderProductDisplayValue;
55
+ exports.isHolderConfigRequired = isHolderConfigRequired;
56
+ exports.isMarkdownBundle = isMarkdownBundle;
22
57
  exports.isTempOrder = isTempOrder;
58
+ exports.mapPaymentItemToOrderPayment = mapPaymentItemToOrderPayment;
59
+ exports.mapPaymentItemsToOrderPayments = mapPaymentItemsToOrderPayments;
60
+ Object.defineProperty(exports, "mergeManualProductDiscountIntoList", {
61
+ enumerable: true,
62
+ get: function () {
63
+ return _manualProductDiscount.mergeManualProductDiscountIntoList;
64
+ }
65
+ });
66
+ exports.mergeOrderProductDisplayFields = mergeOrderProductDisplayFields;
23
67
  exports.mergeRelationForms = void 0;
68
+ exports.normalizeBookingIsAll = normalizeBookingIsAll;
69
+ exports.normalizeBookingSubType = normalizeBookingSubType;
70
+ exports.normalizeOrderProductDiscountList = normalizeOrderProductDiscountList;
71
+ exports.normalizeProductSkuOptions = normalizeProductSkuOptions;
24
72
  exports.normalizeSubmitBooking = normalizeSubmitBooking;
25
73
  exports.normalizeSubmitCollectPaxValue = normalizeSubmitCollectPaxValue;
74
+ exports.productRequiresFormSubject = productRequiresFormSubject;
75
+ exports.resolveEffectivePerUnitDiscount = resolveEffectivePerUnitDiscount;
76
+ exports.resolveIsFormSubject = resolveIsFormSubject;
77
+ Object.defineProperty(exports, "resolveManualDiscountMessage", {
78
+ enumerable: true,
79
+ get: function () {
80
+ return _manualProductDiscount.resolveManualDiscountMessage;
81
+ }
82
+ });
83
+ Object.defineProperty(exports, "resolveManualDiscountOriginTotal", {
84
+ enumerable: true,
85
+ get: function () {
86
+ return _manualProductDiscount.resolveManualDiscountOriginTotal;
87
+ }
88
+ });
89
+ Object.defineProperty(exports, "resolveManualDiscountReasonFromSources", {
90
+ enumerable: true,
91
+ get: function () {
92
+ return _manualProductDiscount.resolveManualDiscountReasonFromSources;
93
+ }
94
+ });
95
+ exports.resolveManualOverrideLineOriginalPrice = resolveManualOverrideLineOriginalPrice;
96
+ exports.resolveRulesManualDiscountFlag = resolveRulesManualDiscountFlag;
97
+ Object.defineProperty(exports, "resolveSafeProductNum", {
98
+ enumerable: true,
99
+ get: function () {
100
+ return _manualProductDiscount.resolveSafeProductNum;
101
+ }
102
+ });
26
103
  exports.resolveSubmitCollectPax = resolveSubmitCollectPax;
104
+ Object.defineProperty(exports, "shouldBuildManualProductDiscount", {
105
+ enumerable: true,
106
+ get: function () {
107
+ return _manualProductDiscount.shouldBuildManualProductDiscount;
108
+ }
109
+ });
110
+ Object.defineProperty(exports, "stripManualProductDiscountItems", {
111
+ enumerable: true,
112
+ get: function () {
113
+ return _manualProductDiscount.stripManualProductDiscountItems;
114
+ }
115
+ });
27
116
  exports.sumOptionUnitPrice = sumOptionUnitPrice;
117
+ Object.defineProperty(exports, "syncManualProductDiscountProductNum", {
118
+ enumerable: true,
119
+ get: function () {
120
+ return _manualProductDiscount.syncManualProductDiscountProductNum;
121
+ }
122
+ });
28
123
  var _dayjs = _interopRequireDefault(require("dayjs"));
29
124
  var _decimal = _interopRequireDefault(require("decimal.js"));
30
125
  var _utils = require("../Holder/utils");
31
126
  var _utils2 = require("../../solution/ScanOrder/utils");
127
+ var _orderCollectionIdentity = require("./utils/orderCollectionIdentity");
128
+ var _manualProductDiscount = require("./utils/manualProductDiscount");
32
129
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
33
130
  /**
34
131
  * 把"含 option 的主商品单价"与 bundle 合成"单行 composite 单价"。
@@ -52,30 +149,274 @@ function composeLinePrice(params) {
52
149
  let total = new _decimal.default(Number(mainPrice) || 0);
53
150
  if (Array.isArray(bundle)) {
54
151
  for (const item of bundle) {
55
- const rawPrice = useOriginalBundle ? item?.original_price ?? item?.product_price ?? item?.price : item?.bundle_selling_price ?? item?.price;
56
- const price = new _decimal.default(Number(rawPrice) || 0);
152
+ // markdown(减价)按带符号净额取减;markup/原价维持相加。
153
+ const signedUnit = getBundleSignedUnit(item, {
154
+ useOriginal: useOriginalBundle
155
+ });
57
156
  const rawNum = item?.num ?? item?.quantity ?? 1;
58
157
  const num = new _decimal.default(Number(rawNum) || 0);
59
- total = total.plus(price.times(num));
158
+ total = total.plus(signedUnit.times(num));
60
159
  }
61
160
  }
62
161
  return total.toDecimalPlaces(2).toFixed(2);
63
162
  }
163
+ function normalizeProductSkuOptions(options) {
164
+ if (!Array.isArray(options)) return [];
165
+ return options.map(option => {
166
+ const normalized = {
167
+ ...(option && typeof option === 'object' ? option : {})
168
+ };
169
+ const addPrice = normalized.add_price ?? normalized.price;
170
+ if (addPrice !== undefined) normalized.add_price = addPrice;
171
+ delete normalized.price;
172
+ return normalized;
173
+ });
174
+ }
175
+ function ensureProductSku(product) {
176
+ const rawSku = product?.product_sku && typeof product.product_sku === 'object' ? product.product_sku : {};
177
+ return {
178
+ ...rawSku,
179
+ option: normalizeProductSkuOptions(rawSku.option)
180
+ };
181
+ }
182
+ function getProductSkuOptions(product) {
183
+ return ensureProductSku(product).option;
184
+ }
64
185
 
65
186
  /**
66
- * 计算 option 单价合计:Σ(option.price × option.num)。
187
+ * 计算 option 单价合计:Σ(option.add_price × option.num)。
188
+ * 兼容入参仍带旧 `price` 的调用方,但运行时归一化后只维护 `add_price`。
67
189
  * 在新语义 v2 下,main_product_original_price = source_product_price + 本函数结果。
68
190
  */
69
191
  function sumOptionUnitPrice(options) {
70
192
  let total = new _decimal.default(0);
71
193
  if (!Array.isArray(options)) return total;
72
194
  for (const opt of options) {
73
- const price = new _decimal.default(Number(opt?.price) || 0);
195
+ const price = new _decimal.default(Number(opt?.add_price ?? opt?.price) || 0);
74
196
  const num = new _decimal.default(Number(opt?.num) || 0);
75
197
  total = total.plus(price.times(num));
76
198
  }
77
199
  return total;
78
200
  }
201
+ function getSafeOrderProductNum(num) {
202
+ const parsedNum = Number(num ?? 1);
203
+ if (!Number.isFinite(parsedNum) || parsedNum < 1) return 1;
204
+ return Math.floor(parsedNum);
205
+ }
206
+ function getOrderProductDepositUnitTotal(product) {
207
+ const metadata = product.metadata || {};
208
+ const paymentPrice = metadata.main_product_attached_bundle_payment_price;
209
+ const mainSellingPrice = metadata.main_product_selling_price;
210
+ const fallbackPrice = product.selling_price;
211
+ return new _decimal.default(Number(paymentPrice ?? mainSellingPrice ?? fallbackPrice) || 0);
212
+ }
213
+ function normalizeDepositProtocols(protocols) {
214
+ if (!Array.isArray(protocols)) return [];
215
+ return protocols.reduce((result, protocol) => {
216
+ const id = Number(protocol?.id);
217
+ if (!Number.isFinite(id)) return result;
218
+ result.push({
219
+ id: Math.floor(id),
220
+ title: String(protocol?.title || '')
221
+ });
222
+ return result;
223
+ }, []);
224
+ }
225
+ function getDepositPolicyIds(depositData) {
226
+ const ids = [...(Array.isArray(depositData?.self_deposit_policy_ids) ? depositData.self_deposit_policy_ids : []), ...(Array.isArray(depositData?.deposit_policy_ids) ? depositData.deposit_policy_ids : [])];
227
+ return ids.reduce((result, id) => {
228
+ const normalizedId = Number(id);
229
+ if (!Number.isFinite(normalizedId)) return result;
230
+ result.push(Math.floor(normalizedId));
231
+ return result;
232
+ }, []);
233
+ }
234
+ function handleOrderProductDeposit(params) {
235
+ const {
236
+ depositData,
237
+ total,
238
+ num
239
+ } = params;
240
+ if (!depositData) return {
241
+ result: false,
242
+ depositTotal: new _decimal.default(0)
243
+ };
244
+ const {
245
+ deposit_fixed,
246
+ deposit_percentage
247
+ } = depositData;
248
+ if (typeof deposit_fixed !== 'string' || typeof deposit_percentage !== 'string') {
249
+ return {
250
+ result: false,
251
+ depositTotal: new _decimal.default(0)
252
+ };
253
+ }
254
+ const depositFixed = new _decimal.default(deposit_fixed || 0);
255
+ const depositPercent = new _decimal.default(deposit_percentage || 0);
256
+ const productTotalPrice = new _decimal.default(total || 0);
257
+ const deposit = depositFixed.plus(depositPercent.times(productTotalPrice));
258
+ return {
259
+ depositTotal: deposit.times(num),
260
+ result: true
261
+ };
262
+ }
263
+ function resolveOrderProductDepositSource(product, runtimeOrigin) {
264
+ const record = product;
265
+ const origin = record._origin || runtimeOrigin || {};
266
+ const originProduct = origin.product || {};
267
+ return {
268
+ ...originProduct,
269
+ ...origin,
270
+ ...record,
271
+ custom_deposit_data: record.custom_deposit_data ?? origin.custom_deposit_data ?? originProduct.custom_deposit_data,
272
+ variant: record.variant ?? origin.variant ?? originProduct.variant
273
+ };
274
+ }
275
+ function getOrderProductDeposit(product, runtimeOrigin) {
276
+ const depositProduct = resolveOrderProductDepositSource(product, runtimeOrigin);
277
+ const depositData = depositProduct.custom_deposit_data;
278
+ if (depositData?.has_deposit != 1) return null;
279
+ const num = getSafeOrderProductNum(product.num);
280
+ const total = getOrderProductDepositUnitTotal(product).times(num);
281
+ const allProtocols = normalizeDepositProtocols(depositData.deposit_policy_data);
282
+ const protocolIds = [];
283
+ let depositTotal = new _decimal.default(0);
284
+ let hasDeposit = false;
285
+ if (typeof depositData.deposit_fixed === 'string' && typeof depositData.deposit_percentage === 'string') {
286
+ const result = handleOrderProductDeposit({
287
+ depositData,
288
+ total,
289
+ num
290
+ });
291
+ if (result.result) {
292
+ depositTotal = result.depositTotal;
293
+ protocolIds.push(...getDepositPolicyIds({
294
+ self_deposit_policy_ids: depositData.self_deposit_policy_ids
295
+ }));
296
+ hasDeposit = true;
297
+ }
298
+ } else {
299
+ const productVariantId = depositProduct.product_variant_id ?? product.product_variant_id;
300
+ if (productVariantId) {
301
+ const variantProduct = (depositProduct.variant || []).find(item => item?.id === productVariantId);
302
+ const variantDepositData = variantProduct?.custom_deposit_data;
303
+ const result = handleOrderProductDeposit({
304
+ depositData: variantDepositData,
305
+ total,
306
+ num
307
+ });
308
+ if (result.result) {
309
+ depositTotal = result.depositTotal;
310
+ protocolIds.push(...getDepositPolicyIds({
311
+ self_deposit_policy_ids: variantDepositData?.self_deposit_policy_ids
312
+ }));
313
+ hasDeposit = true;
314
+ }
315
+ }
316
+ if (Array.isArray(product.product_bundle) && product.product_bundle.length) {
317
+ depositTotal = product.product_bundle.reduce((accumulator, bundleProduct) => {
318
+ const bundleDepositData = bundleProduct?.custom_deposit_data;
319
+ const result = handleOrderProductDeposit({
320
+ depositData: bundleDepositData,
321
+ total: bundleProduct?.price,
322
+ num: getSafeOrderProductNum(bundleProduct?.num || 1)
323
+ });
324
+ if (!result.result) return accumulator;
325
+ protocolIds.push(...getDepositPolicyIds({
326
+ self_deposit_policy_ids: bundleDepositData?.self_deposit_policy_ids
327
+ }));
328
+ hasDeposit = true;
329
+ return accumulator.plus(result.depositTotal);
330
+ }, new _decimal.default(0));
331
+ }
332
+ }
333
+ if (!hasDeposit) return null;
334
+ const protocolIdSet = new Set(protocolIds);
335
+ const protocols = allProtocols.filter(protocol => protocolIdSet.has(protocol.id));
336
+ return {
337
+ total: depositTotal.toNumber(),
338
+ protocols
339
+ };
340
+ }
341
+
342
+ /**
343
+ * 仅基于 tempOrder.products 重算整单定金。
344
+ *
345
+ * 商品折扣在进入定金计算前已体现在商品行价格字段中,
346
+ * 因此这里只按商品定金规则汇总,不再扣减 discount_list;tempOrder.bookings 不参与这里的计算。
347
+ */
348
+ function calculateOrderProductsDeposit(products = [], options) {
349
+ const protocolMap = new Map();
350
+ let total = new _decimal.default(0);
351
+ let hasDeposit = false;
352
+ for (const product of products || []) {
353
+ const productUid = product.metadata?.unique_identification_number;
354
+ const runtimeOrigin = productUid ? options?.productsByUid?.[productUid]?.origin : undefined;
355
+ const deposit = getOrderProductDeposit(product, runtimeOrigin);
356
+ product.deposit = deposit;
357
+ if (!deposit?.total || Number(deposit.total) <= 0) continue;
358
+ total = total.plus(deposit.total);
359
+ for (const protocol of deposit.protocols || []) {
360
+ if (!protocolMap.has(protocol.id)) protocolMap.set(protocol.id, protocol);
361
+ }
362
+ hasDeposit = true;
363
+ }
364
+ const roundedTotal = total.toDecimalPlaces(2, _decimal.default.ROUND_HALF_UP);
365
+ const protocols = [...protocolMap.values()];
366
+ return {
367
+ total: roundedTotal.toNumber(),
368
+ totalText: roundedTotal.toFixed(2),
369
+ protocols,
370
+ deposit_policy_ids: protocols.map(protocol => protocol.id),
371
+ hasDeposit
372
+ };
373
+ }
374
+
375
+ /**
376
+ * 判定套餐子商品是否为 markdown(减价)类型。
377
+ * 与后端口径一致:price_type='markdown' 且 price_type_ext 非 'product_price'(原价子商品)。
378
+ */
379
+ function isMarkdownBundle(bundle) {
380
+ if (!bundle || typeof bundle !== 'object') return false;
381
+ const ext = bundle.price_type_ext ?? bundle.custom_price_type_ext;
382
+ const type = bundle.price_type ?? bundle.custom_price_type;
383
+ return type === 'markdown' && ext !== 'product_price';
384
+ }
385
+
386
+ /**
387
+ * 套餐子商品对「行价」的带符号单位贡献(与后端 markdown 口径对齐)。
388
+ *
389
+ * - markdown(减价):净额 = |catalog 毛价| − Σ(option.add_price×num),**取负**(行内减)。
390
+ * catalog 毛价优先 `price`/`custom_price`(避免读已落净额的 `bundle_selling_price` 造成 option 二次扣减);
391
+ * 仅当毛价缺失(历史/hydrate 仅剩净额)时退回 `bundle_selling_price`,且此时不再扣 option。
392
+ * - markup(加价)/原价子商品:维持 `+(bundle_selling_price ?? price)`,行为不变。
393
+ */
394
+ function getBundleSignedUnit(bundle, options) {
395
+ if (!bundle || typeof bundle !== 'object') return new _decimal.default(0);
396
+ const useOriginal = options?.useOriginal ?? false;
397
+ if (isMarkdownBundle(bundle)) {
398
+ const catalog = useOriginal ? bundle.original_price ?? bundle.product_price ?? bundle.custom_price ?? bundle.base_price ?? bundle.price : bundle.custom_price ?? bundle.product_price ?? bundle.original_price ?? bundle.base_price ?? bundle.price;
399
+ if (catalog !== undefined && catalog !== null && catalog !== '') {
400
+ const raw = new _decimal.default(Number(catalog) || 0);
401
+ // 负值表示价格已是「带符号净额」(如 cart/changePrice 产出的 -3.7),直接取用不再扣 option,
402
+ // 避免与下游 option 二次扣减;正值为毛价(H5 路径,如 5),净额 = price − Σoptions 后取负。
403
+ if (raw.lt(0)) return raw;
404
+ return raw.minus(sumOptionUnitPrice(bundle.option)).negated();
405
+ }
406
+ // 仅剩净额(无毛价)兜底:直接取负,不再扣 option
407
+ return new _decimal.default(Number(bundle.bundle_selling_price) || 0).abs().negated();
408
+ }
409
+ const rawPrice = useOriginal ? bundle.original_price ?? bundle.product_price ?? bundle.price : bundle.bundle_selling_price ?? bundle.price;
410
+ return new _decimal.default(Number(rawPrice) || 0);
411
+ }
412
+
413
+ /**
414
+ * 套餐子商品 `bundle_selling_price` 的存储净额(正值幅度)。
415
+ * markdown 取净额绝对值(3.70);markup/原价维持原值。
416
+ */
417
+ function getBundleSellingMagnitude(bundle) {
418
+ return getBundleSignedUnit(bundle).abs();
419
+ }
79
420
 
80
421
  /**
81
422
  * OrderModule 默认 Rules 钩子工厂。
@@ -89,7 +430,7 @@ function sumOptionUnitPrice(options) {
89
430
  * 不含折扣。**权威源**。
90
431
  * - `metadata.main_product_original_price` = source + Σ(option.price × option.num)(含 option、不含折扣)。
91
432
  * - `metadata.main_product_selling_price` = main_original − 主商品券 per-unit amount(含 option、含折扣)。
92
- * - `payment_price`:出站 payload 中由 `selling_price` 派生以兼容后端(composite)。
433
+ * - `payment_price`:出站 payload 中为行 composite 已减整单折扣均摊价(无折扣时与 `selling_price` 同义)。
93
434
  *
94
435
  * Rules 钩子契约(hook_adapt 方案,Rules 内部保持不变):
95
436
  * - `getProduct.price` / `getProduct.original_price` 回传 **source-level 主价**
@@ -106,16 +447,206 @@ function sumOptionUnitPrice(options) {
106
447
  *
107
448
  * 导出为纯函数方便单测直接驱动钩子,不依赖 OrderModule 实例。
108
449
  */
450
+
451
+ /** Rules calcDiscount 用手动价标记;undefined 表示交给 Rules 用 total/origin_total 推断 */
452
+ function resolveRulesManualDiscountFlag(metadata) {
453
+ if (!metadata) return undefined;
454
+ if (metadata.is_manual_discount === true || metadata.is_manual_discount === 1) {
455
+ return true;
456
+ }
457
+ const priceOverride = metadata.price_override;
458
+ if (priceOverride !== undefined && priceOverride !== null && priceOverride !== '') {
459
+ return true;
460
+ }
461
+ return undefined;
462
+ }
463
+ function normalizeOrderProductDiscountAmount(amount) {
464
+ if (amount === undefined || amount === null) return undefined;
465
+ return String(amount);
466
+ }
467
+
468
+ /**
469
+ * 商品行折扣只保留后端 OrderProductDiscountItem 协议字段。
470
+ * Rules/券计算过程中的 `_num`、`config` 等运行态字段不应进入 tempOrder 或提交 payload。
471
+ */
472
+ function normalizeOrderProductDiscountList(discountList) {
473
+ if (!Array.isArray(discountList)) return [];
474
+ return discountList.filter(item => !!item && typeof item === 'object').map(item => {
475
+ const normalized = {
476
+ discount_id: Number.isFinite(Number(item.discount_id)) ? Number(item.discount_id) : 0
477
+ };
478
+ if (item.order_discount_id === null) {
479
+ normalized.order_discount_id = null;
480
+ } else if (item.order_discount_id !== undefined) {
481
+ const orderDiscountId = Number(item.order_discount_id);
482
+ if (Number.isFinite(orderDiscountId)) {
483
+ normalized.order_discount_id = orderDiscountId;
484
+ }
485
+ }
486
+ if (item.type !== undefined) normalized.type = item.type;
487
+ const amount = normalizeOrderProductDiscountAmount(item.amount);
488
+ if (amount !== undefined) normalized.amount = amount;
489
+ if (item.discount && typeof item.discount === 'object') {
490
+ normalized.discount = {
491
+ ...item.discount
492
+ };
493
+ }
494
+ if (item.metadata === null) {
495
+ normalized.metadata = null;
496
+ } else if (item.metadata && typeof item.metadata === 'object') {
497
+ normalized.metadata = {
498
+ ...item.metadata
499
+ };
500
+ }
501
+ return normalized;
502
+ });
503
+ }
504
+ function resolveSubmitBundleMapId(bundle) {
505
+ const metadataMapId = bundle.metadata?.custom_product_bundle_map_id;
506
+ if (metadataMapId !== undefined && metadataMapId !== null && metadataMapId !== '') {
507
+ return metadataMapId;
508
+ }
509
+ if (bundle._id !== undefined && bundle._id !== null && bundle._id !== '') {
510
+ return bundle._id;
511
+ }
512
+ return undefined;
513
+ }
514
+ function collectSubmitProductDiscountList(product) {
515
+ const submitDiscountList = Array.isArray(product?.discount_list) ? [...product.discount_list] : [];
516
+ for (const bundle of product?.product_bundle || []) {
517
+ if (!bundle || typeof bundle !== 'object') continue;
518
+ const bundleMapId = resolveSubmitBundleMapId(bundle);
519
+ const bundleDiscountList = Array.isArray(bundle.discount_list) ? bundle.discount_list : [];
520
+ for (const discount of bundleDiscountList) {
521
+ if (!discount || typeof discount !== 'object') continue;
522
+ const discountMetadata = discount.metadata && typeof discount.metadata === 'object' ? discount.metadata : {};
523
+ const discountMapId = discountMetadata.custom_product_bundle_map_id !== undefined && discountMetadata.custom_product_bundle_map_id !== null && discountMetadata.custom_product_bundle_map_id !== '' ? discountMetadata.custom_product_bundle_map_id : bundleMapId;
524
+ submitDiscountList.push({
525
+ ...discount,
526
+ ...(discountMapId !== undefined ? {
527
+ metadata: {
528
+ ...discountMetadata,
529
+ custom_product_bundle_map_id: discountMapId
530
+ }
531
+ } : {})
532
+ });
533
+ }
534
+ }
535
+ return normalizeOrderProductDiscountList(submitDiscountList);
536
+ }
537
+
538
+ /**
539
+ * 手动改价(price_override)时行级 original_price(composite 单价):
540
+ * 购物车划线价应对齐 metadata.origin._extend.origin_total(改价前单价 composite,如 40),
541
+ * 而非 bundle 子项 catalog 原价合成的 composedOriginalPrice(如 30)。
542
+ *
543
+ * origin_total 与 SkuDetailModal / cacheItem._extend 一致,为单价语义,不再除以 num。
544
+ */
545
+ function resolveManualOverrideLineOriginalPrice(params, fallbackCompositeOriginal) {
546
+ const metadata = params.metadata || {};
547
+ const priceOverride = metadata.price_override;
548
+ if (priceOverride === undefined || priceOverride === null || priceOverride === '') {
549
+ return fallbackCompositeOriginal;
550
+ }
551
+ const refTotal = metadata.origin?._extend?.origin_total;
552
+ if (refTotal !== undefined && refTotal !== null && refTotal !== '') {
553
+ const parsed = Number(refTotal);
554
+ if (Number.isFinite(parsed)) {
555
+ return new _decimal.default(parsed).toDecimalPlaces(2).toFixed(2);
556
+ }
557
+ }
558
+ const lineOriginal = params.lineOriginalPrice;
559
+ if (lineOriginal !== undefined && lineOriginal !== null && lineOriginal !== '') {
560
+ const parsedLine = Number(lineOriginal);
561
+ const parsedFallback = Number(fallbackCompositeOriginal);
562
+ if (Number.isFinite(parsedLine) && Number.isFinite(parsedFallback) && parsedLine > parsedFallback) {
563
+ return new _decimal.default(parsedLine).toDecimalPlaces(2).toFixed(2);
564
+ }
565
+ }
566
+ return fallbackCompositeOriginal;
567
+ }
568
+
569
+ /**
570
+ * 计算商品行「每单位有效折扣」:discount_list 合计 + 被 Rules 剥离但仍 inPromotion 的差额。
571
+ *
572
+ * Rules.calcDiscount 会把 type=promotion 从 discount_list 去掉,但 metadata._promotion 仍保留;
573
+ * applyProductDiscountPrices 必须回读 _promotion,否则抬价/降价都会在第二步被 source 原价覆盖。
574
+ *
575
+ * @example
576
+ * resolveEffectivePerUnitDiscount({ metadata: { _promotion: { inPromotion: true, originalPrice: 1, finalPrice: 5 } }, discount_list: [] });
577
+ * // => -4
578
+ */
579
+ function resolveEffectivePerUnitDiscount(product) {
580
+ const discountList = Array.isArray(product?.discount_list) ? product.discount_list : [];
581
+ const fromList = discountList.reduce((sum, discount) => sum + Number(discount?.amount || 0), 0);
582
+ const hasPromoDiscountItem = discountList.some(item => item?.type === 'promotion');
583
+ if (hasPromoDiscountItem) return fromList;
584
+ const metadata = product?.metadata || {};
585
+ const promo = metadata._promotion;
586
+ if (promo?.inPromotion !== true) return fromList;
587
+ const optionSum = sumOptionUnitPrice(getProductSkuOptions(product)).toNumber();
588
+
589
+ // applyPromotion 写价后、Rules 剥离 promotion discount 的兜底
590
+ if (!discountList.length && metadata.source_product_price != null && metadata.main_product_selling_price != null) {
591
+ const source = Number(metadata.source_product_price);
592
+ const selling = Number(metadata.main_product_selling_price) - optionSum;
593
+ if (Number.isFinite(source) && Number.isFinite(selling) && source !== selling) {
594
+ return fromList + (source - selling);
595
+ }
596
+ }
597
+ const originalPrice = Number(promo.originalPrice);
598
+ const finalPrice = Number(promo.finalPrice);
599
+ if (!Number.isFinite(originalPrice) || !Number.isFinite(finalPrice)) return fromList;
600
+ return fromList + (originalPrice - finalPrice);
601
+ }
109
602
  function createDefaultOrderRulesHooks() {
110
603
  const toUnitPriceString = (totalLike, num) => {
111
604
  const effectiveNum = Number(num) > 0 ? Number(num) : 1;
112
605
  return new _decimal.default(Number(totalLike) || 0).dividedBy(effectiveNum).toDecimalPlaces(2).toString();
113
606
  };
607
+ const getBundleIdentity = bundle => {
608
+ return [bundle.bundle_id ?? bundle.id ?? '', bundle.bundle_product_id ?? bundle._bundle_product_id ?? bundle.product_id ?? '', bundle.bundle_variant_id ?? bundle.variant_id ?? bundle.product_variant_id ?? 0, bundle.bundle_group_id ?? bundle.group_id ?? ''].join('|');
609
+ };
610
+ const mergeRulesBundlePrices = (currentBundle, rulesBundle) => {
611
+ if (!Array.isArray(rulesBundle)) return rulesBundle;
612
+ const currentByIdentity = new Map();
613
+ (currentBundle || []).forEach(bundle => {
614
+ if (!bundle || typeof bundle !== 'object') return;
615
+ currentByIdentity.set(getBundleIdentity(bundle), bundle);
616
+ });
617
+ return rulesBundle.map(bundle => {
618
+ if (!bundle || typeof bundle !== 'object') return bundle;
619
+ const current = currentByIdentity.get(getBundleIdentity(bundle));
620
+ if (!current) return bundle;
621
+ const bundleWithDisplayFields = {
622
+ ...bundle,
623
+ ...(isEmptyOrderProductDisplayValue(bundle.cover) && !isEmptyOrderProductDisplayValue(current.cover) ? {
624
+ cover: current.cover
625
+ } : {})
626
+ };
627
+ const isProductPriceBundle = (bundle.price_type_ext ?? current.price_type_ext) === 'product_price';
628
+ if (!isProductPriceBundle) return bundleWithDisplayFields;
629
+ const sourcePrice = current.price ?? current.custom_price ?? current.original_price ?? current.bundle_selling_price;
630
+ const originalPrice = current.original_price ?? current.product_price ?? current.price ?? current.custom_price ?? sourcePrice;
631
+ return {
632
+ ...bundleWithDisplayFields,
633
+ ...(sourcePrice !== undefined ? {
634
+ price: sourcePrice
635
+ } : {}),
636
+ ...(originalPrice !== undefined ? {
637
+ original_price: originalPrice
638
+ } : {}),
639
+ ...(current.original_total !== undefined ? {
640
+ original_total: current.original_total
641
+ } : {})
642
+ };
643
+ });
644
+ };
114
645
  return {
115
646
  getProduct: product => {
116
647
  // source_product_price 是权威源;兜底链:source → mainSelling − options(反推)→ 行 selling_price
117
648
  const metadataAny = product.metadata || {};
118
- const optionSum = sumOptionUnitPrice(product.product_option_item);
649
+ const optionSum = sumOptionUnitPrice(getProductSkuOptions(product));
119
650
  let sourcePrice;
120
651
  if (metadataAny.source_product_price !== undefined) {
121
652
  sourcePrice = String(metadataAny.source_product_price);
@@ -136,9 +667,9 @@ function createDefaultOrderRulesHooks() {
136
667
  id: product.product_id,
137
668
  // Rules 引擎用 _id 作为 processedProductsMap 键;必须与购物车行级 identity 一致,
138
669
  // 否则同 SKU 不同小料会在 calcDiscount 重组时互相覆盖。
139
- // 不透明 identity 契约下,normalizeOrderProduct / restoreTempOrderFromStorage 已保证 identity_key 必存在;
670
+ // 不透明 identity 契约下,normalizeOrderProduct / hydrate 已保证唯一值存在;
140
671
  // 留 fingerprint 分支仅作 Rules 单测里直接传裸 product 时的兜底。
141
- _id: product.identity_key ? `${product.product_id}_${product.product_variant_id}_${product.identity_key}` : `${product.product_id}_${product.product_variant_id}_${(0, _utils2.buildProductLineFingerprint)(product.product_option_item, product.product_bundle)}`,
672
+ _id: product.metadata?.unique_identification_number ? `${product.product_id}_${product.product_variant_id}_${product.metadata.unique_identification_number}` : `${product.product_id}_${product.product_variant_id}_${(0, _utils2.buildProductLineFingerprint)(getProductSkuOptions(product), product.product_bundle)}`,
142
673
  // Rules 内部 getProductTotalPrice / getProductOriginTotalPrice 会各自再叠加 bundle + option,
143
674
  // 所以这里必须喂 source-level(不含 option、不含 bundle、不含折扣)单价,避免双加 option。
144
675
  price: sourcePrice,
@@ -150,18 +681,23 @@ function createDefaultOrderRulesHooks() {
150
681
  num: product.num || 1,
151
682
  discount_list: product.discount_list || [],
152
683
  bundle: product.product_bundle || [],
153
- booking_id: product._origin?.booking_id || null,
684
+ booking_id: product.metadata?.booking_id || null,
154
685
  isClient: false,
155
- isManualDiscount: product._origin?.isManualDiscount || false,
156
- holder_id: product._origin?.holder_id,
157
- startDate: product._origin?.startDate
686
+ // 勿写死 false:否则 Rules 不会走 total≠origin_total 的手动价推断,
687
+ // 无券时会把 bundle 价还原成 original_price(手动改价套餐会从 20 回到 30)。
688
+ isManualDiscount: resolveRulesManualDiscountFlag(metadataAny),
689
+ holder_id: product.metadata?.holder_id,
690
+ startDate: product.metadata?.start_date || product.metadata?.startDate,
691
+ main_product_selling_price: metadataAny.main_product_selling_price !== undefined ? new _decimal.default(Number(metadataAny.main_product_selling_price) || 0).minus(optionSum).toDecimalPlaces(2).toString() : undefined,
692
+ inPromotion: metadataAny._promotion?.inPromotion === true,
693
+ _promotion: metadataAny._promotion
158
694
  };
159
695
  },
160
696
  setProduct: (product, values) => {
161
697
  const nextNum = Number(values.quantity ?? product.num ?? 1) || 1;
162
698
  const metadataAny = product.metadata || {};
163
699
  const existedSource = metadataAny.source_product_price ?? product.selling_price ?? '0';
164
- const nextOptions = product.product_option_item;
700
+ const nextOptions = getProductSkuOptions(product);
165
701
  const optionSum = sumOptionUnitPrice(nextOptions);
166
702
 
167
703
  // Rules 返回的 main_product_selling_price / price / total 均为 source-level。
@@ -176,7 +712,7 @@ function createDefaultOrderRulesHooks() {
176
712
  // 含 option 的 main_product_*
177
713
  const nextMainSellingPrice = new _decimal.default(Number(nextSourceSellingPrice) || 0).plus(optionSum).toDecimalPlaces(2).toFixed(2);
178
714
  const nextMainOriginalPrice = new _decimal.default(Number(nextSourceOriginalPrice) || 0).plus(optionSum).toDecimalPlaces(2).toFixed(2);
179
- const nextBundle = values.bundle ?? product.product_bundle;
715
+ const nextBundle = values.bundle !== undefined ? mergeRulesBundlePrices(product.product_bundle, values.bundle) : product.product_bundle;
180
716
  const composedSellingPrice = composeLinePrice({
181
717
  mainPrice: nextMainSellingPrice,
182
718
  bundle: nextBundle
@@ -186,10 +722,15 @@ function createDefaultOrderRulesHooks() {
186
722
  bundle: nextBundle,
187
723
  useOriginalBundle: true
188
724
  });
725
+ const finalOriginalPrice = resolveManualOverrideLineOriginalPrice({
726
+ num: nextNum,
727
+ metadata: metadataAny,
728
+ lineOriginalPrice: product.original_price
729
+ }, composedOriginalPrice);
189
730
  return {
190
731
  ...product,
191
732
  selling_price: composedSellingPrice,
192
- original_price: composedOriginalPrice,
733
+ original_price: finalOriginalPrice,
193
734
  discount_list: values.discount_list ?? product.discount_list,
194
735
  num: values.quantity ?? product.num,
195
736
  product_bundle: nextBundle,
@@ -291,14 +832,6 @@ const getAllDiscountList = cartItem => {
291
832
 
292
833
  // ─── 以下为从 ScanOrder/utils.ts 迁入的 Order 专属工具函数 ───
293
834
  exports.getAllDiscountList = getAllDiscountList;
294
- function createUuidV4() {
295
- const template = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx';
296
- return template.replace(/[xy]/g, char => {
297
- const randomValue = Math.floor(Math.random() * 16);
298
- const value = char === 'x' ? randomValue : randomValue & 0x3 | 0x8;
299
- return value.toString(16);
300
- });
301
- }
302
835
  function isTempOrder(data) {
303
836
  if (!data || typeof data !== 'object') return false;
304
837
  if (!Array.isArray(data.products)) return false;
@@ -315,70 +848,86 @@ function formatDateTime(date) {
315
848
  return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
316
849
  }
317
850
  function normalizeSubmitPlatform(platform) {
318
- return platform?.toLowerCase() === 'pc' ? 'PC' : 'H5';
851
+ const value = String(platform || '').trim();
852
+ if (!value) return 'H5';
853
+ const lower = value.toLowerCase();
854
+ if (lower === 'pc') return 'PC';
855
+ if (lower === 'h5') return 'H5';
856
+ return value;
319
857
  }
320
858
 
321
- // 出站重命名:后端 checkout 协议字段 option_group_item_id;
322
- // tempOrder / UI 运行时一律使用 product_option_item_id。仅保留后端契约字段,
323
- // 与 src/modules/Cart/utils/cartProduct.ts#formatOptionsToOrigin 形状一致。
859
+ // 后端 checkout 协议字段 option_group_item_id;tempOrder 内部也保持同一字段名。
324
860
  function formatSubmitOptionItems(options) {
325
861
  if (!Array.isArray(options)) return [];
326
- return options.map(d => ({
327
- num: d?.num,
328
- option_group_item_id: d?.option_group_item_id ?? d?.product_option_item_id,
329
- option_group_id: d?.option_group_id
330
- }));
862
+ return options.map(d => {
863
+ const option = {
864
+ ...(d && typeof d === 'object' ? d : {}),
865
+ num: d?.num,
866
+ option_group_item_id: d?.option_group_item_id,
867
+ option_group_id: d?.option_group_id
868
+ };
869
+ const addPrice = d?.add_price ?? d?.price;
870
+ if (addPrice !== undefined) option.add_price = String(addPrice);
871
+ delete option.price;
872
+ return option;
873
+ });
331
874
  }
332
875
  function toBundleNumber(value, fallback = 0) {
333
876
  const parsed = Number(value);
334
877
  return Number.isFinite(parsed) ? parsed : fallback;
335
878
  }
336
- function toBundleCustomPriceString(value) {
879
+ function toMoneyString(value) {
337
880
  const parsed = Number(value);
338
881
  if (Number.isFinite(parsed)) return parsed.toFixed(2);
339
882
  if (value === null || value === undefined || value === '') return '0.00';
340
883
  return String(value);
341
884
  }
342
885
 
343
- // 出站补齐:以 additive 方式把 bundle 项补足为后端 checkout 协议所需的形状,
344
- // 同时把 Summary / Rules 运行时写到 bundle 顶层的 surcharge_fee / relation_surcharge_ids
345
- // 和来自 _origin 的 product_discount_difference 汇总到 metadata。
346
- // 保留原对象上已有字段(如 discount_list、metadata.custom_product_bundle_map_id)
347
- // 以兼容运行时 bundle 归一化、Rules、Summary 的消费。
886
+ // 出站补齐:只输出后端 checkout 协议字段,运行时/详情展示字段不进入 bundle payload。
348
887
  function formatSubmitBundleItems(bundle) {
349
888
  if (!Array.isArray(bundle)) return [];
350
889
  return bundle.map(b => {
351
890
  const rawBundle = b && typeof b === 'object' ? b : {};
352
891
  const existedMetadata = rawBundle.metadata && typeof rawBundle.metadata === 'object' ? rawBundle.metadata : {};
353
- const sellingPriceNum = toBundleNumber(rawBundle.bundle_selling_price ?? rawBundle.price, 0);
354
- const priceNum = toBundleNumber(rawBundle.price, sellingPriceNum);
355
- const priceType = rawBundle.price_type ?? '';
356
- // const customPriceStr = toBundleCustomPriceString(
357
- // rawBundle.custom_price ?? rawBundle.bundle_selling_price ?? rawBundle.price,
358
- // );
892
+ const {
893
+ custom_product_bundle_map_id: _customProductBundleMapId,
894
+ ...submitMetadata
895
+ } = existedMetadata;
359
896
 
897
+ // bundle_selling_price 出站存「正净额」:markdown 为 |price| − Σoptions(如 3.70),
898
+ // markup/原价维持原值;与后端口径一致(price 仍为毛价)。
899
+ const sellingPrice = getBundleSellingMagnitude(rawBundle).toFixed(2);
900
+ // payment 价由整单折扣均摊层产出;缺省(无折扣)时与 selling 净额同义。
901
+ const paymentPrice = rawBundle.bundle_payment_price !== undefined && rawBundle.bundle_payment_price !== null && rawBundle.bundle_payment_price !== '' ? toMoneyString(rawBundle.bundle_payment_price) : sellingPrice;
902
+ const priceValue = rawBundle.price ?? rawBundle.custom_price ?? rawBundle.bundle_selling_price;
360
903
  const relationSurchargeIds = Array.isArray(rawBundle.relation_surcharge_ids) ? rawBundle.relation_surcharge_ids : Array.isArray(existedMetadata.relation_surcharge_ids) ? existedMetadata.relation_surcharge_ids : [];
361
- const surchargeFee = toBundleNumber(rawBundle.surcharge_fee ?? existedMetadata.surcharge_fee, 0);
904
+ const surchargeFee = toMoneyString(rawBundle.surcharge_fee ?? existedMetadata.surcharge_fee);
362
905
  const productDiscountDifference = toBundleNumber(existedMetadata.product_discount_difference, 0);
906
+ const num = toBundleNumber(rawBundle.num ?? rawBundle.quantity, 1);
907
+ const bundleMapId = resolveSubmitBundleMapId(rawBundle);
908
+ const hasSubmitBundleDiscounts = Array.isArray(rawBundle.discount_list) && rawBundle.discount_list.length > 0;
363
909
  return {
364
- ...rawBundle,
365
910
  is_charge_tax: rawBundle.is_charge_tax ?? 0,
366
- tax_fee: toBundleNumber(rawBundle.tax_fee, 0),
367
911
  bundle_variant_id: rawBundle.bundle_variant_id ?? 0,
368
- num: toBundleNumber(rawBundle.num, 1),
912
+ num,
913
+ quantity: toBundleNumber(rawBundle.quantity ?? rawBundle.num, num),
369
914
  extension_id: rawBundle.extension_id ?? 0,
370
915
  extension_type: rawBundle.extension_type ?? 'normal',
371
- price: priceNum,
372
- price_type: priceType,
373
- price_type_ext: rawBundle.price_type_ext ?? '',
374
- // custom_price: customPriceStr,
375
- custom_price_type: rawBundle.custom_price_type ?? priceType ?? '',
376
- bundle_selling_price: sellingPriceNum,
916
+ price: priceValue,
917
+ price_type: rawBundle.price_type ?? rawBundle.custom_price_type ?? '',
918
+ price_type_ext: rawBundle.price_type_ext ?? rawBundle.custom_price_type_ext ?? '',
919
+ bundle_selling_price: sellingPrice,
920
+ bundle_payment_price: paymentPrice,
377
921
  option: formatSubmitOptionItems(rawBundle.option),
378
- bundle_group_id: rawBundle?.group_id,
379
- bundle_id: rawBundle?.id,
922
+ bundle_group_id: rawBundle?.bundle_group_id ?? rawBundle?.group_id,
923
+ bundle_id: rawBundle?.bundle_id ?? rawBundle?.id,
924
+ bundle_product_id: rawBundle?.bundle_product_id ?? rawBundle?._bundle_product_id,
925
+ cover: rawBundle.cover,
380
926
  metadata: {
381
- ...existedMetadata,
927
+ ...submitMetadata,
928
+ ...(hasSubmitBundleDiscounts && bundleMapId !== undefined ? {
929
+ custom_product_bundle_map_id: bundleMapId
930
+ } : {}),
382
931
  surcharge_fee: surchargeFee,
383
932
  relation_surcharge_ids: relationSurchargeIds,
384
933
  product_discount_difference: productDiscountDifference
@@ -395,7 +944,7 @@ function formatSubmitBundleItems(bundle) {
395
944
  * = `metadata.main_product_selling_price` + Σ(bundle_selling_price × num)。
396
945
  * - `original_price`:**行 composite 券前单价**
397
946
  * = `metadata.main_product_original_price` + Σ(bundle 原价 × num)。
398
- * - `payment_price`:兼容后端的出站字段,直接从 `selling_price` 派生(composite)。
947
+ * - `payment_price`:兼容后端的出站字段,行 composite 已减整单折扣均摊(无折扣时与 `selling_price` 同义)。
399
948
  * - `metadata.source_product_price`:主商品/variant 基础价(不含 option、不含折扣),权威源。
400
949
  * - `metadata.main_product_original_price`:含 option、不含折扣。
401
950
  * - `metadata.main_product_selling_price`:含 option、含主商品折扣。
@@ -405,43 +954,72 @@ function formatSubmitBundleItems(bundle) {
405
954
  */
406
955
  function normalizeSubmitProduct(product) {
407
956
  const {
408
- _origin,
409
- identity_key,
957
+ unique_identification_number: _runtimeUid,
410
958
  ...submitProduct
411
959
  } = product;
960
+ const {
961
+ num,
962
+ surcharge_fee: _productSurchargeFee,
963
+ discount_amount: _deprecatedDiscountAmount,
964
+ discount_type: _deprecatedDiscountType,
965
+ discountway: _deprecatedDiscountway,
966
+ product_discount_reason: _deprecatedProductDiscountReason,
967
+ discount_per: _deprecatedDiscountPer,
968
+ ...productRest
969
+ } = submitProduct;
970
+ delete productRest[`product_${'option'}_item`];
412
971
  const rawMetadata = submitProduct.metadata || {};
413
972
  const bookingUid = rawMetadata.booking_uid;
414
973
  const cleanMetadata = {};
415
974
  if (rawMetadata.unique_identification_number) {
416
975
  cleanMetadata.unique_identification_number = rawMetadata.unique_identification_number;
417
976
  }
418
- const priceMetaKeys = ['main_product_original_price', 'main_product_selling_price', 'source_product_price', 'price_schema_version'];
977
+ const priceMetaKeys = ['main_product_original_price', 'main_product_selling_price', 'main_product_attached_bundle_selling_price', 'main_product_attached_bundle_payment_price', 'average_discount_amount_rate', 'source_product_price', 'main_product_attached_bundle_surcharge_fee', 'main_product_attached_bundle_tax_fee', 'surcharge_rounding_remainder', 'tax_fee_rounding_remainder', 'relation_surcharge_ids', 'discountable_flag', 'parent_unique_identification_number', 'product_add_schedule_time'];
419
978
  for (const key of priceMetaKeys) {
420
979
  if (rawMetadata[key] !== undefined) {
421
980
  cleanMetadata[key] = rawMetadata[key];
422
981
  }
423
982
  }
983
+ cleanMetadata.product_discount_difference = rawMetadata.product_discount_difference ?? '';
424
984
  if (rawMetadata.price_breakdown) {
425
985
  cleanMetadata.price_breakdown = rawMetadata.price_breakdown;
426
986
  }
427
987
  if (rawMetadata.is_rule !== undefined) {
428
988
  cleanMetadata.is_rule = rawMetadata.is_rule;
429
989
  }
990
+ if (rawMetadata.holder_config !== undefined) {
991
+ cleanMetadata.holder_config = rawMetadata.holder_config;
992
+ }
993
+ const productSku = {
994
+ ...(submitProduct.product_sku && typeof submitProduct.product_sku === 'object' ? submitProduct.product_sku : {}),
995
+ option: formatSubmitOptionItems(submitProduct.product_sku?.option)
996
+ };
430
997
  return {
431
- ...submitProduct,
998
+ ...productRest,
432
999
  ...(bookingUid ? {
433
1000
  booking_uid: bookingUid
434
1001
  } : {}),
435
- product_option_item: formatSubmitOptionItems(submitProduct.product_option_item),
436
- discount_list: submitProduct.discount_list || [],
1002
+ product_quantity: toBundleNumber(num ?? submitProduct.product_quantity, 1),
1003
+ product_sku: productSku,
1004
+ discount_list: collectSubmitProductDiscountList(submitProduct),
437
1005
  product_bundle: formatSubmitBundleItems(submitProduct.product_bundle),
438
1006
  metadata: cleanMetadata,
439
- // 出站兼容:后端消费 payment_price 字段,这里从 selling_price 直接派生。
440
- // 新语义下 selling_price 是 composite(含 option/bundle),payment_price 同语义。
441
- payment_price: submitProduct.selling_price
1007
+ // 出站兼容:后端消费 payment_price 字段(行 composite 已减整单折扣均摊)。
1008
+ // 由整单折扣均摊层产出;缺省(无折扣)时与 selling_price 同义。
1009
+ payment_price: submitProduct.payment_price ?? submitProduct.selling_price
442
1010
  };
443
1011
  }
444
- const SUBMIT_BOOKING_METADATA_WHITELIST = ['unique_identification_number', 'collect_pax'];
1012
+ const SUBMIT_BOOKING_METADATA_WHITELIST = ['unique_identification_number', 'collect_pax', 'capacity', 'holder_id', 'resource_select_type', 'holder'];
1013
+ function normalizeBookingIsAll(booking) {
1014
+ return booking.is_all === true || booking.is_all === 1 || booking.is_all === '1' || booking.is_all === 'true' ? 1 : 0;
1015
+ }
1016
+ function normalizeBookingSubType(booking) {
1017
+ if (normalizeBookingIsAll(booking) === 1) return 'days';
1018
+ if (booking.sub_type !== undefined && booking.sub_type !== null && booking.sub_type !== '') {
1019
+ return String(booking.sub_type);
1020
+ }
1021
+ return 'minutes';
1022
+ }
445
1023
  function normalizeSubmitBooking(booking) {
446
1024
  const rawMetadata = booking.metadata || {};
447
1025
  const cleanMetadata = {};
@@ -450,8 +1028,18 @@ function normalizeSubmitBooking(booking) {
450
1028
  cleanMetadata[key] = rawMetadata[key];
451
1029
  }
452
1030
  }
1031
+ if (cleanMetadata.holder_id === undefined) cleanMetadata.holder_id = null;
1032
+ const bookingRecord = booking;
1033
+ const startDate = bookingRecord.start_date;
1034
+ const shouldRestoreSelectDate = bookingRecord.select_date === undefined || bookingRecord.select_date === null || bookingRecord.select_date === '';
1035
+ const selectDateFromStartDate = typeof startDate === 'string' ? startDate.split(/\s+/)[0] : startDate;
453
1036
  return {
454
1037
  ...booking,
1038
+ ...(shouldRestoreSelectDate && selectDateFromStartDate ? {
1039
+ select_date: selectDateFromStartDate
1040
+ } : {}),
1041
+ is_all: normalizeBookingIsAll(bookingRecord),
1042
+ sub_type: normalizeBookingSubType(bookingRecord),
455
1043
  metadata: cleanMetadata
456
1044
  };
457
1045
  }
@@ -479,27 +1067,26 @@ function resolveSubmitCollectPax(tempOrder) {
479
1067
  return normalizeSubmitCollectPaxValue(tempOrder.metadata?.collect_pax);
480
1068
  }
481
1069
  function createDefaultTempOrder(params) {
482
- const summary = params.summary || (0, _utils2.createEmptySummary)();
483
1070
  return {
484
1071
  order_id: null,
485
- relation_id: undefined,
486
- table_form_id: undefined,
487
- resource_id: undefined,
488
- order_number: '',
489
- shop_order_number: '',
490
- type: 'appointment_booking',
491
- business_code: 'appointment_booking',
1072
+ external_sale_number: '',
1073
+ order_number: null,
1074
+ shop_order_number: null,
1075
+ shop_full_order_number: null,
1076
+ type: '',
1077
+ business_code: undefined,
492
1078
  platform: 'h5',
493
1079
  sales_channel: 'my_pisel',
494
1080
  order_sales_channel: 'online_store',
495
1081
  status: 'normal',
496
1082
  payment_status: 'payment_processing',
497
1083
  shipping_status: 'unfulfilled',
498
- customer_id: null,
1084
+ customer_id: 0,
499
1085
  customer_name: '',
500
1086
  country_calling_code: '',
501
1087
  phone: '',
502
1088
  email: '',
1089
+ customer: null,
503
1090
  is_price_include_tax: 1,
504
1091
  tax_title: '',
505
1092
  tax_country_code: '',
@@ -512,18 +1099,21 @@ function createDefaultTempOrder(params) {
512
1099
  surcharge_fee: '0.00',
513
1100
  note: '',
514
1101
  schedule_date: params.now,
515
- created_at: params.now,
1102
+ created_at: undefined,
1103
+ request_unique_idempotency_token: '',
1104
+ vouchers: [],
516
1105
  products: [],
517
1106
  bookings: [],
518
1107
  payments: [],
519
1108
  surcharges: [],
520
- discount_list: [],
521
1109
  relation_forms: [],
522
1110
  contacts: [],
523
- contacts_info: [],
1111
+ contacts_info: null,
524
1112
  holder: null,
525
- summary,
526
- metadata: {}
1113
+ metadata: {},
1114
+ _extend: {
1115
+ productsByUid: {}
1116
+ }
527
1117
  };
528
1118
  }
529
1119
  function buildSubmitPayload(params) {
@@ -535,51 +1125,52 @@ function buildSubmitPayload(params) {
535
1125
  businessCode,
536
1126
  channel,
537
1127
  type,
1128
+ summary,
538
1129
  enhance
539
1130
  } = params;
540
1131
  const scheduleDate = tempOrder.schedule_date || tempOrder.created_at || formatDateTime(now);
541
- const summary = tempOrder.summary || (0, _utils2.createEmptySummary)();
542
- const relationId = tempOrder.relation_id;
543
- const tableFormId = tempOrder.table_form_id;
544
- const bookingUuid = createUuidV4();
545
- const bookingStart = (0, _dayjs.default)(now);
546
- const bookingDuration = resolveTableOccupancyDuration(tempOrder);
547
- const bookingEnd = bookingStart.add(bookingDuration, 'minute');
548
- const bookings = relationId && tableFormId ? [{
549
- relation_id: 0,
550
- form_id: 0,
551
- start_time: bookingStart.format('HH:mm'),
552
- start_date: bookingStart.format('YYYY-MM-DD'),
553
- end_time: bookingEnd.format('HH:mm'),
554
- end_date: bookingEnd.format('YYYY-MM-DD'),
555
- duration: bookingDuration,
556
- metadata: {
557
- unique_identification_number: bookingUuid,
558
- collect_pax: resolveSubmitCollectPax(tempOrder)
559
- },
560
- select_date: bookingStart.format('YYYY-MM-DD'),
561
- is_all: false,
562
- like_status: 'common',
563
- schedule_id: 0,
564
- relation_type: '',
565
- number: 1
566
- }] : tempOrder.bookings || [];
567
- const formRecordIds = relationId && tableFormId ? [{
568
- form_id: tableFormId,
569
- form_record_id: relationId
570
- }] : undefined;
1132
+ const bookingUuid = (0, _orderCollectionIdentity.createUuidV4)();
571
1133
  const {
572
- created_at: _createdAt,
1134
+ _extend: _extend,
1135
+ relation_id: _relationId,
1136
+ table_form_id: _tableFormId,
1137
+ resource_id: _resourceId,
573
1138
  summary: _summary,
574
- surcharges: _surcharges,
1139
+ lastOrderInfo: _lastOrderInfo,
1140
+ paid_amount: _paidAmount,
1141
+ shop_id: _shopId,
1142
+ shop_note: _shopNote,
1143
+ shop_service_type: _shopServiceType,
1144
+ start_time: _startTime,
1145
+ customer: _customer,
1146
+ discount_list: _discountList,
1147
+ tax_fee: _rootTaxFee,
1148
+ total_amount: _totalAmount,
1149
+ total_refund_amount: _totalRefundAmount,
1150
+ create_date: _createDate,
1151
+ holder_id: _holderId,
575
1152
  ...tempOrderRest
576
1153
  } = tempOrder;
1154
+ let submitType = type ?? tempOrder.type;
1155
+ if (!submitType) {
1156
+ // 如果外部没有传递 types,根据订单里当前是否有 bookings 来决定是 appointment_booking 还是 virtual
1157
+ if (tempOrder?.bookings?.length) {
1158
+ submitType = 'appointment_booking';
1159
+ } else {
1160
+ submitType = 'virtual';
1161
+ }
1162
+ }
577
1163
  const payload = {
578
1164
  ...tempOrderRest,
579
- platform: normalizeSubmitPlatform(platform ?? tempOrder.platform),
580
- request_unique_idempotency_token: cacheId,
581
- type: type ?? tempOrder.type ?? 'table-order',
582
- business_code: businessCode ?? tempOrder.business_code ?? 'table-order',
1165
+ customer_id: tempOrder.customer_id ?? 1,
1166
+ customer_name: tempOrder.customer_name || 'Walk-In',
1167
+ order_number: tempOrder.order_number ?? null,
1168
+ shop_order_number: tempOrder.shop_order_number ?? null,
1169
+ shop_full_order_number: tempOrder.shop_full_order_number ?? null,
1170
+ platform: normalizeSubmitPlatform(platform),
1171
+ request_unique_idempotency_token: params?.request_unique_idempotency_token || tempOrder.request_unique_idempotency_token || `${tempOrder.external_sale_number}_${now.getTime()}`,
1172
+ type: submitType,
1173
+ business_code: businessCode ?? tempOrder.business_code,
583
1174
  sales_channel: tempOrder.sales_channel || 'my_pisel',
584
1175
  order_sales_channel: channel ?? tempOrder.order_sales_channel ?? 'online_store',
585
1176
  status: tempOrder.status || 'normal',
@@ -590,18 +1181,18 @@ function buildSubmitPayload(params) {
590
1181
  is_deposit: tempOrder.is_deposit ?? 0,
591
1182
  deposit_amount: tempOrder.deposit_amount || '0.00',
592
1183
  shop_discount: tempOrder.shop_discount || '0.00',
593
- surcharge_fee: summary.surcharge_fee || tempOrder.surcharge_fee || '0.00',
1184
+ surcharge_fee: tempOrder.surcharge_fee || '0.00',
594
1185
  note: tempOrder.note || '',
1186
+ delivery_type: tempOrder.delivery_type || 'nil',
595
1187
  schedule_date: scheduleDate,
596
- bookings: bookings.map(booking => normalizeSubmitBooking(booking)),
1188
+ // created_at: tempOrder.created_at || formatDateTime(now), 和后端协定这个字段先不提交到接口
1189
+ bookings: (tempOrder.bookings || []).filter(booking => Number(booking.parent_id) !== 0).map(booking => normalizeSubmitBooking(booking)),
597
1190
  payments: tempOrder.payments || [],
598
- // discount_list: tempOrder.discount_list || [],
599
1191
  relation_forms: tempOrder.relation_forms || [],
600
- form_record_ids: formRecordIds,
601
1192
  // contacts: tempOrder.contacts || [],
602
- contacts_info: tempOrder.contacts_info || [],
1193
+ contacts_info: tempOrder.contacts_info && !Array.isArray(tempOrder.contacts_info) ? tempOrder.contacts_info : null,
603
1194
  // holder: tempOrder.holder || null,
604
- // summary,
1195
+ summary: summary || tempOrder.summary || (0, _utils2.createEmptySummary)(),
605
1196
  metadata: (() => {
606
1197
  const {
607
1198
  collect_pax: _collectPax,
@@ -614,23 +1205,96 @@ function buildSubmitPayload(params) {
614
1205
  })(),
615
1206
  products: (tempOrder.products || []).map(product => normalizeSubmitProduct(product))
616
1207
  };
617
- return enhance ? enhance(payload, {
1208
+ if (!enhance) return payload;
1209
+ const enhancedPayload = enhance(payload, {
618
1210
  tempOrder,
619
1211
  bookingUuid,
620
1212
  now
621
- }) : payload;
1213
+ });
1214
+ return {
1215
+ ...enhancedPayload,
1216
+ summary: enhancedPayload.summary ?? payload.summary
1217
+ };
622
1218
  }
623
-
624
- /** 加单(scanOrderMore)不应提交 booking 关联的虚拟规则商品行 */
625
- function filterProductsForScanOrderMore(products) {
626
- return (products || []).filter(p => p.metadata?.is_rule !== true);
1219
+ function mapPaymentItemToOrderPayment(paymentItem) {
1220
+ const item = paymentItem;
1221
+ const customPaymentId = item.custom_payment_id ?? item.id;
1222
+ const metadata = item.metadata && typeof item.metadata === 'object' ? {
1223
+ ...item.metadata
1224
+ } : {};
1225
+ if (item.uuid && !metadata.unique_payment_number) {
1226
+ metadata.unique_payment_number = String(item.uuid);
1227
+ }
1228
+ const paymentMetadataKeys = ['rounding_rule', 'shop_wallet_pass_id', 'actual_paid_amount', 'change_given_amount'];
1229
+ for (const key of paymentMetadataKeys) {
1230
+ if (item[key] !== undefined && metadata[key] === undefined) {
1231
+ metadata[key] = item[key];
1232
+ }
1233
+ }
1234
+ const timestamp = (0, _dayjs.default)().format('YYYY-MM-DD HH:mm:ss');
1235
+ const paymentTime = item.payment_time ?? timestamp;
1236
+ const createdAt = item.created_at ?? timestamp;
1237
+ return {
1238
+ ...(item.order_payment_id !== undefined ? {
1239
+ order_payment_id: item.order_payment_id
1240
+ } : {}),
1241
+ amount: String(item.amount ?? '0.00'),
1242
+ code: String(item.code || ''),
1243
+ custom_payment_id: Number(customPaymentId ?? 0),
1244
+ ...(item.custom_payment_type !== undefined ? {
1245
+ custom_payment_type: String(item.custom_payment_type)
1246
+ } : {}),
1247
+ name: String(item.name || ''),
1248
+ type: String(item.type || ''),
1249
+ ...(item.voucher_id !== undefined ? {
1250
+ voucher_id: item.voucher_id
1251
+ } : {}),
1252
+ ...(item.origin_amount !== undefined ? {
1253
+ origin_amount: String(item.origin_amount)
1254
+ } : {}),
1255
+ ...(item.service_fee !== undefined ? {
1256
+ service_fee: String(item.service_fee)
1257
+ } : {}),
1258
+ ...(item.status !== undefined ? {
1259
+ status: item.status
1260
+ } : {}),
1261
+ payment_time: paymentTime,
1262
+ created_at: createdAt,
1263
+ ...(item.wallet_pass_usage_unit !== undefined ? {
1264
+ wallet_pass_usage_unit: item.wallet_pass_usage_unit
1265
+ } : {}),
1266
+ ...(item.wallet_pass_use_value !== undefined ? {
1267
+ wallet_pass_use_value: item.wallet_pass_use_value
1268
+ } : {}),
1269
+ ...(item.fund_record && typeof item.fund_record === 'object' && !Array.isArray(item.fund_record) ? {
1270
+ fund_record: {
1271
+ ...item.fund_record
1272
+ }
1273
+ } : {}),
1274
+ metadata,
1275
+ ...(item.rounding_amount !== undefined ? {
1276
+ rounding_amount: String(item.rounding_amount)
1277
+ } : {}),
1278
+ ...(item.service_charge !== undefined ? {
1279
+ service_charge: item.service_charge
1280
+ } : {}),
1281
+ ...(item.order_payment_type !== undefined ? {
1282
+ order_payment_type: item.order_payment_type
1283
+ } : {}),
1284
+ ...(item.updated_at !== undefined ? {
1285
+ updated_at: item.updated_at
1286
+ } : {})
1287
+ };
1288
+ }
1289
+ function mapPaymentItemsToOrderPayments(paymentItems, options = {}) {
1290
+ if (!Array.isArray(paymentItems)) return [];
1291
+ return paymentItems.filter(item => options.includeVoided || item.status !== 'voided').map(item => mapPaymentItemToOrderPayment(item));
627
1292
  }
628
1293
 
629
1294
  /**
630
1295
  * 历史 V1 加餐结构映射器。
631
1296
  *
632
- * @deprecated 加餐(`PUT /order/order/product/:id`)已改为与正常 checkout 共用
633
- * `ScanOrderSubmitProduct` 新结构(见 `OrderModule.submitTempOrder` 的加餐分支)。
1297
+ * @deprecated 标准 Sales checkout 已统一提交结构。
634
1298
  * 本函数仅保留以兼容历史接入方,未来会移除。新代码请勿再调用。
635
1299
  */
636
1300
  function formatV1Product(products) {
@@ -638,14 +1302,186 @@ function formatV1Product(products) {
638
1302
  return {
639
1303
  bundle: product.product_bundle,
640
1304
  key: product.product_id,
641
- option: product.product_option_item,
1305
+ option: product.product_sku?.option || [],
642
1306
  product_id: product.product_id,
643
1307
  product_variant_id: product.product_variant_id,
644
- num: product.num,
1308
+ num: product.product_quantity,
645
1309
  note: String(product.note ?? ''),
646
1310
  rowKey: product.product_id,
647
1311
  session: null,
648
- unique: createUuidV4()
1312
+ unique: (0, _orderCollectionIdentity.createUuidV4)()
649
1313
  };
650
1314
  });
1315
+ }
1316
+
1317
+ /**
1318
+ * 判断 holder_id 是否已分配(非空)。
1319
+ *
1320
+ * @example
1321
+ * hasAssignedHolderId(12); // true
1322
+ * hasAssignedHolderId([]); // false
1323
+ */
1324
+ function hasAssignedHolderId(value) {
1325
+ if (value === undefined || value === null || value === '') return false;
1326
+ if (Array.isArray(value)) return value.length > 0;
1327
+ return true;
1328
+ }
1329
+
1330
+ /**
1331
+ * 清空 tempOrder 购物车行上的 holder 分配(products / bookings / runtime origin)。
1332
+ * 移除或更换下单客户时调用,与 booking 页换客户清 holder 行为对齐。
1333
+ *
1334
+ * @example
1335
+ * clearTempOrderHolderAssignments(tempOrder);
1336
+ */
1337
+ function clearTempOrderHolderAssignments(tempOrder) {
1338
+ let changed = false;
1339
+ const resetHolderField = (target, key = 'holder_id') => {
1340
+ if (!hasAssignedHolderId(target[key])) return;
1341
+ target[key] = null;
1342
+ changed = true;
1343
+ };
1344
+ for (const product of tempOrder.products || []) {
1345
+ if (product.metadata && typeof product.metadata === 'object') {
1346
+ resetHolderField(product.metadata);
1347
+ }
1348
+ }
1349
+ for (const booking of tempOrder.bookings || []) {
1350
+ const bookingRecord = booking;
1351
+ if (booking.metadata && typeof booking.metadata === 'object') {
1352
+ resetHolderField(booking.metadata);
1353
+ }
1354
+ if (bookingRecord.holder !== undefined && bookingRecord.holder !== null) {
1355
+ delete bookingRecord.holder;
1356
+ changed = true;
1357
+ }
1358
+ }
1359
+ const productsByUid = tempOrder._extend?.productsByUid;
1360
+ if (productsByUid && typeof productsByUid === 'object') {
1361
+ for (const entry of Object.values(productsByUid)) {
1362
+ const origin = entry?.origin;
1363
+ if (!origin || typeof origin !== 'object') continue;
1364
+ if (origin.metadata && typeof origin.metadata === 'object') {
1365
+ resetHolderField(origin.metadata);
1366
+ }
1367
+ if (origin._extend && typeof origin._extend === 'object') {
1368
+ resetHolderField(origin._extend);
1369
+ }
1370
+ }
1371
+ }
1372
+ return changed;
1373
+ }
1374
+
1375
+ /**
1376
+ * 读取购物车行的 stable uid(优先 metadata.unique_identification_number)。
1377
+ *
1378
+ * @example
1379
+ * getOrderProductLineUid({ metadata: { unique_identification_number: 'line-1' } }); // 'line-1'
1380
+ */
1381
+ function getOrderProductLineUid(product) {
1382
+ if (!product || typeof product !== 'object') return null;
1383
+ const uid = product.metadata?.unique_identification_number ?? product.unique_identification_number;
1384
+ if (uid === undefined || uid === null || uid === '') return null;
1385
+ return String(uid);
1386
+ }
1387
+ function getRuntimeProductOrigin(tempOrder, product) {
1388
+ const uid = getOrderProductLineUid(product);
1389
+ if (uid && tempOrder?._extend?.productsByUid?.[uid]?.origin) {
1390
+ return tempOrder._extend.productsByUid[uid].origin;
1391
+ }
1392
+ const matchedProduct = (tempOrder?.products || []).find(item => item === product || getOrderProductLineUid(item) === uid || String(item?.product_id ?? item?.id ?? '') === String(product?.product_id ?? product?.id ?? ''));
1393
+ const matchedUid = matchedProduct ? getOrderProductLineUid(matchedProduct) : '';
1394
+ return matchedUid ? tempOrder?._extend?.productsByUid?.[matchedUid]?.origin || null : null;
1395
+ }
1396
+ function isHolderConfigRequired(holderConfig) {
1397
+ if (!holderConfig || typeof holderConfig !== 'object') return false;
1398
+ if (holderConfig.status === 'disable') return false;
1399
+ return Number(holderConfig.required) === 1;
1400
+ }
1401
+ function productRequiresFormSubject(tempOrder, product) {
1402
+ const _orderProduct = tempOrder?.products?.find(item => {
1403
+ return item.metadata.unique_identification_number === product._id || item.product_id === product.id;
1404
+ });
1405
+ const runtimeOrigin = getRuntimeProductOrigin(tempOrder, product);
1406
+ return isHolderConfigRequired(_orderProduct?.metadata?.holder_config) || isHolderConfigRequired(runtimeOrigin?.holder_config) || isHolderConfigRequired(product?.holder_config);
1407
+ }
1408
+ function resolveIsFormSubject(tempOrder, product) {
1409
+ debugger;
1410
+ // 旧版预约维度的 holder 类型仍然生效;新版则从当前商品 holder_config 判断。
1411
+ if (tempOrder?.holder?.type === 'form') return true;
1412
+ if (product) return productRequiresFormSubject(tempOrder, product);
1413
+ return (tempOrder?.products || []).some(item => productRequiresFormSubject(tempOrder, item));
1414
+ }
1415
+
1416
+ /**
1417
+ * 判断展示字段是否为空(含 i18n 对象全空)。
1418
+ *
1419
+ * @example
1420
+ * isEmptyOrderProductDisplayValue(''); // true
1421
+ * isEmptyOrderProductDisplayValue({ auto: '桌台费' }); // false
1422
+ */
1423
+ function isEmptyOrderProductDisplayValue(value) {
1424
+ if (value === undefined || value === null) return true;
1425
+ if (typeof value === 'string') return value.trim() === '';
1426
+ if (typeof value === 'object') {
1427
+ const record = value;
1428
+ return !Object.values(record).some(item => typeof item === 'string' && item.trim() !== '');
1429
+ }
1430
+ return false;
1431
+ }
1432
+
1433
+ /**
1434
+ * calcDiscount / applyDiscount 回写时 Rules 往往不带 product_title 等展示字段;
1435
+ * 用旧行补齐,避免编辑态 hydrate 后标题被冲掉。
1436
+ *
1437
+ * @example
1438
+ * mergeOrderProductDisplayFields(
1439
+ * { product_title: { auto: '桌台费' }, metadata: { unique_identification_number: 'u1' } },
1440
+ * { selling_price: '10.00', metadata: { unique_identification_number: 'u1' } },
1441
+ * );
1442
+ */
1443
+ function mergeOrderProductDisplayFields(previous, next) {
1444
+ const merged = {
1445
+ ...next
1446
+ };
1447
+ const displayFields = ['product_title', 'product_name', 'product_cover', 'cover', 'product_cover', 'variant_title', 'product_sku', 'payment_price'];
1448
+ displayFields.forEach(field => {
1449
+ if (isEmptyOrderProductDisplayValue(merged[field]) && !isEmptyOrderProductDisplayValue(previous[field])) {
1450
+ merged[field] = previous[field];
1451
+ }
1452
+ });
1453
+ if (previous.metadata || next.metadata) {
1454
+ merged.metadata = {
1455
+ ...(previous.metadata || {}),
1456
+ ...(next.metadata || {})
1457
+ };
1458
+ ['product_title', 'product_name'].forEach(field => {
1459
+ if (isEmptyOrderProductDisplayValue(merged.metadata[field]) && !isEmptyOrderProductDisplayValue(previous.metadata?.[field])) {
1460
+ merged.metadata[field] = previous.metadata[field];
1461
+ }
1462
+ });
1463
+ }
1464
+ if (previous._extend && !merged._extend) {
1465
+ merged._extend = previous._extend;
1466
+ }
1467
+ return merged;
1468
+ }
1469
+
1470
+ /**
1471
+ * 按 uid 索引 tempOrder.products,供 applyDiscount 合并展示字段。
1472
+ *
1473
+ * @example
1474
+ * indexOrderProductsByUid([{ metadata: { unique_identification_number: 'u1' } }]);
1475
+ */
1476
+ function indexOrderProductsByUid(products) {
1477
+ const map = new Map();
1478
+ (products || []).forEach((product, index) => {
1479
+ const uid = getOrderProductLineUid(product);
1480
+ if (uid) {
1481
+ map.set(uid, product);
1482
+ return;
1483
+ }
1484
+ map.set(`__index__:${index}`, product);
1485
+ });
1486
+ return map;
651
1487
  }