@stamhoofd/backend 2.14.0 → 2.16.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.
Files changed (31) hide show
  1. package/.env.template.json +2 -1
  2. package/index.ts +15 -1
  3. package/package.json +14 -12
  4. package/src/email-recipient-loaders/members.ts +61 -0
  5. package/src/endpoints/admin/memberships/GetChargeMembershipsSummaryEndpoint.ts +1 -1
  6. package/src/endpoints/admin/organizations/GetOrganizationsEndpoint.ts +5 -183
  7. package/src/endpoints/global/files/ExportToExcelEndpoint.ts +163 -0
  8. package/src/endpoints/global/files/GetFileCache.ts +69 -0
  9. package/src/endpoints/global/files/UploadFile.ts +4 -1
  10. package/src/endpoints/global/files/UploadImage.ts +14 -2
  11. package/src/endpoints/global/members/GetMembersEndpoint.ts +12 -299
  12. package/src/endpoints/organization/dashboard/email/EmailEndpoint.ts +22 -2
  13. package/src/endpoints/organization/dashboard/payments/GetPaymentsEndpoint.ts +6 -134
  14. package/src/endpoints/organization/dashboard/webshops/GetWebshopOrdersEndpoint.ts +5 -3
  15. package/src/endpoints/organization/dashboard/webshops/GetWebshopTicketsEndpoint.ts +5 -3
  16. package/src/excel-loaders/members.ts +101 -0
  17. package/src/excel-loaders/payments.ts +539 -0
  18. package/src/helpers/AdminPermissionChecker.ts +0 -3
  19. package/src/helpers/AuthenticatedStructures.ts +2 -0
  20. package/src/helpers/FileCache.ts +158 -0
  21. package/src/helpers/fetchToAsyncIterator.ts +34 -0
  22. package/src/sql-filters/balance-item-payments.ts +13 -0
  23. package/src/sql-filters/members.ts +179 -0
  24. package/src/sql-filters/organizations.ts +115 -0
  25. package/src/sql-filters/payments.ts +78 -0
  26. package/src/sql-filters/registrations.ts +24 -0
  27. package/src/sql-sorters/members.ts +46 -0
  28. package/src/sql-sorters/organizations.ts +71 -0
  29. package/src/sql-sorters/payments.ts +50 -0
  30. package/tsconfig.json +3 -4
  31. 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 { Email, Member, MemberWithRegistrations, Platform } from '@stamhoofd/models';
6
- import { SQL, SQLAge, SQLConcat, SQLFilterDefinitions, SQLOrderBy, SQLOrderByDirection, SQLScalar, SQLSortDefinitions, baseSQLFilterCompilers, compileToSQLFilter, compileToSQLSorter, createSQLColumnFilterCompiler, createSQLExpressionFilterCompiler, createSQLFilterNamespace, createSQLRelationFilterCompiler, joinSQLQuery } from "@stamhoofd/sql";
7
- import { CountFilteredRequest, EmailRecipientFilterType, LimitedFilteredRequest, MembersBlob, PaginatedResponse, PermissionLevel, StamhoofdFilter, getSortFilter, mergeFilters } from '@stamhoofd/structures';
8
- import { DataValidator, Formatter } from '@stamhoofd/utility';
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 { filterCompilers as organizationFilterCompilers } from '../../admin/organizations/GetOrganizationsEndpoint';
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
- Email.recipientLoaders.set(EmailRecipientFilterType.Members, {
20
- fetch: async (query: LimitedFilteredRequest) => {
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
- ...email,
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?.replace("<body>", "<body>"+prefix) ?? null,
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, SQLCast, SQLConcat, SQLFilterDefinitions, SQLJsonUnquote, SQLOrderBy, SQLOrderByDirection, SQLScalar, SQLSortDefinitions, baseSQLFilterCompilers, compileToSQLFilter, compileToSQLSorter, createSQLColumnFilterCompiler, createSQLExpressionFilterCompiler, createSQLFilterNamespace, createSQLRelationFilterCompiler } from "@stamhoofd/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 balanceItemPaymentsCompilers: SQLFilterDefinitions = {
19
- ...baseSQLFilterCompilers,
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, false),
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(request: DecodedRequest<Params, Query, Body>) {
30
- const organization = await Context.setOrganizationScope();
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(request: DecodedRequest<Params, Query, Body>) {
30
- const organization = await Context.setOrganizationScope();
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
+ })