@stamhoofd/backend 2.83.5 → 2.84.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.
Files changed (100) hide show
  1. package/index.ts +19 -4
  2. package/package.json +18 -14
  3. package/src/crons/amazon-ses.ts +26 -5
  4. package/src/crons/balance-emails.ts +18 -17
  5. package/src/email-recipient-loaders/registrations.ts +87 -0
  6. package/src/endpoints/global/addresses/SearchRegionsEndpoint.ts +5 -2
  7. package/src/endpoints/global/email/PatchEmailEndpoint.test.ts +40 -40
  8. package/src/endpoints/global/events/PatchEventNotificationsEndpoint.test.ts +28 -22
  9. package/src/endpoints/global/events/PatchEventsEndpoint.ts +81 -49
  10. package/src/endpoints/global/files/UploadFile.ts +11 -16
  11. package/src/endpoints/global/groups/GetGroupsEndpoint.test.ts +234 -0
  12. package/src/endpoints/global/groups/GetGroupsEndpoint.ts +117 -43
  13. package/src/endpoints/global/members/GetMembersEndpoint.test.ts +1054 -0
  14. package/src/endpoints/global/members/GetMembersEndpoint.ts +163 -141
  15. package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.test.ts +6 -6
  16. package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.ts +0 -16
  17. package/src/endpoints/global/members/helpers/validateGroupFilter.ts +73 -0
  18. package/src/endpoints/global/registration/GetPaymentRegistrations.ts +1 -2
  19. package/src/endpoints/global/registration/GetRegistrationsCountEndpoint.ts +43 -0
  20. package/src/endpoints/global/registration/GetRegistrationsEndpoint.test.ts +1016 -0
  21. package/src/endpoints/global/registration/GetRegistrationsEndpoint.ts +234 -0
  22. package/src/endpoints/global/registration/PatchUserMembersEndpoint.test.ts +5 -5
  23. package/src/endpoints/global/registration/RegisterMembersEndpoint.test.ts +474 -554
  24. package/src/endpoints/global/registration/RegisterMembersEndpoint.ts +191 -52
  25. package/src/endpoints/global/registration-periods/GetRegistrationPeriodsEndpoint.ts +107 -9
  26. package/src/endpoints/organization/dashboard/email-templates/GetEmailTemplatesEndpoint.test.ts +89 -0
  27. package/src/endpoints/organization/dashboard/email-templates/GetEmailTemplatesEndpoint.ts +9 -6
  28. package/src/endpoints/organization/dashboard/email-templates/PatchEmailTemplatesEndpoint.test.ts +88 -0
  29. package/src/endpoints/organization/dashboard/email-templates/PatchEmailTemplatesEndpoint.ts +0 -6
  30. package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.ts +10 -6
  31. package/src/endpoints/organization/dashboard/payments/GetMemberBalanceEndpoint.ts +10 -25
  32. package/src/endpoints/organization/dashboard/payments/PatchBalanceItemsEndpoint.ts +0 -5
  33. package/src/endpoints/organization/dashboard/payments/PatchPaymentsEndpoint.ts +0 -5
  34. package/src/endpoints/organization/dashboard/receivable-balances/GetReceivableBalanceEndpoint.ts +4 -0
  35. package/src/endpoints/organization/dashboard/receivable-balances/GetReceivableBalancesEndpoint.ts +1 -0
  36. package/src/endpoints/organization/dashboard/registration-periods/GetOrganizationRegistrationPeriodsEndpoint.test.ts +44 -19
  37. package/src/endpoints/organization/dashboard/registration-periods/GetOrganizationRegistrationPeriodsEndpoint.ts +140 -25
  38. package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.ts +40 -10
  39. package/src/endpoints/organization/dashboard/users/CreateApiUserEndpoint.test.ts +2 -2
  40. package/src/endpoints/organization/dashboard/users/PatchApiUserEndpoint.test.ts +2 -2
  41. package/src/endpoints/organization/dashboard/webshops/PatchWebshopEndpoint.ts +4 -1
  42. package/src/endpoints/organization/dashboard/webshops/PatchWebshopOrdersEndpoint.ts +2 -2
  43. package/src/endpoints/organization/webshops/PlaceOrderEndpoint.ts +2 -2
  44. package/src/excel-loaders/members.ts +233 -232
  45. package/src/excel-loaders/payments.ts +1 -1
  46. package/src/excel-loaders/receivable-balances.ts +1 -1
  47. package/src/excel-loaders/registrations.ts +153 -0
  48. package/src/helpers/AdminPermissionChecker.ts +65 -37
  49. package/src/helpers/AuthenticatedStructures.ts +43 -3
  50. package/src/helpers/Context.ts +29 -1
  51. package/src/helpers/GlobalHelper.ts +3 -1
  52. package/src/helpers/GroupedThrottledQueue.test.ts +219 -0
  53. package/src/helpers/GroupedThrottledQueue.ts +108 -0
  54. package/src/helpers/LimitedFilteredRequestHelper.ts +26 -1
  55. package/src/helpers/MemberCharger.ts +0 -5
  56. package/src/helpers/MembershipCharger.ts +3 -9
  57. package/src/helpers/OrganizationCharger.ts +0 -5
  58. package/src/helpers/ThrottledQueue.test.ts +194 -0
  59. package/src/helpers/ThrottledQueue.ts +145 -0
  60. package/src/helpers/XlsxTransformerColumnHelper.ts +44 -1
  61. package/src/middleware/ContextMiddleware.ts +1 -1
  62. package/src/seeds/1728928974-update-cached-outstanding-balance-from-items.ts +2 -1
  63. package/src/seeds/1735577912-update-cached-outstanding-balance-from-items.ts +2 -1
  64. package/src/services/BalanceItemPaymentService.ts +1 -33
  65. package/src/services/BalanceItemService.ts +167 -48
  66. package/src/services/FileSignService.ts +18 -13
  67. package/src/services/MemberRecordStore.ts +28 -19
  68. package/src/services/PaymentReallocationService.test.ts +25 -14
  69. package/src/services/PaymentReallocationService.ts +29 -10
  70. package/src/services/PaymentService.ts +4 -16
  71. package/src/services/PlatformMembershipService.ts +8 -4
  72. package/src/services/RegistrationService.ts +66 -2
  73. package/src/sql-filters/base-registration-filter-compilers.ts +43 -0
  74. package/src/sql-filters/groups.ts +67 -0
  75. package/src/sql-filters/members.ts +33 -58
  76. package/src/sql-filters/organization-registration-periods.ts +8 -0
  77. package/src/sql-filters/registration-periods.ts +8 -0
  78. package/src/sql-filters/registrations.ts +11 -22
  79. package/src/sql-sorters/groups.ts +24 -0
  80. package/src/sql-sorters/organization-registration-periods.ts +24 -0
  81. package/src/sql-sorters/registration-periods.ts +47 -0
  82. package/src/sql-sorters/registrations.ts +77 -0
  83. package/tests/actions/patchOrganizationMember.ts +27 -0
  84. package/tests/actions/patchPaymentStatus.ts +45 -0
  85. package/tests/actions/patchUserMember.ts +27 -0
  86. package/tests/assertions/assertBalances.ts +49 -0
  87. package/tests/e2e/api-rate-limits.test.ts +5 -5
  88. package/tests/e2e/bundle-discounts.test.ts +4060 -0
  89. package/tests/e2e/charge-members.test.ts +27 -24
  90. package/tests/e2e/documents.test.ts +398 -0
  91. package/tests/e2e/register.test.ts +292 -312
  92. package/tests/helpers/PayconiqMocker.ts +55 -0
  93. package/tests/init/index.ts +5 -0
  94. package/tests/init/initAdmin.ts +14 -0
  95. package/tests/init/initBundleDiscount.ts +47 -0
  96. package/tests/init/initPayconiq.ts +9 -0
  97. package/tests/init/initPlatformAdmin.ts +13 -0
  98. package/tests/init/initStripe.ts +21 -0
  99. package/tests/jest.setup.ts +29 -0
  100. package/src/seeds-temporary/1736266448-recall-balance-item-price-paid.ts +0 -70
@@ -1,10 +1,11 @@
1
1
  import { Request } from '@simonbackx/simple-endpoints';
2
- import { Email } from '@stamhoofd/email';
3
- import { BalanceItemFactory, Group, GroupFactory, MemberFactory, MemberWithRegistrations, Organization, OrganizationFactory, Registration, RegistrationFactory, RegistrationPeriod, RegistrationPeriodFactory, Token, UserFactory } from '@stamhoofd/models';
4
- import { BalanceItemCartItem, BalanceItemType, Company, GroupOption, GroupOptionMenu, IDRegisterCart, IDRegisterCheckout, IDRegisterItem, OrganizationPackages, PayconiqAccount, PaymentCustomer, PaymentMethod, PermissionLevel, Permissions, ReduceablePrice, RegisterItemOption, STPackageStatus, STPackageType, UserPermissions, Version } from '@stamhoofd/structures';
5
- import nock from 'nock';
2
+ import { EmailMocker } from '@stamhoofd/email';
3
+ import { BalanceItem, 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';
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
+ import { initPayconiq } from '../../../../tests/init/initPayconiq';
8
9
  import { RegisterMembersEndpoint } from './RegisterMembersEndpoint';
9
10
 
10
11
  const baseUrl = `/v${Version}/members/register`;
@@ -13,16 +14,12 @@ describe('Endpoint.RegisterMembers', () => {
13
14
  // #region global
14
15
  const endpoint = new RegisterMembersEndpoint();
15
16
  let period: RegistrationPeriod;
16
-
17
- // #region helpers
17
+ let defaultPermissionLevel = PermissionLevel.None;
18
18
  const post = async (body: IDRegisterCheckout, organization: Organization, token: Token) => {
19
19
  const request = Request.buildJson('POST', baseUrl, organization.getApiHost(), body);
20
20
  request.headers.authorization = 'Bearer ' + token.accessToken;
21
21
  return await testServer.test(endpoint, request);
22
22
  };
23
- // #endregion
24
-
25
- // #endregion
26
23
 
27
24
  beforeAll(async () => {
28
25
  const previousPeriod = await new RegistrationPeriodFactory({
@@ -32,28 +29,39 @@ describe('Endpoint.RegisterMembers', () => {
32
29
 
33
30
  period = await new RegistrationPeriodFactory({
34
31
  startDate: new Date(2023, 0, 1),
35
- endDate: new Date(2023, 11, 31),
32
+ endDate: new Date(2030, 11, 31),
36
33
  previousPeriodId: previousPeriod.id,
37
34
  }).create();
38
35
  });
39
36
 
37
+ beforeEach(async () => {
38
+ TestUtils.setEnvironment('userMode', 'platform');
39
+ });
40
+
40
41
  afterEach(() => {
41
42
  jest.restoreAllMocks();
43
+ jest.useRealTimers();
42
44
  });
43
45
 
44
46
  const initOrganization = async (registrationPeriod: RegistrationPeriod = period) => {
45
- return await new OrganizationFactory({ period: registrationPeriod })
47
+ const organization = await new OrganizationFactory({ period: registrationPeriod })
46
48
  .create();
49
+
50
+ const organizationRegistrationPeriod = await new OrganizationRegistrationPeriodFactory({ organization, period: registrationPeriod }).create();
51
+
52
+ return { organization, organizationRegistrationPeriod };
47
53
  };
48
54
 
49
- const initData = async ({ otherMemberAmount = 0, permissionLevel = PermissionLevel.Full }: { otherMemberAmount?: number; permissionLevel?: PermissionLevel } = {}) => {
50
- const organization = await initOrganization(period);
55
+ async function initData({ otherMemberAmount = 0, permissionLevel = defaultPermissionLevel }: { otherMemberAmount?: number; permissionLevel?: PermissionLevel } = {}) {
56
+ const { organization, organizationRegistrationPeriod } = await initOrganization(period);
51
57
 
52
58
  const user = await new UserFactory({
53
59
  organization,
54
- permissions: Permissions.create({
55
- level: permissionLevel,
56
- }),
60
+ permissions: permissionLevel !== PermissionLevel.None
61
+ ? Permissions.create({
62
+ level: permissionLevel,
63
+ })
64
+ : null,
57
65
  })
58
66
  .create();
59
67
 
@@ -71,8 +79,8 @@ describe('Endpoint.RegisterMembers', () => {
71
79
 
72
80
  const group = await new GroupFactory({
73
81
  organization,
74
- price: 25,
75
- stock: 5,
82
+ price: 25_00,
83
+ stock: 500,
76
84
  })
77
85
  .create();
78
86
 
@@ -80,6 +88,7 @@ describe('Endpoint.RegisterMembers', () => {
80
88
 
81
89
  return {
82
90
  organization,
91
+ organizationRegistrationPeriod,
83
92
  user,
84
93
  token,
85
94
  member,
@@ -87,60 +96,15 @@ describe('Endpoint.RegisterMembers', () => {
87
96
  group,
88
97
  groupPrice,
89
98
  };
90
- };
91
-
92
- describe('Register', () => {
93
- test('Should fail if cannot manage finances', async () => {
94
- // #region arrange
95
- const { member, group, groupPrice, organization, token } = await initData();
96
- const organization2 = await initOrganization();
97
-
98
- const registration = await new RegistrationFactory({
99
- member,
100
- group,
101
- groupPrice,
102
- }).create();
103
-
104
- const body = IDRegisterCheckout.create({
105
- cart: IDRegisterCart.create({
106
- items: [
107
- IDRegisterItem.create({
108
- id: uuidv4(),
109
- replaceRegistrationIds: [],
110
- options: [],
111
- groupPrice,
112
- organizationId: organization.id,
113
- groupId: group.id,
114
- memberId: member.id,
115
- }),
116
- ],
117
- balanceItems: [],
118
- deleteRegistrationIds: [registration.id],
119
- }),
120
- administrationFee: 0,
121
- freeContribution: 0,
122
- paymentMethod: PaymentMethod.PointOfSale,
123
- totalPrice: 5,
124
- asOrganizationId: organization2.id,
125
- customer: null,
126
- });
127
- // #endregion
99
+ }
128
100
 
129
- // #region act and assert
130
- await expect(async () => await post(body, organization, token))
131
- .rejects
132
- .toThrow('No permission to register as this organization for a different organization');
133
- // #endregion
101
+ describe('Register as member', () => {
102
+ beforeEach(() => {
103
+ defaultPermissionLevel = PermissionLevel.None;
134
104
  });
135
105
 
136
106
  test('Should fail if demo limit reached', async () => {
137
- // #region arrange
138
- (STAMHOOFD.userMode as string) = 'organization';
139
-
140
- const spySendWebmaster = jest.spyOn(Email, 'sendWebmaster').mockImplementation(() => {
141
- // do nothing
142
- });
143
-
107
+ TestUtils.setEnvironment('userMode', 'organization');
144
108
  const { member, group, groupPrice, organization, token, otherMembers } = await initData({ otherMemberAmount: 10 });
145
109
 
146
110
  organization.meta.packages = OrganizationPackages.create({
@@ -171,8 +135,7 @@ describe('Endpoint.RegisterMembers', () => {
171
135
  administrationFee: 0,
172
136
  freeContribution: 0,
173
137
  paymentMethod: PaymentMethod.PointOfSale,
174
- totalPrice: 25,
175
- asOrganizationId: organization.id,
138
+ totalPrice: 25_00,
176
139
  customer: null,
177
140
  });
178
141
 
@@ -198,27 +161,24 @@ describe('Endpoint.RegisterMembers', () => {
198
161
  administrationFee: 0,
199
162
  freeContribution: 0,
200
163
  paymentMethod: PaymentMethod.PointOfSale,
201
- totalPrice: 25,
202
- asOrganizationId: organization.id,
164
+ totalPrice: 25_00,
203
165
  customer: null,
204
166
  });
205
- // #endregion
206
-
207
- // #region act and assert
208
-
209
- await expect(async () => await post(body, organization, token))
167
+ await expect(post(body, organization, token))
210
168
  .rejects
211
- .toThrow('Too many e-mails limited');
212
-
213
- expect(spySendWebmaster).toHaveBeenCalledOnce();
214
- // #endregion
169
+ .toThrow(STExpect.simpleError({ code: 'too_many_emails_period' }));
215
170
 
216
- (STAMHOOFD.userMode as string) = 'platform';
171
+ expect(await EmailMocker.transactional.getSucceededEmails()).toEqual([
172
+ expect.objectContaining({
173
+ to: '"Stamhoofd" <hallo@stamhoofd.be>',
174
+ from: '"Ravot" <webmaster@stamhoofd.be>',
175
+ subject: '[Limiet] Limiet bereikt voor aantal inschrijvingen',
176
+ }),
177
+ ]);
217
178
  });
218
179
 
219
- test('Should fail if balance items changed', async () => {
220
- // #region arrange
221
- const { member, group, user, groupPrice, organization, token } = await initData();
180
+ test('Should fail if balance item deleted', async () => {
181
+ const { member, user, organization, token } = await initData();
222
182
 
223
183
  const balanceItem1 = await new BalanceItemFactory({
224
184
  organizationId: organization.id,
@@ -237,17 +197,7 @@ describe('Endpoint.RegisterMembers', () => {
237
197
 
238
198
  const body = IDRegisterCheckout.create({
239
199
  cart: IDRegisterCart.create({
240
- items: [
241
- IDRegisterItem.create({
242
- id: uuidv4(),
243
- replaceRegistrationIds: [],
244
- options: [],
245
- groupPrice,
246
- organizationId: organization.id,
247
- groupId: group.id,
248
- memberId: member.id,
249
- }),
250
- ],
200
+ items: [],
251
201
  balanceItems: [
252
202
  cartItem,
253
203
  ],
@@ -256,23 +206,19 @@ describe('Endpoint.RegisterMembers', () => {
256
206
  administrationFee: 0,
257
207
  freeContribution: 0,
258
208
  paymentMethod: PaymentMethod.PointOfSale,
259
- totalPrice: 45,
209
+ totalPrice: 20,
260
210
  customer: null,
261
211
  });
262
- // #endregion
263
212
 
264
- // #region act and assert
265
213
  await balanceItem1.delete();
266
214
 
267
215
  await expect(async () => await post(body, organization, token))
268
216
  .rejects
269
217
  .toThrow(new RegExp('Oeps, één of meerdere openstaande bedragen in jouw winkelmandje zijn aangepast'));
270
- // #endregion
271
218
  });
272
219
 
273
- test('Should fail when pay balance item as organization', async () => {
274
- // #region arrange
275
- const { member, group, user, groupPrice, organization, token } = await initData();
220
+ test('Should fail if balance item price difference', async () => {
221
+ const { member, user, organization, token } = await initData();
276
222
 
277
223
  const balanceItem1 = await new BalanceItemFactory({
278
224
  organizationId: organization.id,
@@ -286,22 +232,12 @@ describe('Endpoint.RegisterMembers', () => {
286
232
 
287
233
  const cartItem = BalanceItemCartItem.create({
288
234
  item: balanceItem1.getStructure(),
289
- price: 20,
235
+ price: 30, // too much
290
236
  });
291
237
 
292
238
  const body = IDRegisterCheckout.create({
293
239
  cart: IDRegisterCart.create({
294
- items: [
295
- IDRegisterItem.create({
296
- id: uuidv4(),
297
- replaceRegistrationIds: [],
298
- options: [],
299
- groupPrice,
300
- organizationId: organization.id,
301
- groupId: group.id,
302
- memberId: member.id,
303
- }),
304
- ],
240
+ items: [],
305
241
  balanceItems: [
306
242
  cartItem,
307
243
  ],
@@ -310,17 +246,13 @@ describe('Endpoint.RegisterMembers', () => {
310
246
  administrationFee: 0,
311
247
  freeContribution: 0,
312
248
  paymentMethod: PaymentMethod.PointOfSale,
313
- totalPrice: 45,
314
- asOrganizationId: organization.id,
249
+ totalPrice: 30,
315
250
  customer: null,
316
251
  });
317
- // #endregion
318
252
 
319
- // #region act and assert
320
- await expect(async () => await post(body, organization, token))
253
+ await expect(post(body, organization, token))
321
254
  .rejects
322
- .toThrow(new RegExp('Not possible to pay balance items as the organization'));
323
- // #endregion
255
+ .toThrow(STExpect.simpleError({ code: 'changed_price' }));
324
256
  });
325
257
 
326
258
  test('Should fail if has no write access for member', async () => {
@@ -348,7 +280,7 @@ describe('Endpoint.RegisterMembers', () => {
348
280
  administrationFee: 0,
349
281
  freeContribution: 0,
350
282
  paymentMethod: PaymentMethod.PointOfSale,
351
- totalPrice: 25,
283
+ totalPrice: 25_00,
352
284
  customer: null,
353
285
  });
354
286
  // #endregion
@@ -356,7 +288,7 @@ describe('Endpoint.RegisterMembers', () => {
356
288
  // #region act and assert
357
289
  await expect(async () => await post(body, organization, token))
358
290
  .rejects
359
- .toThrow(new RegExp('No permission to register this member'));
291
+ .toThrow(new RegExp('Member not found'));
360
292
  // #endregion
361
293
  });
362
294
 
@@ -400,7 +332,6 @@ describe('Endpoint.RegisterMembers', () => {
400
332
  });
401
333
 
402
334
  test('Should fail if price changed', async () => {
403
- // #region arrange
404
335
  const { member, group, groupPrice, organization, token } = await initData();
405
336
 
406
337
  const body = IDRegisterCheckout.create({
@@ -421,22 +352,16 @@ describe('Endpoint.RegisterMembers', () => {
421
352
  administrationFee: 0,
422
353
  freeContribution: 0,
423
354
  paymentMethod: PaymentMethod.PointOfSale,
424
- totalPrice: 30,
425
- asOrganizationId: organization.id,
355
+ totalPrice: groupPrice.price.price + 5, // too much
426
356
  customer: null,
427
357
  });
428
- // #endregion
429
-
430
- // #region act and assert
431
358
 
432
359
  await expect(async () => await post(body, organization, token))
433
360
  .rejects
434
- .toThrow(new RegExp('Oeps! De prijs is gewijzigd terwijl je aan het afrekenen was'));
435
- // #endregion
361
+ .toThrow(STExpect.simpleError({ code: 'changed_price' }));
436
362
  });
437
363
 
438
364
  test('Should fail if member is already registered', async () => {
439
- // #region arrange
440
365
  const { organization, group, groupPrice, token, member } = await initData();
441
366
 
442
367
  const body = IDRegisterCheckout.create({
@@ -459,24 +384,20 @@ describe('Endpoint.RegisterMembers', () => {
459
384
  administrationFee: 0,
460
385
  freeContribution: 0,
461
386
  paymentMethod: PaymentMethod.PointOfSale,
462
- totalPrice: 25,
387
+ totalPrice: 25_00,
463
388
  customer: null,
464
389
  });
465
- // #endregion
466
390
 
467
- // #region act and assert
468
391
  // register first time
469
392
  await post(body, organization, token);
470
393
 
471
394
  // second time should fail
472
395
  await expect(async () => await post(body, organization, token))
473
396
  .rejects
474
- .toThrow(new RegExp('Already registered'));
475
- // #endregion
397
+ .toThrow(STExpect.simpleError({ code: 'already_registered' }));
476
398
  });
477
399
 
478
400
  test('Should fail if duplicate registration in cart', async () => {
479
- // #region arrange
480
401
  const { organization, group, groupPrice, token, member } = await initData();
481
402
 
482
403
  const body = IDRegisterCheckout.create({
@@ -501,9 +422,6 @@ describe('Endpoint.RegisterMembers', () => {
501
422
  memberId: member.id,
502
423
  }),
503
424
  ],
504
- balanceItems: [
505
- ],
506
- deleteRegistrationIds: [],
507
425
  }),
508
426
  administrationFee: 0,
509
427
  freeContribution: 0,
@@ -511,30 +429,18 @@ describe('Endpoint.RegisterMembers', () => {
511
429
  totalPrice: 50,
512
430
  customer: null,
513
431
  });
514
- // #endregion
515
432
 
516
- // #region act and assert
517
433
  await expect(async () => await post(body, organization, token))
518
434
  .rejects
519
- .toThrow(new RegExp('duplicate_register_item'));
520
- // #endregion
435
+ .toThrow(STExpect.simpleErrors([
436
+ { code: 'duplicate_register_item' },
437
+ { code: 'duplicate_register_item' },
438
+ ]));
521
439
  });
522
440
 
523
- test('Should fail register by other organization if disabled by group', async () => {
441
+ test('Should fail if invalid payment', async () => {
524
442
  // #region arrange
525
- const { organization, group, groupPrice, token, member, user } = await initData();
526
-
527
- const { organization: organization2 } = await initData();
528
-
529
- user.permissions = UserPermissions.create({
530
- organizationPermissions: new Map([
531
- [organization2.id, Permissions.create({
532
- level: PermissionLevel.Full,
533
- })],
534
- ]),
535
- });
536
-
537
- await user.save();
443
+ const { organization, group, groupPrice, token, member } = await initData();
538
444
 
539
445
  const body = IDRegisterCheckout.create({
540
446
  cart: IDRegisterCart.create({
@@ -555,23 +461,24 @@ describe('Endpoint.RegisterMembers', () => {
555
461
  }),
556
462
  administrationFee: 0,
557
463
  freeContribution: 0,
558
- paymentMethod: PaymentMethod.PointOfSale,
559
- totalPrice: 25,
464
+ paymentMethod: PaymentMethod.CreditCard,
465
+ totalPrice: 25_00,
560
466
  customer: null,
561
- asOrganizationId: organization2.id,
562
467
  });
563
468
  // #endregion
564
469
 
565
470
  // #region act and assert
566
471
  await expect(async () => await post(body, organization, token))
567
472
  .rejects
568
- .toThrow(new RegExp('allowRegistrationsByOrganization disabled'));
473
+ .toThrow(new RegExp('Oeps, je hebt geen geldige betaalmethode geselecteerd'));
569
474
  // #endregion
570
475
  });
571
476
 
572
- test('Should fail if invalid payment', async () => {
477
+ test('Should fail if no redirect url for online payment', async () => {
573
478
  // #region arrange
574
479
  const { organization, group, groupPrice, token, member } = await initData();
480
+ organization.meta.registrationPaymentConfiguration.paymentMethods.push(PaymentMethod.Bancontact);
481
+ await organization.save();
575
482
 
576
483
  const body = IDRegisterCheckout.create({
577
484
  cart: IDRegisterCart.create({
@@ -592,8 +499,9 @@ describe('Endpoint.RegisterMembers', () => {
592
499
  }),
593
500
  administrationFee: 0,
594
501
  freeContribution: 0,
595
- paymentMethod: PaymentMethod.CreditCard,
596
- totalPrice: 25,
502
+ paymentMethod: PaymentMethod.Bancontact,
503
+ totalPrice: 25_00,
504
+ cancelUrl: new URL('https://www.stamhoofd.be'),
597
505
  customer: null,
598
506
  });
599
507
  // #endregion
@@ -601,12 +509,11 @@ describe('Endpoint.RegisterMembers', () => {
601
509
  // #region act and assert
602
510
  await expect(async () => await post(body, organization, token))
603
511
  .rejects
604
- .toThrow(new RegExp('Oeps, je hebt geen geldige betaalmethode geselecteerd'));
512
+ .toThrow(new RegExp('redirectUrl or cancelUrl is missing'));
605
513
  // #endregion
606
514
  });
607
515
 
608
- test('Should fail if no redirect url for online payment', async () => {
609
- // #region arrange
516
+ test('Should fail if no cancel url for online payment', async () => {
610
517
  const { organization, group, groupPrice, token, member } = await initData();
611
518
  organization.meta.registrationPaymentConfiguration.paymentMethods.push(PaymentMethod.Bancontact);
612
519
  await organization.save();
@@ -624,31 +531,24 @@ describe('Endpoint.RegisterMembers', () => {
624
531
  memberId: member.id,
625
532
  }),
626
533
  ],
627
- balanceItems: [
628
- ],
629
- deleteRegistrationIds: [],
630
534
  }),
631
535
  administrationFee: 0,
632
536
  freeContribution: 0,
633
537
  paymentMethod: PaymentMethod.Bancontact,
634
- totalPrice: 25,
635
- cancelUrl: new URL('https://www.stamhoofd.be'),
538
+ totalPrice: 25_00,
539
+ redirectUrl: new URL('https://www.stamhoofd.be'),
636
540
  customer: null,
637
541
  });
638
- // #endregion
639
542
 
640
- // #region act and assert
641
543
  await expect(async () => await post(body, organization, token))
642
544
  .rejects
643
545
  .toThrow(new RegExp('redirectUrl or cancelUrl is missing'));
644
- // #endregion
645
546
  });
646
547
 
647
- test('Should fail if no cancel url for online payment', async () => {
648
- // #region arrange
548
+ test('Should not reserve for point of sale payment method if group has max members', async () => {
649
549
  const { organization, group, groupPrice, token, member } = await initData();
650
- organization.meta.registrationPaymentConfiguration.paymentMethods.push(PaymentMethod.Bancontact);
651
- await organization.save();
550
+ group.settings.maxMembers = 5;
551
+ await group.save();
652
552
 
653
553
  const body = IDRegisterCheckout.create({
654
554
  cart: IDRegisterCart.create({
@@ -663,38 +563,39 @@ describe('Endpoint.RegisterMembers', () => {
663
563
  memberId: member.id,
664
564
  }),
665
565
  ],
666
- balanceItems: [
667
- ],
668
- deleteRegistrationIds: [],
669
566
  }),
670
567
  administrationFee: 0,
671
568
  freeContribution: 0,
672
- paymentMethod: PaymentMethod.Bancontact,
673
- totalPrice: 25,
674
- redirectUrl: new URL('https://www.stamhoofd.be'),
675
- customer: null,
569
+ paymentMethod: PaymentMethod.PointOfSale,
570
+ totalPrice: 25_00,
676
571
  });
677
- // #endregion
678
572
 
679
- // #region act and assert
680
- await expect(async () => await post(body, organization, token))
681
- .rejects
682
- .toThrow(new RegExp('redirectUrl or cancelUrl is missing'));
683
- // #endregion
573
+ const response = await post(body, organization, token);
574
+ expect(response.body.registrations.length).toBe(1);
575
+
576
+ const updatedRegistration = (await Registration.getByID(response.body.registrations[0].id))!;
577
+ expect(updatedRegistration.registeredAt).not.toBeNull();
578
+ expect(updatedRegistration.reservedUntil).toBeNull();
579
+
580
+ // Check if response is up-to-date (if it fails here, data storage is okay, but returned API response is not up-to-date)
581
+ expect(response.body.registrations[0].registeredAt).not.toBeNull();
582
+ expect(response.body.registrations[0].reservedUntil).toBeNull();
684
583
  });
685
584
 
686
- test('Should reserve if group has max members', async () => {
687
- // #region arrange
585
+ test('Should reuse recently deactivated registration if it has zero balance', async () => {
688
586
  const { organization, group, groupPrice, token, member } = await initData();
689
- group.settings.maxMembers = 5;
690
- await group.save();
587
+ const firstRegistration = await new RegistrationFactory({
588
+ member,
589
+ group: group,
590
+ groupPrice: group.settings.prices[0],
591
+ deactivatedAt: new Date(new Date().getTime() - 1 * 24 * 60 * 60 * 1000), // 1 day ago
592
+ }).create();
691
593
 
692
594
  const body = IDRegisterCheckout.create({
693
595
  cart: IDRegisterCart.create({
694
596
  items: [
695
597
  IDRegisterItem.create({
696
598
  id: uuidv4(),
697
- replaceRegistrationIds: [],
698
599
  options: [],
699
600
  groupPrice,
700
601
  organizationId: organization.id,
@@ -702,50 +603,26 @@ describe('Endpoint.RegisterMembers', () => {
702
603
  memberId: member.id,
703
604
  }),
704
605
  ],
705
- balanceItems: [
706
- ],
707
- deleteRegistrationIds: [],
708
606
  }),
709
607
  administrationFee: 0,
710
608
  freeContribution: 0,
711
609
  paymentMethod: PaymentMethod.PointOfSale,
712
- totalPrice: 25,
610
+ totalPrice: 25_00,
713
611
  });
714
- // #endregion
715
612
 
716
- // #region act and assert
717
613
  const response = await post(body, organization, token);
718
614
  expect(response.body.registrations.length).toBe(1);
719
- expect(response.body.registrations[0].reservedUntil).not.toBeNull();
720
- // #endregion
615
+ expect(response.body.registrations[0].id).toEqual(firstRegistration.id);
721
616
  });
722
617
 
723
- test('Should reuse existing registration', async () => {
724
- // #region arrange
725
- const { organization, group, groupPrice, token, member, user } = await initData();
726
- group.settings.allowRegistrationsByOrganization = true;
727
- await group.save();
728
-
729
- user.permissions = UserPermissions.create({
730
- organizationPermissions: new Map([
731
- [organization.id, Permissions.create({
732
- level: PermissionLevel.Full,
733
- })],
734
- ]),
735
- });
736
-
737
- await user.save();
738
-
739
- const group2 = await new GroupFactory({
740
- organization,
741
- price: 25,
742
- stock: 5,
743
- }).create();
618
+ test('Should not reuse existing registration if it is older than 7 days', async () => {
619
+ const { organization, group, groupPrice, token, member } = await initData();
744
620
 
745
621
  const firstRegistration = await new RegistrationFactory({
746
622
  member,
747
- group: group2,
748
- groupPrice: group2.settings.prices[0],
623
+ group: group,
624
+ groupPrice: group.settings.prices[0],
625
+ deactivatedAt: new Date(new Date().getTime() - 8 * 24 * 60 * 60 * 1000), // 8 days ago
749
626
  }).create();
750
627
 
751
628
  const body = IDRegisterCheckout.create({
@@ -753,7 +630,6 @@ describe('Endpoint.RegisterMembers', () => {
753
630
  items: [
754
631
  IDRegisterItem.create({
755
632
  id: uuidv4(),
756
- replaceRegistrationIds: [firstRegistration.id],
757
633
  options: [],
758
634
  groupPrice,
759
635
  organizationId: organization.id,
@@ -761,57 +637,46 @@ describe('Endpoint.RegisterMembers', () => {
761
637
  memberId: member.id,
762
638
  }),
763
639
  ],
764
- balanceItems: [
765
- ],
766
- deleteRegistrationIds: [],
767
640
  }),
768
641
  administrationFee: 0,
769
642
  freeContribution: 0,
770
643
  paymentMethod: PaymentMethod.PointOfSale,
771
- totalPrice: 25,
772
- asOrganizationId: organization.id,
644
+ totalPrice: 25_00,
773
645
  });
774
646
 
775
- // #endregion
776
-
777
- // #region act and assert
778
647
  const response = await post(body, organization, token);
779
648
  expect(response.body.registrations.length).toBe(1);
780
- expect(response.body.registrations[0].id).toEqual(firstRegistration.id);
781
- // #endregion
649
+ expect(response.body.registrations[0].id).not.toEqual(firstRegistration.id);
782
650
  });
783
651
 
784
- test('Should reuse recently deactivated registration', async () => {
785
- // #region arrange
652
+ test('Should not reuse existing registration if has a non-zero balance', async () => {
786
653
  const { organization, group, groupPrice, token, member, user } = await initData();
787
- group.settings.allowRegistrationsByOrganization = true;
788
- await group.save();
789
-
790
- user.permissions = UserPermissions.create({
791
- organizationPermissions: new Map([
792
- [organization.id, Permissions.create({
793
- level: PermissionLevel.Full,
794
- })],
795
- ]),
796
- });
797
-
798
- await user.save();
799
-
800
654
  const firstRegistration = await new RegistrationFactory({
801
655
  member,
802
656
  group,
803
- groupPrice,
657
+ groupPrice: group.settings.prices[0],
658
+ deactivatedAt: new Date(new Date().getTime() - 1 * 24 * 60 * 60 * 1000), // 1 day ago
804
659
  }).create();
805
660
 
806
- firstRegistration.deactivatedAt = new Date();
807
- await firstRegistration.save();
661
+ // Create a balance item for the first registration
662
+ const item = await new BalanceItemFactory({
663
+ organizationId: organization.id,
664
+ memberId: member.id,
665
+ userId: user.id,
666
+ type: BalanceItemType.Registration,
667
+ amount: 1,
668
+ unitPrice: group.settings.prices[0].price.price,
669
+ registrationId: firstRegistration.id,
670
+ }).create();
671
+
672
+ // Update outstanding cache
673
+ // await BalanceItem.updateOutstanding([item]);
808
674
 
809
675
  const body = IDRegisterCheckout.create({
810
676
  cart: IDRegisterCart.create({
811
677
  items: [
812
678
  IDRegisterItem.create({
813
679
  id: uuidv4(),
814
- replaceRegistrationIds: [],
815
680
  options: [],
816
681
  groupPrice,
817
682
  organizationId: organization.id,
@@ -819,24 +684,16 @@ describe('Endpoint.RegisterMembers', () => {
819
684
  memberId: member.id,
820
685
  }),
821
686
  ],
822
- balanceItems: [
823
- ],
824
- deleteRegistrationIds: [],
825
687
  }),
826
688
  administrationFee: 0,
827
689
  freeContribution: 0,
828
690
  paymentMethod: PaymentMethod.PointOfSale,
829
- totalPrice: 25,
830
- asOrganizationId: organization.id,
691
+ totalPrice: 25_00,
831
692
  });
832
693
 
833
- // #endregion
834
-
835
- // #region act and assert
836
694
  const response = await post(body, organization, token);
837
695
  expect(response.body.registrations.length).toBe(1);
838
- expect(response.body.registrations[0].id).toEqual(firstRegistration.id);
839
- // #endregion
696
+ expect(response.body.registrations[0].id).not.toEqual(firstRegistration.id);
840
697
  });
841
698
 
842
699
  test('Should update registered members', async () => {
@@ -862,8 +719,7 @@ describe('Endpoint.RegisterMembers', () => {
862
719
  administrationFee: 0,
863
720
  freeContribution: 0,
864
721
  paymentMethod: PaymentMethod.PointOfSale,
865
- totalPrice: 25,
866
- asOrganizationId: organization.id,
722
+ totalPrice: 25_00,
867
723
  customer: null,
868
724
  });
869
725
  // #endregion
@@ -880,23 +736,9 @@ describe('Endpoint.RegisterMembers', () => {
880
736
  expect(updatedGroup!.settings.reservedMembers).toBe(0);
881
737
  });
882
738
 
883
- test('Should update reserved members', async () => {
884
- // #region arrange
739
+ test('Should set reserved members when using online payments', async () => {
885
740
  const { member, organization, token } = await initData();
886
-
887
- organization.meta.registrationPaymentConfiguration.paymentMethods = [PaymentMethod.PointOfSale, PaymentMethod.Payconiq];
888
-
889
- organization.privateMeta.payconiqAccounts = [PayconiqAccount.create({
890
- id: uuidv4(),
891
- apiKey: 'testKey',
892
- merchantId: 'test',
893
- profileId: 'test',
894
- name: 'test',
895
- iban: 'BE56587127952688', // = random IBAN
896
- callbackUrl: 'https://www.example.com',
897
- })];
898
-
899
- await organization.save();
741
+ await initPayconiq({ organization });
900
742
 
901
743
  const group2 = await new GroupFactory({
902
744
  organization,
@@ -912,54 +754,40 @@ describe('Endpoint.RegisterMembers', () => {
912
754
  items: [
913
755
  IDRegisterItem.create({
914
756
  id: uuidv4(),
915
- replaceRegistrationIds: [],
916
- options: [],
917
757
  groupPrice: groupPrice2,
918
758
  organizationId: organization.id,
919
759
  groupId: group2.id,
920
760
  memberId: member.id,
921
761
  }),
922
762
  ],
923
- balanceItems: [],
924
- deleteRegistrationIds: [],
925
763
  }),
926
- administrationFee: 0,
927
- freeContribution: 0,
928
764
  paymentMethod: PaymentMethod.Payconiq,
929
765
  redirectUrl: new URL('https://www.example.com'),
930
766
  cancelUrl: new URL('https://www.example.com'),
931
767
  totalPrice: 15,
932
- customer: null,
933
768
  });
934
769
 
935
- nock('https://api.ext.payconiq.com')
936
- .post('/v3/payments')
937
- .reply(200, {
938
- paymentId: 'testPaymentId',
939
- _links: {
940
- checkout: {
941
- href: 'https://www.example.com',
942
- },
943
- },
944
- });
945
- // #endregion
946
-
947
- // act
948
770
  const response = await post(body, organization, token);
949
771
 
950
- // assert
951
772
  expect(response.body).toBeDefined();
952
773
  expect(response.body.registrations.length).toBe(1);
774
+ expect(response.body.paymentUrl).toMatch(/payconiq\.com/);
775
+
776
+ expect(response.body.registrations[0]).toMatchObject({
777
+ registeredAt: null,
778
+ deactivatedAt: null,
779
+ reservedUntil: expect.any(Date),
780
+ });
953
781
 
954
- const updatedGroup = await Group.getByID(group2.id);
955
- expect(updatedGroup!.settings.registeredMembers).toBe(0);
956
- expect(updatedGroup!.settings.reservedMembers).toBe(1);
782
+ await group2.refresh();
783
+ expect(group2.settings.registeredMembers).toBe(0);
784
+ expect(group2.settings.reservedMembers).toBe(1);
957
785
  });
958
786
 
959
- test('Register for group with trial should set trail period', async () => {
787
+ test('Register for group with trial should set trial period', async () => {
960
788
  // #region arrange
961
789
  const date = new Date('2023-05-14');
962
- jest.useFakeTimers().setSystemTime(date);
790
+ jest.useFakeTimers({ advanceTimers: true, doNotFake: ['setTimeout', 'clearTimeout', 'hrtime', 'nextTick', 'performance', 'queueMicrotask', 'setImmediate', 'clearImmediate'] }).setSystemTime(date);
963
791
 
964
792
  try {
965
793
  const { member, group, groupPrice, organization, token } = await initData();
@@ -987,7 +815,6 @@ describe('Endpoint.RegisterMembers', () => {
987
815
  freeContribution: 0,
988
816
  paymentMethod: PaymentMethod.PointOfSale,
989
817
  totalPrice: 0,
990
- asOrganizationId: organization.id,
991
818
  customer: null,
992
819
  });
993
820
  // #endregion
@@ -1006,9 +833,9 @@ describe('Endpoint.RegisterMembers', () => {
1006
833
  expect(trialUntil!.getDate()).toBe(19);
1007
834
  }
1008
835
  finally {
1009
- jest.useFakeTimers().resetAllMocks();
836
+ jest.useRealTimers();
1010
837
  }
1011
- });
838
+ }, 20_000);
1012
839
 
1013
840
  test('Should update group stock reservations', async () => {
1014
841
  // #region arrange
@@ -1035,8 +862,7 @@ describe('Endpoint.RegisterMembers', () => {
1035
862
  administrationFee: 0,
1036
863
  freeContribution: 0,
1037
864
  paymentMethod: PaymentMethod.PointOfSale,
1038
- totalPrice: 25,
1039
- asOrganizationId: organization.id,
865
+ totalPrice: 25_00,
1040
866
  customer: null,
1041
867
  });
1042
868
  // #endregion
@@ -1178,7 +1004,6 @@ describe('Endpoint.RegisterMembers', () => {
1178
1004
  });
1179
1005
 
1180
1006
  test('Should fail if max option exceeded', async () => {
1181
- // #region arrange
1182
1007
  const { organization, group, groupPrice, token, member } = await initData();
1183
1008
 
1184
1009
  const option1 = GroupOption.create({
@@ -1187,8 +1012,8 @@ describe('Endpoint.RegisterMembers', () => {
1187
1012
  maximum: 5,
1188
1013
  allowAmount: true,
1189
1014
  price: ReduceablePrice.create({
1190
- price: 5,
1191
- reducedPrice: 3,
1015
+ price: 5_00,
1016
+ reducedPrice: 3_00,
1192
1017
  }),
1193
1018
  });
1194
1019
 
@@ -1198,8 +1023,8 @@ describe('Endpoint.RegisterMembers', () => {
1198
1023
  maximum: 2,
1199
1024
  allowAmount: true,
1200
1025
  price: ReduceablePrice.create({
1201
- price: 3,
1202
- reducedPrice: 1,
1026
+ price: 3_00,
1027
+ reducedPrice: 1_00,
1203
1028
  }),
1204
1029
  });
1205
1030
 
@@ -1213,7 +1038,252 @@ describe('Endpoint.RegisterMembers', () => {
1213
1038
  optionMenu,
1214
1039
  ];
1215
1040
 
1216
- await group.save();
1041
+ await group.save();
1042
+
1043
+ const body = IDRegisterCheckout.create({
1044
+ cart: IDRegisterCart.create({
1045
+ items: [
1046
+ IDRegisterItem.create({
1047
+ id: uuidv4(),
1048
+ replaceRegistrationIds: [],
1049
+ options: [
1050
+ RegisterItemOption.create({
1051
+ option: option1,
1052
+ amount: 2,
1053
+ optionMenu,
1054
+ }),
1055
+ RegisterItemOption.create({
1056
+ option: option2,
1057
+ amount: 5,
1058
+ optionMenu,
1059
+ }),
1060
+ ],
1061
+ groupPrice,
1062
+ organizationId: organization.id,
1063
+ groupId: group.id,
1064
+ memberId: member.id,
1065
+ }),
1066
+ ],
1067
+ balanceItems: [
1068
+ ],
1069
+ deleteRegistrationIds: [],
1070
+ }),
1071
+ administrationFee: 0,
1072
+ freeContribution: 0,
1073
+ paymentMethod: PaymentMethod.PointOfSale,
1074
+ totalPrice: 50_00,
1075
+ customer: null,
1076
+ });
1077
+
1078
+ await expect(async () => await post(body, organization, token))
1079
+ .rejects
1080
+ .toThrow(new RegExp('Option maximum exceeded'));
1081
+ });
1082
+
1083
+ test('Should not fail if max option not exceeded', async () => {
1084
+ const { organization, group, groupPrice, token, member } = await initData();
1085
+
1086
+ const option1 = GroupOption.create({
1087
+ name: 'option 1',
1088
+ stock: 4,
1089
+ maximum: 5,
1090
+ allowAmount: true,
1091
+ price: ReduceablePrice.create({
1092
+ price: 5_00,
1093
+ reducedPrice: 3_00,
1094
+ }),
1095
+ });
1096
+
1097
+ const option2 = GroupOption.create({
1098
+ name: 'option 2',
1099
+ stock: 5,
1100
+ maximum: 5,
1101
+ allowAmount: true,
1102
+ price: ReduceablePrice.create({
1103
+ price: 3_00,
1104
+ reducedPrice: 1_00,
1105
+ }),
1106
+ });
1107
+
1108
+ const optionMenu = GroupOptionMenu.create({
1109
+ name: 'option menu 1',
1110
+ multipleChoice: true,
1111
+ options: [option1, option2],
1112
+ });
1113
+
1114
+ group.settings.optionMenus = [
1115
+ optionMenu,
1116
+ ];
1117
+
1118
+ await group.save();
1119
+
1120
+ const body = IDRegisterCheckout.create({
1121
+ cart: IDRegisterCart.create({
1122
+ items: [
1123
+ IDRegisterItem.create({
1124
+ id: uuidv4(),
1125
+ replaceRegistrationIds: [],
1126
+ options: [
1127
+ RegisterItemOption.create({
1128
+ option: option1,
1129
+ amount: 2,
1130
+ optionMenu,
1131
+ }),
1132
+ RegisterItemOption.create({
1133
+ option: option2,
1134
+ amount: 5,
1135
+ optionMenu,
1136
+ }),
1137
+ ],
1138
+ groupPrice,
1139
+ organizationId: organization.id,
1140
+ groupId: group.id,
1141
+ memberId: member.id,
1142
+ }),
1143
+ ],
1144
+ balanceItems: [
1145
+ ],
1146
+ deleteRegistrationIds: [],
1147
+ }),
1148
+ administrationFee: 0,
1149
+ freeContribution: 0,
1150
+ paymentMethod: PaymentMethod.PointOfSale,
1151
+ totalPrice: 50_00,
1152
+ customer: null,
1153
+ });
1154
+
1155
+ const result = await post(body, organization, token);
1156
+ expect(result).toBeDefined();
1157
+ });
1158
+ });
1159
+
1160
+ describe('Register as organization', () => {
1161
+ beforeEach(() => {
1162
+ defaultPermissionLevel = PermissionLevel.Full;
1163
+ });
1164
+
1165
+ test('Should reuse recently deactivated registration', async () => {
1166
+ const { organization, group, groupPrice, token, member } = await initData();
1167
+ group.settings.allowRegistrationsByOrganization = true;
1168
+ await group.save();
1169
+
1170
+ const firstRegistration = await new RegistrationFactory({
1171
+ member,
1172
+ group,
1173
+ groupPrice,
1174
+ }).create();
1175
+
1176
+ firstRegistration.deactivatedAt = new Date();
1177
+ await firstRegistration.save();
1178
+
1179
+ const body = IDRegisterCheckout.create({
1180
+ cart: IDRegisterCart.create({
1181
+ items: [
1182
+ IDRegisterItem.create({
1183
+ id: uuidv4(),
1184
+ replaceRegistrationIds: [],
1185
+ options: [],
1186
+ groupPrice,
1187
+ organizationId: organization.id,
1188
+ groupId: group.id,
1189
+ memberId: member.id,
1190
+ }),
1191
+ ],
1192
+ balanceItems: [
1193
+ ],
1194
+ deleteRegistrationIds: [],
1195
+ }),
1196
+ administrationFee: 0,
1197
+ freeContribution: 0,
1198
+ paymentMethod: PaymentMethod.PointOfSale,
1199
+ totalPrice: 25_00,
1200
+ asOrganizationId: organization.id,
1201
+ });
1202
+
1203
+ const response = await post(body, organization, token);
1204
+ expect(response.body.registrations.length).toBe(1);
1205
+ expect(response.body.registrations[0].id).toEqual(firstRegistration.id);
1206
+ });
1207
+
1208
+ test('Cannot pay balances as organization', async () => {
1209
+ const { member, user, organization, token } = await initData();
1210
+
1211
+ const balanceItem1 = await new BalanceItemFactory({
1212
+ organizationId: organization.id,
1213
+ memberId: member.id,
1214
+ userId: user.id,
1215
+ payingOrganizationId: organization.id,
1216
+ type: BalanceItemType.Registration,
1217
+ amount: 10,
1218
+ unitPrice: 2,
1219
+ }).create();
1220
+
1221
+ const cartItem = BalanceItemCartItem.create({
1222
+ item: balanceItem1.getStructure(),
1223
+ price: 20,
1224
+ });
1225
+
1226
+ const body = IDRegisterCheckout.create({
1227
+ cart: IDRegisterCart.create({
1228
+ items: [],
1229
+ balanceItems: [
1230
+ cartItem,
1231
+ ],
1232
+ deleteRegistrationIds: [],
1233
+ }),
1234
+ administrationFee: 0,
1235
+ freeContribution: 0,
1236
+ paymentMethod: PaymentMethod.PointOfSale,
1237
+ totalPrice: 20,
1238
+ asOrganizationId: organization.id,
1239
+ customer: null,
1240
+ });
1241
+
1242
+ await expect(post(body, organization, token))
1243
+ .rejects
1244
+ .toThrow(STExpect.simpleError({
1245
+ code: 'cannot_pay_balance_items',
1246
+ }));
1247
+ });
1248
+ });
1249
+
1250
+ describe('Register by other organization', () => {
1251
+ beforeEach(() => {
1252
+ defaultPermissionLevel = PermissionLevel.Full;
1253
+ });
1254
+
1255
+ async function initDualData(options?: Parameters<typeof initData>[0]) {
1256
+ const base = await initData(options);
1257
+
1258
+ base.group.settings.allowRegistrationsByOrganization = true;
1259
+ await base.group.save();
1260
+
1261
+ // Give the user permission for a different organization
1262
+ const { organization: organization2 } = await initOrganization();
1263
+ base.user.permissions = UserPermissions.create({
1264
+ organizationPermissions: new Map([
1265
+ [organization2.id, Permissions.create({
1266
+ level: options?.permissionLevel ?? defaultPermissionLevel,
1267
+ })],
1268
+ ]),
1269
+ });
1270
+ await base.user.save();
1271
+
1272
+ return {
1273
+ ...base,
1274
+ organization2,
1275
+ };
1276
+ }
1277
+
1278
+ test('Should set paying organization id', async () => {
1279
+ const { organization, group, groupPrice, member, token, organization2 } = await initDualData();
1280
+
1281
+ const company = Company.create({
1282
+ name: 'test company',
1283
+ });
1284
+
1285
+ organization2.meta.companies.push(company);
1286
+ await organization2.save();
1217
1287
 
1218
1288
  const body = IDRegisterCheckout.create({
1219
1289
  cart: IDRegisterCart.create({
@@ -1221,18 +1291,7 @@ describe('Endpoint.RegisterMembers', () => {
1221
1291
  IDRegisterItem.create({
1222
1292
  id: uuidv4(),
1223
1293
  replaceRegistrationIds: [],
1224
- options: [
1225
- RegisterItemOption.create({
1226
- option: option1,
1227
- amount: 2,
1228
- optionMenu,
1229
- }),
1230
- RegisterItemOption.create({
1231
- option: option2,
1232
- amount: 5,
1233
- optionMenu,
1234
- }),
1235
- ],
1294
+ options: [],
1236
1295
  groupPrice,
1237
1296
  organizationId: organization.id,
1238
1297
  groupId: group.id,
@@ -1246,55 +1305,27 @@ describe('Endpoint.RegisterMembers', () => {
1246
1305
  administrationFee: 0,
1247
1306
  freeContribution: 0,
1248
1307
  paymentMethod: PaymentMethod.PointOfSale,
1249
- totalPrice: 50,
1250
- customer: null,
1308
+ totalPrice: 25_00,
1309
+ customer: PaymentCustomer.create({
1310
+ company,
1311
+ }),
1312
+ asOrganizationId: organization2.id,
1251
1313
  });
1252
- // #endregion
1253
1314
 
1254
- // #region act and assert
1255
- await expect(async () => await post(body, organization, token))
1256
- .rejects
1257
- .toThrow(new RegExp('Option maximum exceeded'));
1258
- // #endregion
1315
+ const response = await post(body, organization, token);
1316
+ expect(response.body.registrations.length).toBe(1);
1317
+ expect(response.body.registrations[0].payingOrganizationId).toEqual(organization2.id);
1259
1318
  });
1260
1319
 
1261
- test('Should not fail if max option not exceeded', async () => {
1262
- // #region arrange
1263
- const { organization, group, groupPrice, token, member } = await initData();
1264
-
1265
- const option1 = GroupOption.create({
1266
- name: 'option 1',
1267
- stock: 4,
1268
- maximum: 5,
1269
- allowAmount: true,
1270
- price: ReduceablePrice.create({
1271
- price: 5,
1272
- reducedPrice: 3,
1273
- }),
1274
- });
1275
-
1276
- const option2 = GroupOption.create({
1277
- name: 'option 2',
1278
- stock: 5,
1279
- maximum: 5,
1280
- allowAmount: true,
1281
- price: ReduceablePrice.create({
1282
- price: 3,
1283
- reducedPrice: 1,
1284
- }),
1285
- });
1320
+ test('Should fail if not sufficient permissions', async () => {
1321
+ const { organization, group, groupPrice, member, token, organization2 } = await initDualData({ permissionLevel: PermissionLevel.Read });
1286
1322
 
1287
- const optionMenu = GroupOptionMenu.create({
1288
- name: 'option menu 1',
1289
- multipleChoice: true,
1290
- options: [option1, option2],
1323
+ const company = Company.create({
1324
+ name: 'test company',
1291
1325
  });
1292
1326
 
1293
- group.settings.optionMenus = [
1294
- optionMenu,
1295
- ];
1296
-
1297
- await group.save();
1327
+ organization2.meta.companies.push(company);
1328
+ await organization2.save();
1298
1329
 
1299
1330
  const body = IDRegisterCheckout.create({
1300
1331
  cart: IDRegisterCart.create({
@@ -1302,18 +1333,7 @@ describe('Endpoint.RegisterMembers', () => {
1302
1333
  IDRegisterItem.create({
1303
1334
  id: uuidv4(),
1304
1335
  replaceRegistrationIds: [],
1305
- options: [
1306
- RegisterItemOption.create({
1307
- option: option1,
1308
- amount: 2,
1309
- optionMenu,
1310
- }),
1311
- RegisterItemOption.create({
1312
- option: option2,
1313
- amount: 5,
1314
- optionMenu,
1315
- }),
1316
- ],
1336
+ options: [],
1317
1337
  groupPrice,
1318
1338
  organizationId: organization.id,
1319
1339
  groupId: group.id,
@@ -1327,34 +1347,23 @@ describe('Endpoint.RegisterMembers', () => {
1327
1347
  administrationFee: 0,
1328
1348
  freeContribution: 0,
1329
1349
  paymentMethod: PaymentMethod.PointOfSale,
1330
- totalPrice: 50,
1331
- customer: null,
1350
+ totalPrice: 25_00,
1351
+ customer: PaymentCustomer.create({
1352
+ company,
1353
+ }),
1354
+ asOrganizationId: organization2.id,
1332
1355
  });
1333
- // #endregion
1334
1356
 
1335
- // #region act and assert
1336
- const result = await post(body, organization, token);
1337
- expect(result).toBeDefined();
1338
- // #endregion
1357
+ await expect(async () => await post(body, organization, token))
1358
+ .rejects
1359
+ .toThrow(STExpect.simpleError({ code: 'forbidden' }));
1339
1360
  });
1340
- });
1341
1361
 
1342
- describe('Register by other organization', () => {
1343
1362
  test('Should fail if disabled by group', async () => {
1344
- // #region arrange
1345
- const { organization, group, groupPrice, token, member, user } = await initData();
1346
-
1347
- const { organization: organization2 } = await initData();
1348
-
1349
- user.permissions = UserPermissions.create({
1350
- organizationPermissions: new Map([
1351
- [organization2.id, Permissions.create({
1352
- level: PermissionLevel.Full,
1353
- })],
1354
- ]),
1355
- });
1363
+ const { organization, group, groupPrice, member, token, organization2 } = await initDualData();
1356
1364
 
1357
- await user.save();
1365
+ group.settings.allowRegistrationsByOrganization = false;
1366
+ await group.save();
1358
1367
 
1359
1368
  const body = IDRegisterCheckout.create({
1360
1369
  cart: IDRegisterCart.create({
@@ -1376,36 +1385,18 @@ describe('Endpoint.RegisterMembers', () => {
1376
1385
  administrationFee: 0,
1377
1386
  freeContribution: 0,
1378
1387
  paymentMethod: PaymentMethod.PointOfSale,
1379
- totalPrice: 25,
1388
+ totalPrice: 25_00,
1380
1389
  customer: null,
1381
1390
  asOrganizationId: organization2.id,
1382
1391
  });
1383
- // #endregion
1384
1392
 
1385
- // #region act and assert
1386
1393
  await expect(async () => await post(body, organization, token))
1387
1394
  .rejects
1388
- .toThrow(new RegExp('allowRegistrationsByOrganization disabled'));
1389
- // #endregion
1395
+ .toThrow(STExpect.simpleError({ code: 'as_organization_disabled' }));
1390
1396
  });
1391
1397
 
1392
1398
  test('Should fail if no customer', async () => {
1393
- // #region arrange
1394
- const { organization, group, groupPrice, token, member, user } = await initData();
1395
- group.settings.allowRegistrationsByOrganization = true;
1396
- await group.save();
1397
-
1398
- const { organization: organization2 } = await initData();
1399
-
1400
- user.permissions = UserPermissions.create({
1401
- organizationPermissions: new Map([
1402
- [organization2.id, Permissions.create({
1403
- level: PermissionLevel.Full,
1404
- })],
1405
- ]),
1406
- });
1407
-
1408
- await user.save();
1399
+ const { organization, group, groupPrice, member, token, organization2 } = await initDualData();
1409
1400
 
1410
1401
  const body = IDRegisterCheckout.create({
1411
1402
  cart: IDRegisterCart.create({
@@ -1427,36 +1418,24 @@ describe('Endpoint.RegisterMembers', () => {
1427
1418
  administrationFee: 0,
1428
1419
  freeContribution: 0,
1429
1420
  paymentMethod: PaymentMethod.PointOfSale,
1430
- totalPrice: 25,
1421
+ totalPrice: 25_00,
1431
1422
  customer: null,
1432
1423
  asOrganizationId: organization2.id,
1433
1424
  });
1434
- // #endregion
1435
1425
 
1436
- // #region act and assert
1437
1426
  await expect(async () => await post(body, organization, token))
1438
1427
  .rejects
1439
1428
  .toThrow(new RegExp('customer is required when paying as an organization'));
1440
- // #endregion
1441
1429
  });
1442
1430
 
1443
- test('Should fail if no company on customer', async () => {
1444
- // #region arrange
1445
- const { organization, group, groupPrice, token, member, user } = await initData();
1446
- group.settings.allowRegistrationsByOrganization = true;
1447
- await group.save();
1448
-
1449
- const { organization: organization2 } = await initData();
1450
-
1451
- user.permissions = UserPermissions.create({
1452
- organizationPermissions: new Map([
1453
- [organization2.id, Permissions.create({
1454
- level: PermissionLevel.Full,
1455
- })],
1456
- ]),
1457
- });
1431
+ test('Deleting registrations is not allowed', async () => {
1432
+ const { member, group, groupPrice, organization, token, organization2 } = await initDualData();
1458
1433
 
1459
- await user.save();
1434
+ const registration = await new RegistrationFactory({
1435
+ member,
1436
+ group,
1437
+ groupPrice,
1438
+ }).create();
1460
1439
 
1461
1440
  const body = IDRegisterCheckout.create({
1462
1441
  cart: IDRegisterCart.create({
@@ -1471,45 +1450,24 @@ describe('Endpoint.RegisterMembers', () => {
1471
1450
  memberId: member.id,
1472
1451
  }),
1473
1452
  ],
1474
- balanceItems: [
1475
- ],
1476
- deleteRegistrationIds: [],
1453
+ balanceItems: [],
1454
+ deleteRegistrationIds: [registration.id],
1477
1455
  }),
1478
1456
  administrationFee: 0,
1479
1457
  freeContribution: 0,
1480
1458
  paymentMethod: PaymentMethod.PointOfSale,
1481
- totalPrice: 25,
1482
- customer: PaymentCustomer.create({
1483
- company: null,
1484
- }),
1459
+ totalPrice: 5,
1485
1460
  asOrganizationId: organization2.id,
1461
+ customer: null,
1486
1462
  });
1487
- // #endregion
1488
1463
 
1489
- // #region act and assert
1490
1464
  await expect(async () => await post(body, organization, token))
1491
1465
  .rejects
1492
- .toThrow(new RegExp('customer.company is required'));
1493
- // #endregion
1466
+ .toThrow(STExpect.simpleError({ code: 'forbidden' }));
1494
1467
  });
1495
1468
 
1496
- test('Should fail if company does not exist on organization', async () => {
1497
- // #region arrange
1498
- const { organization, group, groupPrice, token, member, user } = await initData();
1499
- group.settings.allowRegistrationsByOrganization = true;
1500
- await group.save();
1501
-
1502
- const { organization: organization2 } = await initData();
1503
-
1504
- user.permissions = UserPermissions.create({
1505
- organizationPermissions: new Map([
1506
- [organization2.id, Permissions.create({
1507
- level: PermissionLevel.Full,
1508
- })],
1509
- ]),
1510
- });
1511
-
1512
- await user.save();
1469
+ test('Should fail if no company on customer', async () => {
1470
+ const { organization, group, groupPrice, member, token, organization2 } = await initDualData();
1513
1471
 
1514
1472
  const body = IDRegisterCheckout.create({
1515
1473
  cart: IDRegisterCart.create({
@@ -1531,46 +1489,20 @@ describe('Endpoint.RegisterMembers', () => {
1531
1489
  administrationFee: 0,
1532
1490
  freeContribution: 0,
1533
1491
  paymentMethod: PaymentMethod.PointOfSale,
1534
- totalPrice: 25,
1492
+ totalPrice: 25_00,
1535
1493
  customer: PaymentCustomer.create({
1536
- company: Company.create({
1537
- name: 'test company',
1538
- }),
1494
+ company: null,
1539
1495
  }),
1540
1496
  asOrganizationId: organization2.id,
1541
1497
  });
1542
- // #endregion
1543
1498
 
1544
- // #region act and assert
1545
1499
  await expect(async () => await post(body, organization, token))
1546
1500
  .rejects
1547
- .toThrow(new RegExp('Oeps, de facturatiegegevens die je probeerde te selecteren lijken niet meer te bestaan.'));
1548
- // #endregion
1501
+ .toThrow(new RegExp('customer.company is required'));
1549
1502
  });
1550
1503
 
1551
- test('Should set paying organization id', async () => {
1552
- // #region arrange
1553
- const { organization, group, groupPrice, token, member, user } = await initData();
1554
- group.settings.allowRegistrationsByOrganization = true;
1555
- await group.save();
1556
-
1557
- const { organization: organization2 } = await initData();
1558
- const company = Company.create({
1559
- name: 'test company',
1560
- });
1561
-
1562
- organization2.meta.companies.push(company);
1563
- await organization2.save();
1564
-
1565
- user.permissions = UserPermissions.create({
1566
- organizationPermissions: new Map([
1567
- [organization2.id, Permissions.create({
1568
- level: PermissionLevel.Full,
1569
- })],
1570
- ]),
1571
- });
1572
-
1573
- await user.save();
1504
+ test('Should fail if company does not exist on organization', async () => {
1505
+ const { organization, group, groupPrice, member, token, organization2 } = await initDualData();
1574
1506
 
1575
1507
  const body = IDRegisterCheckout.create({
1576
1508
  cart: IDRegisterCart.create({
@@ -1592,25 +1524,27 @@ describe('Endpoint.RegisterMembers', () => {
1592
1524
  administrationFee: 0,
1593
1525
  freeContribution: 0,
1594
1526
  paymentMethod: PaymentMethod.PointOfSale,
1595
- totalPrice: 25,
1527
+ totalPrice: 25_00,
1596
1528
  customer: PaymentCustomer.create({
1597
- company,
1529
+ company: Company.create({
1530
+ name: 'test company',
1531
+ }),
1598
1532
  }),
1599
1533
  asOrganizationId: organization2.id,
1600
1534
  });
1601
- // #endregion
1602
1535
 
1603
- // #region act and assert
1604
- const response = await post(body, organization, token);
1605
- expect(response.body.registrations.length).toBe(1);
1606
- expect(response.body.registrations[0].payingOrganizationId).toEqual(organization2.id);
1607
- // #endregion
1536
+ await expect(async () => await post(body, organization, token))
1537
+ .rejects
1538
+ .toThrow(new RegExp('Oeps, de facturatiegegevens die je probeerde te selecteren lijken niet meer te bestaan.'));
1608
1539
  });
1609
1540
  });
1610
1541
 
1611
1542
  describe('Replace registrations', () => {
1543
+ beforeEach(() => {
1544
+ defaultPermissionLevel = PermissionLevel.Full;
1545
+ });
1546
+
1612
1547
  test('Should update registered members', async () => {
1613
- // #region arrange
1614
1548
  const { organization, group: group1, groupPrice: groupPrice1, token, member } = await initData();
1615
1549
 
1616
1550
  const registration = await new RegistrationFactory({
@@ -1651,11 +1585,7 @@ describe('Endpoint.RegisterMembers', () => {
1651
1585
  asOrganizationId: organization.id,
1652
1586
  customer: null,
1653
1587
  });
1654
- // #endregion
1655
-
1656
- // #region act and assert
1657
1588
 
1658
- // update occupancy to be sure occupancy is 1
1659
1589
  await group1.updateOccupancy();
1660
1590
  expect(group1.settings.registeredMembers).toBe(1);
1661
1591
 
@@ -1673,16 +1603,14 @@ describe('Endpoint.RegisterMembers', () => {
1673
1603
  // occupancy should go from 1 to 0 because the registration should be replaced
1674
1604
  expect(updatedGroup1After!.settings.registeredMembers).toBe(0);
1675
1605
  expect(updatedGroup1After!.settings.reservedMembers).toBe(0);
1676
- // #endregion
1677
1606
  });
1678
1607
 
1679
- test('Should set paid as organization on new registration', async () => {
1680
- // #region arrange
1608
+ test('When replacing a registration, we should keep the original paying organization id', async () => {
1681
1609
  const { organization, group: group1, groupPrice: groupPrice1, token, member, user } = await initData();
1682
1610
 
1683
1611
  group1.settings.allowRegistrationsByOrganization = true;
1684
1612
  await group1.save();
1685
- const organization2 = await initOrganization();
1613
+ const { organization: organization2 } = await initOrganization();
1686
1614
 
1687
1615
  user.permissions = UserPermissions.create({
1688
1616
  organizationPermissions: new Map([
@@ -1726,7 +1654,7 @@ describe('Endpoint.RegisterMembers', () => {
1726
1654
  administrationFee: 0,
1727
1655
  freeContribution: 0,
1728
1656
  paymentMethod: PaymentMethod.PointOfSale,
1729
- totalPrice: 25,
1657
+ totalPrice: 25_00,
1730
1658
  asOrganizationId: organization2.id,
1731
1659
  customer: PaymentCustomer.create({
1732
1660
  company,
@@ -1739,7 +1667,7 @@ describe('Endpoint.RegisterMembers', () => {
1739
1667
 
1740
1668
  const group = await new GroupFactory({
1741
1669
  organization,
1742
- price: 30,
1670
+ price: 30_00,
1743
1671
  stock: 5,
1744
1672
  }).create();
1745
1673
 
@@ -1764,15 +1692,13 @@ describe('Endpoint.RegisterMembers', () => {
1764
1692
  administrationFee: 0,
1765
1693
  freeContribution: 0,
1766
1694
  paymentMethod: PaymentMethod.PointOfSale,
1767
- totalPrice: 30,
1695
+ totalPrice: 30_00 - 25_00,
1768
1696
  asOrganizationId: organization.id,
1769
1697
  customer: PaymentCustomer.create({
1770
1698
  company,
1771
1699
  }),
1772
1700
  });
1773
- // #endregion
1774
1701
 
1775
- // #region act and assert
1776
1702
  const response = await post(body2, organization, token);
1777
1703
 
1778
1704
  expect(response.body).toBeDefined();
@@ -1780,7 +1706,6 @@ describe('Endpoint.RegisterMembers', () => {
1780
1706
 
1781
1707
  // the payingOrganizationId should equal the id of the paying organization of the replaced registration
1782
1708
  expect(response.body.registrations[0].payingOrganizationId).toEqual(organization2.id);
1783
- // #endregion
1784
1709
  });
1785
1710
 
1786
1711
  test('Replace registration by registration of other member should fail', async () => {
@@ -1887,6 +1812,10 @@ describe('Endpoint.RegisterMembers', () => {
1887
1812
  });
1888
1813
 
1889
1814
  describe('Delete registrations', () => {
1815
+ beforeEach(() => {
1816
+ defaultPermissionLevel = PermissionLevel.Full;
1817
+ });
1818
+
1890
1819
  test('Should update registered members', async () => {
1891
1820
  // #region arrange
1892
1821
  const { member, group: group1, groupPrice: groupPrice1, organization: organization1, token } = await initData();
@@ -1954,9 +1883,10 @@ describe('Endpoint.RegisterMembers', () => {
1954
1883
  // #endregion
1955
1884
  });
1956
1885
 
1957
- test('Should throw error if with payment', async () => {
1886
+ test('Should throw error if deleting registrations as normal member', async () => {
1958
1887
  // #region arrange
1959
1888
  const { member, group: group1, groupPrice: groupPrice1, organization: organization1, token } = await initData();
1889
+ await initPayconiq({ organization: organization1 });
1960
1890
 
1961
1891
  const registration = await new RegistrationFactory({
1962
1892
  member,
@@ -1969,7 +1899,6 @@ describe('Endpoint.RegisterMembers', () => {
1969
1899
  organization: organization1,
1970
1900
  price: 30,
1971
1901
  stock: 5,
1972
- maxMembers: 1,
1973
1902
  }).create();
1974
1903
 
1975
1904
  const groupPrice = group.settings.prices[0];
@@ -1998,16 +1927,8 @@ describe('Endpoint.RegisterMembers', () => {
1998
1927
  totalPrice: 5,
1999
1928
  customer: null,
2000
1929
  });
2001
- // #endregion
2002
-
2003
- // #region act and assert
2004
-
2005
- // update occupancy to be sure occupancy is 1
2006
- await group1.updateOccupancy();
2007
- expect(group1.settings.registeredMembers).toBe(1);
2008
1930
 
2009
1931
  await expect(async () => await post(body, organization1, token)).rejects.toThrow('Permission denied: you are not allowed to delete registrations');
2010
- // #endregion
2011
1932
  });
2012
1933
 
2013
1934
  test('Should deactivate registration', async () => {
@@ -2064,7 +1985,6 @@ describe('Endpoint.RegisterMembers', () => {
2064
1985
 
2065
1986
  test('Should fail if invalid cancelation fee', async () => {
2066
1987
  for (const cancellationFeePercentage of [10001, -1]) {
2067
- // #region arrange
2068
1988
  const { member, group: group1, groupPrice: groupPrice1, organization, token } = await initData();
2069
1989
 
2070
1990
  const body1 = IDRegisterCheckout.create({
@@ -2086,7 +2006,7 @@ describe('Endpoint.RegisterMembers', () => {
2086
2006
  administrationFee: 0,
2087
2007
  freeContribution: 0,
2088
2008
  paymentMethod: PaymentMethod.PointOfSale,
2089
- totalPrice: 25,
2009
+ totalPrice: 25_00,
2090
2010
  asOrganizationId: organization.id,
2091
2011
  customer: null,
2092
2012
  });
@@ -2124,17 +2044,17 @@ describe('Endpoint.RegisterMembers', () => {
2124
2044
  administrationFee: 0,
2125
2045
  freeContribution: 0,
2126
2046
  paymentMethod: PaymentMethod.PointOfSale,
2127
- totalPrice: 30,
2047
+ totalPrice: 30 - 25 + Math.round(25 * cancellationFeePercentage / 10000),
2128
2048
  asOrganizationId: organization.id,
2129
2049
  customer: null,
2130
2050
  });
2131
- // #endregion
2132
2051
 
2133
- // #region act and assert
2134
2052
  await expect(async () => await post(body2, organization, token))
2135
2053
  .rejects
2136
- .toThrow(new RegExp('Invalid cancellation fee percentage.'));
2137
- // #endregion
2054
+ .toThrow(STExpect.simpleError({
2055
+ field: 'cancellationFeePercentage',
2056
+ code: 'invalid_field',
2057
+ }));
2138
2058
  }
2139
2059
  });
2140
2060