@stamhoofd/backend 2.52.0 → 2.54.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 +10 -10
- package/src/endpoints/global/events/PatchEventsEndpoint.ts +10 -1
- package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.ts +27 -4
- package/src/endpoints/global/organizations/GetOrganizationFromUriEndpoint.ts +0 -1
- package/src/endpoints/global/organizations/SearchOrganizationEndpoint.ts +2 -5
- package/src/endpoints/global/registration/RegisterMembersEndpoint.ts +25 -13
- package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.ts +4 -0
- package/src/excel-loaders/members.ts +147 -2
- package/src/excel-loaders/organizations.ts +252 -9
- package/src/excel-loaders/payments.ts +52 -2
- package/src/helpers/AdminPermissionChecker.ts +4 -1
- package/src/helpers/AuthenticatedStructures.ts +27 -2
- package/src/helpers/xlsxAddressTransformerColumnFactory.ts +8 -15
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stamhoofd/backend",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.54.0",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -36,14 +36,14 @@
|
|
|
36
36
|
"@simonbackx/simple-encoding": "2.16.6",
|
|
37
37
|
"@simonbackx/simple-endpoints": "1.14.0",
|
|
38
38
|
"@simonbackx/simple-logging": "^1.0.1",
|
|
39
|
-
"@stamhoofd/backend-i18n": "2.
|
|
40
|
-
"@stamhoofd/backend-middleware": "2.
|
|
41
|
-
"@stamhoofd/email": "2.
|
|
42
|
-
"@stamhoofd/models": "2.
|
|
43
|
-
"@stamhoofd/queues": "2.
|
|
44
|
-
"@stamhoofd/sql": "2.
|
|
45
|
-
"@stamhoofd/structures": "2.
|
|
46
|
-
"@stamhoofd/utility": "2.
|
|
39
|
+
"@stamhoofd/backend-i18n": "2.54.0",
|
|
40
|
+
"@stamhoofd/backend-middleware": "2.54.0",
|
|
41
|
+
"@stamhoofd/email": "2.54.0",
|
|
42
|
+
"@stamhoofd/models": "2.54.0",
|
|
43
|
+
"@stamhoofd/queues": "2.54.0",
|
|
44
|
+
"@stamhoofd/sql": "2.54.0",
|
|
45
|
+
"@stamhoofd/structures": "2.54.0",
|
|
46
|
+
"@stamhoofd/utility": "2.54.0",
|
|
47
47
|
"archiver": "^7.0.1",
|
|
48
48
|
"aws-sdk": "^2.885.0",
|
|
49
49
|
"axios": "1.6.8",
|
|
@@ -63,5 +63,5 @@
|
|
|
63
63
|
"publishConfig": {
|
|
64
64
|
"access": "public"
|
|
65
65
|
},
|
|
66
|
-
"gitHead": "
|
|
66
|
+
"gitHead": "d895bc5e468a87792398ca4e762d1216bf750baf"
|
|
67
67
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AutoEncoderPatchType, Decoder, PatchableArrayAutoEncoder, PatchableArrayDecoder, patchObject, StringDecoder } from '@simonbackx/simple-encoding';
|
|
2
2
|
import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
|
|
3
3
|
import { Event, Group, Platform, RegistrationPeriod } from '@stamhoofd/models';
|
|
4
|
-
import { Event as EventStruct, GroupType, NamedObject } from '@stamhoofd/structures';
|
|
4
|
+
import { Event as EventStruct, GroupType, NamedObject, Group as GroupStruct } from '@stamhoofd/structures';
|
|
5
5
|
|
|
6
6
|
import { SimpleError } from '@simonbackx/simple-errors';
|
|
7
7
|
import { SQL, SQLWhereSign } from '@stamhoofd/sql';
|
|
@@ -218,6 +218,15 @@ export class PatchEventsEndpoint extends Endpoint<Params, Query, Body, ResponseB
|
|
|
218
218
|
event.groupId = group.id;
|
|
219
219
|
}
|
|
220
220
|
}
|
|
221
|
+
else {
|
|
222
|
+
if (patch.startDate || patch.endDate) {
|
|
223
|
+
// Correct period id if needed
|
|
224
|
+
const period = await RegistrationPeriod.getByDate(event.startDate);
|
|
225
|
+
if (event.groupId) {
|
|
226
|
+
await PatchOrganizationRegistrationPeriodsEndpoint.patchGroup(GroupStruct.patch({ id: event.groupId }), period);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
221
230
|
|
|
222
231
|
if (type.isLocationRequired === true) {
|
|
223
232
|
PatchEventsEndpoint.throwIfAddressIsMissing(event);
|
|
@@ -8,6 +8,7 @@ import { Formatter } from '@stamhoofd/utility';
|
|
|
8
8
|
|
|
9
9
|
import { Email } from '@stamhoofd/email';
|
|
10
10
|
import { QueueHandler } from '@stamhoofd/queues';
|
|
11
|
+
import { SQL, SQLWhereSign } from '@stamhoofd/sql';
|
|
11
12
|
import { AuthenticatedStructures } from '../../../helpers/AuthenticatedStructures';
|
|
12
13
|
import { Context } from '../../../helpers/Context';
|
|
13
14
|
import { MembershipCharger } from '../../../helpers/MembershipCharger';
|
|
@@ -440,10 +441,32 @@ export class PatchOrganizationMembersEndpoint extends Endpoint<Params, Query, Bo
|
|
|
440
441
|
}
|
|
441
442
|
|
|
442
443
|
// Check duplicate memberships
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
444
|
+
const existing = await MemberPlatformMembership.select()
|
|
445
|
+
.where('memberId', member.id)
|
|
446
|
+
.where('membershipTypeId', put.membershipTypeId)
|
|
447
|
+
.where('periodId', put.periodId)
|
|
448
|
+
.where(
|
|
449
|
+
SQL.where('startDate', SQLWhereSign.LessEqual, put.startDate)
|
|
450
|
+
.and('endDate', SQLWhereSign.GreaterEqual, put.startDate),
|
|
451
|
+
)
|
|
452
|
+
.orWhere(
|
|
453
|
+
SQL.where('startDate', SQLWhereSign.LessEqual, put.endDate)
|
|
454
|
+
.and('endDate', SQLWhereSign.GreaterEqual, put.endDate),
|
|
455
|
+
)
|
|
456
|
+
.orWhere(
|
|
457
|
+
SQL.where('startDate', SQLWhereSign.GreaterEqual, put.startDate)
|
|
458
|
+
.and('endDate', SQLWhereSign.LessEqual, put.endDate),
|
|
459
|
+
)
|
|
460
|
+
.first(false);
|
|
461
|
+
|
|
462
|
+
if (existing) {
|
|
463
|
+
throw new SimpleError({
|
|
464
|
+
code: 'invalid_field',
|
|
465
|
+
field: 'startDate',
|
|
466
|
+
message: 'Invalid start date',
|
|
467
|
+
human: 'Je kan geen aansluiting toevoegen die overlapt met een bestaande aansluiting van hetzelfde type',
|
|
468
|
+
});
|
|
469
|
+
}
|
|
447
470
|
|
|
448
471
|
const membership = new MemberPlatformMembership();
|
|
449
472
|
membership.id = put.id;
|
|
@@ -3,7 +3,6 @@ import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-
|
|
|
3
3
|
import { SimpleError } from '@simonbackx/simple-errors';
|
|
4
4
|
import { Organization } from '@stamhoofd/models';
|
|
5
5
|
import { Organization as OrganizationStruct } from '@stamhoofd/structures';
|
|
6
|
-
import { GoogleTranslateHelper } from '@stamhoofd/utility';
|
|
7
6
|
import { AuthenticatedStructures } from '../../../helpers/AuthenticatedStructures';
|
|
8
7
|
type Params = Record<string, never>;
|
|
9
8
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AutoEncoder, Decoder, field, StringDecoder } from '@simonbackx/simple-encoding';
|
|
2
2
|
import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
|
|
3
3
|
import { Organization } from '@stamhoofd/models';
|
|
4
|
-
import { Organization as OrganizationStruct
|
|
4
|
+
import { Organization as OrganizationStruct } from '@stamhoofd/structures';
|
|
5
5
|
import { AuthenticatedStructures } from '../../../helpers/AuthenticatedStructures';
|
|
6
6
|
|
|
7
7
|
type Params = Record<string, never>;
|
|
@@ -12,7 +12,7 @@ class Query extends AutoEncoder {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
type Body = undefined;
|
|
15
|
-
type ResponseBody =
|
|
15
|
+
type ResponseBody = OrganizationStruct[];
|
|
16
16
|
|
|
17
17
|
export class SearchOrganizationEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
18
18
|
protected queryDecoder = Query as Decoder<Query>;
|
|
@@ -55,9 +55,6 @@ export class SearchOrganizationEndpoint extends Endpoint<Params, Query, Body, Re
|
|
|
55
55
|
],
|
|
56
56
|
});
|
|
57
57
|
|
|
58
|
-
if (request.request.getVersion() < 169) {
|
|
59
|
-
return new Response(organizations.map(o => OrganizationSimple.create(o)));
|
|
60
|
-
}
|
|
61
58
|
return new Response(await Promise.all(organizations.map(o => AuthenticatedStructures.organization(o))));
|
|
62
59
|
}
|
|
63
60
|
}
|
|
@@ -408,7 +408,7 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
|
|
|
408
408
|
balanceItem2.memberId = registration.memberId;
|
|
409
409
|
|
|
410
410
|
// If the paying organization hasn't paid yet, this should be hidden and move to pending as soon as the paying organization has paid
|
|
411
|
-
balanceItem2.status = shouldMarkValid ? BalanceItemStatus.Pending : BalanceItemStatus.Hidden;
|
|
411
|
+
balanceItem2.status = BalanceItemStatus.Hidden; // shouldMarkValid ? BalanceItemStatus.Pending : BalanceItemStatus.Hidden;
|
|
412
412
|
await balanceItem2.save();
|
|
413
413
|
|
|
414
414
|
// do not add to createdBalanceItems array because we don't want to add this to the payment if we create a payment
|
|
@@ -419,7 +419,7 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
|
|
|
419
419
|
balanceItem.userId = user.id;
|
|
420
420
|
}
|
|
421
421
|
|
|
422
|
-
balanceItem.status = shouldMarkValid ? BalanceItemStatus.Pending : BalanceItemStatus.Hidden;
|
|
422
|
+
balanceItem.status = BalanceItemStatus.Hidden; // shouldMarkValid ? BalanceItemStatus.Pending : BalanceItemStatus.Hidden;
|
|
423
423
|
balanceItem.pricePaid = 0;
|
|
424
424
|
|
|
425
425
|
// Connect the 'pay back' balance item to this balance item. As soon as this balance item is paid, we'll mark the other one as pending so the outstanding balance for the member increases
|
|
@@ -434,24 +434,24 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
|
|
|
434
434
|
const { item, registration } = bundle;
|
|
435
435
|
registration.reservedUntil = null;
|
|
436
436
|
|
|
437
|
-
if (shouldMarkValid) {
|
|
437
|
+
/* if (shouldMarkValid) {
|
|
438
438
|
await registration.markValid({ skipEmail: bundle.item.replaceRegistrations.length > 0 });
|
|
439
439
|
}
|
|
440
|
-
else {
|
|
441
|
-
|
|
442
|
-
|
|
440
|
+
else { */
|
|
441
|
+
// Reserve registration for 30 minutes (if needed)
|
|
442
|
+
const group = groups.find(g => g.id === registration.groupId);
|
|
443
443
|
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
}
|
|
447
|
-
await registration.save();
|
|
444
|
+
if (group && group.settings.maxMembers !== null) {
|
|
445
|
+
registration.reservedUntil = new Date(new Date().getTime() + 1000 * 60 * 30);
|
|
448
446
|
}
|
|
447
|
+
await registration.save();
|
|
448
|
+
// }
|
|
449
449
|
|
|
450
450
|
// Note: we should always create the balance items: even when the price is zero
|
|
451
451
|
// Otherwise we don't know which registrations to activate after payment
|
|
452
452
|
|
|
453
453
|
if (shouldMarkValid && item.calculatedPrice === 0) {
|
|
454
|
-
continue;
|
|
454
|
+
// continue;
|
|
455
455
|
}
|
|
456
456
|
|
|
457
457
|
// Create balance items
|
|
@@ -538,7 +538,7 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
|
|
|
538
538
|
if (oldestMember) {
|
|
539
539
|
balanceItem.memberId = oldestMember.id;
|
|
540
540
|
}
|
|
541
|
-
balanceItem.status = shouldMarkValid ? BalanceItemStatus.Pending : BalanceItemStatus.Hidden;
|
|
541
|
+
balanceItem.status = BalanceItemStatus.Hidden; // shouldMarkValid ? BalanceItemStatus.Pending : BalanceItemStatus.Hidden;
|
|
542
542
|
await balanceItem.save();
|
|
543
543
|
createdBalanceItems.push(balanceItem);
|
|
544
544
|
}
|
|
@@ -563,7 +563,7 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
|
|
|
563
563
|
}
|
|
564
564
|
}
|
|
565
565
|
|
|
566
|
-
balanceItem.status = shouldMarkValid ? BalanceItemStatus.Pending : BalanceItemStatus.Hidden;
|
|
566
|
+
balanceItem.status = BalanceItemStatus.Hidden; // shouldMarkValid ? BalanceItemStatus.Pending : BalanceItemStatus.Hidden;
|
|
567
567
|
await balanceItem.save();
|
|
568
568
|
|
|
569
569
|
createdBalanceItems.push(balanceItem);
|
|
@@ -580,6 +580,16 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
|
|
|
580
580
|
let paymentUrl: string | null = null;
|
|
581
581
|
let payment: Payment | null = null;
|
|
582
582
|
|
|
583
|
+
// Delaying markign as valid as late as possible so any errors will prevent creating valid balance items
|
|
584
|
+
async function markValidIfNeeded() {
|
|
585
|
+
if (shouldMarkValid) {
|
|
586
|
+
for (const balanceItem of [...createdBalanceItems, ...unrelatedCreatedBalanceItems]) {
|
|
587
|
+
// Mark vlaid
|
|
588
|
+
await balanceItem.markPaid(payment, organization);
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
583
593
|
if (whoWillPayNow !== 'nobody') {
|
|
584
594
|
const mappedBalanceItems = new Map<BalanceItem, number>();
|
|
585
595
|
|
|
@@ -606,6 +616,7 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
|
|
|
606
616
|
checkout: request.body,
|
|
607
617
|
members,
|
|
608
618
|
});
|
|
619
|
+
await markValidIfNeeded();
|
|
609
620
|
|
|
610
621
|
if (response) {
|
|
611
622
|
paymentUrl = response.paymentUrl;
|
|
@@ -618,6 +629,7 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
|
|
|
618
629
|
}
|
|
619
630
|
}
|
|
620
631
|
else {
|
|
632
|
+
await markValidIfNeeded();
|
|
621
633
|
await BalanceItem.updateOutstanding([...createdBalanceItems, ...unrelatedCreatedBalanceItems]);
|
|
622
634
|
}
|
|
623
635
|
|
|
@@ -321,6 +321,8 @@ export class PatchOrganizationRegistrationPeriodsEndpoint extends Endpoint<Param
|
|
|
321
321
|
if (period) {
|
|
322
322
|
model.periodId = period.id;
|
|
323
323
|
model.settings.period = period.getBaseStructure();
|
|
324
|
+
model.settings.startDate = period.startDate;
|
|
325
|
+
model.settings.endDate = period.endDate;
|
|
324
326
|
}
|
|
325
327
|
|
|
326
328
|
const patch = struct;
|
|
@@ -424,6 +426,8 @@ export class PatchOrganizationRegistrationPeriodsEndpoint extends Endpoint<Param
|
|
|
424
426
|
model.status = struct.status;
|
|
425
427
|
model.type = struct.type;
|
|
426
428
|
model.settings.period = period.getBaseStructure();
|
|
429
|
+
model.settings.startDate = period.startDate;
|
|
430
|
+
model.settings.endDate = period.endDate;
|
|
427
431
|
|
|
428
432
|
if (!await Context.auth.canAccessGroup(model, PermissionLevel.Full)) {
|
|
429
433
|
// Create a temporary permission role for this user
|
|
@@ -96,7 +96,6 @@ const sheet: XlsxTransformerSheet<PlatformMember, PlatformMember> = {
|
|
|
96
96
|
},
|
|
97
97
|
XlsxTransformerColumnHelper.createAddressColumns<PlatformMember>({
|
|
98
98
|
matchId: 'address',
|
|
99
|
-
identifier: 'Adres',
|
|
100
99
|
getAddress: ({ patchedMember: object }: PlatformMember) => {
|
|
101
100
|
// get member address if exists
|
|
102
101
|
const memberAddress = object.details.address;
|
|
@@ -227,7 +226,6 @@ const sheet: XlsxTransformerSheet<PlatformMember, PlatformMember> = {
|
|
|
227
226
|
},
|
|
228
227
|
...XlsxTransformerColumnHelper.createColumnsForAddresses<PlatformMember>({
|
|
229
228
|
matchIdStart: 'unverifiedAddresses',
|
|
230
|
-
identifier: 'Niet-geverifieerd adres',
|
|
231
229
|
getAddresses: object => object.patchedMember.details.unverifiedAddresses,
|
|
232
230
|
limit: 2,
|
|
233
231
|
}),
|
|
@@ -277,6 +275,153 @@ const sheet: XlsxTransformerSheet<PlatformMember, PlatformMember> = {
|
|
|
277
275
|
}
|
|
278
276
|
},
|
|
279
277
|
},
|
|
278
|
+
|
|
279
|
+
// Registration records
|
|
280
|
+
{
|
|
281
|
+
match(id) {
|
|
282
|
+
if (id.startsWith('groups.')) {
|
|
283
|
+
const splitted = id.split('.');
|
|
284
|
+
if (splitted.length < 3) {
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const groupId = splitted[1];
|
|
289
|
+
const recordName = splitted[2];
|
|
290
|
+
|
|
291
|
+
function getRegistration(object: PlatformMember) {
|
|
292
|
+
return object.filterRegistrations({ groupIds: [groupId] })[0] ?? null;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (recordName === 'price') {
|
|
296
|
+
// Tarief
|
|
297
|
+
return [
|
|
298
|
+
{
|
|
299
|
+
id: `groups.${groupId}.${recordName}`,
|
|
300
|
+
name: 'Tarief',
|
|
301
|
+
width: 30,
|
|
302
|
+
getValue: (member: PlatformMember) => {
|
|
303
|
+
const registration = getRegistration(member);
|
|
304
|
+
if (!registration) {
|
|
305
|
+
return {
|
|
306
|
+
value: '',
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return {
|
|
311
|
+
value: registration.groupPrice.name,
|
|
312
|
+
};
|
|
313
|
+
},
|
|
314
|
+
},
|
|
315
|
+
];
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if (recordName === 'optionMenu') {
|
|
319
|
+
if (splitted.length < 4) {
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const menuId = splitted[3];
|
|
324
|
+
|
|
325
|
+
if (splitted.length > 4) {
|
|
326
|
+
const optionId = splitted[4];
|
|
327
|
+
const returnAmount = splitted.length > 5 && splitted[5] === 'amount';
|
|
328
|
+
|
|
329
|
+
// Option menu
|
|
330
|
+
return [
|
|
331
|
+
{
|
|
332
|
+
id: `groups.${groupId}.${recordName}.${menuId}.${optionId}${returnAmount ? '.amount' : ''}`,
|
|
333
|
+
name: 'Keuzemenu aantal',
|
|
334
|
+
width: 30,
|
|
335
|
+
getValue: (member: PlatformMember) => {
|
|
336
|
+
const registration = getRegistration(member);
|
|
337
|
+
if (!registration) {
|
|
338
|
+
return {
|
|
339
|
+
value: '',
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
const options = registration.options.filter(o => o.optionMenu.id === menuId && o.option.id === optionId);
|
|
343
|
+
|
|
344
|
+
if (!options.length) {
|
|
345
|
+
return {
|
|
346
|
+
value: '',
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
return {
|
|
351
|
+
style: options.length === 1 && returnAmount
|
|
352
|
+
? {
|
|
353
|
+
numberFormat: {
|
|
354
|
+
id: XlsxBuiltInNumberFormat.Number,
|
|
355
|
+
},
|
|
356
|
+
}
|
|
357
|
+
: {},
|
|
358
|
+
value: options.length === 1 && returnAmount ? options[0].amount : options.map(option => returnAmount ? option.amount : option).join(', '),
|
|
359
|
+
};
|
|
360
|
+
},
|
|
361
|
+
},
|
|
362
|
+
];
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Option menu
|
|
366
|
+
return [
|
|
367
|
+
{
|
|
368
|
+
id: `groups.${groupId}.${recordName}.${menuId}`,
|
|
369
|
+
name: 'Keuzemenu',
|
|
370
|
+
width: 30,
|
|
371
|
+
getValue: (member: PlatformMember) => {
|
|
372
|
+
const registration = getRegistration(member);
|
|
373
|
+
if (!registration) {
|
|
374
|
+
return {
|
|
375
|
+
value: '',
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
const options = registration.options.filter(o => o.optionMenu.id === menuId);
|
|
379
|
+
|
|
380
|
+
if (!options.length) {
|
|
381
|
+
return {
|
|
382
|
+
value: '',
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
return {
|
|
387
|
+
value: options.map(option => (option.amount > 1 ? `${option.amount}x ` : '') + option.option.name).join(', '),
|
|
388
|
+
};
|
|
389
|
+
},
|
|
390
|
+
},
|
|
391
|
+
];
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
if (recordName === 'recordAnswers') {
|
|
395
|
+
if (splitted.length < 4) {
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
const recordId = splitted[3];
|
|
400
|
+
return [
|
|
401
|
+
{
|
|
402
|
+
id: `groups.${groupId}.${recordName}.${recordId}`,
|
|
403
|
+
name: 'Vraag',
|
|
404
|
+
width: 35,
|
|
405
|
+
getValue: (member: PlatformMember) => {
|
|
406
|
+
const registration = getRegistration(member);
|
|
407
|
+
if (!registration) {
|
|
408
|
+
return {
|
|
409
|
+
value: '',
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
return {
|
|
414
|
+
value: registration.recordAnswers.get(recordId)?.excelValues[0]?.value ?? '',
|
|
415
|
+
};
|
|
416
|
+
},
|
|
417
|
+
},
|
|
418
|
+
];
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
},
|
|
424
|
+
},
|
|
280
425
|
],
|
|
281
426
|
};
|
|
282
427
|
|
|
@@ -1,18 +1,40 @@
|
|
|
1
1
|
import { XlsxTransformerSheet } from '@stamhoofd/excel-writer';
|
|
2
|
-
import { ExcelExportType, LimitedFilteredRequest, Organization as OrganizationStruct } from '@stamhoofd/structures';
|
|
2
|
+
import { Platform as PlatformStruct, ExcelExportType, LimitedFilteredRequest, Organization as OrganizationStruct, MemberResponsibilityRecord as MemberResponsibilityRecordStruct, PaginatedResponse, MemberWithRegistrationsBlob, Premise } from '@stamhoofd/structures';
|
|
3
3
|
import { GetOrganizationsEndpoint } from '../endpoints/admin/organizations/GetOrganizationsEndpoint';
|
|
4
4
|
import { ExportToExcelEndpoint } from '../endpoints/global/files/ExportToExcelEndpoint';
|
|
5
|
+
import { XlsxTransformerColumnHelper } from '../helpers/xlsxAddressTransformerColumnFactory';
|
|
6
|
+
import { Group, Member, MemberResponsibilityRecord } from '@stamhoofd/models';
|
|
7
|
+
import { Formatter, Sorter } from '@stamhoofd/utility';
|
|
8
|
+
import { ArrayDecoder, field } from '@simonbackx/simple-encoding';
|
|
9
|
+
import { AuthenticatedStructures } from '../helpers/AuthenticatedStructures';
|
|
10
|
+
|
|
11
|
+
class MemberResponsibilityRecordWithMember extends MemberResponsibilityRecordStruct {
|
|
12
|
+
@field({ decoder: MemberWithRegistrationsBlob })
|
|
13
|
+
member: MemberWithRegistrationsBlob;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
class OrganizationWithResponsibilities extends OrganizationStruct {
|
|
17
|
+
@field({ decoder: new ArrayDecoder(MemberResponsibilityRecordWithMember) })
|
|
18
|
+
responsibilities: MemberResponsibilityRecordWithMember[];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
class MemberResponsibilityRecordWithMemberAndOrganization extends MemberResponsibilityRecordWithMember {
|
|
22
|
+
@field({ decoder: OrganizationWithResponsibilities })
|
|
23
|
+
organization: OrganizationWithResponsibilities;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
type Object = OrganizationWithResponsibilities;
|
|
5
27
|
|
|
6
28
|
// Assign to a typed variable to assure we have correct type checking in place
|
|
7
|
-
const sheet: XlsxTransformerSheet<
|
|
29
|
+
const sheet: XlsxTransformerSheet<Object, Object> = {
|
|
8
30
|
id: 'organizations',
|
|
9
|
-
name: '
|
|
31
|
+
name: 'Groepen',
|
|
10
32
|
columns: [
|
|
11
33
|
{
|
|
12
34
|
id: 'id',
|
|
13
35
|
name: 'ID',
|
|
14
|
-
width:
|
|
15
|
-
getValue: (object:
|
|
36
|
+
width: 35,
|
|
37
|
+
getValue: (object: Object) => ({
|
|
16
38
|
value: object.id,
|
|
17
39
|
}),
|
|
18
40
|
},
|
|
@@ -20,7 +42,7 @@ const sheet: XlsxTransformerSheet<OrganizationStruct, OrganizationStruct> = {
|
|
|
20
42
|
id: 'uri',
|
|
21
43
|
name: 'Groepsnummer',
|
|
22
44
|
width: 20,
|
|
23
|
-
getValue: (object:
|
|
45
|
+
getValue: (object: Object) => ({
|
|
24
46
|
value: object.uri,
|
|
25
47
|
}),
|
|
26
48
|
},
|
|
@@ -28,20 +50,241 @@ const sheet: XlsxTransformerSheet<OrganizationStruct, OrganizationStruct> = {
|
|
|
28
50
|
id: 'name',
|
|
29
51
|
name: 'Naam',
|
|
30
52
|
width: 50,
|
|
31
|
-
getValue: (object:
|
|
53
|
+
getValue: (object: Object) => ({
|
|
32
54
|
value: object.name,
|
|
33
55
|
}),
|
|
34
56
|
},
|
|
57
|
+
{
|
|
58
|
+
id: 'tags',
|
|
59
|
+
name: 'Tags',
|
|
60
|
+
width: 50,
|
|
61
|
+
getValue: (object: Object) => {
|
|
62
|
+
const platform = PlatformStruct.shared;
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
value: object.meta.tags.map(tag => platform.config.tags.find(t => t.id === tag)?.name ?? 'Onbekend').join(', '),
|
|
66
|
+
};
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
XlsxTransformerColumnHelper.createAddressColumns<OrganizationStruct>({
|
|
70
|
+
matchId: 'address',
|
|
71
|
+
getAddress: object => object.address,
|
|
72
|
+
}),
|
|
73
|
+
],
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const responsibilities: XlsxTransformerSheet<Object, MemberResponsibilityRecordWithMemberAndOrganization> = {
|
|
77
|
+
id: 'responsibilities',
|
|
78
|
+
name: 'Functies',
|
|
79
|
+
transform(organization) {
|
|
80
|
+
return organization.responsibilities.map(r => MemberResponsibilityRecordWithMemberAndOrganization.create({
|
|
81
|
+
...r,
|
|
82
|
+
organization,
|
|
83
|
+
}));
|
|
84
|
+
},
|
|
85
|
+
columns: [
|
|
86
|
+
{
|
|
87
|
+
id: 'organization.id',
|
|
88
|
+
name: 'ID',
|
|
89
|
+
width: 35,
|
|
90
|
+
getValue: (object: MemberResponsibilityRecordWithMemberAndOrganization) => ({
|
|
91
|
+
value: object.organization.id,
|
|
92
|
+
}),
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
id: 'organization.uri',
|
|
96
|
+
name: 'Groepsnummer',
|
|
97
|
+
width: 20,
|
|
98
|
+
getValue: (object: MemberResponsibilityRecordWithMemberAndOrganization) => ({
|
|
99
|
+
value: object.organization.uri,
|
|
100
|
+
}),
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
id: 'organization.name',
|
|
104
|
+
name: 'Groepsnaam',
|
|
105
|
+
width: 50,
|
|
106
|
+
getValue: (object: MemberResponsibilityRecordWithMemberAndOrganization) => ({
|
|
107
|
+
value: object.organization.name,
|
|
108
|
+
}),
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
id: 'responsibility.name',
|
|
112
|
+
name: 'Functie',
|
|
113
|
+
width: 50,
|
|
114
|
+
getValue: (object: MemberResponsibilityRecordWithMemberAndOrganization) => {
|
|
115
|
+
const platform = PlatformStruct.shared;
|
|
116
|
+
const responsibility = platform.config.responsibilities.find(r => r.id === object.responsibilityId) ?? object.organization.privateMeta?.responsibilities.find(r => r.id === object.responsibilityId);
|
|
117
|
+
|
|
118
|
+
if (!responsibility) {
|
|
119
|
+
return {
|
|
120
|
+
value: 'Onbekende functie',
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return {
|
|
125
|
+
value: responsibility.name + (responsibility.isGroupBased ? ' van ' + (object.group?.settings.name ?? 'Onbekende groep') : ''),
|
|
126
|
+
};
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
id: 'responsibility.member.firstName',
|
|
131
|
+
name: 'Voornaam',
|
|
132
|
+
width: 30,
|
|
133
|
+
getValue: (object: MemberResponsibilityRecordWithMemberAndOrganization) => ({
|
|
134
|
+
value: object.member.firstName,
|
|
135
|
+
}),
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
id: 'responsibility.member.lastName',
|
|
139
|
+
name: 'Achternaam',
|
|
140
|
+
width: 30,
|
|
141
|
+
getValue: (object: MemberResponsibilityRecordWithMemberAndOrganization) => ({
|
|
142
|
+
value: object.member.details.lastName,
|
|
143
|
+
}),
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
id: 'responsibility.member.email',
|
|
147
|
+
name: 'E-mailadres lid',
|
|
148
|
+
width: 50,
|
|
149
|
+
getValue: (object: MemberResponsibilityRecordWithMemberAndOrganization) => ({
|
|
150
|
+
value: object.member.details.email,
|
|
151
|
+
}),
|
|
152
|
+
},
|
|
153
|
+
XlsxTransformerColumnHelper.createAddressColumns<MemberResponsibilityRecordWithMemberAndOrganization>({
|
|
154
|
+
matchId: 'responsibility.member.address',
|
|
155
|
+
getAddress: object => object.member.details.address ?? object.member.details.parents[0]?.address ?? object.member.details.parents[1]?.address ?? null,
|
|
156
|
+
}),
|
|
157
|
+
],
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
type PremiseWithOrganization = { organization: Object; premise: Premise };
|
|
161
|
+
const premises: XlsxTransformerSheet<Object, PremiseWithOrganization> = {
|
|
162
|
+
id: 'premises',
|
|
163
|
+
name: 'Lokalen',
|
|
164
|
+
transform(organization) {
|
|
165
|
+
return organization.privateMeta?.premises.map(r => ({
|
|
166
|
+
organization,
|
|
167
|
+
premise: r,
|
|
168
|
+
})) ?? [];
|
|
169
|
+
},
|
|
170
|
+
columns: [
|
|
171
|
+
{
|
|
172
|
+
id: 'organization.id',
|
|
173
|
+
name: 'ID',
|
|
174
|
+
width: 35,
|
|
175
|
+
getValue: (object: PremiseWithOrganization) => ({
|
|
176
|
+
value: object.organization.id,
|
|
177
|
+
}),
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
id: 'organization.uri',
|
|
181
|
+
name: 'Groepsnummer',
|
|
182
|
+
width: 20,
|
|
183
|
+
getValue: (object: PremiseWithOrganization) => ({
|
|
184
|
+
value: object.organization.uri,
|
|
185
|
+
}),
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
id: 'organization.name',
|
|
189
|
+
name: 'Groepsnaam',
|
|
190
|
+
width: 50,
|
|
191
|
+
getValue: (object: PremiseWithOrganization) => ({
|
|
192
|
+
value: object.organization.name,
|
|
193
|
+
}),
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
id: 'premise.name',
|
|
197
|
+
name: 'Naam',
|
|
198
|
+
width: 20,
|
|
199
|
+
getValue: (object: PremiseWithOrganization) => ({
|
|
200
|
+
value: object.premise.name,
|
|
201
|
+
}),
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
id: 'premise.type',
|
|
205
|
+
name: 'Type',
|
|
206
|
+
width: 20,
|
|
207
|
+
getValue: (object: PremiseWithOrganization) => {
|
|
208
|
+
const ids = object.premise.premiseTypeIds;
|
|
209
|
+
const platform = PlatformStruct.shared;
|
|
210
|
+
return {
|
|
211
|
+
value: ids.map(id => platform.config.premiseTypes.find(t => t.id === id)?.name ?? 'Onbekend').join(', '),
|
|
212
|
+
};
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
XlsxTransformerColumnHelper.createAddressColumns<PremiseWithOrganization>({
|
|
216
|
+
matchId: 'premise.address',
|
|
217
|
+
getAddress: object => object.premise.address,
|
|
218
|
+
}),
|
|
35
219
|
],
|
|
36
220
|
};
|
|
37
221
|
|
|
38
222
|
ExportToExcelEndpoint.loaders.set(ExcelExportType.Organizations, {
|
|
39
223
|
fetch: async (query: LimitedFilteredRequest) => {
|
|
40
|
-
const
|
|
224
|
+
const organizations = await GetOrganizationsEndpoint.buildData(query);
|
|
225
|
+
|
|
226
|
+
// Now load all responsibilities with members with an active responsibility for theses organizations
|
|
227
|
+
const organizationIds = organizations.results.map(o => o.id);
|
|
228
|
+
const responsibilities = organizationIds.length
|
|
229
|
+
? await MemberResponsibilityRecord.select()
|
|
230
|
+
.where('organizationId', organizationIds)
|
|
231
|
+
.where('endDate', null)
|
|
232
|
+
.fetch()
|
|
233
|
+
: [];
|
|
234
|
+
|
|
235
|
+
// Load groups and members
|
|
236
|
+
const groupIds = Formatter.uniqueArray(responsibilities.map(o => o.groupId).filter(g => g !== null));
|
|
237
|
+
const memberIds = Formatter.uniqueArray(responsibilities.map(o => o.memberId).filter(m => m !== null));
|
|
238
|
+
|
|
239
|
+
const members = await Member.getBlobByIds(...memberIds);
|
|
240
|
+
const groups = await Group.getByIDs(...groupIds);
|
|
241
|
+
const memberStructs = await AuthenticatedStructures.members(members);
|
|
242
|
+
const groupStructs = await AuthenticatedStructures.groups(groups);
|
|
243
|
+
const platform = PlatformStruct.shared;
|
|
244
|
+
|
|
245
|
+
const mappedOrganizations = organizations.results.map((o) => {
|
|
246
|
+
const resp = responsibilities.filter(r => r.organizationId === o.id);
|
|
247
|
+
|
|
248
|
+
const mappedResponsibilities = resp.map((r) => {
|
|
249
|
+
const member = memberStructs.find(m => m.id === r.memberId);
|
|
250
|
+
const group = groupStructs.find(g => g.id === r.groupId);
|
|
251
|
+
|
|
252
|
+
return MemberResponsibilityRecordWithMember.create({
|
|
253
|
+
...r,
|
|
254
|
+
member: member,
|
|
255
|
+
group: group ? group : null,
|
|
256
|
+
});
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
// Sort responsibilites by index in platform config
|
|
260
|
+
// and, if the same, sort by order of default age group id if it has a group
|
|
261
|
+
mappedResponsibilities.sort((a, b) => {
|
|
262
|
+
const aIndex = platform.config.responsibilities.findIndex(r => r.id === a.responsibilityId);
|
|
263
|
+
const bIndex = platform.config.responsibilities.findIndex(r => r.id === b.responsibilityId);
|
|
264
|
+
|
|
265
|
+
const groupAIndex = platform.config.defaultAgeGroups.findIndex(g => g.id === a.group?.defaultAgeGroupId);
|
|
266
|
+
const groupBIndex = platform.config.defaultAgeGroups.findIndex(g => g.id === b.group?.defaultAgeGroupId);
|
|
267
|
+
|
|
268
|
+
return Sorter.stack(
|
|
269
|
+
aIndex - bIndex,
|
|
270
|
+
groupAIndex - groupBIndex,
|
|
271
|
+
);
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
return OrganizationWithResponsibilities.create({
|
|
275
|
+
...o,
|
|
276
|
+
responsibilities: mappedResponsibilities,
|
|
277
|
+
});
|
|
278
|
+
});
|
|
41
279
|
|
|
42
|
-
return
|
|
280
|
+
return new PaginatedResponse({
|
|
281
|
+
results: mappedOrganizations,
|
|
282
|
+
next: organizations.next,
|
|
283
|
+
});
|
|
43
284
|
},
|
|
44
285
|
sheets: [
|
|
45
286
|
sheet,
|
|
287
|
+
responsibilities,
|
|
288
|
+
premises,
|
|
46
289
|
],
|
|
47
290
|
});
|
|
@@ -45,6 +45,7 @@ ExportToExcelEndpoint.loaders.set(ExcelExportType.Payments, {
|
|
|
45
45
|
columns: [
|
|
46
46
|
...getGeneralColumns(),
|
|
47
47
|
...getInvoiceColumns(),
|
|
48
|
+
...getPayingOrganizationColumns(),
|
|
48
49
|
...getSettlementColumns(),
|
|
49
50
|
...getStripeColumns(),
|
|
50
51
|
...getTransferColumns(),
|
|
@@ -64,6 +65,7 @@ ExportToExcelEndpoint.loaders.set(ExcelExportType.Payments, {
|
|
|
64
65
|
...[
|
|
65
66
|
...getGeneralColumns(),
|
|
66
67
|
...getInvoiceColumns(),
|
|
68
|
+
...getPayingOrganizationColumns(),
|
|
67
69
|
].map((c) => {
|
|
68
70
|
if ('match' in c) {
|
|
69
71
|
return {
|
|
@@ -127,6 +129,16 @@ function getBalanceItemColumns(): XlsxTransformerColumn<PaymentWithItem>[] {
|
|
|
127
129
|
value: getBalanceItemTypeName(object.balanceItemPayment.balanceItem.type),
|
|
128
130
|
}),
|
|
129
131
|
},
|
|
132
|
+
{
|
|
133
|
+
id: 'balanceItem.category',
|
|
134
|
+
name: 'Categorie',
|
|
135
|
+
width: 30,
|
|
136
|
+
getValue: (object: PaymentWithItem) => {
|
|
137
|
+
return {
|
|
138
|
+
value: Formatter.capitalizeFirstLetter(object.balanceItemPayment.balanceItem.category),
|
|
139
|
+
};
|
|
140
|
+
},
|
|
141
|
+
},
|
|
130
142
|
{
|
|
131
143
|
id: 'balanceItem.description',
|
|
132
144
|
name: 'Beschrijving',
|
|
@@ -245,7 +257,7 @@ function getGeneralColumns(): XlsxTransformerConcreteColumn<PaymentGeneral>[] {
|
|
|
245
257
|
name: 'Betaalprovider',
|
|
246
258
|
width: 16,
|
|
247
259
|
getValue: (object: PaymentGeneralWithStripeAccount) => ({
|
|
248
|
-
value: object.provider,
|
|
260
|
+
value: object.provider ?? PaymentMethodHelper.getNameCapitalized(object.method),
|
|
249
261
|
}),
|
|
250
262
|
},
|
|
251
263
|
{
|
|
@@ -432,6 +444,45 @@ function getTransferColumns(): XlsxTransformerColumn<PaymentGeneral>[] {
|
|
|
432
444
|
];
|
|
433
445
|
}
|
|
434
446
|
|
|
447
|
+
function getPayingOrganizationColumns(): XlsxTransformerColumn<PaymentGeneral>[] {
|
|
448
|
+
return [
|
|
449
|
+
{
|
|
450
|
+
id: 'payingOrganization.id',
|
|
451
|
+
name: 'ID betalende groep',
|
|
452
|
+
width: 30,
|
|
453
|
+
getValue: (object: PaymentGeneralWithStripeAccount) => {
|
|
454
|
+
return {
|
|
455
|
+
value: object.payingOrganization?.id || '',
|
|
456
|
+
};
|
|
457
|
+
},
|
|
458
|
+
},
|
|
459
|
+
{
|
|
460
|
+
id: 'payingOrganization.name',
|
|
461
|
+
name: 'Naam betalende groep',
|
|
462
|
+
width: 30,
|
|
463
|
+
getValue: (object: PaymentGeneralWithStripeAccount) => {
|
|
464
|
+
return {
|
|
465
|
+
value: object.payingOrganization?.name || '',
|
|
466
|
+
};
|
|
467
|
+
},
|
|
468
|
+
},
|
|
469
|
+
{
|
|
470
|
+
id: 'payingOrganization.uri',
|
|
471
|
+
name: 'Groepsnummer betalende groep',
|
|
472
|
+
width: 30,
|
|
473
|
+
getValue: (object: PaymentGeneralWithStripeAccount) => {
|
|
474
|
+
return {
|
|
475
|
+
value: object.payingOrganization?.uri || '',
|
|
476
|
+
};
|
|
477
|
+
},
|
|
478
|
+
},
|
|
479
|
+
XlsxTransformerColumnHelper.createAddressColumns<PaymentGeneralWithStripeAccount>({
|
|
480
|
+
matchId: 'payingOrganization.address',
|
|
481
|
+
getAddress: object => object.payingOrganization?.address,
|
|
482
|
+
}),
|
|
483
|
+
];
|
|
484
|
+
}
|
|
485
|
+
|
|
435
486
|
function getInvoiceColumns(): XlsxTransformerColumn<PaymentGeneral>[] {
|
|
436
487
|
return [
|
|
437
488
|
{
|
|
@@ -487,7 +538,6 @@ function getInvoiceColumns(): XlsxTransformerColumn<PaymentGeneral>[] {
|
|
|
487
538
|
XlsxTransformerColumnHelper.createAddressColumns<PaymentGeneralWithStripeAccount>({
|
|
488
539
|
matchId: 'customer.company.address',
|
|
489
540
|
getAddress: object => object.customer?.company?.address,
|
|
490
|
-
identifier: 'Adres',
|
|
491
541
|
}),
|
|
492
542
|
{
|
|
493
543
|
id: 'customer.company.administrationEmail',
|
|
@@ -131,7 +131,10 @@ export class AdminPermissionChecker {
|
|
|
131
131
|
if (organizationId) {
|
|
132
132
|
// If request is scoped to a different organization
|
|
133
133
|
if (this.organization && organizationId !== this.organization.id) {
|
|
134
|
-
|
|
134
|
+
if (STAMHOOFD.userMode === 'organization') {
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
// Otherwise allow for convenience
|
|
135
138
|
}
|
|
136
139
|
|
|
137
140
|
// If user is limited to scope
|
|
@@ -42,10 +42,13 @@ export class AuthenticatedStructures {
|
|
|
42
42
|
|
|
43
43
|
console.log('includeSettlements', includeSettlements);
|
|
44
44
|
|
|
45
|
+
const { payingOrganizations } = await Payment.loadPayingOrganizations(payments);
|
|
46
|
+
|
|
45
47
|
return Payment.getGeneralStructureFromRelations({
|
|
46
48
|
payments,
|
|
47
49
|
balanceItemPayments,
|
|
48
50
|
balanceItems,
|
|
51
|
+
payingOrganizations,
|
|
49
52
|
}, includeSettlements);
|
|
50
53
|
}
|
|
51
54
|
|
|
@@ -312,6 +315,10 @@ export class AuthenticatedStructures {
|
|
|
312
315
|
return structs;
|
|
313
316
|
}
|
|
314
317
|
|
|
318
|
+
static async members(members: MemberWithRegistrations[]): Promise<MemberWithRegistrationsBlob[]> {
|
|
319
|
+
return (await this.membersBlob(members, false)).members;
|
|
320
|
+
}
|
|
321
|
+
|
|
315
322
|
static async membersBlob(members: MemberWithRegistrations[], includeContextOrganization = false, includeUser?: User): Promise<MembersBlob> {
|
|
316
323
|
if (members.length === 0 && !includeUser) {
|
|
317
324
|
return MembersBlob.create({ members: [], organizations: [] });
|
|
@@ -323,8 +330,26 @@ export class AuthenticatedStructures {
|
|
|
323
330
|
if (includeContextOrganization || organizationId !== Context.auth.organization?.id) {
|
|
324
331
|
const found = organizations.get(organizationId);
|
|
325
332
|
if (!found) {
|
|
326
|
-
|
|
327
|
-
|
|
333
|
+
try {
|
|
334
|
+
const organization = await Context.auth.getOrganization(organizationId);
|
|
335
|
+
organizations.set(organization.id, organization);
|
|
336
|
+
}
|
|
337
|
+
catch (e) {
|
|
338
|
+
if (e.message.includes('Unexpected missing organization')) {
|
|
339
|
+
// This user has permissions for an organization that is deleted
|
|
340
|
+
console.error(e);
|
|
341
|
+
console.error('User has permissions for an organization that is not found:', organizationId, 'userid', includeUser.id);
|
|
342
|
+
|
|
343
|
+
// Remove permissions for this organization
|
|
344
|
+
if (includeUser.permissions) {
|
|
345
|
+
includeUser.permissions.organizationPermissions.delete(organizationId);
|
|
346
|
+
await includeUser.save();
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
else {
|
|
350
|
+
throw e;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
328
353
|
}
|
|
329
354
|
}
|
|
330
355
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { XlsxTransformerColumn } from '@stamhoofd/excel-writer';
|
|
2
|
-
import { Address, CountryHelper,
|
|
2
|
+
import { Address, CountryHelper, Parent, ParentTypeHelper, PlatformMember } from '@stamhoofd/structures';
|
|
3
3
|
|
|
4
4
|
export class XlsxTransformerColumnHelper {
|
|
5
5
|
static formatBoolean(value: boolean | undefined | null): string {
|
|
@@ -21,14 +21,13 @@ export class XlsxTransformerColumnHelper {
|
|
|
21
21
|
];
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
static createColumnsForAddresses<T>({ limit, getAddresses, matchIdStart
|
|
24
|
+
static createColumnsForAddresses<T>({ limit, getAddresses, matchIdStart }: { limit: number; getAddresses: (object: T) => Address[]; matchIdStart: string }): XlsxTransformerColumn<T>[] {
|
|
25
25
|
const result: XlsxTransformerColumn<unknown>[] = [];
|
|
26
26
|
|
|
27
27
|
for (let i = 0; i <= limit; i++) {
|
|
28
28
|
const column = this.createAddressColumns({
|
|
29
29
|
matchId: `${matchIdStart}.${i}`,
|
|
30
30
|
getAddress: (object: T) => getAddresses(object)[i],
|
|
31
|
-
identifier: `${identifier} ${i + 1}`,
|
|
32
31
|
});
|
|
33
32
|
|
|
34
33
|
result.push(column);
|
|
@@ -94,18 +93,12 @@ export class XlsxTransformerColumnHelper {
|
|
|
94
93
|
XlsxTransformerColumnHelper.createAddressColumns<PlatformMember>({
|
|
95
94
|
matchId: getId('address'),
|
|
96
95
|
getAddress: member => getParent(member)?.address,
|
|
97
|
-
identifier: getName('Adres'),
|
|
98
96
|
}),
|
|
99
97
|
];
|
|
100
98
|
}
|
|
101
99
|
|
|
102
|
-
static createAddressColumns<T>({ matchId,
|
|
100
|
+
static createAddressColumns<T>({ matchId, getAddress }: { matchId: string; getAddress: (object: T) => Address | null | undefined }): XlsxTransformerColumn<T> {
|
|
103
101
|
const getId = (value: string) => matchId + '.' + value;
|
|
104
|
-
const identifierText = identifier ? `${identifier} - ` : '';
|
|
105
|
-
const getName = (value: string) => {
|
|
106
|
-
const name = `${identifierText}${value}`;
|
|
107
|
-
return name[0].toUpperCase() + name.slice(1);
|
|
108
|
-
};
|
|
109
102
|
|
|
110
103
|
return {
|
|
111
104
|
match: (id) => {
|
|
@@ -113,7 +106,7 @@ export class XlsxTransformerColumnHelper {
|
|
|
113
106
|
return [
|
|
114
107
|
{
|
|
115
108
|
id: getId('street'),
|
|
116
|
-
name:
|
|
109
|
+
name: `Straat`,
|
|
117
110
|
width: 30,
|
|
118
111
|
getValue: (object: T) => {
|
|
119
112
|
const address = getAddress(object);
|
|
@@ -124,7 +117,7 @@ export class XlsxTransformerColumnHelper {
|
|
|
124
117
|
},
|
|
125
118
|
{
|
|
126
119
|
id: getId('number'),
|
|
127
|
-
name:
|
|
120
|
+
name: 'Nummer',
|
|
128
121
|
width: 20,
|
|
129
122
|
getValue: (object: T) => {
|
|
130
123
|
const address = getAddress(object);
|
|
@@ -135,7 +128,7 @@ export class XlsxTransformerColumnHelper {
|
|
|
135
128
|
},
|
|
136
129
|
{
|
|
137
130
|
id: getId('postalCode'),
|
|
138
|
-
name:
|
|
131
|
+
name: 'Postcode',
|
|
139
132
|
width: 20,
|
|
140
133
|
getValue: (object: T) => {
|
|
141
134
|
const address = getAddress(object);
|
|
@@ -146,7 +139,7 @@ export class XlsxTransformerColumnHelper {
|
|
|
146
139
|
},
|
|
147
140
|
{
|
|
148
141
|
id: getId('city'),
|
|
149
|
-
name:
|
|
142
|
+
name: 'Stad',
|
|
150
143
|
width: 20,
|
|
151
144
|
getValue: (object: T) => {
|
|
152
145
|
const address = getAddress(object);
|
|
@@ -157,7 +150,7 @@ export class XlsxTransformerColumnHelper {
|
|
|
157
150
|
},
|
|
158
151
|
{
|
|
159
152
|
id: getId('country'),
|
|
160
|
-
name:
|
|
153
|
+
name: 'Land',
|
|
161
154
|
width: 20,
|
|
162
155
|
getValue: (object: T) => {
|
|
163
156
|
const address = getAddress(object);
|