@stamhoofd/backend 2.115.0 → 2.116.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 +10 -10
- package/src/audit-logs/ModelLogger.ts +2 -3
- package/src/boot.ts +4 -4
- package/src/crons/delete-archived-data.ts +47 -0
- package/src/crons/index.ts +1 -0
- package/src/debug.ts +230 -0
- package/src/email-recipient-loaders/documents.ts +1 -1
- package/src/email-recipient-loaders/payments.ts +1 -1
- package/src/endpoints/auth/PatchUserEndpoint.ts +4 -4
- package/src/endpoints/global/members/GetMemberFamilyEndpoint.ts +4 -4
- package/src/endpoints/global/members/GetMembersEndpoint.ts +4 -16
- package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.ts +8 -7
- package/src/endpoints/global/registration/GetUserDetailedPayableBalanceEndpoint.ts +2 -2
- package/src/endpoints/global/registration/GetUserDocumentsEndpoint.ts +2 -2
- package/src/endpoints/global/registration/RegisterMembersEndpoint.test.ts +2 -2
- package/src/endpoints/global/registration/RegisterMembersEndpoint.ts +12 -7
- package/src/endpoints/organization/dashboard/documents/PatchDocumentEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/email/CheckEmailBouncesEndpoint.ts +7 -4
- package/src/endpoints/organization/dashboard/payments/PatchBalanceItemsEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/receivable-balances/GetReceivableBalanceEndpoint.ts +12 -12
- package/src/helpers/AdminPermissionChecker.ts +20 -17
- package/src/helpers/AuthenticatedStructures.ts +177 -107
- package/src/helpers/Context.ts +2 -0
- package/src/helpers/PeriodHelper.ts +5 -45
- package/src/helpers/SetupStepUpdater.ts +5 -4
- package/src/helpers/outstandingBalanceJoin.ts +3 -1
- package/src/services/PaymentService.ts +12 -0
- package/src/services/PlatformMembershipService.ts +3 -3
- package/src/sql-filters/members.ts +1 -1
- package/src/sql-sorters/members.ts +2 -2
- package/tests/e2e/register.test.ts +10 -10
- package/src/endpoints/global/registration/GetPaymentRegistrations.ts +0 -67
|
@@ -4,7 +4,7 @@ import { SimpleError } from '@simonbackx/simple-errors';
|
|
|
4
4
|
import { Document, DocumentTemplate, Member, Registration } from '@stamhoofd/models';
|
|
5
5
|
import { DocumentStatus, Document as DocumentStruct, PermissionLevel } from '@stamhoofd/structures';
|
|
6
6
|
|
|
7
|
-
import { Context } from '../../../../helpers/Context';
|
|
7
|
+
import { Context } from '../../../../helpers/Context.js';
|
|
8
8
|
|
|
9
9
|
type Params = Record<string, never>;
|
|
10
10
|
type Query = undefined;
|
|
@@ -104,7 +104,7 @@ export class PatchDocumentEndpoint extends Endpoint<Params, Query, Body, Respons
|
|
|
104
104
|
put.memberId = registration.memberId;
|
|
105
105
|
}
|
|
106
106
|
if (put.memberId) {
|
|
107
|
-
const member = await Member.
|
|
107
|
+
const member = await Member.getByIdWithUsersAndRegistrations(put.memberId);
|
|
108
108
|
if (!member || !await Context.auth.canAccessMember(member, PermissionLevel.Read)) {
|
|
109
109
|
throw new SimpleError({
|
|
110
110
|
code: 'not_found',
|
|
@@ -4,7 +4,7 @@ import { SimpleError } from '@simonbackx/simple-errors';
|
|
|
4
4
|
import { EmailAddress } from '@stamhoofd/email';
|
|
5
5
|
import { EmailInformation } from '@stamhoofd/structures';
|
|
6
6
|
|
|
7
|
-
import { Context } from '../../../../helpers/Context';
|
|
7
|
+
import { Context } from '../../../../helpers/Context.js';
|
|
8
8
|
|
|
9
9
|
type Params = Record<string, never>;
|
|
10
10
|
type Query = undefined;
|
|
@@ -28,10 +28,13 @@ export class CheckEmailBouncesEndpoint extends Endpoint<Params, Query, Body, Res
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
async handle(request: DecodedRequest<Params, Query, Body>) {
|
|
31
|
-
const organization = await Context.
|
|
31
|
+
const organization = await Context.setOptionalOrganizationScope();
|
|
32
32
|
await Context.authenticate();
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
// null if platform
|
|
35
|
+
const organizationId: string | null = organization ? organization.id : null;
|
|
36
|
+
|
|
37
|
+
if (!await Context.auth.canAccessEmailBounces(organizationId)) {
|
|
35
38
|
throw Context.auth.error();
|
|
36
39
|
}
|
|
37
40
|
|
|
@@ -44,7 +47,7 @@ export class CheckEmailBouncesEndpoint extends Endpoint<Params, Query, Body, Res
|
|
|
44
47
|
});
|
|
45
48
|
}
|
|
46
49
|
|
|
47
|
-
const emails = await EmailAddress.getByEmails(request.body,
|
|
50
|
+
const emails = await EmailAddress.getByEmails(request.body, organizationId);
|
|
48
51
|
return new Response(emails.map(e => EmailInformation.create(e)));
|
|
49
52
|
}
|
|
50
53
|
}
|
|
@@ -242,7 +242,7 @@ export class PatchBalanceItemsEndpoint extends Endpoint<Params, Query, Body, Res
|
|
|
242
242
|
}
|
|
243
243
|
|
|
244
244
|
async validateMemberId(memberId: string) {
|
|
245
|
-
const member = (await Member.
|
|
245
|
+
const member = (await Member.getByIdWithUsersAndRegistrations(memberId));
|
|
246
246
|
|
|
247
247
|
if (!member || !(await Context.auth.canLinkBalanceItemToMember(member))) {
|
|
248
248
|
throw new SimpleError({
|
package/src/endpoints/organization/dashboard/receivable-balances/GetReceivableBalanceEndpoint.ts
CHANGED
|
@@ -2,10 +2,10 @@ import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-
|
|
|
2
2
|
import { DetailedReceivableBalance, PaymentStatus, ReceivableBalanceType } from '@stamhoofd/structures';
|
|
3
3
|
|
|
4
4
|
import { BalanceItem, BalanceItemPayment, CachedBalance, MemberUser, Payment } from '@stamhoofd/models';
|
|
5
|
-
import { Context } from '../../../../helpers/Context';
|
|
6
|
-
import { AuthenticatedStructures } from '../../../../helpers/AuthenticatedStructures';
|
|
5
|
+
import { Context } from '../../../../helpers/Context.js';
|
|
6
|
+
import { AuthenticatedStructures } from '../../../../helpers/AuthenticatedStructures.js';
|
|
7
7
|
import { SQL } from '@stamhoofd/sql';
|
|
8
|
-
import { BalanceItemService } from '../../../../services/BalanceItemService';
|
|
8
|
+
import { BalanceItemService } from '../../../../services/BalanceItemService.js';
|
|
9
9
|
import { Formatter } from '@stamhoofd/utility';
|
|
10
10
|
|
|
11
11
|
type Params = { id: string; type: ReceivableBalanceType };
|
|
@@ -44,7 +44,7 @@ export class GetReceivableBalanceEndpoint extends Endpoint<Params, Query, Body,
|
|
|
44
44
|
switch (request.params.type) {
|
|
45
45
|
case ReceivableBalanceType.organization: {
|
|
46
46
|
// Force cache updates, because sometimes the cache could be out of date
|
|
47
|
-
BalanceItemService.scheduleOrganizationUpdate(organization.id, request.params.id);
|
|
47
|
+
// BalanceItemService.scheduleOrganizationUpdate(organization.id, request.params.id);
|
|
48
48
|
|
|
49
49
|
paymentModels = await Payment.select()
|
|
50
50
|
.where('organizationId', organization.id)
|
|
@@ -67,7 +67,7 @@ export class GetReceivableBalanceEndpoint extends Endpoint<Params, Query, Body,
|
|
|
67
67
|
|
|
68
68
|
case ReceivableBalanceType.member: {
|
|
69
69
|
// Force cache updates, because sometimes the cache could be out of date
|
|
70
|
-
BalanceItemService.scheduleMemberUpdate(organization.id, request.params.id);
|
|
70
|
+
// BalanceItemService.scheduleMemberUpdate(organization.id, request.params.id);
|
|
71
71
|
|
|
72
72
|
paymentModels = await Payment.select()
|
|
73
73
|
.where('organizationId', organization.id)
|
|
@@ -93,11 +93,11 @@ export class GetReceivableBalanceEndpoint extends Endpoint<Params, Query, Body,
|
|
|
93
93
|
const memberIds = Formatter.uniqueArray(memberUsers.map(mu => mu.membersId));
|
|
94
94
|
|
|
95
95
|
// Force cache updates, because sometimes the cache could be out of date
|
|
96
|
-
BalanceItemService.scheduleUserUpdate(organization.id, request.params.id);
|
|
97
|
-
|
|
98
|
-
for (const memberId of memberIds) {
|
|
99
|
-
|
|
100
|
-
}
|
|
96
|
+
// BalanceItemService.scheduleUserUpdate(organization.id, request.params.id);
|
|
97
|
+
//
|
|
98
|
+
// for (const memberId of memberIds) {
|
|
99
|
+
// BalanceItemService.scheduleMemberUpdate(organization.id, memberId);
|
|
100
|
+
// }
|
|
101
101
|
|
|
102
102
|
const q = Payment.select()
|
|
103
103
|
.where('organizationId', organization.id)
|
|
@@ -131,7 +131,7 @@ export class GetReceivableBalanceEndpoint extends Endpoint<Params, Query, Body,
|
|
|
131
131
|
|
|
132
132
|
case ReceivableBalanceType.userWithoutMembers: {
|
|
133
133
|
// Force cache updates, because sometimes the cache could be out of date
|
|
134
|
-
BalanceItemService.scheduleUserUpdate(organization.id, request.params.id);
|
|
134
|
+
// BalanceItemService.scheduleUserUpdate(organization.id, request.params.id);
|
|
135
135
|
|
|
136
136
|
const q = Payment.select()
|
|
137
137
|
.where('organizationId', organization.id)
|
|
@@ -176,7 +176,7 @@ export class GetReceivableBalanceEndpoint extends Endpoint<Params, Query, Body,
|
|
|
176
176
|
}
|
|
177
177
|
|
|
178
178
|
// Flush caches (this makes sure that we do a reload in the frontend after a registration or change, we get the newest balances)
|
|
179
|
-
await BalanceItemService.flushCaches(organization.id);
|
|
179
|
+
// await BalanceItemService.flushCaches(organization.id);
|
|
180
180
|
const balanceItemModels = await CachedBalance.balanceForObjects(organization.id, [request.params.id], request.params.type);
|
|
181
181
|
const balanceItems = await BalanceItem.getStructureWithPayments(balanceItemModels);
|
|
182
182
|
const payments = await AuthenticatedStructures.paymentsGeneral(paymentModels, false);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AutoEncoderPatchType, PatchMap } from '@simonbackx/simple-encoding';
|
|
2
2
|
import { isSimpleError, isSimpleErrors, SimpleError } from '@simonbackx/simple-errors';
|
|
3
|
-
import { BalanceItem, CachedBalance, Document, Email, EmailTemplate, Event, EventNotification, Group, Member, MemberPlatformMembership, MemberWithRegistrations, Order, Organization, OrganizationRegistrationPeriod, Payment, Registration, User, Webshop } from '@stamhoofd/models';
|
|
3
|
+
import { BalanceItem, CachedBalance, Document, Email, EmailTemplate, Event, EventNotification, Group, Member, MemberPlatformMembership, MemberWithRegistrations, MemberWithUsers, MemberWithUsersAndRegistrations, MemberWithUsersRegistrationsAndGroups, Order, Organization, OrganizationRegistrationPeriod, Payment, Registration, User, Webshop } from '@stamhoofd/models';
|
|
4
4
|
import { AccessRight, EmailTemplate as EmailTemplateStruct, EventPermissionChecker, FinancialSupportSettings, GroupCategory, GroupStatus, GroupType, MemberWithRegistrationsBlob, PermissionLevel, PermissionsResourceType, Platform as PlatformStruct, ReceivableBalanceType, RecordSettings, ResourcePermissions, UitpasNumberDetails, UitpasSocialTariff, UitpasSocialTariffStatus } from '@stamhoofd/structures';
|
|
5
5
|
import { Formatter } from '@stamhoofd/utility';
|
|
6
6
|
import { MemberRecordStore } from '../services/MemberRecordStore.js';
|
|
@@ -16,7 +16,7 @@ export class AdminPermissionChecker {
|
|
|
16
16
|
/**
|
|
17
17
|
* The member that is linked to this user = is this user
|
|
18
18
|
*/
|
|
19
|
-
member:
|
|
19
|
+
member: MemberWithUsersRegistrationsAndGroups | null = null;
|
|
20
20
|
platform: PlatformStruct;
|
|
21
21
|
|
|
22
22
|
organizationCache: Map<string, Organization | Promise<Organization | undefined>> = new Map();
|
|
@@ -361,7 +361,7 @@ export class AdminPermissionChecker {
|
|
|
361
361
|
return await this.hasFullAccess(organizationId);
|
|
362
362
|
}
|
|
363
363
|
|
|
364
|
-
async canAccessMember(member:
|
|
364
|
+
async canAccessMember(member: MemberWithUsersAndRegistrations, permissionLevel: PermissionLevel = PermissionLevel.Read) {
|
|
365
365
|
if (permissionLevel !== PermissionLevel.Full && this.isUserManager(member)) {
|
|
366
366
|
return true;
|
|
367
367
|
}
|
|
@@ -407,7 +407,7 @@ export class AdminPermissionChecker {
|
|
|
407
407
|
* The server will temporarily grant the user access to this member, and store this in the server
|
|
408
408
|
* memory. This is required for adding new members to an organization (first add member -> then patch with registrations, which requires write access).
|
|
409
409
|
*/
|
|
410
|
-
async temporarilyGrantMemberAccess(member:
|
|
410
|
+
async temporarilyGrantMemberAccess(member: MemberWithUsersRegistrationsAndGroups, permissionLevel: PermissionLevel = PermissionLevel.Write) {
|
|
411
411
|
console.log('Temporarily granting access to member', member.id, 'for user', this.user.id);
|
|
412
412
|
addTemporaryMemberAccess(this.user.id, member.id, permissionLevel);
|
|
413
413
|
}
|
|
@@ -415,11 +415,11 @@ export class AdminPermissionChecker {
|
|
|
415
415
|
/**
|
|
416
416
|
* Only full admins can delete members permanently
|
|
417
417
|
*/
|
|
418
|
-
async canDeleteMember(member:
|
|
418
|
+
async canDeleteMember(member: MemberWithUsersAndRegistrations) {
|
|
419
419
|
if (member.registrations.length === 0 && this.isUserManager(member)) {
|
|
420
420
|
const cachedBalance = await CachedBalance.getForObjects([member.id], null, ReceivableBalanceType.member);
|
|
421
421
|
if (cachedBalance.length === 0 || (cachedBalance[0].amountOpen === 0 && cachedBalance[0].amountPending === 0)) {
|
|
422
|
-
const platformMemberships = await MemberPlatformMembership.where(
|
|
422
|
+
const platformMemberships = await MemberPlatformMembership.select().where('memberId', member.id).fetch();
|
|
423
423
|
if (platformMemberships.length === 0) {
|
|
424
424
|
return true;
|
|
425
425
|
}
|
|
@@ -436,7 +436,7 @@ export class AdminPermissionChecker {
|
|
|
436
436
|
/**
|
|
437
437
|
* Note: only checks admin permissions. Users that 'own' this member can also access it but that does not use the AdminPermissionChecker
|
|
438
438
|
*/
|
|
439
|
-
async canAccessRegistration(registration: Registration, permissionLevel: PermissionLevel = PermissionLevel.Read, checkMember: boolean |
|
|
439
|
+
async canAccessRegistration(registration: Registration, permissionLevel: PermissionLevel = PermissionLevel.Read, checkMember: boolean | MemberWithUsersRegistrationsAndGroups = true) {
|
|
440
440
|
if (registration.deactivatedAt || !registration.registeredAt) {
|
|
441
441
|
if (!checkMember) {
|
|
442
442
|
// We can't grant access to a member because of a deactivated registration
|
|
@@ -840,7 +840,7 @@ export class AdminPermissionChecker {
|
|
|
840
840
|
return false;
|
|
841
841
|
}
|
|
842
842
|
|
|
843
|
-
async canLinkBalanceItemToMember(member:
|
|
843
|
+
async canLinkBalanceItemToMember(member: MemberWithUsersAndRegistrations) {
|
|
844
844
|
if (!this.checkScope(member.organizationId)) {
|
|
845
845
|
return false;
|
|
846
846
|
}
|
|
@@ -928,8 +928,11 @@ export class AdminPermissionChecker {
|
|
|
928
928
|
return this.hasFullAccess(organizationId);
|
|
929
929
|
}
|
|
930
930
|
|
|
931
|
-
async canAccessEmailBounces(organizationId: string) {
|
|
932
|
-
|
|
931
|
+
async canAccessEmailBounces(organizationId: string | null) {
|
|
932
|
+
if (organizationId) {
|
|
933
|
+
return this.hasSomeAccess(organizationId);
|
|
934
|
+
}
|
|
935
|
+
return this.hasPlatformFullAccess();
|
|
933
936
|
}
|
|
934
937
|
|
|
935
938
|
async canSendEmails(organizationId: Organization | string | null) {
|
|
@@ -1105,14 +1108,14 @@ export class AdminPermissionChecker {
|
|
|
1105
1108
|
return !!organizationPermissions && organizationPermissions.hasAccess(level);
|
|
1106
1109
|
}
|
|
1107
1110
|
|
|
1108
|
-
isUserManager(member:
|
|
1111
|
+
isUserManager(member: MemberWithUsers) {
|
|
1109
1112
|
return !!member.users.find(u => u.id === this.user.id);
|
|
1110
1113
|
}
|
|
1111
1114
|
|
|
1112
1115
|
/**
|
|
1113
1116
|
* Return a list of RecordSettings the current user can view or edit
|
|
1114
1117
|
*/
|
|
1115
|
-
async hasFinancialMemberAccess(member:
|
|
1118
|
+
async hasFinancialMemberAccess(member: MemberWithUsersAndRegistrations, level: PermissionLevel = PermissionLevel.Read, organizationId?: string): Promise<boolean> {
|
|
1116
1119
|
const isUserManager = this.isUserManager(member);
|
|
1117
1120
|
|
|
1118
1121
|
if (isUserManager && level === PermissionLevel.Read) {
|
|
@@ -1197,7 +1200,7 @@ export class AdminPermissionChecker {
|
|
|
1197
1200
|
/**
|
|
1198
1201
|
* Return a list of RecordSettings the current user can view or edit
|
|
1199
1202
|
*/
|
|
1200
|
-
async hasNRNAccess(member:
|
|
1203
|
+
async hasNRNAccess(member: MemberWithUsersAndRegistrations, level: PermissionLevel = PermissionLevel.Read): Promise<boolean> {
|
|
1201
1204
|
const isUserManager = this.isUserManager(member);
|
|
1202
1205
|
|
|
1203
1206
|
if (isUserManager) {
|
|
@@ -1319,7 +1322,7 @@ export class AdminPermissionChecker {
|
|
|
1319
1322
|
};
|
|
1320
1323
|
}
|
|
1321
1324
|
|
|
1322
|
-
async checkRecordAccess(member:
|
|
1325
|
+
async checkRecordAccess(member: MemberWithUsersRegistrationsAndGroups, recordId: string, level: PermissionLevel = PermissionLevel.Read): Promise<{ canAccess: false; record: RecordSettings | null } | { canAccess: true; record: RecordSettings }> {
|
|
1323
1326
|
const record = await MemberRecordStore.getRecord(recordId);
|
|
1324
1327
|
if (!record) {
|
|
1325
1328
|
return {
|
|
@@ -1452,7 +1455,7 @@ export class AdminPermissionChecker {
|
|
|
1452
1455
|
/**
|
|
1453
1456
|
* Changes data inline
|
|
1454
1457
|
*/
|
|
1455
|
-
async filterMemberData(member:
|
|
1458
|
+
async filterMemberData(member: MemberWithUsersRegistrationsAndGroups, data: MemberWithRegistrationsBlob, options?: { forAdminCartCalculation?: boolean }): Promise<MemberWithRegistrationsBlob> {
|
|
1456
1459
|
const cloned = data.clone();
|
|
1457
1460
|
|
|
1458
1461
|
for (const [key, value] of cloned.details.recordAnswers.entries()) {
|
|
@@ -1511,7 +1514,7 @@ export class AdminPermissionChecker {
|
|
|
1511
1514
|
/**
|
|
1512
1515
|
* Only for creating new members
|
|
1513
1516
|
*/
|
|
1514
|
-
filterMemberPut(member:
|
|
1517
|
+
filterMemberPut(member: MemberWithUsersRegistrationsAndGroups, data: MemberWithRegistrationsBlob, options: { asUserManager: boolean }) {
|
|
1515
1518
|
if (options.asUserManager || STAMHOOFD.userMode === 'platform') {
|
|
1516
1519
|
// A user manager cannot choose the member number + in platform mode, nobody can choose the member number
|
|
1517
1520
|
data.details.memberNumber = null;
|
|
@@ -1526,7 +1529,7 @@ export class AdminPermissionChecker {
|
|
|
1526
1529
|
}
|
|
1527
1530
|
}
|
|
1528
1531
|
|
|
1529
|
-
async filterMemberPatch(member:
|
|
1532
|
+
async filterMemberPatch(member: MemberWithUsersRegistrationsAndGroups, data: AutoEncoderPatchType<MemberWithRegistrationsBlob>): Promise<AutoEncoderPatchType<MemberWithRegistrationsBlob>> {
|
|
1530
1533
|
if (!data.details) {
|
|
1531
1534
|
return data;
|
|
1532
1535
|
}
|