@stamhoofd/backend 2.83.4 → 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
@@ -50,12 +50,15 @@ export class PatchOrganizationRegistrationPeriodsEndpoint extends Endpoint<Param
50
50
  periods.push(await PatchOrganizationRegistrationPeriodsEndpoint.createOrganizationPeriod(organization, put));
51
51
  }
52
52
 
53
+ const forceGroupIds: string[] = [];
54
+
53
55
  for (const patch of request.body.getPatches()) {
54
56
  const organizationPeriod = await OrganizationRegistrationPeriod.getByID(patch.id);
55
57
  if (!organizationPeriod || organizationPeriod.organizationId !== organization.id) {
56
58
  throw new SimpleError({
57
59
  code: 'not_found',
58
60
  message: 'Period not found',
61
+ human: $t('347463a0-1e9a-40fe-996c-d77ba13bc05b'),
59
62
  statusCode: 404,
60
63
  });
61
64
  }
@@ -66,16 +69,16 @@ export class PatchOrganizationRegistrationPeriodsEndpoint extends Endpoint<Param
66
69
  throw new SimpleError({
67
70
  code: 'not_found',
68
71
  message: 'Period not found',
72
+ human: $t('347463a0-1e9a-40fe-996c-d77ba13bc05b'),
69
73
  statusCode: 404,
70
74
  });
71
75
  }
72
76
 
73
77
  if (period.locked) {
74
78
  throw new SimpleError({
75
- code: 'not_found',
76
- message: 'Period not found',
77
- human: $t(`d19ec3c4-6320-4f65-8890-74ba1a5bc375`) + ' ' + period.getStructure().name + ' ' + $t(`cb4b72b3-3990-4870-ab85-08ef7c7eadaa`),
78
- statusCode: 404,
79
+ code: 'locked_period',
80
+ message: 'This period is locked',
81
+ human: $t(`cc658db0-ee90-40fc-8a8c-1074a4f1f2f2`, { '2000-2001': period.getStructure().nameShort }),
79
82
  });
80
83
  }
81
84
 
@@ -209,12 +212,14 @@ export class PatchOrganizationRegistrationPeriodsEndpoint extends Endpoint<Param
209
212
 
210
213
  for (const groupPut of patch.groups.getPuts()) {
211
214
  shouldUpdateSetupSteps = true;
212
- await PatchOrganizationRegistrationPeriodsEndpoint.createGroup(groupPut.put, organization.id, period, { allowedIds });
215
+ const group = await PatchOrganizationRegistrationPeriodsEndpoint.createGroup(groupPut.put, organization.id, period, { allowedIds });
213
216
  deleteUnreachable = true;
217
+ forceGroupIds.push(group.id);
214
218
  }
215
219
 
216
220
  for (const struct of patch.groups.getPatches()) {
217
- await PatchOrganizationRegistrationPeriodsEndpoint.patchGroup(struct, period);
221
+ const group = await PatchOrganizationRegistrationPeriodsEndpoint.patchGroup(struct, period);
222
+ forceGroupIds.push(group.id);
218
223
  }
219
224
 
220
225
  if (deleteUnreachable) {
@@ -233,7 +238,7 @@ export class PatchOrganizationRegistrationPeriodsEndpoint extends Endpoint<Param
233
238
  }
234
239
 
235
240
  return new Response(
236
- await AuthenticatedStructures.organizationRegistrationPeriods(periods),
241
+ await AuthenticatedStructures.organizationRegistrationPeriods(periods, undefined, { forceGroupIds }),
237
242
  );
238
243
  }
239
244
 
@@ -272,19 +277,39 @@ export class PatchOrganizationRegistrationPeriodsEndpoint extends Endpoint<Param
272
277
  static async createOrganizationPeriod(organization: Organization, struct: OrganizationRegistrationPeriodStruct) {
273
278
  const period = await RegistrationPeriod.getByID(struct.period.id);
274
279
 
275
- if (!period || period.locked) {
280
+ if (!period) {
276
281
  throw new SimpleError({
277
282
  code: 'not_found',
278
283
  message: 'Period not found',
284
+ human: $t('347463a0-1e9a-40fe-996c-d77ba13bc05b'),
279
285
  statusCode: 404,
280
286
  });
281
287
  }
282
288
 
289
+ if (period?.locked) {
290
+ throw new SimpleError({
291
+ code: 'locked_period',
292
+ message: 'Period is locked',
293
+ human: $t(`cc658db0-ee90-40fc-8a8c-1074a4f1f2f2`, { '2000-2001': period.getStructure().nameShort }),
294
+ });
295
+ }
296
+
283
297
  const maximumStart = 1000 * 60 * 60 * 24 * 31 * 2; // 2 months in advance
284
- if (period.startDate > new Date(Date.now() + maximumStart)) {
298
+ if (STAMHOOFD.environment !== 'development' && period.startDate > new Date(Date.now() + maximumStart)) {
285
299
  throw new SimpleError({
286
300
  code: 'invalid_field',
287
- message: 'Het werkjaar die je wilt instellen is nog niet gestart',
301
+ message: 'Period start date is too far in the future',
302
+ human: $t('2655c006-55af-47e6-959c-16acdb1917dc'),
303
+ field: 'period',
304
+ });
305
+ }
306
+
307
+ // Period has ended
308
+ if (STAMHOOFD.environment !== 'development' && period.endDate < new Date()) {
309
+ throw new SimpleError({
310
+ code: 'invalid_field',
311
+ message: 'Period has ended',
312
+ human: $t('44ff6618-ecbf-408b-9cc1-b1479e8ba8a2'),
288
313
  field: 'period',
289
314
  });
290
315
  }
@@ -450,6 +475,8 @@ export class PatchOrganizationRegistrationPeriodsEndpoint extends Endpoint<Param
450
475
 
451
476
  await model.updateOccupancy();
452
477
  await model.save();
478
+
479
+ return model;
453
480
  }
454
481
 
455
482
  static async createGroup(struct: GroupStruct, organizationId: string, period: RegistrationPeriod, options?: { allowedIds?: string[] }): Promise<Group> {
@@ -465,6 +492,9 @@ export class PatchOrganizationRegistrationPeriodsEndpoint extends Endpoint<Param
465
492
  }
466
493
  }
467
494
  }
495
+ else {
496
+ // For events, permission checking needs to happen outside this method (access to event = access to group)
497
+ }
468
498
 
469
499
  if (period.locked) {
470
500
  throw new SimpleError({
@@ -3,7 +3,7 @@ import { OrganizationFactory, Token, UserFactory } from '@stamhoofd/models';
3
3
 
4
4
  import { testServer } from '../../../../../tests/helpers/TestServer';
5
5
  import { PatchApiUserEndpoint } from './PatchApiUserEndpoint';
6
- import { SHExpect, TestUtils } from '@stamhoofd/test-utils';
6
+ import { STExpect, TestUtils } from '@stamhoofd/test-utils';
7
7
  import { ApiUser, ApiUserRateLimits, PermissionLevel, Permissions, UserMeta, UserPermissions } from '@stamhoofd/structures';
8
8
  import { CreateApiUserEndpoint } from './CreateApiUserEndpoint';
9
9
 
@@ -63,7 +63,7 @@ describe('Endpoint.CreateApiUserEndpoint', () => {
63
63
  }));
64
64
  createRequest.headers.authorization = 'Bearer ' + token.accessToken;
65
65
 
66
- await expect(testServer.test(endpoint, createRequest)).rejects.toThrow(SHExpect.simpleError({
66
+ await expect(testServer.test(endpoint, createRequest)).rejects.toThrow(STExpect.simpleError({
67
67
  code: 'permission_denied',
68
68
  }));
69
69
  });
@@ -3,7 +3,7 @@ import { OrganizationFactory, Token, UserFactory } from '@stamhoofd/models';
3
3
 
4
4
  import { testServer } from '../../../../../tests/helpers/TestServer';
5
5
  import { PatchApiUserEndpoint } from './PatchApiUserEndpoint';
6
- import { SHExpect, TestUtils } from '@stamhoofd/test-utils';
6
+ import { STExpect, TestUtils } from '@stamhoofd/test-utils';
7
7
  import { ApiUser, ApiUserRateLimits, PermissionLevel, Permissions, PermissionsResourceType, ResourcePermissions, UserMeta, UserPermissions } from '@stamhoofd/structures';
8
8
  import { CreateApiUserEndpoint } from './CreateApiUserEndpoint';
9
9
  import { PatchMap } from '@simonbackx/simple-encoding';
@@ -82,7 +82,7 @@ describe('Endpoint.PatchApiUserEndpoint', () => {
82
82
  }));
83
83
  r.headers.authorization = 'Bearer ' + token.accessToken;
84
84
 
85
- await expect(testServer.test(endpoint, r)).rejects.toThrow(SHExpect.simpleError({
85
+ await expect(testServer.test(endpoint, r)).rejects.toThrow(STExpect.simpleError({
86
86
  code: 'permission_denied',
87
87
  }));
88
88
  });
@@ -1,7 +1,7 @@
1
1
  import { AutoEncoderPatchType, Decoder } from '@simonbackx/simple-encoding';
2
2
  import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
3
3
  import { SimpleError } from '@simonbackx/simple-errors';
4
- import { Token, Webshop } from '@stamhoofd/models';
4
+ import { Webshop } from '@stamhoofd/models';
5
5
  import { QueueHandler } from '@stamhoofd/queues';
6
6
  import { PermissionLevel, PrivateWebshop, WebshopPrivateMetaData } from '@stamhoofd/structures';
7
7
  import { Formatter } from '@stamhoofd/utility';
@@ -52,6 +52,9 @@ export class PatchWebshopEndpoint extends Endpoint<Params, Query, Body, Response
52
52
  // Do all updates
53
53
  if (request.body.meta) {
54
54
  request.body.meta.domainActive = undefined;
55
+ if (request.body.meta.customCode !== undefined && !await Context.auth.hasFullAccess(organization.id)) {
56
+ throw Context.auth.error($t('c8e499c2-8d90-480c-a15e-d9da5291b40e'));
57
+ }
55
58
  webshop.meta.patchOrPut(request.body.meta);
56
59
  }
57
60
 
@@ -3,7 +3,7 @@ import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-
3
3
  import { SimpleError } from '@simonbackx/simple-errors';
4
4
  import { BalanceItem, BalanceItemPayment, Order, Payment, Webshop, WebshopCounter } from '@stamhoofd/models';
5
5
  import { QueueHandler } from '@stamhoofd/queues';
6
- import { AuditLogSource, BalanceItemRelation, BalanceItemRelationType, BalanceItemStatus, BalanceItemType, OrderStatus, PaymentMethod, PaymentStatus, PermissionLevel, PrivateOrder, Webshop as WebshopStruct } from '@stamhoofd/structures';
6
+ import { AuditLogSource, BalanceItemRelation, BalanceItemRelationType, BalanceItemStatus, BalanceItemType, OrderStatus, PaymentMethod, PaymentStatus, PermissionLevel, PrivateOrder, TranslatedString, Webshop as WebshopStruct } from '@stamhoofd/structures';
7
7
 
8
8
  import { Context } from '../../../../helpers/Context';
9
9
  import { AuditLogService } from '../../../../services/AuditLogService';
@@ -178,7 +178,7 @@ export class PatchWebshopOrdersEndpoint extends Endpoint<Params, Query, Body, Re
178
178
  BalanceItemRelationType.Webshop,
179
179
  BalanceItemRelation.create({
180
180
  id: webshop.id,
181
- name: webshop.meta.name,
181
+ name: new TranslatedString(webshop.meta.name),
182
182
  }),
183
183
  ],
184
184
  ]);
@@ -5,7 +5,7 @@ import { SimpleError } from '@simonbackx/simple-errors';
5
5
  import { Email } from '@stamhoofd/email';
6
6
  import { BalanceItem, BalanceItemPayment, MolliePayment, MollieToken, Order, PayconiqPayment, Payment, RateLimiter, Webshop, WebshopDiscountCode } from '@stamhoofd/models';
7
7
  import { QueueHandler } from '@stamhoofd/queues';
8
- import { AuditLogSource, BalanceItemRelation, BalanceItemRelationType, BalanceItemStatus, BalanceItemType, OrderData, OrderResponse, Order as OrderStruct, PaymentCustomer, PaymentMethod, PaymentMethodHelper, PaymentProvider, PaymentStatus, Payment as PaymentStruct, Version, WebshopAuthType, Webshop as WebshopStruct } from '@stamhoofd/structures';
8
+ import { AuditLogSource, BalanceItemRelation, BalanceItemRelationType, BalanceItemStatus, BalanceItemType, OrderData, OrderResponse, Order as OrderStruct, PaymentCustomer, PaymentMethod, PaymentMethodHelper, PaymentProvider, PaymentStatus, Payment as PaymentStruct, TranslatedString, Version, WebshopAuthType, Webshop as WebshopStruct } from '@stamhoofd/structures';
9
9
  import { Formatter } from '@stamhoofd/utility';
10
10
 
11
11
  import { BuckarooHelper } from '../../../helpers/BuckarooHelper';
@@ -205,7 +205,7 @@ export class PlaceOrderEndpoint extends Endpoint<Params, Query, Body, ResponseBo
205
205
  BalanceItemRelationType.Webshop,
206
206
  BalanceItemRelation.create({
207
207
  id: webshop.id,
208
- name: webshop.meta.name,
208
+ name: new TranslatedString(webshop.meta.name),
209
209
  }),
210
210
  ],
211
211
  ]);