@pisell/pisellos 0.0.567 → 0.0.569

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (557) hide show
  1. package/dist/core/index.js +6 -9
  2. package/dist/model/strategy/adapter/promotion/evaluator.js +3 -2
  3. package/dist/model/strategy/adapter/walletPass/evaluator.js +0 -2
  4. package/dist/model/strategy/adapter/walletPass/example.js +3 -26
  5. package/dist/model/strategy/adapter/walletPass/utils.js +7 -7
  6. package/dist/model/strategy/strategy-example.js +5 -19
  7. package/dist/modules/BaseModule.d.ts +2 -2
  8. package/dist/modules/BaseModule.js +4 -4
  9. package/dist/modules/BookingContext/index.d.ts +48 -0
  10. package/dist/modules/BookingContext/index.js +566 -0
  11. package/dist/modules/BookingContext/types.d.ts +102 -0
  12. package/dist/modules/BookingContext/types.js +51 -0
  13. package/dist/modules/BookingContext/utils/buildCacheItemFromOrderLine.d.ts +16 -0
  14. package/dist/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +302 -0
  15. package/dist/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.d.ts +8 -0
  16. package/dist/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +149 -0
  17. package/dist/modules/BookingContext/utils/cacheItemToBookingInput.d.ts +76 -0
  18. package/dist/modules/BookingContext/utils/cacheItemToBookingInput.js +249 -0
  19. package/dist/modules/BookingContext/utils/flexible.d.ts +28 -0
  20. package/dist/modules/BookingContext/utils/flexible.js +56 -0
  21. package/dist/modules/BookingContext/utils/formatResourceList.d.ts +8 -0
  22. package/dist/modules/BookingContext/utils/formatResourceList.js +38 -0
  23. package/dist/modules/BookingContext/utils/index.d.ts +11 -0
  24. package/dist/modules/BookingContext/utils/index.js +11 -0
  25. package/dist/modules/BookingContext/utils/noResource.d.ts +13 -0
  26. package/dist/modules/BookingContext/utils/noResource.js +77 -0
  27. package/dist/modules/BookingContext/utils/orderLineDisplay.d.ts +1 -0
  28. package/dist/modules/BookingContext/utils/orderLineDisplay.js +36 -0
  29. package/dist/modules/BookingContext/utils/productExtend.d.ts +72 -0
  30. package/dist/modules/BookingContext/utils/productExtend.js +339 -0
  31. package/dist/modules/BookingContext/utils/resourceErrors.d.ts +41 -0
  32. package/dist/modules/BookingContext/utils/resourceErrors.js +74 -0
  33. package/dist/modules/BookingContext/utils/resourceSelection.d.ts +7 -0
  34. package/dist/modules/BookingContext/utils/resourceSelection.js +24 -0
  35. package/dist/modules/BookingContext/utils/resources.d.ts +80 -0
  36. package/dist/modules/BookingContext/utils/resources.js +486 -0
  37. package/dist/modules/BookingContext/utils/serviceTimes.d.ts +55 -0
  38. package/dist/modules/BookingContext/utils/serviceTimes.js +151 -0
  39. package/dist/modules/BookingContext/utils/timeSlices.d.ts +42 -0
  40. package/dist/modules/BookingContext/utils/timeSlices.js +100 -0
  41. package/dist/modules/Cart/index.js +0 -3
  42. package/dist/modules/Cart/utils/cartProduct.js +26 -6
  43. package/dist/modules/Cart/utils/changePrice.js +27 -20
  44. package/dist/modules/Customer/index.d.ts +20 -0
  45. package/dist/modules/Customer/index.js +172 -83
  46. package/dist/modules/Customer/types.d.ts +2 -0
  47. package/dist/modules/Date/index.js +1 -5
  48. package/dist/modules/Discount/index.d.ts +2 -1
  49. package/dist/modules/Discount/index.js +37 -12
  50. package/dist/modules/Discount/types.d.ts +1 -0
  51. package/dist/modules/OpenData/index.d.ts +8 -0
  52. package/dist/modules/OpenData/index.js +35 -17
  53. package/dist/modules/Order/index.d.ts +449 -24
  54. package/dist/modules/Order/index.js +5437 -608
  55. package/dist/modules/Order/types.d.ts +693 -88
  56. package/dist/modules/Order/types.js +68 -1
  57. package/dist/modules/Order/utils/bundleDiscountHydration.d.ts +5 -0
  58. package/dist/modules/Order/utils/bundleDiscountHydration.js +144 -0
  59. package/dist/modules/Order/utils/manualProductDiscount.d.ts +106 -0
  60. package/dist/modules/Order/utils/manualProductDiscount.js +210 -0
  61. package/dist/modules/Order/utils/orderCollectionIdentity.d.ts +53 -0
  62. package/dist/modules/Order/utils/orderCollectionIdentity.js +405 -0
  63. package/dist/modules/Order/utils.d.ts +167 -18
  64. package/dist/modules/Order/utils.js +960 -165
  65. package/dist/modules/Payment/index.d.ts +10 -9
  66. package/dist/modules/Payment/index.js +508 -874
  67. package/dist/modules/Payment/types.d.ts +40 -16
  68. package/dist/modules/Payment/utils.d.ts +1 -1
  69. package/dist/modules/Payment/utils.js +4 -3
  70. package/dist/modules/Payment/walletpass.js +48 -34
  71. package/dist/modules/Product/types.d.ts +29 -7
  72. package/dist/modules/ProductList/index.d.ts +17 -13
  73. package/dist/modules/ProductList/index.js +184 -74
  74. package/dist/modules/ProductList/types.d.ts +17 -0
  75. package/dist/modules/Quotation/index.d.ts +22 -2
  76. package/dist/modules/Quotation/index.js +108 -42
  77. package/dist/modules/Quotation/types.d.ts +19 -0
  78. package/dist/modules/ResourcePlanner/index.d.ts +24 -0
  79. package/dist/modules/ResourcePlanner/index.js +181 -0
  80. package/dist/modules/ResourcePlanner/planner.d.ts +14 -0
  81. package/dist/modules/ResourcePlanner/planner.js +1069 -0
  82. package/dist/modules/ResourcePlanner/types.d.ts +227 -0
  83. package/dist/modules/ResourcePlanner/types.js +1 -0
  84. package/dist/modules/Rules/index.d.ts +8 -10
  85. package/dist/modules/Rules/index.js +219 -114
  86. package/dist/modules/Rules/types.d.ts +14 -3
  87. package/dist/modules/SalesSummary/index.d.ts +23 -26
  88. package/dist/modules/SalesSummary/index.js +152 -46
  89. package/dist/modules/SalesSummary/types.d.ts +9 -1
  90. package/dist/modules/SalesSummary/utils.d.ts +3 -22
  91. package/dist/modules/SalesSummary/utils.js +803 -163
  92. package/dist/modules/Schedule/getDateIsInSchedule.js +14 -7
  93. package/dist/modules/Schedule/index.d.ts +18 -5
  94. package/dist/modules/Schedule/index.js +78 -24
  95. package/dist/modules/Schedule/types.d.ts +2 -0
  96. package/dist/modules/Schedule/utils.js +0 -1
  97. package/dist/modules/Step/index.js +0 -1
  98. package/dist/modules/Summary/index.js +4 -3
  99. package/dist/modules/SurchargeList/index.d.ts +18 -0
  100. package/dist/modules/SurchargeList/index.js +185 -0
  101. package/dist/modules/SurchargeList/types.d.ts +15 -0
  102. package/dist/modules/SurchargeList/types.js +1 -0
  103. package/dist/modules/index.d.ts +3 -0
  104. package/dist/modules/index.js +4 -1
  105. package/dist/plugins/app.d.ts +2 -0
  106. package/dist/plugins/request.d.ts +1 -0
  107. package/dist/plugins/request.js +1 -4
  108. package/dist/plugins/window.js +2 -6
  109. package/dist/server/index.d.ts +462 -8
  110. package/dist/server/index.js +6137 -714
  111. package/dist/server/modules/floor-plan/index.d.ts +39 -0
  112. package/dist/server/modules/floor-plan/index.js +595 -0
  113. package/dist/server/modules/floor-plan/types.d.ts +43 -0
  114. package/dist/server/modules/floor-plan/types.js +13 -0
  115. package/dist/server/modules/index.d.ts +7 -2
  116. package/dist/server/modules/index.js +6 -0
  117. package/dist/server/modules/menu/index.js +32 -41
  118. package/dist/server/modules/order/index.d.ts +207 -14
  119. package/dist/server/modules/order/index.js +2326 -379
  120. package/dist/server/modules/order/types.d.ts +76 -5
  121. package/dist/server/modules/order/types.js +14 -1
  122. package/dist/server/modules/order/utils/filterBookings.d.ts +7 -1
  123. package/dist/server/modules/order/utils/filterBookings.js +118 -11
  124. package/dist/server/modules/order/utils/filterOrders.js +242 -14
  125. package/dist/server/modules/payment/index.d.ts +28 -0
  126. package/dist/server/modules/payment/index.js +305 -0
  127. package/dist/server/modules/payment/types.d.ts +9 -0
  128. package/dist/server/modules/payment/types.js +1 -0
  129. package/dist/server/modules/products/index.d.ts +105 -14
  130. package/dist/server/modules/products/index.js +1397 -540
  131. package/dist/server/modules/products/types.d.ts +34 -1
  132. package/dist/server/modules/products/types.js +4 -0
  133. package/dist/server/modules/quotation/index.d.ts +39 -0
  134. package/dist/server/modules/quotation/index.js +289 -86
  135. package/dist/server/modules/resource/index.d.ts +2 -3
  136. package/dist/server/modules/resource/index.js +105 -84
  137. package/dist/server/modules/schedule/index.d.ts +6 -4
  138. package/dist/server/modules/schedule/index.js +137 -114
  139. package/dist/server/test.json +713 -0
  140. package/dist/server/types.d.ts +2 -0
  141. package/dist/server/utils/product.js +2 -3
  142. package/dist/server/utils/small-ticket.d.ts +71 -0
  143. package/dist/server/utils/small-ticket.js +963 -0
  144. package/dist/solution/BaseSales/index.d.ts +378 -0
  145. package/dist/solution/BaseSales/index.js +4006 -0
  146. package/dist/solution/BaseSales/types.d.ts +394 -0
  147. package/dist/solution/BaseSales/types.js +40 -0
  148. package/dist/solution/BaseSales/utils/cartPromotion.d.ts +287 -0
  149. package/dist/solution/BaseSales/utils/cartPromotion.js +1450 -0
  150. package/dist/solution/BaseSales/utils/parseSalesResponse.d.ts +144 -0
  151. package/dist/solution/BaseSales/utils/parseSalesResponse.js +157 -0
  152. package/dist/solution/BaseSales/utils/quotationPrice.d.ts +18 -0
  153. package/dist/solution/BaseSales/utils/quotationPrice.js +250 -0
  154. package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.d.ts +63 -0
  155. package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +218 -0
  156. package/dist/solution/BaseSales/utils.d.ts +40 -0
  157. package/dist/solution/BaseSales/utils.js +263 -0
  158. package/dist/solution/BookingByStep/index.js +7 -59
  159. package/dist/solution/BookingByStep/utils/capacity.js +1 -11
  160. package/dist/solution/BookingByStep/utils/resources.js +1 -3
  161. package/dist/solution/BookingByStep/utils/timeslots.js +2 -43
  162. package/dist/solution/BookingTicket/index.d.ts +393 -24
  163. package/dist/solution/BookingTicket/index.js +2506 -445
  164. package/dist/solution/BookingTicket/types.d.ts +191 -6
  165. package/dist/solution/BookingTicket/types.js +83 -10
  166. package/dist/solution/BookingTicket/utils/addProductDecision.d.ts +117 -0
  167. package/dist/solution/BookingTicket/utils/addProductDecision.js +346 -0
  168. package/dist/solution/BookingTicket/utils/addTimeAvailability.d.ts +35 -0
  169. package/dist/solution/BookingTicket/utils/addTimeAvailability.js +95 -0
  170. package/dist/solution/BookingTicket/utils/bookingStatus.d.ts +77 -0
  171. package/dist/solution/BookingTicket/utils/bookingStatus.js +136 -0
  172. package/dist/solution/BookingTicket/utils/cartView.d.ts +29 -0
  173. package/dist/solution/BookingTicket/utils/cartView.js +209 -0
  174. package/dist/solution/BookingTicket/utils/exampleData.d.ts +123 -0
  175. package/dist/solution/BookingTicket/utils/exampleData.js +183 -0
  176. package/dist/solution/BookingTicket/utils/orderCustomer.d.ts +4 -0
  177. package/dist/solution/BookingTicket/utils/orderCustomer.js +31 -0
  178. package/dist/solution/BookingTicket/utils/resolveBestAddTimePlan.d.ts +185 -0
  179. package/dist/solution/BookingTicket/utils/resolveBestAddTimePlan.js +438 -0
  180. package/dist/solution/BookingTicket/utils/scan/applyGlobalScan.d.ts +36 -0
  181. package/dist/solution/BookingTicket/utils/scan/applyGlobalScan.js +400 -0
  182. package/dist/solution/BookingTicket/utils/scan/cloudSearch.d.ts +4 -0
  183. package/dist/solution/BookingTicket/utils/scan/cloudSearch.js +41 -1
  184. package/dist/solution/BookingTicket/utils/scan/formatGlobalScan.d.ts +32 -0
  185. package/dist/solution/BookingTicket/utils/scan/formatGlobalScan.js +56 -0
  186. package/dist/solution/BookingTicket/utils/scan/handleScan.d.ts +3 -1
  187. package/dist/solution/BookingTicket/utils/scan/handleScan.js +36 -34
  188. package/dist/solution/BookingTicket/utils/scan/index.d.ts +5 -0
  189. package/dist/solution/BookingTicket/utils/scan/index.js +26 -18
  190. package/dist/solution/BookingTicket/utils/scan/scanProductValues.d.ts +21 -0
  191. package/dist/solution/BookingTicket/utils/scan/scanProductValues.js +150 -0
  192. package/dist/solution/BookingTicket/utils/sessionCatalogStale.d.ts +23 -0
  193. package/dist/solution/BookingTicket/utils/sessionCatalogStale.js +78 -0
  194. package/dist/solution/BuyTickets/index.js +9 -12
  195. package/dist/solution/Checkout/index.d.ts +1 -0
  196. package/dist/solution/Checkout/index.js +199 -277
  197. package/dist/solution/Checkout/utils/index.js +8 -20
  198. package/dist/solution/RegisterAndLogin/config.js +2 -10
  199. package/dist/solution/RegisterAndLogin/index.js +30 -76
  200. package/dist/solution/Sales/index.d.ts +50 -5
  201. package/dist/solution/Sales/index.js +484 -92
  202. package/dist/solution/Sales/types.d.ts +26 -2
  203. package/dist/solution/ScanOrder/index.d.ts +27 -21
  204. package/dist/solution/ScanOrder/index.js +509 -454
  205. package/dist/solution/ScanOrder/types.d.ts +33 -11
  206. package/dist/solution/ScanOrder/types.js +4 -5
  207. package/dist/solution/ScanOrder/utils.d.ts +4 -1
  208. package/dist/solution/ScanOrder/utils.js +109 -34
  209. package/dist/solution/ShopDiscount/index.js +6 -11
  210. package/dist/solution/UnifiedBookingSales/index.d.ts +183 -0
  211. package/dist/solution/UnifiedBookingSales/index.js +1871 -0
  212. package/dist/solution/UnifiedBookingSales/types.d.ts +143 -0
  213. package/dist/solution/UnifiedBookingSales/types.js +11 -0
  214. package/dist/solution/VenueBooking/index.d.ts +34 -20
  215. package/dist/solution/VenueBooking/index.js +630 -505
  216. package/dist/solution/VenueBooking/sales-section-4-annotated.json +0 -2
  217. package/dist/solution/VenueBooking/utils/slotMerge.d.ts +6 -0
  218. package/dist/solution/VenueBooking/utils/slotMerge.js +9 -3
  219. package/dist/solution/index.d.ts +2 -0
  220. package/dist/solution/index.js +3 -1
  221. package/dist/types/index.d.ts +11 -1
  222. package/dist/utils/task.js +15 -18
  223. package/dist/utils/watch.js +0 -1
  224. package/lib/apis/picoding.js +2 -0
  225. package/lib/core/index.js +130 -131
  226. package/lib/effects/index.js +46 -57
  227. package/lib/index.js +92 -53
  228. package/lib/model/index.js +14 -21
  229. package/lib/model/strategy/adapter/dataVariant/adapter.js +38 -50
  230. package/lib/model/strategy/adapter/dataVariant/evaluator.js +162 -236
  231. package/lib/model/strategy/adapter/dataVariant/examples.js +229 -237
  232. package/lib/model/strategy/adapter/dataVariant/index.js +39 -34
  233. package/lib/model/strategy/adapter/dataVariant/type.js +57 -28
  234. package/lib/model/strategy/adapter/index.js +100 -64
  235. package/lib/model/strategy/adapter/itemRule/adapter.js +156 -144
  236. package/lib/model/strategy/adapter/itemRule/evaluator.js +36 -48
  237. package/lib/model/strategy/adapter/itemRule/examples.js +295 -225
  238. package/lib/model/strategy/adapter/itemRule/index.js +83 -57
  239. package/lib/model/strategy/adapter/itemRule/type.js +97 -36
  240. package/lib/model/strategy/adapter/promotion/adapter.js +84 -63
  241. package/lib/model/strategy/adapter/promotion/evaluator.js +383 -232
  242. package/lib/model/strategy/adapter/promotion/examples.js +139 -159
  243. package/lib/model/strategy/adapter/promotion/index.js +1 -49
  244. package/lib/model/strategy/adapter/promotion/type.js +199 -35
  245. package/lib/model/strategy/adapter/type.js +4 -16
  246. package/lib/model/strategy/adapter/walletPass/evaluator.js +180 -152
  247. package/lib/model/strategy/adapter/walletPass/example.js +217 -190
  248. package/lib/model/strategy/adapter/walletPass/index.js +75 -51
  249. package/lib/model/strategy/adapter/walletPass/locales.js +36 -58
  250. package/lib/model/strategy/adapter/walletPass/type.js +4 -16
  251. package/lib/model/strategy/adapter/walletPass/utils.js +654 -402
  252. package/lib/model/strategy/engine.js +270 -168
  253. package/lib/model/strategy/index.js +49 -34
  254. package/lib/model/strategy/strategy-example.js +243 -239
  255. package/lib/model/strategy/type.js +100 -40
  256. package/lib/modules/Account/index.js +39 -53
  257. package/lib/modules/Account/types.js +20 -33
  258. package/lib/modules/AccountList/index.js +116 -154
  259. package/lib/modules/AccountList/types.js +30 -31
  260. package/lib/modules/AccountList/utils.js +18 -30
  261. package/lib/modules/BaseModule.d.ts +2 -2
  262. package/lib/modules/BaseModule.js +26 -49
  263. package/lib/modules/BookingContext/index.d.ts +48 -0
  264. package/lib/modules/BookingContext/index.js +390 -0
  265. package/lib/modules/BookingContext/types.d.ts +102 -0
  266. package/lib/modules/BookingContext/types.js +48 -0
  267. package/lib/modules/BookingContext/utils/buildCacheItemFromOrderLine.d.ts +16 -0
  268. package/lib/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +286 -0
  269. package/lib/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.d.ts +8 -0
  270. package/lib/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +148 -0
  271. package/lib/modules/BookingContext/utils/cacheItemToBookingInput.d.ts +76 -0
  272. package/lib/modules/BookingContext/utils/cacheItemToBookingInput.js +242 -0
  273. package/lib/modules/BookingContext/utils/flexible.d.ts +28 -0
  274. package/lib/modules/BookingContext/utils/flexible.js +63 -0
  275. package/lib/modules/BookingContext/utils/formatResourceList.d.ts +8 -0
  276. package/lib/modules/BookingContext/utils/formatResourceList.js +35 -0
  277. package/lib/modules/BookingContext/utils/index.d.ts +11 -0
  278. package/lib/modules/BookingContext/utils/index.js +126 -0
  279. package/lib/modules/BookingContext/utils/noResource.d.ts +13 -0
  280. package/lib/modules/BookingContext/utils/noResource.js +78 -0
  281. package/lib/modules/BookingContext/utils/orderLineDisplay.d.ts +1 -0
  282. package/lib/modules/BookingContext/utils/orderLineDisplay.js +29 -0
  283. package/lib/modules/BookingContext/utils/productExtend.d.ts +72 -0
  284. package/lib/modules/BookingContext/utils/productExtend.js +330 -0
  285. package/lib/modules/BookingContext/utils/resourceErrors.d.ts +41 -0
  286. package/lib/modules/BookingContext/utils/resourceErrors.js +80 -0
  287. package/lib/modules/BookingContext/utils/resourceSelection.d.ts +7 -0
  288. package/lib/modules/BookingContext/utils/resourceSelection.js +24 -0
  289. package/lib/modules/BookingContext/utils/resources.d.ts +80 -0
  290. package/lib/modules/BookingContext/utils/resources.js +419 -0
  291. package/lib/modules/BookingContext/utils/serviceTimes.d.ts +55 -0
  292. package/lib/modules/BookingContext/utils/serviceTimes.js +161 -0
  293. package/lib/modules/BookingContext/utils/timeSlices.d.ts +42 -0
  294. package/lib/modules/BookingContext/utils/timeSlices.js +114 -0
  295. package/lib/modules/Cart/index.js +170 -124
  296. package/lib/modules/Cart/types.js +46 -51
  297. package/lib/modules/Cart/utils/cartAccount.js +40 -39
  298. package/lib/modules/Cart/utils/cartDate.js +56 -61
  299. package/lib/modules/Cart/utils/cartDiscount.js +28 -33
  300. package/lib/modules/Cart/utils/cartNote.js +27 -32
  301. package/lib/modules/Cart/utils/cartProduct.js +331 -244
  302. package/lib/modules/Cart/utils/cartRelationForms.js +29 -35
  303. package/lib/modules/Cart/utils/cartResource.js +72 -78
  304. package/lib/modules/Cart/utils/changePrice.js +37 -67
  305. package/lib/modules/Cart/utils/index.js +106 -48
  306. package/lib/modules/Customer/constants.js +13 -34
  307. package/lib/modules/Customer/index.d.ts +20 -0
  308. package/lib/modules/Customer/index.js +270 -154
  309. package/lib/modules/Customer/types.d.ts +2 -0
  310. package/lib/modules/Customer/types.js +38 -35
  311. package/lib/modules/Date/index.js +116 -115
  312. package/lib/modules/Date/types.js +16 -28
  313. package/lib/modules/Date/utils.js +174 -123
  314. package/lib/modules/Discount/index.d.ts +2 -1
  315. package/lib/modules/Discount/index.js +133 -113
  316. package/lib/modules/Discount/types.d.ts +1 -0
  317. package/lib/modules/Discount/types.js +9 -31
  318. package/lib/modules/Guests/index.js +30 -56
  319. package/lib/modules/Guests/types.js +23 -33
  320. package/lib/modules/Holder/index.js +209 -189
  321. package/lib/modules/Holder/types.js +4 -16
  322. package/lib/modules/Holder/utils.js +20 -49
  323. package/lib/modules/OpenData/index.d.ts +8 -0
  324. package/lib/modules/OpenData/index.js +81 -74
  325. package/lib/modules/OpenData/types.js +4 -16
  326. package/lib/modules/OpenData/utils.js +44 -78
  327. package/lib/modules/Order/index.d.ts +449 -24
  328. package/lib/modules/Order/index.js +3834 -489
  329. package/lib/modules/Order/types.d.ts +693 -88
  330. package/lib/modules/Order/types.js +78 -33
  331. package/lib/modules/Order/utils/bundleDiscountHydration.d.ts +5 -0
  332. package/lib/modules/Order/utils/bundleDiscountHydration.js +106 -0
  333. package/lib/modules/Order/utils/manualProductDiscount.d.ts +106 -0
  334. package/lib/modules/Order/utils/manualProductDiscount.js +207 -0
  335. package/lib/modules/Order/utils/orderCollectionIdentity.d.ts +53 -0
  336. package/lib/modules/Order/utils/orderCollectionIdentity.js +353 -0
  337. package/lib/modules/Order/utils.d.ts +167 -18
  338. package/lib/modules/Order/utils.js +1247 -345
  339. package/lib/modules/Payment/cash.js +20 -33
  340. package/lib/modules/Payment/cashRecommendationAlgorithm.js +145 -76
  341. package/lib/modules/Payment/eftpos.js +16 -30
  342. package/lib/modules/Payment/index.d.ts +10 -9
  343. package/lib/modules/Payment/index.js +542 -502
  344. package/lib/modules/Payment/mx51.js +1 -0
  345. package/lib/modules/Payment/types.d.ts +40 -16
  346. package/lib/modules/Payment/types.js +216 -107
  347. package/lib/modules/Payment/utils.d.ts +1 -1
  348. package/lib/modules/Payment/utils.js +53 -49
  349. package/lib/modules/Payment/walletpass.js +231 -203
  350. package/lib/modules/Product/index.js +51 -46
  351. package/lib/modules/Product/types.d.ts +29 -7
  352. package/lib/modules/Product/types.js +4 -16
  353. package/lib/modules/Product/utils.js +32 -33
  354. package/lib/modules/ProductList/index.d.ts +17 -13
  355. package/lib/modules/ProductList/index.js +160 -118
  356. package/lib/modules/ProductList/types.d.ts +17 -0
  357. package/lib/modules/ProductList/types.js +9 -31
  358. package/lib/modules/Quotation/index.d.ts +22 -2
  359. package/lib/modules/Quotation/index.js +123 -89
  360. package/lib/modules/Quotation/types.d.ts +19 -0
  361. package/lib/modules/Quotation/types.js +4 -16
  362. package/lib/modules/Resource/index.js +27 -59
  363. package/lib/modules/Resource/types.js +22 -32
  364. package/lib/modules/Resource/utils.js +30 -38
  365. package/lib/modules/ResourcePlanner/index.d.ts +24 -0
  366. package/lib/modules/ResourcePlanner/index.js +147 -0
  367. package/lib/modules/ResourcePlanner/planner.d.ts +14 -0
  368. package/lib/modules/ResourcePlanner/planner.js +895 -0
  369. package/lib/modules/ResourcePlanner/types.d.ts +227 -0
  370. package/lib/modules/ResourcePlanner/types.js +5 -0
  371. package/lib/modules/Rules/index.d.ts +8 -10
  372. package/lib/modules/Rules/index.js +1286 -894
  373. package/lib/modules/Rules/types.d.ts +14 -3
  374. package/lib/modules/Rules/types.js +17 -40
  375. package/lib/modules/SalesSummary/index.d.ts +23 -26
  376. package/lib/modules/SalesSummary/index.js +130 -71
  377. package/lib/modules/SalesSummary/types.d.ts +9 -1
  378. package/lib/modules/SalesSummary/types.js +4 -16
  379. package/lib/modules/SalesSummary/utils.d.ts +3 -22
  380. package/lib/modules/SalesSummary/utils.js +738 -250
  381. package/lib/modules/ScanOrderLogger/index.js +59 -79
  382. package/lib/modules/ScanOrderLogger/providers/feishu.js +54 -78
  383. package/lib/modules/ScanOrderLogger/providers/grafana.js +13 -36
  384. package/lib/modules/ScanOrderLogger/types.js +4 -16
  385. package/lib/modules/Schedule/getDateIsInSchedule.js +345 -171
  386. package/lib/modules/Schedule/index.d.ts +18 -5
  387. package/lib/modules/Schedule/index.js +139 -114
  388. package/lib/modules/Schedule/type.js +4 -16
  389. package/lib/modules/Schedule/types.d.ts +2 -0
  390. package/lib/modules/Schedule/types.js +4 -16
  391. package/lib/modules/Schedule/utils.js +433 -291
  392. package/lib/modules/Step/index.js +40 -52
  393. package/lib/modules/Step/tyeps.js +4 -16
  394. package/lib/modules/Summary/index.js +66 -70
  395. package/lib/modules/Summary/types.js +4 -16
  396. package/lib/modules/Summary/utils.js +782 -429
  397. package/lib/modules/SurchargeList/index.d.ts +18 -0
  398. package/lib/modules/SurchargeList/index.js +95 -0
  399. package/lib/modules/SurchargeList/types.d.ts +15 -0
  400. package/lib/modules/SurchargeList/types.js +5 -0
  401. package/lib/modules/index.d.ts +3 -0
  402. package/lib/modules/index.js +245 -57
  403. package/lib/plugins/app.d.ts +2 -0
  404. package/lib/plugins/app.js +4 -16
  405. package/lib/plugins/index.js +36 -25
  406. package/lib/plugins/request.d.ts +1 -0
  407. package/lib/plugins/request.js +137 -126
  408. package/lib/plugins/shopStore.js +4 -16
  409. package/lib/plugins/user.js +4 -16
  410. package/lib/plugins/window.js +171 -179
  411. package/lib/server/index.d.ts +462 -8
  412. package/lib/server/index.js +4668 -713
  413. package/lib/server/modules/floor-plan/index.d.ts +39 -0
  414. package/lib/server/modules/floor-plan/index.js +306 -0
  415. package/lib/server/modules/floor-plan/types.d.ts +43 -0
  416. package/lib/server/modules/floor-plan/types.js +19 -0
  417. package/lib/server/modules/index.d.ts +7 -2
  418. package/lib/server/modules/index.js +106 -59
  419. package/lib/server/modules/menu/index.js +146 -122
  420. package/lib/server/modules/menu/types.js +18 -31
  421. package/lib/server/modules/order/index.d.ts +207 -14
  422. package/lib/server/modules/order/index.js +1463 -316
  423. package/lib/server/modules/order/types.d.ts +76 -5
  424. package/lib/server/modules/order/types.js +117 -32
  425. package/lib/server/modules/order/utils/filterBookings.d.ts +7 -1
  426. package/lib/server/modules/order/utils/filterBookings.js +288 -146
  427. package/lib/server/modules/order/utils/filterOrders.js +318 -64
  428. package/lib/server/modules/payment/index.d.ts +28 -0
  429. package/lib/server/modules/payment/index.js +168 -0
  430. package/lib/server/modules/payment/types.d.ts +9 -0
  431. package/lib/server/modules/payment/types.js +5 -0
  432. package/lib/server/modules/products/index.d.ts +105 -14
  433. package/lib/server/modules/products/index.js +969 -383
  434. package/lib/server/modules/products/types.d.ts +34 -1
  435. package/lib/server/modules/products/types.js +44 -34
  436. package/lib/server/modules/quotation/index.d.ts +39 -0
  437. package/lib/server/modules/quotation/index.js +220 -77
  438. package/lib/server/modules/quotation/types.js +21 -31
  439. package/lib/server/modules/resource/index.d.ts +2 -3
  440. package/lib/server/modules/resource/index.js +269 -214
  441. package/lib/server/modules/resource/types.js +42 -33
  442. package/lib/server/modules/schedule/index.d.ts +6 -4
  443. package/lib/server/modules/schedule/index.js +158 -132
  444. package/lib/server/modules/schedule/types.js +14 -21
  445. package/lib/server/modules/schedule/utils.js +334 -163
  446. package/lib/server/test.json +713 -0
  447. package/lib/server/types.d.ts +2 -0
  448. package/lib/server/types.js +4 -16
  449. package/lib/server/utils/index.js +25 -23
  450. package/lib/server/utils/product.js +197 -140
  451. package/lib/server/utils/schedule.js +34 -41
  452. package/lib/server/utils/small-ticket.d.ts +71 -0
  453. package/lib/server/utils/small-ticket.js +926 -0
  454. package/lib/server/utils/time.js +40 -49
  455. package/lib/solution/BaseSales/index.d.ts +378 -0
  456. package/lib/solution/BaseSales/index.js +2367 -0
  457. package/lib/solution/BaseSales/types.d.ts +394 -0
  458. package/lib/solution/BaseSales/types.js +47 -0
  459. package/lib/solution/BaseSales/utils/cartPromotion.d.ts +287 -0
  460. package/lib/solution/BaseSales/utils/cartPromotion.js +1155 -0
  461. package/lib/solution/BaseSales/utils/parseSalesResponse.d.ts +144 -0
  462. package/lib/solution/BaseSales/utils/parseSalesResponse.js +140 -0
  463. package/lib/solution/BaseSales/utils/quotationPrice.d.ts +18 -0
  464. package/lib/solution/BaseSales/utils/quotationPrice.js +231 -0
  465. package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.d.ts +63 -0
  466. package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +215 -0
  467. package/lib/solution/BaseSales/utils.d.ts +40 -0
  468. package/lib/solution/BaseSales/utils.js +259 -0
  469. package/lib/solution/BookingByStep/index.js +1530 -1343
  470. package/lib/solution/BookingByStep/types.js +40 -99
  471. package/lib/solution/BookingByStep/utils/capacity.js +192 -160
  472. package/lib/solution/BookingByStep/utils/products.js +42 -52
  473. package/lib/solution/BookingByStep/utils/resources.js +527 -330
  474. package/lib/solution/BookingByStep/utils/stock.js +65 -44
  475. package/lib/solution/BookingByStep/utils/timeslots.js +129 -131
  476. package/lib/solution/BookingTicket/index.d.ts +393 -24
  477. package/lib/solution/BookingTicket/index.js +1459 -160
  478. package/lib/solution/BookingTicket/types.d.ts +191 -6
  479. package/lib/solution/BookingTicket/types.js +101 -66
  480. package/lib/solution/BookingTicket/utils/addProductDecision.d.ts +117 -0
  481. package/lib/solution/BookingTicket/utils/addProductDecision.js +353 -0
  482. package/lib/solution/BookingTicket/utils/addTimeAvailability.d.ts +35 -0
  483. package/lib/solution/BookingTicket/utils/addTimeAvailability.js +94 -0
  484. package/lib/solution/BookingTicket/utils/bookingStatus.d.ts +77 -0
  485. package/lib/solution/BookingTicket/utils/bookingStatus.js +136 -0
  486. package/lib/solution/BookingTicket/utils/cartView.d.ts +29 -0
  487. package/lib/solution/BookingTicket/utils/cartView.js +200 -0
  488. package/lib/solution/BookingTicket/utils/exampleData.d.ts +123 -0
  489. package/lib/solution/BookingTicket/utils/exampleData.js +185 -0
  490. package/lib/solution/BookingTicket/utils/orderCustomer.d.ts +4 -0
  491. package/lib/solution/BookingTicket/utils/orderCustomer.js +35 -0
  492. package/lib/solution/BookingTicket/utils/resolveBestAddTimePlan.d.ts +185 -0
  493. package/lib/solution/BookingTicket/utils/resolveBestAddTimePlan.js +387 -0
  494. package/lib/solution/BookingTicket/utils/scan/applyGlobalScan.d.ts +36 -0
  495. package/lib/solution/BookingTicket/utils/scan/applyGlobalScan.js +220 -0
  496. package/lib/solution/BookingTicket/utils/scan/cloudSearch.d.ts +4 -0
  497. package/lib/solution/BookingTicket/utils/scan/cloudSearch.js +85 -80
  498. package/lib/solution/BookingTicket/utils/scan/formatGlobalScan.d.ts +32 -0
  499. package/lib/solution/BookingTicket/utils/scan/formatGlobalScan.js +64 -0
  500. package/lib/solution/BookingTicket/utils/scan/handleScan.d.ts +3 -1
  501. package/lib/solution/BookingTicket/utils/scan/handleScan.js +84 -80
  502. package/lib/solution/BookingTicket/utils/scan/index.d.ts +5 -0
  503. package/lib/solution/BookingTicket/utils/scan/index.js +103 -95
  504. package/lib/solution/BookingTicket/utils/scan/scanCache.js +90 -43
  505. package/lib/solution/BookingTicket/utils/scan/scanProductValues.d.ts +21 -0
  506. package/lib/solution/BookingTicket/utils/scan/scanProductValues.js +115 -0
  507. package/lib/solution/BookingTicket/utils/sessionCatalogStale.d.ts +23 -0
  508. package/lib/solution/BookingTicket/utils/sessionCatalogStale.js +83 -0
  509. package/lib/solution/BuyTickets/index.js +67 -70
  510. package/lib/solution/BuyTickets/types.js +22 -38
  511. package/lib/solution/Checkout/index.d.ts +1 -0
  512. package/lib/solution/Checkout/index.js +1461 -1169
  513. package/lib/solution/Checkout/types.js +91 -61
  514. package/lib/solution/Checkout/utils/index.js +228 -159
  515. package/lib/solution/PlaceOrder/index.js +55 -0
  516. package/lib/solution/RegisterAndLogin/config.js +495 -470
  517. package/lib/solution/RegisterAndLogin/index.js +633 -630
  518. package/lib/solution/RegisterAndLogin/types.js +189 -76
  519. package/lib/solution/RegisterAndLogin/utils.js +183 -125
  520. package/lib/solution/Sales/index.d.ts +50 -5
  521. package/lib/solution/Sales/index.js +495 -220
  522. package/lib/solution/Sales/types.d.ts +26 -2
  523. package/lib/solution/Sales/types.js +27 -33
  524. package/lib/solution/ScanOrder/index.d.ts +27 -21
  525. package/lib/solution/ScanOrder/index.js +908 -904
  526. package/lib/solution/ScanOrder/types.d.ts +33 -11
  527. package/lib/solution/ScanOrder/types.js +33 -34
  528. package/lib/solution/ScanOrder/utils.d.ts +4 -1
  529. package/lib/solution/ScanOrder/utils.js +457 -344
  530. package/lib/solution/ShopDiscount/index.js +230 -236
  531. package/lib/solution/ShopDiscount/types.js +15 -37
  532. package/lib/solution/ShopDiscount/utils.js +309 -172
  533. package/lib/solution/UnifiedBookingSales/index.d.ts +183 -0
  534. package/lib/solution/UnifiedBookingSales/index.js +1050 -0
  535. package/lib/solution/UnifiedBookingSales/types.d.ts +143 -0
  536. package/lib/solution/UnifiedBookingSales/types.js +14 -0
  537. package/lib/solution/VenueBooking/index.d.ts +34 -20
  538. package/lib/solution/VenueBooking/index.js +982 -950
  539. package/lib/solution/VenueBooking/sales-section-4-annotated.json +0 -2
  540. package/lib/solution/VenueBooking/types.js +20 -40
  541. package/lib/solution/VenueBooking/utils/dateSummary.js +22 -54
  542. package/lib/solution/VenueBooking/utils/resource.js +31 -54
  543. package/lib/solution/VenueBooking/utils/slotMerge.d.ts +6 -0
  544. package/lib/solution/VenueBooking/utils/slotMerge.js +96 -108
  545. package/lib/solution/VenueBooking/utils/smartPricing.js +112 -100
  546. package/lib/solution/VenueBooking/utils/timeSlot.js +133 -132
  547. package/lib/solution/VenueBooking/utils.js +130 -67
  548. package/lib/solution/index.d.ts +2 -0
  549. package/lib/solution/index.js +124 -37
  550. package/lib/store/createStore.js +32 -29
  551. package/lib/types/index.d.ts +11 -1
  552. package/lib/types/index.js +4 -16
  553. package/lib/utils/task.js +36 -36
  554. package/lib/utils/watch.js +81 -47
  555. package/package.json +2 -1
  556. package/dist/solution/Checkout/appointmentDemo.json +0 -1
  557. package/lib/solution/Checkout/appointmentDemo.json +0 -1
@@ -0,0 +1,1155 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.appendPromotionTags = appendPromotionTags;
7
+ exports.buildConsolidateKeyForPromotion = buildConsolidateKeyForPromotion;
8
+ exports.calculateUnfulfilledPromotions = calculateUnfulfilledPromotions;
9
+ exports.extractProductMatchIdentifiersFromStrategyConfig = extractProductMatchIdentifiersFromStrategyConfig;
10
+ exports.getOrderProductBasePrice = getOrderProductBasePrice;
11
+ exports.getOrderProductOriginalPriceForPromotion = getOrderProductOriginalPriceForPromotion;
12
+ exports.getOrderProductUid = getOrderProductUid;
13
+ exports.mergeIdenticalPromotionCartLines = mergeIdenticalPromotionCartLines;
14
+ exports.processCartPromotion = processCartPromotion;
15
+ exports.resolveStrategyEligibleProducts = resolveStrategyEligibleProducts;
16
+ exports.resolveStrategyRequiredQuantity = resolveStrategyRequiredQuantity;
17
+ var _decimal = _interopRequireDefault(require("decimal.js"));
18
+ var _lodashEs = require("lodash-es");
19
+ var _utils = require("../../../modules/Order/utils");
20
+ var _utils2 = require("../../ScanOrder/utils");
21
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
22
+ /**
23
+ * Cart promotion 工具集(OS 内部纯函数)
24
+ *
25
+ * 这是 `private-materials/components/booking/hooks/usePromotion.ts` 中促销/赠品
26
+ * 核心计算(evaluator → 拆分 / 计价 → 赠品差异化 / 未满足提示 / 标签)的 OS 等价实现。
27
+ *
28
+ * 与 legacy 的关键差异:
29
+ * - legacy 操作 booking `_extend.*` 形状;OS 操作 `OrderProduct.metadata.*` 形状。
30
+ * - 行唯一标识不再用 `_id`,统一改用 `metadata.unique_identification_number`。
31
+ * - 赠品标签存到 `metadata._giftInfo`;主商品促销信息存到 `metadata._promotion`;
32
+ * 商品卡列表的促销 tag 存到 `metadata._promotionTags`。
33
+ *
34
+ * 仅暴露纯函数,不持有任何状态;评估器以参数注入。
35
+ *
36
+ * @example
37
+ * const evaluator = appHelper.utils.promotionEvaluator;
38
+ * const result = processCartPromotion(tempOrder.products, evaluator);
39
+ * // result.products: 处理后的商品(带 _promotion / 拆分)
40
+ * // result.giftActions: 需要 add / reduce / remove 的赠品操作
41
+ * // result.unfulfilledPromotions: 未满足策略列表(购物车底部 alert)
42
+ */
43
+
44
+ // ============================================
45
+ // 类型定义
46
+ // ============================================
47
+
48
+ /** Cart 评估器协议(只描述本文件用到的方法,避免循环依赖) */
49
+
50
+ /** 单个赠品减量项 */
51
+
52
+ /** 赠品新增项 */
53
+
54
+ /** 赠品操作集合(OS 侧根据它落地 tempOrder.products) */
55
+
56
+ /** 未满足促销提示项(购物车底部 alert 数据源) */
57
+
58
+ /** 商品卡促销标签结构(写入 `metadata._promotionTags`) */
59
+
60
+ /** processCartPromotion 输出 */
61
+
62
+ // ============================================
63
+ // 字段适配 helper
64
+ // ============================================
65
+
66
+ /**
67
+ * 获取商品的「主商品基础价」用于评估器入参。
68
+ *
69
+ * 优先级(与 OS v2 schema 一致):
70
+ * 1. `metadata.source_product_price`
71
+ * 2. `metadata.main_product_original_price` 减去 option 合计
72
+ * 3. `original_price`
73
+ */
74
+ function getOrderProductBasePrice(product) {
75
+ const metadata = product?.metadata || {};
76
+ const optionSum = (0, _utils.sumOptionUnitPrice)((0, _utils.getProductSkuOptions)(product)).toNumber();
77
+ if (metadata.source_product_price !== undefined) {
78
+ const v = Number(metadata.source_product_price);
79
+ return Number.isFinite(v) ? v : 0;
80
+ }
81
+ if (metadata.main_product_original_price !== undefined) {
82
+ const v = Number(metadata.main_product_original_price) - optionSum;
83
+ return Number.isFinite(v) ? v : 0;
84
+ }
85
+ const v = Number(product?.original_price ?? product?.selling_price ?? 0);
86
+ return Number.isFinite(v) ? v : 0;
87
+ }
88
+
89
+ /**
90
+ * 促销均摊专用原价(对齐 legacy `getMainOriginalPrice` 语义)。
91
+ *
92
+ * 必须用券前原价做 X_ITEMS_FOR_Y_PRICE 比例拆分;`source_product_price` 在上一轮 applyPromotion
93
+ * 后可能已被写成促销价(如 5 / 9 / 1),若继续用它会导致同商品两行出现 9+1 而非 5+5。
94
+ *
95
+ * @example
96
+ * getOrderProductOriginalPriceForPromotion({ metadata: { source_product_price: '9', main_product_original_price: '5.80' } });
97
+ * // => 5.8
98
+ */
99
+ function getOrderProductOriginalPriceForPromotion(product) {
100
+ const metadata = product?.metadata || {};
101
+ const optionSum = (0, _utils.sumOptionUnitPrice)((0, _utils.getProductSkuOptions)(product)).toNumber();
102
+ if (metadata.main_product_original_price !== undefined && metadata.main_product_original_price !== null && metadata.main_product_original_price !== '') {
103
+ const v = Number(metadata.main_product_original_price) - optionSum;
104
+ if (Number.isFinite(v) && v >= 0) return v;
105
+ }
106
+ const promoOriginal = metadata._promotion?.originalPrice;
107
+ if (promoOriginal !== undefined && promoOriginal !== null && promoOriginal !== '') {
108
+ const v = Number(promoOriginal);
109
+ if (Number.isFinite(v) && v >= 0) return v;
110
+ }
111
+ return getOrderProductBasePrice(product);
112
+ }
113
+
114
+ /**
115
+ * 写入/清除促销 discount_list(type=promotion)。
116
+ *
117
+ * applyDiscount → applyProductDiscountPrices 会从 source_product_price 减去 discount_list 合计
118
+ * 重算 main_product_selling_price;保留券前 source 时必须同步写入促销差额,否则会还原成原价。
119
+ *
120
+ * @example
121
+ * updatePromotionDiscountList(line, { title: '2 items for $10', amount: '17.00', original_amount: '22.00', fixed_amount: '17.00' });
122
+ */
123
+ function updatePromotionDiscountList(item, promotionDiscount) {
124
+ let discountList = Array.isArray(item.discount_list) ? [...item.discount_list] : [];
125
+ discountList = discountList.filter(d => d?.type !== 'promotion');
126
+ if (promotionDiscount) {
127
+ discountList.push({
128
+ type: 'promotion',
129
+ amount: promotionDiscount.amount,
130
+ discount: {
131
+ original_amount: promotionDiscount.original_amount,
132
+ fixed_amount: promotionDiscount.fixed_amount,
133
+ title: promotionDiscount.title,
134
+ resource_id: ''
135
+ }
136
+ });
137
+ }
138
+ if (discountList.length > 0) {
139
+ item.discount_list = discountList;
140
+ } else {
141
+ delete item.discount_list;
142
+ }
143
+ }
144
+ const X_ITEMS_FOR_Y_PRICE = 'X_ITEMS_FOR_Y_PRICE';
145
+ const BUY_X_GET_Y_FREE = 'BUY_X_GET_Y_FREE';
146
+
147
+ /**
148
+ * 将促销单价写入 OrderProduct(含 metadata 三字段 + promotion discount_list)。
149
+ *
150
+ * @example
151
+ * applyPromoUnitPriceToLine(newItem, { promoUnitPrice: '5.00', originalBasePrice: 1, strategyTitle: '2 for 10' });
152
+ */
153
+ function applyPromoUnitPriceToLine(item, input) {
154
+ const metadata = item.metadata || (item.metadata = {});
155
+ const optionSum = (0, _utils.sumOptionUnitPrice)((0, _utils.getProductSkuOptions)(item)).toNumber();
156
+ const promoUnitPrice = new _decimal.default(Number(input.promoUnitPrice) || 0).toDecimalPlaces(2).toFixed(2);
157
+ const mainSelling = new _decimal.default(Number(promoUnitPrice) || 0).plus(optionSum).toDecimalPlaces(2).toFixed(2);
158
+ const catalogSource = new _decimal.default(input.originalBasePrice).toDecimalPlaces(2).toFixed(2);
159
+ metadata.source_product_price = catalogSource;
160
+ metadata.main_product_original_price = new _decimal.default(Number(catalogSource) || 0).plus(optionSum).toDecimalPlaces(2).toFixed(2);
161
+ metadata.main_product_selling_price = mainSelling;
162
+ metadata.price_schema_version = 2;
163
+ item.selling_price = (0, _utils.composeLinePrice)({
164
+ mainPrice: mainSelling,
165
+ bundle: item.product_bundle
166
+ });
167
+ item.original_price = (0, _utils.composeLinePrice)({
168
+ mainPrice: metadata.main_product_original_price,
169
+ bundle: item.product_bundle,
170
+ useOriginalBundle: true
171
+ });
172
+ const discountAmount = new _decimal.default(input.originalBasePrice).minus(Number(promoUnitPrice) || 0).toDecimalPlaces(2).toFixed(2);
173
+ if (Number(catalogSource) !== Number(promoUnitPrice)) {
174
+ updatePromotionDiscountList(item, {
175
+ title: input.strategyTitle || '',
176
+ amount: discountAmount,
177
+ original_amount: catalogSource,
178
+ fixed_amount: discountAmount
179
+ });
180
+ } else {
181
+ updatePromotionDiscountList(item, null);
182
+ }
183
+ }
184
+ function getPromotionActionType(promotion) {
185
+ if (promotion?.actionType) return String(promotion.actionType);
186
+ const actions = promotion?.strategyMetadata?.actions;
187
+ if (Array.isArray(actions) && actions[0]?.type) return String(actions[0].type);
188
+ return undefined;
189
+ }
190
+
191
+ /**
192
+ * 同款行凑满 X件Y元 且仅该行参与时,按 Y/X 计算期望单价(2 件 $10 → $5)。
193
+ *
194
+ * @example
195
+ * resolveXItemsExpectedUnitPrice({ actionType: 'X_ITEMS_FOR_Y_PRICE', actionDetail: { x: 2, price: 10 } }, 2);
196
+ */
197
+ function resolveXItemsExpectedUnitPrice(promotion, sourceCartNum) {
198
+ if (getPromotionActionType(promotion) !== X_ITEMS_FOR_Y_PRICE) return undefined;
199
+ const detail = promotion?.actionDetail;
200
+ const x = Number(detail?.x);
201
+ const y = Number(detail?.price);
202
+ if (!Number.isFinite(x) || x <= 0 || !Number.isFinite(y)) return undefined;
203
+ if (sourceCartNum < x) return undefined;
204
+ return Number(new _decimal.default(y).div(x).toDecimalPlaces(2).toFixed(2));
205
+ }
206
+
207
+ /**
208
+ * 合并「同商品 + 同规格指纹 + 同促销策略 + 同促销单价」的参与促销行(num 累加)。
209
+ * 避免 Browse Items 连续加购同款时拆成多行且单价被错误均摊。
210
+ *
211
+ * @example
212
+ * mergeIdenticalPromotionCartLines(processedProducts);
213
+ */
214
+ function mergeIdenticalPromotionCartLines(products) {
215
+ if (!Array.isArray(products) || products.length <= 1) {
216
+ return [...(products || [])];
217
+ }
218
+ const merged = [];
219
+ const indexByKey = new Map();
220
+ for (const line of products) {
221
+ if (getGiftInfo(line)) {
222
+ merged.push(line);
223
+ continue;
224
+ }
225
+ const promotion = line?.metadata?._promotion;
226
+ if (!promotion?.inPromotion || !promotion.strategyId) {
227
+ merged.push(line);
228
+ continue;
229
+ }
230
+ const fingerprint = (0, _utils2.buildProductLineFingerprint)((0, _utils.getProductSkuOptions)(line), line.product_bundle);
231
+ const unitPrice = String(line.metadata?.source_product_price ?? line.selling_price ?? '');
232
+ const promoUnitPrice = String(promotion.finalPrice ?? line.metadata?.main_product_selling_price ?? '');
233
+ const noteSignature = getProductLineNoteSignature(line);
234
+ const lineMergeBoundary = getPromotionLineMergeBoundary(line);
235
+ const bookingUid = line?.booking_uid ?? line?.metadata?.booking_uid;
236
+ const orderDetailId = line?.order_detail_id ?? line?.orderDetailId;
237
+ const mergeKey = [line.product_id, line.product_variant_id ?? 0, fingerprint, noteSignature, lineMergeBoundary, bookingUid ? String(bookingUid) : '', orderDetailId !== undefined && orderDetailId !== null && orderDetailId !== '' ? String(orderDetailId) : '', promotion.strategyId, unitPrice, promoUnitPrice].join('#');
238
+ const hitIndex = indexByKey.get(mergeKey);
239
+ if (hitIndex === undefined) {
240
+ indexByKey.set(mergeKey, merged.length);
241
+ merged.push(line);
242
+ continue;
243
+ }
244
+ const target = merged[hitIndex];
245
+ target.num = (0, _utils2.getSafeProductNum)(Number(target.num || 0) + Number(line.num || 0));
246
+ }
247
+ return merged;
248
+ }
249
+
250
+ /**
251
+ * 同一原购物车行被多个「inPromotion」评估结果命中时(如买赠不改价 + X件Y元),
252
+ * 优先保留 X件Y元 定价,并恢复加购前的行数量。
253
+ *
254
+ * @example
255
+ * dedupePromotionProcessedLines(processedProducts);
256
+ */
257
+ function dedupePromotionProcessedLines(lines) {
258
+ if (!Array.isArray(lines) || lines.length <= 1) return [...(lines || [])];
259
+ const passthrough = [];
260
+ const buckets = new Map();
261
+ for (const line of lines) {
262
+ const promo = line?.metadata?._promotion;
263
+ const originalUid = String(line._originalProductUid || getOrderProductUid(line) || '');
264
+ if (!promo?.inPromotion || !originalUid) {
265
+ passthrough.push(line);
266
+ continue;
267
+ }
268
+ const group = buckets.get(originalUid) || [];
269
+ group.push(line);
270
+ buckets.set(originalUid, group);
271
+ }
272
+ const promotionDelta = line => {
273
+ const promo = line.metadata?._promotion;
274
+ const original = Number(promo?.originalPrice);
275
+ const final = Number(promo?.finalPrice);
276
+ if (!Number.isFinite(original) || !Number.isFinite(final)) return 0;
277
+ return Math.abs(original - final);
278
+ };
279
+ const deduped = [...passthrough];
280
+ for (const [originalUid, group] of buckets) {
281
+ if (group.length === 1) {
282
+ deduped.push(group[0]);
283
+ continue;
284
+ }
285
+ const sourceCartNum = Math.max(...group.map(line => Number(line._sourceCartNum || line.num || 0)));
286
+ const pricingLines = group.filter(line => promotionDelta(line) > 0);
287
+ const xItemsLines = (pricingLines.length ? pricingLines : group).filter(line => getPromotionActionType(line.metadata?._promotion) === X_ITEMS_FOR_Y_PRICE);
288
+ const pool = xItemsLines.length ? xItemsLines : pricingLines.length ? pricingLines : group;
289
+ let winner = pool[0];
290
+ const expectedUnit = resolveXItemsExpectedUnitPrice(winner.metadata?._promotion, sourceCartNum);
291
+ if (expectedUnit != null && pool.length > 1) {
292
+ winner = pool.reduce((best, line) => {
293
+ const bestDiff = Math.abs(Number(best.metadata?._promotion?.finalPrice) - expectedUnit);
294
+ const lineDiff = Math.abs(Number(line.metadata?._promotion?.finalPrice) - expectedUnit);
295
+ return lineDiff < bestDiff ? line : best;
296
+ }, pool[0]);
297
+ } else if (pool.length > 1 && !expectedUnit) {
298
+ winner = pool.reduce((best, line) => {
299
+ const bestFinal = Number(best.metadata?._promotion?.finalPrice);
300
+ const lineFinal = Number(line.metadata?._promotion?.finalPrice);
301
+ const original = Number(line.metadata?._promotion?.originalPrice);
302
+ if (Number.isFinite(original) && lineFinal >= original && bestFinal >= original) {
303
+ return lineFinal < bestFinal ? line : best;
304
+ }
305
+ return promotionDelta(line) > promotionDelta(best) ? line : best;
306
+ }, pool[0]);
307
+ }
308
+
309
+ // X件Y元:评估器会按单件均摊产出多条 inPromotion 行(如 $1 + $9),
310
+ // dedupe 应合并为「同组 X件Y元 行 num 之和」,不能用 sourceCartNum(整单 3 件)。
311
+ const winnerIsXItems = getPromotionActionType(winner.metadata?._promotion) === X_ITEMS_FOR_Y_PRICE;
312
+ if (winnerIsXItems) {
313
+ const xItemsGroupLines = group.filter(line => getPromotionActionType(line.metadata?._promotion) === X_ITEMS_FOR_Y_PRICE);
314
+ const evaluatedPromoQty = xItemsGroupLines.reduce((sum, line) => sum + Number(line.num || 0), 0);
315
+ winner.num = (0, _utils2.getSafeProductNum)(evaluatedPromoQty || winner.num || 1);
316
+ } else {
317
+ winner.num = (0, _utils2.getSafeProductNum)(sourceCartNum || winner.num || 1);
318
+ }
319
+ const recomputeUnit = resolveXItemsExpectedUnitPrice(winner.metadata?._promotion, sourceCartNum);
320
+ if (recomputeUnit != null) {
321
+ const promo = winner.metadata._promotion;
322
+ applyPromoUnitPriceToLine(winner, {
323
+ promoUnitPrice: recomputeUnit,
324
+ originalBasePrice: Number(promo.originalPrice),
325
+ strategyTitle: promo.strategyMetadata?.name
326
+ });
327
+ promo.finalPrice = recomputeUnit;
328
+ }
329
+ for (const line of group) {
330
+ const giftInfo = line.metadata?._promotion?.giftInfo;
331
+ if (giftInfo && winner.metadata?._promotion && !winner.metadata._promotion.giftInfo) {
332
+ winner.metadata._promotion.giftInfo = giftInfo;
333
+ }
334
+ }
335
+ deduped.push(winner);
336
+ }
337
+ return deduped;
338
+ }
339
+
340
+ /** 获取行 uid(统一以 `metadata.unique_identification_number` 为唯一键) */
341
+ function getOrderProductUid(product) {
342
+ return String(product?.metadata?.unique_identification_number ?? product?.unique_identification_number ?? '');
343
+ }
344
+ function getGiftInfo(product) {
345
+ return product?.metadata?._giftInfo || product?._giftInfo || null;
346
+ }
347
+ function getProductLineNoteSignature(product) {
348
+ const note = product?.note;
349
+ if (typeof note !== 'string') return '';
350
+ const normalizedNote = note.trim();
351
+ if (!normalizedNote) return '';
352
+ const uid = getOrderProductUid(product);
353
+ return uid ? `note:${uid}` : `note:${normalizedNote}`;
354
+ }
355
+
356
+ /**
357
+ * 编辑订单时,历史行仍参与整车促销评估,但不能与本次新增行合并。
358
+ * 以历史行 uid 作为促销前后的行边界;新增行返回空边界,仍保留原有合并语义。
359
+ */
360
+ function getPromotionLineMergeBoundary(product) {
361
+ if (product?.metadata?.is_edit_for_runtime !== true) return '';
362
+ return `edit:${getOrderProductUid(product)}`;
363
+ }
364
+ function isPersistedGeneratedVoucher(product) {
365
+ const orderDetailId = product?.order_detail_id ?? product?.orderDetailId;
366
+ if (orderDetailId === undefined || orderDetailId === null || orderDetailId === '') {
367
+ return false;
368
+ }
369
+ const metadata = product?.metadata || {};
370
+ return product?.extension_type === 'product_voucher' || metadata.parent_order_detail_id !== undefined || metadata.gift_card_type !== undefined;
371
+ }
372
+ function getProductBundleForEvaluator(product) {
373
+ const bundles = Array.isArray(product?.product_bundle) ? product.product_bundle : [];
374
+ return bundles.map(b => ({
375
+ product_id: b?.bundle_product_id ?? b?._bundle_product_id ?? b?.product_id,
376
+ product_variant_id: b?.bundle_variant_id ?? b?.product_variant_id ?? 0,
377
+ quantity: Number(b?.num ?? b?.quantity ?? 1) || 1,
378
+ price: Number(b?.bundle_selling_price ?? b?.price ?? 0) || 0,
379
+ original_price: Number(b?.original_price ?? b?.product_price ?? b?.price ?? 0) || 0,
380
+ id: b?.bundle_id ?? b?.id ?? 0,
381
+ _bundle_product_id: b?._bundle_product_id ?? b?.bundle_product_id ?? b?.product_id
382
+ }));
383
+ }
384
+
385
+ /**
386
+ * 促销评估前合并同 SKU 拆分行(含促销/原价行),避免加购后仅前 2 件享受优惠。
387
+ * 带 `booking_uid` 的预约行不参与合并(每次加车独立 booking + 商品行)。
388
+ *
389
+ * @example
390
+ * consolidateMainProductsForPromotion([
391
+ * { item: promoLineNum2, originalListIndex: 0 },
392
+ * { item: regularLineNum2, originalListIndex: 1 },
393
+ * ]);
394
+ * // => 单行 num=4,清除 _promotion 后重新评估
395
+ */
396
+ function buildConsolidateKeyForPromotion(item) {
397
+ const fingerprint = (0, _utils2.buildProductLineFingerprint)((0, _utils.getProductSkuOptions)(item), item.product_bundle);
398
+ const noteSignature = getProductLineNoteSignature(item);
399
+ const lineMergeBoundary = getPromotionLineMergeBoundary(item);
400
+ const bookingUid = item?.booking_uid ?? item?.metadata?.booking_uid;
401
+ const orderDetailId = item?.order_detail_id ?? item?.orderDetailId;
402
+ if (orderDetailId !== undefined && orderDetailId !== null && orderDetailId !== '') {
403
+ return [item.product_id, item.product_variant_id ?? 0, fingerprint, noteSignature, lineMergeBoundary, String(orderDetailId)].join('#');
404
+ }
405
+ if (bookingUid !== undefined && bookingUid !== null && String(bookingUid) !== '') {
406
+ return [item.product_id, item.product_variant_id ?? 0, fingerprint, noteSignature, lineMergeBoundary, String(bookingUid)].join('#');
407
+ }
408
+ return [item.product_id, item.product_variant_id ?? 0, fingerprint, noteSignature, lineMergeBoundary].join('#');
409
+ }
410
+ function consolidateMainProductsForPromotion(entries) {
411
+ if (!Array.isArray(entries) || entries.length <= 1) {
412
+ return [...(entries || [])];
413
+ }
414
+ const groups = new Map();
415
+ for (const {
416
+ item,
417
+ originalListIndex
418
+ } of entries) {
419
+ const key = buildConsolidateKeyForPromotion(item);
420
+ const wasInPromotion = item?.metadata?._promotion?.inPromotion === true;
421
+ const existing = groups.get(key);
422
+ if (!existing) {
423
+ const mergedItem = (0, _lodashEs.cloneDeep)(item);
424
+ const metadata = mergedItem.metadata || {};
425
+ delete metadata._promotion;
426
+ mergedItem.metadata = metadata;
427
+ if (Array.isArray(mergedItem.product_bundle)) {
428
+ for (const bundle of mergedItem.product_bundle) {
429
+ if (bundle && typeof bundle === 'object') delete bundle._promotion;
430
+ }
431
+ }
432
+ if (wasInPromotion) {
433
+ mergedItem._wasInPromotionBeforeConsolidate = true;
434
+ }
435
+ groups.set(key, {
436
+ item: mergedItem,
437
+ originalListIndex
438
+ });
439
+ continue;
440
+ }
441
+ existing.item.num = (0, _utils2.getSafeProductNum)(Number(existing.item.num || 0) + Number(item.num || 0));
442
+ if (wasInPromotion) {
443
+ existing.item._wasInPromotionBeforeConsolidate = true;
444
+ }
445
+ }
446
+ return Array.from(groups.values());
447
+ }
448
+
449
+ /**
450
+ * X件Y元 整组凑满时按每组 x 件拆开展示(4 件 → 2 行各 2 件 $10)。
451
+ *
452
+ * @example
453
+ * splitXItemsPromoLinesByGroup(mergedLines, strategyActionMap);
454
+ */
455
+ function splitXItemsPromoLinesByGroup(lines, strategyActionMap) {
456
+ const result = [];
457
+ for (const line of lines) {
458
+ const promo = line?.metadata?._promotion;
459
+ if (!promo?.inPromotion || !promo.strategyId) {
460
+ result.push(line);
461
+ continue;
462
+ }
463
+ if (getPromotionActionType(promo) !== X_ITEMS_FOR_Y_PRICE) {
464
+ result.push(line);
465
+ continue;
466
+ }
467
+ const actionMeta = strategyActionMap.get(promo.strategyId);
468
+ const detail = actionMeta?.actionDetail ?? promo.actionDetail;
469
+ const x = Number(detail?.x);
470
+ const num = Number(line.num || 0);
471
+ const isCumulative = detail?.cumulative !== false;
472
+ if (!isCumulative || !Number.isFinite(x) || x <= 0 || num <= x || num % x !== 0) {
473
+ result.push(line);
474
+ continue;
475
+ }
476
+ const groupCount = num / x;
477
+ for (let i = 0; i < groupCount; i++) {
478
+ const splitLine = (0, _lodashEs.cloneDeep)(line);
479
+ splitLine.num = (0, _utils2.getSafeProductNum)(x);
480
+ splitLine.metadata = {
481
+ ...(splitLine.metadata || {}),
482
+ unique_identification_number: (0, _utils.createUuidV4)()
483
+ };
484
+ result.push(splitLine);
485
+ }
486
+ }
487
+ return result;
488
+ }
489
+
490
+ /** 生成新 numeric id(仅用于评估器内部追踪) */
491
+ function generateNumericId() {
492
+ return Math.floor(Math.random() * 1_000_000_000) + Date.now();
493
+ }
494
+
495
+ /**
496
+ * 把 OrderProduct 转成 evaluator 的 PromotionProduct(仅评估期使用)。
497
+ *
498
+ * 注意:评估器是「无状态、按 product_id + variant 组」工作的;这里给每行分配一个
499
+ * numericId 用于关联回原 OrderProduct(通过 originalItemMap)。
500
+ */
501
+ function convertToPromotionProduct(product, index) {
502
+ const metadata = product?.metadata || {};
503
+ const numericId = generateNumericId();
504
+ const originalItem = product;
505
+ return {
506
+ id: numericId,
507
+ product_id: Number(product?.product_id ?? 0) || 0,
508
+ product_variant_id: Number(product?.product_variant_id ?? 0) || 0,
509
+ name: metadata.product_name || product?.title || '',
510
+ price: getOrderProductOriginalPriceForPromotion(product),
511
+ quantity: Number(product?.num ?? 1) || 1,
512
+ bundle: getProductBundleForEvaluator(product),
513
+ _originalItem: originalItem,
514
+ _originalId: getOrderProductUid(product),
515
+ _originalIndex: index
516
+ };
517
+ }
518
+
519
+ // ============================================
520
+ // 主流程:processCartPromotion
521
+ // ============================================
522
+
523
+ /**
524
+ * 计算赠品差异化操作(toAdd / toReduce / toRemove)。
525
+ *
526
+ * 与 legacy `calculateGiftActions` 等价;唯一差别是行键从 `_id` 改成 `metadata.unique_identification_number`。
527
+ */
528
+ function calculateGiftActions(gifts, existingGifts) {
529
+ const toAdd = [];
530
+ const toReduce = [];
531
+ const toRemove = [];
532
+
533
+ // 按 strategyId 分组现有赠品
534
+ const existingByStrategy = new Map();
535
+ for (const gift of existingGifts) {
536
+ const strategyId = getGiftInfo(gift)?.strategyId;
537
+ if (!strategyId) continue;
538
+ const existing = existingByStrategy.get(strategyId);
539
+ const qty = Number(gift?.num ?? 1) || 1;
540
+ if (existing) {
541
+ existing.items.push(gift);
542
+ existing.totalQuantity += qty;
543
+ } else {
544
+ existingByStrategy.set(strategyId, {
545
+ items: [gift],
546
+ totalQuantity: qty
547
+ });
548
+ }
549
+ }
550
+ const processedStrategyIds = new Set();
551
+ for (const giftInfo of gifts) {
552
+ const {
553
+ strategyId,
554
+ strategyName,
555
+ giftCount,
556
+ giftOptions,
557
+ sourceProductIds
558
+ } = giftInfo;
559
+ processedStrategyIds.add(strategyId);
560
+ const existing = existingByStrategy.get(strategyId);
561
+ const existingCount = existing?.totalQuantity ?? 0;
562
+ const diff = giftCount - existingCount;
563
+ if (diff > 0) {
564
+ // 判断「用户手动减赠品」: 若评估器的 giftCount 没变,diff>0 说明是手动减,跳过补回
565
+ const lastEvaluated = getGiftInfo(existing?.items?.[0])?.lastEvaluatedGiftCount;
566
+ const strategyChanged = lastEvaluated === undefined || giftCount !== lastEvaluated;
567
+ if (strategyChanged) {
568
+ toAdd.push({
569
+ strategyId,
570
+ strategyName,
571
+ giftCount: diff,
572
+ giftOptions,
573
+ sourceProductIds
574
+ });
575
+ }
576
+ } else if (diff < 0) {
577
+ // 减赠品:后进先出
578
+ let remaining = Math.abs(diff);
579
+ const reduceItems = [];
580
+ const removeIds = [];
581
+ const reversed = [...(existing?.items || [])].reverse();
582
+ for (const giftItem of reversed) {
583
+ if (remaining <= 0) break;
584
+ const uid = getOrderProductUid(giftItem);
585
+ const qty = Number(giftItem?.num ?? 1) || 1;
586
+ if (qty <= remaining) {
587
+ removeIds.push(uid);
588
+ remaining -= qty;
589
+ } else {
590
+ reduceItems.push({
591
+ uid,
592
+ currentQuantity: qty,
593
+ newQuantity: qty - remaining
594
+ });
595
+ remaining = 0;
596
+ }
597
+ }
598
+ if (reduceItems.length > 0) toReduce.push({
599
+ strategyId,
600
+ items: reduceItems
601
+ });
602
+ toRemove.push(...removeIds);
603
+ }
604
+ }
605
+
606
+ // 失效策略的赠品:全部 remove
607
+ for (const [strategyId, existing] of existingByStrategy) {
608
+ if (processedStrategyIds.has(strategyId)) continue;
609
+ for (const giftItem of existing.items) {
610
+ toRemove.push(getOrderProductUid(giftItem));
611
+ }
612
+ }
613
+ return {
614
+ toAdd,
615
+ toReduce,
616
+ toRemove
617
+ };
618
+ }
619
+
620
+ /**
621
+ * 从策略配置的 product_match 规则提取可参与商品列表。
622
+ *
623
+ * @example
624
+ * extractProductMatchIdentifiersFromStrategyConfig(strategyConfig);
625
+ * // => [{ product_id: 60736, product_variant_id: 0 }]
626
+ */
627
+ function extractProductMatchIdentifiersFromStrategyConfig(strategyConfig) {
628
+ const rules = strategyConfig?.conditions?.rules;
629
+ if (!Array.isArray(rules)) return [];
630
+ for (const rule of rules) {
631
+ if (rule?.field === 'productIdAndVariantId' && (rule?.operator === 'product_match' || rule?.operator === 'object_in') && Array.isArray(rule?.value)) {
632
+ return rule.value.map(item => ({
633
+ product_id: Number(item?.product_id) || 0,
634
+ product_variant_id: Number(item?.product_variant_id) || 0
635
+ })).filter(item => item.product_id > 0);
636
+ }
637
+ }
638
+ return [];
639
+ }
640
+
641
+ /**
642
+ * 解析策略凑单所需件数(evaluator 原始返回可能不含 requiredQuantity)。
643
+ *
644
+ * @example
645
+ * resolveStrategyRequiredQuantity({ actionType: 'X_ITEMS_FOR_Y_PRICE', actionDetail: { x: 2 } });
646
+ */
647
+ function resolveStrategyRequiredQuantity(matched) {
648
+ const preset = Number(matched.requiredQuantity);
649
+ if (Number.isFinite(preset) && preset > 0) return preset;
650
+ const detail = matched.actionDetail || {};
651
+ if (matched.actionType === 'X_ITEMS_FOR_Y_PRICE') {
652
+ return Number(detail.x) || 2;
653
+ }
654
+ if (matched.actionType === 'BUY_X_GET_Y_FREE') {
655
+ return Number(detail.buyQuantity) || 1;
656
+ }
657
+ return 1;
658
+ }
659
+
660
+ /**
661
+ * 解析 Browse Items 弹窗候选商品(优先 eligibleProducts,否则读 strategyConfig)。
662
+ *
663
+ * @example
664
+ * resolveStrategyEligibleProducts(matchedStrategy);
665
+ */
666
+ function resolveStrategyEligibleProducts(matched) {
667
+ if (Array.isArray(matched.eligibleProducts) && matched.eligibleProducts.length > 0) {
668
+ return matched.eligibleProducts;
669
+ }
670
+ return extractProductMatchIdentifiersFromStrategyConfig(matched.strategyConfig);
671
+ }
672
+
673
+ /**
674
+ * 计算未满足促销条件(用于购物车底部「再买 N 件即可享受 XX」alert)。
675
+ *
676
+ * 数据源:商品的 `metadata._promotion.inPromotion === false`(来自 processCartPromotion
677
+ * 第 8 步追加的「有适用策略但当前未参与促销」标记)。
678
+ */
679
+ function calculateUnfulfilledPromotions(processedProducts, applicableStrategiesResult, mainProducts) {
680
+ const unfulfilledMap = new Map();
681
+ for (let i = 0; i < processedProducts.length; i++) {
682
+ const product = processedProducts[i];
683
+ const promotion = product?.metadata?._promotion;
684
+ if (!promotion || promotion.inPromotion !== false) continue;
685
+ const strategyId = promotion.strategyId;
686
+ if (!strategyId) continue;
687
+ const lookupUid = String(product?._originalProductUid ?? product?.metadata?.unique_identification_number ?? '');
688
+ const originalIndex = mainProducts.findIndex(item => getOrderProductUid(item) === lookupUid);
689
+ if (originalIndex === -1) continue;
690
+ const strategyData = applicableStrategiesResult[originalIndex];
691
+ if (!strategyData || !strategyData.hasApplicableStrategy) continue;
692
+ const matched = strategyData.applicableStrategies.find(s => s.strategyId === strategyId);
693
+ if (!matched) continue;
694
+ const quantity = Number(product?.num ?? 1) || 1;
695
+ if (unfulfilledMap.has(strategyId)) {
696
+ unfulfilledMap.get(strategyId).currentQuantity += quantity;
697
+ } else {
698
+ unfulfilledMap.set(strategyId, {
699
+ strategyId,
700
+ strategyName: matched.strategyName,
701
+ actionType: matched.actionType,
702
+ currentQuantity: quantity,
703
+ requiredQuantity: resolveStrategyRequiredQuantity(matched),
704
+ needQuantity: 0,
705
+ eligibleProducts: resolveStrategyEligibleProducts(matched),
706
+ strategyMetadata: matched.strategyMetadata,
707
+ display: matched.display
708
+ });
709
+ }
710
+ }
711
+ const result = [];
712
+ for (const data of unfulfilledMap.values()) {
713
+ const needQuantity = data.requiredQuantity - data.currentQuantity;
714
+ if (needQuantity > 0) {
715
+ result.push({
716
+ ...data,
717
+ needQuantity
718
+ });
719
+ }
720
+ }
721
+ return result;
722
+ }
723
+
724
+ /**
725
+ * 给商品列表追加促销标签(写入 `metadata._promotionTags`),供 ProductCard 渲染。
726
+ *
727
+ * - 没有适用策略的商品会清除 `_promotionTags`(避免脏数据)。
728
+ * - 仅基于 product_id 匹配(matchVariant=false),与商品卡通用场景对齐。
729
+ *
730
+ * @example
731
+ * const list = appendPromotionTags(catalog, evaluator);
732
+ * // list[i].metadata._promotionTags = [{ text, type, strategyId }]
733
+ */
734
+ function appendPromotionTags(products, evaluator) {
735
+ if (!Array.isArray(products) || products.length === 0 || !evaluator) {
736
+ return products || [];
737
+ }
738
+
739
+ // 评估器期望的 product shape,最少字段集
740
+ const evaluatorInput = products.map((item, index) => ({
741
+ id: index,
742
+ product_id: Number(item?.id ?? item?.product_id ?? 0) || 0,
743
+ product_variant_id: Number(item?.product_variant_id ?? 0) || 0,
744
+ name: item?.title || item?.name || '',
745
+ price: Number(item?.price ?? 0) || 0,
746
+ quantity: Number(item?.quantity ?? 1) || 1,
747
+ bundle: []
748
+ }));
749
+ const strategies = evaluator.getProductsApplicableStrategies({
750
+ products: evaluatorInput
751
+ }, false);
752
+ return products.map((item, index) => {
753
+ const data = strategies[index];
754
+ const tags = [];
755
+ if (data?.hasApplicableStrategy) {
756
+ for (const strategy of data.applicableStrategies) {
757
+ if (strategy.display) {
758
+ tags.push({
759
+ text: strategy.display.text,
760
+ type: strategy.display.type,
761
+ strategyId: strategy.strategyId
762
+ });
763
+ }
764
+ }
765
+ }
766
+ // 商品卡兼容两种位置:顶层 `_promotionTags` 与 `metadata._promotionTags`
767
+ // ProductCard 现有实现读顶层(详见 productSelect/ProductCard/index.tsx),
768
+ // 此处同时写顶层以保持视图直连;OrderProduct 行版本走 metadata(避免顶层非协议字段)。
769
+ if (tags.length === 0) {
770
+ const {
771
+ _promotionTags,
772
+ ...rest
773
+ } = item;
774
+ void _promotionTags;
775
+ return rest;
776
+ }
777
+ return {
778
+ ...item,
779
+ _promotionTags: tags
780
+ };
781
+ });
782
+ }
783
+
784
+ /**
785
+ * 处理购物车的促销计算与赠品差异化。
786
+ *
787
+ * 流程:
788
+ * 1. 分离 main / gift / edit-product 三类(带 `booking_id` 的编辑态商品不参与促销)
789
+ * 2. 转 PromotionProduct 喂评估器 → 拿到 `evaluateCartWithPricing` 与 `getProductsApplicableStrategies`
790
+ * 3. 把 PricedProduct 映射回 OrderProduct 形态:
791
+ * - 参与促销的,写 `metadata._promotion` + 调整 `selling_price` / `main_product_selling_price`
792
+ * - 拆分的,生成新 uid,复制原行其它字段
793
+ * 4. 计算 `_giftInfo` 挂主商品(每个策略首个主商品)
794
+ * 5. 计算 `giftActions`(toAdd / toReduce / toRemove)
795
+ * 6. 计算 `unfulfilledPromotions`
796
+ *
797
+ * @example
798
+ * const { products, giftActions, unfulfilledPromotions } = processCartPromotion(
799
+ * tempOrder.products,
800
+ * evaluator,
801
+ * );
802
+ */
803
+ function processCartPromotion(list, evaluator, options) {
804
+ const empty = {
805
+ products: Array.isArray(list) ? [...list] : [],
806
+ totalDiscount: 0,
807
+ gifts: [],
808
+ giftActions: {
809
+ toAdd: [],
810
+ toReduce: [],
811
+ toRemove: []
812
+ },
813
+ unfulfilledPromotions: []
814
+ };
815
+ if (!list || list.length === 0 || !evaluator) return empty;
816
+
817
+ // 0. 分类
818
+ const mainProductsWithIndex = [];
819
+ const existingGiftsWithIndex = [];
820
+ const editProductsWithIndex = [];
821
+ for (let i = 0; i < list.length; i++) {
822
+ const item = list[i];
823
+ const giftInfo = getGiftInfo(item);
824
+ if (isPersistedGeneratedVoucher(item)) {
825
+ editProductsWithIndex.push({
826
+ item,
827
+ originalListIndex: i
828
+ });
829
+ } else if (giftInfo) {
830
+ existingGiftsWithIndex.push({
831
+ item,
832
+ originalListIndex: i
833
+ });
834
+ } else if (item?.booking_id !== undefined && item?.booking_id !== null && item.booking_id !== 0 && item.booking_id !== '0') {
835
+ editProductsWithIndex.push({
836
+ item,
837
+ originalListIndex: i
838
+ });
839
+ } else {
840
+ mainProductsWithIndex.push({
841
+ item,
842
+ originalListIndex: i
843
+ });
844
+ }
845
+ }
846
+ const consolidatedMainProductsWithIndex = consolidateMainProductsForPromotion(mainProductsWithIndex);
847
+ const mainProducts = consolidatedMainProductsWithIndex.map(x => x.item);
848
+ const existingGifts = existingGiftsWithIndex.map(x => x.item);
849
+ if (mainProducts.length === 0) {
850
+ const editProducts = editProductsWithIndex.map(x => x.item);
851
+ if (existingGifts.length > 0) {
852
+ const toRemove = [];
853
+ for (const gift of existingGifts) {
854
+ const uid = getOrderProductUid(gift);
855
+ if (uid) toRemove.push(uid);
856
+ }
857
+ return {
858
+ products: editProducts,
859
+ totalDiscount: 0,
860
+ gifts: [],
861
+ giftActions: {
862
+ toAdd: [],
863
+ toReduce: [],
864
+ toRemove
865
+ },
866
+ unfulfilledPromotions: []
867
+ };
868
+ }
869
+ return {
870
+ ...empty,
871
+ products: editProducts.length > 0 ? editProducts : [...list]
872
+ };
873
+ }
874
+
875
+ // 1. 转 PromotionProduct
876
+ const promotionProducts = [];
877
+ const originalItemMap = new Map();
878
+ const productsForStrategies = consolidatedMainProductsWithIndex.map(({
879
+ item
880
+ }, i) => ({
881
+ id: i,
882
+ product_id: Number(item?.product_id ?? 0) || 0,
883
+ product_variant_id: Number(item?.product_variant_id ?? 0) || 0,
884
+ name: item?.metadata?.product_name || item?.title || '',
885
+ price: getOrderProductBasePrice(item),
886
+ quantity: Number(item?.num ?? 1) || 1,
887
+ bundle: getProductBundleForEvaluator(item)
888
+ }));
889
+ for (let i = 0; i < consolidatedMainProductsWithIndex.length; i++) {
890
+ const {
891
+ item,
892
+ originalListIndex
893
+ } = consolidatedMainProductsWithIndex[i];
894
+ const promotionProduct = convertToPromotionProduct(item, originalListIndex);
895
+ promotionProducts.push(promotionProduct);
896
+ originalItemMap.set(promotionProduct.id, {
897
+ item,
898
+ index: originalListIndex
899
+ });
900
+ }
901
+
902
+ // 2. 拿适用策略
903
+ const applicableStrategiesResult = evaluator.getProductsApplicableStrategies({
904
+ products: productsForStrategies,
905
+ channel: options?.channel
906
+ });
907
+ const strategyActionMap = new Map();
908
+ for (const row of applicableStrategiesResult || []) {
909
+ for (const strategy of row?.applicableStrategies || []) {
910
+ if (!strategy?.strategyId) continue;
911
+ strategyActionMap.set(strategy.strategyId, {
912
+ actionType: strategy.actionType,
913
+ actionDetail: strategy.actionDetail
914
+ });
915
+ }
916
+ }
917
+
918
+ // 3. 评估器
919
+ const result = evaluator.evaluateCartWithPricing({
920
+ products: promotionProducts,
921
+ channel: options?.channel
922
+ });
923
+
924
+ // 4. 收集每个策略的 sourceProductIds(参与策略的主商品 uid)
925
+ const strategySourceProductIds = new Map();
926
+ for (const priced of result.products) {
927
+ if (priced.inPromotion && priced.strategyId) {
928
+ const sourceId = priced.originalId ?? priced.id;
929
+ const originalData = originalItemMap.get(sourceId);
930
+ if (originalData) {
931
+ const uid = getOrderProductUid(originalData.item);
932
+ if (!uid) continue;
933
+ const arr = strategySourceProductIds.get(priced.strategyId) || [];
934
+ if (!arr.includes(uid)) arr.push(uid);
935
+ strategySourceProductIds.set(priced.strategyId, arr);
936
+ }
937
+ }
938
+ }
939
+
940
+ // 5. 扩展 gifts,加 sourceProductIds
941
+ const extendedGifts = result.gifts.map(gift => ({
942
+ ...gift,
943
+ sourceProductIds: strategySourceProductIds.get(gift.strategyId) || []
944
+ }));
945
+
946
+ // 6. PricedProduct → OrderProduct(含拆分与价格调整)
947
+ const strategyGiftInfoMap = new Map();
948
+ for (const gift of extendedGifts) strategyGiftInfoMap.set(gift.strategyId, gift);
949
+ const strategyGiftInfoOwner = new Map();
950
+ const processedProducts = [];
951
+ for (const priced of result.products) {
952
+ const rawSourceId = priced.originalId ?? priced.id;
953
+ const sourceId = Number(rawSourceId);
954
+ const originalData = Number.isFinite(sourceId) ? originalItemMap.get(sourceId) : undefined;
955
+ if (!originalData) continue;
956
+ const {
957
+ item: originalItem,
958
+ index: originalIndex
959
+ } = originalData;
960
+ const pricedProductId = Number(priced.product_id ?? 0);
961
+ const originalProductId = Number(originalItem.product_id ?? 0);
962
+ if (pricedProductId > 0 && originalProductId > 0 && pricedProductId !== originalProductId) {
963
+ continue;
964
+ }
965
+ const newItem = (0, _lodashEs.cloneDeep)(originalItem);
966
+ const metadata = newItem.metadata || {};
967
+ const sourceCartNum = (0, _utils2.getSafeProductNum)(originalItem.num);
968
+ newItem._sourceCartNum = sourceCartNum;
969
+
970
+ // 清掉历史促销字段
971
+ const wasInPromotion = metadata._promotion?.inPromotion === true || originalItem._wasInPromotionBeforeConsolidate === true;
972
+ delete metadata._promotion;
973
+ if (Array.isArray(newItem.product_bundle)) {
974
+ for (const b of newItem.product_bundle) {
975
+ if (b && typeof b === 'object') delete b._promotion;
976
+ }
977
+ }
978
+ const originalBasePrice = getOrderProductOriginalPriceForPromotion(originalItem);
979
+ const shouldModifyPrice = priced.inPromotion || wasInPromotion;
980
+ const priceUnchangedPromo = priced.inPromotion && Number(priced.finalPrice) === Number(originalBasePrice);
981
+ if (shouldModifyPrice) {
982
+ if (priced.inPromotion && !priceUnchangedPromo) {
983
+ const promoUnitPrice = new _decimal.default(priced.finalPrice).toDecimalPlaces(2).toFixed(2);
984
+ applyPromoUnitPriceToLine(newItem, {
985
+ promoUnitPrice,
986
+ originalBasePrice,
987
+ strategyTitle: priced.strategyMetadata?.name
988
+ });
989
+ } else {
990
+ // 撤销上一轮促销:还原 selling_price 到 main_product_original_price 派生值
991
+ const optionSum = (0, _utils.sumOptionUnitPrice)((0, _utils.getProductSkuOptions)(newItem)).toNumber();
992
+ const sourceRestore = new _decimal.default(originalBasePrice).toDecimalPlaces(2).toFixed(2);
993
+ const mainRestore = new _decimal.default(Number(sourceRestore) || 0).plus(optionSum).toDecimalPlaces(2).toFixed(2);
994
+ metadata.source_product_price = sourceRestore;
995
+ metadata.main_product_selling_price = mainRestore;
996
+ metadata.price_schema_version = 2;
997
+ newItem.selling_price = (0, _utils.composeLinePrice)({
998
+ mainPrice: mainRestore,
999
+ bundle: newItem.product_bundle
1000
+ });
1001
+ updatePromotionDiscountList(newItem, null);
1002
+ }
1003
+ }
1004
+ newItem.num = priced.isSplit ? (0, _utils2.getSafeProductNum)(priced.quantity) : sourceCartNum;
1005
+
1006
+ // 拆分行:生成新 uid,复制原行
1007
+ if (priced.isSplit) {
1008
+ metadata.unique_identification_number = (0, _utils.createUuidV4)();
1009
+ }
1010
+ if (priced.strategyId) {
1011
+ const actionMeta = strategyActionMap.get(priced.strategyId);
1012
+ const promotionInfo = {
1013
+ strategyId: priced.strategyId,
1014
+ strategyMetadata: priced.strategyMetadata,
1015
+ inPromotion: priced.inPromotion,
1016
+ originalPrice: originalBasePrice,
1017
+ finalPrice: priced.finalPrice,
1018
+ actionType: actionMeta?.actionType,
1019
+ actionDetail: actionMeta?.actionDetail
1020
+ };
1021
+ const bundleIdx = priced.matchedBundleIndex;
1022
+ if (bundleIdx !== undefined && Array.isArray(newItem.product_bundle) && newItem.product_bundle[bundleIdx]) {
1023
+ newItem.product_bundle[bundleIdx]._promotion = promotionInfo;
1024
+ } else {
1025
+ metadata._promotion = promotionInfo;
1026
+ }
1027
+ }
1028
+ newItem.metadata = metadata;
1029
+
1030
+ // 5.1 第一个参与「买X送Y」策略的主商品,挂 giftInfo
1031
+ if (priced.inPromotion && priced.strategyId) {
1032
+ const gift = strategyGiftInfoMap.get(priced.strategyId);
1033
+ if (gift && Array.isArray(gift.giftOptions) && gift.giftOptions.length > 0 && !strategyGiftInfoOwner.has(priced.strategyId)) {
1034
+ const giftInfoData = {
1035
+ strategyName: gift.strategyName,
1036
+ giftCount: gift.giftCount,
1037
+ giftOptions: gift.giftOptions
1038
+ };
1039
+ const bundleIdx = priced.matchedBundleIndex;
1040
+ if (bundleIdx !== undefined && Array.isArray(newItem.product_bundle) && newItem.product_bundle[bundleIdx]?._promotion) {
1041
+ newItem.product_bundle[bundleIdx]._promotion.giftInfo = giftInfoData;
1042
+ } else if (metadata._promotion) {
1043
+ metadata._promotion.giftInfo = giftInfoData;
1044
+ }
1045
+ strategyGiftInfoOwner.set(priced.strategyId, getOrderProductUid(newItem));
1046
+ }
1047
+ }
1048
+
1049
+ // 记录原商品 uid(用于排序与未满足匹配)
1050
+ newItem._sortIndex = originalIndex;
1051
+ newItem._sortSubIndex = priced.inPromotion ? 0 : 1;
1052
+ newItem._originalProductUid = getOrderProductUid(originalItem);
1053
+ processedProducts.push(newItem);
1054
+ }
1055
+
1056
+ // 7. 赠品/编辑商品原样拼回;同时同步赠品的 sourceProductIds / sourceProductId
1057
+ for (const {
1058
+ item,
1059
+ originalListIndex
1060
+ } of existingGiftsWithIndex) {
1061
+ const next = item;
1062
+ next._sortIndex = originalListIndex;
1063
+ next._sortSubIndex = 0;
1064
+ const strategyId = getGiftInfo(next)?.strategyId;
1065
+ if (strategyId) {
1066
+ const latest = strategyGiftInfoMap.get(strategyId);
1067
+ const owner = strategyGiftInfoOwner.get(strategyId);
1068
+ if (latest && next.metadata?._giftInfo) {
1069
+ next.metadata._giftInfo.sourceProductIds = latest.sourceProductIds;
1070
+ }
1071
+ if (owner && next.metadata?._giftInfo) {
1072
+ next.metadata._giftInfo.sourceProductId = owner;
1073
+ }
1074
+ }
1075
+ processedProducts.push(next);
1076
+ }
1077
+ for (const {
1078
+ item,
1079
+ originalListIndex
1080
+ } of editProductsWithIndex) {
1081
+ const next = item;
1082
+ next._sortIndex = originalListIndex;
1083
+ next._sortSubIndex = 0;
1084
+ processedProducts.push(next);
1085
+ }
1086
+
1087
+ // 8. 排序回原顺序,参与促销的拆分行排在前
1088
+ processedProducts.sort((a, b) => {
1089
+ if (a._sortIndex !== b._sortIndex) return a._sortIndex - b._sortIndex;
1090
+ return a._sortSubIndex - b._sortSubIndex;
1091
+ });
1092
+
1093
+ // 8.1 同款参与促销行:先去重(买赠+满减并存)再合并 num
1094
+ const dedupedPromotionLines = dedupePromotionProcessedLines(processedProducts);
1095
+ processedProducts.splice(0, processedProducts.length, ...dedupedPromotionLines);
1096
+ const mergedPromotionLines = mergeIdenticalPromotionCartLines(processedProducts);
1097
+ processedProducts.splice(0, processedProducts.length, ...mergedPromotionLines);
1098
+ const splitPromoLines = splitXItemsPromoLinesByGroup(processedProducts, strategyActionMap);
1099
+ processedProducts.splice(0, processedProducts.length, ...splitPromoLines);
1100
+
1101
+ // 9. 给「有适用策略但未参与促销」的主商品打 inPromotion:false
1102
+ for (const product of processedProducts) {
1103
+ const metadata = product.metadata || {};
1104
+ if (metadata._promotion) continue;
1105
+ if (getGiftInfo(product)) continue;
1106
+ const lookupUid = String(product._originalProductUid ?? getOrderProductUid(product));
1107
+ const originalIndex = mainProducts.findIndex(item => getOrderProductUid(item) === lookupUid);
1108
+ if (originalIndex === -1) continue;
1109
+ const strategyData = applicableStrategiesResult[originalIndex];
1110
+ if (!strategyData || !strategyData.hasApplicableStrategy) continue;
1111
+ const first = strategyData.applicableStrategies[0];
1112
+ if (!first) continue;
1113
+ const originalPrice = getOrderProductOriginalPriceForPromotion(mainProducts[originalIndex]);
1114
+ metadata._promotion = {
1115
+ strategyId: first.strategyId,
1116
+ strategyMetadata: first.strategyMetadata,
1117
+ inPromotion: false,
1118
+ originalPrice,
1119
+ finalPrice: originalPrice
1120
+ };
1121
+ product.metadata = metadata;
1122
+ }
1123
+
1124
+ // 10. 赠品操作(在更新 lastEvaluatedGiftCount 前做对比)
1125
+ const giftActions = calculateGiftActions(extendedGifts, existingGifts);
1126
+
1127
+ // 10.1 更新 lastEvaluatedGiftCount,便于下次评估时判别「策略已变」
1128
+ for (const gift of existingGifts) {
1129
+ const strategyId = getGiftInfo(gift)?.strategyId;
1130
+ if (!strategyId) continue;
1131
+ const latest = strategyGiftInfoMap.get(strategyId);
1132
+ if (latest && gift.metadata?._giftInfo) {
1133
+ gift.metadata._giftInfo.lastEvaluatedGiftCount = latest.giftCount;
1134
+ }
1135
+ }
1136
+
1137
+ // 11. 未满足促销
1138
+ const unfulfilledPromotions = calculateUnfulfilledPromotions(processedProducts, applicableStrategiesResult, mainProducts);
1139
+
1140
+ // 12. 清理排序辅助字段
1141
+ for (const product of processedProducts) {
1142
+ delete product._sortIndex;
1143
+ delete product._sortSubIndex;
1144
+ delete product._originalProductUid;
1145
+ delete product._sourceCartNum;
1146
+ delete product._wasInPromotionBeforeConsolidate;
1147
+ }
1148
+ return {
1149
+ products: processedProducts,
1150
+ totalDiscount: result.totalDiscount,
1151
+ gifts: extendedGifts,
1152
+ giftActions,
1153
+ unfulfilledPromotions
1154
+ };
1155
+ }