@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
@@ -0,0 +1,419 @@
1
+ import { SimpleError } from '@simonbackx/simple-errors';
2
+ import { Email } from '@stamhoofd/email';
3
+ import { BalanceItem, BalanceItemPayment, Invoice, Organization, Payment, StripeAccount, User } from '@stamhoofd/models';
4
+ import { QueueHandler } from '@stamhoofd/queues';
5
+ import { BalanceItemRelation, BalanceItemRelationType, BalanceItemStatus, BalanceItemType, getPaymentProviderName, InvoiceStruct, PaymentCustomer, PaymentMethod, PaymentProvider, PaymentStatus, PaymentType, TranslatedString } from '@stamhoofd/structures';
6
+ import { Formatter } from '@stamhoofd/utility';
7
+ import Stripe from 'stripe';
8
+ import { InvoiceService } from '../services/InvoiceService.js';
9
+ import { PaymentService } from '../services/PaymentService.js';
10
+ import { AuthenticatedStructures } from './AuthenticatedStructures.js';
11
+ import { VATService } from '../services/VATService.js';
12
+
13
+ export class ApplicationFeeDetails {
14
+ transferFee = 0;
15
+ serviceFee = 0;
16
+ count = 0;
17
+ minimumDate: Date | null = null;
18
+ maximumDate: Date | null = null;
19
+
20
+ constructor(details: { count?: number; transferFee: number; serviceFee: number; minimumDate: Date | null; maximumDate: Date | null }) {
21
+ this.count = details.count ?? 0;
22
+ this.transferFee = details.transferFee;
23
+ this.serviceFee = details.serviceFee;
24
+ this.minimumDate = details.minimumDate;
25
+ this.maximumDate = details.maximumDate;
26
+ }
27
+
28
+ get amount() {
29
+ return this.transferFee + this.serviceFee;
30
+ }
31
+
32
+ static fromStripe(transaction: Stripe.BalanceTransaction) {
33
+ const source = transaction.source as Stripe.ApplicationFee;
34
+ const metadata = (source.originating_transaction as Stripe.Charge).metadata;
35
+
36
+ const serviceFeeStr = metadata.serviceFee as unknown;
37
+ if (serviceFeeStr === undefined || typeof serviceFeeStr !== 'string') {
38
+ throw new Error('Missing serviceFee metadata');
39
+ }
40
+
41
+ const parsed = parseInt(serviceFeeStr);
42
+ if (isNaN(parsed) || !isFinite(parsed)) {
43
+ throw new Error('Invalid serviceFee metadata');
44
+ }
45
+ const serviceFee = parsed * 100; // in cents
46
+ const transferFee = transaction.amount * 100 - serviceFee;
47
+
48
+ return new ApplicationFeeDetails({
49
+ count: 1,
50
+ serviceFee,
51
+ transferFee,
52
+ minimumDate: new Date(transaction.created * 1000),
53
+ maximumDate: new Date(transaction.created * 1000),
54
+ });
55
+ }
56
+
57
+ add(fee: Stripe.BalanceTransaction) {
58
+ this.combine(ApplicationFeeDetails.fromStripe(fee));
59
+ }
60
+
61
+ remove(fee: Stripe.BalanceTransaction) {
62
+ this.combine(ApplicationFeeDetails.fromStripe(fee));
63
+ }
64
+
65
+ combine(other: ApplicationFeeDetails) {
66
+ this.serviceFee += other.serviceFee;
67
+ this.transferFee += other.transferFee;
68
+ this.count += other.count;
69
+ if (other.minimumDate && (this.minimumDate === null || other.minimumDate < this.minimumDate)) {
70
+ this.minimumDate = other.minimumDate;
71
+ }
72
+ if (other.maximumDate && (this.maximumDate === null || other.maximumDate > this.maximumDate)) {
73
+ this.maximumDate = other.maximumDate;
74
+ }
75
+ }
76
+ }
77
+
78
+ class StripeReport {
79
+ readonly applicationFeesPerAccount = new Map<string, ApplicationFeeDetails>();
80
+
81
+ add(balanceTransaction: Stripe.BalanceTransaction) {
82
+ if (balanceTransaction.type === 'application_fee' || balanceTransaction.type === 'application_fee_refund') {
83
+ const source = balanceTransaction.source as Stripe.ApplicationFee;
84
+ const account = typeof source.account === 'string' ? source.account : source.account.id;
85
+
86
+ if (account) {
87
+ // We don't included refunded fees because these are test payments from test account
88
+ const currentAmount = this.applicationFeesPerAccount.get(account);
89
+ if (currentAmount === undefined) {
90
+ this.applicationFeesPerAccount.set(account, ApplicationFeeDetails.fromStripe(balanceTransaction));
91
+ } else {
92
+ currentAmount.add(balanceTransaction);
93
+ }
94
+ }
95
+ }
96
+ }
97
+
98
+ combine(report: StripeReport) {
99
+ for (const [account, amount] of report.applicationFeesPerAccount.entries()) {
100
+ const currentAmount = this.applicationFeesPerAccount.get(account);
101
+ if (currentAmount === undefined) {
102
+ this.applicationFeesPerAccount.set(account, amount);
103
+ } else {
104
+ currentAmount.combine(amount);
105
+ }
106
+ }
107
+ return this;
108
+ }
109
+ }
110
+
111
+ export class StripeReportInvoicer {
112
+ private readonly report: StripeReport;
113
+ start: Date;
114
+ end: Date;
115
+
116
+ constructor(report: StripeReport, start: Date, end: Date) {
117
+ this.report = report;
118
+ this.start = start;
119
+ this.end = end;
120
+ }
121
+
122
+ async generateInvoices(sellingOrganization: Organization, reference: string) {
123
+ const invoices: Invoice[] = [];
124
+ for (const [account, details] of this.report.applicationFeesPerAccount) {
125
+ try {
126
+ const invoice = await this.generateInvoice(sellingOrganization, reference, account, details);
127
+ if (invoice) {
128
+ invoices.push(invoice);
129
+ }
130
+ } catch (e) {
131
+ console.error(e);
132
+ // Send email notification of this error
133
+ Email.sendWebmaster({
134
+ subject: 'Aanmaken Stripe facturen voor ' + account + ' - ' + reference + ' mislukt',
135
+ html: 'Aanmaken Stripe facturen voor ' + account + ' - ' + reference + ' mislukt. <br><br> ' + e.toString(),
136
+ });
137
+ }
138
+ }
139
+ return invoices;
140
+ }
141
+
142
+ static async hasInvoice(sellingOrganization: Organization, reference: string) {
143
+ return !!await Invoice.select().where('organizationId', sellingOrganization.id).where('reference', reference).where('number', '!=', null).first(false);
144
+ }
145
+
146
+ async generateInvoice(sellingOrganization: Organization, reference: string, accountId: string, applicationFee: ApplicationFeeDetails) {
147
+ if (applicationFee.amount === 0) {
148
+ return;
149
+ }
150
+
151
+ const seller = sellingOrganization.meta.companies[0];
152
+ if (!seller) {
153
+ return;
154
+ }
155
+
156
+ // Search for the organization in Stamhoofd
157
+ const stripeAccount = await StripeAccount.select().where('accountId', accountId).first(false);
158
+ if (!stripeAccount) {
159
+ if (STAMHOOFD.environment === 'test') {
160
+ console.error('No organization found for Stripe account ' + accountId);
161
+ return;
162
+ }
163
+ throw new SimpleError({
164
+ code: 'stripe_account_not_found',
165
+ message: 'No organization found for Stripe account ' + accountId,
166
+ });
167
+ }
168
+ const organization = await Organization.getByID(stripeAccount.organizationId);
169
+
170
+ if (!organization) {
171
+ throw new SimpleError({
172
+ code: 'organization_not_found',
173
+ message: 'No organization found for Stripe account ' + accountId,
174
+ });
175
+ }
176
+
177
+ const existingInvoices = await Invoice.select()
178
+ .where('organizationId', sellingOrganization.id)
179
+ .where('payingOrganizationId', organization.id)
180
+ .where('reference', reference)
181
+ .where('number', '!=', null)
182
+ .fetch();
183
+
184
+ for (const i of existingInvoices) {
185
+ if (!i.stripeAccountId && i.totalWithVAT === applicationFee.amount) {
186
+ i.stripeAccountId = accountId;
187
+
188
+ console.log('Set stripe account id for invoice ' + i.number + ' to ' + accountId);
189
+ await i.save();
190
+ }
191
+
192
+ if (!i.stripeAccountId) {
193
+ throw new SimpleError({
194
+ code: 'invoice_already_exists_with_different_amount',
195
+ message: 'Invoice without account id already exists with different amount ' + organization.id + ' expected ' + applicationFee.amount + ' got ' + i.totalWithVAT + ' in invoice ' + i.number,
196
+ });
197
+ }
198
+
199
+ if (i.stripeAccountId === accountId) {
200
+ if (i.totalWithVAT !== applicationFee.amount) {
201
+ throw new SimpleError({
202
+ code: 'invoice_already_exists_with_different_amount',
203
+ message: 'Invoice already exists with different amount ' + organization.id + ' expected ' + applicationFee.amount + ' got ' + i.totalWithVAT + ' in invoice ' + i.number,
204
+ });
205
+ }
206
+
207
+ // Already invoiced
208
+ console.warn('Tried to invoice an already invoiced.');
209
+ return;
210
+ }
211
+ }
212
+
213
+ const customer = PaymentCustomer.create({
214
+ company: sellingOrganization.defaultCompanies[0],
215
+ });
216
+
217
+ const balanceItems: BalanceItem[] = [];
218
+
219
+ if (applicationFee.serviceFee !== 0) {
220
+ const item = new BalanceItem();
221
+ item.type = BalanceItemType.ServiceFee;
222
+ item.description = $t('%1Wd', {
223
+ startDate: Formatter.startDate(this.start, false, true),
224
+ endDate: Formatter.endDate(this.end, false, true),
225
+ });
226
+ item.relations.set(BalanceItemRelationType.PaymentProvider, BalanceItemRelation.create({
227
+ id: PaymentProvider.Stripe,
228
+ name: TranslatedString.create(getPaymentProviderName(PaymentProvider.Stripe)),
229
+ }));
230
+ item.payingOrganizationId = organization.id;
231
+ item.organizationId = sellingOrganization.id;
232
+ item.VATPercentage = 21;
233
+ item.VATExcempt = VATService.getVATExcempt({
234
+ company: organization.defaultCompanies[0] ?? null,
235
+ sellingOrganization,
236
+ type: 'services',
237
+ });
238
+ item.VATIncluded = true;
239
+ item.quantity = 1;
240
+ item.unitPrice = applicationFee.serviceFee;
241
+ item.createdAt = new Date();
242
+ item.status = BalanceItemStatus.Hidden;
243
+ item.startDate = this.start;
244
+ item.endDate = this.end;
245
+ await item.save();
246
+ balanceItems.push(item);
247
+ }
248
+
249
+ if (applicationFee.transferFee !== 0) {
250
+ const item = new BalanceItem();
251
+ item.type = BalanceItemType.TransferFee;
252
+ item.description = $t('%1Xx', {
253
+ startDate: Formatter.startDate(this.start, false, true),
254
+ endDate: Formatter.endDate(this.end, false, true),
255
+ });
256
+ item.relations.set(BalanceItemRelationType.PaymentProvider, BalanceItemRelation.create({
257
+ id: PaymentProvider.Stripe,
258
+ name: TranslatedString.create(getPaymentProviderName(PaymentProvider.Stripe)),
259
+ }));
260
+ item.payingOrganizationId = organization.id;
261
+ item.organizationId = sellingOrganization.id;
262
+ item.VATPercentage = 21;
263
+ item.VATExcempt = VATService.getVATExcempt({
264
+ company: organization.defaultCompanies[0] ?? null,
265
+ sellingOrganization,
266
+ type: 'services',
267
+ });
268
+ item.VATIncluded = true;
269
+ item.quantity = 1;
270
+ item.unitPrice = applicationFee.transferFee;
271
+ item.createdAt = new Date();
272
+ item.status = BalanceItemStatus.Hidden;
273
+ item.startDate = this.start;
274
+ item.endDate = this.end;
275
+ await item.save();
276
+ balanceItems.push(item);
277
+ }
278
+
279
+ const systemUser = await User.getSystem();
280
+
281
+ // Done validation
282
+ const payment = new Payment();
283
+ payment.adminUserId = systemUser.id;
284
+
285
+ // Who will receive this money?
286
+ payment.organizationId = sellingOrganization.id;
287
+
288
+ // Who paid
289
+ payment.payingOrganizationId = organization.id;
290
+ payment.customer = customer;
291
+
292
+ payment.status = PaymentStatus.Pending;
293
+ payment.price = applicationFee.amount;
294
+ payment.roundingAmount = 0;
295
+ payment.method = PaymentMethod.Unknown;
296
+ payment.type = PaymentType.Payment;
297
+ payment.createMandate = null;
298
+
299
+ payment.provider = PaymentProvider.Stripe;
300
+ payment.stripeAccountId = stripeAccount.id;
301
+ await payment.save();
302
+
303
+ for (const balanceItem of balanceItems) {
304
+ // Create one balance item payment to pay it in one payment
305
+ const balanceItemPayment = new BalanceItemPayment();
306
+ balanceItemPayment.balanceItemId = balanceItem.id;
307
+ balanceItemPayment.paymentId = payment.id;
308
+ balanceItemPayment.organizationId = organization.id;
309
+ balanceItemPayment.price = balanceItem.priceWithVAT;
310
+ await balanceItemPayment.save();
311
+ }
312
+
313
+ // Create invoice (before payment, to prevent auto-invoicing)
314
+ const generalStructs = await AuthenticatedStructures.paymentsGeneral([payment], false);
315
+
316
+ const invoiceStruct = InvoiceStruct.create({
317
+ seller,
318
+ customer: payment.customer,
319
+ payments: generalStructs,
320
+ });
321
+ invoiceStruct.buildFromPayments();
322
+
323
+ invoiceStruct.reference = reference;
324
+ invoiceStruct.stripeAccountId = accountId;
325
+
326
+ await PaymentService.handlePaymentStatusUpdate(payment, sellingOrganization, PaymentStatus.Succeeded, this.end);
327
+ const invoice = await InvoiceService.createFrom(sellingOrganization, invoiceStruct);
328
+
329
+ return invoice;
330
+ }
331
+ }
332
+
333
+ export class StripeInvoicer {
334
+ private stripe: Stripe;
335
+
336
+ constructor({ secretKey }: { secretKey: string }) {
337
+ this.stripe = new Stripe(secretKey, { apiVersion: '2024-06-20', typescript: true, maxNetworkRetries: 1, timeout: 10000 });
338
+ }
339
+
340
+ static getMonthUnixStartEnd(date: Date) {
341
+ const start = Math.floor((new Date(date.getFullYear(), date.getMonth(), 1, 0, 0, 0, 0).getTime()) / 1000);
342
+ const end = Math.ceil((new Date(date.getFullYear(), date.getMonth() + 1, 1, 0, 0, 0, 0).getTime() - 1000) / 1000);
343
+ return { start, end };
344
+ }
345
+
346
+ // Loops all months until all invoices are generated
347
+ async generateAllInvoices(sellingOrganization: Organization, options?: { force?: boolean; start?: Date }) {
348
+ const startMonth = options?.start ?? new Date(2026, 0, 1);
349
+ const stopAt = new Date(Date.now() + (STAMHOOFD.environment === 'production' ? (-60 * 60 * 24 * 1000) : (60 * 60 * 24 * 1000 * 31))); // One day margin before creating invoices
350
+ let currentMonth = new Date(startMonth);
351
+
352
+ while (true) {
353
+ const { start, end } = StripeInvoicer.getMonthUnixStartEnd(currentMonth);
354
+
355
+ if (end >= stopAt.getTime() / 1000) {
356
+ // Stop
357
+ break;
358
+ }
359
+ await this.generateInvoices(sellingOrganization, currentMonth, options);
360
+ currentMonth = new Date(currentMonth.getFullYear(), currentMonth.getMonth() + 1, 1);
361
+ }
362
+ }
363
+
364
+ async generateInvoices(sellingOrganization: Organization, month: Date, options?: { force?: boolean }) {
365
+ const { start, end } = StripeInvoicer.getMonthUnixStartEnd(month);
366
+ const reference = 'stripe-fees-' + Formatter.dateIso(new Date(start * 1000));
367
+
368
+ try {
369
+ await QueueHandler.schedule(reference, async () => {
370
+ if (!options?.force && await StripeReportInvoicer.hasInvoice(sellingOrganization, reference)) {
371
+ console.log('Already invoiced month', reference);
372
+ return;
373
+ }
374
+
375
+ console.log('Generating invoices for ', reference, start, end);
376
+ const reportFees = await this.fetchBalanceItems({
377
+ created: {
378
+ gte: start,
379
+ lte: end,
380
+ },
381
+ type: 'application_fee',
382
+ expand: ['data.source', 'data.source.originating_transaction'],
383
+ });
384
+ const reportRefund = await this.fetchBalanceItems({
385
+ created: {
386
+ gte: start,
387
+ lte: end,
388
+ },
389
+ type: 'application_fee_refund',
390
+ expand: ['data.source', 'data.source.originating_transaction'],
391
+ });
392
+
393
+ const report = reportFees.combine(reportRefund);
394
+ const invoicer = new StripeReportInvoicer(report, new Date(start * 1000), new Date(end * 1000));
395
+ await invoicer.generateInvoices(sellingOrganization, reference);
396
+ });
397
+ } catch (e) {
398
+ console.error(e);
399
+
400
+ // Send email notification of this error
401
+ Email.sendWebmaster({
402
+ subject: 'Aanmaken Stripe facturen voor ' + Formatter.dateNumber(month) + ' mislukt',
403
+ html: 'Aanmaken Stripe facturen voor ' + Formatter.dateNumber(month) + ' mislukt. <br><br> ' + e.toString(),
404
+ });
405
+ }
406
+ }
407
+
408
+ private async fetchBalanceItems(options: Stripe.BalanceTransactionListParams) {
409
+ // For the given payout, fetch all balance items
410
+ const params = { ...options };
411
+ const report = new StripeReport();
412
+
413
+ for await (const balanceItem of this.stripe.balanceTransactions.list(params)) {
414
+ report.add(balanceItem);
415
+ }
416
+
417
+ return report;
418
+ }
419
+ }
@@ -53,14 +53,12 @@ export class StripePayoutChecker {
53
53
  // Get all payments for this payout
54
54
  try {
55
55
  await this.fetchBalanceItems(payout);
56
- }
57
- catch (e) {
56
+ } catch (e) {
58
57
  console.error('Error for payout ' + payout.id);
59
58
  console.error(e);
60
59
  }
61
60
  }
62
- }
63
- catch (e) {
61
+ } catch (e) {
64
62
  console.error(e);
65
63
  }
66
64
  }
@@ -119,8 +117,7 @@ export class StripePayoutChecker {
119
117
  if (stripePayments.length === 1) {
120
118
  paymentId = stripePayments[0].paymentId;
121
119
  console.log('Found missing payment metadata for payment intent ', originatingTransaction.payment_intent, paymentId);
122
- }
123
- else {
120
+ } else {
124
121
  // Probably a card payment
125
122
  // Search for the checkout session
126
123
  const checkoutSession = await this.stripePlatform.checkout.sessions.list({
@@ -138,12 +135,10 @@ export class StripePayoutChecker {
138
135
  if (stripeCheckoutSessions.length === 1) {
139
136
  paymentId = stripeCheckoutSessions[0].paymentId;
140
137
  console.log('Found missing payment metadata for payment intent ', originatingTransaction.payment_intent, paymentId);
141
- }
142
- else {
138
+ } else {
143
139
  console.log('No payment found for checkout session ' + session.id);
144
140
  }
145
- }
146
- else {
141
+ } else {
147
142
  console.log('No Stripe Checkout Sessions found for payment intent ' + paymentIntentId);
148
143
  }
149
144
  }
@@ -1,21 +1,22 @@
1
1
  import { Organization, Platform } from '@stamhoofd/models';
2
2
  import { QueueHandler } from '@stamhoofd/queues';
3
- import type { OrganizationTag} from '@stamhoofd/structures';
3
+ import type { OrganizationTag } from '@stamhoofd/structures';
4
4
  import { AuditLogSource, TagHelper as SharedTagHelper } from '@stamhoofd/structures';
5
5
  import { AuditLogService } from '../services/AuditLogService.js';
6
6
 
7
7
  export class TagHelper extends SharedTagHelper {
8
8
  static async updateOrganizations() {
9
9
  const queueId = 'update-tags-on-organizations';
10
- QueueHandler.cancel(queueId);
10
+ QueueHandler.abort(queueId);
11
11
 
12
12
  await AuditLogService.setContext({ source: AuditLogSource.System }, async () => {
13
- await QueueHandler.schedule(queueId, async () => {
13
+ await QueueHandler.schedule(queueId, async ({ abort }) => {
14
14
  const sharedPlatform = await Platform.getShared();
15
15
 
16
16
  const tagCounts = new Map<string, number>();
17
17
 
18
18
  for await (const organization of Organization.select().all()) {
19
+ abort.throwIfAborted();
19
20
  organization.meta.tags = this.getAllTagsFromHierarchy(organization.meta.tags, sharedPlatform.config.tags);
20
21
 
21
22
  for (const tag of organization.meta.tags) {
@@ -26,11 +27,9 @@ export class TagHelper extends SharedTagHelper {
26
27
 
27
28
  // Reload platform to avoid race conditions
28
29
  const platform = await Platform.getForEditing();
29
- for (const [tag, count] of tagCounts.entries()) {
30
- const tagObject = platform.config.tags.find(t => t.id === tag);
31
- if (tagObject) {
32
- tagObject.organizationCount = count;
33
- }
30
+ for (const tagObject of platform.config.tags) {
31
+ abort.throwIfAborted();
32
+ tagObject.organizationCount = tagCounts.get(tagObject.id) ?? 0;
34
33
  }
35
34
  await platform.save();
36
35
  });
@@ -33,8 +33,7 @@ export function deleteExpiredTemporaryMemberAccess() {
33
33
 
34
34
  if (filtered.length === 0) {
35
35
  userMemberTemporaryAccessCache.delete(userId);
36
- }
37
- else {
36
+ } else {
38
37
  userMemberTemporaryAccessCache.set(userId, filtered);
39
38
  }
40
39
  }
@@ -83,8 +83,7 @@ export class ThrottledQueue<T> {
83
83
  const batch = batchSize < items.length ? items.slice(i, i + batchSize) : items;
84
84
  try {
85
85
  await this.handler(batch);
86
- }
87
- catch (error) {
86
+ } catch (error) {
88
87
  console.error('Error processing batch in ThrottledQueue:', error);
89
88
  }
90
89
  }
@@ -51,8 +51,7 @@ export class UitpasTokenRepository {
51
51
  let repo = new UitpasTokenRepository(model);
52
52
  try {
53
53
  await repo.getNewAccessToken();
54
- }
55
- catch (e) {
54
+ } catch (e) {
56
55
  return false; // not valid
57
56
  }
58
57
  // valid -> store
@@ -55,8 +55,7 @@ export class ViesHelperStatic {
55
55
  if (company.companyNumber) {
56
56
  if (company.VATNumber !== null && company.address.country === Country.Belgium) {
57
57
  // Already validated
58
- }
59
- else {
58
+ } else {
60
59
  // Need to validate
61
60
  const result = await ViesHelper.checkCompanyNumber(company.address.country, company.companyNumber);
62
61
  patch.companyNumber = result.companyNumber;
@@ -103,12 +102,10 @@ export class ViesHelperStatic {
103
102
  companyNumber: corrected.substring(2),
104
103
  VATNumber: corrected,
105
104
  };
106
- }
107
- catch (e) {
105
+ } catch (e) {
108
106
  if (isSimpleError(e) || isSimpleErrors(e)) {
109
107
  // Ignore: normal that it is not a valid VAT number
110
- }
111
- else {
108
+ } else {
112
109
  // Other errors should be thrown
113
110
  throw e;
114
111
  }
@@ -128,7 +125,7 @@ export class ViesHelperStatic {
128
125
  if (!result.isValid) {
129
126
  throw new SimpleError({
130
127
  code: 'invalid_field',
131
- message: $t('%1To', {'number': vatNumber}),
128
+ message: $t('%1To', { number: vatNumber }),
132
129
  field: 'VATNumber',
133
130
  });
134
131
  }
@@ -136,7 +133,7 @@ export class ViesHelperStatic {
136
133
  const formatted = result.value ?? vatNumber;
137
134
 
138
135
  if (STAMHOOFD.environment === 'development' && formatted === 'NL301828519B01') {
139
- return formatted
136
+ return formatted;
140
137
  }
141
138
 
142
139
  try {
@@ -154,12 +151,11 @@ export class ViesHelperStatic {
154
151
  if (!response.valid) {
155
152
  throw new SimpleError({
156
153
  code: 'invalid_field',
157
- message: $t('%1TG', {'vat-number': formatted}),
154
+ message: $t('%1TG', { 'vat-number': formatted }),
158
155
  field: 'VATNumber',
159
156
  });
160
157
  }
161
- }
162
- catch (e) {
158
+ } catch (e) {
163
159
  if (isSimpleError(e) || isSimpleErrors(e)) {
164
160
  throw e;
165
161
  }
@@ -1,6 +1,6 @@
1
1
  import type { XlsxTransformerColumn, XlsxTransformerConcreteColumn } from '@stamhoofd/excel-writer';
2
2
  import { isXlsxTransformerConcreteColumn } from '@stamhoofd/excel-writer';
3
- import type { Address, Parent, PlatformMember, RecordAnswer, RecordSettings} from '@stamhoofd/structures';
3
+ import type { Address, Parent, PlatformMember, RecordAnswer, RecordSettings } from '@stamhoofd/structures';
4
4
  import { CountryHelper, ParentTypeHelper, RecordCategory, RecordType } from '@stamhoofd/structures';
5
5
 
6
6
  export class XlsxTransformerColumnHelper {
@@ -25,7 +25,7 @@ export const registrationCachedBalanceJoin = SQL.leftJoin(
25
25
  new SQLSelectAs(
26
26
  new SQLSum(
27
27
  SQL.calculation(SQL.column('amountOpen'))
28
- .add(SQL.column('amountPending'))
28
+ .add(SQL.column('amountPending')),
29
29
  ),
30
30
  new SQLAlias('toPay'),
31
31
  ),
@@ -33,7 +33,7 @@ export const registrationCachedBalanceJoin = SQL.leftJoin(
33
33
  new SQLSum(
34
34
  SQL.calculation(SQL.column('amountOpen'))
35
35
  .add(SQL.column('amountPaid'))
36
- .add(SQL.column('amountPending'))
36
+ .add(SQL.column('amountPending')),
37
37
  ),
38
38
  new SQLAlias('price'),
39
39
  ),
@@ -1,7 +1,7 @@
1
1
  import type { AutoEncoderPatchType } from '@simonbackx/simple-encoding';
2
2
  import { isSimpleError, SimpleError } from '@simonbackx/simple-errors';
3
3
  import { Member } from '@stamhoofd/models';
4
- import type { MemberDetails, ReviewTimes} from '@stamhoofd/structures';
4
+ import type { MemberDetails, ReviewTimes } from '@stamhoofd/structures';
5
5
  import { UitpasSocialTariff, UitpasSocialTariffStatus } from '@stamhoofd/structures';
6
6
  import type { GetPassResponse } from '../services/uitpas/PassholderEndpoints.js';
7
7
  import { UitpasService } from '../services/uitpas/UitpasService.js';
@@ -42,8 +42,7 @@ export async function updateMemberDetailsUitpasNumberForPatch(memberId: string,
42
42
 
43
43
  try {
44
44
  isUpdated = await updateMemberDetailsUitpasNumber(details);
45
- }
46
- catch (error: any) {
45
+ } catch (error: any) {
47
46
  const isUitpasEqual = previousUitpasNumber !== null && previousUitpasNumber === details.uitpasNumberDetails.uitpasNumber;
48
47
 
49
48
  if (isSimpleError(error)) {
@@ -94,8 +93,7 @@ export function uitpasApiResponseToSocialTariff(response: GetPassResponse): Uitp
94
93
  if (response.socialTariff.endDate) {
95
94
  try {
96
95
  endDate = new Date(response.socialTariff.endDate);
97
- }
98
- catch (e) {
96
+ } catch (e) {
99
97
  console.error(e);
100
98
  console.error('endDate: ', response.socialTariff.endDate);
101
99