@stamhoofd/backend 2.116.0 → 2.117.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 (263) hide show
  1. package/migrations.ts +1 -1
  2. package/package.json +10 -10
  3. package/src/audit-logs/DocumentTemplateLogger.ts +1 -1
  4. package/src/audit-logs/EmailAddressLogger.ts +1 -1
  5. package/src/audit-logs/EmailLogger.ts +1 -1
  6. package/src/audit-logs/EmailTemplateLogger.ts +1 -1
  7. package/src/audit-logs/EventLogger.ts +1 -1
  8. package/src/audit-logs/GroupLogger.ts +1 -1
  9. package/src/audit-logs/MemberLogger.ts +1 -1
  10. package/src/audit-logs/MemberPlatformMembershipLogger.ts +1 -1
  11. package/src/audit-logs/MemberResponsibilityRecordLogger.ts +1 -1
  12. package/src/audit-logs/ModelLogger.ts +25 -16
  13. package/src/audit-logs/OrderLogger.ts +1 -1
  14. package/src/audit-logs/OrganizationLogger.ts +23 -3
  15. package/src/audit-logs/OrganizationRegistrationPeriodLogger.ts +1 -1
  16. package/src/audit-logs/PaymentLogger.ts +1 -1
  17. package/src/audit-logs/PlatformLogger.ts +1 -1
  18. package/src/audit-logs/RegistrationLogger.ts +1 -1
  19. package/src/audit-logs/RegistrationPeriodLogger.ts +1 -1
  20. package/src/audit-logs/StripeAccountLogger.ts +1 -1
  21. package/src/audit-logs/UserLogger.ts +1 -1
  22. package/src/audit-logs/WebshopLogger.ts +1 -1
  23. package/src/audit-logs/init.ts +40 -0
  24. package/src/boot.ts +3 -0
  25. package/src/crons/amazon-ses.ts +1 -1
  26. package/src/crons/balance-emails.ts +1 -1
  27. package/src/crons/clearExcelCache.test.ts +1 -1
  28. package/src/crons/endFunctionsOfUsersWithoutRegistration.ts +1 -1
  29. package/src/crons.ts +3 -3
  30. package/src/email-recipient-loaders/members.ts +1 -1
  31. package/src/email-recipient-loaders/orders.ts +3 -3
  32. package/src/email-recipient-loaders/payments.ts +117 -352
  33. package/src/email-replacements/getEmailReplacementsForPayment.ts +321 -0
  34. package/src/endpoints/admin/members/ChargeMembersEndpoint.ts +9 -7
  35. package/src/endpoints/admin/memberships/ChargeMembershipsEndpoint.ts +2 -2
  36. package/src/endpoints/admin/memberships/GetChargeMembershipsSummaryEndpoint.ts +1 -1
  37. package/src/endpoints/admin/organizations/ChargeOrganizationsEndpoint.ts +16 -50
  38. package/src/endpoints/admin/organizations/GetOrganizationsCountEndpoint.ts +2 -2
  39. package/src/endpoints/admin/organizations/GetOrganizationsEndpoint.ts +4 -4
  40. package/src/endpoints/admin/organizations/PatchOrganizationsEndpoint.ts +21 -6
  41. package/src/endpoints/admin/registrations/ChargeRegistrationsEndpoint.ts +9 -7
  42. package/src/endpoints/auth/CreateAdminEndpoint.ts +2 -2
  43. package/src/endpoints/auth/CreateTokenEndpoint.test.ts +2 -2
  44. package/src/endpoints/auth/CreateTokenEndpoint.ts +1 -1
  45. package/src/endpoints/auth/DeleteTokenEndpoint.ts +1 -1
  46. package/src/endpoints/auth/DeleteUserEndpoint.ts +1 -1
  47. package/src/endpoints/auth/ForgotPasswordEndpoint.ts +1 -1
  48. package/src/endpoints/auth/GetOtherUserEndpoint.ts +2 -2
  49. package/src/endpoints/auth/GetUserEndpoint.test.ts +2 -2
  50. package/src/endpoints/auth/GetUserEndpoint.ts +2 -2
  51. package/src/endpoints/auth/OpenIDConnectAuthTokenEndpoint.ts +2 -2
  52. package/src/endpoints/auth/OpenIDConnectCallbackEndpoint.ts +2 -2
  53. package/src/endpoints/auth/OpenIDConnectStartEndpoint.ts +2 -2
  54. package/src/endpoints/auth/PollEmailVerificationEndpoint.ts +1 -1
  55. package/src/endpoints/auth/RetryEmailVerificationEndpoint.ts +1 -1
  56. package/src/endpoints/auth/SignupEndpoint.ts +1 -1
  57. package/src/endpoints/auth/VerifyEmailEndpoint.ts +1 -1
  58. package/src/endpoints/global/addresses/ValidateAddressEndpoint.ts +1 -1
  59. package/src/endpoints/global/audit-logs/GetAuditLogsEndpoint.ts +4 -4
  60. package/src/endpoints/global/billing/ActivatePackagesEndpoint.ts +191 -7
  61. package/src/endpoints/global/billing/DeactivatePackageEndpoint.ts +64 -0
  62. package/src/endpoints/global/email/GetAdminEmailsEndpoint.test.ts +2 -2
  63. package/src/endpoints/global/email/GetAdminEmailsEndpoint.ts +3 -3
  64. package/src/endpoints/global/email/GetEmailAddressEndpoint.ts +1 -1
  65. package/src/endpoints/global/email/GetEmailEndpoint.ts +1 -1
  66. package/src/endpoints/global/email/GetUserEmailsEndpoint.test.ts +2 -2
  67. package/src/endpoints/global/email/GetUserEmailsEndpoint.ts +3 -3
  68. package/src/endpoints/global/email/ManageEmailAddressEndpoint.ts +62 -16
  69. package/src/endpoints/global/email/PatchEmailEndpoint.test.ts +6 -6
  70. package/src/endpoints/global/email-recipients/GetEmailRecipientsCountEndpoint.ts +2 -2
  71. package/src/endpoints/global/email-recipients/GetEmailRecipientsEndpoint.test.ts +2 -2
  72. package/src/endpoints/global/email-recipients/GetEmailRecipientsEndpoint.ts +4 -4
  73. package/src/endpoints/global/email-recipients/RetryEmailRecipientEndpoint.ts +1 -1
  74. package/src/endpoints/global/email-recipients/helpers/validateEmailRecipientFilter.ts +1 -1
  75. package/src/endpoints/global/events/GetEventNotificationsCountEndpoint.ts +2 -2
  76. package/src/endpoints/global/events/GetEventNotificationsEndpoint.ts +4 -4
  77. package/src/endpoints/global/events/GetEventsEndpoint.ts +4 -4
  78. package/src/endpoints/global/events/PatchEventNotificationsEndpoint.test.ts +2 -2
  79. package/src/endpoints/global/events/PatchEventNotificationsEndpoint.ts +3 -3
  80. package/src/endpoints/global/events/PatchEventsEndpoint.test.ts +2 -2
  81. package/src/endpoints/global/events/PatchEventsEndpoint.ts +5 -5
  82. package/src/endpoints/global/files/ExportToExcelEndpoint.ts +4 -4
  83. package/src/endpoints/global/files/GetFileCache.ts +2 -2
  84. package/src/endpoints/global/files/UploadFile.ts +2 -2
  85. package/src/endpoints/global/files/UploadImage.ts +1 -1
  86. package/src/endpoints/global/groups/GetGroupsEndpoint.test.ts +3 -3
  87. package/src/endpoints/global/groups/GetGroupsEndpoint.ts +4 -4
  88. package/src/endpoints/global/members/GetMembersEndpoint.test.ts +3 -3
  89. package/src/endpoints/global/members/helpers/validateGroupFilter.ts +1 -1
  90. package/src/endpoints/global/organizations/CreateOrganizationEndpoint.test.ts +2 -2
  91. package/src/endpoints/global/organizations/CreateOrganizationEndpoint.ts +23 -12
  92. package/src/endpoints/global/organizations/GetOrganizationFromDomainEndpoint.test.ts +2 -2
  93. package/src/endpoints/global/organizations/GetOrganizationFromDomainEndpoint.ts +1 -1
  94. package/src/endpoints/global/organizations/GetOrganizationFromUriEndpoint.ts +1 -1
  95. package/src/endpoints/global/organizations/SearchOrganizationEndpoint.test.ts +2 -2
  96. package/src/endpoints/global/organizations/SearchOrganizationEndpoint.ts +1 -1
  97. package/src/endpoints/global/payments/StripeWebhookEndpoint.ts +3 -3
  98. package/src/endpoints/global/platform/GetPlatformAdminsEndpoint.ts +1 -1
  99. package/src/endpoints/global/platform/GetPlatformEndpoint.test.ts +2 -2
  100. package/src/endpoints/global/platform/GetPlatformEndpoint.ts +1 -1
  101. package/src/endpoints/global/platform/PatchPlatformEnpoint.test.ts +2 -2
  102. package/src/endpoints/global/platform/PatchPlatformEnpoint.ts +7 -7
  103. package/src/endpoints/global/registration/GetRegistrationsEndpoint.test.ts +2 -2
  104. package/src/endpoints/global/registration/GetRegistrationsEndpoint.ts +1 -2
  105. package/src/endpoints/global/registration/GetUserMembersEndpoint.ts +2 -2
  106. package/src/endpoints/global/registration/GetUserPayableBalanceEndpoint.ts +2 -2
  107. package/src/endpoints/global/registration/RegisterMembersEndpoint.ts +14 -384
  108. package/src/endpoints/global/registration-periods/GetRegistrationPeriodsEndpoint.ts +3 -3
  109. package/src/endpoints/global/registration-periods/PatchRegistrationPeriodsEndpoint.ts +2 -2
  110. package/src/endpoints/global/sso/GetSSOEndpoint.ts +2 -2
  111. package/src/endpoints/global/sso/SetSSOEndpoint.ts +3 -3
  112. package/src/endpoints/organization/dashboard/balance-items/GetBalanceItemEndpoint.ts +55 -0
  113. package/src/endpoints/organization/dashboard/balance-items/GetBalanceItemsCountEndpoint.ts +43 -0
  114. package/src/endpoints/organization/dashboard/balance-items/GetBalanceItemsEndpoint.ts +160 -0
  115. package/src/endpoints/organization/dashboard/billing/GetOrganizationDetailedPayableBalanceEndpoint.ts +2 -2
  116. package/src/endpoints/organization/dashboard/billing/GetOrganizationPayableBalanceEndpoint.ts +2 -2
  117. package/src/endpoints/organization/dashboard/billing/GetPackagesEndpoint.test.ts +3 -3
  118. package/src/endpoints/organization/dashboard/billing/GetPackagesEndpoint.ts +3 -3
  119. package/src/endpoints/organization/dashboard/documents/GetDocumentTemplateXML.ts +1 -1
  120. package/src/endpoints/organization/dashboard/documents/GetDocumentsCountEndpoint.ts +2 -2
  121. package/src/endpoints/organization/dashboard/email/CheckEmailBouncesEndpoint.ts +30 -2
  122. package/src/endpoints/organization/dashboard/email-templates/GetEmailTemplatesEndpoint.test.ts +2 -2
  123. package/src/endpoints/organization/dashboard/email-templates/GetEmailTemplatesEndpoint.ts +3 -3
  124. package/src/endpoints/organization/dashboard/email-templates/PatchEmailTemplatesEndpoint.test.ts +2 -2
  125. package/src/endpoints/organization/dashboard/email-templates/PatchEmailTemplatesEndpoint.ts +1 -1
  126. package/src/endpoints/organization/dashboard/invoices/PatchInvoicesEndpoint.ts +53 -0
  127. package/src/endpoints/organization/dashboard/mollie/CheckMollieEndpoint.ts +2 -2
  128. package/src/endpoints/organization/dashboard/mollie/ConnectMollieEndpoint.ts +3 -3
  129. package/src/endpoints/organization/dashboard/mollie/DisconnectMollieEndpoint.ts +2 -2
  130. package/src/endpoints/organization/dashboard/mollie/GetMollieDashboardEndpoint.ts +1 -1
  131. package/src/endpoints/organization/dashboard/nolt/CreateNoltTokenEndpoint.ts +1 -1
  132. package/src/endpoints/organization/dashboard/organization/GetOrganizationArchivedGroups.ts +2 -2
  133. package/src/endpoints/organization/dashboard/organization/GetOrganizationDeletedGroups.ts +2 -2
  134. package/src/endpoints/organization/dashboard/organization/GetUitpasClientIdEndpoint.ts +2 -2
  135. package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.test.ts +2 -2
  136. package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.ts +12 -1
  137. package/src/endpoints/organization/dashboard/organization/SearchUitpasOrganizersEndpoint.ts +2 -2
  138. package/src/endpoints/organization/dashboard/organization/SetOrganizationDomainEndpoint.ts +2 -2
  139. package/src/endpoints/organization/dashboard/organization/SetUitpasClientCredentialsEndpoint.ts +2 -2
  140. package/src/endpoints/organization/dashboard/payments/GetPaymentsCountEndpoint.ts +2 -2
  141. package/src/endpoints/organization/dashboard/payments/GetPaymentsEndpoint.ts +4 -4
  142. package/src/endpoints/organization/dashboard/receivable-balances/GetReceivableBalancesCountEndpoint.ts +2 -2
  143. package/src/endpoints/organization/dashboard/receivable-balances/GetReceivableBalancesEndpoint.ts +4 -4
  144. package/src/endpoints/organization/dashboard/registration-periods/GetOrganizationRegistrationPeriodsEndpoint.test.ts +2 -2
  145. package/src/endpoints/organization/dashboard/registration-periods/GetOrganizationRegistrationPeriodsEndpoint.ts +4 -4
  146. package/src/endpoints/organization/dashboard/registration-periods/MoveRegistrationPeriods.test.ts +2 -2
  147. package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.test.ts +2 -2
  148. package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.ts +3 -3
  149. package/src/endpoints/organization/dashboard/registration-periods/SetupStepReviewEndpoint.ts +2 -2
  150. package/src/endpoints/organization/dashboard/stripe/ConnectStripeEndpoint.ts +3 -3
  151. package/src/endpoints/organization/dashboard/stripe/DeleteStripeAccountEndpoint.ts +3 -3
  152. package/src/endpoints/organization/dashboard/stripe/GetStripeAccountLinkEndpoint.ts +2 -2
  153. package/src/endpoints/organization/dashboard/stripe/GetStripeAccountsEndpoint.ts +1 -1
  154. package/src/endpoints/organization/dashboard/stripe/GetStripeLoginLinkEndpoint.ts +2 -2
  155. package/src/endpoints/organization/dashboard/stripe/UpdateStripeAccountEndpoint.ts +3 -3
  156. package/src/endpoints/organization/dashboard/users/CreateApiUserEndpoint.test.ts +3 -3
  157. package/src/endpoints/organization/dashboard/users/CreateApiUserEndpoint.ts +1 -1
  158. package/src/endpoints/organization/dashboard/users/DeleteUserEndpoint.ts +1 -1
  159. package/src/endpoints/organization/dashboard/users/GetApiUsersEndpoint.ts +1 -1
  160. package/src/endpoints/organization/dashboard/users/GetOrganizationAdminsEndpoint.ts +1 -1
  161. package/src/endpoints/organization/dashboard/users/PatchApiUserEndpoint.test.ts +3 -3
  162. package/src/endpoints/organization/dashboard/users/PatchApiUserEndpoint.ts +1 -1
  163. package/src/endpoints/organization/dashboard/webshops/CreateWebshopEndpoint.ts +1 -1
  164. package/src/endpoints/organization/dashboard/webshops/DeleteWebshopEndpoint.ts +2 -2
  165. package/src/endpoints/organization/dashboard/webshops/GetDiscountCodesEndpoint.ts +1 -1
  166. package/src/endpoints/organization/dashboard/webshops/GetWebshopOrdersCountEndpoint.ts +2 -2
  167. package/src/endpoints/organization/dashboard/webshops/GetWebshopOrdersEndpoint.ts +5 -5
  168. package/src/endpoints/organization/dashboard/webshops/GetWebshopTicketsEndpoint.ts +5 -5
  169. package/src/endpoints/organization/dashboard/webshops/GetWebshopUriAvailabilityEndpoint.ts +1 -1
  170. package/src/endpoints/organization/dashboard/webshops/PatchDiscountCodesEndpoint.ts +1 -1
  171. package/src/endpoints/organization/dashboard/webshops/PatchWebshopEndpoint.ts +1 -1
  172. package/src/endpoints/organization/dashboard/webshops/PatchWebshopOrdersEndpoint.ts +1 -1
  173. package/src/endpoints/organization/dashboard/webshops/PatchWebshopTicketsEndpoint.ts +1 -1
  174. package/src/endpoints/organization/dashboard/webshops/SearchUitpasEventsEndpoint.ts +2 -2
  175. package/src/endpoints/organization/dashboard/webshops/VerifyWebshopDomainEndpoint.ts +1 -1
  176. package/src/endpoints/organization/shared/ExchangePaymentEndpoint.ts +3 -3
  177. package/src/endpoints/organization/shared/GetDocumentHtml.ts +1 -1
  178. package/src/endpoints/organization/shared/GetPaymentEndpoint.ts +2 -2
  179. package/src/endpoints/organization/shared/auth/GetOrganizationEndpoint.test.ts +2 -2
  180. package/src/endpoints/organization/shared/auth/GetOrganizationEndpoint.ts +2 -2
  181. package/src/endpoints/organization/webshops/CheckWebshopDiscountCodesEndpoint.ts +1 -1
  182. package/src/endpoints/organization/webshops/GetOrderByPaymentEndpoint.ts +1 -1
  183. package/src/endpoints/organization/webshops/GetOrderEndpoint.ts +1 -1
  184. package/src/endpoints/organization/webshops/GetTicketsEndpoint.ts +1 -1
  185. package/src/endpoints/organization/webshops/GetWebshopEndpoint.test.ts +2 -2
  186. package/src/endpoints/organization/webshops/GetWebshopEndpoint.ts +2 -2
  187. package/src/endpoints/organization/webshops/PlaceOrderEndpoint.test.ts +4 -4
  188. package/src/excel-loaders/balance-items.ts +268 -0
  189. package/src/excel-loaders/event-notifications.ts +3 -3
  190. package/src/excel-loaders/index.ts +6 -5
  191. package/src/excel-loaders/organizations.ts +4 -4
  192. package/src/excel-loaders/payments.ts +11 -3
  193. package/src/excel-loaders/receivable-balances.ts +2 -2
  194. package/src/helpers/AddressValidator.test.ts +1 -1
  195. package/src/helpers/AddressValidator.ts +1 -1
  196. package/src/helpers/AdminPermissionChecker.ts +1 -1
  197. package/src/helpers/AuthenticatedStructures.ts +17 -2
  198. package/src/helpers/EmailResumer.ts +1 -1
  199. package/src/helpers/FlagMomentCleanup.ts +1 -1
  200. package/src/helpers/ForwardHandler.test.ts +1 -1
  201. package/src/helpers/ForwardHandler.ts +1 -1
  202. package/src/helpers/GlobalHelper.ts +4 -4
  203. package/src/helpers/GroupBuilder.ts +417 -0
  204. package/src/helpers/GroupedThrottledQueue.test.ts +1 -1
  205. package/src/helpers/GroupedThrottledQueue.ts +1 -1
  206. package/src/helpers/MemberUserSyncer.test.ts +1 -1
  207. package/src/helpers/MemberUserSyncer.ts +1 -1
  208. package/src/helpers/ServiceFeeHelper.ts +5 -1
  209. package/src/helpers/TagHelper.ts +1 -1
  210. package/src/helpers/ThrottledQueue.test.ts +1 -1
  211. package/src/helpers/ViesHelper.ts +9 -0
  212. package/src/helpers/email-html-helpers.ts +0 -41
  213. package/src/middleware/ContextMiddleware.ts +1 -1
  214. package/src/seeds/1726572303-schedule-stock-updates.ts +1 -1
  215. package/src/seeds/1726847064-setup-steps.ts +1 -1
  216. package/src/seeds/1728928974-update-cached-outstanding-balance-from-items.ts +1 -1
  217. package/src/seeds/1740046783-update-membership.ts +1 -1
  218. package/src/seeds/1754560914-groups-prices.test.ts +1 -1
  219. package/src/seeds/1755876819-remove-duplicate-members.ts +1 -1
  220. package/src/seeds/1760702454-update-cached-outstanding-balance-from-items.ts +1 -1
  221. package/src/seeds/1761665607-sync-member-users.ts +1 -1
  222. package/src/seeds/data/default-email-templates.sql +1 -1
  223. package/src/seeds-temporary/1732117645-move-rrn.ts +1 -1
  224. package/src/services/AuditLogService.ts +1 -41
  225. package/src/services/BalanceItemPaymentService.ts +1 -1
  226. package/src/services/BalanceItemService.ts +12 -5
  227. package/src/services/EventNotificationService.ts +3 -3
  228. package/src/services/InvoiceService.ts +131 -17
  229. package/src/services/MemberRecordStore.ts +1 -1
  230. package/src/services/PaymentReallocationService.test.ts +9 -10
  231. package/src/services/PaymentReallocationService.ts +1 -1
  232. package/src/services/PaymentService.ts +548 -18
  233. package/src/services/RegistrationService.ts +3 -3
  234. package/src/services/SSOService.ts +2 -2
  235. package/src/services/STPackageService.ts +241 -0
  236. package/src/services/uitpas/UitpasService.test.ts +1 -1
  237. package/src/sql-filters/balance-items.ts +56 -0
  238. package/src/sql-filters/emails.ts +1 -1
  239. package/src/sql-filters/event-notifications.ts +2 -2
  240. package/src/sql-filters/events.ts +51 -0
  241. package/src/sql-filters/members.ts +37 -1
  242. package/src/sql-filters/receivable-balances.ts +3 -3
  243. package/src/sql-filters/users.ts +10 -0
  244. package/src/sql-sorters/balance-items.ts +36 -0
  245. package/src/sql-sorters/document-templates.ts +2 -2
  246. package/src/sql-sorters/documents.ts +2 -2
  247. package/src/sql-sorters/event-notifications.ts +1 -1
  248. package/src/sql-sorters/orders.ts +2 -2
  249. package/src/sql-sorters/tickets.ts +2 -2
  250. package/tests/actions/patchOrganizationMember.ts +3 -3
  251. package/tests/actions/patchPaymentStatus.ts +3 -3
  252. package/tests/actions/patchUserMember.ts +2 -2
  253. package/tests/assertions/assertBalances.ts +1 -1
  254. package/tests/e2e/api-rate-limits.test.ts +3 -3
  255. package/tests/e2e/charge-members.test.ts +14 -14
  256. package/tests/e2e/documents.test.ts +8 -8
  257. package/tests/e2e/private-files.test.ts +4 -4
  258. package/tests/e2e/stock.test.ts +4 -4
  259. package/tests/e2e/tickets.test.ts +4 -4
  260. package/tests/helpers/TestServer.ts +2 -2
  261. package/tests/init/index.ts +7 -7
  262. package/tests/init/initAdmin.ts +1 -1
  263. /package/src/endpoints/organization/dashboard/{payments → balance-items}/PatchBalanceItemsEndpoint.ts +0 -0
@@ -205,6 +205,8 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
205
205
 
206
206
  const platformMembers: PlatformMember[] = [];
207
207
 
208
+ let payingOrganization: Organization | null = null;
209
+
208
210
  if (request.body.asOrganizationId) {
209
211
  const memberIds = Formatter.uniqueArray(
210
212
  [...request.body.memberIds, ...deleteRegistrationModels.map(i => i.memberId), ...balanceItemsModels.map(i => i.memberId).filter(m => m !== null)],
@@ -233,6 +235,7 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
233
235
  platformMembers.push(platformMember);
234
236
  }
235
237
 
238
+ payingOrganization = await Context.auth.getOrganization(request.body.asOrganizationId);
236
239
  if (memberIds.length > 0 && request.body.asOrganizationId && request.body.asOrganizationId !== organization.id) {
237
240
  // For registering members at a different organization, you need full permissions
238
241
  if (!await Context.auth.hasFullAccess(request.body.asOrganizationId)) {
@@ -288,6 +291,7 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
288
291
  if (whoWillPayNow === 'nobody') {
289
292
  // Safe and important to ignore: we are only updating the outstanding amounts
290
293
  // If we would throw here, that could leak personal data (e.g. that the user uses financial support)
294
+ request.body.totalPrice = totalPrice;
291
295
  }
292
296
  else {
293
297
  // when whoWillPay = organization/member, we should throw or the payment amount could be different / incorrect.
@@ -450,37 +454,10 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
450
454
  registrations.push(registration);
451
455
  }
452
456
 
453
- // Validate payment method
454
- if (totalPrice > 0 && whoWillPayNow !== 'nobody') {
455
- const allowedPaymentMethods = organization.meta.registrationPaymentConfiguration.getAvailablePaymentMethods({
456
- amount: totalPrice,
457
- customer: checkout.customer,
458
- });
459
-
460
- if (!checkout.paymentMethod || !allowedPaymentMethods.includes(checkout.paymentMethod)) {
461
- throw new SimpleError({
462
- code: 'invalid_payment_method',
463
- message: $t(`2b1ca6a0-662e-4326-ada1-10239b6ddc6f`),
464
- });
465
- }
466
-
467
- if ((checkout.paymentMethod !== PaymentMethod.Transfer && checkout.paymentMethod !== PaymentMethod.PointOfSale) && (!request.body.redirectUrl || !request.body.cancelUrl)) {
468
- throw new SimpleError({
469
- code: 'missing_fields',
470
- message: 'redirectUrl or cancelUrl is missing and is required for non-zero online payments',
471
- human: $t(`ebe54b63-2de6-4f22-a5ed-d3fe65194562`),
472
- });
473
- }
474
- }
475
- else {
476
- checkout.paymentMethod = PaymentMethod.Unknown;
477
- }
478
-
479
457
  console.log('Registering members using whoWillPayNow', whoWillPayNow, checkout.paymentMethod, totalPrice);
480
458
 
481
459
  const createdBalanceItems: BalanceItem[] = [];
482
460
  const deletedBalanceItems: BalanceItem[] = [];
483
- const shouldMarkValid = whoWillPayNow === 'nobody' || checkout.paymentMethod === PaymentMethod.Transfer || checkout.paymentMethod === PaymentMethod.PointOfSale || checkout.paymentMethod === PaymentMethod.Unknown;
484
461
 
485
462
  // Create negative balance items
486
463
  for (const { registration: registrationStruct, deleted } of [
@@ -834,21 +811,6 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
834
811
  // Keep a copy because createdBalanceItems will be altered - and we don't want to mark added items as valid
835
812
  const markValidList = [...createdBalanceItems];
836
813
 
837
- async function markValidIfNeeded() {
838
- if (shouldMarkValid) {
839
- for (const balanceItem of markValidList) {
840
- // Mark valid
841
- await BalanceItemService.markPaid(balanceItem, payment, organization);
842
- }
843
-
844
- // Flush balance caches so we return an up-to-date balance
845
- await BalanceItemService.flushRegistrationDiscountsCache();
846
-
847
- // We'll need to update the returned registrations as their values will have changed by marking the registration as valid
848
- await Registration.refreshAll(registrations);
849
- }
850
- }
851
-
852
814
  if (whoWillPayNow !== 'nobody') {
853
815
  const mappedBalanceItems = new Map<BalanceItem, number>();
854
816
 
@@ -868,14 +830,15 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
868
830
  }
869
831
 
870
832
  try {
871
- const response = await this.createPayment({
833
+ const response = await PaymentService.createPayment({
872
834
  balanceItems: mappedBalanceItems,
873
835
  organization,
874
836
  user,
875
837
  checkout: request.body,
876
838
  members,
839
+ serviceFeeType: 'members',
840
+ payingOrganization,
877
841
  });
878
- await markValidIfNeeded();
879
842
 
880
843
  if (response) {
881
844
  paymentUrl = response.paymentUrl;
@@ -889,7 +852,10 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
889
852
  }
890
853
  }
891
854
  else {
892
- await markValidIfNeeded();
855
+ // Mark as paid/valid without creating a payment
856
+ for (const balanceItem of markValidList) {
857
+ await BalanceItemService.markPaid(balanceItem, null, organization);
858
+ }
893
859
  }
894
860
 
895
861
  // Update occupancy
@@ -903,6 +869,9 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
903
869
  // Flush caches so data is up to date in response
904
870
  await BalanceItemService.flushCaches(organization.id);
905
871
 
872
+ // We'll need to update the returned registrations as their values will have changed by marking the registration as valid
873
+ await Registration.refreshAll(registrations);
874
+
906
875
  // Force reload registrations and group data
907
876
  Member.unloadRegistrations(members);
908
877
  await Member.loadRegistrations(members, true);
@@ -915,343 +884,4 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
915
884
  paymentQRCode,
916
885
  }));
917
886
  }
918
-
919
- async createPayment({ balanceItems, organization, user, checkout, members }: { balanceItems: Map<BalanceItem, number>; organization: Organization; user: User; checkout: IDRegisterCheckout; members: MemberWithUsersRegistrationsAndGroups[] }) {
920
- // Calculate total price to pay
921
- let totalPrice = 0;
922
- const payMembers: MemberWithUsersRegistrationsAndGroups[] = [];
923
- let hasNegative = false;
924
-
925
- for (const [balanceItem, price] of balanceItems) {
926
- if (organization.id !== balanceItem.organizationId) {
927
- throw new Error('Unexpected balance item from other organization');
928
- }
929
-
930
- if (price > 0 && price > Math.max(balanceItem.priceOpen, balanceItem.price - balanceItem.pricePaid)) {
931
- throw new SimpleError({
932
- code: 'invalid_data',
933
- message: $t(`38ddccb2-7cf6-4b47-aa71-d11ad73386d8`),
934
- });
935
- }
936
-
937
- if (price < 0 && price < Math.min(balanceItem.priceOpen, balanceItem.price - balanceItem.pricePaid)) {
938
- throw new SimpleError({
939
- code: 'invalid_data',
940
- message: $t(`dd14a1d9-c569-4d5e-bb26-569ecede4c52`),
941
- });
942
- }
943
-
944
- if (price < 0) {
945
- hasNegative = true;
946
- }
947
-
948
- totalPrice += price;
949
-
950
- if (price > 0 && balanceItem.memberId) {
951
- const member = members.find(m => m.id === balanceItem.memberId);
952
- if (!member) {
953
- throw new SimpleError({
954
- code: 'invalid_data',
955
- message: $t(`e64b8269-1cda-434d-8d6f-35be23a9d6e9`),
956
- });
957
- }
958
- payMembers.push(member);
959
- }
960
- }
961
-
962
- if (totalPrice < 0) {
963
- // todo: try to make it non-negative by reducing some balance items
964
- throw new SimpleError({
965
- code: 'negative_price',
966
- message: $t(`725715e5-b0ac-43c1-adef-dd42b8907327`),
967
- });
968
- }
969
-
970
- if (totalPrice !== checkout.totalPrice) {
971
- // Changed!
972
- throw new SimpleError({
973
- code: 'changed_price',
974
- message: $t(`e424d549-2bb8-4103-9a14-ac4063d7d454`, { total: Formatter.price(totalPrice) }),
975
- });
976
- }
977
-
978
- const payment = new Payment();
979
- payment.method = checkout.paymentMethod ?? PaymentMethod.Unknown;
980
-
981
- if (totalPrice === 0) {
982
- if (balanceItems.size === 0) {
983
- return;
984
- }
985
- // Create an egalizing payment
986
- payment.method = PaymentMethod.Unknown;
987
-
988
- if (hasNegative) {
989
- payment.type = PaymentType.Reallocation;
990
- }
991
- }
992
- else if (payment.method === PaymentMethod.Unknown) {
993
- throw new SimpleError({
994
- code: 'invalid_data',
995
- message: $t(`86c7b6f7-3ec9-4af3-a5e6-b5de6de80d73`),
996
- });
997
- }
998
-
999
- // Who will receive this money?
1000
- payment.organizationId = organization.id;
1001
-
1002
- // Who paid
1003
- payment.payingUserId = user.id;
1004
- payment.payingOrganizationId = checkout.asOrganizationId ?? null;
1005
-
1006
- // Fill in customer:
1007
- payment.customer = PaymentCustomer.create({
1008
- firstName: user.firstName,
1009
- lastName: user.lastName,
1010
- email: user.email,
1011
- });
1012
-
1013
- // Use structured transfer description prefix
1014
- let prefix = '';
1015
-
1016
- if (checkout.asOrganizationId) {
1017
- if (!checkout.customer) {
1018
- throw new SimpleError({
1019
- code: 'missing_fields',
1020
- message: 'customer is required when paying as an organization',
1021
- human: $t(`d483aa9a-289c-4c59-955f-d2f99ec533ab`),
1022
- });
1023
- }
1024
-
1025
- if (!checkout.customer.company) {
1026
- throw new SimpleError({
1027
- code: 'missing_fields',
1028
- message: 'customer.company is required when paying as an organization',
1029
- human: $t(`bc89861d-a799-4100-b06c-29d6808ba8d2`),
1030
- });
1031
- }
1032
-
1033
- const payingOrganization = await Organization.getByID(checkout.asOrganizationId);
1034
- if (!payingOrganization) {
1035
- throw new SimpleError({
1036
- code: 'invalid_data',
1037
- message: $t(`492117ce-4d5f-4cff-8f3f-8fa56bbb0fee`),
1038
- });
1039
- }
1040
-
1041
- // Search company id
1042
- // this avoids needing to check the VAT number every time
1043
- const id = checkout.customer.company.id;
1044
- const foundCompany = payingOrganization.meta.companies.find(c => c.id === id);
1045
-
1046
- if (!foundCompany) {
1047
- throw new SimpleError({
1048
- code: 'invalid_data',
1049
- message: $t(`0ab71307-8f4f-4701-b120-b552a1b6bdd0`),
1050
- });
1051
- }
1052
-
1053
- payment.customer.company = foundCompany;
1054
-
1055
- const orgNumber = parseInt(payingOrganization.uri);
1056
-
1057
- if (orgNumber !== 0 && !isNaN(orgNumber)) {
1058
- prefix = orgNumber + '';
1059
- }
1060
- }
1061
-
1062
- payment.status = PaymentStatus.Created;
1063
- payment.paidAt = null;
1064
- payment.price = totalPrice;
1065
- PaymentService.round(payment);
1066
- totalPrice = payment.price;
1067
-
1068
- if (totalPrice === 0) {
1069
- payment.status = PaymentStatus.Succeeded;
1070
- payment.paidAt = new Date();
1071
- }
1072
-
1073
- if (payment.method === PaymentMethod.Transfer) {
1074
- // remark: we cannot add the lastnames, these will get added in the frontend when it is decrypted
1075
- payment.transferSettings = organization.mappedTransferSettings;
1076
-
1077
- if (!payment.transferSettings.iban) {
1078
- throw new SimpleError({
1079
- code: 'no_iban',
1080
- message: 'No IBAN',
1081
- human: $t(`cc8b5066-a7e4-4eae-b556-f56de5d3502c`),
1082
- });
1083
- }
1084
-
1085
- const m = payMembers.map(r => r.details);
1086
- payment.generateDescription(
1087
- organization,
1088
- Formatter.groupNamesByFamily(m),
1089
- {
1090
- name: Formatter.groupNamesByFamily(m),
1091
- naam: Formatter.groupNamesByFamily(m),
1092
- email: user.email,
1093
- prefix,
1094
- },
1095
- );
1096
- }
1097
-
1098
- // Determine the payment provider
1099
- // Throws if invalid
1100
- const { provider, stripeAccount } = await organization.getPaymentProviderFor(payment.method, organization.privateMeta.registrationPaymentConfiguration);
1101
- payment.provider = provider;
1102
- payment.stripeAccountId = stripeAccount?.id ?? null;
1103
- ServiceFeeHelper.setServiceFee(payment, organization, 'members', [...balanceItems.entries()].map(([_, p]) => p));
1104
-
1105
- await payment.save();
1106
-
1107
- // Create balance item payments
1108
- const balanceItemPayments: (BalanceItemPayment & { balanceItem: BalanceItem })[] = [];
1109
-
1110
- for (const [balanceItem, price] of balanceItems) {
1111
- // Create one balance item payment to pay it in one payment
1112
- const balanceItemPayment = new BalanceItemPayment();
1113
- balanceItemPayment.balanceItemId = balanceItem.id;
1114
- balanceItemPayment.paymentId = payment.id;
1115
- balanceItemPayment.organizationId = organization.id;
1116
- balanceItemPayment.price = price;
1117
- await balanceItemPayment.save();
1118
-
1119
- balanceItemPayments.push(balanceItemPayment.setRelation(BalanceItemPayment.balanceItem, balanceItem));
1120
- }
1121
-
1122
- const description = $t(`33a926ea-9bc7-444e-becc-c0f2f70e1f0e`) + ' ' + organization.name;
1123
-
1124
- let paymentUrl: string | null = null;
1125
- let paymentQRCode: string | null = null;
1126
-
1127
- try {
1128
- // Update balance items
1129
- if (payment.method === PaymentMethod.Transfer) {
1130
- // Send a small reminder email
1131
- try {
1132
- await Registration.sendTransferEmail(user, organization, payment);
1133
- }
1134
- catch (e) {
1135
- console.error('Failed to send transfer email');
1136
- console.error(e);
1137
- }
1138
- }
1139
- else if (payment.method !== PaymentMethod.PointOfSale && payment.method !== PaymentMethod.Unknown) {
1140
- if (!checkout.redirectUrl || !checkout.cancelUrl) {
1141
- throw new Error('Should have been caught earlier');
1142
- }
1143
-
1144
- const _redirectUrl = new URL(checkout.redirectUrl);
1145
- _redirectUrl.searchParams.set('paymentId', payment.id);
1146
- _redirectUrl.searchParams.set('organizationId', organization.id); // makes sure the client uses the token associated with this organization when fetching payment polling status
1147
-
1148
- const _cancelUrl = new URL(checkout.cancelUrl);
1149
- _cancelUrl.searchParams.set('paymentId', payment.id);
1150
- _cancelUrl.searchParams.set('cancel', 'true');
1151
- _cancelUrl.searchParams.set('organizationId', organization.id); // makes sure the client uses the token associated with this organization when fetching payment polling status
1152
-
1153
- const redirectUrl = _redirectUrl.href;
1154
- const cancelUrl = _cancelUrl.href;
1155
-
1156
- const webhookUrl = 'https://' + organization.getApiHost() + '/v' + Version + '/payments/' + encodeURIComponent(payment.id) + '?exchange=true';
1157
-
1158
- if (payment.provider === PaymentProvider.Stripe) {
1159
- const stripeResult = await StripeHelper.createPayment({
1160
- payment,
1161
- stripeAccount,
1162
- redirectUrl,
1163
- cancelUrl,
1164
- statementDescriptor: organization.name,
1165
- metadata: {
1166
- organization: organization.id,
1167
- user: user.id,
1168
- payment: payment.id,
1169
- },
1170
- i18n: Context.i18n,
1171
- lineItems: balanceItemPayments,
1172
- organization,
1173
- customer: {
1174
- name: user.name ?? payMembers[0]?.details.name ?? $t(`bd1e59c8-3d4c-4097-ab35-0ce7b20d0e50`),
1175
- email: user.email,
1176
- },
1177
- });
1178
- paymentUrl = stripeResult.paymentUrl;
1179
- }
1180
- else if (payment.provider === PaymentProvider.Mollie) {
1181
- // Mollie payment
1182
- const token = await MollieToken.getTokenFor(organization.id);
1183
- if (!token) {
1184
- throw new SimpleError({
1185
- code: '',
1186
- message: $t(`b77e1f68-8928-42a2-802b-059fa73bedc3`, { method: PaymentMethodHelper.getName(payment.method) }),
1187
- });
1188
- }
1189
- const profileId = organization.privateMeta.mollieProfile?.id ?? await token.getProfileId(organization.getHost());
1190
- if (!profileId) {
1191
- throw new SimpleError({
1192
- code: '',
1193
- message: $t(`5574469f-8eee-47fe-9fb6-1b097142ac75`, { method: PaymentMethodHelper.getName(payment.method) }),
1194
- });
1195
- }
1196
- const mollieClient = createMollieClient({ accessToken: await token.getAccessToken() });
1197
- const locale = Context.i18n.locale.replace('-', '_');
1198
- const molliePayment = await mollieClient.payments.create({
1199
- amount: {
1200
- currency: 'EUR',
1201
- value: (totalPrice / 100).toFixed(2),
1202
- },
1203
- method: payment.method == PaymentMethod.Bancontact ? molliePaymentMethod.bancontact : (payment.method == PaymentMethod.iDEAL ? molliePaymentMethod.ideal : molliePaymentMethod.creditcard),
1204
- testmode: organization.privateMeta.useTestPayments ?? STAMHOOFD.environment !== 'production',
1205
- profileId,
1206
- description,
1207
- redirectUrl,
1208
- webhookUrl,
1209
- metadata: {
1210
- paymentId: payment.id,
1211
- },
1212
- locale: ['en_US', 'en_GB', 'nl_NL', 'nl_BE', 'fr_FR', 'fr_BE', 'de_DE', 'de_AT', 'de_CH', 'es_ES', 'ca_ES', 'pt_PT', 'it_IT', 'nb_NO', 'sv_SE', 'fi_FI', 'da_DK', 'is_IS', 'hu_HU', 'pl_PL', 'lv_LV', 'lt_LT'].includes(locale) ? (locale as any) : null,
1213
- });
1214
- paymentUrl = molliePayment.getCheckoutUrl();
1215
-
1216
- // Save payment
1217
- const dbPayment = new MolliePayment();
1218
- dbPayment.paymentId = payment.id;
1219
- dbPayment.mollieId = molliePayment.id;
1220
- await dbPayment.save();
1221
- }
1222
- else if (payment.provider === PaymentProvider.Payconiq) {
1223
- ({ paymentUrl, paymentQRCode } = await PayconiqPayment.createPayment(payment, organization, description, redirectUrl, webhookUrl));
1224
- }
1225
- else if (payment.provider == PaymentProvider.Buckaroo) {
1226
- // Increase request timeout because buckaroo is super slow (in development)
1227
- Context.request.request?.setTimeout(60 * 1000);
1228
- const buckaroo = new BuckarooHelper(organization.privateMeta?.buckarooSettings?.key ?? '', organization.privateMeta?.buckarooSettings?.secret ?? '', organization.privateMeta.useTestPayments ?? STAMHOOFD.environment !== 'production');
1229
- const ip = Context.request.getIP();
1230
- paymentUrl = await buckaroo.createPayment(payment, ip, description, redirectUrl, webhookUrl);
1231
- await payment.save();
1232
-
1233
- // TypeScript doesn't understand that the status can change and isn't a const....
1234
- if ((payment.status as any) === PaymentStatus.Failed) {
1235
- throw new SimpleError({
1236
- code: 'payment_failed',
1237
- message: $t(`b77e1f68-8928-42a2-802b-059fa73bedc3`, { method: PaymentMethodHelper.getName(payment.method) }),
1238
- });
1239
- }
1240
- }
1241
- }
1242
- }
1243
- catch (e) {
1244
- await PaymentService.handlePaymentStatusUpdate(payment, organization, PaymentStatus.Failed);
1245
- throw e;
1246
- }
1247
-
1248
- return {
1249
- payment,
1250
- balanceItemPayments,
1251
- provider,
1252
- stripeAccount,
1253
- paymentUrl,
1254
- paymentQRCode,
1255
- };
1256
- }
1257
887
  }
@@ -5,9 +5,9 @@ import { Decoder } from '@simonbackx/simple-encoding';
5
5
  import { SimpleError } from '@simonbackx/simple-errors';
6
6
  import { RegistrationPeriod } from '@stamhoofd/models';
7
7
  import { applySQLSorter, compileToSQLFilter, SQLFilterDefinitions, SQLSortDefinitions } from '@stamhoofd/sql';
8
- import { Context } from '../../../helpers/Context';
9
- import { registrationPeriodFilterCompilers } from '../../../sql-filters/registration-periods';
10
- import { registrationPeriodSorters } from '../../../sql-sorters/registration-periods';
8
+ import { Context } from '../../../helpers/Context.js';
9
+ import { registrationPeriodFilterCompilers } from '../../../sql-filters/registration-periods.js';
10
+ import { registrationPeriodSorters } from '../../../sql-sorters/registration-periods.js';
11
11
 
12
12
  type Params = Record<string, never>;
13
13
  type Query = LimitedFilteredRequest;
@@ -4,8 +4,8 @@ import { RegistrationPeriod as RegistrationPeriodStruct } from '@stamhoofd/struc
4
4
 
5
5
  import { SimpleError } from '@simonbackx/simple-errors';
6
6
  import { Organization, Platform, RegistrationPeriod } from '@stamhoofd/models';
7
- import { Context } from '../../../helpers/Context';
8
- import { PeriodHelper } from '../../../helpers/PeriodHelper';
7
+ import { Context } from '../../../helpers/Context.js';
8
+ import { PeriodHelper } from '../../../helpers/PeriodHelper.js';
9
9
 
10
10
  type Params = Record<string, never>;
11
11
  type Query = undefined;
@@ -1,10 +1,10 @@
1
1
  import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
2
2
  import { LoginProviderType, OpenIDClientConfiguration } from '@stamhoofd/structures';
3
3
 
4
- import { Context } from '../../../helpers/Context';
4
+ import { Context } from '../../../helpers/Context.js';
5
5
  import { Platform } from '@stamhoofd/models';
6
6
  import { AutoEncoder, Decoder, EnumDecoder, field } from '@simonbackx/simple-encoding';
7
- import { SSOService } from '../../../services/SSOService';
7
+ import { SSOService } from '../../../services/SSOService.js';
8
8
 
9
9
  type Params = Record<string, never>;
10
10
  export class SSOQuery extends AutoEncoder {
@@ -2,9 +2,9 @@ import { AutoEncoderPatchType, Decoder } from '@simonbackx/simple-encoding';
2
2
  import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
3
3
  import { OpenIDClientConfiguration } from '@stamhoofd/structures';
4
4
 
5
- import { Context } from '../../../helpers/Context';
6
- import { SSOService } from '../../../services/SSOService';
7
- import { SSOQuery } from './GetSSOEndpoint';
5
+ import { Context } from '../../../helpers/Context.js';
6
+ import { SSOService } from '../../../services/SSOService.js';
7
+ import { SSOQuery } from './GetSSOEndpoint.js';
8
8
 
9
9
  type Params = Record<string, never>;
10
10
  type Query = SSOQuery;
@@ -0,0 +1,55 @@
1
+ import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
2
+ import { SimpleError } from '@simonbackx/simple-errors';
3
+ import { BalanceItem } from '@stamhoofd/models';
4
+ import { BalanceItemWithPayments } from '@stamhoofd/structures';
5
+
6
+ import { AuthenticatedStructures } from '../../../../helpers/AuthenticatedStructures.js';
7
+ import { Context } from '../../../../helpers/Context.js';
8
+
9
+ type Params = { id: string };
10
+ type Query = undefined;
11
+ type Body = undefined;
12
+ type ResponseBody = BalanceItemWithPayments;
13
+
14
+ export class GetBalanceItemEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
15
+ protected doesMatch(request: Request): [true, Params] | [false] {
16
+ if (request.method !== 'GET') {
17
+ return [false];
18
+ }
19
+
20
+ const params = Endpoint.parseParameters(request.url, '/balance-items/@id', { id: String });
21
+
22
+ if (params) {
23
+ return [true, params as Params];
24
+ }
25
+ return [false];
26
+ }
27
+
28
+ async handle(request: DecodedRequest<Params, Query, Body>) {
29
+ const organization = await Context.setOrganizationScope();
30
+ await Context.authenticate();
31
+
32
+ if (!await Context.auth.hasSomeAccess(organization.id)) {
33
+ throw Context.auth.error();
34
+ }
35
+
36
+ const balanceItem = await BalanceItem.getByID(request.params.id);
37
+
38
+ if (!balanceItem || balanceItem.organizationId !== organization.id) {
39
+ throw new SimpleError({
40
+ code: 'not_found',
41
+ statusCode: 404,
42
+ message: 'Balance item not found',
43
+ human: $t('7bc08947-2409-4a7c-b597-2d6d43884a96'),
44
+ });
45
+ }
46
+
47
+ if (!await Context.auth.canAccessBalanceItems([balanceItem])) {
48
+ throw Context.auth.error();
49
+ }
50
+
51
+ return new Response(
52
+ (await AuthenticatedStructures.balanceItemsWithPayments([balanceItem]))[0],
53
+ );
54
+ }
55
+ }
@@ -0,0 +1,43 @@
1
+ import { Decoder } from '@simonbackx/simple-encoding';
2
+ import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
3
+ import { CountFilteredRequest, CountResponse } from '@stamhoofd/structures';
4
+
5
+ import { Context } from '../../../../helpers/Context.js';
6
+ import { GetBalanceItemsEndpoint } from './GetBalanceItemsEndpoint.js';
7
+
8
+ type Params = Record<string, never>;
9
+ type Query = CountFilteredRequest;
10
+ type Body = undefined;
11
+ type ResponseBody = CountResponse;
12
+
13
+ export class GetBalanceItemsCountEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
14
+ queryDecoder = CountFilteredRequest as Decoder<CountFilteredRequest>;
15
+
16
+ protected doesMatch(request: Request): [true, Params] | [false] {
17
+ if (request.method !== 'GET') {
18
+ return [false];
19
+ }
20
+
21
+ const params = Endpoint.parseParameters(request.url, '/balance-items/count', {});
22
+
23
+ if (params) {
24
+ return [true, params as Params];
25
+ }
26
+ return [false];
27
+ }
28
+
29
+ async handle(request: DecodedRequest<Params, Query, Body>) {
30
+ await Context.setOrganizationScope();
31
+ await Context.authenticate();
32
+ const query = await GetBalanceItemsEndpoint.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
+ }