@stamhoofd/backend 2.90.3 → 2.92.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 (47) hide show
  1. package/package.json +10 -10
  2. package/src/audit-logs/EmailLogger.ts +4 -4
  3. package/src/audit-logs/ModelLogger.ts +0 -1
  4. package/src/crons/endFunctionsOfUsersWithoutRegistration.ts +20 -0
  5. package/src/endpoints/admin/organizations/GetOrganizationsEndpoint.ts +2 -0
  6. package/src/endpoints/global/email/CreateEmailEndpoint.ts +30 -7
  7. package/src/endpoints/global/email/GetAdminEmailsEndpoint.ts +207 -0
  8. package/src/endpoints/global/email/GetEmailEndpoint.ts +5 -1
  9. package/src/endpoints/global/email/PatchEmailEndpoint.test.ts +404 -8
  10. package/src/endpoints/global/email/PatchEmailEndpoint.ts +67 -22
  11. package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.ts +6 -4
  12. package/src/endpoints/global/platform/PatchPlatformEnpoint.ts +9 -7
  13. package/src/endpoints/global/registration/RegisterMembersEndpoint.ts +112 -105
  14. package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.ts +1 -1
  15. package/src/endpoints/organization/dashboard/organization/SetUitpasClientCredentialsEndpoint.ts +5 -5
  16. package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.ts +1 -1
  17. package/src/endpoints/organization/dashboard/webshops/DeleteWebshopEndpoint.ts +10 -1
  18. package/src/endpoints/organization/dashboard/webshops/PatchWebshopOrdersEndpoint.ts +8 -1
  19. package/src/endpoints/organization/dashboard/webshops/SearchUitpasEventsEndpoint.ts +1 -1
  20. package/src/endpoints/organization/webshops/PlaceOrderEndpoint.ts +2 -67
  21. package/src/helpers/AdminPermissionChecker.ts +81 -10
  22. package/src/helpers/FlagMomentCleanup.ts +13 -1
  23. package/src/helpers/GroupedThrottledQueue.ts +5 -3
  24. package/src/helpers/PeriodHelper.ts +10 -137
  25. package/src/helpers/SetupStepUpdater.ts +54 -7
  26. package/src/helpers/UitpasTokenRepository.ts +3 -3
  27. package/src/seeds/1750090030-records-configuration.ts +5 -1
  28. package/src/seeds/1752848560-groups-registration-periods.ts +768 -0
  29. package/src/seeds/1755181288-remove-duplicate-members.ts +145 -0
  30. package/src/seeds/1755532883-update-email-sender-ids.ts +47 -0
  31. package/src/services/BalanceItemService.ts +12 -7
  32. package/src/services/DocumentService.ts +0 -1
  33. package/src/services/RegistrationService.ts +30 -1
  34. package/src/services/uitpas/UitpasService.ts +72 -3
  35. package/src/services/uitpas/cancelTicketSales.ts +1 -1
  36. package/src/services/uitpas/checkPermissionsFor.ts +9 -9
  37. package/src/services/uitpas/checkUitpasNumbers.ts +3 -2
  38. package/src/services/uitpas/getSocialTariffForEvent.ts +4 -4
  39. package/src/services/uitpas/getSocialTariffForUitpasNumbers.ts +5 -5
  40. package/src/services/uitpas/registerTicketSales.ts +4 -4
  41. package/src/services/uitpas/searchUitpasEvents.ts +3 -3
  42. package/src/services/uitpas/searchUitpasOrganizers.ts +3 -3
  43. package/src/sql-filters/emails.ts +65 -0
  44. package/src/sql-filters/members.ts +1 -1
  45. package/src/sql-filters/organizations.ts +52 -0
  46. package/src/sql-sorters/emails.ts +47 -0
  47. package/tests/e2e/register.test.ts +1 -1
@@ -25,7 +25,7 @@ function assertIsOrganizersResponse(json: unknown): asserts json is OrganizersRe
25
25
  throw new SimpleError({
26
26
  code: 'invalid_response_searching_uitpas_organizers',
27
27
  message: `Invalid response when searching for UiTPAS organizers`,
28
- human: $t(`Er is een fout opgetreden bij het zoeken naar UiTPAS-organisaties. Probeer het later opnieuw.`),
28
+ human: $t(`e1aab1a7-fded-4e5e-b994-6607684764ac`),
29
29
  });
30
30
  }
31
31
  }
@@ -37,7 +37,7 @@ export async function searchUitpasOrganizers(access_token: string, name: string)
37
37
  throw new SimpleError({
38
38
  code: 'empty_uitpas_organizer_name',
39
39
  message: `Empty name when searching for UiTPAS organizers`,
40
- human: $t(`Je moet een naam opgeven om UiTPAS-organisaties te zoeken.`),
40
+ human: $t(`963105d8-a70a-4ba4-9ddc-b7f908d44664`),
41
41
  });
42
42
  }
43
43
  const baseUrl = 'https://api-test.uitpas.be/organizers';
@@ -66,7 +66,7 @@ export async function searchUitpasOrganizers(access_token: string, name: string)
66
66
  throw new SimpleError({
67
67
  code: 'unsuccessful_response_searching_uitpas_organizers',
68
68
  message: `Unsuccessful response when searching for UiTPAS organizers`,
69
- human: $t(`Er is een fout opgetreden bij het verbinden met UiTPAS. Probeer het later opnieuw.`),
69
+ human: $t(`ed4e876c-6a40-49a7-ab65-2a4d5f31c13f`),
70
70
  });
71
71
  }
72
72
  const json = await response.json().catch(() => {
@@ -0,0 +1,65 @@
1
+ import { baseSQLFilterCompilers, createColumnFilter, SQL, SQLFilterDefinitions, SQLValueType } from '@stamhoofd/sql';
2
+
3
+ export const emailFilterCompilers: SQLFilterDefinitions = {
4
+ ...baseSQLFilterCompilers,
5
+ id: createColumnFilter({
6
+ expression: SQL.column('id'),
7
+ type: SQLValueType.String,
8
+ nullable: false,
9
+ }),
10
+ organizationId: createColumnFilter({
11
+ expression: SQL.column('organizationId'),
12
+ type: SQLValueType.String,
13
+ nullable: true,
14
+ }),
15
+ userId: createColumnFilter({
16
+ expression: SQL.column('userId'),
17
+ type: SQLValueType.String,
18
+ nullable: true,
19
+ }),
20
+ emailType: createColumnFilter({
21
+ expression: SQL.column('emailType'),
22
+ type: SQLValueType.String,
23
+ nullable: true,
24
+ }),
25
+ subject: createColumnFilter({
26
+ expression: SQL.column('subject'),
27
+ type: SQLValueType.String,
28
+ nullable: true,
29
+ }),
30
+ fromAddress: createColumnFilter({
31
+ expression: SQL.column('fromAddress'),
32
+ type: SQLValueType.String,
33
+ nullable: true,
34
+ }),
35
+ text: createColumnFilter({
36
+ expression: SQL.column('text'),
37
+ type: SQLValueType.String,
38
+ nullable: true,
39
+ }),
40
+ status: createColumnFilter({
41
+ expression: SQL.column('status'),
42
+ type: SQLValueType.String,
43
+ nullable: false,
44
+ }),
45
+ recipientStatus: createColumnFilter({
46
+ expression: SQL.column('recipientStatus'),
47
+ type: SQLValueType.String,
48
+ nullable: false,
49
+ }),
50
+ recipientCount: createColumnFilter({
51
+ expression: SQL.column('recipientCount'),
52
+ type: SQLValueType.Number,
53
+ nullable: true,
54
+ }),
55
+ createdAt: createColumnFilter({
56
+ expression: SQL.column('createdAt'),
57
+ type: SQLValueType.Datetime,
58
+ nullable: true,
59
+ }),
60
+ sentAt: createColumnFilter({
61
+ expression: SQL.column('sentAt'),
62
+ type: SQLValueType.Datetime,
63
+ nullable: true,
64
+ }),
65
+ };
@@ -457,7 +457,7 @@ export const memberFilterCompilers: SQLFilterDefinitions = {
457
457
  throw new SimpleError({
458
458
  code: 'permission_denied',
459
459
  message: 'No permissions for financial support filter (organization scope).',
460
- human: result.record ? $t(`Je hebt niet voldoende toegangsrechten om te filteren op {recordName}`, { recordName: result.record.name }) : $t(`Je hebt niet voldoende toegangsrechten om te filteren op dit gegevensveld`),
460
+ human: result.record ? $t(`3560487e-3f2c-4cc9-ad7f-4e9a0fc1bbb8`, { recordName: result.record.name }) : $t(`Je hebt niet voldoende toegangsrechten om te filteren op dit gegevensveld`),
461
461
  statusCode: 400,
462
462
  });
463
463
  }
@@ -187,4 +187,56 @@ export const organizationFilterCompilers: SQLFilterDefinitions = {
187
187
  }),
188
188
  },
189
189
  ),
190
+ companies: createExistsFilter(
191
+ /**
192
+ * There is a bug in MySQL 8 that is fixed in 9.3
193
+ * where EXISTS (select * from json_table(...)) does not work
194
+ * To fix this, we do a double select with join inside the select
195
+ * It is a bit slower, but it works for now.
196
+ */
197
+ SQL.select()
198
+ .from('organizations', 'innerOrganizations')
199
+ .join(
200
+ SQL.join(
201
+ SQL.jsonTable(
202
+ SQL.jsonValue(SQL.column('innerOrganizations', 'meta'), '$.value.companies'),
203
+ 'companies',
204
+ )
205
+ .addColumn(
206
+ 'companyNumber',
207
+ 'TEXT',
208
+ '$.companyNumber',
209
+ )
210
+ .addColumn(
211
+ 'VATNumber',
212
+ 'TEXT',
213
+ '$.VATNumber',
214
+ )
215
+ .addColumn(
216
+ 'name',
217
+ 'TEXT',
218
+ '$.name',
219
+ ),
220
+ ),
221
+ )
222
+ .where(SQL.column('innerOrganizations', 'id'), SQL.column('organizations', 'id')),
223
+ {
224
+ ...baseSQLFilterCompilers,
225
+ name: createColumnFilter({
226
+ expression: SQL.column('companies', 'name'),
227
+ type: SQLValueType.String,
228
+ nullable: true,
229
+ }),
230
+ companyNumber: createColumnFilter({
231
+ expression: SQL.column('companies', 'companyNumber'),
232
+ type: SQLValueType.String,
233
+ nullable: true,
234
+ }),
235
+ VATNumber: createColumnFilter({
236
+ expression: SQL.column('companies', 'VATNumber'),
237
+ type: SQLValueType.String,
238
+ nullable: true,
239
+ }),
240
+ },
241
+ ),
190
242
  };
@@ -0,0 +1,47 @@
1
+ import { Email } from '@stamhoofd/models';
2
+ import { SQL, SQLOrderBy, SQLOrderByDirection, SQLSortDefinitions } from '@stamhoofd/sql';
3
+ import { Formatter } from '@stamhoofd/utility';
4
+
5
+ export const emailSorters: SQLSortDefinitions<Email> = {
6
+ // WARNING! TEST NEW SORTERS THOROUGHLY!
7
+ // Try to avoid creating sorters on fields that er not 1:1 with the database, that often causes pagination issues if not thought through
8
+ // An example: sorting on 'name' is not a good idea, because it is a concatenation of two fields.
9
+ // You might be tempted to use ORDER BY firstName, lastName, but that will not work as expected and it needs to be ORDER BY CONCAT(firstName, ' ', lastName)
10
+ // Why? Because ORDER BY firstName, lastName produces a different order dan ORDER BY CONCAT(firstName, ' ', lastName) if there are multiple people with spaces in the first name
11
+ // And that again causes issues with pagination because the next query will append a filter of name > 'John Doe' - causing duplicate and/or skipped results
12
+ // What if you need mapping? simply map the sorters in the frontend: name -> firstname, lastname, age -> birthDay, etc.
13
+
14
+ id: {
15
+ getValue(a) {
16
+ return a.id;
17
+ },
18
+ toSQL: (direction: SQLOrderByDirection): SQLOrderBy => {
19
+ return new SQLOrderBy({
20
+ column: SQL.column('id'),
21
+ direction,
22
+ });
23
+ },
24
+ },
25
+ createdAt: {
26
+ getValue(a) {
27
+ return Formatter.dateTimeIso(a.createdAt, 'UTC');
28
+ },
29
+ toSQL: (direction: SQLOrderByDirection): SQLOrderBy => {
30
+ return new SQLOrderBy({
31
+ column: SQL.column('createdAt'),
32
+ direction,
33
+ });
34
+ },
35
+ },
36
+ sentAt: {
37
+ getValue(a) {
38
+ return a.sentAt ? Formatter.dateTimeIso(a.sentAt, 'UTC') : null;
39
+ },
40
+ toSQL: (direction: SQLOrderByDirection): SQLOrderBy => {
41
+ return new SQLOrderBy({
42
+ column: SQL.column('sentAt'),
43
+ direction,
44
+ });
45
+ },
46
+ },
47
+ };
@@ -1195,7 +1195,7 @@ describe('E2E.Register', () => {
1195
1195
  expect(trialUntil).not.toBeNull();
1196
1196
  expect(trialUntil!.getFullYear()).toBe(2023);
1197
1197
  expect(trialUntil!.getMonth()).toBe(4);
1198
- expect(trialUntil!.getDate()).toBe(24);
1198
+ expect(trialUntil!.getDate()).toBe(19);
1199
1199
  }
1200
1200
  finally {
1201
1201
  jest.useRealTimers().resetAllMocks();