@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
@@ -0,0 +1,116 @@
1
+ import type { AutoEncoderPatchType, Encodeable, Identifiable, Patchable, PatchableArray } from '@simonbackx/simple-encoding';
2
+ import { isPatchableArray } from '@simonbackx/simple-encoding';
3
+ import { SimpleError } from '@simonbackx/simple-errors';
4
+ import type { RecordCategory, RecordSettings, RecordType } from '@stamhoofd/structures';
5
+ import { RecordAnswerDecoder } from '@stamhoofd/structures';
6
+
7
+ export class RecordAnswerHelper {
8
+ static throwIfPatchOrPutIsInvalid(original: RecordCategory[], patchOrPut: RecordCategory[] | PatchableArray<string, RecordCategory, AutoEncoderPatchType<RecordCategory>>) {
9
+ if (original.length === 0) {
10
+ return;
11
+ }
12
+
13
+ const updatedRecordSettings = this.getAllRecords(patchOrPut)
14
+ // only check if the types changed
15
+ .filter(p => p.type !== undefined || p.fileType !== undefined);
16
+
17
+ if (updatedRecordSettings.length === 0) {
18
+ return;
19
+ }
20
+
21
+ const updatRecordSettingsMap = new Map<string, RecordSettings | AutoEncoderPatchType<RecordSettings>>(updatedRecordSettings.map(p => [p.id, p]));
22
+
23
+ for (const category of original) {
24
+ for (const originalRecord of category.getAllRecords()) {
25
+ const updatedSettings = updatRecordSettingsMap.get(originalRecord.id);
26
+ if (updatedSettings) {
27
+ this.throwIfInvalidRecordSettingsUpdate(originalRecord, updatedSettings);
28
+
29
+ // remove from map
30
+ updatRecordSettingsMap.delete(originalRecord.id);
31
+
32
+ // stop looping if all updated records are checked
33
+ if (updatRecordSettingsMap.size === 0) {
34
+ break;
35
+ }
36
+ }
37
+ }
38
+ }
39
+ }
40
+
41
+ static throwIfPatchOrPutsAreInvalid<
42
+ Key extends string & keyof Put,
43
+ Id extends string | number,
44
+ Put extends (Identifiable<Id> & Encodeable & Patchable<Patch> & Record<Key, RecordCategory[]>),
45
+ Patch extends (Identifiable<Id> & Encodeable) | Put,
46
+ >(originals: Put[], patchOrPuts: Put[] | Patch[] | PatchableArray<Id, Put, Patch>, key: string) {
47
+ if (isPatchableArray(patchOrPuts)) {
48
+ const puts = patchOrPuts.getPuts().map(p => p.put);
49
+ this.throwIfPatchOrPutsAreInvalid<Key, Id, Put, Patch>(originals, puts, key);
50
+ const patches = patchOrPuts.getPatches();
51
+ this.throwIfPatchOrPutsAreInvalid<Key, Id, Put, Patch>(originals, patches, key);
52
+ } else {
53
+ for (const patchOrPut of patchOrPuts) {
54
+ const original = originals.find(o => o.id === patchOrPut.id);
55
+ if (original) {
56
+ const originalRecordCategories: RecordCategory[] = original[key];
57
+ const newRecordCategories: RecordCategory[] | PatchableArray<string, RecordCategory, AutoEncoderPatchType<RecordCategory>> = patchOrPut[key];
58
+ if (originalRecordCategories && newRecordCategories) {
59
+ this.throwIfPatchOrPutIsInvalid(originalRecordCategories, newRecordCategories);
60
+ }
61
+ }
62
+ }
63
+ }
64
+ }
65
+
66
+ private static throwIfInvalidRecordSettingsUpdate(original: RecordSettings, updated: RecordSettings | AutoEncoderPatchType<RecordSettings>) {
67
+ if (updated.type !== undefined
68
+ && original.type !== updated.type
69
+ // changing between types with the same class should be allowed
70
+ && !this.haveTypesSameClass(original.type, updated.type)) {
71
+ throw new SimpleError({
72
+ code: 'invalid_record_type',
73
+ message: `Cannot change record type from ${original.type} to ${updated.type}`,
74
+ human: $t(`%1Qn`, { name: original.name }),
75
+ });
76
+ }
77
+
78
+ if (updated.fileType !== undefined && ((original.fileType ?? null) !== (updated.fileType ?? null))) {
79
+ throw new SimpleError({
80
+ code: 'invalid_record_type',
81
+ message: `Cannot change record file type from ${original.fileType} to ${updated.fileType}`,
82
+ human: $t(`%1SP`, { name: original.name }),
83
+ });
84
+ }
85
+ }
86
+
87
+ static haveTypesSameClass(a: RecordType, b: RecordType) {
88
+ return RecordAnswerDecoder.getClassForType(a) === RecordAnswerDecoder.getClassForType(b);
89
+ }
90
+
91
+ private static getAllRecords(patchOrPut: RecordCategory[] | PatchableArray<string, RecordCategory, AutoEncoderPatchType<RecordCategory>>): (RecordSettings | AutoEncoderPatchType<RecordSettings>)[] {
92
+ if (isPatchableArray(patchOrPut)) {
93
+ const patches = patchOrPut.getPatches().flatMap(p => this.getAllRecordsFromRecordCategoryPatch(p));
94
+ const puts = patchOrPut.getPuts().flatMap(p => p.put.getAllRecords());
95
+ return [...patches, ...puts];
96
+ }
97
+ return patchOrPut.flatMap(c => c.getAllRecords());
98
+ }
99
+
100
+ private static getAllRecordsFromRecordCategoryPatch(category: AutoEncoderPatchType<RecordCategory>) {
101
+ const results: (RecordSettings | AutoEncoderPatchType<RecordSettings>)[] = [
102
+ ...category.records.getPatches(),
103
+ ...category.records.getPuts().map(p => p.put),
104
+ ];
105
+
106
+ if (category.childCategories) {
107
+ const childCategoryPatches = category.childCategories.getPatches().flatMap(p => this.getAllRecordsFromRecordCategoryPatch(p));
108
+ results.push(...childCategoryPatches);
109
+
110
+ const childCategoryPuts = category.childCategories.getPuts().flatMap(p => p.put.getAllRecords());
111
+ results.push(...childCategoryPuts);
112
+ }
113
+
114
+ return results;
115
+ }
116
+ }
@@ -0,0 +1,106 @@
1
+ import { Database } from '@simonbackx/simple-database';
2
+ import type { SQLSelect } from '@stamhoofd/sql';
3
+ import type { ProgressLogger } from '@stamhoofd/utility';
4
+ import { LoggingTools } from '@stamhoofd/utility';
5
+
6
+ /**
7
+ * If an error is thrown, first await all pending items before throwing, otherwise we risk quiting mid running actions
8
+ */
9
+ export function allSettledButThrowFirst<T>(promises: Promise<T>[]): Promise<T[]> {
10
+ return Promise.allSettled(promises).then((results) => {
11
+ return results.map((r) => {
12
+ if (r.status === 'fulfilled') {
13
+ return r.value;
14
+ }
15
+ throw r.reason;
16
+ });
17
+ });
18
+ }
19
+
20
+ export class SeedTools {
21
+ static createBatchProcessor<T>(options: BatchProcessorArgs<T>) {
22
+ return new BatchProcessor(options);
23
+ }
24
+
25
+ static async loop<T extends { id: string }>(options: BatchProcessorArgs<T> & { query: SQLSelect<T> }) {
26
+ const batchProcessor = SeedTools.createBatchProcessor(options);
27
+
28
+ const progressLogger = await LoggingTools.createProgressLoggerFromQuery(options.query.clone());
29
+ batchProcessor.setProgressLogger(progressLogger);
30
+
31
+ for await (const item of options.query.limit(options.batchSize).all()) {
32
+ await batchProcessor.execute(item as T);
33
+ }
34
+ await batchProcessor.finish();
35
+
36
+ return {
37
+ total: progressLogger.total,
38
+ };
39
+ }
40
+
41
+ static async loopBatched<T extends { id: string }>(options: { batchSize: number; batchAction: (items: T[]) => Promise<void>; query: SQLSelect<T> }) {
42
+ const progressLogger = await LoggingTools.createProgressLoggerFromQuery(options.query.clone());
43
+
44
+ for await (const batch of options.query.limit(options.batchSize).allBatched()) {
45
+ await options.batchAction(batch as T[]);
46
+ progressLogger.update(batch.length);
47
+ }
48
+
49
+ return {
50
+ total: progressLogger.total,
51
+ };
52
+ }
53
+ }
54
+
55
+ type BatchProcessorArgs<T> = {
56
+ batchSize: number;
57
+ useTransactionPerBatch?: boolean;
58
+ action: (item: T) => Promise<void>;
59
+ };
60
+
61
+ class BatchProcessor<T> {
62
+ private currentBatch: Promise<void>[] = [];
63
+ private readonly batchSize: number;
64
+ private readonly originalAction: (item: T) => Promise<void>;
65
+ private action: (item: T) => Promise<void>;
66
+ private readonly useTransactionPerBatch: boolean;
67
+
68
+ constructor({ batchSize, action, useTransactionPerBatch }: BatchProcessorArgs<T>) {
69
+ this.batchSize = batchSize;
70
+ this.originalAction = action;
71
+ this.action = action;
72
+ this.useTransactionPerBatch = useTransactionPerBatch ?? false;
73
+ }
74
+
75
+ setProgressLogger(progressLogger: ProgressLogger) {
76
+ this.action = async (item: T) => {
77
+ await this.originalAction(item);
78
+ progressLogger.update();
79
+ };
80
+ }
81
+
82
+ async execute(item: T): Promise<void> {
83
+ if (this.currentBatch.length >= this.batchSize) {
84
+ await this.finishCurrentBatch();
85
+ }
86
+
87
+ this.currentBatch.push(this.action(item));
88
+ }
89
+
90
+ private async finishCurrentBatch() {
91
+ const batch = this.currentBatch;
92
+ this.currentBatch = [];
93
+
94
+ if (this.useTransactionPerBatch) {
95
+ await Database.beginTransaction(async () => {
96
+ await allSettledButThrowFirst(batch);
97
+ });
98
+ } else {
99
+ await allSettledButThrowFirst(batch);
100
+ }
101
+ }
102
+
103
+ async finish(): Promise<void> {
104
+ await this.finishCurrentBatch();
105
+ }
106
+ }
@@ -1,21 +1,64 @@
1
- import type { Organization, Payment } from '@stamhoofd/models';
2
- import { calculateVATPercentage, PaymentProvider, STPackageType } from '@stamhoofd/structures';
1
+ import { SimpleError } from '@simonbackx/simple-errors';
2
+ import type { Payment, StripeAccount } from '@stamhoofd/models';
3
+ import { Organization, Platform } from '@stamhoofd/models';
4
+ import { PaymentMethod, PaymentProvider, STPackageType } from '@stamhoofd/structures';
5
+ import { VATService } from '../services/VATService.js';
6
+ import { STMath } from '@stamhoofd/utility';
7
+
8
+ const CACHE_TIMEOUT = 1000 * 60 * 5; // 5 minutes
3
9
 
4
10
  export class ServiceFeeHelper {
5
- static setServiceFee(payment: Payment, organization: Organization, type: 'webshop' | 'members' | 'tickets' | 'system', itemPrices: number[]): void {
6
- if (type === 'system') {
7
- return;
11
+ static #cachedOrganization: Organization | null = null;
12
+ static #cachedOrganizationDate: Date | null = null;
13
+
14
+ /**
15
+ * Organization that is invoicing
16
+ */
17
+ static async getInvoicingOrganization() {
18
+ if (this.#cachedOrganization && this.#cachedOrganizationDate && this.#cachedOrganizationDate > new Date(Date.now() - CACHE_TIMEOUT)) {
19
+ return this.#cachedOrganization;
20
+ }
21
+ const platform = await Platform.getShared();
22
+ const membershipId = platform.membershipOrganizationId;
23
+ if (!membershipId) {
24
+ return null;
8
25
  }
9
26
 
10
- let vat = 0;
27
+ const org = await Organization.getByID(membershipId, true);
28
+ this.#cachedOrganization = org;
29
+ this.#cachedOrganizationDate = new Date();
11
30
 
12
- // Calculate item count
13
- if (payment.provider === PaymentProvider.Stripe && payment.stripeAccountId) {
14
- // Don't chagne
15
- vat = calculateVATPercentage(organization.address, organization.meta.VATNumber);
31
+ return org;
32
+ }
33
+
34
+ /**
35
+ * VAT rate used to charge service fees or transfer fees to organization by the invoicing organization (membershipOrganizationId)
36
+ */
37
+ static async getVATPercentage(organization: Organization) {
38
+ const invoicingOrganization = await this.getInvoicingOrganization();
39
+ if (!invoicingOrganization) {
40
+ throw new SimpleError({
41
+ code: 'unavailable',
42
+ message: 'Payments are temporarily unavailable due to a lack of VAT settings',
43
+ human: $t('%1ay'),
44
+ });
16
45
  }
17
- else {
18
- // don't charge VAT (we'll add the VAT to the invoice)
46
+
47
+ const customer = organization.defaultCompanies[0];
48
+
49
+ if (VATService.isVATExcempt({
50
+ sellingOrganization: invoicingOrganization,
51
+ company: customer,
52
+ })) {
53
+ return 0;
54
+ }
55
+
56
+ return 21;
57
+ }
58
+
59
+ static async setServiceFee(payment: Payment, organization: Organization, type: 'webshop' | 'members' | 'tickets' | 'system', itemPrices: number[]): Promise<void> {
60
+ if (type === 'system') {
61
+ return;
19
62
  }
20
63
 
21
64
  function calculateFee(price: number, minimumFee: number | null, maximumFee: number | null, fixed: number, percentageTimes100: number) {
@@ -55,7 +98,18 @@ export class ServiceFeeHelper {
55
98
  }
56
99
 
57
100
  // Add VAT
58
- serviceFee = serviceFee * (100 + vat) / 100;
101
+ if (serviceFee !== 0) {
102
+ let vat = 0;
103
+
104
+ // Calculate item count
105
+ if (payment.provider === PaymentProvider.Stripe && payment.stripeAccountId) {
106
+ // Don't chagne
107
+ vat = await this.getVATPercentage(organization);
108
+ } else {
109
+ // don't charge VAT (we'll add the VAT to the invoice)
110
+ }
111
+ serviceFee = serviceFee * (100 + vat) / 100;
112
+ }
59
113
 
60
114
  // Round service fee to 2 decimal places
61
115
  serviceFee = Math.round(serviceFee / 100) * 100;
@@ -63,9 +117,65 @@ export class ServiceFeeHelper {
63
117
  console.log('Service fee for payment', payment.id, type, 'is', serviceFee);
64
118
  if (payment.provider === PaymentProvider.Stripe && payment.stripeAccountId) {
65
119
  payment.serviceFeePayout = serviceFee;
66
- }
67
- else {
120
+ } else {
68
121
  payment.serviceFeeManual = serviceFee;
69
122
  }
70
123
  }
124
+
125
+ /**
126
+ * Depends on the situation.
127
+ */
128
+ static async setTransferFee({
129
+ payment,
130
+ organization,
131
+ stripeAccount,
132
+ }: {
133
+ payment: Payment;
134
+ organization: Organization;
135
+ stripeAccount?: StripeAccount | null | undefined;
136
+ }) {
137
+ if (payment.provider !== PaymentProvider.Stripe && payment.provider !== PaymentProvider.Buckaroo) {
138
+ return;
139
+ }
140
+
141
+ if (payment.provider === PaymentProvider.Stripe && !stripeAccount) {
142
+ throw new Error('Missing StripeAccount');
143
+ }
144
+
145
+ const totalPrice = STMath.round(payment.price / 100); // Convert from 4 decimal places to 2 decimal places for this calculation
146
+
147
+ if (totalPrice <= 0) {
148
+ return;
149
+ }
150
+
151
+ if (stripeAccount && stripeAccount.meta.type === 'standard') {
152
+ // Submerchant is charged by Stripe for the fees directly
153
+ return;
154
+ }
155
+
156
+ let fee = 0;
157
+ const vat = await this.getVATPercentage(organization);
158
+
159
+ function calculateFee(fixed: number, percentageTimes100: number) {
160
+ return STMath.round(STMath.round(fixed + Math.max(1, totalPrice * percentageTimes100 / 100 / 100)) * (100 + vat) / 100); // € 0,21 + 0,2%
161
+ }
162
+
163
+ if (payment.method === PaymentMethod.iDEAL) {
164
+ fee = calculateFee(21, 20); // € 0,21 + 0,2%
165
+ } else if (payment.method === PaymentMethod.Bancontact) {
166
+ fee = calculateFee(24, 20); // € 0,24 + 0,2%
167
+ } else {
168
+ fee = calculateFee(25, 150); // € 0,25 + 1,5%
169
+ }
170
+
171
+ payment.transferFee = fee * 100; // Convert back to 4 decimal places for storage
172
+
173
+ if (payment.provider === PaymentProvider.Buckaroo) {
174
+ // We'll need to charge
175
+ payment.transferFeeManual = payment.transferFee;
176
+ console.log('Transfer fee for payment', payment.id, 'is', payment.transferFee, '(manually charged)');
177
+ } else {
178
+ console.log('Transfer fee for payment', payment.id, 'is', payment.transferFee);
179
+ }
180
+ }
71
181
  }
@@ -14,17 +14,17 @@ import { SQL, SQLWhereSign } from '@stamhoofd/sql';
14
14
  import type {
15
15
  MemberResponsibility,
16
16
  Platform as PlatformStruct,
17
- SetupSteps} from '@stamhoofd/structures';
17
+ SetupSteps,
18
+ } from '@stamhoofd/structures';
18
19
  import {
19
20
  AuditLogSource,
20
21
  GroupType,
21
22
  RecordCategory,
22
- SetupStepType
23
+ SetupStepType,
23
24
  } from '@stamhoofd/structures';
24
25
  import { Formatter } from '@stamhoofd/utility';
25
26
  import { AuditLogService } from '../services/AuditLogService.js';
26
27
  import { GroupedThrottledQueue } from './GroupedThrottledQueue.js';
27
- import { AuthenticatedStructures } from './AuthenticatedStructures.js';
28
28
 
29
29
  type SetupStepOperation = (setupSteps: SetupSteps, organization: Organization, platform: PlatformStruct) => void | Promise<void>;
30
30
 
@@ -71,6 +71,10 @@ export class SetupStepUpdater {
71
71
  static isListening = false;
72
72
 
73
73
  static listen() {
74
+ if (STAMHOOFD.userMode === 'organization') {
75
+ return;
76
+ }
77
+
74
78
  if (this.isListening) {
75
79
  return;
76
80
  }
@@ -111,14 +115,14 @@ export class SetupStepUpdater {
111
115
  SetupStepType,
112
116
  SetupStepOperation
113
117
  > = {
114
- [SetupStepType.Responsibilities]: this.updateStepResponsibilities,
115
- [SetupStepType.Companies]: this.updateStepCompanies,
116
- [SetupStepType.Groups]: this.updateStepGroups,
117
- [SetupStepType.Premises]: this.updateStepPremises,
118
- [SetupStepType.Emails]: this.updateStepEmails,
119
- [SetupStepType.Payment]: this.updateStepPayment,
120
- [SetupStepType.Registrations]: this.updateStepRegistrations,
121
- };
118
+ [SetupStepType.Responsibilities]: this.updateStepResponsibilities,
119
+ [SetupStepType.Companies]: this.updateStepCompanies,
120
+ [SetupStepType.Groups]: this.updateStepGroups,
121
+ [SetupStepType.Premises]: this.updateStepPremises,
122
+ [SetupStepType.Emails]: this.updateStepEmails,
123
+ [SetupStepType.Payment]: this.updateStepPayment,
124
+ [SetupStepType.Registrations]: this.updateStepRegistrations,
125
+ };
122
126
 
123
127
  static async updateSetupStepsForAllOrganizationsInCurrentPeriod({
124
128
  batchSize,
@@ -191,6 +195,10 @@ export class SetupStepUpdater {
191
195
  }
192
196
 
193
197
  static async updateForOrganizationId(id: string, options?: { types?: SetupStepType[] }) {
198
+ if (STAMHOOFD.userMode === 'organization') {
199
+ return;
200
+ }
201
+
194
202
  const organization = await Organization.getByID(id);
195
203
  if (!organization) {
196
204
  throw new SimpleError({
@@ -215,6 +223,10 @@ export class SetupStepUpdater {
215
223
  types?: SetupStepType[];
216
224
  } = {},
217
225
  ) {
226
+ if (STAMHOOFD.userMode === 'organization') {
227
+ return;
228
+ }
229
+
218
230
  if (!platform) {
219
231
  platform = await Platform.getSharedPrivateStruct();
220
232
  if (!platform) {
@@ -312,8 +324,7 @@ export class SetupStepUpdater {
312
324
  totalSteps,
313
325
  finishedSteps,
314
326
  });
315
- }
316
- else {
327
+ } else {
317
328
  // if no premise types, remove step
318
329
  setupSteps.remove(SetupStepType.Premises);
319
330
  }
@@ -346,8 +357,7 @@ export class SetupStepUpdater {
346
357
  platform.config.organizationLevelRecordsConfiguration.recordCategories,
347
358
  organization.getBaseStructureWithPrivateMeta(), // private data needed for answers
348
359
  );
349
- }
350
- catch (error) {
360
+ } catch (error) {
351
361
  console.error('Error validating record categories for organization', organization.id, error);
352
362
  finishedSteps = 0;
353
363
  }
@@ -375,10 +385,10 @@ export class SetupStepUpdater {
375
385
  const allRecords = responsibilityIds.length === 0
376
386
  ? []
377
387
  : await MemberResponsibilityRecord.select()
378
- .where('responsibilityId', responsibilityIds)
379
- .where('organizationId', organization.id)
380
- .where(SQL.where('endDate', SQLWhereSign.Greater, now).or('endDate', null))
381
- .fetch();
388
+ .where('responsibilityId', responsibilityIds)
389
+ .where('organizationId', organization.id)
390
+ .where(SQL.where('endDate', SQLWhereSign.Greater, now).or('endDate', null))
391
+ .fetch();
382
392
 
383
393
  // Remove invalid responsibilities: members that are not registered in the current period
384
394
  const memberIds = Formatter.uniqueArray(allRecords.map(r => r.memberId));
@@ -444,8 +454,7 @@ export class SetupStepUpdater {
444
454
  totalRecordsWithThisResponsibility++;
445
455
  }
446
456
  }
447
- }
448
- else {
457
+ } else {
449
458
  for (const record of records) {
450
459
  if (record.responsibilityId === responsibilityId && record.groupId === group.id) {
451
460
  totalRecordsWithThisResponsibility++;
@@ -488,8 +497,7 @@ export class SetupStepUpdater {
488
497
 
489
498
  if (finishedSteps >= totalSteps) {
490
499
  setupSteps.markReviewed(SetupStepType.Emails, { userId: 'backend', userName: 'backend' });
491
- }
492
- else {
500
+ } else {
493
501
  setupSteps.resetReviewed(SetupStepType.Emails);
494
502
  }
495
503
  }
@@ -510,11 +518,11 @@ export class SetupStepUpdater {
510
518
 
511
519
  const groupsWithDefaultAgeGroups = defaultAgeGroupIds.length > 0
512
520
  ? await Group.select()
513
- .where('organizationId', organization.id)
514
- .where('periodId', platform.period.id)
515
- .where('defaultAgeGroupId', defaultAgeGroupIds)
516
- .where('deletedAt', null)
517
- .fetch()
521
+ .where('organizationId', organization.id)
522
+ .where('periodId', platform.period.id)
523
+ .where('defaultAgeGroupId', defaultAgeGroupIds)
524
+ .where('deletedAt', null)
525
+ .fetch()
518
526
  : [];
519
527
 
520
528
  let totalSteps = 0;
@@ -530,8 +538,7 @@ export class SetupStepUpdater {
530
538
  if (!processedDefaultAgeGroupIds.has(defaultAgeGroup.id)) {
531
539
  totalSteps += defaultAgeGroup.minimumRequiredMembers;
532
540
  processedDefaultAgeGroupIds.set(defaultAgeGroup.id, Math.min(defaultAgeGroup.minimumRequiredMembers, group.settings.registeredMembers ?? 0));
533
- }
534
- else {
541
+ } else {
535
542
  processedDefaultAgeGroupIds.set(defaultAgeGroup.id,
536
543
  Math.min(
537
544
  defaultAgeGroup.minimumRequiredMembers,
@@ -550,8 +557,7 @@ export class SetupStepUpdater {
550
557
 
551
558
  if (finishedSteps >= totalSteps) {
552
559
  setupSteps.markReviewed(SetupStepType.Registrations, { userId: 'backend', userName: 'backend' });
553
- }
554
- else {
560
+ } else {
555
561
  setupSteps.resetReviewed(SetupStepType.Registrations);
556
562
  }
557
563
  }
@@ -0,0 +1,52 @@
1
+ import { SimpleError } from '@simonbackx/simple-errors';
2
+ import type { StamhoofdCompareValue, StamhoofdFilter } from '@stamhoofd/structures';
3
+ import { FilterWrapperMarker, getMagicRelationValue, isMagicFilter, isMagicRelationFilter, unwrapFilter } from '@stamhoofd/structures';
4
+
5
+ export class StamhoofdFilterAccessHelper {
6
+ static getGroupIdsFromFilter(filter: StamhoofdFilter): StamhoofdFilter<StamhoofdCompareValue>[] {
7
+ let rawGroupIds: StamhoofdFilter<StamhoofdCompareValue>[] | null = null;
8
+
9
+ if (typeof filter === 'string' || isMagicFilter(filter)) {
10
+ rawGroupIds = [filter];
11
+ } else {
12
+ const { markerValue } = unwrapFilter(filter, {
13
+ $in: FilterWrapperMarker,
14
+ });
15
+
16
+ if (!Array.isArray(markerValue)) {
17
+ throw new SimpleError({
18
+ code: 'invalid_field',
19
+ field: 'filter',
20
+ message: 'You must filter on a group of the organization you are trying to access',
21
+ human: $t(`%1HG`),
22
+ });
23
+ }
24
+
25
+ if (markerValue.length === 0) {
26
+ throw new SimpleError({
27
+ code: 'invalid_field',
28
+ field: 'filter',
29
+ message: 'Filtering on an empty list of groups is not supported',
30
+ });
31
+ }
32
+
33
+ rawGroupIds = markerValue;
34
+ }
35
+
36
+ return rawGroupIds.map((raw) => {
37
+ if (typeof raw === 'string') {
38
+ return raw;
39
+ }
40
+
41
+ if (isMagicRelationFilter(raw)) {
42
+ return getMagicRelationValue(raw);
43
+ }
44
+
45
+ throw new SimpleError({
46
+ code: 'invalid_field',
47
+ field: 'filter',
48
+ message: 'Invalid group ID in filter',
49
+ });
50
+ });
51
+ }
52
+ }