@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.
- package/index.ts +2 -2
- package/package.json +20 -20
- package/src/crons/disable-auto-update-documents.test.ts +164 -0
- package/src/crons/disable-auto-update-documents.ts +82 -0
- package/src/endpoints/admin/members/ChargeMembersEndpoint.ts +5 -5
- package/src/endpoints/admin/registrations/ChargeRegistrationsEndpoint.ts +87 -0
- package/src/endpoints/global/members/GetMembersCountEndpoint.ts +2 -2
- package/src/endpoints/global/members/GetMembersEndpoint.ts +5 -5
- package/src/endpoints/global/registration/GetRegistrationsCountEndpoint.ts +2 -2
- package/src/endpoints/global/registration/GetRegistrationsEndpoint.ts +14 -11
- package/src/endpoints/global/registration/RegisterMembersEndpoint.ts +9 -8
- package/src/endpoints/organization/dashboard/documents/GetDocumentTemplatesCountEndpoint.ts +48 -0
- package/src/endpoints/organization/dashboard/documents/GetDocumentTemplatesEndpoint.ts +95 -19
- package/src/endpoints/organization/dashboard/documents/PatchDocumentTemplatesEndpoint.test.ts +282 -0
- package/src/endpoints/organization/dashboard/documents/{PatchDocumentTemplateEndpoint.ts → PatchDocumentTemplatesEndpoint.ts} +56 -3
- package/src/excel-loaders/members.ts +62 -8
- package/src/excel-loaders/registrations.ts +180 -9
- package/src/helpers/LimitedFilteredRequestHelper.ts +24 -0
- package/src/helpers/MemberCharger.ts +16 -4
- package/src/helpers/SQLTranslatedString.ts +14 -0
- package/src/helpers/TagHelper.test.ts +9 -9
- package/src/helpers/fetchToAsyncIterator.ts +1 -1
- package/src/helpers/outstandingBalanceJoin.ts +49 -0
- package/src/seeds/1765896674-document-update-year.test.ts +179 -0
- package/src/seeds/1765896674-document-update-year.ts +75 -0
- package/src/seeds/1766150402-document-published-at.test.ts +46 -0
- package/src/seeds/1766150402-document-published-at.ts +20 -0
- package/src/services/PaymentService.ts +14 -32
- package/src/sql-filters/base-registration-filter-compilers.ts +51 -4
- package/src/sql-filters/document-templates.ts +45 -0
- package/src/sql-filters/documents.ts +1 -1
- package/src/sql-filters/events.ts +6 -6
- package/src/sql-filters/groups.ts +7 -6
- package/src/sql-filters/members.ts +31 -26
- package/src/sql-filters/orders.ts +16 -16
- package/src/sql-filters/organizations.ts +11 -11
- package/src/sql-filters/payments.ts +10 -10
- package/src/sql-filters/registrations.ts +14 -6
- package/src/sql-sorters/document-templates.ts +79 -0
- package/src/sql-sorters/documents.ts +1 -1
- package/src/sql-sorters/members.ts +22 -0
- package/src/sql-sorters/orders.ts +5 -5
- package/src/sql-sorters/organizations.ts +3 -3
- package/src/sql-sorters/registrations.ts +186 -15
|
@@ -1,10 +1,31 @@
|
|
|
1
|
-
import { Member } from '@stamhoofd/models';
|
|
2
|
-
import { SQL, SQLOrderBy, SQLOrderByDirection, SQLSortDefinitions } from '@stamhoofd/sql';
|
|
3
|
-
import { RegistrationWithMemberBlob } from '@stamhoofd/structures';
|
|
1
|
+
import { Group, Member, Organization } from '@stamhoofd/models';
|
|
2
|
+
import { SQL, SQLIfNull, SQLOrderBy, SQLOrderByDirection, SQLSortDefinitions } from '@stamhoofd/sql';
|
|
3
|
+
import { MemberWithRegistrationsBlob, Organization as OrganizationStruct, RegistrationWithMemberBlob } from '@stamhoofd/structures';
|
|
4
4
|
import { Formatter } from '@stamhoofd/utility';
|
|
5
|
-
import {
|
|
5
|
+
import { memberCachedBalanceForOrganizationJoin, registrationCachedBalanceJoin } from '../helpers/outstandingBalanceJoin.js';
|
|
6
|
+
import { SQLTranslatedString } from '../helpers/SQLTranslatedString.js';
|
|
7
|
+
import { groupJoin, memberJoin, organizationJoin } from '../sql-filters/registrations.js';
|
|
6
8
|
|
|
7
|
-
export
|
|
9
|
+
export class RegistrationSortData {
|
|
10
|
+
readonly registration: RegistrationWithMemberBlob;
|
|
11
|
+
private organizations: OrganizationStruct[];
|
|
12
|
+
|
|
13
|
+
constructor({ registration, organizations }: { registration: RegistrationWithMemberBlob; organizations: OrganizationStruct[] }) {
|
|
14
|
+
this.registration = registration;
|
|
15
|
+
this.organizations = organizations;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
get organization() {
|
|
19
|
+
const organization = this.organizations.find(o => o.id === this.registration.organizationId);
|
|
20
|
+
if (!organization) {
|
|
21
|
+
throw new Error('Organization not found for registration');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return organization;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const registrationSorters: SQLSortDefinitions<RegistrationSortData> = {
|
|
8
29
|
// WARNING! TEST NEW SORTERS THOROUGHLY!
|
|
9
30
|
// Try to avoid creating sorters on fields that er not 1:1 with the database, that often causes pagination issues if not thought through
|
|
10
31
|
// An example: sorting on 'name' is not a good idea, because it is a concatenation of two fields.
|
|
@@ -14,8 +35,8 @@ export const registrationSorters: SQLSortDefinitions<RegistrationWithMemberBlob>
|
|
|
14
35
|
// What if you need mapping? simply map the sorters in the frontend: name -> firstname, lastname, age -> birthDay, etc.
|
|
15
36
|
|
|
16
37
|
'id': {
|
|
17
|
-
getValue(
|
|
18
|
-
return
|
|
38
|
+
getValue({ registration }) {
|
|
39
|
+
return registration.id;
|
|
19
40
|
},
|
|
20
41
|
toSQL: (direction: SQLOrderByDirection): SQLOrderBy => {
|
|
21
42
|
return new SQLOrderBy({
|
|
@@ -25,8 +46,8 @@ export const registrationSorters: SQLSortDefinitions<RegistrationWithMemberBlob>
|
|
|
25
46
|
},
|
|
26
47
|
},
|
|
27
48
|
'registeredAt': {
|
|
28
|
-
getValue(
|
|
29
|
-
return
|
|
49
|
+
getValue({ registration }) {
|
|
50
|
+
return registration.registeredAt;
|
|
30
51
|
},
|
|
31
52
|
toSQL: (direction: SQLOrderByDirection): SQLOrderBy => {
|
|
32
53
|
return new SQLOrderBy({
|
|
@@ -35,9 +56,95 @@ export const registrationSorters: SQLSortDefinitions<RegistrationWithMemberBlob>
|
|
|
35
56
|
});
|
|
36
57
|
},
|
|
37
58
|
},
|
|
59
|
+
'groupPrice': {
|
|
60
|
+
getValue: ({ registration }) => registration.groupPrice.name.toString(),
|
|
61
|
+
toSQL: (direction: SQLOrderByDirection): SQLOrderBy => {
|
|
62
|
+
return new SQLOrderBy({
|
|
63
|
+
column: new SQLTranslatedString(SQL.column('groupPrice'), '$.value.name'),
|
|
64
|
+
direction,
|
|
65
|
+
});
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
'trialUntil': {
|
|
69
|
+
getValue({ registration }) {
|
|
70
|
+
return registration.trialUntil;
|
|
71
|
+
},
|
|
72
|
+
toSQL: (direction: SQLOrderByDirection): SQLOrderBy => {
|
|
73
|
+
return new SQLOrderBy({
|
|
74
|
+
column: SQL.column('trialUntil'),
|
|
75
|
+
direction,
|
|
76
|
+
});
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
'startDate': {
|
|
80
|
+
getValue({ registration }) {
|
|
81
|
+
return registration.startDate;
|
|
82
|
+
},
|
|
83
|
+
toSQL: (direction: SQLOrderByDirection): SQLOrderBy => {
|
|
84
|
+
return new SQLOrderBy({
|
|
85
|
+
column: SQL.column('startDate'),
|
|
86
|
+
direction,
|
|
87
|
+
});
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
'endDate': {
|
|
91
|
+
getValue({ registration }) {
|
|
92
|
+
return registration.endDate;
|
|
93
|
+
},
|
|
94
|
+
toSQL: (direction: SQLOrderByDirection): SQLOrderBy => {
|
|
95
|
+
return new SQLOrderBy({
|
|
96
|
+
column: SQL.column('endDate'),
|
|
97
|
+
direction,
|
|
98
|
+
});
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
'memberCachedBalance.amountOpen': {
|
|
102
|
+
getValue({ registration }) {
|
|
103
|
+
return registration.member.balances.reduce((sum, r) => sum + (r.amountOpen), 0);
|
|
104
|
+
},
|
|
105
|
+
toSQL: (direction: SQLOrderByDirection): SQLOrderBy => {
|
|
106
|
+
return new SQLOrderBy({
|
|
107
|
+
column: new SQLIfNull(SQL.column('memberCachedBalance', 'amountOpen'), 0),
|
|
108
|
+
direction,
|
|
109
|
+
});
|
|
110
|
+
},
|
|
111
|
+
join: memberCachedBalanceForOrganizationJoin,
|
|
112
|
+
select: [SQL.column('memberCachedBalance', 'amountOpen')],
|
|
113
|
+
},
|
|
114
|
+
'registrationCachedBalance.toPay': {
|
|
115
|
+
getValue({ registration }) {
|
|
116
|
+
return registration.balances.reduce((sum, r) => sum + (r.amountOpen + r.amountPending), 0);
|
|
117
|
+
},
|
|
118
|
+
toSQL: (direction: SQLOrderByDirection): SQLOrderBy => {
|
|
119
|
+
return new SQLOrderBy({
|
|
120
|
+
column: new SQLIfNull(SQL.column('registrationCachedBalance', 'toPay'), 0),
|
|
121
|
+
direction,
|
|
122
|
+
});
|
|
123
|
+
},
|
|
124
|
+
join: registrationCachedBalanceJoin,
|
|
125
|
+
select: [SQL.column('registrationCachedBalance', 'toPay')],
|
|
126
|
+
},
|
|
127
|
+
'registrationCachedBalance.price': {
|
|
128
|
+
getValue({ registration }) {
|
|
129
|
+
return registration.balances.reduce((sum, r) => sum + (r.amountOpen + r.amountPaid + r.amountPending), 0);
|
|
130
|
+
},
|
|
131
|
+
toSQL: (direction: SQLOrderByDirection): SQLOrderBy => {
|
|
132
|
+
return new SQLOrderBy({
|
|
133
|
+
column: new SQLIfNull(SQL.column('registrationCachedBalance', 'price'), 0),
|
|
134
|
+
direction,
|
|
135
|
+
});
|
|
136
|
+
},
|
|
137
|
+
join: registrationCachedBalanceJoin,
|
|
138
|
+
select: [SQL.column('registrationCachedBalance', 'price')],
|
|
139
|
+
},
|
|
140
|
+
'member.memberNumber': createMemberColumnSorter({
|
|
141
|
+
columnName: 'memberNumber',
|
|
142
|
+
getValue: member => member.details.memberNumber ?? '',
|
|
143
|
+
|
|
144
|
+
}),
|
|
38
145
|
'member.firstName': {
|
|
39
|
-
getValue(
|
|
40
|
-
return
|
|
146
|
+
getValue({ registration }) {
|
|
147
|
+
return registration.member.firstName;
|
|
41
148
|
},
|
|
42
149
|
toSQL: (direction: SQLOrderByDirection): SQLOrderBy => {
|
|
43
150
|
return new SQLOrderBy({
|
|
@@ -49,8 +156,8 @@ export const registrationSorters: SQLSortDefinitions<RegistrationWithMemberBlob>
|
|
|
49
156
|
select: [SQL.column(Member.table, 'firstName')],
|
|
50
157
|
},
|
|
51
158
|
'member.lastName': {
|
|
52
|
-
getValue(
|
|
53
|
-
return
|
|
159
|
+
getValue({ registration }) {
|
|
160
|
+
return registration.member.lastName;
|
|
54
161
|
},
|
|
55
162
|
toSQL: (direction: SQLOrderByDirection): SQLOrderBy => {
|
|
56
163
|
return new SQLOrderBy({
|
|
@@ -62,8 +169,8 @@ export const registrationSorters: SQLSortDefinitions<RegistrationWithMemberBlob>
|
|
|
62
169
|
select: [SQL.column(Member.table, 'lastName')],
|
|
63
170
|
},
|
|
64
171
|
'member.birthDay': {
|
|
65
|
-
getValue(
|
|
66
|
-
return
|
|
172
|
+
getValue({ registration }) {
|
|
173
|
+
return registration.member.details.birthDay === null ? null : Formatter.dateIso(registration.member.details.birthDay);
|
|
67
174
|
},
|
|
68
175
|
toSQL: (direction: SQLOrderByDirection): SQLOrderBy => {
|
|
69
176
|
return new SQLOrderBy({
|
|
@@ -74,4 +181,68 @@ export const registrationSorters: SQLSortDefinitions<RegistrationWithMemberBlob>
|
|
|
74
181
|
join: memberJoin,
|
|
75
182
|
select: [SQL.column(Member.table, 'birthDay')],
|
|
76
183
|
},
|
|
184
|
+
'member.createdAt': {
|
|
185
|
+
getValue({ registration }) {
|
|
186
|
+
return registration.member.createdAt;
|
|
187
|
+
},
|
|
188
|
+
toSQL: (direction: SQLOrderByDirection): SQLOrderBy => {
|
|
189
|
+
return new SQLOrderBy({
|
|
190
|
+
column: SQL.column(Member.table, 'createdAt'),
|
|
191
|
+
direction,
|
|
192
|
+
});
|
|
193
|
+
},
|
|
194
|
+
join: memberJoin,
|
|
195
|
+
select: [SQL.column(Member.table, 'createdAt')],
|
|
196
|
+
},
|
|
197
|
+
'organization.name': {
|
|
198
|
+
getValue: ({ organization }) => organization.name,
|
|
199
|
+
toSQL: (direction: SQLOrderByDirection): SQLOrderBy => {
|
|
200
|
+
return new SQLOrderBy({
|
|
201
|
+
column: SQL.column(Organization.table, 'name'),
|
|
202
|
+
direction,
|
|
203
|
+
});
|
|
204
|
+
},
|
|
205
|
+
join: organizationJoin,
|
|
206
|
+
select: [SQL.column(Organization.table, 'name')],
|
|
207
|
+
},
|
|
208
|
+
'organization.uri': {
|
|
209
|
+
getValue: ({ organization }) => organization.uri,
|
|
210
|
+
toSQL: (direction: SQLOrderByDirection): SQLOrderBy => {
|
|
211
|
+
return new SQLOrderBy({
|
|
212
|
+
column: SQL.column(Organization.table, 'uri'),
|
|
213
|
+
direction,
|
|
214
|
+
});
|
|
215
|
+
},
|
|
216
|
+
join: organizationJoin,
|
|
217
|
+
select: [SQL.column(Organization.table, 'uri')],
|
|
218
|
+
},
|
|
219
|
+
'group.name': {
|
|
220
|
+
getValue: ({ registration }) => registration.group.settings.name.toString(),
|
|
221
|
+
toSQL: (direction: SQLOrderByDirection): SQLOrderBy => {
|
|
222
|
+
return new SQLOrderBy({
|
|
223
|
+
column: new SQLTranslatedString(SQL.column(Group.table, 'settings'), '$.value.name'),
|
|
224
|
+
direction,
|
|
225
|
+
});
|
|
226
|
+
},
|
|
227
|
+
join: groupJoin,
|
|
228
|
+
},
|
|
77
229
|
};
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Helper function for simple sort on member column
|
|
233
|
+
* @param param0
|
|
234
|
+
* @returns
|
|
235
|
+
*/
|
|
236
|
+
function createMemberColumnSorter<T>({ columnName, getValue }: { columnName: string; getValue: (member: MemberWithRegistrationsBlob) => T }) {
|
|
237
|
+
return {
|
|
238
|
+
getValue: ({ registration }: RegistrationSortData) => getValue(registration.member),
|
|
239
|
+
toSQL: (direction: SQLOrderByDirection): SQLOrderBy => {
|
|
240
|
+
return new SQLOrderBy({
|
|
241
|
+
column: SQL.column(Member.table, columnName),
|
|
242
|
+
direction,
|
|
243
|
+
});
|
|
244
|
+
},
|
|
245
|
+
join: memberJoin,
|
|
246
|
+
select: [SQL.column(Member.table, columnName)],
|
|
247
|
+
};
|
|
248
|
+
}
|