@stamhoofd/backend 2.103.1 → 2.105.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/endpoints/global/events/PatchEventsEndpoint.test.ts +264 -2
- package/src/endpoints/global/events/PatchEventsEndpoint.ts +15 -4
- package/src/endpoints/global/files/ExportToExcelEndpoint.ts +1 -1
- package/src/endpoints/global/registration/RegisterMembersEndpoint.test.ts +130 -2
- package/src/endpoints/global/registration/RegisterMembersEndpoint.ts +17 -3
- package/src/endpoints/organization/dashboard/registration-periods/MoveRegistrationPeriods.test.ts +486 -0
- package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.ts +130 -10
- package/src/helpers/AdminPermissionChecker.ts +15 -7
- package/src/helpers/PeriodHelper.ts +1 -1
- package/src/helpers/StripePayoutChecker.ts +7 -1
- package/src/helpers/fetchToAsyncIterator.ts +7 -0
- package/tests/assertions/assertBalances.ts +13 -1
- package/tests/jest.setup.ts +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stamhoofd/backend",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.105.0",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -45,14 +45,14 @@
|
|
|
45
45
|
"@simonbackx/simple-encoding": "2.22.0",
|
|
46
46
|
"@simonbackx/simple-endpoints": "1.20.1",
|
|
47
47
|
"@simonbackx/simple-logging": "^1.0.1",
|
|
48
|
-
"@stamhoofd/backend-i18n": "2.
|
|
49
|
-
"@stamhoofd/backend-middleware": "2.
|
|
50
|
-
"@stamhoofd/email": "2.
|
|
51
|
-
"@stamhoofd/models": "2.
|
|
52
|
-
"@stamhoofd/queues": "2.
|
|
53
|
-
"@stamhoofd/sql": "2.
|
|
54
|
-
"@stamhoofd/structures": "2.
|
|
55
|
-
"@stamhoofd/utility": "2.
|
|
48
|
+
"@stamhoofd/backend-i18n": "2.105.0",
|
|
49
|
+
"@stamhoofd/backend-middleware": "2.105.0",
|
|
50
|
+
"@stamhoofd/email": "2.105.0",
|
|
51
|
+
"@stamhoofd/models": "2.105.0",
|
|
52
|
+
"@stamhoofd/queues": "2.105.0",
|
|
53
|
+
"@stamhoofd/sql": "2.105.0",
|
|
54
|
+
"@stamhoofd/structures": "2.105.0",
|
|
55
|
+
"@stamhoofd/utility": "2.105.0",
|
|
56
56
|
"archiver": "^7.0.1",
|
|
57
57
|
"axios": "^1.8.2",
|
|
58
58
|
"cookie": "^0.7.0",
|
|
@@ -70,5 +70,5 @@
|
|
|
70
70
|
"publishConfig": {
|
|
71
71
|
"access": "public"
|
|
72
72
|
},
|
|
73
|
-
"gitHead": "
|
|
73
|
+
"gitHead": "491186e203464f2b7c0ade81dadc8e24b0fc1c69"
|
|
74
74
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { PatchableArray } from '@simonbackx/simple-encoding';
|
|
2
2
|
import { Endpoint, Request } from '@simonbackx/simple-endpoints';
|
|
3
|
-
import { EventFactory, Organization, OrganizationFactory, PlatformEventTypeFactory, RegistrationPeriodFactory, Token, User, UserFactory } from '@stamhoofd/models';
|
|
4
|
-
import { AccessRight, Event, Permissions, PermissionsResourceType, ResourcePermissions } from '@stamhoofd/structures';
|
|
3
|
+
import { EventFactory, Organization, OrganizationFactory, OrganizationRegistrationPeriodFactory, PlatformEventTypeFactory, RegistrationPeriodFactory, Token, User, UserFactory } from '@stamhoofd/models';
|
|
4
|
+
import { AccessRight, Event, Group, GroupSettings, GroupType, PermissionLevel, Permissions, PermissionsResourceType, ResourcePermissions, TranslatedString } from '@stamhoofd/structures';
|
|
5
5
|
import { STExpect, TestUtils } from '@stamhoofd/test-utils';
|
|
6
6
|
import { testServer } from '../../../../tests/helpers/TestServer';
|
|
7
7
|
import { PatchEventsEndpoint } from './PatchEventsEndpoint';
|
|
@@ -57,6 +57,10 @@ describe('Endpoint.PatchEventsEndpoint', () => {
|
|
|
57
57
|
}).create();
|
|
58
58
|
});
|
|
59
59
|
|
|
60
|
+
beforeEach(async () => {
|
|
61
|
+
TestUtils.setEnvironment('userMode', 'platform');
|
|
62
|
+
});
|
|
63
|
+
|
|
60
64
|
test('A normal user with write access can create an event', async () => {
|
|
61
65
|
const organization = await new OrganizationFactory({}).create();
|
|
62
66
|
const user = await new UserFactory({
|
|
@@ -168,6 +172,264 @@ describe('Endpoint.PatchEventsEndpoint', () => {
|
|
|
168
172
|
TestUtils.setEnvironment('userMode', 'organization');
|
|
169
173
|
});
|
|
170
174
|
|
|
175
|
+
test('Cannot create group for event in non-default period as non-full admin', async () => {
|
|
176
|
+
const organization = await new OrganizationFactory({}).create();
|
|
177
|
+
const user = await new UserFactory({
|
|
178
|
+
organization,
|
|
179
|
+
permissions: minimumUserPermissions,
|
|
180
|
+
}).create();
|
|
181
|
+
|
|
182
|
+
const period1 = await new RegistrationPeriodFactory({
|
|
183
|
+
startDate: new Date('2000-01-01'),
|
|
184
|
+
endDate: new Date('2001-01-01'),
|
|
185
|
+
organization,
|
|
186
|
+
}).create();
|
|
187
|
+
|
|
188
|
+
await new OrganizationRegistrationPeriodFactory({
|
|
189
|
+
period: period1,
|
|
190
|
+
organization,
|
|
191
|
+
}).create();
|
|
192
|
+
|
|
193
|
+
const period2 = await new RegistrationPeriodFactory({
|
|
194
|
+
startDate: new Date('2001-01-01'),
|
|
195
|
+
endDate: new Date('2002-01-01'),
|
|
196
|
+
organization,
|
|
197
|
+
}).create();
|
|
198
|
+
|
|
199
|
+
await new OrganizationRegistrationPeriodFactory({
|
|
200
|
+
period: period2,
|
|
201
|
+
organization,
|
|
202
|
+
}).create();
|
|
203
|
+
|
|
204
|
+
organization.periodId = period2.id;
|
|
205
|
+
await organization.save();
|
|
206
|
+
|
|
207
|
+
const event = await new EventFactory({
|
|
208
|
+
organization,
|
|
209
|
+
name: 'test event',
|
|
210
|
+
startDate: new Date('2000-02-10'),
|
|
211
|
+
endDate: new Date('2000-02-12'),
|
|
212
|
+
}).create();
|
|
213
|
+
|
|
214
|
+
const body: Body = new PatchableArray();
|
|
215
|
+
const newEvent = Event.patch({
|
|
216
|
+
id: event.id,
|
|
217
|
+
group: Group.create({
|
|
218
|
+
settings: GroupSettings.create({
|
|
219
|
+
description: TranslatedString.create('Inschrijvingsgroep'),
|
|
220
|
+
}),
|
|
221
|
+
}),
|
|
222
|
+
});
|
|
223
|
+
body.addPatch(newEvent);
|
|
224
|
+
|
|
225
|
+
await expect(TestRequest.patch({ body, user, organization })).rejects.toThrow(STExpect.simpleError({
|
|
226
|
+
code: 'permission_denied',
|
|
227
|
+
}));
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
test('Can create group for event in non-default period as full admin', async () => {
|
|
231
|
+
const organization = await new OrganizationFactory({}).create();
|
|
232
|
+
const user = await new UserFactory({
|
|
233
|
+
organization,
|
|
234
|
+
permissions: Permissions.create({ level: PermissionLevel.Full }),
|
|
235
|
+
}).create();
|
|
236
|
+
|
|
237
|
+
const period1 = await new RegistrationPeriodFactory({
|
|
238
|
+
startDate: new Date('2000-01-01'),
|
|
239
|
+
endDate: new Date('2001-01-01'),
|
|
240
|
+
organization,
|
|
241
|
+
}).create();
|
|
242
|
+
|
|
243
|
+
await new OrganizationRegistrationPeriodFactory({
|
|
244
|
+
period: period1,
|
|
245
|
+
organization,
|
|
246
|
+
}).create();
|
|
247
|
+
|
|
248
|
+
const period2 = await new RegistrationPeriodFactory({
|
|
249
|
+
startDate: new Date('2001-01-01'),
|
|
250
|
+
endDate: new Date('2002-01-01'),
|
|
251
|
+
organization,
|
|
252
|
+
}).create();
|
|
253
|
+
|
|
254
|
+
await new OrganizationRegistrationPeriodFactory({
|
|
255
|
+
period: period2,
|
|
256
|
+
organization,
|
|
257
|
+
}).create();
|
|
258
|
+
|
|
259
|
+
organization.periodId = period2.id;
|
|
260
|
+
await organization.save();
|
|
261
|
+
|
|
262
|
+
const event = await new EventFactory({
|
|
263
|
+
organization,
|
|
264
|
+
name: 'test event',
|
|
265
|
+
startDate: new Date('2000-02-10'),
|
|
266
|
+
endDate: new Date('2000-02-12'),
|
|
267
|
+
}).create();
|
|
268
|
+
|
|
269
|
+
const body: Body = new PatchableArray();
|
|
270
|
+
const newEvent = Event.patch({
|
|
271
|
+
id: event.id,
|
|
272
|
+
group: Group.create({
|
|
273
|
+
settings: GroupSettings.create({
|
|
274
|
+
description: TranslatedString.create('Inschrijvingsgroep'),
|
|
275
|
+
}),
|
|
276
|
+
}),
|
|
277
|
+
});
|
|
278
|
+
body.addPatch(newEvent);
|
|
279
|
+
|
|
280
|
+
const result = await TestRequest.patch({ body, user, organization });
|
|
281
|
+
expect(result.status).toBe(200);
|
|
282
|
+
expect(result.body).toHaveLength(1);
|
|
283
|
+
expect(result.body[0].group).toBeDefined();
|
|
284
|
+
expect(result.body[0].group!.settings.name).toEqual(TranslatedString.create('test event'));
|
|
285
|
+
expect(result.body[0].group!.settings.description).toEqual(TranslatedString.create('Inschrijvingsgroep'));
|
|
286
|
+
expect(result.body[0].group!.periodId).toEqual(period1.id);
|
|
287
|
+
expect(result.body[0].group!.organizationId).toEqual(organization.id);
|
|
288
|
+
expect(result.body[0].group!.type).toEqual(GroupType.EventRegistration);
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
test('Can create group for event in default period as non-full admin', async () => {
|
|
292
|
+
const organization = await new OrganizationFactory({}).create();
|
|
293
|
+
const user = await new UserFactory({
|
|
294
|
+
organization,
|
|
295
|
+
permissions: minimumUserPermissions,
|
|
296
|
+
}).create();
|
|
297
|
+
|
|
298
|
+
const period1 = await new RegistrationPeriodFactory({
|
|
299
|
+
startDate: new Date('2000-01-01'),
|
|
300
|
+
endDate: new Date('2001-01-01'),
|
|
301
|
+
organization,
|
|
302
|
+
}).create();
|
|
303
|
+
|
|
304
|
+
await new OrganizationRegistrationPeriodFactory({
|
|
305
|
+
period: period1,
|
|
306
|
+
organization,
|
|
307
|
+
}).create();
|
|
308
|
+
|
|
309
|
+
const period2 = await new RegistrationPeriodFactory({
|
|
310
|
+
startDate: new Date('2001-01-01'),
|
|
311
|
+
endDate: new Date('2002-01-01'),
|
|
312
|
+
organization,
|
|
313
|
+
}).create();
|
|
314
|
+
|
|
315
|
+
await new OrganizationRegistrationPeriodFactory({
|
|
316
|
+
period: period2,
|
|
317
|
+
organization,
|
|
318
|
+
}).create();
|
|
319
|
+
|
|
320
|
+
organization.periodId = period2.id;
|
|
321
|
+
await organization.save();
|
|
322
|
+
|
|
323
|
+
const event = await new EventFactory({
|
|
324
|
+
organization,
|
|
325
|
+
name: 'test event',
|
|
326
|
+
startDate: new Date('2001-02-10'),
|
|
327
|
+
endDate: new Date('2001-02-12'),
|
|
328
|
+
}).create();
|
|
329
|
+
|
|
330
|
+
const body: Body = new PatchableArray();
|
|
331
|
+
const newEvent = Event.patch({
|
|
332
|
+
id: event.id,
|
|
333
|
+
group: Group.create({
|
|
334
|
+
settings: GroupSettings.create({
|
|
335
|
+
description: TranslatedString.create('Inschrijvingsgroep'),
|
|
336
|
+
}),
|
|
337
|
+
}),
|
|
338
|
+
});
|
|
339
|
+
body.addPatch(newEvent);
|
|
340
|
+
|
|
341
|
+
const result = await TestRequest.patch({ body, user, organization });
|
|
342
|
+
expect(result.status).toBe(200);
|
|
343
|
+
expect(result.body).toHaveLength(1);
|
|
344
|
+
expect(result.body[0].group).toBeDefined();
|
|
345
|
+
expect(result.body[0].group!.settings.name).toEqual(TranslatedString.create('test event'));
|
|
346
|
+
expect(result.body[0].group!.settings.description).toEqual(TranslatedString.create('Inschrijvingsgroep'));
|
|
347
|
+
expect(result.body[0].group!.periodId).toEqual(period2.id);
|
|
348
|
+
expect(result.body[0].group!.organizationId).toEqual(organization.id);
|
|
349
|
+
expect(result.body[0].group!.type).toEqual(GroupType.EventRegistration);
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
test('Changing an event startDate will move the registration period of the corresponding group', async () => {
|
|
353
|
+
const organization = await new OrganizationFactory({}).create();
|
|
354
|
+
const user = await new UserFactory({
|
|
355
|
+
organization,
|
|
356
|
+
permissions: Permissions.create({
|
|
357
|
+
level: PermissionLevel.Full,
|
|
358
|
+
}), // full permissions are required, otherwise you'll get a permission error
|
|
359
|
+
}).create();
|
|
360
|
+
|
|
361
|
+
const period1 = await new RegistrationPeriodFactory({
|
|
362
|
+
startDate: new Date('2000-01-01'),
|
|
363
|
+
endDate: new Date('2001-01-01'),
|
|
364
|
+
organization,
|
|
365
|
+
}).create();
|
|
366
|
+
|
|
367
|
+
await new OrganizationRegistrationPeriodFactory({
|
|
368
|
+
period: period1,
|
|
369
|
+
organization,
|
|
370
|
+
}).create();
|
|
371
|
+
|
|
372
|
+
const period2 = await new RegistrationPeriodFactory({
|
|
373
|
+
startDate: new Date('2001-01-01'),
|
|
374
|
+
endDate: new Date('2002-01-01'),
|
|
375
|
+
organization,
|
|
376
|
+
}).create();
|
|
377
|
+
|
|
378
|
+
await new OrganizationRegistrationPeriodFactory({
|
|
379
|
+
period: period2,
|
|
380
|
+
organization,
|
|
381
|
+
}).create();
|
|
382
|
+
|
|
383
|
+
const event = await new EventFactory({
|
|
384
|
+
organization,
|
|
385
|
+
name: 'test event',
|
|
386
|
+
startDate: new Date('2000-02-10'),
|
|
387
|
+
endDate: new Date('2000-02-12'),
|
|
388
|
+
}).create();
|
|
389
|
+
|
|
390
|
+
async function part1() {
|
|
391
|
+
const body: Body = new PatchableArray();
|
|
392
|
+
const newEvent = Event.patch({
|
|
393
|
+
id: event.id,
|
|
394
|
+
group: Group.create({
|
|
395
|
+
settings: GroupSettings.create({
|
|
396
|
+
description: TranslatedString.create('Inschrijvingsgroep'),
|
|
397
|
+
}),
|
|
398
|
+
}),
|
|
399
|
+
});
|
|
400
|
+
body.addPatch(newEvent);
|
|
401
|
+
|
|
402
|
+
const result = await TestRequest.patch({ body, user, organization });
|
|
403
|
+
expect(result.status).toBe(200);
|
|
404
|
+
expect(result.body).toHaveLength(1);
|
|
405
|
+
expect(result.body[0].group).toBeDefined();
|
|
406
|
+
expect(result.body[0].group!.settings.name).toEqual(TranslatedString.create('test event'));
|
|
407
|
+
expect(result.body[0].group!.settings.description).toEqual(TranslatedString.create('Inschrijvingsgroep'));
|
|
408
|
+
expect(result.body[0].group!.periodId).toEqual(period1.id);
|
|
409
|
+
expect(result.body[0].group!.organizationId).toEqual(organization.id);
|
|
410
|
+
expect(result.body[0].group!.type).toEqual(GroupType.EventRegistration);
|
|
411
|
+
}
|
|
412
|
+
await part1();
|
|
413
|
+
|
|
414
|
+
// Now alter start date
|
|
415
|
+
async function part2() {
|
|
416
|
+
const body: Body = new PatchableArray();
|
|
417
|
+
const newEvent = Event.patch({
|
|
418
|
+
id: event.id,
|
|
419
|
+
startDate: new Date('2001-02-10'),
|
|
420
|
+
endDate: new Date('2001-02-12'),
|
|
421
|
+
});
|
|
422
|
+
body.addPatch(newEvent);
|
|
423
|
+
|
|
424
|
+
const result = await TestRequest.patch({ body, user, organization });
|
|
425
|
+
expect(result.status).toBe(200);
|
|
426
|
+
expect(result.body).toHaveLength(1);
|
|
427
|
+
expect(result.body[0].group).toBeDefined();
|
|
428
|
+
expect(result.body[0].group!.periodId).toEqual(period2.id);
|
|
429
|
+
}
|
|
430
|
+
await part2();
|
|
431
|
+
});
|
|
432
|
+
|
|
171
433
|
test('A normal user with write access can create an event where the type has a maximum', async () => {
|
|
172
434
|
const organization = await new OrganizationFactory({}).create();
|
|
173
435
|
|
|
@@ -38,7 +38,7 @@ export class PatchEventsEndpoint extends Endpoint<Params, Query, Body, ResponseB
|
|
|
38
38
|
if (!period) {
|
|
39
39
|
throw new SimpleError({
|
|
40
40
|
code: 'invalid_period',
|
|
41
|
-
message: 'No period found for this start date',
|
|
41
|
+
message: 'No period found for this start date: ' + Formatter.dateIso(event.startDate),
|
|
42
42
|
human: Context.i18n.$t('5959a6a9-064a-413c-871f-c74a145ed569'),
|
|
43
43
|
field: 'startDate',
|
|
44
44
|
});
|
|
@@ -52,6 +52,10 @@ export class PatchEventsEndpoint extends Endpoint<Params, Query, Body, ResponseB
|
|
|
52
52
|
putGroup.organizationId = event.organizationId;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
if (!await Context.auth.canAccessGroupsInPeriod(period.id, putGroup.organizationId)) {
|
|
56
|
+
throw Context.auth.error($t(`0d47541d-abf2-45b5-a06c-3a85b7fa3994`));
|
|
57
|
+
}
|
|
58
|
+
|
|
55
59
|
if (!group) {
|
|
56
60
|
putGroup.type = GroupType.EventRegistration;
|
|
57
61
|
|
|
@@ -254,7 +258,7 @@ export class PatchEventsEndpoint extends Endpoint<Params, Query, Body, ResponseB
|
|
|
254
258
|
patch.group.type = GroupType.EventRegistration;
|
|
255
259
|
|
|
256
260
|
const period = await RegistrationPeriod.getByDate(event.startDate, event.organizationId);
|
|
257
|
-
group = await PatchOrganizationRegistrationPeriodsEndpoint.patchGroup(patch.group, period);
|
|
261
|
+
group = await PatchOrganizationRegistrationPeriodsEndpoint.patchGroup(patch.group, period, { isPatchingEvent: true });
|
|
258
262
|
}
|
|
259
263
|
else {
|
|
260
264
|
if (event.groupId === patch.group.id) {
|
|
@@ -276,10 +280,17 @@ export class PatchEventsEndpoint extends Endpoint<Params, Query, Body, ResponseB
|
|
|
276
280
|
if (patch.startDate || patch.endDate) {
|
|
277
281
|
// Correct period id if needed
|
|
278
282
|
const period = await RegistrationPeriod.getByDate(event.startDate, event.organizationId);
|
|
279
|
-
if (event.groupId) {
|
|
283
|
+
if (event.groupId && period) {
|
|
280
284
|
await AuditLogService.setContext({ source: AuditLogSource.System }, async () => {
|
|
281
285
|
if (event.groupId) {
|
|
282
|
-
group = await PatchOrganizationRegistrationPeriodsEndpoint.patchGroup(
|
|
286
|
+
group = await PatchOrganizationRegistrationPeriodsEndpoint.patchGroup(
|
|
287
|
+
GroupStruct.patch({
|
|
288
|
+
id: event.groupId,
|
|
289
|
+
periodId: period.id,
|
|
290
|
+
}),
|
|
291
|
+
period,
|
|
292
|
+
{ isPatchingEvent: true },
|
|
293
|
+
);
|
|
283
294
|
}
|
|
284
295
|
});
|
|
285
296
|
}
|
|
@@ -154,7 +154,7 @@ export class ExportToExcelEndpoint extends Endpoint<Params, Query, Body, Respons
|
|
|
154
154
|
await exportToExcel({
|
|
155
155
|
definitions: loader.sheets,
|
|
156
156
|
writer,
|
|
157
|
-
dataGenerator: fetchToAsyncIterator(request.filter, loader),
|
|
157
|
+
dataGenerator: fetchToAsyncIterator(request.filter, loader, { signFiles: true }),
|
|
158
158
|
filter: request.workbookFilter,
|
|
159
159
|
});
|
|
160
160
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Request } from '@simonbackx/simple-endpoints';
|
|
2
2
|
import { EmailMocker } from '@stamhoofd/email';
|
|
3
3
|
import { BalanceItemFactory, Group, GroupFactory, MemberFactory, MemberWithRegistrations, Organization, OrganizationFactory, OrganizationRegistrationPeriodFactory, Registration, RegistrationFactory, RegistrationPeriod, RegistrationPeriodFactory, Token, UserFactory } from '@stamhoofd/models';
|
|
4
|
-
import { BalanceItemCartItem, BalanceItemStatus, BalanceItemType, BooleanStatus, Company, GroupOption, GroupOptionMenu, IDRegisterCart, IDRegisterCheckout, IDRegisterItem, OrganizationPackages, PaymentCustomer, PaymentMethod, PermissionLevel, Permissions, PermissionsResourceType, ReduceablePrice, RegisterItemOption, ResourcePermissions, STPackageStatus, STPackageType, UserPermissions, Version } from '@stamhoofd/structures';
|
|
4
|
+
import { AccessRight, BalanceItemCartItem, BalanceItemStatus, BalanceItemType, BooleanStatus, Company, GroupOption, GroupOptionMenu, IDRegisterCart, IDRegisterCheckout, IDRegisterItem, OrganizationPackages, PaymentCustomer, PaymentMethod, PermissionLevel, Permissions, PermissionsResourceType, ReduceablePrice, RegisterItemOption, ResourcePermissions, STPackageStatus, STPackageType, UserPermissions, Version } from '@stamhoofd/structures';
|
|
5
5
|
import { STExpect, TestUtils } from '@stamhoofd/test-utils';
|
|
6
6
|
import { v4 as uuidv4 } from 'uuid';
|
|
7
7
|
import { testServer } from '../../../../tests/helpers/TestServer';
|
|
@@ -10,6 +10,8 @@ import { RegisterMembersEndpoint } from './RegisterMembersEndpoint';
|
|
|
10
10
|
import { assertBalances } from '../../../../tests/assertions/assertBalances';
|
|
11
11
|
import PersistentFile from 'formidable/PersistentFile';
|
|
12
12
|
import { PatchMap } from '@simonbackx/simple-encoding';
|
|
13
|
+
import { initAdmin, initPermissionRole } from '../../../../tests/init';
|
|
14
|
+
import { BalanceItemService } from '../../../services/BalanceItemService';
|
|
13
15
|
|
|
14
16
|
const baseUrl = `/v${Version}/members/register`;
|
|
15
17
|
|
|
@@ -1219,6 +1221,132 @@ describe('Endpoint.RegisterMembers', () => {
|
|
|
1219
1221
|
defaultLinkMembersToUser = false;
|
|
1220
1222
|
});
|
|
1221
1223
|
|
|
1224
|
+
test('[Regression] Pay balance between organizations requires only finance access rights', async () => {
|
|
1225
|
+
const { member, organization, token, user } = await initData();
|
|
1226
|
+
const role = await initPermissionRole({
|
|
1227
|
+
organization,
|
|
1228
|
+
accessRights: [AccessRight.OrganizationFinanceDirector],
|
|
1229
|
+
});
|
|
1230
|
+
const { admin, adminToken } = await initAdmin({
|
|
1231
|
+
organization,
|
|
1232
|
+
permissions: Permissions.create({
|
|
1233
|
+
level: PermissionLevel.None,
|
|
1234
|
+
roles: [role],
|
|
1235
|
+
}),
|
|
1236
|
+
});
|
|
1237
|
+
|
|
1238
|
+
const company = Company.create({});
|
|
1239
|
+
organization.meta.companies.push(company);
|
|
1240
|
+
await organization.save();
|
|
1241
|
+
|
|
1242
|
+
const otherOrganization = await new OrganizationFactory({ }).create();
|
|
1243
|
+
|
|
1244
|
+
const balanceItem1 = await new BalanceItemFactory({
|
|
1245
|
+
organizationId: otherOrganization.id,
|
|
1246
|
+
memberId: null,
|
|
1247
|
+
userId: null,
|
|
1248
|
+
payingOrganizationId: organization.id,
|
|
1249
|
+
type: BalanceItemType.PlatformMembership,
|
|
1250
|
+
amount: 10,
|
|
1251
|
+
unitPrice: 2,
|
|
1252
|
+
}).create();
|
|
1253
|
+
|
|
1254
|
+
const cartItem = BalanceItemCartItem.create({
|
|
1255
|
+
item: balanceItem1.getStructure(),
|
|
1256
|
+
price: 20,
|
|
1257
|
+
});
|
|
1258
|
+
|
|
1259
|
+
const body = IDRegisterCheckout.create({
|
|
1260
|
+
cart: IDRegisterCart.create({
|
|
1261
|
+
items: [],
|
|
1262
|
+
balanceItems: [
|
|
1263
|
+
cartItem,
|
|
1264
|
+
],
|
|
1265
|
+
deleteRegistrationIds: [],
|
|
1266
|
+
}),
|
|
1267
|
+
administrationFee: 0,
|
|
1268
|
+
freeContribution: 0,
|
|
1269
|
+
paymentMethod: PaymentMethod.PointOfSale,
|
|
1270
|
+
totalPrice: 20,
|
|
1271
|
+
asOrganizationId: organization.id,
|
|
1272
|
+
customer: PaymentCustomer.create({
|
|
1273
|
+
firstName: user.firstName,
|
|
1274
|
+
lastName: user.lastName,
|
|
1275
|
+
email: user.email,
|
|
1276
|
+
company,
|
|
1277
|
+
}),
|
|
1278
|
+
});
|
|
1279
|
+
|
|
1280
|
+
const response = await post(body, otherOrganization, adminToken);
|
|
1281
|
+
expect(response.body.registrations.length).toBe(0);
|
|
1282
|
+
|
|
1283
|
+
await BalanceItemService.flushAll();
|
|
1284
|
+
await balanceItem1.refresh();
|
|
1285
|
+
|
|
1286
|
+
expect(balanceItem1).toMatchObject({
|
|
1287
|
+
amount: 10,
|
|
1288
|
+
unitPrice: 2,
|
|
1289
|
+
priceOpen: 0,
|
|
1290
|
+
pricePending: 20,
|
|
1291
|
+
pricePaid: 0,
|
|
1292
|
+
});
|
|
1293
|
+
});
|
|
1294
|
+
|
|
1295
|
+
test('Pay balance between organizations not possible without finance access rights', async () => {
|
|
1296
|
+
const { member, user, organization, token } = await initData({
|
|
1297
|
+
permissionLevel: PermissionLevel.Write,
|
|
1298
|
+
});
|
|
1299
|
+
|
|
1300
|
+
const otherOrganization = await new OrganizationFactory({ }).create();
|
|
1301
|
+
|
|
1302
|
+
const company = Company.create({});
|
|
1303
|
+
organization.meta.companies.push(company);
|
|
1304
|
+
await organization.save();
|
|
1305
|
+
|
|
1306
|
+
const balanceItem1 = await new BalanceItemFactory({
|
|
1307
|
+
organizationId: otherOrganization.id,
|
|
1308
|
+
memberId: null,
|
|
1309
|
+
userId: null,
|
|
1310
|
+
payingOrganizationId: organization.id,
|
|
1311
|
+
type: BalanceItemType.PlatformMembership,
|
|
1312
|
+
amount: 10,
|
|
1313
|
+
unitPrice: 2,
|
|
1314
|
+
}).create();
|
|
1315
|
+
|
|
1316
|
+
const cartItem = BalanceItemCartItem.create({
|
|
1317
|
+
item: balanceItem1.getStructure(),
|
|
1318
|
+
price: 20,
|
|
1319
|
+
});
|
|
1320
|
+
|
|
1321
|
+
const body = IDRegisterCheckout.create({
|
|
1322
|
+
cart: IDRegisterCart.create({
|
|
1323
|
+
items: [],
|
|
1324
|
+
balanceItems: [
|
|
1325
|
+
cartItem,
|
|
1326
|
+
],
|
|
1327
|
+
deleteRegistrationIds: [],
|
|
1328
|
+
}),
|
|
1329
|
+
administrationFee: 0,
|
|
1330
|
+
freeContribution: 0,
|
|
1331
|
+
paymentMethod: PaymentMethod.PointOfSale,
|
|
1332
|
+
totalPrice: 20,
|
|
1333
|
+
asOrganizationId: organization.id,
|
|
1334
|
+
customer: PaymentCustomer.create({
|
|
1335
|
+
firstName: user.firstName,
|
|
1336
|
+
lastName: user.lastName,
|
|
1337
|
+
email: user.email,
|
|
1338
|
+
company,
|
|
1339
|
+
}),
|
|
1340
|
+
});
|
|
1341
|
+
|
|
1342
|
+
await expect(post(body, otherOrganization, token))
|
|
1343
|
+
.rejects
|
|
1344
|
+
.toThrow(STExpect.simpleError({
|
|
1345
|
+
code: 'forbidden',
|
|
1346
|
+
message: 'No permission to checkout as this organization for a different organization',
|
|
1347
|
+
}));
|
|
1348
|
+
});
|
|
1349
|
+
|
|
1222
1350
|
test('Should reuse recently deactivated registration', async () => {
|
|
1223
1351
|
const { organization, group, groupPrice, token, member } = await initData();
|
|
1224
1352
|
group.settings.allowRegistrationsByOrganization = true;
|
|
@@ -1262,7 +1390,7 @@ describe('Endpoint.RegisterMembers', () => {
|
|
|
1262
1390
|
expect(response.body.registrations[0].id).toEqual(firstRegistration.id);
|
|
1263
1391
|
});
|
|
1264
1392
|
|
|
1265
|
-
test('Cannot pay balances as organization', async () => {
|
|
1393
|
+
test('Cannot pay member balances as organization', async () => {
|
|
1266
1394
|
const { member, user, organization, token } = await initData();
|
|
1267
1395
|
|
|
1268
1396
|
const balanceItem1 = await new BalanceItemFactory({
|
|
@@ -85,11 +85,13 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
|
|
|
85
85
|
whoWillPayNow = 'nobody';
|
|
86
86
|
}
|
|
87
87
|
else if (request.body.asOrganizationId && request.body.asOrganizationId !== organization.id) {
|
|
88
|
-
|
|
88
|
+
// Paying balance to a different organization requires finance permissions
|
|
89
|
+
// we'll check later if you are also registering members, which requires full permissions
|
|
90
|
+
if (!await Context.auth.canManageFinances(request.body.asOrganizationId)) {
|
|
89
91
|
throw new SimpleError({
|
|
90
92
|
code: 'forbidden',
|
|
91
|
-
message: 'No permission to
|
|
92
|
-
human: $t(`
|
|
93
|
+
message: 'No permission to checkout as this organization for a different organization',
|
|
94
|
+
human: $t(`eff21f4c-2d50-4553-9eb9-8a9e399f4124`),
|
|
93
95
|
statusCode: 403,
|
|
94
96
|
});
|
|
95
97
|
}
|
|
@@ -207,6 +209,18 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
|
|
|
207
209
|
}
|
|
208
210
|
platformMembers.push(platformMember);
|
|
209
211
|
}
|
|
212
|
+
|
|
213
|
+
if (memberIds.length > 0 && request.body.asOrganizationId && request.body.asOrganizationId !== organization.id) {
|
|
214
|
+
// For registering members at a different organization, you need full permissions
|
|
215
|
+
if (!await Context.auth.hasFullAccess(request.body.asOrganizationId)) {
|
|
216
|
+
throw new SimpleError({
|
|
217
|
+
code: 'forbidden',
|
|
218
|
+
message: 'No permission to register as this organization for a different organization',
|
|
219
|
+
human: $t(`62fe6e39-f6b0-4836-b0f7-dc84d22a81e3`),
|
|
220
|
+
statusCode: 403,
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
}
|
|
210
224
|
}
|
|
211
225
|
else {
|
|
212
226
|
const blob = await AuthenticatedStructures.membersBlob(members, true);
|