@stamhoofd/backend 2.7.0 → 2.8.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.
- package/package.json +2 -2
- package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.ts +32 -365
- package/src/endpoints/global/registration/PatchUserMembersEndpoint.ts +8 -11
- package/src/endpoints/global/registration/RegisterMembersEndpoint.ts +100 -85
- package/src/endpoints/global/registration-periods/PatchRegistrationPeriodsEndpoint.ts +2 -3
- package/src/helpers/AdminPermissionChecker.ts +18 -22
- package/src/helpers/AuthenticatedStructures.ts +0 -1
- package/src/helpers/MemberUserSyncer.ts +42 -14
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stamhoofd/backend",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.8.0",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -50,5 +50,5 @@
|
|
|
50
50
|
"postmark": "4.0.2",
|
|
51
51
|
"stripe": "^16.6.0"
|
|
52
52
|
},
|
|
53
|
-
"gitHead": "
|
|
53
|
+
"gitHead": "1e0e1853f8dea6b47718433b2890907042c17ddc"
|
|
54
54
|
}
|
|
@@ -76,15 +76,6 @@ export class PatchOrganizationMembersEndpoint extends Endpoint<Params, Query, Bo
|
|
|
76
76
|
const balanceItemRegistrationIdsPerOrganization: Map<string, string[]> = new Map()
|
|
77
77
|
const updateMembershipMemberIds = new Set<string>()
|
|
78
78
|
|
|
79
|
-
function addBalanceItemRegistrationId(organizationId: string, registrationId: string) {
|
|
80
|
-
const existing = balanceItemRegistrationIdsPerOrganization.get(organizationId);
|
|
81
|
-
if (existing) {
|
|
82
|
-
existing.push(registrationId)
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
balanceItemRegistrationIdsPerOrganization.set(organizationId, [registrationId])
|
|
86
|
-
}
|
|
87
|
-
|
|
88
79
|
// Loop all members one by one
|
|
89
80
|
for (const put of request.body.getPuts()) {
|
|
90
81
|
const struct = put.put
|
|
@@ -118,38 +109,14 @@ export class PatchOrganizationMembersEndpoint extends Endpoint<Params, Query, Bo
|
|
|
118
109
|
}
|
|
119
110
|
}
|
|
120
111
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
statusCode: 400
|
|
130
|
-
})
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// Throw early
|
|
135
|
-
for (const registrationStruct of struct.registrations) {
|
|
136
|
-
const group = await getGroup(registrationStruct.groupId)
|
|
137
|
-
if (!group || group.organizationId !== registrationStruct.organizationId || !await Context.auth.canAccessGroup(group, PermissionLevel.Write)) {
|
|
138
|
-
throw Context.auth.notFoundOrNoAccess("Je hebt niet voldoende rechten om leden toe te voegen in deze groep")
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
const period = await RegistrationPeriod.getByID(group.periodId)
|
|
142
|
-
if (!period || period.locked) {
|
|
143
|
-
throw new SimpleError({
|
|
144
|
-
code: "period_locked",
|
|
145
|
-
message: "Deze inschrijvingsperiode is afgesloten en staat geen wijzigingen meer toe.",
|
|
146
|
-
})
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// Set organization id of member based on registrations
|
|
150
|
-
if (!organization && STAMHOOFD.userMode !== 'platform' && !member.organizationId) {
|
|
151
|
-
member.organizationId = group.organizationId
|
|
152
|
-
}
|
|
112
|
+
// We risk creating a new member without being able to access it manually afterwards
|
|
113
|
+
if ((organization && !await Context.auth.hasFullAccess(organization.id)) || (!organization && !Context.auth.hasPlatformFullAccess())) {
|
|
114
|
+
throw new SimpleError({
|
|
115
|
+
code: "missing_group",
|
|
116
|
+
message: "Missing group",
|
|
117
|
+
human: "Je moet hoofdbeheerder zijn om een lid toe te voegen in het systeem",
|
|
118
|
+
statusCode: 400
|
|
119
|
+
})
|
|
153
120
|
}
|
|
154
121
|
|
|
155
122
|
if (STAMHOOFD.userMode !== 'platform' && !member.organizationId) {
|
|
@@ -167,16 +134,7 @@ export class PatchOrganizationMembersEndpoint extends Endpoint<Params, Query, Bo
|
|
|
167
134
|
if ((STAMHOOFD.environment == "development" || STAMHOOFD.environment == "staging") && organization) {
|
|
168
135
|
if (member.details.firstName.toLocaleLowerCase() == "create" && parseInt(member.details.lastName) > 0) {
|
|
169
136
|
const count = parseInt(member.details.lastName);
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
for (const registrationStruct of struct.registrations) {
|
|
173
|
-
const g = await getGroup(registrationStruct.groupId)
|
|
174
|
-
if (g) {
|
|
175
|
-
group = g
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
await this.createDummyMembers(organization, group, count)
|
|
137
|
+
await this.createDummyMembers(organization, count)
|
|
180
138
|
|
|
181
139
|
// Skip creating this member
|
|
182
140
|
continue;
|
|
@@ -188,20 +146,6 @@ export class PatchOrganizationMembersEndpoint extends Endpoint<Params, Query, Bo
|
|
|
188
146
|
balanceItemMemberIds.push(member.id)
|
|
189
147
|
updateMembershipMemberIds.add(member.id)
|
|
190
148
|
|
|
191
|
-
// Add registrations
|
|
192
|
-
for (const registrationStruct of struct.registrations) {
|
|
193
|
-
const group = await getGroup(registrationStruct.groupId)
|
|
194
|
-
if (!group || group.organizationId !== registrationStruct.organizationId || !await Context.auth.canAccessGroup(group, PermissionLevel.Write)) {
|
|
195
|
-
throw Context.auth.notFoundOrNoAccess("Je hebt niet voldoende rechten om leden toe te voegen in deze groep")
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
const reg = await this.addRegistration(member, registrationStruct, group)
|
|
199
|
-
addBalanceItemRegistrationId(reg.organizationId, reg.id)
|
|
200
|
-
|
|
201
|
-
// Update occupancy at the end of the call
|
|
202
|
-
updateGroups.set(group.id, group)
|
|
203
|
-
}
|
|
204
|
-
|
|
205
149
|
// Auto link users based on data
|
|
206
150
|
await MemberUserSyncer.onChangeMember(member)
|
|
207
151
|
}
|
|
@@ -233,192 +177,6 @@ export class PatchOrganizationMembersEndpoint extends Endpoint<Params, Query, Bo
|
|
|
233
177
|
// Update documents
|
|
234
178
|
await Document.updateForMember(member.id)
|
|
235
179
|
|
|
236
|
-
// Update registrations
|
|
237
|
-
for (const patchRegistration of patch.registrations.getPatches()) {
|
|
238
|
-
const registration = member.registrations.find(r => r.id === patchRegistration.id)
|
|
239
|
-
if (!registration || registration.memberId != member.id || (!await Context.auth.canAccessRegistration(registration, PermissionLevel.Write))) {
|
|
240
|
-
throw new SimpleError({
|
|
241
|
-
code: "permission_denied",
|
|
242
|
-
message: "You don't have permissions to access this endpoint",
|
|
243
|
-
human: "Je hebt geen toegang om deze registratie te wijzigen"
|
|
244
|
-
})
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
let group: Group | null = null
|
|
248
|
-
|
|
249
|
-
console.log('Patch registration', patchRegistration)
|
|
250
|
-
|
|
251
|
-
if (patchRegistration.group) {
|
|
252
|
-
patchRegistration.groupId = patchRegistration.group.id
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
if (patchRegistration.groupId) {
|
|
256
|
-
group = await getGroup(patchRegistration.groupId)
|
|
257
|
-
if (group) {
|
|
258
|
-
// We need to update group occupancy because we moved a member to it
|
|
259
|
-
updateGroups.set(group.id, group)
|
|
260
|
-
}
|
|
261
|
-
const oldGroup = await getGroup(registration.groupId)
|
|
262
|
-
if (oldGroup) {
|
|
263
|
-
// We need to update this group occupancy because we moved one member away from it
|
|
264
|
-
updateGroups.set(oldGroup.id, oldGroup)
|
|
265
|
-
}
|
|
266
|
-
} else {
|
|
267
|
-
group = await getGroup(registration.groupId)
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
if (!group || group.organizationId !== (patchRegistration.organizationId ?? registration.organizationId)) {
|
|
271
|
-
throw new SimpleError({
|
|
272
|
-
code: "invalid_field",
|
|
273
|
-
message: "Group doesn't exist",
|
|
274
|
-
human: "De groep naarwaar je dit lid wilt verplaatsen bestaat niet",
|
|
275
|
-
field: "groupId"
|
|
276
|
-
})
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
if (!await Context.auth.canAccessGroup(group, PermissionLevel.Write)) {
|
|
280
|
-
throw Context.auth.error("Je hebt niet voldoende rechten om leden te verplaatsen naar deze groep")
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
if (patchRegistration.cycle && patchRegistration.cycle > group.cycle) {
|
|
284
|
-
throw new SimpleError({
|
|
285
|
-
code: "invalid_field",
|
|
286
|
-
message: "Invalid cycle",
|
|
287
|
-
human: "Je kan een lid niet inschrijven voor een groep die nog moet starten",
|
|
288
|
-
field: "cycle"
|
|
289
|
-
})
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
const period = await RegistrationPeriod.getByID(group.periodId)
|
|
293
|
-
if (!period || period.locked) {
|
|
294
|
-
throw new SimpleError({
|
|
295
|
-
code: "period_locked",
|
|
296
|
-
message: "Deze inschrijvingsperiode is afgesloten en staat geen wijzigingen meer toe.",
|
|
297
|
-
})
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
// TODO: allow group changes
|
|
301
|
-
registration.waitingList = patchRegistration.waitingList ?? registration.waitingList
|
|
302
|
-
|
|
303
|
-
if (!registration.waitingList && registration.registeredAt === null) {
|
|
304
|
-
registration.registeredAt = new Date()
|
|
305
|
-
}
|
|
306
|
-
registration.canRegister = patchRegistration.canRegister ?? registration.canRegister
|
|
307
|
-
if (!registration.waitingList) {
|
|
308
|
-
registration.canRegister = false
|
|
309
|
-
}
|
|
310
|
-
registration.cycle = patchRegistration.cycle ?? registration.cycle
|
|
311
|
-
registration.groupId = patchRegistration.groupId ?? registration.groupId
|
|
312
|
-
registration.group = group
|
|
313
|
-
registration.organizationId = patchRegistration.organizationId ?? registration.organizationId
|
|
314
|
-
|
|
315
|
-
// Check if we should create a placeholder payment?
|
|
316
|
-
|
|
317
|
-
if (patchRegistration.cycle !== undefined || patchRegistration.waitingList !== undefined || patchRegistration.canRegister !== undefined) {
|
|
318
|
-
// We need to update occupancy (because cycle / waitlist change)
|
|
319
|
-
updateGroups.set(group.id, group)
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
if (patchRegistration.price) {
|
|
323
|
-
// Create balance item
|
|
324
|
-
const balanceItem = new BalanceItem();
|
|
325
|
-
balanceItem.registrationId = registration.id;
|
|
326
|
-
balanceItem.price = patchRegistration.price
|
|
327
|
-
balanceItem.description = group ? `Inschrijving ${group.settings.name}` : `Inschrijving`
|
|
328
|
-
balanceItem.pricePaid = patchRegistration.pricePaid ?? 0
|
|
329
|
-
balanceItem.memberId = registration.memberId;
|
|
330
|
-
balanceItem.userId = member.users[0]?.id ?? null
|
|
331
|
-
balanceItem.organizationId = group.organizationId
|
|
332
|
-
balanceItem.status = BalanceItemStatus.Pending;
|
|
333
|
-
await balanceItem.save();
|
|
334
|
-
|
|
335
|
-
addBalanceItemRegistrationId(registration.organizationId, registration.id)
|
|
336
|
-
balanceItemMemberIds.push(member.id)
|
|
337
|
-
|
|
338
|
-
if (balanceItem.pricePaid > 0) {
|
|
339
|
-
// Create an Unknown payment and attach it to the balance item
|
|
340
|
-
const payment = new Payment();
|
|
341
|
-
payment.userId = member.users[0]?.id ?? null
|
|
342
|
-
payment.organizationId = member.organizationId
|
|
343
|
-
payment.method = PaymentMethod.Unknown
|
|
344
|
-
payment.status = PaymentStatus.Succeeded
|
|
345
|
-
payment.price = balanceItem.pricePaid;
|
|
346
|
-
payment.paidAt = new Date()
|
|
347
|
-
payment.provider = null
|
|
348
|
-
await payment.save()
|
|
349
|
-
|
|
350
|
-
const balanceItemPayment = new BalanceItemPayment()
|
|
351
|
-
balanceItemPayment.balanceItemId = balanceItem.id;
|
|
352
|
-
balanceItemPayment.paymentId = payment.id;
|
|
353
|
-
balanceItemPayment.organizationId = group.organizationId
|
|
354
|
-
balanceItemPayment.price = payment.price;
|
|
355
|
-
await balanceItemPayment.save();
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
await registration.save()
|
|
360
|
-
updateMembershipMemberIds.add(member.id)
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
for (const deleteId of patch.registrations.getDeletes()) {
|
|
364
|
-
const registration = member.registrations.find(r => r.id === deleteId)
|
|
365
|
-
if (!registration || registration.memberId != member.id) {
|
|
366
|
-
throw new SimpleError({
|
|
367
|
-
code: "permission_denied",
|
|
368
|
-
message: "You don't have permissions to access this endpoint",
|
|
369
|
-
human: "Je hebt geen toegang om deze registratie te wijzigen"
|
|
370
|
-
})
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
if (!await Context.auth.canAccessRegistration(registration, PermissionLevel.Write)) {
|
|
374
|
-
throw Context.auth.error("Je hebt niet voldoende rechten om deze inschrijving te verwijderen")
|
|
375
|
-
}
|
|
376
|
-
const oldGroup = await getGroup(registration.groupId)
|
|
377
|
-
const period = oldGroup && await RegistrationPeriod.getByID(oldGroup.periodId)
|
|
378
|
-
if (!period || period.locked) {
|
|
379
|
-
throw new SimpleError({
|
|
380
|
-
code: "period_locked",
|
|
381
|
-
message: "Deze inschrijvingsperiode is afgesloten en staat geen wijzigingen meer toe.",
|
|
382
|
-
})
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
balanceItemMemberIds.push(member.id)
|
|
386
|
-
updateMembershipMemberIds.add(member.id)
|
|
387
|
-
await BalanceItem.deleteForDeletedRegistration(registration.id)
|
|
388
|
-
await registration.delete()
|
|
389
|
-
member.registrations = member.registrations.filter(r => r.id !== deleteId)
|
|
390
|
-
|
|
391
|
-
if (oldGroup) {
|
|
392
|
-
// We need to update this group occupancy because we moved one member away from it
|
|
393
|
-
updateGroups.set(oldGroup.id, oldGroup)
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
// Add registrations
|
|
398
|
-
for (const registrationStruct of patch.registrations.getPuts()) {
|
|
399
|
-
const struct = registrationStruct.put
|
|
400
|
-
const group = await getGroup(struct.groupId)
|
|
401
|
-
|
|
402
|
-
if (!group || group.organizationId !== struct.organizationId || !await Context.auth.canAccessGroup(group, PermissionLevel.Write)) {
|
|
403
|
-
throw Context.auth.error("Je hebt niet voldoende rechten om inschrijvingen in deze groep te maken")
|
|
404
|
-
}
|
|
405
|
-
const period = await RegistrationPeriod.getByID(group.periodId)
|
|
406
|
-
if (!period || period.locked) {
|
|
407
|
-
throw new SimpleError({
|
|
408
|
-
code: "period_locked",
|
|
409
|
-
message: "Deze inschrijvingsperiode is afgesloten en staat geen wijzigingen meer toe.",
|
|
410
|
-
})
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
const reg = await this.addRegistration(member, struct, group)
|
|
414
|
-
balanceItemMemberIds.push(member.id)
|
|
415
|
-
updateMembershipMemberIds.add(member.id)
|
|
416
|
-
addBalanceItemRegistrationId(reg.organizationId, reg.id)
|
|
417
|
-
|
|
418
|
-
// We need to update this group occupancy because we moved one member away from it
|
|
419
|
-
updateGroups.set(group.id, group)
|
|
420
|
-
}
|
|
421
|
-
|
|
422
180
|
// Update responsibilities
|
|
423
181
|
for (const patchResponsibility of patch.responsibilities.getPatches()) {
|
|
424
182
|
if (!Context.auth.hasPlatformFullAccess() && !(organization && await Context.auth.hasFullAccess(organization.id))) {
|
|
@@ -570,6 +328,26 @@ export class PatchOrganizationMembersEndpoint extends Endpoint<Params, Query, Bo
|
|
|
570
328
|
// Auto link users based on data
|
|
571
329
|
await MemberUserSyncer.onChangeMember(member)
|
|
572
330
|
|
|
331
|
+
// Allow to remove access for certain users
|
|
332
|
+
for (const id of patch.users.getDeletes()) {
|
|
333
|
+
const user = member.users.find(u => u.id === id)
|
|
334
|
+
if (!user) {
|
|
335
|
+
// Ignore silently
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
if (MemberUserSyncer.doesEmailHaveAccess(member.details, user.email)) {
|
|
340
|
+
throw new SimpleError({
|
|
341
|
+
code: "invalid_field",
|
|
342
|
+
message: "Invalid email",
|
|
343
|
+
human: "Je kan een account niet de toegang ontzetten tot een lid als het e-mailadres nog steeds is opgeslagen als onderdeel van de gegevens van dat lid. Verwijder eerst het e-mailadres uit de gegevens van het lid en ontkoppel daarna het account."
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// Remove access
|
|
348
|
+
await MemberUserSyncer.unlinkUser(user, member)
|
|
349
|
+
}
|
|
350
|
+
|
|
573
351
|
// Add platform memberships
|
|
574
352
|
for (const {put} of patch.platformMemberships.getPuts()) {
|
|
575
353
|
if (put.periodId !== platform.periodId) {
|
|
@@ -745,120 +523,9 @@ export class PatchOrganizationMembersEndpoint extends Endpoint<Params, Query, Bo
|
|
|
745
523
|
}
|
|
746
524
|
}
|
|
747
525
|
|
|
748
|
-
async
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
const existings = await Registration.where({
|
|
752
|
-
memberId: member.id,
|
|
753
|
-
groupId: registrationStruct.groupId,
|
|
754
|
-
cycle: registrationStruct.cycle
|
|
755
|
-
}, { limit: 1 })
|
|
756
|
-
const existing = existings.length > 0 ? existings[0] : null
|
|
757
|
-
|
|
758
|
-
// If the existing is invalid, delete it.
|
|
759
|
-
if (existing && !existing.registeredAt && !existing.waitingList) {
|
|
760
|
-
console.log('Deleting invalid registration', existing.id)
|
|
761
|
-
await existing.delete()
|
|
762
|
-
} else if (existing) {
|
|
763
|
-
throw new SimpleError({
|
|
764
|
-
code: "invalid_field",
|
|
765
|
-
message: "Registration already exists",
|
|
766
|
-
human: existing.waitingList ? "Dit lid staat al op de wachtlijst voor deze groep" : "Dit lid is al ingeschreven voor deze groep",
|
|
767
|
-
field: "groupId"
|
|
768
|
-
});
|
|
769
|
-
}
|
|
770
|
-
|
|
771
|
-
if (!group) {
|
|
772
|
-
throw new SimpleError({
|
|
773
|
-
code: 'invalid_field',
|
|
774
|
-
field: 'groupId',
|
|
775
|
-
message: 'Invalid groupId',
|
|
776
|
-
human: 'Deze inschrijvingsgroep is ongeldig'
|
|
777
|
-
})
|
|
778
|
-
}
|
|
779
|
-
|
|
780
|
-
const registration = new Registration()
|
|
781
|
-
registration.groupId = registrationStruct.groupId
|
|
782
|
-
registration.organizationId = group.organizationId
|
|
783
|
-
registration.periodId = group.periodId
|
|
784
|
-
registration.cycle = registrationStruct.cycle
|
|
785
|
-
registration.memberId = member.id
|
|
786
|
-
registration.registeredAt = registrationStruct.registeredAt
|
|
787
|
-
registration.waitingList = registrationStruct.waitingList
|
|
788
|
-
registration.createdAt = registrationStruct.createdAt ?? new Date()
|
|
789
|
-
|
|
790
|
-
if (registration.waitingList) {
|
|
791
|
-
registration.registeredAt = null
|
|
792
|
-
}
|
|
793
|
-
registration.canRegister = registrationStruct.canRegister
|
|
794
|
-
|
|
795
|
-
if (!registration.waitingList) {
|
|
796
|
-
registration.canRegister = false
|
|
797
|
-
}
|
|
798
|
-
registration.deactivatedAt = registrationStruct.deactivatedAt
|
|
799
|
-
|
|
800
|
-
await registration.save()
|
|
801
|
-
member.registrations.push(registration.setRelation(Registration.group, group))
|
|
802
|
-
|
|
803
|
-
if (registrationStruct.price) {
|
|
804
|
-
// Create balance item
|
|
805
|
-
const balanceItem = new BalanceItem();
|
|
806
|
-
balanceItem.registrationId = registration.id;
|
|
807
|
-
balanceItem.price = registrationStruct.price
|
|
808
|
-
balanceItem.description = group ? `Inschrijving ${group.settings.name}` : `Inschrijving`
|
|
809
|
-
balanceItem.pricePaid = registrationStruct.pricePaid ?? 0
|
|
810
|
-
balanceItem.memberId = registration.memberId;
|
|
811
|
-
balanceItem.userId = member.users[0]?.id ?? null
|
|
812
|
-
balanceItem.organizationId = group.organizationId
|
|
813
|
-
balanceItem.status = BalanceItemStatus.Pending;
|
|
814
|
-
await balanceItem.save();
|
|
815
|
-
|
|
816
|
-
if (balanceItem.pricePaid > 0) {
|
|
817
|
-
// Create an Unknown payment and attach it to the balance item
|
|
818
|
-
const payment = new Payment();
|
|
819
|
-
payment.userId = member.users[0]?.id ?? null
|
|
820
|
-
payment.organizationId = member.organizationId
|
|
821
|
-
payment.method = PaymentMethod.Unknown
|
|
822
|
-
payment.status = PaymentStatus.Succeeded
|
|
823
|
-
payment.price = balanceItem.pricePaid;
|
|
824
|
-
payment.paidAt = new Date()
|
|
825
|
-
payment.provider = null
|
|
826
|
-
await payment.save()
|
|
827
|
-
|
|
828
|
-
const balanceItemPayment = new BalanceItemPayment()
|
|
829
|
-
balanceItemPayment.balanceItemId = balanceItem.id;
|
|
830
|
-
balanceItemPayment.paymentId = payment.id;
|
|
831
|
-
balanceItemPayment.organizationId = group.organizationId
|
|
832
|
-
balanceItemPayment.price = payment.price;
|
|
833
|
-
await balanceItemPayment.save();
|
|
834
|
-
}
|
|
835
|
-
}
|
|
836
|
-
|
|
837
|
-
return registration
|
|
838
|
-
}
|
|
839
|
-
|
|
840
|
-
async createDummyMembers(organization: Organization, group: Group, count: number) {
|
|
841
|
-
const members = await new MemberFactory({
|
|
842
|
-
organization,
|
|
843
|
-
minAge: group.settings.minAge ?? undefined,
|
|
844
|
-
maxAge: group.settings.maxAge ?? undefined
|
|
526
|
+
async createDummyMembers(organization: Organization, count: number) {
|
|
527
|
+
await new MemberFactory({
|
|
528
|
+
organization
|
|
845
529
|
}).createMultiple(count)
|
|
846
|
-
|
|
847
|
-
for (const m of members) {
|
|
848
|
-
const member = m.setManyRelation(Member.registrations as unknown as OneToManyRelation<"registrations", Member, Registration>, []).setManyRelation(Member.users, [])
|
|
849
|
-
const d = new Date(new Date().getTime() - Math.random() * 60 * 1000 * 60 * 24 * 60)
|
|
850
|
-
|
|
851
|
-
// Create a registration for this member for thisg roup
|
|
852
|
-
const registration = new Registration()
|
|
853
|
-
registration.organizationId = organization.id
|
|
854
|
-
registration.memberId = member.id
|
|
855
|
-
registration.groupId = group.id
|
|
856
|
-
registration.periodId = group.periodId
|
|
857
|
-
registration.cycle = group.cycle
|
|
858
|
-
registration.registeredAt = d
|
|
859
|
-
|
|
860
|
-
member.registrations.push(registration)
|
|
861
|
-
await registration.save()
|
|
862
|
-
}
|
|
863
530
|
}
|
|
864
531
|
}
|
|
@@ -4,10 +4,10 @@ import { SimpleError } from '@simonbackx/simple-errors';
|
|
|
4
4
|
import { Document, Member } from '@stamhoofd/models';
|
|
5
5
|
import { MemberWithRegistrationsBlob, MembersBlob } from "@stamhoofd/structures";
|
|
6
6
|
|
|
7
|
-
import { Context } from '../../../helpers/Context';
|
|
8
|
-
import { PatchOrganizationMembersEndpoint } from '../../global/members/PatchOrganizationMembersEndpoint';
|
|
9
7
|
import { AuthenticatedStructures } from '../../../helpers/AuthenticatedStructures';
|
|
8
|
+
import { Context } from '../../../helpers/Context';
|
|
10
9
|
import { MemberUserSyncer } from '../../../helpers/MemberUserSyncer';
|
|
10
|
+
import { PatchOrganizationMembersEndpoint } from '../../global/members/PatchOrganizationMembersEndpoint';
|
|
11
11
|
type Params = Record<string, never>;
|
|
12
12
|
type Query = undefined;
|
|
13
13
|
type Body = PatchableArrayAutoEncoder<MemberWithRegistrationsBlob>
|
|
@@ -48,15 +48,6 @@ export class PatchUserMembersEndpoint extends Endpoint<Params, Query, Body, Resp
|
|
|
48
48
|
struct.details.cleanData()
|
|
49
49
|
member.details = struct.details
|
|
50
50
|
|
|
51
|
-
if (!struct.details) {
|
|
52
|
-
throw new SimpleError({
|
|
53
|
-
code: "invalid_data",
|
|
54
|
-
message: "No details provided",
|
|
55
|
-
human: "Opgelet! Je gebruikt een oudere versie van de inschrijvingspagina die niet langer wordt ondersteund. Herlaad de website grondig en wis je browser cache.",
|
|
56
|
-
field: "details"
|
|
57
|
-
})
|
|
58
|
-
}
|
|
59
|
-
|
|
60
51
|
// Check for duplicates and prevent creating a duplicate member by a user
|
|
61
52
|
const duplicate = await PatchOrganizationMembersEndpoint.checkDuplicate(member);
|
|
62
53
|
if (duplicate) {
|
|
@@ -85,6 +76,12 @@ export class PatchUserMembersEndpoint extends Endpoint<Params, Query, Body, Resp
|
|
|
85
76
|
if (updatedMember) {
|
|
86
77
|
// Make sure we also give access to other parents
|
|
87
78
|
await MemberUserSyncer.onChangeMember(updatedMember)
|
|
79
|
+
|
|
80
|
+
if (!updatedMember.users.find(u => u.id === user.id)) {
|
|
81
|
+
// Also link the user to the member if the email address is missing in the details
|
|
82
|
+
await MemberUserSyncer.linkUser(user.email, updatedMember, true)
|
|
83
|
+
}
|
|
84
|
+
|
|
88
85
|
await Document.updateForMember(updatedMember.id)
|
|
89
86
|
}
|
|
90
87
|
}
|
|
@@ -98,8 +98,11 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
|
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
+
const deleteRegistrationIds = request.body.cart.deleteRegistrationIds
|
|
102
|
+
const deleteRegistrationModels = (deleteRegistrationIds.length ? (await Registration.getByIDs(...deleteRegistrationIds)) : []).filter(r => r.organizationId === organization.id)
|
|
103
|
+
|
|
101
104
|
const memberIds = Formatter.uniqueArray(
|
|
102
|
-
[...request.body.cart.items.map(i => i.memberId), ...
|
|
105
|
+
[...request.body.cart.items.map(i => i.memberId), ...deleteRegistrationModels.map(i => i.memberId)]
|
|
103
106
|
)
|
|
104
107
|
const members = await Member.getBlobByIds(...memberIds)
|
|
105
108
|
const groupIds = Formatter.uniqueArray(request.body.cart.items.map(i => i.groupId))
|
|
@@ -174,23 +177,23 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
|
|
|
174
177
|
|
|
175
178
|
// Validate balance items (can only happen serverside)
|
|
176
179
|
const balanceItemIds = request.body.cart.balanceItems.map(i => i.item.id)
|
|
177
|
-
let
|
|
178
|
-
let
|
|
180
|
+
let memberBalanceItemsStructs: MemberBalanceItem[] = []
|
|
181
|
+
let balanceItemsModels: BalanceItem[] = []
|
|
179
182
|
if (balanceItemIds.length > 0) {
|
|
180
|
-
|
|
181
|
-
if (
|
|
183
|
+
balanceItemsModels = await BalanceItem.where({ id: { sign:'IN', value: balanceItemIds }, organizationId: organization.id })
|
|
184
|
+
if (balanceItemsModels.length != balanceItemIds.length) {
|
|
182
185
|
throw new SimpleError({
|
|
183
186
|
code: "invalid_data",
|
|
184
187
|
message: "Oeps, één of meerdere openstaande bedragen in jouw winkelmandje zijn aangepast. Herlaad de pagina en probeer opnieuw."
|
|
185
188
|
})
|
|
186
189
|
}
|
|
187
|
-
|
|
190
|
+
memberBalanceItemsStructs = await BalanceItem.getMemberStructure(balanceItemsModels)
|
|
188
191
|
}
|
|
189
192
|
|
|
190
193
|
console.log('isAdminFromSameOrganization', checkout.isAdminFromSameOrganization)
|
|
191
194
|
|
|
192
195
|
// Validate the cart
|
|
193
|
-
checkout.validate({memberBalanceItems})
|
|
196
|
+
checkout.validate({memberBalanceItems: memberBalanceItemsStructs})
|
|
194
197
|
|
|
195
198
|
// Recalculate the price
|
|
196
199
|
checkout.updatePrices()
|
|
@@ -233,34 +236,37 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
|
|
|
233
236
|
|
|
234
237
|
// Check if this member is already registered in this group?
|
|
235
238
|
const existingRegistrations = await Registration.where({ memberId: member.id, groupId: item.groupId, cycle: group.cycle })
|
|
236
|
-
let registration: RegistrationWithMemberAndGroup | undefined = undefined;
|
|
237
239
|
|
|
238
240
|
for (const existingRegistration of existingRegistrations) {
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
241
|
+
if (item.replaceRegistrations.some(r => r.id === existingRegistration.id)) {
|
|
242
|
+
// Safe
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if (checkout.cart.deleteRegistrations.some(r => r.id === existingRegistration.id)) {
|
|
247
|
+
// Safe
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
243
250
|
|
|
244
251
|
if (existingRegistration.registeredAt !== null && existingRegistration.deactivatedAt === null) {
|
|
245
252
|
throw new SimpleError({
|
|
246
253
|
code: "already_registered",
|
|
247
|
-
message:
|
|
254
|
+
message: `${member.firstName} is al ingeschreven voor ${group.settings.name}. Mogelijks heb je meerdere keren proberen in te schrijven en is het intussen wel gelukt. Herlaad de pagina best even om zeker te zijn.`
|
|
248
255
|
})
|
|
249
256
|
}
|
|
250
257
|
}
|
|
251
258
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
registration.periodId = group.periodId
|
|
258
|
-
}
|
|
259
|
+
const registration = new Registration()
|
|
260
|
+
.setRelation(registrationMemberRelation, member as Member)
|
|
261
|
+
.setRelation(Registration.group, group)
|
|
262
|
+
registration.organizationId = organization.id
|
|
263
|
+
registration.periodId = group.periodId
|
|
259
264
|
|
|
260
265
|
registration.memberId = member.id
|
|
261
266
|
registration.groupId = group.id
|
|
262
|
-
registration.
|
|
263
|
-
registration.
|
|
267
|
+
registration.price = 0 // will get filled by balance items themselves
|
|
268
|
+
registration.groupPrice = item.groupPrice;
|
|
269
|
+
registration.options = item.options
|
|
264
270
|
|
|
265
271
|
payRegistrations.push({
|
|
266
272
|
registration,
|
|
@@ -305,8 +311,59 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
|
|
|
305
311
|
|
|
306
312
|
console.log('Registering members using whoWillPayNow', whoWillPayNow, checkout.paymentMethod, totalPrice)
|
|
307
313
|
|
|
308
|
-
const
|
|
309
|
-
const shouldMarkValid = whoWillPayNow === 'nobody' || checkout.paymentMethod === PaymentMethod.Transfer || checkout.paymentMethod === PaymentMethod.PointOfSale
|
|
314
|
+
const createdBalanceItems: BalanceItem[] = []
|
|
315
|
+
const shouldMarkValid = whoWillPayNow === 'nobody' || checkout.paymentMethod === PaymentMethod.Transfer || checkout.paymentMethod === PaymentMethod.PointOfSale || checkout.paymentMethod === PaymentMethod.Unknown
|
|
316
|
+
|
|
317
|
+
// Create negative balance items
|
|
318
|
+
for (const registrationStruct of [...checkout.cart.deleteRegistrations, ...checkout.cart.items.flatMap(i => i.replaceRegistrations)]) {
|
|
319
|
+
if (whoWillPayNow !== 'nobody') {
|
|
320
|
+
// this also fixes the issue that we cannot delete the registration right away if we would need to wait for a payment
|
|
321
|
+
throw new SimpleError({
|
|
322
|
+
code: "forbidden",
|
|
323
|
+
message: "Permission denied: you are not allowed to delete registrations",
|
|
324
|
+
human: "Oeps, je hebt geen toestemming om inschrijvingen te verwijderen.",
|
|
325
|
+
statusCode: 403
|
|
326
|
+
})
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const existingRegistration = await Registration.getByID(registrationStruct.id)
|
|
330
|
+
if (!existingRegistration || existingRegistration.organizationId !== organization.id) {
|
|
331
|
+
throw new SimpleError({
|
|
332
|
+
code: "invalid_data",
|
|
333
|
+
message: "Oeps, één of meerdere inschrijvingen die je probeert te verwijderen lijken niet meer te bestaan. Herlaad de pagina en probeer opnieuw."
|
|
334
|
+
})
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (!await Context.auth.canAccessRegistration(existingRegistration, PermissionLevel.Write)) {
|
|
338
|
+
throw new SimpleError({
|
|
339
|
+
code: "forbidden",
|
|
340
|
+
message: "Je hebt geen toegaansrechten om deze inschrijving te verwijderen.",
|
|
341
|
+
statusCode: 403
|
|
342
|
+
})
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
if (existingRegistration.deactivatedAt || !existingRegistration.registeredAt) {
|
|
346
|
+
throw new SimpleError({
|
|
347
|
+
code: "invalid_data",
|
|
348
|
+
message: "Oeps, één of meerdere inschrijvingen die je probeert te verwijderen was al verwijderd. Herlaad de pagina en probeer opnieuw."
|
|
349
|
+
})
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// We can alter right away since whoWillPayNow is nobody, and shouldMarkValid will always be true
|
|
353
|
+
// Find all balance items of this registration and set them to zero
|
|
354
|
+
await BalanceItem.deleteForDeletedRegistration(existingRegistration.id)
|
|
355
|
+
|
|
356
|
+
// Clear the registration
|
|
357
|
+
await existingRegistration.deactivate()
|
|
358
|
+
|
|
359
|
+
const group = groups.find(g => g.id === existingRegistration.groupId)
|
|
360
|
+
if (!group) {
|
|
361
|
+
const g = await Group.getByID(existingRegistration.groupId)
|
|
362
|
+
if (g) {
|
|
363
|
+
groups.push(g)
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
310
367
|
|
|
311
368
|
// Save registrations and add extra data if needed
|
|
312
369
|
for (const bundle of payRegistrations) {
|
|
@@ -366,7 +423,7 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
|
|
|
366
423
|
balanceItem2.status = shouldMarkValid ? BalanceItemStatus.Pending : BalanceItemStatus.Hidden
|
|
367
424
|
await balanceItem2.save();
|
|
368
425
|
|
|
369
|
-
// do not add to
|
|
426
|
+
// do not add to createdBalanceItems array because we don't want to add this to the payment if we create a payment
|
|
370
427
|
} else {
|
|
371
428
|
balanceItem.memberId = registration.memberId;
|
|
372
429
|
balanceItem.userId = user.id
|
|
@@ -379,7 +436,7 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
|
|
|
379
436
|
balanceItem.dependingBalanceItemId = balanceItem2?.id ?? null
|
|
380
437
|
|
|
381
438
|
await balanceItem.save();
|
|
382
|
-
|
|
439
|
+
createdBalanceItems.push(balanceItem)
|
|
383
440
|
}
|
|
384
441
|
|
|
385
442
|
const oldestMember = members.slice().sort((a, b) => b.details.defaultAge - a.details.defaultAge)[0]
|
|
@@ -399,7 +456,7 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
|
|
|
399
456
|
}
|
|
400
457
|
balanceItem.status = shouldMarkValid ? BalanceItemStatus.Pending : BalanceItemStatus.Hidden
|
|
401
458
|
await balanceItem.save();
|
|
402
|
-
|
|
459
|
+
createdBalanceItems.push(balanceItem)
|
|
403
460
|
}
|
|
404
461
|
|
|
405
462
|
if (checkout.administrationFee && whoWillPayNow !== 'nobody') {
|
|
@@ -423,62 +480,15 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
|
|
|
423
480
|
balanceItem.status = shouldMarkValid ? BalanceItemStatus.Pending : BalanceItemStatus.Hidden
|
|
424
481
|
await balanceItem.save();
|
|
425
482
|
|
|
426
|
-
|
|
483
|
+
createdBalanceItems.push(balanceItem);
|
|
427
484
|
}
|
|
428
485
|
|
|
429
486
|
if (checkout.cart.balanceItems.length && whoWillPayNow === 'nobody') {
|
|
430
|
-
throw new
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
if (whoWillPayNow !== 'nobody') {
|
|
436
|
-
// this also fixes the issue that we cannot delete the registration right away if we would need to wait for a payment
|
|
437
|
-
throw new SimpleError({
|
|
438
|
-
code: "forbidden",
|
|
439
|
-
message: "Permission denied: you are not allowed to delete registrations",
|
|
440
|
-
human: "Oeps, je hebt geen toestemming om inschrijvingen te verwijderen.",
|
|
441
|
-
statusCode: 403
|
|
442
|
-
})
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
const existingRegistration = await Registration.getByID(registrationStruct.id)
|
|
446
|
-
if (!existingRegistration || existingRegistration.organizationId !== organization.id) {
|
|
447
|
-
throw new SimpleError({
|
|
448
|
-
code: "invalid_data",
|
|
449
|
-
message: "Oeps, één of meerdere inschrijvingen die je probeert te verwijderen lijken niet meer te bestaan. Herlaad de pagina en probeer opnieuw."
|
|
450
|
-
})
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
if (!await Context.auth.canAccessRegistration(existingRegistration, PermissionLevel.Write)) {
|
|
454
|
-
throw new SimpleError({
|
|
455
|
-
code: "forbidden",
|
|
456
|
-
message: "Je hebt geen toegaansrechten om deze inschrijving te verwijderen.",
|
|
457
|
-
statusCode: 403
|
|
458
|
-
})
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
if (existingRegistration.deactivatedAt || !existingRegistration.registeredAt) {
|
|
462
|
-
throw new SimpleError({
|
|
463
|
-
code: "invalid_data",
|
|
464
|
-
message: "Oeps, één of meerdere inschrijvingen die je probeert te verwijderen was al verwijderd. Herlaad de pagina en probeer opnieuw."
|
|
465
|
-
})
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
// We can alter right away since whoWillPayNow is nobody, and shouldMarkValid will always be true
|
|
469
|
-
// Find all balance items of this registration and set them to zero
|
|
470
|
-
await BalanceItem.deleteForDeletedRegistration(existingRegistration.id)
|
|
471
|
-
|
|
472
|
-
// Clear the registration
|
|
473
|
-
await existingRegistration.deactivate()
|
|
474
|
-
|
|
475
|
-
const group = groups.find(g => g.id === existingRegistration.groupId)
|
|
476
|
-
if (!group) {
|
|
477
|
-
const g = await Group.getByID(existingRegistration.groupId)
|
|
478
|
-
if (g) {
|
|
479
|
-
groups.push(g)
|
|
480
|
-
}
|
|
481
|
-
}
|
|
487
|
+
throw new SimpleError({
|
|
488
|
+
code: 'invalid_data',
|
|
489
|
+
message: 'Not possible to pay balance items as the organization',
|
|
490
|
+
statusCode: 400
|
|
491
|
+
})
|
|
482
492
|
}
|
|
483
493
|
|
|
484
494
|
let paymentUrl: string | null = null
|
|
@@ -487,19 +497,21 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
|
|
|
487
497
|
if (whoWillPayNow !== 'nobody') {
|
|
488
498
|
const mappedBalanceItems = new Map<BalanceItem, number>()
|
|
489
499
|
|
|
490
|
-
for (const item of
|
|
500
|
+
for (const item of createdBalanceItems) {
|
|
491
501
|
mappedBalanceItems.set(item, item.price)
|
|
492
502
|
}
|
|
493
503
|
|
|
494
504
|
for (const item of checkout.cart.balanceItems) {
|
|
495
|
-
const balanceItem =
|
|
505
|
+
const balanceItem = balanceItemsModels.find(i => i.id === item.item.id)
|
|
496
506
|
if (!balanceItem) {
|
|
497
507
|
throw new Error('Balance item not found')
|
|
498
508
|
}
|
|
499
509
|
mappedBalanceItems.set(balanceItem, item.price)
|
|
500
|
-
|
|
510
|
+
createdBalanceItems.push(balanceItem)
|
|
501
511
|
}
|
|
502
512
|
|
|
513
|
+
// Make sure every price is accurate before creating a payment
|
|
514
|
+
await BalanceItem.updateOutstanding(createdBalanceItems, organization.id)
|
|
503
515
|
const response = await this.createPayment({
|
|
504
516
|
balanceItems: mappedBalanceItems,
|
|
505
517
|
organization,
|
|
@@ -512,9 +524,10 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
|
|
|
512
524
|
paymentUrl = response.paymentUrl
|
|
513
525
|
payment = response.payment
|
|
514
526
|
}
|
|
527
|
+
} else {
|
|
528
|
+
await BalanceItem.updateOutstanding(createdBalanceItems, organization.id)
|
|
515
529
|
}
|
|
516
530
|
|
|
517
|
-
await BalanceItem.updateOutstanding(items, organization.id)
|
|
518
531
|
|
|
519
532
|
// Update occupancy
|
|
520
533
|
for (const group of groups) {
|
|
@@ -524,9 +537,11 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
|
|
|
524
537
|
}
|
|
525
538
|
}
|
|
526
539
|
|
|
540
|
+
const updatedMembers = await Member.getBlobByIds(...memberIds)
|
|
541
|
+
|
|
527
542
|
return new Response(RegisterResponse.create({
|
|
528
543
|
payment: payment ? PaymentStruct.create(payment) : null,
|
|
529
|
-
members: await AuthenticatedStructures.membersBlob(
|
|
544
|
+
members: await AuthenticatedStructures.membersBlob(updatedMembers),
|
|
530
545
|
registrations: registrations.map(r => Member.getRegistrationWithMemberStructure(r)),
|
|
531
546
|
paymentUrl
|
|
532
547
|
}));
|
|
@@ -2,10 +2,9 @@ import { ConvertArrayToPatchableArray, Decoder, PatchableArrayAutoEncoder, Patch
|
|
|
2
2
|
import { DecodedRequest, Endpoint, Request, Response } from "@simonbackx/simple-endpoints";
|
|
3
3
|
import { RegistrationPeriod as RegistrationPeriodStruct } from "@stamhoofd/structures";
|
|
4
4
|
|
|
5
|
-
import { AuthenticatedStructures } from '../../../helpers/AuthenticatedStructures';
|
|
6
|
-
import { Context } from '../../../helpers/Context';
|
|
7
|
-
import { Platform, RegistrationPeriod } from '@stamhoofd/models';
|
|
8
5
|
import { SimpleError } from '@simonbackx/simple-errors';
|
|
6
|
+
import { Platform, RegistrationPeriod } from '@stamhoofd/models';
|
|
7
|
+
import { Context } from '../../../helpers/Context';
|
|
9
8
|
|
|
10
9
|
type Params = Record<string, never>;
|
|
11
10
|
type Query = undefined;
|
|
@@ -207,10 +207,11 @@ export class AdminPermissionChecker {
|
|
|
207
207
|
return await this.hasFullAccess(organizationId)
|
|
208
208
|
}
|
|
209
209
|
|
|
210
|
-
/**
|
|
211
|
-
* Note: only checks admin permissions. Users that 'own' this member can also access it but that does not use the AdminPermissionChecker
|
|
212
|
-
*/
|
|
213
210
|
async canAccessMember(member: MemberWithRegistrations, permissionLevel: PermissionLevel = PermissionLevel.Read) {
|
|
211
|
+
if (this.isUserManager(member) && permissionLevel !== PermissionLevel.Full) {
|
|
212
|
+
return true;
|
|
213
|
+
}
|
|
214
|
+
|
|
214
215
|
// Check user has permissions
|
|
215
216
|
if (!this.user.permissions) {
|
|
216
217
|
return false
|
|
@@ -761,18 +762,6 @@ export class AdminPermissionChecker {
|
|
|
761
762
|
for (const category of organization.meta.recordsConfiguration.recordCategories) {
|
|
762
763
|
recordCategories.push(category)
|
|
763
764
|
}
|
|
764
|
-
|
|
765
|
-
for (const [id] of organization.meta.recordsConfiguration.inheritedRecordCategories) {
|
|
766
|
-
if (recordCategories.find(c => c.id === id)) {
|
|
767
|
-
// Already added
|
|
768
|
-
continue;
|
|
769
|
-
}
|
|
770
|
-
|
|
771
|
-
const category = this.platform.config.recordsConfiguration.recordCategories.find(c => c.id === id)
|
|
772
|
-
if (category) {
|
|
773
|
-
recordCategories.push(category)
|
|
774
|
-
}
|
|
775
|
-
}
|
|
776
765
|
continue;
|
|
777
766
|
}
|
|
778
767
|
|
|
@@ -788,17 +777,15 @@ export class AdminPermissionChecker {
|
|
|
788
777
|
}
|
|
789
778
|
}
|
|
790
779
|
|
|
791
|
-
for
|
|
792
|
-
|
|
780
|
+
// Platform ones where we have been given permissions for in this organization
|
|
781
|
+
for (const category of this.platform.config.recordsConfiguration.recordCategories) {
|
|
782
|
+
if (recordCategories.find(c => c.id === category.id)) {
|
|
793
783
|
// Already added
|
|
794
784
|
continue;
|
|
795
785
|
}
|
|
796
786
|
|
|
797
|
-
if (permissions.hasResourceAccess(PermissionsResourceType.RecordCategories, id, level)) {
|
|
798
|
-
|
|
799
|
-
if (category) {
|
|
800
|
-
recordCategories.push(category)
|
|
801
|
-
}
|
|
787
|
+
if (permissions.hasResourceAccess(PermissionsResourceType.RecordCategories, category.id, level)) {
|
|
788
|
+
recordCategories.push(category)
|
|
802
789
|
}
|
|
803
790
|
}
|
|
804
791
|
}
|
|
@@ -936,6 +923,7 @@ export class AdminPermissionChecker {
|
|
|
936
923
|
// Has financial read access?
|
|
937
924
|
if (!await this.hasFinancialMemberAccess(member, PermissionLevel.Read)) {
|
|
938
925
|
cloned.details.requiresFinancialSupport = null
|
|
926
|
+
cloned.details.uitpasNumber = null
|
|
939
927
|
cloned.outstandingBalance = 0
|
|
940
928
|
|
|
941
929
|
for (const registration of cloned.registrations) {
|
|
@@ -1029,6 +1017,14 @@ export class AdminPermissionChecker {
|
|
|
1029
1017
|
})
|
|
1030
1018
|
}
|
|
1031
1019
|
|
|
1020
|
+
if (data.details.uitpasNumber) {
|
|
1021
|
+
throw new SimpleError({
|
|
1022
|
+
code: 'permission_denied',
|
|
1023
|
+
message: 'Je hebt geen toegangsrechten om het UiTPAS-nummer van dit lid aan te passen',
|
|
1024
|
+
statusCode: 400
|
|
1025
|
+
})
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1032
1028
|
if (data.outstandingBalance) {
|
|
1033
1029
|
throw new SimpleError({
|
|
1034
1030
|
code: 'permission_denied',
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Member, MemberResponsibilityRecord, MemberWithRegistrations, User } from "@stamhoofd/models";
|
|
2
2
|
import { SQL } from "@stamhoofd/sql";
|
|
3
|
-
import { Permissions, UserPermissions } from "@stamhoofd/structures";
|
|
3
|
+
import { MemberDetails, Permissions, UserPermissions } from "@stamhoofd/structures";
|
|
4
4
|
|
|
5
5
|
export class MemberUserSyncerStatic {
|
|
6
6
|
/**
|
|
@@ -8,15 +8,8 @@ export class MemberUserSyncerStatic {
|
|
|
8
8
|
* - responsibilities have changed
|
|
9
9
|
* - email addresses have changed
|
|
10
10
|
*/
|
|
11
|
-
async onChangeMember(member: MemberWithRegistrations) {
|
|
12
|
-
const userEmails =
|
|
13
|
-
|
|
14
|
-
if (member.details.email) {
|
|
15
|
-
userEmails.push(member.details.email)
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const unverifiedEmails: string[] = member.details.unverifiedEmails;
|
|
19
|
-
const parentAndUnverifiedEmails = member.details.parentsHaveAccess ? member.details.parents.flatMap(p => p.email ? [p.email, ...p.alternativeEmails] : p.alternativeEmails).concat(unverifiedEmails) : []
|
|
11
|
+
async onChangeMember(member: MemberWithRegistrations, unlinkUsers: boolean = false) {
|
|
12
|
+
const {userEmails, parentAndUnverifiedEmails} = this.getMemberAccessEmails(member.details)
|
|
20
13
|
|
|
21
14
|
// Make sure all these users have access to the member
|
|
22
15
|
for (const email of userEmails) {
|
|
@@ -29,14 +22,49 @@ export class MemberUserSyncerStatic {
|
|
|
29
22
|
await this.linkUser(email, member, true)
|
|
30
23
|
}
|
|
31
24
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
25
|
+
if (unlinkUsers && !member.details.parentsHaveAccess) {
|
|
26
|
+
// Remove access of users that are not in this list
|
|
27
|
+
// NOTE: we should only do this once a year (preferably on the birthday of the member)
|
|
28
|
+
// only once because otherwise users loose the access to a member during the creation of the member, or when they have changed their email address
|
|
29
|
+
// users can regain access to a member after they have lost control by using the normal verification flow when detecting duplicate members
|
|
30
|
+
|
|
31
|
+
for (const user of member.users) {
|
|
32
|
+
if (!userEmails.includes(user.email) && !parentAndUnverifiedEmails.includes(user.email)) {
|
|
33
|
+
await this.unlinkUser(user, member)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
} else {
|
|
37
|
+
// Only auto unlink users that do not have an account
|
|
38
|
+
for (const user of member.users) {
|
|
39
|
+
if (!user.hasAccount() && !userEmails.includes(user.email) && !parentAndUnverifiedEmails.includes(user.email)) {
|
|
40
|
+
await this.unlinkUser(user, member)
|
|
41
|
+
}
|
|
36
42
|
}
|
|
37
43
|
}
|
|
38
44
|
}
|
|
39
45
|
|
|
46
|
+
getMemberAccessEmails(details: MemberDetails) {
|
|
47
|
+
const userEmails = [...details.alternativeEmails]
|
|
48
|
+
|
|
49
|
+
if (details.email) {
|
|
50
|
+
userEmails.push(details.email)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const unverifiedEmails: string[] = details.unverifiedEmails;
|
|
54
|
+
const parentAndUnverifiedEmails = details.parentsHaveAccess ? details.parents.flatMap(p => p.email ? [p.email, ...p.alternativeEmails] : p.alternativeEmails).concat(unverifiedEmails) : []
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
userEmails,
|
|
58
|
+
parentAndUnverifiedEmails,
|
|
59
|
+
emails: userEmails.concat(parentAndUnverifiedEmails)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
doesEmailHaveAccess(details: MemberDetails, email: string) {
|
|
64
|
+
const {emails} = this.getMemberAccessEmails(details)
|
|
65
|
+
return emails.includes(email)
|
|
66
|
+
}
|
|
67
|
+
|
|
40
68
|
async onDeleteMember(member: MemberWithRegistrations) {
|
|
41
69
|
for (const u of member.users) {
|
|
42
70
|
console.log("Unlinking user "+u.email+" from deleted member "+member.id)
|