@stamhoofd/backend 2.15.0 → 2.17.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/.env.template.json +2 -1
- package/index.ts +15 -1
- package/package.json +14 -12
- package/src/email-recipient-loaders/members.ts +61 -0
- package/src/endpoints/admin/memberships/GetChargeMembershipsSummaryEndpoint.ts +1 -1
- package/src/endpoints/admin/organizations/GetOrganizationsEndpoint.ts +5 -183
- package/src/endpoints/global/files/ExportToExcelEndpoint.ts +163 -0
- package/src/endpoints/global/files/GetFileCache.ts +69 -0
- package/src/endpoints/global/files/UploadFile.ts +4 -1
- package/src/endpoints/global/files/UploadImage.ts +14 -2
- package/src/endpoints/global/members/GetMembersEndpoint.ts +12 -299
- package/src/endpoints/organization/dashboard/email/EmailEndpoint.ts +22 -2
- package/src/endpoints/organization/dashboard/payments/GetPaymentsEndpoint.ts +6 -134
- package/src/endpoints/organization/dashboard/webshops/GetWebshopOrdersEndpoint.ts +5 -3
- package/src/endpoints/organization/dashboard/webshops/GetWebshopTicketsEndpoint.ts +5 -3
- package/src/excel-loaders/members.ts +101 -0
- package/src/excel-loaders/payments.ts +539 -0
- package/src/helpers/AdminPermissionChecker.ts +0 -3
- package/src/helpers/AuthenticatedStructures.ts +2 -0
- package/src/helpers/FileCache.ts +158 -0
- package/src/helpers/fetchToAsyncIterator.ts +34 -0
- package/src/sql-filters/balance-item-payments.ts +13 -0
- package/src/sql-filters/members.ts +179 -0
- package/src/sql-filters/organizations.ts +115 -0
- package/src/sql-filters/payments.ts +78 -0
- package/src/sql-filters/registrations.ts +24 -0
- package/src/sql-sorters/members.ts +46 -0
- package/src/sql-sorters/organizations.ts +71 -0
- package/src/sql-sorters/payments.ts +50 -0
- package/tsconfig.json +3 -4
- package/src/endpoints/organization/dashboard/payments/legacy/GetPaymentsEndpoint.ts +0 -170
|
@@ -2,314 +2,23 @@
|
|
|
2
2
|
import { Decoder } from '@simonbackx/simple-encoding';
|
|
3
3
|
import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
|
|
4
4
|
import { SimpleError } from '@simonbackx/simple-errors';
|
|
5
|
-
import {
|
|
6
|
-
import { SQL,
|
|
7
|
-
import { CountFilteredRequest,
|
|
8
|
-
import { DataValidator
|
|
5
|
+
import { Member, Platform } from '@stamhoofd/models';
|
|
6
|
+
import { SQL, compileToSQLFilter, compileToSQLSorter } from "@stamhoofd/sql";
|
|
7
|
+
import { CountFilteredRequest, LimitedFilteredRequest, MembersBlob, PaginatedResponse, PermissionLevel, StamhoofdFilter, getSortFilter } from '@stamhoofd/structures';
|
|
8
|
+
import { DataValidator } from '@stamhoofd/utility';
|
|
9
9
|
|
|
10
10
|
import { AuthenticatedStructures } from '../../../helpers/AuthenticatedStructures';
|
|
11
11
|
import { Context } from '../../../helpers/Context';
|
|
12
|
-
import {
|
|
12
|
+
import { memberFilterCompilers } from '../../../sql-filters/members';
|
|
13
|
+
import { memberSorters } from '../../../sql-sorters/members';
|
|
13
14
|
|
|
14
15
|
type Params = Record<string, never>;
|
|
15
16
|
type Query = LimitedFilteredRequest;
|
|
16
17
|
type Body = undefined;
|
|
17
18
|
type ResponseBody = PaginatedResponse<MembersBlob, LimitedFilteredRequest>
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const result = await GetMembersEndpoint.buildData(query)
|
|
22
|
-
|
|
23
|
-
return new PaginatedResponse({
|
|
24
|
-
results: result.results.members.flatMap(m => m.getEmailRecipients(['member'])),
|
|
25
|
-
next: result.next
|
|
26
|
-
});
|
|
27
|
-
},
|
|
28
|
-
|
|
29
|
-
count: async (query: LimitedFilteredRequest) => {
|
|
30
|
-
query.filter = mergeFilters([query.filter, {
|
|
31
|
-
'email': {
|
|
32
|
-
$neq: null
|
|
33
|
-
}
|
|
34
|
-
}])
|
|
35
|
-
const q = await GetMembersEndpoint.buildQuery(query)
|
|
36
|
-
return await q.count();
|
|
37
|
-
}
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
Email.recipientLoaders.set(EmailRecipientFilterType.MemberParents, {
|
|
41
|
-
fetch: async (query: LimitedFilteredRequest) => {
|
|
42
|
-
const result = await GetMembersEndpoint.buildData(query)
|
|
43
|
-
|
|
44
|
-
return new PaginatedResponse({
|
|
45
|
-
results: result.results.members.flatMap(m => m.getEmailRecipients(['parents'])),
|
|
46
|
-
next: result.next
|
|
47
|
-
});
|
|
48
|
-
},
|
|
49
|
-
|
|
50
|
-
count: async (query: LimitedFilteredRequest) => {
|
|
51
|
-
const q = await GetMembersEndpoint.buildQuery(query)
|
|
52
|
-
return await q.sum(
|
|
53
|
-
SQL.jsonLength(SQL.column('details'), '$.value.parents[*].email')
|
|
54
|
-
);
|
|
55
|
-
}
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
Email.recipientLoaders.set(EmailRecipientFilterType.MemberUnverified, {
|
|
59
|
-
fetch: async (query: LimitedFilteredRequest) => {
|
|
60
|
-
const result = await GetMembersEndpoint.buildData(query)
|
|
61
|
-
|
|
62
|
-
return new PaginatedResponse({
|
|
63
|
-
results: result.results.members.flatMap(m => m.getEmailRecipients(['unverified'])),
|
|
64
|
-
next: result.next
|
|
65
|
-
});
|
|
66
|
-
},
|
|
67
|
-
|
|
68
|
-
count: async (query: LimitedFilteredRequest) => {
|
|
69
|
-
const q = await GetMembersEndpoint.buildQuery(query)
|
|
70
|
-
return await q.sum(
|
|
71
|
-
SQL.jsonLength(SQL.column('details'), '$.value.unverifiedEmails')
|
|
72
|
-
);
|
|
73
|
-
}
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
const registrationFilterCompilers: SQLFilterDefinitions = {
|
|
77
|
-
...baseSQLFilterCompilers,
|
|
78
|
-
"price": createSQLColumnFilterCompiler('price', {nullable: true}),
|
|
79
|
-
"pricePaid": createSQLColumnFilterCompiler('pricePaid'),
|
|
80
|
-
"canRegister": createSQLColumnFilterCompiler('canRegister'),
|
|
81
|
-
"organizationId": createSQLColumnFilterCompiler('organizationId'),
|
|
82
|
-
"groupId": createSQLColumnFilterCompiler('groupId'),
|
|
83
|
-
"registeredAt": createSQLColumnFilterCompiler('registeredAt', {nullable: true}),
|
|
84
|
-
"periodId": createSQLColumnFilterCompiler(SQL.column('registrations', 'periodId')),
|
|
85
|
-
|
|
86
|
-
"group": createSQLFilterNamespace({
|
|
87
|
-
...baseSQLFilterCompilers,
|
|
88
|
-
id: createSQLColumnFilterCompiler('groupId'),
|
|
89
|
-
name: createSQLExpressionFilterCompiler(
|
|
90
|
-
SQL.jsonValue(SQL.column('groups', 'settings'), '$.value.name')
|
|
91
|
-
),
|
|
92
|
-
status: createSQLExpressionFilterCompiler(
|
|
93
|
-
SQL.column('groups', 'status')
|
|
94
|
-
),
|
|
95
|
-
defaultAgeGroupId: createSQLColumnFilterCompiler(SQL.column('groups', 'defaultAgeGroupId'), {nullable: true}),
|
|
96
|
-
})
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const filterCompilers: SQLFilterDefinitions = {
|
|
100
|
-
...baseSQLFilterCompilers,
|
|
101
|
-
id: createSQLColumnFilterCompiler('id'),
|
|
102
|
-
name: createSQLExpressionFilterCompiler(
|
|
103
|
-
new SQLConcat(
|
|
104
|
-
SQL.column('firstName'),
|
|
105
|
-
new SQLScalar(' '),
|
|
106
|
-
SQL.column('lastName'),
|
|
107
|
-
)
|
|
108
|
-
),
|
|
109
|
-
age: createSQLExpressionFilterCompiler(
|
|
110
|
-
new SQLAge(SQL.column('birthDay')),
|
|
111
|
-
{nullable: true}
|
|
112
|
-
),
|
|
113
|
-
gender: createSQLExpressionFilterCompiler(
|
|
114
|
-
SQL.jsonValue(SQL.column('details'), '$.value.gender'),
|
|
115
|
-
{isJSONValue: true}
|
|
116
|
-
),
|
|
117
|
-
birthDay: createSQLColumnFilterCompiler('birthDay', {
|
|
118
|
-
normalizeValue: (d) => {
|
|
119
|
-
if (typeof d === 'number') {
|
|
120
|
-
const date = new Date(d)
|
|
121
|
-
return Formatter.dateIso(date);
|
|
122
|
-
}
|
|
123
|
-
return d;
|
|
124
|
-
}
|
|
125
|
-
}),
|
|
126
|
-
organizationName: createSQLExpressionFilterCompiler(
|
|
127
|
-
SQL.column('organizations', 'name')
|
|
128
|
-
),
|
|
129
|
-
|
|
130
|
-
email: createSQLExpressionFilterCompiler(
|
|
131
|
-
SQL.jsonValue(SQL.column('details'), '$.value.email'),
|
|
132
|
-
{isJSONValue: true}
|
|
133
|
-
),
|
|
134
|
-
|
|
135
|
-
parentEmail: createSQLExpressionFilterCompiler(
|
|
136
|
-
SQL.jsonValue(SQL.column('details'), '$.value.parents[*].email'),
|
|
137
|
-
{isJSONValue: true, isJSONObject: true}
|
|
138
|
-
),
|
|
139
|
-
|
|
140
|
-
registrations: createSQLRelationFilterCompiler(
|
|
141
|
-
SQL.select()
|
|
142
|
-
.from(
|
|
143
|
-
SQL.table('registrations')
|
|
144
|
-
).join(
|
|
145
|
-
SQL.join(
|
|
146
|
-
SQL.table('groups')
|
|
147
|
-
).where(
|
|
148
|
-
SQL.column('groups', 'id'),
|
|
149
|
-
SQL.column('registrations', 'groupId')
|
|
150
|
-
)
|
|
151
|
-
)
|
|
152
|
-
.join(
|
|
153
|
-
SQL.join(
|
|
154
|
-
SQL.table('organizations')
|
|
155
|
-
).where(
|
|
156
|
-
SQL.column('organizations', 'id'),
|
|
157
|
-
SQL.column('registrations', 'organizationId')
|
|
158
|
-
)
|
|
159
|
-
)
|
|
160
|
-
.where(
|
|
161
|
-
SQL.column('memberId'),
|
|
162
|
-
SQL.column('members', 'id'),
|
|
163
|
-
).whereNot(
|
|
164
|
-
SQL.column('registeredAt'),
|
|
165
|
-
null,
|
|
166
|
-
).where(
|
|
167
|
-
SQL.column('deactivatedAt'),
|
|
168
|
-
null,
|
|
169
|
-
).where(
|
|
170
|
-
SQL.column('groups', 'deletedAt'),
|
|
171
|
-
null
|
|
172
|
-
),
|
|
173
|
-
{
|
|
174
|
-
...registrationFilterCompilers,
|
|
175
|
-
"organization": createSQLFilterNamespace(organizationFilterCompilers)
|
|
176
|
-
}
|
|
177
|
-
),
|
|
178
|
-
|
|
179
|
-
responsibilities: createSQLRelationFilterCompiler(
|
|
180
|
-
SQL.select()
|
|
181
|
-
.from(
|
|
182
|
-
SQL.table('member_responsibility_records')
|
|
183
|
-
)
|
|
184
|
-
.join(
|
|
185
|
-
SQL.leftJoin(
|
|
186
|
-
SQL.table('groups')
|
|
187
|
-
).where(
|
|
188
|
-
SQL.column('groups', 'id'),
|
|
189
|
-
SQL.column('member_responsibility_records', 'groupId')
|
|
190
|
-
)
|
|
191
|
-
)
|
|
192
|
-
.where(
|
|
193
|
-
SQL.column('memberId'),
|
|
194
|
-
SQL.column('members', 'id'),
|
|
195
|
-
),
|
|
196
|
-
{
|
|
197
|
-
...baseSQLFilterCompilers,
|
|
198
|
-
// Alias for responsibilityId
|
|
199
|
-
"id": createSQLColumnFilterCompiler(SQL.column('member_responsibility_records', 'responsibilityId')),
|
|
200
|
-
"responsibilityId": createSQLColumnFilterCompiler(SQL.column('member_responsibility_records', 'responsibilityId')),
|
|
201
|
-
"organizationId": createSQLColumnFilterCompiler(SQL.column('member_responsibility_records', 'organizationId')),
|
|
202
|
-
"startDate": createSQLColumnFilterCompiler(SQL.column('member_responsibility_records', 'startDate')),
|
|
203
|
-
"endDate": createSQLColumnFilterCompiler(SQL.column('member_responsibility_records', 'endDate')),
|
|
204
|
-
"group": createSQLFilterNamespace({
|
|
205
|
-
...baseSQLFilterCompilers,
|
|
206
|
-
id: createSQLColumnFilterCompiler(SQL.column('groups', 'id')),
|
|
207
|
-
defaultAgeGroupId: createSQLColumnFilterCompiler(SQL.column('groups', 'defaultAgeGroupId')),
|
|
208
|
-
})
|
|
209
|
-
}
|
|
210
|
-
),
|
|
211
|
-
|
|
212
|
-
platformMemberships: createSQLRelationFilterCompiler(
|
|
213
|
-
SQL.select()
|
|
214
|
-
.from(
|
|
215
|
-
SQL.table('member_platform_memberships')
|
|
216
|
-
)
|
|
217
|
-
.where(
|
|
218
|
-
SQL.column('memberId'),
|
|
219
|
-
SQL.column('members', 'id'),
|
|
220
|
-
)
|
|
221
|
-
.where(
|
|
222
|
-
SQL.column('deletedAt'),
|
|
223
|
-
null,
|
|
224
|
-
),
|
|
225
|
-
{
|
|
226
|
-
...baseSQLFilterCompilers,
|
|
227
|
-
"id": createSQLColumnFilterCompiler(SQL.column('member_platform_memberships', 'id')),
|
|
228
|
-
"membershipTypeId": createSQLColumnFilterCompiler(SQL.column('member_platform_memberships', 'membershipTypeId')),
|
|
229
|
-
"organizationId": createSQLColumnFilterCompiler(SQL.column('member_platform_memberships', 'organizationId')),
|
|
230
|
-
"periodId": createSQLColumnFilterCompiler(SQL.column('member_platform_memberships', 'periodId')),
|
|
231
|
-
"price": createSQLColumnFilterCompiler(SQL.column('member_platform_memberships', 'price')),
|
|
232
|
-
"invoiceId": createSQLColumnFilterCompiler(SQL.column('member_platform_memberships', 'invoiceId')),
|
|
233
|
-
"startDate": createSQLColumnFilterCompiler(SQL.column('member_platform_memberships', 'startDate')),
|
|
234
|
-
"endDate": createSQLColumnFilterCompiler(SQL.column('member_platform_memberships', 'endDate')),
|
|
235
|
-
"expireDate": createSQLColumnFilterCompiler(SQL.column('member_platform_memberships', 'expireDate')),
|
|
236
|
-
}
|
|
237
|
-
),
|
|
238
|
-
|
|
239
|
-
organizations: createSQLRelationFilterCompiler(
|
|
240
|
-
SQL.select()
|
|
241
|
-
.from(
|
|
242
|
-
SQL.table('registrations')
|
|
243
|
-
).join(
|
|
244
|
-
SQL.join(
|
|
245
|
-
SQL.table('groups')
|
|
246
|
-
).where(
|
|
247
|
-
SQL.column('groups', 'id'),
|
|
248
|
-
SQL.column('registrations', 'groupId')
|
|
249
|
-
)
|
|
250
|
-
).join(
|
|
251
|
-
SQL.join(
|
|
252
|
-
SQL.table('organizations')
|
|
253
|
-
).where(
|
|
254
|
-
SQL.column('organizations', 'id'),
|
|
255
|
-
SQL.column('registrations', 'organizationId')
|
|
256
|
-
)
|
|
257
|
-
).where(
|
|
258
|
-
SQL.column('memberId'),
|
|
259
|
-
SQL.column('members', 'id'),
|
|
260
|
-
).whereNot(
|
|
261
|
-
SQL.column('registeredAt'),
|
|
262
|
-
null,
|
|
263
|
-
).where(
|
|
264
|
-
SQL.column('groups', 'deletedAt'),
|
|
265
|
-
null
|
|
266
|
-
),
|
|
267
|
-
organizationFilterCompilers
|
|
268
|
-
),
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
const sorters: SQLSortDefinitions<MemberWithRegistrations> = {
|
|
272
|
-
'id': {
|
|
273
|
-
getValue(a) {
|
|
274
|
-
return a.id
|
|
275
|
-
},
|
|
276
|
-
toSQL: (direction: SQLOrderByDirection): SQLOrderBy => {
|
|
277
|
-
return new SQLOrderBy({
|
|
278
|
-
column: SQL.column('id'),
|
|
279
|
-
direction
|
|
280
|
-
})
|
|
281
|
-
}
|
|
282
|
-
},
|
|
283
|
-
'name': {
|
|
284
|
-
getValue(a) {
|
|
285
|
-
return a.details.name
|
|
286
|
-
},
|
|
287
|
-
toSQL: (direction: SQLOrderByDirection): SQLOrderBy => {
|
|
288
|
-
return SQLOrderBy.combine([
|
|
289
|
-
new SQLOrderBy({
|
|
290
|
-
column: SQL.column('firstName'),
|
|
291
|
-
direction
|
|
292
|
-
}),
|
|
293
|
-
new SQLOrderBy({
|
|
294
|
-
column: SQL.column('lastName'),
|
|
295
|
-
direction
|
|
296
|
-
})
|
|
297
|
-
])
|
|
298
|
-
}
|
|
299
|
-
},
|
|
300
|
-
'birthDay': {
|
|
301
|
-
getValue(a) {
|
|
302
|
-
return a.details.birthDay ? Formatter.dateIso(a.details.birthDay) : null
|
|
303
|
-
},
|
|
304
|
-
toSQL: (direction: SQLOrderByDirection): SQLOrderBy => {
|
|
305
|
-
return new SQLOrderBy({
|
|
306
|
-
column: SQL.column('birthDay'),
|
|
307
|
-
direction
|
|
308
|
-
})
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
// Note: never add mapped sortings, that should happen in the frontend -> e.g. map age to birthDay
|
|
312
|
-
}
|
|
20
|
+
const sorters = memberSorters
|
|
21
|
+
const filterCompilers = memberFilterCompilers
|
|
313
22
|
|
|
314
23
|
export class GetMembersEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
315
24
|
queryDecoder = LimitedFilteredRequest as Decoder<LimitedFilteredRequest>
|
|
@@ -365,6 +74,10 @@ export class GetMembersEndpoint extends Endpoint<Params, Query, Body, ResponseBo
|
|
|
365
74
|
// Add organization scope filter
|
|
366
75
|
const groups = await Context.auth.getAccessibleGroups(organization.id)
|
|
367
76
|
|
|
77
|
+
if (groups.length === 0) {
|
|
78
|
+
throw Context.auth.error()
|
|
79
|
+
}
|
|
80
|
+
|
|
368
81
|
if (groups === 'all') {
|
|
369
82
|
if (await Context.auth.hasFullAccess(organization.id)) {
|
|
370
83
|
// Can access full history for now
|
|
@@ -196,9 +196,29 @@ export class EmailEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
|
196
196
|
|
|
197
197
|
const email = request.body
|
|
198
198
|
|
|
199
|
+
if (!email.html) {
|
|
200
|
+
throw new SimpleError({
|
|
201
|
+
code: "missing_field",
|
|
202
|
+
message: "Missing html",
|
|
203
|
+
human: "Je hebt geen inhoud ingevuld voor je e-mail. Vul een bericht in en probeer opnieuw.",
|
|
204
|
+
field: "html"
|
|
205
|
+
})
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (!email.subject) {
|
|
209
|
+
throw new SimpleError({
|
|
210
|
+
code: "missing_field",
|
|
211
|
+
message: "Missing subject",
|
|
212
|
+
human: "Je hebt geen onderwerp ingevuld voor je e-mail. Vul een onderwerp in en probeer opnieuw.",
|
|
213
|
+
field: "subject"
|
|
214
|
+
})
|
|
215
|
+
}
|
|
216
|
+
|
|
199
217
|
// Create e-mail builder
|
|
200
218
|
const builder = await getEmailBuilder(organization, {
|
|
201
|
-
|
|
219
|
+
subject: email.subject,
|
|
220
|
+
html: email.html,
|
|
221
|
+
recipients: email.recipients,
|
|
202
222
|
from,
|
|
203
223
|
replyTo,
|
|
204
224
|
attachments,
|
|
@@ -218,7 +238,7 @@ export class EmailEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
|
218
238
|
const builder2 = await getEmailBuilder(organization, {
|
|
219
239
|
...email,
|
|
220
240
|
subject: "[KOPIE] "+email.subject,
|
|
221
|
-
html: email.html
|
|
241
|
+
html: email.html.replace("<body>", "<body>"+prefix),
|
|
222
242
|
recipients: [
|
|
223
243
|
recipient
|
|
224
244
|
],
|
|
@@ -3,149 +3,21 @@ import { Decoder } from '@simonbackx/simple-encoding';
|
|
|
3
3
|
import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
|
|
4
4
|
import { SimpleError } from '@simonbackx/simple-errors';
|
|
5
5
|
import { Payment } from '@stamhoofd/models';
|
|
6
|
-
import { SQL,
|
|
6
|
+
import { SQL, compileToSQLFilter, compileToSQLSorter } from "@stamhoofd/sql";
|
|
7
7
|
import { CountFilteredRequest, LimitedFilteredRequest, PaginatedResponse, PaymentGeneral, StamhoofdFilter, getSortFilter } from '@stamhoofd/structures';
|
|
8
|
-
import { Formatter } from '@stamhoofd/utility';
|
|
9
8
|
|
|
10
9
|
import { AuthenticatedStructures } from '../../../../helpers/AuthenticatedStructures';
|
|
11
10
|
import { Context } from '../../../../helpers/Context';
|
|
11
|
+
import { paymentFilterCompilers } from '../../../../sql-filters/payments';
|
|
12
|
+
import { paymentSorters } from '../../../../sql-sorters/payments';
|
|
12
13
|
|
|
13
14
|
type Params = Record<string, never>;
|
|
14
15
|
type Query = LimitedFilteredRequest;
|
|
15
16
|
type Body = undefined;
|
|
16
17
|
type ResponseBody = PaginatedResponse<PaymentGeneral[], LimitedFilteredRequest>
|
|
17
18
|
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
"id": createSQLColumnFilterCompiler(SQL.column('balance_item_payments', 'id')),
|
|
21
|
-
"price": createSQLColumnFilterCompiler(SQL.column('balance_item_payments', 'price')),
|
|
22
|
-
|
|
23
|
-
"balanceItem": createSQLFilterNamespace({
|
|
24
|
-
...baseSQLFilterCompilers,
|
|
25
|
-
id: createSQLColumnFilterCompiler(SQL.column('balance_items', 'id')),
|
|
26
|
-
description: createSQLColumnFilterCompiler(SQL.column('balance_items', 'description')),
|
|
27
|
-
})
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const filterCompilers: SQLFilterDefinitions = {
|
|
31
|
-
...baseSQLFilterCompilers,
|
|
32
|
-
id: createSQLColumnFilterCompiler('id'),
|
|
33
|
-
method: createSQLColumnFilterCompiler('method'),
|
|
34
|
-
status: createSQLColumnFilterCompiler('status'),
|
|
35
|
-
organizationId: createSQLColumnFilterCompiler('organizationId'),
|
|
36
|
-
createdAt: createSQLColumnFilterCompiler('createdAt'),
|
|
37
|
-
updatedAt: createSQLColumnFilterCompiler('updatedAt'),
|
|
38
|
-
paidAt: createSQLColumnFilterCompiler('paidAt', {nullable: true}),
|
|
39
|
-
price: createSQLColumnFilterCompiler('price'),
|
|
40
|
-
provider: createSQLColumnFilterCompiler('provider', {nullable: true}),
|
|
41
|
-
customer: createSQLFilterNamespace({
|
|
42
|
-
...baseSQLFilterCompilers,
|
|
43
|
-
email: createSQLExpressionFilterCompiler(
|
|
44
|
-
SQL.jsonValue(SQL.column('customer'), '$.value.email'),
|
|
45
|
-
{isJSONValue: true}
|
|
46
|
-
),
|
|
47
|
-
firstName: createSQLExpressionFilterCompiler(
|
|
48
|
-
SQL.jsonValue(SQL.column('customer'), '$.value.firstName'),
|
|
49
|
-
{isJSONValue: true}
|
|
50
|
-
),
|
|
51
|
-
lastName: createSQLExpressionFilterCompiler(
|
|
52
|
-
SQL.jsonValue(SQL.column('customer'), '$.value.lastName'),
|
|
53
|
-
{isJSONValue: true}
|
|
54
|
-
),
|
|
55
|
-
name: createSQLExpressionFilterCompiler(
|
|
56
|
-
new SQLCast(
|
|
57
|
-
new SQLConcat(
|
|
58
|
-
new SQLJsonUnquote(SQL.jsonValue(SQL.column('customer'), '$.value.firstName')),
|
|
59
|
-
new SQLScalar(' '),
|
|
60
|
-
new SQLJsonUnquote(SQL.jsonValue(SQL.column('customer'), '$.value.lastName')),
|
|
61
|
-
),
|
|
62
|
-
'CHAR'
|
|
63
|
-
)
|
|
64
|
-
),
|
|
65
|
-
company: createSQLFilterNamespace({
|
|
66
|
-
name: createSQLExpressionFilterCompiler(
|
|
67
|
-
SQL.jsonValue(SQL.column('customer'), '$.value.company.name'),
|
|
68
|
-
{isJSONValue: true}
|
|
69
|
-
),
|
|
70
|
-
VATNumber: createSQLExpressionFilterCompiler(
|
|
71
|
-
SQL.jsonValue(SQL.column('customer'), '$.value.company.VATNumber'),
|
|
72
|
-
{isJSONValue: true}
|
|
73
|
-
),
|
|
74
|
-
companyNumber: createSQLExpressionFilterCompiler(
|
|
75
|
-
SQL.jsonValue(SQL.column('customer'), '$.value.company.companyNumber'),
|
|
76
|
-
{isJSONValue: true}
|
|
77
|
-
),
|
|
78
|
-
administrationEmail: createSQLExpressionFilterCompiler(
|
|
79
|
-
SQL.jsonValue(SQL.column('customer'), '$.value.company.administrationEmail'),
|
|
80
|
-
{isJSONValue: true}
|
|
81
|
-
),
|
|
82
|
-
})
|
|
83
|
-
}),
|
|
84
|
-
balanceItemPayments: createSQLRelationFilterCompiler(
|
|
85
|
-
SQL.select()
|
|
86
|
-
.from(
|
|
87
|
-
SQL.table('balance_item_payments')
|
|
88
|
-
).join(
|
|
89
|
-
SQL.join(
|
|
90
|
-
SQL.table('balance_items')
|
|
91
|
-
).where(
|
|
92
|
-
SQL.column('balance_items', 'id'),
|
|
93
|
-
SQL.column('balance_item_payments', 'balanceItemId')
|
|
94
|
-
)
|
|
95
|
-
).where(
|
|
96
|
-
SQL.column('paymentId'),
|
|
97
|
-
SQL.column('payments', 'id')
|
|
98
|
-
),
|
|
99
|
-
balanceItemPaymentsCompilers
|
|
100
|
-
),
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
const sorters: SQLSortDefinitions<Payment> = {
|
|
104
|
-
'id': {
|
|
105
|
-
getValue(a) {
|
|
106
|
-
return a.id
|
|
107
|
-
},
|
|
108
|
-
toSQL: (direction: SQLOrderByDirection): SQLOrderBy => {
|
|
109
|
-
return new SQLOrderBy({
|
|
110
|
-
column: SQL.column('id'),
|
|
111
|
-
direction
|
|
112
|
-
})
|
|
113
|
-
}
|
|
114
|
-
},
|
|
115
|
-
'createdAt': {
|
|
116
|
-
getValue(a) {
|
|
117
|
-
return Formatter.dateTimeIso(a.createdAt, 'UTC')
|
|
118
|
-
},
|
|
119
|
-
toSQL: (direction: SQLOrderByDirection): SQLOrderBy => {
|
|
120
|
-
return new SQLOrderBy({
|
|
121
|
-
column: SQL.column('createdAt'),
|
|
122
|
-
direction
|
|
123
|
-
})
|
|
124
|
-
}
|
|
125
|
-
},
|
|
126
|
-
'paidAt': {
|
|
127
|
-
getValue(a) {
|
|
128
|
-
return a.paidAt !== null ? Formatter.dateTimeIso(a.paidAt, 'UTC') : null
|
|
129
|
-
},
|
|
130
|
-
toSQL: (direction: SQLOrderByDirection): SQLOrderBy => {
|
|
131
|
-
return new SQLOrderBy({
|
|
132
|
-
column: SQL.column('paidAt'),
|
|
133
|
-
direction
|
|
134
|
-
})
|
|
135
|
-
}
|
|
136
|
-
},
|
|
137
|
-
'price': {
|
|
138
|
-
getValue(a) {
|
|
139
|
-
return a.price
|
|
140
|
-
},
|
|
141
|
-
toSQL: (direction: SQLOrderByDirection): SQLOrderBy => {
|
|
142
|
-
return new SQLOrderBy({
|
|
143
|
-
column: SQL.column('price'),
|
|
144
|
-
direction
|
|
145
|
-
})
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
}
|
|
19
|
+
const filterCompilers = paymentFilterCompilers
|
|
20
|
+
const sorters = paymentSorters
|
|
149
21
|
|
|
150
22
|
export class GetPaymentsEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
151
23
|
queryDecoder = LimitedFilteredRequest as Decoder<LimitedFilteredRequest>
|
|
@@ -296,7 +168,7 @@ export class GetPaymentsEndpoint extends Endpoint<Params, Query, Body, ResponseB
|
|
|
296
168
|
}
|
|
297
169
|
|
|
298
170
|
return new PaginatedResponse<PaymentGeneral[], LimitedFilteredRequest>({
|
|
299
|
-
results: await AuthenticatedStructures.paymentsGeneral(payments,
|
|
171
|
+
results: await AuthenticatedStructures.paymentsGeneral(payments, true),
|
|
300
172
|
next
|
|
301
173
|
});
|
|
302
174
|
}
|
|
@@ -26,8 +26,10 @@ export class GetWebshopOrdersEndpoint extends Endpoint<Params, Query, Body, Resp
|
|
|
26
26
|
return [false];
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
async handle(
|
|
30
|
-
|
|
29
|
+
async handle(_: DecodedRequest<Params, Query, Body>): Promise<Response<ResponseBody>> {
|
|
30
|
+
await Promise.resolve();
|
|
31
|
+
throw new Error('Not implemented');
|
|
32
|
+
/*const organization = await Context.setOrganizationScope();
|
|
31
33
|
await Context.authenticate()
|
|
32
34
|
|
|
33
35
|
// Fast throw first (more in depth checking for patches later)
|
|
@@ -78,6 +80,6 @@ export class GetWebshopOrdersEndpoint extends Endpoint<Params, Query, Body, Resp
|
|
|
78
80
|
afterNumber: orders[orders.length - 1].number ?? undefined
|
|
79
81
|
}) : undefined
|
|
80
82
|
})
|
|
81
|
-
)
|
|
83
|
+
);*/
|
|
82
84
|
}
|
|
83
85
|
}
|
|
@@ -26,8 +26,10 @@ export class GetWebshopTicketsEndpoint extends Endpoint<Params, Query, Body, Res
|
|
|
26
26
|
return [false];
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
async handle(
|
|
30
|
-
|
|
29
|
+
async handle(_: DecodedRequest<Params, Query, Body>): Promise<Response<ResponseBody>> {
|
|
30
|
+
await Promise.resolve();
|
|
31
|
+
throw new Error('Not implemented');
|
|
32
|
+
/*const organization = await Context.setOrganizationScope();
|
|
31
33
|
await Context.authenticate()
|
|
32
34
|
|
|
33
35
|
// Fast throw first (more in depth checking for patches later)
|
|
@@ -63,6 +65,6 @@ export class GetWebshopTicketsEndpoint extends Endpoint<Params, Query, Body, Res
|
|
|
63
65
|
lastId: tickets[tickets.length - 1].id ?? undefined
|
|
64
66
|
}) : undefined
|
|
65
67
|
})
|
|
66
|
-
)
|
|
68
|
+
);*/
|
|
67
69
|
}
|
|
68
70
|
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { XlsxBuiltInNumberFormat } from "@stamhoofd/excel-writer";
|
|
2
|
+
import { ExcelExportType, LimitedFilteredRequest, PaginatedResponse, MemberWithRegistrationsBlob, Platform } from "@stamhoofd/structures";
|
|
3
|
+
import { ExportToExcelEndpoint } from "../endpoints/global/files/ExportToExcelEndpoint";
|
|
4
|
+
import { GetMembersEndpoint } from "../endpoints/global/members/GetMembersEndpoint";
|
|
5
|
+
import { Context } from "../helpers/Context";
|
|
6
|
+
|
|
7
|
+
ExportToExcelEndpoint.loaders.set(ExcelExportType.Members, {
|
|
8
|
+
fetch: async (query: LimitedFilteredRequest) => {
|
|
9
|
+
const result = await GetMembersEndpoint.buildData(query)
|
|
10
|
+
|
|
11
|
+
return new PaginatedResponse({
|
|
12
|
+
results: result.results.members,
|
|
13
|
+
next: result.next
|
|
14
|
+
});
|
|
15
|
+
},
|
|
16
|
+
sheets: [
|
|
17
|
+
{
|
|
18
|
+
id: 'members',
|
|
19
|
+
name: 'Leden',
|
|
20
|
+
columns: [
|
|
21
|
+
{
|
|
22
|
+
id: 'id',
|
|
23
|
+
name: 'ID',
|
|
24
|
+
width: 20,
|
|
25
|
+
getValue: (object: MemberWithRegistrationsBlob) => ({
|
|
26
|
+
value: object.id
|
|
27
|
+
})
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
id: 'firstName',
|
|
31
|
+
name: 'Voornaam',
|
|
32
|
+
width: 20,
|
|
33
|
+
getValue: (object: MemberWithRegistrationsBlob) => ({
|
|
34
|
+
value: object.details.firstName
|
|
35
|
+
})
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
id: 'lastName',
|
|
39
|
+
name: 'Achternaam',
|
|
40
|
+
width: 20,
|
|
41
|
+
getValue: (object: MemberWithRegistrationsBlob) => ({
|
|
42
|
+
value: object.details.lastName
|
|
43
|
+
})
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
id: 'birthDay',
|
|
47
|
+
name: 'Geboortedatum',
|
|
48
|
+
width: 20,
|
|
49
|
+
getValue: (object: MemberWithRegistrationsBlob) => ({
|
|
50
|
+
value: object.details.birthDay,
|
|
51
|
+
style: {
|
|
52
|
+
numberFormat: {
|
|
53
|
+
id: XlsxBuiltInNumberFormat.DateSlash
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
})
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
// Dynamic records
|
|
60
|
+
{
|
|
61
|
+
match(id) {
|
|
62
|
+
console.log('match', id)
|
|
63
|
+
if (id.startsWith('recordAnswers.')) {
|
|
64
|
+
const platform = Platform.shared
|
|
65
|
+
const organization = Context.organization
|
|
66
|
+
|
|
67
|
+
const recordSettings = [
|
|
68
|
+
...(organization?.meta.recordsConfiguration.recordCategories.flatMap(category => category.getAllRecords()) ?? []),
|
|
69
|
+
...platform.config.recordsConfiguration.recordCategories.flatMap(category => category.getAllRecords())
|
|
70
|
+
]
|
|
71
|
+
|
|
72
|
+
const recordSettingId = id.split('.')[1];
|
|
73
|
+
console.log('recordSettingId', recordSettingId)
|
|
74
|
+
const recordSetting = recordSettings.find(r => r.id === recordSettingId)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
if (!recordSetting) {
|
|
78
|
+
// Will throw a proper error itself
|
|
79
|
+
console.log('recordSetting not found', recordSettings)
|
|
80
|
+
return
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const columns = recordSetting.excelColumns
|
|
84
|
+
|
|
85
|
+
return columns.map((columnName, index) => {
|
|
86
|
+
return {
|
|
87
|
+
id: `recordAnswers.${recordSettingId}.${index}`,
|
|
88
|
+
name: columnName,
|
|
89
|
+
width: 20,
|
|
90
|
+
getValue: (object: MemberWithRegistrationsBlob) => ({
|
|
91
|
+
value: object.details.recordAnswers.get(recordSettingId)?.excelValues[index]?.value ?? ''
|
|
92
|
+
})
|
|
93
|
+
}
|
|
94
|
+
})
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
}
|
|
98
|
+
]
|
|
99
|
+
}
|
|
100
|
+
]
|
|
101
|
+
})
|