@stamhoofd/backend 2.115.1 → 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 (284) 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 +27 -19
  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 +6 -4
  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/delete-archived-data.ts +47 -0
  29. package/src/crons/endFunctionsOfUsersWithoutRegistration.ts +1 -1
  30. package/src/crons/index.ts +1 -0
  31. package/src/crons.ts +3 -3
  32. package/src/debug.ts +230 -0
  33. package/src/email-recipient-loaders/documents.ts +1 -1
  34. package/src/email-recipient-loaders/members.ts +1 -1
  35. package/src/email-recipient-loaders/orders.ts +3 -3
  36. package/src/email-recipient-loaders/payments.ts +118 -353
  37. package/src/email-replacements/getEmailReplacementsForPayment.ts +321 -0
  38. package/src/endpoints/admin/members/ChargeMembersEndpoint.ts +9 -7
  39. package/src/endpoints/admin/memberships/ChargeMembershipsEndpoint.ts +2 -2
  40. package/src/endpoints/admin/memberships/GetChargeMembershipsSummaryEndpoint.ts +1 -1
  41. package/src/endpoints/admin/organizations/ChargeOrganizationsEndpoint.ts +16 -50
  42. package/src/endpoints/admin/organizations/GetOrganizationsCountEndpoint.ts +2 -2
  43. package/src/endpoints/admin/organizations/GetOrganizationsEndpoint.ts +4 -4
  44. package/src/endpoints/admin/organizations/PatchOrganizationsEndpoint.ts +21 -6
  45. package/src/endpoints/admin/registrations/ChargeRegistrationsEndpoint.ts +9 -7
  46. package/src/endpoints/auth/CreateAdminEndpoint.ts +2 -2
  47. package/src/endpoints/auth/CreateTokenEndpoint.test.ts +2 -2
  48. package/src/endpoints/auth/CreateTokenEndpoint.ts +1 -1
  49. package/src/endpoints/auth/DeleteTokenEndpoint.ts +1 -1
  50. package/src/endpoints/auth/DeleteUserEndpoint.ts +1 -1
  51. package/src/endpoints/auth/ForgotPasswordEndpoint.ts +1 -1
  52. package/src/endpoints/auth/GetOtherUserEndpoint.ts +2 -2
  53. package/src/endpoints/auth/GetUserEndpoint.test.ts +2 -2
  54. package/src/endpoints/auth/GetUserEndpoint.ts +2 -2
  55. package/src/endpoints/auth/OpenIDConnectAuthTokenEndpoint.ts +2 -2
  56. package/src/endpoints/auth/OpenIDConnectCallbackEndpoint.ts +2 -2
  57. package/src/endpoints/auth/OpenIDConnectStartEndpoint.ts +2 -2
  58. package/src/endpoints/auth/PatchUserEndpoint.ts +4 -4
  59. package/src/endpoints/auth/PollEmailVerificationEndpoint.ts +1 -1
  60. package/src/endpoints/auth/RetryEmailVerificationEndpoint.ts +1 -1
  61. package/src/endpoints/auth/SignupEndpoint.ts +1 -1
  62. package/src/endpoints/auth/VerifyEmailEndpoint.ts +1 -1
  63. package/src/endpoints/global/addresses/ValidateAddressEndpoint.ts +1 -1
  64. package/src/endpoints/global/audit-logs/GetAuditLogsEndpoint.ts +4 -4
  65. package/src/endpoints/global/billing/ActivatePackagesEndpoint.ts +191 -7
  66. package/src/endpoints/global/billing/DeactivatePackageEndpoint.ts +64 -0
  67. package/src/endpoints/global/email/GetAdminEmailsEndpoint.test.ts +2 -2
  68. package/src/endpoints/global/email/GetAdminEmailsEndpoint.ts +3 -3
  69. package/src/endpoints/global/email/GetEmailAddressEndpoint.ts +1 -1
  70. package/src/endpoints/global/email/GetEmailEndpoint.ts +1 -1
  71. package/src/endpoints/global/email/GetUserEmailsEndpoint.test.ts +2 -2
  72. package/src/endpoints/global/email/GetUserEmailsEndpoint.ts +3 -3
  73. package/src/endpoints/global/email/ManageEmailAddressEndpoint.ts +62 -16
  74. package/src/endpoints/global/email/PatchEmailEndpoint.test.ts +6 -6
  75. package/src/endpoints/global/email-recipients/GetEmailRecipientsCountEndpoint.ts +2 -2
  76. package/src/endpoints/global/email-recipients/GetEmailRecipientsEndpoint.test.ts +2 -2
  77. package/src/endpoints/global/email-recipients/GetEmailRecipientsEndpoint.ts +4 -4
  78. package/src/endpoints/global/email-recipients/RetryEmailRecipientEndpoint.ts +1 -1
  79. package/src/endpoints/global/email-recipients/helpers/validateEmailRecipientFilter.ts +1 -1
  80. package/src/endpoints/global/events/GetEventNotificationsCountEndpoint.ts +2 -2
  81. package/src/endpoints/global/events/GetEventNotificationsEndpoint.ts +4 -4
  82. package/src/endpoints/global/events/GetEventsEndpoint.ts +4 -4
  83. package/src/endpoints/global/events/PatchEventNotificationsEndpoint.test.ts +2 -2
  84. package/src/endpoints/global/events/PatchEventNotificationsEndpoint.ts +3 -3
  85. package/src/endpoints/global/events/PatchEventsEndpoint.test.ts +2 -2
  86. package/src/endpoints/global/events/PatchEventsEndpoint.ts +5 -5
  87. package/src/endpoints/global/files/ExportToExcelEndpoint.ts +4 -4
  88. package/src/endpoints/global/files/GetFileCache.ts +2 -2
  89. package/src/endpoints/global/files/UploadFile.ts +2 -2
  90. package/src/endpoints/global/files/UploadImage.ts +1 -1
  91. package/src/endpoints/global/groups/GetGroupsEndpoint.test.ts +3 -3
  92. package/src/endpoints/global/groups/GetGroupsEndpoint.ts +4 -4
  93. package/src/endpoints/global/members/GetMemberFamilyEndpoint.ts +4 -4
  94. package/src/endpoints/global/members/GetMembersEndpoint.test.ts +3 -3
  95. package/src/endpoints/global/members/GetMembersEndpoint.ts +4 -16
  96. package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.ts +8 -7
  97. package/src/endpoints/global/members/helpers/validateGroupFilter.ts +1 -1
  98. package/src/endpoints/global/organizations/CreateOrganizationEndpoint.test.ts +2 -2
  99. package/src/endpoints/global/organizations/CreateOrganizationEndpoint.ts +23 -12
  100. package/src/endpoints/global/organizations/GetOrganizationFromDomainEndpoint.test.ts +2 -2
  101. package/src/endpoints/global/organizations/GetOrganizationFromDomainEndpoint.ts +1 -1
  102. package/src/endpoints/global/organizations/GetOrganizationFromUriEndpoint.ts +1 -1
  103. package/src/endpoints/global/organizations/SearchOrganizationEndpoint.test.ts +2 -2
  104. package/src/endpoints/global/organizations/SearchOrganizationEndpoint.ts +1 -1
  105. package/src/endpoints/global/payments/StripeWebhookEndpoint.ts +3 -3
  106. package/src/endpoints/global/platform/GetPlatformAdminsEndpoint.ts +1 -1
  107. package/src/endpoints/global/platform/GetPlatformEndpoint.test.ts +2 -2
  108. package/src/endpoints/global/platform/GetPlatformEndpoint.ts +1 -1
  109. package/src/endpoints/global/platform/PatchPlatformEnpoint.test.ts +2 -2
  110. package/src/endpoints/global/platform/PatchPlatformEnpoint.ts +7 -7
  111. package/src/endpoints/global/registration/GetRegistrationsEndpoint.test.ts +2 -2
  112. package/src/endpoints/global/registration/GetRegistrationsEndpoint.ts +1 -2
  113. package/src/endpoints/global/registration/GetUserDetailedPayableBalanceEndpoint.ts +2 -2
  114. package/src/endpoints/global/registration/GetUserDocumentsEndpoint.ts +2 -2
  115. package/src/endpoints/global/registration/GetUserMembersEndpoint.ts +2 -2
  116. package/src/endpoints/global/registration/GetUserPayableBalanceEndpoint.ts +2 -2
  117. package/src/endpoints/global/registration/RegisterMembersEndpoint.test.ts +2 -2
  118. package/src/endpoints/global/registration/RegisterMembersEndpoint.ts +24 -389
  119. package/src/endpoints/global/registration-periods/GetRegistrationPeriodsEndpoint.ts +3 -3
  120. package/src/endpoints/global/registration-periods/PatchRegistrationPeriodsEndpoint.ts +2 -2
  121. package/src/endpoints/global/sso/GetSSOEndpoint.ts +2 -2
  122. package/src/endpoints/global/sso/SetSSOEndpoint.ts +3 -3
  123. package/src/endpoints/organization/dashboard/balance-items/GetBalanceItemEndpoint.ts +55 -0
  124. package/src/endpoints/organization/dashboard/balance-items/GetBalanceItemsCountEndpoint.ts +43 -0
  125. package/src/endpoints/organization/dashboard/balance-items/GetBalanceItemsEndpoint.ts +160 -0
  126. package/src/endpoints/organization/dashboard/{payments → balance-items}/PatchBalanceItemsEndpoint.ts +1 -1
  127. package/src/endpoints/organization/dashboard/billing/GetOrganizationDetailedPayableBalanceEndpoint.ts +2 -2
  128. package/src/endpoints/organization/dashboard/billing/GetOrganizationPayableBalanceEndpoint.ts +2 -2
  129. package/src/endpoints/organization/dashboard/billing/GetPackagesEndpoint.test.ts +3 -3
  130. package/src/endpoints/organization/dashboard/billing/GetPackagesEndpoint.ts +3 -3
  131. package/src/endpoints/organization/dashboard/documents/GetDocumentTemplateXML.ts +1 -1
  132. package/src/endpoints/organization/dashboard/documents/GetDocumentsCountEndpoint.ts +2 -2
  133. package/src/endpoints/organization/dashboard/documents/PatchDocumentEndpoint.ts +2 -2
  134. package/src/endpoints/organization/dashboard/email/CheckEmailBouncesEndpoint.ts +37 -6
  135. package/src/endpoints/organization/dashboard/email-templates/GetEmailTemplatesEndpoint.test.ts +2 -2
  136. package/src/endpoints/organization/dashboard/email-templates/GetEmailTemplatesEndpoint.ts +3 -3
  137. package/src/endpoints/organization/dashboard/email-templates/PatchEmailTemplatesEndpoint.test.ts +2 -2
  138. package/src/endpoints/organization/dashboard/email-templates/PatchEmailTemplatesEndpoint.ts +1 -1
  139. package/src/endpoints/organization/dashboard/invoices/PatchInvoicesEndpoint.ts +53 -0
  140. package/src/endpoints/organization/dashboard/mollie/CheckMollieEndpoint.ts +2 -2
  141. package/src/endpoints/organization/dashboard/mollie/ConnectMollieEndpoint.ts +3 -3
  142. package/src/endpoints/organization/dashboard/mollie/DisconnectMollieEndpoint.ts +2 -2
  143. package/src/endpoints/organization/dashboard/mollie/GetMollieDashboardEndpoint.ts +1 -1
  144. package/src/endpoints/organization/dashboard/nolt/CreateNoltTokenEndpoint.ts +1 -1
  145. package/src/endpoints/organization/dashboard/organization/GetOrganizationArchivedGroups.ts +2 -2
  146. package/src/endpoints/organization/dashboard/organization/GetOrganizationDeletedGroups.ts +2 -2
  147. package/src/endpoints/organization/dashboard/organization/GetUitpasClientIdEndpoint.ts +2 -2
  148. package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.test.ts +2 -2
  149. package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.ts +12 -1
  150. package/src/endpoints/organization/dashboard/organization/SearchUitpasOrganizersEndpoint.ts +2 -2
  151. package/src/endpoints/organization/dashboard/organization/SetOrganizationDomainEndpoint.ts +2 -2
  152. package/src/endpoints/organization/dashboard/organization/SetUitpasClientCredentialsEndpoint.ts +2 -2
  153. package/src/endpoints/organization/dashboard/payments/GetPaymentsCountEndpoint.ts +2 -2
  154. package/src/endpoints/organization/dashboard/payments/GetPaymentsEndpoint.ts +4 -4
  155. package/src/endpoints/organization/dashboard/receivable-balances/GetReceivableBalanceEndpoint.ts +12 -12
  156. package/src/endpoints/organization/dashboard/receivable-balances/GetReceivableBalancesCountEndpoint.ts +2 -2
  157. package/src/endpoints/organization/dashboard/receivable-balances/GetReceivableBalancesEndpoint.ts +4 -4
  158. package/src/endpoints/organization/dashboard/registration-periods/GetOrganizationRegistrationPeriodsEndpoint.test.ts +2 -2
  159. package/src/endpoints/organization/dashboard/registration-periods/GetOrganizationRegistrationPeriodsEndpoint.ts +4 -4
  160. package/src/endpoints/organization/dashboard/registration-periods/MoveRegistrationPeriods.test.ts +2 -2
  161. package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.test.ts +2 -2
  162. package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.ts +3 -3
  163. package/src/endpoints/organization/dashboard/registration-periods/SetupStepReviewEndpoint.ts +2 -2
  164. package/src/endpoints/organization/dashboard/stripe/ConnectStripeEndpoint.ts +3 -3
  165. package/src/endpoints/organization/dashboard/stripe/DeleteStripeAccountEndpoint.ts +3 -3
  166. package/src/endpoints/organization/dashboard/stripe/GetStripeAccountLinkEndpoint.ts +2 -2
  167. package/src/endpoints/organization/dashboard/stripe/GetStripeAccountsEndpoint.ts +1 -1
  168. package/src/endpoints/organization/dashboard/stripe/GetStripeLoginLinkEndpoint.ts +2 -2
  169. package/src/endpoints/organization/dashboard/stripe/UpdateStripeAccountEndpoint.ts +3 -3
  170. package/src/endpoints/organization/dashboard/users/CreateApiUserEndpoint.test.ts +3 -3
  171. package/src/endpoints/organization/dashboard/users/CreateApiUserEndpoint.ts +1 -1
  172. package/src/endpoints/organization/dashboard/users/DeleteUserEndpoint.ts +1 -1
  173. package/src/endpoints/organization/dashboard/users/GetApiUsersEndpoint.ts +1 -1
  174. package/src/endpoints/organization/dashboard/users/GetOrganizationAdminsEndpoint.ts +1 -1
  175. package/src/endpoints/organization/dashboard/users/PatchApiUserEndpoint.test.ts +3 -3
  176. package/src/endpoints/organization/dashboard/users/PatchApiUserEndpoint.ts +1 -1
  177. package/src/endpoints/organization/dashboard/webshops/CreateWebshopEndpoint.ts +1 -1
  178. package/src/endpoints/organization/dashboard/webshops/DeleteWebshopEndpoint.ts +2 -2
  179. package/src/endpoints/organization/dashboard/webshops/GetDiscountCodesEndpoint.ts +1 -1
  180. package/src/endpoints/organization/dashboard/webshops/GetWebshopOrdersCountEndpoint.ts +2 -2
  181. package/src/endpoints/organization/dashboard/webshops/GetWebshopOrdersEndpoint.ts +5 -5
  182. package/src/endpoints/organization/dashboard/webshops/GetWebshopTicketsEndpoint.ts +5 -5
  183. package/src/endpoints/organization/dashboard/webshops/GetWebshopUriAvailabilityEndpoint.ts +1 -1
  184. package/src/endpoints/organization/dashboard/webshops/PatchDiscountCodesEndpoint.ts +1 -1
  185. package/src/endpoints/organization/dashboard/webshops/PatchWebshopEndpoint.ts +1 -1
  186. package/src/endpoints/organization/dashboard/webshops/PatchWebshopOrdersEndpoint.ts +1 -1
  187. package/src/endpoints/organization/dashboard/webshops/PatchWebshopTicketsEndpoint.ts +1 -1
  188. package/src/endpoints/organization/dashboard/webshops/SearchUitpasEventsEndpoint.ts +2 -2
  189. package/src/endpoints/organization/dashboard/webshops/VerifyWebshopDomainEndpoint.ts +1 -1
  190. package/src/endpoints/organization/shared/ExchangePaymentEndpoint.ts +3 -3
  191. package/src/endpoints/organization/shared/GetDocumentHtml.ts +1 -1
  192. package/src/endpoints/organization/shared/GetPaymentEndpoint.ts +2 -2
  193. package/src/endpoints/organization/shared/auth/GetOrganizationEndpoint.test.ts +2 -2
  194. package/src/endpoints/organization/shared/auth/GetOrganizationEndpoint.ts +2 -2
  195. package/src/endpoints/organization/webshops/CheckWebshopDiscountCodesEndpoint.ts +1 -1
  196. package/src/endpoints/organization/webshops/GetOrderByPaymentEndpoint.ts +1 -1
  197. package/src/endpoints/organization/webshops/GetOrderEndpoint.ts +1 -1
  198. package/src/endpoints/organization/webshops/GetTicketsEndpoint.ts +1 -1
  199. package/src/endpoints/organization/webshops/GetWebshopEndpoint.test.ts +2 -2
  200. package/src/endpoints/organization/webshops/GetWebshopEndpoint.ts +2 -2
  201. package/src/endpoints/organization/webshops/PlaceOrderEndpoint.test.ts +4 -4
  202. package/src/excel-loaders/balance-items.ts +268 -0
  203. package/src/excel-loaders/event-notifications.ts +3 -3
  204. package/src/excel-loaders/index.ts +6 -5
  205. package/src/excel-loaders/organizations.ts +4 -4
  206. package/src/excel-loaders/payments.ts +11 -3
  207. package/src/excel-loaders/receivable-balances.ts +2 -2
  208. package/src/helpers/AddressValidator.test.ts +1 -1
  209. package/src/helpers/AddressValidator.ts +1 -1
  210. package/src/helpers/AdminPermissionChecker.ts +20 -17
  211. package/src/helpers/AuthenticatedStructures.ts +194 -109
  212. package/src/helpers/Context.ts +2 -0
  213. package/src/helpers/EmailResumer.ts +1 -1
  214. package/src/helpers/FlagMomentCleanup.ts +1 -1
  215. package/src/helpers/ForwardHandler.test.ts +1 -1
  216. package/src/helpers/ForwardHandler.ts +1 -1
  217. package/src/helpers/GlobalHelper.ts +4 -4
  218. package/src/helpers/GroupBuilder.ts +417 -0
  219. package/src/helpers/GroupedThrottledQueue.test.ts +1 -1
  220. package/src/helpers/GroupedThrottledQueue.ts +1 -1
  221. package/src/helpers/MemberUserSyncer.test.ts +1 -1
  222. package/src/helpers/MemberUserSyncer.ts +1 -1
  223. package/src/helpers/PeriodHelper.ts +5 -45
  224. package/src/helpers/ServiceFeeHelper.ts +5 -1
  225. package/src/helpers/SetupStepUpdater.ts +5 -4
  226. package/src/helpers/TagHelper.ts +1 -1
  227. package/src/helpers/ThrottledQueue.test.ts +1 -1
  228. package/src/helpers/ViesHelper.ts +9 -0
  229. package/src/helpers/email-html-helpers.ts +0 -41
  230. package/src/helpers/outstandingBalanceJoin.ts +3 -1
  231. package/src/middleware/ContextMiddleware.ts +1 -1
  232. package/src/seeds/1726572303-schedule-stock-updates.ts +1 -1
  233. package/src/seeds/1726847064-setup-steps.ts +1 -1
  234. package/src/seeds/1728928974-update-cached-outstanding-balance-from-items.ts +1 -1
  235. package/src/seeds/1740046783-update-membership.ts +1 -1
  236. package/src/seeds/1754560914-groups-prices.test.ts +1 -1
  237. package/src/seeds/1755876819-remove-duplicate-members.ts +1 -1
  238. package/src/seeds/1760702454-update-cached-outstanding-balance-from-items.ts +1 -1
  239. package/src/seeds/1761665607-sync-member-users.ts +1 -1
  240. package/src/seeds/data/default-email-templates.sql +1 -1
  241. package/src/seeds-temporary/1732117645-move-rrn.ts +1 -1
  242. package/src/services/AuditLogService.ts +1 -41
  243. package/src/services/BalanceItemPaymentService.ts +1 -1
  244. package/src/services/BalanceItemService.ts +12 -5
  245. package/src/services/EventNotificationService.ts +3 -3
  246. package/src/services/InvoiceService.ts +131 -17
  247. package/src/services/MemberRecordStore.ts +1 -1
  248. package/src/services/PaymentReallocationService.test.ts +9 -10
  249. package/src/services/PaymentReallocationService.ts +1 -1
  250. package/src/services/PaymentService.ts +560 -18
  251. package/src/services/PlatformMembershipService.ts +3 -3
  252. package/src/services/RegistrationService.ts +3 -3
  253. package/src/services/SSOService.ts +2 -2
  254. package/src/services/STPackageService.ts +241 -0
  255. package/src/services/uitpas/UitpasService.test.ts +1 -1
  256. package/src/sql-filters/balance-items.ts +56 -0
  257. package/src/sql-filters/emails.ts +1 -1
  258. package/src/sql-filters/event-notifications.ts +2 -2
  259. package/src/sql-filters/events.ts +51 -0
  260. package/src/sql-filters/members.ts +38 -2
  261. package/src/sql-filters/receivable-balances.ts +3 -3
  262. package/src/sql-filters/users.ts +10 -0
  263. package/src/sql-sorters/balance-items.ts +36 -0
  264. package/src/sql-sorters/document-templates.ts +2 -2
  265. package/src/sql-sorters/documents.ts +2 -2
  266. package/src/sql-sorters/event-notifications.ts +1 -1
  267. package/src/sql-sorters/members.ts +2 -2
  268. package/src/sql-sorters/orders.ts +2 -2
  269. package/src/sql-sorters/tickets.ts +2 -2
  270. package/tests/actions/patchOrganizationMember.ts +3 -3
  271. package/tests/actions/patchPaymentStatus.ts +3 -3
  272. package/tests/actions/patchUserMember.ts +2 -2
  273. package/tests/assertions/assertBalances.ts +1 -1
  274. package/tests/e2e/api-rate-limits.test.ts +3 -3
  275. package/tests/e2e/charge-members.test.ts +14 -14
  276. package/tests/e2e/documents.test.ts +8 -8
  277. package/tests/e2e/private-files.test.ts +4 -4
  278. package/tests/e2e/register.test.ts +10 -10
  279. package/tests/e2e/stock.test.ts +4 -4
  280. package/tests/e2e/tickets.test.ts +4 -4
  281. package/tests/helpers/TestServer.ts +2 -2
  282. package/tests/init/index.ts +7 -7
  283. package/tests/init/initAdmin.ts +1 -1
  284. package/src/endpoints/global/registration/GetPaymentRegistrations.ts +0 -67
@@ -1,6 +1,6 @@
1
1
  import { AutoEncoderPatchType, PatchMap } from '@simonbackx/simple-encoding';
2
2
  import { isSimpleError, isSimpleErrors, SimpleError } from '@simonbackx/simple-errors';
3
- import { BalanceItem, CachedBalance, Document, Email, EmailTemplate, Event, EventNotification, Group, Member, MemberPlatformMembership, MemberWithRegistrations, Order, Organization, OrganizationRegistrationPeriod, Payment, Registration, User, Webshop } from '@stamhoofd/models';
3
+ import { BalanceItem, CachedBalance, Document, Email, EmailTemplate, Event, EventNotification, Group, Member, MemberPlatformMembership, MemberWithRegistrations, MemberWithUsers, MemberWithUsersAndRegistrations, MemberWithUsersRegistrationsAndGroups, Order, Organization, OrganizationRegistrationPeriod, Payment, Registration, User, Webshop } from '@stamhoofd/models';
4
4
  import { AccessRight, EmailTemplate as EmailTemplateStruct, EventPermissionChecker, FinancialSupportSettings, GroupCategory, GroupStatus, GroupType, MemberWithRegistrationsBlob, PermissionLevel, PermissionsResourceType, Platform as PlatformStruct, ReceivableBalanceType, RecordSettings, ResourcePermissions, UitpasNumberDetails, UitpasSocialTariff, UitpasSocialTariffStatus } from '@stamhoofd/structures';
5
5
  import { Formatter } from '@stamhoofd/utility';
6
6
  import { MemberRecordStore } from '../services/MemberRecordStore.js';
@@ -16,7 +16,7 @@ export class AdminPermissionChecker {
16
16
  /**
17
17
  * The member that is linked to this user = is this user
18
18
  */
19
- member: MemberWithRegistrations | null = null;
19
+ member: MemberWithUsersRegistrationsAndGroups | null = null;
20
20
  platform: PlatformStruct;
21
21
 
22
22
  organizationCache: Map<string, Organization | Promise<Organization | undefined>> = new Map();
@@ -361,7 +361,7 @@ export class AdminPermissionChecker {
361
361
  return await this.hasFullAccess(organizationId);
362
362
  }
363
363
 
364
- async canAccessMember(member: MemberWithRegistrations, permissionLevel: PermissionLevel = PermissionLevel.Read) {
364
+ async canAccessMember(member: MemberWithUsersAndRegistrations, permissionLevel: PermissionLevel = PermissionLevel.Read) {
365
365
  if (permissionLevel !== PermissionLevel.Full && this.isUserManager(member)) {
366
366
  return true;
367
367
  }
@@ -407,7 +407,7 @@ export class AdminPermissionChecker {
407
407
  * The server will temporarily grant the user access to this member, and store this in the server
408
408
  * memory. This is required for adding new members to an organization (first add member -> then patch with registrations, which requires write access).
409
409
  */
410
- async temporarilyGrantMemberAccess(member: MemberWithRegistrations, permissionLevel: PermissionLevel = PermissionLevel.Write) {
410
+ async temporarilyGrantMemberAccess(member: MemberWithUsersRegistrationsAndGroups, permissionLevel: PermissionLevel = PermissionLevel.Write) {
411
411
  console.log('Temporarily granting access to member', member.id, 'for user', this.user.id);
412
412
  addTemporaryMemberAccess(this.user.id, member.id, permissionLevel);
413
413
  }
@@ -415,11 +415,11 @@ export class AdminPermissionChecker {
415
415
  /**
416
416
  * Only full admins can delete members permanently
417
417
  */
418
- async canDeleteMember(member: MemberWithRegistrations) {
418
+ async canDeleteMember(member: MemberWithUsersAndRegistrations) {
419
419
  if (member.registrations.length === 0 && this.isUserManager(member)) {
420
420
  const cachedBalance = await CachedBalance.getForObjects([member.id], null, ReceivableBalanceType.member);
421
421
  if (cachedBalance.length === 0 || (cachedBalance[0].amountOpen === 0 && cachedBalance[0].amountPending === 0)) {
422
- const platformMemberships = await MemberPlatformMembership.where({ memberId: member.id });
422
+ const platformMemberships = await MemberPlatformMembership.select().where('memberId', member.id).fetch();
423
423
  if (platformMemberships.length === 0) {
424
424
  return true;
425
425
  }
@@ -436,7 +436,7 @@ export class AdminPermissionChecker {
436
436
  /**
437
437
  * Note: only checks admin permissions. Users that 'own' this member can also access it but that does not use the AdminPermissionChecker
438
438
  */
439
- async canAccessRegistration(registration: Registration, permissionLevel: PermissionLevel = PermissionLevel.Read, checkMember: boolean | MemberWithRegistrations = true) {
439
+ async canAccessRegistration(registration: Registration, permissionLevel: PermissionLevel = PermissionLevel.Read, checkMember: boolean | MemberWithUsersRegistrationsAndGroups = true) {
440
440
  if (registration.deactivatedAt || !registration.registeredAt) {
441
441
  if (!checkMember) {
442
442
  // We can't grant access to a member because of a deactivated registration
@@ -840,7 +840,7 @@ export class AdminPermissionChecker {
840
840
  return false;
841
841
  }
842
842
 
843
- async canLinkBalanceItemToMember(member: MemberWithRegistrations) {
843
+ async canLinkBalanceItemToMember(member: MemberWithUsersAndRegistrations) {
844
844
  if (!this.checkScope(member.organizationId)) {
845
845
  return false;
846
846
  }
@@ -928,8 +928,11 @@ export class AdminPermissionChecker {
928
928
  return this.hasFullAccess(organizationId);
929
929
  }
930
930
 
931
- async canAccessEmailBounces(organizationId: string) {
932
- return this.hasSomeAccess(organizationId);
931
+ async canAccessEmailBounces(organizationId: string | null) {
932
+ if (organizationId) {
933
+ return this.hasSomeAccess(organizationId);
934
+ }
935
+ return this.hasSomePlatformAccess();
933
936
  }
934
937
 
935
938
  async canSendEmails(organizationId: Organization | string | null) {
@@ -1105,14 +1108,14 @@ export class AdminPermissionChecker {
1105
1108
  return !!organizationPermissions && organizationPermissions.hasAccess(level);
1106
1109
  }
1107
1110
 
1108
- isUserManager(member: MemberWithRegistrations) {
1111
+ isUserManager(member: MemberWithUsers) {
1109
1112
  return !!member.users.find(u => u.id === this.user.id);
1110
1113
  }
1111
1114
 
1112
1115
  /**
1113
1116
  * Return a list of RecordSettings the current user can view or edit
1114
1117
  */
1115
- async hasFinancialMemberAccess(member: MemberWithRegistrations, level: PermissionLevel = PermissionLevel.Read, organizationId?: string): Promise<boolean> {
1118
+ async hasFinancialMemberAccess(member: MemberWithUsersAndRegistrations, level: PermissionLevel = PermissionLevel.Read, organizationId?: string): Promise<boolean> {
1116
1119
  const isUserManager = this.isUserManager(member);
1117
1120
 
1118
1121
  if (isUserManager && level === PermissionLevel.Read) {
@@ -1197,7 +1200,7 @@ export class AdminPermissionChecker {
1197
1200
  /**
1198
1201
  * Return a list of RecordSettings the current user can view or edit
1199
1202
  */
1200
- async hasNRNAccess(member: MemberWithRegistrations, level: PermissionLevel = PermissionLevel.Read): Promise<boolean> {
1203
+ async hasNRNAccess(member: MemberWithUsersAndRegistrations, level: PermissionLevel = PermissionLevel.Read): Promise<boolean> {
1201
1204
  const isUserManager = this.isUserManager(member);
1202
1205
 
1203
1206
  if (isUserManager) {
@@ -1319,7 +1322,7 @@ export class AdminPermissionChecker {
1319
1322
  };
1320
1323
  }
1321
1324
 
1322
- async checkRecordAccess(member: MemberWithRegistrations, recordId: string, level: PermissionLevel = PermissionLevel.Read): Promise<{ canAccess: false; record: RecordSettings | null } | { canAccess: true; record: RecordSettings }> {
1325
+ async checkRecordAccess(member: MemberWithUsersRegistrationsAndGroups, recordId: string, level: PermissionLevel = PermissionLevel.Read): Promise<{ canAccess: false; record: RecordSettings | null } | { canAccess: true; record: RecordSettings }> {
1323
1326
  const record = await MemberRecordStore.getRecord(recordId);
1324
1327
  if (!record) {
1325
1328
  return {
@@ -1452,7 +1455,7 @@ export class AdminPermissionChecker {
1452
1455
  /**
1453
1456
  * Changes data inline
1454
1457
  */
1455
- async filterMemberData(member: MemberWithRegistrations, data: MemberWithRegistrationsBlob, options?: { forAdminCartCalculation?: boolean }): Promise<MemberWithRegistrationsBlob> {
1458
+ async filterMemberData(member: MemberWithUsersRegistrationsAndGroups, data: MemberWithRegistrationsBlob, options?: { forAdminCartCalculation?: boolean }): Promise<MemberWithRegistrationsBlob> {
1456
1459
  const cloned = data.clone();
1457
1460
 
1458
1461
  for (const [key, value] of cloned.details.recordAnswers.entries()) {
@@ -1511,7 +1514,7 @@ export class AdminPermissionChecker {
1511
1514
  /**
1512
1515
  * Only for creating new members
1513
1516
  */
1514
- filterMemberPut(member: MemberWithRegistrations, data: MemberWithRegistrationsBlob, options: { asUserManager: boolean }) {
1517
+ filterMemberPut(member: MemberWithUsersRegistrationsAndGroups, data: MemberWithRegistrationsBlob, options: { asUserManager: boolean }) {
1515
1518
  if (options.asUserManager || STAMHOOFD.userMode === 'platform') {
1516
1519
  // A user manager cannot choose the member number + in platform mode, nobody can choose the member number
1517
1520
  data.details.memberNumber = null;
@@ -1526,7 +1529,7 @@ export class AdminPermissionChecker {
1526
1529
  }
1527
1530
  }
1528
1531
 
1529
- async filterMemberPatch(member: MemberWithRegistrations, data: AutoEncoderPatchType<MemberWithRegistrationsBlob>): Promise<AutoEncoderPatchType<MemberWithRegistrationsBlob>> {
1532
+ async filterMemberPatch(member: MemberWithUsersRegistrationsAndGroups, data: AutoEncoderPatchType<MemberWithRegistrationsBlob>): Promise<AutoEncoderPatchType<MemberWithRegistrationsBlob>> {
1530
1533
  if (!data.details) {
1531
1534
  return data;
1532
1535
  }
@@ -1,11 +1,10 @@
1
1
  import { SimpleError } from '@simonbackx/simple-errors';
2
- import { AuditLog, BalanceItem, CachedBalance, Document, Event, EventNotification, Group, Invoice, Member, MemberPlatformMembership, MemberResponsibilityRecord, MemberWithRegistrations, Order, Organization, OrganizationRegistrationPeriod, Payment, Registration, RegistrationPeriod, Ticket, User, Webshop } from '@stamhoofd/models';
3
- import { AuditLogReplacement, AuditLogReplacementType, AuditLog as AuditLogStruct, Company, DetailedReceivableBalance, Document as DocumentStruct, EventNotification as EventNotificationStruct, Event as EventStruct, GenericBalance, Group as GroupStruct, GroupType, InvoicedBalanceItem, InvoiceStruct, MemberPlatformMembership as MemberPlatformMembershipStruct, MembersBlob, MemberWithRegistrationsBlob, NamedObject, OrganizationRegistrationPeriod as OrganizationRegistrationPeriodStruct, Organization as OrganizationStruct, PaymentCustomer, PaymentGeneral, PermissionLevel, Platform, PrivateOrder, PrivateWebshop, ReceivableBalanceObject, ReceivableBalanceObjectContact, ReceivableBalance as ReceivableBalanceStruct, ReceivableBalanceType, RegistrationsBlob, RegistrationWithMemberBlob, TicketPrivate, UserWithMembers, WebshopPreview, Webshop as WebshopStruct } from '@stamhoofd/structures';
2
+ import { AuditLog, BalanceItem, CachedBalance, Document, Event, EventNotification, Group, Invoice, Member, MemberPlatformMembership, MemberResponsibilityRecord, MemberWithUsersRegistrationsAndGroups, Order, Organization, OrganizationRegistrationPeriod, Payment, Registration, RegistrationPeriod, Ticket, User, Webshop } from '@stamhoofd/models';
3
+ import { AuditLogReplacement, AuditLogReplacementType, AuditLog as AuditLogStruct, DetailedReceivableBalance, Document as DocumentStruct, EventNotification as EventNotificationStruct, Event as EventStruct, GenericBalance, Group as GroupStruct, GroupType, InvoicedBalanceItem, InvoiceStruct, MemberPlatformMembership as MemberPlatformMembershipStruct, MembersBlob, MemberWithRegistrationsBlob, NamedObject, OrganizationRegistrationPeriod as OrganizationRegistrationPeriodStruct, Organization as OrganizationStruct, PaymentCustomer, PaymentGeneral, PermissionLevel, Platform, PrivateOrder, PrivateWebshop, ReceivableBalanceObject, ReceivableBalanceObjectContact, ReceivableBalance as ReceivableBalanceStruct, ReceivableBalanceType, RegistrationsBlob, RegistrationWithMemberBlob, TicketPrivate, UserWithMembers, WebshopPreview, Webshop as WebshopStruct, BalanceItem as BalanceItemStruct } from '@stamhoofd/structures';
4
4
  import { Sorter } from '@stamhoofd/utility';
5
5
 
6
6
  import { SQL } from '@stamhoofd/sql';
7
7
  import { Formatter } from '@stamhoofd/utility';
8
- import { BalanceItemService } from '../services/BalanceItemService.js';
9
8
  import { Context } from './Context.js';
10
9
 
11
10
  /**
@@ -61,6 +60,9 @@ export class AuthenticatedStructures {
61
60
 
62
61
  const { invoicedBalanceItems } = await Invoice.loadBalanceItems(invoices);
63
62
 
63
+ const paymentModels = await Payment.select().where('invoiceId', invoices.map(i => i.id)).fetch();
64
+ const payments = await this.paymentsGeneral(paymentModels, false);
65
+
64
66
  return invoices.map((invoice) => {
65
67
  const items = invoicedBalanceItems.filter(i => i.invoiceId === invoice.id);
66
68
  return InvoiceStruct.create({
@@ -68,6 +70,7 @@ export class AuthenticatedStructures {
68
70
  items: items.map((item) => {
69
71
  return InvoicedBalanceItem.create(item);
70
72
  }),
73
+ payments: payments.filter(p => p.invoiceId === invoice.id),
71
74
  });
72
75
  });
73
76
  }
@@ -222,8 +225,8 @@ export class AuthenticatedStructures {
222
225
  return [];
223
226
  }
224
227
 
225
- // #region get period ids / organizations map
226
228
  const periodIdOrganizationsMap = new Map<string, Organization[]>();
229
+ const organizationIdsToGetWebshopsFor: string[] = [];
227
230
 
228
231
  for (const organization of organizations) {
229
232
  const periodId = organization.periodId;
@@ -235,47 +238,62 @@ export class AuthenticatedStructures {
235
238
  periodIdOrganizationsMap.set(periodId, [organization]);
236
239
  }
237
240
  }
238
- // #endregion
239
241
 
240
- // #region get registration period and whether private data can be accessed for each organization
241
- const organizationData: Map<string, { organizationRegistrationPeriod: OrganizationRegistrationPeriod; canAccessPrivateData: boolean }> = new Map();
242
- const organizationIdsToGetWebshopsFor: string[] = [];
242
+ // Get registration period and whether private data can be accessed for each organization
243
+ const organizationData: Map<string, { organizationRegistrationPeriod: OrganizationRegistrationPeriodStruct; canAccessPrivateData: boolean }> = new Map();
244
+ const allPeriodIds = periodIdOrganizationsMap.keys();
245
+ const allPeriods = await RegistrationPeriod.getByIDs(...allPeriodIds);
246
+ const periodMap = new Map<string, RegistrationPeriod>(allPeriods.map(p => [p.id, p]));
243
247
 
244
248
  for (const [periodId, organizations] of periodIdOrganizationsMap.entries()) {
245
- const organizationMap = new Map(organizations.map(o => [o.id, o]));
246
-
247
249
  const result = await OrganizationRegistrationPeriod.where({
248
250
  periodId,
249
251
  organizationId: {
250
252
  sign: 'IN',
251
- value: Array.from(organizationMap.keys()),
253
+ value: Formatter.uniqueArray(organizations.map(o => o.id)),
252
254
  },
253
255
  });
256
+ const period = periodMap.get(periodId);
257
+ if (!period) {
258
+ console.error('Period not found for id', periodId);
259
+ throw new Error('Period not found for id ' + periodId);
260
+ }
254
261
 
255
- const organizationRegistrationPeriods = new Map(result.map(r => [r.organizationId, r]));
262
+ // Automatically create missing organization periods for now
263
+ for (const organization of organizations) {
264
+ const organizationPeriodModel = result.find(r => r.organizationId === organization.id);
265
+ if (!organizationPeriodModel) {
266
+ result.push(await organization.getPeriod());
267
+ }
268
+ }
269
+
270
+ // Do this onece per period, because otherwise we can't optimize fetching the groups for each period
271
+ const organizationRegistrationPeriods = await this.organizationRegistrationPeriods(result, [period]);
256
272
 
257
273
  for (const organization of organizations) {
258
274
  const organizationId = organization.id;
259
- const organizationRegistrationPeriod = organizationRegistrationPeriods.get(organizationId) ?? await organization.getPeriod();
275
+ const organizationPeriodModel = result.find(r => r.organizationId === organizationId);
276
+ if (organizationPeriodModel) {
277
+ const organizationRegistrationPeriod = organizationRegistrationPeriods.find(o => o.id === organizationPeriodModel.id);
278
+ if (!organizationRegistrationPeriod) {
279
+ throw new Error('Organization registration period not found for id ' + organizationPeriodModel.id);
280
+ }
260
281
 
261
- // check if private data can be accessed
262
- const canAccessPrivateData = await Context.optionalAuth?.canAccessPrivateOrganizationData(organization) ?? false;
263
- if (canAccessPrivateData) {
264
- organizationIdsToGetWebshopsFor.push(organizationId);
282
+ // check if private data can be accessed
283
+ const canAccessPrivateData = await Context.optionalAuth?.canAccessPrivateOrganizationData(organization) ?? false;
284
+ if (canAccessPrivateData) {
285
+ organizationIdsToGetWebshopsFor.push(organization.id);
286
+ }
287
+ organizationData.set(organizationId, { organizationRegistrationPeriod, canAccessPrivateData });
288
+ }
289
+ else {
290
+ // Should never happen
291
+ throw new Error('Organization registration period model not found for organization id ' + organizationId + ' and period id ' + periodId);
265
292
  }
266
-
267
- organizationData.set(organizationId, { organizationRegistrationPeriod, canAccessPrivateData });
268
293
  }
269
294
  }
270
- // #endregion
271
-
272
- // #region get periods
273
- const allPeriodIds = periodIdOrganizationsMap.keys();
274
- const allPeriods = await RegistrationPeriod.getByIDs(...allPeriodIds);
275
- const periodMap = new Map<string, RegistrationPeriod>(allPeriods.map(p => [p.id, p]));
276
- // #endregion
277
295
 
278
- // #region get webshop previews
296
+ // Get webshop previews
279
297
  const webshops = organizationIdsToGetWebshopsFor.length > 0
280
298
  ? await Webshop.where(
281
299
  {
@@ -306,9 +324,8 @@ export class AuthenticatedStructures {
306
324
  webshopPreviews.set(organizationId, [preview]);
307
325
  }
308
326
  }
309
- // #endregion
310
327
 
311
- // #region create organization structs
328
+ // Create organization structs
312
329
  const results: OrganizationStruct[] = [];
313
330
 
314
331
  for (const organization of organizations) {
@@ -327,7 +344,7 @@ export class AuthenticatedStructures {
327
344
 
328
345
  let result: OrganizationStruct;
329
346
 
330
- const period = await this.organizationRegistrationPeriod(data.organizationRegistrationPeriod, [registrationPeriod]);
347
+ const period = data.organizationRegistrationPeriod;
331
348
  const baseStruct = organization.getBaseStructure();
332
349
 
333
350
  if (data.canAccessPrivateData) {
@@ -347,7 +364,6 @@ export class AuthenticatedStructures {
347
364
 
348
365
  results.push(result);
349
366
  }
350
- // #endregion
351
367
 
352
368
  return results;
353
369
  }
@@ -365,7 +381,7 @@ export class AuthenticatedStructures {
365
381
 
366
382
  static async userWithMembers(user: User): Promise<UserWithMembers> {
367
383
  const members = await Member.getMembersWithRegistrationForUser(user);
368
- const filtered: MemberWithRegistrations[] = [];
384
+ const filtered: MemberWithUsersRegistrationsAndGroups[] = [];
369
385
  for (const member of members) {
370
386
  if (await Context.auth.canAccessMember(member, PermissionLevel.Read)) {
371
387
  filtered.push(member);
@@ -403,28 +419,142 @@ export class AuthenticatedStructures {
403
419
  return structs;
404
420
  }
405
421
 
406
- static async members(members: MemberWithRegistrations[]): Promise<MemberWithRegistrationsBlob[]> {
422
+ static async members(members: MemberWithUsersRegistrationsAndGroups[]): Promise<MemberWithRegistrationsBlob[]> {
407
423
  return (await this.membersBlob(members, false)).members;
408
424
  }
409
425
 
410
- static async membersBlob(members: MemberWithRegistrations[], includeContextOrganization = false, includeUser?: User, options?: { forAdminCartCalculation?: boolean }): Promise<MembersBlob> {
426
+ static async membersBlob(members: MemberWithUsersRegistrationsAndGroups[], includeContextOrganization = false, includeUser?: User, options?: { forAdminCartCalculation?: boolean }): Promise<MembersBlob> {
411
427
  if (members.length === 0 && !includeUser) {
412
428
  return MembersBlob.create({ members: [], organizations: [] });
413
429
  }
430
+
414
431
  const organizations = new Map<string, Organization>();
432
+ const relevantPeriodIds = Formatter.uniqueArray([
433
+ Platform.shared.period.previousPeriodId,
434
+ Platform.shared.period.id,
435
+ Platform.shared.period.nextPeriodId,
436
+ Context.organization?.periodId ?? null,
437
+ ].filter(id => id !== null)) as string[];
415
438
 
416
439
  const registrationIds = Formatter.uniqueArray(members.flatMap(m => m.registrations.map(r => r.id)));
417
440
 
418
- if (Context.organization) {
419
- await BalanceItemService.flushCaches(Context.organization.id);
441
+ // This code is a bit messy, we need to optimize it a bit. But it is this complex because it tries
442
+ // to load as many relations as possible in advance in one go, to avoid doing queries inside the loops.
443
+ // this is very important as this method is called very often.
444
+
445
+ // Load responsibilities
446
+ const responsibilities = members.length > 0
447
+ ? await MemberResponsibilityRecord.select()
448
+ .where('memberId', members.map(m => m.id))
449
+ .where(SQL.where('endDate', null).or('endDate', '>', new Date()))
450
+ .fetch()
451
+ : [];
452
+ const platformMemberships = members.length > 0
453
+ ? await MemberPlatformMembership.select()
454
+ .where('memberId', members.map(m => m.id))
455
+ .where('deletedAt', null)
456
+ .where('periodId', relevantPeriodIds)
457
+ .fetch()
458
+ : [];
459
+
460
+ // Load organizations
461
+ const organizationIds = responsibilities.map(r => r.organizationId)
462
+ .concat(
463
+ platformMemberships.map(r => r.organizationId),
464
+ )
465
+ .filter(id => id !== null);
466
+
467
+ if (includeUser) {
468
+ for (const organizationId of includeUser.permissions?.organizationPermissions.keys() ?? []) {
469
+ if (includeContextOrganization || organizationId !== Context.auth.organization?.id) {
470
+ organizationIds.push(organizationId);
471
+ }
472
+ }
473
+ }
474
+
475
+ if (STAMHOOFD.singleOrganization && !Context.auth.organization) {
476
+ organizationIds.push(STAMHOOFD.singleOrganization);
477
+ }
478
+
479
+ const membershipOrganizationId = Platform.shared.membershipOrganizationId;
480
+ if (membershipOrganizationId && Context.auth.hasSomePlatformAccess()) {
481
+ if (await Context.auth.hasSomeAccess(membershipOrganizationId)) {
482
+ organizationIds.push(membershipOrganizationId);
483
+ }
484
+ }
485
+
486
+ for (const member of members) {
487
+ for (const registration of member.registrations) {
488
+ organizationIds.push(registration.organizationId);
489
+ }
490
+ }
491
+
492
+ // Now fetch organizations
493
+ // todo: optimize this
494
+ for (const id of organizationIds) {
495
+ if (includeContextOrganization || id !== Context.auth.organization?.id) {
496
+ const found = organizations.get(id);
497
+ if (!found) {
498
+ const organization = await Context.auth.getOrganization(id);
499
+ organizations.set(organization.id, organization);
500
+ }
501
+ }
502
+ }
503
+
504
+ // Already fetch these organizations fully, because then we can reuse the groups in here
505
+ const activeOrganizations: Organization[] = [];
506
+
507
+ for (const organization of organizations.values()) {
508
+ if (organization.active || await Context.auth.hasFullAccess(organization.id)) {
509
+ activeOrganizations.push(organization);
510
+ }
511
+ }
512
+ const organizationStructs = await this.organizations(activeOrganizations);
513
+
514
+ let groupIds = Formatter.uniqueArray(
515
+ [
516
+ ...members.flatMap(m => m.registrations.map(r => r.groupId)).filter(id => id !== null),
517
+ ...responsibilities.map(r => r.groupId).filter(id => id !== null),
518
+ ],
519
+ );
520
+
521
+ // Set gropus that we already loaded
522
+ const allGroups = new Map<string, GroupStruct>();
523
+ for (const organization of organizationStructs) {
524
+ for (const group of organization.period.groups) {
525
+ allGroups.set(group.id, group);
526
+ }
527
+ }
528
+
529
+ const preloadedGroupModels: Map<string, Group> = new Map();
530
+
531
+ for (const member of members) {
532
+ for (const registration of member.registrations) {
533
+ if (!allGroups.has(registration.groupId) && !preloadedGroupModels.has(registration.groupId)) {
534
+ preloadedGroupModels.set(registration.group.id, registration.group);
535
+ }
536
+ }
420
537
  }
538
+
539
+ // Remove duplicates and already loaded ones
540
+ groupIds = groupIds.filter(id => !allGroups.has(id) && !preloadedGroupModels.has(id));
541
+ const groups = groupIds.length > 0 ? [...preloadedGroupModels.values(), ...await Group.getByIDs(...groupIds)] : [...preloadedGroupModels.values()];
542
+ if (groupIds.length > 0 && STAMHOOFD.environment === 'development') {
543
+ console.log('Doing extra group query for groups', groupIds);
544
+ }
545
+ const groupStructs = await this.groups(groups);
546
+ for (const group of groupStructs) {
547
+ allGroups.set(group.id, group);
548
+ }
549
+
421
550
  const balances = await CachedBalance.getForObjects(registrationIds, null, ReceivableBalanceType.registration);
422
551
  const memberIds = members.map(m => m.id);
423
552
  const allMemberBalances = Context.organization
424
553
  ? (await CachedBalance.getForObjects(memberIds, Context.organization.id, ReceivableBalanceType.member))
425
554
  : [];
426
555
 
427
- if (includeUser) {
556
+ // Delete organizations that no longer exist in the permissions of the user
557
+ /* if (includeUser) {
428
558
  for (const organizationId of includeUser.permissions?.organizationPermissions.keys() ?? []) {
429
559
  if (includeContextOrganization || organizationId !== Context.auth.organization?.id) {
430
560
  const found = organizations.get(organizationId);
@@ -452,26 +582,7 @@ export class AuthenticatedStructures {
452
582
  }
453
583
  }
454
584
  }
455
- }
456
-
457
- if (includeContextOrganization && STAMHOOFD.singleOrganization && !Context.auth.organization) {
458
- const found = organizations.get(STAMHOOFD.singleOrganization);
459
- if (!found) {
460
- const organization = await Context.auth.getOrganization(STAMHOOFD.singleOrganization);
461
- organizations.set(organization.id, organization);
462
- }
463
- }
464
-
465
- const membershipOrganizationId = Platform.shared.membershipOrganizationId;
466
- if (Context.auth.hasSomePlatformAccess() && membershipOrganizationId) {
467
- if (await Context.auth.hasSomeAccess(membershipOrganizationId)) {
468
- const found = organizations.get(membershipOrganizationId);
469
- if (!found) {
470
- const organization = await Context.auth.getOrganization(membershipOrganizationId);
471
- organizations.set(organization.id, organization);
472
- }
473
- }
474
- }
585
+ } */
475
586
 
476
587
  const memberBlobs: MemberWithRegistrationsBlob[] = [];
477
588
  for (const member of members) {
@@ -524,7 +635,13 @@ export class AuthenticatedStructures {
524
635
  return GenericBalance.create(b);
525
636
  }))
526
637
  : [];
527
- base.group = await this.group(r.group);
638
+ const g = allGroups.get(r.groupId);
639
+ if (g) {
640
+ base.group = g;
641
+ }
642
+ else {
643
+ console.error('Group not preloaded for registration', r.id, r.groupId);
644
+ }
528
645
 
529
646
  return base;
530
647
  }),
@@ -538,55 +655,6 @@ export class AuthenticatedStructures {
538
655
  );
539
656
  }
540
657
 
541
- // Load responsibilities
542
- const responsibilities = members.length > 0 ? await MemberResponsibilityRecord.where({ memberId: { sign: 'IN', value: members.map(m => m.id) } }) : [];
543
- const platformMemberships = members.length > 0 ? await MemberPlatformMembership.where({ deletedAt: null, memberId: { sign: 'IN', value: members.map(m => m.id) } }) : [];
544
-
545
- // Load missing organizations
546
- const organizationIds = Formatter.uniqueArray(responsibilities.map(r => r.organizationId).concat(platformMemberships.map(r => r.organizationId)).filter(id => id !== null));
547
- for (const id of organizationIds) {
548
- if (includeContextOrganization || id !== Context.auth.organization?.id) {
549
- const found = organizations.get(id);
550
- if (!found) {
551
- const organization = await Context.auth.getOrganization(id);
552
- organizations.set(organization.id, organization);
553
- }
554
- }
555
- }
556
-
557
- const activeOrganizations: Organization[] = [];
558
-
559
- for (const organization of organizations.values()) {
560
- if (organization.active || await Context.auth.hasFullAccess(organization.id)) {
561
- activeOrganizations.push(organization);
562
- }
563
- }
564
- const organizationStructs = await this.organizations(activeOrganizations);
565
-
566
- // Load missing groups
567
- const allGroups = new Map<string, GroupStruct>();
568
- for (const organization of organizationStructs) {
569
- for (const group of organization.period.groups) {
570
- allGroups.set(group.id, group);
571
- }
572
- }
573
-
574
- for (const blob of memberBlobs) {
575
- for (const registration of blob.registrations) {
576
- if (registration.group) {
577
- allGroups.set(registration.group.id, registration.group);
578
- }
579
- }
580
- }
581
-
582
- const groupIds = Formatter.uniqueArray(responsibilities.map(r => r.groupId).filter(id => id !== null)).filter(id => !allGroups.has(id));
583
- const groups = groupIds.length > 0 ? await Group.getByIDs(...groupIds) : [];
584
- const groupStructs = await this.groups(groups);
585
-
586
- for (const group of groupStructs) {
587
- allGroups.set(group.id, group);
588
- }
589
-
590
658
  for (const blob of memberBlobs) {
591
659
  blob.responsibilities = responsibilities.filter(r => r.memberId == blob.id).map((r) => {
592
660
  const group = allGroups.get(r.groupId ?? '') ?? null;
@@ -627,7 +695,7 @@ export class AuthenticatedStructures {
627
695
  return (await this.eventNotifications([eventNotification]))[0];
628
696
  }
629
697
 
630
- static async registrationsBlob(registrations: (Registration & { group: Group })[], members: MemberWithRegistrations[], includeContextOrganization = false, includeUser?: User): Promise<RegistrationsBlob> {
698
+ static async registrationsBlob(registrations: (Registration & { group: Group })[], members: MemberWithUsersRegistrationsAndGroups[], includeContextOrganization = false, includeUser?: User): Promise<RegistrationsBlob> {
631
699
  const membersBlob = await this.membersBlob(members, includeContextOrganization, includeUser);
632
700
 
633
701
  const memberBlobs = membersBlob.members;
@@ -756,6 +824,10 @@ export class AuthenticatedStructures {
756
824
  }
757
825
 
758
826
  static async orders(orders: Order[]): Promise<PrivateOrder[]> {
827
+ if (orders.length === 0) {
828
+ return [];
829
+ }
830
+
759
831
  const result: PrivateOrder[] = [];
760
832
  const webshopIds = new Set(orders.map(o => o.webshopId));
761
833
 
@@ -775,9 +847,10 @@ export class AuthenticatedStructures {
775
847
  });
776
848
  }
777
849
  }
850
+ const allBalanceItems = await BalanceItem.select().where('orderId', orders.map(o => o.id)).fetch();
778
851
 
779
852
  for (const order of orders) {
780
- const balanceItems = await BalanceItem.where({ orderId: order.id });
853
+ const balanceItems = allBalanceItems.filter(b => b.orderId === order.id);
781
854
 
782
855
  const struct = PrivateOrder.create({
783
856
  ...order,
@@ -1133,4 +1206,16 @@ export class AuthenticatedStructures {
1133
1206
 
1134
1207
  return structs;
1135
1208
  }
1209
+
1210
+ static async balanceItems(balanceItems: BalanceItem[]) {
1211
+ return balanceItems.map((balanceItem) => {
1212
+ return BalanceItemStruct.create({
1213
+ ...balanceItem,
1214
+ });
1215
+ });
1216
+ }
1217
+
1218
+ static async balanceItemsWithPayments(balanceItems: BalanceItem[]) {
1219
+ return await BalanceItem.getStructureWithPayments(balanceItems);
1220
+ }
1136
1221
  }
@@ -52,6 +52,8 @@ export class AuthorizationPostBody extends AutoEncoder {
52
52
 
53
53
  export class ContextInstance {
54
54
  request: Request;
55
+ queries: { query: string; time?: number }[] = [];
56
+ timers: Map<string, number> = new Map();
55
57
 
56
58
  user?: User;
57
59
  organization?: Organization;
@@ -1,7 +1,7 @@
1
1
  import { Email, Organization, User } from '@stamhoofd/models';
2
2
  import { SQL } from '@stamhoofd/sql';
3
3
  import { EmailStatus } from '@stamhoofd/structures';
4
- import { ContextInstance } from './Context';
4
+ import { ContextInstance } from './Context.js';
5
5
 
6
6
  export async function resumeEmails() {
7
7
  const query = SQL.select()
@@ -1,7 +1,7 @@
1
1
  import { Group, Member, MemberResponsibilityRecord, Organization, Platform, Registration } from '@stamhoofd/models';
2
2
  import { SQL, SQLWhereExists } from '@stamhoofd/sql';
3
3
  import { GroupType } from '@stamhoofd/structures';
4
- import { MemberUserSyncer } from './MemberUserSyncer';
4
+ import { MemberUserSyncer } from './MemberUserSyncer.js';
5
5
 
6
6
  export class FlagMomentCleanup {
7
7
  /**
@@ -2,7 +2,7 @@ import { EmailAddress } from '@stamhoofd/email';
2
2
  import { OrganizationFactory, UserFactory } from '@stamhoofd/models';
3
3
  import { OrganizationEmail, PermissionLevel, Permissions } from '@stamhoofd/structures';
4
4
 
5
- import { ForwardHandler } from './ForwardHandler';
5
+ import { ForwardHandler } from './ForwardHandler.js';
6
6
 
7
7
  describe('ForwardHandler', () => {
8
8
  it('should send to default e-mail', async () => {
@@ -111,7 +111,7 @@ export class ForwardHandler {
111
111
  return doBounce();
112
112
  }
113
113
 
114
- console.log('Forward to ' + defaultEmail);
114
+ console.log('Forward to', defaultEmail);
115
115
 
116
116
  let html: string | undefined = undefined;
117
117
 
@@ -1,8 +1,8 @@
1
1
  import { I18n } from '@stamhoofd/backend-i18n';
2
- import { BalanceItemService } from '../services/BalanceItemService';
3
- import { FileSignService } from '../services/FileSignService';
4
- import { MemberRecordStore } from '../services/MemberRecordStore';
5
- import { ContextInstance } from './Context';
2
+ import { BalanceItemService } from '../services/BalanceItemService.js';
3
+ import { FileSignService } from '../services/FileSignService.js';
4
+ import { MemberRecordStore } from '../services/MemberRecordStore.js';
5
+ import { ContextInstance } from './Context.js';
6
6
 
7
7
  export class GlobalHelper {
8
8
  static async load() {