@stamhoofd/backend 2.46.0 → 2.48.1

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/index.ts CHANGED
@@ -86,9 +86,11 @@ const start = async () => {
86
86
  // Register Excel loaders
87
87
  await import('./src/excel-loaders/members');
88
88
  await import('./src/excel-loaders/payments');
89
+ await import('./src/excel-loaders/organizations');
89
90
 
90
91
  // Register Email Recipient loaders
91
92
  await import('./src/email-recipient-loaders/members');
93
+ await import('./src/email-recipient-loaders/orders');
92
94
 
93
95
  routerServer.listen(STAMHOOFD.PORT ?? 9090);
94
96
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stamhoofd/backend",
3
- "version": "2.46.0",
3
+ "version": "2.48.1",
4
4
  "main": "./dist/index.js",
5
5
  "exports": {
6
6
  ".": {
@@ -36,14 +36,14 @@
36
36
  "@simonbackx/simple-encoding": "2.15.1",
37
37
  "@simonbackx/simple-endpoints": "1.14.0",
38
38
  "@simonbackx/simple-logging": "^1.0.1",
39
- "@stamhoofd/backend-i18n": "2.46.0",
40
- "@stamhoofd/backend-middleware": "2.46.0",
41
- "@stamhoofd/email": "2.46.0",
42
- "@stamhoofd/models": "2.46.0",
43
- "@stamhoofd/queues": "2.46.0",
44
- "@stamhoofd/sql": "2.46.0",
45
- "@stamhoofd/structures": "2.46.0",
46
- "@stamhoofd/utility": "2.46.0",
39
+ "@stamhoofd/backend-i18n": "2.48.1",
40
+ "@stamhoofd/backend-middleware": "2.48.1",
41
+ "@stamhoofd/email": "2.48.1",
42
+ "@stamhoofd/models": "2.48.1",
43
+ "@stamhoofd/queues": "2.48.1",
44
+ "@stamhoofd/sql": "2.48.1",
45
+ "@stamhoofd/structures": "2.48.1",
46
+ "@stamhoofd/utility": "2.48.1",
47
47
  "archiver": "^7.0.1",
48
48
  "aws-sdk": "^2.885.0",
49
49
  "axios": "1.6.8",
@@ -60,5 +60,5 @@
60
60
  "postmark": "^4.0.5",
61
61
  "stripe": "^16.6.0"
62
62
  },
63
- "gitHead": "894c197231d17b270d47f7b4530ccb4e571482e4"
63
+ "gitHead": "06c0bb89184e23767bf05d75c9ade7e01b9bb77a"
64
64
  }
@@ -0,0 +1,60 @@
1
+ import { Email, Webshop } from '@stamhoofd/models';
2
+ import { EmailRecipient, EmailRecipientFilterType, LimitedFilteredRequest, mergeFilters, PaginatedResponse, WebshopPreview } from '@stamhoofd/structures';
3
+ import { GetWebshopOrdersEndpoint } from '../endpoints/organization/dashboard/webshops/GetWebshopOrdersEndpoint';
4
+ import { AuthenticatedStructures } from '../helpers/AuthenticatedStructures';
5
+ import { Context } from '../helpers/Context';
6
+
7
+ Email.recipientLoaders.set(EmailRecipientFilterType.Orders, {
8
+ fetch: async (query: LimitedFilteredRequest) => {
9
+ // #region get organization struct
10
+ const organization = Context.organization;
11
+ if (organization === undefined) {
12
+ throw new Error('Organization is undefined');
13
+ }
14
+ const organizationStruct = organization.getBaseStructure();
15
+ // #endregion
16
+
17
+ const result = await GetWebshopOrdersEndpoint.buildData(query);
18
+
19
+ // #region get webshop previews
20
+ const webshopIds = new Set(result.results.map(order => order.webshopId));
21
+ const webshopPreviewMap = new Map<string, WebshopPreview>();
22
+
23
+ for (const webshopId of webshopIds) {
24
+ const webshop = await Webshop.getByID(webshopId);
25
+
26
+ if (webshop === undefined) {
27
+ // todo: change error?
28
+ throw new Error('Webshop is undefined');
29
+ }
30
+
31
+ webshopPreviewMap.set(webshopId, AuthenticatedStructures.webshopPreview(webshop));
32
+ }
33
+ // #endregion
34
+
35
+ // #region get recipients
36
+ const recipients: EmailRecipient[] = [];
37
+
38
+ for (const order of result.results) {
39
+ // todo: filter double emails? => should probably happen in query?
40
+ const webshopPreview = webshopPreviewMap.get(order.webshopId)!;
41
+ recipients.push(order.getEmailRecipient(organizationStruct, webshopPreview));
42
+ }
43
+ // #endregion
44
+
45
+ return new PaginatedResponse({
46
+ results: recipients,
47
+ next: result.next,
48
+ });
49
+ },
50
+
51
+ count: async (query: LimitedFilteredRequest) => {
52
+ query.filter = mergeFilters([query.filter, {
53
+ email: {
54
+ $neq: null,
55
+ },
56
+ }]);
57
+ const q = GetWebshopOrdersEndpoint.buildQuery(query);
58
+ return await q.count();
59
+ },
60
+ });
@@ -9,6 +9,7 @@ import { AuthenticatedStructures } from '../../../helpers/AuthenticatedStructure
9
9
  import { Context } from '../../../helpers/Context';
10
10
  import { organizationFilterCompilers } from '../../../sql-filters/organizations';
11
11
  import { organizationSorters } from '../../../sql-sorters/organizations';
12
+ import { SQLResultNamespacedRow } from '@simonbackx/simple-database';
12
13
 
13
14
  type Params = Record<string, never>;
14
15
  type Query = LimitedFilteredRequest;
@@ -55,6 +56,7 @@ export class GetOrganizationsEndpoint extends Endpoint<Params, Query, Body, Resp
55
56
  .select(
56
57
  SQL.wildcard('organizations'),
57
58
  )
59
+ .setMaxExecutionTime(15 * 1000)
58
60
  .from(
59
61
  SQL.table('organizations'),
60
62
  );
@@ -114,7 +116,23 @@ export class GetOrganizationsEndpoint extends Endpoint<Params, Query, Body, Resp
114
116
  });
115
117
  }
116
118
 
117
- const data = await GetOrganizationsEndpoint.buildQuery(requestQuery).fetch();
119
+ const query = GetOrganizationsEndpoint.buildQuery(requestQuery);
120
+ let data: SQLResultNamespacedRow[];
121
+
122
+ try {
123
+ data = await query.fetch();
124
+ }
125
+ catch (error) {
126
+ if (error.message.includes('ER_QUERY_TIMEOUT')) {
127
+ throw new SimpleError({
128
+ code: 'timeout',
129
+ message: 'Query took too long',
130
+ human: 'Deze opzoeking is te complex en duurt te lang. Probeer een eenvoudigere zoekopdracht of probeer terug op een rustiger tijdstip.',
131
+ });
132
+ }
133
+ throw error;
134
+ }
135
+
118
136
  const organizations = Organization.fromRows(data, 'organizations');
119
137
 
120
138
  let next: LimitedFilteredRequest | undefined;
@@ -2,11 +2,11 @@ import { AutoEncoderPatchType, Decoder, PatchableArrayAutoEncoder, PatchableArra
2
2
  import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
3
3
  import { SimpleError } from '@simonbackx/simple-errors';
4
4
  import { Organization, OrganizationRegistrationPeriod, Platform } from '@stamhoofd/models';
5
- import { OrganizationMetaData, Organization as OrganizationStruct } from '@stamhoofd/structures';
5
+ import { Organization as OrganizationStruct } from '@stamhoofd/structures';
6
6
 
7
- import { Context } from '../../../helpers/Context';
8
- import { AuthenticatedStructures } from '../../../helpers/AuthenticatedStructures';
9
7
  import { Formatter } from '@stamhoofd/utility';
8
+ import { AuthenticatedStructures } from '../../../helpers/AuthenticatedStructures';
9
+ import { Context } from '../../../helpers/Context';
10
10
 
11
11
  type Params = Record<string, never>;
12
12
  type Query = undefined;
@@ -55,38 +55,6 @@ export class PatchOrganizationsEndpoint extends Endpoint<Params, Query, Body, Re
55
55
  await organization.delete();
56
56
  }
57
57
 
58
- // Bulk tag editing
59
- for (const patch of request.body.getPatches()) {
60
- const organization = await Organization.getByID(patch.id);
61
- if (!organization) {
62
- throw new SimpleError({ code: 'not_found', message: 'Organization not found', statusCode: 404 });
63
- }
64
-
65
- if (patch.meta?.tags) {
66
- const cleanedPatch = OrganizationMetaData.patch({
67
- tags: patch.meta.tags as any,
68
- });
69
- const patchedMeta = organization.meta.patch(cleanedPatch);
70
- for (const tag of patchedMeta.tags) {
71
- if (!platform.config.tags.find(t => t.id === tag)) {
72
- throw new SimpleError({ code: 'invalid_tag', message: 'Invalid tag', statusCode: 400 });
73
- }
74
- }
75
-
76
- // Sort tags based on platform config order
77
- patchedMeta.tags.sort((a, b) => {
78
- const aIndex = platform.config.tags.findIndex(t => t.id === a);
79
- const bIndex = platform.config.tags.findIndex(t => t.id === b);
80
- return aIndex - bIndex;
81
- });
82
-
83
- organization.meta.tags = patchedMeta.tags;
84
- }
85
-
86
- await organization.save();
87
- result.push(organization);
88
- }
89
-
90
58
  // Organization creation
91
59
  for (const { put } of request.body.getPuts()) {
92
60
  if (!Context.auth.hasPlatformFullAccess()) {
@@ -11,6 +11,7 @@ import { AuthenticatedStructures } from '../../../helpers/AuthenticatedStructure
11
11
  import { Context } from '../../../helpers/Context';
12
12
  import { memberFilterCompilers } from '../../../sql-filters/members';
13
13
  import { memberSorters } from '../../../sql-sorters/members';
14
+ import { SQLResultNamespacedRow } from '@simonbackx/simple-database';
14
15
 
15
16
  type Params = Record<string, never>;
16
17
  type Query = LimitedFilteredRequest;
@@ -120,6 +121,7 @@ export class GetMembersEndpoint extends Endpoint<Params, Query, Body, ResponseBo
120
121
  .select(
121
122
  SQL.column('members', 'id'),
122
123
  )
124
+ .setMaxExecutionTime(15 * 1000)
123
125
  .from(
124
126
  SQL.table('members'),
125
127
  );
@@ -236,7 +238,21 @@ export class GetMembersEndpoint extends Endpoint<Params, Query, Body, ResponseBo
236
238
 
237
239
  static async buildData(requestQuery: LimitedFilteredRequest) {
238
240
  const query = await GetMembersEndpoint.buildQuery(requestQuery);
239
- const data = await query.fetch();
241
+ let data: SQLResultNamespacedRow[];
242
+
243
+ try {
244
+ data = await query.fetch();
245
+ }
246
+ catch (error) {
247
+ if (error.message.includes('ER_QUERY_TIMEOUT')) {
248
+ throw new SimpleError({
249
+ code: 'timeout',
250
+ message: 'Query took too long',
251
+ human: 'Deze opzoeking is te complex en duurt te lang. Probeer een eenvoudigere zoekopdracht of probeer terug op een rustiger tijdstip.',
252
+ });
253
+ }
254
+ throw error;
255
+ }
240
256
 
241
257
  const memberIds = data.map((r) => {
242
258
  if (typeof r.members.id === 'string') {
@@ -9,6 +9,7 @@ import { Context } from '../../../helpers/Context';
9
9
  import { MembershipCharger } from '../../../helpers/MembershipCharger';
10
10
  import { MembershipHelper } from '../../../helpers/MembershipHelper';
11
11
  import { PeriodHelper } from '../../../helpers/PeriodHelper';
12
+ import { TagHelper } from '../../../helpers/TagHelper';
12
13
 
13
14
  type Params = Record<string, never>;
14
15
  type Query = undefined;
@@ -78,6 +79,7 @@ export class PatchPlatformEndpoint extends Endpoint<
78
79
  let shouldUpdateSetupSteps = false;
79
80
  let shouldMoveToPeriod: RegistrationPeriod | null = null;
80
81
  let shouldUpdateMemberships = false;
82
+ let shouldUpdateTags = false;
81
83
 
82
84
  if (request.body.config) {
83
85
  if (!Context.auth.hasPlatformFullAccess()) {
@@ -88,22 +90,39 @@ export class PatchPlatformEndpoint extends Endpoint<
88
90
 
89
91
  // Update config
90
92
  if (newConfig) {
91
- if (newConfig.premiseTypes || newConfig.responsibilities) {
92
- const oldConfig = platform.config.clone();
93
+ const shouldCheckSteps = newConfig.premiseTypes || newConfig.responsibilities;
94
+ if (shouldCheckSteps) {
95
+ const oldConfig: PlatformConfig = platform.config.clone();
93
96
  platform.config = patchObject(platform.config, newConfig);
94
- const currentConfig = platform.config;
95
-
96
- shouldUpdateSetupSteps = this.shouldUpdateSetupSteps(
97
- currentConfig,
98
- newConfig,
99
- oldConfig,
100
- );
97
+ const currentConfig: PlatformConfig = platform.config;
98
+
99
+ if (shouldCheckSteps) {
100
+ shouldUpdateSetupSteps = this.shouldUpdateSetupSteps(
101
+ currentConfig,
102
+ newConfig,
103
+ oldConfig,
104
+ );
105
+ }
101
106
  }
102
107
  else {
103
108
  platform.config = patchObject(platform.config, newConfig);
104
109
  }
105
110
  }
106
111
 
112
+ if (newConfig.tags && isPatchableArray(newConfig.tags) && newConfig.tags.changes.length > 0) {
113
+ let newTags = (platform.config as PlatformConfig).tags;
114
+ newTags = TagHelper.getCleanedUpTags(newTags);
115
+ platform.config.tags = newTags;
116
+ const areTagsValid = TagHelper.validateTags(newTags);
117
+ if (!areTagsValid) {
118
+ throw new SimpleError({
119
+ code: 'invalid_tags',
120
+ message: 'Invalid tags',
121
+ });
122
+ }
123
+ shouldUpdateTags = true;
124
+ }
125
+
107
126
  if (newConfig.membershipTypes && isPatchableArray(newConfig.membershipTypes) && newConfig.membershipTypes.changes.length > 0) {
108
127
  shouldUpdateMemberships = true;
109
128
  }
@@ -190,6 +209,10 @@ export class PatchPlatformEndpoint extends Endpoint<
190
209
  }
191
210
  }
192
211
 
212
+ if (shouldUpdateTags) {
213
+ await TagHelper.updateOrganizations();
214
+ }
215
+
193
216
  if (shouldMoveToPeriod) {
194
217
  PeriodHelper.moveAllOrganizationsToPeriod(shouldMoveToPeriod).catch(console.error);
195
218
  }
@@ -713,6 +713,9 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
713
713
  email: user.email,
714
714
  });
715
715
 
716
+ // Use structured transfer description prefix
717
+ let prefix = '';
718
+
716
719
  if (checkout.asOrganizationId) {
717
720
  if (!checkout.customer) {
718
721
  throw new SimpleError({
@@ -751,6 +754,12 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
751
754
  }
752
755
 
753
756
  payment.customer.company = foundCompany;
757
+
758
+ const orgNumber = parseInt(payingOrganization.uri);
759
+
760
+ if (orgNumber !== 0 && !isNaN(orgNumber)) {
761
+ prefix = orgNumber + '';
762
+ }
754
763
  }
755
764
 
756
765
  payment.method = checkout.paymentMethod;
@@ -777,6 +786,7 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
777
786
  name: Formatter.groupNamesByFamily(m),
778
787
  naam: Formatter.groupNamesByFamily(m),
779
788
  email: user.email,
789
+ prefix,
780
790
  },
781
791
  );
782
792
  }
@@ -2,12 +2,13 @@ import { AutoEncoderPatchType, Decoder, isPatchableArray, ObjectData, PatchableA
2
2
  import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
3
3
  import { SimpleError, SimpleErrors } from '@simonbackx/simple-errors';
4
4
  import { Organization, OrganizationRegistrationPeriod, PayconiqPayment, Platform, RegistrationPeriod, SetupStepUpdater, StripeAccount, Webshop } from '@stamhoofd/models';
5
- import { BuckarooSettings, Company, OrganizationMetaData, OrganizationPatch, Organization as OrganizationStruct, PayconiqAccount, PaymentMethod, PaymentMethodHelper, PermissionLevel } from '@stamhoofd/structures';
5
+ import { BuckarooSettings, Company, OrganizationMetaData, OrganizationPatch, Organization as OrganizationStruct, PayconiqAccount, PaymentMethod, PaymentMethodHelper, PermissionLevel, PlatformConfig } from '@stamhoofd/structures';
6
6
  import { Formatter } from '@stamhoofd/utility';
7
7
 
8
8
  import { AuthenticatedStructures } from '../../../../helpers/AuthenticatedStructures';
9
9
  import { BuckarooHelper } from '../../../../helpers/BuckarooHelper';
10
10
  import { Context } from '../../../../helpers/Context';
11
+ import { TagHelper } from '../../../../helpers/TagHelper';
11
12
  import { ViesHelper } from '../../../../helpers/ViesHelper';
12
13
 
13
14
  type Params = Record<string, never>;
@@ -63,6 +64,7 @@ export class PatchOrganizationEndpoint extends Endpoint<Params, Query, Body, Res
63
64
 
64
65
  const errors = new SimpleErrors();
65
66
  let shouldUpdateSetupSteps = false;
67
+ let updateTags = false;
66
68
 
67
69
  if (await Context.auth.hasFullAccess(organization.id)) {
68
70
  organization.name = request.body.name ?? organization.name;
@@ -204,26 +206,6 @@ export class PatchOrganizationEndpoint extends Endpoint<Params, Query, Body, Res
204
206
  // Allow admin patches (permissions only atm). No put atm
205
207
  if (request.body.admins) {
206
208
  throw new Error('Temporary removed to keep code cleaner. Please use different endpoints.');
207
- // for (const patch of request.body.admins.getPatches()) {
208
- // if (patch.permissions) {
209
- // const admin = await User.getByID(patch.id)
210
- // if (!admin || !await Context.auth.canAccessUser(admin, PermissionLevel.Full)) {
211
- // throw new SimpleError({
212
- // code: "invalid_field",
213
- // message: "De beheerder die je wilt wijzigen bestaat niet (meer)",
214
- // field: "admins"
215
- // })
216
- // }
217
- // admin.permissions = UserPermissions.limitedPatch(admin.permissions, patch.permissions, organization.id)
218
- // if (admin.id === user.id && (!admin.permissions || !admin.permissions.forOrganization(organization)?.hasFullAccess())) {
219
- // throw new SimpleError({
220
- // code: "permission_denied",
221
- // message: "Je kan jezelf niet verwijderen als hoofdbeheerder"
222
- // })
223
- // }
224
- // await admin.save()
225
- // }
226
- // }
227
209
  }
228
210
 
229
211
  if (request.body.meta) {
@@ -285,22 +267,19 @@ export class PatchOrganizationEndpoint extends Endpoint<Params, Query, Body, Res
285
267
  const cleanedPatch = OrganizationMetaData.patch({
286
268
  tags: request.body.meta.tags as any,
287
269
  });
288
- const platform = await Platform.getShared();
289
- const patchedMeta = organization.meta.patch(cleanedPatch);
270
+
271
+ const platform: Platform = await Platform.getShared();
272
+ const patchedMeta: OrganizationMetaData = organization.meta.patch(cleanedPatch);
290
273
  for (const tag of patchedMeta.tags) {
291
274
  if (!platform.config.tags.find(t => t.id === tag)) {
292
275
  throw new SimpleError({ code: 'invalid_tag', message: 'Invalid tag', statusCode: 400 });
293
276
  }
294
277
  }
295
278
 
296
- // Sort tags based on platform config order
297
- patchedMeta.tags.sort((a, b) => {
298
- const aIndex = platform.config.tags.findIndex(t => t.id === a);
299
- const bIndex = platform.config.tags.findIndex(t => t.id === b);
300
- return aIndex - bIndex;
301
- });
279
+ const platformConfig: PlatformConfig = platform.config;
280
+ organization.meta.tags = TagHelper.getAllTagsFromHierarchy(patchedMeta.tags, platformConfig.tags);
302
281
 
303
- organization.meta.tags = patchedMeta.tags;
282
+ updateTags = true;
304
283
  }
305
284
  }
306
285
 
@@ -402,6 +381,10 @@ export class PatchOrganizationEndpoint extends Endpoint<Params, Query, Body, Res
402
381
  await SetupStepUpdater.updateForOrganization(organization);
403
382
  }
404
383
 
384
+ if (updateTags) {
385
+ await TagHelper.updateOrganizations();
386
+ }
387
+
405
388
  return new Response(await AuthenticatedStructures.organization(organization));
406
389
  }
407
390
 
@@ -3,12 +3,13 @@ import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-
3
3
  import { SimpleError } from '@simonbackx/simple-errors';
4
4
  import { Payment } from '@stamhoofd/models';
5
5
  import { SQL, compileToSQLFilter, compileToSQLSorter } from '@stamhoofd/sql';
6
- import { CountFilteredRequest, LimitedFilteredRequest, PaginatedResponse, PaymentGeneral, StamhoofdFilter, assertSort, getSortFilter } from '@stamhoofd/structures';
6
+ import { CountFilteredRequest, LimitedFilteredRequest, PaginatedResponse, PaymentGeneral, StamhoofdFilter, TransferSettings, assertSort, getSortFilter } from '@stamhoofd/structures';
7
7
 
8
8
  import { AuthenticatedStructures } from '../../../../helpers/AuthenticatedStructures';
9
9
  import { Context } from '../../../../helpers/Context';
10
10
  import { paymentFilterCompilers } from '../../../../sql-filters/payments';
11
11
  import { paymentSorters } from '../../../../sql-sorters/payments';
12
+ import { SQLResultNamespacedRow } from '@simonbackx/simple-database';
12
13
 
13
14
  type Params = Record<string, never>;
14
15
  type Query = LimitedFilteredRequest;
@@ -52,6 +53,7 @@ export class GetPaymentsEndpoint extends Endpoint<Params, Query, Body, ResponseB
52
53
 
53
54
  const query = SQL
54
55
  .select()
56
+ .setMaxExecutionTime(15 * 1000)
55
57
  .from(
56
58
  SQL.table('payments'),
57
59
  );
@@ -97,6 +99,11 @@ export class GetPaymentsEndpoint extends Endpoint<Params, Query, Body, ResponseB
97
99
  },
98
100
  },
99
101
  },
102
+ {
103
+ transferDescription: {
104
+ $contains: q.search,
105
+ },
106
+ },
100
107
  ],
101
108
  };
102
109
 
@@ -123,6 +130,17 @@ export class GetPaymentsEndpoint extends Endpoint<Params, Query, Body, ResponseB
123
130
  };
124
131
  }
125
132
 
133
+ const transferDescription = q.search.replaceAll('+', '').replaceAll('/', '');
134
+ if (transferDescription.length === '562100153542'.length && !isNaN(parseInt(transferDescription))) {
135
+ // Format to
136
+ const formatted = TransferSettings.structureOGM(transferDescription);
137
+
138
+ // Search for structured transfer
139
+ searchFilter = {
140
+ transferDescription: formatted,
141
+ };
142
+ }
143
+
126
144
  if (searchFilter) {
127
145
  query.where(compileToSQLFilter(searchFilter, filterCompilers));
128
146
  }
@@ -143,7 +161,21 @@ export class GetPaymentsEndpoint extends Endpoint<Params, Query, Body, ResponseB
143
161
 
144
162
  static async buildData(requestQuery: LimitedFilteredRequest) {
145
163
  const query = await this.buildQuery(requestQuery);
146
- const data = await query.fetch();
164
+ let data: SQLResultNamespacedRow[];
165
+
166
+ try {
167
+ data = await query.fetch();
168
+ }
169
+ catch (error) {
170
+ if (error.message.includes('ER_QUERY_TIMEOUT')) {
171
+ throw new SimpleError({
172
+ code: 'timeout',
173
+ message: 'Query took too long',
174
+ human: 'Deze opzoeking is te complex en duurt te lang. Probeer een eenvoudigere zoekopdracht of probeer terug op een rustiger tijdstip.',
175
+ });
176
+ }
177
+ throw error;
178
+ }
147
179
 
148
180
  const payments = Payment.fromRows(data, 'payments');
149
181
 
@@ -245,6 +245,8 @@ export class PatchOrganizationRegistrationPeriodsEndpoint extends Endpoint<Param
245
245
  await organizationPeriod.save();
246
246
 
247
247
  for (const s of struct.groups) {
248
+ s.settings.registeredMembers = 0;
249
+ s.settings.reservedMembers = 0;
248
250
  await PatchOrganizationRegistrationPeriodsEndpoint.createGroup(s, organization.id, period);
249
251
  }
250
252
  const groups = await Group.getAll(organization.id, organizationPeriod.periodId);
@@ -1,12 +1,11 @@
1
1
  import { Decoder } from '@simonbackx/simple-encoding';
2
2
  import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
3
- import { CountFilteredRequest, CountResponse, PermissionLevel } from '@stamhoofd/structures';
3
+ import { CountFilteredRequest, CountResponse } from '@stamhoofd/structures';
4
4
 
5
- import { Webshop } from '@stamhoofd/models';
6
5
  import { Context } from '../../../../helpers/Context';
7
6
  import { GetWebshopOrdersEndpoint } from './GetWebshopOrdersEndpoint';
8
7
 
9
- type Params = { id: string };
8
+ type Params = Record<string, never>;
10
9
  type Query = CountFilteredRequest;
11
10
  type Body = undefined;
12
11
  type ResponseBody = CountResponse;
@@ -19,7 +18,7 @@ export class GetWebshopOrdersCountEndpoint extends Endpoint<Params, Query, Body,
19
18
  return [false];
20
19
  }
21
20
 
22
- const params = Endpoint.parseParameters(request.url, '/webshop/@id/orders/count', { id: String });
21
+ const params = Endpoint.parseParameters(request.url, '/webshop/orders/count', {});
23
22
 
24
23
  if (params) {
25
24
  return [true, params as Params];
@@ -36,14 +35,7 @@ export class GetWebshopOrdersCountEndpoint extends Endpoint<Params, Query, Body,
36
35
  throw Context.auth.error();
37
36
  }
38
37
 
39
- const webshopId = request.params.id;
40
-
41
- const webshop = await Webshop.getByID(webshopId);
42
- if (!webshop || !await Context.auth.canAccessWebshop(webshop, PermissionLevel.Read)) {
43
- throw Context.auth.notFoundOrNoAccess('Je hebt geen toegang tot de bestellingen van deze webshop');
44
- }
45
-
46
- const query = GetWebshopOrdersEndpoint.buildQuery(webshopId, request.query);
38
+ const query = GetWebshopOrdersEndpoint.buildQuery(request.query);
47
39
 
48
40
  const count = await query
49
41
  .count();