@stamhoofd/backend 2.89.2 → 2.90.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +12 -11
- package/src/boot.ts +2 -0
- package/src/crons/balance-emails.ts +1 -6
- package/src/endpoints/admin/organizations/GetOrganizationsEndpoint.ts +1 -1
- package/src/endpoints/admin/organizations/SearchUitpasOrganizersEndpoint.ts +42 -0
- package/src/endpoints/global/audit-logs/GetAuditLogsEndpoint.ts +4 -4
- package/src/endpoints/global/events/GetEventNotificationsEndpoint.ts +3 -3
- package/src/endpoints/global/events/GetEventsEndpoint.ts +2 -2
- package/src/endpoints/global/events/PatchEventsEndpoint.ts +23 -2
- package/src/endpoints/global/groups/GetGroupsEndpoint.ts +6 -6
- package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.test.ts +8 -6
- package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.ts +2 -2
- package/src/endpoints/global/platform/GetPlatformEndpoint.ts +1 -0
- package/src/endpoints/global/registration/PatchUserMembersEndpoint.test.ts +10 -8
- package/src/endpoints/global/registration/RegisterMembersEndpoint.ts +11 -0
- package/src/endpoints/global/registration-periods/GetRegistrationPeriodsEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/documents/GetDocumentsEndpoint.ts +3 -6
- package/src/endpoints/organization/dashboard/organization/GetUitpasClientIdEndpoint.ts +38 -0
- package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.ts +31 -1
- package/src/endpoints/organization/dashboard/organization/SetUitpasClientCredentialsEndpoint.ts +108 -0
- package/src/endpoints/organization/dashboard/payments/GetPaymentsEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/receivable-balances/GetReceivableBalancesEndpoint.ts +1 -2
- package/src/endpoints/organization/dashboard/registration-periods/GetOrganizationRegistrationPeriodsEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.ts +9 -1
- package/src/endpoints/organization/dashboard/webshops/GetWebshopOrdersEndpoint.ts +3 -2
- package/src/endpoints/organization/dashboard/webshops/GetWebshopTicketsEndpoint.ts +1 -1
- package/src/endpoints/organization/webshops/GetWebshopEndpoint.test.ts +2 -9
- package/src/endpoints/organization/webshops/GetWebshopEndpoint.ts +1 -7
- package/src/endpoints/organization/webshops/PlaceOrderEndpoint.ts +68 -1
- package/src/endpoints/organization/webshops/RetrieveUitpasSocialTariffPriceEndpoint.ts +27 -20
- package/src/helpers/AdminPermissionChecker.ts +129 -22
- package/src/helpers/AuthenticatedStructures.ts +13 -10
- package/src/helpers/Context.ts +1 -1
- package/src/helpers/UitpasTokenRepository.ts +125 -35
- package/src/helpers/ViesHelper.ts +2 -1
- package/src/seeds/0000000002-clear-stamhoofd-email-templates.ts +13 -0
- package/src/seeds/0000000003-default-email-templates.ts +20 -0
- package/src/seeds/data/default-email-templates.sql +55 -0
- package/src/services/RegistrationService.ts +6 -4
- package/src/services/uitpas/UitpasService.test.ts +23 -0
- package/src/services/uitpas/UitpasService.ts +222 -0
- package/src/services/uitpas/checkPermissionsFor.ts +111 -0
- package/src/services/uitpas/checkUitpasNumbers.ts +180 -0
- package/src/services/uitpas/getSocialTariffForEvent.ts +90 -0
- package/src/services/uitpas/getSocialTariffForUitpasNumbers.ts +181 -0
- package/src/services/uitpas/searchUitpasOrganizers.ts +93 -0
- package/src/sql-filters/audit-logs.ts +26 -6
- package/src/sql-filters/balance-item-payments.ts +23 -8
- package/src/sql-filters/base-registration-filter-compilers.ts +74 -23
- package/src/sql-filters/documents.ts +46 -13
- package/src/sql-filters/event-notifications.ts +48 -12
- package/src/sql-filters/events.ts +62 -26
- package/src/sql-filters/groups.ts +12 -12
- package/src/sql-filters/members.ts +325 -137
- package/src/sql-filters/orders.ts +96 -48
- package/src/sql-filters/organization-registration-periods.ts +16 -4
- package/src/sql-filters/organizations.ts +105 -99
- package/src/sql-filters/payments.ts +97 -47
- package/src/sql-filters/receivable-balances.ts +56 -19
- package/src/sql-filters/registration-periods.ts +16 -4
- package/src/sql-filters/registrations.ts +2 -2
- package/src/sql-filters/shared/EmailRelationFilterCompilers.ts +14 -8
- package/src/sql-filters/tickets.ts +26 -6
- package/tests/e2e/charge-members.test.ts +1 -0
- package/src/helpers/UitpasNumberValidator.test.ts +0 -23
- package/src/helpers/UitpasNumberValidator.ts +0 -185
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stamhoofd/backend",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.90.0",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
"dev": "wait-on ../../shared/middleware/dist/index.js && concurrently -r 'yarn -s build --watch --preserveWatchOutput' \"yarn -s dev:watch\"",
|
|
13
13
|
"dev:watch": "wait-on ./dist/index.js && nodemon --quiet --inspect=5858 --watch dist --watch '../../../shared/*/dist/' --watch '../../shared/*/dist/' --ext .ts,.json,.sql,.js --delay 2000ms --exec 'node --enable-source-maps ./dist/index.js' --signal SIGTERM",
|
|
14
14
|
"dev:backend": "yarn -s dev",
|
|
15
|
-
"build": "rm -rf ./dist/src/migrations && rm -rf ./dist/src/seeds && tsc -b",
|
|
15
|
+
"build": "rm -rf ./dist/src/migrations && rm -rf ./dist/src/seeds && yarn -s copy-assets && tsc -b",
|
|
16
|
+
"copy-assets": "rsync --delete --mkpath --exclude='*.ts' --exclude='*.js' -r --checksum ./src/ ./dist/src/",
|
|
16
17
|
"build:full": "yarn -s clear && yarn -s build",
|
|
17
18
|
"clear": "rm -rf ./dist",
|
|
18
19
|
"start": "yarn -s build && node --enable-source-maps ./dist/index.js",
|
|
@@ -44,14 +45,14 @@
|
|
|
44
45
|
"@simonbackx/simple-encoding": "2.22.0",
|
|
45
46
|
"@simonbackx/simple-endpoints": "1.20.1",
|
|
46
47
|
"@simonbackx/simple-logging": "^1.0.1",
|
|
47
|
-
"@stamhoofd/backend-i18n": "2.
|
|
48
|
-
"@stamhoofd/backend-middleware": "2.
|
|
49
|
-
"@stamhoofd/email": "2.
|
|
50
|
-
"@stamhoofd/models": "2.
|
|
51
|
-
"@stamhoofd/queues": "2.
|
|
52
|
-
"@stamhoofd/sql": "2.
|
|
53
|
-
"@stamhoofd/structures": "2.
|
|
54
|
-
"@stamhoofd/utility": "2.
|
|
48
|
+
"@stamhoofd/backend-i18n": "2.90.0",
|
|
49
|
+
"@stamhoofd/backend-middleware": "2.90.0",
|
|
50
|
+
"@stamhoofd/email": "2.90.0",
|
|
51
|
+
"@stamhoofd/models": "2.90.0",
|
|
52
|
+
"@stamhoofd/queues": "2.90.0",
|
|
53
|
+
"@stamhoofd/sql": "2.90.0",
|
|
54
|
+
"@stamhoofd/structures": "2.90.0",
|
|
55
|
+
"@stamhoofd/utility": "2.90.0",
|
|
55
56
|
"archiver": "^7.0.1",
|
|
56
57
|
"axios": "^1.8.2",
|
|
57
58
|
"cookie": "^0.7.0",
|
|
@@ -69,5 +70,5 @@
|
|
|
69
70
|
"publishConfig": {
|
|
70
71
|
"access": "public"
|
|
71
72
|
},
|
|
72
|
-
"gitHead": "
|
|
73
|
+
"gitHead": "d360b2ce795e378363765863457cd595f4841b73"
|
|
73
74
|
}
|
package/src/boot.ts
CHANGED
|
@@ -20,6 +20,7 @@ import { PlatformMembershipService } from './services/PlatformMembershipService'
|
|
|
20
20
|
import { UniqueUserService } from './services/UniqueUserService';
|
|
21
21
|
import { QueueHandler } from '@stamhoofd/queues';
|
|
22
22
|
import { SimpleError } from '@simonbackx/simple-errors';
|
|
23
|
+
import { UitpasService } from './services/uitpas/UitpasService';
|
|
23
24
|
|
|
24
25
|
process.on('unhandledRejection', (error: Error) => {
|
|
25
26
|
console.error('unhandledRejection');
|
|
@@ -229,6 +230,7 @@ const start = async () => {
|
|
|
229
230
|
PlatformMembershipService.listen();
|
|
230
231
|
DocumentService.listen();
|
|
231
232
|
SetupStepUpdater.listen();
|
|
233
|
+
UitpasService.listen();
|
|
232
234
|
|
|
233
235
|
startCrons();
|
|
234
236
|
seeds().catch(console.error);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { registerCron } from '@stamhoofd/crons';
|
|
2
|
-
import { CachedBalance, Email, EmailRecipient, Organization,
|
|
2
|
+
import { CachedBalance, Email, EmailRecipient, Organization, User } from '@stamhoofd/models';
|
|
3
3
|
import { IterableSQLSelect, readDynamicSQLExpression, SQL, SQLCalculation, SQLPlusSign } from '@stamhoofd/sql';
|
|
4
4
|
import { EmailRecipientFilter, EmailRecipientFilterType, EmailRecipientSubfilter, EmailTemplateType, OrganizationEmail, ReceivableBalanceType, StamhoofdFilter } from '@stamhoofd/structures';
|
|
5
5
|
import { ContextInstance } from '../helpers/Context';
|
|
@@ -31,11 +31,6 @@ async function balanceEmails() {
|
|
|
31
31
|
savedIterator = Organization.select().limit(10).all();
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
const platform = await Platform.getSharedPrivateStruct();
|
|
35
|
-
|
|
36
|
-
if (!platform.config.featureFlags.includes('balance-emails')) {
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
34
|
const systemUser = await User.getSystem();
|
|
40
35
|
|
|
41
36
|
for await (const organization of savedIterator.maxQueries(5)) {
|
|
@@ -2,7 +2,7 @@ import { Decoder } from '@simonbackx/simple-encoding';
|
|
|
2
2
|
import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
|
|
3
3
|
import { SimpleError } from '@simonbackx/simple-errors';
|
|
4
4
|
import { Organization } from '@stamhoofd/models';
|
|
5
|
-
import { SQL,
|
|
5
|
+
import { SQL, applySQLSorter, compileToSQLFilter } from '@stamhoofd/sql';
|
|
6
6
|
import { CountFilteredRequest, LimitedFilteredRequest, Organization as OrganizationStruct, PaginatedResponse, PermissionLevel, StamhoofdFilter, assertSort, getSortFilter } from '@stamhoofd/structures';
|
|
7
7
|
|
|
8
8
|
import { SQLResultNamespacedRow } from '@simonbackx/simple-database';
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
|
|
2
|
+
import { UitpasService } from '../../../services/uitpas/UitpasService';
|
|
3
|
+
import { UitpasOrganizersResponse } from '@stamhoofd/structures';
|
|
4
|
+
import { AutoEncoder, Decoder, field, StringDecoder } from '@simonbackx/simple-encoding';
|
|
5
|
+
import { Context } from '../../../helpers/Context';
|
|
6
|
+
|
|
7
|
+
type Params = Record<string, never>;
|
|
8
|
+
class Query extends AutoEncoder {
|
|
9
|
+
@field({ decoder: StringDecoder })
|
|
10
|
+
name: string;
|
|
11
|
+
}
|
|
12
|
+
type Body = undefined;
|
|
13
|
+
type ResponseBody = UitpasOrganizersResponse;
|
|
14
|
+
|
|
15
|
+
export class SearchUitpasOrganizersEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
16
|
+
queryDecoder = Query as Decoder<Query>;
|
|
17
|
+
|
|
18
|
+
protected doesMatch(request: Request): [true, Params] | [false] {
|
|
19
|
+
if (request.method !== 'GET') {
|
|
20
|
+
return [false];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const params = Endpoint.parseParameters(request.url, '/organization/search-uitpas-organizers', {});
|
|
24
|
+
|
|
25
|
+
if (params) {
|
|
26
|
+
return [true, params as Params];
|
|
27
|
+
}
|
|
28
|
+
return [false];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async handle(request: DecodedRequest<Params, Query, Body>) {
|
|
32
|
+
const organization = await Context.setOrganizationScope();
|
|
33
|
+
await Context.authenticate();
|
|
34
|
+
|
|
35
|
+
if (!await Context.auth.hasFullAccess(organization.id)) {
|
|
36
|
+
throw Context.auth.error();
|
|
37
|
+
}
|
|
38
|
+
const uitpasOrganizersResponse = await UitpasService.searchUitpasOrganizers(request.query.name);
|
|
39
|
+
|
|
40
|
+
return new Response(uitpasOrganizersResponse);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -2,7 +2,7 @@ import { Decoder } from '@simonbackx/simple-encoding';
|
|
|
2
2
|
import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
|
|
3
3
|
import { SimpleError } from '@simonbackx/simple-errors';
|
|
4
4
|
import { AuditLog } from '@stamhoofd/models';
|
|
5
|
-
import { SQL,
|
|
5
|
+
import { SQL, SQLSortDefinitions, applySQLSorter, compileToSQLFilter } from '@stamhoofd/sql';
|
|
6
6
|
import { AuditLog as AuditLogStruct, CountFilteredRequest, LimitedFilteredRequest, PaginatedResponse, StamhoofdFilter, assertSort, getSortFilter } from '@stamhoofd/structures';
|
|
7
7
|
|
|
8
8
|
import { AuthenticatedStructures } from '../../../helpers/AuthenticatedStructures';
|
|
@@ -15,7 +15,7 @@ type Query = LimitedFilteredRequest;
|
|
|
15
15
|
type Body = undefined;
|
|
16
16
|
type ResponseBody = PaginatedResponse<AuditLogStruct[], LimitedFilteredRequest>;
|
|
17
17
|
|
|
18
|
-
const filterCompilers
|
|
18
|
+
const filterCompilers = auditLogFilterCompilers;
|
|
19
19
|
const sorters: SQLSortDefinitions<AuditLog> = auditLogSorters;
|
|
20
20
|
|
|
21
21
|
export class GetAuditLogsEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
@@ -46,13 +46,13 @@ export class GetAuditLogsEndpoint extends Endpoint<Params, Query, Body, Response
|
|
|
46
46
|
scopeFilter = {
|
|
47
47
|
organizationId: organization.id,
|
|
48
48
|
};
|
|
49
|
-
}
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
50
51
|
if (!q.filter || typeof q.filter !== 'object' || !('objectId' in q.filter)) {
|
|
51
52
|
scopeFilter = {
|
|
52
53
|
organizationId: organization.id,
|
|
53
54
|
};
|
|
54
55
|
}
|
|
55
|
-
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
else {
|
|
@@ -2,21 +2,21 @@ import { Decoder } from '@simonbackx/simple-encoding';
|
|
|
2
2
|
import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
|
|
3
3
|
import { SimpleError } from '@simonbackx/simple-errors';
|
|
4
4
|
import { EventNotification } from '@stamhoofd/models';
|
|
5
|
-
import { SQL,
|
|
5
|
+
import { SQL, SQLSortDefinitions, applySQLSorter, compileToSQLFilter } from '@stamhoofd/sql';
|
|
6
6
|
import { AccessRight, CountFilteredRequest, EventNotification as EventNotificationStruct, LimitedFilteredRequest, PaginatedResponse, StamhoofdFilter, assertSort, getSortFilter } from '@stamhoofd/structures';
|
|
7
7
|
|
|
8
|
+
import { SQLResultNamespacedRow } from '@simonbackx/simple-database';
|
|
8
9
|
import { AuthenticatedStructures } from '../../../helpers/AuthenticatedStructures';
|
|
9
10
|
import { Context } from '../../../helpers/Context';
|
|
10
11
|
import { eventNotificationsFilterCompilers } from '../../../sql-filters/event-notifications';
|
|
11
12
|
import { eventNotificationsSorters } from '../../../sql-sorters/event-notifications';
|
|
12
|
-
import { SQLResultNamespacedRow } from '@simonbackx/simple-database';
|
|
13
13
|
|
|
14
14
|
type Params = Record<string, never>;
|
|
15
15
|
type Query = LimitedFilteredRequest;
|
|
16
16
|
type Body = undefined;
|
|
17
17
|
type ResponseBody = PaginatedResponse<EventNotificationStruct[], LimitedFilteredRequest>;
|
|
18
18
|
|
|
19
|
-
const filterCompilers
|
|
19
|
+
const filterCompilers = eventNotificationsFilterCompilers;
|
|
20
20
|
const sorters: SQLSortDefinitions<SQLResultNamespacedRow> = eventNotificationsSorters;
|
|
21
21
|
|
|
22
22
|
export class GetEventNotificationsEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
@@ -2,7 +2,7 @@ import { Decoder } from '@simonbackx/simple-encoding';
|
|
|
2
2
|
import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
|
|
3
3
|
import { SimpleError } from '@simonbackx/simple-errors';
|
|
4
4
|
import { Event } from '@stamhoofd/models';
|
|
5
|
-
import { SQL,
|
|
5
|
+
import { SQL, SQLSortDefinitions, applySQLSorter, compileToSQLFilter } from '@stamhoofd/sql';
|
|
6
6
|
import { CountFilteredRequest, Event as EventStruct, LimitedFilteredRequest, PaginatedResponse, StamhoofdFilter, assertSort, getSortFilter } from '@stamhoofd/structures';
|
|
7
7
|
|
|
8
8
|
import { AuthenticatedStructures } from '../../../helpers/AuthenticatedStructures';
|
|
@@ -15,7 +15,7 @@ type Query = LimitedFilteredRequest;
|
|
|
15
15
|
type Body = undefined;
|
|
16
16
|
type ResponseBody = PaginatedResponse<EventStruct[], LimitedFilteredRequest>;
|
|
17
17
|
|
|
18
|
-
const filterCompilers
|
|
18
|
+
const filterCompilers = eventFilterCompilers;
|
|
19
19
|
const sorters: SQLSortDefinitions<Event> = eventSorters;
|
|
20
20
|
|
|
21
21
|
export class GetEventsEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
@@ -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
|
-
import { Event, Group, Platform, RegistrationPeriod } from '@stamhoofd/models';
|
|
4
|
-
import { AuditLogSource, Event as EventStruct, Group as GroupStruct, GroupType, NamedObject } from '@stamhoofd/structures';
|
|
3
|
+
import { Event, Group, Platform, RegistrationPeriod, Webshop } from '@stamhoofd/models';
|
|
4
|
+
import { AuditLogSource, Event as EventStruct, Group as GroupStruct, GroupType, NamedObject, PermissionLevel } from '@stamhoofd/structures';
|
|
5
5
|
|
|
6
6
|
import { SimpleError } from '@simonbackx/simple-errors';
|
|
7
7
|
import { SQL, SQLWhereSign } from '@stamhoofd/sql';
|
|
@@ -309,6 +309,27 @@ export class PatchEventsEndpoint extends Endpoint<Params, Query, Body, ResponseB
|
|
|
309
309
|
PatchEventsEndpoint.throwIfAddressIsMissing(event);
|
|
310
310
|
}
|
|
311
311
|
|
|
312
|
+
if (patch.webshopId !== undefined) {
|
|
313
|
+
if (patch.webshopId === null) {
|
|
314
|
+
event.webshopId = null;
|
|
315
|
+
}
|
|
316
|
+
else {
|
|
317
|
+
const webshop = await Webshop.getByID(patch.webshopId);
|
|
318
|
+
if (!webshop || (event.organizationId && webshop.organizationId !== event.organizationId)) {
|
|
319
|
+
throw new SimpleError({
|
|
320
|
+
code: 'not_found',
|
|
321
|
+
message: 'Webshop not found',
|
|
322
|
+
human: $t(`c5f3d2c3-9d7a-473d-ba91-63ce104a2de5`),
|
|
323
|
+
field: 'webshopId',
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
if (!await Context.auth.canAccessWebshop(webshop, PermissionLevel.Read)) {
|
|
327
|
+
throw Context.auth.error($t('2ee1d364-8747-430d-8a33-094e01df465e'));
|
|
328
|
+
}
|
|
329
|
+
event.webshopId = webshop.id;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
312
333
|
await event.save();
|
|
313
334
|
|
|
314
335
|
if (event.groupId) {
|
|
@@ -4,7 +4,7 @@ import { assertSort, CountFilteredRequest, getSortFilter, Group as GroupStruct,
|
|
|
4
4
|
import { Decoder } from '@simonbackx/simple-encoding';
|
|
5
5
|
import { SimpleError } from '@simonbackx/simple-errors';
|
|
6
6
|
import { Group } from '@stamhoofd/models';
|
|
7
|
-
import { applySQLSorter,
|
|
7
|
+
import { applySQLSorter, compileToSQLFilter, SQLFilterDefinitions, SQLSortDefinitions } from '@stamhoofd/sql';
|
|
8
8
|
import { AuthenticatedStructures } from '../../../helpers/AuthenticatedStructures';
|
|
9
9
|
import { Context } from '../../../helpers/Context';
|
|
10
10
|
import { groupFilterCompilers } from '../../../sql-filters/groups';
|
|
@@ -15,7 +15,7 @@ type Query = LimitedFilteredRequest;
|
|
|
15
15
|
type Body = undefined;
|
|
16
16
|
type ResponseBody = PaginatedResponse<GroupStruct[], LimitedFilteredRequest>;
|
|
17
17
|
|
|
18
|
-
const filterCompilers:
|
|
18
|
+
const filterCompilers: SQLFilterDefinitions = groupFilterCompilers;
|
|
19
19
|
const sorters: SQLSortDefinitions<Group> = groupSorters;
|
|
20
20
|
|
|
21
21
|
export class GetGroupsEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
@@ -47,11 +47,11 @@ export class GetGroupsEndpoint extends Endpoint<Params, Query, Body, ResponseBod
|
|
|
47
47
|
const query = Group.select();
|
|
48
48
|
|
|
49
49
|
if (scopeFilter) {
|
|
50
|
-
query.where(await
|
|
50
|
+
query.where(await compileToSQLFilter(scopeFilter, filterCompilers));
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
if (q.filter) {
|
|
54
|
-
query.where(await
|
|
54
|
+
query.where(await compileToSQLFilter(q.filter, filterCompilers));
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
if (q.search) {
|
|
@@ -62,13 +62,13 @@ export class GetGroupsEndpoint extends Endpoint<Params, Query, Body, ResponseBod
|
|
|
62
62
|
};
|
|
63
63
|
|
|
64
64
|
if (searchFilter) {
|
|
65
|
-
query.where(await
|
|
65
|
+
query.where(await compileToSQLFilter(searchFilter, filterCompilers));
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
if (q instanceof LimitedFilteredRequest) {
|
|
70
70
|
if (q.pageFilter) {
|
|
71
|
-
query.where(await
|
|
71
|
+
query.where(await compileToSQLFilter(q.pageFilter, filterCompilers));
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
q.sort = assertSort(q.sort, [{ key: 'id' }]);
|
|
@@ -77,11 +77,12 @@ describe('Endpoint.PatchOrganizationMembersEndpoint', () => {
|
|
|
77
77
|
const token = await Token.createToken(user);
|
|
78
78
|
|
|
79
79
|
const arr: Body = new PatchableArray();
|
|
80
|
+
const newBirthDay = new Date(existingMember.details.birthDay!.getTime() + 1);
|
|
80
81
|
const put = MemberWithRegistrationsBlob.create({
|
|
81
82
|
details: MemberDetails.create({
|
|
82
83
|
firstName,
|
|
83
84
|
lastName,
|
|
84
|
-
birthDay:
|
|
85
|
+
birthDay: newBirthDay,
|
|
85
86
|
email: 'anewemail@example.com',
|
|
86
87
|
}),
|
|
87
88
|
});
|
|
@@ -100,7 +101,7 @@ describe('Endpoint.PatchOrganizationMembersEndpoint', () => {
|
|
|
100
101
|
const member = response.body.members[0];
|
|
101
102
|
expect(member.details.firstName).toBe(firstName);
|
|
102
103
|
expect(member.details.lastName).toBe(lastName);
|
|
103
|
-
expect(member.details.birthDay).toEqual(
|
|
104
|
+
expect(member.details.birthDay).toEqual(newBirthDay);
|
|
104
105
|
expect(member.details.email).toBe('anewemail@example.com'); // this has been merged
|
|
105
106
|
expect(member.details.alternativeEmails).toHaveLength(0);
|
|
106
107
|
});
|
|
@@ -141,11 +142,12 @@ describe('Endpoint.PatchOrganizationMembersEndpoint', () => {
|
|
|
141
142
|
const token = await Token.createToken(user);
|
|
142
143
|
|
|
143
144
|
const arr: Body = new PatchableArray();
|
|
145
|
+
const newBirthDay = new Date(existingMember.details.birthDay!.getTime() + 1);
|
|
144
146
|
const put = MemberWithRegistrationsBlob.create({
|
|
145
147
|
details: MemberDetails.create({
|
|
146
148
|
firstName,
|
|
147
149
|
lastName,
|
|
148
|
-
birthDay:
|
|
150
|
+
birthDay: newBirthDay,
|
|
149
151
|
securityCode: existingMember.details.securityCode,
|
|
150
152
|
email: 'anewemail@example.com',
|
|
151
153
|
}),
|
|
@@ -165,9 +167,9 @@ describe('Endpoint.PatchOrganizationMembersEndpoint', () => {
|
|
|
165
167
|
const member = response.body.members[0];
|
|
166
168
|
expect(member.details.firstName).toBe(firstName);
|
|
167
169
|
expect(member.details.lastName).toBe(lastName);
|
|
168
|
-
expect(member.details.birthDay).toEqual(
|
|
169
|
-
expect(member.details.email).toBe('
|
|
170
|
-
expect(member.details.alternativeEmails).toEqual(['
|
|
170
|
+
expect(member.details.birthDay).toEqual(newBirthDay);
|
|
171
|
+
expect(member.details.email).toBe('anewemail@example.com'); // this has been merged
|
|
172
|
+
expect(member.details.alternativeEmails).toEqual(['original@example.com']); // this has been merged
|
|
171
173
|
|
|
172
174
|
// Check the registration is still there
|
|
173
175
|
expect(member.registrations.length).toBe(1);
|
|
@@ -14,10 +14,10 @@ import { Context } from '../../../helpers/Context';
|
|
|
14
14
|
import { MembershipCharger } from '../../../helpers/MembershipCharger';
|
|
15
15
|
import { MemberUserSyncer } from '../../../helpers/MemberUserSyncer';
|
|
16
16
|
import { SetupStepUpdater } from '../../../helpers/SetupStepUpdater';
|
|
17
|
+
import { MemberNumberService } from '../../../services/MemberNumberService';
|
|
17
18
|
import { PlatformMembershipService } from '../../../services/PlatformMembershipService';
|
|
18
19
|
import { RegistrationService } from '../../../services/RegistrationService';
|
|
19
20
|
import { shouldCheckIfMemberIsDuplicateForPatch } from './shouldCheckIfMemberIsDuplicate';
|
|
20
|
-
import { MemberNumberService } from '../../../services/MemberNumberService';
|
|
21
21
|
|
|
22
22
|
type Params = Record<string, never>;
|
|
23
23
|
type Query = undefined;
|
|
@@ -957,7 +957,7 @@ export class PatchOrganizationMembersEndpoint extends Endpoint<Params, Query, Bo
|
|
|
957
957
|
throw new SimpleError({
|
|
958
958
|
code: 'known_member_missing_rights',
|
|
959
959
|
message: 'Creating known member without sufficient access rights',
|
|
960
|
-
human:
|
|
960
|
+
human: $t(`{member} is al gekend in ons systeem, maar jouw e-mailadres niet. Om toegang te krijgen heb je de beveiligingscode nodig.`, { member: member.details.firstName }),
|
|
961
961
|
statusCode: 400,
|
|
962
962
|
});
|
|
963
963
|
}
|
|
@@ -24,6 +24,7 @@ export class GetPlatformEndpoint extends Endpoint<Params, Query, Body, ResponseB
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
async handle(_: DecodedRequest<Params, Query, Body>) {
|
|
27
|
+
await Context.setOptionalOrganizationScope({ willAuthenticate: true });
|
|
27
28
|
await Context.optionalAuthenticate({ allowWithoutAccount: false });
|
|
28
29
|
|
|
29
30
|
if (Context.optionalAuth?.hasSomePlatformAccess()) {
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
+
import { Database } from '@simonbackx/simple-database';
|
|
1
2
|
import { PatchableArray, PatchMap } from '@simonbackx/simple-encoding';
|
|
2
3
|
import { Endpoint, Request } from '@simonbackx/simple-endpoints';
|
|
3
4
|
import { GroupFactory, MemberFactory, OrganizationFactory, Platform, RegistrationFactory, Token, UserFactory } from '@stamhoofd/models';
|
|
4
5
|
import { MemberDetails, MemberWithRegistrationsBlob, OrganizationMetaData, OrganizationRecordsConfiguration, Parent, PatchAnswers, PermissionLevel, RecordCategory, RecordSettings, RecordTextAnswer, TranslatedString } from '@stamhoofd/structures';
|
|
6
|
+
import { STExpect, TestUtils } from '@stamhoofd/test-utils';
|
|
5
7
|
import { testServer } from '../../../../tests/helpers/TestServer';
|
|
6
8
|
import { PatchUserMembersEndpoint } from './PatchUserMembersEndpoint';
|
|
7
|
-
import { Database } from '@simonbackx/simple-database';
|
|
8
|
-
import { STExpect, TestUtils } from '@stamhoofd/test-utils';
|
|
9
9
|
|
|
10
10
|
const baseUrl = `/members`;
|
|
11
11
|
const endpoint = new PatchUserMembersEndpoint();
|
|
@@ -69,11 +69,12 @@ describe('Endpoint.PatchUserMembersEndpoint', () => {
|
|
|
69
69
|
const token = await Token.createToken(user);
|
|
70
70
|
|
|
71
71
|
const arr: Body = new PatchableArray();
|
|
72
|
+
const newBirthDay = new Date(existingMember.details.birthDay!.getTime() + 1);
|
|
72
73
|
const put = MemberWithRegistrationsBlob.create({
|
|
73
74
|
details: MemberDetails.create({
|
|
74
75
|
firstName,
|
|
75
76
|
lastName,
|
|
76
|
-
birthDay:
|
|
77
|
+
birthDay: newBirthDay,
|
|
77
78
|
email: 'anewemail@example.com',
|
|
78
79
|
}),
|
|
79
80
|
});
|
|
@@ -92,7 +93,7 @@ describe('Endpoint.PatchUserMembersEndpoint', () => {
|
|
|
92
93
|
const member = response.body.members[0];
|
|
93
94
|
expect(member.details.firstName).toBe(firstName);
|
|
94
95
|
expect(member.details.lastName).toBe(lastName);
|
|
95
|
-
expect(member.details.birthDay).toEqual(
|
|
96
|
+
expect(member.details.birthDay).toEqual(newBirthDay);
|
|
96
97
|
expect(member.details.email).toBe('anewemail@example.com'); // this has been merged
|
|
97
98
|
expect(member.details.alternativeEmails).toHaveLength(0);
|
|
98
99
|
});
|
|
@@ -129,11 +130,12 @@ describe('Endpoint.PatchUserMembersEndpoint', () => {
|
|
|
129
130
|
const token = await Token.createToken(user);
|
|
130
131
|
|
|
131
132
|
const arr: Body = new PatchableArray();
|
|
133
|
+
const newBirthDay = new Date(existingMember.details.birthDay!.getTime() + 1);
|
|
132
134
|
const put = MemberWithRegistrationsBlob.create({
|
|
133
135
|
details: MemberDetails.create({
|
|
134
136
|
firstName,
|
|
135
137
|
lastName,
|
|
136
|
-
birthDay:
|
|
138
|
+
birthDay: newBirthDay,
|
|
137
139
|
securityCode: existingMember.details.securityCode,
|
|
138
140
|
email: 'anewemail@example.com',
|
|
139
141
|
}),
|
|
@@ -153,9 +155,9 @@ describe('Endpoint.PatchUserMembersEndpoint', () => {
|
|
|
153
155
|
const member = response.body.members[0];
|
|
154
156
|
expect(member.details.firstName).toBe(firstName);
|
|
155
157
|
expect(member.details.lastName).toBe(lastName);
|
|
156
|
-
expect(member.details.birthDay).toEqual(
|
|
157
|
-
expect(member.details.email).toBe('
|
|
158
|
-
expect(member.details.alternativeEmails).toEqual(['
|
|
158
|
+
expect(member.details.birthDay).toEqual(newBirthDay);
|
|
159
|
+
expect(member.details.email).toBe('anewemail@example.com'); // this has been merged
|
|
160
|
+
expect(member.details.alternativeEmails).toEqual(['original@example.com']); // this has been merged
|
|
159
161
|
|
|
160
162
|
// Check the registration is still there
|
|
161
163
|
expect(member.registrations.length).toBe(1);
|
|
@@ -345,6 +345,17 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
|
|
|
345
345
|
registration.pricePaid = 0;
|
|
346
346
|
registration.payingOrganizationId = null;
|
|
347
347
|
|
|
348
|
+
if (checkout.isAdminFromSameOrganization) {
|
|
349
|
+
registration.sendConfirmationEmail = checkout.sendConfirmationEmail;
|
|
350
|
+
}
|
|
351
|
+
else {
|
|
352
|
+
registration.sendConfirmationEmail = true;
|
|
353
|
+
if (checkout.asOrganizationId) {
|
|
354
|
+
// use group default
|
|
355
|
+
registration.sendConfirmationEmail = group.privateSettings.sendConfirmationEmailForManualRegistrations;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
348
359
|
// NOTE: we don't reset deactivatedAt - registeredAt, because those will get reset when markValid is called later on (while keeping the original registeredAt date)
|
|
349
360
|
// registration.deactivatedAt = null;
|
|
350
361
|
// registration.registeredAt = null; // this is required to trigger platform membership updates
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
|
|
2
|
-
import { assertSort, CountFilteredRequest, getSortFilter, LimitedFilteredRequest, RegistrationPeriod as RegistrationPeriodStruct,
|
|
2
|
+
import { assertSort, CountFilteredRequest, getSortFilter, LimitedFilteredRequest, PaginatedResponse, RegistrationPeriod as RegistrationPeriodStruct, StamhoofdFilter } from '@stamhoofd/structures';
|
|
3
3
|
|
|
4
|
+
import { Decoder } from '@simonbackx/simple-encoding';
|
|
4
5
|
import { SimpleError } from '@simonbackx/simple-errors';
|
|
5
6
|
import { RegistrationPeriod } from '@stamhoofd/models';
|
|
6
7
|
import { applySQLSorter, compileToSQLFilter, SQLFilterDefinitions, SQLSortDefinitions } from '@stamhoofd/sql';
|
|
7
8
|
import { registrationPeriodFilterCompilers } from '../../../sql-filters/registration-periods';
|
|
8
9
|
import { registrationPeriodSorters } from '../../../sql-sorters/registration-periods';
|
|
9
|
-
import { Decoder } from '@simonbackx/simple-encoding';
|
|
10
10
|
|
|
11
11
|
type Params = Record<string, never>;
|
|
12
12
|
type Query = LimitedFilteredRequest;
|
|
@@ -3,7 +3,7 @@ import { Document } from '@stamhoofd/models';
|
|
|
3
3
|
import { assertSort, CountFilteredRequest, Document as DocumentStruct, getSortFilter, LimitedFilteredRequest, PaginatedResponse, SearchFilterFactory, StamhoofdFilter } from '@stamhoofd/structures';
|
|
4
4
|
|
|
5
5
|
import { Decoder } from '@simonbackx/simple-encoding';
|
|
6
|
-
import {
|
|
6
|
+
import { applySQLSorter, compileToSQLFilter, SQL, SQLFilterDefinitions, SQLSortDefinitions } from '@stamhoofd/sql';
|
|
7
7
|
import { AuthenticatedStructures } from '../../../../helpers/AuthenticatedStructures';
|
|
8
8
|
import { Context } from '../../../../helpers/Context';
|
|
9
9
|
import { LimitedFilteredRequestHelper } from '../../../../helpers/LimitedFilteredRequestHelper';
|
|
@@ -46,9 +46,7 @@ export class GetDocumentsEndpoint extends Endpoint<Params, Query, Body, Response
|
|
|
46
46
|
const query = SQL
|
|
47
47
|
.select(SQL.wildcard(documentTable))
|
|
48
48
|
.from(SQL.table(documentTable))
|
|
49
|
-
.where(
|
|
50
|
-
organizationId: organization.id,
|
|
51
|
-
}, filterCompilers));
|
|
49
|
+
.where('organizationId', organization.id);
|
|
52
50
|
|
|
53
51
|
if (q.filter) {
|
|
54
52
|
query.where(await compileToSQLFilter(q.filter, filterCompilers));
|
|
@@ -151,7 +149,6 @@ function getDocumentSearchFilter(search: string | null): StamhoofdFilter | null
|
|
|
151
149
|
$contains: search,
|
|
152
150
|
},
|
|
153
151
|
},
|
|
154
|
-
]
|
|
155
|
-
,
|
|
152
|
+
],
|
|
156
153
|
};
|
|
157
154
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
|
|
2
|
+
import { UitpasGetClientIdResponse } from '@stamhoofd/structures';
|
|
3
|
+
|
|
4
|
+
import { Context } from '../../../../helpers/Context';
|
|
5
|
+
import { UitpasService } from '../../../../services/uitpas/UitpasService';
|
|
6
|
+
|
|
7
|
+
type Params = Record<string, never>;
|
|
8
|
+
type Query = undefined;
|
|
9
|
+
type Body = undefined;
|
|
10
|
+
type ResponseBody = UitpasGetClientIdResponse;
|
|
11
|
+
|
|
12
|
+
export class GetUitpasClientIdEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
13
|
+
protected doesMatch(request: Request): [true, Params] | [false] {
|
|
14
|
+
if (request.method !== 'GET') {
|
|
15
|
+
return [false];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const params = Endpoint.parseParameters(request.url, '/organization/uitpas-client-id', {});
|
|
19
|
+
|
|
20
|
+
if (params) {
|
|
21
|
+
return [true, params as Params];
|
|
22
|
+
}
|
|
23
|
+
return [false];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async handle() {
|
|
27
|
+
const organization = await Context.setOrganizationScope();
|
|
28
|
+
await Context.authenticate();
|
|
29
|
+
|
|
30
|
+
if (!await Context.auth.hasFullAccess(organization.id)) {
|
|
31
|
+
throw Context.auth.error();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const resp = new UitpasGetClientIdResponse();
|
|
35
|
+
resp.clientId = await UitpasService.getClientIdFor(organization.id);
|
|
36
|
+
return new Response(resp);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -2,7 +2,7 @@ import { AutoEncoderPatchType, cloneObject, Decoder, isPatchableArray, isPatchMa
|
|
|
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, StripeAccount, Webshop } from '@stamhoofd/models';
|
|
5
|
-
import { BuckarooSettings, Company, MemberResponsibility, OrganizationMetaData, Organization as OrganizationStruct, PayconiqAccount, PaymentMethod, PaymentMethodHelper, PermissionLevel, PermissionRoleDetailed, PermissionRoleForResponsibility, PermissionsResourceType, ResourcePermissions } from '@stamhoofd/structures';
|
|
5
|
+
import { BuckarooSettings, Company, MemberResponsibility, OrganizationMetaData, Organization as OrganizationStruct, PayconiqAccount, PaymentMethod, PaymentMethodHelper, PermissionLevel, PermissionRoleDetailed, PermissionRoleForResponsibility, PermissionsResourceType, ResourcePermissions, UitpasClientCredentialsStatus } from '@stamhoofd/structures';
|
|
6
6
|
import { Formatter } from '@stamhoofd/utility';
|
|
7
7
|
|
|
8
8
|
import { AuthenticatedStructures } from '../../../../helpers/AuthenticatedStructures';
|
|
@@ -12,6 +12,7 @@ import { MemberUserSyncer } from '../../../../helpers/MemberUserSyncer';
|
|
|
12
12
|
import { SetupStepUpdater } from '../../../../helpers/SetupStepUpdater';
|
|
13
13
|
import { TagHelper } from '../../../../helpers/TagHelper';
|
|
14
14
|
import { ViesHelper } from '../../../../helpers/ViesHelper';
|
|
15
|
+
import { UitpasService } from '../../../../services/uitpas/UitpasService';
|
|
15
16
|
|
|
16
17
|
type Params = Record<string, never>;
|
|
17
18
|
type Query = undefined;
|
|
@@ -297,6 +298,35 @@ export class PatchOrganizationEndpoint extends Endpoint<Params, Query, Body, Res
|
|
|
297
298
|
|
|
298
299
|
updateTags = true;
|
|
299
300
|
}
|
|
301
|
+
|
|
302
|
+
if (request.body.meta.uitpasClientCredentialsStatus) {
|
|
303
|
+
throw new SimpleError({
|
|
304
|
+
code: 'invalid_field',
|
|
305
|
+
message: 'You cannot set the uitpasClientCredentialsStatus manually',
|
|
306
|
+
human: $t('Je kan de status van de UiTPAS-credentials niet handmatig instellen'),
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
if (request.body.meta.uitpasOrganizerId) {
|
|
311
|
+
const oldStatus = organization.meta.uitpasClientCredentialsStatus;
|
|
312
|
+
// re-evaluate the status
|
|
313
|
+
if (oldStatus !== UitpasClientCredentialsStatus.NotConfigured) {
|
|
314
|
+
organization.meta.uitpasClientCredentialsStatus = UitpasClientCredentialsStatus.NotChecked;
|
|
315
|
+
organization.meta.uitpasOrganizerId = request.body.meta.uitpasOrganizerId;
|
|
316
|
+
const { status } = await UitpasService.checkPermissionsFor(organization.id, organization.meta.uitpasOrganizerId);
|
|
317
|
+
organization.meta.uitpasClientCredentialsStatus = status;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// human message is ignored here
|
|
321
|
+
// if (human) {
|
|
322
|
+
// const e = new SimpleError({
|
|
323
|
+
// code: 'uitpas-client-credentials-error',
|
|
324
|
+
// message: 'set-uitpas-credentials-returned-human-message',
|
|
325
|
+
// human: human,
|
|
326
|
+
// })
|
|
327
|
+
// errors.addError(e)
|
|
328
|
+
// }
|
|
329
|
+
}
|
|
300
330
|
}
|
|
301
331
|
|
|
302
332
|
if (request.body.active !== undefined) {
|