@stamhoofd/backend 2.103.0 → 2.104.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stamhoofd/backend",
3
- "version": "2.103.0",
3
+ "version": "2.104.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.103.0",
49
- "@stamhoofd/backend-middleware": "2.103.0",
50
- "@stamhoofd/email": "2.103.0",
51
- "@stamhoofd/models": "2.103.0",
52
- "@stamhoofd/queues": "2.103.0",
53
- "@stamhoofd/sql": "2.103.0",
54
- "@stamhoofd/structures": "2.103.0",
55
- "@stamhoofd/utility": "2.103.0",
48
+ "@stamhoofd/backend-i18n": "2.104.0",
49
+ "@stamhoofd/backend-middleware": "2.104.0",
50
+ "@stamhoofd/email": "2.104.0",
51
+ "@stamhoofd/models": "2.104.0",
52
+ "@stamhoofd/queues": "2.104.0",
53
+ "@stamhoofd/sql": "2.104.0",
54
+ "@stamhoofd/structures": "2.104.0",
55
+ "@stamhoofd/utility": "2.104.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": "81c0e08f64f5c9a32a513cf6cfd2d48e3601f1a8"
73
+ "gitHead": "b0ab736ce6f13f9f663503ff0967caa8bb660723"
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(GroupStruct.patch({ id: event.groupId }), period);
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
- if (!await Context.auth.hasFullAccess(request.body.asOrganizationId)) {
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 register as this organization for a different organization',
92
- human: $t(`62fe6e39-f6b0-4836-b0f7-dc84d22a81e3`),
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);