@stamhoofd/backend 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.template.json +63 -0
- package/.eslintrc.js +61 -0
- package/README.md +40 -0
- package/index.ts +172 -0
- package/jest.config.js +11 -0
- package/migrations.ts +33 -0
- package/package.json +48 -0
- package/src/crons.ts +845 -0
- package/src/endpoints/admin/organizations/GetOrganizationsCountEndpoint.ts +42 -0
- package/src/endpoints/admin/organizations/GetOrganizationsEndpoint.ts +320 -0
- package/src/endpoints/admin/organizations/PatchOrganizationsEndpoint.ts +171 -0
- package/src/endpoints/auth/CreateAdminEndpoint.ts +137 -0
- package/src/endpoints/auth/CreateTokenEndpoint.test.ts +68 -0
- package/src/endpoints/auth/CreateTokenEndpoint.ts +200 -0
- package/src/endpoints/auth/DeleteTokenEndpoint.ts +31 -0
- package/src/endpoints/auth/ForgotPasswordEndpoint.ts +70 -0
- package/src/endpoints/auth/GetUserEndpoint.test.ts +64 -0
- package/src/endpoints/auth/GetUserEndpoint.ts +57 -0
- package/src/endpoints/auth/PatchApiUserEndpoint.ts +90 -0
- package/src/endpoints/auth/PatchUserEndpoint.ts +122 -0
- package/src/endpoints/auth/PollEmailVerificationEndpoint.ts +37 -0
- package/src/endpoints/auth/RetryEmailVerificationEndpoint.ts +41 -0
- package/src/endpoints/auth/SignupEndpoint.ts +107 -0
- package/src/endpoints/auth/VerifyEmailEndpoint.ts +89 -0
- package/src/endpoints/global/addresses/SearchRegionsEndpoint.ts +95 -0
- package/src/endpoints/global/addresses/ValidateAddressEndpoint.ts +31 -0
- package/src/endpoints/global/caddy/CheckDomainCertEndpoint.ts +101 -0
- package/src/endpoints/global/email/GetEmailAddressEndpoint.ts +53 -0
- package/src/endpoints/global/email/ManageEmailAddressEndpoint.ts +57 -0
- package/src/endpoints/global/files/UploadFile.ts +147 -0
- package/src/endpoints/global/files/UploadImage.ts +119 -0
- package/src/endpoints/global/members/GetMemberFamilyEndpoint.ts +76 -0
- package/src/endpoints/global/members/GetMembersCountEndpoint.ts +43 -0
- package/src/endpoints/global/members/GetMembersEndpoint.ts +429 -0
- package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.ts +734 -0
- package/src/endpoints/global/organizations/CheckRegisterCodeEndpoint.ts +45 -0
- package/src/endpoints/global/organizations/CreateOrganizationEndpoint.test.ts +105 -0
- package/src/endpoints/global/organizations/CreateOrganizationEndpoint.ts +146 -0
- package/src/endpoints/global/organizations/GetOrganizationFromDomainEndpoint.test.ts +52 -0
- package/src/endpoints/global/organizations/GetOrganizationFromDomainEndpoint.ts +80 -0
- package/src/endpoints/global/organizations/GetOrganizationFromUriEndpoint.ts +49 -0
- package/src/endpoints/global/organizations/SearchOrganizationEndpoint.test.ts +58 -0
- package/src/endpoints/global/organizations/SearchOrganizationEndpoint.ts +62 -0
- package/src/endpoints/global/payments/ExchangeSTPaymentEndpoint.ts +153 -0
- package/src/endpoints/global/payments/StripeWebhookEndpoint.ts +134 -0
- package/src/endpoints/global/platform/GetPlatformAdminsEndpoint.ts +44 -0
- package/src/endpoints/global/platform/GetPlatformEnpoint.ts +39 -0
- package/src/endpoints/global/platform/PatchPlatformEnpoint.ts +63 -0
- package/src/endpoints/global/registration/GetPaymentRegistrations.ts +68 -0
- package/src/endpoints/global/registration/GetUserBalanceEndpoint.ts +39 -0
- package/src/endpoints/global/registration/GetUserDocumentsEndpoint.ts +80 -0
- package/src/endpoints/global/registration/GetUserMembersEndpoint.ts +41 -0
- package/src/endpoints/global/registration/PatchUserMembersEndpoint.ts +134 -0
- package/src/endpoints/global/registration/RegisterMembersEndpoint.ts +521 -0
- package/src/endpoints/global/registration-periods/GetRegistrationPeriodsEndpoint.ts +37 -0
- package/src/endpoints/global/registration-periods/PatchRegistrationPeriodsEndpoint.ts +115 -0
- package/src/endpoints/global/webshops/GetWebshopFromDomainEndpoint.ts +187 -0
- package/src/endpoints/organization/dashboard/billing/ActivatePackagesEndpoint.ts +424 -0
- package/src/endpoints/organization/dashboard/billing/DeactivatePackageEndpoint.ts +67 -0
- package/src/endpoints/organization/dashboard/billing/GetBillingStatusEndpoint.ts +39 -0
- package/src/endpoints/organization/dashboard/documents/GetDocumentTemplateXML.ts +57 -0
- package/src/endpoints/organization/dashboard/documents/GetDocumentTemplatesEndpoint.ts +50 -0
- package/src/endpoints/organization/dashboard/documents/GetDocumentsEndpoint.ts +50 -0
- package/src/endpoints/organization/dashboard/documents/PatchDocumentEndpoint.ts +129 -0
- package/src/endpoints/organization/dashboard/documents/PatchDocumentTemplateEndpoint.ts +114 -0
- package/src/endpoints/organization/dashboard/email/CheckEmailBouncesEndpoint.ts +50 -0
- package/src/endpoints/organization/dashboard/email/EmailEndpoint.ts +234 -0
- package/src/endpoints/organization/dashboard/email-templates/GetEmailTemplatesEndpoint.ts +62 -0
- package/src/endpoints/organization/dashboard/email-templates/PatchEmailTemplatesEndpoint.ts +85 -0
- package/src/endpoints/organization/dashboard/mollie/CheckMollieEndpoint.ts +80 -0
- package/src/endpoints/organization/dashboard/mollie/ConnectMollieEndpoint.ts +54 -0
- package/src/endpoints/organization/dashboard/mollie/DisconnectMollieEndpoint.ts +49 -0
- package/src/endpoints/organization/dashboard/mollie/GetMollieDashboardEndpoint.ts +63 -0
- package/src/endpoints/organization/dashboard/nolt/CreateNoltTokenEndpoint.ts +61 -0
- package/src/endpoints/organization/dashboard/organization/ApplyRegisterCodeEndpoint.test.ts +64 -0
- package/src/endpoints/organization/dashboard/organization/ApplyRegisterCodeEndpoint.ts +84 -0
- package/src/endpoints/organization/dashboard/organization/GetOrganizationArchivedGroups.ts +43 -0
- package/src/endpoints/organization/dashboard/organization/GetOrganizationDeletedGroups.ts +42 -0
- package/src/endpoints/organization/dashboard/organization/GetOrganizationSSOEndpoint.ts +43 -0
- package/src/endpoints/organization/dashboard/organization/GetRegisterCodeEndpoint.ts +65 -0
- package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.test.ts +281 -0
- package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.ts +338 -0
- package/src/endpoints/organization/dashboard/organization/SetOrganizationDomainEndpoint.ts +196 -0
- package/src/endpoints/organization/dashboard/organization/SetOrganizationSSOEndpoint.ts +50 -0
- package/src/endpoints/organization/dashboard/payments/GetMemberBalanceEndpoint.ts +48 -0
- package/src/endpoints/organization/dashboard/payments/GetPaymentsEndpoint.ts +207 -0
- package/src/endpoints/organization/dashboard/payments/PatchBalanceItemsEndpoint.ts +202 -0
- package/src/endpoints/organization/dashboard/payments/PatchPaymentsEndpoint.ts +233 -0
- package/src/endpoints/organization/dashboard/registration-periods/GetOrganizationRegistrationPeriodsEndpoint.ts +66 -0
- package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.ts +210 -0
- package/src/endpoints/organization/dashboard/stripe/ConnectStripeEndpoint.ts +93 -0
- package/src/endpoints/organization/dashboard/stripe/DeleteStripeAccountEndpoint.ts +59 -0
- package/src/endpoints/organization/dashboard/stripe/GetStripeAccountLinkEndpoint.ts +78 -0
- package/src/endpoints/organization/dashboard/stripe/GetStripeAccountsEndpoint.ts +40 -0
- package/src/endpoints/organization/dashboard/stripe/GetStripeLoginLinkEndpoint.ts +69 -0
- package/src/endpoints/organization/dashboard/stripe/UpdateStripeAccountEndpoint.ts +52 -0
- package/src/endpoints/organization/dashboard/users/CreateApiUserEndpoint.ts +73 -0
- package/src/endpoints/organization/dashboard/users/DeleteUserEndpoint.ts +60 -0
- package/src/endpoints/organization/dashboard/users/GetApiUsersEndpoint.ts +47 -0
- package/src/endpoints/organization/dashboard/users/GetOrganizationAdminsEndpoint.ts +41 -0
- package/src/endpoints/organization/dashboard/webshops/CreateWebshopEndpoint.ts +217 -0
- package/src/endpoints/organization/dashboard/webshops/DeleteWebshopEndpoint.ts +51 -0
- package/src/endpoints/organization/dashboard/webshops/GetDiscountCodesEndpoint.ts +47 -0
- package/src/endpoints/organization/dashboard/webshops/GetWebshopOrdersEndpoint.ts +83 -0
- package/src/endpoints/organization/dashboard/webshops/GetWebshopTicketsEndpoint.ts +68 -0
- package/src/endpoints/organization/dashboard/webshops/GetWebshopUriAvailabilityEndpoint.ts +69 -0
- package/src/endpoints/organization/dashboard/webshops/PatchDiscountCodesEndpoint.ts +125 -0
- package/src/endpoints/organization/dashboard/webshops/PatchWebshopEndpoint.ts +204 -0
- package/src/endpoints/organization/dashboard/webshops/PatchWebshopOrdersEndpoint.ts +278 -0
- package/src/endpoints/organization/dashboard/webshops/PatchWebshopTicketsEndpoint.ts +80 -0
- package/src/endpoints/organization/dashboard/webshops/VerifyWebshopDomainEndpoint.ts +60 -0
- package/src/endpoints/organization/shared/ExchangePaymentEndpoint.ts +379 -0
- package/src/endpoints/organization/shared/GetDocumentHtml.ts +54 -0
- package/src/endpoints/organization/shared/GetPaymentEndpoint.ts +45 -0
- package/src/endpoints/organization/shared/auth/GetOrganizationEndpoint.test.ts +78 -0
- package/src/endpoints/organization/shared/auth/GetOrganizationEndpoint.ts +34 -0
- package/src/endpoints/organization/shared/auth/OpenIDConnectCallbackEndpoint.ts +44 -0
- package/src/endpoints/organization/shared/auth/OpenIDConnectStartEndpoint.ts +82 -0
- package/src/endpoints/organization/webshops/CheckWebshopDiscountCodesEndpoint.ts +59 -0
- package/src/endpoints/organization/webshops/GetOrderByPaymentEndpoint.ts +51 -0
- package/src/endpoints/organization/webshops/GetOrderEndpoint.ts +40 -0
- package/src/endpoints/organization/webshops/GetTicketsEndpoint.ts +124 -0
- package/src/endpoints/organization/webshops/GetWebshopEndpoint.test.ts +130 -0
- package/src/endpoints/organization/webshops/GetWebshopEndpoint.ts +50 -0
- package/src/endpoints/organization/webshops/PlaceOrderEndpoint.test.ts +450 -0
- package/src/endpoints/organization/webshops/PlaceOrderEndpoint.ts +335 -0
- package/src/helpers/AddressValidator.test.ts +40 -0
- package/src/helpers/AddressValidator.ts +256 -0
- package/src/helpers/AdminPermissionChecker.ts +1031 -0
- package/src/helpers/AuthenticatedStructures.ts +158 -0
- package/src/helpers/BuckarooHelper.ts +279 -0
- package/src/helpers/CheckSettlements.ts +215 -0
- package/src/helpers/Context.ts +202 -0
- package/src/helpers/CookieHelper.ts +45 -0
- package/src/helpers/ForwardHandler.test.ts +216 -0
- package/src/helpers/ForwardHandler.ts +140 -0
- package/src/helpers/OpenIDConnectHelper.ts +284 -0
- package/src/helpers/StripeHelper.ts +293 -0
- package/src/helpers/StripePayoutChecker.ts +188 -0
- package/src/middleware/ContextMiddleware.ts +16 -0
- package/src/migrations/1646578856-validate-addresses.ts +60 -0
- package/src/seeds/0000000000-example.ts +13 -0
- package/src/seeds/1715028563-user-permissions.ts +52 -0
- package/tests/e2e/stock.test.ts +2120 -0
- package/tests/e2e/tickets.test.ts +926 -0
- package/tests/helpers/StripeMocker.ts +362 -0
- package/tests/helpers/TestServer.ts +21 -0
- package/tests/jest.global.setup.ts +29 -0
- package/tests/jest.setup.ts +59 -0
- package/tsconfig.json +42 -0
|
@@ -0,0 +1,734 @@
|
|
|
1
|
+
import { OneToManyRelation } from '@simonbackx/simple-database';
|
|
2
|
+
import { ConvertArrayToPatchableArray, Decoder, PatchableArrayAutoEncoder, PatchableArrayDecoder, StringDecoder } from '@simonbackx/simple-encoding';
|
|
3
|
+
import { DecodedRequest, Endpoint, Request, Response } from "@simonbackx/simple-endpoints";
|
|
4
|
+
import { SimpleError } from "@simonbackx/simple-errors";
|
|
5
|
+
import { BalanceItem, BalanceItemPayment, Document, Group, Member, MemberFactory, MemberResponsibilityRecord, MemberWithRegistrations, Organization, Payment, Platform, Registration, User } from '@stamhoofd/models';
|
|
6
|
+
import { BalanceItemStatus, MemberWithRegistrationsBlob, MembersBlob, PaymentMethod, PaymentStatus, PermissionLevel, Registration as RegistrationStruct, User as UserStruct } from "@stamhoofd/structures";
|
|
7
|
+
import { Formatter } from '@stamhoofd/utility';
|
|
8
|
+
|
|
9
|
+
import { AuthenticatedStructures } from '../../../helpers/AuthenticatedStructures';
|
|
10
|
+
import { Context } from '../../../helpers/Context';
|
|
11
|
+
|
|
12
|
+
type Params = Record<string, never>;
|
|
13
|
+
type Query = undefined;
|
|
14
|
+
type Body = PatchableArrayAutoEncoder<MemberWithRegistrationsBlob>
|
|
15
|
+
type ResponseBody = MembersBlob
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* One endpoint to create, patch and delete members and their registrations and payments
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
export class PatchOrganizationMembersEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
22
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
23
|
+
bodyDecoder = new PatchableArrayDecoder(MemberWithRegistrationsBlob as any, MemberWithRegistrationsBlob.patchType(), StringDecoder) as any as Decoder<ConvertArrayToPatchableArray<MemberWithRegistrationsBlob[]>>
|
|
24
|
+
|
|
25
|
+
protected doesMatch(request: Request): [true, Params] | [false] {
|
|
26
|
+
if (request.method != "PATCH") {
|
|
27
|
+
return [false];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const params = Endpoint.parseParameters(request.url, "/organization/members", {});
|
|
31
|
+
|
|
32
|
+
if (params) {
|
|
33
|
+
return [true, params as Params];
|
|
34
|
+
}
|
|
35
|
+
return [false];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async handle(request: DecodedRequest<Params, Query, Body>) {
|
|
39
|
+
const organization = await Context.setOptionalOrganizationScope();
|
|
40
|
+
await Context.authenticate()
|
|
41
|
+
|
|
42
|
+
// Fast throw first (more in depth checking for patches later)
|
|
43
|
+
if (organization) {
|
|
44
|
+
if (!await Context.auth.hasSomeAccess(organization.id)) {
|
|
45
|
+
throw Context.auth.error()
|
|
46
|
+
}
|
|
47
|
+
} else {
|
|
48
|
+
if (!Context.auth.hasSomePlatformAccess()) {
|
|
49
|
+
throw Context.auth.error()
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const members: MemberWithRegistrations[] = []
|
|
54
|
+
|
|
55
|
+
// Cache
|
|
56
|
+
const groups: Group[] = []
|
|
57
|
+
|
|
58
|
+
async function getGroup(id: string) {
|
|
59
|
+
const f = groups.find(g => g.id === id)
|
|
60
|
+
if (f) {
|
|
61
|
+
return f
|
|
62
|
+
}
|
|
63
|
+
const group = await Group.getByID(id)
|
|
64
|
+
if (group) {
|
|
65
|
+
groups.push(group)
|
|
66
|
+
return group
|
|
67
|
+
}
|
|
68
|
+
return null
|
|
69
|
+
}
|
|
70
|
+
const updateGroups = new Map<string, Group>()
|
|
71
|
+
|
|
72
|
+
const balanceItemMemberIds: string[] = []
|
|
73
|
+
const balanceItemRegistrationIdsPerOrganization: Map<string, string[]> = new Map()
|
|
74
|
+
|
|
75
|
+
function addBalanceItemRegistrationId(organizationId: string, registrationId: string) {
|
|
76
|
+
const existing = balanceItemRegistrationIdsPerOrganization.get(organizationId);
|
|
77
|
+
if (existing) {
|
|
78
|
+
existing.push(registrationId)
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
balanceItemRegistrationIdsPerOrganization.set(organizationId, [registrationId])
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Loop all members one by one
|
|
85
|
+
for (const put of request.body.getPuts()) {
|
|
86
|
+
const struct = put.put
|
|
87
|
+
let member = new Member()
|
|
88
|
+
.setManyRelation(Member.registrations as any as OneToManyRelation<"registrations", Member, Registration & {group: Group}>, [])
|
|
89
|
+
.setManyRelation(Member.users, [])
|
|
90
|
+
member.id = struct.id
|
|
91
|
+
|
|
92
|
+
if (organization && STAMHOOFD.userMode !== 'platform') {
|
|
93
|
+
member.organizationId = organization.id
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
struct.details.cleanData()
|
|
97
|
+
member.details = struct.details
|
|
98
|
+
|
|
99
|
+
const duplicate = await PatchOrganizationMembersEndpoint.checkDuplicate(member);
|
|
100
|
+
if (duplicate) {
|
|
101
|
+
// Merge data
|
|
102
|
+
duplicate.details.merge(member.details)
|
|
103
|
+
member = duplicate
|
|
104
|
+
|
|
105
|
+
// Only save after checking permissions
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (struct.registrations.length === 0) {
|
|
109
|
+
throw new SimpleError({
|
|
110
|
+
code: "missing_group",
|
|
111
|
+
message: "Missing group",
|
|
112
|
+
human: "Schrijf een nieuw lid altijd in voor minstens één groep",
|
|
113
|
+
statusCode: 400
|
|
114
|
+
})
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Throw early
|
|
118
|
+
for (const registrationStruct of struct.registrations) {
|
|
119
|
+
const group = await getGroup(registrationStruct.groupId)
|
|
120
|
+
if (!group || group.organizationId !== registrationStruct.organizationId || !await Context.auth.canAccessGroup(group, PermissionLevel.Write)) {
|
|
121
|
+
throw Context.auth.notFoundOrNoAccess("Je hebt niet voldoende rechten om leden toe te voegen in deze groep")
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Set organization id of member based on registrations
|
|
125
|
+
if (!organization && STAMHOOFD.userMode !== 'platform' && !member.organizationId) {
|
|
126
|
+
member.organizationId = group.organizationId
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (STAMHOOFD.userMode !== 'platform' && !member.organizationId) {
|
|
131
|
+
throw new SimpleError({
|
|
132
|
+
code: "missing_organization",
|
|
133
|
+
message: "Missing organization",
|
|
134
|
+
human: "Je moet een organisatie selecteren voor dit lid",
|
|
135
|
+
statusCode: 400
|
|
136
|
+
})
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* In development mode, we allow some secret usernames to create fake data
|
|
141
|
+
*/
|
|
142
|
+
if ((STAMHOOFD.environment == "development" || STAMHOOFD.environment == "staging") && organization) {
|
|
143
|
+
if (member.details.firstName.toLocaleLowerCase() == "create" && parseInt(member.details.lastName) > 0) {
|
|
144
|
+
const count = parseInt(member.details.lastName);
|
|
145
|
+
let group = groups[0];
|
|
146
|
+
|
|
147
|
+
for (const registrationStruct of struct.registrations) {
|
|
148
|
+
const g = await getGroup(registrationStruct.groupId)
|
|
149
|
+
if (g) {
|
|
150
|
+
group = g
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
await this.createDummyMembers(organization, group, count)
|
|
155
|
+
|
|
156
|
+
// Skip creating this member
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
await member.save()
|
|
162
|
+
members.push(member)
|
|
163
|
+
balanceItemMemberIds.push(member.id)
|
|
164
|
+
|
|
165
|
+
// Add registrations
|
|
166
|
+
for (const registrationStruct of struct.registrations) {
|
|
167
|
+
const group = await getGroup(registrationStruct.groupId)
|
|
168
|
+
if (!group || group.organizationId !== registrationStruct.organizationId || !await Context.auth.canAccessGroup(group, PermissionLevel.Write)) {
|
|
169
|
+
throw Context.auth.notFoundOrNoAccess("Je hebt niet voldoende rechten om leden toe te voegen in deze groep")
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const reg = await this.addRegistration(member, registrationStruct, group)
|
|
173
|
+
addBalanceItemRegistrationId(reg.organizationId, reg.id)
|
|
174
|
+
|
|
175
|
+
// Update occupancy at the end of the call
|
|
176
|
+
updateGroups.set(group.id, group)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Add users if they don't exist (only placeholders allowed)
|
|
180
|
+
for (const placeholder of struct.users) {
|
|
181
|
+
await PatchOrganizationMembersEndpoint.linkUser(placeholder, member)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Auto link users based on data
|
|
185
|
+
await PatchOrganizationMembersEndpoint.updateManagers(member)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Loop all members one by one
|
|
189
|
+
for (let patch of request.body.getPatches()) {
|
|
190
|
+
const member = members.find(m => m.id === patch.id) ?? await Member.getWithRegistrations(patch.id)
|
|
191
|
+
if (!member || !await Context.auth.canAccessMember(member, PermissionLevel.Write)) {
|
|
192
|
+
throw Context.auth.notFoundOrNoAccess("Je hebt geen toegang tot dit lid of het bestaat niet")
|
|
193
|
+
}
|
|
194
|
+
patch = await Context.auth.filterMemberPatch(member, patch)
|
|
195
|
+
|
|
196
|
+
if (patch.details) {
|
|
197
|
+
if (patch.details.isPut()) {
|
|
198
|
+
throw new SimpleError({
|
|
199
|
+
code: "not_allowed",
|
|
200
|
+
message: "Cannot override details",
|
|
201
|
+
human: "Er ging iets mis bij het aanpassen van de gegevens van dit lid. Probeer het later opnieuw en neem contact op als het probleem zich blijft voordoen.",
|
|
202
|
+
field: "details"
|
|
203
|
+
})
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
member.details.patchOrPut(patch.details)
|
|
207
|
+
member.details.cleanData()
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
await member.save();
|
|
211
|
+
|
|
212
|
+
// Update documents
|
|
213
|
+
await Document.updateForMember(member.id)
|
|
214
|
+
|
|
215
|
+
// Update registrations
|
|
216
|
+
for (const patchRegistration of patch.registrations.getPatches()) {
|
|
217
|
+
const registration = member.registrations.find(r => r.id === patchRegistration.id)
|
|
218
|
+
if (!registration || registration.memberId != member.id) {
|
|
219
|
+
throw new SimpleError({
|
|
220
|
+
code: "permission_denied",
|
|
221
|
+
message: "You don't have permissions to access this endpoint",
|
|
222
|
+
human: "Je hebt geen toegang om deze registratie te wijzigen"
|
|
223
|
+
})
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
let group: Group | null = null
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
if (patchRegistration.groupId) {
|
|
230
|
+
group = await getGroup(patchRegistration.groupId)
|
|
231
|
+
if (group) {
|
|
232
|
+
// We need to update group occupancy because we moved a member to it
|
|
233
|
+
updateGroups.set(group.id, group)
|
|
234
|
+
}
|
|
235
|
+
const oldGroup = await getGroup(registration.groupId)
|
|
236
|
+
if (oldGroup) {
|
|
237
|
+
// We need to update this group occupancy because we moved one member away from it
|
|
238
|
+
updateGroups.set(oldGroup.id, oldGroup)
|
|
239
|
+
}
|
|
240
|
+
} else {
|
|
241
|
+
group = await getGroup(registration.groupId)
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (!group || group.organizationId !== (patchRegistration.organizationId ?? registration.organizationId)) {
|
|
245
|
+
throw new SimpleError({
|
|
246
|
+
code: "invalid_field",
|
|
247
|
+
message: "Group doesn't exist",
|
|
248
|
+
human: "De groep naarwaar je dit lid wilt verplaatsen bestaat niet",
|
|
249
|
+
field: "groupId"
|
|
250
|
+
})
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (!await Context.auth.canAccessGroup(group, PermissionLevel.Write)) {
|
|
254
|
+
throw Context.auth.error("Je hebt niet voldoende rechten om leden te verplaatsen naar deze groep")
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (patchRegistration.cycle && patchRegistration.cycle > group.cycle) {
|
|
258
|
+
throw new SimpleError({
|
|
259
|
+
code: "invalid_field",
|
|
260
|
+
message: "Invalid cycle",
|
|
261
|
+
human: "Je kan een lid niet inschrijven voor een groep die nog moet starten",
|
|
262
|
+
field: "cycle"
|
|
263
|
+
})
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// TODO: allow group changes
|
|
267
|
+
registration.waitingList = patchRegistration.waitingList ?? registration.waitingList
|
|
268
|
+
|
|
269
|
+
if (!registration.waitingList && registration.registeredAt === null) {
|
|
270
|
+
registration.registeredAt = new Date()
|
|
271
|
+
}
|
|
272
|
+
registration.canRegister = patchRegistration.canRegister ?? registration.canRegister
|
|
273
|
+
if (!registration.waitingList) {
|
|
274
|
+
registration.canRegister = false
|
|
275
|
+
}
|
|
276
|
+
registration.cycle = patchRegistration.cycle ?? registration.cycle
|
|
277
|
+
registration.groupId = patchRegistration.groupId ?? registration.groupId
|
|
278
|
+
registration.organizationId = patchRegistration.organizationId ?? registration.organizationId
|
|
279
|
+
|
|
280
|
+
// Check if we should create a placeholder payment?
|
|
281
|
+
|
|
282
|
+
if (patchRegistration.cycle !== undefined || patchRegistration.waitingList !== undefined || patchRegistration.canRegister !== undefined) {
|
|
283
|
+
// We need to update occupancy (because cycle / waitlist change)
|
|
284
|
+
updateGroups.set(group.id, group)
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (patchRegistration.price) {
|
|
288
|
+
// Create balance item
|
|
289
|
+
const balanceItem = new BalanceItem();
|
|
290
|
+
balanceItem.registrationId = registration.id;
|
|
291
|
+
balanceItem.price = patchRegistration.price
|
|
292
|
+
balanceItem.description = group ? `Inschrijving ${group.settings.name}` : `Inschrijving`
|
|
293
|
+
balanceItem.pricePaid = patchRegistration.pricePaid ?? 0
|
|
294
|
+
balanceItem.memberId = registration.memberId;
|
|
295
|
+
balanceItem.userId = member.users[0]?.id ?? null
|
|
296
|
+
balanceItem.organizationId = group.organizationId
|
|
297
|
+
balanceItem.status = BalanceItemStatus.Pending;
|
|
298
|
+
await balanceItem.save();
|
|
299
|
+
|
|
300
|
+
addBalanceItemRegistrationId(registration.organizationId, registration.id)
|
|
301
|
+
balanceItemMemberIds.push(member.id)
|
|
302
|
+
|
|
303
|
+
if (balanceItem.pricePaid > 0) {
|
|
304
|
+
// Create an Unknown payment and attach it to the balance item
|
|
305
|
+
const payment = new Payment();
|
|
306
|
+
payment.userId = member.users[0]?.id ?? null
|
|
307
|
+
payment.organizationId = member.organizationId
|
|
308
|
+
payment.method = PaymentMethod.Unknown
|
|
309
|
+
payment.status = PaymentStatus.Succeeded
|
|
310
|
+
payment.price = balanceItem.pricePaid;
|
|
311
|
+
payment.paidAt = new Date()
|
|
312
|
+
payment.provider = null
|
|
313
|
+
await payment.save()
|
|
314
|
+
|
|
315
|
+
const balanceItemPayment = new BalanceItemPayment()
|
|
316
|
+
balanceItemPayment.balanceItemId = balanceItem.id;
|
|
317
|
+
balanceItemPayment.paymentId = payment.id;
|
|
318
|
+
balanceItemPayment.organizationId = group.organizationId
|
|
319
|
+
balanceItemPayment.price = payment.price;
|
|
320
|
+
await balanceItemPayment.save();
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
await registration.save()
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
for (const deleteId of patch.registrations.getDeletes()) {
|
|
328
|
+
const registration = member.registrations.find(r => r.id === deleteId)
|
|
329
|
+
if (!registration || registration.memberId != member.id) {
|
|
330
|
+
throw new SimpleError({
|
|
331
|
+
code: "permission_denied",
|
|
332
|
+
message: "You don't have permissions to access this endpoint",
|
|
333
|
+
human: "Je hebt geen toegang om deze registratie te wijzigen"
|
|
334
|
+
})
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (!await Context.auth.canAccessRegistration(registration, PermissionLevel.Write)) {
|
|
338
|
+
throw Context.auth.error("Je hebt niet voldoende rechten om deze inschrijving te verwijderen")
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
balanceItemMemberIds.push(member.id)
|
|
342
|
+
await BalanceItem.deleteForDeletedRegistration(registration.id)
|
|
343
|
+
await registration.delete()
|
|
344
|
+
member.registrations = member.registrations.filter(r => r.id !== deleteId)
|
|
345
|
+
|
|
346
|
+
const oldGroup = await getGroup(registration.groupId)
|
|
347
|
+
if (oldGroup) {
|
|
348
|
+
// We need to update this group occupancy because we moved one member away from it
|
|
349
|
+
updateGroups.set(oldGroup.id, oldGroup)
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// Add registrations
|
|
354
|
+
for (const registrationStruct of patch.registrations.getPuts()) {
|
|
355
|
+
const struct = registrationStruct.put
|
|
356
|
+
const group = await getGroup(struct.groupId)
|
|
357
|
+
|
|
358
|
+
if (!group || group.organizationId !== struct.organizationId || !await Context.auth.canAccessGroup(group, PermissionLevel.Write)) {
|
|
359
|
+
throw Context.auth.error("Je hebt niet voldoende rechten om inschrijvingen in deze groep te maken")
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
const reg = await this.addRegistration(member, struct, group)
|
|
363
|
+
balanceItemMemberIds.push(member.id)
|
|
364
|
+
addBalanceItemRegistrationId(reg.organizationId, reg.id)
|
|
365
|
+
|
|
366
|
+
// We need to update this group occupancy because we moved one member away from it
|
|
367
|
+
updateGroups.set(group.id, group)
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Update responsibilities
|
|
371
|
+
for (const patchResponsibility of patch.responsibilities.getPatches()) {
|
|
372
|
+
if (!Context.auth.hasPlatformFullAccess() && !(organization && await Context.auth.hasFullAccess(organization.id))) {
|
|
373
|
+
throw Context.auth.error("Je hebt niet voldoende rechten om functies van leden aan te passen")
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
const responsibilityRecord = await MemberResponsibilityRecord.getByID(patchResponsibility.id)
|
|
377
|
+
if (!responsibilityRecord || responsibilityRecord.memberId != member.id || (organization && responsibilityRecord.organizationId !== organization.id)) {
|
|
378
|
+
throw new SimpleError({
|
|
379
|
+
code: "permission_denied",
|
|
380
|
+
message: "You don't have permissions to access this endpoint",
|
|
381
|
+
human: "Je hebt geen toegang om deze functie te wijzigen"
|
|
382
|
+
})
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
const platform = await Platform.getShared()
|
|
386
|
+
const responsibility = platform.config.responsibilities.find(r => r.id === patchResponsibility.responsibilityId)
|
|
387
|
+
|
|
388
|
+
if (responsibility && !responsibility.assignableByOrganizations && !Context.auth.hasPlatformFullAccess()) {
|
|
389
|
+
throw Context.auth.error("Je hebt niet voldoende rechten om deze functie aan te passen")
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// Allow patching begin and end date
|
|
393
|
+
if (patchResponsibility.endDate !== undefined) {
|
|
394
|
+
if (responsibilityRecord.endDate) {
|
|
395
|
+
if (!Context.auth.hasPlatformFullAccess()) {
|
|
396
|
+
throw Context.auth.error("Je hebt niet voldoende rechten om reeds beëindigde functies aan te passen")
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
responsibilityRecord.endDate = patchResponsibility.endDate
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
if (patchResponsibility.startDate !== undefined) {
|
|
403
|
+
|
|
404
|
+
if (patchResponsibility.startDate > new Date()) {
|
|
405
|
+
throw Context.auth.error("Je kan de startdatum van een functie niet in de toekomst zetten")
|
|
406
|
+
}
|
|
407
|
+
const daysDiff = Math.abs((new Date().getTime() - patchResponsibility.startDate.getTime()) / (1000 * 60 * 60 * 24))
|
|
408
|
+
|
|
409
|
+
if (daysDiff > 60 && !Context.auth.hasPlatformFullAccess()) {
|
|
410
|
+
throw Context.auth.error("Je kan de startdatum van een functie niet zoveel verplaatsen")
|
|
411
|
+
}
|
|
412
|
+
responsibilityRecord.startDate = patchResponsibility.startDate
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
await responsibilityRecord.save()
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// Update responsibilities
|
|
419
|
+
for (const {put} of patch.responsibilities.getPuts()) {
|
|
420
|
+
if (!Context.auth.hasPlatformFullAccess() && !(organization && await Context.auth.hasFullAccess(organization.id))) {
|
|
421
|
+
throw Context.auth.error("Je hebt niet voldoende rechten om functies van leden aan te passen")
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
const platform = await Platform.getShared()
|
|
425
|
+
const responsibility = platform.config.responsibilities.find(r => r.id === put.responsibilityId)
|
|
426
|
+
|
|
427
|
+
if (!responsibility || (!responsibility.assignableByOrganizations && !Context.auth.hasPlatformFullAccess())) {
|
|
428
|
+
throw Context.auth.error("Je hebt niet voldoende rechten om deze functie toe te kennen")
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
const model = new MemberResponsibilityRecord()
|
|
432
|
+
model.memberId = member.id
|
|
433
|
+
model.responsibilityId = responsibility.id
|
|
434
|
+
|
|
435
|
+
if (responsibility.assignableByOrganizations) {
|
|
436
|
+
if (organization) {
|
|
437
|
+
model.organizationId = organization.id
|
|
438
|
+
} else {
|
|
439
|
+
if (!put.organizationId) {
|
|
440
|
+
if (!Context.auth.hasPlatformFullAccess()) {
|
|
441
|
+
throw Context.auth.error("Je hebt niet voldoende rechten om deze functie toe te kennen")
|
|
442
|
+
}
|
|
443
|
+
} else if (!await Context.auth.hasFullAccess(put.organizationId)) {
|
|
444
|
+
throw Context.auth.error("Je hebt niet voldoende rechten om functies van leden toe te kennen voor deze vereniging")
|
|
445
|
+
}
|
|
446
|
+
model.organizationId = put.organizationId
|
|
447
|
+
}
|
|
448
|
+
} else {
|
|
449
|
+
model.organizationId = null
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// Allow patching begin and end date
|
|
453
|
+
model.endDate = put.endDate
|
|
454
|
+
|
|
455
|
+
if (put.startDate > new Date()) {
|
|
456
|
+
throw Context.auth.error("Je kan de startdatum van een functie niet in de toekomst zetten")
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
model.startDate = put.startDate
|
|
460
|
+
|
|
461
|
+
await model.save()
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// Link users
|
|
465
|
+
for (const placeholder of patch.users.getPuts()) {
|
|
466
|
+
await PatchOrganizationMembersEndpoint.linkUser(placeholder.put, member)
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// Unlink users
|
|
470
|
+
for (const userId of patch.users.getDeletes()) {
|
|
471
|
+
await PatchOrganizationMembersEndpoint.unlinkUser(userId, member)
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// Auto link users based on data
|
|
475
|
+
if (patch.users.changes.length || patch.details) {
|
|
476
|
+
await PatchOrganizationMembersEndpoint.updateManagers(member)
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
if (!members.find(m => m.id === member.id)) {
|
|
480
|
+
members.push(member)
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// Loop all members one by one
|
|
485
|
+
for (const id of request.body.getDeletes()) {
|
|
486
|
+
const member = await Member.getWithRegistrations(id)
|
|
487
|
+
if (!member || !await Context.auth.canDeleteMember(member)) {
|
|
488
|
+
throw Context.auth.error("Je hebt niet voldoende rechten om dit lid te verwijderen")
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
await User.deleteForDeletedMember(member.id)
|
|
492
|
+
await BalanceItem.deleteForDeletedMember(member.id)
|
|
493
|
+
await member.delete()
|
|
494
|
+
|
|
495
|
+
// Update occupancy of this member because we removed registrations
|
|
496
|
+
const groupIds = member.registrations.flatMap(r => r.groupId)
|
|
497
|
+
for (const id of groupIds) {
|
|
498
|
+
const group = await getGroup(id)
|
|
499
|
+
if (group) {
|
|
500
|
+
// We need to update this group occupancy because we moved one member away from it
|
|
501
|
+
updateGroups.set(group.id, group)
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
await Member.updateOutstandingBalance(Formatter.uniqueArray(balanceItemMemberIds))
|
|
507
|
+
for (const [organizationId, balanceItemRegistrationIds] of balanceItemRegistrationIdsPerOrganization) {
|
|
508
|
+
await Registration.updateOutstandingBalance(Formatter.uniqueArray(balanceItemRegistrationIds), organizationId)
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// Loop all groups and update occupancy if needed
|
|
512
|
+
for (const group of updateGroups.values()) {
|
|
513
|
+
await group.updateOccupancy()
|
|
514
|
+
await group.save()
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// We need to refetch the outstanding amounts of members that have changed
|
|
518
|
+
const updatedMembers = balanceItemMemberIds.length > 0 ? await Member.getBlobByIds(...balanceItemMemberIds) : []
|
|
519
|
+
for (const member of updatedMembers) {
|
|
520
|
+
const index = members.findIndex(m => m.id === member.id)
|
|
521
|
+
if (index !== -1) {
|
|
522
|
+
members[index] = member
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
return new Response(
|
|
527
|
+
await AuthenticatedStructures.membersBlob(members)
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
static async checkDuplicate(member: Member) {
|
|
532
|
+
if (!member.details.birthDay) {
|
|
533
|
+
return
|
|
534
|
+
}
|
|
535
|
+
const existingMembers = await Member.where({ organizationId: member.organizationId, firstName: member.details.firstName, lastName: member.details.lastName, birthDay: Formatter.dateIso(member.details.birthDay) });
|
|
536
|
+
|
|
537
|
+
if (existingMembers.length > 0) {
|
|
538
|
+
const withRegistrations = await Member.getBlobByIds(...existingMembers.map(m => m.id))
|
|
539
|
+
for (const member of withRegistrations) {
|
|
540
|
+
if (member.registrations.length > 0) {
|
|
541
|
+
return member
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
if (withRegistrations.length > 0) {
|
|
546
|
+
return withRegistrations[0]
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
async addRegistration(member: Member & Record<"registrations", (Registration & {group: Group})[]> & Record<"users", User[]>, registrationStruct: RegistrationStruct, group: Group) {
|
|
552
|
+
// Check if this member has this registration already.
|
|
553
|
+
// Note: we cannot use the relation here, because invalid ones or reserved ones are not loaded in there
|
|
554
|
+
const existings = await Registration.where({
|
|
555
|
+
memberId: member.id,
|
|
556
|
+
groupId: registrationStruct.groupId,
|
|
557
|
+
cycle: registrationStruct.cycle
|
|
558
|
+
}, { limit: 1 })
|
|
559
|
+
const existing = existings.length > 0 ? existings[0] : null
|
|
560
|
+
|
|
561
|
+
// If the existing is invalid, delete it.
|
|
562
|
+
if (existing && !existing.registeredAt && !existing.waitingList) {
|
|
563
|
+
console.log('Deleting invalid registration', existing.id)
|
|
564
|
+
await existing.delete()
|
|
565
|
+
} else if (existing) {
|
|
566
|
+
throw new SimpleError({
|
|
567
|
+
code: "invalid_field",
|
|
568
|
+
message: "Registration already exists",
|
|
569
|
+
human: existing.waitingList ? "Dit lid staat al op de wachtlijst voor deze groep" : "Dit lid is al ingeschreven voor deze groep",
|
|
570
|
+
field: "groupId"
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
if (!group) {
|
|
575
|
+
throw new SimpleError({
|
|
576
|
+
code: 'invalid_field',
|
|
577
|
+
field: 'groupId',
|
|
578
|
+
message: 'Invalid groupId',
|
|
579
|
+
human: 'Deze inschrijvingsgroep is ongeldig'
|
|
580
|
+
})
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
const registration = new Registration()
|
|
584
|
+
registration.groupId = registrationStruct.groupId
|
|
585
|
+
registration.organizationId = group.organizationId
|
|
586
|
+
registration.periodId = group.periodId
|
|
587
|
+
registration.cycle = registrationStruct.cycle
|
|
588
|
+
registration.memberId = member.id
|
|
589
|
+
registration.registeredAt = registrationStruct.registeredAt
|
|
590
|
+
registration.waitingList = registrationStruct.waitingList
|
|
591
|
+
registration.createdAt = registrationStruct.createdAt ?? new Date()
|
|
592
|
+
|
|
593
|
+
if (registration.waitingList) {
|
|
594
|
+
registration.registeredAt = null
|
|
595
|
+
}
|
|
596
|
+
registration.canRegister = registrationStruct.canRegister
|
|
597
|
+
|
|
598
|
+
if (!registration.waitingList) {
|
|
599
|
+
registration.canRegister = false
|
|
600
|
+
}
|
|
601
|
+
registration.deactivatedAt = registrationStruct.deactivatedAt
|
|
602
|
+
|
|
603
|
+
await registration.save()
|
|
604
|
+
member.registrations.push(registration.setRelation(Registration.group, group))
|
|
605
|
+
|
|
606
|
+
if (registrationStruct.price) {
|
|
607
|
+
// Create balance item
|
|
608
|
+
const balanceItem = new BalanceItem();
|
|
609
|
+
balanceItem.registrationId = registration.id;
|
|
610
|
+
balanceItem.price = registrationStruct.price
|
|
611
|
+
balanceItem.description = group ? `Inschrijving ${group.settings.name}` : `Inschrijving`
|
|
612
|
+
balanceItem.pricePaid = registrationStruct.pricePaid ?? 0
|
|
613
|
+
balanceItem.memberId = registration.memberId;
|
|
614
|
+
balanceItem.userId = member.users[0]?.id ?? null
|
|
615
|
+
balanceItem.organizationId = group.organizationId
|
|
616
|
+
balanceItem.status = BalanceItemStatus.Pending;
|
|
617
|
+
await balanceItem.save();
|
|
618
|
+
|
|
619
|
+
if (balanceItem.pricePaid > 0) {
|
|
620
|
+
// Create an Unknown payment and attach it to the balance item
|
|
621
|
+
const payment = new Payment();
|
|
622
|
+
payment.userId = member.users[0]?.id ?? null
|
|
623
|
+
payment.organizationId = member.organizationId
|
|
624
|
+
payment.method = PaymentMethod.Unknown
|
|
625
|
+
payment.status = PaymentStatus.Succeeded
|
|
626
|
+
payment.price = balanceItem.pricePaid;
|
|
627
|
+
payment.paidAt = new Date()
|
|
628
|
+
payment.provider = null
|
|
629
|
+
await payment.save()
|
|
630
|
+
|
|
631
|
+
const balanceItemPayment = new BalanceItemPayment()
|
|
632
|
+
balanceItemPayment.balanceItemId = balanceItem.id;
|
|
633
|
+
balanceItemPayment.paymentId = payment.id;
|
|
634
|
+
balanceItemPayment.organizationId = group.organizationId
|
|
635
|
+
balanceItemPayment.price = payment.price;
|
|
636
|
+
await balanceItemPayment.save();
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
return registration
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
async createDummyMembers(organization: Organization, group: Group, count: number) {
|
|
644
|
+
const members = await new MemberFactory({
|
|
645
|
+
organization,
|
|
646
|
+
minAge: group.settings.minAge ?? undefined,
|
|
647
|
+
maxAge: group.settings.maxAge ?? undefined
|
|
648
|
+
}).createMultiple(count)
|
|
649
|
+
|
|
650
|
+
for (const m of members) {
|
|
651
|
+
const member = m.setManyRelation(Member.registrations as unknown as OneToManyRelation<"registrations", Member, Registration>, []).setManyRelation(Member.users, [])
|
|
652
|
+
const d = new Date(new Date().getTime() - Math.random() * 60 * 1000 * 60 * 24 * 60)
|
|
653
|
+
|
|
654
|
+
// Create a registration for this member for thisg roup
|
|
655
|
+
const registration = new Registration()
|
|
656
|
+
registration.organizationId = organization.id
|
|
657
|
+
registration.memberId = member.id
|
|
658
|
+
registration.groupId = group.id
|
|
659
|
+
registration.periodId = group.periodId
|
|
660
|
+
registration.cycle = group.cycle
|
|
661
|
+
registration.registeredAt = d
|
|
662
|
+
|
|
663
|
+
member.registrations.push(registration)
|
|
664
|
+
await registration.save()
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
static async updateManagers(member: MemberWithRegistrations) {
|
|
669
|
+
// Check accounts
|
|
670
|
+
const managers = member.details.getManagerEmails()
|
|
671
|
+
|
|
672
|
+
for(const email of managers) {
|
|
673
|
+
const u = member.users.find(u => u.email.toLocaleLowerCase() === email.toLocaleLowerCase())
|
|
674
|
+
if (!u) {
|
|
675
|
+
console.log("Linking user "+email+" to member "+member.id)
|
|
676
|
+
await PatchOrganizationMembersEndpoint.linkUser(UserStruct.create({
|
|
677
|
+
firstName: member.details.parents.find(p => p.email === email)?.firstName,
|
|
678
|
+
lastName: member.details.parents.find(p => p.email === email)?.lastName,
|
|
679
|
+
email,
|
|
680
|
+
}), member)
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
// Delete accounts that should no longer have access
|
|
685
|
+
for (const u of member.users) {
|
|
686
|
+
if (!u.hasAccount()) {
|
|
687
|
+
// And not in managers list (case insensitive)
|
|
688
|
+
if (!managers.find(m => m.toLocaleLowerCase() === u.email.toLocaleLowerCase())) {
|
|
689
|
+
console.log("Unlinking user "+u.email+" from member "+member.id)
|
|
690
|
+
await PatchOrganizationMembersEndpoint.unlinkUser(u.id, member)
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
static async linkUser(user: UserStruct, member: MemberWithRegistrations) {
|
|
697
|
+
const email = user.email
|
|
698
|
+
let u = await User.getForAuthentication(member.organizationId, email, {allowWithoutAccount: true});
|
|
699
|
+
if (u) {
|
|
700
|
+
console.log("Giving an existing user access to a member: "+u.id)
|
|
701
|
+
} else {
|
|
702
|
+
u = new User()
|
|
703
|
+
u.organizationId = member.organizationId
|
|
704
|
+
u.email = email
|
|
705
|
+
u.firstName = user.firstName
|
|
706
|
+
u.lastName = user.lastName
|
|
707
|
+
await u.save()
|
|
708
|
+
|
|
709
|
+
console.log("Created new (placeholder) user that has access to a member: "+u.id)
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
await Member.users.reverse("members").link(u, [member])
|
|
713
|
+
|
|
714
|
+
// Update model relation to correct response
|
|
715
|
+
member.users.push(u)
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
static async unlinkUser(userId: string, member: MemberWithRegistrations) {
|
|
719
|
+
console.log("Removing access for "+ userId +" to member "+member.id)
|
|
720
|
+
const existingIndex = member.users.findIndex(u => u.id === userId)
|
|
721
|
+
if (existingIndex === -1) {
|
|
722
|
+
throw new SimpleError({
|
|
723
|
+
code: "user_not_found",
|
|
724
|
+
message: "Unlinking a user that doesn't exists anymore",
|
|
725
|
+
human: "Je probeert de toegang van een account tot een lid te verwijderen, maar dat account bestaat niet (meer)"
|
|
726
|
+
})
|
|
727
|
+
}
|
|
728
|
+
const existing = member.users[existingIndex]
|
|
729
|
+
await Member.users.reverse("members").unlink(existing, member)
|
|
730
|
+
|
|
731
|
+
// Update model relation to correct response
|
|
732
|
+
member.users.splice(existingIndex, 1)
|
|
733
|
+
}
|
|
734
|
+
}
|