@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
@@ -2,50 +2,43 @@ import { isSimpleError, isSimpleErrors } from '@simonbackx/simple-errors';
2
2
  import { registerCron } from '@stamhoofd/crons';
3
3
  import type { Invoice } from '@stamhoofd/models';
4
4
  import { Organization, Payment, sendEmailTemplate } from '@stamhoofd/models';
5
- import type { IterableSQLSelect } from '@stamhoofd/sql';
6
5
  import { EmailTemplateType, InvoiceStruct, PaymentStatus, Replacement } from '@stamhoofd/structures';
7
6
  import { Formatter, Sorter } from '@stamhoofd/utility';
8
7
  import { AuthenticatedStructures } from '../helpers/AuthenticatedStructures.js';
9
8
  import { InvoiceService } from '../services/InvoiceService.js';
9
+ import { useSavedIterator } from './helpers/useSavedIterator.js';
10
+ import { isOutside } from './helpers/isOutside.js';
10
11
 
11
12
  registerCron('invoices', invoices);
12
13
 
13
- let lastFullRun = new Date(0);
14
- let savedIterator: IterableSQLSelect<Organization> | null = null;
14
+ const { iterate, isHoursAgo } = useSavedIterator(() => {
15
+ return Organization.select();
16
+ }, { limit: 10, maxQueries: 5 });
15
17
 
16
18
  const bootAt = new Date();
17
19
 
18
20
  async function invoices() {
19
- // Do not run within 30 minutes after boot to avoid creating multiple email models for emails that failed to send
20
- if (bootAt.getTime() > new Date().getTime() - 1000 * 60 * 30 && STAMHOOFD.environment !== 'development') {
21
+ // Do not run within 5 hours after boot
22
+ if (bootAt.getTime() > new Date().getTime() - 1000 * 60 * 60 * 5 && STAMHOOFD.environment !== 'development') {
21
23
  return;
22
24
  }
23
25
 
24
- if (lastFullRun.getTime() > new Date().getTime() - 1000 * 60 * 60 * 12) {
26
+ if (isOutside('03:00', '10:00')) {
25
27
  return;
26
28
  }
27
29
 
28
- if ((new Date().getHours() > 10 || new Date().getHours() < 3) && STAMHOOFD.environment !== 'development') {
30
+ if (!isHoursAgo(12)) {
29
31
  return;
30
32
  }
31
33
 
32
34
  // Get the next x organization to send e-mails for
33
- if (savedIterator === null) {
34
- savedIterator = Organization.select().limit(10).all();
35
- }
36
-
37
- for await (const organization of savedIterator.maxQueries(5)) {
35
+ for await (const organization of iterate()) {
38
36
  if (!organization.meta.invoicesEnabled) {
39
37
  continue;
40
38
  }
41
39
 
42
40
  // Create all invoices for this organization
43
- await createInvoicesFor(organization)
44
- }
45
-
46
- if (savedIterator.isDone) {
47
- savedIterator = null;
48
- lastFullRun = new Date();
41
+ await createInvoicesFor(organization);
49
42
  }
50
43
  }
51
44
 
@@ -57,12 +50,12 @@ async function createInvoicesFor(organization: Organization) {
57
50
 
58
51
  // Belgian rules: allowed to invoice up to the 15th day of the next month. We extend it with one month to fix mistakes.
59
52
  const today = Formatter.luxon();
60
- const startDate = today.day <= 15 ? today.minus({month: 2}).startOf('month') : today.minus({month: 1}).startOf('month');
53
+ const startDate = today.day <= 15 ? today.minus({ month: 2 }).startOf('month') : today.minus({ month: 1 }).startOf('month');
61
54
 
62
55
  // Don't invoice below 1 euro - unless we reached the timeout date for invoices (end of month + 15 days - 3 days margin)
63
- const invoiceLimit = 1_0000
56
+ const invoiceLimit = STAMHOOFD.environment === 'development' ? 0 : 1_0000;
64
57
  function getPaymentTimeoutDate(p: Payment) {
65
- return Formatter.luxon(p.paidAt ?? p.createdAt).plus({month: 1}).set({day: 15 - 3}).startOf('day').toJSDate()
58
+ return Formatter.luxon(p.paidAt ?? p.createdAt).plus({ month: 1 }).set({ day: 15 - 3 }).startOf('day').toJSDate();
66
59
  }
67
60
 
68
61
  console.log('Fetching all payments between ' + Formatter.dateTime(startDate.toJSDate()) + ' and now for ' + organization.name);
@@ -74,6 +67,7 @@ async function createInvoicesFor(organization: Organization) {
74
67
  .where('invoiceId', null)
75
68
  .where('customer', '!=', null)
76
69
  .where('payingOrganizationId', '!=', null)
70
+ .where('price', '!=', 0)
77
71
  .limit(1_000)
78
72
  .orderBy('payingOrganizationId')
79
73
  .fetch();
@@ -93,21 +87,21 @@ async function createInvoicesFor(organization: Organization) {
93
87
  const id = JSON.stringify(blob);
94
88
  const existing = groups.get(id);
95
89
  if (existing) {
96
- existing.push(payment)
90
+ existing.push(payment);
97
91
  } else {
98
- groups.set(id, [payment])
92
+ groups.set(id, [payment]);
99
93
  }
100
94
  }
101
95
 
102
96
  console.log('Invoicing ' + groups.size + ' customers');
103
97
 
104
- const errors: string[] = []
105
- const invoices: Invoice[] = []
98
+ const errors: string[] = [];
99
+ const invoices: Invoice[] = [];
106
100
  let skipped = 0;
107
101
 
108
102
  for (const [_, payments] of groups) {
109
103
  // Group from last to newest (so we use the last customer details if the address changed during the month)
110
- payments.sort((a, b) => Sorter.byDateValue(a.createdAt, b.createdAt))
104
+ payments.sort((a, b) => Sorter.byDateValue(a.createdAt, b.createdAt));
111
105
  const customer = payments[0].customer!.dynamicName;
112
106
  try {
113
107
  const generalStructs = await AuthenticatedStructures.paymentsGeneral(payments, false);
@@ -119,28 +113,28 @@ async function createInvoicesFor(organization: Organization) {
119
113
  });
120
114
  invoice.buildFromPayments();
121
115
 
122
- if (invoice.totalWithVAT < invoiceLimit) {
116
+ if (invoice.totalWithVAT >= 0 && invoice.totalWithVAT < invoiceLimit) {
123
117
  const first = new Date(Math.min(...payments.map(p => getPaymentTimeoutDate(p).getTime())));
124
118
  if (first > new Date()) {
125
- console.log('Delaying invoicing ' + customer + ' at ' + organization.id + ' until ' + Formatter.dateIso(first))
119
+ console.log('Delaying invoicing ' + customer + ' at ' + organization.id + ' until ' + Formatter.dateIso(first));
126
120
  skipped += 1;
127
121
  continue;
128
122
  } else {
129
- console.log('Invoiced low priced invoice, because of date ' + Formatter.dateIso(first) + ' being in the past')
123
+ console.log('Invoiced low priced invoice, because of date ' + Formatter.dateIso(first) + ' being in the past');
130
124
  }
131
125
  }
132
126
 
133
127
  const model = await InvoiceService.createFrom(organization, invoice);
134
- invoices.push(model)
128
+ invoices.push(model);
135
129
  } catch (e) {
136
130
  console.error(payments.map(p => p.id), e);
137
131
 
138
- const prefix = customer + ' ('+payments.map(p => '<a href="'+Formatter.escapeHtml('https://'+organization.getDashboardHost() + '/boekhouding/betalingen/' + p.id)+'">'+ Formatter.escapeHtml($t('%14a') + ' ' + p.id.substring(0, 8))+'</a>').join(', ') + '): ';
132
+ const prefix = customer + ' (' + payments.map(p => '<a href="' + Formatter.escapeHtml('https://' + organization.getDashboardHost() + '/boekhouding/betalingen/' + p.id) + '">' + Formatter.escapeHtml($t('%14a') + ' ' + p.id.substring(0, 8)) + '</a>').join(', ') + '): ';
139
133
 
140
134
  if (isSimpleError(e) || isSimpleErrors(e)) {
141
- errors.push(prefix + Formatter.escapeHtml(e.getHuman()))
135
+ errors.push(prefix + Formatter.escapeHtml(e.getHuman()));
142
136
  } else {
143
- errors.push(prefix + Formatter.escapeHtml($t('%1ED')))
137
+ errors.push(prefix + Formatter.escapeHtml($t('%1ED')));
144
138
  }
145
139
  }
146
140
  }
@@ -150,7 +144,7 @@ async function createInvoicesFor(organization: Organization) {
150
144
  if (errors.length) {
151
145
  await sendEmailTemplate(organization, {
152
146
  template: {
153
- type: EmailTemplateType.InvoiceGenerationErrors
147
+ type: EmailTemplateType.InvoiceGenerationErrors,
154
148
  },
155
149
  recipients: await organization.getAdminRecipients(),
156
150
  type: 'transactional',
@@ -158,9 +152,9 @@ async function createInvoicesFor(organization: Organization) {
158
152
  defaultReplacements: [
159
153
  Replacement.create({
160
154
  token: 'errors',
161
- html: '<ul><li>'+errors.join('</li><li>')+'</li></ul>' + (skipped > 0 ? '<p>'+Formatter.escapeHtml(skipped === 1 ? $t('%1U4') : $t('%1Sp', {count: skipped}))+'</p>' : '')
162
- })
163
- ]
164
- })
155
+ html: '<ul><li>' + errors.join('</li><li>') + '</li></ul>' + (skipped > 0 ? '<p>' + Formatter.escapeHtml(skipped === 1 ? $t('%1U4') : $t('%1Sp', { count: skipped })) + '</p>' : ''),
156
+ }),
157
+ ],
158
+ });
165
159
  }
166
160
  }
@@ -0,0 +1,50 @@
1
+ import { registerCron } from '@stamhoofd/crons';
2
+ import { Organization, Platform } from '@stamhoofd/models';
3
+ import { useSavedIterator } from './helpers/useSavedIterator.js';
4
+ import { STPackageService } from '../services/STPackageService.js';
5
+ import { BalanceItemStatus } from '@stamhoofd/structures';
6
+
7
+ // Charge manual service fees every night
8
+ registerCron('members-fees', chargeMembers);
9
+
10
+ const { iterate, isHoursAgo } = useSavedIterator(() => {
11
+ return Organization.select();
12
+ }, { limit: 10, maxQueries: 5 });
13
+
14
+ async function chargeMembers() {
15
+ if (STAMHOOFD.environment !== 'development' && (new Date().getHours() > 5 || new Date().getHours() < 2)) {
16
+ return;
17
+ }
18
+
19
+ if (!isHoursAgo(12)) {
20
+ return;
21
+ }
22
+
23
+ const membershipOrganizationId = (await Platform.getShared()).membershipOrganizationId;
24
+ if (!membershipOrganizationId) {
25
+ return;
26
+ }
27
+
28
+ const membershipOrganization = await Organization.getByID(membershipOrganizationId, true);
29
+
30
+ for await (const organization of iterate()) {
31
+ await chargeMemberFeesForOrganization(organization, membershipOrganization);
32
+ }
33
+ }
34
+
35
+ /**
36
+ * Charge the (increase in) per-member fees for every active package of an organization.
37
+ * For per-member packages this only charges the members that aren't paid/pending yet.
38
+ */
39
+ export async function chargeMemberFeesForOrganization(organization: Organization, sellingOrganization: Organization) {
40
+ const packages = await STPackageService.getActivePackages(organization.id);
41
+
42
+ for (const pack of packages) {
43
+ const item = await STPackageService.chargePackage(pack, sellingOrganization, organization.defaultCompanies[0]);
44
+ if (item) {
45
+ item.status = BalanceItemStatus.Due;
46
+ await item.save();
47
+ console.log('Charged', item.id);
48
+ }
49
+ }
50
+ }
@@ -5,15 +5,15 @@ import { PaymentService } from '../services/PaymentService.js';
5
5
 
6
6
  registerCron('mollie-chargebacks', checkMollieChargebacks);
7
7
 
8
- let lastRun: Date | null = null
8
+ let lastRun: Date | null = null;
9
9
  export async function checkMollieChargebacks() {
10
10
  if (STAMHOOFD.environment !== 'development') {
11
11
  if (lastRun && new Date().getTime() - lastRun.getTime() < 1000 * 60 * 60 * 24) {
12
- return
12
+ return;
13
13
  }
14
- lastRun = new Date()
14
+ lastRun = new Date();
15
15
  }
16
- await doCheckMollieChargebacks(false)
16
+ await doCheckMollieChargebacks(false);
17
17
  }
18
18
 
19
19
  export async function doCheckMollieChargebacks(checkAll = false) {
@@ -24,11 +24,11 @@ export async function doCheckMollieChargebacks(checkAll = false) {
24
24
  // Loop all mollie tokens
25
25
  for await (const token of MollieToken.select().limit(1).all({}, 'organizationId')) {
26
26
  // todo
27
- const sellingOrganization = await Organization.getByID(token.id)
27
+ const sellingOrganization = await Organization.getByID(token.id);
28
28
  if (sellingOrganization) {
29
- const service = await MollieService.create({ sellingOrganization })
29
+ const service = await MollieService.create({ sellingOrganization });
30
30
  if (service) {
31
- await checkMollieChargebacksFor(service, checkAll)
31
+ await checkMollieChargebacksFor(service, checkAll);
32
32
  }
33
33
  }
34
34
  }
@@ -40,11 +40,11 @@ export async function checkMollieChargebacksFor(service: MollieService, checkAll
40
40
  }
41
41
 
42
42
  // Check last 3 days
43
- const offset = new Date(Date.now() - 1000 * 60 * 60 * 24 * 4)
43
+ const offset = new Date(Date.now() - 1000 * 60 * 60 * 24 * 4);
44
44
 
45
45
  // due to a bug in mollie client code, testmode paramter is missing in the typescript definitions
46
46
  // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
47
- for await (const chargeback of service.client.chargebacks.iterate({sort: 'desc', testmode: service.testMode} as any)) {
47
+ for await (const chargeback of service.client.chargebacks.iterate({ sort: 'desc', testmode: service.testMode } as any)) {
48
48
  if (!checkAll && new Date(chargeback.createdAt) < offset) {
49
49
  break;
50
50
  }
@@ -63,22 +63,21 @@ export async function checkMollieChargebacksFor(service: MollieService, checkAll
63
63
  if (payment) {
64
64
  try {
65
65
  const amount = Math.round(parseFloat(chargeback.amount.value) * 100) * 100;
66
- const createdPayment = await PaymentService.registerChargeback(payment, amount)
66
+ const createdPayment = await PaymentService.registerChargeback(payment, amount, new Date(chargeback.createdAt));
67
67
 
68
68
  // Link Mollie chargeback ID (so we can set settlement later in the settlements cron)
69
69
  const molliePayment = new MolliePayment();
70
- molliePayment.paymentId = createdPayment.id
70
+ molliePayment.paymentId = createdPayment.id;
71
71
  molliePayment.mollieId = chargeback.id;
72
- await molliePayment.save()
72
+ await molliePayment.save();
73
73
  } catch (e) {
74
- console.error('Failed to register chargeback ' + chargeback.id, e)
74
+ console.error('Failed to register chargeback ' + chargeback.id, e);
75
75
  }
76
76
  }
77
77
  } else {
78
- console.error('Invalid chargeback payment id '+chargeback.paymentId + ', not found')
78
+ console.error('Invalid chargeback payment id ' + chargeback.paymentId + ', not found');
79
79
  }
80
80
  }
81
-
82
81
  }
83
82
 
84
83
  if (STAMHOOFD.environment !== 'development') {
@@ -0,0 +1,152 @@
1
+ import { registerCron } from '@stamhoofd/crons';
2
+ import { BalanceItem, Organization, Payment, Platform } from '@stamhoofd/models';
3
+ import { SQL } from '@stamhoofd/sql';
4
+ import { BalanceItemStatus, BalanceItemType, PaymentMethod, PaymentStatus } from '@stamhoofd/structures';
5
+ import { Formatter } from '@stamhoofd/utility';
6
+ import { BalanceItemService } from '../services/BalanceItemService.js';
7
+ import { PaymentService } from '../services/PaymentService.js';
8
+ import { VATService } from '../services/VATService.js';
9
+
10
+ // Charge manual service fees every night
11
+ registerCron('service-fees', chargeServiceFees);
12
+
13
+ async function chargeServiceFees() {
14
+ if (STAMHOOFD.environment !== 'development' && (new Date().getHours() > 5 || new Date().getHours() < 2)) {
15
+ return;
16
+ }
17
+ const membershipOrganizationId = (await Platform.getShared()).membershipOrganizationId;
18
+ if (!membershipOrganizationId) {
19
+ return;
20
+ }
21
+
22
+ const membershipOrganization = await Organization.getByID(membershipOrganizationId, true);
23
+
24
+ // Succeeded payments
25
+ const payments = await Payment.select()
26
+ .where('status', PaymentStatus.Succeeded)
27
+ .where('serviceFeeManual', '>', 0)
28
+ .where('serviceFeeManualCharged', 0)
29
+ .where('createdAt', '<', STAMHOOFD.environment !== 'development' ? new Date() : Formatter.dateIso(new Date(Date.now() + 1000 * 60 * 60 * 24)))
30
+ .limit(200)
31
+ .orderBy('createdAt', 'ASC')
32
+ .fetch();
33
+
34
+ // Pending payments older than 14 days with method = Transfer or PointOfSale
35
+ const pendingPayments = await Payment.select()
36
+ .where('status', [PaymentStatus.Pending, PaymentStatus.Created])
37
+ .where('method', [PaymentMethod.Transfer, PaymentMethod.PointOfSale, PaymentMethod.Unknown])
38
+ .where('serviceFeeManual', '>', 0)
39
+ .where('serviceFeeManualCharged', 0)
40
+ .where('createdAt', '<', Formatter.dateIso(new Date(Date.now() - 1000 * 60 * 60 * 24 * 14)))
41
+ .limit(200)
42
+ .orderBy('createdAt', 'ASC')
43
+ .fetch();
44
+
45
+ const allPayments = [...payments, ...pendingPayments];
46
+
47
+ console.log('Found ' + allPayments.length + ' payments to charge service fees for');
48
+
49
+ const mappedToOrganizationId = new Map<string, Payment[]>();
50
+ for (const payment of allPayments) {
51
+ if (!payment.organizationId) {
52
+ console.warn('Payment without organizationId, skipping', payment.id);
53
+ continue;
54
+ }
55
+ if (!mappedToOrganizationId.has(payment.organizationId)) {
56
+ mappedToOrganizationId.set(payment.organizationId, []);
57
+ }
58
+ mappedToOrganizationId.get(payment.organizationId)!.push(payment);
59
+ }
60
+
61
+ for (const [organizationId, payments] of mappedToOrganizationId.entries()) {
62
+ const organization = await Organization.getByID(organizationId);
63
+ if (!organization) {
64
+ console.warn('[chargeServiceFees] Organization not found for ID', organizationId);
65
+ continue;
66
+ }
67
+
68
+ // Group payments by Formatter.dateNumber(p.createdAt)
69
+ const groupedPayments: Map<string, Payment[]> = new Map();
70
+ for (const payment of payments) {
71
+ const date = Formatter.dateNumber(payment.createdAt);
72
+ if (!groupedPayments.has(date)) {
73
+ groupedPayments.set(date, []);
74
+ }
75
+ groupedPayments.get(date)!.push(payment);
76
+ }
77
+
78
+ for (const [_, payments] of groupedPayments) {
79
+ const totalFees = payments.reduce((sum, payment) => sum + payment.serviceFeeManual - payment.serviceFeeManualCharged, 0);
80
+ const groupedByDate = new Map<string, Payment[]>();
81
+
82
+ for (const p of payments) {
83
+ const date = Formatter.dateNumber(p.createdAt);
84
+ const existing = groupedByDate.get(date);
85
+ if (existing) {
86
+ existing.push(p);
87
+ } else {
88
+ groupedByDate.set(date, [p]);
89
+ }
90
+ }
91
+
92
+ for (const [day, payments] of groupedByDate) {
93
+ const date = new Date(Math.min(...payments.map(p => p.createdAt.getTime())));
94
+ const startOfDay = Formatter.luxon(date).startOf('day').toJSDate();
95
+ const endOfDay = Formatter.luxon(date).endOf('day').toJSDate();
96
+
97
+ // Ms don't work when querying MySQL, they get lost
98
+ startOfDay.setMilliseconds(0);
99
+ endOfDay.setMilliseconds(0);
100
+
101
+ const item = new BalanceItem();
102
+ item.type = BalanceItemType.ServiceFee;
103
+ item.description = $t('%1cJ', { date: day });
104
+ item.payingOrganizationId = organization.id;
105
+ item.organizationId = membershipOrganizationId;
106
+ item.VATPercentage = 21;
107
+ item.VATExcempt = VATService.getVATExcempt({
108
+ company: organization.defaultCompanies[0] ?? null,
109
+ sellingOrganization: membershipOrganization,
110
+ type: 'services',
111
+ });
112
+ item.VATIncluded = false;
113
+ item.quantity = 1;
114
+ item.unitPrice = totalFees;
115
+ item.createdAt = new Date();
116
+ item.status = BalanceItemStatus.Due;
117
+ item.startDate = startOfDay;
118
+ item.endDate = endOfDay;
119
+
120
+ // Check if we find an existing one
121
+ const existing = await BalanceItem.select()
122
+ .where('type', BalanceItemType.ServiceFee)
123
+ .where('organizationId', membershipOrganizationId)
124
+ .where('VATPercentage', item.VATPercentage)
125
+ .where('VATIncluded', item.VATIncluded)
126
+ .where('VATExcempt', item.VATExcempt)
127
+ .where('payingOrganizationId', organization.id)
128
+ .where('status', BalanceItemStatus.Due)
129
+ .where('startDate', startOfDay)
130
+ .where('endDate', endOfDay)
131
+ .first(false);
132
+
133
+ if (existing) {
134
+ existing.unitPrice += totalFees;
135
+ await existing.save();
136
+ BalanceItemService.scheduleUpdate(existing);
137
+ } else {
138
+ await item.save();
139
+ BalanceItemService.scheduleUpdate(item);
140
+ }
141
+
142
+ const { changedRows } = await Payment.update()
143
+ .where('id', payments.map(p => p.id))
144
+ .set('serviceFeeManualCharged', SQL.column('serviceFeeManual'))
145
+ .update();
146
+ if (changedRows !== payments.length) {
147
+ console.error('Unexpected change in serviceFeeManualCharged. Expected ' + payments.length + ', updated only ' + changedRows);
148
+ }
149
+ }
150
+ }
151
+ }
152
+ }
@@ -0,0 +1,48 @@
1
+ import { registerCron } from '@stamhoofd/crons';
2
+ import { Formatter } from '@stamhoofd/utility';
3
+ import { StripeInvoicer } from '../helpers/StripeInvoicer.js';
4
+ import { Organization, Platform } from '@stamhoofd/models';
5
+
6
+ registerCron('stripe-invoices', createStripeInvoices);
7
+
8
+ let lastStripeInvoice: Date | null = null;
9
+
10
+ async function createStripeInvoices() {
11
+ if (STAMHOOFD.environment !== 'production') {
12
+ // return;
13
+ }
14
+
15
+ if (STAMHOOFD.userMode === 'platform') {
16
+ return;
17
+ }
18
+
19
+ if (STAMHOOFD.STRIPE_CONNECT_METHOD === 'standard') {
20
+ return;
21
+ }
22
+
23
+ // Wait for the next day before doing a new check
24
+ const today = new Date();
25
+ if (lastStripeInvoice && Formatter.dateIso(lastStripeInvoice) === Formatter.dateIso(today)) {
26
+ console.log('Stripe check done for this day');
27
+ return;
28
+ }
29
+ console.log('Creating Stripe Invoices...');
30
+
31
+ if (!STAMHOOFD.STRIPE_SECRET_KEY) {
32
+ console.log('No stripe key set');
33
+ return;
34
+ }
35
+
36
+ const membershipOrganizationId = (await Platform.getShared()).membershipOrganizationId;
37
+ if (!membershipOrganizationId) {
38
+ return;
39
+ }
40
+
41
+ const membershipOrganization = await Organization.getByID(membershipOrganizationId, true);
42
+
43
+ const invoicer = new StripeInvoicer({
44
+ secretKey: STAMHOOFD.STRIPE_SECRET_KEY,
45
+ });
46
+ await invoicer.generateAllInvoices(membershipOrganization);
47
+ lastStripeInvoice = new Date();
48
+ }