@stamhoofd/backend 2.101.0 → 2.103.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/crons.ts +21 -26
- package/src/email-recipient-loaders/receivable-balances.ts +18 -11
- package/src/endpoints/global/events/PatchEventsEndpoint.ts +1 -0
- package/src/endpoints/global/members/helpers/validateGroupFilter.ts +12 -8
- package/src/endpoints/global/registration/GetRegistrationsEndpoint.ts +30 -7
- package/src/endpoints/global/registration/GetUserPayableBalanceEndpoint.ts +4 -4
- package/src/endpoints/global/registration/RegisterMembersEndpoint.test.ts +382 -31
- package/src/endpoints/global/registration/RegisterMembersEndpoint.ts +47 -23
- package/src/endpoints/organization/dashboard/billing/GetOrganizationPayableBalanceEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/receivable-balances/GetReceivableBalanceEndpoint.ts +60 -8
- package/src/endpoints/organization/dashboard/receivable-balances/GetReceivableBalancesEndpoint.ts +6 -1
- package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.ts +12 -9
- package/src/endpoints/organization/webshops/PlaceOrderEndpoint.ts +4 -2
- package/src/excel-loaders/receivable-balances.ts +2 -2
- package/src/helpers/AdminPermissionChecker.ts +68 -10
- package/src/helpers/AuthenticatedStructures.ts +18 -10
- package/src/helpers/MemberUserSyncer.ts +2 -2
- package/src/seeds/{1735577912-update-cached-outstanding-balance-from-items.ts → 1760702454-update-cached-outstanding-balance-from-items.ts} +2 -0
- package/src/services/BalanceItemService.ts +12 -0
- package/src/services/PaymentService.ts +3 -0
- package/src/sql-filters/base-registration-filter-compilers.ts +59 -0
- package/src/sql-filters/orders.ts +97 -1
- package/src/sql-filters/receivable-balances.ts +7 -1
- package/tests/e2e/bundle-discounts.test.ts +327 -1
- package/tests/helpers/PayconiqMocker.ts +22 -0
- package/tests/init/index.ts +1 -0
- package/tests/init/initAdmin.ts +2 -2
- package/tests/init/initPermissionRole.ts +12 -0
|
@@ -1,12 +1,15 @@
|
|
|
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, BalanceItemType, Company, GroupOption, GroupOptionMenu, IDRegisterCart, IDRegisterCheckout, IDRegisterItem, OrganizationPackages, PaymentCustomer, PaymentMethod, PermissionLevel, Permissions, ReduceablePrice, RegisterItemOption, STPackageStatus, STPackageType, UserPermissions, Version } from '@stamhoofd/structures';
|
|
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';
|
|
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';
|
|
8
8
|
import { initPayconiq } from '../../../../tests/init/initPayconiq';
|
|
9
9
|
import { RegisterMembersEndpoint } from './RegisterMembersEndpoint';
|
|
10
|
+
import { assertBalances } from '../../../../tests/assertions/assertBalances';
|
|
11
|
+
import PersistentFile from 'formidable/PersistentFile';
|
|
12
|
+
import { PatchMap } from '@simonbackx/simple-encoding';
|
|
10
13
|
|
|
11
14
|
const baseUrl = `/v${Version}/members/register`;
|
|
12
15
|
|
|
@@ -15,6 +18,7 @@ describe('Endpoint.RegisterMembers', () => {
|
|
|
15
18
|
const endpoint = new RegisterMembersEndpoint();
|
|
16
19
|
let period: RegistrationPeriod;
|
|
17
20
|
let defaultPermissionLevel = PermissionLevel.None;
|
|
21
|
+
let defaultLinkMembersToUser = true;
|
|
18
22
|
const post = async (body: IDRegisterCheckout, organization: Organization, token: Token) => {
|
|
19
23
|
const request = Request.buildJson('POST', baseUrl, organization.getApiHost(), body);
|
|
20
24
|
request.headers.authorization = 'Bearer ' + token.accessToken;
|
|
@@ -52,7 +56,7 @@ describe('Endpoint.RegisterMembers', () => {
|
|
|
52
56
|
return { organization, organizationRegistrationPeriod };
|
|
53
57
|
};
|
|
54
58
|
|
|
55
|
-
async function initData({ otherMemberAmount = 0, permissionLevel = defaultPermissionLevel }: { otherMemberAmount?: number; permissionLevel?: PermissionLevel } = {}) {
|
|
59
|
+
async function initData({ otherMemberAmount = 0, groupPermissionLevel = PermissionLevel.None, memberPermissionLevel = PermissionLevel.None, permissionLevel = defaultPermissionLevel, linkMembersToUser = defaultLinkMembersToUser }: { otherMemberAmount?: number; memberPermissionLevel?: PermissionLevel; groupPermissionLevel?: PermissionLevel; permissionLevel?: PermissionLevel; linkMembersToUser?: boolean } = {}) {
|
|
56
60
|
const { organization, organizationRegistrationPeriod } = await initOrganization(period);
|
|
57
61
|
|
|
58
62
|
const user = await new UserFactory({
|
|
@@ -67,23 +71,73 @@ describe('Endpoint.RegisterMembers', () => {
|
|
|
67
71
|
|
|
68
72
|
const token = await Token.createToken(user);
|
|
69
73
|
|
|
70
|
-
const member = await new MemberFactory({ organization, user })
|
|
74
|
+
const member = await new MemberFactory({ organization, user: linkMembersToUser ? user : undefined })
|
|
71
75
|
.create();
|
|
72
76
|
|
|
73
77
|
const otherMembers: MemberWithRegistrations[] = [];
|
|
74
78
|
|
|
75
79
|
for (let i = 0; i < otherMemberAmount; i++) {
|
|
76
|
-
otherMembers.push(await new MemberFactory({ organization, user })
|
|
80
|
+
otherMembers.push(await new MemberFactory({ organization, user: linkMembersToUser ? user : undefined })
|
|
77
81
|
.create());
|
|
78
82
|
}
|
|
79
83
|
|
|
84
|
+
if (!linkMembersToUser && (permissionLevel !== PermissionLevel.None || memberPermissionLevel !== null)) {
|
|
85
|
+
// Give write permission to the member by registering them for another group
|
|
86
|
+
const genericGroup = await new GroupFactory({
|
|
87
|
+
organization,
|
|
88
|
+
price: 0,
|
|
89
|
+
})
|
|
90
|
+
.create();
|
|
91
|
+
|
|
92
|
+
await new RegistrationFactory({
|
|
93
|
+
member,
|
|
94
|
+
group: genericGroup,
|
|
95
|
+
groupPrice: genericGroup.settings.prices[0],
|
|
96
|
+
}).create();
|
|
97
|
+
|
|
98
|
+
if (memberPermissionLevel !== PermissionLevel.None) {
|
|
99
|
+
// Grant permissions to this genericGroup
|
|
100
|
+
user.permissions = user.permissions ?? UserPermissions.create({});
|
|
101
|
+
let org = user.permissions.organizationPermissions.get(organization.id) ?? Permissions.create({});
|
|
102
|
+
const p = Permissions.patch({});
|
|
103
|
+
p.resources.set(PermissionsResourceType.Groups, new PatchMap([[
|
|
104
|
+
genericGroup.id, ResourcePermissions.patch({
|
|
105
|
+
level: memberPermissionLevel,
|
|
106
|
+
}),
|
|
107
|
+
]]));
|
|
108
|
+
org = org.patch(p);
|
|
109
|
+
user.permissions.organizationPermissions.set(organization.id, org);
|
|
110
|
+
|
|
111
|
+
// Save
|
|
112
|
+
await user.save();
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
80
116
|
const group = await new GroupFactory({
|
|
81
117
|
organization,
|
|
82
118
|
price: 25_00,
|
|
119
|
+
reducedPrice: 12_50,
|
|
83
120
|
stock: 500,
|
|
84
121
|
})
|
|
85
122
|
.create();
|
|
86
123
|
|
|
124
|
+
if (groupPermissionLevel !== PermissionLevel.None) {
|
|
125
|
+
// Grant permissions to this genericGroup
|
|
126
|
+
user.permissions = user.permissions ?? UserPermissions.create({});
|
|
127
|
+
let org = user.permissions.organizationPermissions.get(organization.id) ?? Permissions.create({});
|
|
128
|
+
const p = Permissions.patch({});
|
|
129
|
+
p.resources.set(PermissionsResourceType.Groups, new PatchMap([[
|
|
130
|
+
group.id, ResourcePermissions.patch({
|
|
131
|
+
level: groupPermissionLevel,
|
|
132
|
+
}),
|
|
133
|
+
]]));
|
|
134
|
+
org = org.patch(p);
|
|
135
|
+
user.permissions.organizationPermissions.set(organization.id, org);
|
|
136
|
+
|
|
137
|
+
// Save
|
|
138
|
+
await user.save();
|
|
139
|
+
}
|
|
140
|
+
|
|
87
141
|
const groupPrice = group.settings.prices[0];
|
|
88
142
|
|
|
89
143
|
return {
|
|
@@ -101,6 +155,7 @@ describe('Endpoint.RegisterMembers', () => {
|
|
|
101
155
|
describe('Register as member', () => {
|
|
102
156
|
beforeEach(() => {
|
|
103
157
|
defaultPermissionLevel = PermissionLevel.None;
|
|
158
|
+
defaultLinkMembersToUser = true;
|
|
104
159
|
});
|
|
105
160
|
|
|
106
161
|
test('Should fail if demo limit reached', async () => {
|
|
@@ -1161,6 +1216,7 @@ describe('Endpoint.RegisterMembers', () => {
|
|
|
1161
1216
|
describe('Register as organization', () => {
|
|
1162
1217
|
beforeEach(() => {
|
|
1163
1218
|
defaultPermissionLevel = PermissionLevel.Full;
|
|
1219
|
+
defaultLinkMembersToUser = false;
|
|
1164
1220
|
});
|
|
1165
1221
|
|
|
1166
1222
|
test('Should reuse recently deactivated registration', async () => {
|
|
@@ -1246,15 +1302,272 @@ describe('Endpoint.RegisterMembers', () => {
|
|
|
1246
1302
|
code: 'cannot_pay_balance_items',
|
|
1247
1303
|
}));
|
|
1248
1304
|
});
|
|
1305
|
+
|
|
1306
|
+
/**
|
|
1307
|
+
* Case: you have write permission - but no access to financial data.
|
|
1308
|
+
*/
|
|
1309
|
+
test('Registering members with financial support', async () => {
|
|
1310
|
+
const { member, user, groupPrice, group, organization, token } = await initData({
|
|
1311
|
+
permissionLevel: PermissionLevel.Write,
|
|
1312
|
+
});
|
|
1313
|
+
|
|
1314
|
+
// Set member financial data status
|
|
1315
|
+
member.details.requiresFinancialSupport = BooleanStatus.create({ value: true });
|
|
1316
|
+
await member.save();
|
|
1317
|
+
|
|
1318
|
+
const body = IDRegisterCheckout.create({
|
|
1319
|
+
cart: IDRegisterCart.create({
|
|
1320
|
+
items: [
|
|
1321
|
+
IDRegisterItem.create({
|
|
1322
|
+
id: uuidv4(),
|
|
1323
|
+
replaceRegistrationIds: [],
|
|
1324
|
+
options: [],
|
|
1325
|
+
groupPrice,
|
|
1326
|
+
organizationId: organization.id,
|
|
1327
|
+
groupId: group.id,
|
|
1328
|
+
memberId: member.id,
|
|
1329
|
+
}),
|
|
1330
|
+
],
|
|
1331
|
+
balanceItems: [
|
|
1332
|
+
],
|
|
1333
|
+
deleteRegistrationIds: [],
|
|
1334
|
+
}),
|
|
1335
|
+
administrationFee: 0,
|
|
1336
|
+
freeContribution: 0,
|
|
1337
|
+
paymentMethod: PaymentMethod.PointOfSale,
|
|
1338
|
+
totalPrice: 25_00, // This is wrong, but the admin should not know that since he/she does not know the actual financial status of the member
|
|
1339
|
+
asOrganizationId: organization.id,
|
|
1340
|
+
});
|
|
1341
|
+
|
|
1342
|
+
const response = await post(body, organization, token);
|
|
1343
|
+
expect(response.body.registrations.length).toBe(1);
|
|
1344
|
+
|
|
1345
|
+
// No balance information leaks
|
|
1346
|
+
expect(response.body.registrations[0].balances.length).toEqual(0);
|
|
1347
|
+
|
|
1348
|
+
// Check acual charged amount
|
|
1349
|
+
const registration = (await Registration.getByID(response.body.registrations[0].id))!;
|
|
1350
|
+
expect(registration).toBeDefined();
|
|
1351
|
+
expect(registration.discounts).toMatchMap(new Map([]));
|
|
1352
|
+
|
|
1353
|
+
// Check balance has been added
|
|
1354
|
+
await assertBalances({ member }, [
|
|
1355
|
+
{
|
|
1356
|
+
type: BalanceItemType.Registration,
|
|
1357
|
+
registrationId: registration.id,
|
|
1358
|
+
amount: 1,
|
|
1359
|
+
price: 12_50,
|
|
1360
|
+
status: BalanceItemStatus.Due,
|
|
1361
|
+
priceOpen: 12_50,
|
|
1362
|
+
pricePending: 0,
|
|
1363
|
+
},
|
|
1364
|
+
]);
|
|
1365
|
+
|
|
1366
|
+
// Check member in response doesn't include sensitive data
|
|
1367
|
+
const memberInResponse = response.body.members.members.find(m => m.id === member.id)!;
|
|
1368
|
+
expect(memberInResponse).toBeDefined();
|
|
1369
|
+
expect(memberInResponse.details.requiresFinancialSupport).toBe(null);
|
|
1370
|
+
const returnedRegistration = memberInResponse.registrations.find(r => r.id === registration.id)!;
|
|
1371
|
+
expect(returnedRegistration).toBeDefined();
|
|
1372
|
+
expect(returnedRegistration.balances.length).toBe(0);
|
|
1373
|
+
});
|
|
1374
|
+
|
|
1375
|
+
/**
|
|
1376
|
+
* Negative test for previous test
|
|
1377
|
+
*/
|
|
1378
|
+
test('Can register a member as full admin', async () => {
|
|
1379
|
+
const { member, groupPrice, group, organization, token } = await initData({});
|
|
1380
|
+
|
|
1381
|
+
const body = IDRegisterCheckout.create({
|
|
1382
|
+
cart: IDRegisterCart.create({
|
|
1383
|
+
items: [
|
|
1384
|
+
IDRegisterItem.create({
|
|
1385
|
+
id: uuidv4(),
|
|
1386
|
+
replaceRegistrationIds: [],
|
|
1387
|
+
options: [],
|
|
1388
|
+
groupPrice,
|
|
1389
|
+
organizationId: organization.id,
|
|
1390
|
+
groupId: group.id,
|
|
1391
|
+
memberId: member.id,
|
|
1392
|
+
}),
|
|
1393
|
+
],
|
|
1394
|
+
balanceItems: [
|
|
1395
|
+
],
|
|
1396
|
+
deleteRegistrationIds: [],
|
|
1397
|
+
}),
|
|
1398
|
+
administrationFee: 0,
|
|
1399
|
+
freeContribution: 0,
|
|
1400
|
+
paymentMethod: PaymentMethod.PointOfSale,
|
|
1401
|
+
totalPrice: 25_00,
|
|
1402
|
+
asOrganizationId: organization.id,
|
|
1403
|
+
});
|
|
1404
|
+
|
|
1405
|
+
const response = await post(body, organization, token);
|
|
1406
|
+
expect(response.body.registrations.length).toBe(1);
|
|
1407
|
+
|
|
1408
|
+
// Check acual charged amount
|
|
1409
|
+
const registration = (await Registration.getByID(response.body.registrations[0].id))!;
|
|
1410
|
+
expect(registration).toBeDefined();
|
|
1411
|
+
expect(registration.discounts).toMatchMap(new Map([]));
|
|
1412
|
+
|
|
1413
|
+
// Check balance has been added
|
|
1414
|
+
await assertBalances({ member }, [
|
|
1415
|
+
{
|
|
1416
|
+
type: BalanceItemType.Registration,
|
|
1417
|
+
registrationId: registration.id,
|
|
1418
|
+
amount: 1,
|
|
1419
|
+
price: 25_00,
|
|
1420
|
+
status: BalanceItemStatus.Due,
|
|
1421
|
+
priceOpen: 25_00,
|
|
1422
|
+
pricePending: 0,
|
|
1423
|
+
},
|
|
1424
|
+
]);
|
|
1425
|
+
|
|
1426
|
+
// Check member in response does include financial data
|
|
1427
|
+
const memberInResponse = response.body.members.members.find(m => m.id === member.id)!;
|
|
1428
|
+
expect(memberInResponse).toBeDefined();
|
|
1429
|
+
const returnedRegistration = memberInResponse.registrations.find(r => r.id === registration.id)!;
|
|
1430
|
+
expect(returnedRegistration).toBeDefined();
|
|
1431
|
+
expect(returnedRegistration.balances.length).not.toBe(0);
|
|
1432
|
+
});
|
|
1433
|
+
|
|
1434
|
+
test('Can register a member as admin with write permission to new group only', async () => {
|
|
1435
|
+
// read permission to existing member, write permission to new group you want to register the member in
|
|
1436
|
+
const { member, groupPrice, group, organization, token } = await initData({
|
|
1437
|
+
permissionLevel: PermissionLevel.None,
|
|
1438
|
+
groupPermissionLevel: PermissionLevel.Write,
|
|
1439
|
+
memberPermissionLevel: PermissionLevel.Read,
|
|
1440
|
+
});
|
|
1441
|
+
|
|
1442
|
+
const body = IDRegisterCheckout.create({
|
|
1443
|
+
cart: IDRegisterCart.create({
|
|
1444
|
+
items: [
|
|
1445
|
+
IDRegisterItem.create({
|
|
1446
|
+
id: uuidv4(),
|
|
1447
|
+
replaceRegistrationIds: [],
|
|
1448
|
+
options: [],
|
|
1449
|
+
groupPrice,
|
|
1450
|
+
organizationId: organization.id,
|
|
1451
|
+
groupId: group.id,
|
|
1452
|
+
memberId: member.id,
|
|
1453
|
+
}),
|
|
1454
|
+
],
|
|
1455
|
+
balanceItems: [
|
|
1456
|
+
],
|
|
1457
|
+
deleteRegistrationIds: [],
|
|
1458
|
+
}),
|
|
1459
|
+
administrationFee: 0,
|
|
1460
|
+
freeContribution: 0,
|
|
1461
|
+
paymentMethod: PaymentMethod.PointOfSale,
|
|
1462
|
+
totalPrice: 25_00,
|
|
1463
|
+
asOrganizationId: organization.id,
|
|
1464
|
+
});
|
|
1465
|
+
|
|
1466
|
+
const response = await post(body, organization, token);
|
|
1467
|
+
expect(response.body.registrations.length).toBe(1);
|
|
1468
|
+
|
|
1469
|
+
// No balance information leaks
|
|
1470
|
+
expect(response.body.registrations[0].balances.length).toEqual(0);
|
|
1471
|
+
|
|
1472
|
+
// Check acual charged amount
|
|
1473
|
+
const registration = (await Registration.getByID(response.body.registrations[0].id))!;
|
|
1474
|
+
expect(registration).toBeDefined();
|
|
1475
|
+
expect(registration.discounts).toMatchMap(new Map([]));
|
|
1476
|
+
|
|
1477
|
+
// Check balance has been added
|
|
1478
|
+
await assertBalances({ member }, [
|
|
1479
|
+
{
|
|
1480
|
+
type: BalanceItemType.Registration,
|
|
1481
|
+
registrationId: registration.id,
|
|
1482
|
+
amount: 1,
|
|
1483
|
+
price: 25_00,
|
|
1484
|
+
status: BalanceItemStatus.Due,
|
|
1485
|
+
priceOpen: 25_00,
|
|
1486
|
+
pricePending: 0,
|
|
1487
|
+
},
|
|
1488
|
+
]);
|
|
1489
|
+
});
|
|
1490
|
+
|
|
1491
|
+
test('Cannot register a member with only read permissions', async () => {
|
|
1492
|
+
const { member, groupPrice, group, organization, token } = await initData({
|
|
1493
|
+
permissionLevel: PermissionLevel.None,
|
|
1494
|
+
groupPermissionLevel: PermissionLevel.Read,
|
|
1495
|
+
memberPermissionLevel: PermissionLevel.Read,
|
|
1496
|
+
});
|
|
1497
|
+
|
|
1498
|
+
const body = IDRegisterCheckout.create({
|
|
1499
|
+
cart: IDRegisterCart.create({
|
|
1500
|
+
items: [
|
|
1501
|
+
IDRegisterItem.create({
|
|
1502
|
+
id: uuidv4(),
|
|
1503
|
+
replaceRegistrationIds: [],
|
|
1504
|
+
options: [],
|
|
1505
|
+
groupPrice,
|
|
1506
|
+
organizationId: organization.id,
|
|
1507
|
+
groupId: group.id,
|
|
1508
|
+
memberId: member.id,
|
|
1509
|
+
}),
|
|
1510
|
+
],
|
|
1511
|
+
balanceItems: [
|
|
1512
|
+
],
|
|
1513
|
+
deleteRegistrationIds: [],
|
|
1514
|
+
}),
|
|
1515
|
+
administrationFee: 0,
|
|
1516
|
+
freeContribution: 0,
|
|
1517
|
+
paymentMethod: PaymentMethod.PointOfSale,
|
|
1518
|
+
totalPrice: 25_00,
|
|
1519
|
+
asOrganizationId: organization.id,
|
|
1520
|
+
});
|
|
1521
|
+
|
|
1522
|
+
// send request and check occupancy
|
|
1523
|
+
await expect(async () => await post(body, organization, token)).rejects.toThrow('No permission to register in this group');
|
|
1524
|
+
});
|
|
1525
|
+
|
|
1526
|
+
test('Cannot register a member with only read permissions even when having full permissions to the specific member', async () => {
|
|
1527
|
+
const { member, groupPrice, group, organization, token } = await initData({
|
|
1528
|
+
// No global organization permissions
|
|
1529
|
+
permissionLevel: PermissionLevel.None,
|
|
1530
|
+
groupPermissionLevel: PermissionLevel.Read, // Only read access to the new group
|
|
1531
|
+
memberPermissionLevel: PermissionLevel.Full, // Full access to the member
|
|
1532
|
+
});
|
|
1533
|
+
|
|
1534
|
+
const body = IDRegisterCheckout.create({
|
|
1535
|
+
cart: IDRegisterCart.create({
|
|
1536
|
+
items: [
|
|
1537
|
+
IDRegisterItem.create({
|
|
1538
|
+
id: uuidv4(),
|
|
1539
|
+
replaceRegistrationIds: [],
|
|
1540
|
+
options: [],
|
|
1541
|
+
groupPrice,
|
|
1542
|
+
organizationId: organization.id,
|
|
1543
|
+
groupId: group.id,
|
|
1544
|
+
memberId: member.id,
|
|
1545
|
+
}),
|
|
1546
|
+
],
|
|
1547
|
+
balanceItems: [
|
|
1548
|
+
],
|
|
1549
|
+
deleteRegistrationIds: [],
|
|
1550
|
+
}),
|
|
1551
|
+
administrationFee: 0,
|
|
1552
|
+
freeContribution: 0,
|
|
1553
|
+
paymentMethod: PaymentMethod.PointOfSale,
|
|
1554
|
+
totalPrice: 25_00,
|
|
1555
|
+
asOrganizationId: organization.id,
|
|
1556
|
+
});
|
|
1557
|
+
|
|
1558
|
+
// send request and check occupancy
|
|
1559
|
+
await expect(async () => await post(body, organization, token)).rejects.toThrow('No permission to register in this group');
|
|
1560
|
+
});
|
|
1249
1561
|
});
|
|
1250
1562
|
|
|
1251
1563
|
describe('Register by other organization', () => {
|
|
1252
1564
|
beforeEach(() => {
|
|
1253
1565
|
defaultPermissionLevel = PermissionLevel.Full;
|
|
1566
|
+
defaultLinkMembersToUser = false;
|
|
1254
1567
|
});
|
|
1255
1568
|
|
|
1256
1569
|
async function initDualData(options?: Parameters<typeof initData>[0]) {
|
|
1257
|
-
const base = await initData(options);
|
|
1570
|
+
const base = await initData({ ...options, permissionLevel: PermissionLevel.None });
|
|
1258
1571
|
|
|
1259
1572
|
base.group.settings.allowRegistrationsByOrganization = true;
|
|
1260
1573
|
await base.group.save();
|
|
@@ -1270,6 +1583,18 @@ describe('Endpoint.RegisterMembers', () => {
|
|
|
1270
1583
|
});
|
|
1271
1584
|
await base.user.save();
|
|
1272
1585
|
|
|
1586
|
+
// Give the user permission to the original member
|
|
1587
|
+
const genericGroup = await new GroupFactory({
|
|
1588
|
+
organization: organization2,
|
|
1589
|
+
price: 0,
|
|
1590
|
+
}).create();
|
|
1591
|
+
|
|
1592
|
+
await new RegistrationFactory({
|
|
1593
|
+
member: base.member,
|
|
1594
|
+
group: genericGroup,
|
|
1595
|
+
groupPrice: genericGroup.settings.prices[0],
|
|
1596
|
+
}).create();
|
|
1597
|
+
|
|
1273
1598
|
return {
|
|
1274
1599
|
...base,
|
|
1275
1600
|
organization2,
|
|
@@ -1760,9 +2085,11 @@ describe('Endpoint.RegisterMembers', () => {
|
|
|
1760
2085
|
// #endregion
|
|
1761
2086
|
});
|
|
1762
2087
|
|
|
1763
|
-
test('Move registration should fail
|
|
1764
|
-
|
|
1765
|
-
|
|
2088
|
+
test('Move registration should fail via member portal (no asOrganizationId set)', async () => {
|
|
2089
|
+
const { organization, group: group1, groupPrice: groupPrice1, token, member } = await initData({
|
|
2090
|
+
permissionLevel: defaultPermissionLevel,
|
|
2091
|
+
linkMembersToUser: true,
|
|
2092
|
+
});
|
|
1766
2093
|
|
|
1767
2094
|
const registration = await new RegistrationFactory({
|
|
1768
2095
|
member,
|
|
@@ -1801,14 +2128,9 @@ describe('Endpoint.RegisterMembers', () => {
|
|
|
1801
2128
|
totalPrice: 5,
|
|
1802
2129
|
customer: null,
|
|
1803
2130
|
});
|
|
1804
|
-
// #endregion
|
|
1805
|
-
|
|
1806
|
-
// #region act and assert
|
|
1807
2131
|
|
|
1808
2132
|
// send request and check occupancy
|
|
1809
2133
|
await expect(async () => await post(body, organization, token)).rejects.toThrow('Not allowed to move registrations');
|
|
1810
|
-
|
|
1811
|
-
// #endregion
|
|
1812
2134
|
});
|
|
1813
2135
|
});
|
|
1814
2136
|
|
|
@@ -1885,8 +2207,10 @@ describe('Endpoint.RegisterMembers', () => {
|
|
|
1885
2207
|
});
|
|
1886
2208
|
|
|
1887
2209
|
test('Should throw error if deleting registrations as normal member', async () => {
|
|
1888
|
-
|
|
1889
|
-
|
|
2210
|
+
const { member, group: group1, groupPrice: groupPrice1, organization: organization1, token } = await initData({
|
|
2211
|
+
permissionLevel: PermissionLevel.Full,
|
|
2212
|
+
linkMembersToUser: true,
|
|
2213
|
+
});
|
|
1890
2214
|
await initPayconiq({ organization: organization1 });
|
|
1891
2215
|
|
|
1892
2216
|
const registration = await new RegistrationFactory({
|
|
@@ -1933,7 +2257,6 @@ describe('Endpoint.RegisterMembers', () => {
|
|
|
1933
2257
|
});
|
|
1934
2258
|
|
|
1935
2259
|
test('Should deactivate registration', async () => {
|
|
1936
|
-
// #region arrange
|
|
1937
2260
|
const { member, group: group1, groupPrice: groupPrice1, organization, token } = await initData();
|
|
1938
2261
|
|
|
1939
2262
|
const registration = await new RegistrationFactory({
|
|
@@ -1973,15 +2296,12 @@ describe('Endpoint.RegisterMembers', () => {
|
|
|
1973
2296
|
asOrganizationId: organization.id,
|
|
1974
2297
|
customer: null,
|
|
1975
2298
|
});
|
|
1976
|
-
// #endregion
|
|
1977
2299
|
|
|
1978
|
-
// #region act and assert
|
|
1979
2300
|
await post(body, organization, token);
|
|
1980
2301
|
|
|
1981
2302
|
const updatedRegistration = await Registration.getByID(registration.id);
|
|
1982
2303
|
expect(updatedRegistration).toBeDefined();
|
|
1983
2304
|
expect(updatedRegistration!.deactivatedAt).not.toBe(null);
|
|
1984
|
-
// #endregion
|
|
1985
2305
|
});
|
|
1986
2306
|
|
|
1987
2307
|
test('Should fail if invalid cancelation fee', async () => {
|
|
@@ -2059,9 +2379,10 @@ describe('Endpoint.RegisterMembers', () => {
|
|
|
2059
2379
|
}
|
|
2060
2380
|
});
|
|
2061
2381
|
|
|
2062
|
-
test('
|
|
2063
|
-
|
|
2064
|
-
|
|
2382
|
+
test('Cannot delete registrations via the member portal', async () => {
|
|
2383
|
+
const { member, group: group1, groupPrice: groupPrice1, organization, token } = await initData({
|
|
2384
|
+
linkMembersToUser: true,
|
|
2385
|
+
});
|
|
2065
2386
|
|
|
2066
2387
|
const registration = await new RegistrationFactory({
|
|
2067
2388
|
member,
|
|
@@ -2099,18 +2420,51 @@ describe('Endpoint.RegisterMembers', () => {
|
|
|
2099
2420
|
totalPrice: 5,
|
|
2100
2421
|
customer: null,
|
|
2101
2422
|
});
|
|
2102
|
-
// #endregion
|
|
2103
2423
|
|
|
2104
|
-
// #region act and assert
|
|
2105
2424
|
await expect(async () => await post(body, organization, token))
|
|
2106
2425
|
.rejects
|
|
2107
2426
|
.toThrow(new RegExp('Permission denied: you are not allowed to delete registrations'));
|
|
2108
|
-
// #endregion
|
|
2109
2427
|
});
|
|
2110
2428
|
|
|
2111
|
-
test('
|
|
2112
|
-
|
|
2113
|
-
|
|
2429
|
+
test('Cannot delete registrations as admin if no write permission to group', async () => {
|
|
2430
|
+
const { member, group: group1, groupPrice: groupPrice1, organization, token } = await initData({
|
|
2431
|
+
permissionLevel: PermissionLevel.None,
|
|
2432
|
+
groupPermissionLevel: PermissionLevel.Read,
|
|
2433
|
+
memberPermissionLevel: PermissionLevel.Full,
|
|
2434
|
+
linkMembersToUser: false,
|
|
2435
|
+
});
|
|
2436
|
+
|
|
2437
|
+
const registration = await new RegistrationFactory({
|
|
2438
|
+
member,
|
|
2439
|
+
group: group1,
|
|
2440
|
+
groupPrice: groupPrice1,
|
|
2441
|
+
}).create();
|
|
2442
|
+
|
|
2443
|
+
const body = IDRegisterCheckout.create({
|
|
2444
|
+
cart: IDRegisterCart.create({
|
|
2445
|
+
items: [],
|
|
2446
|
+
balanceItems: [],
|
|
2447
|
+
deleteRegistrationIds: [registration.id],
|
|
2448
|
+
}),
|
|
2449
|
+
administrationFee: 0,
|
|
2450
|
+
freeContribution: 0,
|
|
2451
|
+
paymentMethod: PaymentMethod.PointOfSale,
|
|
2452
|
+
totalPrice: 0,
|
|
2453
|
+
customer: null,
|
|
2454
|
+
asOrganizationId: organization.id,
|
|
2455
|
+
});
|
|
2456
|
+
|
|
2457
|
+
await expect(async () => await post(body, organization, token)).rejects.toThrow(/No permission to delete this registration/);
|
|
2458
|
+
});
|
|
2459
|
+
|
|
2460
|
+
/**
|
|
2461
|
+
* userManager does not allow unregistering your own members, even when you have some admin permissions and set asOrganizationId
|
|
2462
|
+
*/
|
|
2463
|
+
test('Cannot delete registrations as admin if no write permission to group but does have userManager permissions', async () => {
|
|
2464
|
+
const { member, group: group1, groupPrice: groupPrice1, organization, token } = await initData({
|
|
2465
|
+
permissionLevel: PermissionLevel.Read,
|
|
2466
|
+
linkMembersToUser: true,
|
|
2467
|
+
});
|
|
2114
2468
|
|
|
2115
2469
|
const registration = await new RegistrationFactory({
|
|
2116
2470
|
member,
|
|
@@ -2131,11 +2485,8 @@ describe('Endpoint.RegisterMembers', () => {
|
|
|
2131
2485
|
customer: null,
|
|
2132
2486
|
asOrganizationId: organization.id,
|
|
2133
2487
|
});
|
|
2134
|
-
// #endregion
|
|
2135
2488
|
|
|
2136
|
-
// #region act and assert
|
|
2137
2489
|
await expect(async () => await post(body, organization, token)).rejects.toThrow(/No permission to delete this registration/);
|
|
2138
|
-
// #endregion
|
|
2139
2490
|
});
|
|
2140
2491
|
|
|
2141
2492
|
test('Should fail if registration does not exist anymore', async () => {
|