@stamhoofd/backend 2.81.0 → 2.83.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 (108) hide show
  1. package/package.json +10 -10
  2. package/src/audit-logs/GroupLogger.ts +3 -3
  3. package/src/audit-logs/MemberResponsibilityRecordLogger.ts +1 -1
  4. package/src/audit-logs/OrderLogger.ts +1 -1
  5. package/src/audit-logs/RegistrationLogger.ts +1 -1
  6. package/src/endpoints/admin/members/ChargeMembersEndpoint.ts +4 -4
  7. package/src/endpoints/admin/memberships/ChargeMembershipsEndpoint.ts +1 -1
  8. package/src/endpoints/admin/organizations/ChargeOrganizationsEndpoint.ts +5 -5
  9. package/src/endpoints/admin/organizations/GetOrganizationsEndpoint.ts +1 -1
  10. package/src/endpoints/admin/organizations/PatchOrganizationsEndpoint.ts +8 -8
  11. package/src/endpoints/auth/CreateAdminEndpoint.ts +2 -2
  12. package/src/endpoints/auth/CreateTokenEndpoint.ts +10 -10
  13. package/src/endpoints/auth/ForgotPasswordEndpoint.ts +2 -2
  14. package/src/endpoints/auth/PatchUserEndpoint.ts +9 -9
  15. package/src/endpoints/auth/SignupEndpoint.ts +2 -2
  16. package/src/endpoints/auth/VerifyEmailEndpoint.ts +3 -3
  17. package/src/endpoints/global/audit-logs/GetAuditLogsEndpoint.ts +1 -1
  18. package/src/endpoints/global/email/GetEmailAddressEndpoint.ts +1 -1
  19. package/src/endpoints/global/email/GetEmailEndpoint.ts +1 -1
  20. package/src/endpoints/global/email/ManageEmailAddressEndpoint.ts +1 -1
  21. package/src/endpoints/global/email/PatchEmailEndpoint.test.ts +139 -0
  22. package/src/endpoints/global/email/PatchEmailEndpoint.ts +30 -7
  23. package/src/endpoints/global/events/GetEventNotificationsEndpoint.ts +1 -1
  24. package/src/endpoints/global/events/PatchEventNotificationsEndpoint.test.ts +16 -35
  25. package/src/endpoints/global/events/PatchEventNotificationsEndpoint.ts +1 -1
  26. package/src/endpoints/global/events/PatchEventsEndpoint.ts +22 -16
  27. package/src/endpoints/global/files/ExportToExcelEndpoint.ts +1 -1
  28. package/src/endpoints/global/files/UploadFile.ts +14 -2
  29. package/src/endpoints/global/files/UploadImage.ts +2 -2
  30. package/src/endpoints/global/members/GetMemberFamilyEndpoint.ts +2 -2
  31. package/src/endpoints/global/members/GetMembersEndpoint.ts +1 -1
  32. package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.test.ts +19 -19
  33. package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.ts +34 -34
  34. package/src/endpoints/global/organizations/CheckRegisterCodeEndpoint.ts +1 -1
  35. package/src/endpoints/global/organizations/CreateOrganizationEndpoint.ts +5 -5
  36. package/src/endpoints/global/payments/StripeWebhookEndpoint.ts +5 -1
  37. package/src/endpoints/global/platform/GetPlatformEndpoint.test.ts +68 -0
  38. package/src/endpoints/global/platform/PatchPlatformEnpoint.ts +1 -1
  39. package/src/endpoints/global/registration/GetPaymentRegistrations.ts +2 -2
  40. package/src/endpoints/global/registration/PatchUserMembersEndpoint.test.ts +15 -17
  41. package/src/endpoints/global/registration/PatchUserMembersEndpoint.ts +4 -4
  42. package/src/endpoints/global/registration/RegisterMembersEndpoint.ts +37 -37
  43. package/src/endpoints/global/registration-periods/PatchRegistrationPeriodsEndpoint.ts +2 -2
  44. package/src/endpoints/global/webshops/GetWebshopFromDomainEndpoint.ts +2 -2
  45. package/src/endpoints/organization/dashboard/documents/GetDocumentTemplateXML.ts +1 -1
  46. package/src/endpoints/organization/dashboard/documents/PatchDocumentEndpoint.ts +5 -5
  47. package/src/endpoints/organization/dashboard/documents/PatchDocumentTemplateEndpoint.ts +2 -2
  48. package/src/endpoints/organization/dashboard/email/CheckEmailBouncesEndpoint.ts +1 -1
  49. package/src/endpoints/organization/dashboard/email-templates/PatchEmailTemplatesEndpoint.ts +3 -3
  50. package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.ts +9 -9
  51. package/src/endpoints/organization/dashboard/organization/SetOrganizationDomainEndpoint.ts +4 -4
  52. package/src/endpoints/organization/dashboard/payments/GetMemberBalanceEndpoint.ts +1 -1
  53. package/src/endpoints/organization/dashboard/payments/GetPaymentsEndpoint.ts +1 -1
  54. package/src/endpoints/organization/dashboard/payments/PatchBalanceItemsEndpoint.ts +11 -11
  55. package/src/endpoints/organization/dashboard/payments/PatchPaymentsEndpoint.ts +13 -13
  56. package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.ts +16 -16
  57. package/src/endpoints/organization/dashboard/stripe/ConnectStripeEndpoint.ts +1 -1
  58. package/src/endpoints/organization/dashboard/stripe/DeleteStripeAccountEndpoint.ts +2 -2
  59. package/src/endpoints/organization/dashboard/stripe/GetStripeAccountLinkEndpoint.ts +1 -1
  60. package/src/endpoints/organization/dashboard/stripe/GetStripeLoginLinkEndpoint.ts +1 -1
  61. package/src/endpoints/organization/dashboard/stripe/UpdateStripeAccountEndpoint.ts +1 -1
  62. package/src/endpoints/organization/dashboard/users/CreateApiUserEndpoint.test.ts +106 -0
  63. package/src/endpoints/organization/dashboard/users/CreateApiUserEndpoint.ts +16 -3
  64. package/src/endpoints/organization/dashboard/users/DeleteUserEndpoint.ts +2 -2
  65. package/src/endpoints/organization/dashboard/users/PatchApiUserEndpoint.test.ts +247 -0
  66. package/src/endpoints/{auth → organization/dashboard/users}/PatchApiUserEndpoint.ts +25 -6
  67. package/src/endpoints/organization/dashboard/webshops/CreateWebshopEndpoint.ts +4 -4
  68. package/src/endpoints/organization/dashboard/webshops/PatchDiscountCodesEndpoint.ts +2 -2
  69. package/src/endpoints/organization/dashboard/webshops/PatchWebshopEndpoint.ts +8 -8
  70. package/src/endpoints/organization/dashboard/webshops/PatchWebshopTicketsEndpoint.ts +1 -1
  71. package/src/endpoints/organization/shared/ExchangePaymentEndpoint.ts +1 -1
  72. package/src/endpoints/organization/shared/GetDocumentHtml.ts +2 -2
  73. package/src/endpoints/organization/shared/GetPaymentEndpoint.ts +1 -1
  74. package/src/endpoints/organization/shared/auth/GetOrganizationEndpoint.test.ts +5 -0
  75. package/src/endpoints/organization/webshops/CheckWebshopDiscountCodesEndpoint.ts +1 -1
  76. package/src/endpoints/organization/webshops/GetOrderByPaymentEndpoint.ts +2 -2
  77. package/src/endpoints/organization/webshops/GetOrderEndpoint.ts +1 -1
  78. package/src/endpoints/organization/webshops/GetTicketsEndpoint.ts +3 -3
  79. package/src/endpoints/organization/webshops/GetWebshopEndpoint.test.ts +6 -1
  80. package/src/endpoints/organization/webshops/GetWebshopEndpoint.ts +1 -1
  81. package/src/endpoints/organization/webshops/PlaceOrderEndpoint.ts +10 -8
  82. package/src/excel-loaders/event-notifications.ts +11 -11
  83. package/src/excel-loaders/members.ts +34 -34
  84. package/src/excel-loaders/organizations.ts +23 -23
  85. package/src/excel-loaders/payments.ts +39 -39
  86. package/src/excel-loaders/receivable-balances.ts +21 -21
  87. package/src/helpers/AddressValidator.ts +6 -6
  88. package/src/helpers/AdminPermissionChecker.ts +7 -4
  89. package/src/helpers/AuthenticatedStructures.ts +16 -8
  90. package/src/helpers/BuckarooHelper.ts +1 -1
  91. package/src/helpers/CheckSettlements.ts +1 -1
  92. package/src/helpers/Context.ts +31 -15
  93. package/src/helpers/FileCache.ts +7 -7
  94. package/src/helpers/ForwardHandler.ts +1 -1
  95. package/src/helpers/GlobalHelper.ts +6 -4
  96. package/src/helpers/MembershipCharger.ts +2 -2
  97. package/src/helpers/SetupStepUpdater.ts +1 -1
  98. package/src/helpers/StripeHelper.ts +18 -7
  99. package/src/helpers/XlsxTransformerColumnHelper.ts +18 -18
  100. package/src/services/DocumentService.ts +1 -1
  101. package/src/services/EventNotificationService.ts +1 -1
  102. package/src/services/MemberNumberService.ts +3 -3
  103. package/src/services/SSOService.ts +5 -5
  104. package/src/sql-filters/members.ts +1 -1
  105. package/tests/e2e/api-rate-limits.test.ts +188 -0
  106. package/tests/e2e/private-files.test.ts +3 -3
  107. package/tests/helpers/StripeMocker.ts +7 -1
  108. /package/src/endpoints/global/platform/{GetPlatformEnpoint.ts → GetPlatformEndpoint.ts} +0 -0
@@ -35,7 +35,7 @@ export class AuthenticatedStructures {
35
35
  throw new SimpleError({
36
36
  code: 'permission_denied',
37
37
  message: 'Permission denied',
38
- human: 'Je hebt geen toegang tot deze betaling',
38
+ human: $t(`9f5ee239-d01b-4ee1-961b-2e3224489781`),
39
39
  });
40
40
  }
41
41
  }
@@ -423,6 +423,14 @@ export class AuthenticatedStructures {
423
423
  }
424
424
  }
425
425
 
426
+ if (includeContextOrganization && STAMHOOFD.singleOrganization && !Context.auth.organization) {
427
+ const found = organizations.get(STAMHOOFD.singleOrganization);
428
+ if (!found) {
429
+ const organization = await Context.auth.getOrganization(STAMHOOFD.singleOrganization);
430
+ organizations.set(organization.id, organization);
431
+ }
432
+ }
433
+
426
434
  const memberBlobs: MemberWithRegistrationsBlob[] = [];
427
435
  for (const member of members) {
428
436
  for (const registration of member.registrations) {
@@ -603,7 +611,7 @@ export class AuthenticatedStructures {
603
611
  throw new SimpleError({
604
612
  code: 'organization_not_found',
605
613
  message: 'Organization not found',
606
- human: 'Organisatie niet gevonden',
614
+ human: $t(`b6f89130-e727-4f85-b3a9-18b97c4f6ab6`),
607
615
  });
608
616
  }
609
617
 
@@ -639,7 +647,7 @@ export class AuthenticatedStructures {
639
647
  throw new SimpleError({
640
648
  code: 'permission_denied',
641
649
  message: 'Permission denied',
642
- human: 'Je hebt geen toegang tot de orders van deze webshop',
650
+ human: $t(`78cd49fe-260c-4fdc-ad83-e605734c684f`),
643
651
  });
644
652
  }
645
653
  }
@@ -673,7 +681,7 @@ export class AuthenticatedStructures {
673
681
  throw new SimpleError({
674
682
  code: 'permission_denied',
675
683
  message: 'Permission denied',
676
- human: 'Je hebt geen toegang tot de documenten van deze template',
684
+ human: $t(`792f644d-f3eb-4772-88b9-edb88f0b6773`),
677
685
  });
678
686
  }
679
687
  }
@@ -706,7 +714,7 @@ export class AuthenticatedStructures {
706
714
  throw new SimpleError({
707
715
  code: 'permission_denied',
708
716
  message: 'Permission denied',
709
- human: 'Je hebt geen toegang tot de tickets van deze webshop',
717
+ human: $t(`b5079e56-2480-4ce6-a3a2-3f244540fa0e`),
710
718
  });
711
719
  }
712
720
  }
@@ -953,13 +961,13 @@ export class AuthenticatedStructures {
953
961
  if (user.permissions?.platform !== null) {
954
962
  userStruct = NamedObject.create({
955
963
  id: '',
956
- name: 'Beheerder van ' + Platform.shared.config.name,
964
+ name: $t(`da016ffd-45c5-41cc-90e4-d4e81105ebe0`) + ' ' + Platform.shared.config.name,
957
965
  });
958
966
  }
959
967
  else {
960
968
  userStruct = NamedObject.create({
961
969
  id: '',
962
- name: 'Onbekend',
970
+ name: $t(`bd1e59c8-3d4c-4097-ab35-0ce7b20d0e50`),
963
971
  });
964
972
  }
965
973
  }
@@ -978,7 +986,7 @@ export class AuthenticatedStructures {
978
986
  const org = organizations.find(o => o.id === log.organizationId);
979
987
  replacements.set('org', AuditLogReplacement.create({
980
988
  id: log.organizationId,
981
- value: org?.name ?? 'verwijderde vereniging',
989
+ value: org?.name ?? $t(`cc098cc0-d849-4808-b53a-0b99755b3f99`),
982
990
  type: AuditLogReplacementType.Organization,
983
991
  }));
984
992
  }
@@ -210,7 +210,7 @@ export class BuckarooHelper {
210
210
  throw new SimpleError({
211
211
  code: 'buckaroo_error',
212
212
  message: 'Failed to create payment',
213
- human: 'Er ging iets mis bij het starten van de betaling. Herlaad de pagina en probeer het opnieuw.',
213
+ human: $t(`08f883bb-cdc7-4d50-a35e-e2fbeaf6f284`),
214
214
  });
215
215
  }
216
216
  }
@@ -23,7 +23,7 @@ type MolliePaymentJSON = {
23
23
  let lastSettlementCheck: Date | null = null;
24
24
 
25
25
  export async function checkAllStripePayouts(checkAll = false) {
26
- if (STAMHOOFD.environment !== 'production') {
26
+ if (STAMHOOFD.environment !== 'production' || !STAMHOOFD.STRIPE_SECRET_KEY) {
27
27
  console.log('Skip settlement check');
28
28
  return;
29
29
  }
@@ -4,29 +4,42 @@ import { I18n } from '@stamhoofd/backend-i18n';
4
4
  import { Organization, Platform, RateLimiter, Token, User } from '@stamhoofd/models';
5
5
  import { AsyncLocalStorage } from 'async_hooks';
6
6
 
7
+ import { AutoEncoder, Decoder, field, StringDecoder } from '@simonbackx/simple-encoding';
8
+ import { ApiUserRateLimits } from '@stamhoofd/structures';
7
9
  import { AdminPermissionChecker } from './AdminPermissionChecker';
8
- import { AutoEncoder, field, Decoder, StringDecoder } from '@simonbackx/simple-encoding';
9
10
 
10
11
  export const apiUserRateLimiter = new RateLimiter({
11
12
  limits: [
12
13
  {
13
- // Block heavy bursts (5req/s for 5s)
14
- limit: 25,
14
+ limit: {
15
+ '': 25, // (5req/s for 5s)
16
+ [ApiUserRateLimits.Medium]: 10 * 5, // (10req/s for 5s)
17
+ [ApiUserRateLimits.High]: 25 * 5, // (100req/s for 5s)
18
+ },
15
19
  duration: 5 * 1000,
16
20
  },
17
21
  {
18
- // max 1req/s during 150s
19
- limit: 150,
20
- duration: 150 * 1000,
22
+ limit: {
23
+ '': 120, // max 1req/s during 150s
24
+ [ApiUserRateLimits.Medium]: 240, // (2req/s for 150s)
25
+ [ApiUserRateLimits.High]: 480, // (4req/s for 150s)
26
+ },
27
+ duration: 120 * 1000,
21
28
  },
22
29
  {
23
- // 1000 requests per hour
24
- limit: 1000,
30
+ limit: {
31
+ '': 1000, // ± 0.27 request/s sustained for an hour = 3.6s between each request
32
+ [ApiUserRateLimits.Medium]: 2000, // ± 0.56 request/s sustained for an hour
33
+ [ApiUserRateLimits.High]: 4000, // ± 1.11 request/s sustained for an hour
34
+ },
25
35
  duration: 60 * 1000 * 60,
26
36
  },
27
37
  {
28
- // 2000 requests per day
29
- limit: 2000,
38
+ limit: {
39
+ '': 2_000, // max 2000 requests per day
40
+ [ApiUserRateLimits.Medium]: 14_400, // max 4000 requests per day
41
+ [ApiUserRateLimits.High]: 18_000, // max 10 requests per minute, sustained for a full day
42
+ },
30
43
  duration: 24 * 60 * 1000 * 60,
31
44
  },
32
45
  ],
@@ -169,7 +182,10 @@ export class ContextInstance {
169
182
  return await this.authenticate({ allowWithoutAccount });
170
183
  }
171
184
  catch (e) {
172
- return {};
185
+ if (e.code === 'not_authenticated') {
186
+ return {};
187
+ }
188
+ throw e;
173
189
  }
174
190
  }
175
191
 
@@ -210,7 +226,7 @@ export class ContextInstance {
210
226
  throw new SimpleError({
211
227
  code: 'invalid_access_token',
212
228
  message: 'The access token is invalid',
213
- human: 'Je bent automatisch uitgelogd, log opnieuw in om verder te gaan',
229
+ human: $t(`739f88f4-e87d-4872-aef3-8124a59b160c`),
214
230
  statusCode: 401,
215
231
  });
216
232
  }
@@ -219,7 +235,7 @@ export class ContextInstance {
219
235
  throw new SimpleError({
220
236
  code: 'expired_access_token',
221
237
  message: 'The access token is expired',
222
- human: 'Je bent automatisch uitgelogd, log opnieuw in om verder te gaan',
238
+ human: $t(`739f88f4-e87d-4872-aef3-8124a59b160c`),
223
239
  statusCode: 401,
224
240
  });
225
241
  }
@@ -228,14 +244,14 @@ export class ContextInstance {
228
244
  throw new SimpleError({
229
245
  code: 'not_activated',
230
246
  message: 'This user is not yet activated',
231
- human: 'Maak een account aan op dit e-mailadres om een wachtwoord in te stellen voor je inlogt.',
247
+ human: $t(`28cf3aaf-d6b3-4325-8b01-4c0e754034ed`),
232
248
  statusCode: 401,
233
249
  });
234
250
  }
235
251
 
236
252
  // Rate limits for api users
237
253
  if (token.user.isApiUser) {
238
- apiUserRateLimiter.track(this.organization?.id ?? token.user.id);
254
+ apiUserRateLimiter.track(this.organization?.id ?? token.user.id, 1, token.user.meta?.rateLimits ?? undefined);
239
255
  }
240
256
 
241
257
  const user = token.user;
@@ -61,7 +61,7 @@ export class FileCache {
61
61
  throw new SimpleError({
62
62
  code: 'invalid_file',
63
63
  message: 'Invalid file',
64
- human: 'Ongeldig bestand',
64
+ human: $t(`a175829f-1075-4e04-ab5f-8f65bb715635`),
65
65
  statusCode: 400,
66
66
  });
67
67
  }
@@ -73,7 +73,7 @@ export class FileCache {
73
73
  throw new SimpleError({
74
74
  code: 'invalid_file',
75
75
  message: 'Invalid file',
76
- human: 'Ongeldig bestand',
76
+ human: $t(`a175829f-1075-4e04-ab5f-8f65bb715635`),
77
77
  statusCode: 400,
78
78
  });
79
79
  }
@@ -86,7 +86,7 @@ export class FileCache {
86
86
  throw new SimpleError({
87
87
  code: 'invalid_file',
88
88
  message: 'Invalid file',
89
- human: 'Onbekend karakters in bestandsnaam',
89
+ human: $t(`d5d7b908-50a5-4b3e-94d2-1e54e549764b`),
90
90
  statusCode: 400,
91
91
  });
92
92
  }
@@ -99,7 +99,7 @@ export class FileCache {
99
99
  throw new SimpleError({
100
100
  code: 'invalid_file',
101
101
  message: 'Invalid file',
102
- human: 'Ongelidge datum',
102
+ human: $t(`594fab23-b257-4d51-8d7b-eb3e75bc48ba`),
103
103
  statusCode: 400,
104
104
  });
105
105
  }
@@ -111,7 +111,7 @@ export class FileCache {
111
111
  throw new SimpleError({
112
112
  code: 'invalid_file',
113
113
  message: 'Invalid file',
114
- human: 'Ongeldige datum',
114
+ human: $t(`796645b6-ded0-4d1b-a439-0062f9b6edc1`),
115
115
  statusCode: 400,
116
116
  });
117
117
  }
@@ -126,7 +126,7 @@ export class FileCache {
126
126
  throw new SimpleError({
127
127
  code: 'file_expired',
128
128
  message: 'File expired',
129
- human: 'Het bestand is verlopen',
129
+ human: $t(`8a9a35d5-ff86-48e3-81e1-42cf85708e06`),
130
130
  statusCode: 404,
131
131
  });
132
132
  }
@@ -142,7 +142,7 @@ export class FileCache {
142
142
  throw new SimpleError({
143
143
  code: 'file_expired',
144
144
  message: 'File expired',
145
- human: 'Het bestand bestaat niet',
145
+ human: $t(`d445388b-9062-488b-9fa7-d15682e26fd4`),
146
146
  statusCode: 404,
147
147
  });
148
148
  }
@@ -67,7 +67,7 @@ export class ForwardHandler {
67
67
  let defaultEmail: EmailInterfaceRecipient[] = [Email.getWebmasterToEmail()];
68
68
  let organizationEmails: EmailInterfaceRecipient[] = [];
69
69
  const platform = await Platform.getShared();
70
- const extraDescription = 'Dit bericht werd verstuurd naar ' + email + ', en werd automatisch doorgestuurd naar alle beheerders. Stel in ' + platform.config.name + ' de e-mailadressen in om ervoor te zorgen dat antwoorden naar een specifiek e-mailadres worden verstuurd.';
70
+ const extraDescription = $t(`24bc9aad-bc92-4d27-bfcd-055113d792fa`) + ' ' + email + $t(`97b9b042-c5b6-42dc-8238-e0e8392fcf26`) + ' ' + platform.config.name + ' ' + $t(`f510fb0c-c180-455a-8f23-7e09e344e47a`);
71
71
 
72
72
  function doBounce() {
73
73
  if (!from) {
@@ -1,7 +1,6 @@
1
1
  import { I18n } from '@stamhoofd/backend-i18n';
2
2
  import { FileSignService } from '../services/FileSignService';
3
3
  import { Context, ContextInstance } from './Context';
4
- import { Address, Country } from '@stamhoofd/structures';
5
4
  import { MemberRecordStore } from '../services/MemberRecordStore';
6
5
 
7
6
  export class GlobalHelper {
@@ -13,8 +12,11 @@ export class GlobalHelper {
13
12
  }
14
13
 
15
14
  private static loadGlobalTranslateFunction() {
16
- (global as any).$t = (key: string, replace?: Record<string, string>) => Context.i18n.$t(key, replace);
17
- (global as any).$getLanguage = () => ContextInstance.optional?.i18n.language ?? 'nl';
18
- (global as any).$getCountry = () => ContextInstance.optional?.i18n.country ?? STAMHOOFD.fixedCountry ?? Country.Belgium;
15
+ function getI18n() {
16
+ return ContextInstance.optional?.i18n ?? new I18n(I18n.defaultLanguage, STAMHOOFD.fixedCountry ?? I18n.defaultCountry);
17
+ }
18
+ (global as any).$t = (key: string, replace?: Record<string, string>) => getI18n().$t(key, replace);
19
+ (global as any).$getLanguage = () => getI18n().language;
20
+ (global as any).$getCountry = () => getI18n().country;
19
21
  }
20
22
  }
@@ -18,7 +18,7 @@ export const MembershipCharger = {
18
18
  throw new SimpleError({
19
19
  code: 'missing_membership_organization',
20
20
  message: 'Missing membershipOrganizationId',
21
- human: 'Er is geen lokale groep verantwoordelijk voor de aanrekening van aansluitingen geconfigureerd',
21
+ human: $t(`dd9d20ea-cf50-46be-8eb3-5c85bc5f30c8`),
22
22
  });
23
23
  }
24
24
 
@@ -98,7 +98,7 @@ export const MembershipCharger = {
98
98
  const balanceItem = new BalanceItem();
99
99
  balanceItem.unitPrice = membership.price;
100
100
  balanceItem.amount = 1;
101
- balanceItem.description = Formatter.dateNumber(membership.startDate, true) + ' tot ' + Formatter.dateNumber(membership.expireDate ?? membership.endDate, true);
101
+ balanceItem.description = Formatter.dateNumber(membership.startDate, true) + ' ' + $t(`3e515054-91e7-43ed-a9ce-563b626f337d`) + ' ' + Formatter.dateNumber(membership.expireDate ?? membership.endDate, true);
102
102
  balanceItem.relations = new Map([
103
103
  [
104
104
  BalanceItemRelationType.Member,
@@ -176,7 +176,7 @@ export class SetupStepUpdater {
176
176
  throw new SimpleError({
177
177
  code: 'not_found',
178
178
  message: 'Organization not found',
179
- human: 'De organisatie werd niet gevonden',
179
+ human: $t(`e73b24c3-584d-4a0c-beae-c50f46c33d90`),
180
180
  });
181
181
  }
182
182
 
@@ -6,7 +6,18 @@ import { Formatter } from '@stamhoofd/utility';
6
6
  import Stripe from 'stripe';
7
7
 
8
8
  export class StripeHelper {
9
+ static get notConfiguredError() {
10
+ return new SimpleError({
11
+ code: 'not_configured',
12
+ message: 'Stripe is not yet configured for this platform',
13
+ human: $t('4f1361c2-c860-46e7-a242-933bfe56a7ed'),
14
+ });
15
+ }
16
+
9
17
  static getInstance(accountId: string | null = null) {
18
+ if (!STAMHOOFD.STRIPE_SECRET_KEY) {
19
+ throw this.notConfiguredError;
20
+ }
10
21
  return new Stripe(STAMHOOFD.STRIPE_SECRET_KEY, { apiVersion: '2024-06-20', typescript: true, maxNetworkRetries: 0, timeout: 10000, stripeAccount: accountId ?? undefined });
11
22
  }
12
23
 
@@ -81,7 +92,7 @@ export class StripeHelper {
81
92
  }
82
93
 
83
94
  static async getStatus(payment: Payment, cancel = false, testMode = false): Promise<PaymentStatus> {
84
- if (testMode && !STAMHOOFD.STRIPE_SECRET_KEY.startsWith('sk_test_')) {
95
+ if (testMode && !STAMHOOFD.STRIPE_SECRET_KEY?.startsWith('sk_test_')) {
85
96
  // Do not query anything
86
97
  return payment.status;
87
98
  }
@@ -204,7 +215,7 @@ export class StripeHelper {
204
215
  if (!stripeAccount) {
205
216
  throw new SimpleError({
206
217
  code: '',
207
- message: 'Betaling via ' + PaymentMethodHelper.getName(payment.method) + ' is onbeschikbaar',
218
+ message: $t(`b77e1f68-8928-42a2-802b-059fa73bedc3`, { method: PaymentMethodHelper.getName(payment.method) }),
208
219
  });
209
220
  }
210
221
 
@@ -214,7 +225,7 @@ export class StripeHelper {
214
225
  throw new SimpleError({
215
226
  code: 'minmum_amount',
216
227
  message: 'The minimum amount for an online payment is € 0,50',
217
- human: 'Het minimum bedrag voor een online betaling is € 0,50',
228
+ human: $t(`dae9058f-0aa7-4fcb-9f1d-fc918c65784b`),
218
229
  });
219
230
  }
220
231
 
@@ -256,7 +267,7 @@ export class StripeHelper {
256
267
  const paymentMethod = await stripe.paymentMethods.create({
257
268
  type: payment.method.toLowerCase() as 'bancontact' | 'ideal',
258
269
  billing_details: {
259
- name: payment.customer?.dynamicName || (customer.name.length > 2 ? customer.name : 'Onbekend'),
270
+ name: payment.customer?.dynamicName || (customer.name.length > 2 ? customer.name : $t(`bd1e59c8-3d4c-4097-ab35-0ce7b20d0e50`)),
260
271
  email: payment.customer?.dynamicEmail || customer.email,
261
272
  address: payment.customer?.company?.address
262
273
  ? {
@@ -295,7 +306,7 @@ export class StripeHelper {
295
306
  console.error('Stripe payment intent status is not requires_action', paymentIntent);
296
307
  throw new SimpleError({
297
308
  code: 'invalid_status',
298
- message: 'Betaling via ' + PaymentMethodHelper.getName(payment.method) + ' is onbeschikbaar',
309
+ message: $t(`55d699ae-3da4-45ca-a30e-0f194b08edf7`) + ' ' + PaymentMethodHelper.getName(payment.method) + ' ' + $t(`277dc49e-922a-444f-ba2b-b3442d855358`),
299
310
  });
300
311
  }
301
312
 
@@ -336,7 +347,7 @@ export class StripeHelper {
336
347
  throw new SimpleError({
337
348
  code: 'invalid_price',
338
349
  message: 'De totale prijs van de betaling komt niet overeen met de prijs van de items',
339
- human: 'Er ging iets mis bij het aanmaken van de betaling. Probeer opnieuw of gebruik een andere betaalmethode.',
350
+ human: $t(`e66b54d3-70fb-4b40-b3e5-21bc795ba704`),
340
351
  statusCode: 500,
341
352
  });
342
353
  }
@@ -378,7 +389,7 @@ export class StripeHelper {
378
389
  console.error('Stripe session has no url', session);
379
390
  throw new SimpleError({
380
391
  code: 'invalid_status',
381
- message: 'Betaling via ' + PaymentMethodHelper.getName(payment.method) + ' is onbeschikbaar',
392
+ message: $t(`55d699ae-3da4-45ca-a30e-0f194b08edf7`) + ' ' + PaymentMethodHelper.getName(payment.method) + ' ' + $t(`277dc49e-922a-444f-ba2b-b3442d855358`),
382
393
  });
383
394
  }
384
395
  paymentUrl = session.url;
@@ -4,11 +4,11 @@ import { Address, CountryHelper, Parent, ParentTypeHelper, PlatformMember, Recor
4
4
  export class XlsxTransformerColumnHelper {
5
5
  static formatBoolean(value: boolean | undefined | null): string {
6
6
  if (value === true) {
7
- return 'Ja';
7
+ return $t(`1ae8cbc7-9ef5-43db-b9a3-0117dfa43be1`);
8
8
  }
9
9
 
10
10
  if (value === false) {
11
- return 'Nee';
11
+ return $t(`b8b730fb-f1a3-4c13-8ec4-0aebe08a1449`);
12
12
  }
13
13
 
14
14
  return '';
@@ -48,7 +48,7 @@ export class XlsxTransformerColumnHelper {
48
48
  return [
49
49
  {
50
50
  id: getId('type'),
51
- name: getName('Type'),
51
+ name: getName($t(`f97ad8c1-31d2-4b61-9e09-3be86eaeba08`)),
52
52
  width: 20,
53
53
  getValue: (member: PlatformMember) => {
54
54
  const parent = getParent(member);
@@ -60,7 +60,7 @@ export class XlsxTransformerColumnHelper {
60
60
  },
61
61
  {
62
62
  id: getId('firstName'),
63
- name: getName('Voornaam'),
63
+ name: getName($t(`efca0579-0543-4636-a996-384bc9f0527e`)),
64
64
  width: 20,
65
65
  getValue: (member: PlatformMember) => ({
66
66
  value: getParent(member)?.firstName ?? '',
@@ -68,7 +68,7 @@ export class XlsxTransformerColumnHelper {
68
68
  },
69
69
  {
70
70
  id: getId('lastName'),
71
- name: getName('Achternaam'),
71
+ name: getName($t(`4a5e438e-08a1-411e-9b66-410eea7ded73`)),
72
72
  width: 20,
73
73
  getValue: (member: PlatformMember) => ({
74
74
  value: getParent(member)?.lastName ?? '',
@@ -76,7 +76,7 @@ export class XlsxTransformerColumnHelper {
76
76
  },
77
77
  {
78
78
  id: getId('phone'),
79
- name: getName('Telefoonnummer'),
79
+ name: getName($t(`856aaa1c-bc62-4e45-9ae5-4c7e7dca23ab`)),
80
80
  width: 20,
81
81
  getValue: (member: PlatformMember) => ({
82
82
  value: getParent(member)?.phone ?? '',
@@ -84,7 +84,7 @@ export class XlsxTransformerColumnHelper {
84
84
  },
85
85
  {
86
86
  id: getId('email'),
87
- name: getName('E-mailadres'),
87
+ name: getName($t(`82f4b6ed-afee-4655-9f07-22802e0e7ad9`)),
88
88
  width: 20,
89
89
  getValue: (member: PlatformMember) => ({
90
90
  value: getParent(member)?.email ?? '',
@@ -92,7 +92,7 @@ export class XlsxTransformerColumnHelper {
92
92
  },
93
93
  {
94
94
  id: getId('nationalRegisterNumber'),
95
- name: getName('Rijksregisternummer'),
95
+ name: getName($t(`00881b27-7501-4c56-98de-55618be2bf11`)),
96
96
  width: 20,
97
97
  getValue: (member: PlatformMember) => ({
98
98
  value: getParent(member)?.nationalRegisterNumber?.toString() ?? '',
@@ -115,7 +115,7 @@ export class XlsxTransformerColumnHelper {
115
115
  {
116
116
  id: getId('street'),
117
117
  name: `Straat`,
118
- defaultCategory: 'Adres', // Ignore this name
118
+ defaultCategory: $t(`2f10996e-ea97-4345-b997-c93198c7d67f`), // Ignore this name
119
119
  width: 40,
120
120
  getValue: (object: T) => {
121
121
  const address = getAddress(object);
@@ -126,8 +126,8 @@ export class XlsxTransformerColumnHelper {
126
126
  },
127
127
  {
128
128
  id: getId('number'),
129
- name: 'Nummer',
130
- defaultCategory: 'Adres', // Ignore this name
129
+ name: $t(`cc1cf4a7-0bd2-4fa7-8ff2-0a12470a738d`),
130
+ defaultCategory: $t(`2f10996e-ea97-4345-b997-c93198c7d67f`), // Ignore this name
131
131
  width: 20,
132
132
  getValue: (object: T) => {
133
133
  const address = getAddress(object);
@@ -138,8 +138,8 @@ export class XlsxTransformerColumnHelper {
138
138
  },
139
139
  {
140
140
  id: getId('postalCode'),
141
- name: 'Postcode',
142
- defaultCategory: 'Adres', // Ignore this name
141
+ name: $t(`dafc7b04-dfb2-4dbc-8bcf-f7e9c6356442`),
142
+ defaultCategory: $t(`2f10996e-ea97-4345-b997-c93198c7d67f`), // Ignore this name
143
143
  width: 20,
144
144
  getValue: (object: T) => {
145
145
  const address = getAddress(object);
@@ -150,8 +150,8 @@ export class XlsxTransformerColumnHelper {
150
150
  },
151
151
  {
152
152
  id: getId('city'),
153
- name: 'Stad',
154
- defaultCategory: 'Adres', // Ignore this name
153
+ name: $t(`3d538399-3585-4be6-b03d-c12afa7183e8`),
154
+ defaultCategory: $t(`2f10996e-ea97-4345-b997-c93198c7d67f`), // Ignore this name
155
155
  width: 20,
156
156
  getValue: (object: T) => {
157
157
  const address = getAddress(object);
@@ -162,8 +162,8 @@ export class XlsxTransformerColumnHelper {
162
162
  },
163
163
  {
164
164
  id: getId('country'),
165
- name: 'Land',
166
- defaultCategory: 'Adres', // Ignore this name
165
+ name: $t(`cce830e0-6c05-405f-a800-4c217dc3235f`),
166
+ defaultCategory: $t(`2f10996e-ea97-4345-b997-c93198c7d67f`), // Ignore this name
167
167
  width: 20,
168
168
  getValue: (object: T) => {
169
169
  const address = getAddress(object);
@@ -215,7 +215,7 @@ export class XlsxTransformerColumnHelper {
215
215
  name,
216
216
  width: width ?? 20,
217
217
  defaultCategory,
218
- category: recordCategory.name,
218
+ category: recordCategory.name.toString(),
219
219
  getValue: (object: T) => {
220
220
  const answers = getRecordAnswers(object);
221
221
  const b = (answers.get(recordSettingId)?.excelValues[index] ?? {
@@ -7,7 +7,7 @@ function getGroupFieldsAffectingDocuments(group: Group): PlainObject {
7
7
  type: group.type,
8
8
  startDate: group.settings.startDate.getTime(),
9
9
  endDate: group.settings.endDate.getTime(),
10
- name: group.settings.name,
10
+ name: group.settings.name.toString(),
11
11
  };
12
12
  }
13
13
 
@@ -132,7 +132,7 @@ export class EventNotificationService {
132
132
  }
133
133
  const events = EventNotification.events.isLoaded(notification) ? notification.events : await EventNotification.events.load(notification);
134
134
  const type = await this.validateType(notification);
135
- let submitterName = 'Anoniem';
135
+ let submitterName = $t(`95c51d5c-0945-4fcf-90e9-764940e7f54d`);
136
136
 
137
137
  if (notification.submittedBy) {
138
138
  const user = await User.getByID(notification.submittedBy);
@@ -36,7 +36,7 @@ export class MemberNumberService {
36
36
  throw new SimpleError({
37
37
  code: 'assign_member_number',
38
38
  message: error.message,
39
- human: 'Er is iets misgegaan bij het aanmaken van het lidnummer.',
39
+ human: $t(`3a2c3e9d-4ac8-44a1-9690-98e8e4623298`),
40
40
  });
41
41
  }
42
42
  }
@@ -61,7 +61,7 @@ export class MemberNumberService {
61
61
  throw new SimpleError({
62
62
  code: 'assign_member_number',
63
63
  message: 'Missing birthDay',
64
- human: 'Er kon geen lidnummer aangemaakt worden omdat er geen geboortedatum is ingesteld.',
64
+ human: $t(`3e6429f3-1fc2-42ad-b585-4da7da164267`),
65
65
  });
66
66
  }
67
67
 
@@ -123,7 +123,7 @@ export class MemberNumberService {
123
123
  throw new SimpleError({
124
124
  code: 'assign_member_number',
125
125
  message: `Duplicate member numbers (last try: ${memberNumber}, tries: ${tries})`,
126
- human: 'Er kon geen uniek lidnummer aangemaakt worden. Mogelijks zijn er teveel leden met dezelfde geboortedatum. Neem contact op met de vereniging.',
126
+ human: $t(`49742012-1ca8-4b91-a176-9ce3e17c1fe0`),
127
127
  });
128
128
  }
129
129
  }
@@ -204,7 +204,7 @@ export class SSOService {
204
204
  throw new SimpleError({
205
205
  code: 'invalid_user',
206
206
  message: 'User not allowed to use this login method',
207
- human: 'Je kan deze inlogmethode niet gebruiken',
207
+ human: $t(`6180b5ee-b9c1-401d-89c1-c18d8ab77d74`),
208
208
  statusCode: 400,
209
209
  });
210
210
  }
@@ -463,7 +463,7 @@ export class SSOService {
463
463
  return response;
464
464
  }
465
465
  catch (e) {
466
- const message = (isSimpleError(e) || isSimpleErrors(e) ? e.getHuman() : 'Er ging iets mis.');
466
+ const message = (isSimpleError(e) || isSimpleErrors(e) ? e.getHuman() : $t(`bcfb1217-01fa-4116-b0bc-54c8c6dae284`));
467
467
  console.error('Error in openID callback', e);
468
468
  return SSOServiceWithSession.getErrorRedirectResponse(session, message);
469
469
  }
@@ -574,7 +574,7 @@ export class SSOServiceWithSession {
574
574
  throw new SimpleError({
575
575
  code: 'invalid_user',
576
576
  message: 'User not found: please log in with the same email address as the user you are trying to link',
577
- human: 'Je moet inloggen met hetzelfde e-mailadres als het account dat je probeert te koppelen',
577
+ human: $t(`3321ecb9-7600-498f-8ab9-b1625197804a`),
578
578
  statusCode: 404,
579
579
  });
580
580
  }
@@ -628,7 +628,7 @@ export class SSOServiceWithSession {
628
628
  throw new SimpleError({
629
629
  code: 'error',
630
630
  message: 'Could not generate token',
631
- human: 'Er ging iets mis bij het aanmelden',
631
+ human: $t(`f40ddd3d-a986-4ec1-9db8-32ec1376c4e8`),
632
632
  statusCode: 500,
633
633
  });
634
634
  }
@@ -648,7 +648,7 @@ export class SSOServiceWithSession {
648
648
  return response;
649
649
  }
650
650
  catch (e) {
651
- const message = (isSimpleError(e) || isSimpleErrors(e) ? e.getHuman() : 'Er ging iets mis.');
651
+ const message = (isSimpleError(e) || isSimpleErrors(e) ? e.getHuman() : $t(`bcfb1217-01fa-4116-b0bc-54c8c6dae284`));
652
652
  console.error('Error in openID callback', e);
653
653
  return SSOServiceWithSession.getErrorRedirectResponse(session, message);
654
654
  }
@@ -59,7 +59,7 @@ export const memberFilterCompilers: SQLFilterDefinitions = {
59
59
  throw new SimpleError({
60
60
  code: 'permission_denied',
61
61
  message: 'No permissions for financial support filter (organization scope).',
62
- human: 'Je hebt geen toegangsrechten om deze filter te gebruiken.',
62
+ human: $t(`64d658fa-0727-4924-9448-b243fe8e10a1`),
63
63
  statusCode: 400,
64
64
  });
65
65
  }