@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,85 @@
1
+ import { Migration } from '@simonbackx/simple-database';
2
+ import { AuditLog } from '@stamhoofd/models';
3
+ import { AuditLogReplacement, AuditLogReplacementType, getLegacyAuditLogEnumReplacement } from '@stamhoofd/structures';
4
+
5
+ export function migrateAuditLogReplacementLegacyEnums(replacement: AuditLogReplacement | undefined): { replacement: AuditLogReplacement | undefined; changed: boolean } {
6
+ if (!replacement) {
7
+ return { replacement, changed: false };
8
+ }
9
+
10
+ if (replacement.type === AuditLogReplacementType.Key) {
11
+ const migrated = getLegacyAuditLogEnumReplacement(replacement.value);
12
+ if (migrated) {
13
+ return { replacement: migrated, changed: true };
14
+ }
15
+ }
16
+
17
+ if (replacement.type === AuditLogReplacementType.Array) {
18
+ let changed = false;
19
+ const values = replacement.values.map((value) => {
20
+ const result = migrateAuditLogReplacementLegacyEnums(value);
21
+ changed ||= result.changed;
22
+ return result.replacement ?? value;
23
+ });
24
+
25
+ if (changed) {
26
+ const clone = replacement.clone();
27
+ clone.values = values;
28
+ return { replacement: clone, changed: true };
29
+ }
30
+ }
31
+
32
+ return { replacement, changed: false };
33
+ }
34
+
35
+ export function migrateAuditLogLegacyEnums(log: AuditLog): boolean {
36
+ let changed = false;
37
+
38
+ for (const [key, replacement] of log.replacements) {
39
+ const result = migrateAuditLogReplacementLegacyEnums(replacement);
40
+ if (result.changed && result.replacement) {
41
+ log.replacements.set(key, result.replacement);
42
+ changed = true;
43
+ }
44
+ }
45
+
46
+ for (const item of log.patchList) {
47
+ const key = migrateAuditLogReplacementLegacyEnums(item.key);
48
+ if (key.changed && key.replacement) {
49
+ item.key = key.replacement;
50
+ changed = true;
51
+ }
52
+
53
+ const oldValue = migrateAuditLogReplacementLegacyEnums(item.oldValue);
54
+ if (oldValue.changed) {
55
+ item.oldValue = oldValue.replacement;
56
+ changed = true;
57
+ }
58
+
59
+ const value = migrateAuditLogReplacementLegacyEnums(item.value);
60
+ if (value.changed) {
61
+ item.value = value.replacement;
62
+ changed = true;
63
+ }
64
+ }
65
+
66
+ return changed;
67
+ }
68
+
69
+ export default new Migration(async () => {
70
+ console.log('Start migrating legacy audit log enum replacements.');
71
+
72
+ const batchSize = 100;
73
+ let count = 0;
74
+
75
+ for await (const log of AuditLog.select().limit(batchSize).all()) {
76
+ if (!migrateAuditLogLegacyEnums(log)) {
77
+ continue;
78
+ }
79
+
80
+ await log.save();
81
+ count += 1;
82
+ }
83
+
84
+ console.log('Finished migrating legacy audit log enum replacements for ' + count + ' audit logs.');
85
+ });
@@ -0,0 +1,51 @@
1
+ import { column, Model } from '@simonbackx/simple-database';
2
+ import { EnumDecoder } from '@simonbackx/simple-encoding';
3
+ import { PaymentStatus } from '@stamhoofd/structures';
4
+
5
+ import { AuditLogService, modelLogDefinitions } from './AuditLogService.js';
6
+
7
+ enum UnregisteredAuditLogStatus {
8
+ Pending = 'Pending',
9
+ }
10
+
11
+ class UnregisteredAuditLogModel extends Model {
12
+ static table = 'unregistered_audit_log_models';
13
+
14
+ @column({ type: 'string', decoder: new EnumDecoder(UnregisteredAuditLogStatus) })
15
+ status = UnregisteredAuditLogStatus.Pending;
16
+ }
17
+
18
+ class RegisteredAuditLogModel extends Model {
19
+ static table = 'registered_audit_log_models';
20
+
21
+ @column({ type: 'string', decoder: new EnumDecoder(PaymentStatus) })
22
+ status = PaymentStatus.Pending;
23
+ }
24
+
25
+ describe('AuditLogService', () => {
26
+ const originalModelLogDefinitions = new Map(modelLogDefinitions);
27
+
28
+ afterEach(() => {
29
+ modelLogDefinitions.clear();
30
+ for (const [model, definition] of originalModelLogDefinitions) {
31
+ modelLogDefinitions.set(model, definition);
32
+ }
33
+ });
34
+
35
+ test('It throws when an audited enum column has no registered translation helper', () => {
36
+ modelLogDefinitions.clear();
37
+ modelLogDefinitions.set(UnregisteredAuditLogModel, {
38
+ async logEvent() {
39
+ // Not needed for validation.
40
+ },
41
+ });
42
+ modelLogDefinitions.set(RegisteredAuditLogModel, {
43
+ async logEvent() {
44
+ // Not needed for validation.
45
+ },
46
+ });
47
+
48
+ expect(() => AuditLogService.validateAuditLogEnumTranslations())
49
+ .toThrow('Missing audit log enum translation registration for: UnregisteredAuditLogModel.status');
50
+ });
51
+ });
@@ -1,5 +1,7 @@
1
1
  import type { ModelEvent } from '@simonbackx/simple-database';
2
2
  import { Model } from '@simonbackx/simple-database';
3
+ import { ArrayDecoder, EnumDecoder, MapDecoder, SymbolDecoder } from '@simonbackx/simple-encoding';
4
+ import { getAuditLogEnumDecoderObject, getAuditLogEnumType } from '@stamhoofd/structures';
3
5
  import type { AuditLogSource } from '@stamhoofd/structures';
4
6
  import { AsyncLocalStorage } from 'node:async_hooks';
5
7
 
@@ -15,6 +17,26 @@ export type AuditLogContextSettings = {
15
17
  };
16
18
  export const modelLogDefinitions = new Map<typeof Model, { logEvent: (event: ModelEvent) => Promise<any> }>();
17
19
 
20
+ function getEnumDecoders(decoder: unknown): EnumDecoder<any>[] {
21
+ if (decoder instanceof EnumDecoder) {
22
+ return [decoder];
23
+ }
24
+
25
+ if (decoder instanceof SymbolDecoder) {
26
+ return getEnumDecoders(decoder.decoder);
27
+ }
28
+
29
+ if (decoder instanceof ArrayDecoder) {
30
+ return getEnumDecoders(decoder.decoder);
31
+ }
32
+
33
+ if (decoder instanceof MapDecoder) {
34
+ return [...getEnumDecoders(decoder.keyDecoder), ...getEnumDecoders(decoder.valueDecoder)];
35
+ }
36
+
37
+ return [];
38
+ }
39
+
18
40
  export class AuditLogService {
19
41
  private constructor() { }
20
42
  static disableLocalStore = new AsyncLocalStorage<AuditLogContextSettings>();
@@ -47,6 +69,29 @@ export class AuditLogService {
47
69
 
48
70
  static listening = false;
49
71
 
72
+ static validateAuditLogEnumTranslations() {
73
+ const missing: string[] = [];
74
+
75
+ for (const [model] of modelLogDefinitions) {
76
+ const columns = model.columns;
77
+ if (!columns) {
78
+ continue;
79
+ }
80
+
81
+ for (const [key, column] of columns) {
82
+ for (const enumDecoder of getEnumDecoders(column.decoder)) {
83
+ if (!getAuditLogEnumType(getAuditLogEnumDecoderObject(enumDecoder))) {
84
+ missing.push(`${model.name}.${key}`);
85
+ }
86
+ }
87
+ }
88
+ }
89
+
90
+ if (missing.length > 0) {
91
+ throw new Error(`Missing audit log enum translation registration for: ${missing.join(', ')}`);
92
+ }
93
+ }
94
+
50
95
  static listen() {
51
96
  if (this.listening) {
52
97
  return;
@@ -1,7 +1,8 @@
1
1
  import { Model } from '@simonbackx/simple-database';
2
- import type { Organization, Payment } from '@stamhoofd/models';
3
- import { BalanceItem, CachedBalance, Document, MemberUser, Order, Webshop } from '@stamhoofd/models';
4
- import { AuditLogSource, BalanceItemStatus, BalanceItemType, OrderStatus, PaymentStatus, ReceivableBalanceType } from '@stamhoofd/structures';
2
+ import type { Organization } from '@stamhoofd/models';
3
+ import { BalanceItem, CachedBalance, Document, MemberUser, Order, Payment, Platform, Webshop } from '@stamhoofd/models';
4
+ import { SQL } from '@stamhoofd/sql';
5
+ import { AuditLogSource, BalanceItemStatus, BalanceItemType, OrderStatus, PaymentStatus, PaymentType, ReceivableBalanceType } from '@stamhoofd/structures';
5
6
  import { Formatter } from '@stamhoofd/utility';
6
7
  import { GroupedThrottledQueue } from '../helpers/GroupedThrottledQueue.js';
7
8
  import { ThrottledQueue } from '../helpers/ThrottledQueue.js';
@@ -33,11 +34,21 @@ const userUpdateQueue = new GroupedThrottledQueue(async (organizationId: string,
33
34
  }, { maxDelay: 10_000 });
34
35
 
35
36
  const organizationUpdateQueue = new GroupedThrottledQueue(async (organizationId: string, organizationIds: string[]) => {
36
- await CachedBalance.updateForOrganizations(organizationId, organizationIds);
37
+ const results = await CachedBalance.updateForOrganizations(organizationId, organizationIds);
37
38
 
38
39
  for (const payingOrganizationId of organizationIds) {
39
40
  await PaymentReallocationService.reallocate(organizationId, payingOrganizationId, ReceivableBalanceType.organization);
40
41
  }
42
+
43
+ const platform = await Platform.getShared();
44
+ if (organizationId === platform.membershipOrganizationId) {
45
+ // Clear failed payments
46
+ for (const [organizationId, result] of results) {
47
+ if (result.amountOpen === 0 && result.amountPending === 0) {
48
+ await STPackageService.markBalanceRestored(organizationId);
49
+ }
50
+ }
51
+ }
41
52
  }, { maxDelay: 60_000 });
42
53
 
43
54
  export const registrationUpdateQueue = new GroupedThrottledQueue(async (organizationId: string, registrationIds: string[]) => {
@@ -51,10 +62,10 @@ const bundleDiscountsUpdateQueue = new ThrottledQueue(async (registrationIds: st
51
62
  }
52
63
  }, { maxDelay: 10_000 });
53
64
 
54
- export const BalanceItemService = {
55
- listening: false,
65
+ export class BalanceItemService {
66
+ static listening = false;
56
67
 
57
- listen() {
68
+ static listen() {
58
69
  if (this.listening) {
59
70
  return;
60
71
  }
@@ -65,7 +76,7 @@ export const BalanceItemService = {
65
76
  }
66
77
 
67
78
  if (event.type === 'created' || event.type === 'deleted') {
68
- await this.scheduleUpdate(event.model);
79
+ this.scheduleUpdate(event.model);
69
80
  return;
70
81
  }
71
82
 
@@ -80,10 +91,10 @@ export const BalanceItemService = {
80
91
  || 'payingOrganizationId' in event.changedFields
81
92
  || 'registrationId' in event.changedFields
82
93
  ) {
83
- await this.scheduleUpdate(event.model);
94
+ this.scheduleUpdate(event.model);
84
95
  }
85
96
  });
86
- },
97
+ }
87
98
 
88
99
  /**
89
100
  * Schedule an update for the balance item:
@@ -91,8 +102,8 @@ export const BalanceItemService = {
91
102
  *
92
103
  * Does not execute the update immediately, but schedules it to be run in the background.
93
104
  */
94
- async scheduleUpdate(item: BalanceItem) {
95
- await this.scheduleUpdates([item]);
105
+ static scheduleUpdate(item: BalanceItem) {
106
+ this.scheduleUpdates([item]);
96
107
 
97
108
  // Todo: optimize
98
109
  if (item.type === BalanceItemType.RegistrationBundleDiscount) {
@@ -101,58 +112,58 @@ export const BalanceItemService = {
101
112
  bundleDiscountsUpdateQueue.addItem(item.registrationId);
102
113
  }
103
114
  }
104
- },
115
+ }
105
116
 
106
117
  /**
107
118
  * Call this when a payment or payment balance items have changed.
108
119
  * It will also call updateOutstanding automatically, so no need to call that separately again
109
120
  */
110
- async updatePaidAndPending(items: BalanceItem[]) {
121
+ static async updatePaidAndPending(items: BalanceItem[]) {
111
122
  console.log('updatePaidAndPending for', items.length, 'items');
112
123
  await BalanceItem.updatePricePaid(Formatter.uniqueArray(items.map(i => i.id)));
113
- await this.scheduleUpdates(items);
114
- },
124
+ this.scheduleUpdates(items);
125
+ }
115
126
 
116
127
  /**
117
128
  * Call this when a payment or payment balance items have changed.
118
129
  * It will also call updateOutstanding automatically, so no need to call that separately again
119
130
  */
120
- async updateInvoiced(items: (BalanceItem | string)[]) {
131
+ static async updateInvoiced(items: (BalanceItem | string)[]) {
121
132
  console.log('updateInvoiced for', items.length, 'items');
122
133
  await BalanceItem.updateInvoiced(Formatter.uniqueArray(items.map(i => typeof i === 'string' ? i : i.id)));
123
- },
134
+ }
124
135
 
125
136
  /**
126
137
  * In some situations we need immediate updates
127
138
  */
128
- async flushRegistrationDiscountsCache() {
139
+ static async flushRegistrationDiscountsCache() {
129
140
  await bundleDiscountsUpdateQueue.flushAndWait();
130
- },
141
+ }
131
142
 
132
143
  /**
133
144
  * Make sure all the pending changes for cached balances are run
134
145
  */
135
- async flushCaches(organizationId: string) {
146
+ static async flushCaches(organizationId: string) {
136
147
  await memberUpdateQueue.flushGroupAndWait(organizationId);
137
148
  await userUpdateQueue.flushGroupAndWait(organizationId);
138
149
  await organizationUpdateQueue.flushGroupAndWait(organizationId);
139
150
  await registrationUpdateQueue.flushGroupAndWait(organizationId);
140
151
  await bundleDiscountsUpdateQueue.flushAndWait();
141
- },
152
+ }
142
153
 
143
- async flushAll() {
154
+ static async flushAll() {
144
155
  await memberUpdateQueue.flushAndWait();
145
156
  await userUpdateQueue.flushAndWait();
146
157
  await organizationUpdateQueue.flushAndWait();
147
158
  await registrationUpdateQueue.flushAndWait();
148
159
  await bundleDiscountsUpdateQueue.flushAndWait();
149
- },
160
+ }
150
161
 
151
162
  /**
152
163
  * Update how many every object in the system owes or needs to be reimbursed
153
164
  * and also updates the pricePaid/pricePending cached values in Balance items and members
154
165
  */
155
- async scheduleUpdates(items: BalanceItem[]) {
166
+ static scheduleUpdates(items: BalanceItem[]) {
156
167
  console.log('Schedule outstanding balance for', items.length, 'items');
157
168
 
158
169
  for (const item of items) {
@@ -172,21 +183,21 @@ export const BalanceItemService = {
172
183
  registrationUpdateQueue.addItem(item.organizationId, item.registrationId);
173
184
  }
174
185
  }
175
- },
186
+ }
176
187
 
177
- scheduleUserUpdate(organizationId: string, userId: string) {
188
+ static scheduleUserUpdate(organizationId: string, userId: string) {
178
189
  userUpdateQueue.addItem(organizationId, userId);
179
- },
190
+ }
180
191
 
181
- scheduleMemberUpdate(organizationId: string, memberId: string) {
192
+ static cheduleMemberUpdate(organizationId: string, memberId: string) {
182
193
  memberUpdateQueue.addItem(organizationId, memberId);
183
- },
194
+ }
184
195
 
185
- scheduleOrganizationUpdate(organizationId: string, payingOrganizationId: string) {
196
+ static scheduleOrganizationUpdate(organizationId: string, payingOrganizationId: string) {
186
197
  organizationUpdateQueue.addItem(organizationId, payingOrganizationId);
187
- },
198
+ }
188
199
 
189
- async markDue(balanceItem: BalanceItem) {
200
+ static async markDue(balanceItem: BalanceItem) {
190
201
  if (balanceItem.status === BalanceItemStatus.Hidden) {
191
202
  balanceItem.status = BalanceItemStatus.Due;
192
203
  await balanceItem.save();
@@ -200,9 +211,21 @@ export const BalanceItemService = {
200
211
  await balanceItem.save();
201
212
  }
202
213
  }
203
- },
214
+ }
215
+
216
+ static async markPaid(balanceItem: BalanceItem, payment: Payment | null, organization: Organization) {
217
+ if (payment && payment.type === PaymentType.Chargeback) {
218
+ const realPayment = payment.reversingPaymentId ? (await Payment.getByID(payment.reversingPaymentId) ?? null) : null;
219
+ await this.handleChargeback(balanceItem, payment, realPayment, organization);
220
+ return;
221
+ }
222
+
223
+ if (payment && payment.price !== 0 && payment.refundedAmount === payment.price) {
224
+ // Charged back, already handled
225
+ // First cancel the chargeback if the payment should resucceed again
226
+ return;
227
+ }
204
228
 
205
- async markPaid(balanceItem: BalanceItem, payment: Payment | null, organization: Organization) {
206
229
  await this.markDue(balanceItem);
207
230
  let shouldMarkUpdated = true;
208
231
 
@@ -260,9 +283,9 @@ export const BalanceItemService = {
260
283
 
261
284
  balanceItem.paidAt = new Date();
262
285
  await balanceItem.save();
263
- },
286
+ }
264
287
 
265
- async markUpdated(balanceItem: BalanceItem, payment: Payment | null, organization: Organization) {
288
+ static async markUpdated(balanceItem: BalanceItem, payment: Payment | null, organization: Organization) {
266
289
  // For orders: mark order as changed (so they are refetched in front ends)
267
290
  if (balanceItem.orderId) {
268
291
  await AuditLogService.setContext({ source: AuditLogSource.Payment }, async () => {
@@ -274,9 +297,9 @@ export const BalanceItemService = {
274
297
  }
275
298
  });
276
299
  }
277
- },
300
+ }
278
301
 
279
- async undoPaid(balanceItem: BalanceItem, payment: Payment | null, organization: Organization) {
302
+ static async undoPaid(balanceItem: BalanceItem, payment: Payment | null, organization: Organization) {
280
303
  // If order
281
304
  if (balanceItem.orderId) {
282
305
  const order = await Order.getByID(balanceItem.orderId);
@@ -285,9 +308,24 @@ export const BalanceItemService = {
285
308
  await order.undoPaid(payment, organization);
286
309
  }
287
310
  }
288
- },
311
+ }
312
+
313
+ /**
314
+ *
315
+ */
316
+ static async handleChargeback(balanceItem: BalanceItem, chargeback: Payment | null, payment: Payment | null, organization: Organization) {
317
+ if (balanceItem.failedAt) {
318
+ // Already ran side effects
319
+ return;
320
+ }
289
321
 
290
- async markFailed(balanceItem: BalanceItem, payment: Payment, organization: Organization) {
322
+ // todo
323
+
324
+ balanceItem.failedAt = new Date();
325
+ await balanceItem.save();
326
+ }
327
+
328
+ static async markFailed(balanceItem: BalanceItem, payment: Payment, organization: Organization) {
291
329
  // If order
292
330
  if (balanceItem.orderId) {
293
331
  const order = await Order.getByID(balanceItem.orderId);
@@ -300,9 +338,10 @@ export const BalanceItemService = {
300
338
  }
301
339
  }
302
340
  }
303
- },
341
+ await this.handleChargeback(balanceItem, null, payment, organization);
342
+ }
304
343
 
305
- async undoFailed(balanceItem: BalanceItem, payment: Payment, organization: Organization) {
344
+ static async undoFailed(balanceItem: BalanceItem, payment: Payment, organization: Organization) {
306
345
  // If order
307
346
  if (balanceItem.orderId) {
308
347
  const order = await Order.getByID(balanceItem.orderId);
@@ -310,5 +349,63 @@ export const BalanceItemService = {
310
349
  await order.undoPaymentFailed(payment, organization);
311
350
  }
312
351
  }
313
- },
352
+ }
353
+
354
+ /**
355
+ * Apply discounts to the balance a user is going to checkout
356
+ */
357
+ static async applyDiscountsToCheckout({ minimumAmount, totalPrice, balanceItems, sellingOrganizationId, payingOrganizationId, payingUserId }: {
358
+ balanceItems: Map<BalanceItem, number>;
359
+ totalPrice: number;
360
+ minimumAmount?: number;
361
+ sellingOrganizationId: string;
362
+ payingOrganizationId?: string | null | undefined;
363
+ payingUserId?: string | null | undefined;
364
+ }) {
365
+ if (totalPrice <= (minimumAmount ?? 0)) {
366
+ return {
367
+ totalPrice,
368
+ };
369
+ }
370
+
371
+ let items: BalanceItem[];
372
+ if (payingOrganizationId) {
373
+ items = await BalanceItem.select()
374
+ .where('organizationId', sellingOrganizationId)
375
+ .where('payingOrganizationId', payingOrganizationId)
376
+ .whereNot('status', BalanceItemStatus.Hidden)
377
+ .where('priceOpen', '<', 0)
378
+ .fetch();
379
+ } else if (payingUserId) {
380
+ const memberUsers = await MemberUser.select().where('usersId', payingUserId).fetch();
381
+ const memberIds = Formatter.uniqueArray(memberUsers.map(mu => mu.membersId));
382
+
383
+ items = await BalanceItem.select()
384
+ .where('organizationId', sellingOrganizationId)
385
+ .where(
386
+ SQL.where('userId', payingUserId)
387
+ .or('memberId', memberIds),
388
+ )
389
+ .whereNot('status', BalanceItemStatus.Hidden)
390
+ .where('priceOpen', '<', 0)
391
+ .fetch();
392
+ } else {
393
+ return {
394
+ totalPrice,
395
+ };
396
+ }
397
+
398
+ for (const item of items) {
399
+ const remove = Math.min(totalPrice - (minimumAmount ?? 0), -item.priceOpen);
400
+ if (remove === 0) {
401
+ break;
402
+ }
403
+ balanceItems.set(item, -remove);
404
+ totalPrice -= remove;
405
+ }
406
+
407
+ return {
408
+ totalPrice,
409
+ };
410
+ }
314
411
  };
@@ -56,8 +56,7 @@ class StaticCpuService {
56
56
  }
57
57
  if (size === this.maxSamples) {
58
58
  return this.samples.reduce((a, b) => a + b, 0) / size;
59
- }
60
- else {
59
+ } else {
61
60
  // To read before current index
62
61
  const toReadBeforeIndex = Math.min(size, this.currentIndex);
63
62
  const toReadFromEnd = size - toReadBeforeIndex;
@@ -89,13 +88,11 @@ class StaticCpuService {
89
88
  if (five > 80) {
90
89
  // Danger zone, in this case we don't want to log all slow queries any longer because the information won't be trustworthy.
91
90
  SQLLogger.slowQueryThresholdMs = null;
92
- }
93
- else {
91
+ } else {
94
92
  if (five < 20) {
95
93
  // No load, safe to log all slow queries
96
94
  SQLLogger.slowQueryThresholdMs = 300;
97
- }
98
- else {
95
+ } else {
99
96
  SQLLogger.slowQueryThresholdMs = 500;
100
97
  }
101
98
  }
@@ -10,8 +10,7 @@ describe('DatabaseCollationService', () => {
10
10
  expect(error).toContain('MySQL collation mismatch');
11
11
  expect(error).toContain('expected utf8mb4_0900_ai_ci');
12
12
  expect(error).toContain('collation_connection=utf8mb4_general_ci');
13
- }
14
- finally {
13
+ } finally {
15
14
  await Database.statement('SET collation_connection = DEFAULT');
16
15
  }
17
16
  });
@@ -33,8 +33,7 @@ export class DocumentService {
33
33
  // If a group is changed - update the documents for it
34
34
  await Document.updateForGroup(event.model.id, event.model.organizationId);
35
35
  }
36
- }
37
- catch (e) {
36
+ } catch (e) {
38
37
  console.error('Failed to update documents after group change', e);
39
38
  }
40
39
  });
@@ -1,6 +1,6 @@
1
1
  import { SimpleError } from '@simonbackx/simple-errors';
2
2
  import { EventNotification, Member, MemberResponsibilityRecord, Organization, Platform, sendEmailTemplate, User } from '@stamhoofd/models';
3
- import type { EmailTemplateType} from '@stamhoofd/structures';
3
+ import type { EmailTemplateType } from '@stamhoofd/structures';
4
4
  import { PermissionLevel, Recipient, RecordCategory, Replacement } from '@stamhoofd/structures';
5
5
  import { Formatter } from '@stamhoofd/utility';
6
6
  import { AdminPermissionChecker } from '../helpers/AdminPermissionChecker.js';
@@ -74,8 +74,7 @@ export class FileSignService {
74
74
  }
75
75
 
76
76
  return true;
77
- }
78
- catch (e) {
77
+ } catch (e) {
79
78
  if (e instanceof jose.errors.JWSSignatureVerificationFailed) {
80
79
  return false;
81
80
  }
@@ -120,8 +119,7 @@ export class FileSignService {
120
119
  ...file,
121
120
  signedUrl: url,
122
121
  });
123
- }
124
- catch (e) {
122
+ } catch (e) {
125
123
  console.error('Failed to generate signedUrl for file:', e);
126
124
  return null;
127
125
  }