@stamhoofd/backend 2.93.0 → 2.95.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.
@@ -0,0 +1,34 @@
1
+ import { Migration } from '@simonbackx/simple-database';
2
+ import { logger } from '@simonbackx/simple-logging';
3
+ import { Organization, Platform, RegistrationPeriod } from '@stamhoofd/models';
4
+
5
+ export default new Migration(async () => {
6
+ if (STAMHOOFD.environment == 'test') {
7
+ console.log('skipped in tests');
8
+ return;
9
+ }
10
+
11
+ process.stdout.write('\n');
12
+
13
+ await logger.setContext({ tags: ['seed'] }, async () => {
14
+ if (STAMHOOFD.userMode === 'platform') {
15
+ await RegistrationPeriod.updatePreviousNextPeriods(null);
16
+ }
17
+ else {
18
+ for await (const organization of Organization.select().all()) {
19
+ await RegistrationPeriod.updatePreviousNextPeriods(organization.id);
20
+ }
21
+ }
22
+ });
23
+ console.log('Updated periods');
24
+
25
+ // Now update platform
26
+ const platform = await Platform.getForEditing();
27
+ await platform.setPreviousPeriodId();
28
+ await platform.save();
29
+
30
+ console.log('Updated platform');
31
+
32
+ // Do something here
33
+ return Promise.resolve();
34
+ });
@@ -0,0 +1,76 @@
1
+ import { Migration } from '@simonbackx/simple-database';
2
+ import { Email, EmailRecipient } from '@stamhoofd/models';
3
+ import { SQL, SQLAlias, SQLCount, SQLSelectAs } from '@stamhoofd/sql';
4
+ import { EmailStatus } from '@stamhoofd/structures';
5
+
6
+ export default new Migration(async () => {
7
+ if (STAMHOOFD.environment === 'test') {
8
+ console.log('skipped in tests');
9
+ return;
10
+ }
11
+
12
+ console.log('Start setting counts of emails.');
13
+
14
+ const batchSize = 100;
15
+ let count = 0;
16
+
17
+ for await (const email of Email.select()
18
+ .where('status', EmailStatus.Sent)
19
+ .where('succeededCount', 0)
20
+ .where('failedCount', 0)
21
+ .where('emailRecipientsCount', '!=', 0)
22
+ .where('emailRecipientsCount', '!=', null)
23
+ .where('createdAt', '<', new Date('2025-08-28 00:00:00')).limit(batchSize).all()) {
24
+ const query = SQL.select(
25
+ new SQLSelectAs(
26
+ new SQLCount(
27
+ SQL.column('failError'),
28
+ ),
29
+ new SQLAlias('data__failedCount'),
30
+ ),
31
+ // If the current amount_due is negative, we can ignore that negative part if there is a future due item
32
+ new SQLSelectAs(
33
+ new SQLCount(
34
+ SQL.column('sentAt'),
35
+ ),
36
+ new SQLAlias('data__succeededCount'),
37
+ ),
38
+ )
39
+ .from(EmailRecipient.table)
40
+ .where('emailId', email.id);
41
+
42
+ const result = await query.fetch();
43
+ if (result.length !== 1) {
44
+ console.error('Unexpected result', result);
45
+ continue;
46
+ }
47
+ const row = result[0]['data'];
48
+ if (!row) {
49
+ console.error('Unexpected result row', result);
50
+ continue;
51
+ }
52
+
53
+ let failedCount = row['failedCount'];
54
+ const succeededCount = row['succeededCount'];
55
+
56
+ if (typeof failedCount !== 'number' || typeof succeededCount !== 'number') {
57
+ console.error('Unexpected result values', row);
58
+ return;
59
+ }
60
+
61
+ if (email.emailRecipientsCount !== null && failedCount + succeededCount !== email.emailRecipientsCount) {
62
+ console.warn(`Email ${email.id} has ${email.emailRecipientsCount} recipients, but ${failedCount} failed and ${succeededCount} succeeded. Correcting failedCount to `, email.emailRecipientsCount - succeededCount);
63
+ failedCount = email.emailRecipientsCount - succeededCount;
64
+ }
65
+
66
+ // Send an update query
67
+ await Email.update()
68
+ .where('id', email.id)
69
+ .set('succeededCount', succeededCount)
70
+ .set('failedCount', failedCount)
71
+ .update();
72
+ count += 1;
73
+ }
74
+
75
+ console.log('Finished saving ' + count + ' emails.');
76
+ });
@@ -7,11 +7,31 @@ export const emailRecipientsFilterCompilers: SQLFilterDefinitions = {
7
7
  type: SQLValueType.String,
8
8
  nullable: false,
9
9
  }),
10
+ memberId: createColumnFilter({
11
+ expression: SQL.column('memberId'),
12
+ type: SQLValueType.String,
13
+ nullable: true,
14
+ }),
15
+ userId: createColumnFilter({
16
+ expression: SQL.column('userId'),
17
+ type: SQLValueType.String,
18
+ nullable: true,
19
+ }),
10
20
  email: createColumnFilter({
11
21
  expression: SQL.column('email'),
12
22
  type: SQLValueType.String,
13
23
  nullable: true,
14
24
  }),
25
+ firstName: createColumnFilter({
26
+ expression: SQL.column('firstName'),
27
+ type: SQLValueType.String,
28
+ nullable: true,
29
+ }),
30
+ lastName: createColumnFilter({
31
+ expression: SQL.column('lastName'),
32
+ type: SQLValueType.String,
33
+ nullable: true,
34
+ }),
15
35
  name: createColumnFilter({
16
36
  expression: new SQLConcat(
17
37
  SQL.column('firstName'),
@@ -26,6 +46,11 @@ export const emailRecipientsFilterCompilers: SQLFilterDefinitions = {
26
46
  type: SQLValueType.String,
27
47
  nullable: true,
28
48
  }),
49
+ duplicateOfRecipientId: createColumnFilter({
50
+ expression: SQL.column('duplicateOfRecipientId'),
51
+ type: SQLValueType.String,
52
+ nullable: true,
53
+ }),
29
54
  emailId: createColumnFilter({
30
55
  expression: SQL.column('emailId'),
31
56
  type: SQLValueType.String,
@@ -1,12 +1,32 @@
1
- import { baseSQLFilterCompilers, createColumnFilter, SQL, SQLFilterDefinitions, SQLValueType } from '@stamhoofd/sql';
1
+ import { baseSQLFilterCompilers, createColumnFilter, createExistsFilter, SQL, SQLFilterDefinitions, SQLValueType } from '@stamhoofd/sql';
2
+ import { emailRecipientsFilterCompilers } from './email-recipients';
2
3
 
3
- export const emailFilterCompilers: SQLFilterDefinitions = {
4
+ export const userEmailFilterCompilers: SQLFilterDefinitions = {
4
5
  ...baseSQLFilterCompilers,
5
6
  id: createColumnFilter({
6
7
  expression: SQL.column('id'),
7
8
  type: SQLValueType.String,
8
9
  nullable: false,
9
10
  }),
11
+ sentAt: createColumnFilter({
12
+ expression: SQL.column('sentAt'),
13
+ type: SQLValueType.Datetime,
14
+ nullable: true,
15
+ }),
16
+ senderId: createColumnFilter({
17
+ expression: SQL.column('senderId'),
18
+ type: SQLValueType.String,
19
+ nullable: true,
20
+ }),
21
+ };
22
+
23
+ export const emailFilterCompilers: SQLFilterDefinitions = {
24
+ ...userEmailFilterCompilers,
25
+ id: createColumnFilter({
26
+ expression: SQL.column('id'),
27
+ type: SQLValueType.String,
28
+ nullable: false,
29
+ }),
10
30
  organizationId: createColumnFilter({
11
31
  expression: SQL.column('organizationId'),
12
32
  type: SQLValueType.String,
@@ -92,4 +112,22 @@ export const emailFilterCompilers: SQLFilterDefinitions = {
92
112
  type: SQLValueType.String,
93
113
  nullable: true,
94
114
  }),
115
+ deletedAt: createColumnFilter({
116
+ expression: SQL.column('deletedAt'),
117
+ type: SQLValueType.Datetime,
118
+ nullable: true,
119
+ }),
120
+ recipients: createExistsFilter(
121
+ SQL.select()
122
+ .from(
123
+ SQL.table('email_recipients'),
124
+ )
125
+ .where(
126
+ SQL.column('emailId'),
127
+ SQL.parentColumn('id'),
128
+ ),
129
+ {
130
+ ...emailRecipientsFilterCompilers,
131
+ },
132
+ ),
95
133
  };
@@ -52,6 +52,16 @@ export const eventFilterCompilers: SQLFilterDefinitions = {
52
52
  type: SQLValueType.JSONArray,
53
53
  nullable: true,
54
54
  }),
55
+ 'minAge': createColumnFilter({
56
+ expression: SQL.jsonValue(SQL.column('meta'), '$.value.minAge'),
57
+ type: SQLValueType.Number,
58
+ nullable: true,
59
+ }),
60
+ 'maxAge': createColumnFilter({
61
+ expression: SQL.jsonValue(SQL.column('meta'), '$.value.maxAge'),
62
+ type: SQLValueType.Number,
63
+ nullable: true,
64
+ }),
55
65
  'meta.visible': createColumnFilter({
56
66
  expression: SQL.jsonValue(SQL.column('meta'), '$.value.visible'),
57
67
  type: SQLValueType.JSONBoolean,
@@ -1,55 +0,0 @@
1
- import { Migration } from '@simonbackx/simple-database';
2
- import { logger } from '@simonbackx/simple-logging';
3
- import { Platform, RegistrationPeriod } from '@stamhoofd/models';
4
-
5
- export default new Migration(async () => {
6
- if (STAMHOOFD.environment == 'test') {
7
- console.log('skipped in tests');
8
- return;
9
- }
10
-
11
- process.stdout.write('\n');
12
- let c = 0;
13
- let id: string = '';
14
-
15
- await logger.setContext({ tags: ['seed'] }, async () => {
16
- while (true) {
17
- const items = await RegistrationPeriod.where({
18
- id: {
19
- value: id,
20
- sign: '>',
21
- },
22
- }, { limit: 1000, sort: ['id'] });
23
-
24
- if (items.length === 0) {
25
- break;
26
- }
27
-
28
- process.stdout.write('.');
29
-
30
- for (const item of items) {
31
- await item.setPreviousPeriodId();
32
- if (await item.save()) {
33
- c += 1;
34
- }
35
- }
36
-
37
- if (items.length < 1000) {
38
- break;
39
- }
40
- id = items[items.length - 1].id;
41
- }
42
- });
43
-
44
- console.log('Updated ' + c + ' registration periods');
45
-
46
- // Now update platform
47
- const platform = await Platform.getForEditing();
48
- await platform.setPreviousPeriodId();
49
- await platform.save();
50
-
51
- console.log('Updated platform');
52
-
53
- // Do something here
54
- return Promise.resolve();
55
- });