@stamhoofd/backend 2.121.0 → 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 (429) hide show
  1. package/output-2.txt +1 -0
  2. package/output.txt +1 -0
  3. package/package.json +69 -52
  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 +24 -24
  8. package/src/audit-logs/init.ts +8 -2
  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 +5 -0
  19. package/src/crons/invoices.ts +32 -38
  20. package/src/crons/members-fees.ts +50 -0
  21. package/src/crons/mollie-chargebacks.ts +14 -15
  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 +7 -64
  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 +4 -6
  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 +1 -1
  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 +11 -11
  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 +35 -34
  122. package/src/endpoints/global/registration-invitations/GetRegistrationInvitationsCountEndpoint.ts +1 -1
  123. package/src/endpoints/global/registration-invitations/GetRegistrationInvitationsEndpoint.test.ts +62 -63
  124. package/src/endpoints/global/registration-invitations/GetRegistrationInvitationsEndpoint.ts +6 -9
  125. package/src/endpoints/global/registration-invitations/PatchRegistrationInvitationsEndpoint.test.ts +51 -52
  126. package/src/endpoints/global/registration-invitations/PatchRegistrationInvitationsEndpoint.ts +6 -7
  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 +7 -9
  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/organization/dashboard/billing/DeactivatePackageEndpoint.ts +1 -5
  142. package/src/endpoints/organization/dashboard/billing/DeleteOrganizationMandateEndpoint.ts +11 -11
  143. package/src/endpoints/organization/dashboard/billing/GetOrganizationDetailedPayableBalanceCollectionEndpoint.ts +2 -2
  144. package/src/endpoints/organization/dashboard/billing/GetOrganizationDetailedPayableBalanceEndpoint.ts +8 -8
  145. package/src/endpoints/organization/dashboard/billing/GetOrganizationMandatesEndpoint.ts +9 -9
  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 +4 -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 +60 -57
  152. package/src/endpoints/organization/dashboard/billing/PatchOrganizationMandatesEndpoint.ts +20 -20
  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 +10 -6
  168. package/src/endpoints/organization/dashboard/invoices/PatchInvoicesEndpoint.ts +11 -2
  169. package/src/endpoints/organization/dashboard/mollie/CheckMollieEndpoint.ts +4 -5
  170. package/src/endpoints/organization/dashboard/mollie/ConnectMollieEndpoint.ts +5 -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 +50 -12
  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 +21 -14
  185. package/src/endpoints/organization/dashboard/receivable-balances/ChargeReceivableBalancesEndpoint.ts +35 -28
  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 +19 -18
  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 +22 -7
  215. package/src/endpoints/organization/dashboard/webshops/PatchWebshopOrdersEndpoint.ts +20 -27
  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 +2 -2
  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 +16 -33
  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 +50 -63
  246. package/src/helpers/AuthenticatedStructures.ts +69 -55
  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 +2 -2
  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 +105 -110
  268. package/src/helpers/RecordAnswerHelper.ts +7 -7
  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 +14 -36
  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 +7 -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/{seeds → migrations}/1750090030-records-configuration.ts +33 -32
  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 +70 -26
  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/1779121240-invoice-email-template.sql +6 -0
  300. package/src/migrations/1779986893-default-payment-failed-email-templates.sql +4 -0
  301. package/src/migrations/1780578166-convert-legacy-billing.ts +13 -0
  302. package/src/{seeds/1755876819-remove-duplicate-members.ts → migrations/1780665426-remove-duplicate-members.ts} +23 -4
  303. package/src/migrations/1780933598-document-groups.ts +88 -0
  304. package/src/migrations/1781692342-balance-item-relations.ts +13 -0
  305. package/src/migrations/1781857866-platform-admins.ts +103 -0
  306. package/src/migrations/1781857867-rm-old-platform-admins.ts +25 -0
  307. package/src/seeds/0000000001-throw.ts +5 -0
  308. package/src/seeds/1760702452-update-balance-price-paid.ts +19 -0
  309. package/src/seeds/1760702453-update-balance-invoiced.ts +19 -0
  310. package/src/seeds/1760702454-update-cached-outstanding-balance-from-items.ts +51 -14
  311. package/src/seeds/1773754928-force-save-members.ts +41 -8
  312. package/src/seeds/1780665427-schedule-stock-updates.ts +45 -0
  313. package/src/seeds/1780665428-group-update-occupancy.ts +31 -0
  314. package/src/seeds/1780915001-fill-payment-customer.ts +70 -0
  315. package/src/seeds/1780915002-update-orders.ts +39 -0
  316. package/src/seeds/1782047708-organization-company-address.ts +44 -0
  317. package/src/seeds-temporary/1769088653-uitpas-status.ts +1 -2
  318. package/src/seeds-temporary/1780416000-migrate-audit-log-legacy-enums.test.ts +69 -0
  319. package/src/seeds-temporary/1780416000-migrate-audit-log-legacy-enums.ts +85 -0
  320. package/src/services/AuditLogService.test.ts +51 -0
  321. package/src/services/AuditLogService.ts +45 -0
  322. package/src/services/BalanceItemService.ts +141 -44
  323. package/src/services/CpuService.ts +3 -6
  324. package/src/services/DatabaseCollationService.test.ts +1 -2
  325. package/src/services/DocumentService.ts +1 -2
  326. package/src/services/EventNotificationService.ts +1 -1
  327. package/src/services/FileSignService.ts +2 -4
  328. package/src/services/InvoicePdfService.ts +286 -0
  329. package/src/services/InvoiceService.ts +204 -251
  330. package/src/services/InvoiceXMLService.ts +424 -0
  331. package/src/services/MemberNumberService.ts +6 -11
  332. package/src/services/MemberRecordStore.ts +8 -10
  333. package/src/services/MollieService.ts +114 -121
  334. package/src/services/PaymentMandateService.ts +45 -45
  335. package/src/services/PaymentReallocationService.test.ts +1 -1
  336. package/src/services/PaymentReallocationService.ts +7 -9
  337. package/src/services/PaymentService.ts +444 -308
  338. package/src/services/PlatformMembershipService.ts +4 -8
  339. package/src/services/ReferralService.ts +223 -0
  340. package/src/services/RegistrationService.ts +63 -53
  341. package/src/services/SSOService.test.ts +119 -0
  342. package/src/services/SSOService.ts +56 -49
  343. package/src/services/STPackageService.ts +97 -61
  344. package/src/services/UniqueMemberNumberService.ts +69 -0
  345. package/src/services/UniqueUserService.ts +3 -6
  346. package/src/services/VATService.ts +85 -0
  347. package/src/services/uitpas/PassholderEndpoints.ts +3 -6
  348. package/src/services/uitpas/UitpasService.ts +6 -10
  349. package/src/services/uitpas/checkUitpasNumbers.ts +2 -4
  350. package/src/services/uitpas/getSocialTariffForUitpasNumbers.ts +3 -6
  351. package/src/services/uitpas/searchUitpasEvents.ts +1 -2
  352. package/src/sql-filters/audit-logs.ts +1 -1
  353. package/src/sql-filters/balance-item-payments.ts +1 -1
  354. package/src/sql-filters/balance-items.ts +1 -1
  355. package/src/sql-filters/base-registration-filter-compilers.ts +5 -33
  356. package/src/sql-filters/document-templates.ts +1 -1
  357. package/src/sql-filters/documents.ts +1 -1
  358. package/src/sql-filters/email-recipients.ts +1 -1
  359. package/src/sql-filters/emails.ts +1 -1
  360. package/src/sql-filters/event-notifications.ts +1 -1
  361. package/src/sql-filters/events.ts +6 -1
  362. package/src/sql-filters/groups.ts +5 -0
  363. package/src/sql-filters/invoiced-balance-items.ts +1 -1
  364. package/src/sql-filters/invoices.ts +1 -1
  365. package/src/sql-filters/member-responsibility-records.ts +1 -1
  366. package/src/sql-filters/members.ts +1 -1
  367. package/src/sql-filters/orders.ts +4 -4
  368. package/src/sql-filters/organization-registration-periods.ts +1 -1
  369. package/src/sql-filters/organizations.ts +1 -1
  370. package/src/sql-filters/platform-memberships.ts +3 -3
  371. package/src/sql-filters/receivable-balances.ts +1 -1
  372. package/src/sql-filters/registration-periods.ts +1 -1
  373. package/src/sql-filters/registrations.ts +1 -1
  374. package/src/sql-filters/tickets.ts +1 -1
  375. package/src/sql-filters/users.ts +1 -1
  376. package/src/sql-sorters/groups.ts +23 -0
  377. package/src/sql-sorters/platform-memberships.ts +16 -16
  378. package/src/sql-sorters/registration-invitations.ts +2 -2
  379. package/tests/actions/patchOrganizationMember.ts +1 -1
  380. package/tests/actions/patchUserMember.ts +1 -1
  381. package/tests/assertions/assertBalances.ts +6 -12
  382. package/tests/e2e/api-rate-limits.test.ts +12 -24
  383. package/tests/e2e/bundle-discounts.test.ts +4 -1
  384. package/tests/e2e/charge-members.test.ts +13 -11
  385. package/tests/e2e/documents.test.ts +4 -1
  386. package/tests/e2e/register.test.ts +7 -9
  387. package/tests/e2e/stock.test.ts +4 -3
  388. package/tests/e2e/tickets.test.ts +1 -1
  389. package/tests/helpers/MollieMocker.ts +462 -0
  390. package/tests/helpers/PayconiqMocker.ts +138 -45
  391. package/tests/helpers/StripeMocker.ts +9 -6
  392. package/tests/helpers/index.ts +4 -0
  393. package/tests/init/initAdmin.ts +2 -2
  394. package/tests/init/initBundleDiscount.ts +1 -1
  395. package/tests/init/initMembershipOrganization.ts +13 -0
  396. package/tests/init/initPermissionRole.ts +3 -4
  397. package/tests/vitest.global.setup.ts +1 -1
  398. package/vitest.config.js +6 -0
  399. package/.nvmrc +0 -1
  400. package/src/helpers/GroupBuilder.ts +0 -418
  401. package/src/seeds/0000000000-example.ts +0 -11
  402. package/src/seeds/1722256498-group-update-occupancy.ts +0 -52
  403. package/src/seeds/1726572303-schedule-stock-updates.ts +0 -52
  404. package/src/seeds/1726847064-setup-steps.ts +0 -16
  405. package/src/seeds/1728928974-update-cached-outstanding-balance-from-items.ts +0 -31
  406. package/src/seeds/1729253172-update-orders.ts +0 -21
  407. package/src/seeds/1733319079-fill-paying-organization-ids.ts +0 -68
  408. package/src/seeds/1733748412-update-deleted-order-numbers.ts +0 -109
  409. package/src/seeds/1733994455-balance-item-status-open.ts +0 -30
  410. package/src/seeds/1740046783-update-membership.ts +0 -17
  411. package/src/seeds/1741008870-fix-auditlog-description.ts +0 -50
  412. package/src/seeds/1741011468-fix-auditlog-description-uft16.ts +0 -88
  413. package/src/seeds/1741268179-fix-group-dates-of-events.ts +0 -32
  414. package/src/seeds/1751445358-upload-email-attachments.ts +0 -106
  415. package/src/seeds/1755532883-update-email-sender-ids.ts +0 -47
  416. package/src/seeds/1755790070-fill-email-recipient-errors.ts +0 -96
  417. package/src/seeds/1756115432-remove-old-drafts.ts +0 -16
  418. package/src/seeds/1756115433-fill-email-recipient-organization-id.ts +0 -30
  419. package/src/seeds/1756303697-update-email-counts.ts +0 -76
  420. package/src/seeds/1761665607-sync-member-users.ts +0 -59
  421. package/src/seeds/1765896674-document-update-year.ts +0 -66
  422. /package/src/{seeds → migrations}/0000000001-development-user.ts +0 -0
  423. /package/src/{seeds → migrations}/0000000002-clear-stamhoofd-email-templates.ts +0 -0
  424. /package/src/{seeds → migrations}/0000000003-default-email-templates.ts +0 -0
  425. /package/src/{seeds → migrations}/1752848561-groups-registration-periods.test.ts +0 -0
  426. /package/src/{seeds → migrations}/1766150402-document-published-at.test.ts +0 -0
  427. /package/src/{seeds → migrations}/1766150402-document-published-at.ts +0 -0
  428. /package/src/{seeds → migrations}/1779121239-default-invoice-email-template.sql +0 -0
  429. /package/src/{seeds → migrations}/data/default-email-templates.sql +0 -0
@@ -1,9 +1,9 @@
1
- import type { AutoEncoderPatchType, Decoder, PatchableArrayAutoEncoder} from '@simonbackx/simple-encoding';
1
+ import type { AutoEncoderPatchType, Decoder, PatchableArrayAutoEncoder } from '@simonbackx/simple-encoding';
2
2
  import { PatchableArrayDecoder, patchObject, StringDecoder } from '@simonbackx/simple-encoding';
3
- import type { DecodedRequest, Request} from '@simonbackx/simple-endpoints';
3
+ import type { DecodedRequest, Request } from '@simonbackx/simple-endpoints';
4
4
  import { Endpoint, Response } from '@simonbackx/simple-endpoints';
5
5
  import { Event, Group, OrganizationRegistrationPeriod, Platform, RegistrationPeriod, Webshop } from '@stamhoofd/models';
6
- import { AuditLogSource, Event as EventStruct, Group as GroupStruct, GroupType, NamedObject, PermissionLevel } from '@stamhoofd/structures';
6
+ import { AuditLogSource, Event as EventStruct, Group as GroupStruct, GroupType, NamedObject, OrganizationEventType, PermissionLevel, PlatformEventType } from '@stamhoofd/structures';
7
7
 
8
8
  import { SimpleError } from '@simonbackx/simple-errors';
9
9
  import { SQL, SQLWhereSign } from '@stamhoofd/sql';
@@ -67,8 +67,7 @@ export class PatchEventsEndpoint extends Endpoint<Params, Query, Body, ResponseB
67
67
  period,
68
68
  { allowedIds: [putGroup.id] },
69
69
  );
70
- }
71
- else {
70
+ } else {
72
71
  if (group.type !== GroupType.EventRegistration) {
73
72
  throw new SimpleError({
74
73
  code: 'invalid_group',
@@ -88,8 +87,7 @@ export class PatchEventsEndpoint extends Endpoint<Params, Query, Body, ResponseB
88
87
  if (!await Context.auth.hasSomeAccess(organization.id)) {
89
88
  throw Context.auth.error();
90
89
  }
91
- }
92
- else {
90
+ } else {
93
91
  if (!Context.auth.hasSomePlatformAccess()) {
94
92
  throw Context.auth.error();
95
93
  }
@@ -138,13 +136,16 @@ export class PatchEventsEndpoint extends Endpoint<Params, Query, Body, ResponseB
138
136
  event.endDate = put.endDate;
139
137
  event.typeId = put.typeId;
140
138
 
139
+ event.meta = put.meta;
141
140
  event.meta.organizationCache = eventOrganization ? NamedObject.create({ id: eventOrganization.id, name: eventOrganization.name }) : null;
141
+
142
+ PatchEventsEndpoint.validateOrCorrectEventMeta(event);
143
+
142
144
  await PatchEventsEndpoint.checkEventLimits(event);
143
145
 
144
146
  if (put.group) {
145
147
  await this.saveEventAndLinkExistingGroup(event, put.group);
146
- }
147
- else {
148
+ } else {
148
149
  await event.save();
149
150
  }
150
151
 
@@ -181,40 +182,12 @@ export class PatchEventsEndpoint extends Endpoint<Params, Query, Body, ResponseB
181
182
  event.organizationId = patch.organizationId;
182
183
  }
183
184
 
184
- if (event.organizationId === null && event.meta.groups !== null) {
185
- event.meta.groups = null;
186
- console.error('Removed groups because organizationId is null for event', event.id);
187
- }
188
-
189
- if (event.meta.groups && event.meta.groups.length === 0) {
190
- throw new SimpleError({
191
- code: 'invalid_field',
192
- message: 'Empty groups',
193
- human: $t(`%DV`),
194
- });
195
- }
196
-
197
- if (event.meta.defaultAgeGroupIds && event.meta.defaultAgeGroupIds.length === 0) {
198
- throw new SimpleError({
199
- code: 'invalid_field',
200
- message: 'Empty default age groups',
201
- human: $t(`%DW`),
202
- });
203
- }
204
-
205
- if (event.meta.organizationTagIds && event.meta.organizationTagIds.length === 0) {
206
- throw new SimpleError({
207
- code: 'invalid_field',
208
- message: 'Empty organization tag ids',
209
- human: $t(`%DX`),
210
- });
211
- }
185
+ PatchEventsEndpoint.validateOrCorrectEventMeta(event);
212
186
 
213
187
  const eventOrganization = await Context.auth.checkEventAccess(event);
214
188
  if (eventOrganization) {
215
189
  event.meta.organizationCache = NamedObject.create({ id: eventOrganization.id, name: eventOrganization.name });
216
- }
217
- else {
190
+ } else {
218
191
  event.meta.organizationCache = null;
219
192
  }
220
193
 
@@ -237,8 +210,7 @@ export class PatchEventsEndpoint extends Endpoint<Params, Query, Body, ResponseB
237
210
  await PatchOrganizationRegistrationPeriodsEndpoint.deleteGroup(event.groupId);
238
211
  event.groupId = null;
239
212
  }
240
- }
241
- else if (patch.group.isPatch()) {
213
+ } else if (patch.group.isPatch()) {
242
214
  if (!event.groupId) {
243
215
  throw new SimpleError({
244
216
  code: 'invalid_field',
@@ -251,13 +223,11 @@ export class PatchEventsEndpoint extends Endpoint<Params, Query, Body, ResponseB
251
223
 
252
224
  const period = await RegistrationPeriod.getByDate(event.startDate, event.organizationId);
253
225
  group = await PatchOrganizationRegistrationPeriodsEndpoint.patchGroup(patch.group, period, { isPatchingEvent: true });
254
- }
255
- else {
226
+ } else {
256
227
  if (event.groupId === patch.group.id) {
257
228
  // ignore: bad practice: puts are not allowed like this
258
229
  // risk of deleting data
259
- }
260
- else {
230
+ } else {
261
231
  if (event.groupId) {
262
232
  // need to delete old group first
263
233
  await PatchOrganizationRegistrationPeriodsEndpoint.deleteGroup(event.groupId);
@@ -267,8 +237,7 @@ export class PatchEventsEndpoint extends Endpoint<Params, Query, Body, ResponseB
267
237
  event.groupId = group.id;
268
238
  }
269
239
  }
270
- }
271
- else {
240
+ } else {
272
241
  if (patch.startDate || patch.endDate) {
273
242
  // Correct period id if needed
274
243
  const period = await RegistrationPeriod.getByDate(event.startDate, event.organizationId);
@@ -292,8 +261,7 @@ export class PatchEventsEndpoint extends Endpoint<Params, Query, Body, ResponseB
292
261
  if (group || patch.organizationId !== undefined) {
293
262
  if (event.organizationId === null) {
294
263
  // No validation required
295
- }
296
- else {
264
+ } else {
297
265
  // Validate organizationId of group
298
266
  if (event.groupId) {
299
267
  group = group ?? (await Group.getByID(event.groupId) ?? null);
@@ -312,8 +280,7 @@ export class PatchEventsEndpoint extends Endpoint<Params, Query, Body, ResponseB
312
280
  if (patch.webshopId !== undefined) {
313
281
  if (patch.webshopId === null) {
314
282
  event.webshopId = null;
315
- }
316
- else {
283
+ } else {
317
284
  const webshop = await Webshop.getByID(patch.webshopId);
318
285
  if (!webshop || (event.organizationId && webshop.organizationId !== event.organizationId)) {
319
286
  throw new SimpleError({
@@ -366,13 +333,47 @@ export class PatchEventsEndpoint extends Endpoint<Params, Query, Body, ResponseB
366
333
  );
367
334
  }
368
335
 
369
- static async validateEventType(typeId: string) {
370
- return (await this.getEventType(typeId)).id;
336
+ static validateOrCorrectEventMeta(event: Event) {
337
+ if (event.organizationId === null && event.meta.groups !== null) {
338
+ event.meta.groups = null;
339
+ console.error('Removed groups because organizationId is null for event', event.id);
340
+ }
341
+
342
+ if (event.meta.groups && event.meta.groups.length === 0) {
343
+ throw new SimpleError({
344
+ code: 'invalid_field',
345
+ message: 'Empty groups',
346
+ human: $t(`%DV`),
347
+ });
348
+ }
349
+
350
+ if (event.meta.defaultAgeGroupIds && event.meta.defaultAgeGroupIds.length === 0) {
351
+ throw new SimpleError({
352
+ code: 'invalid_field',
353
+ message: 'Empty default age groups',
354
+ human: $t(`%DW`),
355
+ });
356
+ }
357
+
358
+ if (event.meta.organizationTagIds && event.meta.organizationTagIds.length === 0) {
359
+ throw new SimpleError({
360
+ code: 'invalid_field',
361
+ message: 'Empty organization tag ids',
362
+ human: $t(`%DX`),
363
+ });
364
+ }
371
365
  }
372
366
 
373
- static async getEventType(typeId: string) {
374
- const platform = await Platform.getSharedStruct();
375
- const type = platform.config.eventTypes.find(t => t.id == typeId);
367
+ static async getEventType(typeId: string): Promise<OrganizationEventType | PlatformEventType> {
368
+ let type: OrganizationEventType | PlatformEventType | undefined = undefined;
369
+
370
+ if (STAMHOOFD.userMode === 'platform') {
371
+ const platform = await Platform.getSharedStruct();
372
+ type = platform.config.eventTypes.find(t => t.id === typeId);
373
+ } else if (typeId === OrganizationEventType.DEFAULT_ID) {
374
+ type = OrganizationEventType.createDefault();
375
+ }
376
+
376
377
  if (!type) {
377
378
  throw new SimpleError({
378
379
  code: 'invalid_field',
@@ -387,21 +388,6 @@ export class PatchEventsEndpoint extends Endpoint<Params, Query, Body, ResponseB
387
388
  static async checkEventLimits(event: Event) {
388
389
  const type = await this.getEventType(event.typeId);
389
390
 
390
- if (type.isLocationRequired) {
391
- const address = event.meta.location?.address;
392
-
393
- if (!address) {
394
- throw new SimpleError({
395
- code: 'invalid_field',
396
- message: 'Empty number',
397
- human: $t(`%vQ`),
398
- field: 'event_required',
399
- });
400
- }
401
-
402
- address.throwIfIncomplete();
403
- }
404
-
405
391
  if (event.name.length < 2) {
406
392
  throw new SimpleError({
407
393
  code: 'invalid_field',
@@ -420,69 +406,85 @@ export class PatchEventsEndpoint extends Endpoint<Params, Query, Body, ResponseB
420
406
  });
421
407
  }
422
408
 
423
- if (type.maximumDays !== null || type.minimumDays !== null) {
424
- const start = Formatter.luxon(event.startDate).startOf('day');
425
- const end = Formatter.luxon(event.endDate).startOf('day');
426
-
427
- const days = end.diff(start, 'days').days + 1;
409
+ if (type instanceof PlatformEventType) {
410
+ if (type.isLocationRequired) {
411
+ const address = event.meta.location?.address;
428
412
 
429
- console.log('Detected days:', days);
413
+ if (!address) {
414
+ throw new SimpleError({
415
+ code: 'invalid_field',
416
+ message: 'Empty number',
417
+ human: $t(`%vQ`),
418
+ field: 'event_required',
419
+ });
420
+ }
430
421
 
431
- if (type.minimumDays !== null && days < type.minimumDays) {
432
- throw new SimpleError({
433
- code: 'minimum_days',
434
- message: 'An event with this type has a minimum of ' + type.minimumDays + ' days',
435
- human: $t(`%14D`, {
436
- name: type.name,
437
- days: Formatter.pluralText(type.minimumDays, $t(`%1N7`), $t(`%1N6`)),
438
- }),
439
- field: 'startDate',
440
- });
422
+ address.throwIfIncomplete();
441
423
  }
442
424
 
443
- if (type.maximumDays !== null && days > type.maximumDays) {
444
- throw new SimpleError({
445
- code: 'maximum_days',
446
- message: 'An event with this type has a maximum of ' + type.maximumDays + ' days',
447
- human: $t(`%14E`, {
448
- name: type.name,
449
- days: Formatter.pluralText(type.maximumDays, $t(`%1N7`), $t(`%1N6`)),
450
- }),
451
- field: 'startDate',
452
- });
453
- }
454
- }
425
+ if (type.maximumDays !== null || type.minimumDays !== null) {
426
+ const start = Formatter.luxon(event.startDate).startOf('day');
427
+ const end = Formatter.luxon(event.endDate).startOf('day');
428
+
429
+ const days = end.diff(start, 'days').days + 1;
430
+
431
+ console.log('Detected days:', days);
455
432
 
456
- if (type.maximum && (!event.existsInDatabase || ('typeId' in (event.getChangedDatabaseProperties()).fields))) {
457
- const currentPeriod = await RegistrationPeriod.getByDate(event.startDate, event.organizationId);
458
- console.log('event.startDate', event.startDate);
459
- if (currentPeriod) {
460
- const count = await SQL.select().from(
461
- SQL.table(Event.table),
462
- )
463
- .where(SQL.column('organizationId'), event.organizationId)
464
- .where(SQL.column('typeId'), event.typeId)
465
- .where(SQL.column('id'), SQLWhereSign.NotEqual, event.id)
466
- .where(SQL.column('startDate'), SQLWhereSign.GreaterEqual, currentPeriod.startDate)
467
- .where(SQL.column('endDate'), SQLWhereSign.LessEqual, currentPeriod.endDate)
468
- .count();
469
-
470
- if (count >= type.maximum) {
433
+ if (type.minimumDays !== null && days < type.minimumDays) {
471
434
  throw new SimpleError({
472
- code: 'type_maximum_reached',
473
- message: 'Maximum number of events with this type reached',
474
- human: $t(`%Dd`) + ' ' + type.name + ' ' + $t(`%De`) + type.maximum + ')',
475
- field: 'typeId',
435
+ code: 'minimum_days',
436
+ message: 'An event with this type has a minimum of ' + type.minimumDays + ' days',
437
+ human: $t(`%14D`, {
438
+ name: type.name,
439
+ days: Formatter.pluralText(type.minimumDays, $t(`%1N7`), $t(`%1N6`)),
440
+ }),
441
+ field: 'startDate',
442
+ });
443
+ }
444
+
445
+ if (type.maximumDays !== null && days > type.maximumDays) {
446
+ throw new SimpleError({
447
+ code: 'maximum_days',
448
+ message: 'An event with this type has a maximum of ' + type.maximumDays + ' days',
449
+ human: $t(`%14E`, {
450
+ name: type.name,
451
+ days: Formatter.pluralText(type.maximumDays, $t(`%1N7`), $t(`%1N6`)),
452
+ }),
453
+ field: 'startDate',
476
454
  });
477
455
  }
478
456
  }
479
- else {
480
- throw new SimpleError({
481
- code: 'invalid_period',
482
- message: 'No period found for this start date',
483
- human: $t(`%Df`),
484
- field: 'startDate',
485
- });
457
+
458
+ if (type.maximum && (!event.existsInDatabase || ('typeId' in (event.getChangedDatabaseProperties()).fields))) {
459
+ const currentPeriod = await RegistrationPeriod.getByDate(event.startDate, event.organizationId);
460
+ console.log('event.startDate', event.startDate);
461
+ if (currentPeriod) {
462
+ const count = await SQL.select().from(
463
+ SQL.table(Event.table),
464
+ )
465
+ .where(SQL.column('organizationId'), event.organizationId)
466
+ .where(SQL.column('typeId'), event.typeId)
467
+ .where(SQL.column('id'), SQLWhereSign.NotEqual, event.id)
468
+ .where(SQL.column('startDate'), SQLWhereSign.GreaterEqual, currentPeriod.startDate)
469
+ .where(SQL.column('endDate'), SQLWhereSign.LessEqual, currentPeriod.endDate)
470
+ .count();
471
+
472
+ if (count >= type.maximum) {
473
+ throw new SimpleError({
474
+ code: 'type_maximum_reached',
475
+ message: 'Maximum number of events with this type reached',
476
+ human: $t(`%Dd`) + ' ' + type.name + ' ' + $t(`%De`) + type.maximum + ')',
477
+ field: 'typeId',
478
+ });
479
+ }
480
+ } else {
481
+ throw new SimpleError({
482
+ code: 'invalid_period',
483
+ message: 'No period found for this start date',
484
+ human: $t(`%Df`),
485
+ field: 'startDate',
486
+ });
487
+ }
486
488
  }
487
489
  }
488
490
  }
@@ -553,8 +555,7 @@ export class PatchEventsEndpoint extends Endpoint<Params, Query, Body, ResponseB
553
555
  await AuditLogService.setContext({ source: AuditLogSource.System }, async () => {
554
556
  await event.syncGroupRequirements(existingGroup);
555
557
  });
556
- }
557
- catch (e) {
558
+ } catch (e) {
558
559
  // reset the group
559
560
  existingGroup.type = originalGroupType;
560
561
  existingGroup.settings.eventId = null;
@@ -1,12 +1,12 @@
1
1
  import type { Decoder } from '@simonbackx/simple-encoding';
2
- import type { DecodedRequest, Request} from '@simonbackx/simple-endpoints';
2
+ import type { DecodedRequest, Request } from '@simonbackx/simple-endpoints';
3
3
  import { Endpoint, Response } from '@simonbackx/simple-endpoints';
4
4
  import { SimpleError } from '@simonbackx/simple-errors';
5
- import type { XlsxTransformerSheet} from '@stamhoofd/excel-writer';
5
+ import type { XlsxTransformerSheet } from '@stamhoofd/excel-writer';
6
6
  import { ArchiverWriterAdapter, exportToExcel, XlsxWriter } from '@stamhoofd/excel-writer';
7
7
  import { Platform, RateLimiter, sendEmailTemplate } from '@stamhoofd/models';
8
8
  import { QueueHandler } from '@stamhoofd/queues';
9
- import type { ExcelExportType, IPaginatedResponse, LimitedFilteredRequest} from '@stamhoofd/structures';
9
+ import type { ExcelExportType, IPaginatedResponse, LimitedFilteredRequest } from '@stamhoofd/structures';
10
10
  import { EmailTemplateType, ExcelExportRequest, ExcelExportResponse, Replacement, Version } from '@stamhoofd/structures';
11
11
  import { sleep } from '@stamhoofd/utility';
12
12
  import { Context } from '../../../helpers/Context.js';
@@ -89,7 +89,7 @@ export class ExportToExcelEndpoint extends Endpoint<Params, Query, Body, Respons
89
89
  await Platform.getSharedStruct();
90
90
 
91
91
  const result = await Promise.race([
92
- this.job(loader, request.body, request.params.type).then(async (url: string) => {
92
+ this.job(loader, request.body, request.params.type, user.id).then(async (url: string) => {
93
93
  if (sendEmail) {
94
94
  await sendEmailTemplate(null, {
95
95
  template: {
@@ -138,10 +138,10 @@ export class ExportToExcelEndpoint extends Endpoint<Params, Query, Body, Respons
138
138
  }));
139
139
  }
140
140
 
141
- async job(loader: ExcelExporter<unknown>, request: ExcelExportRequest, type: string): Promise<string> {
141
+ async job(loader: ExcelExporter<unknown>, request: ExcelExportRequest, type: string, userId: string): Promise<string> {
142
142
  // Only run 1 export per user at the same time
143
- return await QueueHandler.schedule('user-export-to-excel-' + Context.user!.id, async () => {
144
- // Allow maximum 2 running Excel jobs at the same time for all users
143
+ return await QueueHandler.schedule('user-export-to-excel-' + userId, async () => {
144
+ // Allow maximum 5 running Excel jobs at the same time for all users; queue the rest.
145
145
  return await QueueHandler.schedule('export-to-excel', async () => {
146
146
  // Estimate how long it will take.
147
147
  // If too long, we'll schedule it and write it to Digitalocean Spaces
@@ -163,9 +163,9 @@ export class ExportToExcelEndpoint extends Endpoint<Params, Query, Body, Respons
163
163
 
164
164
  console.log('Done writing excel file');
165
165
 
166
- const url = 'https://' + STAMHOOFD.domains.api + '/v' + Version + '/file-cache?file=' + encodeURIComponent(file) + '&name=' + encodeURIComponent(type);
166
+ const url = 'https://' + STAMHOOFD.domains.api + '/v' + Version + '/file-cache?file=' + encodeURIComponent(file) + '&name=' + encodeURIComponent(request.title ?? type);
167
167
  return url;
168
- }, 2);
168
+ }, 5);
169
169
  });
170
170
  }
171
171
  }
@@ -1,6 +1,6 @@
1
- import type { Decoder} from '@simonbackx/simple-encoding';
1
+ import type { Decoder } from '@simonbackx/simple-encoding';
2
2
  import { AutoEncoder, field, StringDecoder } from '@simonbackx/simple-encoding';
3
- import type { DecodedRequest, Request} from '@simonbackx/simple-endpoints';
3
+ import type { DecodedRequest, Request } from '@simonbackx/simple-endpoints';
4
4
  import { Endpoint, Response } from '@simonbackx/simple-endpoints';
5
5
  import type { Readable } from 'node:stream';
6
6
  import { Context } from '../../../helpers/Context.js';
@@ -60,8 +60,7 @@ export class GetFileCache extends Endpoint<Params, Query, Body, ResponseBody> {
60
60
  if (request.query.name) {
61
61
  const slug = Formatter.fileSlug(request.query.name) + extension;
62
62
  response.headers['Content-Disposition'] = `attachment; filename="${slug}"`;
63
- }
64
- else {
63
+ } else {
65
64
  response.headers['Content-Disposition'] = `attachment; filename="bestand${extension}"`;
66
65
  }
67
66
  response.headers['Content-Length'] = contentLength.toString();
@@ -1,5 +1,5 @@
1
1
  import { PutObjectCommand, S3Client } from '@aws-sdk/client-s3'; // ES Modules import
2
- import type { DecodedRequest, Request} from '@simonbackx/simple-endpoints';
2
+ import type { DecodedRequest, Request } from '@simonbackx/simple-endpoints';
3
3
  import { Endpoint, Response } from '@simonbackx/simple-endpoints';
4
4
  import { SimpleError } from '@simonbackx/simple-errors';
5
5
  import { File } from '@stamhoofd/structures';
@@ -8,7 +8,7 @@ import formidable from 'formidable';
8
8
  import { promises as fs } from 'fs';
9
9
  import { v4 as uuidv4 } from 'uuid';
10
10
 
11
- import type { Decoder} from '@simonbackx/simple-encoding';
11
+ import type { Decoder } from '@simonbackx/simple-encoding';
12
12
  import { AutoEncoder, BooleanDecoder, field } from '@simonbackx/simple-encoding';
13
13
  import { Context } from '../../../helpers/Context.js';
14
14
  import { limiter } from './UploadImage.js';
@@ -133,8 +133,7 @@ export class UploadFile extends Endpoint<Params, Query, Body, ResponseBody> {
133
133
  if (request.query.isPrivate && user) {
134
134
  // Private files
135
135
  prefix += 'users/' + user.id + '/';
136
- }
137
- else {
136
+ } else {
138
137
  // Public files
139
138
  prefix += 'p/';
140
139
  }
@@ -1,6 +1,6 @@
1
- import type { Decoder} from '@simonbackx/simple-encoding';
1
+ import type { Decoder } from '@simonbackx/simple-encoding';
2
2
  import { AutoEncoder, BooleanDecoder, field, ObjectData } from '@simonbackx/simple-encoding';
3
- import type { DecodedRequest, Request} from '@simonbackx/simple-endpoints';
3
+ import type { DecodedRequest, Request } from '@simonbackx/simple-endpoints';
4
4
  import { Endpoint, Response } from '@simonbackx/simple-endpoints';
5
5
  import { SimpleError } from '@simonbackx/simple-errors';
6
6
  import { Image, RateLimiter } from '@stamhoofd/models';
@@ -125,8 +125,7 @@ export class UploadImage extends Endpoint<Params, Query, Body, ResponseBody> {
125
125
  try {
126
126
  const resolutions = new ObjectData(JSON.parse(fields.resolutions[0]), { version: request.request.getVersion() }).array(ResolutionRequest as Decoder<ResolutionRequest>);
127
127
  resolve([files.file[0], resolutions]);
128
- }
129
- catch (e) {
128
+ } catch (e) {
130
129
  reject(e);
131
130
  }
132
131
  });
@@ -36,7 +36,7 @@ export class GetGroupsCountEndpoint extends Endpoint<Params, Query, Body, Respon
36
36
  throw Context.auth.error();
37
37
  }
38
38
  }
39
-
39
+
40
40
  const query = await GetGroupsEndpoint.buildQuery(request.query);
41
41
 
42
42
  const count = await query
@@ -1,5 +1,5 @@
1
1
  import { Request } from '@simonbackx/simple-endpoints';
2
- import type { Organization, RegistrationPeriod} from '@stamhoofd/models';
2
+ import type { Organization, RegistrationPeriod } from '@stamhoofd/models';
3
3
  import { GroupFactory, OrganizationFactory, OrganizationRegistrationPeriodFactory, RegistrationPeriodFactory, Token, UserFactory } from '@stamhoofd/models';
4
4
  import type { StamhoofdFilter } from '@stamhoofd/structures';
5
5
  import { BundleDiscountGroupPriceSettings, GroupPrice, GroupType, LimitedFilteredRequest } from '@stamhoofd/structures';
@@ -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 type { CountFilteredRequest, Group as GroupStruct, StamhoofdFilter } from '@stamhoofd/structures';
4
4
  import { assertSort, getSortFilter, LimitedFilteredRequest, PaginatedResponse } from '@stamhoofd/structures';
@@ -61,7 +61,9 @@ export class GetGroupsEndpoint extends Endpoint<Params, Query, Body, ResponseBod
61
61
  let searchFilter: StamhoofdFilter | null = null;
62
62
 
63
63
  searchFilter = {
64
- id: q.search,
64
+ name: {
65
+ $contains: q.search,
66
+ },
65
67
  };
66
68
 
67
69
  if (searchFilter) {
@@ -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 type { MemberWithUsersRegistrationsAndGroups } from '@stamhoofd/models';
4
4
  import { Member } from '@stamhoofd/models';
@@ -38,8 +38,7 @@ export class GetMemberFamilyEndpoint extends Endpoint<Params, Query, Body, Respo
38
38
  if (!await Context.auth.hasSomeAccess(organization.id)) {
39
39
  throw Context.auth.error();
40
40
  }
41
- }
42
- else {
41
+ } else {
43
42
  if (!Context.auth.hasSomePlatformAccess()) {
44
43
  throw Context.auth.error();
45
44
  }
@@ -1,5 +1,5 @@
1
1
  import type { Decoder } from '@simonbackx/simple-encoding';
2
- import type { DecodedRequest, Request} from '@simonbackx/simple-endpoints';
2
+ import type { DecodedRequest, Request } from '@simonbackx/simple-endpoints';
3
3
  import { Endpoint, Response } from '@simonbackx/simple-endpoints';
4
4
  import { CountFilteredRequest, CountResponse } from '@stamhoofd/structures';
5
5
 
@@ -1,6 +1,6 @@
1
- import type { Endpoint} from '@simonbackx/simple-endpoints';
1
+ import type { Endpoint } from '@simonbackx/simple-endpoints';
2
2
  import { Request } from '@simonbackx/simple-endpoints';
3
- import type { RegistrationPeriod} from '@stamhoofd/models';
3
+ import type { RegistrationPeriod } from '@stamhoofd/models';
4
4
  import { EventFactory, GroupFactory, MemberFactory, OrganizationFactory, RecordCategoryFactory, RegistrationFactory, RegistrationPeriodFactory, Token, UserFactory } from '@stamhoofd/models';
5
5
  import { AccessRight, EventMeta, GroupType, LimitedFilteredRequest, NamedObject, PermissionLevel, PermissionRoleDetailed, Permissions, PermissionsResourceType, RecordAnswer, RecordTextAnswer, RecordType, ResourcePermissions } from '@stamhoofd/structures';
6
6
  import { STExpect, TestUtils } from '@stamhoofd/test-utils';
@@ -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',