@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,11 +1,11 @@
1
- import { Member, MemberResponsibilityRecord, MemberWithRegistrations, User } from "@stamhoofd/models";
2
- import { SQL } from "@stamhoofd/sql";
3
- import { MemberDetails, Permissions, UserPermissions } from "@stamhoofd/structures";
4
- import crypto from "crypto";
5
- import basex from "base-x";
1
+ import { Member, MemberResponsibilityRecord, MemberWithRegistrations, User } from '@stamhoofd/models';
2
+ import { SQL } from '@stamhoofd/sql';
3
+ import { MemberDetails, Permissions, UserPermissions } from '@stamhoofd/structures';
4
+ import crypto from 'crypto';
5
+ import basex from 'base-x';
6
6
 
7
- const ALPHABET = '123456789ABCDEFGHJKMNPQRSTUVWXYZ' // Note: we removed 0, O, I and l to make it easier for humans
8
- const customBase = basex(ALPHABET)
7
+ const ALPHABET = '123456789ABCDEFGHJKMNPQRSTUVWXYZ'; // Note: we removed 0, O, I and l to make it easier for humans
8
+ const customBase = basex(ALPHABET);
9
9
 
10
10
  async function randomBytes(size: number): Promise<Buffer> {
11
11
  return new Promise((resolve, reject) => {
@@ -26,23 +26,23 @@ export class MemberUserSyncerStatic {
26
26
  * - email addresses have changed
27
27
  */
28
28
  async onChangeMember(member: MemberWithRegistrations, unlinkUsers: boolean = false) {
29
- const {userEmails, parentAndUnverifiedEmails} = this.getMemberAccessEmails(member.details)
29
+ const { userEmails, parentAndUnverifiedEmails } = this.getMemberAccessEmails(member.details);
30
30
 
31
31
  // Make sure all these users have access to the member
32
32
  for (const email of userEmails) {
33
33
  // Link users that are found with these email addresses.
34
- await this.linkUser(email, member, false, true)
34
+ await this.linkUser(email, member, false, true);
35
35
  }
36
36
 
37
37
  for (const email of parentAndUnverifiedEmails) {
38
38
  if (userEmails.includes(email)) {
39
39
  continue;
40
40
  }
41
-
41
+
42
42
  // Link parents and unverified emails
43
43
  // Now we add the responsibility permissions to the parent if there are no userEmails
44
44
  const asParent = userEmails.length > 0 || !member.details.unverifiedEmails.includes(email) || member.details.defaultAge < 16;
45
- await this.linkUser(email, member, asParent, true)
45
+ await this.linkUser(email, member, asParent, true);
46
46
  }
47
47
 
48
48
  if (unlinkUsers && !member.details.parentsHaveAccess) {
@@ -53,226 +53,231 @@ export class MemberUserSyncerStatic {
53
53
 
54
54
  for (const user of member.users) {
55
55
  if (!userEmails.includes(user.email) && !parentAndUnverifiedEmails.includes(user.email)) {
56
- await this.unlinkUser(user, member)
56
+ await this.unlinkUser(user, member);
57
57
  }
58
58
  }
59
- } else {
59
+ }
60
+ else {
60
61
  // Only auto unlink users that do not have an account
61
62
  for (const user of member.users) {
62
63
  if (!userEmails.includes(user.email) && !parentAndUnverifiedEmails.includes(user.email)) {
63
64
  if (!user.hasAccount()) {
64
- await this.unlinkUser(user, member)
65
- } else {
65
+ await this.unlinkUser(user, member);
66
+ }
67
+ else {
66
68
  // Make sure only linked as a parent, not as user self
67
69
  // This makes sure we don't inherit permissions and aren't counted as 'being' the member
68
- await this.linkUser(user.email, member, true)
70
+ await this.linkUser(user.email, member, true);
69
71
  }
70
72
  }
71
73
  }
72
74
  }
73
75
 
74
76
  if (member.details.securityCode === null) {
75
- console.log("Generating security code for member "+member.id)
77
+ console.log('Generating security code for member ' + member.id);
76
78
 
77
79
  const length = 16;
78
80
  const code = customBase.encode(await randomBytes(100)).toUpperCase().substring(0, length);
79
- member.details.securityCode = code
80
- await member.save()
81
+ member.details.securityCode = code;
82
+ await member.save();
81
83
  }
82
84
  }
83
85
 
84
86
  getMemberAccessEmails(details: MemberDetails) {
85
- const userEmails = [...details.alternativeEmails]
87
+ const userEmails = [...details.alternativeEmails];
86
88
 
87
89
  if (details.email) {
88
- userEmails.push(details.email)
90
+ userEmails.push(details.email);
89
91
  }
90
92
 
91
93
  const unverifiedEmails: string[] = details.unverifiedEmails;
92
- const parentAndUnverifiedEmails = details.parentsHaveAccess ? details.parents.flatMap(p => p.email ? [p.email, ...p.alternativeEmails] : p.alternativeEmails).concat(unverifiedEmails) : details.unverifiedEmails
94
+ const parentAndUnverifiedEmails = details.parentsHaveAccess ? details.parents.flatMap(p => p.email ? [p.email, ...p.alternativeEmails] : p.alternativeEmails).concat(unverifiedEmails) : details.unverifiedEmails;
93
95
 
94
96
  return {
95
97
  userEmails,
96
98
  parentAndUnverifiedEmails,
97
- emails: userEmails.concat(parentAndUnverifiedEmails)
98
- }
99
+ emails: userEmails.concat(parentAndUnverifiedEmails),
100
+ };
99
101
  }
100
102
 
101
103
  doesEmailHaveAccess(details: MemberDetails, email: string) {
102
- const {emails} = this.getMemberAccessEmails(details)
103
- return emails.includes(email)
104
+ const { emails } = this.getMemberAccessEmails(details);
105
+ return emails.includes(email);
104
106
  }
105
107
 
106
108
  async onDeleteMember(member: MemberWithRegistrations) {
107
109
  for (const u of member.users) {
108
- console.log("Unlinking user "+u.email+" from deleted member "+member.id)
109
- await this.unlinkUser(u, member)
110
+ console.log('Unlinking user ' + u.email + ' from deleted member ' + member.id);
111
+ await this.unlinkUser(u, member);
110
112
  }
111
113
  }
112
114
 
113
115
  async getResponsibilitiesForMembers(memberIds: string[]) {
114
116
  const rows = await SQL.select()
115
117
  .from(SQL.table(MemberResponsibilityRecord.table))
116
- .where(SQL.column("memberId"), memberIds)
117
- .where(SQL.column("endDate"), null)
118
+ .where(SQL.column('memberId'), memberIds)
119
+ .where(SQL.column('endDate'), null)
118
120
  .fetch();
119
121
 
120
- return MemberResponsibilityRecord.fromRows(rows, MemberResponsibilityRecord.table)
122
+ return MemberResponsibilityRecord.fromRows(rows, MemberResponsibilityRecord.table);
121
123
  }
122
124
 
123
125
  async updateInheritedPermissions(user: User) {
124
- const responsibilities = user.memberId ? (await this.getResponsibilitiesForMembers([user.memberId])) : []
126
+ const responsibilities = user.memberId ? (await this.getResponsibilitiesForMembers([user.memberId])) : [];
125
127
 
126
128
  // Check if the member has active registrations
127
129
  // otherwise -> do not inherit any permissions
128
130
 
129
- user.permissions = user.permissions ?? UserPermissions.create({})
131
+ user.permissions = user.permissions ?? UserPermissions.create({});
130
132
 
131
133
  // Group responsibilities by organization
132
- const responsibilitiesByOrganization: Map<string|null, MemberResponsibilityRecord[]> = new Map()
134
+ const responsibilitiesByOrganization: Map<string | null, MemberResponsibilityRecord[]> = new Map();
133
135
 
134
- responsibilitiesByOrganization.set(null, [])
136
+ responsibilitiesByOrganization.set(null, []);
135
137
 
136
138
  for (const organizationId of user.permissions.organizationPermissions.keys()) {
137
139
  // Make sure we reset responsibilities for organizations that are not in the list
138
- responsibilitiesByOrganization.set(organizationId, [])
140
+ responsibilitiesByOrganization.set(organizationId, []);
139
141
  }
140
142
 
141
143
  for (const responsibility of responsibilities) {
142
- const v = responsibilitiesByOrganization.get(responsibility.organizationId) ?? []
143
- responsibilitiesByOrganization.set(responsibility.organizationId, v)
144
- v.push(responsibility)
144
+ const v = responsibilitiesByOrganization.get(responsibility.organizationId) ?? [];
145
+ responsibilitiesByOrganization.set(responsibility.organizationId, v);
146
+ v.push(responsibility);
145
147
  }
146
148
 
147
149
  for (const organizationId of responsibilitiesByOrganization.keys()) {
148
150
  if (organizationId === null) {
149
151
  const patch = user.permissions.convertPlatformPatch(
150
152
  Permissions.patch({
151
- responsibilities: (responsibilitiesByOrganization.get(organizationId) ?? []).map(r => r.getBaseStructure()) as any
152
- })
153
- )
154
- user.permissions = user.permissions.patch(patch)
155
- } else {
153
+ responsibilities: (responsibilitiesByOrganization.get(organizationId) ?? []).map(r => r.getBaseStructure()) as any,
154
+ }),
155
+ );
156
+ user.permissions = user.permissions.patch(patch);
157
+ }
158
+ else {
156
159
  const patch = user.permissions.convertPatch(
157
160
  Permissions.patch({
158
- responsibilities: (responsibilitiesByOrganization.get(organizationId) ?? []).map(r => r.getBaseStructure()) as any
161
+ responsibilities: (responsibilitiesByOrganization.get(organizationId) ?? []).map(r => r.getBaseStructure()) as any,
159
162
  }),
160
- organizationId
161
- )
162
- user.permissions = user.permissions.patch(patch)
163
+ organizationId,
164
+ );
165
+ user.permissions = user.permissions.patch(patch);
163
166
  }
164
-
165
167
  }
166
168
 
167
169
  // Platform permissions
168
- user.permissions.clearEmptyPermissions()
170
+ user.permissions.clearEmptyPermissions();
169
171
 
170
172
  if (user.permissions.isEmpty) {
171
173
  user.permissions = null;
172
174
  }
173
175
 
174
- await user.save()
176
+ await user.save();
175
177
  }
176
178
 
177
179
  async unlinkUser(user: User, member: MemberWithRegistrations) {
178
- console.log("Removing access for "+ user.id +" to member "+member.id)
179
- await Member.users.reverse("members").unlink(user, member)
180
+ console.log('Removing access for ' + user.id + ' to member ' + member.id);
181
+ await Member.users.reverse('members').unlink(user, member);
180
182
 
181
183
  if (user.memberId === member.id) {
182
184
  user.memberId = null;
183
- await user.save()
185
+ await user.save();
184
186
  }
185
187
 
186
188
  // Update model relation to correct response
187
- const existingIndex = member.users.findIndex(u => u.id === user.id)
189
+ const existingIndex = member.users.findIndex(u => u.id === user.id);
188
190
  if (existingIndex !== -1) {
189
- member.users.splice(existingIndex, 1)
191
+ member.users.splice(existingIndex, 1);
190
192
  }
191
193
 
192
- await this.updateInheritedPermissions(user)
194
+ await this.updateInheritedPermissions(user);
193
195
  }
194
196
 
195
197
  async linkUser(email: string, member: MemberWithRegistrations, asParent: boolean, updateNameIfEqual = true) {
196
- console.log('Linking user', email, 'to member', member.id, 'as parent', asParent, 'update name if equal', updateNameIfEqual)
198
+ console.log('Linking user', email, 'to member', member.id, 'as parent', asParent, 'update name if equal', updateNameIfEqual);
197
199
 
198
- let user = member.users.find(u => u.email.toLocaleLowerCase() === email.toLocaleLowerCase()) ?? await User.getForAuthentication(member.organizationId, email, {allowWithoutAccount: true})
200
+ let user = member.users.find(u => u.email.toLocaleLowerCase() === email.toLocaleLowerCase()) ?? await User.getForAuthentication(member.organizationId, email, { allowWithoutAccount: true });
199
201
 
200
202
  if (user) {
201
- //console.log("Giving an existing user access to a member: " + user.id + ' - ' + member.id)
203
+ // console.log("Giving an existing user access to a member: " + user.id + ' - ' + member.id)
202
204
  if (!asParent) {
203
205
  if (user.memberId && user.memberId !== member.id) {
204
- console.error('Found conflicting user with multiple members', user.id, 'members', user.memberId, 'to', member.id)
205
-
206
- const otherMember = await Member.getWithRegistrations(user.memberId)
206
+ console.error('Found conflicting user with multiple members', user.id, 'members', user.memberId, 'to', member.id);
207
+
208
+ const otherMember = await Member.getWithRegistrations(user.memberId);
207
209
 
208
210
  if (otherMember) {
209
211
  if (otherMember.registrations.length > 0 && member.registrations.length === 0) {
210
212
  // Choose the other member
211
213
  // don't make changes
212
- console.error('Resolved to current member - no changes made')
213
- return
214
+ console.error('Resolved to current member - no changes made');
215
+ return;
214
216
  }
215
217
 
216
- const responsibilities = await this.getResponsibilitiesForMembers([otherMember.id, member.id])
217
- const responsibilitiesOther = responsibilities.filter(r => r.memberId === otherMember.id)
218
- const responsibilitiesCurrent = responsibilities.filter(r => r.memberId === member.id)
218
+ const responsibilities = await this.getResponsibilitiesForMembers([otherMember.id, member.id]);
219
+ const responsibilitiesOther = responsibilities.filter(r => r.memberId === otherMember.id);
220
+ const responsibilitiesCurrent = responsibilities.filter(r => r.memberId === member.id);
219
221
 
220
222
  if (responsibilitiesOther.length >= responsibilitiesCurrent.length) {
221
- console.error('Resolved to current member because of more responsibilities - no changes made')
222
- return
223
+ console.error('Resolved to current member because of more responsibilities - no changes made');
224
+ return;
223
225
  }
224
226
  }
225
227
  }
226
228
 
227
229
  if (updateNameIfEqual) {
228
- user.firstName = member.details.firstName
229
- user.lastName = member.details.lastName
230
+ user.firstName = member.details.firstName;
231
+ user.lastName = member.details.lastName;
230
232
  }
231
233
  user.memberId = member.id;
232
- await this.updateInheritedPermissions(user)
233
- } else {
234
+ await this.updateInheritedPermissions(user);
235
+ }
236
+ else {
234
237
  if (user.memberId === member.id) {
235
238
  // Unlink: parents are never 'equal' to the member
236
239
  user.memberId = null;
237
- await this.updateInheritedPermissions(user)
240
+ await this.updateInheritedPermissions(user);
238
241
  }
239
242
 
240
243
  if (!user.firstName && !user.lastName) {
241
- const parents = member.details.parents.filter(p => p.email === email)
244
+ const parents = member.details.parents.filter(p => p.email === email);
242
245
  if (parents.length === 1) {
243
246
  if (updateNameIfEqual) {
244
- user.firstName = parents[0].firstName
245
- user.lastName = parents[0].lastName
247
+ user.firstName = parents[0].firstName;
248
+ user.lastName = parents[0].lastName;
246
249
  }
247
- await user.save()
250
+ await user.save();
248
251
  }
249
252
  }
250
253
 
251
254
  if (user.firstName === member.details.firstName && user.lastName === member.details.lastName) {
252
255
  user.firstName = null;
253
256
  user.lastName = null;
254
- await user.save()
257
+ await user.save();
255
258
  }
256
259
  }
257
- } else {
260
+ }
261
+ else {
258
262
  // Create a new placeholder user
259
- user = new User()
260
- user.organizationId = member.organizationId
261
- user.email = email
263
+ user = new User();
264
+ user.organizationId = member.organizationId;
265
+ user.email = email;
262
266
 
263
267
  if (!asParent) {
264
268
  if (updateNameIfEqual) {
265
- user.firstName = member.details.firstName
266
- user.lastName = member.details.lastName
269
+ user.firstName = member.details.firstName;
270
+ user.lastName = member.details.lastName;
267
271
  }
268
272
  user.memberId = member.id;
269
- await this.updateInheritedPermissions(user)
270
- } else {
271
- const parents = member.details.parents.filter(p => p.email === email)
273
+ await this.updateInheritedPermissions(user);
274
+ }
275
+ else {
276
+ const parents = member.details.parents.filter(p => p.email === email);
272
277
  if (parents.length === 1) {
273
278
  if (updateNameIfEqual) {
274
- user.firstName = parents[0].firstName
275
- user.lastName = parents[0].lastName
279
+ user.firstName = parents[0].firstName;
280
+ user.lastName = parents[0].lastName;
276
281
  }
277
282
  }
278
283
 
@@ -281,18 +286,18 @@ export class MemberUserSyncerStatic {
281
286
  user.lastName = null;
282
287
  }
283
288
 
284
- await user.save()
289
+ await user.save();
285
290
  }
286
291
 
287
- console.log("Created new (placeholder) user that has access to a member: "+user.id)
292
+ console.log('Created new (placeholder) user that has access to a member: ' + user.id);
288
293
  }
289
294
 
290
295
  // Update model relation to correct response
291
296
  if (!member.users.find(u => u.id === user.id)) {
292
- await Member.users.reverse("members").link(user, [member])
293
- member.users.push(user)
297
+ await Member.users.reverse('members').link(user, [member]);
298
+ member.users.push(user);
294
299
  }
295
300
  }
296
301
  }
297
302
 
298
- export const MemberUserSyncer = new MemberUserSyncerStatic()
303
+ export const MemberUserSyncer = new MemberUserSyncerStatic();
@@ -1,35 +1,34 @@
1
- import { SimpleError } from "@simonbackx/simple-errors";
2
- import { BalanceItem, Member, MemberPlatformMembership, Platform } from "@stamhoofd/models";
3
- import { SQL, SQLOrderBy, SQLWhereSign } from "@stamhoofd/sql";
4
- import { BalanceItemRelation, BalanceItemRelationType, BalanceItemType } from "@stamhoofd/structures";
5
- import { Formatter } from "@stamhoofd/utility";
1
+ import { SimpleError } from '@simonbackx/simple-errors';
2
+ import { BalanceItem, Member, MemberPlatformMembership, Platform } from '@stamhoofd/models';
3
+ import { SQL, SQLOrderBy, SQLWhereSign } from '@stamhoofd/sql';
4
+ import { BalanceItemRelation, BalanceItemRelationType, BalanceItemType } from '@stamhoofd/structures';
5
+ import { Formatter } from '@stamhoofd/utility';
6
6
 
7
7
  export const MembershipCharger = {
8
8
  async charge() {
9
- console.log('Charging memberships...')
10
-
9
+ console.log('Charging memberships...');
10
+
11
11
  // Loop all
12
- let lastId = "";
13
- const platform = await Platform.getShared()
14
- const chargeVia = platform.membershipOrganizationId
12
+ let lastId = '';
13
+ const platform = await Platform.getShared();
14
+ const chargeVia = platform.membershipOrganizationId;
15
15
 
16
16
  if (!chargeVia) {
17
17
  throw new SimpleError({
18
18
  code: 'missing_membership_organization',
19
19
  message: 'Missing membershipOrganizationId',
20
- human: 'Er is geen lokale groep verantwoordelijk voor de aanrekening van aansluitingen geconfigureerd'
21
- })
20
+ human: 'Er is geen lokale groep verantwoordelijk voor de aanrekening van aansluitingen geconfigureerd',
21
+ });
22
22
  }
23
23
 
24
24
  function getType(id: string) {
25
- return platform.config.membershipTypes.find(t => t.id === id)
25
+ return platform.config.membershipTypes.find(t => t.id === id);
26
26
  }
27
27
 
28
28
  let createdCount = 0;
29
29
  let createdPrice = 0;
30
30
  const chunkSize = 100;
31
31
 
32
- // eslint-disable-next-line no-constant-condition
33
32
  while (true) {
34
33
  const memberships = await MemberPlatformMembership.select()
35
34
  .where('id', SQLWhereSign.Greater, lastId)
@@ -40,18 +39,18 @@ export const MembershipCharger = {
40
39
  .orderBy(
41
40
  new SQLOrderBy({
42
41
  column: SQL.column('id'),
43
- direction: 'ASC'
44
- })
42
+ direction: 'ASC',
43
+ }),
45
44
  )
46
45
  .fetch();
47
-
46
+
48
47
  if (memberships.length === 0) {
49
48
  break;
50
49
  }
51
50
 
52
- const memberIds = Formatter.uniqueArray(memberships.map(m => m.memberId))
53
- const members = await Member.getByIDs(...memberIds)
54
- const createdBalanceItems: BalanceItem[] = []
51
+ const memberIds = Formatter.uniqueArray(memberships.map(m => m.memberId));
52
+ const members = await Member.getByIDs(...memberIds);
53
+ const createdBalanceItems: BalanceItem[] = [];
55
54
 
56
55
  for (const membership of memberships) {
57
56
  // charge
@@ -61,7 +60,7 @@ export const MembershipCharger = {
61
60
 
62
61
  const type = getType(membership.membershipTypeId);
63
62
  if (!type) {
64
- console.error('Unknown membership type id ', membership.membershipTypeId)
63
+ console.error('Unknown membership type id ', membership.membershipTypeId);
65
64
  continue;
66
65
  }
67
66
 
@@ -69,83 +68,83 @@ export const MembershipCharger = {
69
68
  continue;
70
69
  }
71
70
 
72
- const member = members.find(m => m.id === membership.memberId)
71
+ const member = members.find(m => m.id === membership.memberId);
73
72
 
74
73
  if (!member) {
75
- console.error('Unexpected missing member id ', membership.memberId, 'for membership', membership.id)
74
+ console.error('Unexpected missing member id ', membership.memberId, 'for membership', membership.id);
76
75
  continue;
77
76
  }
78
77
 
79
78
  // Force price update (required because could have changed - especially for free memberships in combination with deletes)
80
79
  try {
81
- await membership.calculatePrice(member)
82
- } catch (e) {
83
- console.error('Failed to update price for membership. Not charged.', membership.id, e)
80
+ await membership.calculatePrice(member);
81
+ }
82
+ catch (e) {
83
+ console.error('Failed to update price for membership. Not charged.', membership.id, e);
84
84
  continue;
85
85
  }
86
86
 
87
87
  const balanceItem = new BalanceItem();
88
- balanceItem.unitPrice = membership.price
89
- balanceItem.amount = 1
90
- balanceItem.description = Formatter.dateNumber(membership.startDate, true) + " tot " + Formatter.dateNumber(membership.expireDate ?? membership.endDate, true)
88
+ balanceItem.unitPrice = membership.price;
89
+ balanceItem.amount = 1;
90
+ balanceItem.description = Formatter.dateNumber(membership.startDate, true) + ' tot ' + Formatter.dateNumber(membership.expireDate ?? membership.endDate, true);
91
91
  balanceItem.relations = new Map([
92
92
  [
93
- BalanceItemRelationType.Member,
93
+ BalanceItemRelationType.Member,
94
94
  BalanceItemRelation.create({
95
95
  id: member.id,
96
- name: member.details.name
97
- })
96
+ name: member.details.name,
97
+ }),
98
98
  ],
99
99
  [
100
- BalanceItemRelationType.MembershipType,
100
+ BalanceItemRelationType.MembershipType,
101
101
  BalanceItemRelation.create({
102
102
  id: type.id,
103
- name: type.name
104
- })
105
- ]
106
- ])
103
+ name: type.name,
104
+ }),
105
+ ],
106
+ ]);
107
107
 
108
- balanceItem.type = BalanceItemType.PlatformMembership
109
- balanceItem.organizationId = chargeVia
110
- balanceItem.payingOrganizationId = membership.organizationId
108
+ balanceItem.type = BalanceItemType.PlatformMembership;
109
+ balanceItem.organizationId = chargeVia;
110
+ balanceItem.payingOrganizationId = membership.organizationId;
111
111
 
112
112
  await balanceItem.save();
113
113
  membership.balanceItemId = balanceItem.id;
114
114
  membership.maximumFreeAmount = membership.freeAmount;
115
- await membership.save()
115
+ await membership.save();
116
116
 
117
- createdBalanceItems.push(balanceItem)
117
+ createdBalanceItems.push(balanceItem);
118
118
 
119
119
  createdCount += 1;
120
- createdPrice += membership.price
120
+ createdPrice += membership.price;
121
121
  }
122
122
 
123
- await BalanceItem.updateOutstanding(createdBalanceItems)
123
+ await BalanceItem.updateOutstanding(createdBalanceItems);
124
124
 
125
125
  if (memberships.length < chunkSize) {
126
126
  break;
127
127
  }
128
-
128
+
129
129
  const z = lastId;
130
130
  lastId = memberships[memberships.length - 1].id;
131
131
 
132
132
  if (lastId === z) {
133
- throw new Error('Unexpected infinite loop found in MembershipCharger')
133
+ throw new Error('Unexpected infinite loop found in MembershipCharger');
134
134
  }
135
135
  }
136
136
 
137
- console.log('Charged ' + Formatter.integer(createdCount) +' memberships, for a total value of ' + Formatter.price(createdPrice))
137
+ console.log('Charged ' + Formatter.integer(createdCount) + ' memberships, for a total value of ' + Formatter.price(createdPrice));
138
138
  },
139
139
 
140
140
  async updatePrices(organizationId?: string) {
141
- console.log('Update prices...')
142
-
141
+ console.log('Update prices...');
142
+
143
143
  // Loop all
144
- let lastId = "";
144
+ let lastId = '';
145
145
  let createdCount = 0;
146
146
  const chunkSize = 100;
147
147
 
148
- // eslint-disable-next-line no-constant-condition
149
148
  while (true) {
150
149
  const q = MemberPlatformMembership.select()
151
150
  .where('id', SQLWhereSign.Greater, lastId)
@@ -153,58 +152,59 @@ export const MembershipCharger = {
153
152
  .where('deletedAt', null);
154
153
 
155
154
  if (organizationId) {
156
- q.where('organizationId', organizationId)
155
+ q.where('organizationId', organizationId);
157
156
  }
158
-
157
+
159
158
  const memberships = await q
160
159
  .limit(chunkSize)
161
160
  .orderBy(
162
161
  new SQLOrderBy({
163
162
  column: SQL.column('id'),
164
- direction: 'ASC'
165
- })
163
+ direction: 'ASC',
164
+ }),
166
165
  )
167
166
  .fetch();
168
-
167
+
169
168
  if (memberships.length === 0) {
170
169
  break;
171
170
  }
172
171
 
173
- const memberIds = Formatter.uniqueArray(memberships.map(m => m.memberId))
174
- const members = await Member.getByIDs(...memberIds)
172
+ const memberIds = Formatter.uniqueArray(memberships.map(m => m.memberId));
173
+ const members = await Member.getByIDs(...memberIds);
175
174
 
176
175
  for (const membership of memberships) {
177
- const member = members.find(m => m.id === membership.memberId)
176
+ const member = members.find(m => m.id === membership.memberId);
178
177
 
179
178
  if (!member) {
180
- console.error('Unexpected missing member id ', membership.memberId, 'for membership', membership.id)
179
+ console.error('Unexpected missing member id ', membership.memberId, 'for membership', membership.id);
181
180
  continue;
182
181
  }
183
182
 
184
183
  // Force price update (required because could have changed - especially for free memberships in combination with deletes)
185
184
  try {
186
- await membership.calculatePrice(member)
187
- await membership.save()
188
- } catch (e) {
189
- console.error('Failed to update price for membership', membership.id, e)
185
+ await membership.calculatePrice(member);
186
+ await membership.save();
187
+ }
188
+ catch (e) {
189
+ console.error('Failed to update price for membership', membership.id, e);
190
190
  continue;
191
191
  }
192
- console.log('Updated price for membership', membership.id, membership.price)
192
+ console.log('Updated price for membership', membership.id, membership.price);
193
193
  createdCount += 1;
194
194
  }
195
195
 
196
196
  if (memberships.length < chunkSize) {
197
197
  break;
198
198
  }
199
-
199
+
200
200
  const z = lastId;
201
201
  lastId = memberships[memberships.length - 1].id;
202
202
 
203
203
  if (lastId === z) {
204
- throw new Error('Unexpected infinite loop found in MembershipCharger')
204
+ throw new Error('Unexpected infinite loop found in MembershipCharger');
205
205
  }
206
206
  }
207
207
 
208
- console.log('Updated prices of ' + Formatter.integer(createdCount) +' memberships')
209
- }
208
+ console.log('Updated prices of ' + Formatter.integer(createdCount) + ' memberships');
209
+ },
210
210
  };