@stamhoofd/backend 2.120.6 → 2.122.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 (434) hide show
  1. package/output-2.txt +1 -0
  2. package/output.txt +1 -0
  3. package/package.json +70 -53
  4. package/src/audit-logs/EventNotificationLogger.ts +40 -0
  5. package/src/audit-logs/ModelLogger.ts +19 -15
  6. package/src/audit-logs/OrganizationLogger.ts +1 -1
  7. package/src/audit-logs/RegistrationInvitationLogger.ts +46 -0
  8. package/src/audit-logs/init.ts +9 -1
  9. package/src/boot.ts +19 -18
  10. package/src/crons/amazon-ses.ts +13 -26
  11. package/src/crons/balance-emails.ts +3 -5
  12. package/src/crons/clearExcelCache.test.ts +1 -1
  13. package/src/crons/clearExcelCache.ts +1 -2
  14. package/src/crons/drip-emails.ts +42 -0
  15. package/src/crons/endFunctionsOfUsersWithoutRegistration.ts +4 -0
  16. package/src/crons/helpers/isOutside.ts +11 -0
  17. package/src/crons/helpers/useSavedIterator.ts +32 -0
  18. package/src/crons/index.ts +7 -0
  19. package/src/crons/invoices.ts +160 -0
  20. package/src/crons/members-fees.ts +50 -0
  21. package/src/crons/mollie-chargebacks.ts +86 -0
  22. package/src/crons/service-fees.ts +152 -0
  23. package/src/crons/stripe-invoices.ts +48 -0
  24. package/src/crons/transfer-fees.ts +176 -0
  25. package/src/crons/update-cached-balances.ts +4 -4
  26. package/src/crons.ts +54 -74
  27. package/src/debug.ts +4 -2
  28. package/src/email-recipient-loaders/documents.ts +1 -1
  29. package/src/email-recipient-loaders/members.ts +1 -1
  30. package/src/email-recipient-loaders/payments.ts +87 -46
  31. package/src/email-recipient-loaders/registrations.ts +1 -1
  32. package/src/email-replacements/getEmailReplacementsForPayment.ts +5 -9
  33. package/src/endpoints/admin/members/ChargeMembersEndpoint.ts +1 -1
  34. package/src/endpoints/admin/memberships/ChargeMembershipsEndpoint.ts +1 -1
  35. package/src/endpoints/admin/memberships/GetChargeMembershipsSummaryEndpoint.ts +3 -5
  36. package/src/endpoints/admin/organizations/ChargeOrganizationsEndpoint.ts +1 -1
  37. package/src/endpoints/admin/organizations/GetOrganizationsCountEndpoint.ts +1 -1
  38. package/src/endpoints/admin/organizations/GetOrganizationsEndpoint.ts +13 -7
  39. package/src/endpoints/admin/organizations/PatchOrganizationsEndpoint.ts +4 -6
  40. package/src/endpoints/admin/organizations/PatchPackagesEndpoint.test.ts +10 -6
  41. package/src/endpoints/admin/organizations/PatchPackagesEndpoint.ts +39 -21
  42. package/src/endpoints/admin/registrations/ChargeRegistrationsEndpoint.ts +1 -1
  43. package/src/endpoints/auth/CreateAdminEndpoint.ts +23 -5
  44. package/src/endpoints/auth/CreateTokenEndpoint.ts +2 -2
  45. package/src/endpoints/auth/DeleteTokenEndpoint.ts +1 -1
  46. package/src/endpoints/auth/DeleteUserEndpoint.ts +3 -3
  47. package/src/endpoints/auth/ForgotPasswordEndpoint.ts +1 -1
  48. package/src/endpoints/auth/GetOtherUserEndpoint.ts +1 -1
  49. package/src/endpoints/auth/GetUserEndpoint.ts +1 -1
  50. package/src/endpoints/auth/OpenIDConnectAuthTokenEndpoint.ts +1 -1
  51. package/src/endpoints/auth/OpenIDConnectStartEndpoint.ts +1 -1
  52. package/src/endpoints/auth/PatchUserEndpoint.ts +5 -8
  53. package/src/endpoints/auth/PollEmailVerificationEndpoint.ts +1 -1
  54. package/src/endpoints/auth/RetryEmailVerificationEndpoint.ts +1 -1
  55. package/src/endpoints/auth/SignupEndpoint.ts +2 -3
  56. package/src/endpoints/auth/VerifyEmailEndpoint.ts +20 -3
  57. package/src/endpoints/frontend/FrontendEnvironmentEndpoint.ts +2 -1
  58. package/src/endpoints/global/addresses/SearchRegionsEndpoint.ts +2 -4
  59. package/src/endpoints/global/addresses/ValidateAddressEndpoint.ts +1 -1
  60. package/src/endpoints/global/audit-logs/GetAuditLogsEndpoint.ts +5 -7
  61. package/src/endpoints/global/caddy/CheckDomainCertEndpoint.ts +2 -2
  62. package/src/endpoints/global/email/CreateEmailEndpoint.ts +3 -4
  63. package/src/endpoints/global/email/GetAdminEmailsEndpoint.test.ts +1 -1
  64. package/src/endpoints/global/email/GetAdminEmailsEndpoint.ts +1 -2
  65. package/src/endpoints/global/email/GetEmailAddressEndpoint.ts +4 -6
  66. package/src/endpoints/global/email/GetEmailEndpoint.ts +1 -1
  67. package/src/endpoints/global/email/GetUserEmailsEndpoint.test.ts +1 -1
  68. package/src/endpoints/global/email/GetUserEmailsEndpoint.ts +1 -1
  69. package/src/endpoints/global/email/ManageEmailAddressEndpoint.ts +6 -10
  70. package/src/endpoints/global/email/PatchEmailEndpoint.test.ts +4 -7
  71. package/src/endpoints/global/email/PatchEmailEndpoint.ts +6 -9
  72. package/src/endpoints/global/email-recipients/GetEmailRecipientsCountEndpoint.ts +1 -1
  73. package/src/endpoints/global/email-recipients/GetEmailRecipientsEndpoint.test.ts +1 -1
  74. package/src/endpoints/global/email-recipients/GetEmailRecipientsEndpoint.ts +1 -1
  75. package/src/endpoints/global/email-recipients/RetryEmailRecipientEndpoint.ts +2 -2
  76. package/src/endpoints/global/events/GetEventNotificationsCountEndpoint.ts +1 -1
  77. package/src/endpoints/global/events/GetEventNotificationsEndpoint.ts +3 -3
  78. package/src/endpoints/global/events/GetEventsEndpoint.ts +3 -3
  79. package/src/endpoints/global/events/PatchEventNotificationsEndpoint.test.ts +49 -4
  80. package/src/endpoints/global/events/PatchEventNotificationsEndpoint.ts +5 -8
  81. package/src/endpoints/global/events/PatchEventsEndpoint.test.ts +12 -44
  82. package/src/endpoints/global/events/PatchEventsEndpoint.ts +128 -127
  83. package/src/endpoints/global/files/ExportToExcelEndpoint.ts +9 -9
  84. package/src/endpoints/global/files/GetFileCache.ts +3 -4
  85. package/src/endpoints/global/files/UploadFile.ts +3 -4
  86. package/src/endpoints/global/files/UploadImage.ts +3 -4
  87. package/src/endpoints/global/groups/GetGroupsCountEndpoint.ts +51 -0
  88. package/src/endpoints/global/groups/GetGroupsEndpoint.test.ts +1 -1
  89. package/src/endpoints/global/groups/GetGroupsEndpoint.ts +4 -2
  90. package/src/endpoints/global/members/GetMemberFamilyEndpoint.ts +2 -3
  91. package/src/endpoints/global/members/GetMembersCountEndpoint.ts +1 -1
  92. package/src/endpoints/global/members/GetMembersEndpoint.test.ts +2 -2
  93. package/src/endpoints/global/members/GetMembersEndpoint.ts +10 -12
  94. package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.test.ts +195 -12
  95. package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.ts +16 -22
  96. package/src/endpoints/global/members/helpers/validateGroupFilter.ts +6 -30
  97. package/src/endpoints/global/members/shouldCheckIfMemberIsDuplicate.ts +1 -1
  98. package/src/endpoints/global/organizations/CheckRegisterCodeEndpoint.ts +1 -1
  99. package/src/endpoints/global/organizations/CreateOrganizationEndpoint.ts +40 -8
  100. package/src/endpoints/global/organizations/GetOrganizationFromDomainEndpoint.ts +2 -2
  101. package/src/endpoints/global/organizations/GetOrganizationFromUriEndpoint.ts +2 -2
  102. package/src/endpoints/global/organizations/SearchOrganizationEndpoint.test.ts +4 -1
  103. package/src/endpoints/global/organizations/SearchOrganizationEndpoint.ts +4 -5
  104. package/src/endpoints/global/payments/StripeWebhookEndpoint.ts +9 -16
  105. package/src/endpoints/global/platform/GetPlatformAdminsEndpoint.ts +1 -1
  106. package/src/endpoints/global/platform/GetPlatformEndpoint.ts +1 -1
  107. package/src/endpoints/global/platform/PatchPlatformEnpoint.test.ts +1 -1
  108. package/src/endpoints/global/platform/PatchPlatformEnpoint.ts +32 -13
  109. package/src/endpoints/global/platform-memberships/GetPlatformMembershipsCountEndpoint.ts +31 -31
  110. package/src/endpoints/global/platform-memberships/GetPlatformMembershipsEndpoint.ts +18 -20
  111. package/src/endpoints/global/registration/GetRegistrationsCountEndpoint.ts +1 -1
  112. package/src/endpoints/global/registration/GetRegistrationsEndpoint.test.ts +1 -1
  113. package/src/endpoints/global/registration/GetRegistrationsEndpoint.ts +6 -10
  114. package/src/endpoints/global/registration/GetUserDetailedPayableBalanceEndpoint.ts +35 -6
  115. package/src/endpoints/global/registration/GetUserDocumentsEndpoint.ts +2 -2
  116. package/src/endpoints/global/registration/GetUserMembersEndpoint.ts +1 -1
  117. package/src/endpoints/global/registration/GetUserPayableBalanceEndpoint.ts +1 -1
  118. package/src/endpoints/global/registration/PatchUserMembersEndpoint.test.ts +2 -2
  119. package/src/endpoints/global/registration/PatchUserMembersEndpoint.ts +3 -3
  120. package/src/endpoints/global/registration/RegisterMembersEndpoint.test.ts +90 -6
  121. package/src/endpoints/global/registration/RegisterMembersEndpoint.ts +38 -33
  122. package/src/endpoints/global/registration-invitations/GetRegistrationInvitationsCountEndpoint.ts +45 -0
  123. package/src/endpoints/global/registration-invitations/GetRegistrationInvitationsEndpoint.test.ts +494 -0
  124. package/src/endpoints/global/registration-invitations/GetRegistrationInvitationsEndpoint.ts +213 -0
  125. package/src/endpoints/global/registration-invitations/PatchRegistrationInvitationsEndpoint.test.ts +404 -0
  126. package/src/endpoints/global/registration-invitations/PatchRegistrationInvitationsEndpoint.ts +167 -0
  127. package/src/endpoints/global/registration-periods/GetRegistrationPeriodsCountEndpoint.ts +43 -0
  128. package/src/endpoints/global/registration-periods/GetRegistrationPeriodsEndpoint.ts +1 -1
  129. package/src/endpoints/global/registration-periods/PatchRegistrationPeriodsEndpoint.ts +13 -5
  130. package/src/endpoints/global/sso/GetSSOEndpoint.ts +2 -3
  131. package/src/endpoints/global/sso/SetSSOEndpoint.ts +1 -1
  132. package/src/endpoints/global/webshops/GetWebshopFromDomainEndpoint.ts +2 -2
  133. package/src/endpoints/global/webshops/GetWebshopsEndpoint.test.ts +1 -2
  134. package/src/endpoints/global/webshops/GetWebshopsEndpoint.ts +2 -4
  135. package/src/endpoints/organization/dashboard/balance-items/GetBalanceItemEndpoint.ts +2 -2
  136. package/src/endpoints/organization/dashboard/balance-items/GetBalanceItemsCountEndpoint.ts +1 -1
  137. package/src/endpoints/organization/dashboard/balance-items/GetBalanceItemsEndpoint.ts +3 -4
  138. package/src/endpoints/organization/dashboard/balance-items/PatchBalanceItemsEndpoint.ts +19 -6
  139. package/src/endpoints/organization/dashboard/billing/ApplyRegisterCodeEndpoint.test.ts +73 -0
  140. package/src/endpoints/organization/dashboard/billing/ApplyRegisterCodeEndpoint.ts +89 -0
  141. package/src/endpoints/{global → organization/dashboard}/billing/DeactivatePackageEndpoint.ts +4 -9
  142. package/src/endpoints/organization/dashboard/billing/DeleteOrganizationMandateEndpoint.ts +62 -0
  143. package/src/endpoints/organization/dashboard/billing/GetOrganizationDetailedPayableBalanceCollectionEndpoint.ts +56 -0
  144. package/src/endpoints/organization/dashboard/billing/GetOrganizationDetailedPayableBalanceEndpoint.ts +41 -18
  145. package/src/endpoints/organization/dashboard/billing/GetOrganizationMandatesEndpoint.ts +64 -0
  146. package/src/endpoints/organization/dashboard/billing/GetOrganizationPayableBalanceEndpoint.ts +2 -2
  147. package/src/endpoints/organization/dashboard/billing/GetOrganizationPayableInvoicesEndpoint.ts +68 -0
  148. package/src/endpoints/organization/dashboard/billing/GetPackagesEndpoint.ts +12 -4
  149. package/src/endpoints/organization/dashboard/billing/GetRegisterCodeEndpoint.ts +66 -0
  150. package/src/endpoints/organization/dashboard/billing/OrganizationCheckoutEndpoint.test.ts +949 -0
  151. package/src/endpoints/organization/dashboard/billing/OrganizationCheckoutEndpoint.ts +311 -0
  152. package/src/endpoints/organization/dashboard/billing/PatchOrganizationMandatesEndpoint.ts +94 -0
  153. package/src/endpoints/organization/dashboard/documents/GetDocumentTemplateXML.ts +1 -1
  154. package/src/endpoints/organization/dashboard/documents/GetDocumentTemplatesCountEndpoint.ts +1 -1
  155. package/src/endpoints/organization/dashboard/documents/GetDocumentTemplatesEndpoint.ts +1 -1
  156. package/src/endpoints/organization/dashboard/documents/GetDocumentsCountEndpoint.ts +1 -1
  157. package/src/endpoints/organization/dashboard/documents/GetDocumentsEndpoint.ts +1 -1
  158. package/src/endpoints/organization/dashboard/documents/PatchDocumentEndpoint.ts +10 -2
  159. package/src/endpoints/organization/dashboard/documents/PatchDocumentTemplatesEndpoint.test.ts +2 -2
  160. package/src/endpoints/organization/dashboard/documents/PatchDocumentTemplatesEndpoint.ts +2 -2
  161. package/src/endpoints/organization/dashboard/email/CheckEmailBouncesEndpoint.ts +1 -1
  162. package/src/endpoints/organization/dashboard/email-templates/GetEmailTemplatesEndpoint.test.ts +1 -1
  163. package/src/endpoints/organization/dashboard/email-templates/GetEmailTemplatesEndpoint.ts +10 -8
  164. package/src/endpoints/organization/dashboard/email-templates/PatchEmailTemplatesEndpoint.test.ts +1 -1
  165. package/src/endpoints/organization/dashboard/email-templates/PatchEmailTemplatesEndpoint.ts +5 -6
  166. package/src/endpoints/organization/dashboard/invoices/GetInvoicesCountEndpoint.ts +1 -1
  167. package/src/endpoints/organization/dashboard/invoices/GetInvoicesEndpoint.ts +15 -4
  168. package/src/endpoints/organization/dashboard/invoices/PatchInvoicesEndpoint.ts +11 -2
  169. package/src/endpoints/organization/dashboard/mollie/CheckMollieEndpoint.ts +8 -8
  170. package/src/endpoints/organization/dashboard/mollie/ConnectMollieEndpoint.ts +10 -5
  171. package/src/endpoints/organization/dashboard/mollie/DisconnectMollieEndpoint.ts +2 -2
  172. package/src/endpoints/organization/dashboard/mollie/GetMollieDashboardEndpoint.ts +7 -7
  173. package/src/endpoints/organization/dashboard/nolt/CreateNoltTokenEndpoint.ts +1 -1
  174. package/src/endpoints/organization/dashboard/organization/GetOrganizationArchivedGroups.ts +2 -2
  175. package/src/endpoints/organization/dashboard/organization/GetOrganizationDeletedGroups.ts +1 -1
  176. package/src/endpoints/organization/dashboard/organization/GetUitpasClientIdEndpoint.ts +1 -1
  177. package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.test.ts +2 -2
  178. package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.ts +67 -20
  179. package/src/endpoints/organization/dashboard/organization/SearchUitpasOrganizersEndpoint.ts +2 -2
  180. package/src/endpoints/organization/dashboard/organization/SetOrganizationDomainEndpoint.ts +5 -10
  181. package/src/endpoints/organization/dashboard/organization/SetUitpasClientCredentialsEndpoint.ts +1 -1
  182. package/src/endpoints/organization/dashboard/payments/GetPaymentsCountEndpoint.ts +1 -1
  183. package/src/endpoints/organization/dashboard/payments/GetPaymentsEndpoint.ts +3 -4
  184. package/src/endpoints/organization/dashboard/payments/PatchPaymentsEndpoint.ts +24 -17
  185. package/src/endpoints/organization/dashboard/receivable-balances/ChargeReceivableBalancesEndpoint.ts +134 -0
  186. package/src/endpoints/organization/dashboard/receivable-balances/GetReceivableBalanceEndpoint.ts +2 -3
  187. package/src/endpoints/organization/dashboard/receivable-balances/GetReceivableBalancesCountEndpoint.ts +1 -1
  188. package/src/endpoints/organization/dashboard/receivable-balances/GetReceivableBalancesEndpoint.ts +1 -1
  189. package/src/endpoints/organization/dashboard/registration-periods/GetOrganizationRegistrationPeriodsEndpoint.test.ts +1 -1
  190. package/src/endpoints/organization/dashboard/registration-periods/GetOrganizationRegistrationPeriodsEndpoint.ts +2 -3
  191. package/src/endpoints/organization/dashboard/registration-periods/MoveRegistrationPeriods.test.ts +1 -1
  192. package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.test.ts +1 -1
  193. package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.ts +32 -22
  194. package/src/endpoints/organization/dashboard/registration-periods/SetupStepReviewEndpoint.ts +4 -5
  195. package/src/endpoints/organization/dashboard/stripe/ConnectStripeEndpoint.ts +1 -1
  196. package/src/endpoints/organization/dashboard/stripe/DeleteStripeAccountEndpoint.ts +2 -3
  197. package/src/endpoints/organization/dashboard/stripe/GetStripeAccountLinkEndpoint.ts +2 -2
  198. package/src/endpoints/organization/dashboard/stripe/GetStripeAccountsEndpoint.ts +1 -1
  199. package/src/endpoints/organization/dashboard/stripe/GetStripeLoginLinkEndpoint.ts +2 -2
  200. package/src/endpoints/organization/dashboard/stripe/UpdateStripeAccountEndpoint.ts +1 -1
  201. package/src/endpoints/organization/dashboard/users/CreateApiUserEndpoint.ts +1 -1
  202. package/src/endpoints/organization/dashboard/users/DeleteUserEndpoint.ts +1 -1
  203. package/src/endpoints/organization/dashboard/users/GetApiUsersEndpoint.ts +1 -1
  204. package/src/endpoints/organization/dashboard/users/GetOrganizationAdminsEndpoint.ts +1 -1
  205. package/src/endpoints/organization/dashboard/users/PatchApiUserEndpoint.ts +4 -6
  206. package/src/endpoints/organization/dashboard/webshops/CreateWebshopEndpoint.ts +10 -15
  207. package/src/endpoints/organization/dashboard/webshops/DeleteWebshopEndpoint.ts +1 -1
  208. package/src/endpoints/organization/dashboard/webshops/GetDiscountCodesEndpoint.ts +2 -2
  209. package/src/endpoints/organization/dashboard/webshops/GetWebshopOrdersCountEndpoint.ts +1 -1
  210. package/src/endpoints/organization/dashboard/webshops/GetWebshopOrdersEndpoint.ts +1 -1
  211. package/src/endpoints/organization/dashboard/webshops/GetWebshopTicketsEndpoint.ts +1 -1
  212. package/src/endpoints/organization/dashboard/webshops/GetWebshopUriAvailabilityEndpoint.ts +11 -2
  213. package/src/endpoints/organization/dashboard/webshops/PatchDiscountCodesEndpoint.ts +4 -6
  214. package/src/endpoints/organization/dashboard/webshops/PatchWebshopEndpoint.ts +29 -8
  215. package/src/endpoints/organization/dashboard/webshops/PatchWebshopOrdersEndpoint.ts +21 -28
  216. package/src/endpoints/organization/dashboard/webshops/PatchWebshopTicketsEndpoint.ts +1 -1
  217. package/src/endpoints/organization/dashboard/webshops/SearchUitpasEventsEndpoint.ts +2 -2
  218. package/src/endpoints/organization/dashboard/webshops/VerifyWebshopDomainEndpoint.ts +2 -3
  219. package/src/endpoints/organization/dashboard/webshops/WebshopReservedPathSegments.test.ts +95 -0
  220. package/src/endpoints/organization/shared/ExchangePaymentEndpoint.ts +15 -13
  221. package/src/endpoints/organization/shared/GetDocumentHtml.ts +1 -1
  222. package/src/endpoints/organization/shared/GetPaymentEndpoint.ts +1 -1
  223. package/src/endpoints/organization/shared/GetUitpasNumberDetailsEndpoint.ts +3 -5
  224. package/src/endpoints/organization/shared/auth/GetOrganizationEndpoint.ts +1 -1
  225. package/src/endpoints/organization/webshops/CheckWebshopDiscountCodesEndpoint.ts +4 -1
  226. package/src/endpoints/organization/webshops/GetOrderByPaymentEndpoint.ts +14 -3
  227. package/src/endpoints/organization/webshops/GetOrderEndpoint.ts +14 -2
  228. package/src/endpoints/organization/webshops/GetTicketsEndpoint.ts +37 -16
  229. package/src/endpoints/organization/webshops/GetWebshopEndpoint.test.ts +34 -2
  230. package/src/endpoints/organization/webshops/GetWebshopEndpoint.ts +4 -2
  231. package/src/endpoints/organization/webshops/PlaceOrderEndpoint.test.ts +74 -3
  232. package/src/endpoints/organization/webshops/PlaceOrderEndpoint.ts +28 -50
  233. package/src/endpoints/organization/webshops/RetrieveUitpasSocialTariffPriceEndpoint.ts +3 -5
  234. package/src/endpoints/organization/webshops/WebshopAuthHelper.ts +36 -0
  235. package/src/endpoints/system/HealthEndpoint.ts +1 -1
  236. package/src/excel-loaders/balance-items.ts +1 -1
  237. package/src/excel-loaders/event-notifications.ts +1 -1
  238. package/src/excel-loaders/members.ts +1 -1
  239. package/src/excel-loaders/organizations.ts +3 -3
  240. package/src/excel-loaders/payments.ts +31 -1
  241. package/src/excel-loaders/platform-memberships.ts +13 -14
  242. package/src/excel-loaders/receivable-balances.ts +2 -2
  243. package/src/excel-loaders/registrations.ts +2 -3
  244. package/src/helpers/AddressValidator.ts +7 -14
  245. package/src/helpers/AdminPermissionChecker.ts +60 -65
  246. package/src/helpers/AuthenticatedStructures.ts +153 -51
  247. package/src/helpers/BalanceItemRelationsBackfiller.test.ts +174 -0
  248. package/src/helpers/BalanceItemRelationsBackfiller.ts +163 -0
  249. package/src/helpers/BuckarooHelper.ts +2 -4
  250. package/src/helpers/CheckSettlements.ts +12 -24
  251. package/src/helpers/Context.ts +15 -10
  252. package/src/helpers/CookieHelper.ts +1 -2
  253. package/src/helpers/EmailResumer.ts +1 -2
  254. package/src/helpers/FileCache.ts +2 -3
  255. package/src/helpers/FinancialSupportHelper.ts +21 -0
  256. package/src/helpers/FlagMomentCleanup.ts +1 -2
  257. package/src/helpers/ForwardHandler.ts +5 -10
  258. package/src/helpers/GlobalHelper.ts +3 -3
  259. package/src/helpers/LegacyBillingConverter.ts +787 -0
  260. package/src/helpers/MemberMerger.test.ts +782 -0
  261. package/src/helpers/MemberMerger.ts +600 -0
  262. package/src/helpers/MemberUserSyncer.ts +6 -12
  263. package/src/helpers/MembershipCharger.ts +2 -4
  264. package/src/helpers/PaymentCustomerResolver.test.ts +203 -0
  265. package/src/helpers/PaymentCustomerResolver.ts +144 -0
  266. package/src/helpers/PeriodHelper.ts +2 -3
  267. package/src/helpers/RecordAnswerHelper.test.ts +741 -0
  268. package/src/helpers/RecordAnswerHelper.ts +116 -0
  269. package/src/helpers/SeedTools.ts +106 -0
  270. package/src/helpers/ServiceFeeHelper.ts +125 -15
  271. package/src/helpers/SetupStepUpdater.ts +38 -32
  272. package/src/helpers/StamhoofdFilterAccessHelper.ts +52 -0
  273. package/src/helpers/StripeHelper.ts +15 -38
  274. package/src/helpers/StripeInvoicer.ts +419 -0
  275. package/src/helpers/StripePayoutChecker.ts +5 -10
  276. package/src/helpers/TagHelper.ts +7 -8
  277. package/src/helpers/TemporaryMemberAccess.ts +1 -2
  278. package/src/helpers/ThrottledQueue.ts +1 -2
  279. package/src/helpers/UitpasTokenRepository.ts +1 -2
  280. package/src/helpers/ViesHelper.ts +11 -11
  281. package/src/helpers/XlsxTransformerColumnHelper.ts +1 -1
  282. package/src/helpers/outstandingBalanceJoin.ts +2 -2
  283. package/src/helpers/updateMemberDetailsUitpasNumber.ts +3 -5
  284. package/src/migrate.ts +12 -5
  285. package/src/{seeds → migrations}/0000000004-single-organization.ts +10 -1
  286. package/src/migrations/0000000006-default-organization.ts +139 -0
  287. package/src/{seeds → migrations}/1715028563-user-permissions.ts +17 -27
  288. package/src/{seeds/1765896674-document-update-year.test.ts → migrations/1750090029-document-update-year.test.ts} +27 -26
  289. package/src/migrations/1750090029-document-update-year.ts +103 -0
  290. package/src/migrations/1750090030-records-configuration.ts +92 -0
  291. package/src/migrations/1750090031-records-configuration-webshops.ts +36 -0
  292. package/src/migrations/1752848540-national-numbers.ts +585 -0
  293. package/src/migrations/1752848550-records-configuration-group-filter.ts +1064 -0
  294. package/src/{seeds → migrations}/1752848561-groups-registration-periods.ts +95 -27
  295. package/src/{seeds → migrations}/1754560914-groups-prices.test.ts +1 -1
  296. package/src/{seeds → migrations}/1754560914-groups-prices.ts +9 -15
  297. package/src/{seeds → migrations}/1756293699-fill-previous-next-period-id.ts +1 -2
  298. package/src/migrations/1761665607-sync-member-users.ts +40 -0
  299. package/src/migrations/1779121239-default-invoice-email-template.sql +3 -0
  300. package/src/migrations/1779121240-invoice-email-template.sql +6 -0
  301. package/src/migrations/1779986893-default-payment-failed-email-templates.sql +4 -0
  302. package/src/migrations/1780578166-convert-legacy-billing.ts +13 -0
  303. package/src/{seeds/1755876819-remove-duplicate-members.ts → migrations/1780665426-remove-duplicate-members.ts} +23 -4
  304. package/src/migrations/1780933598-document-groups.ts +88 -0
  305. package/src/migrations/1781692342-balance-item-relations.ts +13 -0
  306. package/src/migrations/1781857866-platform-admins.ts +103 -0
  307. package/src/migrations/1781857867-rm-old-platform-admins.ts +25 -0
  308. package/src/seeds/0000000001-throw.ts +5 -0
  309. package/src/seeds/1760702452-update-balance-price-paid.ts +19 -0
  310. package/src/seeds/1760702453-update-balance-invoiced.ts +19 -0
  311. package/src/seeds/1760702454-update-cached-outstanding-balance-from-items.ts +51 -14
  312. package/src/seeds/1773754928-force-save-members.ts +41 -8
  313. package/src/seeds/1780665427-schedule-stock-updates.ts +45 -0
  314. package/src/seeds/1780665428-group-update-occupancy.ts +31 -0
  315. package/src/seeds/1780915001-fill-payment-customer.ts +70 -0
  316. package/src/seeds/1780915002-update-orders.ts +39 -0
  317. package/src/seeds/1782047708-organization-company-address.ts +44 -0
  318. package/src/seeds-temporary/1769088653-uitpas-status.ts +1 -2
  319. package/src/seeds-temporary/1780416000-migrate-audit-log-legacy-enums.test.ts +69 -0
  320. package/src/seeds-temporary/1780416000-migrate-audit-log-legacy-enums.ts +85 -0
  321. package/src/services/AuditLogService.test.ts +51 -0
  322. package/src/services/AuditLogService.ts +45 -0
  323. package/src/services/BalanceItemService.ts +148 -55
  324. package/src/services/CpuService.ts +3 -6
  325. package/src/services/DatabaseCollationService.test.ts +1 -2
  326. package/src/services/DocumentService.ts +1 -2
  327. package/src/services/EventNotificationService.ts +1 -1
  328. package/src/services/FileSignService.ts +2 -4
  329. package/src/services/InvoicePdfService.ts +286 -0
  330. package/src/services/InvoiceService.ts +330 -77
  331. package/src/services/InvoiceXMLService.ts +424 -0
  332. package/src/services/MemberNumberService.ts +6 -11
  333. package/src/services/MemberRecordStore.ts +8 -10
  334. package/src/services/MollieService.ts +530 -0
  335. package/src/services/PaymentMandateService.ts +214 -0
  336. package/src/services/PaymentReallocationService.test.ts +1 -1
  337. package/src/services/PaymentReallocationService.ts +7 -9
  338. package/src/services/PaymentService.ts +820 -328
  339. package/src/services/PlatformMembershipService.ts +5 -9
  340. package/src/services/ReferralService.ts +223 -0
  341. package/src/services/RegistrationService.ts +125 -54
  342. package/src/services/SSOService.test.ts +119 -0
  343. package/src/services/SSOService.ts +56 -49
  344. package/src/services/STPackageService.ts +97 -68
  345. package/src/services/UniqueMemberNumberService.ts +69 -0
  346. package/src/services/UniqueUserService.ts +3 -6
  347. package/src/services/VATService.ts +85 -0
  348. package/src/services/data/invoice.hbs.html +686 -0
  349. package/src/services/uitpas/PassholderEndpoints.ts +3 -6
  350. package/src/services/uitpas/UitpasService.ts +6 -10
  351. package/src/services/uitpas/checkUitpasNumbers.ts +2 -4
  352. package/src/services/uitpas/getSocialTariffForUitpasNumbers.ts +3 -6
  353. package/src/services/uitpas/searchUitpasEvents.ts +1 -2
  354. package/src/sql-filters/audit-logs.ts +1 -1
  355. package/src/sql-filters/balance-item-payments.ts +1 -1
  356. package/src/sql-filters/balance-items.ts +1 -1
  357. package/src/sql-filters/base-registration-filter-compilers.ts +5 -33
  358. package/src/sql-filters/document-templates.ts +1 -1
  359. package/src/sql-filters/documents.ts +1 -1
  360. package/src/sql-filters/email-recipients.ts +1 -1
  361. package/src/sql-filters/emails.ts +1 -1
  362. package/src/sql-filters/event-notifications.ts +1 -1
  363. package/src/sql-filters/events.ts +6 -1
  364. package/src/sql-filters/groups.ts +16 -1
  365. package/src/sql-filters/invoiced-balance-items.ts +1 -1
  366. package/src/sql-filters/invoices.ts +1 -1
  367. package/src/sql-filters/member-responsibility-records.ts +1 -1
  368. package/src/sql-filters/members.ts +1 -1
  369. package/src/sql-filters/orders.ts +4 -4
  370. package/src/sql-filters/organization-registration-periods.ts +1 -1
  371. package/src/sql-filters/organizations.ts +1 -1
  372. package/src/sql-filters/payments.ts +5 -0
  373. package/src/sql-filters/platform-memberships.ts +3 -3
  374. package/src/sql-filters/receivable-balances.ts +1 -1
  375. package/src/sql-filters/registration-invitations.ts +90 -0
  376. package/src/sql-filters/registration-periods.ts +1 -1
  377. package/src/sql-filters/registrations.ts +1 -1
  378. package/src/sql-filters/tickets.ts +1 -1
  379. package/src/sql-filters/users.ts +1 -1
  380. package/src/sql-sorters/groups.ts +23 -0
  381. package/src/sql-sorters/platform-memberships.ts +16 -16
  382. package/src/sql-sorters/registration-invitations.ts +36 -0
  383. package/tests/actions/patchOrganizationMember.ts +1 -1
  384. package/tests/actions/patchUserMember.ts +1 -1
  385. package/tests/assertions/assertBalances.ts +6 -12
  386. package/tests/e2e/api-rate-limits.test.ts +12 -24
  387. package/tests/e2e/bundle-discounts.test.ts +4 -1
  388. package/tests/e2e/charge-members.test.ts +13 -11
  389. package/tests/e2e/documents.test.ts +4 -1
  390. package/tests/e2e/register.test.ts +7 -9
  391. package/tests/e2e/stock.test.ts +4 -3
  392. package/tests/e2e/tickets.test.ts +1 -1
  393. package/tests/helpers/MollieMocker.ts +462 -0
  394. package/tests/helpers/PayconiqMocker.ts +138 -45
  395. package/tests/helpers/StripeMocker.ts +9 -6
  396. package/tests/helpers/index.ts +4 -0
  397. package/tests/init/initAdmin.ts +2 -2
  398. package/tests/init/initBundleDiscount.ts +1 -1
  399. package/tests/init/initMembershipOrganization.ts +13 -0
  400. package/tests/init/initPermissionRole.ts +3 -4
  401. package/tests/vitest.global.setup.ts +1 -1
  402. package/vitest.config.js +7 -0
  403. package/.nvmrc +0 -1
  404. package/src/endpoints/global/billing/ActivatePackagesEndpoint.ts +0 -216
  405. package/src/helpers/GroupBuilder.ts +0 -418
  406. package/src/seeds/0000000000-example.ts +0 -11
  407. package/src/seeds/1722256498-group-update-occupancy.ts +0 -52
  408. package/src/seeds/1726572303-schedule-stock-updates.ts +0 -52
  409. package/src/seeds/1726847064-setup-steps.ts +0 -16
  410. package/src/seeds/1728928974-update-cached-outstanding-balance-from-items.ts +0 -31
  411. package/src/seeds/1729253172-update-orders.ts +0 -21
  412. package/src/seeds/1733319079-fill-paying-organization-ids.ts +0 -68
  413. package/src/seeds/1733748412-update-deleted-order-numbers.ts +0 -109
  414. package/src/seeds/1733994455-balance-item-status-open.ts +0 -30
  415. package/src/seeds/1740046783-update-membership.ts +0 -17
  416. package/src/seeds/1741008870-fix-auditlog-description.ts +0 -50
  417. package/src/seeds/1741011468-fix-auditlog-description-uft16.ts +0 -88
  418. package/src/seeds/1741268179-fix-group-dates-of-events.ts +0 -32
  419. package/src/seeds/1750090030-records-configuration.ts +0 -26
  420. package/src/seeds/1751445358-upload-email-attachments.ts +0 -106
  421. package/src/seeds/1755532883-update-email-sender-ids.ts +0 -47
  422. package/src/seeds/1755790070-fill-email-recipient-errors.ts +0 -96
  423. package/src/seeds/1756115432-remove-old-drafts.ts +0 -16
  424. package/src/seeds/1756115433-fill-email-recipient-organization-id.ts +0 -30
  425. package/src/seeds/1756303697-update-email-counts.ts +0 -76
  426. package/src/seeds/1761665607-sync-member-users.ts +0 -59
  427. package/src/seeds/1765896674-document-update-year.ts +0 -66
  428. /package/src/{seeds → migrations}/0000000001-development-user.ts +0 -0
  429. /package/src/{seeds → migrations}/0000000002-clear-stamhoofd-email-templates.ts +0 -0
  430. /package/src/{seeds → migrations}/0000000003-default-email-templates.ts +0 -0
  431. /package/src/{seeds → migrations}/1752848561-groups-registration-periods.test.ts +0 -0
  432. /package/src/{seeds → migrations}/1766150402-document-published-at.test.ts +0 -0
  433. /package/src/{seeds → migrations}/1766150402-document-published-at.ts +0 -0
  434. /package/src/{seeds → migrations}/data/default-email-templates.sql +0 -0
@@ -1,8 +1,9 @@
1
1
  import { SimpleError } from '@simonbackx/simple-errors';
2
- import type { AuditLog, Document, EventNotification, MemberWithUsersRegistrationsAndGroups, Order, Ticket} from '@stamhoofd/models';
3
- import { BalanceItem, CachedBalance, Event, Group, Invoice, Member, MemberPlatformMembership, MemberResponsibilityRecord, Organization, OrganizationRegistrationPeriod, Payment, Registration, RegistrationPeriod, User, Webshop } from '@stamhoofd/models';
4
- import type { PaymentGeneral} from '@stamhoofd/structures';
5
- import { AuditLogReplacement, AuditLogReplacementType, AuditLog as AuditLogStruct, DetailedReceivableBalance, Document as DocumentStruct, EventNotification as EventNotificationStruct, Event as EventStruct, GenericBalance, Group as GroupStruct, GroupType, InvoicedBalanceItem, InvoiceStruct, MemberPlatformMembership as MemberPlatformMembershipStruct, MembersBlob, MemberWithRegistrationsBlob, NamedObject, OrganizationRegistrationPeriod as OrganizationRegistrationPeriodStruct, Organization as OrganizationStruct, PaymentCustomer, PermissionLevel, Platform, PrivateOrder, PrivateWebshop, ReceivableBalanceObject, ReceivableBalanceObjectContact, ReceivableBalance as ReceivableBalanceStruct, ReceivableBalanceType, RegistrationsBlob, RegistrationWithMemberBlob, TicketPrivate, UserWithMembers, WebshopPreview, Webshop as WebshopStruct, BalanceItem as BalanceItemStruct } from '@stamhoofd/structures';
2
+ import type { AuditLog, Document, EventNotification, MemberWithUsersRegistrationsAndGroups, Order, Ticket } from '@stamhoofd/models';
3
+ import { BalanceItem, CachedBalance, Event, Group, Invoice, Member, MemberPlatformMembership, MemberResponsibilityRecord, Organization, OrganizationRegistrationPeriod, Payment, Registration, RegistrationInvitation, RegistrationPeriod, User, Webshop } from '@stamhoofd/models';
4
+ import type { PaymentGeneral } from '@stamhoofd/structures';
5
+ import { BaseOrganization, getAppHost } from '@stamhoofd/structures';
6
+ import { Payment as PaymentStruct, AuditLogReplacement, AuditLogReplacementType, AuditLog as AuditLogStruct, BalanceItem as BalanceItemStruct, DetailedReceivableBalance, Document as DocumentStruct, EventNotification as EventNotificationStruct, Event as EventStruct, GenericBalance, Group as GroupStruct, GroupType, InvitationGroupData, InvitationMemberData, InvoicedBalanceItem, InvoiceStruct, MemberPlatformMembership as MemberPlatformMembershipStruct, MemberRegistrationInvitation, MembersBlob, MemberWithRegistrationsBlob, NamedObject, OrganizationRegistrationPeriod as OrganizationRegistrationPeriodStruct, Organization as OrganizationStruct, PaymentCustomer, PermissionLevel, Platform, PrivateOrder, PrivateWebshop, ReceivableBalanceObject, ReceivableBalanceObjectContact, ReceivableBalance as ReceivableBalanceStruct, ReceivableBalanceType, RegistrationInvitation as RegistrationInvitationStruct, RegistrationsBlob, RegistrationWithMemberBlob, TicketPrivate, UserWithMembers, WebshopPreview, Webshop as WebshopStruct } from '@stamhoofd/structures';
6
7
  import { Sorter } from '@stamhoofd/utility';
7
8
 
8
9
  import { SQL } from '@stamhoofd/sql';
@@ -34,7 +35,7 @@ export class AuthenticatedStructures {
34
35
  const { registrations, orders } = await Payment.loadBalanceItemRelations(balanceItems);
35
36
 
36
37
  // Note: permission checking is moved here for performacne to avoid loading the data multiple times
37
- if (!(await Context.auth.canAccessBalanceItems(balanceItems, PermissionLevel.Read, { registrations, orders }))) {
38
+ if (!(await Context.optionalAuth?.canAccessBalanceItems(balanceItems, PermissionLevel.Read, { registrations, orders }))) {
38
39
  throw new SimpleError({
39
40
  code: 'permission_denied',
40
41
  message: 'Permission denied',
@@ -43,7 +44,7 @@ export class AuthenticatedStructures {
43
44
  }
44
45
  }
45
46
 
46
- const includeSettlements = checkPermissions && !!Context.user && !!Context.user.permissions;
47
+ const includeSettlements = checkPermissions && !!Context.user && !!Context.user.permissions && payments.every(p => !!Context.optionalAuth?.checkScope(p.organizationId));
47
48
 
48
49
  const { payingOrganizations } = await Payment.loadPayingOrganizations(payments);
49
50
 
@@ -55,6 +56,22 @@ export class AuthenticatedStructures {
55
56
  }, includeSettlements);
56
57
  }
57
58
 
59
+ /**
60
+ *
61
+ * @param payments
62
+ * @param checkPermissions Only set to undefined when not returned in the API + not for public use
63
+ * @returns
64
+ */
65
+ static async payments(payments: Payment[]): Promise<PaymentStruct[]> {
66
+ if (payments.length === 0) {
67
+ return [];
68
+ }
69
+
70
+ return payments.map((p) => {
71
+ return PaymentStruct.create(p);
72
+ });
73
+ }
74
+
58
75
  static async invoices(invoices: Invoice[]): Promise<InvoiceStruct[]> {
59
76
  if (invoices.length === 0) {
60
77
  return [];
@@ -92,8 +109,7 @@ export class AuthenticatedStructures {
92
109
 
93
110
  if (existingGroup) {
94
111
  waitingLists.push(existingGroup);
95
- }
96
- else {
112
+ } else {
97
113
  waitingListsToFetch.push(waitingListId);
98
114
  }
99
115
  }
@@ -159,13 +175,11 @@ export class AuthenticatedStructures {
159
175
  .where(whereGroupIds)
160
176
  .orWhere(whereWaitingList)
161
177
  .fetch();
162
- }
163
- else {
178
+ } else {
164
179
  groups = await Group.select()
165
180
  .where(whereGroupIds).fetch();
166
181
  }
167
- }
168
- else if (organizationIds.length && periodIds.length) {
182
+ } else if (organizationIds.length && periodIds.length) {
169
183
  groups = await Group.select()
170
184
  .where(whereWaitingList)
171
185
  .fetch();
@@ -222,6 +236,16 @@ export class AuthenticatedStructures {
222
236
  return WebshopPreview.create(webshop);
223
237
  }
224
238
 
239
+ static async baseOrganizations(organizations: Organization[]): Promise<BaseOrganization[]> {
240
+ if (organizations.length === 0) {
241
+ return [];
242
+ }
243
+
244
+ return organizations.map((o) => {
245
+ return BaseOrganization.create(o);
246
+ });
247
+ }
248
+
225
249
  static async organizations(organizations: Organization[]): Promise<OrganizationStruct[]> {
226
250
  if (organizations.length === 0) {
227
251
  return [];
@@ -235,8 +259,7 @@ export class AuthenticatedStructures {
235
259
  const array = periodIdOrganizationsMap.get(periodId);
236
260
  if (array !== undefined) {
237
261
  array.push(organization);
238
- }
239
- else {
262
+ } else {
240
263
  periodIdOrganizationsMap.set(periodId, [organization]);
241
264
  }
242
265
  }
@@ -287,8 +310,7 @@ export class AuthenticatedStructures {
287
310
  organizationIdsToGetWebshopsFor.push(organization.id);
288
311
  }
289
312
  organizationData.set(organizationId, { organizationRegistrationPeriod, canAccessPrivateData });
290
- }
291
- else {
313
+ } else {
292
314
  // Should never happen
293
315
  throw new Error('Organization registration period model not found for organization id ' + organizationId + ' and period id ' + periodId);
294
316
  }
@@ -298,14 +320,14 @@ export class AuthenticatedStructures {
298
320
  // Get webshop previews
299
321
  const webshops = organizationIdsToGetWebshopsFor.length > 0
300
322
  ? await Webshop.where(
301
- {
302
- organizationId: {
303
- sign: 'IN',
304
- value: organizationIdsToGetWebshopsFor,
323
+ {
324
+ organizationId: {
325
+ sign: 'IN',
326
+ value: organizationIdsToGetWebshopsFor,
327
+ },
305
328
  },
306
- },
307
- { select: Webshop.selectColumnsWithout(undefined, 'products', 'categories') },
308
- )
329
+ { select: Webshop.selectColumnsWithout(undefined, 'products', 'categories') },
330
+ )
309
331
  : [];
310
332
 
311
333
  const webshopPreviews = new Map<string, WebshopPreview[]>();
@@ -321,8 +343,7 @@ export class AuthenticatedStructures {
321
343
 
322
344
  if (array) {
323
345
  array.push(preview);
324
- }
325
- else {
346
+ } else {
326
347
  webshopPreviews.set(organizationId, [preview]);
327
348
  }
328
349
  }
@@ -356,8 +377,7 @@ export class AuthenticatedStructures {
356
377
  privateMeta: organization.privateMeta.removedPrivateKeys,
357
378
  webshops: webshopPreviews.get(organization.id)?.sort((a, b) => Sorter.byDateValue(b.createdAt, a.createdAt)),
358
379
  });
359
- }
360
- else {
380
+ } else {
361
381
  result = OrganizationStruct.create({
362
382
  ...baseStruct,
363
383
  period,
@@ -447,18 +467,25 @@ export class AuthenticatedStructures {
447
467
  // Load responsibilities
448
468
  const responsibilities = members.length > 0
449
469
  ? await MemberResponsibilityRecord.select()
450
- .where('memberId', members.map(m => m.id))
451
- .where(SQL.where('endDate', null).or('endDate', '>', new Date()))
452
- .fetch()
470
+ .where('memberId', members.map(m => m.id))
471
+ .where(SQL.where('endDate', null).or('endDate', '>', new Date()))
472
+ .fetch()
453
473
  : [];
454
474
  const platformMemberships = members.length > 0
455
475
  ? await MemberPlatformMembership.select()
456
- .where('memberId', members.map(m => m.id))
457
- .where('deletedAt', null)
458
- .where('periodId', relevantPeriodIds)
459
- .fetch()
476
+ .where('memberId', members.map(m => m.id))
477
+ .where('deletedAt', null)
478
+ .where('periodId', relevantPeriodIds)
479
+ .fetch()
480
+ : [];
481
+ const registrationInvitations = members.length > 0
482
+ ? await RegistrationInvitation.select()
483
+ .where('memberId', members.map(m => m.id))
484
+ .fetch()
460
485
  : [];
461
486
 
487
+ const memberRegistrationInvitations: Map<string, MemberRegistrationInvitation[]> = registrationInvitations.length > 0 ? await this.memberRegistrationInvitations(registrationInvitations) : new Map();
488
+
462
489
  // Load organizations
463
490
  const organizationIds = responsibilities.map(r => r.organizationId)
464
491
  .concat(
@@ -640,8 +667,7 @@ export class AuthenticatedStructures {
640
667
  const g = allGroups.get(r.groupId);
641
668
  if (g) {
642
669
  base.group = g;
643
- }
644
- else {
670
+ } else {
645
671
  console.error('Group not preloaded for registration', r.id, r.groupId);
646
672
  }
647
673
 
@@ -663,6 +689,7 @@ export class AuthenticatedStructures {
663
689
  return r.getStructure(group);
664
690
  });
665
691
  blob.platformMemberships = platformMemberships.filter(r => r.memberId == blob.id).map(r => MemberPlatformMembershipStruct.create(r));
692
+ blob.registrationInvitations = memberRegistrationInvitations.get(blob.id) ?? [];
666
693
  }
667
694
 
668
695
  return MembersBlob.create({
@@ -775,8 +802,7 @@ export class AuthenticatedStructures {
775
802
  const array = eventIdsMapping.get(notificationId);
776
803
  if (array) {
777
804
  array.push(eventId);
778
- }
779
- else {
805
+ } else {
780
806
  eventIdsMapping.set(notificationId, [eventId]);
781
807
  }
782
808
 
@@ -935,7 +961,7 @@ export class AuthenticatedStructures {
935
961
  return (await this.receivableBalancesHelper(balances)).map(({ balance, object }) => ReceivableBalanceStruct.create({ ...balance, object }));
936
962
  }
937
963
 
938
- private static async receivableBalancesHelper(balances: CachedBalance[]): Promise< { balance: CachedBalance; object: ReceivableBalanceObject }[]> {
964
+ private static async receivableBalancesHelper(balances: CachedBalance[]): Promise<{ balance: CachedBalance; object: ReceivableBalanceObject }[]> {
939
965
  if (balances.length === 0) {
940
966
  return [];
941
967
  }
@@ -972,7 +998,7 @@ export class AuthenticatedStructures {
972
998
  const result: { balance: CachedBalance; object: ReceivableBalanceObject }[] = [];
973
999
 
974
1000
  function getMemberContacts(member: Member, balance: CachedBalance) {
975
- const url = Context.organization && Context.organization.id === balance.organizationId ? 'https://' + Context.organization.getHost() : '';
1001
+ const url = Context.organization && Context.organization.id === balance.organizationId ? 'https://' + getAppHost('registration', Context.organization, false) : '';
976
1002
  return [
977
1003
  ...(member.details.getMemberEmails().length
978
1004
  ? [
@@ -1065,8 +1091,7 @@ export class AuthenticatedStructures {
1065
1091
  })),
1066
1092
  });
1067
1093
  }
1068
- }
1069
- else if (balance.objectType === ReceivableBalanceType.member) {
1094
+ } else if (balance.objectType === ReceivableBalanceType.member) {
1070
1095
  const member = members.find(m => m.id === balance.objectId) ?? null;
1071
1096
  if (member) {
1072
1097
  object = ReceivableBalanceObject.create({
@@ -1076,8 +1101,7 @@ export class AuthenticatedStructures {
1076
1101
  contacts: getMemberContacts(member, balance),
1077
1102
  });
1078
1103
  }
1079
- }
1080
- else if (balance.objectType === ReceivableBalanceType.registration) {
1104
+ } else if (balance.objectType === ReceivableBalanceType.registration) {
1081
1105
  const registration = registrations.find(r => r.id === balance.objectId) ?? null;
1082
1106
  if (!registration) {
1083
1107
  continue;
@@ -1091,11 +1115,10 @@ export class AuthenticatedStructures {
1091
1115
  contacts: getMemberContacts(member, balance),
1092
1116
  });
1093
1117
  }
1094
- }
1095
- else if (balance.objectType === ReceivableBalanceType.user || balance.objectType === ReceivableBalanceType.userWithoutMembers) {
1118
+ } else if (balance.objectType === ReceivableBalanceType.user || balance.objectType === ReceivableBalanceType.userWithoutMembers) {
1096
1119
  const user = users.find(m => m.id === balance.objectId) ?? null;
1097
1120
  if (user) {
1098
- const url = Context.organization && Context.organization.id === balance.organizationId ? 'https://' + Context.organization.getHost() : '';
1121
+ const url = Context.organization && Context.organization.id === balance.organizationId ? 'https://' + getAppHost('registration', Context.organization, user.permissions?.forOrganization(Context.organization)?.isEmpty === false) : '';
1099
1122
  object = ReceivableBalanceObject.create({
1100
1123
  id: balance.objectId,
1101
1124
  name: user.name || user.email,
@@ -1169,15 +1192,13 @@ export class AuthenticatedStructures {
1169
1192
  id: '',
1170
1193
  name: $t(`%wi`) + ' ' + Platform.shared.config.name,
1171
1194
  });
1172
- }
1173
- else {
1195
+ } else {
1174
1196
  userStruct = NamedObject.create({
1175
1197
  id: '',
1176
1198
  name: $t(`%Gr`),
1177
1199
  });
1178
1200
  }
1179
- }
1180
- else {
1201
+ } else {
1181
1202
  userStruct = NamedObject.create({
1182
1203
  id: user.id,
1183
1204
  name: (user.firstName || user.lastName) ? (user.firstName + ' ' + user.lastName) : user.email,
@@ -1220,4 +1241,85 @@ export class AuthenticatedStructures {
1220
1241
  static async balanceItemsWithPayments(balanceItems: BalanceItem[]) {
1221
1242
  return await BalanceItem.getStructureWithPayments(balanceItems);
1222
1243
  }
1244
+
1245
+ static async memberRegistrationInvitations(invitations: RegistrationInvitation[]): Promise<Map<string, MemberRegistrationInvitation[]>> {
1246
+ const results = new Map<string, MemberRegistrationInvitation[]>();
1247
+ const groups = await Group.getByIDs(...invitations.map(i => i.groupId));
1248
+
1249
+ for (const invitation of invitations) {
1250
+ const group = groups.find(g => g.id === invitation.groupId);
1251
+ if (!group) {
1252
+ throw new SimpleError({
1253
+ code: 'group_not_found',
1254
+ message: 'Group not found',
1255
+ human: $t(`%1SM`),
1256
+ });
1257
+ }
1258
+
1259
+ const result = MemberRegistrationInvitation.create({
1260
+ id: invitation.id,
1261
+ group: InvitationGroupData.create({
1262
+ id: invitation.groupId,
1263
+ name: group.settings.name,
1264
+ type: group.type,
1265
+ periodId: group.periodId,
1266
+ }),
1267
+ organizationId: invitation.organizationId,
1268
+ createdAt: invitation.createdAt,
1269
+ });
1270
+
1271
+ let array = results.get(invitation.memberId);
1272
+ if (!array) {
1273
+ array = [result];
1274
+ results.set(invitation.memberId, array);
1275
+ } else {
1276
+ array.push(result);
1277
+ }
1278
+ }
1279
+
1280
+ return results;
1281
+ }
1282
+
1283
+ static async registrationInvitations(invitations: RegistrationInvitation[]): Promise<RegistrationInvitationStruct[]> {
1284
+ const members = await Member.getByIDs(...invitations.map(i => i.memberId));
1285
+ const groups = await Group.getByIDs(...invitations.map(i => i.groupId));
1286
+
1287
+ return invitations.map((invitation) => {
1288
+ const member = members.find(m => m.id === invitation.memberId);
1289
+ if (!member) {
1290
+ throw new SimpleError({
1291
+ code: 'member_not_found',
1292
+ message: 'Member not found',
1293
+ human: $t(`%EO`),
1294
+ });
1295
+ }
1296
+
1297
+ const group = groups.find(g => g.id === invitation.groupId);
1298
+ if (!group) {
1299
+ throw new SimpleError({
1300
+ code: 'group_not_found',
1301
+ message: 'Group not found',
1302
+ human: $t(`%1SM`),
1303
+ });
1304
+ }
1305
+
1306
+ return RegistrationInvitationStruct.create({
1307
+ id: invitation.id,
1308
+ group: InvitationGroupData.create({
1309
+ id: group.id,
1310
+ name: group.settings.name,
1311
+ type: group.type,
1312
+ periodId: group.periodId,
1313
+ }),
1314
+ organizationId: invitation.organizationId,
1315
+ member: InvitationMemberData.create({
1316
+ id: member.id,
1317
+ firstName: member.firstName,
1318
+ lastName: member.lastName,
1319
+ birthDay: member.details.birthDay,
1320
+ }),
1321
+ createdAt: invitation.createdAt,
1322
+ });
1323
+ });
1324
+ }
1223
1325
  }
@@ -0,0 +1,174 @@
1
+ import type { Organization, RegistrationPeriod, Webshop } from '@stamhoofd/models';
2
+ import { BalanceItem, BalanceItemFactory, GroupFactory, MemberFactory, OrganizationFactory, OrganizationRegistrationPeriodFactory, Order, RegistrationFactory, RegistrationPeriodFactory, WebshopFactory } from '@stamhoofd/models';
3
+ import { BalanceItemRelation, BalanceItemRelationType, BalanceItemType, OrderData, ReduceablePrice, TranslatedString } from '@stamhoofd/structures';
4
+ import { TestUtils } from '@stamhoofd/test-utils';
5
+ import { v4 as uuidv4 } from 'uuid';
6
+ import { backfillBalanceItemRelations } from './BalanceItemRelationsBackfiller.js';
7
+
8
+ describe('helper.BalanceItemRelationsBackfiller', () => {
9
+ let period: RegistrationPeriod;
10
+
11
+ beforeAll(async () => {
12
+ period = await new RegistrationPeriodFactory({
13
+ startDate: new Date(2023, 0, 1),
14
+ endDate: new Date(2030, 11, 31),
15
+ }).create();
16
+ });
17
+
18
+ beforeEach(() => {
19
+ TestUtils.setEnvironment('userMode', 'platform');
20
+ });
21
+
22
+ async function createOrder(organization: Organization, webshop: Webshop) {
23
+ const order = new Order();
24
+ order.organizationId = organization.id;
25
+ order.webshopId = webshop.id;
26
+ order.data = OrderData.create({});
27
+ await order.save();
28
+ return order;
29
+ }
30
+
31
+ it('fills member, group and group price relations for registration balance items', async () => {
32
+ const organization = await new OrganizationFactory({ period }).create();
33
+ await new OrganizationRegistrationPeriodFactory({ organization, period }).create();
34
+
35
+ const member = await new MemberFactory({ organization, firstName: 'John', lastName: 'Doe' }).create();
36
+ const group = await new GroupFactory({ organization, period }).create();
37
+ const registration = await new RegistrationFactory({ member, group }).create();
38
+
39
+ const balanceItem = await new BalanceItemFactory({
40
+ organizationId: organization.id,
41
+ memberId: member.id,
42
+ registrationId: registration.id,
43
+ type: BalanceItemType.Registration,
44
+ amount: 1,
45
+ unitPrice: 25_00,
46
+ }).create();
47
+
48
+ expect(balanceItem.relations.size).toBe(0);
49
+
50
+ await backfillBalanceItemRelations();
51
+
52
+ const updated = await BalanceItem.getByID(balanceItem.id);
53
+ expect(updated?.relations.get(BalanceItemRelationType.Member)).toMatchObject({ id: member.id });
54
+ expect(updated?.relations.get(BalanceItemRelationType.Member)?.name.toString()).toBe('John Doe');
55
+ expect(updated?.relations.get(BalanceItemRelationType.Group)).toMatchObject({ id: group.id });
56
+ // Only one price configured, so no group price relation
57
+ expect(updated?.relations.has(BalanceItemRelationType.GroupPrice)).toBe(false);
58
+ });
59
+
60
+ it('fills the group price relation when the group has multiple prices', async () => {
61
+ const organization = await new OrganizationFactory({ period }).create();
62
+ await new OrganizationRegistrationPeriodFactory({ organization, period }).create();
63
+
64
+ const member = await new MemberFactory({ organization }).create();
65
+ const group = await new GroupFactory({ organization, period }).create();
66
+
67
+ // Add a second price so the relation should be filled
68
+ group.settings.prices.push(group.settings.prices[0].clone());
69
+ group.settings.prices[1].id = uuidv4();
70
+ group.settings.prices[1].name = new TranslatedString('Late price');
71
+ group.settings.prices[1].price = ReduceablePrice.create({ price: 30_00 });
72
+ await group.save();
73
+
74
+ const registration = await new RegistrationFactory({ member, group, groupPrice: group.settings.prices[1] }).create();
75
+
76
+ const balanceItem = await new BalanceItemFactory({
77
+ organizationId: organization.id,
78
+ memberId: member.id,
79
+ registrationId: registration.id,
80
+ type: BalanceItemType.Registration,
81
+ amount: 1,
82
+ unitPrice: 30_00,
83
+ }).create();
84
+
85
+ await backfillBalanceItemRelations();
86
+
87
+ const updated = await BalanceItem.getByID(balanceItem.id);
88
+ expect(updated?.relations.get(BalanceItemRelationType.GroupPrice)).toMatchObject({ id: group.settings.prices[1].id });
89
+ });
90
+
91
+ it('copies the registration period dates onto the balance item', async () => {
92
+ const organization = await new OrganizationFactory({ period }).create();
93
+ await new OrganizationRegistrationPeriodFactory({ organization, period }).create();
94
+
95
+ const member = await new MemberFactory({ organization }).create();
96
+ const group = await new GroupFactory({ organization, period }).create();
97
+ const registration = await new RegistrationFactory({ member, group }).create();
98
+
99
+ const startDate = new Date(2024, 7, 1);
100
+ const endDate = new Date(2025, 6, 31);
101
+ registration.startDate = startDate;
102
+ registration.endDate = endDate;
103
+ await registration.save();
104
+
105
+ const balanceItem = await new BalanceItemFactory({
106
+ organizationId: organization.id,
107
+ memberId: member.id,
108
+ registrationId: registration.id,
109
+ type: BalanceItemType.Registration,
110
+ amount: 1,
111
+ unitPrice: 25_00,
112
+ }).create();
113
+
114
+ await backfillBalanceItemRelations();
115
+
116
+ const updated = await BalanceItem.getByID(balanceItem.id);
117
+ expect(updated?.startDate?.getTime()).toBe(startDate.getTime());
118
+ expect(updated?.endDate?.getTime()).toBe(endDate.getTime());
119
+ });
120
+
121
+ it('fills the webshop relation for order balance items', async () => {
122
+ const organization = await new OrganizationFactory({ period }).create();
123
+ const webshop = await new WebshopFactory({ organizationId: organization.id, name: 'My webshop' }).create();
124
+ const order = await createOrder(organization, webshop);
125
+
126
+ const balanceItem = await new BalanceItemFactory({
127
+ organizationId: organization.id,
128
+ orderId: order.id,
129
+ type: BalanceItemType.Order,
130
+ amount: 1,
131
+ unitPrice: 12_00,
132
+ }).create();
133
+
134
+ expect(balanceItem.relations.size).toBe(0);
135
+
136
+ await backfillBalanceItemRelations();
137
+
138
+ const updated = await BalanceItem.getByID(balanceItem.id);
139
+ expect(updated?.relations.get(BalanceItemRelationType.Webshop)).toMatchObject({ id: webshop.id });
140
+ expect(updated?.relations.get(BalanceItemRelationType.Webshop)?.name.toString()).toBe('My webshop');
141
+ });
142
+
143
+ it('does not overwrite existing relations', async () => {
144
+ const organization = await new OrganizationFactory({ period }).create();
145
+ await new OrganizationRegistrationPeriodFactory({ organization, period }).create();
146
+
147
+ const member = await new MemberFactory({ organization }).create();
148
+ const group = await new GroupFactory({ organization, period }).create();
149
+ const registration = await new RegistrationFactory({ member, group }).create();
150
+
151
+ const existingRelations = new Map([
152
+ [
153
+ BalanceItemRelationType.Member,
154
+ BalanceItemRelation.create({ id: member.id, name: new TranslatedString('Custom name') }),
155
+ ],
156
+ ]);
157
+
158
+ const balanceItem = await new BalanceItemFactory({
159
+ organizationId: organization.id,
160
+ memberId: member.id,
161
+ registrationId: registration.id,
162
+ type: BalanceItemType.Registration,
163
+ amount: 1,
164
+ unitPrice: 25_00,
165
+ relations: existingRelations,
166
+ }).create();
167
+
168
+ await backfillBalanceItemRelations();
169
+
170
+ const updated = await BalanceItem.getByID(balanceItem.id);
171
+ expect(updated?.relations.size).toBe(1);
172
+ expect(updated?.relations.get(BalanceItemRelationType.Member)?.name.toString()).toBe('Custom name');
173
+ });
174
+ });