@stamhoofd/backend 2.120.6 → 2.122.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (434) hide show
  1. package/output-2.txt +1 -0
  2. package/output.txt +1 -0
  3. package/package.json +70 -53
  4. package/src/audit-logs/EventNotificationLogger.ts +40 -0
  5. package/src/audit-logs/ModelLogger.ts +19 -15
  6. package/src/audit-logs/OrganizationLogger.ts +1 -1
  7. package/src/audit-logs/RegistrationInvitationLogger.ts +46 -0
  8. package/src/audit-logs/init.ts +9 -1
  9. package/src/boot.ts +19 -18
  10. package/src/crons/amazon-ses.ts +13 -26
  11. package/src/crons/balance-emails.ts +3 -5
  12. package/src/crons/clearExcelCache.test.ts +1 -1
  13. package/src/crons/clearExcelCache.ts +1 -2
  14. package/src/crons/drip-emails.ts +42 -0
  15. package/src/crons/endFunctionsOfUsersWithoutRegistration.ts +4 -0
  16. package/src/crons/helpers/isOutside.ts +11 -0
  17. package/src/crons/helpers/useSavedIterator.ts +32 -0
  18. package/src/crons/index.ts +7 -0
  19. package/src/crons/invoices.ts +160 -0
  20. package/src/crons/members-fees.ts +50 -0
  21. package/src/crons/mollie-chargebacks.ts +86 -0
  22. package/src/crons/service-fees.ts +152 -0
  23. package/src/crons/stripe-invoices.ts +48 -0
  24. package/src/crons/transfer-fees.ts +176 -0
  25. package/src/crons/update-cached-balances.ts +4 -4
  26. package/src/crons.ts +54 -74
  27. package/src/debug.ts +4 -2
  28. package/src/email-recipient-loaders/documents.ts +1 -1
  29. package/src/email-recipient-loaders/members.ts +1 -1
  30. package/src/email-recipient-loaders/payments.ts +87 -46
  31. package/src/email-recipient-loaders/registrations.ts +1 -1
  32. package/src/email-replacements/getEmailReplacementsForPayment.ts +5 -9
  33. package/src/endpoints/admin/members/ChargeMembersEndpoint.ts +1 -1
  34. package/src/endpoints/admin/memberships/ChargeMembershipsEndpoint.ts +1 -1
  35. package/src/endpoints/admin/memberships/GetChargeMembershipsSummaryEndpoint.ts +3 -5
  36. package/src/endpoints/admin/organizations/ChargeOrganizationsEndpoint.ts +1 -1
  37. package/src/endpoints/admin/organizations/GetOrganizationsCountEndpoint.ts +1 -1
  38. package/src/endpoints/admin/organizations/GetOrganizationsEndpoint.ts +13 -7
  39. package/src/endpoints/admin/organizations/PatchOrganizationsEndpoint.ts +4 -6
  40. package/src/endpoints/admin/organizations/PatchPackagesEndpoint.test.ts +10 -6
  41. package/src/endpoints/admin/organizations/PatchPackagesEndpoint.ts +39 -21
  42. package/src/endpoints/admin/registrations/ChargeRegistrationsEndpoint.ts +1 -1
  43. package/src/endpoints/auth/CreateAdminEndpoint.ts +23 -5
  44. package/src/endpoints/auth/CreateTokenEndpoint.ts +2 -2
  45. package/src/endpoints/auth/DeleteTokenEndpoint.ts +1 -1
  46. package/src/endpoints/auth/DeleteUserEndpoint.ts +3 -3
  47. package/src/endpoints/auth/ForgotPasswordEndpoint.ts +1 -1
  48. package/src/endpoints/auth/GetOtherUserEndpoint.ts +1 -1
  49. package/src/endpoints/auth/GetUserEndpoint.ts +1 -1
  50. package/src/endpoints/auth/OpenIDConnectAuthTokenEndpoint.ts +1 -1
  51. package/src/endpoints/auth/OpenIDConnectStartEndpoint.ts +1 -1
  52. package/src/endpoints/auth/PatchUserEndpoint.ts +5 -8
  53. package/src/endpoints/auth/PollEmailVerificationEndpoint.ts +1 -1
  54. package/src/endpoints/auth/RetryEmailVerificationEndpoint.ts +1 -1
  55. package/src/endpoints/auth/SignupEndpoint.ts +2 -3
  56. package/src/endpoints/auth/VerifyEmailEndpoint.ts +20 -3
  57. package/src/endpoints/frontend/FrontendEnvironmentEndpoint.ts +2 -1
  58. package/src/endpoints/global/addresses/SearchRegionsEndpoint.ts +2 -4
  59. package/src/endpoints/global/addresses/ValidateAddressEndpoint.ts +1 -1
  60. package/src/endpoints/global/audit-logs/GetAuditLogsEndpoint.ts +5 -7
  61. package/src/endpoints/global/caddy/CheckDomainCertEndpoint.ts +2 -2
  62. package/src/endpoints/global/email/CreateEmailEndpoint.ts +3 -4
  63. package/src/endpoints/global/email/GetAdminEmailsEndpoint.test.ts +1 -1
  64. package/src/endpoints/global/email/GetAdminEmailsEndpoint.ts +1 -2
  65. package/src/endpoints/global/email/GetEmailAddressEndpoint.ts +4 -6
  66. package/src/endpoints/global/email/GetEmailEndpoint.ts +1 -1
  67. package/src/endpoints/global/email/GetUserEmailsEndpoint.test.ts +1 -1
  68. package/src/endpoints/global/email/GetUserEmailsEndpoint.ts +1 -1
  69. package/src/endpoints/global/email/ManageEmailAddressEndpoint.ts +6 -10
  70. package/src/endpoints/global/email/PatchEmailEndpoint.test.ts +4 -7
  71. package/src/endpoints/global/email/PatchEmailEndpoint.ts +6 -9
  72. package/src/endpoints/global/email-recipients/GetEmailRecipientsCountEndpoint.ts +1 -1
  73. package/src/endpoints/global/email-recipients/GetEmailRecipientsEndpoint.test.ts +1 -1
  74. package/src/endpoints/global/email-recipients/GetEmailRecipientsEndpoint.ts +1 -1
  75. package/src/endpoints/global/email-recipients/RetryEmailRecipientEndpoint.ts +2 -2
  76. package/src/endpoints/global/events/GetEventNotificationsCountEndpoint.ts +1 -1
  77. package/src/endpoints/global/events/GetEventNotificationsEndpoint.ts +3 -3
  78. package/src/endpoints/global/events/GetEventsEndpoint.ts +3 -3
  79. package/src/endpoints/global/events/PatchEventNotificationsEndpoint.test.ts +49 -4
  80. package/src/endpoints/global/events/PatchEventNotificationsEndpoint.ts +5 -8
  81. package/src/endpoints/global/events/PatchEventsEndpoint.test.ts +12 -44
  82. package/src/endpoints/global/events/PatchEventsEndpoint.ts +128 -127
  83. package/src/endpoints/global/files/ExportToExcelEndpoint.ts +9 -9
  84. package/src/endpoints/global/files/GetFileCache.ts +3 -4
  85. package/src/endpoints/global/files/UploadFile.ts +3 -4
  86. package/src/endpoints/global/files/UploadImage.ts +3 -4
  87. package/src/endpoints/global/groups/GetGroupsCountEndpoint.ts +51 -0
  88. package/src/endpoints/global/groups/GetGroupsEndpoint.test.ts +1 -1
  89. package/src/endpoints/global/groups/GetGroupsEndpoint.ts +4 -2
  90. package/src/endpoints/global/members/GetMemberFamilyEndpoint.ts +2 -3
  91. package/src/endpoints/global/members/GetMembersCountEndpoint.ts +1 -1
  92. package/src/endpoints/global/members/GetMembersEndpoint.test.ts +2 -2
  93. package/src/endpoints/global/members/GetMembersEndpoint.ts +10 -12
  94. package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.test.ts +195 -12
  95. package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.ts +16 -22
  96. package/src/endpoints/global/members/helpers/validateGroupFilter.ts +6 -30
  97. package/src/endpoints/global/members/shouldCheckIfMemberIsDuplicate.ts +1 -1
  98. package/src/endpoints/global/organizations/CheckRegisterCodeEndpoint.ts +1 -1
  99. package/src/endpoints/global/organizations/CreateOrganizationEndpoint.ts +40 -8
  100. package/src/endpoints/global/organizations/GetOrganizationFromDomainEndpoint.ts +2 -2
  101. package/src/endpoints/global/organizations/GetOrganizationFromUriEndpoint.ts +2 -2
  102. package/src/endpoints/global/organizations/SearchOrganizationEndpoint.test.ts +4 -1
  103. package/src/endpoints/global/organizations/SearchOrganizationEndpoint.ts +4 -5
  104. package/src/endpoints/global/payments/StripeWebhookEndpoint.ts +9 -16
  105. package/src/endpoints/global/platform/GetPlatformAdminsEndpoint.ts +1 -1
  106. package/src/endpoints/global/platform/GetPlatformEndpoint.ts +1 -1
  107. package/src/endpoints/global/platform/PatchPlatformEnpoint.test.ts +1 -1
  108. package/src/endpoints/global/platform/PatchPlatformEnpoint.ts +32 -13
  109. package/src/endpoints/global/platform-memberships/GetPlatformMembershipsCountEndpoint.ts +31 -31
  110. package/src/endpoints/global/platform-memberships/GetPlatformMembershipsEndpoint.ts +18 -20
  111. package/src/endpoints/global/registration/GetRegistrationsCountEndpoint.ts +1 -1
  112. package/src/endpoints/global/registration/GetRegistrationsEndpoint.test.ts +1 -1
  113. package/src/endpoints/global/registration/GetRegistrationsEndpoint.ts +6 -10
  114. package/src/endpoints/global/registration/GetUserDetailedPayableBalanceEndpoint.ts +35 -6
  115. package/src/endpoints/global/registration/GetUserDocumentsEndpoint.ts +2 -2
  116. package/src/endpoints/global/registration/GetUserMembersEndpoint.ts +1 -1
  117. package/src/endpoints/global/registration/GetUserPayableBalanceEndpoint.ts +1 -1
  118. package/src/endpoints/global/registration/PatchUserMembersEndpoint.test.ts +2 -2
  119. package/src/endpoints/global/registration/PatchUserMembersEndpoint.ts +3 -3
  120. package/src/endpoints/global/registration/RegisterMembersEndpoint.test.ts +90 -6
  121. package/src/endpoints/global/registration/RegisterMembersEndpoint.ts +38 -33
  122. package/src/endpoints/global/registration-invitations/GetRegistrationInvitationsCountEndpoint.ts +45 -0
  123. package/src/endpoints/global/registration-invitations/GetRegistrationInvitationsEndpoint.test.ts +494 -0
  124. package/src/endpoints/global/registration-invitations/GetRegistrationInvitationsEndpoint.ts +213 -0
  125. package/src/endpoints/global/registration-invitations/PatchRegistrationInvitationsEndpoint.test.ts +404 -0
  126. package/src/endpoints/global/registration-invitations/PatchRegistrationInvitationsEndpoint.ts +167 -0
  127. package/src/endpoints/global/registration-periods/GetRegistrationPeriodsCountEndpoint.ts +43 -0
  128. package/src/endpoints/global/registration-periods/GetRegistrationPeriodsEndpoint.ts +1 -1
  129. package/src/endpoints/global/registration-periods/PatchRegistrationPeriodsEndpoint.ts +13 -5
  130. package/src/endpoints/global/sso/GetSSOEndpoint.ts +2 -3
  131. package/src/endpoints/global/sso/SetSSOEndpoint.ts +1 -1
  132. package/src/endpoints/global/webshops/GetWebshopFromDomainEndpoint.ts +2 -2
  133. package/src/endpoints/global/webshops/GetWebshopsEndpoint.test.ts +1 -2
  134. package/src/endpoints/global/webshops/GetWebshopsEndpoint.ts +2 -4
  135. package/src/endpoints/organization/dashboard/balance-items/GetBalanceItemEndpoint.ts +2 -2
  136. package/src/endpoints/organization/dashboard/balance-items/GetBalanceItemsCountEndpoint.ts +1 -1
  137. package/src/endpoints/organization/dashboard/balance-items/GetBalanceItemsEndpoint.ts +3 -4
  138. package/src/endpoints/organization/dashboard/balance-items/PatchBalanceItemsEndpoint.ts +19 -6
  139. package/src/endpoints/organization/dashboard/billing/ApplyRegisterCodeEndpoint.test.ts +73 -0
  140. package/src/endpoints/organization/dashboard/billing/ApplyRegisterCodeEndpoint.ts +89 -0
  141. package/src/endpoints/{global → organization/dashboard}/billing/DeactivatePackageEndpoint.ts +4 -9
  142. package/src/endpoints/organization/dashboard/billing/DeleteOrganizationMandateEndpoint.ts +62 -0
  143. package/src/endpoints/organization/dashboard/billing/GetOrganizationDetailedPayableBalanceCollectionEndpoint.ts +56 -0
  144. package/src/endpoints/organization/dashboard/billing/GetOrganizationDetailedPayableBalanceEndpoint.ts +41 -18
  145. package/src/endpoints/organization/dashboard/billing/GetOrganizationMandatesEndpoint.ts +64 -0
  146. package/src/endpoints/organization/dashboard/billing/GetOrganizationPayableBalanceEndpoint.ts +2 -2
  147. package/src/endpoints/organization/dashboard/billing/GetOrganizationPayableInvoicesEndpoint.ts +68 -0
  148. package/src/endpoints/organization/dashboard/billing/GetPackagesEndpoint.ts +12 -4
  149. package/src/endpoints/organization/dashboard/billing/GetRegisterCodeEndpoint.ts +66 -0
  150. package/src/endpoints/organization/dashboard/billing/OrganizationCheckoutEndpoint.test.ts +949 -0
  151. package/src/endpoints/organization/dashboard/billing/OrganizationCheckoutEndpoint.ts +311 -0
  152. package/src/endpoints/organization/dashboard/billing/PatchOrganizationMandatesEndpoint.ts +94 -0
  153. package/src/endpoints/organization/dashboard/documents/GetDocumentTemplateXML.ts +1 -1
  154. package/src/endpoints/organization/dashboard/documents/GetDocumentTemplatesCountEndpoint.ts +1 -1
  155. package/src/endpoints/organization/dashboard/documents/GetDocumentTemplatesEndpoint.ts +1 -1
  156. package/src/endpoints/organization/dashboard/documents/GetDocumentsCountEndpoint.ts +1 -1
  157. package/src/endpoints/organization/dashboard/documents/GetDocumentsEndpoint.ts +1 -1
  158. package/src/endpoints/organization/dashboard/documents/PatchDocumentEndpoint.ts +10 -2
  159. package/src/endpoints/organization/dashboard/documents/PatchDocumentTemplatesEndpoint.test.ts +2 -2
  160. package/src/endpoints/organization/dashboard/documents/PatchDocumentTemplatesEndpoint.ts +2 -2
  161. package/src/endpoints/organization/dashboard/email/CheckEmailBouncesEndpoint.ts +1 -1
  162. package/src/endpoints/organization/dashboard/email-templates/GetEmailTemplatesEndpoint.test.ts +1 -1
  163. package/src/endpoints/organization/dashboard/email-templates/GetEmailTemplatesEndpoint.ts +10 -8
  164. package/src/endpoints/organization/dashboard/email-templates/PatchEmailTemplatesEndpoint.test.ts +1 -1
  165. package/src/endpoints/organization/dashboard/email-templates/PatchEmailTemplatesEndpoint.ts +5 -6
  166. package/src/endpoints/organization/dashboard/invoices/GetInvoicesCountEndpoint.ts +1 -1
  167. package/src/endpoints/organization/dashboard/invoices/GetInvoicesEndpoint.ts +15 -4
  168. package/src/endpoints/organization/dashboard/invoices/PatchInvoicesEndpoint.ts +11 -2
  169. package/src/endpoints/organization/dashboard/mollie/CheckMollieEndpoint.ts +8 -8
  170. package/src/endpoints/organization/dashboard/mollie/ConnectMollieEndpoint.ts +10 -5
  171. package/src/endpoints/organization/dashboard/mollie/DisconnectMollieEndpoint.ts +2 -2
  172. package/src/endpoints/organization/dashboard/mollie/GetMollieDashboardEndpoint.ts +7 -7
  173. package/src/endpoints/organization/dashboard/nolt/CreateNoltTokenEndpoint.ts +1 -1
  174. package/src/endpoints/organization/dashboard/organization/GetOrganizationArchivedGroups.ts +2 -2
  175. package/src/endpoints/organization/dashboard/organization/GetOrganizationDeletedGroups.ts +1 -1
  176. package/src/endpoints/organization/dashboard/organization/GetUitpasClientIdEndpoint.ts +1 -1
  177. package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.test.ts +2 -2
  178. package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.ts +67 -20
  179. package/src/endpoints/organization/dashboard/organization/SearchUitpasOrganizersEndpoint.ts +2 -2
  180. package/src/endpoints/organization/dashboard/organization/SetOrganizationDomainEndpoint.ts +5 -10
  181. package/src/endpoints/organization/dashboard/organization/SetUitpasClientCredentialsEndpoint.ts +1 -1
  182. package/src/endpoints/organization/dashboard/payments/GetPaymentsCountEndpoint.ts +1 -1
  183. package/src/endpoints/organization/dashboard/payments/GetPaymentsEndpoint.ts +3 -4
  184. package/src/endpoints/organization/dashboard/payments/PatchPaymentsEndpoint.ts +24 -17
  185. package/src/endpoints/organization/dashboard/receivable-balances/ChargeReceivableBalancesEndpoint.ts +134 -0
  186. package/src/endpoints/organization/dashboard/receivable-balances/GetReceivableBalanceEndpoint.ts +2 -3
  187. package/src/endpoints/organization/dashboard/receivable-balances/GetReceivableBalancesCountEndpoint.ts +1 -1
  188. package/src/endpoints/organization/dashboard/receivable-balances/GetReceivableBalancesEndpoint.ts +1 -1
  189. package/src/endpoints/organization/dashboard/registration-periods/GetOrganizationRegistrationPeriodsEndpoint.test.ts +1 -1
  190. package/src/endpoints/organization/dashboard/registration-periods/GetOrganizationRegistrationPeriodsEndpoint.ts +2 -3
  191. package/src/endpoints/organization/dashboard/registration-periods/MoveRegistrationPeriods.test.ts +1 -1
  192. package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.test.ts +1 -1
  193. package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.ts +32 -22
  194. package/src/endpoints/organization/dashboard/registration-periods/SetupStepReviewEndpoint.ts +4 -5
  195. package/src/endpoints/organization/dashboard/stripe/ConnectStripeEndpoint.ts +1 -1
  196. package/src/endpoints/organization/dashboard/stripe/DeleteStripeAccountEndpoint.ts +2 -3
  197. package/src/endpoints/organization/dashboard/stripe/GetStripeAccountLinkEndpoint.ts +2 -2
  198. package/src/endpoints/organization/dashboard/stripe/GetStripeAccountsEndpoint.ts +1 -1
  199. package/src/endpoints/organization/dashboard/stripe/GetStripeLoginLinkEndpoint.ts +2 -2
  200. package/src/endpoints/organization/dashboard/stripe/UpdateStripeAccountEndpoint.ts +1 -1
  201. package/src/endpoints/organization/dashboard/users/CreateApiUserEndpoint.ts +1 -1
  202. package/src/endpoints/organization/dashboard/users/DeleteUserEndpoint.ts +1 -1
  203. package/src/endpoints/organization/dashboard/users/GetApiUsersEndpoint.ts +1 -1
  204. package/src/endpoints/organization/dashboard/users/GetOrganizationAdminsEndpoint.ts +1 -1
  205. package/src/endpoints/organization/dashboard/users/PatchApiUserEndpoint.ts +4 -6
  206. package/src/endpoints/organization/dashboard/webshops/CreateWebshopEndpoint.ts +10 -15
  207. package/src/endpoints/organization/dashboard/webshops/DeleteWebshopEndpoint.ts +1 -1
  208. package/src/endpoints/organization/dashboard/webshops/GetDiscountCodesEndpoint.ts +2 -2
  209. package/src/endpoints/organization/dashboard/webshops/GetWebshopOrdersCountEndpoint.ts +1 -1
  210. package/src/endpoints/organization/dashboard/webshops/GetWebshopOrdersEndpoint.ts +1 -1
  211. package/src/endpoints/organization/dashboard/webshops/GetWebshopTicketsEndpoint.ts +1 -1
  212. package/src/endpoints/organization/dashboard/webshops/GetWebshopUriAvailabilityEndpoint.ts +11 -2
  213. package/src/endpoints/organization/dashboard/webshops/PatchDiscountCodesEndpoint.ts +4 -6
  214. package/src/endpoints/organization/dashboard/webshops/PatchWebshopEndpoint.ts +29 -8
  215. package/src/endpoints/organization/dashboard/webshops/PatchWebshopOrdersEndpoint.ts +21 -28
  216. package/src/endpoints/organization/dashboard/webshops/PatchWebshopTicketsEndpoint.ts +1 -1
  217. package/src/endpoints/organization/dashboard/webshops/SearchUitpasEventsEndpoint.ts +2 -2
  218. package/src/endpoints/organization/dashboard/webshops/VerifyWebshopDomainEndpoint.ts +2 -3
  219. package/src/endpoints/organization/dashboard/webshops/WebshopReservedPathSegments.test.ts +95 -0
  220. package/src/endpoints/organization/shared/ExchangePaymentEndpoint.ts +15 -13
  221. package/src/endpoints/organization/shared/GetDocumentHtml.ts +1 -1
  222. package/src/endpoints/organization/shared/GetPaymentEndpoint.ts +1 -1
  223. package/src/endpoints/organization/shared/GetUitpasNumberDetailsEndpoint.ts +3 -5
  224. package/src/endpoints/organization/shared/auth/GetOrganizationEndpoint.ts +1 -1
  225. package/src/endpoints/organization/webshops/CheckWebshopDiscountCodesEndpoint.ts +4 -1
  226. package/src/endpoints/organization/webshops/GetOrderByPaymentEndpoint.ts +14 -3
  227. package/src/endpoints/organization/webshops/GetOrderEndpoint.ts +14 -2
  228. package/src/endpoints/organization/webshops/GetTicketsEndpoint.ts +37 -16
  229. package/src/endpoints/organization/webshops/GetWebshopEndpoint.test.ts +34 -2
  230. package/src/endpoints/organization/webshops/GetWebshopEndpoint.ts +4 -2
  231. package/src/endpoints/organization/webshops/PlaceOrderEndpoint.test.ts +74 -3
  232. package/src/endpoints/organization/webshops/PlaceOrderEndpoint.ts +28 -50
  233. package/src/endpoints/organization/webshops/RetrieveUitpasSocialTariffPriceEndpoint.ts +3 -5
  234. package/src/endpoints/organization/webshops/WebshopAuthHelper.ts +36 -0
  235. package/src/endpoints/system/HealthEndpoint.ts +1 -1
  236. package/src/excel-loaders/balance-items.ts +1 -1
  237. package/src/excel-loaders/event-notifications.ts +1 -1
  238. package/src/excel-loaders/members.ts +1 -1
  239. package/src/excel-loaders/organizations.ts +3 -3
  240. package/src/excel-loaders/payments.ts +31 -1
  241. package/src/excel-loaders/platform-memberships.ts +13 -14
  242. package/src/excel-loaders/receivable-balances.ts +2 -2
  243. package/src/excel-loaders/registrations.ts +2 -3
  244. package/src/helpers/AddressValidator.ts +7 -14
  245. package/src/helpers/AdminPermissionChecker.ts +60 -65
  246. package/src/helpers/AuthenticatedStructures.ts +153 -51
  247. package/src/helpers/BalanceItemRelationsBackfiller.test.ts +174 -0
  248. package/src/helpers/BalanceItemRelationsBackfiller.ts +163 -0
  249. package/src/helpers/BuckarooHelper.ts +2 -4
  250. package/src/helpers/CheckSettlements.ts +12 -24
  251. package/src/helpers/Context.ts +15 -10
  252. package/src/helpers/CookieHelper.ts +1 -2
  253. package/src/helpers/EmailResumer.ts +1 -2
  254. package/src/helpers/FileCache.ts +2 -3
  255. package/src/helpers/FinancialSupportHelper.ts +21 -0
  256. package/src/helpers/FlagMomentCleanup.ts +1 -2
  257. package/src/helpers/ForwardHandler.ts +5 -10
  258. package/src/helpers/GlobalHelper.ts +3 -3
  259. package/src/helpers/LegacyBillingConverter.ts +787 -0
  260. package/src/helpers/MemberMerger.test.ts +782 -0
  261. package/src/helpers/MemberMerger.ts +600 -0
  262. package/src/helpers/MemberUserSyncer.ts +6 -12
  263. package/src/helpers/MembershipCharger.ts +2 -4
  264. package/src/helpers/PaymentCustomerResolver.test.ts +203 -0
  265. package/src/helpers/PaymentCustomerResolver.ts +144 -0
  266. package/src/helpers/PeriodHelper.ts +2 -3
  267. package/src/helpers/RecordAnswerHelper.test.ts +741 -0
  268. package/src/helpers/RecordAnswerHelper.ts +116 -0
  269. package/src/helpers/SeedTools.ts +106 -0
  270. package/src/helpers/ServiceFeeHelper.ts +125 -15
  271. package/src/helpers/SetupStepUpdater.ts +38 -32
  272. package/src/helpers/StamhoofdFilterAccessHelper.ts +52 -0
  273. package/src/helpers/StripeHelper.ts +15 -38
  274. package/src/helpers/StripeInvoicer.ts +419 -0
  275. package/src/helpers/StripePayoutChecker.ts +5 -10
  276. package/src/helpers/TagHelper.ts +7 -8
  277. package/src/helpers/TemporaryMemberAccess.ts +1 -2
  278. package/src/helpers/ThrottledQueue.ts +1 -2
  279. package/src/helpers/UitpasTokenRepository.ts +1 -2
  280. package/src/helpers/ViesHelper.ts +11 -11
  281. package/src/helpers/XlsxTransformerColumnHelper.ts +1 -1
  282. package/src/helpers/outstandingBalanceJoin.ts +2 -2
  283. package/src/helpers/updateMemberDetailsUitpasNumber.ts +3 -5
  284. package/src/migrate.ts +12 -5
  285. package/src/{seeds → migrations}/0000000004-single-organization.ts +10 -1
  286. package/src/migrations/0000000006-default-organization.ts +139 -0
  287. package/src/{seeds → migrations}/1715028563-user-permissions.ts +17 -27
  288. package/src/{seeds/1765896674-document-update-year.test.ts → migrations/1750090029-document-update-year.test.ts} +27 -26
  289. package/src/migrations/1750090029-document-update-year.ts +103 -0
  290. package/src/migrations/1750090030-records-configuration.ts +92 -0
  291. package/src/migrations/1750090031-records-configuration-webshops.ts +36 -0
  292. package/src/migrations/1752848540-national-numbers.ts +585 -0
  293. package/src/migrations/1752848550-records-configuration-group-filter.ts +1064 -0
  294. package/src/{seeds → migrations}/1752848561-groups-registration-periods.ts +95 -27
  295. package/src/{seeds → migrations}/1754560914-groups-prices.test.ts +1 -1
  296. package/src/{seeds → migrations}/1754560914-groups-prices.ts +9 -15
  297. package/src/{seeds → migrations}/1756293699-fill-previous-next-period-id.ts +1 -2
  298. package/src/migrations/1761665607-sync-member-users.ts +40 -0
  299. package/src/migrations/1779121239-default-invoice-email-template.sql +3 -0
  300. package/src/migrations/1779121240-invoice-email-template.sql +6 -0
  301. package/src/migrations/1779986893-default-payment-failed-email-templates.sql +4 -0
  302. package/src/migrations/1780578166-convert-legacy-billing.ts +13 -0
  303. package/src/{seeds/1755876819-remove-duplicate-members.ts → migrations/1780665426-remove-duplicate-members.ts} +23 -4
  304. package/src/migrations/1780933598-document-groups.ts +88 -0
  305. package/src/migrations/1781692342-balance-item-relations.ts +13 -0
  306. package/src/migrations/1781857866-platform-admins.ts +103 -0
  307. package/src/migrations/1781857867-rm-old-platform-admins.ts +25 -0
  308. package/src/seeds/0000000001-throw.ts +5 -0
  309. package/src/seeds/1760702452-update-balance-price-paid.ts +19 -0
  310. package/src/seeds/1760702453-update-balance-invoiced.ts +19 -0
  311. package/src/seeds/1760702454-update-cached-outstanding-balance-from-items.ts +51 -14
  312. package/src/seeds/1773754928-force-save-members.ts +41 -8
  313. package/src/seeds/1780665427-schedule-stock-updates.ts +45 -0
  314. package/src/seeds/1780665428-group-update-occupancy.ts +31 -0
  315. package/src/seeds/1780915001-fill-payment-customer.ts +70 -0
  316. package/src/seeds/1780915002-update-orders.ts +39 -0
  317. package/src/seeds/1782047708-organization-company-address.ts +44 -0
  318. package/src/seeds-temporary/1769088653-uitpas-status.ts +1 -2
  319. package/src/seeds-temporary/1780416000-migrate-audit-log-legacy-enums.test.ts +69 -0
  320. package/src/seeds-temporary/1780416000-migrate-audit-log-legacy-enums.ts +85 -0
  321. package/src/services/AuditLogService.test.ts +51 -0
  322. package/src/services/AuditLogService.ts +45 -0
  323. package/src/services/BalanceItemService.ts +148 -55
  324. package/src/services/CpuService.ts +3 -6
  325. package/src/services/DatabaseCollationService.test.ts +1 -2
  326. package/src/services/DocumentService.ts +1 -2
  327. package/src/services/EventNotificationService.ts +1 -1
  328. package/src/services/FileSignService.ts +2 -4
  329. package/src/services/InvoicePdfService.ts +286 -0
  330. package/src/services/InvoiceService.ts +330 -77
  331. package/src/services/InvoiceXMLService.ts +424 -0
  332. package/src/services/MemberNumberService.ts +6 -11
  333. package/src/services/MemberRecordStore.ts +8 -10
  334. package/src/services/MollieService.ts +530 -0
  335. package/src/services/PaymentMandateService.ts +214 -0
  336. package/src/services/PaymentReallocationService.test.ts +1 -1
  337. package/src/services/PaymentReallocationService.ts +7 -9
  338. package/src/services/PaymentService.ts +820 -328
  339. package/src/services/PlatformMembershipService.ts +5 -9
  340. package/src/services/ReferralService.ts +223 -0
  341. package/src/services/RegistrationService.ts +125 -54
  342. package/src/services/SSOService.test.ts +119 -0
  343. package/src/services/SSOService.ts +56 -49
  344. package/src/services/STPackageService.ts +97 -68
  345. package/src/services/UniqueMemberNumberService.ts +69 -0
  346. package/src/services/UniqueUserService.ts +3 -6
  347. package/src/services/VATService.ts +85 -0
  348. package/src/services/data/invoice.hbs.html +686 -0
  349. package/src/services/uitpas/PassholderEndpoints.ts +3 -6
  350. package/src/services/uitpas/UitpasService.ts +6 -10
  351. package/src/services/uitpas/checkUitpasNumbers.ts +2 -4
  352. package/src/services/uitpas/getSocialTariffForUitpasNumbers.ts +3 -6
  353. package/src/services/uitpas/searchUitpasEvents.ts +1 -2
  354. package/src/sql-filters/audit-logs.ts +1 -1
  355. package/src/sql-filters/balance-item-payments.ts +1 -1
  356. package/src/sql-filters/balance-items.ts +1 -1
  357. package/src/sql-filters/base-registration-filter-compilers.ts +5 -33
  358. package/src/sql-filters/document-templates.ts +1 -1
  359. package/src/sql-filters/documents.ts +1 -1
  360. package/src/sql-filters/email-recipients.ts +1 -1
  361. package/src/sql-filters/emails.ts +1 -1
  362. package/src/sql-filters/event-notifications.ts +1 -1
  363. package/src/sql-filters/events.ts +6 -1
  364. package/src/sql-filters/groups.ts +16 -1
  365. package/src/sql-filters/invoiced-balance-items.ts +1 -1
  366. package/src/sql-filters/invoices.ts +1 -1
  367. package/src/sql-filters/member-responsibility-records.ts +1 -1
  368. package/src/sql-filters/members.ts +1 -1
  369. package/src/sql-filters/orders.ts +4 -4
  370. package/src/sql-filters/organization-registration-periods.ts +1 -1
  371. package/src/sql-filters/organizations.ts +1 -1
  372. package/src/sql-filters/payments.ts +5 -0
  373. package/src/sql-filters/platform-memberships.ts +3 -3
  374. package/src/sql-filters/receivable-balances.ts +1 -1
  375. package/src/sql-filters/registration-invitations.ts +90 -0
  376. package/src/sql-filters/registration-periods.ts +1 -1
  377. package/src/sql-filters/registrations.ts +1 -1
  378. package/src/sql-filters/tickets.ts +1 -1
  379. package/src/sql-filters/users.ts +1 -1
  380. package/src/sql-sorters/groups.ts +23 -0
  381. package/src/sql-sorters/platform-memberships.ts +16 -16
  382. package/src/sql-sorters/registration-invitations.ts +36 -0
  383. package/tests/actions/patchOrganizationMember.ts +1 -1
  384. package/tests/actions/patchUserMember.ts +1 -1
  385. package/tests/assertions/assertBalances.ts +6 -12
  386. package/tests/e2e/api-rate-limits.test.ts +12 -24
  387. package/tests/e2e/bundle-discounts.test.ts +4 -1
  388. package/tests/e2e/charge-members.test.ts +13 -11
  389. package/tests/e2e/documents.test.ts +4 -1
  390. package/tests/e2e/register.test.ts +7 -9
  391. package/tests/e2e/stock.test.ts +4 -3
  392. package/tests/e2e/tickets.test.ts +1 -1
  393. package/tests/helpers/MollieMocker.ts +462 -0
  394. package/tests/helpers/PayconiqMocker.ts +138 -45
  395. package/tests/helpers/StripeMocker.ts +9 -6
  396. package/tests/helpers/index.ts +4 -0
  397. package/tests/init/initAdmin.ts +2 -2
  398. package/tests/init/initBundleDiscount.ts +1 -1
  399. package/tests/init/initMembershipOrganization.ts +13 -0
  400. package/tests/init/initPermissionRole.ts +3 -4
  401. package/tests/vitest.global.setup.ts +1 -1
  402. package/vitest.config.js +7 -0
  403. package/.nvmrc +0 -1
  404. package/src/endpoints/global/billing/ActivatePackagesEndpoint.ts +0 -216
  405. package/src/helpers/GroupBuilder.ts +0 -418
  406. package/src/seeds/0000000000-example.ts +0 -11
  407. package/src/seeds/1722256498-group-update-occupancy.ts +0 -52
  408. package/src/seeds/1726572303-schedule-stock-updates.ts +0 -52
  409. package/src/seeds/1726847064-setup-steps.ts +0 -16
  410. package/src/seeds/1728928974-update-cached-outstanding-balance-from-items.ts +0 -31
  411. package/src/seeds/1729253172-update-orders.ts +0 -21
  412. package/src/seeds/1733319079-fill-paying-organization-ids.ts +0 -68
  413. package/src/seeds/1733748412-update-deleted-order-numbers.ts +0 -109
  414. package/src/seeds/1733994455-balance-item-status-open.ts +0 -30
  415. package/src/seeds/1740046783-update-membership.ts +0 -17
  416. package/src/seeds/1741008870-fix-auditlog-description.ts +0 -50
  417. package/src/seeds/1741011468-fix-auditlog-description-uft16.ts +0 -88
  418. package/src/seeds/1741268179-fix-group-dates-of-events.ts +0 -32
  419. package/src/seeds/1750090030-records-configuration.ts +0 -26
  420. package/src/seeds/1751445358-upload-email-attachments.ts +0 -106
  421. package/src/seeds/1755532883-update-email-sender-ids.ts +0 -47
  422. package/src/seeds/1755790070-fill-email-recipient-errors.ts +0 -96
  423. package/src/seeds/1756115432-remove-old-drafts.ts +0 -16
  424. package/src/seeds/1756115433-fill-email-recipient-organization-id.ts +0 -30
  425. package/src/seeds/1756303697-update-email-counts.ts +0 -76
  426. package/src/seeds/1761665607-sync-member-users.ts +0 -59
  427. package/src/seeds/1765896674-document-update-year.ts +0 -66
  428. /package/src/{seeds → migrations}/0000000001-development-user.ts +0 -0
  429. /package/src/{seeds → migrations}/0000000002-clear-stamhoofd-email-templates.ts +0 -0
  430. /package/src/{seeds → migrations}/0000000003-default-email-templates.ts +0 -0
  431. /package/src/{seeds → migrations}/1752848561-groups-registration-periods.test.ts +0 -0
  432. /package/src/{seeds → migrations}/1766150402-document-published-at.test.ts +0 -0
  433. /package/src/{seeds → migrations}/1766150402-document-published-at.ts +0 -0
  434. /package/src/{seeds → migrations}/data/default-email-templates.sql +0 -0
@@ -0,0 +1,167 @@
1
+ import type { AutoEncoderPatchType, Decoder, PatchableArrayAutoEncoder } from '@simonbackx/simple-encoding';
2
+ import { PatchableArrayDecoder, StringDecoder } from '@simonbackx/simple-encoding';
3
+ import type { DecodedRequest, Request } from '@simonbackx/simple-endpoints';
4
+ import { Endpoint, Response } from '@simonbackx/simple-endpoints';
5
+ import type { RegistrationInvitation as RegistrationInvitationStruct } from '@stamhoofd/structures';
6
+ import { GroupType, PermissionLevel, RegistrationInvitationRequest } from '@stamhoofd/structures';
7
+
8
+ import { SimpleError } from '@simonbackx/simple-errors';
9
+ import { Group, Member, RegistrationInvitation } from '@stamhoofd/models';
10
+ import { AuthenticatedStructures } from '../../../helpers/AuthenticatedStructures.js';
11
+ import { Context } from '../../../helpers/Context.js';
12
+
13
+ type Params = Record<string, never>;
14
+ type Query = undefined;
15
+ type Body = PatchableArrayAutoEncoder<RegistrationInvitationRequest>;
16
+ type ResponseBody = RegistrationInvitationStruct[];
17
+
18
+ export class PatchRegistrationInvitationsEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
19
+ bodyDecoder = new PatchableArrayDecoder(
20
+ RegistrationInvitationRequest as Decoder<RegistrationInvitationRequest>,
21
+ RegistrationInvitationRequest.patchType() as Decoder<AutoEncoderPatchType<RegistrationInvitationRequest>>,
22
+ StringDecoder);
23
+
24
+ protected doesMatch(request: Request): [true, Params] | [false] {
25
+ if (request.method !== 'PATCH') {
26
+ return [false];
27
+ }
28
+
29
+ const params = Endpoint.parseParameters(request.url, '/registration-invitations', {});
30
+
31
+ if (params) {
32
+ return [true, params as Params];
33
+ }
34
+ return [false];
35
+ }
36
+
37
+ async handle(request: DecodedRequest<Params, Query, Body>) {
38
+ const organization = await Context.setOrganizationScope();
39
+ await Context.authenticate();
40
+
41
+ // Fast throw first (more in depth checking for patches later)
42
+ if (!await Context.auth.hasSomeAccess(organization.id)) {
43
+ throw Context.auth.error();
44
+ }
45
+
46
+ const invitations: RegistrationInvitation[] = [];
47
+ let duplicateCount = 0;
48
+
49
+ const puts = request.body.getPuts();
50
+ for (const { put } of puts) {
51
+ await this.checkCanCreateRegistrationInvitation(put, organization.id);
52
+
53
+ const invitation = new RegistrationInvitation();
54
+ invitation.id = put.id;
55
+ invitation.organizationId = organization.id;
56
+ invitation.groupId = put.groupId;
57
+ invitation.memberId = put.memberId;
58
+
59
+ try {
60
+ await invitation.save();
61
+ invitations.push(invitation);
62
+ } catch (e) {
63
+ // update if duplicate
64
+ if (e.code === 'ER_DUP_ENTRY') {
65
+ const duplicate = await RegistrationInvitation.select()
66
+ .where('groupId', invitation.groupId)
67
+ .andWhere('memberId', invitation.memberId)
68
+ .first(false);
69
+
70
+ if (duplicate) {
71
+ invitations.push(duplicate);
72
+ }
73
+
74
+ duplicateCount += 1;
75
+ continue;
76
+ } else {
77
+ throw e;
78
+ }
79
+ }
80
+ }
81
+
82
+ if (request.body.getPatches().length > 0) {
83
+ throw new SimpleError({
84
+ code: 'patch_not_supported',
85
+ statusCode: 405,
86
+ message: 'Patching invitations is not supported. Only puts and deletes are supported.',
87
+ });
88
+ }
89
+
90
+ for (const id of request.body.getDeletes()) {
91
+ const invitation = await RegistrationInvitation.getByID(id);
92
+ if (!invitation) {
93
+ throw new SimpleError({
94
+ code: 'not_found',
95
+ statusCode: 404,
96
+ message: 'Registration invitation not found',
97
+ });
98
+ }
99
+
100
+ // Anyone with write access to the group can delete invitations for the group
101
+ const group = await Group.getByID(invitation.groupId);
102
+
103
+ if (!group || !await Context.auth.canAccessGroup(group, PermissionLevel.Write)) {
104
+ throw Context.auth.error($t(`%1UN`));
105
+ }
106
+
107
+ await invitation.delete();
108
+ }
109
+
110
+ // show an error if all puts were duplicates
111
+ if (puts.length > 0 && puts.length === duplicateCount) {
112
+ throw new SimpleError({
113
+ code: 'duplicate_entry',
114
+ statusCode: 409,
115
+ message: 'Duplicate entry',
116
+ human: puts.length === 1 ? $t(`%1RS`) : $t(`%1RP`),
117
+ });
118
+ }
119
+
120
+ return new Response(
121
+ await AuthenticatedStructures.registrationInvitations(invitations),
122
+ );
123
+ }
124
+
125
+ /**
126
+ * Will throw if not allowed to invite.
127
+ * @param invitation
128
+ * @param organizationId id of organization to invite for, should match the organizationId in the invitation
129
+ */
130
+ private async checkCanCreateRegistrationInvitation(invitation: RegistrationInvitationRequest, organizationId: string) {
131
+ const group = await Group.getByID(invitation.groupId);
132
+
133
+ if (!group || group.organizationId !== organizationId || !await Context.auth.canAccessGroup(group, PermissionLevel.Write)) {
134
+ throw Context.auth.error($t(`%1ST`));
135
+ }
136
+
137
+ // cannot invite for waiting list
138
+ if (group.type === GroupType.WaitingList) {
139
+ throw new SimpleError({
140
+ code: 'bad_group',
141
+ statusCode: 400,
142
+ message: 'Not allowed to invite for waiting list',
143
+ });
144
+ }
145
+
146
+ const member = await Member.getByIdWithUsersAndRegistrations(invitation.memberId);
147
+
148
+ if (!member
149
+ // in userMode 'organization' we can only invite members from the same organization
150
+ || (STAMHOOFD.userMode === 'organization' && member.organizationId !== organizationId)
151
+ // read access is suficient
152
+ || !await Context.auth.canAccessMember(member, PermissionLevel.Read)
153
+ ) {
154
+ throw Context.auth.error($t(`%1Qv`));
155
+ }
156
+
157
+ // cannot invite if already registered
158
+ if (member.registrations.some(r => r.groupId === group.id && r.registeredAt !== null && r.deactivatedAt === null)) {
159
+ throw new SimpleError({
160
+ code: 'bad_group',
161
+ statusCode: 400,
162
+ message: 'The member is already registered for this group',
163
+ human: $t('%1S2'),
164
+ });
165
+ }
166
+ }
167
+ }
@@ -0,0 +1,43 @@
1
+ import type { Decoder } from '@simonbackx/simple-encoding';
2
+ import type { DecodedRequest, Request } from '@simonbackx/simple-endpoints';
3
+ import { Endpoint, Response } from '@simonbackx/simple-endpoints';
4
+ import { CountFilteredRequest, CountResponse } from '@stamhoofd/structures';
5
+
6
+ import { Context } from '../../../helpers/Context.js';
7
+ import { GetRegistrationPeriodsEndpoint } from './GetRegistrationPeriodsEndpoint.js';
8
+
9
+ type Params = Record<string, never>;
10
+ type Query = CountFilteredRequest;
11
+ type Body = undefined;
12
+ type ResponseBody = CountResponse;
13
+
14
+ export class GetRegistrationPeriodsCountEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
15
+ queryDecoder = CountFilteredRequest as Decoder<CountFilteredRequest>;
16
+
17
+ protected doesMatch(request: Request): [true, Params] | [false] {
18
+ if (request.method !== 'GET') {
19
+ return [false];
20
+ }
21
+
22
+ const params = Endpoint.parseParameters(request.url, '/registration-periods/count', {});
23
+
24
+ if (params) {
25
+ return [true, params as Params];
26
+ }
27
+ return [false];
28
+ }
29
+
30
+ async handle(request: DecodedRequest<Params, Query, Body>) {
31
+ await Context.setUserOrganizationScope();
32
+ const query = await GetRegistrationPeriodsEndpoint.buildQuery(request.query);
33
+
34
+ const count = await query
35
+ .count();
36
+
37
+ return new Response(
38
+ CountResponse.create({
39
+ count,
40
+ }),
41
+ );
42
+ }
43
+ }
@@ -1,4 +1,4 @@
1
- import type { DecodedRequest, Request} from '@simonbackx/simple-endpoints';
1
+ import type { DecodedRequest, Request } from '@simonbackx/simple-endpoints';
2
2
  import { Endpoint, Response } from '@simonbackx/simple-endpoints';
3
3
  import type { CountFilteredRequest, RegistrationPeriod as RegistrationPeriodStruct, StamhoofdFilter } from '@stamhoofd/structures';
4
4
  import { assertSort, getSortFilter, LimitedFilteredRequest, PaginatedResponse } from '@stamhoofd/structures';
@@ -1,11 +1,11 @@
1
- import type { ConvertArrayToPatchableArray, Decoder, PatchableArrayAutoEncoder} from '@simonbackx/simple-encoding';
1
+ import type { ConvertArrayToPatchableArray, Decoder, PatchableArrayAutoEncoder } from '@simonbackx/simple-encoding';
2
2
  import { PatchableArrayDecoder, StringDecoder, patchObject } from '@simonbackx/simple-encoding';
3
- import type { DecodedRequest, Request} from '@simonbackx/simple-endpoints';
3
+ import type { DecodedRequest, Request } from '@simonbackx/simple-endpoints';
4
4
  import { Endpoint, Response } from '@simonbackx/simple-endpoints';
5
5
  import { RegistrationPeriod as RegistrationPeriodStruct } from '@stamhoofd/structures';
6
6
 
7
7
  import { SimpleError } from '@simonbackx/simple-errors';
8
- import { Organization, Platform, RegistrationPeriod } from '@stamhoofd/models';
8
+ import { Group, Organization, Platform, RegistrationPeriod } from '@stamhoofd/models';
9
9
  import { Context } from '../../../helpers/Context.js';
10
10
  import { PeriodHelper } from '../../../helpers/PeriodHelper.js';
11
11
 
@@ -69,8 +69,7 @@ export class PatchRegistrationPeriodsEndpoint extends Endpoint<Params, Query, Bo
69
69
  if (!await Context.auth.hasFullAccess(organization.id)) {
70
70
  throw Context.auth.error();
71
71
  }
72
- }
73
- else {
72
+ } else {
74
73
  if (!Context.auth.hasPlatformFullAccess()) {
75
74
  throw Context.auth.error();
76
75
  }
@@ -172,6 +171,8 @@ export class PatchRegistrationPeriodsEndpoint extends Endpoint<Params, Query, Bo
172
171
 
173
172
  // Schedule patch of all groups in this period
174
173
  PeriodHelper.updateGroupsInPeriod(model).catch(console.error);
174
+
175
+ periods.push(model)
175
176
  }
176
177
 
177
178
  for (const id of request.body.getDeletes()) {
@@ -185,6 +186,13 @@ export class PatchRegistrationPeriodsEndpoint extends Endpoint<Params, Query, Bo
185
186
  });
186
187
  }
187
188
 
189
+ // Delete all groups in this period
190
+ const q = Group.delete().where('periodId', model.id);
191
+ if (organization) {
192
+ q.where('organizationId', organization.id);
193
+ }
194
+ await q;
195
+
188
196
  // Now delete the model
189
197
  await model.delete();
190
198
  await RegistrationPeriod.updatePreviousNextPeriods(model.organizationId);
@@ -1,10 +1,9 @@
1
- import type { DecodedRequest, Request} from '@simonbackx/simple-endpoints';
1
+ import type { DecodedRequest, Request } from '@simonbackx/simple-endpoints';
2
2
  import { Endpoint, Response } from '@simonbackx/simple-endpoints';
3
3
  import { LoginProviderType, OpenIDClientConfiguration } from '@stamhoofd/structures';
4
4
 
5
5
  import { Context } from '../../../helpers/Context.js';
6
- import { Platform } from '@stamhoofd/models';
7
- import type { Decoder} from '@simonbackx/simple-encoding';
6
+ import type { Decoder } from '@simonbackx/simple-encoding';
8
7
  import { AutoEncoder, EnumDecoder, field } from '@simonbackx/simple-encoding';
9
8
  import { SSOService } from '../../../services/SSOService.js';
10
9
 
@@ -1,5 +1,5 @@
1
1
  import type { AutoEncoderPatchType, Decoder } from '@simonbackx/simple-encoding';
2
- import type { DecodedRequest, Request} from '@simonbackx/simple-endpoints';
2
+ import type { DecodedRequest, Request } from '@simonbackx/simple-endpoints';
3
3
  import { Endpoint, Response } from '@simonbackx/simple-endpoints';
4
4
  import { OpenIDClientConfiguration } from '@stamhoofd/structures';
5
5
 
@@ -1,6 +1,6 @@
1
- import type { Decoder} from '@simonbackx/simple-encoding';
1
+ import type { Decoder } from '@simonbackx/simple-encoding';
2
2
  import { AutoEncoder, field, StringDecoder } from '@simonbackx/simple-encoding';
3
- import type { DecodedRequest, Request} from '@simonbackx/simple-endpoints';
3
+ import type { DecodedRequest, Request } from '@simonbackx/simple-endpoints';
4
4
  import { Endpoint, Response } from '@simonbackx/simple-endpoints';
5
5
  import { SimpleError } from '@simonbackx/simple-errors';
6
6
  import { Organization } from '@stamhoofd/models';
@@ -383,8 +383,7 @@ describe('Endpoint.GetWebshopsEndpoint', () => {
383
383
  expect(response.status).toBe(200);
384
384
  expect(response.body.results).toHaveLength(1);
385
385
  expect(response.body.results[0].id).toBe(closedWebshop.id);
386
- }
387
- finally {
386
+ } finally {
388
387
  await Database.statement('SET collation_connection = DEFAULT');
389
388
  }
390
389
  });
@@ -59,8 +59,7 @@ export class GetWebshopsEndpoint extends Endpoint<Params, Query, Body, ResponseB
59
59
  },
60
60
  };
61
61
  query.where(await compileToSQLFilter(scopeFilter, filterCompilers));
62
- }
63
- else {
62
+ } else {
64
63
  // Platform context: check platform access
65
64
  const tags = Context.auth.getPlatformAccessibleOrganizationTags(PermissionLevel.Read);
66
65
 
@@ -124,8 +123,7 @@ export class GetWebshopsEndpoint extends Endpoint<Params, Query, Body, ResponseB
124
123
 
125
124
  try {
126
125
  data = await query.fetch();
127
- }
128
- catch (error) {
126
+ } catch (error) {
129
127
  if (error.message.includes('ER_QUERY_TIMEOUT')) {
130
128
  throw new SimpleError({
131
129
  code: 'timeout',
@@ -1,4 +1,4 @@
1
- import type { DecodedRequest, Request} from '@simonbackx/simple-endpoints';
1
+ import type { DecodedRequest, Request } from '@simonbackx/simple-endpoints';
2
2
  import { Endpoint, Response } from '@simonbackx/simple-endpoints';
3
3
  import { SimpleError } from '@simonbackx/simple-errors';
4
4
  import { BalanceItem } from '@stamhoofd/models';
@@ -20,7 +20,7 @@ export class GetBalanceItemEndpoint extends Endpoint<Params, Query, Body, Respon
20
20
 
21
21
  const params = Endpoint.parseParameters(request.url, '/balance-items/@id', { id: String });
22
22
 
23
- if (params) {
23
+ if (params && params.id !== 'count') {
24
24
  return [true, params as Params];
25
25
  }
26
26
  return [false];
@@ -1,5 +1,5 @@
1
1
  import type { Decoder } from '@simonbackx/simple-encoding';
2
- import type { DecodedRequest, Request} from '@simonbackx/simple-endpoints';
2
+ import type { DecodedRequest, Request } from '@simonbackx/simple-endpoints';
3
3
  import { Endpoint, Response } from '@simonbackx/simple-endpoints';
4
4
  import { CountFilteredRequest, CountResponse } from '@stamhoofd/structures';
5
5
 
@@ -1,10 +1,10 @@
1
1
  import type { Decoder } from '@simonbackx/simple-encoding';
2
- import type { DecodedRequest, Request} from '@simonbackx/simple-endpoints';
2
+ import type { DecodedRequest, Request } from '@simonbackx/simple-endpoints';
3
3
  import { Endpoint, Response } from '@simonbackx/simple-endpoints';
4
4
  import { SimpleError } from '@simonbackx/simple-errors';
5
5
  import { BalanceItem } from '@stamhoofd/models';
6
6
  import { applySQLSorter, compileToSQLFilter } from '@stamhoofd/sql';
7
- import type { BalanceItem as BalanceItemStruct, CountFilteredRequest, StamhoofdFilter} from '@stamhoofd/structures';
7
+ import type { BalanceItem as BalanceItemStruct, CountFilteredRequest, StamhoofdFilter } from '@stamhoofd/structures';
8
8
  import { LimitedFilteredRequest, PaginatedResponse, assertSort, getSortFilter } from '@stamhoofd/structures';
9
9
 
10
10
  import { AuthenticatedStructures } from '../../../../helpers/AuthenticatedStructures.js';
@@ -95,8 +95,7 @@ export class GetBalanceItemsEndpoint extends Endpoint<Params, Query, Body, Respo
95
95
 
96
96
  try {
97
97
  balanceItems = await query.fetch();
98
- }
99
- catch (error) {
98
+ } catch (error) {
100
99
  if (error.message.includes('ER_QUERY_TIMEOUT')) {
101
100
  throw new SimpleError({
102
101
  code: 'timeout',
@@ -1,6 +1,6 @@
1
- import type { AutoEncoderPatchType, Decoder, PatchableArrayAutoEncoder} from '@simonbackx/simple-encoding';
1
+ import type { AutoEncoderPatchType, Decoder, PatchableArrayAutoEncoder } from '@simonbackx/simple-encoding';
2
2
  import { PatchableArrayDecoder, StringDecoder } from '@simonbackx/simple-encoding';
3
- import type { DecodedRequest, Request} from '@simonbackx/simple-endpoints';
3
+ import type { DecodedRequest, Request } from '@simonbackx/simple-endpoints';
4
4
  import { Endpoint, Response } from '@simonbackx/simple-endpoints';
5
5
  import { SimpleError } from '@simonbackx/simple-errors';
6
6
  import { BalanceItem, Member, Order, User } from '@stamhoofd/models';
@@ -165,8 +165,7 @@ export class PatchBalanceItemsEndpoint extends Endpoint<Params, Query, Body, Res
165
165
  });
166
166
  }
167
167
  model.memberId = null;
168
- }
169
- else {
168
+ } else {
170
169
  model.memberId = (await this.validateMemberId(patch.memberId)).id;
171
170
  }
172
171
  }
@@ -188,10 +187,25 @@ export class PatchBalanceItemsEndpoint extends Endpoint<Params, Query, Body, Res
188
187
  model.unitPrice = patch.unitPrice ?? model.unitPrice;
189
188
  model.amount = patch.amount ?? model.amount;
190
189
  model.dueAt = patch.dueAt === undefined ? model.dueAt : patch.dueAt;
190
+
191
+ const VATPercentageBefore = model.VATPercentage;
192
+ const includedBefore = model.VATIncluded;
193
+ const excemptBefore = model.VATExcempt;
194
+
191
195
  model.VATIncluded = patch.VATIncluded === undefined ? model.VATIncluded : patch.VATIncluded;
192
196
  model.VATPercentage = patch.VATPercentage === undefined ? model.VATPercentage : patch.VATPercentage;
193
197
  model.VATExcempt = patch.VATExcempt === undefined ? model.VATExcempt : patch.VATExcempt;
194
198
 
199
+ if (model.priceInvoiced !== 0) {
200
+ if (model.VATPercentage !== VATPercentageBefore || model.VATIncluded !== includedBefore || model.VATExcempt !== excemptBefore) {
201
+ throw new SimpleError({
202
+ code: 'invoiced',
203
+ message: 'You cannot change VAT settings of balance items when the balance item has been invoiced',
204
+ human: $t('%1Rt'),
205
+ });
206
+ }
207
+ }
208
+
195
209
  if ((patch.dueAt !== undefined || patch.unitPrice !== undefined) && model.dueAt && model.price < 0) {
196
210
  throw new SimpleError({
197
211
  code: 'invalid_price',
@@ -214,8 +228,7 @@ export class PatchBalanceItemsEndpoint extends Endpoint<Params, Query, Body, Res
214
228
  if (model.pricePaid === 0) {
215
229
  model.status = BalanceItemStatus.Hidden;
216
230
  }
217
- }
218
- else if (patch.status) {
231
+ } else if (patch.status) {
219
232
  model.status = patch.status;
220
233
  }
221
234
 
@@ -0,0 +1,73 @@
1
+ import { Request } from '@simonbackx/simple-endpoints';
2
+ import { BalanceItem, OrganizationFactory, RegisterCodeFactory, Token, UserFactory } from '@stamhoofd/models';
3
+ import { PermissionLevel, Permissions } from '@stamhoofd/structures';
4
+
5
+ import { testServer } from '../../../../../tests/helpers/TestServer.js';
6
+ import { ApplyRegisterCodeEndpoint } from './ApplyRegisterCodeEndpoint.js';
7
+ import { initMembershipOrganization } from '../../../../../tests/init/initMembershipOrganization.js';
8
+
9
+ describe('Endpoint.ApplyRegisterCodeEndpoint', () => {
10
+ // Test endpoint
11
+ const endpoint = new ApplyRegisterCodeEndpoint();
12
+
13
+ beforeAll(async () => {
14
+ await initMembershipOrganization();
15
+ });
16
+
17
+ test('Cannot apply a register code if not platform admin', async () => {
18
+ const otherOrganization = await new OrganizationFactory({}).create();
19
+ const code = await new RegisterCodeFactory({ organization: otherOrganization }).create();
20
+
21
+ const organization = await new OrganizationFactory({}).create();
22
+ const user = await new UserFactory({ organization, permissions: Permissions.create({ level: PermissionLevel.Full }) }).create();
23
+ const token = await Token.createToken(user);
24
+
25
+ const r = Request.buildJson(
26
+ 'POST',
27
+ '/billing/register-code',
28
+ organization.getApiHost(),
29
+ {
30
+ registerCode: code.code,
31
+ },
32
+ );
33
+ r.headers.authorization = 'Bearer ' + token.accessToken;
34
+
35
+ await expect(testServer.test(endpoint, r)).rejects.toThrow('You do not have permissions for this action');
36
+ });
37
+
38
+ test('Can apply a register code and apply the discount', async () => {
39
+ const otherOrganization = await new OrganizationFactory({}).create();
40
+ const code = await new RegisterCodeFactory({ organization: otherOrganization }).create();
41
+
42
+ const organization = await new OrganizationFactory({}).create();
43
+ const user = await new UserFactory({
44
+ organization,
45
+ globalPermissions: Permissions.create({ level: PermissionLevel.Full }),
46
+ email: 'admin@stamhoofd.be',
47
+ }).create();
48
+ const token = await Token.createToken(user);
49
+
50
+ const r = Request.buildJson(
51
+ 'POST',
52
+ '/billing/register-code',
53
+ organization.getApiHost(),
54
+ {
55
+ registerCode: code.code,
56
+ },
57
+ );
58
+ r.headers.authorization = 'Bearer ' + token.accessToken;
59
+
60
+ const response = await testServer.test(endpoint, r);
61
+ expect(response.body).toBeUndefined();
62
+
63
+ // Check if this organization has an open register code
64
+ const credits = await BalanceItem.select().where('payingOrganizationId', organization.id).fetch();
65
+ expect(credits.length).toBe(1);
66
+ expect(credits[0].unitPrice).toBe(-code.value);
67
+
68
+ // Other not yet applied because no package bought yet
69
+ // Check if other organization has an open register code
70
+ const otherCredits = await BalanceItem.select().where('payingOrganizationId', otherOrganization.id).fetch();
71
+ expect(otherCredits.length).toBe(0);
72
+ });
73
+ });
@@ -0,0 +1,89 @@
1
+ import type { Decoder } from '@simonbackx/simple-encoding';
2
+ import { AutoEncoder, field, StringDecoder } from '@simonbackx/simple-encoding';
3
+ import type { DecodedRequest, Request } from '@simonbackx/simple-endpoints';
4
+ import { Endpoint, Response } from '@simonbackx/simple-endpoints';
5
+ import { Email } from '@stamhoofd/email';
6
+ import { RegisterCode, UsedRegisterCode } from '@stamhoofd/models';
7
+
8
+ import { Context } from '../../../../helpers/Context.js';
9
+ import { ReferralService } from '../../../../services/ReferralService.js';
10
+
11
+ type Params = Record<string, never>;
12
+ type Query = undefined;
13
+ type ResponseBody = undefined;
14
+
15
+ class Body extends AutoEncoder {
16
+ @field({ decoder: StringDecoder })
17
+ registerCode: string;
18
+ }
19
+
20
+ /**
21
+ * One endpoint to create, patch and delete groups. Usefull because on organization setup, we need to create multiple groups at once. Also, sometimes we need to link values and update multiple groups at once
22
+ */
23
+
24
+ export class ApplyRegisterCodeEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
25
+ bodyDecoder = Body as Decoder<Body>;
26
+
27
+ protected doesMatch(request: Request): [true, Params] | [false] {
28
+ if (request.method !== 'POST') {
29
+ return [false];
30
+ }
31
+
32
+ const params = Endpoint.parseParameters(request.url, '/billing/register-code', {});
33
+
34
+ if (params) {
35
+ return [true, params as Params];
36
+ }
37
+ return [false];
38
+ }
39
+
40
+ async handle(request: DecodedRequest<Params, Query, Body>) {
41
+ const organization = await Context.setOrganizationScope();
42
+ await Context.authenticate();
43
+
44
+ if (!Context.auth.hasPlatformFullAccess()) {
45
+ throw Context.auth.error();
46
+ }
47
+
48
+ let code = request.body.registerCode;
49
+
50
+ if (code.startsWith('https:')) {
51
+ try {
52
+ const url = new URL(code);
53
+ const codeParam = url.searchParams.get('code');
54
+ if (codeParam) {
55
+ console.log('Parsed code from URL', codeParam);
56
+ code = codeParam;
57
+ }
58
+ } catch (e) {
59
+ console.error('Tried parsing code as URL but failed', code);
60
+ }
61
+ }
62
+
63
+ const { models, emails } = await ReferralService.markUsed(organization, code);
64
+
65
+ for (const model of models) {
66
+ await model.save();
67
+ }
68
+
69
+ for (const email of emails) {
70
+ Email.send({
71
+ ...email,
72
+ from: Email.getQuickFromEmail(organization.address.country),
73
+ });
74
+ }
75
+
76
+ if (organization.meta.packages.isPaid) {
77
+ // Already bought something: apply credit to other organization immediately
78
+ const code = await UsedRegisterCode.getFor(organization.id);
79
+ if (code && !code.balanceItemId) {
80
+ console.log('Rewarding code ' + code.id + ' for payment');
81
+
82
+ // Deze code werd nog niet beloond
83
+ await ReferralService.reward(code);
84
+ }
85
+ }
86
+
87
+ return new Response(undefined);
88
+ }
89
+ }
@@ -1,10 +1,9 @@
1
- import type { DecodedRequest, Request} from '@simonbackx/simple-endpoints';
1
+ import type { DecodedRequest, Request } from '@simonbackx/simple-endpoints';
2
2
  import { Endpoint, Response } from '@simonbackx/simple-endpoints';
3
3
  import { SimpleError } from '@simonbackx/simple-errors';
4
- import { STPackage } from '@stamhoofd/models';
5
4
 
6
- import { Context } from '../../../helpers/Context.js';
7
- import { STPackageService } from '../../../services/STPackageService.js';
5
+ import { Context } from '../../../../helpers/Context.js';
6
+ import { STPackageService } from '../../../../services/STPackageService.js';
8
7
  type Params = { id: string };
9
8
  type Query = undefined;
10
9
  type ResponseBody = undefined;
@@ -53,11 +52,7 @@ export class DeactivatePackageEndpoint extends Endpoint<Params, Query, Body, Res
53
52
  });
54
53
  }
55
54
 
56
- // Deactivate
57
- pack.removeAt = new Date();
58
- pack.removeAt.setTime(pack.removeAt.getTime() - 5_000);
59
- await pack.save();
60
-
55
+ await STPackageService.deactivatePackage(pack);
61
56
  await STPackageService.updateOrganizationPackages(organization.id);
62
57
 
63
58
  return new Response(undefined);