@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 +2 -0
- package/package.json +10 -10
- package/src/email-recipient-loaders/orders.ts +60 -0
- package/src/endpoints/admin/organizations/GetOrganizationsEndpoint.ts +19 -1
- package/src/endpoints/admin/organizations/PatchOrganizationsEndpoint.ts +3 -35
- package/src/endpoints/global/members/GetMembersEndpoint.ts +17 -1
- package/src/endpoints/global/platform/PatchPlatformEnpoint.ts +32 -9
- package/src/endpoints/global/registration/RegisterMembersEndpoint.ts +10 -0
- package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.ts +13 -30
- package/src/endpoints/organization/dashboard/payments/GetPaymentsEndpoint.ts +34 -2
- package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.ts +2 -0
- package/src/endpoints/organization/dashboard/webshops/GetWebshopOrdersCountEndpoint.ts +4 -12
- package/src/endpoints/organization/dashboard/webshops/GetWebshopOrdersEndpoint.ts +10 -30
- package/src/excel-loaders/organizations.ts +47 -0
- package/src/helpers/AuthenticatedStructures.ts +6 -2
- package/src/helpers/ModelHelper.ts +32 -0
- package/src/helpers/TagHelper.test.ts +373 -0
- package/src/helpers/TagHelper.ts +170 -0
- package/src/seeds/1729253172-update-orders.ts +32 -0
- package/src/sql-filters/members.ts +54 -19
- package/src/sql-filters/orders.ts +42 -7
- package/src/sql-filters/organizations.ts +4 -0
- package/src/sql-filters/payments.ts +1 -0
- package/src/sql-filters/registrations.ts +3 -1
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.
|
|
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.
|
|
40
|
-
"@stamhoofd/backend-middleware": "2.
|
|
41
|
-
"@stamhoofd/email": "2.
|
|
42
|
-
"@stamhoofd/models": "2.
|
|
43
|
-
"@stamhoofd/queues": "2.
|
|
44
|
-
"@stamhoofd/sql": "2.
|
|
45
|
-
"@stamhoofd/structures": "2.
|
|
46
|
-
"@stamhoofd/utility": "2.
|
|
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": "
|
|
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
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
92
|
-
|
|
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
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
-
|
|
289
|
-
const
|
|
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
|
-
|
|
297
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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();
|