@stamhoofd/backend 2.36.2 → 2.38.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/package.json +10 -10
- package/src/endpoints/admin/memberships/ChargeMembershipsEndpoint.ts +1 -2
- package/src/endpoints/admin/organizations/PatchOrganizationsEndpoint.ts +8 -0
- package/src/endpoints/auth/CreateAdminEndpoint.ts +4 -3
- package/src/endpoints/auth/PatchUserEndpoint.ts +2 -2
- package/src/endpoints/global/addresses/SearchRegionsEndpoint.ts +12 -0
- package/src/endpoints/global/email/CreateEmailEndpoint.ts +2 -0
- package/src/endpoints/global/email/PatchEmailEndpoint.ts +6 -0
- package/src/endpoints/global/events/PatchEventsEndpoint.ts +1 -1
- package/src/endpoints/global/files/ExportToExcelEndpoint.ts +9 -10
- package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.ts +46 -24
- package/src/endpoints/global/platform/PatchPlatformEnpoint.ts +11 -4
- package/src/endpoints/global/registration/PatchUserMembersEndpoint.ts +2 -0
- package/src/endpoints/global/registration/RegisterMembersEndpoint.test.ts +453 -0
- package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.ts +19 -2
- package/src/endpoints/organization/shared/ExchangePaymentEndpoint.ts +13 -2
- package/src/endpoints/organization/shared/GetPaymentEndpoint.ts +1 -1
- package/src/excel-loaders/members.ts +87 -24
- package/src/helpers/AddressValidator.ts +11 -0
- package/src/helpers/AdminPermissionChecker.ts +14 -1
- package/src/helpers/Context.ts +2 -2
- package/src/helpers/MembershipCharger.ts +84 -2
- package/src/helpers/fetchToAsyncIterator.ts +3 -4
- package/src/seeds/1726494420-update-cached-outstanding-balance-from-items.ts +40 -0
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
import { Request } from "@simonbackx/simple-endpoints";
|
|
2
|
+
import { Group, GroupFactory, MemberFactory, MemberWithRegistrations, Organization, OrganizationFactory, RegistrationFactory, RegistrationPeriod, RegistrationPeriodFactory, Token, User, UserFactory } from "@stamhoofd/models";
|
|
3
|
+
import { GroupPrice, IDRegisterCart, IDRegisterCheckout, IDRegisterItem, PayconiqAccount, PaymentMethod, PermissionLevel, Permissions, Version } from "@stamhoofd/structures";
|
|
4
|
+
import nock from "nock";
|
|
5
|
+
import { v4 as uuidv4 } from "uuid";
|
|
6
|
+
import { testServer } from "../../../../tests/helpers/TestServer";
|
|
7
|
+
import { RegisterMembersEndpoint } from "./RegisterMembersEndpoint";
|
|
8
|
+
|
|
9
|
+
const baseUrl = `/v${Version}/members/register`
|
|
10
|
+
|
|
11
|
+
describe("Endpoint.RegisterMembers", () => {
|
|
12
|
+
//#region global
|
|
13
|
+
const endpoint = new RegisterMembersEndpoint();
|
|
14
|
+
let period: RegistrationPeriod;
|
|
15
|
+
let organization: Organization;
|
|
16
|
+
let user: User;
|
|
17
|
+
let token: Token;
|
|
18
|
+
let member: MemberWithRegistrations;
|
|
19
|
+
let group1: Group;
|
|
20
|
+
let groupPrice1: GroupPrice;
|
|
21
|
+
let group2: Group;
|
|
22
|
+
let groupPrice2: GroupPrice;
|
|
23
|
+
|
|
24
|
+
//#region helpers
|
|
25
|
+
const post = async (body: IDRegisterCheckout) => {
|
|
26
|
+
const request = Request.buildJson("POST", baseUrl,organization.getApiHost(), body);
|
|
27
|
+
request.headers.authorization = "Bearer "+token.accessToken;
|
|
28
|
+
return await testServer.test(endpoint, request);
|
|
29
|
+
}
|
|
30
|
+
//#endregion
|
|
31
|
+
|
|
32
|
+
//#endregion
|
|
33
|
+
|
|
34
|
+
beforeAll(async () => {
|
|
35
|
+
period = await new RegistrationPeriodFactory({}).create();
|
|
36
|
+
organization = await new OrganizationFactory({ period }).create();
|
|
37
|
+
organization.meta.registrationPaymentConfiguration.paymentMethods = [PaymentMethod.PointOfSale, PaymentMethod.Payconiq];
|
|
38
|
+
|
|
39
|
+
organization.privateMeta.payconiqAccounts = [PayconiqAccount.create({
|
|
40
|
+
id: uuidv4(),
|
|
41
|
+
apiKey: 'test',
|
|
42
|
+
merchantId: 'test',
|
|
43
|
+
profileId: 'test',
|
|
44
|
+
name: 'test',
|
|
45
|
+
iban: 'BE56587127952688', // = random IBAN
|
|
46
|
+
callbackUrl: 'https://example.com'
|
|
47
|
+
})]
|
|
48
|
+
|
|
49
|
+
user = await new UserFactory({
|
|
50
|
+
organization,
|
|
51
|
+
permissions: Permissions.create({
|
|
52
|
+
level: PermissionLevel.Full
|
|
53
|
+
})
|
|
54
|
+
}).create();
|
|
55
|
+
token = await Token.createToken(user);
|
|
56
|
+
member = await new MemberFactory({ organization, user }).create();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
beforeEach(async () => {
|
|
60
|
+
//#region groups
|
|
61
|
+
group1 = await new GroupFactory({
|
|
62
|
+
organization,
|
|
63
|
+
price: 25,
|
|
64
|
+
stock: 5
|
|
65
|
+
}).create();
|
|
66
|
+
|
|
67
|
+
groupPrice1 = group1.settings.prices[0];
|
|
68
|
+
|
|
69
|
+
group2 = await new GroupFactory({
|
|
70
|
+
organization,
|
|
71
|
+
price: 15,
|
|
72
|
+
stock: 4,
|
|
73
|
+
maxMembers: 1
|
|
74
|
+
}).create();
|
|
75
|
+
|
|
76
|
+
groupPrice2 = group2.settings.prices[0];
|
|
77
|
+
//#endregion
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
describe('Register member', () => {
|
|
81
|
+
|
|
82
|
+
test("Should update registered mmebers", async () => {
|
|
83
|
+
//#region arrange
|
|
84
|
+
const body = IDRegisterCheckout.create({
|
|
85
|
+
cart: IDRegisterCart.create({
|
|
86
|
+
items: [
|
|
87
|
+
IDRegisterItem.create({
|
|
88
|
+
id: uuidv4(),
|
|
89
|
+
replaceRegistrationIds: [],
|
|
90
|
+
options: [],
|
|
91
|
+
groupPrice: groupPrice1,
|
|
92
|
+
organizationId: organization.id,
|
|
93
|
+
groupId: group1.id,
|
|
94
|
+
memberId: member.id
|
|
95
|
+
})
|
|
96
|
+
],
|
|
97
|
+
balanceItems: [],
|
|
98
|
+
deleteRegistrationIds: []
|
|
99
|
+
}),
|
|
100
|
+
administrationFee: 0,
|
|
101
|
+
freeContribution: 0,
|
|
102
|
+
paymentMethod: PaymentMethod.PointOfSale,
|
|
103
|
+
totalPrice: 25,
|
|
104
|
+
asOrganizationId: organization.id,
|
|
105
|
+
customer: null,
|
|
106
|
+
});
|
|
107
|
+
//#endregion
|
|
108
|
+
|
|
109
|
+
// act
|
|
110
|
+
const response = await post(body);
|
|
111
|
+
|
|
112
|
+
// assert
|
|
113
|
+
expect(response.body).toBeDefined();
|
|
114
|
+
expect(response.body.registrations.length).toBe(1);
|
|
115
|
+
|
|
116
|
+
const updatedGroup = await Group.getByID(group1.id);
|
|
117
|
+
expect(updatedGroup!.settings.registeredMembers).toBe(1);
|
|
118
|
+
expect(updatedGroup!.settings.reservedMembers).toBe(0);
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
test("Should update reserved members", async () => {
|
|
122
|
+
//#region arrange
|
|
123
|
+
const body = IDRegisterCheckout.create({
|
|
124
|
+
cart: IDRegisterCart.create({
|
|
125
|
+
items: [
|
|
126
|
+
IDRegisterItem.create({
|
|
127
|
+
id: uuidv4(),
|
|
128
|
+
replaceRegistrationIds: [],
|
|
129
|
+
options: [],
|
|
130
|
+
groupPrice: groupPrice2,
|
|
131
|
+
organizationId: organization.id,
|
|
132
|
+
groupId: group2.id,
|
|
133
|
+
memberId: member.id
|
|
134
|
+
})
|
|
135
|
+
],
|
|
136
|
+
balanceItems: [],
|
|
137
|
+
deleteRegistrationIds: []
|
|
138
|
+
}),
|
|
139
|
+
administrationFee: 0,
|
|
140
|
+
freeContribution: 0,
|
|
141
|
+
paymentMethod: PaymentMethod.Payconiq,
|
|
142
|
+
redirectUrl: new URL("https://www.example.com"),
|
|
143
|
+
cancelUrl: new URL("https://www.example.com"),
|
|
144
|
+
totalPrice: 15,
|
|
145
|
+
customer: null,
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
nock('https://api.ext.payconiq.com')
|
|
149
|
+
.post('/v3/payments')
|
|
150
|
+
.reply(200, {
|
|
151
|
+
paymentId: 'testPaymentId',
|
|
152
|
+
_links: {
|
|
153
|
+
checkout: {
|
|
154
|
+
href: 'https://www.example.com'
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
//#endregion
|
|
159
|
+
|
|
160
|
+
// act
|
|
161
|
+
const response = await post(body);
|
|
162
|
+
|
|
163
|
+
// assert
|
|
164
|
+
expect(response.body).toBeDefined();
|
|
165
|
+
expect(response.body.registrations.length).toBe(1);
|
|
166
|
+
|
|
167
|
+
const updatedGroup = await Group.getByID(group2.id);
|
|
168
|
+
expect(updatedGroup!.settings.registeredMembers).toBe(0);
|
|
169
|
+
expect(updatedGroup!.settings.reservedMembers).toBe(1);
|
|
170
|
+
})
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
describe('Register member with replace registration', () => {
|
|
174
|
+
|
|
175
|
+
test("Should update registered members", async () => {
|
|
176
|
+
//#region arrange
|
|
177
|
+
const registration = await new RegistrationFactory({
|
|
178
|
+
member,
|
|
179
|
+
group: group1,
|
|
180
|
+
groupPrice: groupPrice1
|
|
181
|
+
})
|
|
182
|
+
.create();
|
|
183
|
+
|
|
184
|
+
const group = await new GroupFactory({
|
|
185
|
+
organization,
|
|
186
|
+
price: 30,
|
|
187
|
+
stock: 5
|
|
188
|
+
}).create();
|
|
189
|
+
|
|
190
|
+
const groupPrice = group.settings.prices[0];
|
|
191
|
+
|
|
192
|
+
const body = IDRegisterCheckout.create({
|
|
193
|
+
cart: IDRegisterCart.create({
|
|
194
|
+
items: [
|
|
195
|
+
IDRegisterItem.create({
|
|
196
|
+
id: uuidv4(),
|
|
197
|
+
replaceRegistrationIds: [registration.id],
|
|
198
|
+
options: [],
|
|
199
|
+
groupPrice,
|
|
200
|
+
organizationId: organization.id,
|
|
201
|
+
groupId: group.id,
|
|
202
|
+
memberId: member.id
|
|
203
|
+
})
|
|
204
|
+
],
|
|
205
|
+
balanceItems: [],
|
|
206
|
+
deleteRegistrationIds: []
|
|
207
|
+
}),
|
|
208
|
+
administrationFee: 0,
|
|
209
|
+
freeContribution: 0,
|
|
210
|
+
paymentMethod: PaymentMethod.PointOfSale,
|
|
211
|
+
totalPrice: 5,
|
|
212
|
+
asOrganizationId: organization.id,
|
|
213
|
+
customer: null,
|
|
214
|
+
});
|
|
215
|
+
//#endregion
|
|
216
|
+
|
|
217
|
+
//#region act and assert
|
|
218
|
+
|
|
219
|
+
// update occupancy to be sure occupancy is 1
|
|
220
|
+
await group1.updateOccupancy();
|
|
221
|
+
expect(group1.settings.registeredMembers).toBe(1);
|
|
222
|
+
|
|
223
|
+
// send request and check occupancy
|
|
224
|
+
const response = await post(body);
|
|
225
|
+
|
|
226
|
+
expect(response.body).toBeDefined();
|
|
227
|
+
expect(response.body.registrations.length).toBe(1);
|
|
228
|
+
|
|
229
|
+
const updatedGroup = await Group.getByID(group.id);
|
|
230
|
+
expect(updatedGroup!.settings.registeredMembers).toBe(1);
|
|
231
|
+
expect(updatedGroup!.settings.reservedMembers).toBe(0);
|
|
232
|
+
|
|
233
|
+
const updatedGroup1After = await Group.getByID(group1.id);
|
|
234
|
+
// occupancy should go from 1 to 0 because the registration should be replaced
|
|
235
|
+
expect(updatedGroup1After!.settings.registeredMembers).toBe(0);
|
|
236
|
+
expect(updatedGroup1After!.settings.reservedMembers).toBe(0);
|
|
237
|
+
//#endregion
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
test("Should throw error if with payment", async () => {
|
|
241
|
+
//#region arrange
|
|
242
|
+
const registration = await new RegistrationFactory({
|
|
243
|
+
member,
|
|
244
|
+
group: group1,
|
|
245
|
+
groupPrice: groupPrice1
|
|
246
|
+
})
|
|
247
|
+
.create();
|
|
248
|
+
|
|
249
|
+
const group = await new GroupFactory({
|
|
250
|
+
organization,
|
|
251
|
+
price: 30,
|
|
252
|
+
stock: 5,
|
|
253
|
+
maxMembers: 1
|
|
254
|
+
}).create();
|
|
255
|
+
|
|
256
|
+
const groupPrice = group.settings.prices[0];
|
|
257
|
+
|
|
258
|
+
const body = IDRegisterCheckout.create({
|
|
259
|
+
cart: IDRegisterCart.create({
|
|
260
|
+
items: [
|
|
261
|
+
IDRegisterItem.create({
|
|
262
|
+
id: uuidv4(),
|
|
263
|
+
replaceRegistrationIds: [registration.id],
|
|
264
|
+
options: [],
|
|
265
|
+
groupPrice,
|
|
266
|
+
organizationId: organization.id,
|
|
267
|
+
groupId: group.id,
|
|
268
|
+
memberId: member.id
|
|
269
|
+
})
|
|
270
|
+
],
|
|
271
|
+
balanceItems: [],
|
|
272
|
+
deleteRegistrationIds: []
|
|
273
|
+
}),
|
|
274
|
+
administrationFee: 0,
|
|
275
|
+
freeContribution: 0,
|
|
276
|
+
paymentMethod: PaymentMethod.Payconiq,
|
|
277
|
+
redirectUrl: new URL("https://www.example.com"),
|
|
278
|
+
cancelUrl: new URL("https://www.example.com"),
|
|
279
|
+
totalPrice: 5,
|
|
280
|
+
customer: null,
|
|
281
|
+
});
|
|
282
|
+
//#endregion
|
|
283
|
+
|
|
284
|
+
//#region act and assert
|
|
285
|
+
|
|
286
|
+
// update occupancy to be sure occupancy is 1
|
|
287
|
+
await group1.updateOccupancy();
|
|
288
|
+
expect(group1.settings.registeredMembers).toBe(1);
|
|
289
|
+
|
|
290
|
+
await expect(async () => await post(body)).rejects.toThrow("Not allowed to move registrations");
|
|
291
|
+
//#endregion
|
|
292
|
+
})
|
|
293
|
+
})
|
|
294
|
+
|
|
295
|
+
describe('Register member with delete registration', () => {
|
|
296
|
+
|
|
297
|
+
test("Should update registered members", async () => {
|
|
298
|
+
//#region arrange
|
|
299
|
+
const registration = await new RegistrationFactory({
|
|
300
|
+
member,
|
|
301
|
+
group: group1,
|
|
302
|
+
groupPrice: groupPrice1
|
|
303
|
+
})
|
|
304
|
+
.create();
|
|
305
|
+
|
|
306
|
+
const group = await new GroupFactory({
|
|
307
|
+
organization,
|
|
308
|
+
price: 30,
|
|
309
|
+
stock: 5
|
|
310
|
+
}).create();
|
|
311
|
+
|
|
312
|
+
const groupPrice = group.settings.prices[0];
|
|
313
|
+
|
|
314
|
+
const body = IDRegisterCheckout.create({
|
|
315
|
+
cart: IDRegisterCart.create({
|
|
316
|
+
items: [
|
|
317
|
+
IDRegisterItem.create({
|
|
318
|
+
id: uuidv4(),
|
|
319
|
+
replaceRegistrationIds: [],
|
|
320
|
+
options: [],
|
|
321
|
+
groupPrice,
|
|
322
|
+
organizationId: organization.id,
|
|
323
|
+
groupId: group.id,
|
|
324
|
+
memberId: member.id
|
|
325
|
+
})
|
|
326
|
+
],
|
|
327
|
+
balanceItems: [],
|
|
328
|
+
deleteRegistrationIds: [registration.id]
|
|
329
|
+
}),
|
|
330
|
+
administrationFee: 0,
|
|
331
|
+
freeContribution: 0,
|
|
332
|
+
paymentMethod: PaymentMethod.PointOfSale,
|
|
333
|
+
totalPrice: 5,
|
|
334
|
+
asOrganizationId: organization.id,
|
|
335
|
+
customer: null,
|
|
336
|
+
});
|
|
337
|
+
//#endregion
|
|
338
|
+
|
|
339
|
+
//#region act and assert
|
|
340
|
+
|
|
341
|
+
// update occupancy to be sure occupancy is 1
|
|
342
|
+
await group1.updateOccupancy();
|
|
343
|
+
expect(group1.settings.registeredMembers).toBe(1);
|
|
344
|
+
|
|
345
|
+
// send request and check occupancy
|
|
346
|
+
const response = await post(body);
|
|
347
|
+
|
|
348
|
+
expect(response.body).toBeDefined();
|
|
349
|
+
expect(response.body.registrations.length).toBe(1);
|
|
350
|
+
|
|
351
|
+
const updatedGroup = await Group.getByID(group.id);
|
|
352
|
+
expect(updatedGroup!.settings.registeredMembers).toBe(1);
|
|
353
|
+
expect(updatedGroup!.settings.reservedMembers).toBe(0);
|
|
354
|
+
|
|
355
|
+
const updatedGroup1After = await Group.getByID(group1.id);
|
|
356
|
+
// occupancy should go from 1 to 0 because the registration should be deleted
|
|
357
|
+
expect(updatedGroup1After!.settings.registeredMembers).toBe(0);
|
|
358
|
+
expect(updatedGroup1After!.settings.reservedMembers).toBe(0);
|
|
359
|
+
//#endregion
|
|
360
|
+
})
|
|
361
|
+
|
|
362
|
+
test("Should throw error if with payment", async () => {
|
|
363
|
+
//#region arrange
|
|
364
|
+
const registration = await new RegistrationFactory({
|
|
365
|
+
member,
|
|
366
|
+
group: group1,
|
|
367
|
+
groupPrice: groupPrice1
|
|
368
|
+
})
|
|
369
|
+
.create();
|
|
370
|
+
|
|
371
|
+
const group = await new GroupFactory({
|
|
372
|
+
organization,
|
|
373
|
+
price: 30,
|
|
374
|
+
stock: 5,
|
|
375
|
+
maxMembers: 1
|
|
376
|
+
}).create();
|
|
377
|
+
|
|
378
|
+
const groupPrice = group.settings.prices[0];
|
|
379
|
+
|
|
380
|
+
const body = IDRegisterCheckout.create({
|
|
381
|
+
cart: IDRegisterCart.create({
|
|
382
|
+
items: [
|
|
383
|
+
IDRegisterItem.create({
|
|
384
|
+
id: uuidv4(),
|
|
385
|
+
replaceRegistrationIds: [],
|
|
386
|
+
options: [],
|
|
387
|
+
groupPrice,
|
|
388
|
+
organizationId: organization.id,
|
|
389
|
+
groupId: group.id,
|
|
390
|
+
memberId: member.id
|
|
391
|
+
})
|
|
392
|
+
],
|
|
393
|
+
balanceItems: [],
|
|
394
|
+
deleteRegistrationIds: [registration.id]
|
|
395
|
+
}),
|
|
396
|
+
administrationFee: 0,
|
|
397
|
+
freeContribution: 0,
|
|
398
|
+
paymentMethod: PaymentMethod.Payconiq,
|
|
399
|
+
redirectUrl: new URL("https://www.example.com"),
|
|
400
|
+
cancelUrl: new URL("https://www.example.com"),
|
|
401
|
+
totalPrice: 5,
|
|
402
|
+
customer: null,
|
|
403
|
+
});
|
|
404
|
+
//#endregion
|
|
405
|
+
|
|
406
|
+
//#region act and assert
|
|
407
|
+
|
|
408
|
+
// update occupancy to be sure occupancy is 1
|
|
409
|
+
await group1.updateOccupancy();
|
|
410
|
+
expect(group1.settings.registeredMembers).toBe(1);
|
|
411
|
+
|
|
412
|
+
await expect(async () => await post(body)).rejects.toThrow("Permission denied: you are not allowed to delete registrations");
|
|
413
|
+
//#endregion
|
|
414
|
+
})
|
|
415
|
+
})
|
|
416
|
+
|
|
417
|
+
it('Register member that is already registered should throw error', async () => {
|
|
418
|
+
// create existing registration
|
|
419
|
+
await new RegistrationFactory({
|
|
420
|
+
member,
|
|
421
|
+
group: group1,
|
|
422
|
+
groupPrice: groupPrice1
|
|
423
|
+
})
|
|
424
|
+
.create();
|
|
425
|
+
|
|
426
|
+
// register again
|
|
427
|
+
const body = IDRegisterCheckout.create({
|
|
428
|
+
cart: IDRegisterCart.create({
|
|
429
|
+
items: [
|
|
430
|
+
IDRegisterItem.create({
|
|
431
|
+
id: uuidv4(),
|
|
432
|
+
replaceRegistrationIds: [],
|
|
433
|
+
options: [],
|
|
434
|
+
groupPrice: groupPrice1,
|
|
435
|
+
organizationId: organization.id,
|
|
436
|
+
groupId: group1.id,
|
|
437
|
+
memberId: member.id
|
|
438
|
+
})
|
|
439
|
+
],
|
|
440
|
+
balanceItems: [],
|
|
441
|
+
deleteRegistrationIds: []
|
|
442
|
+
}),
|
|
443
|
+
administrationFee: 0,
|
|
444
|
+
freeContribution: 0,
|
|
445
|
+
paymentMethod: PaymentMethod.PointOfSale,
|
|
446
|
+
totalPrice: groupPrice1.price.price,
|
|
447
|
+
asOrganizationId: organization.id,
|
|
448
|
+
customer: null,
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
await expect(async () => await post(body)).rejects.toThrow("Already registered");
|
|
452
|
+
})
|
|
453
|
+
})
|
|
@@ -37,12 +37,22 @@ export class PatchOrganizationEndpoint extends Endpoint<Params, Query, Body, Res
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
async handle(request: DecodedRequest<Params, Query, Body>) {
|
|
40
|
-
const organization = await Context.setOrganizationScope();
|
|
41
|
-
|
|
40
|
+
const organization = await Context.setOrganizationScope({allowInactive: true});
|
|
41
|
+
await Context.authenticate()
|
|
42
42
|
|
|
43
43
|
if (!await Context.auth.hasSomeAccess(organization.id)) {
|
|
44
44
|
throw Context.auth.error()
|
|
45
45
|
}
|
|
46
|
+
|
|
47
|
+
if (!organization.active && !Context.auth.hasPlatformFullAccess()) {
|
|
48
|
+
throw new SimpleError({
|
|
49
|
+
code: "permission_denied",
|
|
50
|
+
message: "You do not have permissions to edit an inactive organization",
|
|
51
|
+
human: 'Je hebt geen toegangsrechten om een inactieve groep te bewerken',
|
|
52
|
+
statusCode: 403
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
}
|
|
46
56
|
|
|
47
57
|
// check if organization ID matches
|
|
48
58
|
if (request.body.id !== organization.id) {
|
|
@@ -295,6 +305,13 @@ export class PatchOrganizationEndpoint extends Endpoint<Params, Query, Body, Res
|
|
|
295
305
|
}
|
|
296
306
|
}
|
|
297
307
|
|
|
308
|
+
if (request.body.active !== undefined) {
|
|
309
|
+
if (!Context.auth.hasPlatformFullAccess()) {
|
|
310
|
+
throw Context.auth.error('Enkel een platform hoofdbeheerder kan een groep (in)actief maken')
|
|
311
|
+
}
|
|
312
|
+
organization.active = request.body.active;
|
|
313
|
+
}
|
|
314
|
+
|
|
298
315
|
if (request.body.uri) {
|
|
299
316
|
if (!Context.auth.hasPlatformFullAccess()) {
|
|
300
317
|
throw Context.auth.error()
|
|
@@ -46,7 +46,7 @@ export class ExchangePaymentEndpoint extends Endpoint<Params, Query, Body, Respo
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
async handle(request: DecodedRequest<Params, Query, Body>) {
|
|
49
|
-
const organization = await Context.
|
|
49
|
+
const organization = await Context.setOptionalOrganizationScope()
|
|
50
50
|
if (!request.query.exchange) {
|
|
51
51
|
await Context.authenticate()
|
|
52
52
|
}
|
|
@@ -152,7 +152,7 @@ export class ExchangePaymentEndpoint extends Endpoint<Params, Query, Body, Respo
|
|
|
152
152
|
/**
|
|
153
153
|
* ID of payment is needed because of race conditions (need to fetch payment in a race condition save queue)
|
|
154
154
|
*/
|
|
155
|
-
static async pollStatus(paymentId: string,
|
|
155
|
+
static async pollStatus(paymentId: string, org: Organization|null, cancel = false): Promise<Payment | undefined> {
|
|
156
156
|
// Prevent polling the same payment multiple times at the same time: create a queue to prevent races
|
|
157
157
|
QueueHandler.cancel("payments/"+paymentId); // Prevent creating more than one queue item for the same payment
|
|
158
158
|
return await QueueHandler.schedule("payments/"+paymentId, async () => {
|
|
@@ -162,6 +162,17 @@ export class ExchangePaymentEndpoint extends Endpoint<Params, Query, Body, Respo
|
|
|
162
162
|
return
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
+
if (!payment.organizationId) {
|
|
166
|
+
console.error('Payment without organization not supported', payment.id)
|
|
167
|
+
return
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const organization = org ?? await Organization.getByID(payment.organizationId)
|
|
171
|
+
if (!organization) {
|
|
172
|
+
console.error('Organization not found for payment', payment.id)
|
|
173
|
+
return
|
|
174
|
+
}
|
|
175
|
+
|
|
165
176
|
const testMode = organization.privateMeta.useTestPayments ?? STAMHOOFD.environment != 'production'
|
|
166
177
|
|
|
167
178
|
if (payment.status == PaymentStatus.Pending || payment.status == PaymentStatus.Created || (payment.provider === PaymentProvider.Buckaroo && payment.status == PaymentStatus.Failed)) {
|
|
@@ -26,7 +26,7 @@ export class GetPaymentEndpoint extends Endpoint<Params, Query, Body, ResponseBo
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
async handle(request: DecodedRequest<Params, Query, Body>) {
|
|
29
|
-
await Context.
|
|
29
|
+
await Context.setOptionalOrganizationScope()
|
|
30
30
|
await Context.authenticate()
|
|
31
31
|
|
|
32
32
|
const payment = await Payment.getByID(request.params.id);
|