@stamhoofd/structures 2.83.4 → 2.84.0

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 (316) hide show
  1. package/dist/index.d.ts +11 -2
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +11 -2
  4. package/dist/index.js.map +1 -1
  5. package/dist/src/AppliedRegistrationDiscount.d.ts +15 -0
  6. package/dist/src/AppliedRegistrationDiscount.d.ts.map +1 -0
  7. package/dist/src/AppliedRegistrationDiscount.js +24 -0
  8. package/dist/src/AppliedRegistrationDiscount.js.map +1 -0
  9. package/dist/src/BalanceItem.d.ts +13 -4
  10. package/dist/src/BalanceItem.d.ts.map +1 -1
  11. package/dist/src/BalanceItem.js +68 -22
  12. package/dist/src/BalanceItem.js.map +1 -1
  13. package/dist/src/BalanceItemDetailed.d.ts +2 -2
  14. package/dist/src/BalanceItemDetailed.d.ts.map +1 -1
  15. package/dist/src/BalanceItemDetailed.js +2 -2
  16. package/dist/src/BalanceItemDetailed.js.map +1 -1
  17. package/dist/src/BundleDiscount.d.ts +79 -0
  18. package/dist/src/BundleDiscount.d.ts.map +1 -0
  19. package/dist/src/BundleDiscount.js +379 -0
  20. package/dist/src/BundleDiscount.js.map +1 -0
  21. package/dist/src/BundleDiscountGroupPriceSettings.d.ts +15 -0
  22. package/dist/src/BundleDiscountGroupPriceSettings.d.ts.map +1 -0
  23. package/dist/src/BundleDiscountGroupPriceSettings.js +29 -0
  24. package/dist/src/BundleDiscountGroupPriceSettings.js.map +1 -0
  25. package/dist/src/Group.d.ts +2 -0
  26. package/dist/src/Group.d.ts.map +1 -1
  27. package/dist/src/Group.js +10 -0
  28. package/dist/src/Group.js.map +1 -1
  29. package/dist/src/GroupPriceDiscount.d.ts +14 -0
  30. package/dist/src/GroupPriceDiscount.d.ts.map +1 -0
  31. package/dist/src/GroupPriceDiscount.js +39 -0
  32. package/dist/src/GroupPriceDiscount.js.map +1 -0
  33. package/dist/src/GroupSettings.d.ts +7 -8
  34. package/dist/src/GroupSettings.d.ts.map +1 -1
  35. package/dist/src/GroupSettings.js +15 -29
  36. package/dist/src/GroupSettings.js.map +1 -1
  37. package/dist/src/Organization.d.ts +1 -1
  38. package/dist/src/Organization.d.ts.map +1 -1
  39. package/dist/src/Organization.js +15 -2
  40. package/dist/src/Organization.js.map +1 -1
  41. package/dist/src/Permissions.d.ts.map +1 -1
  42. package/dist/src/Permissions.js +1 -1
  43. package/dist/src/Permissions.js.map +1 -1
  44. package/dist/src/Platform.d.ts +1 -1
  45. package/dist/src/Platform.d.ts.map +1 -1
  46. package/dist/src/Platform.js +3 -3
  47. package/dist/src/Platform.js.map +1 -1
  48. package/dist/src/ReduceablePrice.d.ts +9 -0
  49. package/dist/src/ReduceablePrice.d.ts.map +1 -0
  50. package/dist/src/ReduceablePrice.js +29 -0
  51. package/dist/src/ReduceablePrice.js.map +1 -0
  52. package/dist/src/RegistrationPeriod.d.ts +2 -0
  53. package/dist/src/RegistrationPeriod.d.ts.map +1 -1
  54. package/dist/src/RegistrationPeriod.js +5 -0
  55. package/dist/src/RegistrationPeriod.js.map +1 -1
  56. package/dist/src/Token.d.ts.map +1 -1
  57. package/dist/src/Token.js +7 -4
  58. package/dist/src/Token.js.map +1 -1
  59. package/dist/src/Version.d.ts +1 -1
  60. package/dist/src/Version.js +1 -1
  61. package/dist/src/email/Email.d.ts +5 -3
  62. package/dist/src/email/Email.d.ts.map +1 -1
  63. package/dist/src/email/Email.js +3 -0
  64. package/dist/src/email/Email.js.map +1 -1
  65. package/dist/src/endpoints/RegisterResponse.d.ts +2 -2
  66. package/dist/src/endpoints/RegisterResponse.d.ts.map +1 -1
  67. package/dist/src/endpoints/RegisterResponse.js +2 -2
  68. package/dist/src/endpoints/RegisterResponse.js.map +1 -1
  69. package/dist/src/filters/ExcelExportRequest.d.ts +1 -0
  70. package/dist/src/filters/ExcelExportRequest.d.ts.map +1 -1
  71. package/dist/src/filters/ExcelExportRequest.js +1 -0
  72. package/dist/src/filters/ExcelExportRequest.js.map +1 -1
  73. package/dist/src/filters/FilterCompilers.d.ts +18 -0
  74. package/dist/src/filters/FilterCompilers.d.ts.map +1 -0
  75. package/dist/src/filters/FilterCompilers.js +110 -0
  76. package/dist/src/filters/FilterCompilers.js.map +1 -0
  77. package/dist/src/filters/InMemoryFilter.d.ts +5 -3
  78. package/dist/src/filters/InMemoryFilter.d.ts.map +1 -1
  79. package/dist/src/filters/InMemoryFilter.js +20 -75
  80. package/dist/src/filters/InMemoryFilter.js.map +1 -1
  81. package/dist/src/filters/InMemoryFilter.test.d.ts +2 -0
  82. package/dist/src/filters/InMemoryFilter.test.d.ts.map +1 -0
  83. package/dist/src/filters/InMemoryFilter.test.js +293 -0
  84. package/dist/src/filters/InMemoryFilter.test.js.map +1 -0
  85. package/dist/src/helpers/MemberPlatformMembershipHelper.d.ts +12 -0
  86. package/dist/src/helpers/MemberPlatformMembershipHelper.d.ts.map +1 -0
  87. package/dist/src/helpers/MemberPlatformMembershipHelper.js +74 -0
  88. package/dist/src/helpers/MemberPlatformMembershipHelper.js.map +1 -0
  89. package/dist/src/members/Member.d.ts +1 -0
  90. package/dist/src/members/Member.d.ts.map +1 -1
  91. package/dist/src/members/Member.js +3 -0
  92. package/dist/src/members/Member.js.map +1 -1
  93. package/dist/src/members/MembershipStatus.d.ts +13 -0
  94. package/dist/src/members/MembershipStatus.d.ts.map +1 -0
  95. package/dist/src/members/MembershipStatus.js +18 -0
  96. package/dist/src/members/MembershipStatus.js.map +1 -0
  97. package/dist/src/members/PaymentGeneral.js +1 -1
  98. package/dist/src/members/PaymentGeneral.js.map +1 -1
  99. package/dist/src/members/PaymentWithRegistrations.d.ts +2 -2
  100. package/dist/src/members/PaymentWithRegistrations.d.ts.map +1 -1
  101. package/dist/src/members/PaymentWithRegistrations.js +2 -2
  102. package/dist/src/members/PaymentWithRegistrations.js.map +1 -1
  103. package/dist/src/members/PlatformMember.d.ts +24 -14
  104. package/dist/src/members/PlatformMember.d.ts.map +1 -1
  105. package/dist/src/members/PlatformMember.js +93 -116
  106. package/dist/src/members/PlatformMember.js.map +1 -1
  107. package/dist/src/members/PlatformMember.test.js +4 -7
  108. package/dist/src/members/PlatformMember.test.js.map +1 -1
  109. package/dist/src/members/Registration.d.ts +37 -0
  110. package/dist/src/members/Registration.d.ts.map +1 -1
  111. package/dist/src/members/Registration.js +48 -0
  112. package/dist/src/members/Registration.js.map +1 -1
  113. package/dist/src/members/RegistrationWithMember.d.ts +15 -3
  114. package/dist/src/members/RegistrationWithMember.d.ts.map +1 -1
  115. package/dist/src/members/RegistrationWithMember.js +32 -2
  116. package/dist/src/members/RegistrationWithMember.js.map +1 -1
  117. package/dist/src/members/RegistrationWithMemberBlob.d.ts +6 -0
  118. package/dist/src/members/RegistrationWithMemberBlob.d.ts.map +1 -0
  119. package/dist/src/members/RegistrationWithMemberBlob.js +14 -0
  120. package/dist/src/members/RegistrationWithMemberBlob.js.map +1 -0
  121. package/{esm/dist/src/members/RegistrationWithMember.d.ts → dist/src/members/RegistrationWithTinyMember.d.ts} +3 -3
  122. package/dist/src/members/RegistrationWithTinyMember.d.ts.map +1 -0
  123. package/dist/src/members/RegistrationWithTinyMember.js +20 -0
  124. package/dist/src/members/RegistrationWithTinyMember.js.map +1 -0
  125. package/dist/src/members/RegistrationsBlob.d.ts +8 -0
  126. package/dist/src/members/RegistrationsBlob.d.ts.map +1 -0
  127. package/dist/src/{endpoints/GroupsWithOrganizations.js → members/RegistrationsBlob.js} +9 -9
  128. package/dist/src/members/RegistrationsBlob.js.map +1 -0
  129. package/dist/src/members/checkout/RegisterCart.d.ts +18 -2
  130. package/dist/src/members/checkout/RegisterCart.d.ts.map +1 -1
  131. package/dist/src/members/checkout/RegisterCart.js +51 -2
  132. package/dist/src/members/checkout/RegisterCart.js.map +1 -1
  133. package/dist/src/members/checkout/RegisterCart.test.d.ts +2 -0
  134. package/dist/src/members/checkout/RegisterCart.test.d.ts.map +1 -0
  135. package/dist/src/members/checkout/RegisterCart.test.js +1384 -0
  136. package/dist/src/members/checkout/RegisterCart.test.js.map +1 -0
  137. package/dist/src/members/checkout/RegisterCheckout.d.ts +23 -3
  138. package/dist/src/members/checkout/RegisterCheckout.d.ts.map +1 -1
  139. package/dist/src/members/checkout/RegisterCheckout.js +108 -11
  140. package/dist/src/members/checkout/RegisterCheckout.js.map +1 -1
  141. package/dist/src/members/checkout/RegisterItem.d.ts +7 -13
  142. package/dist/src/members/checkout/RegisterItem.d.ts.map +1 -1
  143. package/dist/src/members/checkout/RegisterItem.js +50 -23
  144. package/dist/src/members/checkout/RegisterItem.js.map +1 -1
  145. package/dist/src/members/checkout/RegistrationWithPlatformMember.d.ts +13 -0
  146. package/dist/src/members/checkout/RegistrationWithPlatformMember.d.ts.map +1 -0
  147. package/dist/src/members/checkout/RegistrationWithPlatformMember.js +21 -0
  148. package/dist/src/members/checkout/RegistrationWithPlatformMember.js.map +1 -0
  149. package/dist/src/members/records/RecordCategory.d.ts +1 -0
  150. package/dist/src/members/records/RecordCategory.d.ts.map +1 -1
  151. package/dist/src/members/records/RecordCategory.js +10 -0
  152. package/dist/src/members/records/RecordCategory.js.map +1 -1
  153. package/dist/src/webshops/Webshop.d.ts +1 -0
  154. package/dist/src/webshops/Webshop.d.ts.map +1 -1
  155. package/dist/src/webshops/Webshop.js +4 -1
  156. package/dist/src/webshops/Webshop.js.map +1 -1
  157. package/dist/src/webshops/WebshopMetaData.d.ts +1 -0
  158. package/dist/src/webshops/WebshopMetaData.d.ts.map +1 -1
  159. package/dist/src/webshops/WebshopMetaData.js +4 -0
  160. package/dist/src/webshops/WebshopMetaData.js.map +1 -1
  161. package/esm/dist/index.d.ts +11 -2
  162. package/esm/dist/index.d.ts.map +1 -1
  163. package/esm/dist/index.js +11 -2
  164. package/esm/dist/index.js.map +1 -1
  165. package/esm/dist/src/AppliedRegistrationDiscount.d.ts +15 -0
  166. package/esm/dist/src/AppliedRegistrationDiscount.d.ts.map +1 -0
  167. package/esm/dist/src/AppliedRegistrationDiscount.js +20 -0
  168. package/esm/dist/src/AppliedRegistrationDiscount.js.map +1 -0
  169. package/esm/dist/src/BalanceItem.d.ts +13 -4
  170. package/esm/dist/src/BalanceItem.d.ts.map +1 -1
  171. package/esm/dist/src/BalanceItem.js +68 -22
  172. package/esm/dist/src/BalanceItem.js.map +1 -1
  173. package/esm/dist/src/BalanceItemDetailed.d.ts +2 -2
  174. package/esm/dist/src/BalanceItemDetailed.d.ts.map +1 -1
  175. package/esm/dist/src/BalanceItemDetailed.js +2 -2
  176. package/esm/dist/src/BalanceItemDetailed.js.map +1 -1
  177. package/esm/dist/src/BundleDiscount.d.ts +79 -0
  178. package/esm/dist/src/BundleDiscount.d.ts.map +1 -0
  179. package/esm/dist/src/BundleDiscount.js +374 -0
  180. package/esm/dist/src/BundleDiscount.js.map +1 -0
  181. package/esm/dist/src/BundleDiscountGroupPriceSettings.d.ts +15 -0
  182. package/esm/dist/src/BundleDiscountGroupPriceSettings.d.ts.map +1 -0
  183. package/esm/dist/src/BundleDiscountGroupPriceSettings.js +25 -0
  184. package/esm/dist/src/BundleDiscountGroupPriceSettings.js.map +1 -0
  185. package/esm/dist/src/Group.d.ts +2 -0
  186. package/esm/dist/src/Group.d.ts.map +1 -1
  187. package/esm/dist/src/Group.js +10 -0
  188. package/esm/dist/src/Group.js.map +1 -1
  189. package/esm/dist/src/GroupPriceDiscount.d.ts +14 -0
  190. package/esm/dist/src/GroupPriceDiscount.d.ts.map +1 -0
  191. package/esm/dist/src/GroupPriceDiscount.js +35 -0
  192. package/esm/dist/src/GroupPriceDiscount.js.map +1 -0
  193. package/esm/dist/src/GroupSettings.d.ts +7 -8
  194. package/esm/dist/src/GroupSettings.d.ts.map +1 -1
  195. package/esm/dist/src/GroupSettings.js +10 -23
  196. package/esm/dist/src/GroupSettings.js.map +1 -1
  197. package/esm/dist/src/Organization.d.ts +1 -1
  198. package/esm/dist/src/Organization.d.ts.map +1 -1
  199. package/esm/dist/src/Organization.js +14 -1
  200. package/esm/dist/src/Organization.js.map +1 -1
  201. package/esm/dist/src/Permissions.d.ts.map +1 -1
  202. package/esm/dist/src/Permissions.js +1 -1
  203. package/esm/dist/src/Permissions.js.map +1 -1
  204. package/esm/dist/src/Platform.d.ts +1 -1
  205. package/esm/dist/src/Platform.d.ts.map +1 -1
  206. package/esm/dist/src/Platform.js +1 -1
  207. package/esm/dist/src/Platform.js.map +1 -1
  208. package/esm/dist/src/ReduceablePrice.d.ts +9 -0
  209. package/esm/dist/src/ReduceablePrice.d.ts.map +1 -0
  210. package/esm/dist/src/ReduceablePrice.js +25 -0
  211. package/esm/dist/src/ReduceablePrice.js.map +1 -0
  212. package/esm/dist/src/RegistrationPeriod.d.ts +2 -0
  213. package/esm/dist/src/RegistrationPeriod.d.ts.map +1 -1
  214. package/esm/dist/src/RegistrationPeriod.js +5 -0
  215. package/esm/dist/src/RegistrationPeriod.js.map +1 -1
  216. package/esm/dist/src/Token.d.ts.map +1 -1
  217. package/esm/dist/src/Token.js +7 -4
  218. package/esm/dist/src/Token.js.map +1 -1
  219. package/esm/dist/src/Version.d.ts +1 -1
  220. package/esm/dist/src/Version.js +1 -1
  221. package/esm/dist/src/email/Email.d.ts +5 -3
  222. package/esm/dist/src/email/Email.d.ts.map +1 -1
  223. package/esm/dist/src/email/Email.js +3 -0
  224. package/esm/dist/src/email/Email.js.map +1 -1
  225. package/esm/dist/src/endpoints/RegisterResponse.d.ts +2 -2
  226. package/esm/dist/src/endpoints/RegisterResponse.d.ts.map +1 -1
  227. package/esm/dist/src/endpoints/RegisterResponse.js +2 -2
  228. package/esm/dist/src/endpoints/RegisterResponse.js.map +1 -1
  229. package/esm/dist/src/filters/ExcelExportRequest.d.ts +1 -0
  230. package/esm/dist/src/filters/ExcelExportRequest.d.ts.map +1 -1
  231. package/esm/dist/src/filters/ExcelExportRequest.js +1 -0
  232. package/esm/dist/src/filters/ExcelExportRequest.js.map +1 -1
  233. package/esm/dist/src/filters/FilterCompilers.d.ts +18 -0
  234. package/esm/dist/src/filters/FilterCompilers.d.ts.map +1 -0
  235. package/esm/dist/src/filters/FilterCompilers.js +104 -0
  236. package/esm/dist/src/filters/FilterCompilers.js.map +1 -0
  237. package/esm/dist/src/filters/InMemoryFilter.d.ts +5 -3
  238. package/esm/dist/src/filters/InMemoryFilter.d.ts.map +1 -1
  239. package/esm/dist/src/filters/InMemoryFilter.js +19 -74
  240. package/esm/dist/src/filters/InMemoryFilter.js.map +1 -1
  241. package/esm/dist/src/helpers/MemberPlatformMembershipHelper.d.ts +12 -0
  242. package/esm/dist/src/helpers/MemberPlatformMembershipHelper.d.ts.map +1 -0
  243. package/esm/dist/src/helpers/MemberPlatformMembershipHelper.js +70 -0
  244. package/esm/dist/src/helpers/MemberPlatformMembershipHelper.js.map +1 -0
  245. package/esm/dist/src/members/Member.d.ts +1 -0
  246. package/esm/dist/src/members/Member.d.ts.map +1 -1
  247. package/esm/dist/src/members/Member.js +3 -0
  248. package/esm/dist/src/members/Member.js.map +1 -1
  249. package/esm/dist/src/members/MembershipStatus.d.ts +13 -0
  250. package/esm/dist/src/members/MembershipStatus.d.ts.map +1 -0
  251. package/esm/dist/src/members/MembershipStatus.js +15 -0
  252. package/esm/dist/src/members/MembershipStatus.js.map +1 -0
  253. package/esm/dist/src/members/PaymentGeneral.js +1 -1
  254. package/esm/dist/src/members/PaymentGeneral.js.map +1 -1
  255. package/esm/dist/src/members/PaymentWithRegistrations.d.ts +2 -2
  256. package/esm/dist/src/members/PaymentWithRegistrations.d.ts.map +1 -1
  257. package/esm/dist/src/members/PaymentWithRegistrations.js +2 -2
  258. package/esm/dist/src/members/PaymentWithRegistrations.js.map +1 -1
  259. package/esm/dist/src/members/PlatformMember.d.ts +24 -14
  260. package/esm/dist/src/members/PlatformMember.d.ts.map +1 -1
  261. package/esm/dist/src/members/PlatformMember.js +91 -115
  262. package/esm/dist/src/members/PlatformMember.js.map +1 -1
  263. package/esm/dist/src/members/Registration.d.ts +37 -0
  264. package/esm/dist/src/members/Registration.d.ts.map +1 -1
  265. package/esm/dist/src/members/Registration.js +48 -0
  266. package/esm/dist/src/members/Registration.js.map +1 -1
  267. package/esm/dist/src/members/RegistrationWithMemberBlob.d.ts +6 -0
  268. package/esm/dist/src/members/RegistrationWithMemberBlob.d.ts.map +1 -0
  269. package/esm/dist/src/members/RegistrationWithMemberBlob.js +10 -0
  270. package/esm/dist/src/members/RegistrationWithMemberBlob.js.map +1 -0
  271. package/esm/dist/src/members/RegistrationWithTinyMember.d.ts +7 -0
  272. package/esm/dist/src/members/RegistrationWithTinyMember.d.ts.map +1 -0
  273. package/esm/dist/src/members/{RegistrationWithMember.js → RegistrationWithTinyMember.js} +4 -4
  274. package/esm/dist/src/members/RegistrationWithTinyMember.js.map +1 -0
  275. package/esm/dist/src/members/RegistrationsBlob.d.ts +8 -0
  276. package/esm/dist/src/members/RegistrationsBlob.d.ts.map +1 -0
  277. package/esm/dist/src/members/RegistrationsBlob.js +18 -0
  278. package/esm/dist/src/members/RegistrationsBlob.js.map +1 -0
  279. package/esm/dist/src/members/checkout/RegisterCart.d.ts +18 -2
  280. package/esm/dist/src/members/checkout/RegisterCart.d.ts.map +1 -1
  281. package/esm/dist/src/members/checkout/RegisterCart.js +51 -2
  282. package/esm/dist/src/members/checkout/RegisterCart.js.map +1 -1
  283. package/esm/dist/src/members/checkout/RegisterCheckout.d.ts +23 -3
  284. package/esm/dist/src/members/checkout/RegisterCheckout.d.ts.map +1 -1
  285. package/esm/dist/src/members/checkout/RegisterCheckout.js +108 -11
  286. package/esm/dist/src/members/checkout/RegisterCheckout.js.map +1 -1
  287. package/esm/dist/src/members/checkout/RegisterItem.d.ts +7 -13
  288. package/esm/dist/src/members/checkout/RegisterItem.d.ts.map +1 -1
  289. package/esm/dist/src/members/checkout/RegisterItem.js +48 -20
  290. package/esm/dist/src/members/checkout/RegisterItem.js.map +1 -1
  291. package/esm/dist/src/members/checkout/RegistrationWithPlatformMember.d.ts +13 -0
  292. package/esm/dist/src/members/checkout/RegistrationWithPlatformMember.d.ts.map +1 -0
  293. package/esm/dist/src/members/checkout/RegistrationWithPlatformMember.js +17 -0
  294. package/esm/dist/src/members/checkout/RegistrationWithPlatformMember.js.map +1 -0
  295. package/esm/dist/src/members/records/RecordCategory.d.ts +1 -0
  296. package/esm/dist/src/members/records/RecordCategory.d.ts.map +1 -1
  297. package/esm/dist/src/members/records/RecordCategory.js +11 -1
  298. package/esm/dist/src/members/records/RecordCategory.js.map +1 -1
  299. package/esm/dist/src/webshops/Webshop.d.ts +1 -0
  300. package/esm/dist/src/webshops/Webshop.d.ts.map +1 -1
  301. package/esm/dist/src/webshops/Webshop.js +4 -1
  302. package/esm/dist/src/webshops/Webshop.js.map +1 -1
  303. package/esm/dist/src/webshops/WebshopMetaData.d.ts +1 -0
  304. package/esm/dist/src/webshops/WebshopMetaData.d.ts.map +1 -1
  305. package/esm/dist/src/webshops/WebshopMetaData.js +4 -0
  306. package/esm/dist/src/webshops/WebshopMetaData.js.map +1 -1
  307. package/package.json +2 -2
  308. package/dist/src/endpoints/GroupsWithOrganizations.d.ts +0 -8
  309. package/dist/src/endpoints/GroupsWithOrganizations.d.ts.map +0 -1
  310. package/dist/src/endpoints/GroupsWithOrganizations.js.map +0 -1
  311. package/esm/dist/src/endpoints/GroupsWithOrganizations.d.ts +0 -8
  312. package/esm/dist/src/endpoints/GroupsWithOrganizations.d.ts.map +0 -1
  313. package/esm/dist/src/endpoints/GroupsWithOrganizations.js +0 -18
  314. package/esm/dist/src/endpoints/GroupsWithOrganizations.js.map +0 -1
  315. package/esm/dist/src/members/RegistrationWithMember.d.ts.map +0 -1
  316. package/esm/dist/src/members/RegistrationWithMember.js.map +0 -1
@@ -0,0 +1,1384 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const BundleDiscountGroupPriceSettings_js_1 = require("../../BundleDiscountGroupPriceSettings.js");
4
+ const Group_js_1 = require("../../Group.js");
5
+ const GroupPriceDiscount_js_1 = require("../../GroupPriceDiscount.js");
6
+ const GroupSettings_js_1 = require("../../GroupSettings.js");
7
+ const Organization_js_1 = require("../../Organization.js");
8
+ const Platform_js_1 = require("../../Platform.js");
9
+ const ReduceablePrice_js_1 = require("../../ReduceablePrice.js");
10
+ const RegistrationPeriod_js_1 = require("../../RegistrationPeriod.js");
11
+ const TranslatedString_js_1 = require("../../TranslatedString.js");
12
+ const MemberDetails_js_1 = require("../MemberDetails.js");
13
+ const PlatformMember_js_1 = require("../PlatformMember.js");
14
+ const RegisterItem_js_1 = require("./RegisterItem.js");
15
+ const MemberWithRegistrationsBlob_js_1 = require("../MemberWithRegistrationsBlob.js");
16
+ // Should be last because of circular dependencies
17
+ const BundleDiscount_js_1 = require("../../BundleDiscount.js");
18
+ const Registration_js_1 = require("../Registration.js");
19
+ const AppliedRegistrationDiscount_js_1 = require("../../AppliedRegistrationDiscount.js");
20
+ const RegistrationWithPlatformMember_js_1 = require("./RegistrationWithPlatformMember.js");
21
+ const GenericBalance_js_1 = require("../../GenericBalance.js");
22
+ // Helper functions to reduce code duplication
23
+ function createTestPeriod() {
24
+ const period = RegistrationPeriod_js_1.RegistrationPeriod.create({});
25
+ const organizationRegistrationPeriod = RegistrationPeriod_js_1.OrganizationRegistrationPeriod.create({
26
+ period,
27
+ });
28
+ const organization = Organization_js_1.Organization.create({
29
+ period: organizationRegistrationPeriod,
30
+ });
31
+ return { period, organizationRegistrationPeriod, organization };
32
+ }
33
+ function createTestGroups(organization, period, prices = [5000, 4000], names) {
34
+ return prices.map((price, index) => {
35
+ return Group_js_1.Group.create({
36
+ organizationId: organization.id,
37
+ periodId: period.id,
38
+ settings: GroupSettings_js_1.GroupSettings.create({
39
+ name: names ? new TranslatedString_js_1.TranslatedString(names[index]) : undefined,
40
+ prices: [
41
+ GroupSettings_js_1.GroupPrice.create({
42
+ price: ReduceablePrice_js_1.ReduceablePrice.create({
43
+ price,
44
+ }),
45
+ }),
46
+ ],
47
+ }),
48
+ });
49
+ });
50
+ }
51
+ function createBundleDiscount({ name = 'Bundle discount', countWholeFamily = true, countPerGroup = false, discounts = [
52
+ { value: 1000, type: GroupPriceDiscount_js_1.GroupPriceDiscountType.Fixed },
53
+ { value: 1500, type: GroupPriceDiscount_js_1.GroupPriceDiscountType.Fixed },
54
+ ], } = {}) {
55
+ return BundleDiscount_js_1.BundleDiscount.create({
56
+ name: new TranslatedString_js_1.TranslatedString(name),
57
+ countWholeFamily,
58
+ countPerGroup,
59
+ discounts: discounts.map(d => {
60
+ var _a;
61
+ return GroupPriceDiscount_js_1.GroupPriceDiscount.create({
62
+ value: ReduceablePrice_js_1.ReduceablePrice.create({
63
+ price: d.value,
64
+ reducedPrice: (_a = d.reducedValue) !== null && _a !== void 0 ? _a : null,
65
+ }),
66
+ type: d.type,
67
+ });
68
+ }),
69
+ });
70
+ }
71
+ function createTestFamily(memberCount = 3) {
72
+ const members = Array(memberCount).fill(null).map((_, i) => {
73
+ const names = ['John', 'Jane', 'Jack'];
74
+ return MemberWithRegistrationsBlob_js_1.MemberWithRegistrationsBlob.create({
75
+ details: MemberDetails_js_1.MemberDetails.create({
76
+ firstName: names[i] || `Member${i + 1}`,
77
+ lastName: 'Doe',
78
+ }),
79
+ });
80
+ });
81
+ const blob = MemberWithRegistrationsBlob_js_1.MembersBlob.create({
82
+ members,
83
+ });
84
+ return PlatformMember_js_1.PlatformFamily.create(blob, { platform: Platform_js_1.Platform.create({}) });
85
+ }
86
+ function setupDiscountTest({ memberCount = 3, groupPrices = [5000, 4000], groupNames, bundleDiscount, } = {}) {
87
+ const { period, organizationRegistrationPeriod, organization } = createTestPeriod();
88
+ const groups = createTestGroups(organization, period, groupPrices, groupNames);
89
+ organizationRegistrationPeriod.groups = groups;
90
+ const discount = bundleDiscount || createBundleDiscount();
91
+ organizationRegistrationPeriod.settings.bundleDiscounts.push(discount);
92
+ // Setup bundle discounts on all groups
93
+ groups.forEach((group) => {
94
+ group.settings.prices[0].bundleDiscounts.set(discount.id, BundleDiscountGroupPriceSettings_js_1.BundleDiscountGroupPriceSettings.create({
95
+ name: discount.name,
96
+ }));
97
+ });
98
+ const family = createTestFamily(memberCount);
99
+ return {
100
+ period,
101
+ organizationRegistrationPeriod,
102
+ organization,
103
+ groups,
104
+ discount,
105
+ family,
106
+ };
107
+ }
108
+ function addHistoricRegistration(member, group, organization, appliedDiscounts = new Map()) {
109
+ const registration = Registration_js_1.Registration.create({
110
+ group: group,
111
+ groupPrice: group.settings.prices[0],
112
+ memberId: member.id,
113
+ organizationId: group.organizationId,
114
+ registeredAt: new Date('2023-01-01'),
115
+ startDate: new Date('2023-01-01'),
116
+ price: group.settings.prices[0].price.price,
117
+ balances: [
118
+ GenericBalance_js_1.GenericBalance.create({
119
+ organizationId: organization.id,
120
+ amountPaid: 0,
121
+ amountOpen: group.settings.prices[0].price.price - [...appliedDiscounts.values()].reduce((acc, d) => acc + d.amount, 0),
122
+ amountPending: 0,
123
+ }),
124
+ ],
125
+ discounts: appliedDiscounts,
126
+ });
127
+ member.member.registrations.push(registration);
128
+ member.family.insertOrganization(organization);
129
+ return registration;
130
+ }
131
+ describe('Unit.RegisterCart', () => {
132
+ describe('Bundle Discounts', () => {
133
+ it('A fixed discount is granted for the second and third registration in a bundle', () => {
134
+ const { organization, groups, family } = setupDiscountTest({
135
+ bundleDiscount: createBundleDiscount({
136
+ name: 'Bundle discount',
137
+ discounts: [
138
+ { value: 1000, type: GroupPriceDiscount_js_1.GroupPriceDiscountType.Fixed },
139
+ { value: 1500, type: GroupPriceDiscount_js_1.GroupPriceDiscountType.Fixed },
140
+ ],
141
+ }),
142
+ });
143
+ const [groupA, groupB] = groups;
144
+ const [memberA, memberB, memberC] = family.members;
145
+ const itemA = RegisterItem_js_1.RegisterItem.defaultFor(memberA, groupA, organization);
146
+ const itemB = RegisterItem_js_1.RegisterItem.defaultFor(memberB, groupB, organization);
147
+ const itemC = RegisterItem_js_1.RegisterItem.defaultFor(memberC, groupB, organization);
148
+ // Add to a single cart
149
+ const checkout = family.checkout;
150
+ const cart = checkout.cart;
151
+ cart.add(itemA);
152
+ cart.add(itemB);
153
+ cart.add(itemC);
154
+ cart.calculatePrices();
155
+ // Check price for item A is 50.00
156
+ expect(itemA.calculatedPrice).toEqual(5000);
157
+ expect(itemA.calculatedRefund).toEqual(0);
158
+ expect(itemA.calculatedPriceDueLater).toEqual(0);
159
+ // Check price for items B and C
160
+ expect(itemB.calculatedPrice).toEqual(4000);
161
+ expect(itemB.calculatedRefund).toEqual(0);
162
+ expect(itemB.calculatedPriceDueLater).toEqual(0);
163
+ expect(itemC.calculatedPrice).toEqual(4000);
164
+ expect(itemC.calculatedRefund).toEqual(0);
165
+ expect(itemC.calculatedPriceDueLater).toEqual(0);
166
+ // Check calculated discount is 10_00 + 15_00 = 25_00
167
+ expect(cart.bundleDiscounts).toHaveLength(1);
168
+ expect(cart.bundleDiscounts[0].total).toEqual(2500);
169
+ expect(cart.bundleDiscounts[0].totalAlreadyApplied).toEqual(0);
170
+ expect(cart.bundleDiscounts[0].netTotal).toEqual(2500);
171
+ expect(cart.price).toEqual(5000 + 4000 + 4000);
172
+ expect(checkout.totalPrice).toEqual(5000 + 4000 + 4000 - 2500);
173
+ expect(checkout.priceBreakown).toEqual([
174
+ expect.objectContaining({
175
+ price: 13000,
176
+ }),
177
+ {
178
+ name: 'Bundle discount',
179
+ price: -2500,
180
+ },
181
+ expect.objectContaining({
182
+ price: 13000 - 2500,
183
+ }),
184
+ ]);
185
+ });
186
+ test('A percentage discount is granted for the second and third registration in a bundle', () => {
187
+ const { organization, groups, family } = setupDiscountTest({
188
+ bundleDiscount: createBundleDiscount({
189
+ name: 'Bundle discount',
190
+ discounts: [
191
+ { value: 1000, type: GroupPriceDiscount_js_1.GroupPriceDiscountType.Percentage },
192
+ { value: 1500, type: GroupPriceDiscount_js_1.GroupPriceDiscountType.Percentage },
193
+ ],
194
+ }),
195
+ });
196
+ const [groupA, groupB] = groups;
197
+ const [memberA, memberB, memberC] = family.members;
198
+ const itemA = RegisterItem_js_1.RegisterItem.defaultFor(memberA, groupA, organization);
199
+ const itemB = RegisterItem_js_1.RegisterItem.defaultFor(memberB, groupB, organization);
200
+ const itemC = RegisterItem_js_1.RegisterItem.defaultFor(memberC, groupB, organization);
201
+ // Add to a single cart
202
+ const checkout = family.checkout;
203
+ const cart = checkout.cart;
204
+ cart.add(itemA);
205
+ cart.add(itemB);
206
+ cart.add(itemC);
207
+ cart.calculatePrices();
208
+ // Check price for items
209
+ expect(itemA.calculatedPrice).toEqual(5000);
210
+ expect(itemB.calculatedPrice).toEqual(4000);
211
+ expect(itemC.calculatedPrice).toEqual(4000);
212
+ // 15% will be applied to 50 euro (max), and 10% to one of the 40 euros
213
+ // Check calculated discount is 50_00 * 0.15 + 40_00 * 0.10 = 7_50 + 4_00 = 11_50
214
+ expect(cart.bundleDiscounts).toHaveLength(1);
215
+ expect(cart.bundleDiscounts[0].total).toEqual(1150);
216
+ expect(cart.bundleDiscounts[0].netTotal).toEqual(1150);
217
+ expect(cart.price).toEqual(5000 + 4000 + 4000);
218
+ expect(checkout.totalPrice).toEqual(5000 + 4000 + 4000 - 1150);
219
+ expect(checkout.priceBreakown).toEqual([
220
+ expect.objectContaining({
221
+ price: 13000,
222
+ }),
223
+ {
224
+ name: 'Bundle discount',
225
+ price: -1150,
226
+ },
227
+ expect.objectContaining({
228
+ price: 13000 - 1150,
229
+ }),
230
+ ]);
231
+ });
232
+ test('Historic registrations are included in the calculation', () => {
233
+ const { organization, groups, family } = setupDiscountTest({
234
+ bundleDiscount: createBundleDiscount({
235
+ name: 'Bundle discount',
236
+ discounts: [
237
+ { value: 1000, type: GroupPriceDiscount_js_1.GroupPriceDiscountType.Percentage },
238
+ { value: 1500, type: GroupPriceDiscount_js_1.GroupPriceDiscountType.Percentage },
239
+ ],
240
+ }),
241
+ });
242
+ const [groupA, groupB] = groups;
243
+ const [memberA, memberB, memberC] = family.members;
244
+ // Member A is already registered for group A in the past
245
+ addHistoricRegistration(memberA, groupA, organization);
246
+ const itemB = RegisterItem_js_1.RegisterItem.defaultFor(memberB, groupB, organization);
247
+ const itemC = RegisterItem_js_1.RegisterItem.defaultFor(memberC, groupB, organization);
248
+ // Add to a single cart
249
+ const checkout = family.checkout;
250
+ const cart = checkout.cart;
251
+ cart.add(itemB);
252
+ cart.add(itemC);
253
+ cart.calculatePrices();
254
+ // Check price for items
255
+ expect(itemB.calculatedPrice).toEqual(4000);
256
+ expect(itemC.calculatedPrice).toEqual(4000);
257
+ // 15% will be applied to 50 euro (max), and 10% to one of the 40 euros
258
+ // Check calculated discount is 50_00 * 0.15 + 40_00 * 0.10 = 7_50 + 4_00 = 11_50
259
+ expect(cart.bundleDiscounts).toHaveLength(1);
260
+ expect(cart.bundleDiscounts[0].total).toEqual(1150);
261
+ expect(cart.bundleDiscounts[0].netTotal).toEqual(1150);
262
+ expect(cart.price).toEqual(4000 + 4000);
263
+ expect(checkout.totalPrice).toEqual(4000 + 4000 - 1150);
264
+ expect(checkout.priceBreakown).toEqual([
265
+ expect.objectContaining({
266
+ price: 8000,
267
+ }),
268
+ {
269
+ name: 'Bundle discount',
270
+ price: -1150,
271
+ },
272
+ expect.objectContaining({
273
+ price: 8000 - 1150,
274
+ }),
275
+ ]);
276
+ });
277
+ test('Deleting a registration also removes the attached discount', () => {
278
+ // We have two family members with existing registrations.
279
+ // One of those registrations received a discount earlier.
280
+ // But since we are adding a new registration in our cart, a more optimal discount
281
+ // would be if that registration would get the newly higher discount instead
282
+ // (because it is percentage based)
283
+ const { organization, groups, family, discount } = setupDiscountTest({
284
+ memberCount: 2,
285
+ bundleDiscount: createBundleDiscount({
286
+ name: 'Multiple family members discount',
287
+ countWholeFamily: true,
288
+ countPerGroup: false,
289
+ discounts: [
290
+ { value: 1000, type: GroupPriceDiscount_js_1.GroupPriceDiscountType.Percentage },
291
+ { value: 1500, type: GroupPriceDiscount_js_1.GroupPriceDiscountType.Percentage },
292
+ ],
293
+ }),
294
+ groupPrices: [10000, 4000],
295
+ });
296
+ const [groupA, groupB] = groups;
297
+ const [memberA, memberB] = family.members;
298
+ const registrationA = addHistoricRegistration(memberA, groupA, organization);
299
+ const registrationB = addHistoricRegistration(memberB, groupB, organization, new Map([
300
+ [
301
+ discount.id,
302
+ AppliedRegistrationDiscount_js_1.AppliedRegistrationDiscount.create({
303
+ name: discount.name,
304
+ amount: 1000, // 10% discount on 100_00 = 10_00
305
+ }),
306
+ ],
307
+ ]));
308
+ // If we delete registration B, the discount won't be visible in the cart because it is included in the registration
309
+ const checkout = family.checkout;
310
+ const cart = checkout.cart;
311
+ cart.removeRegistration(new RegistrationWithPlatformMember_js_1.RegistrationWithPlatformMember({
312
+ registration: registrationB,
313
+ member: memberB,
314
+ }));
315
+ cart.calculatePrices();
316
+ expect(cart.bundleDiscounts).toHaveLength(0);
317
+ expect(checkout.totalPrice).toEqual(-3000);
318
+ expect(checkout.priceBreakown).toEqual([
319
+ expect.objectContaining({
320
+ price: -4000 + 1000,
321
+ }),
322
+ expect.objectContaining({
323
+ price: -4000 + 1000,
324
+ }),
325
+ ]);
326
+ // Now readd the registration
327
+ cart.unremoveRegistration(new RegistrationWithPlatformMember_js_1.RegistrationWithPlatformMember({
328
+ registration: registrationB,
329
+ member: memberB,
330
+ }));
331
+ // Remove registration A, this should show the discount because it is a side effect of deleting the registration
332
+ cart.removeRegistration(new RegistrationWithPlatformMember_js_1.RegistrationWithPlatformMember({
333
+ registration: registrationA,
334
+ member: memberA,
335
+ }));
336
+ cart.calculatePrices();
337
+ expect(cart.bundleDiscounts).toHaveLength(1);
338
+ expect(cart.bundleDiscounts[0].total).toEqual(0);
339
+ expect(cart.bundleDiscounts[0].totalAlreadyApplied).toEqual(1000); // The previous discount
340
+ expect(cart.bundleDiscounts[0].netTotal).toEqual(-1000);
341
+ expect(checkout.totalPrice).toEqual(-9000);
342
+ expect(checkout.priceBreakown).toEqual([
343
+ expect.objectContaining({
344
+ price: -10000, // Reeds aangerekend
345
+ }),
346
+ {
347
+ name: '766a39be-a4af-4a04-baf0-1f064d2fed16 (Multiple family members discount)',
348
+ price: 1000,
349
+ },
350
+ expect.objectContaining({
351
+ price: -9000,
352
+ }),
353
+ ]);
354
+ });
355
+ test('Deleting a registration with cancellation fees', () => {
356
+ // We have two family members with existing registrations.
357
+ // One of those registrations received a discount earlier.
358
+ // But since we are adding a new registration in our cart, a more optimal discount
359
+ // would be if that registration would get the newly higher discount instead
360
+ // (because it is percentage based)
361
+ const { organization, groups, family, discount } = setupDiscountTest({
362
+ memberCount: 2,
363
+ bundleDiscount: createBundleDiscount({
364
+ name: 'Multiple family members discount',
365
+ countWholeFamily: true,
366
+ countPerGroup: false,
367
+ discounts: [
368
+ { value: 1000, type: GroupPriceDiscount_js_1.GroupPriceDiscountType.Percentage },
369
+ { value: 1500, type: GroupPriceDiscount_js_1.GroupPriceDiscountType.Percentage },
370
+ ],
371
+ }),
372
+ groupPrices: [10000, 4000],
373
+ });
374
+ const [groupA, groupB] = groups;
375
+ const [memberA, memberB] = family.members;
376
+ const registrationA = addHistoricRegistration(memberA, groupA, organization);
377
+ const registrationB = addHistoricRegistration(memberB, groupB, organization, new Map([
378
+ [
379
+ discount.id,
380
+ AppliedRegistrationDiscount_js_1.AppliedRegistrationDiscount.create({
381
+ name: discount.name,
382
+ amount: 1000, // 10% discount on 100_00 = 10_00
383
+ }),
384
+ ],
385
+ ]));
386
+ // If we delete registration B, the discount won't be visible in the cart because it is included in the registration
387
+ const checkout = family.checkout;
388
+ checkout.cancellationFeePercentage = 5000;
389
+ const cart = checkout.cart;
390
+ cart.removeRegistration(new RegistrationWithPlatformMember_js_1.RegistrationWithPlatformMember({
391
+ registration: registrationB,
392
+ member: memberB,
393
+ }));
394
+ cart.calculatePrices();
395
+ expect(cart.bundleDiscounts).toHaveLength(0);
396
+ expect(checkout.totalPrice).toEqual(-1500);
397
+ expect(checkout.priceBreakown).toEqual([
398
+ expect.objectContaining({
399
+ price: -4000 + 1000, // Already charged amount
400
+ }),
401
+ // Cancellation fee = 50% of the balance total, 30_00 = 15_00
402
+ expect.objectContaining({
403
+ price: 1500,
404
+ }),
405
+ expect.objectContaining({
406
+ price: -1500,
407
+ }),
408
+ ]);
409
+ // Now readd the registration
410
+ cart.unremoveRegistration(new RegistrationWithPlatformMember_js_1.RegistrationWithPlatformMember({
411
+ registration: registrationB,
412
+ member: memberB,
413
+ }));
414
+ // Remove registration A, this should show the discount because it is a side effect of deleting the registration
415
+ cart.removeRegistration(new RegistrationWithPlatformMember_js_1.RegistrationWithPlatformMember({
416
+ registration: registrationA,
417
+ member: memberA,
418
+ }));
419
+ cart.calculatePrices();
420
+ expect(cart.bundleDiscounts).toHaveLength(1);
421
+ expect(cart.bundleDiscounts[0].total).toEqual(0);
422
+ expect(cart.bundleDiscounts[0].totalAlreadyApplied).toEqual(1000); // The previous discount
423
+ expect(cart.bundleDiscounts[0].netTotal).toEqual(-1000);
424
+ expect(checkout.totalPrice).toEqual(-4000);
425
+ expect(checkout.priceBreakown).toEqual([
426
+ expect.objectContaining({
427
+ price: -10000, // Already charged for registration A that is being deleted
428
+ }),
429
+ // Cancellation fee = 50% of the balance total, 100_00 = 50_00
430
+ expect.objectContaining({
431
+ price: 5000,
432
+ }),
433
+ {
434
+ // Side effect in registration B
435
+ name: '766a39be-a4af-4a04-baf0-1f064d2fed16 (Multiple family members discount)',
436
+ price: 1000,
437
+ },
438
+ expect.objectContaining({
439
+ price: -4000,
440
+ }),
441
+ ]);
442
+ });
443
+ test('Replacing a registration without discount also removes the attached discount in another registration if the new registration in not elegible', () => {
444
+ // Create a family with two members, each with a registration
445
+ // Add a discount to one of the registrations (like it should have been calculated at the time)
446
+ // Next, move one of the registrations to a different group that does not have the discount
447
+ // The previously given discount should be added as an extra price on the cart as an adjustment for the discount that should have been given
448
+ const { organization, groups, family, discount } = setupDiscountTest({
449
+ memberCount: 2,
450
+ bundleDiscount: createBundleDiscount({
451
+ name: 'Multiple family members discount',
452
+ countWholeFamily: true,
453
+ countPerGroup: false,
454
+ discounts: [
455
+ { value: 1000, type: GroupPriceDiscount_js_1.GroupPriceDiscountType.Percentage },
456
+ { value: 1500, type: GroupPriceDiscount_js_1.GroupPriceDiscountType.Percentage },
457
+ ],
458
+ }),
459
+ groupPrices: [10000, 4000],
460
+ });
461
+ const [groupA, groupB] = groups;
462
+ const [memberA, memberB] = family.members;
463
+ const registrationA = addHistoricRegistration(memberA, groupA, organization);
464
+ addHistoricRegistration(memberB, groupB, organization, new Map([
465
+ [
466
+ discount.id,
467
+ AppliedRegistrationDiscount_js_1.AppliedRegistrationDiscount.create({
468
+ name: discount.name,
469
+ amount: 1000, // 10% discount on 100_00 = 10_00
470
+ }),
471
+ ],
472
+ ]));
473
+ const groupCPrice = GroupSettings_js_1.GroupPrice.create({
474
+ price: ReduceablePrice_js_1.ReduceablePrice.create({
475
+ price: 5000,
476
+ }),
477
+ });
478
+ const groupC = Group_js_1.Group.create({
479
+ organizationId: organization.id,
480
+ periodId: organization.period.id,
481
+ settings: GroupSettings_js_1.GroupSettings.create({
482
+ name: new TranslatedString_js_1.TranslatedString('Group C'),
483
+ prices: [
484
+ groupCPrice,
485
+ ],
486
+ }),
487
+ });
488
+ const checkout = family.checkout;
489
+ const cart = checkout.cart;
490
+ const itemA = RegisterItem_js_1.RegisterItem.defaultFor(memberA, groupC, organization);
491
+ itemA.replaceRegistrations = [
492
+ new RegistrationWithPlatformMember_js_1.RegistrationWithPlatformMember({
493
+ registration: registrationA,
494
+ member: memberA,
495
+ }),
496
+ ];
497
+ cart.add(itemA);
498
+ cart.calculatePrices();
499
+ // Check price for items
500
+ expect(itemA.calculatedPrice).toEqual(5000);
501
+ expect(itemA.calculatedRefund).toEqual(10000);
502
+ expect(itemA.calculatedPriceDueLater).toEqual(0);
503
+ // Check discount should be given back
504
+ expect(cart.bundleDiscounts).toHaveLength(1);
505
+ expect(cart.bundleDiscounts[0].total).toEqual(0);
506
+ expect(cart.bundleDiscounts[0].totalAlreadyApplied).toEqual(1000); // The previous discount
507
+ expect(cart.bundleDiscounts[0].netTotal).toEqual(-1000);
508
+ expect(checkout.totalPrice).toEqual(5000 - 10000 + 1000);
509
+ expect(checkout.priceBreakown).toEqual([
510
+ expect.objectContaining({
511
+ // Subtotal (normal price)
512
+ price: 5000,
513
+ }),
514
+ expect.objectContaining({
515
+ // Refund for the previous registration
516
+ price: -10000,
517
+ }),
518
+ {
519
+ name: '766a39be-a4af-4a04-baf0-1f064d2fed16 (Multiple family members discount)',
520
+ price: 1000,
521
+ },
522
+ expect.objectContaining({
523
+ price: -5000 + 1000,
524
+ }),
525
+ ]);
526
+ // Now, what if groupC does have a higher discount instead of no discount?
527
+ groupCPrice.bundleDiscounts.set(discount.id, BundleDiscountGroupPriceSettings_js_1.BundleDiscountGroupPriceSettings.create({
528
+ name: discount.name,
529
+ customDiscounts: [
530
+ // Custom discount for this price
531
+ GroupPriceDiscount_js_1.GroupPriceDiscount.create({
532
+ value: ReduceablePrice_js_1.ReduceablePrice.create({
533
+ price: 2500,
534
+ }),
535
+ type: GroupPriceDiscount_js_1.GroupPriceDiscountType.Fixed,
536
+ }),
537
+ ],
538
+ }));
539
+ // Recalculate
540
+ cart.calculatePrices();
541
+ // Check discount should be given back
542
+ expect(cart.bundleDiscounts).toHaveLength(1);
543
+ expect(cart.bundleDiscounts[0].total).toEqual(2500);
544
+ expect(cart.bundleDiscounts[0].totalAlreadyApplied).toEqual(1000); // The previous discount
545
+ expect(cart.bundleDiscounts[0].netTotal).toEqual(1500);
546
+ expect(checkout.totalPrice).toEqual(5000 - 10000 - 1500);
547
+ expect(checkout.priceBreakown).toEqual([
548
+ expect.objectContaining({
549
+ // Subtotal (normal price)
550
+ price: 5000,
551
+ }),
552
+ expect.objectContaining({
553
+ // Refund for the previous registration
554
+ price: -10000,
555
+ }),
556
+ {
557
+ name: 'Multiple family members discount',
558
+ price: -1500,
559
+ },
560
+ expect.objectContaining({
561
+ price: -5000 - 1500,
562
+ }),
563
+ ]);
564
+ });
565
+ test('Replacing a registration with discount also removes the attached discount registration if the new registration in not elegible', () => {
566
+ const { organization, groups, family, discount } = setupDiscountTest({
567
+ memberCount: 2,
568
+ bundleDiscount: createBundleDiscount({
569
+ name: 'Multiple family members discount',
570
+ countWholeFamily: true,
571
+ countPerGroup: false,
572
+ discounts: [
573
+ { value: 1000, type: GroupPriceDiscount_js_1.GroupPriceDiscountType.Percentage },
574
+ { value: 1500, type: GroupPriceDiscount_js_1.GroupPriceDiscountType.Percentage },
575
+ ],
576
+ }),
577
+ groupPrices: [10000, 4000],
578
+ });
579
+ const [groupA, groupB] = groups;
580
+ const [memberA, memberB] = family.members;
581
+ const registrationA = addHistoricRegistration(memberA, groupA, organization, new Map([
582
+ [
583
+ discount.id,
584
+ AppliedRegistrationDiscount_js_1.AppliedRegistrationDiscount.create({
585
+ name: discount.name,
586
+ amount: 1000, // 10% discount on 100_00 = 10_00
587
+ }),
588
+ ],
589
+ ]));
590
+ addHistoricRegistration(memberB, groupB, organization);
591
+ const groupCPrice = GroupSettings_js_1.GroupPrice.create({
592
+ price: ReduceablePrice_js_1.ReduceablePrice.create({
593
+ price: 5000,
594
+ }),
595
+ });
596
+ const groupC = Group_js_1.Group.create({
597
+ organizationId: organization.id,
598
+ periodId: organization.period.id,
599
+ settings: GroupSettings_js_1.GroupSettings.create({
600
+ name: new TranslatedString_js_1.TranslatedString('Group C'),
601
+ prices: [
602
+ groupCPrice,
603
+ ],
604
+ }),
605
+ });
606
+ const checkout = family.checkout;
607
+ const cart = checkout.cart;
608
+ const itemA = RegisterItem_js_1.RegisterItem.defaultFor(memberA, groupC, organization);
609
+ itemA.replaceRegistrations = [
610
+ new RegistrationWithPlatformMember_js_1.RegistrationWithPlatformMember({
611
+ registration: registrationA,
612
+ member: memberA,
613
+ }),
614
+ ];
615
+ cart.add(itemA);
616
+ cart.calculatePrices();
617
+ // Check price for items
618
+ expect(itemA.calculatedPrice).toEqual(5000);
619
+ expect(itemA.calculatedRefund).toEqual(9000);
620
+ expect(itemA.calculatedPriceDueLater).toEqual(0);
621
+ // No discounts anymore
622
+ expect(cart.bundleDiscounts).toHaveLength(0);
623
+ expect(checkout.totalPrice).toEqual(5000 - 9000);
624
+ expect(checkout.priceBreakown).toEqual([
625
+ expect.objectContaining({
626
+ // Subtotal (normal price)
627
+ price: 5000,
628
+ }),
629
+ expect.objectContaining({
630
+ // Refund for the previous registration, without the discount that was granted
631
+ price: -9000,
632
+ }),
633
+ expect.objectContaining({
634
+ price: 5000 - 9000,
635
+ }),
636
+ ]);
637
+ // Now, what if groupC does have a higher discount instead of no discount?
638
+ groupCPrice.bundleDiscounts.set(discount.id, BundleDiscountGroupPriceSettings_js_1.BundleDiscountGroupPriceSettings.create({
639
+ name: discount.name,
640
+ customDiscounts: [
641
+ // Custom discount for this price
642
+ GroupPriceDiscount_js_1.GroupPriceDiscount.create({
643
+ value: ReduceablePrice_js_1.ReduceablePrice.create({
644
+ price: 2500,
645
+ }),
646
+ type: GroupPriceDiscount_js_1.GroupPriceDiscountType.Fixed,
647
+ }),
648
+ ],
649
+ }));
650
+ // Recalculate
651
+ cart.calculatePrices();
652
+ // Check discount should be given back
653
+ expect(cart.bundleDiscounts).toHaveLength(1);
654
+ expect(cart.bundleDiscounts[0].total).toEqual(2500);
655
+ expect(cart.bundleDiscounts[0].totalAlreadyApplied).toEqual(0); // Not included here, because the registration will be removed including the applied discount
656
+ expect(cart.bundleDiscounts[0].netTotal).toEqual(2500);
657
+ expect(checkout.totalPrice).toEqual(5000 - 9000 - 2500);
658
+ expect(checkout.priceBreakown).toEqual([
659
+ expect.objectContaining({
660
+ // Subtotal (normal price)
661
+ price: 5000,
662
+ }),
663
+ expect.objectContaining({
664
+ // Refund for the previous registration
665
+ price: -9000,
666
+ }),
667
+ {
668
+ name: 'Multiple family members discount',
669
+ price: -2500,
670
+ },
671
+ expect.objectContaining({
672
+ price: 5000 - 9000 - 2500,
673
+ }),
674
+ ]);
675
+ });
676
+ test('The last discount is repeated', () => {
677
+ // Create a family with 3 members. Add 3 items to the cart.
678
+ // The bundle discount only defined one discount, but that should be repated for the second and third item
679
+ const { organization, groups, family, discount } = setupDiscountTest({
680
+ memberCount: 3,
681
+ groupPrices: [5000, 4000],
682
+ groupNames: ['Group A', 'Group B'],
683
+ bundleDiscount: createBundleDiscount({
684
+ name: 'Bundle discount',
685
+ discounts: [
686
+ { value: 1000, type: GroupPriceDiscount_js_1.GroupPriceDiscountType.Percentage },
687
+ ],
688
+ }),
689
+ });
690
+ const [groupA, groupB] = groups;
691
+ const [memberA, memberB, memberC] = family.members;
692
+ const itemA = RegisterItem_js_1.RegisterItem.defaultFor(memberA, groupA, organization);
693
+ const itemB = RegisterItem_js_1.RegisterItem.defaultFor(memberB, groupB, organization);
694
+ const itemC = RegisterItem_js_1.RegisterItem.defaultFor(memberC, groupB, organization);
695
+ // Add to a single cart
696
+ const checkout = family.checkout;
697
+ const cart = checkout.cart;
698
+ cart.add(itemA);
699
+ cart.add(itemB);
700
+ cart.add(itemC);
701
+ cart.calculatePrices();
702
+ // Check price for items
703
+ expect(itemA.calculatedPrice).toEqual(5000);
704
+ expect(itemA.calculatedRefund).toEqual(0);
705
+ expect(itemA.calculatedPriceDueLater).toEqual(0);
706
+ expect(itemB.calculatedPrice).toEqual(4000);
707
+ expect(itemB.calculatedRefund).toEqual(0);
708
+ expect(itemB.calculatedPriceDueLater).toEqual(0);
709
+ expect(itemC.calculatedPrice).toEqual(4000);
710
+ expect(itemC.calculatedRefund).toEqual(0);
711
+ expect(itemC.calculatedPriceDueLater).toEqual(0);
712
+ // Check calculated discount is 50_00 * 0.10 + 40_00 * 0.10 = 5_00 + 4_00 = 9_00
713
+ expect(cart.bundleDiscounts).toHaveLength(1);
714
+ expect(cart.bundleDiscounts[0].total).toEqual(900);
715
+ expect(cart.bundleDiscounts[0].totalAlreadyApplied).toEqual(0);
716
+ expect(cart.bundleDiscounts[0].netTotal).toEqual(900);
717
+ expect(cart.price).toEqual(5000 + 4000 + 4000);
718
+ expect(checkout.totalPrice).toEqual(5000 + 4000 + 4000 - 900);
719
+ expect(checkout.priceBreakown).toEqual([
720
+ expect.objectContaining({
721
+ price: 13000,
722
+ }),
723
+ {
724
+ name: 'Bundle discount',
725
+ price: -900,
726
+ },
727
+ expect.objectContaining({
728
+ price: 13000 - 900,
729
+ }),
730
+ ]);
731
+ });
732
+ test('The highest possible discount is granted', () => {
733
+ const { organization, groups, family, discount } = setupDiscountTest({
734
+ memberCount: 3,
735
+ groupPrices: [5000, 4000],
736
+ groupNames: ['Group A', 'Group B'],
737
+ bundleDiscount: createBundleDiscount({
738
+ name: 'Bundle discount',
739
+ discounts: [
740
+ { value: 1000, type: GroupPriceDiscount_js_1.GroupPriceDiscountType.Percentage },
741
+ ],
742
+ }),
743
+ });
744
+ const [groupA, groupB] = groups;
745
+ const [memberA, memberB, memberC] = family.members;
746
+ const itemA = RegisterItem_js_1.RegisterItem.defaultFor(memberA, groupA, organization);
747
+ const itemB = RegisterItem_js_1.RegisterItem.defaultFor(memberB, groupB, organization);
748
+ const itemC = RegisterItem_js_1.RegisterItem.defaultFor(memberC, groupB, organization);
749
+ // Change group B to give 20 euro for the second item, and 0 for the third item
750
+ groupB.settings.prices[0].bundleDiscounts.set(discount.id, BundleDiscountGroupPriceSettings_js_1.BundleDiscountGroupPriceSettings.create({
751
+ name: discount.name,
752
+ customDiscounts: [
753
+ GroupPriceDiscount_js_1.GroupPriceDiscount.create({
754
+ value: ReduceablePrice_js_1.ReduceablePrice.create({
755
+ price: 2000,
756
+ }),
757
+ type: GroupPriceDiscount_js_1.GroupPriceDiscountType.Fixed,
758
+ }),
759
+ GroupPriceDiscount_js_1.GroupPriceDiscount.create({
760
+ value: ReduceablePrice_js_1.ReduceablePrice.create({
761
+ price: 0,
762
+ }),
763
+ type: GroupPriceDiscount_js_1.GroupPriceDiscountType.Fixed,
764
+ }),
765
+ ],
766
+ }));
767
+ // Add to a single cart
768
+ const checkout = family.checkout;
769
+ const cart = checkout.cart;
770
+ cart.add(itemA);
771
+ cart.add(itemB);
772
+ cart.add(itemC);
773
+ cart.calculatePrices();
774
+ // Check price for items
775
+ expect(itemA.calculatedPrice).toEqual(5000);
776
+ expect(itemA.calculatedRefund).toEqual(0);
777
+ expect(itemA.calculatedPriceDueLater).toEqual(0);
778
+ expect(itemB.calculatedPrice).toEqual(4000);
779
+ expect(itemB.calculatedRefund).toEqual(0);
780
+ expect(itemB.calculatedPriceDueLater).toEqual(0);
781
+ expect(itemC.calculatedPrice).toEqual(4000);
782
+ expect(itemC.calculatedRefund).toEqual(0);
783
+ expect(itemC.calculatedPriceDueLater).toEqual(0);
784
+ // Check calculated discount is 50_00 * 0.10 + 20_00 fixed + 0 = 5_00 + 20_00 + 0 = 25_00
785
+ expect(cart.bundleDiscounts).toHaveLength(1);
786
+ expect(cart.bundleDiscounts[0].total).toEqual(2500);
787
+ expect(cart.bundleDiscounts[0].totalAlreadyApplied).toEqual(0);
788
+ expect(cart.bundleDiscounts[0].netTotal).toEqual(2500);
789
+ expect(cart.price).toEqual(5000 + 4000 + 4000);
790
+ expect(checkout.totalPrice).toEqual(5000 + 4000 + 4000 - 2500);
791
+ expect(checkout.priceBreakown).toEqual([
792
+ expect.objectContaining({
793
+ price: 13000,
794
+ }),
795
+ {
796
+ name: 'Bundle discount',
797
+ price: -2500,
798
+ },
799
+ expect.objectContaining({
800
+ price: 13000 - 2500,
801
+ }),
802
+ ]);
803
+ });
804
+ test('Multiple bundles can be combined', () => {
805
+ // Situation: Camp A costs 100 euro
806
+ // Camp B costs 150 euro
807
+ // Camp A and/or campB gives a discount of 10, 20, 30... (all camp discount)
808
+ // On top of that there is an extra discount if you combine multiple expensive camps (camp B) of 5 euro, 10 ...
809
+ const { organization, groups, family, organizationRegistrationPeriod } = setupDiscountTest({
810
+ memberCount: 5,
811
+ groupPrices: [10000, 15000],
812
+ groupNames: ['Camp A', 'Camp B'],
813
+ bundleDiscount: createBundleDiscount({
814
+ name: 'All camp discount',
815
+ discounts: [
816
+ { value: 1000, type: GroupPriceDiscount_js_1.GroupPriceDiscountType.Fixed },
817
+ ],
818
+ }),
819
+ });
820
+ const [campA, campB] = groups;
821
+ const [memberA, memberB, memberC, memberD, memberE] = family.members;
822
+ const expensiveCampDiscount = createBundleDiscount({
823
+ name: 'Expensive camp discount',
824
+ discounts: [
825
+ { value: 500, type: GroupPriceDiscount_js_1.GroupPriceDiscountType.Fixed },
826
+ ],
827
+ });
828
+ // Add discount to organizationRegistrationPeriod
829
+ organizationRegistrationPeriod.settings.bundleDiscounts.push(expensiveCampDiscount);
830
+ // Add camp B to the expensive camp discount
831
+ campB.settings.prices[0].bundleDiscounts.set(expensiveCampDiscount.id, BundleDiscountGroupPriceSettings_js_1.BundleDiscountGroupPriceSettings.create({
832
+ name: expensiveCampDiscount.name,
833
+ }));
834
+ // Add to a single cart
835
+ const checkout = family.checkout;
836
+ const cart = checkout.cart;
837
+ const itemA = RegisterItem_js_1.RegisterItem.defaultFor(memberA, campA, organization);
838
+ const itemB = RegisterItem_js_1.RegisterItem.defaultFor(memberB, campB, organization); // +10_00 discount
839
+ const itemC = RegisterItem_js_1.RegisterItem.defaultFor(memberC, campB, organization); // +10_00 discount and +5_00 discount
840
+ const itemD = RegisterItem_js_1.RegisterItem.defaultFor(memberD, campA, organization); // +10_00 discount
841
+ const itemE = RegisterItem_js_1.RegisterItem.defaultFor(memberE, campB, organization); // +10_00 discount and +5_00 discount
842
+ cart.add(itemA);
843
+ cart.add(itemB);
844
+ cart.add(itemC);
845
+ cart.add(itemD);
846
+ cart.add(itemE);
847
+ cart.calculatePrices();
848
+ expect(cart.bundleDiscounts).toHaveLength(2);
849
+ expect(cart.bundleDiscounts.map((d) => {
850
+ return {
851
+ name: d.name,
852
+ total: d.total,
853
+ netTotal: d.netTotal,
854
+ };
855
+ })).toEqual([
856
+ {
857
+ name: 'All camp discount',
858
+ total: 1000 * 4,
859
+ netTotal: 1000 * 4,
860
+ },
861
+ {
862
+ name: 'Expensive camp discount',
863
+ total: 500 * 2,
864
+ netTotal: 500 * 2,
865
+ },
866
+ ]);
867
+ expect(checkout.priceBreakown).toEqual([
868
+ expect.objectContaining({
869
+ price: 10000 * 2 + 15000 * 3,
870
+ }),
871
+ {
872
+ name: 'All camp discount',
873
+ price: -1000 * 4,
874
+ },
875
+ {
876
+ name: 'Expensive camp discount',
877
+ price: -500 * 2,
878
+ },
879
+ expect.objectContaining({
880
+ price: 10000 * 2 + 15000 * 3 - 1000 * 4 - 500 * 2,
881
+ }),
882
+ ]);
883
+ });
884
+ test('Previous discounts can be altered if a better optimum is found later', () => {
885
+ // We have two family members with existing registrations.
886
+ // One of those registrations received a discount earlier.
887
+ // But since we are adding a new registration in our cart, a more optimal discount
888
+ // would be if that registration would get the newly higher discount instead
889
+ // (because it is percentage based)
890
+ const { organization, groups, family, discount } = setupDiscountTest({
891
+ memberCount: 2,
892
+ bundleDiscount: createBundleDiscount({
893
+ name: 'Multiple family members discount',
894
+ countWholeFamily: true,
895
+ countPerGroup: false,
896
+ discounts: [
897
+ { value: 1000, type: GroupPriceDiscount_js_1.GroupPriceDiscountType.Percentage },
898
+ { value: 1500, type: GroupPriceDiscount_js_1.GroupPriceDiscountType.Percentage },
899
+ ],
900
+ }),
901
+ groupPrices: [10000, 4000],
902
+ });
903
+ const [groupA, groupB] = groups;
904
+ const [memberA, memberB] = family.members;
905
+ const registrationA = addHistoricRegistration(memberA, groupA, organization);
906
+ const registrationB = addHistoricRegistration(memberB, groupB, organization);
907
+ registrationA.discounts.set(discount.id, AppliedRegistrationDiscount_js_1.AppliedRegistrationDiscount.create({
908
+ name: discount.name,
909
+ amount: 1000, // 10% discount on 100_00 = 10_00
910
+ }));
911
+ // Now create itemC
912
+ const itemC = RegisterItem_js_1.RegisterItem.defaultFor(memberA, groupB, organization);
913
+ // Add to a single cart
914
+ const checkout = family.checkout;
915
+ const cart = checkout.cart;
916
+ cart.add(itemC);
917
+ cart.calculatePrices();
918
+ // Check price for items
919
+ expect(itemC.calculatedPrice).toEqual(4000);
920
+ expect(itemC.calculatedRefund).toEqual(0);
921
+ expect(itemC.calculatedPriceDueLater).toEqual(0);
922
+ // Check calculated discount is 100_00 * 0.15 + 40_00 * 0.10 = 15_00 + 4_00 = 19_00
923
+ expect(cart.bundleDiscounts).toHaveLength(1);
924
+ expect(cart.bundleDiscounts[0].total).toEqual(1900);
925
+ expect(cart.bundleDiscounts[0].totalAlreadyApplied).toEqual(1000); // The previous discount
926
+ expect(cart.bundleDiscounts[0].netTotal).toEqual(900);
927
+ expect(cart.price).toEqual(4000);
928
+ expect(checkout.totalPrice).toEqual(4000 - 900);
929
+ expect(checkout.priceBreakown).toEqual([
930
+ expect.objectContaining({
931
+ price: 4000,
932
+ }),
933
+ {
934
+ name: 'Multiple family members discount',
935
+ price: -900,
936
+ },
937
+ expect.objectContaining({
938
+ price: 4000 - 900,
939
+ }),
940
+ ]);
941
+ });
942
+ test('The repeating behaviour can be stopped by setting the last discount to zero', () => {
943
+ // Create a family with 3 members. Add 3 items to the cart.
944
+ // The bundle discount only defined one discount, but that should be repated for the second and third item
945
+ const { organization, groups, family, discount } = setupDiscountTest({
946
+ memberCount: 3,
947
+ groupPrices: [5000, 4000],
948
+ groupNames: ['Group A', 'Group B'],
949
+ bundleDiscount: createBundleDiscount({
950
+ name: 'Bundle discount',
951
+ discounts: [
952
+ { value: 1000, type: GroupPriceDiscount_js_1.GroupPriceDiscountType.Percentage },
953
+ { value: 0, type: GroupPriceDiscount_js_1.GroupPriceDiscountType.Percentage }, // This should stop the repeating behaviour
954
+ ],
955
+ }),
956
+ });
957
+ const [groupA, groupB] = groups;
958
+ const [memberA, memberB, memberC] = family.members;
959
+ const itemA = RegisterItem_js_1.RegisterItem.defaultFor(memberA, groupA, organization);
960
+ const itemB = RegisterItem_js_1.RegisterItem.defaultFor(memberB, groupB, organization);
961
+ const itemC = RegisterItem_js_1.RegisterItem.defaultFor(memberC, groupB, organization);
962
+ // Add to a single cart
963
+ const checkout = family.checkout;
964
+ const cart = checkout.cart;
965
+ cart.add(itemA);
966
+ cart.add(itemB);
967
+ cart.add(itemC);
968
+ cart.calculatePrices();
969
+ // Check price for items
970
+ expect(itemA.calculatedPrice).toEqual(5000);
971
+ expect(itemA.calculatedRefund).toEqual(0);
972
+ expect(itemA.calculatedPriceDueLater).toEqual(0);
973
+ expect(itemB.calculatedPrice).toEqual(4000);
974
+ expect(itemB.calculatedRefund).toEqual(0);
975
+ expect(itemB.calculatedPriceDueLater).toEqual(0);
976
+ expect(itemC.calculatedPrice).toEqual(4000);
977
+ expect(itemC.calculatedRefund).toEqual(0);
978
+ expect(itemC.calculatedPriceDueLater).toEqual(0);
979
+ // Check calculated discount is 50_00 * 0.10 = 5_00
980
+ expect(cart.bundleDiscounts).toHaveLength(1);
981
+ expect(cart.bundleDiscounts[0].total).toEqual(500);
982
+ expect(cart.bundleDiscounts[0].totalAlreadyApplied).toEqual(0);
983
+ expect(cart.bundleDiscounts[0].netTotal).toEqual(500);
984
+ expect(cart.price).toEqual(5000 + 4000 + 4000);
985
+ expect(checkout.totalPrice).toEqual(5000 + 4000 + 4000 - 500);
986
+ expect(checkout.priceBreakown).toEqual([
987
+ expect.objectContaining({
988
+ price: 13000,
989
+ }),
990
+ {
991
+ name: 'Bundle discount',
992
+ price: -500,
993
+ },
994
+ expect.objectContaining({
995
+ price: 13000 - 500,
996
+ }),
997
+ ]);
998
+ });
999
+ test('Gives different discount for members with reduced price', () => {
1000
+ // Create familuy with 3 members.
1001
+ // One member has a reduced price.
1002
+ // The other two members have a normal price.
1003
+ // Create a 50%, 100% discount for the reduced price in a bundle discount (so it should be free in a family with 3 members)
1004
+ const { organization, groups, family } = setupDiscountTest({
1005
+ memberCount: 3,
1006
+ groupPrices: [5000, 4000],
1007
+ groupNames: ['Group A', 'Group B'],
1008
+ bundleDiscount: createBundleDiscount({
1009
+ name: 'Bundle discount',
1010
+ discounts: [
1011
+ { value: 500, type: GroupPriceDiscount_js_1.GroupPriceDiscountType.Percentage, reducedValue: 5000 },
1012
+ { value: 1000, type: GroupPriceDiscount_js_1.GroupPriceDiscountType.Percentage, reducedValue: 10000 },
1013
+ ],
1014
+ }),
1015
+ });
1016
+ const [groupA, groupB] = groups;
1017
+ const [memberA, memberB, memberC] = family.members;
1018
+ // Make sure the last member has a reduced price
1019
+ memberC.member.details.requiresFinancialSupport = MemberDetails_js_1.BooleanStatus.create({ value: true });
1020
+ const itemA = RegisterItem_js_1.RegisterItem.defaultFor(memberA, groupA, organization);
1021
+ const itemB = RegisterItem_js_1.RegisterItem.defaultFor(memberB, groupB, organization);
1022
+ const itemC = RegisterItem_js_1.RegisterItem.defaultFor(memberC, groupB, organization);
1023
+ // Add to a single cart
1024
+ const checkout = family.checkout;
1025
+ const cart = checkout.cart;
1026
+ cart.add(itemA);
1027
+ cart.add(itemB);
1028
+ cart.add(itemC);
1029
+ cart.calculatePrices();
1030
+ // Check price for items
1031
+ expect(itemA.calculatedPrice).toEqual(5000);
1032
+ expect(itemB.calculatedPrice).toEqual(4000);
1033
+ expect(itemC.calculatedPrice).toEqual(4000);
1034
+ // Check calculated discount is 40_00 * 1 + 50_00 * 5% = 40_00 + 2_50 = 42_50
1035
+ expect(cart.bundleDiscounts).toHaveLength(1);
1036
+ expect(cart.bundleDiscounts[0].total).toEqual(4250);
1037
+ expect(cart.bundleDiscounts[0].totalAlreadyApplied).toEqual(0);
1038
+ expect(cart.bundleDiscounts[0].netTotal).toEqual(4250);
1039
+ expect(cart.price).toEqual(5000 + 4000 + 4000);
1040
+ expect(checkout.totalPrice).toEqual(5000 + 4000 + 4000 - 4250);
1041
+ expect(checkout.priceBreakown).toEqual([
1042
+ expect.objectContaining({
1043
+ price: 13000,
1044
+ }),
1045
+ {
1046
+ name: 'Bundle discount',
1047
+ price: -4250,
1048
+ },
1049
+ expect.objectContaining({
1050
+ price: 13000 - 4250,
1051
+ }),
1052
+ ]);
1053
+ // Remove requires financial support, then recalculate
1054
+ memberC.member.details.requiresFinancialSupport = MemberDetails_js_1.BooleanStatus.create({ value: false });
1055
+ cart.calculatePrices();
1056
+ // 10% on 50_00 + 5% on 40_00 = 5_00 + 2_00 = 7_00
1057
+ expect(checkout.priceBreakown).toEqual([
1058
+ expect.objectContaining({
1059
+ price: 13000,
1060
+ }),
1061
+ {
1062
+ name: 'Bundle discount',
1063
+ price: -700,
1064
+ },
1065
+ expect.objectContaining({
1066
+ price: 13000 - 700,
1067
+ }),
1068
+ ]);
1069
+ });
1070
+ test('Bundles without countWholeFamily only count per unique member', () => {
1071
+ // Register member A for both group A and group B
1072
+ // while member B is only registered for group A
1073
+ // Only member A should get the discount for either A or B (if the order matters)
1074
+ const { organization, groups, family } = setupDiscountTest({
1075
+ memberCount: 2,
1076
+ bundleDiscount: createBundleDiscount({
1077
+ name: 'Multiple lessons discount',
1078
+ countWholeFamily: false,
1079
+ countPerGroup: false,
1080
+ discounts: [
1081
+ { value: 1000, type: GroupPriceDiscount_js_1.GroupPriceDiscountType.Percentage },
1082
+ ],
1083
+ }),
1084
+ });
1085
+ const [groupA, groupB] = groups;
1086
+ const [memberA, memberB] = family.members;
1087
+ const itemA = RegisterItem_js_1.RegisterItem.defaultFor(memberA, groupA, organization);
1088
+ const itemB = RegisterItem_js_1.RegisterItem.defaultFor(memberA, groupB, organization);
1089
+ const itemC = RegisterItem_js_1.RegisterItem.defaultFor(memberB, groupA, organization);
1090
+ // Add to a single cart
1091
+ const checkout = family.checkout;
1092
+ const cart = checkout.cart;
1093
+ cart.add(itemA);
1094
+ cart.add(itemB);
1095
+ cart.add(itemC);
1096
+ cart.calculatePrices();
1097
+ // Check price for items
1098
+ expect(itemA.calculatedPrice).toEqual(5000);
1099
+ expect(itemB.calculatedPrice).toEqual(4000);
1100
+ expect(itemC.calculatedPrice).toEqual(5000);
1101
+ // Check calculated discount is 50_00 * 0.10 = 5_00
1102
+ expect(cart.bundleDiscounts.map(c => c.netTotal)).toHaveLength(1);
1103
+ expect(cart.bundleDiscounts[0].total).toEqual(500);
1104
+ expect(cart.bundleDiscounts[0].netTotal).toEqual(500);
1105
+ expect(cart.price).toEqual(5000 + 4000 + 5000);
1106
+ expect(checkout.totalPrice).toEqual(5000 + 4000 + 5000 - 500);
1107
+ expect(checkout.priceBreakown).toEqual([
1108
+ expect.objectContaining({
1109
+ price: 5000 + 4000 + 5000,
1110
+ }),
1111
+ {
1112
+ name: 'Multiple lessons discount (John)',
1113
+ price: -500,
1114
+ },
1115
+ expect.objectContaining({
1116
+ price: 5000 + 4000 + 5000 - 500,
1117
+ }),
1118
+ ]);
1119
+ // Add a historic registration for member B, so it also gets a discount
1120
+ addHistoricRegistration(memberB, groupB, organization);
1121
+ cart.calculatePrices();
1122
+ expect(checkout.priceBreakown).toEqual([
1123
+ expect.objectContaining({
1124
+ price: 5000 + 4000 + 5000, // no change
1125
+ }),
1126
+ {
1127
+ name: 'Multiple lessons discount (John)',
1128
+ price: -500,
1129
+ },
1130
+ {
1131
+ name: 'Multiple lessons discount (Jane)',
1132
+ price: -500,
1133
+ },
1134
+ expect.objectContaining({
1135
+ price: 5000 + 4000 + 5000 - 500 - 500,
1136
+ }),
1137
+ ]);
1138
+ });
1139
+ test('Bundles with countPerGroup only count per unique group', () => {
1140
+ const { organization, groups, family, discount } = setupDiscountTest({
1141
+ memberCount: 2,
1142
+ groupNames: ['Group A', 'Group B'],
1143
+ bundleDiscount: createBundleDiscount({
1144
+ name: 'Multiple family members discount',
1145
+ countWholeFamily: true,
1146
+ countPerGroup: true,
1147
+ discounts: [
1148
+ { value: 1000, type: GroupPriceDiscount_js_1.GroupPriceDiscountType.Percentage },
1149
+ ],
1150
+ }),
1151
+ });
1152
+ const [groupA, groupB] = groups;
1153
+ const [memberA, memberB] = family.members;
1154
+ // Both members register for the different groups, does not give the discount
1155
+ const itemA = RegisterItem_js_1.RegisterItem.defaultFor(memberA, groupA, organization);
1156
+ const itemB = RegisterItem_js_1.RegisterItem.defaultFor(memberB, groupB, organization);
1157
+ // Add to a single cart
1158
+ const checkout = family.checkout;
1159
+ const cart = checkout.cart;
1160
+ cart.add(itemA);
1161
+ cart.add(itemB);
1162
+ cart.calculatePrices();
1163
+ // No discount should be applied yet (need at least 2 different groups)
1164
+ expect(cart.bundleDiscounts).toHaveLength(0);
1165
+ expect(checkout.totalPrice).toEqual(5000 + 4000);
1166
+ // Now register one member for group B as well
1167
+ const itemC = RegisterItem_js_1.RegisterItem.defaultFor(memberA, groupB, organization);
1168
+ cart.add(itemC);
1169
+ cart.calculatePrices();
1170
+ // Now we should get a discount
1171
+ expect(cart.bundleDiscounts).toHaveLength(1);
1172
+ expect(cart.bundleDiscounts[0].total).toEqual(400); // 10% of 40_00 (not 50_00)
1173
+ expect(cart.bundleDiscounts[0].netTotal).toEqual(400);
1174
+ expect(cart.price).toEqual(5000 + 4000 + 4000);
1175
+ expect(checkout.totalPrice).toEqual(5000 + 4000 + 4000 - 400);
1176
+ expect(checkout.priceBreakown).toEqual([
1177
+ expect.objectContaining({
1178
+ price: 5000 + 4000 + 4000,
1179
+ }),
1180
+ {
1181
+ name: 'Multiple family members discount (Group B)',
1182
+ price: -400,
1183
+ },
1184
+ expect.objectContaining({
1185
+ price: 5000 + 4000 + 4000 - 400,
1186
+ }),
1187
+ ]);
1188
+ // Change calculation to count across groups
1189
+ discount.countPerGroup = false;
1190
+ cart.calculatePrices();
1191
+ // Now we should get a discount for both groups
1192
+ expect(checkout.priceBreakown).toEqual([
1193
+ expect.objectContaining({
1194
+ price: 5000 + 4000 + 4000,
1195
+ }),
1196
+ {
1197
+ name: 'Multiple family members discount',
1198
+ price: -900,
1199
+ },
1200
+ expect.objectContaining({
1201
+ price: 5000 + 4000 + 4000 - 900,
1202
+ }),
1203
+ ]);
1204
+ });
1205
+ test('countPerGroup = true is ignored if countWholeFamily is false', () => {
1206
+ const { organization, groups, family, discount } = setupDiscountTest({
1207
+ memberCount: 2,
1208
+ groupNames: ['Group A', 'Group B'],
1209
+ bundleDiscount: createBundleDiscount({
1210
+ name: 'Multiple lessons discount',
1211
+ countWholeFamily: false, // This means only count per unique member
1212
+ countPerGroup: true, // This should be ignored since countWholeFamily is false
1213
+ discounts: [
1214
+ { value: 1000, type: GroupPriceDiscount_js_1.GroupPriceDiscountType.Percentage },
1215
+ ],
1216
+ }),
1217
+ });
1218
+ const [groupA, groupB] = groups;
1219
+ const [memberA, memberB] = family.members;
1220
+ // Register member A for both group A and B
1221
+ // Register member B for group A
1222
+ const itemA = RegisterItem_js_1.RegisterItem.defaultFor(memberA, groupA, organization);
1223
+ const itemB = RegisterItem_js_1.RegisterItem.defaultFor(memberA, groupB, organization);
1224
+ const itemC = RegisterItem_js_1.RegisterItem.defaultFor(memberB, groupA, organization);
1225
+ // Add to a single cart
1226
+ const checkout = family.checkout;
1227
+ const cart = checkout.cart;
1228
+ cart.add(itemA);
1229
+ cart.add(itemB);
1230
+ cart.add(itemC);
1231
+ cart.calculatePrices();
1232
+ // Check price for items
1233
+ expect(itemA.calculatedPrice).toEqual(5000);
1234
+ expect(itemB.calculatedPrice).toEqual(4000);
1235
+ expect(itemC.calculatedPrice).toEqual(5000);
1236
+ // Check calculated discount - should only apply to member A (who has multiple registrations)
1237
+ expect(cart.bundleDiscounts).toHaveLength(1);
1238
+ expect(cart.bundleDiscounts[0].total).toEqual(500); // 10% of 50_00 (we always apply discounts to highest price first)
1239
+ expect(cart.bundleDiscounts[0].netTotal).toEqual(500);
1240
+ expect(cart.price).toEqual(5000 + 4000 + 5000);
1241
+ expect(checkout.totalPrice).toEqual(5000 + 4000 + 5000 - 500);
1242
+ expect(checkout.priceBreakown).toEqual([
1243
+ expect.objectContaining({
1244
+ price: 5000 + 4000 + 5000,
1245
+ }),
1246
+ {
1247
+ name: 'Multiple lessons discount (John)',
1248
+ price: -500,
1249
+ },
1250
+ expect.objectContaining({
1251
+ price: 5000 + 4000 + 5000 - 500,
1252
+ }),
1253
+ ]);
1254
+ // Now explicitly turn on countPerGroup and set countWholeFamily to true
1255
+ // to verify it behaves differently (proving that countPerGroup was indeed ignored before)
1256
+ discount.countWholeFamily = true;
1257
+ discount.countPerGroup = true;
1258
+ cart.calculatePrices();
1259
+ // Now we should get a discount for group A (since two members are registered for it)
1260
+ expect(checkout.priceBreakown).toEqual([
1261
+ expect.objectContaining({
1262
+ price: 5000 + 4000 + 5000,
1263
+ }),
1264
+ {
1265
+ name: 'Multiple lessons discount (Group A)',
1266
+ price: -500, // 10% of the second 50_00
1267
+ },
1268
+ expect.objectContaining({
1269
+ price: 5000 + 4000 + 5000 - 500,
1270
+ }),
1271
+ ]);
1272
+ });
1273
+ test('Past discounts are only altered if a related registration is added or removed', () => {
1274
+ // Create two historic registrations that didn't get an existing discount (= error)
1275
+ // now, add a new registration to the cart that is not applicable to the discount - the old discount should not be altered
1276
+ const { organization, groups, family, period, discount } = setupDiscountTest({
1277
+ memberCount: 2,
1278
+ groupPrices: [5000, 4000],
1279
+ groupNames: ['Group A', 'Group B'],
1280
+ bundleDiscount: createBundleDiscount({
1281
+ name: 'Multiple family members discount',
1282
+ countWholeFamily: true,
1283
+ countPerGroup: false,
1284
+ discounts: [
1285
+ { value: 1000, type: GroupPriceDiscount_js_1.GroupPriceDiscountType.Percentage },
1286
+ ],
1287
+ }),
1288
+ });
1289
+ const [groupA, groupB] = groups;
1290
+ const [memberA, memberB] = family.members;
1291
+ const registrationA = addHistoricRegistration(memberA, groupA, organization);
1292
+ const registrationB = addHistoricRegistration(memberB, groupB, organization);
1293
+ // Create a new group that is not applicable to the discount
1294
+ const [groupC] = createTestGroups(organization, period, [3000], ['Group C']);
1295
+ const itemC = RegisterItem_js_1.RegisterItem.defaultFor(memberA, groupC, organization);
1296
+ // Add to a single cart
1297
+ const checkout = family.checkout;
1298
+ const cart = checkout.cart;
1299
+ cart.add(itemC);
1300
+ cart.calculatePrices();
1301
+ expect(itemC.calculatedPrice).toEqual(3000);
1302
+ expect(cart.bundleDiscounts).toHaveLength(0);
1303
+ expect(checkout.totalPrice).toEqual(3000);
1304
+ expect(checkout.priceBreakown).toEqual([
1305
+ expect.objectContaining({
1306
+ price: 3000,
1307
+ }),
1308
+ ]);
1309
+ // Now also register memberB for group A and add it to the cart
1310
+ const itemD = RegisterItem_js_1.RegisterItem.defaultFor(memberB, groupA, organization);
1311
+ cart.add(itemD);
1312
+ cart.calculatePrices();
1313
+ // Check price for items
1314
+ expect(itemD.calculatedPrice).toEqual(5000);
1315
+ expect(itemC.calculatedPrice).toEqual(3000);
1316
+ // Check calculated discount: should also take old registrations into account
1317
+ // 50_00 * 10% + 50_00 * 10% = 5_00 + 5_00 = 10_00
1318
+ expect(cart.bundleDiscounts).toHaveLength(1);
1319
+ expect(cart.bundleDiscounts[0].total).toEqual(1000);
1320
+ expect(cart.bundleDiscounts[0].totalAlreadyApplied).toEqual(0);
1321
+ expect(cart.bundleDiscounts[0].netTotal).toEqual(1000);
1322
+ expect(cart.price).toEqual(5000 + 3000);
1323
+ expect(checkout.totalPrice).toEqual(5000 + 3000 - 1000);
1324
+ expect(checkout.priceBreakown).toEqual([
1325
+ expect.objectContaining({
1326
+ price: 5000 + 3000,
1327
+ }),
1328
+ {
1329
+ name: 'Multiple family members discount',
1330
+ price: -1000,
1331
+ },
1332
+ expect.objectContaining({
1333
+ price: 5000 + 3000 - 1000,
1334
+ }),
1335
+ ]);
1336
+ });
1337
+ test('Fixed discounts are limited to the group price', () => {
1338
+ const { organization, groups, family, discount } = setupDiscountTest({
1339
+ memberCount: 2,
1340
+ groupNames: ['Group A', 'Group B'],
1341
+ bundleDiscount: createBundleDiscount({
1342
+ name: 'Multiple family members discount',
1343
+ countWholeFamily: true,
1344
+ countPerGroup: false,
1345
+ discounts: [
1346
+ { value: 100000, type: GroupPriceDiscount_js_1.GroupPriceDiscountType.Fixed },
1347
+ ],
1348
+ }),
1349
+ });
1350
+ const [groupA, groupB] = groups;
1351
+ const [memberA, memberB] = family.members;
1352
+ const itemA = RegisterItem_js_1.RegisterItem.defaultFor(memberA, groupA, organization);
1353
+ const itemB = RegisterItem_js_1.RegisterItem.defaultFor(memberB, groupB, organization);
1354
+ // Add to a single cart
1355
+ const checkout = family.checkout;
1356
+ const cart = checkout.cart;
1357
+ cart.add(itemA);
1358
+ cart.add(itemB);
1359
+ cart.calculatePrices();
1360
+ expect(cart.bundleDiscounts).toHaveLength(1);
1361
+ expect(cart.bundleDiscounts[0].total).toEqual(5000);
1362
+ expect(cart.bundleDiscounts[0].totalAlreadyApplied).toEqual(0);
1363
+ expect(cart.bundleDiscounts[0].netTotal).toEqual(5000);
1364
+ expect(cart.price).toEqual(5000 + 4000);
1365
+ expect(checkout.totalPrice).toEqual(4000);
1366
+ expect(checkout.priceBreakown).toEqual([
1367
+ expect.objectContaining({
1368
+ price: 5000 + 4000,
1369
+ }),
1370
+ {
1371
+ name: 'Multiple family members discount',
1372
+ price: -5000,
1373
+ },
1374
+ expect.objectContaining({
1375
+ price: 4000,
1376
+ }),
1377
+ ]);
1378
+ });
1379
+ test.todo('Can handle bundle discounts for a future period');
1380
+ test.todo('Families should be calculated correctly for admins');
1381
+ test.todo('The price per item can never be negative');
1382
+ });
1383
+ });
1384
+ //# sourceMappingURL=RegisterCart.test.js.map