@stamhoofd/backend 2.15.0 → 2.16.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/.env.template.json +2 -1
- package/index.ts +15 -1
- package/package.json +14 -12
- package/src/email-recipient-loaders/members.ts +61 -0
- package/src/endpoints/admin/memberships/GetChargeMembershipsSummaryEndpoint.ts +1 -1
- package/src/endpoints/admin/organizations/GetOrganizationsEndpoint.ts +5 -183
- package/src/endpoints/global/files/ExportToExcelEndpoint.ts +163 -0
- package/src/endpoints/global/files/GetFileCache.ts +69 -0
- package/src/endpoints/global/files/UploadFile.ts +4 -1
- package/src/endpoints/global/files/UploadImage.ts +14 -2
- package/src/endpoints/global/members/GetMembersEndpoint.ts +12 -299
- package/src/endpoints/organization/dashboard/email/EmailEndpoint.ts +22 -2
- package/src/endpoints/organization/dashboard/payments/GetPaymentsEndpoint.ts +6 -134
- package/src/endpoints/organization/dashboard/webshops/GetWebshopOrdersEndpoint.ts +5 -3
- package/src/endpoints/organization/dashboard/webshops/GetWebshopTicketsEndpoint.ts +5 -3
- package/src/excel-loaders/members.ts +101 -0
- package/src/excel-loaders/payments.ts +539 -0
- package/src/helpers/AdminPermissionChecker.ts +0 -3
- package/src/helpers/AuthenticatedStructures.ts +2 -0
- package/src/helpers/FileCache.ts +158 -0
- package/src/helpers/fetchToAsyncIterator.ts +34 -0
- package/src/sql-filters/balance-item-payments.ts +13 -0
- package/src/sql-filters/members.ts +179 -0
- package/src/sql-filters/organizations.ts +115 -0
- package/src/sql-filters/payments.ts +78 -0
- package/src/sql-filters/registrations.ts +24 -0
- package/src/sql-sorters/members.ts +46 -0
- package/src/sql-sorters/organizations.ts +71 -0
- package/src/sql-sorters/payments.ts +50 -0
- package/tsconfig.json +3 -4
- package/src/endpoints/organization/dashboard/payments/legacy/GetPaymentsEndpoint.ts +0 -170
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { Organization } from "@stamhoofd/models"
|
|
2
|
+
import { SQL, SQLOrderBy, SQLOrderByDirection, SQLSortDefinitions } from "@stamhoofd/sql"
|
|
3
|
+
|
|
4
|
+
export const organizationSorters: SQLSortDefinitions<Organization> = {
|
|
5
|
+
'id': {
|
|
6
|
+
getValue(a) {
|
|
7
|
+
return a.id
|
|
8
|
+
},
|
|
9
|
+
toSQL: (direction: SQLOrderByDirection): SQLOrderBy => {
|
|
10
|
+
return new SQLOrderBy({
|
|
11
|
+
column: SQL.column('id'),
|
|
12
|
+
direction
|
|
13
|
+
})
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
'name': {
|
|
17
|
+
getValue(a) {
|
|
18
|
+
return a.name
|
|
19
|
+
},
|
|
20
|
+
toSQL: (direction: SQLOrderByDirection): SQLOrderBy => {
|
|
21
|
+
return new SQLOrderBy({
|
|
22
|
+
column: SQL.column('name'),
|
|
23
|
+
direction
|
|
24
|
+
})
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
'uri': {
|
|
28
|
+
getValue(a) {
|
|
29
|
+
return a.uri
|
|
30
|
+
},
|
|
31
|
+
toSQL: (direction: SQLOrderByDirection): SQLOrderBy => {
|
|
32
|
+
return new SQLOrderBy({
|
|
33
|
+
column: SQL.column('uri'),
|
|
34
|
+
direction
|
|
35
|
+
})
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
'type': {
|
|
39
|
+
getValue(a) {
|
|
40
|
+
return a.meta.type
|
|
41
|
+
},
|
|
42
|
+
toSQL: (direction: SQLOrderByDirection): SQLOrderBy => {
|
|
43
|
+
return new SQLOrderBy({
|
|
44
|
+
column: SQL.jsonValue(SQL.column('meta'), '$.value.type'),
|
|
45
|
+
direction
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
'city': {
|
|
50
|
+
getValue(a) {
|
|
51
|
+
return a.address.city
|
|
52
|
+
},
|
|
53
|
+
toSQL: (direction: SQLOrderByDirection): SQLOrderBy => {
|
|
54
|
+
return new SQLOrderBy({
|
|
55
|
+
column: SQL.jsonValue(SQL.column('address'), '$.value.city'),
|
|
56
|
+
direction
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
'country': {
|
|
61
|
+
getValue(a) {
|
|
62
|
+
return a.address.country
|
|
63
|
+
},
|
|
64
|
+
toSQL: (direction: SQLOrderByDirection): SQLOrderBy => {
|
|
65
|
+
return new SQLOrderBy({
|
|
66
|
+
column: SQL.jsonValue(SQL.column('address'), '$.value.country'),
|
|
67
|
+
direction
|
|
68
|
+
})
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Payment } from "@stamhoofd/models"
|
|
2
|
+
import { SQL, SQLOrderBy, SQLOrderByDirection, SQLSortDefinitions } from "@stamhoofd/sql"
|
|
3
|
+
import { Formatter } from "@stamhoofd/utility"
|
|
4
|
+
|
|
5
|
+
export const paymentSorters: SQLSortDefinitions<Payment> = {
|
|
6
|
+
'id': {
|
|
7
|
+
getValue(a) {
|
|
8
|
+
return a.id
|
|
9
|
+
},
|
|
10
|
+
toSQL: (direction: SQLOrderByDirection): SQLOrderBy => {
|
|
11
|
+
return new SQLOrderBy({
|
|
12
|
+
column: SQL.column('id'),
|
|
13
|
+
direction
|
|
14
|
+
})
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
'createdAt': {
|
|
18
|
+
getValue(a) {
|
|
19
|
+
return Formatter.dateTimeIso(a.createdAt, 'UTC')
|
|
20
|
+
},
|
|
21
|
+
toSQL: (direction: SQLOrderByDirection): SQLOrderBy => {
|
|
22
|
+
return new SQLOrderBy({
|
|
23
|
+
column: SQL.column('createdAt'),
|
|
24
|
+
direction
|
|
25
|
+
})
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
'paidAt': {
|
|
29
|
+
getValue(a) {
|
|
30
|
+
return a.paidAt !== null ? Formatter.dateTimeIso(a.paidAt, 'UTC') : null
|
|
31
|
+
},
|
|
32
|
+
toSQL: (direction: SQLOrderByDirection): SQLOrderBy => {
|
|
33
|
+
return new SQLOrderBy({
|
|
34
|
+
column: SQL.column('paidAt'),
|
|
35
|
+
direction
|
|
36
|
+
})
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
'price': {
|
|
40
|
+
getValue(a) {
|
|
41
|
+
return a.price
|
|
42
|
+
},
|
|
43
|
+
toSQL: (direction: SQLOrderByDirection): SQLOrderBy => {
|
|
44
|
+
return new SQLOrderBy({
|
|
45
|
+
column: SQL.column('price'),
|
|
46
|
+
direction
|
|
47
|
+
})
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
package/tsconfig.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"compilerOptions": {
|
|
3
|
-
"target": "
|
|
3
|
+
"target": "es2022", // needs to be es2019 to support optional chaining. Node.js doesn't support optional chaining yet, so we need the transpiling
|
|
4
4
|
"module": "commonjs",
|
|
5
5
|
"jsx": "preserve",
|
|
6
6
|
"importHelpers": true,
|
|
@@ -14,8 +14,7 @@
|
|
|
14
14
|
"declarationMap": true,
|
|
15
15
|
"outDir": "dist",
|
|
16
16
|
"lib": [
|
|
17
|
-
"
|
|
18
|
-
"ES2021.String",
|
|
17
|
+
"es2022",
|
|
19
18
|
"DOM" // axios fix
|
|
20
19
|
],
|
|
21
20
|
"types": [
|
|
@@ -39,4 +38,4 @@
|
|
|
39
38
|
"ts-node": {
|
|
40
39
|
"files": true
|
|
41
40
|
}
|
|
42
|
-
}
|
|
41
|
+
}
|
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
import { AutoEncoder, DateDecoder, EnumDecoder, field, IntegerDecoder, StringDecoder } from "@simonbackx/simple-encoding";
|
|
2
|
-
import { DecodedRequest, Endpoint, Request, Response } from "@simonbackx/simple-endpoints";
|
|
3
|
-
import { Organization, Payment } from "@stamhoofd/models";
|
|
4
|
-
import { PaymentGeneral, PaymentMethod, PaymentProvider, PaymentStatus } from "@stamhoofd/structures";
|
|
5
|
-
|
|
6
|
-
import { StringArrayDecoder } from "../../../../../decoders/StringArrayDecoder";
|
|
7
|
-
import { AuthenticatedStructures } from "../../../../../helpers/AuthenticatedStructures";
|
|
8
|
-
import { Context } from "../../../../../helpers/Context";
|
|
9
|
-
import { StringNullableDecoder } from "../../../../../decoders/StringNullableDecoder";
|
|
10
|
-
|
|
11
|
-
type Params = Record<string, never>;
|
|
12
|
-
type Body = undefined
|
|
13
|
-
type ResponseBody = PaymentGeneral[]
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class Query extends AutoEncoder {
|
|
17
|
-
/**
|
|
18
|
-
* Usage in combination with paidSince is special!
|
|
19
|
-
*/
|
|
20
|
-
@field({ decoder: StringDecoder, optional: true })
|
|
21
|
-
afterId?: string
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Return all payments that were paid after (and including) this date.
|
|
25
|
-
* Only returns orders **equal** to this date if afterId is not provided or if the id of those payments is also higher.
|
|
26
|
-
*/
|
|
27
|
-
@field({ decoder: DateDecoder, optional: true })
|
|
28
|
-
paidSince?: Date
|
|
29
|
-
|
|
30
|
-
@field({ decoder: DateDecoder, optional: true })
|
|
31
|
-
paidBefore?: Date
|
|
32
|
-
|
|
33
|
-
@field({ decoder: IntegerDecoder, optional: true })
|
|
34
|
-
limit?: number
|
|
35
|
-
|
|
36
|
-
@field({ decoder: new StringArrayDecoder(new EnumDecoder(PaymentMethod)), optional: true })
|
|
37
|
-
methods?: PaymentMethod[]
|
|
38
|
-
|
|
39
|
-
@field({ decoder: new StringArrayDecoder(new StringNullableDecoder(new EnumDecoder(PaymentProvider))), optional: true })
|
|
40
|
-
providers?: (PaymentProvider|null)[]
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export class GetPaymentsEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
44
|
-
protected queryDecoder = Query;
|
|
45
|
-
|
|
46
|
-
protected doesMatch(request: Request): [true, Params] | [false] {
|
|
47
|
-
if (request.method != "GET") {
|
|
48
|
-
return [false];
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const params = Endpoint.parseParameters(request.url, "/organization/payments", {});
|
|
52
|
-
|
|
53
|
-
if (params) {
|
|
54
|
-
return [true, params as Params];
|
|
55
|
-
}
|
|
56
|
-
return [false];
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
async handle(request: DecodedRequest<Params, Query, Body>) {
|
|
60
|
-
const organization = await Context.setOrganizationScope();
|
|
61
|
-
await Context.authenticate()
|
|
62
|
-
|
|
63
|
-
if (!await Context.auth.canManagePayments(organization.id)) {
|
|
64
|
-
throw Context.auth.error()
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
return new Response(
|
|
68
|
-
(await this.getPayments(organization, request.query))
|
|
69
|
-
);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
async getPayments(organization: Organization, query: Query) {
|
|
73
|
-
const paidSince = query.paidSince ?? new Date(Date.now() - (24 * 60 * 60 * 1000 * 7 ))
|
|
74
|
-
paidSince.setMilliseconds(0)
|
|
75
|
-
const payments: Payment[] = []
|
|
76
|
-
|
|
77
|
-
if (query.afterId) {
|
|
78
|
-
// First return all payments with id > afterId and paidAt == paidSince
|
|
79
|
-
payments.push(...await Payment.where({
|
|
80
|
-
organizationId: organization.id,
|
|
81
|
-
paidAt: {
|
|
82
|
-
sign: '=',
|
|
83
|
-
value: paidSince
|
|
84
|
-
},
|
|
85
|
-
id: {
|
|
86
|
-
sign: '>',
|
|
87
|
-
value: query.afterId ?? ""
|
|
88
|
-
},
|
|
89
|
-
method: {
|
|
90
|
-
sign: 'IN',
|
|
91
|
-
value: query.methods ?? [PaymentMethod.Transfer]
|
|
92
|
-
},
|
|
93
|
-
provider: {
|
|
94
|
-
sign: 'IN',
|
|
95
|
-
value: query.providers ?? [null]
|
|
96
|
-
}
|
|
97
|
-
}, {
|
|
98
|
-
limit: query.limit ?? undefined,
|
|
99
|
-
sort: [{
|
|
100
|
-
column: "id",
|
|
101
|
-
direction: "ASC"
|
|
102
|
-
}]
|
|
103
|
-
}));
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
payments.push(...await Payment.where({
|
|
107
|
-
organizationId: organization.id,
|
|
108
|
-
paidAt: query.paidBefore ? [{
|
|
109
|
-
sign: query.afterId ? '>' : '>=',
|
|
110
|
-
value: paidSince
|
|
111
|
-
}, {
|
|
112
|
-
sign: '<=',
|
|
113
|
-
value: query.paidBefore
|
|
114
|
-
}] : {
|
|
115
|
-
sign: query.afterId ? '>' : '>=',
|
|
116
|
-
value: paidSince
|
|
117
|
-
},
|
|
118
|
-
method: {
|
|
119
|
-
sign: 'IN',
|
|
120
|
-
value: query.methods ?? [PaymentMethod.Transfer]
|
|
121
|
-
},
|
|
122
|
-
provider: {
|
|
123
|
-
sign: 'IN',
|
|
124
|
-
value: query.providers ?? [null]
|
|
125
|
-
}
|
|
126
|
-
}, {
|
|
127
|
-
limit: query.limit ? (query.limit - payments.length) : undefined,
|
|
128
|
-
sort: [{
|
|
129
|
-
column: "paidAt",
|
|
130
|
-
direction: "ASC"
|
|
131
|
-
},
|
|
132
|
-
{
|
|
133
|
-
column: "id",
|
|
134
|
-
direction: "ASC"
|
|
135
|
-
}]
|
|
136
|
-
}));
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
if (!query.paidSince && !query.methods && !query.providers) {
|
|
140
|
-
// Default behaviour is to return all not-paid transfer payments that are not yet paid
|
|
141
|
-
|
|
142
|
-
payments.push(...
|
|
143
|
-
await Payment.where({
|
|
144
|
-
organizationId: organization.id,
|
|
145
|
-
paidAt: null,
|
|
146
|
-
method: PaymentMethod.Transfer,
|
|
147
|
-
status: {
|
|
148
|
-
sign: '!=',
|
|
149
|
-
value: PaymentStatus.Failed
|
|
150
|
-
}
|
|
151
|
-
})
|
|
152
|
-
);
|
|
153
|
-
|
|
154
|
-
payments.push(...
|
|
155
|
-
await Payment.where({
|
|
156
|
-
organizationId: organization.id,
|
|
157
|
-
paidAt: null,
|
|
158
|
-
updatedAt: {
|
|
159
|
-
sign: '>',
|
|
160
|
-
value: new Date(Date.now() - (24 * 60 * 60 * 1000 * 7 ))
|
|
161
|
-
},
|
|
162
|
-
method: PaymentMethod.Transfer,
|
|
163
|
-
status: PaymentStatus.Failed
|
|
164
|
-
})
|
|
165
|
-
);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
return await AuthenticatedStructures.paymentsGeneral(payments, true)
|
|
169
|
-
}
|
|
170
|
-
}
|