@stamhoofd/models 2.74.0 → 2.75.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/dist/src/factories/BalanceItemFactory.d.ts +25 -0
- package/dist/src/factories/BalanceItemFactory.d.ts.map +1 -0
- package/dist/src/factories/BalanceItemFactory.js +52 -0
- package/dist/src/factories/BalanceItemFactory.js.map +1 -0
- package/dist/src/factories/GroupFactory.d.ts +0 -2
- package/dist/src/factories/GroupFactory.d.ts.map +1 -1
- package/dist/src/factories/GroupFactory.js +0 -4
- package/dist/src/factories/GroupFactory.js.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/migrations/1739017508-event-notifications.sql +25 -0
- package/dist/src/migrations/1739017509-event-notifications-link.sql +10 -0
- package/dist/src/migrations/1739282590-event-notifications-submitted-at.sql +3 -0
- package/dist/src/models/DocumentTemplate.d.ts.map +1 -1
- package/dist/src/models/DocumentTemplate.js +3 -1
- package/dist/src/models/DocumentTemplate.js.map +1 -1
- package/dist/src/models/EventNotification.d.ts +27 -0
- package/dist/src/models/EventNotification.d.ts.map +1 -0
- package/dist/src/models/EventNotification.js +95 -0
- package/dist/src/models/EventNotification.js.map +1 -0
- package/dist/src/models/Image.d.ts +1 -1
- package/dist/src/models/Image.d.ts.map +1 -1
- package/dist/src/models/Image.js +14 -2
- package/dist/src/models/Image.js.map +1 -1
- package/dist/src/models/MemberPlatformMembership.d.ts +1 -1
- package/dist/src/models/MemberPlatformMembership.d.ts.map +1 -1
- package/dist/src/models/MemberPlatformMembership.js +9 -2
- package/dist/src/models/MemberPlatformMembership.js.map +1 -1
- package/dist/src/models/MemberResponsibilityRecord.d.ts +1 -0
- package/dist/src/models/MemberResponsibilityRecord.d.ts.map +1 -1
- package/dist/src/models/MemberResponsibilityRecord.js +3 -0
- package/dist/src/models/MemberResponsibilityRecord.js.map +1 -1
- package/dist/src/models/Organization.js +1 -1
- package/dist/src/models/Organization.js.map +1 -1
- package/dist/src/models/User.d.ts +4 -1
- package/dist/src/models/User.d.ts.map +1 -1
- package/dist/src/models/User.js +31 -11
- package/dist/src/models/User.js.map +1 -1
- package/dist/src/models/index.d.ts +1 -0
- package/dist/src/models/index.d.ts.map +1 -1
- package/dist/src/models/index.js +1 -0
- package/dist/src/models/index.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/src/factories/BalanceItemFactory.ts +57 -0
- package/src/factories/GroupFactory.ts +1 -6
- package/src/index.ts +1 -0
- package/src/migrations/1739017508-event-notifications.sql +25 -0
- package/src/migrations/1739017509-event-notifications-link.sql +10 -0
- package/src/migrations/1739282590-event-notifications-submitted-at.sql +3 -0
- package/src/models/DocumentTemplate.ts +3 -1
- package/src/models/EventNotification.ts +77 -0
- package/src/models/Image.ts +15 -2
- package/src/models/MemberPlatformMembership.ts +11 -2
- package/src/models/MemberResponsibilityRecord.ts +5 -1
- package/src/models/Organization.ts +1 -1
- package/src/models/User.ts +53 -14
- package/src/models/index.ts +1 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
CREATE TABLE `event_notifications` (
|
|
2
|
+
`id` varchar(36) NOT NULL DEFAULT '',
|
|
3
|
+
`typeId` varchar(36) NOT NULL,
|
|
4
|
+
`periodId` varchar(36) NOT NULL,
|
|
5
|
+
`startDate` datetime NOT NULL,
|
|
6
|
+
`endDate` datetime NOT NULL,
|
|
7
|
+
`status` varchar(36) NOT NULL,
|
|
8
|
+
`feedbackText` text NULL,
|
|
9
|
+
`organizationId` varchar(36) DEFAULT NULL,
|
|
10
|
+
`recordAnswers` json NOT NULL DEFAULT ('{"value": {}, "version": 0}'),
|
|
11
|
+
`createdBy` varchar(36) DEFAULT NULL,
|
|
12
|
+
`submittedBy` varchar(36) DEFAULT NULL,
|
|
13
|
+
`createdAt` datetime NOT NULL,
|
|
14
|
+
`updatedAt` datetime NOT NULL,
|
|
15
|
+
PRIMARY KEY (`id`),
|
|
16
|
+
KEY `organizationId` (`organizationId`),
|
|
17
|
+
KEY `periodId` (`periodId`),
|
|
18
|
+
KEY `createdBy` (`createdBy`),
|
|
19
|
+
KEY `submittedBy` (`submittedBy`),
|
|
20
|
+
KEY `startDate` (`startDate`) USING BTREE,
|
|
21
|
+
CONSTRAINT `organizationId` FOREIGN KEY (`organizationId`) REFERENCES `organizations` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
|
22
|
+
CONSTRAINT `createdBy` FOREIGN KEY (`createdBy`) REFERENCES `users` (`id`) ON DELETE SET NULL ON UPDATE CASCADE,
|
|
23
|
+
CONSTRAINT `submittedBy` FOREIGN KEY (`submittedBy`) REFERENCES `users` (`id`) ON DELETE SET NULL ON UPDATE CASCADE,
|
|
24
|
+
CONSTRAINT `periodId` FOREIGN KEY (`periodId`) REFERENCES `registration_periods` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
|
25
|
+
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
CREATE TABLE `_event_notifications_events` (
|
|
2
|
+
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
|
|
3
|
+
`event_notificationsId` varchar(36) NOT NULL,
|
|
4
|
+
`eventsId` varchar(36) NOT NULL,
|
|
5
|
+
PRIMARY KEY (`id`),
|
|
6
|
+
KEY `event_notificationsId` (`event_notificationsId`),
|
|
7
|
+
KEY `eventsId` (`eventsId`),
|
|
8
|
+
CONSTRAINT `event_notificationsId` FOREIGN KEY (`event_notificationsId`) REFERENCES `event_notifications` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
|
9
|
+
CONSTRAINT `eventsId` FOREIGN KEY (`eventsId`) REFERENCES `events` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
|
10
|
+
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci;
|
|
@@ -432,7 +432,9 @@ export class DocumentTemplate extends QueryableModel {
|
|
|
432
432
|
for (const document of existingDocuments) {
|
|
433
433
|
await this.updateDocumentWithAnswers(document, fieldAnswers);
|
|
434
434
|
document.data.name = this.settings.name;
|
|
435
|
-
|
|
435
|
+
if (existingDocuments.length === 1) {
|
|
436
|
+
document.data.description = description;
|
|
437
|
+
}
|
|
436
438
|
if (document.status === DocumentStatus.Draft || document.status === DocumentStatus.Published) {
|
|
437
439
|
document.status = this.status;
|
|
438
440
|
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { column, ManyToManyRelation } from '@simonbackx/simple-database';
|
|
2
|
+
import { MapDecoder, StringDecoder } from '@simonbackx/simple-encoding';
|
|
3
|
+
import { QueryableModel } from '@stamhoofd/sql';
|
|
4
|
+
import { EventNotificationStatus, RecordAnswer, RecordAnswerDecoder } from '@stamhoofd/structures';
|
|
5
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
6
|
+
import { Event } from './Event';
|
|
7
|
+
|
|
8
|
+
export class EventNotification extends QueryableModel {
|
|
9
|
+
static table = 'event_notifications';
|
|
10
|
+
|
|
11
|
+
@column({ primary: true, type: 'string', beforeSave(value) {
|
|
12
|
+
return value ?? uuidv4();
|
|
13
|
+
} })
|
|
14
|
+
id!: string;
|
|
15
|
+
|
|
16
|
+
@column({ type: 'string' })
|
|
17
|
+
typeId: string;
|
|
18
|
+
|
|
19
|
+
@column({ type: 'string' })
|
|
20
|
+
periodId: string;
|
|
21
|
+
|
|
22
|
+
@column({ type: 'datetime' })
|
|
23
|
+
startDate: Date;
|
|
24
|
+
|
|
25
|
+
@column({ type: 'datetime' })
|
|
26
|
+
endDate: Date;
|
|
27
|
+
|
|
28
|
+
@column({ type: 'string' })
|
|
29
|
+
status = EventNotificationStatus.Draft;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Feedback on a review, e.g. when it is declined (explains which changes need to be made).
|
|
33
|
+
* It is only visible when the status is not 'accepted' or 'pending'.
|
|
34
|
+
*/
|
|
35
|
+
@column({ type: 'string', nullable: true })
|
|
36
|
+
feedbackText: string | null = null;
|
|
37
|
+
|
|
38
|
+
@column({ type: 'string' })
|
|
39
|
+
organizationId: string;
|
|
40
|
+
|
|
41
|
+
@column({ type: 'json', decoder: new MapDecoder(StringDecoder, RecordAnswerDecoder) })
|
|
42
|
+
recordAnswers: Map<string, RecordAnswer> = new Map();
|
|
43
|
+
|
|
44
|
+
@column({ type: 'string', nullable: true })
|
|
45
|
+
createdBy: string | null = null;
|
|
46
|
+
|
|
47
|
+
@column({ type: 'string', nullable: true })
|
|
48
|
+
submittedBy: string | null = null;
|
|
49
|
+
|
|
50
|
+
@column({ type: 'datetime', nullable: true })
|
|
51
|
+
submittedAt: Date | null = null;
|
|
52
|
+
|
|
53
|
+
@column({
|
|
54
|
+
type: 'datetime', beforeSave(old?: any) {
|
|
55
|
+
if (old !== undefined) {
|
|
56
|
+
return old;
|
|
57
|
+
}
|
|
58
|
+
const date = new Date();
|
|
59
|
+
date.setMilliseconds(0);
|
|
60
|
+
return date;
|
|
61
|
+
},
|
|
62
|
+
})
|
|
63
|
+
createdAt: Date;
|
|
64
|
+
|
|
65
|
+
@column({
|
|
66
|
+
type: 'datetime', beforeSave() {
|
|
67
|
+
const date = new Date();
|
|
68
|
+
date.setMilliseconds(0);
|
|
69
|
+
return date;
|
|
70
|
+
},
|
|
71
|
+
skipUpdate: true,
|
|
72
|
+
})
|
|
73
|
+
updatedAt: Date;
|
|
74
|
+
|
|
75
|
+
// Note: all relations should point to their parents, not the other way around to avoid reference cycles
|
|
76
|
+
static events = new ManyToManyRelation(EventNotification, Event, 'events');
|
|
77
|
+
}
|
package/src/models/Image.ts
CHANGED
|
@@ -24,7 +24,7 @@ export class Image extends QueryableModel {
|
|
|
24
24
|
@column({ type: 'datetime' })
|
|
25
25
|
createdAt: Date = new Date();
|
|
26
26
|
|
|
27
|
-
static async create(fileContent: string | Buffer, type: string | undefined, resolutions: ResolutionRequest[]): Promise<Image> {
|
|
27
|
+
static async create(fileContent: string | Buffer, type: string | undefined, resolutions: ResolutionRequest[], isPrivateFile: boolean = false): Promise<Image> {
|
|
28
28
|
if (!STAMHOOFD.SPACES_BUCKET || !STAMHOOFD.SPACES_ENDPOINT || !STAMHOOFD.SPACES_KEY || !STAMHOOFD.SPACES_SECRET) {
|
|
29
29
|
throw new SimpleError({
|
|
30
30
|
code: 'not_available',
|
|
@@ -105,7 +105,7 @@ export class Image extends QueryableModel {
|
|
|
105
105
|
Key: key,
|
|
106
106
|
Body: f.data,
|
|
107
107
|
ContentType: !supportsTransparency ? 'image/jpeg' : 'image/png',
|
|
108
|
-
ACL: 'public-read',
|
|
108
|
+
ACL: isPrivateFile ? 'private' : 'public-read',
|
|
109
109
|
};
|
|
110
110
|
|
|
111
111
|
uploadPromises.push(s3.putObject(params).promise());
|
|
@@ -115,8 +115,20 @@ export class Image extends QueryableModel {
|
|
|
115
115
|
server: 'https://' + STAMHOOFD.SPACES_BUCKET + '.' + STAMHOOFD.SPACES_ENDPOINT,
|
|
116
116
|
path: key,
|
|
117
117
|
size: f.info.size,
|
|
118
|
+
isPrivate: isPrivateFile,
|
|
118
119
|
});
|
|
119
120
|
|
|
121
|
+
if (isPrivateFile) {
|
|
122
|
+
if (!await _file.sign()) {
|
|
123
|
+
throw new SimpleError({
|
|
124
|
+
code: 'failed_to_sign',
|
|
125
|
+
message: 'Failed to sign file',
|
|
126
|
+
human: $t('Er ging iet mis bij het uploaden van jouw bestand. Probeer het later opnieuw (foutcode: SIGN).'),
|
|
127
|
+
statusCode: 500,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
120
132
|
const _image = new Resolution({
|
|
121
133
|
file: _file,
|
|
122
134
|
width: f.info.width,
|
|
@@ -142,6 +154,7 @@ export class Image extends QueryableModel {
|
|
|
142
154
|
server: 'https://' + STAMHOOFD.SPACES_BUCKET + '.' + STAMHOOFD.SPACES_ENDPOINT,
|
|
143
155
|
path: key,
|
|
144
156
|
size: fileContent.length,
|
|
157
|
+
// Don't set private here, as we don't allow to download this file
|
|
145
158
|
});
|
|
146
159
|
|
|
147
160
|
uploadPromises.push(s3.putObject(params).promise());
|
|
@@ -108,8 +108,12 @@ export class MemberPlatformMembership extends QueryableModel {
|
|
|
108
108
|
@column({ type: 'boolean' })
|
|
109
109
|
locked = false;
|
|
110
110
|
|
|
111
|
-
canDelete() {
|
|
112
|
-
if (this.
|
|
111
|
+
canDelete(hasPlatformFullAccess = false) {
|
|
112
|
+
if (this.locked) {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (this.balanceItemId && !hasPlatformFullAccess) {
|
|
113
117
|
return false;
|
|
114
118
|
}
|
|
115
119
|
return true;
|
|
@@ -150,6 +154,11 @@ export class MemberPlatformMembership extends QueryableModel {
|
|
|
150
154
|
}
|
|
151
155
|
|
|
152
156
|
async calculatePrice(member: Member, registration?: Registration) {
|
|
157
|
+
if (this.locked) {
|
|
158
|
+
// price of locked membership cannot be changed
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
153
162
|
const platform = await Platform.getSharedPrivateStruct();
|
|
154
163
|
const membershipType = platform.config.membershipTypes.find(m => m.id === this.membershipTypeId);
|
|
155
164
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { column } from '@simonbackx/simple-database';
|
|
2
|
-
import { QueryableModel } from '@stamhoofd/sql';
|
|
2
|
+
import { QueryableModel, SQL } from '@stamhoofd/sql';
|
|
3
3
|
import { Group as GroupStruct, MemberResponsibilityRecordBase, MemberResponsibilityRecord as MemberResponsibilityRecordStruct } from '@stamhoofd/structures';
|
|
4
4
|
import { v4 as uuidv4 } from 'uuid';
|
|
5
5
|
|
|
@@ -53,4 +53,8 @@ export class MemberResponsibilityRecord extends QueryableModel {
|
|
|
53
53
|
group,
|
|
54
54
|
});
|
|
55
55
|
}
|
|
56
|
+
|
|
57
|
+
static get whereActive() {
|
|
58
|
+
return SQL.where('startDate', '<', new Date()).and(SQL.where('endDate', null).or('endDate', '>', new Date()));
|
|
59
|
+
}
|
|
56
60
|
}
|
|
@@ -806,7 +806,7 @@ export class Organization extends QueryableModel {
|
|
|
806
806
|
async getAdmins() {
|
|
807
807
|
// Circular reference fix
|
|
808
808
|
const User = (await import('./User')).User;
|
|
809
|
-
return await User.getAdmins(
|
|
809
|
+
return await User.getAdmins(this.id, { verified: true });
|
|
810
810
|
}
|
|
811
811
|
|
|
812
812
|
/**
|
package/src/models/User.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { column, Database, ManyToOneRelation } from '@simonbackx/simple-database';
|
|
2
2
|
import { EmailInterfaceRecipient } from '@stamhoofd/email';
|
|
3
|
-
import { QueryableModel } from '@stamhoofd/sql';
|
|
3
|
+
import { QueryableModel, SQL, SQLExpression, SQLJsonExtract } from '@stamhoofd/sql';
|
|
4
4
|
import { LoginProviderType, NewUser, Permissions, Recipient, Replacement, UserMeta, UserPermissions, User as UserStruct } from '@stamhoofd/structures';
|
|
5
5
|
import argon2 from 'argon2';
|
|
6
6
|
import { v4 as uuidv4 } from 'uuid';
|
|
@@ -117,15 +117,52 @@ export class User extends QueryableModel {
|
|
|
117
117
|
});
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
-
static async
|
|
121
|
-
|
|
122
|
-
|
|
120
|
+
static async getPlatformAdmins(options?: { verified?: boolean }) {
|
|
121
|
+
// Custom implementation
|
|
122
|
+
const q = User.select()
|
|
123
|
+
.where('organizationId', null)
|
|
124
|
+
.where('permissions', '!=', null)
|
|
125
|
+
.where(
|
|
126
|
+
SQL.jsonValue(SQL.column('permissions'), '$.value.globalPermissions'),
|
|
127
|
+
'!=',
|
|
128
|
+
null,
|
|
129
|
+
)
|
|
130
|
+
.where('id', '!=', '1');
|
|
131
|
+
|
|
132
|
+
if (options?.verified !== undefined) {
|
|
133
|
+
q.where('verified', options.verified);
|
|
123
134
|
}
|
|
124
135
|
|
|
136
|
+
let global = await q.fetch();
|
|
137
|
+
|
|
138
|
+
// Hide api accounts
|
|
139
|
+
global = global.filter(a => !a.isApiUser);
|
|
140
|
+
|
|
141
|
+
return global;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
static async getAdmins(organizationId: string, options?: { verified?: boolean }) {
|
|
125
145
|
if (STAMHOOFD.userMode === 'platform') {
|
|
126
146
|
// Custom implementation
|
|
127
|
-
|
|
128
|
-
|
|
147
|
+
const q = User.select()
|
|
148
|
+
.where('organizationId', null)
|
|
149
|
+
.where('permissions', '!=', null)
|
|
150
|
+
.where(
|
|
151
|
+
SQL.jsonValue(
|
|
152
|
+
SQL.jsonValue(SQL.column('permissions'), '$.value.organizationPermissions'),
|
|
153
|
+
'$."' + organizationId + '"',
|
|
154
|
+
true,
|
|
155
|
+
),
|
|
156
|
+
'!=',
|
|
157
|
+
null,
|
|
158
|
+
)
|
|
159
|
+
;
|
|
160
|
+
|
|
161
|
+
if (options?.verified !== undefined) {
|
|
162
|
+
q.where('verified', options.verified);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
let global = await q.fetch();
|
|
129
166
|
|
|
130
167
|
// Hide api accounts
|
|
131
168
|
global = global.filter(a => !a.isApiUser);
|
|
@@ -133,19 +170,21 @@ export class User extends QueryableModel {
|
|
|
133
170
|
return global;
|
|
134
171
|
}
|
|
135
172
|
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
173
|
+
const q = User.select()
|
|
174
|
+
.where('organizationId', organizationId)
|
|
175
|
+
.where('permissions', '!=', null)
|
|
176
|
+
;
|
|
140
177
|
|
|
141
178
|
if (options?.verified !== undefined) {
|
|
142
|
-
|
|
179
|
+
q.where('verified', options.verified);
|
|
143
180
|
}
|
|
144
181
|
|
|
145
|
-
|
|
182
|
+
let global = await q.fetch();
|
|
183
|
+
|
|
184
|
+
// Hide api accounts
|
|
185
|
+
global = global.filter(a => !a.isApiUser);
|
|
146
186
|
|
|
147
|
-
|
|
148
|
-
).filter(a => !a.isApiUser);
|
|
187
|
+
return global;
|
|
149
188
|
}
|
|
150
189
|
|
|
151
190
|
static async getApiUsers(organizationIds: string[]) {
|
package/src/models/index.ts
CHANGED
|
@@ -51,6 +51,7 @@ export * from './MemberPlatformMembership';
|
|
|
51
51
|
export * from './Email';
|
|
52
52
|
export * from './EmailRecipient';
|
|
53
53
|
export * from './Event';
|
|
54
|
+
export * from './EventNotification';
|
|
54
55
|
export * from './CachedBalance';
|
|
55
56
|
export * from './AuditLog';
|
|
56
57
|
export * from './MemberUser';
|