@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,11 +1,11 @@
|
|
|
1
1
|
import { createMollieClient, PaymentMethod as molliePaymentMethod } from '@mollie/api-client';
|
|
2
2
|
import { ManyToOneRelation } from '@simonbackx/simple-database';
|
|
3
3
|
import { Decoder } from '@simonbackx/simple-encoding';
|
|
4
|
-
import { DecodedRequest, Endpoint, Request, Response } from
|
|
4
|
+
import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
|
|
5
5
|
import { SimpleError } from '@simonbackx/simple-errors';
|
|
6
6
|
import { Email } from '@stamhoofd/email';
|
|
7
7
|
import { BalanceItem, BalanceItemPayment, Group, Member, MemberWithRegistrations, MolliePayment, MollieToken, Organization, PayconiqPayment, Payment, Platform, RateLimiter, Registration, User } from '@stamhoofd/models';
|
|
8
|
-
import { BalanceItemRelation, BalanceItemRelationType, BalanceItemStatus, BalanceItemType, BalanceItemWithPayments, IDRegisterCheckout, PaymentCustomer, PaymentMethod, PaymentMethodHelper, PaymentProvider, PaymentStatus, Payment as PaymentStruct, PermissionLevel, PlatformFamily, PlatformMember, RegisterItem, RegisterResponse, Version } from
|
|
8
|
+
import { BalanceItemRelation, BalanceItemRelationType, BalanceItemStatus, BalanceItemType, BalanceItemWithPayments, IDRegisterCheckout, PaymentCustomer, PaymentMethod, PaymentMethodHelper, PaymentProvider, PaymentStatus, Payment as PaymentStruct, PermissionLevel, PlatformFamily, PlatformMember, RegisterItem, RegisterResponse, Version } from '@stamhoofd/structures';
|
|
9
9
|
import { Formatter } from '@stamhoofd/utility';
|
|
10
10
|
|
|
11
11
|
import { AuthenticatedStructures } from '../../../helpers/AuthenticatedStructures';
|
|
@@ -14,46 +14,46 @@ import { Context } from '../../../helpers/Context';
|
|
|
14
14
|
import { StripeHelper } from '../../../helpers/StripeHelper';
|
|
15
15
|
type Params = Record<string, never>;
|
|
16
16
|
type Query = undefined;
|
|
17
|
-
type Body = IDRegisterCheckout
|
|
18
|
-
type ResponseBody = RegisterResponse
|
|
17
|
+
type Body = IDRegisterCheckout;
|
|
18
|
+
type ResponseBody = RegisterResponse;
|
|
19
19
|
|
|
20
20
|
export const demoLimiter = new RateLimiter({
|
|
21
21
|
limits: [
|
|
22
|
-
{
|
|
22
|
+
{
|
|
23
23
|
// Max 10 per hour
|
|
24
24
|
limit: 10,
|
|
25
|
-
duration: 60 * 1000 * 60
|
|
25
|
+
duration: 60 * 1000 * 60,
|
|
26
26
|
},
|
|
27
|
-
{
|
|
27
|
+
{
|
|
28
28
|
// Max 20 a day
|
|
29
29
|
limit: 20,
|
|
30
|
-
duration: 24 * 60 * 1000 * 60
|
|
31
|
-
}
|
|
32
|
-
]
|
|
30
|
+
duration: 24 * 60 * 1000 * 60,
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
33
|
});
|
|
34
34
|
|
|
35
|
-
export type RegistrationWithMemberAndGroup = Registration & { member: Member } & { group: Group }
|
|
35
|
+
export type RegistrationWithMemberAndGroup = Registration & { member: Member } & { group: Group };
|
|
36
36
|
|
|
37
37
|
/**
|
|
38
38
|
* Allow to add, patch and delete multiple members simultaneously, which is needed in order to sync relational data that is saved encrypted in multiple members (e.g. parents)
|
|
39
39
|
*/
|
|
40
40
|
export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
41
|
-
bodyDecoder = IDRegisterCheckout as Decoder<IDRegisterCheckout
|
|
41
|
+
bodyDecoder = IDRegisterCheckout as Decoder<IDRegisterCheckout>;
|
|
42
42
|
|
|
43
43
|
protected doesMatch(request: Request): [true, Params] | [false] {
|
|
44
|
-
if (request.method
|
|
44
|
+
if (request.method !== 'POST') {
|
|
45
45
|
return [false];
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
const params = Endpoint.parseParameters(request.url,
|
|
48
|
+
const params = Endpoint.parseParameters(request.url, '/members/register', {});
|
|
49
49
|
|
|
50
50
|
if (params) {
|
|
51
51
|
if (request.getVersion() < 257) {
|
|
52
52
|
throw new SimpleError({
|
|
53
|
-
code:
|
|
54
|
-
message:
|
|
55
|
-
human:
|
|
56
|
-
})
|
|
53
|
+
code: 'not_supported',
|
|
54
|
+
message: 'This version is no longer supported',
|
|
55
|
+
human: 'Oops! Je gebruikt een oude versie van de applicatie om in te schrijven. Herlaad de website en verwijder indien nodig de cache van jouw browser.',
|
|
56
|
+
});
|
|
57
57
|
}
|
|
58
58
|
return [true, params as Params];
|
|
59
59
|
}
|
|
@@ -62,171 +62,173 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
|
|
|
62
62
|
|
|
63
63
|
async handle(request: DecodedRequest<Params, Query, Body>) {
|
|
64
64
|
const organization = await Context.setOrganizationScope();
|
|
65
|
-
const {user} = await Context.authenticate()
|
|
65
|
+
const { user } = await Context.authenticate();
|
|
66
66
|
|
|
67
67
|
if (request.body.asOrganizationId && request.body.asOrganizationId !== organization.id) {
|
|
68
68
|
if (!await Context.auth.hasFullAccess(request.body.asOrganizationId)) {
|
|
69
69
|
throw new SimpleError({
|
|
70
|
-
code:
|
|
71
|
-
message:
|
|
70
|
+
code: 'forbidden',
|
|
71
|
+
message: 'No permission to register as this organization for a different organization',
|
|
72
72
|
human: 'Je hebt niet de juiste toegangsrechten om leden in te schrijven bij een andere organisatie.',
|
|
73
|
-
statusCode: 403
|
|
73
|
+
statusCode: 403,
|
|
74
74
|
});
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
// For non paid organizations, limit amount of tests
|
|
79
79
|
if (!organization.meta.packages.isPaid) {
|
|
80
|
-
const limiter = demoLimiter
|
|
80
|
+
const limiter = demoLimiter;
|
|
81
81
|
|
|
82
82
|
try {
|
|
83
83
|
limiter.track(organization.id, 1);
|
|
84
|
-
}
|
|
84
|
+
}
|
|
85
|
+
catch (e) {
|
|
85
86
|
Email.sendWebmaster({
|
|
86
|
-
subject:
|
|
87
|
-
text:
|
|
88
|
-
})
|
|
87
|
+
subject: '[Limiet] Limiet bereikt voor aantal inschrijvingen',
|
|
88
|
+
text: 'Beste, \nDe limiet werd bereikt voor het aantal inschrijvingen per dag. \nVereniging: ' + organization.id + ' (' + organization.name + ')' + '\n\n' + e.message + '\n\nStamhoofd',
|
|
89
|
+
});
|
|
89
90
|
|
|
90
91
|
throw new SimpleError({
|
|
91
|
-
code:
|
|
92
|
-
message:
|
|
93
|
-
human:
|
|
94
|
-
field:
|
|
95
|
-
})
|
|
92
|
+
code: 'too_many_emails_period',
|
|
93
|
+
message: 'Too many e-mails limited',
|
|
94
|
+
human: 'Oeps! Om spam te voorkomen limiteren we het aantal test inschrijvingen die je per uur of dag kan plaatsen. Probeer over een uur opnieuw of schakel over naar een betaald abonnement.',
|
|
95
|
+
field: 'recipients',
|
|
96
|
+
});
|
|
96
97
|
}
|
|
97
98
|
}
|
|
98
99
|
|
|
99
|
-
const deleteRegistrationIds = request.body.cart.deleteRegistrationIds
|
|
100
|
-
const deleteRegistrationModels = (deleteRegistrationIds.length ? (await Registration.getByIDs(...deleteRegistrationIds)) : []).filter(r => r.organizationId === organization.id)
|
|
100
|
+
const deleteRegistrationIds = request.body.cart.deleteRegistrationIds;
|
|
101
|
+
const deleteRegistrationModels = (deleteRegistrationIds.length ? (await Registration.getByIDs(...deleteRegistrationIds)) : []).filter(r => r.organizationId === organization.id);
|
|
101
102
|
|
|
102
103
|
// Validate balance items (can only happen serverside)
|
|
103
|
-
const balanceItemIds = request.body.cart.balanceItems.map(i => i.item.id)
|
|
104
|
-
let memberBalanceItemsStructs: BalanceItemWithPayments[] = []
|
|
105
|
-
let balanceItemsModels: BalanceItem[] = []
|
|
104
|
+
const balanceItemIds = request.body.cart.balanceItems.map(i => i.item.id);
|
|
105
|
+
let memberBalanceItemsStructs: BalanceItemWithPayments[] = [];
|
|
106
|
+
let balanceItemsModels: BalanceItem[] = [];
|
|
106
107
|
if (balanceItemIds.length > 0) {
|
|
107
|
-
balanceItemsModels = await BalanceItem.where({ id: { sign:'IN', value: balanceItemIds }, organizationId: organization.id })
|
|
108
|
-
if (balanceItemsModels.length
|
|
108
|
+
balanceItemsModels = await BalanceItem.where({ id: { sign: 'IN', value: balanceItemIds }, organizationId: organization.id });
|
|
109
|
+
if (balanceItemsModels.length !== balanceItemIds.length) {
|
|
109
110
|
throw new SimpleError({
|
|
110
|
-
code:
|
|
111
|
-
message:
|
|
112
|
-
})
|
|
111
|
+
code: 'invalid_data',
|
|
112
|
+
message: 'Oeps, één of meerdere openstaande bedragen in jouw winkelmandje zijn aangepast. Herlaad de pagina en probeer opnieuw.',
|
|
113
|
+
});
|
|
113
114
|
}
|
|
114
|
-
memberBalanceItemsStructs = await BalanceItem.getStructureWithPayments(balanceItemsModels)
|
|
115
|
+
memberBalanceItemsStructs = await BalanceItem.getStructureWithPayments(balanceItemsModels);
|
|
115
116
|
}
|
|
116
117
|
|
|
117
118
|
const memberIds = Formatter.uniqueArray(
|
|
118
|
-
[...request.body.memberIds, ...deleteRegistrationModels.map(i => i.memberId), ...balanceItemsModels.map(i => i.memberId).filter(m => m !== null)]
|
|
119
|
-
)
|
|
120
|
-
const members = await Member.getBlobByIds(...memberIds)
|
|
121
|
-
const groupIds = request.body.groupIds
|
|
122
|
-
const groups = await Group.getByIDs(...groupIds)
|
|
119
|
+
[...request.body.memberIds, ...deleteRegistrationModels.map(i => i.memberId), ...balanceItemsModels.map(i => i.memberId).filter(m => m !== null)],
|
|
120
|
+
);
|
|
121
|
+
const members = await Member.getBlobByIds(...memberIds);
|
|
122
|
+
const groupIds = request.body.groupIds;
|
|
123
|
+
const groups = await Group.getByIDs(...groupIds);
|
|
123
124
|
|
|
124
125
|
for (const group of groups) {
|
|
125
126
|
if (group.organizationId !== organization.id) {
|
|
126
127
|
throw new SimpleError({
|
|
127
|
-
code:
|
|
128
|
-
message:
|
|
129
|
-
})
|
|
128
|
+
code: 'invalid_data',
|
|
129
|
+
message: 'Oeps, één of meerdere groepen waarin je probeert in te schrijven lijken niet meer te bestaan. Herlaad de pagina en probeer opnieuw.',
|
|
130
|
+
});
|
|
130
131
|
}
|
|
131
132
|
}
|
|
132
133
|
|
|
133
134
|
for (const member of members) {
|
|
134
135
|
if (!await Context.auth.canAccessMember(member, PermissionLevel.Write)) {
|
|
135
136
|
throw new SimpleError({
|
|
136
|
-
code:
|
|
137
|
-
message:
|
|
137
|
+
code: 'forbidden',
|
|
138
|
+
message: 'No permission to register this member',
|
|
138
139
|
human: 'Je hebt niet de juiste toegangsrechten om dit lid in te schrijven. Je kan enkel leden inschrijven als je minstens bewerkrechten hebt voor dat lid.',
|
|
139
|
-
statusCode: 403
|
|
140
|
+
statusCode: 403,
|
|
140
141
|
});
|
|
141
142
|
}
|
|
142
143
|
}
|
|
143
144
|
|
|
144
|
-
const blob = await AuthenticatedStructures.membersBlob(members, true)
|
|
145
|
-
const platformMembers: PlatformMember[] = []
|
|
145
|
+
const blob = await AuthenticatedStructures.membersBlob(members, true);
|
|
146
|
+
const platformMembers: PlatformMember[] = [];
|
|
146
147
|
|
|
147
148
|
if (request.body.asOrganizationId) {
|
|
148
149
|
const _m = PlatformFamily.createSingles(blob, {
|
|
149
150
|
platform: await Platform.getSharedStruct(),
|
|
150
|
-
contextOrganization: await AuthenticatedStructures.organization(organization)
|
|
151
|
-
})
|
|
152
|
-
platformMembers.push(..._m)
|
|
153
|
-
}
|
|
151
|
+
contextOrganization: await AuthenticatedStructures.organization(organization),
|
|
152
|
+
});
|
|
153
|
+
platformMembers.push(..._m);
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
154
156
|
const family = PlatformFamily.create(blob, {
|
|
155
157
|
platform: await Platform.getSharedStruct(),
|
|
156
|
-
contextOrganization: await AuthenticatedStructures.organization(organization)
|
|
157
|
-
})
|
|
158
|
-
platformMembers.push(...family.members)
|
|
158
|
+
contextOrganization: await AuthenticatedStructures.organization(organization),
|
|
159
|
+
});
|
|
160
|
+
platformMembers.push(...family.members);
|
|
159
161
|
}
|
|
160
162
|
|
|
161
|
-
const organizationStruct = await AuthenticatedStructures.organization(organization)
|
|
163
|
+
const organizationStruct = await AuthenticatedStructures.organization(organization);
|
|
162
164
|
const checkout = request.body.hydrate({
|
|
163
165
|
members: platformMembers,
|
|
164
166
|
groups: await AuthenticatedStructures.groups(groups),
|
|
165
|
-
organizations: [organizationStruct]
|
|
166
|
-
})
|
|
167
|
+
organizations: [organizationStruct],
|
|
168
|
+
});
|
|
167
169
|
|
|
168
170
|
// Set circular references
|
|
169
171
|
for (const member of platformMembers) {
|
|
170
|
-
member.family.checkout = checkout
|
|
172
|
+
member.family.checkout = checkout;
|
|
171
173
|
}
|
|
172
174
|
|
|
173
|
-
checkout.setDefaultOrganization(organizationStruct)
|
|
174
|
-
|
|
175
|
-
const registrations: RegistrationWithMemberAndGroup[] = []
|
|
176
|
-
const payRegistrations: {registration: RegistrationWithMemberAndGroup
|
|
175
|
+
checkout.setDefaultOrganization(organizationStruct);
|
|
176
|
+
|
|
177
|
+
const registrations: RegistrationWithMemberAndGroup[] = [];
|
|
178
|
+
const payRegistrations: { registration: RegistrationWithMemberAndGroup; item: RegisterItem }[] = [];
|
|
177
179
|
const deactivatedRegistrationGroupIds: string[] = [];
|
|
178
180
|
|
|
179
181
|
if (checkout.cart.isEmpty) {
|
|
180
182
|
throw new SimpleError({
|
|
181
|
-
code:
|
|
182
|
-
message:
|
|
183
|
-
})
|
|
183
|
+
code: 'empty_data',
|
|
184
|
+
message: 'Oeps, jouw mandje is leeg. Voeg eerst inschrijvingen toe voor je verder gaat.',
|
|
185
|
+
});
|
|
184
186
|
}
|
|
185
187
|
|
|
186
188
|
// Validate the cart
|
|
187
|
-
checkout.validate({memberBalanceItems: memberBalanceItemsStructs})
|
|
189
|
+
checkout.validate({ memberBalanceItems: memberBalanceItemsStructs });
|
|
188
190
|
|
|
189
191
|
// Recalculate the price
|
|
190
|
-
checkout.updatePrices()
|
|
192
|
+
checkout.updatePrices();
|
|
191
193
|
|
|
192
|
-
const totalPrice = checkout.totalPrice
|
|
194
|
+
const totalPrice = checkout.totalPrice;
|
|
193
195
|
|
|
194
196
|
if (totalPrice !== request.body.totalPrice) {
|
|
195
197
|
throw new SimpleError({
|
|
196
|
-
code:
|
|
197
|
-
message:
|
|
198
|
-
})
|
|
198
|
+
code: 'changed_price',
|
|
199
|
+
message: 'Oeps! De prijs is gewijzigd terwijl je aan het afrekenen was (naar ' + Formatter.price(totalPrice) + '). Herlaad de pagina even om ervoor te zorgen dat je alle aangepaste prijzen ziet. Contacteer de webmaster als je dit probleem blijft ondervinden na het te herladen.',
|
|
200
|
+
});
|
|
199
201
|
}
|
|
200
|
-
|
|
202
|
+
|
|
201
203
|
if (totalPrice < 0) {
|
|
202
204
|
throw new SimpleError({
|
|
203
|
-
code:
|
|
204
|
-
message:
|
|
205
|
-
})
|
|
205
|
+
code: 'empty_data',
|
|
206
|
+
message: 'Oeps! De totaalprijs is negatief.',
|
|
207
|
+
});
|
|
206
208
|
}
|
|
207
209
|
|
|
208
|
-
const registrationMemberRelation = new ManyToOneRelation(Member,
|
|
209
|
-
registrationMemberRelation.foreignKey =
|
|
210
|
+
const registrationMemberRelation = new ManyToOneRelation(Member, 'member');
|
|
211
|
+
registrationMemberRelation.foreignKey = 'memberId';
|
|
210
212
|
|
|
211
213
|
for (const item of checkout.cart.items) {
|
|
212
|
-
const member = members.find(m => m.id == item.memberId)
|
|
214
|
+
const member = members.find(m => m.id == item.memberId);
|
|
213
215
|
if (!member) {
|
|
214
216
|
throw new SimpleError({
|
|
215
|
-
code:
|
|
216
|
-
message:
|
|
217
|
-
})
|
|
217
|
+
code: 'invalid_member',
|
|
218
|
+
message: 'Het lid dat je probeert in te schrijven konden we niet meer terugvinden. Je herlaadt best even de pagina om opnieuw te proberen.',
|
|
219
|
+
});
|
|
218
220
|
}
|
|
219
221
|
|
|
220
222
|
const group = groups.find(g => g.id == item.groupId);
|
|
221
223
|
if (!group) {
|
|
222
224
|
throw new SimpleError({
|
|
223
|
-
code:
|
|
224
|
-
message:
|
|
225
|
-
})
|
|
225
|
+
code: 'invalid_member',
|
|
226
|
+
message: 'De groep waarin je een lid probeert in te schrijven lijkt niet meer te bestaan. Je herlaadt best even de pagina om opnieuw te proberen.',
|
|
227
|
+
});
|
|
226
228
|
}
|
|
227
229
|
|
|
228
230
|
// Check if this member is already registered in this group?
|
|
229
|
-
const existingRegistrations = await Registration.where({ memberId: member.id, groupId: item.groupId, cycle: group.cycle })
|
|
231
|
+
const existingRegistrations = await Registration.where({ memberId: member.id, groupId: item.groupId, cycle: group.cycle });
|
|
230
232
|
|
|
231
233
|
for (const existingRegistration of existingRegistrations) {
|
|
232
234
|
if (item.replaceRegistrations.some(r => r.id === existingRegistration.id)) {
|
|
@@ -241,157 +243,159 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
|
|
|
241
243
|
|
|
242
244
|
if (existingRegistration.registeredAt !== null && existingRegistration.deactivatedAt === null) {
|
|
243
245
|
throw new SimpleError({
|
|
244
|
-
code:
|
|
245
|
-
message: `${member.firstName} is al ingeschreven voor ${group.settings.name}. Mogelijks heb je meerdere keren proberen in te schrijven en is het intussen wel gelukt. Herlaad de pagina best even om zeker te zijn
|
|
246
|
-
})
|
|
246
|
+
code: 'already_registered',
|
|
247
|
+
message: `${member.firstName} is al ingeschreven voor ${group.settings.name}. Mogelijks heb je meerdere keren proberen in te schrijven en is het intussen wel gelukt. Herlaad de pagina best even om zeker te zijn.`,
|
|
248
|
+
});
|
|
247
249
|
}
|
|
248
250
|
}
|
|
249
251
|
|
|
250
252
|
const registration = new Registration()
|
|
251
253
|
.setRelation(registrationMemberRelation, member as Member)
|
|
252
|
-
.setRelation(Registration.group, group)
|
|
253
|
-
registration.organizationId = organization.id
|
|
254
|
-
registration.periodId = group.periodId
|
|
254
|
+
.setRelation(Registration.group, group);
|
|
255
|
+
registration.organizationId = organization.id;
|
|
256
|
+
registration.periodId = group.periodId;
|
|
255
257
|
|
|
256
|
-
registration.memberId = member.id
|
|
257
|
-
registration.groupId = group.id
|
|
258
|
-
registration.price = 0 // will get filled by balance items themselves
|
|
258
|
+
registration.memberId = member.id;
|
|
259
|
+
registration.groupId = group.id;
|
|
260
|
+
registration.price = 0; // will get filled by balance items themselves
|
|
259
261
|
registration.groupPrice = item.groupPrice;
|
|
260
|
-
registration.options = item.options
|
|
262
|
+
registration.options = item.options;
|
|
261
263
|
|
|
262
264
|
payRegistrations.push({
|
|
263
265
|
registration,
|
|
264
|
-
item
|
|
266
|
+
item,
|
|
265
267
|
});
|
|
266
268
|
|
|
267
|
-
registrations.push(registration)
|
|
269
|
+
registrations.push(registration);
|
|
268
270
|
}
|
|
269
271
|
|
|
270
272
|
// Who is going to pay?
|
|
271
|
-
let whoWillPayNow: 'member'|'organization'|'nobody' = 'member' // if this is set to 'organization', there will also be created separate balance items so the member can pay back the paying organization
|
|
273
|
+
let whoWillPayNow: 'member' | 'organization' | 'nobody' = 'member'; // if this is set to 'organization', there will also be created separate balance items so the member can pay back the paying organization
|
|
272
274
|
|
|
273
275
|
if (request.body.asOrganizationId && request.body.asOrganizationId === organization.id) {
|
|
274
276
|
// Will get added to the outstanding amount of the member
|
|
275
|
-
whoWillPayNow = 'nobody'
|
|
276
|
-
}
|
|
277
|
+
whoWillPayNow = 'nobody';
|
|
278
|
+
}
|
|
279
|
+
else if (request.body.asOrganizationId && request.body.asOrganizationId !== organization.id) {
|
|
277
280
|
// The organization will pay to the organizing organization, and it will get added to the outstanding amount of the member towards the paying organization
|
|
278
|
-
whoWillPayNow = 'organization'
|
|
281
|
+
whoWillPayNow = 'organization';
|
|
279
282
|
}
|
|
280
283
|
|
|
281
284
|
// Validate payment method
|
|
282
285
|
if (totalPrice > 0 && whoWillPayNow !== 'nobody') {
|
|
283
|
-
const allowedPaymentMethods = organization.meta.registrationPaymentConfiguration.paymentMethods
|
|
286
|
+
const allowedPaymentMethods = organization.meta.registrationPaymentConfiguration.paymentMethods;
|
|
284
287
|
|
|
285
288
|
if (!checkout.paymentMethod || !allowedPaymentMethods.includes(checkout.paymentMethod)) {
|
|
286
289
|
throw new SimpleError({
|
|
287
|
-
code:
|
|
288
|
-
message:
|
|
289
|
-
})
|
|
290
|
+
code: 'invalid_payment_method',
|
|
291
|
+
message: 'Oeps, je hebt geen geldige betaalmethode geselecteerd. Selecteer een betaalmethode en probeer opnieuw. Herlaad de pagina indien nodig.',
|
|
292
|
+
});
|
|
290
293
|
}
|
|
291
294
|
|
|
292
295
|
if ((checkout.paymentMethod !== PaymentMethod.Transfer && checkout.paymentMethod !== PaymentMethod.PointOfSale) && (!request.body.redirectUrl || !request.body.cancelUrl)) {
|
|
293
296
|
throw new SimpleError({
|
|
294
297
|
code: 'missing_fields',
|
|
295
298
|
message: 'redirectUrl or cancelUrl is missing and is required for non-zero online payments',
|
|
296
|
-
human: 'Er is iets mis. Contacteer de webmaster.'
|
|
297
|
-
})
|
|
299
|
+
human: 'Er is iets mis. Contacteer de webmaster.',
|
|
300
|
+
});
|
|
298
301
|
}
|
|
299
|
-
}
|
|
300
|
-
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
checkout.paymentMethod = PaymentMethod.Unknown;
|
|
301
305
|
}
|
|
302
306
|
|
|
303
|
-
console.log('Registering members using whoWillPayNow', whoWillPayNow, checkout.paymentMethod, totalPrice)
|
|
307
|
+
console.log('Registering members using whoWillPayNow', whoWillPayNow, checkout.paymentMethod, totalPrice);
|
|
308
|
+
|
|
309
|
+
const createdBalanceItems: BalanceItem[] = [];
|
|
310
|
+
const unrelatedCreatedBalanceItems: BalanceItem[] = [];
|
|
311
|
+
const shouldMarkValid = whoWillPayNow === 'nobody' || checkout.paymentMethod === PaymentMethod.Transfer || checkout.paymentMethod === PaymentMethod.PointOfSale || checkout.paymentMethod === PaymentMethod.Unknown;
|
|
304
312
|
|
|
305
|
-
const createdBalanceItems: BalanceItem[] = []
|
|
306
|
-
const unrelatedCreatedBalanceItems: BalanceItem[] = []
|
|
307
|
-
const shouldMarkValid = whoWillPayNow === 'nobody' || checkout.paymentMethod === PaymentMethod.Transfer || checkout.paymentMethod === PaymentMethod.PointOfSale || checkout.paymentMethod === PaymentMethod.Unknown
|
|
308
|
-
|
|
309
313
|
// Create negative balance items
|
|
310
314
|
for (const registrationStruct of [...checkout.cart.deleteRegistrations, ...checkout.cart.items.flatMap(i => i.replaceRegistrations)]) {
|
|
311
315
|
if (whoWillPayNow !== 'nobody') {
|
|
312
|
-
// this also fixes the issue that we cannot delete the registration right away if we would need to wait for a payment
|
|
316
|
+
// this also fixes the issue that we cannot delete the registration right away if we would need to wait for a payment
|
|
313
317
|
throw new SimpleError({
|
|
314
|
-
code:
|
|
315
|
-
message:
|
|
316
|
-
human:
|
|
317
|
-
statusCode: 403
|
|
318
|
-
})
|
|
318
|
+
code: 'forbidden',
|
|
319
|
+
message: 'Permission denied: you are not allowed to delete registrations',
|
|
320
|
+
human: 'Oeps, je hebt geen toestemming om inschrijvingen te verwijderen.',
|
|
321
|
+
statusCode: 403,
|
|
322
|
+
});
|
|
319
323
|
}
|
|
320
324
|
|
|
321
|
-
const existingRegistration = await Registration.getByID(registrationStruct.id)
|
|
325
|
+
const existingRegistration = await Registration.getByID(registrationStruct.id);
|
|
322
326
|
if (!existingRegistration || existingRegistration.organizationId !== organization.id) {
|
|
323
327
|
throw new SimpleError({
|
|
324
|
-
code:
|
|
325
|
-
message:
|
|
326
|
-
})
|
|
328
|
+
code: 'invalid_data',
|
|
329
|
+
message: 'Oeps, één of meerdere inschrijvingen die je probeert te verwijderen lijken niet meer te bestaan. Herlaad de pagina en probeer opnieuw.',
|
|
330
|
+
});
|
|
327
331
|
}
|
|
328
332
|
|
|
329
333
|
if (!await Context.auth.canAccessRegistration(existingRegistration, PermissionLevel.Write)) {
|
|
330
334
|
throw new SimpleError({
|
|
331
|
-
code:
|
|
332
|
-
message:
|
|
333
|
-
statusCode: 403
|
|
334
|
-
})
|
|
335
|
+
code: 'forbidden',
|
|
336
|
+
message: 'Je hebt geen toegaansrechten om deze inschrijving te verwijderen.',
|
|
337
|
+
statusCode: 403,
|
|
338
|
+
});
|
|
335
339
|
}
|
|
336
340
|
|
|
337
341
|
if (existingRegistration.deactivatedAt || !existingRegistration.registeredAt) {
|
|
338
342
|
throw new SimpleError({
|
|
339
|
-
code:
|
|
340
|
-
message:
|
|
341
|
-
})
|
|
343
|
+
code: 'invalid_data',
|
|
344
|
+
message: 'Oeps, één of meerdere inschrijvingen die je probeert te verwijderen was al verwijderd. Herlaad de pagina en probeer opnieuw.',
|
|
345
|
+
});
|
|
342
346
|
}
|
|
343
347
|
|
|
344
348
|
// We can alter right away since whoWillPayNow is nobody, and shouldMarkValid will always be true
|
|
345
349
|
// Find all balance items of this registration and set them to zero
|
|
346
|
-
await BalanceItem.deleteForDeletedRegistration(existingRegistration.id)
|
|
347
|
-
|
|
350
|
+
await BalanceItem.deleteForDeletedRegistration(existingRegistration.id);
|
|
351
|
+
|
|
348
352
|
// Clear the registration
|
|
349
|
-
await existingRegistration.deactivate()
|
|
353
|
+
await existingRegistration.deactivate();
|
|
350
354
|
deactivatedRegistrationGroupIds.push(existingRegistration.groupId);
|
|
351
355
|
|
|
352
|
-
const group = groups.find(g => g.id === existingRegistration.groupId)
|
|
356
|
+
const group = groups.find(g => g.id === existingRegistration.groupId);
|
|
353
357
|
if (!group) {
|
|
354
|
-
const g = await Group.getByID(existingRegistration.groupId)
|
|
358
|
+
const g = await Group.getByID(existingRegistration.groupId);
|
|
355
359
|
if (g) {
|
|
356
|
-
groups.push(g)
|
|
360
|
+
groups.push(g);
|
|
357
361
|
}
|
|
358
362
|
}
|
|
359
363
|
}
|
|
360
364
|
|
|
361
|
-
async function createBalanceItem({registration, amount, unitPrice, description, type, relations}: {amount?: number
|
|
365
|
+
async function createBalanceItem({ registration, amount, unitPrice, description, type, relations }: { amount?: number; registration: RegistrationWithMemberAndGroup; unitPrice: number; description: string; relations: Map<BalanceItemRelationType, BalanceItemRelation>; type: BalanceItemType }) {
|
|
362
366
|
// NOTE: We also need to save zero-price balance items because for online payments, we need to know which registrations to activate after payment
|
|
363
367
|
|
|
364
368
|
// Create balance item
|
|
365
369
|
const balanceItem = new BalanceItem();
|
|
366
370
|
balanceItem.registrationId = registration.id;
|
|
367
|
-
balanceItem.unitPrice = unitPrice
|
|
368
|
-
balanceItem.amount = amount ?? 1
|
|
369
|
-
balanceItem.description = description
|
|
370
|
-
balanceItem.relations = relations
|
|
371
|
-
balanceItem.type = type
|
|
371
|
+
balanceItem.unitPrice = unitPrice;
|
|
372
|
+
balanceItem.amount = amount ?? 1;
|
|
373
|
+
balanceItem.description = description;
|
|
374
|
+
balanceItem.relations = relations;
|
|
375
|
+
balanceItem.type = type;
|
|
372
376
|
|
|
373
377
|
// Who needs to receive this money?
|
|
374
378
|
balanceItem.organizationId = organization.id;
|
|
375
379
|
|
|
376
380
|
// Who is responsible for payment?
|
|
377
|
-
let balanceItem2: BalanceItem | null = null
|
|
381
|
+
let balanceItem2: BalanceItem | null = null;
|
|
378
382
|
if (whoWillPayNow === 'organization' && request.body.asOrganizationId) {
|
|
379
383
|
// Create a separate balance item for this meber to pay back the paying organization
|
|
380
384
|
// this is not yet associated with a payment but will be added to the outstanding balance of the member
|
|
381
385
|
|
|
382
|
-
balanceItem.payingOrganizationId = request.body.asOrganizationId
|
|
386
|
+
balanceItem.payingOrganizationId = request.body.asOrganizationId;
|
|
383
387
|
|
|
384
388
|
balanceItem2 = new BalanceItem();
|
|
385
389
|
|
|
386
390
|
// NOTE: we don't connect the registrationId here
|
|
387
391
|
// because otherwise the total price and pricePaid for the registration would be incorrect
|
|
388
|
-
//balanceItem2.registrationId = registration.id;
|
|
392
|
+
// balanceItem2.registrationId = registration.id;
|
|
389
393
|
|
|
390
|
-
balanceItem2.unitPrice = unitPrice
|
|
391
|
-
balanceItem2.amount = amount ?? 1
|
|
392
|
-
balanceItem2.description = description
|
|
393
|
-
balanceItem2.relations = relations
|
|
394
|
-
balanceItem2.type = type
|
|
394
|
+
balanceItem2.unitPrice = unitPrice;
|
|
395
|
+
balanceItem2.amount = amount ?? 1;
|
|
396
|
+
balanceItem2.description = description;
|
|
397
|
+
balanceItem2.relations = relations;
|
|
398
|
+
balanceItem2.type = type;
|
|
395
399
|
|
|
396
400
|
// Who needs to receive this money?
|
|
397
401
|
balanceItem2.organizationId = request.body.asOrganizationId;
|
|
@@ -400,46 +404,48 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
|
|
|
400
404
|
balanceItem2.memberId = registration.memberId;
|
|
401
405
|
|
|
402
406
|
// If the paying organization hasn't paid yet, this should be hidden and move to pending as soon as the paying organization has paid
|
|
403
|
-
balanceItem2.status = shouldMarkValid ? BalanceItemStatus.Pending : BalanceItemStatus.Hidden
|
|
407
|
+
balanceItem2.status = shouldMarkValid ? BalanceItemStatus.Pending : BalanceItemStatus.Hidden;
|
|
404
408
|
await balanceItem2.save();
|
|
405
409
|
|
|
406
410
|
// do not add to createdBalanceItems array because we don't want to add this to the payment if we create a payment
|
|
407
|
-
unrelatedCreatedBalanceItems.push(balanceItem2)
|
|
408
|
-
}
|
|
411
|
+
unrelatedCreatedBalanceItems.push(balanceItem2);
|
|
412
|
+
}
|
|
413
|
+
else {
|
|
409
414
|
balanceItem.memberId = registration.memberId;
|
|
410
|
-
balanceItem.userId = user.id
|
|
415
|
+
balanceItem.userId = user.id;
|
|
411
416
|
}
|
|
412
417
|
|
|
413
|
-
balanceItem.status = shouldMarkValid ? BalanceItemStatus.Pending : BalanceItemStatus.Hidden
|
|
414
|
-
balanceItem.pricePaid = 0
|
|
415
|
-
|
|
418
|
+
balanceItem.status = shouldMarkValid ? BalanceItemStatus.Pending : BalanceItemStatus.Hidden;
|
|
419
|
+
balanceItem.pricePaid = 0;
|
|
420
|
+
|
|
416
421
|
// Connect the 'pay back' balance item to this balance item. As soon as this balance item is paid, we'll mark the other one as pending so the outstanding balance for the member increases
|
|
417
|
-
balanceItem.dependingBalanceItemId = balanceItem2?.id ?? null
|
|
418
|
-
|
|
422
|
+
balanceItem.dependingBalanceItemId = balanceItem2?.id ?? null;
|
|
423
|
+
|
|
419
424
|
await balanceItem.save();
|
|
420
|
-
createdBalanceItems.push(balanceItem)
|
|
425
|
+
createdBalanceItems.push(balanceItem);
|
|
421
426
|
}
|
|
422
427
|
|
|
423
428
|
// Save registrations and add extra data if needed
|
|
424
429
|
for (const bundle of payRegistrations) {
|
|
425
|
-
const {item, registration} = bundle;
|
|
426
|
-
registration.reservedUntil = null
|
|
430
|
+
const { item, registration } = bundle;
|
|
431
|
+
registration.reservedUntil = null;
|
|
427
432
|
|
|
428
433
|
if (shouldMarkValid) {
|
|
429
|
-
await registration.markValid({skipEmail: bundle.item.replaceRegistrations.length > 0})
|
|
430
|
-
}
|
|
434
|
+
await registration.markValid({ skipEmail: bundle.item.replaceRegistrations.length > 0 });
|
|
435
|
+
}
|
|
436
|
+
else {
|
|
431
437
|
// Reserve registration for 30 minutes (if needed)
|
|
432
|
-
const group = groups.find(g => g.id === registration.groupId)
|
|
438
|
+
const group = groups.find(g => g.id === registration.groupId);
|
|
433
439
|
|
|
434
440
|
if (group && group.settings.maxMembers !== null) {
|
|
435
|
-
registration.reservedUntil = new Date(new Date().getTime() + 1000*60*30)
|
|
441
|
+
registration.reservedUntil = new Date(new Date().getTime() + 1000 * 60 * 30);
|
|
436
442
|
}
|
|
437
|
-
await registration.save()
|
|
443
|
+
await registration.save();
|
|
438
444
|
}
|
|
439
445
|
|
|
440
446
|
// Note: we should always create the balance items: even when the price is zero
|
|
441
447
|
// Otherwise we don't know which registrations to activate after payment
|
|
442
|
-
|
|
448
|
+
|
|
443
449
|
if (shouldMarkValid && item.calculatedPrice === 0) {
|
|
444
450
|
continue;
|
|
445
451
|
}
|
|
@@ -447,46 +453,46 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
|
|
|
447
453
|
// Create balance items
|
|
448
454
|
const sharedRelations: [BalanceItemRelationType, BalanceItemRelation][] = [
|
|
449
455
|
[
|
|
450
|
-
BalanceItemRelationType.Member,
|
|
456
|
+
BalanceItemRelationType.Member,
|
|
451
457
|
BalanceItemRelation.create({
|
|
452
458
|
id: item.member.id,
|
|
453
|
-
name: item.member.patchedMember.name
|
|
454
|
-
})
|
|
459
|
+
name: item.member.patchedMember.name,
|
|
460
|
+
}),
|
|
455
461
|
],
|
|
456
462
|
[
|
|
457
|
-
BalanceItemRelationType.Group,
|
|
463
|
+
BalanceItemRelationType.Group,
|
|
458
464
|
BalanceItemRelation.create({
|
|
459
465
|
id: item.group.id,
|
|
460
|
-
name: item.group.settings.name
|
|
461
|
-
})
|
|
462
|
-
]
|
|
463
|
-
]
|
|
466
|
+
name: item.group.settings.name,
|
|
467
|
+
}),
|
|
468
|
+
],
|
|
469
|
+
];
|
|
464
470
|
|
|
465
471
|
if (item.group.settings.prices.length > 1) {
|
|
466
472
|
sharedRelations.push([
|
|
467
|
-
BalanceItemRelationType.GroupPrice,
|
|
473
|
+
BalanceItemRelationType.GroupPrice,
|
|
468
474
|
BalanceItemRelation.create({
|
|
469
475
|
id: item.groupPrice.id,
|
|
470
|
-
name: item.groupPrice.name
|
|
471
|
-
})
|
|
472
|
-
])
|
|
476
|
+
name: item.groupPrice.name,
|
|
477
|
+
}),
|
|
478
|
+
]);
|
|
473
479
|
}
|
|
474
480
|
|
|
475
481
|
// Base price
|
|
476
482
|
await createBalanceItem({
|
|
477
|
-
registration,
|
|
483
|
+
registration,
|
|
478
484
|
unitPrice: item.groupPrice.price.forMember(item.member),
|
|
479
485
|
type: BalanceItemType.Registration,
|
|
480
486
|
description: `${item.member.patchedMember.name} bij ${item.group.settings.name}`,
|
|
481
487
|
relations: new Map([
|
|
482
|
-
...sharedRelations
|
|
483
|
-
])
|
|
484
|
-
})
|
|
488
|
+
...sharedRelations,
|
|
489
|
+
]),
|
|
490
|
+
});
|
|
485
491
|
|
|
486
492
|
// Options
|
|
487
493
|
for (const option of item.options) {
|
|
488
494
|
await createBalanceItem({
|
|
489
|
-
registration,
|
|
495
|
+
registration,
|
|
490
496
|
amount: option.amount,
|
|
491
497
|
unitPrice: option.option.price.forMember(item.member),
|
|
492
498
|
type: BalanceItemType.Registration,
|
|
@@ -494,66 +500,66 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
|
|
|
494
500
|
relations: new Map([
|
|
495
501
|
...sharedRelations,
|
|
496
502
|
[
|
|
497
|
-
BalanceItemRelationType.GroupOptionMenu,
|
|
503
|
+
BalanceItemRelationType.GroupOptionMenu,
|
|
498
504
|
BalanceItemRelation.create({
|
|
499
505
|
id: option.optionMenu.id,
|
|
500
506
|
name: option.optionMenu.name,
|
|
501
|
-
})
|
|
507
|
+
}),
|
|
502
508
|
],
|
|
503
509
|
[
|
|
504
|
-
BalanceItemRelationType.GroupOption,
|
|
510
|
+
BalanceItemRelationType.GroupOption,
|
|
505
511
|
BalanceItemRelation.create({
|
|
506
512
|
id: option.option.id,
|
|
507
513
|
name: option.option.name,
|
|
508
|
-
})
|
|
509
|
-
]
|
|
510
|
-
])
|
|
511
|
-
})
|
|
514
|
+
}),
|
|
515
|
+
],
|
|
516
|
+
]),
|
|
517
|
+
});
|
|
512
518
|
}
|
|
513
|
-
|
|
514
519
|
}
|
|
515
|
-
|
|
516
|
-
const oldestMember = members.slice().sort((a, b) => b.details.defaultAge - a.details.defaultAge)[0]
|
|
520
|
+
|
|
521
|
+
const oldestMember = members.slice().sort((a, b) => b.details.defaultAge - a.details.defaultAge)[0];
|
|
517
522
|
if (checkout.freeContribution && !request.body.asOrganizationId) {
|
|
518
523
|
// Create balance item
|
|
519
524
|
const balanceItem = new BalanceItem();
|
|
520
|
-
balanceItem.type = BalanceItemType.FreeContribution
|
|
521
|
-
balanceItem.unitPrice = checkout.freeContribution
|
|
522
|
-
balanceItem.description = `Vrije bijdrage
|
|
525
|
+
balanceItem.type = BalanceItemType.FreeContribution;
|
|
526
|
+
balanceItem.unitPrice = checkout.freeContribution;
|
|
527
|
+
balanceItem.description = `Vrije bijdrage`;
|
|
523
528
|
balanceItem.pricePaid = 0;
|
|
524
|
-
balanceItem.userId = user.id
|
|
529
|
+
balanceItem.userId = user.id;
|
|
525
530
|
balanceItem.organizationId = organization.id;
|
|
526
531
|
|
|
527
532
|
// Connect this to the oldest member
|
|
528
|
-
|
|
533
|
+
|
|
529
534
|
if (oldestMember) {
|
|
530
535
|
balanceItem.memberId = oldestMember.id;
|
|
531
536
|
}
|
|
532
|
-
balanceItem.status = shouldMarkValid ? BalanceItemStatus.Pending : BalanceItemStatus.Hidden
|
|
537
|
+
balanceItem.status = shouldMarkValid ? BalanceItemStatus.Pending : BalanceItemStatus.Hidden;
|
|
533
538
|
await balanceItem.save();
|
|
534
|
-
createdBalanceItems.push(balanceItem)
|
|
539
|
+
createdBalanceItems.push(balanceItem);
|
|
535
540
|
}
|
|
536
541
|
|
|
537
542
|
if (checkout.administrationFee && whoWillPayNow !== 'nobody') {
|
|
538
543
|
// Create balance item
|
|
539
544
|
const balanceItem = new BalanceItem();
|
|
540
|
-
balanceItem.type = BalanceItemType.AdministrationFee
|
|
541
|
-
balanceItem.unitPrice = checkout.administrationFee
|
|
542
|
-
balanceItem.description = `Administratiekosten
|
|
545
|
+
balanceItem.type = BalanceItemType.AdministrationFee;
|
|
546
|
+
balanceItem.unitPrice = checkout.administrationFee;
|
|
547
|
+
balanceItem.description = `Administratiekosten`;
|
|
543
548
|
balanceItem.pricePaid = 0;
|
|
544
549
|
balanceItem.organizationId = organization.id;
|
|
545
550
|
|
|
546
551
|
if (request.body.asOrganizationId) {
|
|
547
|
-
balanceItem.payingOrganizationId = request.body.asOrganizationId
|
|
548
|
-
}
|
|
549
|
-
|
|
552
|
+
balanceItem.payingOrganizationId = request.body.asOrganizationId;
|
|
553
|
+
}
|
|
554
|
+
else {
|
|
555
|
+
balanceItem.userId = user.id;
|
|
550
556
|
// Connect this to the oldest member
|
|
551
557
|
if (oldestMember) {
|
|
552
558
|
balanceItem.memberId = oldestMember.id;
|
|
553
559
|
}
|
|
554
560
|
}
|
|
555
561
|
|
|
556
|
-
balanceItem.status = shouldMarkValid ? BalanceItemStatus.Pending : BalanceItemStatus.Hidden
|
|
562
|
+
balanceItem.status = shouldMarkValid ? BalanceItemStatus.Pending : BalanceItemStatus.Hidden;
|
|
557
563
|
await balanceItem.save();
|
|
558
564
|
|
|
559
565
|
createdBalanceItems.push(balanceItem);
|
|
@@ -563,105 +569,107 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
|
|
|
563
569
|
throw new SimpleError({
|
|
564
570
|
code: 'invalid_data',
|
|
565
571
|
message: 'Not possible to pay balance items as the organization',
|
|
566
|
-
statusCode: 400
|
|
567
|
-
})
|
|
572
|
+
statusCode: 400,
|
|
573
|
+
});
|
|
568
574
|
}
|
|
569
575
|
|
|
570
|
-
let paymentUrl: string | null = null
|
|
571
|
-
let payment: Payment | null = null
|
|
576
|
+
let paymentUrl: string | null = null;
|
|
577
|
+
let payment: Payment | null = null;
|
|
572
578
|
|
|
573
579
|
if (whoWillPayNow !== 'nobody') {
|
|
574
|
-
const mappedBalanceItems = new Map<BalanceItem, number>()
|
|
580
|
+
const mappedBalanceItems = new Map<BalanceItem, number>();
|
|
575
581
|
|
|
576
582
|
for (const item of createdBalanceItems) {
|
|
577
|
-
mappedBalanceItems.set(item, item.price)
|
|
583
|
+
mappedBalanceItems.set(item, item.price);
|
|
578
584
|
}
|
|
579
585
|
|
|
580
586
|
for (const item of checkout.cart.balanceItems) {
|
|
581
|
-
const balanceItem = balanceItemsModels.find(i => i.id === item.item.id)
|
|
587
|
+
const balanceItem = balanceItemsModels.find(i => i.id === item.item.id);
|
|
582
588
|
if (!balanceItem) {
|
|
583
|
-
throw new Error('Balance item not found')
|
|
589
|
+
throw new Error('Balance item not found');
|
|
584
590
|
}
|
|
585
|
-
mappedBalanceItems.set(balanceItem, item.price)
|
|
586
|
-
createdBalanceItems.push(balanceItem)
|
|
591
|
+
mappedBalanceItems.set(balanceItem, item.price);
|
|
592
|
+
createdBalanceItems.push(balanceItem);
|
|
587
593
|
}
|
|
588
594
|
|
|
589
595
|
// Make sure every price is accurate before creating a payment
|
|
590
|
-
await BalanceItem.updateOutstanding([...createdBalanceItems, ...unrelatedCreatedBalanceItems])
|
|
596
|
+
await BalanceItem.updateOutstanding([...createdBalanceItems, ...unrelatedCreatedBalanceItems]);
|
|
591
597
|
try {
|
|
592
598
|
const response = await this.createPayment({
|
|
593
599
|
balanceItems: mappedBalanceItems,
|
|
594
600
|
organization,
|
|
595
601
|
user,
|
|
596
602
|
checkout: request.body,
|
|
597
|
-
members
|
|
598
|
-
})
|
|
603
|
+
members,
|
|
604
|
+
});
|
|
599
605
|
|
|
600
606
|
if (response) {
|
|
601
|
-
paymentUrl = response.paymentUrl
|
|
602
|
-
payment = response.payment
|
|
607
|
+
paymentUrl = response.paymentUrl;
|
|
608
|
+
payment = response.payment;
|
|
603
609
|
}
|
|
604
|
-
}
|
|
610
|
+
}
|
|
611
|
+
finally {
|
|
605
612
|
// Update cached balance items pending amount (only created balance items, because those are involved in the payment)
|
|
606
|
-
await BalanceItem.updateOutstanding(createdBalanceItems)
|
|
613
|
+
await BalanceItem.updateOutstanding(createdBalanceItems);
|
|
607
614
|
}
|
|
608
|
-
}
|
|
609
|
-
|
|
615
|
+
}
|
|
616
|
+
else {
|
|
617
|
+
await BalanceItem.updateOutstanding([...createdBalanceItems, ...unrelatedCreatedBalanceItems]);
|
|
610
618
|
}
|
|
611
619
|
|
|
612
620
|
// Update occupancy
|
|
613
621
|
for (const group of groups) {
|
|
614
622
|
if (registrations.some(r => r.groupId === group.id) || deactivatedRegistrationGroupIds.some(id => id === group.id)) {
|
|
615
|
-
await group.updateOccupancy()
|
|
616
|
-
await group.save()
|
|
623
|
+
await group.updateOccupancy();
|
|
624
|
+
await group.save();
|
|
617
625
|
}
|
|
618
626
|
}
|
|
619
627
|
|
|
620
|
-
const updatedMembers = await Member.getBlobByIds(...memberIds)
|
|
628
|
+
const updatedMembers = await Member.getBlobByIds(...memberIds);
|
|
621
629
|
|
|
622
630
|
return new Response(RegisterResponse.create({
|
|
623
631
|
payment: payment ? PaymentStruct.create(payment) : null,
|
|
624
632
|
members: await AuthenticatedStructures.membersBlob(updatedMembers),
|
|
625
633
|
registrations: registrations.map(r => Member.getRegistrationWithMemberStructure(r)),
|
|
626
|
-
paymentUrl
|
|
634
|
+
paymentUrl,
|
|
627
635
|
}));
|
|
628
636
|
}
|
|
629
637
|
|
|
630
|
-
async createPayment({balanceItems, organization, user, checkout, members}: {balanceItems: Map<BalanceItem, number
|
|
638
|
+
async createPayment({ balanceItems, organization, user, checkout, members }: { balanceItems: Map<BalanceItem, number>; organization: Organization; user: User; checkout: IDRegisterCheckout; members: MemberWithRegistrations[] }) {
|
|
631
639
|
// Calculate total price to pay
|
|
632
|
-
let totalPrice = 0
|
|
633
|
-
const payMembers: MemberWithRegistrations[] = []
|
|
640
|
+
let totalPrice = 0;
|
|
641
|
+
const payMembers: MemberWithRegistrations[] = [];
|
|
634
642
|
|
|
635
643
|
for (const [balanceItem, price] of balanceItems) {
|
|
636
644
|
if (organization.id !== balanceItem.organizationId) {
|
|
637
|
-
throw new Error('Unexpected balance item from other organization')
|
|
645
|
+
throw new Error('Unexpected balance item from other organization');
|
|
638
646
|
}
|
|
639
647
|
|
|
640
648
|
if (price > 0 && price > Math.max(balanceItem.priceOpen, balanceItem.price - balanceItem.pricePaid)) {
|
|
641
649
|
throw new SimpleError({
|
|
642
|
-
code:
|
|
643
|
-
message:
|
|
644
|
-
})
|
|
650
|
+
code: 'invalid_data',
|
|
651
|
+
message: 'Oeps, het bedrag dat je probeert te betalen is ongeldig (het bedrag is hoger dan het bedrag dat je moet betalen). Herlaad de pagina en probeer opnieuw.',
|
|
652
|
+
});
|
|
645
653
|
}
|
|
646
654
|
|
|
647
655
|
if (price < 0 && price < Math.min(balanceItem.priceOpen, balanceItem.price - balanceItem.pricePaid)) {
|
|
648
656
|
throw new SimpleError({
|
|
649
|
-
code:
|
|
650
|
-
message:
|
|
651
|
-
})
|
|
657
|
+
code: 'invalid_data',
|
|
658
|
+
message: 'Oeps, het bedrag dat je probeert te betalen is ongeldig (het terug te krijgen bedrag is hoger dan het bedrag dat je kan terug krijgen). Herlaad de pagina en probeer opnieuw.',
|
|
659
|
+
});
|
|
652
660
|
}
|
|
653
661
|
|
|
654
|
-
totalPrice += price
|
|
662
|
+
totalPrice += price;
|
|
655
663
|
|
|
656
664
|
if (price > 0 && balanceItem.memberId) {
|
|
657
|
-
const member = members.find(m => m.id === balanceItem.memberId)
|
|
665
|
+
const member = members.find(m => m.id === balanceItem.memberId);
|
|
658
666
|
if (!member) {
|
|
659
667
|
throw new SimpleError({
|
|
660
|
-
code:
|
|
661
|
-
message:
|
|
662
|
-
})
|
|
668
|
+
code: 'invalid_data',
|
|
669
|
+
message: 'Oeps, het lid dat je probeert in te schrijven konden we niet meer terugvinden. Je herlaadt best even de pagina om opnieuw te proberen.',
|
|
670
|
+
});
|
|
663
671
|
}
|
|
664
|
-
payMembers.push(member)
|
|
672
|
+
payMembers.push(member);
|
|
665
673
|
}
|
|
666
674
|
}
|
|
667
675
|
|
|
@@ -680,150 +688,152 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
|
|
|
680
688
|
|
|
681
689
|
if (!checkout.paymentMethod || checkout.paymentMethod === PaymentMethod.Unknown) {
|
|
682
690
|
throw new SimpleError({
|
|
683
|
-
code:
|
|
684
|
-
message:
|
|
685
|
-
})
|
|
691
|
+
code: 'invalid_data',
|
|
692
|
+
message: 'Oeps, je hebt geen betaalmethode geselecteerd. Selecteer een betaalmethode en probeer opnieuw.',
|
|
693
|
+
});
|
|
686
694
|
}
|
|
687
695
|
|
|
688
|
-
const payment = new Payment()
|
|
696
|
+
const payment = new Payment();
|
|
689
697
|
|
|
690
698
|
// Who will receive this money?
|
|
691
|
-
payment.organizationId = organization.id
|
|
699
|
+
payment.organizationId = organization.id;
|
|
692
700
|
|
|
693
701
|
// Who paid
|
|
694
|
-
payment.payingUserId = user.id
|
|
695
|
-
payment.payingOrganizationId = checkout.asOrganizationId ?? null
|
|
702
|
+
payment.payingUserId = user.id;
|
|
703
|
+
payment.payingOrganizationId = checkout.asOrganizationId ?? null;
|
|
696
704
|
|
|
697
705
|
// Fill in customer:
|
|
698
706
|
payment.customer = PaymentCustomer.create({
|
|
699
707
|
firstName: user.firstName,
|
|
700
708
|
lastName: user.lastName,
|
|
701
709
|
email: user.email,
|
|
702
|
-
})
|
|
710
|
+
});
|
|
703
711
|
|
|
704
712
|
if (checkout.asOrganizationId) {
|
|
705
713
|
if (!checkout.customer) {
|
|
706
714
|
throw new SimpleError({
|
|
707
|
-
code:
|
|
708
|
-
message:
|
|
709
|
-
human:
|
|
710
|
-
})
|
|
715
|
+
code: 'missing_fields',
|
|
716
|
+
message: 'customer is required when paying as an organization',
|
|
717
|
+
human: 'Vul je facturatiegegevens in om verder te gaan.',
|
|
718
|
+
});
|
|
711
719
|
}
|
|
712
720
|
|
|
713
721
|
if (!checkout.customer.company) {
|
|
714
722
|
throw new SimpleError({
|
|
715
|
-
code:
|
|
716
|
-
message:
|
|
717
|
-
human:
|
|
718
|
-
})
|
|
723
|
+
code: 'missing_fields',
|
|
724
|
+
message: 'customer.company is required when paying as an organization',
|
|
725
|
+
human: 'Als je een betaling uitvoert in naam van je vereniging, is het noodzakelijk om facturatiegegevens met bedrijfsgegevens in te vullen.',
|
|
726
|
+
});
|
|
719
727
|
}
|
|
720
728
|
|
|
721
729
|
const payingOrganization = await Organization.getByID(checkout.asOrganizationId);
|
|
722
730
|
if (!payingOrganization) {
|
|
723
731
|
throw new SimpleError({
|
|
724
|
-
code:
|
|
725
|
-
message:
|
|
726
|
-
})
|
|
732
|
+
code: 'invalid_data',
|
|
733
|
+
message: 'Oeps, de organisatie waarvoor je probeert te betalen lijkt niet meer te bestaan. Herlaad de pagina en probeer opnieuw.',
|
|
734
|
+
});
|
|
727
735
|
}
|
|
728
736
|
|
|
729
737
|
// Search company id
|
|
730
738
|
// this avoids needing to check the VAT number every time
|
|
731
|
-
const id = checkout.customer.company.id
|
|
732
|
-
const foundCompany = payingOrganization.meta.companies.find(c => c.id === id)
|
|
739
|
+
const id = checkout.customer.company.id;
|
|
740
|
+
const foundCompany = payingOrganization.meta.companies.find(c => c.id === id);
|
|
733
741
|
|
|
734
742
|
if (!foundCompany) {
|
|
735
743
|
throw new SimpleError({
|
|
736
|
-
code:
|
|
737
|
-
message:
|
|
738
|
-
})
|
|
744
|
+
code: 'invalid_data',
|
|
745
|
+
message: 'Oeps, de facturatiegegevens die je probeerde te selecteren lijken niet meer te bestaan. Herlaad de pagina en probeer opnieuw.',
|
|
746
|
+
});
|
|
739
747
|
}
|
|
740
748
|
|
|
741
|
-
payment.customer.company = foundCompany
|
|
749
|
+
payment.customer.company = foundCompany;
|
|
742
750
|
}
|
|
743
751
|
|
|
744
|
-
payment.method = checkout.paymentMethod
|
|
745
|
-
payment.status = PaymentStatus.Created
|
|
746
|
-
payment.price = totalPrice
|
|
752
|
+
payment.method = checkout.paymentMethod;
|
|
753
|
+
payment.status = PaymentStatus.Created;
|
|
754
|
+
payment.price = totalPrice;
|
|
747
755
|
|
|
748
756
|
if (payment.method == PaymentMethod.Transfer) {
|
|
749
757
|
// remark: we cannot add the lastnames, these will get added in the frontend when it is decrypted
|
|
750
|
-
payment.transferSettings = organization.mappedTransferSettings
|
|
758
|
+
payment.transferSettings = organization.mappedTransferSettings;
|
|
751
759
|
|
|
752
760
|
if (!payment.transferSettings.iban) {
|
|
753
761
|
throw new SimpleError({
|
|
754
|
-
code:
|
|
755
|
-
message:
|
|
756
|
-
human:
|
|
757
|
-
})
|
|
762
|
+
code: 'no_iban',
|
|
763
|
+
message: 'No IBAN',
|
|
764
|
+
human: 'Er is geen rekeningnummer ingesteld voor overschrijvingen. Contacteer de beheerder.',
|
|
765
|
+
});
|
|
758
766
|
}
|
|
759
767
|
|
|
760
|
-
const m = payMembers.map(r => r.details)
|
|
768
|
+
const m = payMembers.map(r => r.details);
|
|
761
769
|
payment.generateDescription(
|
|
762
|
-
organization,
|
|
770
|
+
organization,
|
|
763
771
|
Formatter.groupNamesByFamily(m),
|
|
764
772
|
{
|
|
765
773
|
name: Formatter.groupNamesByFamily(m),
|
|
766
|
-
naam:
|
|
767
|
-
email: user.email
|
|
768
|
-
}
|
|
769
|
-
)
|
|
774
|
+
naam: Formatter.groupNamesByFamily(m),
|
|
775
|
+
email: user.email,
|
|
776
|
+
},
|
|
777
|
+
);
|
|
770
778
|
}
|
|
771
|
-
payment.paidAt = null
|
|
779
|
+
payment.paidAt = null;
|
|
772
780
|
|
|
773
781
|
// Determine the payment provider
|
|
774
782
|
// Throws if invalid
|
|
775
|
-
const {provider, stripeAccount} = await organization.getPaymentProviderFor(payment.method, organization.privateMeta.registrationPaymentConfiguration)
|
|
776
|
-
payment.provider = provider
|
|
777
|
-
payment.stripeAccountId = stripeAccount?.id ?? null
|
|
783
|
+
const { provider, stripeAccount } = await organization.getPaymentProviderFor(payment.method, organization.privateMeta.registrationPaymentConfiguration);
|
|
784
|
+
payment.provider = provider;
|
|
785
|
+
payment.stripeAccountId = stripeAccount?.id ?? null;
|
|
778
786
|
|
|
779
|
-
await payment.save()
|
|
787
|
+
await payment.save();
|
|
780
788
|
|
|
781
789
|
// Create balance item payments
|
|
782
|
-
const balanceItemPayments: (BalanceItemPayment & { balanceItem: BalanceItem })[] = []
|
|
790
|
+
const balanceItemPayments: (BalanceItemPayment & { balanceItem: BalanceItem })[] = [];
|
|
783
791
|
|
|
784
792
|
for (const [balanceItem, price] of balanceItems) {
|
|
785
793
|
// Create one balance item payment to pay it in one payment
|
|
786
|
-
const balanceItemPayment = new BalanceItemPayment()
|
|
794
|
+
const balanceItemPayment = new BalanceItemPayment();
|
|
787
795
|
balanceItemPayment.balanceItemId = balanceItem.id;
|
|
788
796
|
balanceItemPayment.paymentId = payment.id;
|
|
789
797
|
balanceItemPayment.organizationId = organization.id;
|
|
790
798
|
balanceItemPayment.price = price;
|
|
791
799
|
await balanceItemPayment.save();
|
|
792
800
|
|
|
793
|
-
balanceItemPayments.push(balanceItemPayment.setRelation(BalanceItemPayment.balanceItem, balanceItem))
|
|
801
|
+
balanceItemPayments.push(balanceItemPayment.setRelation(BalanceItemPayment.balanceItem, balanceItem));
|
|
794
802
|
}
|
|
795
803
|
|
|
796
|
-
const description = 'Inschrijving '+organization.name
|
|
804
|
+
const description = 'Inschrijving ' + organization.name;
|
|
797
805
|
|
|
798
|
-
let paymentUrl: string | null = null
|
|
806
|
+
let paymentUrl: string | null = null;
|
|
799
807
|
|
|
800
808
|
// Update balance items
|
|
801
809
|
if (payment.method === PaymentMethod.Transfer) {
|
|
802
810
|
// Send a small reminder email
|
|
803
811
|
try {
|
|
804
|
-
await Registration.sendTransferEmail(user, organization, payment)
|
|
805
|
-
}
|
|
806
|
-
|
|
807
|
-
console.error(
|
|
812
|
+
await Registration.sendTransferEmail(user, organization, payment);
|
|
813
|
+
}
|
|
814
|
+
catch (e) {
|
|
815
|
+
console.error('Failed to send transfer email');
|
|
816
|
+
console.error(e);
|
|
808
817
|
}
|
|
809
|
-
}
|
|
818
|
+
}
|
|
819
|
+
else if (payment.method !== PaymentMethod.PointOfSale) {
|
|
810
820
|
if (!checkout.redirectUrl || !checkout.cancelUrl) {
|
|
811
|
-
throw new Error('Should have been caught earlier')
|
|
821
|
+
throw new Error('Should have been caught earlier');
|
|
812
822
|
}
|
|
813
823
|
|
|
814
|
-
const _redirectUrl = new URL(checkout.redirectUrl)
|
|
824
|
+
const _redirectUrl = new URL(checkout.redirectUrl);
|
|
815
825
|
_redirectUrl.searchParams.set('paymentId', payment.id);
|
|
816
826
|
_redirectUrl.searchParams.set('organizationId', organization.id); // makes sure the client uses the token associated with this organization when fetching payment polling status
|
|
817
827
|
|
|
818
|
-
const _cancelUrl = new URL(checkout.cancelUrl)
|
|
828
|
+
const _cancelUrl = new URL(checkout.cancelUrl);
|
|
819
829
|
_cancelUrl.searchParams.set('paymentId', payment.id);
|
|
820
830
|
_cancelUrl.searchParams.set('cancel', 'true');
|
|
821
831
|
_cancelUrl.searchParams.set('organizationId', organization.id); // makes sure the client uses the token associated with this organization when fetching payment polling status
|
|
822
832
|
|
|
823
|
-
const redirectUrl = _redirectUrl.href
|
|
824
|
-
const cancelUrl = _cancelUrl.href
|
|
833
|
+
const redirectUrl = _redirectUrl.href;
|
|
834
|
+
const cancelUrl = _cancelUrl.href;
|
|
825
835
|
|
|
826
|
-
const webhookUrl = 'https://'+organization.getApiHost()+
|
|
836
|
+
const webhookUrl = 'https://' + organization.getApiHost() + '/v' + Version + '/payments/' + encodeURIComponent(payment.id) + '?exchange=true';
|
|
827
837
|
|
|
828
838
|
if (payment.provider === PaymentProvider.Stripe) {
|
|
829
839
|
const stripeResult = await StripeHelper.createPayment({
|
|
@@ -835,7 +845,7 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
|
|
|
835
845
|
metadata: {
|
|
836
846
|
organization: organization.id,
|
|
837
847
|
user: user.id,
|
|
838
|
-
payment: payment.id
|
|
848
|
+
payment: payment.id,
|
|
839
849
|
},
|
|
840
850
|
i18n: Context.i18n,
|
|
841
851
|
lineItems: balanceItemPayments,
|
|
@@ -843,34 +853,34 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
|
|
|
843
853
|
customer: {
|
|
844
854
|
name: user.name ?? payMembers[0]?.details.name ?? 'Onbekend',
|
|
845
855
|
email: user.email,
|
|
846
|
-
}
|
|
856
|
+
},
|
|
847
857
|
});
|
|
848
|
-
paymentUrl = stripeResult.paymentUrl
|
|
849
|
-
}
|
|
850
|
-
|
|
858
|
+
paymentUrl = stripeResult.paymentUrl;
|
|
859
|
+
}
|
|
860
|
+
else if (payment.provider === PaymentProvider.Mollie) {
|
|
851
861
|
// Mollie payment
|
|
852
|
-
const token = await MollieToken.getTokenFor(organization.id)
|
|
862
|
+
const token = await MollieToken.getTokenFor(organization.id);
|
|
853
863
|
if (!token) {
|
|
854
864
|
throw new SimpleError({
|
|
855
|
-
code:
|
|
856
|
-
message:
|
|
857
|
-
})
|
|
865
|
+
code: '',
|
|
866
|
+
message: 'Betaling via ' + PaymentMethodHelper.getName(payment.method) + ' is onbeschikbaar',
|
|
867
|
+
});
|
|
858
868
|
}
|
|
859
|
-
const profileId = organization.privateMeta.mollieProfile?.id ?? await token.getProfileId(organization.getHost())
|
|
869
|
+
const profileId = organization.privateMeta.mollieProfile?.id ?? await token.getProfileId(organization.getHost());
|
|
860
870
|
if (!profileId) {
|
|
861
871
|
throw new SimpleError({
|
|
862
|
-
code:
|
|
863
|
-
message:
|
|
864
|
-
})
|
|
872
|
+
code: '',
|
|
873
|
+
message: 'Betaling via ' + PaymentMethodHelper.getName(payment.method) + ' is tijdelijk onbeschikbaar',
|
|
874
|
+
});
|
|
865
875
|
}
|
|
866
876
|
const mollieClient = createMollieClient({ accessToken: await token.getAccessToken() });
|
|
867
877
|
const molliePayment = await mollieClient.payments.create({
|
|
868
878
|
amount: {
|
|
869
879
|
currency: 'EUR',
|
|
870
|
-
value: (totalPrice / 100).toFixed(2)
|
|
880
|
+
value: (totalPrice / 100).toFixed(2),
|
|
871
881
|
},
|
|
872
882
|
method: payment.method == PaymentMethod.Bancontact ? molliePaymentMethod.bancontact : (payment.method == PaymentMethod.iDEAL ? molliePaymentMethod.ideal : molliePaymentMethod.creditcard),
|
|
873
|
-
testmode: organization.privateMeta.useTestPayments ?? STAMHOOFD.environment
|
|
883
|
+
testmode: organization.privateMeta.useTestPayments ?? STAMHOOFD.environment !== 'production',
|
|
874
884
|
profileId,
|
|
875
885
|
description,
|
|
876
886
|
redirectUrl,
|
|
@@ -879,29 +889,31 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
|
|
|
879
889
|
paymentId: payment.id,
|
|
880
890
|
},
|
|
881
891
|
});
|
|
882
|
-
paymentUrl = molliePayment.getCheckoutUrl()
|
|
892
|
+
paymentUrl = molliePayment.getCheckoutUrl();
|
|
883
893
|
|
|
884
894
|
// Save payment
|
|
885
|
-
const dbPayment = new MolliePayment()
|
|
886
|
-
dbPayment.paymentId = payment.id
|
|
887
|
-
dbPayment.mollieId = molliePayment.id
|
|
895
|
+
const dbPayment = new MolliePayment();
|
|
896
|
+
dbPayment.paymentId = payment.id;
|
|
897
|
+
dbPayment.mollieId = molliePayment.id;
|
|
888
898
|
await dbPayment.save();
|
|
889
|
-
}
|
|
890
|
-
|
|
891
|
-
|
|
899
|
+
}
|
|
900
|
+
else if (payment.provider === PaymentProvider.Payconiq) {
|
|
901
|
+
paymentUrl = await PayconiqPayment.createPayment(payment, organization, description, redirectUrl, webhookUrl);
|
|
902
|
+
}
|
|
903
|
+
else if (payment.provider == PaymentProvider.Buckaroo) {
|
|
892
904
|
// Increase request timeout because buckaroo is super slow (in development)
|
|
893
|
-
Context.request.request?.setTimeout(60 * 1000)
|
|
894
|
-
const buckaroo = new BuckarooHelper(organization.privateMeta?.buckarooSettings?.key ??
|
|
895
|
-
const ip = Context.request.getIP()
|
|
896
|
-
paymentUrl = await buckaroo.createPayment(payment, ip, description, redirectUrl, webhookUrl)
|
|
897
|
-
await payment.save()
|
|
905
|
+
Context.request.request?.setTimeout(60 * 1000);
|
|
906
|
+
const buckaroo = new BuckarooHelper(organization.privateMeta?.buckarooSettings?.key ?? '', organization.privateMeta?.buckarooSettings?.secret ?? '', organization.privateMeta.useTestPayments ?? STAMHOOFD.environment !== 'production');
|
|
907
|
+
const ip = Context.request.getIP();
|
|
908
|
+
paymentUrl = await buckaroo.createPayment(payment, ip, description, redirectUrl, webhookUrl);
|
|
909
|
+
await payment.save();
|
|
898
910
|
|
|
899
911
|
// TypeScript doesn't understand that the status can change and isn't a const....
|
|
900
912
|
if ((payment.status as any) === PaymentStatus.Failed) {
|
|
901
913
|
throw new SimpleError({
|
|
902
|
-
code:
|
|
903
|
-
message:
|
|
904
|
-
})
|
|
914
|
+
code: 'payment_failed',
|
|
915
|
+
message: 'Betaling via ' + PaymentMethodHelper.getName(payment.method) + ' is onbeschikbaar',
|
|
916
|
+
});
|
|
905
917
|
}
|
|
906
918
|
}
|
|
907
919
|
}
|
|
@@ -911,7 +923,7 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
|
|
|
911
923
|
balanceItemPayments,
|
|
912
924
|
provider,
|
|
913
925
|
stripeAccount,
|
|
914
|
-
paymentUrl
|
|
915
|
-
}
|
|
926
|
+
paymentUrl,
|
|
927
|
+
};
|
|
916
928
|
}
|
|
917
929
|
}
|