@stamhoofd/models 2.2.0 → 2.4.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/GroupFactory.d.ts.map +1 -1
- package/dist/src/factories/GroupFactory.js +5 -5
- package/dist/src/factories/GroupFactory.js.map +1 -1
- package/dist/src/helpers/EmailBuilder.d.ts +3 -2
- package/dist/src/helpers/EmailBuilder.d.ts.map +1 -1
- package/dist/src/helpers/EmailBuilder.js +29 -16
- package/dist/src/helpers/EmailBuilder.js.map +1 -1
- package/dist/src/helpers/GroupBuilder.d.ts.map +1 -1
- package/dist/src/helpers/GroupBuilder.js +0 -23
- package/dist/src/helpers/GroupBuilder.js.map +1 -1
- package/dist/src/migrations/1720080975-convert-charset.sql +0 -2
- package/dist/src/migrations/1721050380-email-table.sql +24 -0
- package/dist/src/migrations/1721050381-email-recipients-table.sql +18 -0
- package/dist/src/migrations/1721342679-responsibility-groupId.sql +2 -0
- package/dist/src/migrations/1721342680-responsibility-groupId-foreign-key.sql +1 -0
- package/dist/src/migrations/1721400546-users-memberId.sql +3 -0
- package/dist/src/migrations/1721639159-membership-deleted-at.sql +2 -0
- package/dist/src/migrations/1721639160-membership-generated.sql +2 -0
- package/dist/src/migrations/1721841819-group-type.sql +2 -0
- package/dist/src/migrations/1722090482-events.sql +18 -0
- package/dist/src/models/DocumentTemplate.js +14 -14
- package/dist/src/models/DocumentTemplate.js.map +1 -1
- package/dist/src/models/Email.d.ts +41 -0
- package/dist/src/models/Email.d.ts.map +1 -0
- package/dist/src/models/Email.js +490 -0
- package/dist/src/models/Email.js.map +1 -0
- package/dist/src/models/EmailRecipient.d.ts +20 -0
- package/dist/src/models/EmailRecipient.d.ts.map +1 -0
- package/dist/src/models/EmailRecipient.js +95 -0
- package/dist/src/models/EmailRecipient.js.map +1 -0
- package/dist/src/models/EmailTemplate.d.ts +2 -1
- package/dist/src/models/EmailTemplate.d.ts.map +1 -1
- package/dist/src/models/EmailTemplate.js +4 -0
- package/dist/src/models/EmailTemplate.js.map +1 -1
- package/dist/src/models/Event.d.ts +19 -0
- package/dist/src/models/Event.d.ts.map +1 -0
- package/dist/src/models/Event.js +78 -0
- package/dist/src/models/Event.js.map +1 -0
- package/dist/src/models/Group.d.ts +2 -1
- package/dist/src/models/Group.d.ts.map +1 -1
- package/dist/src/models/Group.js +9 -22
- package/dist/src/models/Group.js.map +1 -1
- package/dist/src/models/Member.d.ts +1 -0
- package/dist/src/models/Member.d.ts.map +1 -1
- package/dist/src/models/Member.js +42 -11
- package/dist/src/models/Member.js.map +1 -1
- package/dist/src/models/MemberPlatformMembership.d.ts +7 -0
- package/dist/src/models/MemberPlatformMembership.d.ts.map +1 -1
- package/dist/src/models/MemberPlatformMembership.js +22 -0
- package/dist/src/models/MemberPlatformMembership.js.map +1 -1
- package/dist/src/models/MemberResponsibilityRecord.d.ts +3 -0
- package/dist/src/models/MemberResponsibilityRecord.d.ts.map +1 -1
- package/dist/src/models/MemberResponsibilityRecord.js +8 -0
- package/dist/src/models/MemberResponsibilityRecord.js.map +1 -1
- package/dist/src/models/Organization.d.ts +8 -1
- package/dist/src/models/Organization.d.ts.map +1 -1
- package/dist/src/models/Organization.js +37 -9
- package/dist/src/models/Organization.js.map +1 -1
- package/dist/src/models/OrganizationRegistrationPeriod.d.ts +1 -0
- package/dist/src/models/OrganizationRegistrationPeriod.d.ts.map +1 -1
- package/dist/src/models/OrganizationRegistrationPeriod.js +7 -0
- package/dist/src/models/OrganizationRegistrationPeriod.js.map +1 -1
- package/dist/src/models/RegistrationPeriod.d.ts +1 -0
- package/dist/src/models/RegistrationPeriod.d.ts.map +1 -1
- package/dist/src/models/RegistrationPeriod.js +12 -0
- package/dist/src/models/RegistrationPeriod.js.map +1 -1
- package/dist/src/models/User.d.ts +2 -1
- package/dist/src/models/User.d.ts.map +1 -1
- package/dist/src/models/User.js +13 -3
- package/dist/src/models/User.js.map +1 -1
- package/dist/src/models/index.d.ts +3 -0
- package/dist/src/models/index.d.ts.map +1 -1
- package/dist/src/models/index.js +3 -0
- package/dist/src/models/index.js.map +1 -1
- package/package.json +2 -2
- package/src/factories/GroupFactory.ts +6 -6
- package/src/helpers/EmailBuilder.ts +33 -18
- package/src/helpers/GroupBuilder.ts +0 -23
- package/src/migrations/1720080975-convert-charset.sql +0 -2
- package/src/migrations/1721050380-email-table.sql +24 -0
- package/src/migrations/1721050381-email-recipients-table.sql +18 -0
- package/src/migrations/1721342679-responsibility-groupId.sql +2 -0
- package/src/migrations/1721342680-responsibility-groupId-foreign-key.sql +1 -0
- package/src/migrations/1721400546-users-memberId.sql +3 -0
- package/src/migrations/1721639159-membership-deleted-at.sql +2 -0
- package/src/migrations/1721639160-membership-generated.sql +2 -0
- package/src/migrations/1721841819-group-type.sql +2 -0
- package/src/migrations/1722090482-events.sql +18 -0
- package/src/models/DocumentTemplate.ts +2 -2
- package/src/models/Email.ts +556 -0
- package/src/models/EmailRecipient.ts +81 -0
- package/src/models/EmailTemplate.ts +5 -1
- package/src/models/Event.ts +71 -0
- package/src/models/Group.ts +10 -37
- package/src/models/Member.ts +60 -12
- package/src/models/MemberPlatformMembership.ts +21 -0
- package/src/models/MemberResponsibilityRecord.ts +7 -0
- package/src/models/Organization.ts +42 -9
- package/src/models/OrganizationRegistrationPeriod.ts +8 -0
- package/src/models/RegistrationPeriod.ts +14 -0
- package/src/models/User.ts +13 -3
- package/src/models/index.ts +3 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
|
|
2
|
+
import { column, Model } from "@simonbackx/simple-database";
|
|
3
|
+
import { EventMeta, Event as EventStruct } from '@stamhoofd/structures';
|
|
4
|
+
import { v4 as uuidv4 } from "uuid";
|
|
5
|
+
import { Group } from "./Group";
|
|
6
|
+
|
|
7
|
+
export class Event extends Model {
|
|
8
|
+
static table = "events";
|
|
9
|
+
|
|
10
|
+
@column({ primary: true, type: "string", beforeSave(value) {
|
|
11
|
+
return value ?? uuidv4();
|
|
12
|
+
} })
|
|
13
|
+
id!: string;
|
|
14
|
+
|
|
15
|
+
@column({ type: "string" })
|
|
16
|
+
name: string
|
|
17
|
+
|
|
18
|
+
@column({ type: "string" })
|
|
19
|
+
typeId: string
|
|
20
|
+
|
|
21
|
+
@column({ type: "string", nullable: true })
|
|
22
|
+
organizationId: string|null = null
|
|
23
|
+
|
|
24
|
+
@column({ type: "string", nullable: true })
|
|
25
|
+
groupId: string|null = null
|
|
26
|
+
|
|
27
|
+
@column({ type: "datetime" })
|
|
28
|
+
startDate: Date
|
|
29
|
+
|
|
30
|
+
@column({ type: "datetime" })
|
|
31
|
+
endDate: Date
|
|
32
|
+
|
|
33
|
+
@column({ type: "json", decoder: EventMeta })
|
|
34
|
+
meta = EventMeta.create({})
|
|
35
|
+
|
|
36
|
+
@column({
|
|
37
|
+
type: "datetime", beforeSave(old?: any) {
|
|
38
|
+
if (old !== undefined) {
|
|
39
|
+
return old;
|
|
40
|
+
}
|
|
41
|
+
const date = new Date()
|
|
42
|
+
date.setMilliseconds(0)
|
|
43
|
+
return date
|
|
44
|
+
}
|
|
45
|
+
})
|
|
46
|
+
createdAt: Date
|
|
47
|
+
|
|
48
|
+
@column({
|
|
49
|
+
type: "datetime", beforeSave() {
|
|
50
|
+
const date = new Date()
|
|
51
|
+
date.setMilliseconds(0)
|
|
52
|
+
return date
|
|
53
|
+
},
|
|
54
|
+
skipUpdate: true
|
|
55
|
+
})
|
|
56
|
+
updatedAt: Date
|
|
57
|
+
|
|
58
|
+
getStructure(group?: Group|null) {
|
|
59
|
+
return EventStruct.create({
|
|
60
|
+
...this,
|
|
61
|
+
group: group ? group.getStructure() : null
|
|
62
|
+
})
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
getPrivateStructure(group?: Group|null) {
|
|
66
|
+
return EventStruct.create({
|
|
67
|
+
...this,
|
|
68
|
+
group: group ? group.getPrivateStructure() : null
|
|
69
|
+
})
|
|
70
|
+
}
|
|
71
|
+
}
|
package/src/models/Group.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { column, Database, ManyToOneRelation, Model, OneToManyRelation } from '@simonbackx/simple-database';
|
|
2
|
-
import {
|
|
2
|
+
import { GroupCategory, GroupPrivateSettings, GroupSettings, GroupStatus, Group as GroupStruct, GroupType } from '@stamhoofd/structures';
|
|
3
3
|
import { v4 as uuidv4 } from "uuid";
|
|
4
4
|
|
|
5
|
-
import { Member, MemberWithRegistrations, OrganizationRegistrationPeriod, Payment, Registration, User } from './';
|
|
6
5
|
import { Formatter } from '@stamhoofd/utility';
|
|
6
|
+
import { Member, MemberWithRegistrations, OrganizationRegistrationPeriod, Payment, Registration, User } from './';
|
|
7
7
|
|
|
8
8
|
if (Member === undefined) {
|
|
9
9
|
throw new Error("Import Member is undefined")
|
|
@@ -28,6 +28,9 @@ export class Group extends Model {
|
|
|
28
28
|
})
|
|
29
29
|
id!: string;
|
|
30
30
|
|
|
31
|
+
@column({ type: "string" })
|
|
32
|
+
type = GroupType.Membership;
|
|
33
|
+
|
|
31
34
|
@column({ type: "json", decoder: GroupSettings })
|
|
32
35
|
settings: GroupSettings;
|
|
33
36
|
|
|
@@ -207,7 +210,6 @@ export class Group extends Model {
|
|
|
207
210
|
"groupId = ? and cycle = ? and waitingList = 0 and registeredAt is not null",
|
|
208
211
|
[this.id, this.cycle]
|
|
209
212
|
)
|
|
210
|
-
//const query = `select count(*) as c from \`${Registration.table}\` where groupId = ? and cycle = ? and (((registeredAt is not null or reservedUntil >= ?) and waitingList = 0) OR (waitingList = 1 AND canRegister = 1))`
|
|
211
213
|
|
|
212
214
|
this.settings.reservedMembers = await Group.getCount(
|
|
213
215
|
"groupId = ? and cycle = ? and ((waitingList = 0 and registeredAt is null AND reservedUntil >= ?) OR (waitingList = 1 and canRegister = 1))",
|
|
@@ -218,37 +220,6 @@ export class Group extends Model {
|
|
|
218
220
|
"groupId = ? and cycle = ? and waitingList = 1",
|
|
219
221
|
[this.id, this.cycle, new Date()]
|
|
220
222
|
)
|
|
221
|
-
|
|
222
|
-
// Loop cycle -1 until current (excluding current)
|
|
223
|
-
for (let cycle = -1; cycle < this.cycle; cycle++) {
|
|
224
|
-
if (!this.settings.cycleSettings.has(cycle)) {
|
|
225
|
-
this.settings.cycleSettings.set(cycle, CycleInformation.create({
|
|
226
|
-
registeredMembers: 0,
|
|
227
|
-
reservedMembers: 0,
|
|
228
|
-
waitingListSize: 0
|
|
229
|
-
}))
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
// Older cycles
|
|
234
|
-
// todo: optimize this a bit
|
|
235
|
-
for (const [cycle, info] of this.settings.cycleSettings) {
|
|
236
|
-
|
|
237
|
-
info.registeredMembers = await Group.getCount(
|
|
238
|
-
"groupId = ? and cycle = ? and waitingList = 0 and registeredAt is not null",
|
|
239
|
-
[this.id, cycle]
|
|
240
|
-
)
|
|
241
|
-
|
|
242
|
-
info.reservedMembers = await Group.getCount(
|
|
243
|
-
"groupId = ? and cycle = ? and ((waitingList = 0 and registeredAt is null AND reservedUntil >= ?) OR (waitingList = 1 and canRegister = 1))",
|
|
244
|
-
[this.id, cycle, new Date()]
|
|
245
|
-
)
|
|
246
|
-
|
|
247
|
-
info.waitingListSize = await Group.getCount(
|
|
248
|
-
"groupId = ? and cycle = ? and waitingList = 1",
|
|
249
|
-
[this.id, cycle, new Date()]
|
|
250
|
-
)
|
|
251
|
-
}
|
|
252
223
|
}
|
|
253
224
|
|
|
254
225
|
static async deleteUnreachable(organizationId: string, period: OrganizationRegistrationPeriod, allGroups: Group[]) {
|
|
@@ -282,10 +253,12 @@ export class Group extends Model {
|
|
|
282
253
|
}
|
|
283
254
|
|
|
284
255
|
for (const group of allGroups) {
|
|
285
|
-
if (!reachable.get(group.id) && group.
|
|
286
|
-
console.log("
|
|
287
|
-
group.
|
|
256
|
+
if (!reachable.get(group.id) && group.deletedAt === null) {
|
|
257
|
+
console.log("Deleting unreachable group "+group.id+" from organization "+organizationId + " org period "+period.id)
|
|
258
|
+
group.deletedAt = new Date()
|
|
288
259
|
await group.save()
|
|
260
|
+
|
|
261
|
+
Member.updateMembershipsForGroupId(group.id)
|
|
289
262
|
}
|
|
290
263
|
}
|
|
291
264
|
}
|
package/src/models/Member.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { column, Database, ManyToManyRelation, ManyToOneRelation, Model, OneToManyRelation } from '@simonbackx/simple-database';
|
|
2
2
|
import { SQL } from "@stamhoofd/sql";
|
|
3
|
-
import { Member as MemberStruct, MemberDetails, MemberWithRegistrationsBlob, RegistrationWithMember as RegistrationWithMemberStruct, User as UserStruct } from '@stamhoofd/structures';
|
|
3
|
+
import { Member as MemberStruct, MemberDetails, MemberWithRegistrationsBlob, RegistrationWithMember as RegistrationWithMemberStruct, User as UserStruct, GroupStatus } from '@stamhoofd/structures';
|
|
4
4
|
import { Formatter, Sorter } from '@stamhoofd/utility';
|
|
5
5
|
import { v4 as uuidv4 } from "uuid";
|
|
6
6
|
|
|
@@ -300,7 +300,9 @@ export class Member extends Model {
|
|
|
300
300
|
if (!g) {
|
|
301
301
|
throw new Error("Group not found")
|
|
302
302
|
}
|
|
303
|
-
|
|
303
|
+
if (g.deletedAt === null) {
|
|
304
|
+
member.registrations.push(registration.setRelation(Registration.group, g))
|
|
305
|
+
}
|
|
304
306
|
}
|
|
305
307
|
}
|
|
306
308
|
|
|
@@ -370,10 +372,7 @@ export class Member extends Model {
|
|
|
370
372
|
...this,
|
|
371
373
|
registrations: this.registrations.map(r => r.getStructure()),
|
|
372
374
|
details: this.details,
|
|
373
|
-
users: this.users.map(u =>
|
|
374
|
-
...u,
|
|
375
|
-
hasAccount: u.hasAccount()
|
|
376
|
-
})),
|
|
375
|
+
users: this.users.map(u => u.getStructure()),
|
|
377
376
|
})
|
|
378
377
|
}
|
|
379
378
|
|
|
@@ -385,6 +384,36 @@ export class Member extends Model {
|
|
|
385
384
|
})
|
|
386
385
|
}
|
|
387
386
|
|
|
387
|
+
static updateMembershipsForGroupId(id: string) {
|
|
388
|
+
QueueHandler.schedule('bulk-update-memberships', async () => {
|
|
389
|
+
console.log('Bulk updating memberships for group id ', id)
|
|
390
|
+
|
|
391
|
+
// Get all members that are registered in this group
|
|
392
|
+
const memberIds = (await SQL.select(
|
|
393
|
+
SQL.column('members', 'id')
|
|
394
|
+
)
|
|
395
|
+
.from(SQL.table(Member.table))
|
|
396
|
+
.join(
|
|
397
|
+
SQL.leftJoin(
|
|
398
|
+
SQL.table(Registration.table)
|
|
399
|
+
).where(
|
|
400
|
+
SQL.column(Registration.table, 'memberId'),
|
|
401
|
+
SQL.column(Member.table, 'id')
|
|
402
|
+
)
|
|
403
|
+
).where(
|
|
404
|
+
SQL.column(Registration.table, 'groupId'),
|
|
405
|
+
id
|
|
406
|
+
).fetch()).flatMap(r => (r.members && (typeof r.members.id) === 'string') ? [r.members.id as string] : [])
|
|
407
|
+
|
|
408
|
+
for (const id of memberIds) {
|
|
409
|
+
const member = await Member.getWithRegistrations(id)
|
|
410
|
+
await member?.updateMemberships()
|
|
411
|
+
}
|
|
412
|
+
}).catch((e) => {
|
|
413
|
+
console.error('Failed to update memberships for group id ', id), e
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
|
|
388
417
|
async updateMemberships(this: MemberWithRegistrations) {
|
|
389
418
|
console.log('Updating memberships for member: ' + this.id)
|
|
390
419
|
return await QueueHandler.schedule('updateMemberships-' + this.id, async () => {
|
|
@@ -410,17 +439,19 @@ export class Member extends Model {
|
|
|
410
439
|
membership: defaultMembership,
|
|
411
440
|
}]
|
|
412
441
|
})
|
|
413
|
-
// Get active memberships for this member
|
|
442
|
+
// Get active memberships for this member that
|
|
414
443
|
const memberships = await MemberPlatformMembership.where({memberId: this.id, periodId: platform.periodId })
|
|
415
444
|
const now = new Date()
|
|
416
|
-
const activeMemberships = memberships.filter(m => m.startDate <= now && m.endDate >= now)
|
|
445
|
+
const activeMemberships = memberships.filter(m => m.startDate <= now && m.endDate >= now && m.deletedAt === null)
|
|
446
|
+
const activeMembershipsUndeletable = activeMemberships.filter(m => !m.canDelete() || !m.generated)
|
|
417
447
|
|
|
418
448
|
if (defaultMemberships.length == 0) {
|
|
419
|
-
// Stop all active memberships
|
|
449
|
+
// Stop all active memberships taht were added automatically
|
|
420
450
|
for (const membership of activeMemberships) {
|
|
421
|
-
if (
|
|
451
|
+
if (membership.canDelete() && membership.generated) {
|
|
422
452
|
console.log('Removing membership because no longer registered member and not yet invoiced for: ' + this.id + ' - membership ' + membership.id)
|
|
423
|
-
|
|
453
|
+
membership.deletedAt = new Date()
|
|
454
|
+
await membership.save()
|
|
424
455
|
}
|
|
425
456
|
}
|
|
426
457
|
|
|
@@ -429,7 +460,7 @@ export class Member extends Model {
|
|
|
429
460
|
}
|
|
430
461
|
|
|
431
462
|
|
|
432
|
-
if (
|
|
463
|
+
if (activeMembershipsUndeletable.length) {
|
|
433
464
|
// Skip automatic additions
|
|
434
465
|
console.log('Skipping automatic membership for: ' + this.id, ' - already has active memberships')
|
|
435
466
|
return
|
|
@@ -447,11 +478,18 @@ export class Member extends Model {
|
|
|
447
478
|
throw new Error("No membership found")
|
|
448
479
|
}
|
|
449
480
|
|
|
481
|
+
// Check if already have the same membership
|
|
482
|
+
if (activeMemberships.find(m => m.membershipTypeId == cheapestMembership.membership.id)) {
|
|
483
|
+
console.log('Skipping automatic membership for: ' + this.id, ' - already has this membership')
|
|
484
|
+
return
|
|
485
|
+
}
|
|
486
|
+
|
|
450
487
|
const periodConfig = cheapestMembership.membership.periods.get(platform.periodId)
|
|
451
488
|
if (!periodConfig) {
|
|
452
489
|
throw new Error("Period config not found")
|
|
453
490
|
}
|
|
454
491
|
|
|
492
|
+
// Can we revive an earlier deleted membership?
|
|
455
493
|
console.log('Creating automatic membership for: ' + this.id + ' - membership type ' + cheapestMembership.membership.id)
|
|
456
494
|
const membership = new MemberPlatformMembership();
|
|
457
495
|
membership.memberId = this.id
|
|
@@ -462,9 +500,19 @@ export class Member extends Model {
|
|
|
462
500
|
membership.startDate = periodConfig.startDate
|
|
463
501
|
membership.endDate = periodConfig.endDate
|
|
464
502
|
membership.expireDate = periodConfig.expireDate
|
|
503
|
+
membership.generated = true;
|
|
465
504
|
|
|
466
505
|
await membership.calculatePrice()
|
|
467
506
|
await membership.save()
|
|
507
|
+
|
|
508
|
+
// This reasoning allows us to replace an existing membership with a cheaper one (not date based ones, but type based ones)
|
|
509
|
+
for (const toDelete of activeMemberships) {
|
|
510
|
+
if (toDelete.canDelete() && toDelete.generated) {
|
|
511
|
+
console.log('Removing membership because cheaper membership found for: ' + this.id + ' - membership ' + toDelete.id)
|
|
512
|
+
toDelete.deletedAt = new Date()
|
|
513
|
+
await toDelete.save()
|
|
514
|
+
}
|
|
515
|
+
}
|
|
468
516
|
});
|
|
469
517
|
}
|
|
470
518
|
}
|
|
@@ -47,6 +47,17 @@ export class MemberPlatformMembership extends Model {
|
|
|
47
47
|
@column({ type: "integer" })
|
|
48
48
|
price = 0;
|
|
49
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Whether this was added automatically by the system
|
|
52
|
+
*/
|
|
53
|
+
@column({ type: "boolean" })
|
|
54
|
+
generated = false
|
|
55
|
+
|
|
56
|
+
@column({
|
|
57
|
+
type: "datetime", nullable: true
|
|
58
|
+
})
|
|
59
|
+
deletedAt: Date|null = null
|
|
60
|
+
|
|
50
61
|
@column({
|
|
51
62
|
type: "datetime", beforeSave(old?: any) {
|
|
52
63
|
if (old !== undefined) {
|
|
@@ -69,6 +80,16 @@ export class MemberPlatformMembership extends Model {
|
|
|
69
80
|
})
|
|
70
81
|
updatedAt: Date
|
|
71
82
|
|
|
83
|
+
canDelete() {
|
|
84
|
+
if (this.invoiceId || this.invoiceItemDetailId) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
delete(): Promise<void> {
|
|
91
|
+
throw new Error('Cannot delete a membership. Use the deletedAt column.');
|
|
92
|
+
}
|
|
72
93
|
|
|
73
94
|
async calculatePrice() {
|
|
74
95
|
if (this.invoiceId || this.invoiceItemDetailId) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { column, Model } from '@simonbackx/simple-database';
|
|
2
2
|
import { v4 as uuidv4 } from "uuid";
|
|
3
|
+
import { MemberResponsibilityRecord as MemberResponsibilityRecordStruct } from '@stamhoofd/structures';
|
|
3
4
|
|
|
4
5
|
export class MemberResponsibilityRecord extends Model {
|
|
5
6
|
static table = "member_responsibility_records"
|
|
@@ -15,6 +16,9 @@ export class MemberResponsibilityRecord extends Model {
|
|
|
15
16
|
@column({ type: "string", nullable: true })
|
|
16
17
|
organizationId: string|null = null;
|
|
17
18
|
|
|
19
|
+
@column({ type: "string", nullable: true })
|
|
20
|
+
groupId: string|null = null;
|
|
21
|
+
|
|
18
22
|
@column({ type: "string" })
|
|
19
23
|
memberId: string
|
|
20
24
|
|
|
@@ -36,4 +40,7 @@ export class MemberResponsibilityRecord extends Model {
|
|
|
36
40
|
@column({ type: "datetime", nullable: true })
|
|
37
41
|
endDate: Date | null = null
|
|
38
42
|
|
|
43
|
+
getStructure() {
|
|
44
|
+
return MemberResponsibilityRecordStruct.create(this)
|
|
45
|
+
}
|
|
39
46
|
}
|
|
@@ -3,7 +3,7 @@ import { DecodedRequest } from '@simonbackx/simple-endpoints';
|
|
|
3
3
|
import { SimpleError } from '@simonbackx/simple-errors';
|
|
4
4
|
import { I18n } from "@stamhoofd/backend-i18n";
|
|
5
5
|
import { Email, EmailInterfaceRecipient } from "@stamhoofd/email";
|
|
6
|
-
import { Address, Country, DNSRecordStatus, EmailTemplateType, OrganizationEmail, OrganizationMetaData, OrganizationPrivateMetaData, OrganizationRecordsConfiguration, OrganizationRegistrationPeriod as OrganizationRegistrationPeriodStruct, Organization as OrganizationStruct, PaymentMethod, PaymentProvider, PrivatePaymentConfiguration, Recipient, Replacement, STPackageType, TransferSettings } from "@stamhoofd/structures";
|
|
6
|
+
import { AccessRight, Address, Country, DNSRecordStatus, EmailTemplateType, OrganizationEmail, OrganizationMetaData, OrganizationPrivateMetaData, OrganizationRecordsConfiguration, OrganizationRegistrationPeriod as OrganizationRegistrationPeriodStruct, Organization as OrganizationStruct, PaymentMethod, PaymentProvider, PrivatePaymentConfiguration, Recipient, Replacement, STPackageType, TransferSettings } from "@stamhoofd/structures";
|
|
7
7
|
import { AWSError } from 'aws-sdk';
|
|
8
8
|
import SES from 'aws-sdk/clients/sesv2';
|
|
9
9
|
import { PromiseResult } from 'aws-sdk/lib/request';
|
|
@@ -13,6 +13,7 @@ import { validateDNSRecords } from "../helpers/DNSValidator";
|
|
|
13
13
|
import { getEmailBuilder } from "../helpers/EmailBuilder";
|
|
14
14
|
import { OrganizationServerMetaData } from '../structures/OrganizationServerMetaData';
|
|
15
15
|
import { EmailTemplate, Group, OrganizationRegistrationPeriod, RegistrationPeriod, StripeAccount } from "./";
|
|
16
|
+
import { QueueHandler } from "@stamhoofd/queues";
|
|
16
17
|
|
|
17
18
|
export class Organization extends Model {
|
|
18
19
|
static table = "organizations";
|
|
@@ -263,12 +264,47 @@ export class Organization extends Model {
|
|
|
263
264
|
return this.id+"." + defaultDomain;
|
|
264
265
|
}
|
|
265
266
|
|
|
266
|
-
async
|
|
267
|
-
const oPeriods = await OrganizationRegistrationPeriod.where({ periodId: this.periodId }, {limit: 1})
|
|
268
|
-
const oPeriod = oPeriods[0];
|
|
267
|
+
async getPeriod({emptyGroups} = {emptyGroups: false}) {
|
|
268
|
+
const oPeriods = await OrganizationRegistrationPeriod.where({ periodId: this.periodId, organizationId: this.id }, {limit: 1})
|
|
269
269
|
const period = await RegistrationPeriod.getByID(this.periodId)
|
|
270
|
+
|
|
271
|
+
if (!period) {
|
|
272
|
+
throw new Error("Period not found")
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
let oPeriod: OrganizationRegistrationPeriod;
|
|
276
|
+
if (oPeriods.length == 0) {
|
|
277
|
+
// Automatically create a period
|
|
278
|
+
oPeriod = await QueueHandler.schedule('create-missing-organization-period', async () => {
|
|
279
|
+
// Race condition check
|
|
280
|
+
const updatedPeriods = await OrganizationRegistrationPeriod.where({ periodId: this.periodId, organizationId: this.id }, {limit: 1})
|
|
281
|
+
|
|
282
|
+
if (updatedPeriods.length) {
|
|
283
|
+
return updatedPeriods[0]
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
console.log('Automatically creating new organization registration period for organization ' + this.id + ' and period ' + this.periodId + ' - organization period is missing')
|
|
287
|
+
const created = new OrganizationRegistrationPeriod()
|
|
288
|
+
created.organizationId = this.id
|
|
289
|
+
created.periodId = this.periodId
|
|
290
|
+
await created.save()
|
|
291
|
+
return created
|
|
292
|
+
})
|
|
293
|
+
} else {
|
|
294
|
+
oPeriod = oPeriods[0];
|
|
295
|
+
}
|
|
270
296
|
const groups = emptyGroups ? [] : (await (await import("./Group")).Group.getAll(this.id, this.periodId))
|
|
271
297
|
|
|
298
|
+
return {
|
|
299
|
+
organizationPeriod: oPeriod,
|
|
300
|
+
period,
|
|
301
|
+
groups
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
async getStructure({emptyGroups} = {emptyGroups: false}): Promise<OrganizationStruct> {
|
|
306
|
+
const {groups, organizationPeriod, period} = await this.getPeriod({emptyGroups})
|
|
307
|
+
|
|
272
308
|
const struct = OrganizationStruct.create({
|
|
273
309
|
id: this.id,
|
|
274
310
|
name: this.name,
|
|
@@ -279,10 +315,7 @@ export class Organization extends Model {
|
|
|
279
315
|
website: this.website,
|
|
280
316
|
groups: groups.map(g => g.getStructure()),
|
|
281
317
|
createdAt: this.createdAt,
|
|
282
|
-
period:
|
|
283
|
-
...oPeriod,
|
|
284
|
-
period: period!.getStructure()
|
|
285
|
-
})
|
|
318
|
+
period: organizationPeriod.getStructure(period, groups)
|
|
286
319
|
})
|
|
287
320
|
|
|
288
321
|
if (this.meta.modules.disableActivities) {
|
|
@@ -879,7 +912,7 @@ export class Organization extends Model {
|
|
|
879
912
|
// Circular reference fix
|
|
880
913
|
const User = (await import('./User')).User;
|
|
881
914
|
const admins = await User.where({ organizationId: this.id, permissions: { sign: "!=", value: null }})
|
|
882
|
-
const filtered = admins.filter(a => a.
|
|
915
|
+
const filtered = admins.filter(a => a.permissions?.forOrganization(this)?.hasAccessRight(AccessRight.OrganizationFinanceDirector))
|
|
883
916
|
|
|
884
917
|
if (filtered.length > 0) {
|
|
885
918
|
return filtered.map(f => f.getEmailTo() ).join(", ")
|
|
@@ -53,6 +53,14 @@ export class OrganizationRegistrationPeriod extends Model {
|
|
|
53
53
|
})
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
getPrivateStructure(this: OrganizationRegistrationPeriod, period: RegistrationPeriod, groups: Group[]) {
|
|
57
|
+
return OrganizationRegistrationPeriodStruct.create({
|
|
58
|
+
...this,
|
|
59
|
+
period: period.getStructure(),
|
|
60
|
+
groups: groups.map(g => g.getPrivateStructure()).sort(GroupStruct.defaultSort)
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
|
|
56
64
|
async cleanCategories(groups: {id: string}[]) {
|
|
57
65
|
const reachable = new Map<string, boolean>()
|
|
58
66
|
const queue = [this.settings.rootCategoryId]
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { column, Model } from '@simonbackx/simple-database';
|
|
2
|
+
import { SQL, SQLWhereSign } from '@stamhoofd/sql';
|
|
2
3
|
import { RegistrationPeriodSettings, RegistrationPeriod as RegistrationPeriodStruct } from '@stamhoofd/structures';
|
|
3
4
|
import { v4 as uuidv4 } from "uuid";
|
|
4
5
|
|
|
@@ -52,4 +53,17 @@ export class RegistrationPeriod extends Model {
|
|
|
52
53
|
getStructure() {
|
|
53
54
|
return RegistrationPeriodStruct.create(this)
|
|
54
55
|
}
|
|
56
|
+
|
|
57
|
+
static async getByDate(date: Date): Promise<RegistrationPeriod|null> {
|
|
58
|
+
const result = await SQL.select().from(SQL.table(this.table))
|
|
59
|
+
.where(SQL.column('startDate'), SQLWhereSign.LessEqual, date)
|
|
60
|
+
.where(SQL.column('endDate'), SQLWhereSign.GreaterEqual, date)
|
|
61
|
+
.first(false);
|
|
62
|
+
|
|
63
|
+
if (result === null || !result[this.table]) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return RegistrationPeriod.fromRow(result[this.table]) ?? null
|
|
68
|
+
}
|
|
55
69
|
}
|
package/src/models/User.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
|
|
2
2
|
import { column, Database, ManyToOneRelation, Model } from "@simonbackx/simple-database";
|
|
3
3
|
import { EmailInterfaceRecipient } from "@stamhoofd/email";
|
|
4
|
-
import { LoginProviderType, NewUser, Permissions, User as UserStruct
|
|
4
|
+
import { LoginProviderType, NewUser, Permissions, UserMeta, UserPermissions, User as UserStruct } from "@stamhoofd/structures";
|
|
5
5
|
import argon2 from "argon2";
|
|
6
6
|
import { v4 as uuidv4 } from "uuid";
|
|
7
7
|
|
|
8
|
-
import { Organization
|
|
8
|
+
import { Organization } from "./";
|
|
9
9
|
|
|
10
10
|
export class User extends Model {
|
|
11
11
|
static table = "users";
|
|
@@ -21,6 +21,9 @@ export class User extends Model {
|
|
|
21
21
|
@column({ foreignKey: User.organization, type: "string", nullable: true })
|
|
22
22
|
organizationId: string|null;
|
|
23
23
|
|
|
24
|
+
@column({ type: "string", nullable: true })
|
|
25
|
+
memberId: string|null = null
|
|
26
|
+
|
|
24
27
|
@column({ type: "string", nullable: true })
|
|
25
28
|
firstName: string | null = null;
|
|
26
29
|
|
|
@@ -347,6 +350,11 @@ export class User extends Model {
|
|
|
347
350
|
throw new Error("Missing organization")
|
|
348
351
|
}
|
|
349
352
|
|
|
353
|
+
if (await User.getForAuthentication(organization?.id ?? null, email, {allowWithoutAccount: true})) {
|
|
354
|
+
// Already exists
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
|
|
350
358
|
const user = new User();
|
|
351
359
|
user.organizationId = STAMHOOFD.userMode === 'platform' ? null : (organization?.id ?? null)
|
|
352
360
|
user.id = id ?? uuidv4()
|
|
@@ -429,7 +437,9 @@ export class User extends Model {
|
|
|
429
437
|
email: this.email,
|
|
430
438
|
verified: this.verified,
|
|
431
439
|
permissions: this.permissions,
|
|
432
|
-
hasAccount: this.hasAccount()
|
|
440
|
+
hasAccount: this.hasAccount(),
|
|
441
|
+
memberId: this.memberId,
|
|
442
|
+
organizationId: this.organizationId
|
|
433
443
|
});
|
|
434
444
|
}
|
|
435
445
|
|
package/src/models/index.ts
CHANGED
|
@@ -48,3 +48,6 @@ export * from "./MemberResponsibilityRecord"
|
|
|
48
48
|
export * from "./OrganizationRegistrationPeriod"
|
|
49
49
|
export * from "./RegistrationPeriod"
|
|
50
50
|
export * from "./MemberPlatformMembership"
|
|
51
|
+
export * from "./Email"
|
|
52
|
+
export * from "./EmailRecipient"
|
|
53
|
+
export * from "./Event"
|