@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
@@ -9,7 +9,7 @@ export class RecordAnswerHelper {
9
9
  if (original.length === 0) {
10
10
  return;
11
11
  }
12
-
12
+
13
13
  const updatedRecordSettings = this.getAllRecords(patchOrPut)
14
14
  // only check if the types changed
15
15
  .filter(p => p.type !== undefined || p.fileType !== undefined);
@@ -42,7 +42,7 @@ export class RecordAnswerHelper {
42
42
  Key extends string & keyof Put,
43
43
  Id extends string | number,
44
44
  Put extends (Identifiable<Id> & Encodeable & Patchable<Patch> & Record<Key, RecordCategory[]>),
45
- Patch extends (Identifiable<Id> & Encodeable) | Put
45
+ Patch extends (Identifiable<Id> & Encodeable) | Put,
46
46
  >(originals: Put[], patchOrPuts: Put[] | Patch[] | PatchableArray<Id, Put, Patch>, key: string) {
47
47
  if (isPatchableArray(patchOrPuts)) {
48
48
  const puts = patchOrPuts.getPuts().map(p => p.put);
@@ -71,16 +71,16 @@ export class RecordAnswerHelper {
71
71
  throw new SimpleError({
72
72
  code: 'invalid_record_type',
73
73
  message: `Cannot change record type from ${original.type} to ${updated.type}`,
74
- human: $t(`%1Qn`, {name: original.name}),
75
- })
74
+ human: $t(`%1Qn`, { name: original.name }),
75
+ });
76
76
  }
77
77
 
78
78
  if (updated.fileType !== undefined && ((original.fileType ?? null) !== (updated.fileType ?? null))) {
79
79
  throw new SimpleError({
80
80
  code: 'invalid_record_type',
81
81
  message: `Cannot change record file type from ${original.fileType} to ${updated.fileType}`,
82
- human: $t(`%1SP`, {name: original.name}),
83
- })
82
+ human: $t(`%1SP`, { name: original.name }),
83
+ });
84
84
  }
85
85
  }
86
86
 
@@ -100,7 +100,7 @@ export class RecordAnswerHelper {
100
100
  private static getAllRecordsFromRecordCategoryPatch(category: AutoEncoderPatchType<RecordCategory>) {
101
101
  const results: (RecordSettings | AutoEncoderPatchType<RecordSettings>)[] = [
102
102
  ...category.records.getPatches(),
103
- ...category.records.getPuts().map(p => p.put)
103
+ ...category.records.getPuts().map(p => p.put),
104
104
  ];
105
105
 
106
106
  if (category.childCategories) {
@@ -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
+ }
@@ -1,8 +1,8 @@
1
1
  import { SimpleError } from '@simonbackx/simple-errors';
2
2
  import type { I18n } from '@stamhoofd/backend-i18n';
3
- import type { BalanceItem, BalanceItemPayment, Organization, StripeAccount} from '@stamhoofd/models';
3
+ import type { Organization, StripeAccount } from '@stamhoofd/models';
4
4
  import { Payment, StripeCheckoutSession, StripePaymentIntent } from '@stamhoofd/models';
5
- import { calculateVATPercentage, PaymentMethod, PaymentMethodHelper, PaymentStatus } from '@stamhoofd/structures';
5
+ import { PaymentMethod, PaymentMethodHelper, PaymentStatus } from '@stamhoofd/structures';
6
6
  import { Formatter } from '@stamhoofd/utility';
7
7
  import Stripe from 'stripe';
8
8
  import { passthroughFetch } from './passthroughFetch.js';
@@ -68,15 +68,13 @@ export class StripeHelper {
68
68
  if (charge.payment_method_details.sepa_debit.last4) {
69
69
  if (charge.payment_method_details.sepa_debit.country === 'BE') {
70
70
  payment.iban = charge.payment_method_details.sepa_debit.country + '•• ' + charge.payment_method_details.sepa_debit.bank_code + '• •••• ' + charge.payment_method_details.sepa_debit.last4;
71
- }
72
- else {
71
+ } else {
73
72
  payment.iban = '•••• ' + charge.payment_method_details.sepa_debit.last4;
74
73
  }
75
74
  }
76
75
  }
77
76
  await payment.save();
78
- }
79
- catch (e) {
77
+ } catch (e) {
80
78
  console.error('Failed processing charge', e);
81
79
  }
82
80
  }
@@ -145,8 +143,7 @@ export class StripeHelper {
145
143
  if (intent.status === 'canceled' || intent.status === 'requires_payment_method') {
146
144
  return PaymentStatus.Failed;
147
145
  }
148
- }
149
- catch (e) {
146
+ } catch (e) {
150
147
  console.error('Error cancelling payment intent', e);
151
148
  }
152
149
  }
@@ -161,6 +158,10 @@ export class StripeHelper {
161
158
  const [model] = await StripeCheckoutSession.where({ paymentId: payment.id }, { limit: 1 });
162
159
 
163
160
  if (!model) {
161
+ if (payment.createdAt.getTime() > Date.now() - 1000 * 10) {
162
+ // Possibly missing model data because the payment has only been created just yet
163
+ return payment.status;
164
+ }
164
165
  return PaymentStatus.Failed;
165
166
  }
166
167
 
@@ -218,7 +219,7 @@ export class StripeHelper {
218
219
  };
219
220
  statementDescriptor: string;
220
221
  i18n: I18n;
221
- metadata: { [key: string]: string | undefined};
222
+ metadata: { [key: string]: string | undefined };
222
223
  organization: Organization;
223
224
  },
224
225
  ): Promise<{ paymentUrl: string }> {
@@ -239,36 +240,14 @@ export class StripeHelper {
239
240
  });
240
241
  }
241
242
 
242
- let fee = 0;
243
- let directCharge = false;
244
- const vat = calculateVATPercentage(organization.address, organization.meta.VATNumber);
245
- function calculateFee(fixed: number, percentageTimes100: number) {
246
- return Math.round(Math.round(fixed + Math.max(1, totalPrice * percentageTimes100 / 100 / 100)) * (100 + vat) / 100); // € 0,21 + 0,2%
247
- }
248
-
249
- if (payment.method === PaymentMethod.iDEAL) {
250
- fee = calculateFee(21, 20); // € 0,21 + 0,2%
251
- }
252
- else if (payment.method === PaymentMethod.Bancontact) {
253
- fee = calculateFee(24, 20); // € 0,24 + 0,2%
254
- }
255
- else {
256
- fee = calculateFee(25, 150); // € 0,25 + 1,5%
257
- }
258
-
259
- if (stripeAccount.meta.type === 'standard') {
260
- // Submerchant is charged by Stripe for the fees directly
261
- fee = 0;
262
- directCharge = true;
263
- }
264
-
265
- payment.transferFee = fee * 100; // Convert back to 4 decimal places for storage
243
+ const directCharge = (stripeAccount.meta.type === 'standard');
244
+ const fee = directCharge ? 0 : Math.round(payment.transferFee / 100);
266
245
  const serviceFee = Math.round(payment.serviceFeePayout / 100);
267
246
 
268
247
  const fullMetadata = {
269
248
  ...(metadata ?? {}),
270
249
  organizationVATNumber: organization.meta.VATNumber,
271
- transactionFee: fee,
250
+ transferFee: fee,
272
251
  serviceFee: serviceFee, // For historic reasons, this is stored in cents
273
252
  };
274
253
 
@@ -335,8 +314,7 @@ export class StripeHelper {
335
314
  paymentIntentModel.accountId = stripeAccount.accountId;
336
315
  }
337
316
  await paymentIntentModel.save();
338
- }
339
- else {
317
+ } else {
340
318
  // Use checkout flow
341
319
  const data: Stripe.Checkout.SessionCreateParams = {
342
320
  mode: 'payment',