@stamhoofd/backend 2.39.1 → 2.40.1

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 (198) hide show
  1. package/eslint.config.mjs +5 -0
  2. package/index.ts +81 -74
  3. package/jest.config.cjs +10 -0
  4. package/migrations.ts +16 -14
  5. package/package.json +11 -11
  6. package/src/crons/clear-excel-cache.test.ts +48 -50
  7. package/src/crons/clear-excel-cache.ts +18 -18
  8. package/src/crons/setup-steps.ts +2 -2
  9. package/src/crons.ts +325 -306
  10. package/src/decoders/StringArrayDecoder.ts +7 -7
  11. package/src/decoders/StringNullableDecoder.ts +1 -2
  12. package/src/email-recipient-loaders/members.ts +22 -22
  13. package/src/endpoints/admin/memberships/ChargeMembershipsEndpoint.ts +8 -9
  14. package/src/endpoints/admin/memberships/GetChargeMembershipsSummaryEndpoint.ts +39 -40
  15. package/src/endpoints/admin/organizations/GetOrganizationsCountEndpoint.ts +8 -8
  16. package/src/endpoints/admin/organizations/GetOrganizationsEndpoint.ts +44 -45
  17. package/src/endpoints/admin/organizations/PatchOrganizationsEndpoint.ts +58 -57
  18. package/src/endpoints/auth/CreateAdminEndpoint.ts +48 -45
  19. package/src/endpoints/auth/CreateTokenEndpoint.test.ts +31 -31
  20. package/src/endpoints/auth/CreateTokenEndpoint.ts +146 -147
  21. package/src/endpoints/auth/DeleteTokenEndpoint.ts +7 -7
  22. package/src/endpoints/auth/DeleteUserEndpoint.ts +15 -15
  23. package/src/endpoints/auth/ForgotPasswordEndpoint.ts +17 -18
  24. package/src/endpoints/auth/GetOtherUserEndpoint.ts +9 -10
  25. package/src/endpoints/auth/GetUserEndpoint.test.ts +32 -35
  26. package/src/endpoints/auth/GetUserEndpoint.ts +5 -6
  27. package/src/endpoints/auth/PatchApiUserEndpoint.ts +35 -33
  28. package/src/endpoints/auth/PatchUserEndpoint.ts +55 -52
  29. package/src/endpoints/auth/PollEmailVerificationEndpoint.ts +9 -9
  30. package/src/endpoints/auth/RetryEmailVerificationEndpoint.ts +8 -8
  31. package/src/endpoints/auth/SignupEndpoint.ts +37 -36
  32. package/src/endpoints/auth/VerifyEmailEndpoint.ts +29 -28
  33. package/src/endpoints/global/addresses/SearchRegionsEndpoint.ts +33 -33
  34. package/src/endpoints/global/addresses/ValidateAddressEndpoint.ts +7 -7
  35. package/src/endpoints/global/caddy/CheckDomainCertEndpoint.ts +37 -37
  36. package/src/endpoints/global/email/CreateEmailEndpoint.ts +30 -30
  37. package/src/endpoints/global/email/GetEmailAddressEndpoint.ts +13 -13
  38. package/src/endpoints/global/email/GetEmailEndpoint.ts +13 -13
  39. package/src/endpoints/global/email/ManageEmailAddressEndpoint.ts +16 -16
  40. package/src/endpoints/global/email/PatchEmailEndpoint.ts +25 -25
  41. package/src/endpoints/global/events/GetEventsEndpoint.ts +43 -44
  42. package/src/endpoints/global/events/PatchEventsEndpoint.ts +127 -172
  43. package/src/endpoints/global/files/ExportToExcelEndpoint.ts +49 -50
  44. package/src/endpoints/global/files/GetFileCache.ts +13 -13
  45. package/src/endpoints/global/files/UploadFile.ts +51 -54
  46. package/src/endpoints/global/files/UploadImage.ts +53 -53
  47. package/src/endpoints/global/groups/GetGroupsEndpoint.ts +25 -25
  48. package/src/endpoints/global/members/GetMemberFamilyEndpoint.ts +24 -23
  49. package/src/endpoints/global/members/GetMembersCountEndpoint.ts +8 -8
  50. package/src/endpoints/global/members/GetMembersEndpoint.ts +105 -102
  51. package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.ts +240 -239
  52. package/src/endpoints/global/organizations/CheckRegisterCodeEndpoint.ts +12 -14
  53. package/src/endpoints/global/organizations/CreateOrganizationEndpoint.test.ts +32 -33
  54. package/src/endpoints/global/organizations/CreateOrganizationEndpoint.ts +48 -57
  55. package/src/endpoints/global/organizations/GetOrganizationFromDomainEndpoint.test.ts +21 -22
  56. package/src/endpoints/global/organizations/GetOrganizationFromDomainEndpoint.ts +28 -28
  57. package/src/endpoints/global/organizations/GetOrganizationFromUriEndpoint.ts +18 -18
  58. package/src/endpoints/global/organizations/SearchOrganizationEndpoint.test.ts +20 -20
  59. package/src/endpoints/global/organizations/SearchOrganizationEndpoint.ts +17 -17
  60. package/src/endpoints/global/payments/StripeWebhookEndpoint.ts +81 -75
  61. package/src/endpoints/global/platform/GetPlatformAdminsEndpoint.ts +14 -14
  62. package/src/endpoints/global/platform/GetPlatformEnpoint.ts +11 -11
  63. package/src/endpoints/global/platform/PatchPlatformEnpoint.ts +71 -68
  64. package/src/endpoints/global/registration/GetPaymentRegistrations.ts +27 -27
  65. package/src/endpoints/global/registration/GetUserBillingStatusEndpoint.ts +30 -30
  66. package/src/endpoints/global/registration/GetUserDetailedBillingStatusEndpoint.ts +34 -34
  67. package/src/endpoints/global/registration/GetUserDocumentsEndpoint.ts +26 -26
  68. package/src/endpoints/global/registration/GetUserMembersEndpoint.ts +12 -12
  69. package/src/endpoints/global/registration/PatchUserMembersEndpoint.ts +90 -90
  70. package/src/endpoints/global/registration/RegisterMembersEndpoint.test.ts +118 -121
  71. package/src/endpoints/global/registration/RegisterMembersEndpoint.ts +362 -350
  72. package/src/endpoints/global/registration-periods/GetRegistrationPeriodsEndpoint.ts +8 -9
  73. package/src/endpoints/global/registration-periods/PatchRegistrationPeriodsEndpoint.ts +21 -21
  74. package/src/endpoints/global/webshops/GetWebshopFromDomainEndpoint.ts +65 -65
  75. package/src/endpoints/organization/dashboard/billing/GetOrganizationBillingStatusEndpoint.ts +9 -9
  76. package/src/endpoints/organization/dashboard/billing/GetOrganizationDetailedBillingStatusEndpoint.ts +14 -14
  77. package/src/endpoints/organization/dashboard/documents/GetDocumentTemplateXML.ts +17 -17
  78. package/src/endpoints/organization/dashboard/documents/GetDocumentTemplatesEndpoint.ts +21 -21
  79. package/src/endpoints/organization/dashboard/documents/GetDocumentsEndpoint.ts +15 -15
  80. package/src/endpoints/organization/dashboard/documents/PatchDocumentEndpoint.ts +52 -52
  81. package/src/endpoints/organization/dashboard/documents/PatchDocumentTemplateEndpoint.ts +37 -37
  82. package/src/endpoints/organization/dashboard/email/CheckEmailBouncesEndpoint.ts +14 -14
  83. package/src/endpoints/organization/dashboard/email/EmailEndpoint.ts +113 -112
  84. package/src/endpoints/organization/dashboard/email-templates/GetEmailTemplatesEndpoint.ts +29 -29
  85. package/src/endpoints/organization/dashboard/email-templates/PatchEmailTemplatesEndpoint.ts +48 -47
  86. package/src/endpoints/organization/dashboard/mollie/CheckMollieEndpoint.ts +22 -21
  87. package/src/endpoints/organization/dashboard/mollie/ConnectMollieEndpoint.ts +13 -14
  88. package/src/endpoints/organization/dashboard/mollie/DisconnectMollieEndpoint.ts +12 -13
  89. package/src/endpoints/organization/dashboard/mollie/GetMollieDashboardEndpoint.ts +24 -24
  90. package/src/endpoints/organization/dashboard/nolt/CreateNoltTokenEndpoint.ts +10 -12
  91. package/src/endpoints/organization/dashboard/organization/GetOrganizationArchivedGroups.ts +14 -14
  92. package/src/endpoints/organization/dashboard/organization/GetOrganizationDeletedGroups.ts +13 -13
  93. package/src/endpoints/organization/dashboard/organization/GetOrganizationSSOEndpoint.ts +12 -12
  94. package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.test.ts +120 -124
  95. package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.ts +172 -173
  96. package/src/endpoints/organization/dashboard/organization/SetOrganizationDomainEndpoint.ts +88 -89
  97. package/src/endpoints/organization/dashboard/organization/SetOrganizationSSOEndpoint.ts +12 -12
  98. package/src/endpoints/organization/dashboard/payments/GetMemberBalanceEndpoint.ts +17 -17
  99. package/src/endpoints/organization/dashboard/payments/GetPaymentsCountEndpoint.ts +8 -8
  100. package/src/endpoints/organization/dashboard/payments/GetPaymentsEndpoint.ts +66 -67
  101. package/src/endpoints/organization/dashboard/payments/PatchBalanceItemsEndpoint.ts +47 -47
  102. package/src/endpoints/organization/dashboard/payments/PatchPaymentsEndpoint.ts +93 -91
  103. package/src/endpoints/organization/dashboard/registration-periods/GetOrganizationRegistrationPeriodsEndpoint.ts +16 -17
  104. package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.ts +170 -167
  105. package/src/endpoints/organization/dashboard/registration-periods/SetupStepReviewEndpoint.ts +25 -24
  106. package/src/endpoints/organization/dashboard/stripe/ConnectStripeEndpoint.ts +22 -23
  107. package/src/endpoints/organization/dashboard/stripe/DeleteStripeAccountEndpoint.ts +22 -22
  108. package/src/endpoints/organization/dashboard/stripe/GetStripeAccountLinkEndpoint.ts +17 -18
  109. package/src/endpoints/organization/dashboard/stripe/GetStripeAccountsEndpoint.ts +8 -9
  110. package/src/endpoints/organization/dashboard/stripe/GetStripeLoginLinkEndpoint.ts +17 -18
  111. package/src/endpoints/organization/dashboard/stripe/UpdateStripeAccountEndpoint.ts +14 -15
  112. package/src/endpoints/organization/dashboard/users/CreateApiUserEndpoint.ts +19 -19
  113. package/src/endpoints/organization/dashboard/users/DeleteUserEndpoint.ts +19 -19
  114. package/src/endpoints/organization/dashboard/users/GetApiUsersEndpoint.ts +14 -14
  115. package/src/endpoints/organization/dashboard/users/GetOrganizationAdminsEndpoint.ts +12 -12
  116. package/src/endpoints/organization/dashboard/webshops/CreateWebshopEndpoint.ts +103 -100
  117. package/src/endpoints/organization/dashboard/webshops/DeleteWebshopEndpoint.ts +11 -12
  118. package/src/endpoints/organization/dashboard/webshops/GetDiscountCodesEndpoint.ts +15 -15
  119. package/src/endpoints/organization/dashboard/webshops/GetWebshopOrdersEndpoint.ts +14 -14
  120. package/src/endpoints/organization/dashboard/webshops/GetWebshopTicketsEndpoint.ts +14 -14
  121. package/src/endpoints/organization/dashboard/webshops/GetWebshopUriAvailabilityEndpoint.ts +23 -23
  122. package/src/endpoints/organization/dashboard/webshops/PatchDiscountCodesEndpoint.ts +54 -52
  123. package/src/endpoints/organization/dashboard/webshops/PatchWebshopEndpoint.ts +84 -81
  124. package/src/endpoints/organization/dashboard/webshops/PatchWebshopOrdersEndpoint.ts +120 -111
  125. package/src/endpoints/organization/dashboard/webshops/PatchWebshopTicketsEndpoint.ts +24 -24
  126. package/src/endpoints/organization/dashboard/webshops/VerifyWebshopDomainEndpoint.ts +18 -18
  127. package/src/endpoints/organization/shared/ExchangePaymentEndpoint.ts +141 -130
  128. package/src/endpoints/organization/shared/GetDocumentHtml.ts +25 -25
  129. package/src/endpoints/organization/shared/GetPaymentEndpoint.ts +18 -18
  130. package/src/endpoints/organization/shared/auth/GetOrganizationEndpoint.test.ts +36 -37
  131. package/src/endpoints/organization/shared/auth/GetOrganizationEndpoint.ts +9 -9
  132. package/src/endpoints/organization/shared/auth/OpenIDConnectCallbackEndpoint.ts +11 -11
  133. package/src/endpoints/organization/shared/auth/OpenIDConnectStartEndpoint.ts +28 -27
  134. package/src/endpoints/organization/webshops/CheckWebshopDiscountCodesEndpoint.ts +20 -20
  135. package/src/endpoints/organization/webshops/GetOrderByPaymentEndpoint.ts +22 -22
  136. package/src/endpoints/organization/webshops/GetOrderEndpoint.ts +14 -14
  137. package/src/endpoints/organization/webshops/GetTicketsEndpoint.ts +57 -56
  138. package/src/endpoints/organization/webshops/GetWebshopEndpoint.test.ts +65 -66
  139. package/src/endpoints/organization/webshops/GetWebshopEndpoint.ts +18 -17
  140. package/src/endpoints/organization/webshops/PlaceOrderEndpoint.test.ts +124 -128
  141. package/src/endpoints/organization/webshops/PlaceOrderEndpoint.ts +154 -145
  142. package/src/excel-loaders/members.ts +275 -273
  143. package/src/excel-loaders/payments.ts +155 -156
  144. package/src/helpers/AddressValidator.test.ts +32 -32
  145. package/src/helpers/AddressValidator.ts +128 -122
  146. package/src/helpers/AdminPermissionChecker.ts +339 -236
  147. package/src/helpers/AuthenticatedStructures.ts +233 -134
  148. package/src/helpers/BuckarooHelper.ts +134 -134
  149. package/src/helpers/CheckSettlements.ts +94 -88
  150. package/src/helpers/Context.ts +87 -86
  151. package/src/helpers/CookieHelper.ts +23 -22
  152. package/src/helpers/EmailResumer.ts +10 -10
  153. package/src/helpers/FileCache.ts +62 -62
  154. package/src/helpers/ForwardHandler.test.ts +122 -124
  155. package/src/helpers/ForwardHandler.ts +76 -70
  156. package/src/helpers/MemberUserSyncer.ts +101 -96
  157. package/src/helpers/MembershipCharger.ts +69 -69
  158. package/src/helpers/MembershipHelper.ts +11 -12
  159. package/src/helpers/OpenIDConnectHelper.ts +85 -82
  160. package/src/helpers/PeriodHelper.ts +65 -70
  161. package/src/helpers/StripeHelper.ts +146 -137
  162. package/src/helpers/StripePayoutChecker.ts +51 -52
  163. package/src/helpers/ViesHelper.ts +46 -44
  164. package/src/helpers/fetchToAsyncIterator.ts +14 -14
  165. package/src/helpers/xlsxAddressTransformerColumnFactory.ts +58 -60
  166. package/src/middleware/ContextMiddleware.ts +5 -5
  167. package/src/migrations/1646578856-validate-addresses.ts +6 -9
  168. package/src/seeds/0000000000-example.ts +3 -5
  169. package/src/seeds/1715028563-user-permissions.ts +16 -18
  170. package/src/seeds/1722256498-group-update-occupancy.ts +12 -12
  171. package/src/seeds/1722344162-sync-member-users.ts +14 -15
  172. package/src/seeds/1722344162-update-membership.ts +6 -6
  173. package/src/seeds/1726055544-balance-item-paid.ts +4 -4
  174. package/src/seeds/1726055545-balance-item-pending.ts +4 -4
  175. package/src/seeds/1726494419-update-cached-outstanding-balance.ts +16 -16
  176. package/src/seeds/1726494420-update-cached-outstanding-balance-from-items.ts +12 -12
  177. package/src/seeds/1726572303-schedule-stock-updates.ts +12 -12
  178. package/src/seeds/1726847064-setup-steps.ts +16 -0
  179. package/src/sql-filters/balance-item-payments.ts +7 -7
  180. package/src/sql-filters/events.ts +14 -14
  181. package/src/sql-filters/members.ts +96 -96
  182. package/src/sql-filters/organizations.ts +139 -75
  183. package/src/sql-filters/payments.ts +28 -28
  184. package/src/sql-filters/registrations.ts +14 -14
  185. package/src/sql-sorters/events.ts +25 -25
  186. package/src/sql-sorters/members.ts +26 -26
  187. package/src/sql-sorters/organizations.ts +36 -36
  188. package/src/sql-sorters/payments.ts +26 -26
  189. package/tests/e2e/stock.test.ts +616 -621
  190. package/tests/e2e/tickets.test.ts +255 -260
  191. package/tests/helpers/StripeMocker.ts +177 -179
  192. package/tests/helpers/TestServer.ts +9 -9
  193. package/tests/jest.global.setup.ts +14 -13
  194. package/tests/jest.setup.ts +33 -32
  195. package/.eslintrc.js +0 -61
  196. package/jest.config.js +0 -11
  197. package/src/helpers/SetupStepsUpdater.ts +0 -359
  198. package/src/seeds/1724076679-setup-steps.ts +0 -16
@@ -1,191 +1,291 @@
1
- import { SimpleError } from "@simonbackx/simple-errors";
2
- import { Event, Group, Member, MemberPlatformMembership, MemberResponsibilityRecord, MemberWithRegistrations, Organization, OrganizationRegistrationPeriod, Payment, RegistrationPeriod, User, Webshop } from "@stamhoofd/models";
1
+ import { SimpleError } from '@simonbackx/simple-errors';
2
+ import { Event, Group, Member, MemberPlatformMembership, MemberResponsibilityRecord, MemberWithRegistrations, Organization, OrganizationRegistrationPeriod, Payment, RegistrationPeriod, User, Webshop } from '@stamhoofd/models';
3
3
  import { Event as EventStruct, Group as GroupStruct, MemberPlatformMembership as MemberPlatformMembershipStruct, MemberWithRegistrationsBlob, MembersBlob, OrganizationRegistrationPeriod as OrganizationRegistrationPeriodStruct, Organization as OrganizationStruct, PaymentGeneral, PermissionLevel, PrivateWebshop, UserWithMembers, WebshopPreview, Webshop as WebshopStruct } from '@stamhoofd/structures';
4
4
 
5
- import { Formatter } from "@stamhoofd/utility";
6
- import { Context } from "./Context";
5
+ import { Formatter } from '@stamhoofd/utility';
6
+ import { Context } from './Context';
7
7
 
8
8
  /**
9
9
  * Builds authenticated structures for the current user
10
10
  */
11
11
  export class AuthenticatedStructures {
12
12
  static async paymentGeneral(payment: Payment, checkPermissions = true): Promise<PaymentGeneral> {
13
- return (await this.paymentsGeneral([payment], checkPermissions))[0]
13
+ return (await this.paymentsGeneral([payment], checkPermissions))[0];
14
14
  }
15
15
 
16
16
  /**
17
- *
18
- * @param payments
17
+ *
18
+ * @param payments
19
19
  * @param checkPermissions Only set to undefined when not returned in the API + not for public use
20
- * @returns
20
+ * @returns
21
21
  */
22
22
  static async paymentsGeneral(payments: Payment[], checkPermissions = true): Promise<PaymentGeneral[]> {
23
23
  if (payments.length === 0) {
24
- return []
24
+ return [];
25
25
  }
26
26
 
27
- const {balanceItemPayments, balanceItems} = await Payment.loadBalanceItems(payments)
28
- const {registrations, orders} = await Payment.loadBalanceItemRelations(balanceItems);
27
+ const { balanceItemPayments, balanceItems } = await Payment.loadBalanceItems(payments);
28
+ const { registrations, orders } = await Payment.loadBalanceItemRelations(balanceItems);
29
29
 
30
30
  if (checkPermissions) {
31
31
  // Note: permission checking is moved here for performacne to avoid loading the data multiple times
32
- if (!(await Context.auth.canAccessBalanceItems(balanceItems, PermissionLevel.Read, {registrations, orders}))) {
32
+ if (!(await Context.auth.canAccessBalanceItems(balanceItems, PermissionLevel.Read, { registrations, orders }))) {
33
33
  throw new SimpleError({
34
- code: "not_found",
35
- message: "Payment not found",
36
- human: "Je hebt geen toegang tot deze betaling"
37
- })
34
+ code: 'not_found',
35
+ message: 'Payment not found',
36
+ human: 'Je hebt geen toegang tot deze betaling',
37
+ });
38
38
  }
39
39
  }
40
40
 
41
- const includeSettlements = checkPermissions && !!Context.user && !!Context.user.permissions
41
+ const includeSettlements = checkPermissions && !!Context.user && !!Context.user.permissions;
42
42
 
43
- console.log('includeSettlements', includeSettlements)
43
+ console.log('includeSettlements', includeSettlements);
44
44
 
45
45
  return Payment.getGeneralStructureFromRelations({
46
46
  payments,
47
47
  balanceItemPayments,
48
- balanceItems
49
- }, includeSettlements)
48
+ balanceItems,
49
+ }, includeSettlements);
50
50
  }
51
51
 
52
52
  static async group(group: Group) {
53
- return (await this.groups([group]))[0]
53
+ return (await this.groups([group]))[0];
54
54
  }
55
55
 
56
56
  static async groups(groups: Group[]) {
57
- const waitingListIds = Formatter.uniqueArray(groups.map(g => g.waitingListId).filter(id => id !== null))
58
- const waitingLists = waitingListIds.length > 0 ? await Group.getByIDs(...waitingListIds) : []
57
+ const waitingListIds = Formatter.uniqueArray(groups.map(g => g.waitingListId).filter(id => id !== null));
58
+ const waitingLists = waitingListIds.length > 0 ? await Group.getByIDs(...waitingListIds) : [];
59
59
 
60
- const structs: GroupStruct[] = []
60
+ const structs: GroupStruct[] = [];
61
61
  for (const group of groups) {
62
- const waitingList = waitingLists.find(g => g.id == group.waitingListId) ?? null
63
- const waitingListStruct = waitingList ? GroupStruct.create(waitingList) : null
62
+ const waitingList = waitingLists.find(g => g.id == group.waitingListId) ?? null;
63
+ const waitingListStruct = waitingList ? GroupStruct.create(waitingList) : null;
64
64
  if (waitingList && waitingListStruct && !await Context.optionalAuth?.canAccessGroup(waitingList)) {
65
65
  waitingListStruct.privateSettings = null;
66
66
  }
67
67
 
68
68
  const struct = GroupStruct.create({
69
69
  ...group,
70
- waitingList: waitingListStruct
71
- })
70
+ waitingList: waitingListStruct,
71
+ });
72
72
 
73
73
  if (!await Context.optionalAuth?.canAccessGroup(group)) {
74
74
  struct.privateSettings = null;
75
75
  }
76
76
 
77
- structs.push(struct)
77
+ structs.push(struct);
78
78
  }
79
79
 
80
80
  return structs;
81
81
  }
82
82
 
83
- static async organizationRegistrationPeriods(organizationRegistrationPeriods: OrganizationRegistrationPeriod[]) {
83
+ static async organizationRegistrationPeriods(organizationRegistrationPeriods: OrganizationRegistrationPeriod[], periods?: RegistrationPeriod[]) {
84
84
  if (organizationRegistrationPeriods.length === 0) {
85
85
  return [];
86
86
  }
87
87
 
88
- const periodIds = Formatter.uniqueArray(organizationRegistrationPeriods.map(p => p.periodId))
89
- const periods = await RegistrationPeriod.getByIDs(...periodIds)
88
+ if (!periods) {
89
+ const periodIds = Formatter.uniqueArray(organizationRegistrationPeriods.map(p => p.periodId));
90
+ periods = await RegistrationPeriod.getByIDs(...periodIds);
91
+ }
90
92
 
91
- const groupIds = Formatter.uniqueArray(organizationRegistrationPeriods.flatMap(p => p.settings.categories.flatMap(c => c.groupIds)))
92
- const groups = groupIds.length ? await Group.getByIDs(...groupIds) : []
93
+ const groupIds = Formatter.uniqueArray(organizationRegistrationPeriods.flatMap(p => p.settings.categories.flatMap(c => c.groupIds)));
94
+ const groups = groupIds.length ? await Group.getByIDs(...groupIds) : [];
93
95
 
94
- const groupStructs = await this.groups(groups)
96
+ const groupStructs = await this.groups(groups);
95
97
 
96
- const structs: OrganizationRegistrationPeriodStruct[] = []
98
+ const structs: OrganizationRegistrationPeriodStruct[] = [];
97
99
  for (const organizationPeriod of organizationRegistrationPeriods) {
98
- const period = periods.find(p => p.id == organizationPeriod.periodId) ?? null
100
+ const period = periods.find(p => p.id == organizationPeriod.periodId) ?? null;
99
101
  if (!period) {
100
- continue
102
+ continue;
101
103
  }
102
- const groupIds = Formatter.uniqueArray(organizationPeriod.settings.categories.flatMap(c => c.groupIds))
104
+ const groupIds = Formatter.uniqueArray(organizationPeriod.settings.categories.flatMap(c => c.groupIds));
103
105
 
104
106
  structs.push(
105
107
  OrganizationRegistrationPeriodStruct.create({
106
108
  ...organizationPeriod,
107
109
  period: period.getStructure(),
108
- groups: groupStructs.filter(gg => groupIds.includes(gg.id)).sort(GroupStruct.defaultSort)
109
- })
110
- )
110
+ groups: groupStructs.filter(gg => groupIds.includes(gg.id)).sort(GroupStruct.defaultSort),
111
+ }),
112
+ );
111
113
  }
112
114
 
113
- return structs
115
+ return structs;
114
116
  }
115
117
 
116
- static async organizationRegistrationPeriod(organizationRegistrationPeriod: OrganizationRegistrationPeriod) {
117
- return (await this.organizationRegistrationPeriods([organizationRegistrationPeriod]))[0]
118
+ static async organizationRegistrationPeriod(organizationRegistrationPeriod: OrganizationRegistrationPeriod, periods?: RegistrationPeriod[]) {
119
+ return (await this.organizationRegistrationPeriods([organizationRegistrationPeriod], periods))[0];
118
120
  }
119
121
 
120
122
  static async webshop(webshop: Webshop) {
121
123
  if (await Context.optionalAuth?.canAccessWebshop(webshop)) {
122
- return PrivateWebshop.create(webshop)
124
+ return PrivateWebshop.create(webshop);
123
125
  }
124
- return WebshopStruct.create(webshop)
126
+ return WebshopStruct.create(webshop);
125
127
  }
126
128
 
127
129
  static async organization(organization: Organization): Promise<OrganizationStruct> {
128
- const organizationPeriod = await organization.getPeriod()
130
+ return (await this.organizations([organization]))[0];
131
+ }
129
132
 
130
- if (await Context.optionalAuth?.canAccessPrivateOrganizationData(organization)) {
131
- const webshops = await Webshop.where({ organizationId: organization.id }, { select: Webshop.selectColumnsWithout(undefined, "products", "categories")})
132
- const webshopStructures: WebshopPreview[] = []
133
+ static async organizations(organizations: Organization[]): Promise<OrganizationStruct[]> {
134
+ if (organizations.length === 0) {
135
+ return [];
136
+ }
133
137
 
134
- for (const w of webshops) {
135
- if (!await Context.auth.canAccessWebshop(w)) {
136
- continue
137
- }
138
- webshopStructures.push(WebshopPreview.create(w))
138
+ // #region get period ids / organizations map
139
+ const periodIdOrganizationsMap = new Map<string, Organization[]>();
140
+
141
+ for (const organization of organizations) {
142
+ const periodId = organization.periodId;
143
+ const array = periodIdOrganizationsMap.get(periodId);
144
+ if (array !== undefined) {
145
+ array.push(organization);
146
+ }
147
+ else {
148
+ periodIdOrganizationsMap.set(periodId, [organization]);
139
149
  }
150
+ }
151
+ // #endregion
140
152
 
141
- return OrganizationStruct.create({
142
- ...organization.getBaseStructure(),
143
- privateMeta: organization.privateMeta,
144
- webshops: webshopStructures,
145
- period: await this.organizationRegistrationPeriod(organizationPeriod)
146
- })
153
+ // #region get registration period and whether private data can be accessed for each organization
154
+ const organizationData: Map<string, { organizationRegistrationPeriod: OrganizationRegistrationPeriod; canAccessPrivateData: boolean }> = new Map();
155
+ const organizationIdsToGetWebshopsFor: string[] = [];
156
+
157
+ for (const [periodId, organizations] of periodIdOrganizationsMap.entries()) {
158
+ const organizationMap = new Map(organizations.map(o => [o.id, o]));
159
+
160
+ const result = await OrganizationRegistrationPeriod.where({
161
+ periodId,
162
+ organizationId: {
163
+ sign: 'IN',
164
+ value: Array.from(organizationMap.keys()),
165
+ },
166
+ });
167
+
168
+ const organizationRegistrationPeriods = new Map(result.map(r => [r.organizationId, r]));
169
+
170
+ for (const organization of organizations) {
171
+ const organizationId = organization.id;
172
+ const organizationRegistrationPeriod = organizationRegistrationPeriods.get(organizationId) ?? await organization.getPeriod();
173
+
174
+ // check if private data can be accessed
175
+ const canAccessPrivateData = await Context.optionalAuth?.canAccessPrivateOrganizationData(organization) ?? false;
176
+ if (canAccessPrivateData) {
177
+ organizationIdsToGetWebshopsFor.push(organizationId);
178
+ }
179
+
180
+ organizationData.set(organizationId, { organizationRegistrationPeriod, canAccessPrivateData });
181
+ }
147
182
  }
148
-
149
- return OrganizationStruct.create({
150
- ...organization.getBaseStructure(),
151
- period: await this.organizationRegistrationPeriod(organizationPeriod)
152
- })
153
- }
183
+ // #endregion
184
+
185
+ // #region get periods
186
+ const allPeriodIds = periodIdOrganizationsMap.keys();
187
+ const allPeriods = await RegistrationPeriod.getByIDs(...allPeriodIds);
188
+ const periodMap = new Map<string, RegistrationPeriod>(allPeriods.map(p => [p.id, p]));
189
+ // #endregion
190
+
191
+ // #region get webshop previews
192
+ const webshops = organizationIdsToGetWebshopsFor.length > 0
193
+ ? await Webshop.where(
194
+ {
195
+ organizationId: {
196
+ sign: 'IN',
197
+ value: organizationIdsToGetWebshopsFor,
198
+ },
199
+ },
200
+ { select: Webshop.selectColumnsWithout(undefined, 'products', 'categories') },
201
+ )
202
+ : [];
154
203
 
155
- static async organizations(organizations: Organization[]): Promise<OrganizationStruct[]> {
156
- // for now simple loop
157
- if (organizations.length > 10) {
158
- console.warn('Trying to load too many organizations at once: ' + organizations.length)
204
+ const webshopPreviews = new Map<string, WebshopPreview[]>();
205
+
206
+ for (const w of webshops) {
207
+ if (!await Context.auth.canAccessWebshop(w)) {
208
+ continue;
209
+ }
210
+
211
+ const organizationId = w.organizationId;
212
+ const array = webshopPreviews.get(organizationId);
213
+ const preview = WebshopPreview.create(w);
214
+
215
+ if (array) {
216
+ array.push(preview);
217
+ }
218
+ else {
219
+ webshopPreviews.set(organizationId, [preview]);
220
+ }
159
221
  }
222
+ // #endregion
223
+
224
+ // #region create organization structs
225
+ const results: OrganizationStruct[] = [];
160
226
 
161
- const structs: OrganizationStruct[] = [];
162
227
  for (const organization of organizations) {
163
- structs.push(await this.organization(organization))
228
+ const registrationPeriod = periodMap.get(organization.periodId);
229
+ if (!registrationPeriod) {
230
+ console.error('Registration period is undefined.');
231
+ continue;
232
+ }
233
+
234
+ const organizationId = organization.id;
235
+ const data = organizationData.get(organizationId);
236
+ if (data === undefined) {
237
+ console.error('Organization data is undefined.');
238
+ continue;
239
+ }
240
+
241
+ let result: OrganizationStruct;
242
+
243
+ const period = await this.organizationRegistrationPeriod(data.organizationRegistrationPeriod, [registrationPeriod]);
244
+ const baseStruct = organization.getBaseStructure();
245
+
246
+ if (data.canAccessPrivateData) {
247
+ result = OrganizationStruct.create({
248
+ ...baseStruct,
249
+ period,
250
+ privateMeta: organization.privateMeta,
251
+ webshops: webshopPreviews.get(organization.id),
252
+ });
253
+ }
254
+ else {
255
+ result = OrganizationStruct.create({
256
+ ...baseStruct,
257
+ period,
258
+ });
259
+ }
260
+
261
+ results.push(result);
164
262
  }
165
- return structs
263
+ // #endregion
264
+
265
+ return results;
166
266
  }
167
267
 
168
268
  static async adminOrganizations(organizations: Organization[]): Promise<OrganizationStruct[]> {
169
269
  const structs: OrganizationStruct[] = [];
170
270
 
171
271
  for (const organization of organizations) {
172
- const base = organization.getBaseStructure()
173
- structs.push(base)
272
+ const base = organization.getBaseStructure();
273
+ structs.push(base);
174
274
  }
175
-
176
- return Promise.resolve(structs)
275
+
276
+ return Promise.resolve(structs);
177
277
  }
178
278
 
179
279
  static async userWithMembers(user: User): Promise<UserWithMembers> {
180
- const members = await Member.getMembersWithRegistrationForUser(user)
280
+ const members = await Member.getMembersWithRegistrationForUser(user);
181
281
 
182
282
  return UserWithMembers.create({
183
283
  ...user,
184
284
  hasAccount: user.hasAccount(),
185
285
 
186
286
  // Always include the current context organization - because it is possible we switch organization and we don't want to refetch every time
187
- members: await this.membersBlob(members, true, user)
188
- })
287
+ members: await this.membersBlob(members, true, user),
288
+ });
189
289
  }
190
290
 
191
291
  /**
@@ -193,134 +293,133 @@ export class AuthenticatedStructures {
193
293
  */
194
294
  static async usersWithMembers(users: User[]): Promise<UserWithMembers[]> {
195
295
  const structs: UserWithMembers[] = [];
196
- const memberIds = Formatter.uniqueArray(users.map(u => u.memberId).filter(id => id !== null))
197
- const members = memberIds.length > 0 ? await Member.getBlobByIds(...memberIds) : []
296
+ const memberIds = Formatter.uniqueArray(users.map(u => u.memberId).filter(id => id !== null));
297
+ const members = memberIds.length > 0 ? await Member.getBlobByIds(...memberIds) : [];
198
298
 
199
299
  for (const user of users) {
200
- const filteredMembers = user.memberId ? members.filter(m => m.id === user.memberId) : []
300
+ const filteredMembers = user.memberId ? members.filter(m => m.id === user.memberId) : [];
201
301
  structs.push(UserWithMembers.create({
202
302
  ...user,
203
303
  hasAccount: user.hasAccount(),
204
- members: await this.membersBlob(filteredMembers, false)
205
- }))
304
+ members: await this.membersBlob(filteredMembers, false),
305
+ }));
206
306
  }
207
-
208
- return structs
307
+
308
+ return structs;
209
309
  }
210
310
 
211
311
  static async membersBlob(members: MemberWithRegistrations[], includeContextOrganization = false, includeUser?: User): Promise<MembersBlob> {
212
312
  if (members.length === 0 && !includeUser) {
213
- return MembersBlob.create({members: [], organizations: []})
313
+ return MembersBlob.create({ members: [], organizations: [] });
214
314
  }
215
- const organizations = new Map<string, Organization>()
216
-
315
+ const organizations = new Map<string, Organization>();
316
+
217
317
  if (includeUser) {
218
318
  for (const organizationId of includeUser.permissions?.organizationPermissions.keys() ?? []) {
219
319
  if (includeContextOrganization || organizationId !== Context.auth.organization?.id) {
220
320
  const found = organizations.get(organizationId);
221
321
  if (!found) {
222
- const organization = await Context.auth.getOrganization(organizationId)
223
- organizations.set(organization.id, organization)
322
+ const organization = await Context.auth.getOrganization(organizationId);
323
+ organizations.set(organization.id, organization);
224
324
  }
225
325
  }
226
326
  }
227
327
  }
228
328
 
229
-
230
- const memberBlobs: MemberWithRegistrationsBlob[] = []
329
+ const memberBlobs: MemberWithRegistrationsBlob[] = [];
231
330
  for (const member of members) {
232
331
  for (const registration of member.registrations) {
233
332
  if (includeContextOrganization || registration.organizationId !== Context.auth.organization?.id) {
234
333
  const found = organizations.get(registration.id);
235
334
  if (!found) {
236
- const organization = await Context.auth.getOrganization(registration.organizationId)
237
- organizations.set(organization.id, organization)
335
+ const organization = await Context.auth.getOrganization(registration.organizationId);
336
+ organizations.set(organization.id, organization);
238
337
  }
239
338
  }
240
339
  }
241
- member.registrations = member.registrations.filter(r => (Context.auth.organization && Context.auth.organization.active && r.organizationId === Context.auth.organization.id) || (organizations.get(r.organizationId)?.active ?? false))
242
- const blob = member.getStructureWithRegistrations()
340
+ member.registrations = member.registrations.filter(r => (Context.auth.organization && Context.auth.organization.active && r.organizationId === Context.auth.organization.id) || (organizations.get(r.organizationId)?.active ?? false));
341
+ const blob = member.getStructureWithRegistrations();
243
342
  memberBlobs.push(
244
- await Context.auth.filterMemberData(member, blob)
245
- )
246
-
343
+ await Context.auth.filterMemberData(member, blob),
344
+ );
247
345
  }
248
346
 
249
347
  // Load responsibilities
250
- const responsibilities = members.length > 0 ? await MemberResponsibilityRecord.where({ memberId: { sign: 'IN', value: members.map(m => m.id) } }) : []
251
- const platformMemberships = members.length > 0 ? await MemberPlatformMembership.where({ deletedAt: null, memberId: { sign: 'IN', value: members.map(m => m.id) } }) : []
348
+ const responsibilities = members.length > 0 ? await MemberResponsibilityRecord.where({ memberId: { sign: 'IN', value: members.map(m => m.id) } }) : [];
349
+ const platformMemberships = members.length > 0 ? await MemberPlatformMembership.where({ deletedAt: null, memberId: { sign: 'IN', value: members.map(m => m.id) } }) : [];
252
350
 
253
351
  // Load missing organizations
254
- const organizationIds = Formatter.uniqueArray(responsibilities.map(r => r.organizationId).filter(id => id !== null))
352
+ const organizationIds = Formatter.uniqueArray(responsibilities.map(r => r.organizationId).filter(id => id !== null));
255
353
  for (const id of organizationIds) {
256
354
  if (includeContextOrganization || id !== Context.auth.organization?.id) {
257
355
  const found = organizations.get(id);
258
356
  if (!found) {
259
- const organization = await Context.auth.getOrganization(id)
260
- organizations.set(organization.id, organization)
357
+ const organization = await Context.auth.getOrganization(id);
358
+ organizations.set(organization.id, organization);
261
359
  }
262
360
  }
263
361
  }
264
362
 
265
- const organizationStructs = await Promise.all([...organizations.values()].filter(o => o.active).map(o => this.organization(o)))
363
+ const activeOrganizations = [...organizations.values()].filter(o => o.active);
364
+ const organizationStructs = await this.organizations(activeOrganizations);
266
365
 
267
366
  // Load missing groups
268
- const allGroups = new Map<string, GroupStruct>()
367
+ const allGroups = new Map<string, GroupStruct>();
269
368
  for (const organization of organizationStructs) {
270
369
  for (const group of organization.period.groups) {
271
- allGroups.set(group.id, group)
370
+ allGroups.set(group.id, group);
272
371
  }
273
372
  }
274
373
 
275
374
  for (const blob of memberBlobs) {
276
375
  for (const registration of blob.registrations) {
277
376
  if (registration.group) {
278
- allGroups.set(registration.group.id, registration.group)
377
+ allGroups.set(registration.group.id, registration.group);
279
378
  }
280
379
  }
281
380
  }
282
381
 
283
- const groupIds = Formatter.uniqueArray(responsibilities.map(r => r.groupId).filter(id => id !== null)).filter(id => !allGroups.has(id))
284
- const groups = groupIds.length > 0 ? await Group.getByIDs(...groupIds) : []
285
- const groupStructs = await this.groups(groups)
286
-
382
+ const groupIds = Formatter.uniqueArray(responsibilities.map(r => r.groupId).filter(id => id !== null)).filter(id => !allGroups.has(id));
383
+ const groups = groupIds.length > 0 ? await Group.getByIDs(...groupIds) : [];
384
+ const groupStructs = await this.groups(groups);
385
+
287
386
  for (const group of groupStructs) {
288
- allGroups.set(group.id, group)
387
+ allGroups.set(group.id, group);
289
388
  }
290
389
 
291
390
  for (const blob of memberBlobs) {
292
- blob.responsibilities = responsibilities.filter(r => r.memberId == blob.id).map(r => {
293
- const group = allGroups.get(r.groupId ?? '') ?? null
294
- return r.getStructure(group)
295
- })
296
- blob.platformMemberships = platformMemberships.filter(r => r.memberId == blob.id).map(r => MemberPlatformMembershipStruct.create(r))
391
+ blob.responsibilities = responsibilities.filter(r => r.memberId == blob.id).map((r) => {
392
+ const group = allGroups.get(r.groupId ?? '') ?? null;
393
+ return r.getStructure(group);
394
+ });
395
+ blob.platformMemberships = platformMemberships.filter(r => r.memberId == blob.id).map(r => MemberPlatformMembershipStruct.create(r));
297
396
  }
298
397
 
299
398
  return MembersBlob.create({
300
399
  members: memberBlobs,
301
- organizations: organizationStructs
302
- })
400
+ organizations: organizationStructs,
401
+ });
303
402
  }
304
403
 
305
404
  static async events(events: Event[]): Promise<EventStruct[]> {
306
405
  // Load groups
307
- const groupIds = events.map(e => e.groupId).filter(id => id !== null)
308
- const groups = groupIds.length > 0 ? await Group.getByIDs(...groupIds) : []
309
- const groupStructs = await this.groups(groups)
406
+ const groupIds = events.map(e => e.groupId).filter(id => id !== null);
407
+ const groups = groupIds.length > 0 ? await Group.getByIDs(...groupIds) : [];
408
+ const groupStructs = await this.groups(groups);
310
409
 
311
- const result: EventStruct[] = []
410
+ const result: EventStruct[] = [];
312
411
 
313
412
  for (const event of events) {
314
- const group = groupStructs.find(g => g.id == event.groupId) ?? null
413
+ const group = groupStructs.find(g => g.id == event.groupId) ?? null;
315
414
 
316
415
  const struct = EventStruct.create({
317
416
  ...event,
318
- group
319
- })
417
+ group,
418
+ });
320
419
 
321
- result.push(struct)
420
+ result.push(struct);
322
421
  }
323
-
324
- return result
422
+
423
+ return result;
325
424
  }
326
425
  }