@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
@@ -83,8 +83,7 @@ export class GetMembersEndpoint extends Endpoint<Params, Query, Body, ResponseBo
83
83
  },
84
84
  },
85
85
  };
86
- }
87
- else {
86
+ } else {
88
87
  // Can only access current period
89
88
  scopeFilter = {
90
89
  registrations: {
@@ -95,8 +94,7 @@ export class GetMembersEndpoint extends Endpoint<Params, Query, Body, ResponseBo
95
94
  },
96
95
  };
97
96
  }
98
- }
99
- else {
97
+ } else {
100
98
  // Check which normal membership groups we have access to and filter on those
101
99
  const groups = await Group.getAll(organization.id, organization.periodId, true, [GroupType.Membership, GroupType.WaitingList]);
102
100
  Context.auth.cacheGroups(groups);
@@ -134,6 +132,10 @@ export class GetMembersEndpoint extends Endpoint<Params, Query, Body, ResponseBo
134
132
  SQL.table('members'),
135
133
  );
136
134
 
135
+ if (organization && STAMHOOFD.userMode === 'organization') {
136
+ query.where('organizationId', organization.id);
137
+ }
138
+
137
139
  if (scopeFilter) {
138
140
  query.where(await compileToSQLFilter(scopeFilter, filterCompilers));
139
141
  }
@@ -197,8 +199,7 @@ export class GetMembersEndpoint extends Endpoint<Params, Query, Body, ResponseBo
197
199
  ],
198
200
  };
199
201
  }
200
- }
201
- catch (e) {
202
+ } catch (e) {
202
203
  console.error('Failed to parse phone number', search, e);
203
204
  }
204
205
  }
@@ -216,8 +217,7 @@ export class GetMembersEndpoint extends Endpoint<Params, Query, Body, ResponseBo
216
217
  // e-mail or name based searching
217
218
  if (searchFilter) {
218
219
  // already done
219
- }
220
- else if (search.includes('@')) {
220
+ } else if (search.includes('@')) {
221
221
  const isCompleteAddress = DataValidator.isEmailValid(search);
222
222
 
223
223
  // Member email address contains, or member parent contains
@@ -240,8 +240,7 @@ export class GetMembersEndpoint extends Endpoint<Params, Query, Body, ResponseBo
240
240
  },
241
241
  ],
242
242
  } as any as StamhoofdFilter;
243
- }
244
- else {
243
+ } else {
245
244
  searchFilter = {
246
245
  name: {
247
246
  $contains: search,
@@ -260,8 +259,7 @@ export class GetMembersEndpoint extends Endpoint<Params, Query, Body, ResponseBo
260
259
 
261
260
  try {
262
261
  data = await query.fetch();
263
- }
264
- catch (error) {
262
+ } catch (error) {
265
263
  if (error.message.includes('ER_QUERY_TIMEOUT')) {
266
264
  throw new SimpleError({
267
265
  code: 'timeout',
@@ -11,6 +11,7 @@ import { STExpect, TestUtils } from '@stamhoofd/test-utils';
11
11
  import { Country } from '@stamhoofd/types/Country';
12
12
  import { testServer } from '../../../../tests/helpers/TestServer.js';
13
13
  import { initUitpasApi } from '../../../../tests/init/index.js';
14
+ import { UniqueMemberNumberService } from '../../../services/UniqueMemberNumberService.js';
14
15
  import { PatchOrganizationMembersEndpoint } from './PatchOrganizationMembersEndpoint.js';
15
16
 
16
17
  const baseUrl = `/organization/members`;
@@ -30,6 +31,7 @@ describe('Endpoint.PatchOrganizationMembersEndpoint', () => {
30
31
  afterEach(async () => {
31
32
  // Delete all members (so the duplicate checks work as expected)
32
33
  await Database.delete('DELETE FROM `members`');
34
+ await UniqueMemberNumberService.check();
33
35
  });
34
36
 
35
37
  describe('Duplicate members', () => {
@@ -83,7 +85,7 @@ describe('Endpoint.PatchOrganizationMembersEndpoint', () => {
83
85
  );
84
86
 
85
87
  const user = await new UserFactory({
86
- permissions: Permissions.create({ level: PermissionLevel.None, resources }),
88
+ permissions: Permissions.create({ level: PermissionLevel.None, resources }),
87
89
  organization, // since we are in platform mode, this will only set the permissions for this organization
88
90
  }).create();
89
91
 
@@ -93,7 +95,7 @@ describe('Endpoint.PatchOrganizationMembersEndpoint', () => {
93
95
  birthDay,
94
96
  generateData: true,
95
97
  // should be member of the same organization
96
- organization
98
+ organization,
97
99
  }).create();
98
100
 
99
101
  const token = await Token.createToken(user);
@@ -136,7 +138,7 @@ describe('Endpoint.PatchOrganizationMembersEndpoint', () => {
136
138
  );
137
139
 
138
140
  const user = await new UserFactory({
139
- permissions: Permissions.create({ level: PermissionLevel.None, resources }),
141
+ permissions: Permissions.create({ level: PermissionLevel.None, resources }),
140
142
  organization, // since we are in platform mode, this will only set the permissions for this organization
141
143
  }).create();
142
144
 
@@ -147,7 +149,7 @@ describe('Endpoint.PatchOrganizationMembersEndpoint', () => {
147
149
  birthDay,
148
150
  generateData: true,
149
151
  // should be member of the same organization
150
- organization
152
+ organization,
151
153
  }).create();
152
154
 
153
155
  const memberToPatch = await new MemberFactory({
@@ -156,7 +158,7 @@ describe('Endpoint.PatchOrganizationMembersEndpoint', () => {
156
158
  birthDay,
157
159
  generateData: true,
158
160
  // should be member of the same organization
159
- organization
161
+ organization,
160
162
  }).create();
161
163
 
162
164
  // Register this member
@@ -173,8 +175,8 @@ describe('Endpoint.PatchOrganizationMembersEndpoint', () => {
173
175
  const patch = MemberWithRegistrationsBlob.patch({
174
176
  id: memberToPatch.id,
175
177
  details: MemberDetails.patch({
176
- firstName
177
- })
178
+ firstName,
179
+ }),
178
180
  });
179
181
 
180
182
  arr.addPatch(patch);
@@ -204,7 +206,7 @@ describe('Endpoint.PatchOrganizationMembersEndpoint', () => {
204
206
  );
205
207
 
206
208
  const user = await new UserFactory({
207
- permissions: Permissions.create({ level: PermissionLevel.None, resources }),
209
+ permissions: Permissions.create({ level: PermissionLevel.None, resources }),
208
210
  organization, // since we are in platform mode, this will only set the permissions for this organization
209
211
  }).create();
210
212
 
@@ -216,8 +218,8 @@ describe('Endpoint.PatchOrganizationMembersEndpoint', () => {
216
218
  // member of other organization
217
219
  organization: otherOrganization,
218
220
  details: MemberDetails.create({
219
- email: 'existing@test.be'
220
- })
221
+ email: 'existing@test.be',
222
+ }),
221
223
  }).create();
222
224
 
223
225
  const token = await Token.createToken(user);
@@ -393,8 +395,9 @@ describe('Endpoint.PatchOrganizationMembersEndpoint', () => {
393
395
  expect(member.details.parents[0]).toEqual(existingMember.details.parents[0]);
394
396
  });
395
397
 
396
- test('Creating a member with duplicate member number throws invalid_field', async () => {
397
- TestUtils.setEnvironment('userMode', 'organization');
398
+ test.skip('Creating a member with duplicate member number throws invalid_field', async () => {
399
+ TestUtils.setEnvironment('userMode', 'platform');
400
+ await UniqueMemberNumberService.check();
398
401
 
399
402
  const organization = await new OrganizationFactory({ }).create();
400
403
  const user = await new UserFactory({
@@ -409,6 +412,8 @@ describe('Endpoint.PatchOrganizationMembersEndpoint', () => {
409
412
  generateData: false,
410
413
  }).create();
411
414
  existingMember.details.memberNumber = 'LID-100';
415
+ // should have same organization id as new member with same member number
416
+ existingMember.organizationId = organization.id;
412
417
  await existingMember.save();
413
418
 
414
419
  const token = await Token.createToken(user);
@@ -431,6 +436,47 @@ describe('Endpoint.PatchOrganizationMembersEndpoint', () => {
431
436
  .rejects
432
437
  .toThrow(STExpect.errorWithCode('invalid_field'));
433
438
  });
439
+
440
+ test('Creating a member with duplicate member number does not invalid_field in organization mode', async () => {
441
+ TestUtils.setEnvironment('userMode', 'organization');
442
+ await UniqueMemberNumberService.check();
443
+
444
+ const organization = await new OrganizationFactory({ }).create();
445
+ const user = await new UserFactory({
446
+ permissions: Permissions.create({ level: PermissionLevel.Full }),
447
+ organization,
448
+ }).create();
449
+
450
+ const existingMember = await new MemberFactory({
451
+ firstName: 'Existing',
452
+ lastName: 'Member',
453
+ birthDay,
454
+ generateData: false,
455
+ }).create();
456
+ existingMember.details.memberNumber = 'LID-100';
457
+ // should have same organization id as new member with same member number
458
+ existingMember.organizationId = organization.id;
459
+ await existingMember.save();
460
+
461
+ const token = await Token.createToken(user);
462
+
463
+ const arr: Body = new PatchableArray();
464
+ const put = MemberWithRegistrationsBlob.create({
465
+ details: MemberDetails.create({
466
+ firstName: 'New',
467
+ lastName: 'Member',
468
+ birthDay: new Date('2015-01-01'),
469
+ memberNumber: 'LID-100',
470
+ }),
471
+ });
472
+ arr.addPut(put);
473
+
474
+ const request = Request.buildJson('PATCH', baseUrl, organization.getApiHost(), arr);
475
+ request.headers.authorization = 'Bearer ' + token.accessToken;
476
+
477
+ const response = await testServer.test(endpoint, request);
478
+ expect(response.body.members[0].details.memberNumber).toEqual('LID-100');
479
+ });
434
480
  });
435
481
 
436
482
  describe('Permission checking', () => {
@@ -820,6 +866,143 @@ describe('Endpoint.PatchOrganizationMembersEndpoint', () => {
820
866
  });
821
867
  });
822
868
 
869
+ test('It is not possible to set a different settings id for a given record', async () => {
870
+ const commentsRecord = RecordSettings.create({
871
+ name: TranslatedString.create('Opmerkingen'),
872
+ externalPermissionLevel: PermissionLevel.Read, // this should be ignored since we are an admin
873
+ });
874
+
875
+ const recordCategory = RecordCategory.create({
876
+ name: TranslatedString.create('Medische fiche'),
877
+ records: [
878
+ commentsRecord,
879
+ ],
880
+ });
881
+ const organization = await new OrganizationFactory({
882
+ meta: OrganizationMetaData.create({
883
+ recordsConfiguration: OrganizationRecordsConfiguration.create({
884
+ recordCategories: [recordCategory],
885
+ }),
886
+ }),
887
+ }).create();
888
+
889
+ const user = await new UserFactory({
890
+ permissions: Permissions.create({ level: PermissionLevel.Full }),
891
+ organization, // since we are in platform mode, this will only set the permissions for this organization
892
+ }).create();
893
+
894
+ const member = await new MemberFactory({
895
+ firstName,
896
+ lastName,
897
+ birthDay,
898
+ generateData: false,
899
+ }).create();
900
+
901
+ // Register this member
902
+ await new RegistrationFactory({
903
+ member,
904
+ organization,
905
+ }).create();
906
+
907
+ const token = await Token.createToken(user);
908
+
909
+ const recordAnswers = new PatchMap() as PatchAnswers;
910
+
911
+ recordAnswers.set(commentsRecord.id, RecordTextAnswer.create({
912
+ settings: RecordSettings.create({
913
+ id: 'malicious',
914
+ name: TranslatedString.create('Opmerkingen'),
915
+ }),
916
+ value: 'Some comments',
917
+ }));
918
+
919
+ const arr: Body = new PatchableArray();
920
+ const patch = MemberWithRegistrationsBlob.patch({
921
+ id: member.id,
922
+ details: MemberDetails.patch({
923
+ recordAnswers,
924
+ }),
925
+ });
926
+ arr.addPatch(patch);
927
+
928
+ const request = Request.buildJson('PATCH', baseUrl, organization.getApiHost(), arr);
929
+ request.headers.authorization = 'Bearer ' + token.accessToken;
930
+ await expect(testServer.test(endpoint, request)).rejects.toThrow(
931
+ STExpect.simpleError({
932
+ code: 'invalid_field',
933
+ field: 'changes.0.patch.details.recordAnswers.' + commentsRecord.id + '.settings.id',
934
+ message: 'Expected record settings id to match map key',
935
+ }),
936
+ );
937
+ });
938
+
939
+ test('Cannot change settings via a patch', async () => {
940
+ // We block this because this coould cause mismatches between the record answer value and record answer settings type
941
+
942
+ const commentsRecord = RecordSettings.create({
943
+ name: TranslatedString.create('Opmerkingen'),
944
+ externalPermissionLevel: PermissionLevel.Read, // this should be ignored since we are an admin
945
+ });
946
+
947
+ const recordCategory = RecordCategory.create({
948
+ name: TranslatedString.create('Medische fiche'),
949
+ records: [
950
+ commentsRecord,
951
+ ],
952
+ });
953
+ const organization = await new OrganizationFactory({
954
+ meta: OrganizationMetaData.create({
955
+ recordsConfiguration: OrganizationRecordsConfiguration.create({
956
+ recordCategories: [recordCategory],
957
+ }),
958
+ }),
959
+ }).create();
960
+
961
+ const user = await new UserFactory({
962
+ permissions: Permissions.create({ level: PermissionLevel.Full }),
963
+ organization, // since we are in platform mode, this will only set the permissions for this organization
964
+ }).create();
965
+
966
+ const member = await new MemberFactory({
967
+ firstName,
968
+ lastName,
969
+ birthDay,
970
+ generateData: false,
971
+ }).create();
972
+
973
+ // Register this member
974
+ await new RegistrationFactory({
975
+ member,
976
+ organization,
977
+ }).create();
978
+
979
+ const token = await Token.createToken(user);
980
+
981
+ const recordAnswers = new PatchMap() as PatchAnswers;
982
+
983
+ recordAnswers.set(commentsRecord.id, RecordTextAnswer.patch({
984
+ value: 'Some comments',
985
+ }));
986
+
987
+ const arr: Body = new PatchableArray();
988
+ const patch = MemberWithRegistrationsBlob.patch({
989
+ id: member.id,
990
+ details: MemberDetails.patch({
991
+ recordAnswers,
992
+ }),
993
+ });
994
+ arr.addPatch(patch);
995
+
996
+ const request = Request.buildJson('PATCH', baseUrl, organization.getApiHost(), arr);
997
+ request.headers.authorization = 'Bearer ' + token.accessToken;
998
+ await expect(testServer.test(endpoint, request)).rejects.toThrow(
999
+ STExpect.simpleError({
1000
+ code: 'missing_field',
1001
+ field: 'changes.0.patch.details.recordAnswers.changes.' + commentsRecord.id,
1002
+ }),
1003
+ );
1004
+ });
1005
+
823
1006
  test('An admin with read only record category permission cannot set the records in that category', async () => {
824
1007
  const commentsRecord = RecordSettings.create({
825
1008
  name: TranslatedString.create('Opmerkingen'),
@@ -5,7 +5,7 @@ import type { DecodedRequest, Request } from '@simonbackx/simple-endpoints';
5
5
  import { Endpoint, Response } from '@simonbackx/simple-endpoints';
6
6
  import { SimpleError } from '@simonbackx/simple-errors';
7
7
  import type { MemberWithUsersRegistrationsAndGroups, Registration } from '@stamhoofd/models';
8
- import { AuditLog, BalanceItem, Document, Group, Member, MemberFactory, MemberPlatformMembership, MemberResponsibilityRecord, mergeTwoMembers, Organization, Platform, RateLimiter, RegistrationPeriod, User } from '@stamhoofd/models';
8
+ import { AuditLog, BalanceItem, Document, Group, Member, MemberFactory, MemberPlatformMembership, MemberResponsibilityRecord, Organization, Platform, RateLimiter, RegistrationPeriod, User } from '@stamhoofd/models';
9
9
  import type { MemberResponsibility, MembersBlob } from '@stamhoofd/structures';
10
10
  import { AuditLogReplacement, AuditLogReplacementType, AuditLogSource, AuditLogType, EmergencyContact, GroupType, MemberDetails, MemberWithRegistrationsBlob, Parent, PermissionLevel, PlatformMembershipTypeBehaviour, SetupStepType } from '@stamhoofd/structures';
11
11
  import { Formatter } from '@stamhoofd/utility';
@@ -23,6 +23,7 @@ import { MemberNumberService } from '../../../services/MemberNumberService.js';
23
23
  import { PlatformMembershipService } from '../../../services/PlatformMembershipService.js';
24
24
  import { RegistrationService } from '../../../services/RegistrationService.js';
25
25
  import { shouldCheckIfMemberIsDuplicateForPatch } from './shouldCheckIfMemberIsDuplicate.js';
26
+ import { mergeTwoMembers } from '../../../helpers/MemberMerger.js';
26
27
 
27
28
  type Params = Record<string, never>;
28
29
  type Query = undefined;
@@ -81,8 +82,7 @@ export class PatchOrganizationMembersEndpoint extends Endpoint<Params, Query, Bo
81
82
  if (!await Context.auth.hasSomeAccess(organization.id)) {
82
83
  throw Context.auth.error();
83
84
  }
84
- }
85
- else {
85
+ } else {
86
86
  if (!Context.auth.hasSomePlatformAccess()) {
87
87
  throw Context.auth.error();
88
88
  }
@@ -147,8 +147,7 @@ export class PatchOrganizationMembersEndpoint extends Endpoint<Params, Query, Bo
147
147
 
148
148
  try {
149
149
  await member.save();
150
- }
151
- catch (error) {
150
+ } catch (error) {
152
151
  PatchOrganizationMembersEndpoint.throwDuplicateMemberNumberError(error);
153
152
  }
154
153
  members.push(member);
@@ -217,14 +216,16 @@ export class PatchOrganizationMembersEndpoint extends Endpoint<Params, Query, Bo
217
216
 
218
217
  // Add new
219
218
  members.push(duplicate);
219
+
220
+ await MemberUserSyncer.onChangeMember(duplicate);
221
+ await Document.updateForMember(duplicate);
220
222
  continue;
221
223
  }
222
224
  }
223
225
 
224
226
  try {
225
227
  await member.save();
226
- }
227
- catch (error) {
228
+ } catch (error) {
228
229
  PatchOrganizationMembersEndpoint.throwDuplicateMemberNumberError(error);
229
230
  }
230
231
 
@@ -340,8 +341,7 @@ export class PatchOrganizationMembersEndpoint extends Endpoint<Params, Query, Bo
340
341
  if (registration.periodId !== org.periodId) {
341
342
  return false;
342
343
  }
343
- }
344
- else {
344
+ } else {
345
345
  if (registration.periodId !== platform.periodIdIfPlatform) {
346
346
  return false;
347
347
  }
@@ -590,8 +590,7 @@ export class PatchOrganizationMembersEndpoint extends Endpoint<Params, Query, Bo
590
590
  message: 'Overlapping memberships',
591
591
  human: $t(`%1ND`),
592
592
  });
593
- }
594
- else if (existing) {
593
+ } else if (existing) {
595
594
  await existing.doDelete();
596
595
  }
597
596
 
@@ -601,8 +600,7 @@ export class PatchOrganizationMembersEndpoint extends Endpoint<Params, Query, Bo
601
600
  if (member.details.memberNumber === null) {
602
601
  try {
603
602
  await MemberNumberService.assignMemberNumber(member, membership);
604
- }
605
- catch (error) {
603
+ } catch (error) {
606
604
  console.error(`Failed to assign member number for id ${member.id}: ${error.message}`);
607
605
  // If the assignment of the member number fails the membership is not created but the member is registered
608
606
  continue;
@@ -654,8 +652,7 @@ export class PatchOrganizationMembersEndpoint extends Endpoint<Params, Query, Bo
654
652
  if (Context.auth.hasPlatformFullAccess()) {
655
653
  membership.locked = p.locked ?? membership.locked;
656
654
  await membership.save();
657
- }
658
- else {
655
+ } else {
659
656
  if (p.locked === true) {
660
657
  throw Context.auth.error($t('%BX'));
661
658
  }
@@ -958,8 +955,7 @@ export class PatchOrganizationMembersEndpoint extends Endpoint<Params, Query, Bo
958
955
  private static async checkSecurityCode(member: MemberWithUsersRegistrationsAndGroups, securityCode: string) {
959
956
  try {
960
957
  securityCodeLimiter.track(member.details.name, 1);
961
- }
962
- catch (e) {
958
+ } catch (e) {
963
959
  Email.sendWebmaster({
964
960
  subject: $t(`%E9`),
965
961
  text: $t(`%EA`) + ' ' + member.details.name + ' ' + $t(`%1G`) + ' ' + member.id + ')' + '\n\n' + e.message + '\n\nStamhoofd',
@@ -1038,11 +1034,9 @@ export class PatchOrganizationMembersEndpoint extends Endpoint<Params, Query, Bo
1038
1034
 
1039
1035
  if ((type === 'put' && await member.isSafeToMergeDuplicateWithoutSecurityCode()) || await Context.auth.canAccessMember(member, PermissionLevel.Write)) {
1040
1036
  console.log('checkSecurityCode: without security code: allowed for ' + member.id);
1041
- }
1042
- else if (securityCode) {
1043
- await this.checkSecurityCode(member, securityCode);
1044
- }
1045
- else {
1037
+ } else if (securityCode) {
1038
+ await this.checkSecurityCode(member, securityCode);
1039
+ } else {
1046
1040
  if (type === 'patch') {
1047
1041
  throw Context.auth.memberNotFoundOrNoAccess();
1048
1042
  }
@@ -2,8 +2,9 @@ import { SimpleError } from '@simonbackx/simple-errors';
2
2
  import type { StamhoofdFilter, WrapperFilter } from '@stamhoofd/structures';
3
3
  import { FilterWrapperMarker, PermissionLevel, unwrapFilter } from '@stamhoofd/structures';
4
4
  import { Context } from '../../../../helpers/Context.js';
5
+ import { StamhoofdFilterAccessHelper } from '../../../../helpers/StamhoofdFilterAccessHelper.js';
5
6
 
6
- export async function validateGroupFilter({ filter, permissionLevel, key }: { filter: StamhoofdFilter; permissionLevel: PermissionLevel; key: string | null }) {
7
+ export async function validateGroupFilter({ filter, permissionLevel, key }: { filter: StamhoofdFilter; permissionLevel: PermissionLevel; key: string | null }): Promise<boolean> {
7
8
  // Require presence of a filter
8
9
  const requiredFilter: WrapperFilter = key
9
10
  ? {
@@ -22,39 +23,15 @@ export async function validateGroupFilter({ filter, permissionLevel, key }: { fi
22
23
  return false;
23
24
  }
24
25
 
25
- const groupIds = typeof unwrapped.markerValue === 'string'
26
- ? [unwrapped.markerValue]
27
- : unwrapFilter(unwrapped.markerValue as StamhoofdFilter, {
28
- $in: FilterWrapperMarker,
29
- })?.markerValue;
30
-
31
- if (!Array.isArray(groupIds)) {
26
+ if (unwrapped.markerValue === undefined) {
32
27
  throw new SimpleError({
33
28
  code: 'invalid_field',
34
29
  field: 'filter',
35
- message: 'You must filter on a group of the organization you are trying to access',
36
- human: $t(`%15e`),
30
+ message: 'Filtering on an undefined list of groups is not supported',
37
31
  });
38
32
  }
39
33
 
40
- if (groupIds.length === 0) {
41
- throw new SimpleError({
42
- code: 'invalid_field',
43
- field: 'filter',
44
- message: 'Filtering on an empty list of groups is not supported',
45
- });
46
- }
47
-
48
- for (const groupId of groupIds) {
49
- if (typeof groupId !== 'string') {
50
- throw new SimpleError({
51
- code: 'invalid_field',
52
- field: 'filter',
53
- message: 'Invalid group ID in filter',
54
- });
55
- }
56
- }
57
-
34
+ const groupIds = StamhoofdFilterAccessHelper.getGroupIdsFromFilter(unwrapped.markerValue);
58
35
  const groups = await Context.auth.getGroups(groupIds as string[]);
59
36
 
60
37
  console.log('Fetching members for groups before', groups.map(g => g.settings.name.toString()));
@@ -66,8 +43,7 @@ export async function validateGroupFilter({ filter, permissionLevel, key }: { fi
66
43
  message: 'You do not have access to this group',
67
44
  human: $t(`%15f`, { groupName: group.settings.name }),
68
45
  });
69
- }
70
- else {
46
+ } else {
71
47
  // Return false so we add additional scope filters (only view overlap)
72
48
  return false;
73
49
  }
@@ -1,5 +1,5 @@
1
1
  import type { AutoEncoderPatchType } from '@simonbackx/simple-encoding';
2
- import type { MemberDetails} from '@stamhoofd/structures';
2
+ import type { MemberDetails } from '@stamhoofd/structures';
3
3
  import { MemberWithRegistrationsBlob } from '@stamhoofd/structures';
4
4
 
5
5
  /**
@@ -1,4 +1,4 @@
1
- import type { DecodedRequest, Request} from '@simonbackx/simple-endpoints';
1
+ import type { DecodedRequest, Request } from '@simonbackx/simple-endpoints';
2
2
  import { Endpoint, Response } from '@simonbackx/simple-endpoints';
3
3
  import { SimpleError } from '@simonbackx/simple-errors';
4
4
  import { Organization, RegisterCode } from '@stamhoofd/models';
@@ -1,16 +1,24 @@
1
+ import type { Model } from '@simonbackx/simple-database';
1
2
  import type { Decoder } from '@simonbackx/simple-encoding';
2
- import type { DecodedRequest, Request} from '@simonbackx/simple-endpoints';
3
+ import type { DecodedRequest, Request } from '@simonbackx/simple-endpoints';
3
4
  import { Endpoint, Response } from '@simonbackx/simple-endpoints';
4
5
  import { SimpleError } from '@simonbackx/simple-errors';
6
+ import type { EmailInterfaceBase } from '@stamhoofd/email';
7
+ import { Email } from '@stamhoofd/email';
5
8
  import { AuditLog, EmailVerificationCode, Organization, OrganizationRegistrationPeriod, RegistrationPeriod, User } from '@stamhoofd/models';
6
- import { AuditLogSource, AuditLogType, CreateOrganization, PermissionLevel, Permissions, RegistrationPeriodSettings, SignupResponse, UserPermissions } from '@stamhoofd/structures';
9
+ import { CreateOrganizationResponse } from '@stamhoofd/structures';
10
+ import { AuditLogSource, AuditLogType, CreateOrganization, PermissionLevel, Permissions, SignupResponse, UserPermissions } from '@stamhoofd/structures';
7
11
  import { Formatter } from '@stamhoofd/utility';
12
+ import { v4 as uuidv4 } from 'uuid';
8
13
  import { AuditLogService } from '../../../services/AuditLogService.js';
14
+ import { ReferralService } from '../../../services/ReferralService.js';
15
+ import { AuthenticatedStructures } from '../../../helpers/AuthenticatedStructures.js';
16
+ import { Context } from '../../../helpers/Context.js';
9
17
 
10
18
  type Params = Record<string, never>;
11
19
  type Query = undefined;
12
20
  type Body = CreateOrganization;
13
- type ResponseBody = SignupResponse;
21
+ type ResponseBody = CreateOrganizationResponse;
14
22
 
15
23
  export class CreateOrganizationEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
16
24
  bodyDecoder = CreateOrganization as Decoder<CreateOrganization>;
@@ -77,9 +85,8 @@ export class CreateOrganizationEndpoint extends Endpoint<Params, Query, Body, Re
77
85
  }
78
86
 
79
87
  // First create the organization
80
- // TODO: add some duplicate validation
81
88
  const organization = new Organization();
82
- organization.id = request.body.organization.id;
89
+ organization.id = uuidv4();
83
90
  organization.name = request.body.organization.name;
84
91
 
85
92
  organization.uri = uri;
@@ -87,6 +94,16 @@ export class CreateOrganizationEndpoint extends Endpoint<Params, Query, Body, Re
87
94
  organization.address = request.body.organization.address;
88
95
  organization.privateMeta.acquisitionTypes = request.body.organization.privateMeta?.acquisitionTypes ?? [];
89
96
 
97
+ // Delay save until after organization is saved, but do validations before the organization is saved
98
+ let registerCodeModels: Model[] = [];
99
+ let delayEmails: EmailInterfaceBase[] = [];
100
+
101
+ if (request.body.registerCode) {
102
+ const applied = await ReferralService.markUsed(organization, request.body.registerCode);
103
+ registerCodeModels = applied.models;
104
+ delayEmails = applied.emails;
105
+ }
106
+
90
107
  const period = await AuditLogService.setContext({ source: AuditLogSource.System }, async () => {
91
108
  const period = new RegistrationPeriod();
92
109
  period.configureForNewOrganization();
@@ -98,8 +115,7 @@ export class CreateOrganizationEndpoint extends Endpoint<Params, Query, Body, Re
98
115
 
99
116
  try {
100
117
  await organization.save();
101
- }
102
- catch (e) {
118
+ } catch (e) {
103
119
  console.error(e);
104
120
  throw new SimpleError({
105
121
  code: 'creating_organization',
@@ -136,14 +152,30 @@ export class CreateOrganizationEndpoint extends Endpoint<Params, Query, Body, Re
136
152
  user.permissions.organizationPermissions.set(organization.id, Permissions.create({ level: PermissionLevel.Full }));
137
153
  await user.save();
138
154
 
155
+ for (const model of registerCodeModels) {
156
+ await model.save();
157
+ }
158
+
139
159
  // Correctly assign creation
140
160
  await AuditLog.update().where('type', AuditLogType.OrganizationAdded).where('objectId', organization.id).set('userId', user.id).set('source', AuditLogSource.User).update();
141
161
 
142
162
  const code = await EmailVerificationCode.createFor(user, user.email);
143
163
  code.send(user, organization, request.i18n).catch(console.error);
144
164
 
145
- return new Response(SignupResponse.create({
165
+ for (const email of delayEmails) {
166
+ Email.send({
167
+ from: Email.getQuickFromEmail(organization.address.country),
168
+ ...email,
169
+ });
170
+ }
171
+
172
+ // Prepare the response as if we were authenticated
173
+ await Context.setManualOrganizationScope(organization);
174
+ await Context.insecurelyAuthenticateAs(user);
175
+
176
+ return new Response(CreateOrganizationResponse.create({
146
177
  token: code.token,
178
+ organization: await AuthenticatedStructures.organization(organization),
147
179
  }));
148
180
  }
149
181
  }