@stamhoofd/backend 2.39.1 → 2.40.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/eslint.config.mjs +5 -0
- package/index.ts +81 -74
- package/jest.config.cjs +10 -0
- package/migrations.ts +16 -14
- package/package.json +11 -11
- package/src/crons/clear-excel-cache.test.ts +48 -50
- package/src/crons/clear-excel-cache.ts +18 -18
- package/src/crons/setup-steps.ts +2 -2
- package/src/crons.ts +325 -306
- package/src/decoders/StringArrayDecoder.ts +7 -7
- package/src/decoders/StringNullableDecoder.ts +1 -2
- package/src/email-recipient-loaders/members.ts +22 -22
- package/src/endpoints/admin/memberships/ChargeMembershipsEndpoint.ts +8 -9
- package/src/endpoints/admin/memberships/GetChargeMembershipsSummaryEndpoint.ts +39 -40
- package/src/endpoints/admin/organizations/GetOrganizationsCountEndpoint.ts +8 -8
- package/src/endpoints/admin/organizations/GetOrganizationsEndpoint.ts +44 -45
- package/src/endpoints/admin/organizations/PatchOrganizationsEndpoint.ts +58 -57
- package/src/endpoints/auth/CreateAdminEndpoint.ts +48 -45
- package/src/endpoints/auth/CreateTokenEndpoint.test.ts +31 -31
- package/src/endpoints/auth/CreateTokenEndpoint.ts +146 -147
- package/src/endpoints/auth/DeleteTokenEndpoint.ts +7 -7
- package/src/endpoints/auth/DeleteUserEndpoint.ts +15 -15
- package/src/endpoints/auth/ForgotPasswordEndpoint.ts +17 -18
- package/src/endpoints/auth/GetOtherUserEndpoint.ts +9 -10
- package/src/endpoints/auth/GetUserEndpoint.test.ts +32 -35
- package/src/endpoints/auth/GetUserEndpoint.ts +5 -6
- package/src/endpoints/auth/PatchApiUserEndpoint.ts +35 -33
- package/src/endpoints/auth/PatchUserEndpoint.ts +55 -52
- package/src/endpoints/auth/PollEmailVerificationEndpoint.ts +9 -9
- package/src/endpoints/auth/RetryEmailVerificationEndpoint.ts +8 -8
- package/src/endpoints/auth/SignupEndpoint.ts +37 -36
- package/src/endpoints/auth/VerifyEmailEndpoint.ts +29 -28
- package/src/endpoints/global/addresses/SearchRegionsEndpoint.ts +33 -33
- package/src/endpoints/global/addresses/ValidateAddressEndpoint.ts +7 -7
- package/src/endpoints/global/caddy/CheckDomainCertEndpoint.ts +37 -37
- package/src/endpoints/global/email/CreateEmailEndpoint.ts +30 -30
- package/src/endpoints/global/email/GetEmailAddressEndpoint.ts +13 -13
- package/src/endpoints/global/email/GetEmailEndpoint.ts +13 -13
- package/src/endpoints/global/email/ManageEmailAddressEndpoint.ts +16 -16
- package/src/endpoints/global/email/PatchEmailEndpoint.ts +25 -25
- package/src/endpoints/global/events/GetEventsEndpoint.ts +43 -44
- package/src/endpoints/global/events/PatchEventsEndpoint.ts +127 -172
- package/src/endpoints/global/files/ExportToExcelEndpoint.ts +49 -50
- package/src/endpoints/global/files/GetFileCache.ts +13 -13
- package/src/endpoints/global/files/UploadFile.ts +51 -54
- package/src/endpoints/global/files/UploadImage.ts +53 -53
- package/src/endpoints/global/groups/GetGroupsEndpoint.ts +25 -25
- package/src/endpoints/global/members/GetMemberFamilyEndpoint.ts +24 -23
- package/src/endpoints/global/members/GetMembersCountEndpoint.ts +8 -8
- package/src/endpoints/global/members/GetMembersEndpoint.ts +105 -102
- package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.ts +240 -239
- package/src/endpoints/global/organizations/CheckRegisterCodeEndpoint.ts +12 -14
- package/src/endpoints/global/organizations/CreateOrganizationEndpoint.test.ts +32 -33
- package/src/endpoints/global/organizations/CreateOrganizationEndpoint.ts +48 -57
- package/src/endpoints/global/organizations/GetOrganizationFromDomainEndpoint.test.ts +21 -22
- package/src/endpoints/global/organizations/GetOrganizationFromDomainEndpoint.ts +28 -28
- package/src/endpoints/global/organizations/GetOrganizationFromUriEndpoint.ts +18 -18
- package/src/endpoints/global/organizations/SearchOrganizationEndpoint.test.ts +20 -20
- package/src/endpoints/global/organizations/SearchOrganizationEndpoint.ts +17 -17
- package/src/endpoints/global/payments/StripeWebhookEndpoint.ts +81 -75
- package/src/endpoints/global/platform/GetPlatformAdminsEndpoint.ts +14 -14
- package/src/endpoints/global/platform/GetPlatformEnpoint.ts +11 -11
- package/src/endpoints/global/platform/PatchPlatformEnpoint.ts +71 -68
- package/src/endpoints/global/registration/GetPaymentRegistrations.ts +27 -27
- package/src/endpoints/global/registration/GetUserBillingStatusEndpoint.ts +30 -30
- package/src/endpoints/global/registration/GetUserDetailedBillingStatusEndpoint.ts +34 -34
- package/src/endpoints/global/registration/GetUserDocumentsEndpoint.ts +26 -26
- package/src/endpoints/global/registration/GetUserMembersEndpoint.ts +12 -12
- package/src/endpoints/global/registration/PatchUserMembersEndpoint.ts +90 -90
- package/src/endpoints/global/registration/RegisterMembersEndpoint.test.ts +118 -121
- package/src/endpoints/global/registration/RegisterMembersEndpoint.ts +362 -350
- package/src/endpoints/global/registration-periods/GetRegistrationPeriodsEndpoint.ts +8 -9
- package/src/endpoints/global/registration-periods/PatchRegistrationPeriodsEndpoint.ts +21 -21
- package/src/endpoints/global/webshops/GetWebshopFromDomainEndpoint.ts +65 -65
- package/src/endpoints/organization/dashboard/billing/GetOrganizationBillingStatusEndpoint.ts +9 -9
- package/src/endpoints/organization/dashboard/billing/GetOrganizationDetailedBillingStatusEndpoint.ts +14 -14
- package/src/endpoints/organization/dashboard/documents/GetDocumentTemplateXML.ts +17 -17
- package/src/endpoints/organization/dashboard/documents/GetDocumentTemplatesEndpoint.ts +21 -21
- package/src/endpoints/organization/dashboard/documents/GetDocumentsEndpoint.ts +15 -15
- package/src/endpoints/organization/dashboard/documents/PatchDocumentEndpoint.ts +52 -52
- package/src/endpoints/organization/dashboard/documents/PatchDocumentTemplateEndpoint.ts +37 -37
- package/src/endpoints/organization/dashboard/email/CheckEmailBouncesEndpoint.ts +14 -14
- package/src/endpoints/organization/dashboard/email/EmailEndpoint.ts +113 -112
- package/src/endpoints/organization/dashboard/email-templates/GetEmailTemplatesEndpoint.ts +29 -29
- package/src/endpoints/organization/dashboard/email-templates/PatchEmailTemplatesEndpoint.ts +48 -47
- package/src/endpoints/organization/dashboard/mollie/CheckMollieEndpoint.ts +22 -21
- package/src/endpoints/organization/dashboard/mollie/ConnectMollieEndpoint.ts +13 -14
- package/src/endpoints/organization/dashboard/mollie/DisconnectMollieEndpoint.ts +12 -13
- package/src/endpoints/organization/dashboard/mollie/GetMollieDashboardEndpoint.ts +24 -24
- package/src/endpoints/organization/dashboard/nolt/CreateNoltTokenEndpoint.ts +10 -12
- package/src/endpoints/organization/dashboard/organization/GetOrganizationArchivedGroups.ts +14 -14
- package/src/endpoints/organization/dashboard/organization/GetOrganizationDeletedGroups.ts +13 -13
- package/src/endpoints/organization/dashboard/organization/GetOrganizationSSOEndpoint.ts +12 -12
- package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.test.ts +120 -124
- package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.ts +172 -173
- package/src/endpoints/organization/dashboard/organization/SetOrganizationDomainEndpoint.ts +88 -89
- package/src/endpoints/organization/dashboard/organization/SetOrganizationSSOEndpoint.ts +12 -12
- package/src/endpoints/organization/dashboard/payments/GetMemberBalanceEndpoint.ts +17 -17
- package/src/endpoints/organization/dashboard/payments/GetPaymentsCountEndpoint.ts +8 -8
- package/src/endpoints/organization/dashboard/payments/GetPaymentsEndpoint.ts +66 -67
- package/src/endpoints/organization/dashboard/payments/PatchBalanceItemsEndpoint.ts +47 -47
- package/src/endpoints/organization/dashboard/payments/PatchPaymentsEndpoint.ts +93 -91
- package/src/endpoints/organization/dashboard/registration-periods/GetOrganizationRegistrationPeriodsEndpoint.ts +16 -17
- package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.ts +170 -167
- package/src/endpoints/organization/dashboard/registration-periods/SetupStepReviewEndpoint.ts +25 -24
- package/src/endpoints/organization/dashboard/stripe/ConnectStripeEndpoint.ts +22 -23
- package/src/endpoints/organization/dashboard/stripe/DeleteStripeAccountEndpoint.ts +22 -22
- package/src/endpoints/organization/dashboard/stripe/GetStripeAccountLinkEndpoint.ts +17 -18
- package/src/endpoints/organization/dashboard/stripe/GetStripeAccountsEndpoint.ts +8 -9
- package/src/endpoints/organization/dashboard/stripe/GetStripeLoginLinkEndpoint.ts +17 -18
- package/src/endpoints/organization/dashboard/stripe/UpdateStripeAccountEndpoint.ts +14 -15
- package/src/endpoints/organization/dashboard/users/CreateApiUserEndpoint.ts +19 -19
- package/src/endpoints/organization/dashboard/users/DeleteUserEndpoint.ts +19 -19
- package/src/endpoints/organization/dashboard/users/GetApiUsersEndpoint.ts +14 -14
- package/src/endpoints/organization/dashboard/users/GetOrganizationAdminsEndpoint.ts +12 -12
- package/src/endpoints/organization/dashboard/webshops/CreateWebshopEndpoint.ts +103 -100
- package/src/endpoints/organization/dashboard/webshops/DeleteWebshopEndpoint.ts +11 -12
- package/src/endpoints/organization/dashboard/webshops/GetDiscountCodesEndpoint.ts +15 -15
- package/src/endpoints/organization/dashboard/webshops/GetWebshopOrdersEndpoint.ts +14 -14
- package/src/endpoints/organization/dashboard/webshops/GetWebshopTicketsEndpoint.ts +14 -14
- package/src/endpoints/organization/dashboard/webshops/GetWebshopUriAvailabilityEndpoint.ts +23 -23
- package/src/endpoints/organization/dashboard/webshops/PatchDiscountCodesEndpoint.ts +54 -52
- package/src/endpoints/organization/dashboard/webshops/PatchWebshopEndpoint.ts +84 -81
- package/src/endpoints/organization/dashboard/webshops/PatchWebshopOrdersEndpoint.ts +120 -111
- package/src/endpoints/organization/dashboard/webshops/PatchWebshopTicketsEndpoint.ts +24 -24
- package/src/endpoints/organization/dashboard/webshops/VerifyWebshopDomainEndpoint.ts +18 -18
- package/src/endpoints/organization/shared/ExchangePaymentEndpoint.ts +141 -130
- package/src/endpoints/organization/shared/GetDocumentHtml.ts +25 -25
- package/src/endpoints/organization/shared/GetPaymentEndpoint.ts +18 -18
- package/src/endpoints/organization/shared/auth/GetOrganizationEndpoint.test.ts +36 -37
- package/src/endpoints/organization/shared/auth/GetOrganizationEndpoint.ts +9 -9
- package/src/endpoints/organization/shared/auth/OpenIDConnectCallbackEndpoint.ts +11 -11
- package/src/endpoints/organization/shared/auth/OpenIDConnectStartEndpoint.ts +28 -27
- package/src/endpoints/organization/webshops/CheckWebshopDiscountCodesEndpoint.ts +20 -20
- package/src/endpoints/organization/webshops/GetOrderByPaymentEndpoint.ts +22 -22
- package/src/endpoints/organization/webshops/GetOrderEndpoint.ts +14 -14
- package/src/endpoints/organization/webshops/GetTicketsEndpoint.ts +57 -56
- package/src/endpoints/organization/webshops/GetWebshopEndpoint.test.ts +65 -66
- package/src/endpoints/organization/webshops/GetWebshopEndpoint.ts +18 -17
- package/src/endpoints/organization/webshops/PlaceOrderEndpoint.test.ts +124 -128
- package/src/endpoints/organization/webshops/PlaceOrderEndpoint.ts +154 -145
- package/src/excel-loaders/members.ts +102 -103
- package/src/excel-loaders/payments.ts +155 -156
- package/src/helpers/AddressValidator.test.ts +32 -32
- package/src/helpers/AddressValidator.ts +128 -122
- package/src/helpers/AdminPermissionChecker.ts +339 -236
- package/src/helpers/AuthenticatedStructures.ts +233 -134
- package/src/helpers/BuckarooHelper.ts +134 -134
- package/src/helpers/CheckSettlements.ts +94 -88
- package/src/helpers/Context.ts +87 -86
- package/src/helpers/CookieHelper.ts +23 -22
- package/src/helpers/EmailResumer.ts +10 -10
- package/src/helpers/FileCache.ts +62 -62
- package/src/helpers/ForwardHandler.test.ts +122 -124
- package/src/helpers/ForwardHandler.ts +76 -70
- package/src/helpers/MemberUserSyncer.ts +101 -96
- package/src/helpers/MembershipCharger.ts +69 -69
- package/src/helpers/MembershipHelper.ts +11 -12
- package/src/helpers/OpenIDConnectHelper.ts +85 -82
- package/src/helpers/PeriodHelper.ts +65 -70
- package/src/helpers/StripeHelper.ts +146 -137
- package/src/helpers/StripePayoutChecker.ts +51 -52
- package/src/helpers/ViesHelper.ts +46 -44
- package/src/helpers/fetchToAsyncIterator.ts +14 -14
- package/src/helpers/xlsxAddressTransformerColumnFactory.ts +50 -52
- package/src/middleware/ContextMiddleware.ts +5 -5
- package/src/migrations/1646578856-validate-addresses.ts +6 -9
- package/src/seeds/0000000000-example.ts +3 -5
- package/src/seeds/1715028563-user-permissions.ts +16 -18
- package/src/seeds/1722256498-group-update-occupancy.ts +12 -12
- package/src/seeds/1722344162-sync-member-users.ts +14 -15
- package/src/seeds/1722344162-update-membership.ts +6 -6
- package/src/seeds/1726055544-balance-item-paid.ts +4 -4
- package/src/seeds/1726055545-balance-item-pending.ts +4 -4
- package/src/seeds/1726494419-update-cached-outstanding-balance.ts +16 -16
- package/src/seeds/1726494420-update-cached-outstanding-balance-from-items.ts +12 -12
- package/src/seeds/1726572303-schedule-stock-updates.ts +12 -12
- package/src/seeds/1726847064-setup-steps.ts +16 -0
- package/src/sql-filters/balance-item-payments.ts +7 -7
- package/src/sql-filters/events.ts +14 -14
- package/src/sql-filters/members.ts +96 -96
- package/src/sql-filters/organizations.ts +139 -75
- package/src/sql-filters/payments.ts +28 -28
- package/src/sql-filters/registrations.ts +14 -14
- package/src/sql-sorters/events.ts +25 -25
- package/src/sql-sorters/members.ts +26 -26
- package/src/sql-sorters/organizations.ts +36 -36
- package/src/sql-sorters/payments.ts +26 -26
- package/tests/e2e/stock.test.ts +616 -621
- package/tests/e2e/tickets.test.ts +255 -260
- package/tests/helpers/StripeMocker.ts +177 -179
- package/tests/helpers/TestServer.ts +9 -9
- package/tests/jest.global.setup.ts +14 -13
- package/tests/jest.setup.ts +33 -32
- package/.eslintrc.js +0 -61
- package/jest.config.js +0 -11
- package/src/helpers/SetupStepsUpdater.ts +0 -359
- package/src/seeds/1724076679-setup-steps.ts +0 -16
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Data, Decoder } from
|
|
1
|
+
import { Data, Decoder } from '@simonbackx/simple-encoding';
|
|
2
2
|
|
|
3
3
|
export class StringArrayDecoder<T> implements Decoder<T[]> {
|
|
4
4
|
decoder: Decoder<T>;
|
|
@@ -11,14 +11,14 @@ export class StringArrayDecoder<T> implements Decoder<T[]> {
|
|
|
11
11
|
const strValue = data.string;
|
|
12
12
|
|
|
13
13
|
// Split on comma
|
|
14
|
-
const parts = strValue.split(
|
|
14
|
+
const parts = strValue.split(',');
|
|
15
15
|
return parts
|
|
16
16
|
.map((v, index) => {
|
|
17
|
-
return data.clone({
|
|
18
|
-
data: v,
|
|
19
|
-
context: data.context,
|
|
20
|
-
field: data.addToCurrentField(index)
|
|
21
|
-
}).decode(this.decoder)
|
|
17
|
+
return data.clone({
|
|
18
|
+
data: v,
|
|
19
|
+
context: data.context,
|
|
20
|
+
field: data.addToCurrentField(index),
|
|
21
|
+
}).decode(this.decoder);
|
|
22
22
|
});
|
|
23
23
|
}
|
|
24
24
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Decoder, Data } from
|
|
1
|
+
import { Decoder, Data } from '@simonbackx/simple-encoding';
|
|
2
2
|
|
|
3
3
|
export class StringNullableDecoder<T> implements Decoder<T | null> {
|
|
4
4
|
decoder: Decoder<T>;
|
|
@@ -15,4 +15,3 @@ export class StringNullableDecoder<T> implements Decoder<T | null> {
|
|
|
15
15
|
return data.decode(this.decoder);
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
|
-
|
|
@@ -1,61 +1,61 @@
|
|
|
1
|
-
import { Email } from
|
|
2
|
-
import { SQL } from
|
|
3
|
-
import { EmailRecipientFilterType, LimitedFilteredRequest, PaginatedResponse, mergeFilters } from
|
|
4
|
-
import { GetMembersEndpoint } from
|
|
1
|
+
import { Email } from '@stamhoofd/models';
|
|
2
|
+
import { SQL } from '@stamhoofd/sql';
|
|
3
|
+
import { EmailRecipientFilterType, LimitedFilteredRequest, PaginatedResponse, mergeFilters } from '@stamhoofd/structures';
|
|
4
|
+
import { GetMembersEndpoint } from '../endpoints/global/members/GetMembersEndpoint';
|
|
5
5
|
|
|
6
6
|
Email.recipientLoaders.set(EmailRecipientFilterType.Members, {
|
|
7
7
|
fetch: async (query: LimitedFilteredRequest) => {
|
|
8
|
-
const result = await GetMembersEndpoint.buildData(query)
|
|
8
|
+
const result = await GetMembersEndpoint.buildData(query);
|
|
9
9
|
|
|
10
10
|
return new PaginatedResponse({
|
|
11
11
|
results: result.results.members.flatMap(m => m.getEmailRecipients(['member'])),
|
|
12
|
-
next: result.next
|
|
12
|
+
next: result.next,
|
|
13
13
|
});
|
|
14
14
|
},
|
|
15
15
|
|
|
16
16
|
count: async (query: LimitedFilteredRequest) => {
|
|
17
17
|
query.filter = mergeFilters([query.filter, {
|
|
18
|
-
|
|
19
|
-
$neq: null
|
|
20
|
-
}
|
|
21
|
-
}])
|
|
22
|
-
const q = await GetMembersEndpoint.buildQuery(query)
|
|
18
|
+
email: {
|
|
19
|
+
$neq: null,
|
|
20
|
+
},
|
|
21
|
+
}]);
|
|
22
|
+
const q = await GetMembersEndpoint.buildQuery(query);
|
|
23
23
|
return await q.count();
|
|
24
|
-
}
|
|
24
|
+
},
|
|
25
25
|
});
|
|
26
26
|
|
|
27
27
|
Email.recipientLoaders.set(EmailRecipientFilterType.MemberParents, {
|
|
28
28
|
fetch: async (query: LimitedFilteredRequest) => {
|
|
29
|
-
const result = await GetMembersEndpoint.buildData(query)
|
|
29
|
+
const result = await GetMembersEndpoint.buildData(query);
|
|
30
30
|
|
|
31
31
|
return new PaginatedResponse({
|
|
32
32
|
results: result.results.members.flatMap(m => m.getEmailRecipients(['parents'])),
|
|
33
|
-
next: result.next
|
|
33
|
+
next: result.next,
|
|
34
34
|
});
|
|
35
35
|
},
|
|
36
36
|
|
|
37
37
|
count: async (query: LimitedFilteredRequest) => {
|
|
38
|
-
const q = await GetMembersEndpoint.buildQuery(query)
|
|
38
|
+
const q = await GetMembersEndpoint.buildQuery(query);
|
|
39
39
|
return await q.sum(
|
|
40
|
-
SQL.jsonLength(SQL.column('details'), '$.value.parents[*].email')
|
|
40
|
+
SQL.jsonLength(SQL.column('details'), '$.value.parents[*].email'),
|
|
41
41
|
);
|
|
42
|
-
}
|
|
42
|
+
},
|
|
43
43
|
});
|
|
44
44
|
|
|
45
45
|
Email.recipientLoaders.set(EmailRecipientFilterType.MemberUnverified, {
|
|
46
46
|
fetch: async (query: LimitedFilteredRequest) => {
|
|
47
|
-
const result = await GetMembersEndpoint.buildData(query)
|
|
47
|
+
const result = await GetMembersEndpoint.buildData(query);
|
|
48
48
|
|
|
49
49
|
return new PaginatedResponse({
|
|
50
50
|
results: result.results.members.flatMap(m => m.getEmailRecipients(['unverified'])),
|
|
51
|
-
next: result.next
|
|
51
|
+
next: result.next,
|
|
52
52
|
});
|
|
53
53
|
},
|
|
54
54
|
|
|
55
55
|
count: async (query: LimitedFilteredRequest) => {
|
|
56
|
-
const q = await GetMembersEndpoint.buildQuery(query)
|
|
56
|
+
const q = await GetMembersEndpoint.buildQuery(query);
|
|
57
57
|
return await q.sum(
|
|
58
|
-
SQL.jsonLength(SQL.column('details'), '$.value.unverifiedEmails')
|
|
58
|
+
SQL.jsonLength(SQL.column('details'), '$.value.unverifiedEmails'),
|
|
59
59
|
);
|
|
60
|
-
}
|
|
60
|
+
},
|
|
61
61
|
});
|
|
@@ -4,7 +4,6 @@ import { QueueHandler } from '@stamhoofd/queues';
|
|
|
4
4
|
import { Context } from '../../../helpers/Context';
|
|
5
5
|
import { MembershipCharger } from '../../../helpers/MembershipCharger';
|
|
6
6
|
|
|
7
|
-
|
|
8
7
|
type Params = Record<string, never>;
|
|
9
8
|
type Query = Record<string, never>;
|
|
10
9
|
type Body = undefined;
|
|
@@ -12,11 +11,11 @@ type ResponseBody = undefined;
|
|
|
12
11
|
|
|
13
12
|
export class ChargeMembershipsEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
14
13
|
protected doesMatch(request: Request): [true, Params] | [false] {
|
|
15
|
-
if (request.method
|
|
14
|
+
if (request.method !== 'POST') {
|
|
16
15
|
return [false];
|
|
17
16
|
}
|
|
18
17
|
|
|
19
|
-
const params = Endpoint.parseParameters(request.url,
|
|
18
|
+
const params = Endpoint.parseParameters(request.url, '/admin/charge-memberships', {});
|
|
20
19
|
|
|
21
20
|
if (params) {
|
|
22
21
|
return [true, params as Params];
|
|
@@ -25,24 +24,24 @@ export class ChargeMembershipsEndpoint extends Endpoint<Params, Query, Body, Res
|
|
|
25
24
|
}
|
|
26
25
|
|
|
27
26
|
async handle(request: DecodedRequest<Params, Query, Body>) {
|
|
28
|
-
await Context.authenticate()
|
|
27
|
+
await Context.authenticate();
|
|
29
28
|
|
|
30
29
|
if (!Context.auth.hasPlatformFullAccess()) {
|
|
31
|
-
throw Context.auth.error()
|
|
30
|
+
throw Context.auth.error();
|
|
32
31
|
}
|
|
33
32
|
|
|
34
33
|
if (QueueHandler.isRunning('charge-memberships')) {
|
|
35
34
|
throw new SimpleError({
|
|
36
35
|
code: 'charge_pending',
|
|
37
36
|
message: 'Charge already pending',
|
|
38
|
-
human: 'Er is al een aanrekening bezig, even geduld.'
|
|
39
|
-
})
|
|
37
|
+
human: 'Er is al een aanrekening bezig, even geduld.',
|
|
38
|
+
});
|
|
40
39
|
}
|
|
41
40
|
|
|
42
41
|
QueueHandler.schedule('charge-memberships', async () => {
|
|
43
|
-
await MembershipCharger.charge()
|
|
42
|
+
await MembershipCharger.charge();
|
|
44
43
|
}).catch(console.error);
|
|
45
|
-
|
|
44
|
+
|
|
46
45
|
return new Response(undefined);
|
|
47
46
|
}
|
|
48
47
|
}
|
|
@@ -6,7 +6,6 @@ import { QueueHandler } from '@stamhoofd/queues';
|
|
|
6
6
|
import { Platform } from '@stamhoofd/models';
|
|
7
7
|
import { SimpleError } from '@simonbackx/simple-errors';
|
|
8
8
|
|
|
9
|
-
|
|
10
9
|
type Params = Record<string, never>;
|
|
11
10
|
type Query = Record<string, never>;
|
|
12
11
|
type Body = undefined;
|
|
@@ -14,11 +13,11 @@ type ResponseBody = ChargeMembershipsSummary;
|
|
|
14
13
|
|
|
15
14
|
export class GetChargeMembershipsSummaryEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
16
15
|
protected doesMatch(request: Request): [true, Params] | [false] {
|
|
17
|
-
if (request.method
|
|
16
|
+
if (request.method !== 'GET') {
|
|
18
17
|
return [false];
|
|
19
18
|
}
|
|
20
19
|
|
|
21
|
-
const params = Endpoint.parseParameters(request.url,
|
|
20
|
+
const params = Endpoint.parseParameters(request.url, '/admin/charge-memberships/summary', {});
|
|
22
21
|
|
|
23
22
|
if (params) {
|
|
24
23
|
return [true, params as Params];
|
|
@@ -27,60 +26,60 @@ export class GetChargeMembershipsSummaryEndpoint extends Endpoint<Params, Query,
|
|
|
27
26
|
}
|
|
28
27
|
|
|
29
28
|
async handle(request: DecodedRequest<Params, Query, Body>) {
|
|
30
|
-
await Context.authenticate()
|
|
29
|
+
await Context.authenticate();
|
|
31
30
|
|
|
32
31
|
if (!Context.auth.hasPlatformFullAccess()) {
|
|
33
|
-
throw Context.auth.error()
|
|
32
|
+
throw Context.auth.error();
|
|
34
33
|
}
|
|
35
34
|
|
|
36
35
|
if (QueueHandler.isRunning('charge-memberships')) {
|
|
37
36
|
return new Response(
|
|
38
37
|
ChargeMembershipsSummary.create({
|
|
39
|
-
running: true
|
|
40
|
-
})
|
|
38
|
+
running: true,
|
|
39
|
+
}),
|
|
41
40
|
);
|
|
42
41
|
}
|
|
43
|
-
|
|
44
|
-
const platform = await Platform.getShared()
|
|
45
|
-
const chargeVia = platform.membershipOrganizationId
|
|
42
|
+
|
|
43
|
+
const platform = await Platform.getShared();
|
|
44
|
+
const chargeVia = platform.membershipOrganizationId;
|
|
46
45
|
|
|
47
46
|
const query = SQL
|
|
48
47
|
.select(
|
|
49
48
|
new SQLSelectAs(
|
|
50
49
|
new SQLCount(
|
|
51
50
|
new SQLDistinct(
|
|
52
|
-
SQL.column('member_platform_memberships', 'id')
|
|
53
|
-
)
|
|
51
|
+
SQL.column('member_platform_memberships', 'id'),
|
|
52
|
+
),
|
|
54
53
|
),
|
|
55
|
-
new SQLAlias('data__memberships')
|
|
54
|
+
new SQLAlias('data__memberships'),
|
|
56
55
|
),
|
|
57
56
|
new SQLSelectAs(
|
|
58
57
|
new SQLCount(
|
|
59
58
|
new SQLDistinct(
|
|
60
|
-
SQL.column('member_platform_memberships', 'memberId')
|
|
61
|
-
)
|
|
59
|
+
SQL.column('member_platform_memberships', 'memberId'),
|
|
60
|
+
),
|
|
62
61
|
),
|
|
63
|
-
new SQLAlias('data__members')
|
|
62
|
+
new SQLAlias('data__members'),
|
|
64
63
|
),
|
|
65
64
|
new SQLSelectAs(
|
|
66
65
|
new SQLCount(
|
|
67
66
|
new SQLDistinct(
|
|
68
|
-
SQL.column('member_platform_memberships', 'organizationId')
|
|
69
|
-
)
|
|
67
|
+
SQL.column('member_platform_memberships', 'organizationId'),
|
|
68
|
+
),
|
|
70
69
|
),
|
|
71
|
-
new SQLAlias('data__organizations')
|
|
70
|
+
new SQLAlias('data__organizations'),
|
|
72
71
|
),
|
|
73
72
|
new SQLSelectAs(
|
|
74
73
|
new SQLSum(
|
|
75
|
-
SQL.column('member_platform_memberships', 'price')
|
|
74
|
+
SQL.column('member_platform_memberships', 'price'),
|
|
76
75
|
),
|
|
77
|
-
new SQLAlias('data__price')
|
|
78
|
-
)
|
|
76
|
+
new SQLAlias('data__price'),
|
|
77
|
+
),
|
|
79
78
|
)
|
|
80
79
|
.from('member_platform_memberships')
|
|
81
80
|
.where('balanceItemId', null)
|
|
82
81
|
.where('deletedAt', null)
|
|
83
|
-
.whereNot('organizationId', chargeVia)
|
|
82
|
+
.whereNot('organizationId', chargeVia);
|
|
84
83
|
|
|
85
84
|
const result = await query.fetch();
|
|
86
85
|
const members = result[0]['data']['members'] as number;
|
|
@@ -95,52 +94,52 @@ export class GetChargeMembershipsSummaryEndpoint extends Endpoint<Params, Query,
|
|
|
95
94
|
members: members ?? 0,
|
|
96
95
|
price: price ?? 0,
|
|
97
96
|
organizations: organizations ?? 0,
|
|
98
|
-
membershipsPerType: await this.fetchPerType(chargeVia)
|
|
99
|
-
})
|
|
97
|
+
membershipsPerType: await this.fetchPerType(chargeVia),
|
|
98
|
+
}),
|
|
100
99
|
);
|
|
101
100
|
}
|
|
102
101
|
|
|
103
|
-
async fetchPerType(chargeVia: string|null) {
|
|
102
|
+
async fetchPerType(chargeVia: string | null) {
|
|
104
103
|
const query = SQL
|
|
105
104
|
.select(
|
|
106
105
|
SQL.column('member_platform_memberships', 'membershipTypeId'),
|
|
107
106
|
new SQLSelectAs(
|
|
108
107
|
new SQLCount(
|
|
109
108
|
new SQLDistinct(
|
|
110
|
-
SQL.column('member_platform_memberships', 'id')
|
|
111
|
-
)
|
|
109
|
+
SQL.column('member_platform_memberships', 'id'),
|
|
110
|
+
),
|
|
112
111
|
),
|
|
113
|
-
new SQLAlias('data__memberships')
|
|
112
|
+
new SQLAlias('data__memberships'),
|
|
114
113
|
),
|
|
115
114
|
new SQLSelectAs(
|
|
116
115
|
new SQLCount(
|
|
117
116
|
new SQLDistinct(
|
|
118
|
-
SQL.column('member_platform_memberships', 'memberId')
|
|
119
|
-
)
|
|
117
|
+
SQL.column('member_platform_memberships', 'memberId'),
|
|
118
|
+
),
|
|
120
119
|
),
|
|
121
|
-
new SQLAlias('data__members')
|
|
120
|
+
new SQLAlias('data__members'),
|
|
122
121
|
),
|
|
123
122
|
new SQLSelectAs(
|
|
124
123
|
new SQLCount(
|
|
125
124
|
new SQLDistinct(
|
|
126
|
-
SQL.column('member_platform_memberships', 'organizationId')
|
|
127
|
-
)
|
|
125
|
+
SQL.column('member_platform_memberships', 'organizationId'),
|
|
126
|
+
),
|
|
128
127
|
),
|
|
129
|
-
new SQLAlias('data__organizations')
|
|
128
|
+
new SQLAlias('data__organizations'),
|
|
130
129
|
),
|
|
131
130
|
new SQLSelectAs(
|
|
132
131
|
new SQLSum(
|
|
133
|
-
SQL.column('member_platform_memberships', 'price')
|
|
132
|
+
SQL.column('member_platform_memberships', 'price'),
|
|
134
133
|
),
|
|
135
|
-
new SQLAlias('data__price')
|
|
136
|
-
)
|
|
134
|
+
new SQLAlias('data__price'),
|
|
135
|
+
),
|
|
137
136
|
)
|
|
138
137
|
.from('member_platform_memberships')
|
|
139
138
|
.where('balanceItemId', null)
|
|
140
139
|
.where('deletedAt', null)
|
|
141
140
|
.whereNot('organizationId', chargeVia)
|
|
142
141
|
.groupBy(
|
|
143
|
-
SQL.column('member_platform_memberships', 'membershipTypeId')
|
|
142
|
+
SQL.column('member_platform_memberships', 'membershipTypeId'),
|
|
144
143
|
);
|
|
145
144
|
|
|
146
145
|
const result = await query.fetch();
|
|
@@ -151,7 +150,7 @@ export class GetChargeMembershipsSummaryEndpoint extends Endpoint<Params, Query,
|
|
|
151
150
|
memberships: row['data']['memberships'] as number,
|
|
152
151
|
members: row['data']['members'] as number,
|
|
153
152
|
price: row['data']['price'] as number,
|
|
154
|
-
organizations: row['data']['organizations'] as number
|
|
153
|
+
organizations: row['data']['organizations'] as number,
|
|
155
154
|
}));
|
|
156
155
|
}
|
|
157
156
|
|
|
@@ -11,14 +11,14 @@ type Body = undefined;
|
|
|
11
11
|
type ResponseBody = CountResponse;
|
|
12
12
|
|
|
13
13
|
export class GetOrganizationsCountEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
14
|
-
queryDecoder = CountFilteredRequest as Decoder<CountFilteredRequest
|
|
14
|
+
queryDecoder = CountFilteredRequest as Decoder<CountFilteredRequest>;
|
|
15
15
|
|
|
16
16
|
protected doesMatch(request: Request): [true, Params] | [false] {
|
|
17
|
-
if (request.method
|
|
17
|
+
if (request.method !== 'GET') {
|
|
18
18
|
return [false];
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
const params = Endpoint.parseParameters(request.url,
|
|
21
|
+
const params = Endpoint.parseParameters(request.url, '/admin/organizations/count', {});
|
|
22
22
|
|
|
23
23
|
if (params) {
|
|
24
24
|
return [true, params as Params];
|
|
@@ -27,16 +27,16 @@ export class GetOrganizationsCountEndpoint extends Endpoint<Params, Query, Body,
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
async handle(request: DecodedRequest<Params, Query, Body>) {
|
|
30
|
-
await Context.authenticate()
|
|
31
|
-
const query = GetOrganizationsEndpoint.buildQuery(request.query)
|
|
32
|
-
|
|
30
|
+
await Context.authenticate();
|
|
31
|
+
const query = GetOrganizationsEndpoint.buildQuery(request.query);
|
|
32
|
+
|
|
33
33
|
const count = await query
|
|
34
34
|
.count();
|
|
35
35
|
|
|
36
36
|
return new Response(
|
|
37
37
|
CountResponse.create({
|
|
38
|
-
count
|
|
39
|
-
})
|
|
38
|
+
count,
|
|
39
|
+
}),
|
|
40
40
|
);
|
|
41
41
|
}
|
|
42
42
|
}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
|
2
1
|
import { Decoder } from '@simonbackx/simple-encoding';
|
|
3
2
|
import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
|
|
4
3
|
import { SimpleError } from '@simonbackx/simple-errors';
|
|
5
4
|
import { Organization } from '@stamhoofd/models';
|
|
6
|
-
import { SQL, compileToSQLFilter, compileToSQLSorter } from
|
|
5
|
+
import { SQL, compileToSQLFilter, compileToSQLSorter } from '@stamhoofd/sql';
|
|
7
6
|
import { CountFilteredRequest, LimitedFilteredRequest, Organization as OrganizationStruct, PaginatedResponse, PermissionLevel, StamhoofdFilter, assertSort, getSortFilter } from '@stamhoofd/structures';
|
|
8
7
|
|
|
9
8
|
import { AuthenticatedStructures } from '../../../helpers/AuthenticatedStructures';
|
|
@@ -14,20 +13,20 @@ import { organizationSorters } from '../../../sql-sorters/organizations';
|
|
|
14
13
|
type Params = Record<string, never>;
|
|
15
14
|
type Query = LimitedFilteredRequest;
|
|
16
15
|
type Body = undefined;
|
|
17
|
-
type ResponseBody = PaginatedResponse<OrganizationStruct[], LimitedFilteredRequest
|
|
16
|
+
type ResponseBody = PaginatedResponse<OrganizationStruct[], LimitedFilteredRequest>;
|
|
18
17
|
|
|
19
|
-
const sorters = organizationSorters
|
|
20
|
-
const filterCompilers = organizationFilterCompilers
|
|
18
|
+
const sorters = organizationSorters;
|
|
19
|
+
const filterCompilers = organizationFilterCompilers;
|
|
21
20
|
|
|
22
21
|
export class GetOrganizationsEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
23
|
-
queryDecoder = LimitedFilteredRequest as Decoder<LimitedFilteredRequest
|
|
22
|
+
queryDecoder = LimitedFilteredRequest as Decoder<LimitedFilteredRequest>;
|
|
24
23
|
|
|
25
24
|
protected doesMatch(request: Request): [true, Params] | [false] {
|
|
26
|
-
if (request.method
|
|
25
|
+
if (request.method !== 'GET') {
|
|
27
26
|
return [false];
|
|
28
27
|
}
|
|
29
28
|
|
|
30
|
-
const params = Endpoint.parseParameters(request.url,
|
|
29
|
+
const params = Endpoint.parseParameters(request.url, '/admin/organizations', {});
|
|
31
30
|
|
|
32
31
|
if (params) {
|
|
33
32
|
return [true, params as Params];
|
|
@@ -35,69 +34,69 @@ export class GetOrganizationsEndpoint extends Endpoint<Params, Query, Body, Resp
|
|
|
35
34
|
return [false];
|
|
36
35
|
}
|
|
37
36
|
|
|
38
|
-
static buildQuery(q: CountFilteredRequest|LimitedFilteredRequest) {
|
|
39
|
-
const tags = Context.auth.getPlatformAccessibleOrganizationTags(PermissionLevel.Read)
|
|
40
|
-
if (tags
|
|
41
|
-
throw Context.auth.error()
|
|
37
|
+
static buildQuery(q: CountFilteredRequest | LimitedFilteredRequest) {
|
|
38
|
+
const tags = Context.auth.getPlatformAccessibleOrganizationTags(PermissionLevel.Read);
|
|
39
|
+
if (tags !== 'all' && tags.length === 0) {
|
|
40
|
+
throw Context.auth.error();
|
|
42
41
|
}
|
|
43
42
|
|
|
44
|
-
let scopeFilter: StamhoofdFilter|undefined = undefined;
|
|
43
|
+
let scopeFilter: StamhoofdFilter | undefined = undefined;
|
|
45
44
|
|
|
46
45
|
if (tags !== 'all') {
|
|
47
46
|
// Add organization scope filter
|
|
48
47
|
scopeFilter = {
|
|
49
48
|
tags: {
|
|
50
|
-
$in: tags
|
|
51
|
-
}
|
|
49
|
+
$in: tags,
|
|
50
|
+
},
|
|
52
51
|
};
|
|
53
52
|
}
|
|
54
|
-
|
|
53
|
+
|
|
55
54
|
const query = SQL
|
|
56
55
|
.select(
|
|
57
|
-
SQL.wildcard('organizations')
|
|
56
|
+
SQL.wildcard('organizations'),
|
|
58
57
|
)
|
|
59
58
|
.from(
|
|
60
|
-
SQL.table('organizations')
|
|
59
|
+
SQL.table('organizations'),
|
|
61
60
|
);
|
|
62
|
-
|
|
61
|
+
|
|
63
62
|
if (scopeFilter) {
|
|
64
|
-
query.where(compileToSQLFilter(scopeFilter, filterCompilers))
|
|
63
|
+
query.where(compileToSQLFilter(scopeFilter, filterCompilers));
|
|
65
64
|
}
|
|
66
65
|
|
|
67
66
|
if (q.filter) {
|
|
68
|
-
query.where(compileToSQLFilter(q.filter, filterCompilers))
|
|
67
|
+
query.where(compileToSQLFilter(q.filter, filterCompilers));
|
|
69
68
|
}
|
|
70
69
|
|
|
71
70
|
if (q.search) {
|
|
72
|
-
let searchFilter: StamhoofdFilter|null = null
|
|
71
|
+
let searchFilter: StamhoofdFilter | null = null;
|
|
73
72
|
|
|
74
73
|
// todo: auto detect e-mailaddresses and search on admins
|
|
75
74
|
searchFilter = {
|
|
76
75
|
name: {
|
|
77
|
-
$contains: q.search
|
|
78
|
-
}
|
|
79
|
-
}
|
|
76
|
+
$contains: q.search,
|
|
77
|
+
},
|
|
78
|
+
};
|
|
80
79
|
|
|
81
80
|
if (searchFilter) {
|
|
82
|
-
query.where(compileToSQLFilter(searchFilter, filterCompilers))
|
|
81
|
+
query.where(compileToSQLFilter(searchFilter, filterCompilers));
|
|
83
82
|
}
|
|
84
83
|
}
|
|
85
84
|
|
|
86
85
|
if (q instanceof LimitedFilteredRequest) {
|
|
87
86
|
if (q.pageFilter) {
|
|
88
|
-
query.where(compileToSQLFilter(q.pageFilter, filterCompilers))
|
|
87
|
+
query.where(compileToSQLFilter(q.pageFilter, filterCompilers));
|
|
89
88
|
}
|
|
90
89
|
|
|
91
|
-
q.sort = assertSort(q.sort, [{key: 'id'}])
|
|
92
|
-
query.orderBy(compileToSQLSorter(q.sort, sorters))
|
|
93
|
-
query.limit(q.limit)
|
|
90
|
+
q.sort = assertSort(q.sort, [{ key: 'id' }]);
|
|
91
|
+
query.orderBy(compileToSQLSorter(q.sort, sorters));
|
|
92
|
+
query.limit(q.limit);
|
|
94
93
|
}
|
|
95
|
-
|
|
96
|
-
return query
|
|
94
|
+
|
|
95
|
+
return query;
|
|
97
96
|
}
|
|
98
97
|
|
|
99
98
|
async handle(request: DecodedRequest<Params, Query, Body>) {
|
|
100
|
-
await Context.authenticate()
|
|
99
|
+
await Context.authenticate();
|
|
101
100
|
|
|
102
101
|
const maxLimit = Context.auth.hasSomePlatformAccess() ? 1000 : 100;
|
|
103
102
|
|
|
@@ -105,22 +104,22 @@ export class GetOrganizationsEndpoint extends Endpoint<Params, Query, Body, Resp
|
|
|
105
104
|
throw new SimpleError({
|
|
106
105
|
code: 'invalid_field',
|
|
107
106
|
field: 'limit',
|
|
108
|
-
message: 'Limit can not be more than ' + maxLimit
|
|
109
|
-
})
|
|
107
|
+
message: 'Limit can not be more than ' + maxLimit,
|
|
108
|
+
});
|
|
110
109
|
}
|
|
111
110
|
|
|
112
111
|
if (request.query.limit < 1) {
|
|
113
112
|
throw new SimpleError({
|
|
114
113
|
code: 'invalid_field',
|
|
115
114
|
field: 'limit',
|
|
116
|
-
message: 'Limit can not be less than 1'
|
|
117
|
-
})
|
|
115
|
+
message: 'Limit can not be less than 1',
|
|
116
|
+
});
|
|
118
117
|
}
|
|
119
|
-
|
|
120
|
-
const data = await GetOrganizationsEndpoint.buildQuery(request.query).fetch()
|
|
118
|
+
|
|
119
|
+
const data = await GetOrganizationsEndpoint.buildQuery(request.query).fetch();
|
|
121
120
|
const organizations = Organization.fromRows(data, 'organizations');
|
|
122
121
|
|
|
123
|
-
let next: LimitedFilteredRequest|undefined;
|
|
122
|
+
let next: LimitedFilteredRequest | undefined;
|
|
124
123
|
|
|
125
124
|
if (organizations.length >= request.query.limit) {
|
|
126
125
|
const lastObject = organizations[organizations.length - 1];
|
|
@@ -131,8 +130,8 @@ export class GetOrganizationsEndpoint extends Endpoint<Params, Query, Body, Resp
|
|
|
131
130
|
pageFilter: nextFilter,
|
|
132
131
|
sort: request.query.sort,
|
|
133
132
|
limit: request.query.limit,
|
|
134
|
-
search: request.query.search
|
|
135
|
-
})
|
|
133
|
+
search: request.query.search,
|
|
134
|
+
});
|
|
136
135
|
|
|
137
136
|
if (JSON.stringify(nextFilter) === JSON.stringify(request.query.pageFilter)) {
|
|
138
137
|
console.error('Found infinite loading loop for', request.query);
|
|
@@ -142,9 +141,9 @@ export class GetOrganizationsEndpoint extends Endpoint<Params, Query, Body, Resp
|
|
|
142
141
|
|
|
143
142
|
return new Response(
|
|
144
143
|
new PaginatedResponse<OrganizationStruct[], LimitedFilteredRequest>({
|
|
145
|
-
results: await AuthenticatedStructures.
|
|
146
|
-
next
|
|
147
|
-
})
|
|
144
|
+
results: await AuthenticatedStructures.organizations(organizations),
|
|
145
|
+
next,
|
|
146
|
+
}),
|
|
148
147
|
);
|
|
149
148
|
}
|
|
150
149
|
}
|