@stamhoofd/backend 2.97.3 → 2.99.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/crons/balance-emails.ts +1 -1
- package/src/endpoints/global/email/GetEmailAddressEndpoint.ts +76 -16
- package/src/endpoints/global/email/ManageEmailAddressEndpoint.ts +103 -15
- package/src/endpoints/global/email-recipients/RetryEmailRecipientEndpoint.ts +83 -0
- package/src/endpoints/global/registration-periods/PatchRegistrationPeriodsEndpoint.ts +23 -2
- package/src/excel-loaders/organizations.ts +17 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stamhoofd/backend",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.99.0",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -45,14 +45,14 @@
|
|
|
45
45
|
"@simonbackx/simple-encoding": "2.22.0",
|
|
46
46
|
"@simonbackx/simple-endpoints": "1.20.1",
|
|
47
47
|
"@simonbackx/simple-logging": "^1.0.1",
|
|
48
|
-
"@stamhoofd/backend-i18n": "2.
|
|
49
|
-
"@stamhoofd/backend-middleware": "2.
|
|
50
|
-
"@stamhoofd/email": "2.
|
|
51
|
-
"@stamhoofd/models": "2.
|
|
52
|
-
"@stamhoofd/queues": "2.
|
|
53
|
-
"@stamhoofd/sql": "2.
|
|
54
|
-
"@stamhoofd/structures": "2.
|
|
55
|
-
"@stamhoofd/utility": "2.
|
|
48
|
+
"@stamhoofd/backend-i18n": "2.99.0",
|
|
49
|
+
"@stamhoofd/backend-middleware": "2.99.0",
|
|
50
|
+
"@stamhoofd/email": "2.99.0",
|
|
51
|
+
"@stamhoofd/models": "2.99.0",
|
|
52
|
+
"@stamhoofd/queues": "2.99.0",
|
|
53
|
+
"@stamhoofd/sql": "2.99.0",
|
|
54
|
+
"@stamhoofd/structures": "2.99.0",
|
|
55
|
+
"@stamhoofd/utility": "2.99.0",
|
|
56
56
|
"archiver": "^7.0.1",
|
|
57
57
|
"axios": "^1.8.2",
|
|
58
58
|
"cookie": "^0.7.0",
|
|
@@ -70,5 +70,5 @@
|
|
|
70
70
|
"publishConfig": {
|
|
71
71
|
"access": "public"
|
|
72
72
|
},
|
|
73
|
-
"gitHead": "
|
|
73
|
+
"gitHead": "7a7e5f54a042186c74843d4d7613ec331bc93451"
|
|
74
74
|
}
|
|
@@ -21,7 +21,7 @@ async function balanceEmails() {
|
|
|
21
21
|
return;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
if ((new Date().getHours() >
|
|
24
|
+
if ((new Date().getHours() > 10 || new Date().getHours() < 6) && STAMHOOFD.environment !== 'development') {
|
|
25
25
|
return;
|
|
26
26
|
}
|
|
27
27
|
|
|
@@ -4,16 +4,20 @@ import { SimpleError } from '@simonbackx/simple-errors';
|
|
|
4
4
|
import { EmailAddress } from '@stamhoofd/email';
|
|
5
5
|
import { Organization } from '@stamhoofd/models';
|
|
6
6
|
import { EmailAddressSettings, OrganizationSimple } from '@stamhoofd/structures';
|
|
7
|
+
import { Context } from '../../../helpers/Context';
|
|
7
8
|
|
|
8
9
|
type Params = Record<string, never>;
|
|
9
10
|
type Body = undefined;
|
|
10
11
|
|
|
11
12
|
class Query extends AutoEncoder {
|
|
12
|
-
@field({ decoder: StringDecoder })
|
|
13
|
-
id: string;
|
|
13
|
+
@field({ decoder: StringDecoder, nullable: true, optional: true })
|
|
14
|
+
id: string | null = null;
|
|
14
15
|
|
|
15
|
-
@field({ decoder: StringDecoder })
|
|
16
|
-
token: string;
|
|
16
|
+
@field({ decoder: StringDecoder, nullable: true, optional: true })
|
|
17
|
+
token: string | null = null;
|
|
18
|
+
|
|
19
|
+
@field({ decoder: StringDecoder, nullable: true, optional: true })
|
|
20
|
+
email: string | null = null;
|
|
17
21
|
}
|
|
18
22
|
|
|
19
23
|
type ResponseBody = EmailAddressSettings;
|
|
@@ -35,19 +39,75 @@ export class GetEmailAddressEndpoint extends Endpoint<Params, Query, Body, Respo
|
|
|
35
39
|
}
|
|
36
40
|
|
|
37
41
|
async handle(request: DecodedRequest<Params, Query, Body>) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
if (request.query.id) {
|
|
43
|
+
if (!request.query.token) {
|
|
44
|
+
throw new SimpleError({
|
|
45
|
+
code: 'missing_field',
|
|
46
|
+
message: 'Missing token',
|
|
47
|
+
field: 'token',
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const email = await EmailAddress.getByID(request.query.id);
|
|
52
|
+
if (!email || email.token !== request.query.token || request.query.token.length < 10 || request.query.id.length < 10) {
|
|
53
|
+
throw new SimpleError({
|
|
54
|
+
code: 'invalid_fields',
|
|
55
|
+
message: 'Invalid token or id',
|
|
56
|
+
human: $t(`ceacb5a8-7777-4366-abcb-9dd90ffb832e`),
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const organization = email.organizationId ? (await Organization.getByID(email.organizationId)) : undefined;
|
|
61
|
+
return new Response(EmailAddressSettings.create({
|
|
62
|
+
...email,
|
|
63
|
+
organization: organization ? OrganizationSimple.create(organization) : null,
|
|
64
|
+
}));
|
|
45
65
|
}
|
|
66
|
+
else {
|
|
67
|
+
if (!request.query.email) {
|
|
68
|
+
throw new SimpleError({
|
|
69
|
+
code: 'missing_field',
|
|
70
|
+
message: 'Missing email or id',
|
|
71
|
+
field: 'email',
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const organization = await Context.setOptionalOrganizationScope();
|
|
76
|
+
await Context.authenticate();
|
|
77
|
+
|
|
78
|
+
if (!Context.auth.hasPlatformFullAccess()) {
|
|
79
|
+
throw Context.auth.error();
|
|
80
|
+
}
|
|
46
81
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
82
|
+
const query = EmailAddress.select().where(
|
|
83
|
+
'email', request.query.email,
|
|
84
|
+
);
|
|
85
|
+
if (organization) {
|
|
86
|
+
query.andWhere('organizationId', organization.id);
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
// No need
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const emails = await query.fetch();
|
|
93
|
+
|
|
94
|
+
if (emails.length === 0) {
|
|
95
|
+
throw new SimpleError({
|
|
96
|
+
code: 'not_found',
|
|
97
|
+
message: 'Email not found',
|
|
98
|
+
human: $t(`9ddb6616-f62d-4c91-82a9-e5cf398e4c4a`),
|
|
99
|
+
statusCode: 404,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return new Response(EmailAddressSettings.create({
|
|
104
|
+
email: request.query.email,
|
|
105
|
+
unsubscribedAll: !!emails.find(e => e.unsubscribedAll),
|
|
106
|
+
unsubscribedMarketing: !!emails.find(e => e.unsubscribedMarketing),
|
|
107
|
+
hardBounce: !!emails.find(e => e.hardBounce),
|
|
108
|
+
markedAsSpam: !!emails.find(e => e.markedAsSpam),
|
|
109
|
+
organization: organization ? OrganizationSimple.create(organization) : null,
|
|
110
|
+
}));
|
|
111
|
+
}
|
|
52
112
|
}
|
|
53
113
|
}
|
|
@@ -2,22 +2,39 @@ import { AutoEncoder, BooleanDecoder, Decoder, field, StringDecoder } from '@sim
|
|
|
2
2
|
import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
|
|
3
3
|
import { SimpleError } from '@simonbackx/simple-errors';
|
|
4
4
|
import { EmailAddress } from '@stamhoofd/email';
|
|
5
|
+
import { Context } from '../../../helpers/Context';
|
|
6
|
+
import { SESv2Client, DeleteSuppressedDestinationCommand } from '@aws-sdk/client-sesv2'; // ES Modules import
|
|
5
7
|
|
|
6
8
|
type Params = Record<string, never>;
|
|
7
9
|
type Query = undefined;
|
|
8
10
|
|
|
9
11
|
class Body extends AutoEncoder {
|
|
10
|
-
@field({ decoder: StringDecoder })
|
|
11
|
-
id: string;
|
|
12
|
+
@field({ decoder: StringDecoder, nullable: true, optional: true })
|
|
13
|
+
id: string | null = null;
|
|
12
14
|
|
|
13
|
-
@field({ decoder: StringDecoder })
|
|
14
|
-
token: string;
|
|
15
|
+
@field({ decoder: StringDecoder, nullable: true, optional: true })
|
|
16
|
+
token: string | null = null;
|
|
17
|
+
|
|
18
|
+
@field({ decoder: StringDecoder, nullable: true, optional: true })
|
|
19
|
+
email: string | null = null;
|
|
15
20
|
|
|
16
21
|
@field({ decoder: BooleanDecoder, optional: true })
|
|
17
22
|
unsubscribedMarketing?: boolean;
|
|
18
23
|
|
|
19
24
|
@field({ decoder: BooleanDecoder, optional: true })
|
|
20
25
|
unsubscribedAll?: boolean;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Set to false to unblock
|
|
29
|
+
*/
|
|
30
|
+
@field({ decoder: BooleanDecoder, optional: true })
|
|
31
|
+
hardBounce?: boolean;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Set to false to unblock
|
|
35
|
+
*/
|
|
36
|
+
@field({ decoder: BooleanDecoder, optional: true })
|
|
37
|
+
markedAsSpam?: boolean;
|
|
21
38
|
}
|
|
22
39
|
|
|
23
40
|
type ResponseBody = undefined;
|
|
@@ -39,19 +56,90 @@ export class ManageEmailAddressEndpoint extends Endpoint<Params, Query, Body, Re
|
|
|
39
56
|
}
|
|
40
57
|
|
|
41
58
|
async handle(request: DecodedRequest<Params, Query, Body>) {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
59
|
+
if (request.body.id) {
|
|
60
|
+
if (!request.body.token) {
|
|
61
|
+
throw new SimpleError({
|
|
62
|
+
code: 'missing_field',
|
|
63
|
+
message: 'Missing token',
|
|
64
|
+
field: 'token',
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
const email = await EmailAddress.getByID(request.body.id);
|
|
68
|
+
if (!email || email.token !== request.body.token || request.body.token.length < 10 || request.body.id.length < 10) {
|
|
69
|
+
throw new SimpleError({
|
|
70
|
+
code: 'invalid_fields',
|
|
71
|
+
message: 'Invalid token or id',
|
|
72
|
+
human: $t(`ceacb5a8-7777-4366-abcb-9dd90ffb832e`),
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
email.unsubscribedAll = request.body.unsubscribedAll ?? email.unsubscribedAll;
|
|
77
|
+
email.unsubscribedMarketing = request.body.unsubscribedMarketing ?? email.unsubscribedMarketing;
|
|
78
|
+
|
|
79
|
+
await email.save();
|
|
80
|
+
return new Response(undefined);
|
|
49
81
|
}
|
|
82
|
+
else {
|
|
83
|
+
if (!request.body.email) {
|
|
84
|
+
throw new SimpleError({
|
|
85
|
+
code: 'missing_field',
|
|
86
|
+
message: 'Missing email or id',
|
|
87
|
+
field: 'email',
|
|
88
|
+
});
|
|
89
|
+
}
|
|
50
90
|
|
|
51
|
-
|
|
52
|
-
|
|
91
|
+
const organization = await Context.setOptionalOrganizationScope();
|
|
92
|
+
await Context.authenticate();
|
|
53
93
|
|
|
54
|
-
|
|
55
|
-
|
|
94
|
+
if (!Context.auth.hasPlatformFullAccess()) {
|
|
95
|
+
throw Context.auth.error();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const query = EmailAddress.select().where(
|
|
99
|
+
'email', request.body.email,
|
|
100
|
+
);
|
|
101
|
+
if (organization) {
|
|
102
|
+
query.andWhere('organizationId', organization.id);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const emails = await query.fetch();
|
|
106
|
+
|
|
107
|
+
if (emails.length === 0) {
|
|
108
|
+
throw new SimpleError({
|
|
109
|
+
code: 'not_found',
|
|
110
|
+
message: 'Email not found',
|
|
111
|
+
human: $t(`9ddb6616-f62d-4c91-82a9-e5cf398e4c4a`),
|
|
112
|
+
statusCode: 404,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
console.log('Updated email address settings', request.body);
|
|
116
|
+
const client = new SESv2Client({});
|
|
117
|
+
|
|
118
|
+
if (request.body.hardBounce === false || request.body.markedAsSpam === false) {
|
|
119
|
+
// Remove from AWS suppression list
|
|
120
|
+
// todo
|
|
121
|
+
try {
|
|
122
|
+
const input = {
|
|
123
|
+
EmailAddress: request.body.email,
|
|
124
|
+
};
|
|
125
|
+
const command = new DeleteSuppressedDestinationCommand(input);
|
|
126
|
+
const response = await client.send(command);
|
|
127
|
+
console.log('Removed from AWS suppression list', request.body.email, response);
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
console.error('Error removing from suppression list', request.body.email, error);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
for (const email of emails) {
|
|
135
|
+
email.unsubscribedAll = request.body.unsubscribedAll ?? email.unsubscribedAll;
|
|
136
|
+
email.unsubscribedMarketing = request.body.unsubscribedMarketing ?? email.unsubscribedMarketing;
|
|
137
|
+
email.hardBounce = request.body.hardBounce ?? email.hardBounce;
|
|
138
|
+
email.markedAsSpam = request.body.markedAsSpam ?? email.markedAsSpam;
|
|
139
|
+
await email.save();
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return new Response(undefined);
|
|
143
|
+
}
|
|
56
144
|
}
|
|
57
145
|
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
|
|
2
|
+
import { Email, EmailRecipient } from '@stamhoofd/models';
|
|
3
|
+
import { EmailRecipient as EmailRecipientStruct, EmailStatus, PermissionLevel } from '@stamhoofd/structures';
|
|
4
|
+
|
|
5
|
+
import { SimpleError } from '@simonbackx/simple-errors';
|
|
6
|
+
import { Context } from '../../../helpers/Context';
|
|
7
|
+
|
|
8
|
+
type Params = { id: string };
|
|
9
|
+
type Query = undefined;
|
|
10
|
+
type Body = undefined;
|
|
11
|
+
type ResponseBody = EmailRecipientStruct;
|
|
12
|
+
|
|
13
|
+
export class RetryEmailRecipientEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
14
|
+
protected doesMatch(request: Request): [true, Params] | [false] {
|
|
15
|
+
if (request.method !== 'POST') {
|
|
16
|
+
return [false];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const params = Endpoint.parseParameters(request.url, '/email-recipients/@id/retry', { id: String });
|
|
20
|
+
|
|
21
|
+
if (params) {
|
|
22
|
+
return [true, params as Params];
|
|
23
|
+
}
|
|
24
|
+
return [false];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async handle(request: DecodedRequest<Params, Query, Body>) {
|
|
28
|
+
const organization = await Context.setOptionalOrganizationScope();
|
|
29
|
+
await Context.authenticate();
|
|
30
|
+
|
|
31
|
+
if (!await Context.auth.canReadEmails(organization)) {
|
|
32
|
+
// Fast fail before query
|
|
33
|
+
throw Context.auth.error();
|
|
34
|
+
}
|
|
35
|
+
const emailRecipient = await EmailRecipient.getByID(request.params.id);
|
|
36
|
+
if (!emailRecipient || emailRecipient.organizationId !== (organization?.id ?? null)) {
|
|
37
|
+
throw new SimpleError({
|
|
38
|
+
code: 'not_found',
|
|
39
|
+
message: 'Email recipient not found',
|
|
40
|
+
human: $t(`2e79bf4a-878b-43b0-902b-9d080c8b7fdf`),
|
|
41
|
+
statusCode: 404,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const model = await Email.getByID(emailRecipient.emailId);
|
|
46
|
+
if (!model || (model.organizationId !== (organization?.id ?? null))) {
|
|
47
|
+
throw new SimpleError({
|
|
48
|
+
code: 'not_found',
|
|
49
|
+
message: 'Email not found',
|
|
50
|
+
human: $t(`9ddb6616-f62d-4c91-82a9-e5cf398e4c4a`),
|
|
51
|
+
statusCode: 404,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (!await Context.auth.canAccessEmail(model, PermissionLevel.Write)) {
|
|
56
|
+
throw Context.auth.error();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (model.status !== EmailStatus.Sent) {
|
|
60
|
+
throw new SimpleError({
|
|
61
|
+
code: 'not_sent',
|
|
62
|
+
message: 'Cant retry email that is not sent',
|
|
63
|
+
statusCode: 400,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (emailRecipient.sentAt) {
|
|
68
|
+
throw new SimpleError({
|
|
69
|
+
code: 'already_sent',
|
|
70
|
+
message: 'Cant retry email recipient that is already sent',
|
|
71
|
+
human: $t(`f3f837eb-d6f7-4c6e-9477-d566a03e09b1`),
|
|
72
|
+
statusCode: 400,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Retry
|
|
77
|
+
await model.resumeSending(emailRecipient.id);
|
|
78
|
+
|
|
79
|
+
await emailRecipient.refresh();
|
|
80
|
+
|
|
81
|
+
return new Response((await EmailRecipient.getStructures([emailRecipient]))[0]);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { ConvertArrayToPatchableArray, Decoder, PatchableArrayAutoEncoder, PatchableArrayDecoder, StringDecoder, patchObject } from '@simonbackx/simple-encoding';
|
|
2
2
|
import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
|
|
3
|
-
import {
|
|
3
|
+
import { RegistrationPeriod as RegistrationPeriodStruct } from '@stamhoofd/structures';
|
|
4
4
|
|
|
5
5
|
import { SimpleError } from '@simonbackx/simple-errors';
|
|
6
6
|
import { Organization, Platform, RegistrationPeriod } from '@stamhoofd/models';
|
|
7
7
|
import { Context } from '../../../helpers/Context';
|
|
8
8
|
import { PeriodHelper } from '../../../helpers/PeriodHelper';
|
|
9
|
-
import { AuditLogService } from '../../../services/AuditLogService';
|
|
10
9
|
|
|
11
10
|
type Params = Record<string, never>;
|
|
12
11
|
type Query = undefined;
|
|
@@ -71,8 +70,17 @@ export class PatchRegistrationPeriodsEndpoint extends Endpoint<Params, Query, Bo
|
|
|
71
70
|
const periods: RegistrationPeriod[] = [];
|
|
72
71
|
|
|
73
72
|
for (const { put } of request.body.getPuts()) {
|
|
73
|
+
if (put.endDate < put.startDate) {
|
|
74
|
+
throw new SimpleError({
|
|
75
|
+
code: 'invalid_field',
|
|
76
|
+
message: $t('De einddatum moet na de startdatum liggen.'),
|
|
77
|
+
field: 'endDate',
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
74
81
|
const period = new RegistrationPeriod();
|
|
75
82
|
period.id = put.id;
|
|
83
|
+
period.customName = put.customName;
|
|
76
84
|
period.startDate = put.startDate;
|
|
77
85
|
period.endDate = put.endDate;
|
|
78
86
|
period.locked = put.locked;
|
|
@@ -129,6 +137,15 @@ export class PatchRegistrationPeriodsEndpoint extends Endpoint<Params, Query, Bo
|
|
|
129
137
|
model.endDate = patch.endDate;
|
|
130
138
|
}
|
|
131
139
|
|
|
140
|
+
// only check if start or end date is patched
|
|
141
|
+
if ((patch.startDate || patch.endDate) && model.endDate < model.startDate) {
|
|
142
|
+
throw new SimpleError({
|
|
143
|
+
code: 'invalid_field',
|
|
144
|
+
message: $t('De einddatum moet na de startdatum liggen.'),
|
|
145
|
+
field: 'endDate',
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
132
149
|
if (patch.locked !== undefined) {
|
|
133
150
|
model.locked = patch.locked;
|
|
134
151
|
}
|
|
@@ -137,6 +154,10 @@ export class PatchRegistrationPeriodsEndpoint extends Endpoint<Params, Query, Bo
|
|
|
137
154
|
model.settings = patchObject(model.settings, patch.settings);
|
|
138
155
|
}
|
|
139
156
|
|
|
157
|
+
if (patch.customName !== undefined) {
|
|
158
|
+
model.customName = patch.customName;
|
|
159
|
+
}
|
|
160
|
+
|
|
140
161
|
await model.updatePreviousNextPeriods();
|
|
141
162
|
await model.save();
|
|
142
163
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
+
import { ArrayDecoder, field } from '@simonbackx/simple-encoding';
|
|
1
2
|
import { XlsxTransformerSheet } from '@stamhoofd/excel-writer';
|
|
2
|
-
import { Platform as PlatformStruct, ExcelExportType, LimitedFilteredRequest, Organization as OrganizationStruct, MemberResponsibilityRecord as MemberResponsibilityRecordStruct, PaginatedResponse, MemberWithRegistrationsBlob, Premise } from '@stamhoofd/structures';
|
|
3
|
-
import { GetOrganizationsEndpoint } from '../endpoints/admin/organizations/GetOrganizationsEndpoint';
|
|
4
|
-
import { ExportToExcelEndpoint } from '../endpoints/global/files/ExportToExcelEndpoint';
|
|
5
|
-
import { XlsxTransformerColumnHelper } from '../helpers/XlsxTransformerColumnHelper';
|
|
6
3
|
import { Group, Member, MemberResponsibilityRecord } from '@stamhoofd/models';
|
|
4
|
+
import { ExcelExportType, LimitedFilteredRequest, MemberResponsibilityRecord as MemberResponsibilityRecordStruct, MemberWithRegistrationsBlob, Organization as OrganizationStruct, PaginatedResponse, Platform as PlatformStruct, Premise } from '@stamhoofd/structures';
|
|
7
5
|
import { Formatter, Sorter } from '@stamhoofd/utility';
|
|
8
|
-
import {
|
|
6
|
+
import { GetOrganizationsEndpoint } from '../endpoints/admin/organizations/GetOrganizationsEndpoint';
|
|
7
|
+
import { ExportToExcelEndpoint } from '../endpoints/global/files/ExportToExcelEndpoint';
|
|
9
8
|
import { AuthenticatedStructures } from '../helpers/AuthenticatedStructures';
|
|
9
|
+
import { XlsxTransformerColumnHelper } from '../helpers/XlsxTransformerColumnHelper';
|
|
10
10
|
|
|
11
11
|
class MemberResponsibilityRecordWithMember extends MemberResponsibilityRecordStruct {
|
|
12
12
|
@field({ decoder: MemberWithRegistrationsBlob })
|
|
@@ -70,6 +70,18 @@ const sheet: XlsxTransformerSheet<Object, Object> = {
|
|
|
70
70
|
matchId: 'address',
|
|
71
71
|
getAddress: object => object.address,
|
|
72
72
|
}),
|
|
73
|
+
// Dynamic records
|
|
74
|
+
XlsxTransformerColumnHelper.createRecordAnswersColumns({
|
|
75
|
+
matchId: 'recordAnswers',
|
|
76
|
+
getRecordAnswers: (object: Object) => object.getRecordAnswers(),
|
|
77
|
+
getRecordCategories: () => {
|
|
78
|
+
const platform = PlatformStruct.shared;
|
|
79
|
+
|
|
80
|
+
return [
|
|
81
|
+
...platform.config.organizationLevelRecordsConfiguration.recordCategories,
|
|
82
|
+
];
|
|
83
|
+
},
|
|
84
|
+
}),
|
|
73
85
|
],
|
|
74
86
|
};
|
|
75
87
|
|