@stamhoofd/backend 2.8.0 → 2.13.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 (35) hide show
  1. package/.env.template.json +3 -1
  2. package/package.json +11 -3
  3. package/src/crons.ts +3 -3
  4. package/src/decoders/StringArrayDecoder.ts +24 -0
  5. package/src/decoders/StringNullableDecoder.ts +18 -0
  6. package/src/endpoints/admin/organizations/GetOrganizationsEndpoint.ts +20 -18
  7. package/src/endpoints/global/email/PatchEmailEndpoint.ts +1 -0
  8. package/src/endpoints/global/events/GetEventsEndpoint.ts +3 -9
  9. package/src/endpoints/global/events/PatchEventsEndpoint.ts +21 -1
  10. package/src/endpoints/global/groups/GetGroupsEndpoint.ts +79 -0
  11. package/src/endpoints/global/members/GetMembersEndpoint.ts +15 -62
  12. package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.ts +2 -2
  13. package/src/endpoints/global/registration/GetUserBalanceEndpoint.ts +3 -3
  14. package/src/endpoints/global/registration/RegisterMembersEndpoint.ts +165 -35
  15. package/src/endpoints/organization/dashboard/email-templates/GetEmailTemplatesEndpoint.ts +20 -23
  16. package/src/endpoints/organization/dashboard/email-templates/PatchEmailTemplatesEndpoint.ts +22 -1
  17. package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.ts +56 -3
  18. package/src/endpoints/organization/dashboard/organization/SetOrganizationDomainEndpoint.ts +3 -3
  19. package/src/endpoints/organization/dashboard/payments/GetMemberBalanceEndpoint.ts +3 -3
  20. package/src/endpoints/organization/dashboard/payments/GetPaymentsCountEndpoint.ts +43 -0
  21. package/src/endpoints/organization/dashboard/payments/GetPaymentsEndpoint.ts +292 -170
  22. package/src/endpoints/organization/dashboard/payments/PatchBalanceItemsEndpoint.ts +22 -37
  23. package/src/endpoints/organization/dashboard/payments/legacy/GetPaymentsEndpoint.ts +170 -0
  24. package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.ts +1 -0
  25. package/src/endpoints/organization/dashboard/webshops/PatchWebshopOrdersEndpoint.ts +14 -4
  26. package/src/endpoints/organization/webshops/PlaceOrderEndpoint.ts +12 -2
  27. package/src/helpers/AdminPermissionChecker.ts +95 -60
  28. package/src/helpers/AuthenticatedStructures.ts +16 -6
  29. package/src/helpers/Context.ts +21 -0
  30. package/src/helpers/EmailResumer.ts +22 -2
  31. package/src/helpers/MemberUserSyncer.ts +8 -2
  32. package/src/helpers/ViesHelper.ts +151 -0
  33. package/src/seeds/1722344160-update-membership.ts +19 -22
  34. package/src/seeds/1722344161-sync-member-users.ts +60 -0
  35. package/.env.json +0 -65
@@ -22,6 +22,7 @@
22
22
  "rendererApi": "renderer.stamhoofd"
23
23
  },
24
24
  "translationNamespace": "stamhoofd",
25
+ "platformName": "stamhoofd",
25
26
  "userMode": "organization",
26
27
 
27
28
  "PORT": 9091,
@@ -59,5 +60,6 @@
59
60
 
60
61
  "NOLT_SSO_SECRET_KEY": "",
61
62
  "INTERNAL_SECRET_KEY": "",
62
- "CRONS_DISABLED": false
63
+ "CRONS_DISABLED": false,
64
+ "WHITELISTED_EMAIL_DESTINATIONS": ["*"]
63
65
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stamhoofd/backend",
3
- "version": "2.8.0",
3
+ "version": "2.13.0",
4
4
  "main": "./dist/index.js",
5
5
  "exports": {
6
6
  ".": {
@@ -9,7 +9,7 @@
9
9
  },
10
10
  "license": "UNLICENCED",
11
11
  "scripts": {
12
- "dev": "concurrently -r \"rm -rf ./dist && wait-on ./dist/index.js && nodemon --quiet --inspect=5858 --watch dist --delay 200ms --exec node --enable-source-maps ./dist/index.js --signal SIGTERM\" 'yarn build --watch --preserveWatchOutput'",
12
+ "dev": "echo 'Waiting for shared backend packages...' && wait-on ../../shared/env/dist/index.js && echo 'Start building backend API' && concurrently -r 'yarn build --watch --preserveWatchOutput' \"wait-on ./dist/index.js && nodemon --quiet --inspect=5858 --watch dist --watch '../../../shared/*/dist/' --watch '../../shared/*/dist/' --ext .ts,.json,.sql,.js --watch .env.json --delay 1000ms --exec 'node --enable-source-maps ./dist/index.js' --signal SIGTERM\"",
13
13
  "dev:backend": "yarn dev",
14
14
  "build": "rm -rf ./dist/src/migrations && rm -rf ./dist/src/seeds && tsc -b",
15
15
  "build:full": "yarn clear && yarn build",
@@ -35,6 +35,14 @@
35
35
  "@simonbackx/simple-database": "1.24.0",
36
36
  "@simonbackx/simple-endpoints": "1.13.0",
37
37
  "@simonbackx/simple-logging": "^1.0.1",
38
+ "@stamhoofd/backend-i18n": "^2.13.0",
39
+ "@stamhoofd/backend-middleware": "^2.13.0",
40
+ "@stamhoofd/email": "^2.13.0",
41
+ "@stamhoofd/models": "^2.13.0",
42
+ "@stamhoofd/queues": "^2.13.0",
43
+ "@stamhoofd/sql": "^2.13.0",
44
+ "@stamhoofd/structures": "^2.13.0",
45
+ "@stamhoofd/utility": "^2.13.0",
38
46
  "aws-sdk": "^2.885.0",
39
47
  "axios": "1.6.8",
40
48
  "cookie": "^0.5.0",
@@ -50,5 +58,5 @@
50
58
  "postmark": "4.0.2",
51
59
  "stripe": "^16.6.0"
52
60
  },
53
- "gitHead": "1e0e1853f8dea6b47718433b2890907042c17ddc"
61
+ "gitHead": "2b130616eebea8cd30feb0f361c7bb0f48b534ba"
54
62
  }
package/src/crons.ts CHANGED
@@ -140,7 +140,7 @@ async function checkWebshopDNS() {
140
140
  }
141
141
 
142
142
  async function checkReplies() {
143
- if (STAMHOOFD.environment === "development") {
143
+ if (STAMHOOFD.environment !== "production") {
144
144
  return;
145
145
  }
146
146
 
@@ -200,7 +200,7 @@ async function checkReplies() {
200
200
  let lastPostmarkCheck: Date | null = null
201
201
  let lastPostmarkId: string | null = null
202
202
  async function checkPostmarkBounces() {
203
- if (STAMHOOFD.environment === "development") {
203
+ if (STAMHOOFD.environment !== "production") {
204
204
  return;
205
205
  }
206
206
 
@@ -842,4 +842,4 @@ export const crons = async () => {
842
842
  }
843
843
  }
844
844
  schedulingJobs = false;
845
- };
845
+ };
@@ -0,0 +1,24 @@
1
+ import { Data, Decoder } from "@simonbackx/simple-encoding";
2
+
3
+ export class StringArrayDecoder<T> implements Decoder<T[]> {
4
+ decoder: Decoder<T>;
5
+
6
+ constructor(decoder: Decoder<T>) {
7
+ this.decoder = decoder;
8
+ }
9
+
10
+ decode(data: Data): T[] {
11
+ const strValue = data.string;
12
+
13
+ // Split on comma
14
+ const parts = strValue.split(",");
15
+ return parts
16
+ .map((v, index) => {
17
+ return data.clone({
18
+ data: v,
19
+ context: data.context,
20
+ field: data.addToCurrentField(index)
21
+ }).decode(this.decoder)
22
+ });
23
+ }
24
+ }
@@ -0,0 +1,18 @@
1
+ import { Decoder, Data } from "@simonbackx/simple-encoding";
2
+
3
+ export class StringNullableDecoder<T> implements Decoder<T | null> {
4
+ decoder: Decoder<T>;
5
+
6
+ constructor(decoder: Decoder<T>) {
7
+ this.decoder = decoder;
8
+ }
9
+
10
+ decode(data: Data): T | null {
11
+ if (data.value === 'null') {
12
+ return null;
13
+ }
14
+
15
+ return data.decode(this.decoder);
16
+ }
17
+ }
18
+
@@ -19,6 +19,9 @@ export const filterCompilers: SQLFilterDefinitions = {
19
19
  id: createSQLExpressionFilterCompiler(
20
20
  SQL.column('organizations', 'id')
21
21
  ),
22
+ uri: createSQLExpressionFilterCompiler(
23
+ SQL.column('organizations', 'uri')
24
+ ),
22
25
  name: createSQLExpressionFilterCompiler(
23
26
  SQL.column('organizations', 'name')
24
27
  ),
@@ -27,33 +30,23 @@ export const filterCompilers: SQLFilterDefinitions = {
27
30
  ),
28
31
  city: createSQLExpressionFilterCompiler(
29
32
  SQL.jsonValue(SQL.column('organizations', 'address'), '$.value.city'),
30
- undefined,
31
- true,
32
- false
33
+ {isJSONValue: true}
33
34
  ),
34
35
  country: createSQLExpressionFilterCompiler(
35
36
  SQL.jsonValue(SQL.column('organizations', 'address'), '$.value.country'),
36
- undefined,
37
- true,
38
- false
37
+ {isJSONValue: true}
39
38
  ),
40
39
  umbrellaOrganization: createSQLExpressionFilterCompiler(
41
40
  SQL.jsonValue(SQL.column('organizations', 'meta'), '$.value.umbrellaOrganization'),
42
- undefined,
43
- true,
44
- false
41
+ {isJSONValue: true}
45
42
  ),
46
43
  type: createSQLExpressionFilterCompiler(
47
44
  SQL.jsonValue(SQL.column('organizations', 'meta'), '$.value.type'),
48
- undefined,
49
- true,
50
- false
45
+ {isJSONValue: true}
51
46
  ),
52
47
  tags: createSQLExpressionFilterCompiler(
53
48
  SQL.jsonValue(SQL.column('organizations', 'meta'), '$.value.tags'),
54
- undefined,
55
- true,
56
- true
49
+ {isJSONValue: true, isJSONObject: true}
57
50
  ),
58
51
  packages: createSQLRelationFilterCompiler(
59
52
  SQL.select().from(
@@ -100,9 +93,7 @@ export const filterCompilers: SQLFilterDefinitions = {
100
93
  ...baseSQLFilterCompilers,
101
94
  "type": createSQLExpressionFilterCompiler(
102
95
  SQL.jsonValue(SQL.column('meta'), '$.value.type'),
103
- undefined,
104
- true,
105
- false
96
+ {isJSONValue: true}
106
97
  )
107
98
  }
108
99
  ),
@@ -160,6 +151,17 @@ const sorters: SQLSortDefinitions<Organization> = {
160
151
  })
161
152
  }
162
153
  },
154
+ 'uri': {
155
+ getValue(a) {
156
+ return a.uri
157
+ },
158
+ toSQL: (direction: SQLOrderByDirection): SQLOrderBy => {
159
+ return new SQLOrderBy({
160
+ column: SQL.column('uri'),
161
+ direction
162
+ })
163
+ }
164
+ },
163
165
  'type': {
164
166
  getValue(a) {
165
167
  return a.meta.type
@@ -100,6 +100,7 @@ export class PatchEmailEndpoint extends Endpoint<Params, Query, Body, ResponseBo
100
100
  }
101
101
 
102
102
  if (request.body.status === EmailStatus.Sending || request.body.status === EmailStatus.Sent) {
103
+ model.throwIfNotReadyToSend()
103
104
  model.send().catch(console.error)
104
105
  }
105
106
 
@@ -24,21 +24,15 @@ const filterCompilers: SQLFilterDefinitions = {
24
24
  endDate: createSQLColumnFilterCompiler('endDate'),
25
25
  groupIds: createSQLExpressionFilterCompiler(
26
26
  SQL.jsonValue(SQL.column('meta'), '$.value.groups[*].id'),
27
- undefined,
28
- true,
29
- true
27
+ {isJSONValue: true, isJSONObject: true}
30
28
  ),
31
29
  defaultAgeGroupIds: createSQLExpressionFilterCompiler(
32
30
  SQL.jsonValue(SQL.column('meta'), '$.value.defaultAgeGroupIds'),
33
- undefined,
34
- true,
35
- true
31
+ {isJSONValue: true, isJSONObject: true}
36
32
  ),
37
33
  organizationTagIds: createSQLExpressionFilterCompiler(
38
34
  SQL.jsonValue(SQL.column('meta'), '$.value.organizationTagIds'),
39
- undefined,
40
- true,
41
- true
35
+ {isJSONValue: true, isJSONObject: true}
42
36
  )
43
37
  }
44
38
 
@@ -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, Organization, Platform, RegistrationPeriod } from '@stamhoofd/models';
4
- import { Event as EventStruct, GroupType, PermissionLevel } from "@stamhoofd/structures";
4
+ import { Event as EventStruct, GroupType, NamedObject, PermissionLevel } from "@stamhoofd/structures";
5
5
 
6
6
  import { SimpleError } from '@simonbackx/simple-errors';
7
7
  import { SQL, SQLWhereSign } from '@stamhoofd/sql';
@@ -81,6 +81,7 @@ export class PatchEventsEndpoint extends Endpoint<Params, Query, Body, ResponseB
81
81
  event.endDate = put.endDate
82
82
  event.meta = put.meta
83
83
  event.typeId = await PatchEventsEndpoint.validateEventType(put.typeId)
84
+ event.meta.organizationCache = eventOrganization ? NamedObject.create({id: eventOrganization.id, name: eventOrganization.name}) : null
84
85
  await PatchEventsEndpoint.checkEventLimits(event)
85
86
 
86
87
  if (!(await Context.auth.canAccessEvent(event, PermissionLevel.Full))) {
@@ -128,6 +129,16 @@ export class PatchEventsEndpoint extends Endpoint<Params, Query, Body, ResponseB
128
129
  event.name = patch.name ?? event.name
129
130
  event.startDate = patch.startDate ?? event.startDate
130
131
  event.endDate = patch.endDate ?? event.endDate
132
+
133
+ if (patch.meta?.organizationCache) {
134
+ throw new SimpleError({
135
+ code: 'invalid_field',
136
+ message: 'Cannot patch organizationCache',
137
+ human: 'Je kan de organizationCache niet aanpassen via een patch',
138
+ field: 'meta.organizationCache'
139
+ })
140
+ }
141
+
131
142
  event.meta = patchObject(event.meta, patch.meta)
132
143
 
133
144
  if (patch.organizationId !== undefined) {
@@ -156,6 +167,15 @@ export class PatchEventsEndpoint extends Endpoint<Params, Query, Body, ResponseB
156
167
  })
157
168
  }
158
169
  event.organizationId = patch.organizationId
170
+ event.meta.organizationCache = eventOrganization ? NamedObject.create({id: eventOrganization.id, name: eventOrganization.name}) : null
171
+ } else {
172
+ // Update cache
173
+ if (event.organizationId) {
174
+ const eventOrganization = await Organization.getByID(event.organizationId)
175
+ if (eventOrganization) {
176
+ event.meta.organizationCache = NamedObject.create({id: eventOrganization.id, name: eventOrganization.name})
177
+ }
178
+ }
159
179
  }
160
180
 
161
181
  event.typeId = patch.typeId ? (await PatchEventsEndpoint.validateEventType(patch.typeId)) : event.typeId
@@ -0,0 +1,79 @@
1
+ import { DecodedRequest, Endpoint, Request, Response } from "@simonbackx/simple-endpoints";
2
+ import { Group, Organization } from '@stamhoofd/models';
3
+ import { GroupsWithOrganizations } from "@stamhoofd/structures";
4
+
5
+ import { AutoEncoder, Decoder, field, StringDecoder } from "@simonbackx/simple-encoding";
6
+ import { Formatter } from "@stamhoofd/utility";
7
+ import { StringArrayDecoder } from "../../../decoders/StringArrayDecoder";
8
+ import { AuthenticatedStructures } from "../../../helpers/AuthenticatedStructures";
9
+ import { Context } from "../../../helpers/Context";
10
+ import { SimpleError } from "@simonbackx/simple-errors";
11
+ type Params = Record<string, never>;
12
+
13
+ class Query extends AutoEncoder {
14
+ @field({ decoder: new StringArrayDecoder(StringDecoder) })
15
+ ids: string[]
16
+
17
+ /**
18
+ * List of organizations the requester already knows and doesn't need to be included in the response
19
+ */
20
+ @field({ decoder: new StringArrayDecoder(StringDecoder), optional: true })
21
+ excludeOrganizationIds: string[] = []
22
+ }
23
+
24
+ type Body = undefined
25
+ type ResponseBody = GroupsWithOrganizations
26
+
27
+ /**
28
+ * Get the members of the user
29
+ */
30
+ export class GetGroupsEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
31
+ queryDecoder = Query as Decoder<Query>
32
+
33
+ protected doesMatch(request: Request): [true, Params] | [false] {
34
+ if (request.method != "GET") {
35
+ return [false];
36
+ }
37
+
38
+ const params = Endpoint.parseParameters(request.url, "/groups", {});
39
+
40
+ if (params) {
41
+ return [true, params as Params];
42
+ }
43
+
44
+ return [false];
45
+ }
46
+
47
+ async handle(request: DecodedRequest<Params, Query, Body>) {
48
+ await Context.setOptionalOrganizationScope();
49
+ await Context.optionalAuthenticate()
50
+
51
+ if (request.query.ids.length === 0) {
52
+ return new Response(
53
+ GroupsWithOrganizations.create({
54
+ groups: [],
55
+ organizations: []
56
+ })
57
+ );
58
+ }
59
+
60
+ if (request.query.ids.length > 100) {
61
+ throw new SimpleError({
62
+ code: "too_many_ids",
63
+ message: "You can't request more than 100 groups at once"
64
+ })
65
+ }
66
+
67
+ const groups = await Group.getByIDs(...request.query.ids)
68
+ const organizationIds = Formatter.uniqueArray(groups.map(g => g.organizationId).filter(id => !request.query.excludeOrganizationIds.includes(id)));
69
+
70
+ const organizations = organizationIds.length > 0 ? (await Organization.getByIDs(...organizationIds)) : [];
71
+
72
+ return new Response(
73
+ GroupsWithOrganizations.create({
74
+ groups: await AuthenticatedStructures.groups(groups),
75
+ organizations: await AuthenticatedStructures.organizations(organizations)
76
+ })
77
+ );
78
+ }
79
+ }
@@ -75,25 +75,12 @@ Email.recipientLoaders.set(EmailRecipientFilterType.MemberUnverified, {
75
75
 
76
76
  const registrationFilterCompilers: SQLFilterDefinitions = {
77
77
  ...baseSQLFilterCompilers,
78
- "price": createSQLColumnFilterCompiler('price'),
78
+ "price": createSQLColumnFilterCompiler('price', {nullable: true}),
79
79
  "pricePaid": createSQLColumnFilterCompiler('pricePaid'),
80
- "waitingList": createSQLColumnFilterCompiler('waitingList'),
81
80
  "canRegister": createSQLColumnFilterCompiler('canRegister'),
82
- "cycle": createSQLColumnFilterCompiler('cycle'),
83
-
84
- "cycleOffset": createSQLExpressionFilterCompiler({
85
- getSQL(options) {
86
- return joinSQLQuery([
87
- SQL.column('groups', 'cycle').getSQL(options),
88
- ' - ',
89
- SQL.column('registrations', 'cycle').getSQL(options)
90
- ])
91
- },
92
- }),
93
-
94
81
  "organizationId": createSQLColumnFilterCompiler('organizationId'),
95
82
  "groupId": createSQLColumnFilterCompiler('groupId'),
96
- "registeredAt": createSQLColumnFilterCompiler('registeredAt'),
83
+ "registeredAt": createSQLColumnFilterCompiler('registeredAt', {nullable: true}),
97
84
  "periodId": createSQLColumnFilterCompiler(SQL.column('registrations', 'periodId')),
98
85
 
99
86
  "group": createSQLFilterNamespace({
@@ -105,7 +92,7 @@ const registrationFilterCompilers: SQLFilterDefinitions = {
105
92
  status: createSQLExpressionFilterCompiler(
106
93
  SQL.column('groups', 'status')
107
94
  ),
108
- defaultAgeGroupId: createSQLColumnFilterCompiler(SQL.column('groups', 'defaultAgeGroupId')),
95
+ defaultAgeGroupId: createSQLColumnFilterCompiler(SQL.column('groups', 'defaultAgeGroupId'), {nullable: true}),
109
96
  })
110
97
  }
111
98
 
@@ -120,20 +107,21 @@ const filterCompilers: SQLFilterDefinitions = {
120
107
  )
121
108
  ),
122
109
  age: createSQLExpressionFilterCompiler(
123
- new SQLAge(SQL.column('birthDay'))
110
+ new SQLAge(SQL.column('birthDay')),
111
+ {nullable: true}
124
112
  ),
125
113
  gender: createSQLExpressionFilterCompiler(
126
114
  SQL.jsonValue(SQL.column('details'), '$.value.gender'),
127
- undefined,
128
- true,
129
- false
115
+ {isJSONValue: true}
130
116
  ),
131
- birthDay: createSQLColumnFilterCompiler('birthDay', (d) => {
132
- if (typeof d === 'number') {
133
- const date = new Date(d)
134
- return Formatter.dateIso(date);
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;
135
124
  }
136
- return d;
137
125
  }),
138
126
  organizationName: createSQLExpressionFilterCompiler(
139
127
  SQL.column('organizations', 'name')
@@ -141,16 +129,12 @@ const filterCompilers: SQLFilterDefinitions = {
141
129
 
142
130
  email: createSQLExpressionFilterCompiler(
143
131
  SQL.jsonValue(SQL.column('details'), '$.value.email'),
144
- undefined,
145
- true,
146
- false
132
+ {isJSONValue: true}
147
133
  ),
148
134
 
149
135
  parentEmail: createSQLExpressionFilterCompiler(
150
136
  SQL.jsonValue(SQL.column('details'), '$.value.parents[*].email'),
151
- undefined,
152
- true,
153
- true
137
+ {isJSONValue: true, isJSONObject: true}
154
138
  ),
155
139
 
156
140
  registrations: createSQLRelationFilterCompiler(
@@ -252,37 +236,6 @@ const filterCompilers: SQLFilterDefinitions = {
252
236
  }
253
237
  ),
254
238
 
255
- /**
256
- * @deprecated?
257
- */
258
- activeRegistrations: createSQLRelationFilterCompiler(
259
- SQL.select()
260
- .from(
261
- SQL.table('registrations')
262
- ).join(
263
- SQL.join(
264
- SQL.table('groups')
265
- ).where(
266
- SQL.column('groups', 'id'),
267
- SQL.column('registrations', 'groupId')
268
- )
269
- )
270
- .where(
271
- SQL.column('memberId'),
272
- SQL.column('members', 'id'),
273
- ).whereNot(
274
- SQL.column('registeredAt'),
275
- null,
276
- ).where(
277
- SQL.column('deactivatedAt'),
278
- null,
279
- ).where(
280
- SQL.column('groups', 'deletedAt'),
281
- null
282
- ),
283
- registrationFilterCompilers
284
- ),
285
-
286
239
  organizations: createSQLRelationFilterCompiler(
287
240
  SQL.select()
288
241
  .from(
@@ -2,8 +2,8 @@ import { OneToManyRelation } from '@simonbackx/simple-database';
2
2
  import { ConvertArrayToPatchableArray, Decoder, PatchableArrayAutoEncoder, PatchableArrayDecoder, StringDecoder } 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 { BalanceItem, BalanceItemPayment, Document, Group, Member, MemberFactory, MemberPlatformMembership, MemberResponsibilityRecord, MemberWithRegistrations, Organization, Payment, Platform, Registration, RegistrationPeriod, User } from '@stamhoofd/models';
6
- import { BalanceItemStatus, MemberWithRegistrationsBlob, MembersBlob, PaymentMethod, PaymentStatus, PermissionLevel, Registration as RegistrationStruct, User as UserStruct } from "@stamhoofd/structures";
5
+ import { BalanceItem, Document, Group, Member, MemberFactory, MemberPlatformMembership, MemberResponsibilityRecord, MemberWithRegistrations, Organization, Platform, Registration, User } from '@stamhoofd/models';
6
+ import { MemberWithRegistrationsBlob, MembersBlob, PermissionLevel } from "@stamhoofd/structures";
7
7
  import { Formatter } from '@stamhoofd/utility';
8
8
 
9
9
  import { AuthenticatedStructures } from '../../../helpers/AuthenticatedStructures';
@@ -1,13 +1,13 @@
1
1
  import { DecodedRequest, Endpoint, Request, Response } from "@simonbackx/simple-endpoints";
2
2
  import { BalanceItem, Member } from "@stamhoofd/models";
3
- import { MemberBalanceItem } from "@stamhoofd/structures";
3
+ import { BalanceItemWithPayments } from "@stamhoofd/structures";
4
4
 
5
5
  import { Context } from "../../../helpers/Context";
6
6
 
7
7
  type Params = Record<string, never>;
8
8
  type Query = undefined
9
9
  type Body = undefined
10
- type ResponseBody = MemberBalanceItem[]
10
+ type ResponseBody = BalanceItemWithPayments[]
11
11
 
12
12
  export class GetUserBalanceEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
13
13
  protected doesMatch(request: Request): [true, Params] | [false] {
@@ -33,7 +33,7 @@ export class GetUserBalanceEndpoint extends Endpoint<Params, Query, Body, Respon
33
33
  const balanceItems = await BalanceItem.balanceItemsForUsersAndMembers(organization?.id ?? null, [user.id], members.map(m => m.id))
34
34
 
35
35
  return new Response(
36
- await BalanceItem.getMemberStructure(balanceItems)
36
+ await BalanceItem.getStructureWithPayments(balanceItems)
37
37
  );
38
38
  }
39
39
  }