@stamhoofd/backend 2.107.3 → 2.109.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 (44) hide show
  1. package/index.ts +2 -2
  2. package/package.json +20 -20
  3. package/src/crons/disable-auto-update-documents.test.ts +164 -0
  4. package/src/crons/disable-auto-update-documents.ts +82 -0
  5. package/src/endpoints/admin/members/ChargeMembersEndpoint.ts +5 -5
  6. package/src/endpoints/admin/registrations/ChargeRegistrationsEndpoint.ts +87 -0
  7. package/src/endpoints/global/members/GetMembersCountEndpoint.ts +2 -2
  8. package/src/endpoints/global/members/GetMembersEndpoint.ts +5 -5
  9. package/src/endpoints/global/registration/GetRegistrationsCountEndpoint.ts +2 -2
  10. package/src/endpoints/global/registration/GetRegistrationsEndpoint.ts +14 -11
  11. package/src/endpoints/global/registration/RegisterMembersEndpoint.ts +9 -8
  12. package/src/endpoints/organization/dashboard/documents/GetDocumentTemplatesCountEndpoint.ts +48 -0
  13. package/src/endpoints/organization/dashboard/documents/GetDocumentTemplatesEndpoint.ts +95 -19
  14. package/src/endpoints/organization/dashboard/documents/PatchDocumentTemplatesEndpoint.test.ts +282 -0
  15. package/src/endpoints/organization/dashboard/documents/{PatchDocumentTemplateEndpoint.ts → PatchDocumentTemplatesEndpoint.ts} +56 -3
  16. package/src/excel-loaders/members.ts +62 -8
  17. package/src/excel-loaders/registrations.ts +180 -9
  18. package/src/helpers/LimitedFilteredRequestHelper.ts +24 -0
  19. package/src/helpers/MemberCharger.ts +16 -4
  20. package/src/helpers/SQLTranslatedString.ts +14 -0
  21. package/src/helpers/TagHelper.test.ts +9 -9
  22. package/src/helpers/fetchToAsyncIterator.ts +1 -1
  23. package/src/helpers/outstandingBalanceJoin.ts +49 -0
  24. package/src/seeds/1765896674-document-update-year.test.ts +179 -0
  25. package/src/seeds/1765896674-document-update-year.ts +75 -0
  26. package/src/seeds/1766150402-document-published-at.test.ts +46 -0
  27. package/src/seeds/1766150402-document-published-at.ts +20 -0
  28. package/src/services/PaymentService.ts +14 -32
  29. package/src/sql-filters/base-registration-filter-compilers.ts +51 -4
  30. package/src/sql-filters/document-templates.ts +45 -0
  31. package/src/sql-filters/documents.ts +1 -1
  32. package/src/sql-filters/events.ts +6 -6
  33. package/src/sql-filters/groups.ts +7 -6
  34. package/src/sql-filters/members.ts +31 -26
  35. package/src/sql-filters/orders.ts +16 -16
  36. package/src/sql-filters/organizations.ts +11 -11
  37. package/src/sql-filters/payments.ts +10 -10
  38. package/src/sql-filters/registrations.ts +14 -6
  39. package/src/sql-sorters/document-templates.ts +79 -0
  40. package/src/sql-sorters/documents.ts +1 -1
  41. package/src/sql-sorters/members.ts +22 -0
  42. package/src/sql-sorters/orders.ts +5 -5
  43. package/src/sql-sorters/organizations.ts +3 -3
  44. package/src/sql-sorters/registrations.ts +186 -15
@@ -0,0 +1,46 @@
1
+ import { DocumentTemplate, DocumentTemplateFactory } from '@stamhoofd/models';
2
+ import { SQL } from '@stamhoofd/sql';
3
+ import { DocumentStatus } from '@stamhoofd/structures';
4
+ import { initPublishedAtForPublishedDocuments } from './1766150402-document-published-at.js';
5
+
6
+ describe('migration.document-published-at', () => {
7
+ it('should init published at for published documents', async () => {
8
+ // arrange
9
+ // published documents
10
+ const document1 = await createDocument(DocumentStatus.Published, new Date(2022, 0, 1));
11
+ const document2 = await createDocument(DocumentStatus.Published, new Date(2023, 5, 17));
12
+ const document3 = await createDocument(DocumentStatus.Published, new Date(2025, 3, 9));
13
+
14
+ // draft documents
15
+ const document4 = await createDocument(DocumentStatus.Draft, new Date(2022, 0, 1));
16
+ const document5 = await createDocument(DocumentStatus.Draft, new Date(2023, 5, 17));
17
+ const document6 = await createDocument(DocumentStatus.Draft, new Date(2025, 3, 9));
18
+
19
+ // reset published at (save does not update published at automatically)
20
+ await SQL.update(DocumentTemplate.table).set('publishedAt', null).where('id', [document1.id, document2.id, document3.id, document4.id, document5.id, document6.id]).update();
21
+
22
+ // act
23
+ await initPublishedAtForPublishedDocuments();
24
+
25
+ // assert
26
+ const updatedDocument1 = await DocumentTemplate.getByID(document1.id);
27
+ const updatedDocument2 = await DocumentTemplate.getByID(document2.id);
28
+ const updatedDocument3 = await DocumentTemplate.getByID(document3.id);
29
+
30
+ const updatedDocument4 = await DocumentTemplate.getByID(document4.id);
31
+ const updatedDocument5 = await DocumentTemplate.getByID(document5.id);
32
+ const updatedDocument6 = await DocumentTemplate.getByID(document6.id);
33
+
34
+ expect(updatedDocument1?.publishedAt?.getTime()).toEqual(document1.createdAt.getTime());
35
+ expect(updatedDocument2?.publishedAt?.getTime()).toEqual(document2.createdAt.getTime());
36
+ expect(updatedDocument3?.publishedAt?.getTime()).toEqual(document3.createdAt.getTime());
37
+
38
+ expect(updatedDocument4?.publishedAt).toBeNull();
39
+ expect(updatedDocument5?.publishedAt).toBeNull();
40
+ expect(updatedDocument6?.publishedAt).toBeNull();
41
+ });
42
+ });
43
+
44
+ async function createDocument(status: DocumentStatus, createdAt: Date) {
45
+ return await new DocumentTemplateFactory({ groups: [], status, createdAt }).create();
46
+ };
@@ -0,0 +1,20 @@
1
+ import { Migration } from '@simonbackx/simple-database';
2
+ import { DocumentTemplate } from '@stamhoofd/models';
3
+ import { SQL } from '@stamhoofd/sql';
4
+ import { DocumentStatus } from '@stamhoofd/structures';
5
+
6
+ export async function initPublishedAtForPublishedDocuments() {
7
+ await SQL.update(DocumentTemplate.table)
8
+ .set('publishedAt', SQL.column('createdAt'))
9
+ .where('publishedAt', null)
10
+ .where('status', DocumentStatus.Published)
11
+ .update();
12
+ };
13
+
14
+ export default new Migration(async () => {
15
+ process.stdout.write('\n');
16
+ console.log('Start init published at for published documents.');
17
+ await initPublishedAtForPublishedDocuments();
18
+
19
+ return Promise.resolve();
20
+ });
@@ -2,11 +2,11 @@ import createMollieClient, { PaymentStatus as MolliePaymentStatus } from '@molli
2
2
  import { BalanceItem, BalanceItemPayment, MolliePayment, MollieToken, Organization, PayconiqPayment, Payment } from '@stamhoofd/models';
3
3
  import { QueueHandler } from '@stamhoofd/queues';
4
4
  import { AuditLogSource, BalanceItemRelation, BalanceItemStatus, BalanceItemType, PaymentMethod, PaymentProvider, PaymentStatus } from '@stamhoofd/structures';
5
- import { BuckarooHelper } from '../helpers/BuckarooHelper';
6
- import { StripeHelper } from '../helpers/StripeHelper';
7
- import { AuditLogService } from './AuditLogService';
8
- import { BalanceItemPaymentService } from './BalanceItemPaymentService';
9
- import { BalanceItemService } from './BalanceItemService';
5
+ import { BuckarooHelper } from '../helpers/BuckarooHelper.js';
6
+ import { StripeHelper } from '../helpers/StripeHelper.js';
7
+ import { AuditLogService } from './AuditLogService.js';
8
+ import { BalanceItemPaymentService } from './BalanceItemPaymentService.js';
9
+ import { BalanceItemService } from './BalanceItemService.js';
10
10
 
11
11
  export const PaymentService = {
12
12
  async handlePaymentStatusUpdate(payment: Payment, organization: Organization, status: PaymentStatus) {
@@ -336,46 +336,28 @@ export const PaymentService = {
336
336
  * Say the total amount to pay is 15,238 because (e.g. because of VAT). In that case,
337
337
  * we'll need to round the payment to 1 cent. That can cause issues in the financial statements because
338
338
  * the total amount of balances does not match the total amount received/paid.
339
- *
339
+ *
340
340
  * To fix that, we create an extra balance item with the difference. So the rounding always matches.
341
+ *
342
+ * TODO: update this method to generate a virtual invoice and use the price of the invoice instead of the rounded payment price, so we don't get differences in calculation
341
343
  */
342
344
  async round(payment: Payment) {
343
- if (!payment.organizationId) {
344
- throw new Error('Cannot round payments without organizationId')
345
- }
346
-
347
345
  const amount = payment.price;
348
- const rounded = Payment.roundPrice(payment.price)
346
+ const rounded = Payment.roundPrice(payment.price);
349
347
  const difference = rounded - amount;
350
348
 
351
349
  if (difference === 0) {
352
350
  return;
353
351
  }
354
352
 
355
- if (difference > 100) {
356
- throw new Error('Unexpected rounding difference.')
353
+ if (difference > 100 || difference < -100) {
354
+ throw new Error('Unexpected rounding difference of ' + difference + ' for payment ' + payment.id);
357
355
  }
358
356
 
359
- const balanceItem = new BalanceItem();
360
- balanceItem.type = BalanceItemType.Rounding;
361
- balanceItem.userId = payment.payingUserId;
362
- balanceItem.payingOrganizationId = payment.payingOrganizationId
363
- balanceItem.unitPrice = difference;
364
- balanceItem.pricePaid = 0;
365
- balanceItem.organizationId = payment.organizationId;
366
- balanceItem.status = BalanceItemStatus.Hidden;
367
- await balanceItem.save();
368
-
369
- // Create balance item payment
370
- const balanceItemPayment = new BalanceItemPayment();
371
- balanceItemPayment.organizationId = payment.organizationId;
372
- balanceItemPayment.balanceItemId = balanceItem.id;
373
- balanceItemPayment.price = difference;
374
- balanceItemPayment.paymentId = payment.id;
375
- await balanceItemPayment.save();
357
+ // payment.roundingAmount = difference;
376
358
 
377
359
  // Change payment total price
378
- payment.price += difference
360
+ payment.price += difference;
379
361
  await payment.save();
380
- }
362
+ },
381
363
  };
@@ -1,8 +1,10 @@
1
1
  import { SimpleError } from '@simonbackx/simple-errors';
2
- import { baseSQLFilterCompilers, createColumnFilter, createExistsFilter, SQL, SQLFilterDefinitions, SQLValueType } from '@stamhoofd/sql';
2
+ import { baseSQLFilterCompilers, createColumnFilter, createExistsFilter, createJoinedRelationFilter, SQL, SQLFilterDefinitions, SQLIfNull, SQLValueType } from '@stamhoofd/sql';
3
3
  import { FilterWrapperMarker, PermissionLevel, StamhoofdFilter, unwrapFilter } from '@stamhoofd/structures';
4
- import { Context } from '../helpers/Context';
5
- import { organizationFilterCompilers } from './organizations';
4
+ import { Context } from '../helpers/Context.js';
5
+ import { memberCachedBalanceForOrganizationJoin, registrationCachedBalanceJoin } from '../helpers/outstandingBalanceJoin.js';
6
+ import { SQLTranslatedString } from '../helpers/SQLTranslatedString.js';
7
+ import { organizationFilterCompilers } from './organizations.js';
6
8
 
7
9
  async function checkGroupIdFilterAccess(filter: StamhoofdFilter, permissionLevel: PermissionLevel) {
8
10
  const groupIds = typeof filter === 'string'
@@ -77,6 +79,11 @@ export const baseRegistrationFilterCompilers: SQLFilterDefinitions = {
77
79
  type: SQLValueType.Number,
78
80
  nullable: false,
79
81
  }),
82
+ groupPrice: createColumnFilter({
83
+ expression: new SQLTranslatedString(SQL.column('groupPrice'), '$.value.name'),
84
+ type: SQLValueType.String,
85
+ nullable: true,
86
+ }),
80
87
  canRegister: createColumnFilter({
81
88
  expression: SQL.column('canRegister'),
82
89
  type: SQLValueType.Boolean,
@@ -110,6 +117,21 @@ export const baseRegistrationFilterCompilers: SQLFilterDefinitions = {
110
117
  type: SQLValueType.Datetime,
111
118
  nullable: true,
112
119
  }),
120
+ trialUntil: createColumnFilter({
121
+ expression: SQL.column('registrations', 'trialUntil'),
122
+ type: SQLValueType.Datetime,
123
+ nullable: true,
124
+ }),
125
+ startDate: createColumnFilter({
126
+ expression: SQL.column('registrations', 'startDate'),
127
+ type: SQLValueType.Datetime,
128
+ nullable: true,
129
+ }),
130
+ endDate: createColumnFilter({
131
+ expression: SQL.column('registrations', 'endDate'),
132
+ type: SQLValueType.Datetime,
133
+ nullable: true,
134
+ }),
113
135
  group: {
114
136
  ...baseSQLFilterCompilers,
115
137
  id: createColumnFilter({
@@ -126,7 +148,7 @@ export const baseRegistrationFilterCompilers: SQLFilterDefinitions = {
126
148
  nullable: false,
127
149
  }),
128
150
  name: createColumnFilter({
129
- expression: SQL.jsonValue(SQL.column('groups', 'settings'), '$.value.name'),
151
+ expression: SQL.jsonExtract(SQL.column('groups', 'settings'), '$.value.name'),
130
152
  type: SQLValueType.JSONString,
131
153
  nullable: false,
132
154
  }),
@@ -150,4 +172,29 @@ export const baseRegistrationFilterCompilers: SQLFilterDefinitions = {
150
172
  ),
151
173
  organizationFilterCompilers,
152
174
  ),
175
+ memberCachedBalance: createJoinedRelationFilter(
176
+ memberCachedBalanceForOrganizationJoin,
177
+ {
178
+ amountOpen: createColumnFilter({
179
+ expression: new SQLIfNull(SQL.column('memberCachedBalance', 'amountOpen'), 0),
180
+ type: SQLValueType.Number,
181
+ nullable: false,
182
+ }),
183
+ },
184
+ ),
185
+ registrationCachedBalance: createJoinedRelationFilter(
186
+ registrationCachedBalanceJoin,
187
+ {
188
+ toPay: createColumnFilter({
189
+ expression: new SQLIfNull(SQL.column('registrationCachedBalance', 'toPay'), 0),
190
+ type: SQLValueType.Number,
191
+ nullable: false,
192
+ }),
193
+ price: createColumnFilter({
194
+ expression: new SQLIfNull(SQL.column('registrationCachedBalance', 'price'), 0),
195
+ type: SQLValueType.Number,
196
+ nullable: false,
197
+ }),
198
+ },
199
+ ),
153
200
  };
@@ -0,0 +1,45 @@
1
+ import { baseSQLFilterCompilers, createColumnFilter, SQL, SQLFilterDefinitions, SQLValueType } from '@stamhoofd/sql';
2
+
3
+ export const documentTemplateFilterCompilers: SQLFilterDefinitions = {
4
+ ...baseSQLFilterCompilers,
5
+ id: createColumnFilter({
6
+ expression: SQL.column('id'),
7
+ type: SQLValueType.String,
8
+ nullable: false,
9
+ }),
10
+ name: createColumnFilter({
11
+ expression: SQL.jsonExtract(SQL.column('settings'), '$.value.name'),
12
+ type: SQLValueType.JSONString,
13
+ nullable: false,
14
+ }),
15
+ year: createColumnFilter({
16
+ expression: SQL.column('year'),
17
+ type: SQLValueType.Number,
18
+ nullable: false,
19
+ }),
20
+ organizationId: createColumnFilter({
21
+ expression: SQL.column('organizationId'),
22
+ type: SQLValueType.String,
23
+ nullable: false,
24
+ }),
25
+ updatedAt: createColumnFilter({
26
+ expression: SQL.column('updatedAt'),
27
+ type: SQLValueType.Datetime,
28
+ nullable: false,
29
+ }),
30
+ createdAt: createColumnFilter({
31
+ expression: SQL.column('createdAt'),
32
+ type: SQLValueType.Datetime,
33
+ nullable: false,
34
+ }),
35
+ status: createColumnFilter({
36
+ expression: SQL.column('status'),
37
+ type: SQLValueType.String,
38
+ nullable: false,
39
+ }),
40
+ type: createColumnFilter({
41
+ expression: SQL.jsonExtract(SQL.column('privateSettings'), '$.value.templateDefinition.type'),
42
+ type: SQLValueType.JSONString,
43
+ nullable: false,
44
+ }),
45
+ };
@@ -8,7 +8,7 @@ export const documentFilterCompilers: SQLFilterDefinitions = {
8
8
  nullable: false,
9
9
  }),
10
10
  description: createColumnFilter({
11
- expression: SQL.jsonValue(SQL.column('data'), '$.value.description'),
11
+ expression: SQL.jsonExtract(SQL.column('data'), '$.value.description'),
12
12
  type: SQLValueType.JSONString,
13
13
  nullable: false,
14
14
  }),
@@ -28,7 +28,7 @@ export const eventFilterCompilers: SQLFilterDefinitions = {
28
28
  nullable: false,
29
29
  }),
30
30
  'groupIds': createColumnFilter({
31
- expression: SQL.jsonValue(SQL.column('meta'), '$.value.groups[*].id'),
31
+ expression: SQL.jsonExtract(SQL.column('meta'), '$.value.groups[*].id'),
32
32
  type: SQLValueType.JSONArray,
33
33
  nullable: true,
34
34
  }),
@@ -43,27 +43,27 @@ export const eventFilterCompilers: SQLFilterDefinitions = {
43
43
  nullable: false,
44
44
  }),
45
45
  'defaultAgeGroupIds': createColumnFilter({
46
- expression: SQL.jsonValue(SQL.column('meta'), '$.value.defaultAgeGroupIds'),
46
+ expression: SQL.jsonExtract(SQL.column('meta'), '$.value.defaultAgeGroupIds'),
47
47
  type: SQLValueType.JSONArray,
48
48
  nullable: true,
49
49
  }),
50
50
  'organizationTagIds': createColumnFilter({
51
- expression: SQL.jsonValue(SQL.column('meta'), '$.value.organizationTagIds'),
51
+ expression: SQL.jsonExtract(SQL.column('meta'), '$.value.organizationTagIds'),
52
52
  type: SQLValueType.JSONArray,
53
53
  nullable: true,
54
54
  }),
55
55
  'minAge': createColumnFilter({
56
- expression: SQL.jsonValue(SQL.column('meta'), '$.value.minAge'),
56
+ expression: SQL.jsonExtract(SQL.column('meta'), '$.value.minAge'),
57
57
  type: SQLValueType.JSONNumber,
58
58
  nullable: true,
59
59
  }),
60
60
  'maxAge': createColumnFilter({
61
- expression: SQL.jsonValue(SQL.column('meta'), '$.value.maxAge'),
61
+ expression: SQL.jsonExtract(SQL.column('meta'), '$.value.maxAge'),
62
62
  type: SQLValueType.JSONNumber,
63
63
  nullable: true,
64
64
  }),
65
65
  'meta.visible': createColumnFilter({
66
- expression: SQL.jsonValue(SQL.column('meta'), '$.value.visible'),
66
+ expression: SQL.jsonExtract(SQL.column('meta'), '$.value.visible'),
67
67
  type: SQLValueType.JSONBoolean,
68
68
  nullable: false,
69
69
  }),
@@ -1,4 +1,5 @@
1
- import { baseSQLFilterCompilers, createColumnFilter, createWildcardColumnFilter, SQL, SQLJsonExtract, SQLFilterDefinitions, SQLValueType } from '@stamhoofd/sql';
1
+ import { baseSQLFilterCompilers, createColumnFilter, createWildcardColumnFilter, SQL, SQLFilterDefinitions, SQLJsonExtract, SQLValueType } from '@stamhoofd/sql';
2
+ import { SQLTranslatedString } from '../helpers/SQLTranslatedString.js';
2
3
 
3
4
  export const groupFilterCompilers: SQLFilterDefinitions = {
4
5
  ...baseSQLFilterCompilers,
@@ -18,9 +19,9 @@ export const groupFilterCompilers: SQLFilterDefinitions = {
18
19
  nullable: false,
19
20
  }),
20
21
  name: createColumnFilter({
21
- expression: SQL.jsonValue(SQL.column('settings'), '$.value.name'),
22
- type: SQLValueType.JSONString,
23
- nullable: false,
22
+ expression: new SQLTranslatedString(SQL.column('settings'), '$.value.name'),
23
+ type: SQLValueType.String,
24
+ nullable: true,
24
25
  }),
25
26
  status: createColumnFilter({
26
27
  expression: SQL.column('status'),
@@ -34,14 +35,14 @@ export const groupFilterCompilers: SQLFilterDefinitions = {
34
35
  }),
35
36
  bundleDiscounts: createWildcardColumnFilter(
36
37
  (key: string) => ({
37
- expression: SQL.jsonValue(SQL.column('settings'), `$.value.prices[*].bundleDiscounts.${SQLJsonExtract.escapePathComponent(key)}`, true),
38
+ expression: SQL.jsonExtract(SQL.column('settings'), `$.value.prices[*].bundleDiscounts.${SQLJsonExtract.escapePathComponent(key)}`, true),
38
39
  type: SQLValueType.JSONArray,
39
40
  nullable: true,
40
41
  }),
41
42
  (key: string) => ({
42
43
  ...baseSQLFilterCompilers,
43
44
  name: createColumnFilter({
44
- expression: SQL.jsonValue(SQL.column('settings'), `$.value.prices[*].bundleDiscounts.${SQLJsonExtract.escapePathComponent(key)}.name`, true),
45
+ expression: SQL.jsonExtract(SQL.column('settings'), `$.value.prices[*].bundleDiscounts.${SQLJsonExtract.escapePathComponent(key)}.name`, true),
45
46
  type: SQLValueType.JSONArray,
46
47
  nullable: true,
47
48
  }),
@@ -1,10 +1,10 @@
1
1
  import { SimpleError } from '@simonbackx/simple-errors';
2
2
  import { Email, Member } from '@stamhoofd/models';
3
- import { baseSQLFilterCompilers, createColumnFilter, createExistsFilter, SQL, SQLAge, SQLCast, SQLConcat, SQLFilterDefinitions, SQLValueType, SQLScalar, createWildcardColumnFilter, SQLJsonExtract } from '@stamhoofd/sql';
3
+ import { baseSQLFilterCompilers, createColumnFilter, createExistsFilter, createWildcardColumnFilter, SQL, SQLAge, SQLCast, SQLConcat, SQLFilterDefinitions, SQLJsonExtract, SQLScalar, SQLValueType } from '@stamhoofd/sql';
4
4
  import { AccessRight } from '@stamhoofd/structures';
5
- import { Context } from '../helpers/Context';
6
- import { baseRegistrationFilterCompilers } from './base-registration-filter-compilers';
7
- import { organizationFilterCompilers } from './organizations';
5
+ import { Context } from '../helpers/Context.js';
6
+ import { baseRegistrationFilterCompilers } from './base-registration-filter-compilers.js';
7
+ import { organizationFilterCompilers } from './organizations.js';
8
8
 
9
9
  const membersTable = SQL.table(Member.table);
10
10
 
@@ -20,7 +20,7 @@ export const memberFilterCompilers: SQLFilterDefinitions = {
20
20
  }),
21
21
  'memberNumber': createColumnFilter({
22
22
  expression: SQL.column(membersTable, 'memberNumber'),
23
- type: SQLValueType.Number,
23
+ type: SQLValueType.String,
24
24
  nullable: true,
25
25
  }),
26
26
  'firstName': createColumnFilter({
@@ -48,7 +48,7 @@ export const memberFilterCompilers: SQLFilterDefinitions = {
48
48
  nullable: true,
49
49
  }),
50
50
  'gender': createColumnFilter({
51
- expression: SQL.jsonValue(SQL.column(membersTable, 'details'), '$.value.gender'),
51
+ expression: SQL.jsonExtract(SQL.column(membersTable, 'details'), '$.value.gender'),
52
52
  type: SQLValueType.JSONString,
53
53
  nullable: false,
54
54
  }),
@@ -58,13 +58,18 @@ export const memberFilterCompilers: SQLFilterDefinitions = {
58
58
  type: SQLValueType.Datetime,
59
59
  nullable: true,
60
60
  }),
61
+ 'createdAt': createColumnFilter({
62
+ expression: SQL.column(membersTable, 'createdAt'),
63
+ type: SQLValueType.Datetime,
64
+ nullable: false,
65
+ }),
61
66
  'organizationName': createColumnFilter({
62
67
  expression: SQL.column('organizations', 'name'),
63
68
  type: SQLValueType.String,
64
69
  nullable: false,
65
70
  }),
66
71
  'details.requiresFinancialSupport': createColumnFilter({
67
- expression: SQL.jsonValue(SQL.column(membersTable, 'details'), '$.value.requiresFinancialSupport.value'),
72
+ expression: SQL.jsonExtract(SQL.column(membersTable, 'details'), '$.value.requiresFinancialSupport.value'),
68
73
  type: SQLValueType.JSONBoolean,
69
74
  nullable: true,
70
75
  checkPermission: async () => {
@@ -94,12 +99,12 @@ export const memberFilterCompilers: SQLFilterDefinitions = {
94
99
  },
95
100
  }),
96
101
  'email': createColumnFilter({
97
- expression: SQL.jsonValue(SQL.column(membersTable, 'details'), '$.value.email'),
102
+ expression: SQL.jsonExtract(SQL.column(membersTable, 'details'), '$.value.email'),
98
103
  type: SQLValueType.JSONString,
99
104
  nullable: true,
100
105
  }),
101
106
  'parentEmail': createColumnFilter({
102
- expression: SQL.jsonValue(SQL.column(membersTable, 'details'), '$.value.parents[*].email'),
107
+ expression: SQL.jsonExtract(SQL.column(membersTable, 'details'), '$.value.parents[*].email'),
103
108
  type: SQLValueType.JSONArray,
104
109
  nullable: true,
105
110
  }),
@@ -132,34 +137,34 @@ export const memberFilterCompilers: SQLFilterDefinitions = {
132
137
  }),
133
138
  },
134
139
  'unverifiedEmail': createColumnFilter({
135
- expression: SQL.jsonValue(SQL.column(membersTable, 'details'), '$.value.unverifiedEmails'),
140
+ expression: SQL.jsonExtract(SQL.column(membersTable, 'details'), '$.value.unverifiedEmails'),
136
141
  type: SQLValueType.JSONArray,
137
142
  nullable: true,
138
143
  }),
139
144
  'phone': createColumnFilter({
140
- expression: SQL.jsonValue(SQL.column(membersTable, 'details'), '$.value.phone'),
145
+ expression: SQL.jsonExtract(SQL.column(membersTable, 'details'), '$.value.phone'),
141
146
  type: SQLValueType.JSONString,
142
147
  nullable: true,
143
148
  }),
144
149
  'details.address': {
145
150
  ...baseSQLFilterCompilers,
146
151
  city: createColumnFilter({
147
- expression: SQL.jsonValue(SQL.column(membersTable, 'details'), '$.value.address.city'),
152
+ expression: SQL.jsonExtract(SQL.column(membersTable, 'details'), '$.value.address.city'),
148
153
  type: SQLValueType.JSONString,
149
154
  nullable: true,
150
155
  }),
151
156
  postalCode: createColumnFilter({
152
- expression: SQL.jsonValue(SQL.column(membersTable, 'details'), '$.value.address.postalCode'),
157
+ expression: SQL.jsonExtract(SQL.column(membersTable, 'details'), '$.value.address.postalCode'),
153
158
  type: SQLValueType.JSONString,
154
159
  nullable: true,
155
160
  }),
156
161
  street: createColumnFilter({
157
- expression: SQL.jsonValue(SQL.column(membersTable, 'details'), '$.value.address.street'),
162
+ expression: SQL.jsonExtract(SQL.column(membersTable, 'details'), '$.value.address.street'),
158
163
  type: SQLValueType.JSONString,
159
164
  nullable: true,
160
165
  }),
161
166
  number: createColumnFilter({
162
- expression: SQL.jsonValue(SQL.column(membersTable, 'details'), '$.value.address.number'),
167
+ expression: SQL.jsonExtract(SQL.column(membersTable, 'details'), '$.value.address.number'),
163
168
  type: SQLValueType.JSONString,
164
169
  nullable: true,
165
170
  }),
@@ -167,33 +172,33 @@ export const memberFilterCompilers: SQLFilterDefinitions = {
167
172
  'details.parents[*].address': {
168
173
  ...baseSQLFilterCompilers,
169
174
  city: createColumnFilter({
170
- expression: SQL.jsonValue(SQL.column(membersTable, 'details'), '$.value.parents[*].address.city'),
175
+ expression: SQL.jsonExtract(SQL.column(membersTable, 'details'), '$.value.parents[*].address.city'),
171
176
  type: SQLValueType.JSONArray,
172
177
  nullable: true,
173
178
  }),
174
179
  postalCode: createColumnFilter({
175
- expression: SQL.jsonValue(SQL.column(membersTable, 'details'), '$.value.parents[*].address.postalCode'),
180
+ expression: SQL.jsonExtract(SQL.column(membersTable, 'details'), '$.value.parents[*].address.postalCode'),
176
181
  type: SQLValueType.JSONArray,
177
182
  nullable: true,
178
183
  }),
179
184
  street: createColumnFilter({
180
- expression: SQL.jsonValue(SQL.column(membersTable, 'details'), '$.value.parents[*].address.street'),
185
+ expression: SQL.jsonExtract(SQL.column(membersTable, 'details'), '$.value.parents[*].address.street'),
181
186
  type: SQLValueType.JSONArray,
182
187
  nullable: true,
183
188
  }),
184
189
  number: createColumnFilter({
185
- expression: SQL.jsonValue(SQL.column(membersTable, 'details'), '$.value.parents[*].address.number'),
190
+ expression: SQL.jsonExtract(SQL.column(membersTable, 'details'), '$.value.parents[*].address.number'),
186
191
  type: SQLValueType.JSONArray,
187
192
  nullable: true,
188
193
  }),
189
194
  },
190
195
  'parentPhone': createColumnFilter({
191
- expression: SQL.jsonValue(SQL.column(membersTable, 'details'), '$.value.parents[*].phone'),
196
+ expression: SQL.jsonExtract(SQL.column(membersTable, 'details'), '$.value.parents[*].phone'),
192
197
  type: SQLValueType.JSONArray,
193
198
  nullable: true,
194
199
  }),
195
200
  'unverifiedPhone': createColumnFilter({
196
- expression: SQL.jsonValue(SQL.column(membersTable, 'details'), '$.value.unverifiedPhones'),
201
+ expression: SQL.jsonExtract(SQL.column(membersTable, 'details'), '$.value.unverifiedPhones'),
197
202
  type: SQLValueType.JSONArray,
198
203
  nullable: true,
199
204
  }),
@@ -458,21 +463,21 @@ export const memberFilterCompilers: SQLFilterDefinitions = {
458
463
  ...baseSQLFilterCompilers,
459
464
  recordAnswers: createWildcardColumnFilter(
460
465
  (key: string) => ({
461
- expression: SQL.jsonValue(SQL.column('details'), `$.value.recordAnswers.${SQLJsonExtract.escapePathComponent(key)}`, true),
466
+ expression: SQL.jsonExtract(SQL.column('details'), `$.value.recordAnswers.${SQLJsonExtract.escapePathComponent(key)}`, true),
462
467
  type: SQLValueType.JSONObject,
463
468
  nullable: true,
464
469
  }),
465
470
  (key: string) => ({
466
471
  ...baseSQLFilterCompilers,
467
472
  selected: createColumnFilter({
468
- expression: SQL.jsonValue(SQL.column('details'), `$.value.recordAnswers.${SQLJsonExtract.escapePathComponent(key)}.selected`, true),
473
+ expression: SQL.jsonExtract(SQL.column('details'), `$.value.recordAnswers.${SQLJsonExtract.escapePathComponent(key)}.selected`, true),
469
474
  type: SQLValueType.JSONBoolean,
470
475
  nullable: true,
471
476
  }),
472
477
  selectedChoice: {
473
478
  ...baseSQLFilterCompilers,
474
479
  id: createColumnFilter({
475
- expression: SQL.jsonValue(SQL.column('details'), `$.value.recordAnswers.${SQLJsonExtract.escapePathComponent(key)}.selectedChoice.id`, true),
480
+ expression: SQL.jsonExtract(SQL.column('details'), `$.value.recordAnswers.${SQLJsonExtract.escapePathComponent(key)}.selectedChoice.id`, true),
476
481
  type: SQLValueType.JSONString,
477
482
  nullable: true,
478
483
  }),
@@ -480,13 +485,13 @@ export const memberFilterCompilers: SQLFilterDefinitions = {
480
485
  selectedChoices: {
481
486
  ...baseSQLFilterCompilers,
482
487
  id: createColumnFilter({
483
- expression: SQL.jsonValue(SQL.column('details'), `$.value.recordAnswers.${SQLJsonExtract.escapePathComponent(key)}.selectedChoices[*].id`, true),
488
+ expression: SQL.jsonExtract(SQL.column('details'), `$.value.recordAnswers.${SQLJsonExtract.escapePathComponent(key)}.selectedChoices[*].id`, true),
484
489
  type: SQLValueType.JSONArray,
485
490
  nullable: true,
486
491
  }),
487
492
  },
488
493
  value: createColumnFilter({
489
- expression: SQL.jsonValue(SQL.column('details'), `$.value.recordAnswers.${SQLJsonExtract.escapePathComponent(key)}.value`, true),
494
+ expression: SQL.jsonExtract(SQL.column('details'), `$.value.recordAnswers.${SQLJsonExtract.escapePathComponent(key)}.value`, true),
490
495
  type: SQLValueType.JSONString,
491
496
  nullable: true,
492
497
  }),