@stamhoofd/backend 2.23.0 → 2.24.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/.env.template.json +5 -0
- package/package.json +10 -10
- package/src/endpoints/auth/SignupEndpoint.ts +1 -1
- package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.ts +28 -1
- package/src/endpoints/global/platform/PatchPlatformEnpoint.ts +8 -1
- package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.ts +60 -23
- package/src/helpers/AuthenticatedStructures.ts +31 -2
- package/src/helpers/MemberUserSyncer.ts +2 -2
- package/src/helpers/PeriodHelper.ts +70 -0
- package/src/helpers/SetupStepsUpdater.ts +17 -8
package/.env.template.json
CHANGED
|
@@ -12,6 +12,11 @@
|
|
|
12
12
|
"BE": "www.be.stamhoofd",
|
|
13
13
|
"NL": "www.nl.stamhoofd"
|
|
14
14
|
},
|
|
15
|
+
"documentation": {
|
|
16
|
+
"": "www.be.stamhoofd/docs",
|
|
17
|
+
"BE": "www.be.stamhoofd/docs",
|
|
18
|
+
"NL": "www.nl.stamhoofd/docs"
|
|
19
|
+
},
|
|
15
20
|
"webshop": {
|
|
16
21
|
"": "shop.be.stamhoofd",
|
|
17
22
|
"BE": "shop.be.stamhoofd",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stamhoofd/backend",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.24.0",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -36,14 +36,14 @@
|
|
|
36
36
|
"@simonbackx/simple-encoding": "2.15.0",
|
|
37
37
|
"@simonbackx/simple-endpoints": "1.14.0",
|
|
38
38
|
"@simonbackx/simple-logging": "^1.0.1",
|
|
39
|
-
"@stamhoofd/backend-i18n": "2.
|
|
40
|
-
"@stamhoofd/backend-middleware": "2.
|
|
41
|
-
"@stamhoofd/email": "2.
|
|
42
|
-
"@stamhoofd/models": "2.
|
|
43
|
-
"@stamhoofd/queues": "2.
|
|
44
|
-
"@stamhoofd/sql": "2.
|
|
45
|
-
"@stamhoofd/structures": "2.
|
|
46
|
-
"@stamhoofd/utility": "2.
|
|
39
|
+
"@stamhoofd/backend-i18n": "2.24.0",
|
|
40
|
+
"@stamhoofd/backend-middleware": "2.24.0",
|
|
41
|
+
"@stamhoofd/email": "2.24.0",
|
|
42
|
+
"@stamhoofd/models": "2.24.0",
|
|
43
|
+
"@stamhoofd/queues": "2.24.0",
|
|
44
|
+
"@stamhoofd/sql": "2.24.0",
|
|
45
|
+
"@stamhoofd/structures": "2.24.0",
|
|
46
|
+
"@stamhoofd/utility": "2.24.0",
|
|
47
47
|
"archiver": "^7.0.1",
|
|
48
48
|
"aws-sdk": "^2.885.0",
|
|
49
49
|
"axios": "1.6.8",
|
|
@@ -60,5 +60,5 @@
|
|
|
60
60
|
"postmark": "4.0.2",
|
|
61
61
|
"stripe": "^16.6.0"
|
|
62
62
|
},
|
|
63
|
-
"gitHead": "
|
|
63
|
+
"gitHead": "bad7d2adfa412af0b0de101274e46ed0b539ae38"
|
|
64
64
|
}
|
|
@@ -64,7 +64,7 @@ export class SignupEndpoint extends Endpoint<Params, Query, Body, ResponseBody>
|
|
|
64
64
|
replyTo: undefined
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
const footer = (!user.permissions && organization ? "\n\n—\n\nOnze ledenadministratie werkt via het Stamhoofd platform, op maat van verenigingen. Probeer het ook via https://"+request.i18n
|
|
67
|
+
const footer = (!user.permissions && organization ? "\n\n—\n\nOnze ledenadministratie werkt via het Stamhoofd platform, op maat van verenigingen. Probeer het ook via https://"+request.i18n.localizedDomains.marketing()+"/ledenadministratie\n\n" : '')
|
|
68
68
|
|
|
69
69
|
const name = organization ? organization.name : 'Stamhoofd'
|
|
70
70
|
// Send email
|
|
@@ -3,7 +3,7 @@ import { ConvertArrayToPatchableArray, Decoder, PatchableArrayAutoEncoder, Patch
|
|
|
3
3
|
import { DecodedRequest, Endpoint, Request, Response } from "@simonbackx/simple-endpoints";
|
|
4
4
|
import { SimpleError } from "@simonbackx/simple-errors";
|
|
5
5
|
import { BalanceItem, Document, Group, Member, MemberFactory, MemberPlatformMembership, MemberResponsibilityRecord, MemberWithRegistrations, Organization, Platform, Registration, User } from '@stamhoofd/models';
|
|
6
|
-
import { MemberWithRegistrationsBlob, MembersBlob, PermissionLevel } from "@stamhoofd/structures";
|
|
6
|
+
import { GroupType, MemberWithRegistrationsBlob, MembersBlob, PermissionLevel } from "@stamhoofd/structures";
|
|
7
7
|
import { Formatter } from '@stamhoofd/utility';
|
|
8
8
|
|
|
9
9
|
import { AuthenticatedStructures } from '../../../helpers/AuthenticatedStructures';
|
|
@@ -267,6 +267,33 @@ export class PatchOrganizationMembersEndpoint extends Endpoint<Params, Query, Bo
|
|
|
267
267
|
})
|
|
268
268
|
}
|
|
269
269
|
|
|
270
|
+
const hasRegistration = member.registrations.some(registration => {
|
|
271
|
+
if (platformResponsibility) {
|
|
272
|
+
if (registration.group.defaultAgeGroupId === null) {
|
|
273
|
+
return false;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (org) {
|
|
278
|
+
if (registration.periodId !== org.periodId) {
|
|
279
|
+
return false;
|
|
280
|
+
}
|
|
281
|
+
} else {
|
|
282
|
+
if (registration.periodId !== platform.periodId) {
|
|
283
|
+
return false;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return registration.deactivatedAt === null && registration.registeredAt !== null && registration.group.type === GroupType.Membership
|
|
287
|
+
})
|
|
288
|
+
|
|
289
|
+
if (!hasRegistration) {
|
|
290
|
+
throw new SimpleError({
|
|
291
|
+
code: "invalid_field",
|
|
292
|
+
message: "Invalid organization",
|
|
293
|
+
human: "Je kan een functie enkel toekennen aan leden die zijn ingeschreven in het huidige werkjaar",
|
|
294
|
+
})
|
|
295
|
+
}
|
|
296
|
+
|
|
270
297
|
const model = new MemberResponsibilityRecord()
|
|
271
298
|
model.memberId = member.id
|
|
272
299
|
model.responsibilityId = responsibility.id
|
|
@@ -6,6 +6,7 @@ import { MemberResponsibility, PlatformConfig, PlatformPremiseType, Platform as
|
|
|
6
6
|
import { SimpleError } from "@simonbackx/simple-errors";
|
|
7
7
|
import { Context } from "../../../helpers/Context";
|
|
8
8
|
import { SetupStepUpdater } from "../../../helpers/SetupStepsUpdater";
|
|
9
|
+
import { PeriodHelper } from "../../../helpers/PeriodHelper";
|
|
9
10
|
|
|
10
11
|
type Params = Record<string, never>;
|
|
11
12
|
type Query = undefined;
|
|
@@ -73,6 +74,7 @@ export class PatchPlatformEndpoint extends Endpoint<
|
|
|
73
74
|
}
|
|
74
75
|
|
|
75
76
|
let shouldUpdateSetupSteps = false;
|
|
77
|
+
let shouldMoveToPeriod: RegistrationPeriod | null = null;
|
|
76
78
|
|
|
77
79
|
if (request.body.config) {
|
|
78
80
|
if (!Context.auth.hasPlatformFullAccess()) {
|
|
@@ -113,6 +115,8 @@ export class PatchPlatformEndpoint extends Endpoint<
|
|
|
113
115
|
});
|
|
114
116
|
}
|
|
115
117
|
platform.periodId = period.id;
|
|
118
|
+
shouldUpdateSetupSteps = true;
|
|
119
|
+
shouldMoveToPeriod = period;
|
|
116
120
|
}
|
|
117
121
|
|
|
118
122
|
if (request.body.membershipOrganizationId !== undefined) {
|
|
@@ -155,7 +159,10 @@ export class PatchPlatformEndpoint extends Endpoint<
|
|
|
155
159
|
|
|
156
160
|
await platform.save();
|
|
157
161
|
|
|
158
|
-
if(
|
|
162
|
+
if (shouldMoveToPeriod) {
|
|
163
|
+
PeriodHelper.moveAllOrganizationsToPeriod(shouldMoveToPeriod).catch(console.error)
|
|
164
|
+
} else if(shouldUpdateSetupSteps) {
|
|
165
|
+
// Do not call this right away when moving to a period, because this needs to happen AFTER moving to the period
|
|
159
166
|
SetupStepUpdater.updateSetupStepsForAllOrganizationsInCurrentPeriod().catch(console.error);
|
|
160
167
|
}
|
|
161
168
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { DecodedRequest, Endpoint, Request, Response } from "@simonbackx/simple-endpoints";
|
|
2
|
-
import { Group as GroupStruct,
|
|
2
|
+
import { GroupPrivateSettings, Group as GroupStruct, GroupType, OrganizationRegistrationPeriod as OrganizationRegistrationPeriodStruct, PermissionLevel, PermissionsResourceType, ResourcePermissions, Version } from "@stamhoofd/structures";
|
|
3
3
|
|
|
4
4
|
import { AutoEncoderPatchType, Decoder, PatchableArrayAutoEncoder, PatchableArrayDecoder, StringDecoder } from "@simonbackx/simple-encoding";
|
|
5
|
-
import { Context } from "../../../../helpers/Context";
|
|
6
|
-
import { Group, Member, OrganizationRegistrationPeriod, Platform, RegistrationPeriod } from "@stamhoofd/models";
|
|
7
5
|
import { SimpleError } from "@simonbackx/simple-errors";
|
|
6
|
+
import { Group, Member, Organization, OrganizationRegistrationPeriod, Platform, RegistrationPeriod } from "@stamhoofd/models";
|
|
8
7
|
import { AuthenticatedStructures } from "../../../../helpers/AuthenticatedStructures";
|
|
8
|
+
import { Context } from "../../../../helpers/Context";
|
|
9
9
|
|
|
10
10
|
type Params = Record<string, never>;
|
|
11
11
|
type Query = undefined;
|
|
@@ -46,9 +46,12 @@ export class PatchOrganizationRegistrationPeriodsEndpoint extends Endpoint<Param
|
|
|
46
46
|
if (!await Context.auth.hasFullAccess(organization.id)) {
|
|
47
47
|
throw Context.auth.error()
|
|
48
48
|
}
|
|
49
|
-
|
|
49
|
+
periods.push(await PatchOrganizationRegistrationPeriodsEndpoint.createOrganizationPeriod(organization, put));
|
|
50
|
+
}
|
|
50
51
|
|
|
51
|
-
|
|
52
|
+
for (const patch of request.body.getPatches()) {
|
|
53
|
+
const organizationPeriod = await OrganizationRegistrationPeriod.getByID(patch.id);
|
|
54
|
+
if (!organizationPeriod || organizationPeriod.organizationId !== organization.id) {
|
|
52
55
|
throw new SimpleError({
|
|
53
56
|
code: "not_found",
|
|
54
57
|
message: "Period not found",
|
|
@@ -56,38 +59,33 @@ export class PatchOrganizationRegistrationPeriodsEndpoint extends Endpoint<Param
|
|
|
56
59
|
})
|
|
57
60
|
}
|
|
58
61
|
|
|
59
|
-
const
|
|
60
|
-
organizationPeriod.id = put.id;
|
|
61
|
-
organizationPeriod.organizationId = organization.id;
|
|
62
|
-
organizationPeriod.periodId = put.period.id;
|
|
63
|
-
organizationPeriod.settings = put.settings;
|
|
64
|
-
await organizationPeriod.save();
|
|
62
|
+
const period = await RegistrationPeriod.getByID(organizationPeriod.periodId);
|
|
65
63
|
|
|
66
|
-
|
|
67
|
-
|
|
64
|
+
if (!period) {
|
|
65
|
+
throw new SimpleError({
|
|
66
|
+
code: "not_found",
|
|
67
|
+
message: "Period not found",
|
|
68
|
+
statusCode: 404
|
|
69
|
+
})
|
|
68
70
|
}
|
|
69
|
-
const groups = await Group.getAll(organization.id, organizationPeriod.periodId)
|
|
70
|
-
|
|
71
|
-
// Delete unreachable categories first
|
|
72
|
-
await organizationPeriod.cleanCategories(groups);
|
|
73
|
-
await Group.deleteUnreachable(organization.id, organizationPeriod, groups)
|
|
74
|
-
periods.push(organizationPeriod);
|
|
75
|
-
}
|
|
76
71
|
|
|
77
|
-
|
|
78
|
-
const organizationPeriod = await OrganizationRegistrationPeriod.getByID(patch.id);
|
|
79
|
-
if (!organizationPeriod || organizationPeriod.organizationId !== organization.id) {
|
|
72
|
+
if (period.locked) {
|
|
80
73
|
throw new SimpleError({
|
|
81
74
|
code: "not_found",
|
|
82
75
|
message: "Period not found",
|
|
76
|
+
human: 'Je kan geen wijzigingen meer aanbrengen in ' + period.getStructure().name + ' omdat deze is afgesloten',
|
|
83
77
|
statusCode: 404
|
|
84
78
|
})
|
|
85
79
|
}
|
|
80
|
+
|
|
86
81
|
let deleteUnreachable = false
|
|
87
82
|
const allowedIds: string[] = []
|
|
88
83
|
|
|
89
84
|
if (await Context.auth.hasFullAccess(organization.id)) {
|
|
90
85
|
if (patch.settings) {
|
|
86
|
+
if(patch.settings.categories) {
|
|
87
|
+
deleteUnreachable = true;
|
|
88
|
+
}
|
|
91
89
|
organizationPeriod.settings.patchOrPut(patch.settings);
|
|
92
90
|
}
|
|
93
91
|
} else {
|
|
@@ -174,6 +172,36 @@ export class PatchOrganizationRegistrationPeriodsEndpoint extends Endpoint<Param
|
|
|
174
172
|
})
|
|
175
173
|
}
|
|
176
174
|
|
|
175
|
+
static async createOrganizationPeriod(organization: Organization, struct: OrganizationRegistrationPeriodStruct) {
|
|
176
|
+
const period = await RegistrationPeriod.getByID(struct.period.id);
|
|
177
|
+
|
|
178
|
+
if (!period || period.locked) {
|
|
179
|
+
throw new SimpleError({
|
|
180
|
+
code: "not_found",
|
|
181
|
+
message: "Period not found",
|
|
182
|
+
statusCode: 404
|
|
183
|
+
})
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const organizationPeriod = new OrganizationRegistrationPeriod();
|
|
187
|
+
organizationPeriod.id = struct.id;
|
|
188
|
+
organizationPeriod.organizationId = organization.id;
|
|
189
|
+
organizationPeriod.periodId = struct.period.id;
|
|
190
|
+
organizationPeriod.settings = struct.settings;
|
|
191
|
+
await organizationPeriod.save();
|
|
192
|
+
|
|
193
|
+
for (const s of struct.groups) {
|
|
194
|
+
await PatchOrganizationRegistrationPeriodsEndpoint.createGroup(s, organization.id, organizationPeriod.periodId)
|
|
195
|
+
}
|
|
196
|
+
const groups = await Group.getAll(organization.id, organizationPeriod.periodId)
|
|
197
|
+
|
|
198
|
+
// Delete unreachable categories first
|
|
199
|
+
await organizationPeriod.cleanCategories(groups);
|
|
200
|
+
await Group.deleteUnreachable(organization.id, organizationPeriod, groups)
|
|
201
|
+
|
|
202
|
+
return organizationPeriod
|
|
203
|
+
}
|
|
204
|
+
|
|
177
205
|
static async deleteGroup(id: string) {
|
|
178
206
|
const model = await Group.getByID(id)
|
|
179
207
|
if (!model || !await Context.auth.canAccessGroup(model, PermissionLevel.Full)) {
|
|
@@ -263,6 +291,15 @@ export class PatchOrganizationRegistrationPeriodsEndpoint extends Endpoint<Param
|
|
|
263
291
|
})
|
|
264
292
|
}
|
|
265
293
|
|
|
294
|
+
if (existing.periodId !== model.periodId) {
|
|
295
|
+
throw new SimpleError({
|
|
296
|
+
code: 'invalid_field',
|
|
297
|
+
field: 'waitingList',
|
|
298
|
+
message: 'Waiting list group is already used in another period',
|
|
299
|
+
human: 'Een wachtlijst kan momenteel niet gedeeld worden tussen verschillende werkjaren'
|
|
300
|
+
})
|
|
301
|
+
}
|
|
302
|
+
|
|
266
303
|
model.waitingListId = existing.id
|
|
267
304
|
} else {
|
|
268
305
|
const group = await PatchOrganizationRegistrationPeriodsEndpoint.createGroup(
|
|
@@ -262,14 +262,43 @@ export class AuthenticatedStructures {
|
|
|
262
262
|
}
|
|
263
263
|
}
|
|
264
264
|
|
|
265
|
+
const organizationStructs = await Promise.all([...organizations.values()].filter(o => o.active).map(o => this.organization(o)))
|
|
266
|
+
|
|
267
|
+
// Load missing groups
|
|
268
|
+
const allGroups = new Map<string, GroupStruct>()
|
|
269
|
+
for (const organization of organizationStructs) {
|
|
270
|
+
for (const group of organization.period.groups) {
|
|
271
|
+
allGroups.set(group.id, group)
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
265
275
|
for (const blob of memberBlobs) {
|
|
266
|
-
|
|
276
|
+
for (const registration of blob.registrations) {
|
|
277
|
+
if (registration.group) {
|
|
278
|
+
allGroups.set(registration.group.id, registration.group)
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const groupIds = Formatter.uniqueArray(responsibilities.map(r => r.groupId).filter(id => id !== null)).filter(id => !allGroups.has(id))
|
|
284
|
+
const groups = groupIds.length > 0 ? await Group.getByIDs(...groupIds) : []
|
|
285
|
+
const groupStructs = await this.groups(groups)
|
|
286
|
+
|
|
287
|
+
for (const group of groupStructs) {
|
|
288
|
+
allGroups.set(group.id, group)
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
for (const blob of memberBlobs) {
|
|
292
|
+
blob.responsibilities = responsibilities.filter(r => r.memberId == blob.id).map(r => {
|
|
293
|
+
const group = allGroups.get(r.groupId ?? '') ?? null
|
|
294
|
+
return r.getStructure(group)
|
|
295
|
+
})
|
|
267
296
|
blob.platformMemberships = platformMemberships.filter(r => r.memberId == blob.id).map(r => MemberPlatformMembershipStruct.create(r))
|
|
268
297
|
}
|
|
269
298
|
|
|
270
299
|
return MembersBlob.create({
|
|
271
300
|
members: memberBlobs,
|
|
272
|
-
organizations:
|
|
301
|
+
organizations: organizationStructs
|
|
273
302
|
})
|
|
274
303
|
}
|
|
275
304
|
|
|
@@ -116,14 +116,14 @@ export class MemberUserSyncerStatic {
|
|
|
116
116
|
if (organizationId === null) {
|
|
117
117
|
const patch = user.permissions.convertPlatformPatch(
|
|
118
118
|
Permissions.patch({
|
|
119
|
-
responsibilities: (responsibilitiesByOrganization.get(organizationId) ?? []).map(r => r.
|
|
119
|
+
responsibilities: (responsibilitiesByOrganization.get(organizationId) ?? []).map(r => r.getBaseStructure()) as any
|
|
120
120
|
})
|
|
121
121
|
)
|
|
122
122
|
user.permissions = user.permissions.patch(patch)
|
|
123
123
|
} else {
|
|
124
124
|
const patch = user.permissions.convertPatch(
|
|
125
125
|
Permissions.patch({
|
|
126
|
-
responsibilities: (responsibilitiesByOrganization.get(organizationId) ?? []).map(r => r.
|
|
126
|
+
responsibilities: (responsibilitiesByOrganization.get(organizationId) ?? []).map(r => r.getBaseStructure()) as any
|
|
127
127
|
}),
|
|
128
128
|
organizationId
|
|
129
129
|
)
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
|
|
2
|
+
import { Organization, OrganizationRegistrationPeriod, RegistrationPeriod } from "@stamhoofd/models";
|
|
3
|
+
import { AuthenticatedStructures } from "./AuthenticatedStructures";
|
|
4
|
+
import { PatchOrganizationRegistrationPeriodsEndpoint } from "../endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint";
|
|
5
|
+
import { QueueHandler } from "@stamhoofd/queues";
|
|
6
|
+
import { SetupStepUpdater } from "./SetupStepsUpdater";
|
|
7
|
+
|
|
8
|
+
export class PeriodHelper {
|
|
9
|
+
static async moveOrganizationToPeriod(organization: Organization, period: RegistrationPeriod) {
|
|
10
|
+
console.log('moveOrganizationToPeriod', organization.id, period.id)
|
|
11
|
+
|
|
12
|
+
await this.createOrganizationPeriodForPeriod(organization, period)
|
|
13
|
+
organization.periodId = period.id
|
|
14
|
+
await organization.save()
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
static async createOrganizationPeriodForPeriod(organization: Organization, period: RegistrationPeriod) {
|
|
18
|
+
const oPeriods = await OrganizationRegistrationPeriod.where({ periodId: period.id, organizationId: organization.id }, {limit: 1})
|
|
19
|
+
|
|
20
|
+
if (oPeriods.length) {
|
|
21
|
+
// Already created
|
|
22
|
+
return oPeriods[0]
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const currentPeriod = await organization.getPeriod()
|
|
26
|
+
if (currentPeriod.periodId === period.id) {
|
|
27
|
+
return currentPeriod
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const struct = await AuthenticatedStructures.organizationRegistrationPeriod(currentPeriod)
|
|
31
|
+
|
|
32
|
+
const duplicate = struct.duplicate(period.getStructure())
|
|
33
|
+
return await PatchOrganizationRegistrationPeriodsEndpoint.createOrganizationPeriod(organization, duplicate)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
static async moveAllOrganizationsToPeriod(period: RegistrationPeriod) {
|
|
37
|
+
const tag = "moveAllOrganizationsToPeriod";
|
|
38
|
+
const batchSize = 10;
|
|
39
|
+
QueueHandler.cancel(tag);
|
|
40
|
+
|
|
41
|
+
await QueueHandler.schedule(tag, async () => {
|
|
42
|
+
let lastId = "";
|
|
43
|
+
|
|
44
|
+
while (true) {
|
|
45
|
+
const organizations = await Organization.where(
|
|
46
|
+
{
|
|
47
|
+
id: { sign: ">", value: lastId },
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
limit: batchSize,
|
|
51
|
+
sort: ["id"]
|
|
52
|
+
}
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
for (const organization of organizations) {
|
|
56
|
+
await this.moveOrganizationToPeriod(organization, period);
|
|
57
|
+
lastId = organization.id;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (organizations.length < batchSize) {
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// When done: update setup steps
|
|
68
|
+
await SetupStepUpdater.updateSetupStepsForAllOrganizationsInCurrentPeriod()
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Group,
|
|
3
|
+
Member,
|
|
3
4
|
MemberResponsibilityRecord,
|
|
4
5
|
Organization,
|
|
5
6
|
OrganizationRegistrationPeriod,
|
|
@@ -8,11 +9,13 @@ import {
|
|
|
8
9
|
import { QueueHandler } from "@stamhoofd/queues";
|
|
9
10
|
import { SQL, SQLWhereSign } from "@stamhoofd/sql";
|
|
10
11
|
import {
|
|
12
|
+
GroupType,
|
|
11
13
|
MemberResponsibility,
|
|
12
14
|
Platform as PlatformStruct,
|
|
13
15
|
SetupStepType,
|
|
14
16
|
SetupSteps
|
|
15
17
|
} from "@stamhoofd/structures";
|
|
18
|
+
import { Formatter } from "@stamhoofd/utility";
|
|
16
19
|
|
|
17
20
|
type SetupStepOperation = (setupSteps: SetupSteps, organization: Organization, platform: PlatformStruct) => void | Promise<void>;
|
|
18
21
|
|
|
@@ -244,12 +247,21 @@ export class SetupStepUpdater {
|
|
|
244
247
|
|
|
245
248
|
const responsibilityIds = organizationBasedResponsibilitiesWithRestriction.map(r => r.id);
|
|
246
249
|
|
|
247
|
-
const
|
|
250
|
+
const allRecords = await MemberResponsibilityRecord.select()
|
|
248
251
|
.where('responsibilityId', responsibilityIds)
|
|
249
252
|
.where('organizationId', organization.id)
|
|
250
253
|
.where(SQL.where('endDate', SQLWhereSign.Greater, now).or('endDate', null))
|
|
251
254
|
.fetch();
|
|
252
255
|
|
|
256
|
+
// Remove invalid responsibilities: members that are not registered in the current period
|
|
257
|
+
const memberIds = Formatter.uniqueArray(allRecords.map(r => r.memberId));
|
|
258
|
+
const members = await Member.getBlobByIds(...memberIds);
|
|
259
|
+
const validMembers = members.filter(m => m.registrations.some(r => r.organizationId === organization.id && r.periodId === organization.periodId && r.group.type === GroupType.Membership && r.deactivatedAt === null && r.registeredAt !== null));
|
|
260
|
+
|
|
261
|
+
const validMembersIds = validMembers.map(m => m.id);
|
|
262
|
+
|
|
263
|
+
const records = allRecords.filter(r => validMembersIds.includes(r.memberId));
|
|
264
|
+
|
|
253
265
|
let totalSteps = 0;
|
|
254
266
|
let finishedSteps = 0;
|
|
255
267
|
|
|
@@ -279,11 +291,11 @@ export class SetupStepUpdater {
|
|
|
279
291
|
for(const {responsibility, group} of flatResponsibilities) {
|
|
280
292
|
const { minimumMembers: min, maximumMembers: max } = responsibility;
|
|
281
293
|
|
|
282
|
-
if (min === null
|
|
294
|
+
if (min === null) {
|
|
283
295
|
continue;
|
|
284
296
|
}
|
|
285
297
|
|
|
286
|
-
totalSteps
|
|
298
|
+
totalSteps += min;
|
|
287
299
|
|
|
288
300
|
const responsibilityId = responsibility.id;
|
|
289
301
|
let totalRecordsWithThisResponsibility = 0;
|
|
@@ -303,14 +315,11 @@ export class SetupStepUpdater {
|
|
|
303
315
|
}
|
|
304
316
|
|
|
305
317
|
if (max !== null && totalRecordsWithThisResponsibility > max) {
|
|
318
|
+
// Not added
|
|
306
319
|
continue;
|
|
307
320
|
}
|
|
308
321
|
|
|
309
|
-
|
|
310
|
-
continue;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
finishedSteps++;
|
|
322
|
+
finishedSteps += Math.min(min, totalRecordsWithThisResponsibility);
|
|
314
323
|
}
|
|
315
324
|
|
|
316
325
|
setupSteps.update(SetupStepType.Responsibilities, {
|