@stamhoofd/backend 2.73.3 → 2.75.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/index.ts +7 -2
- package/package.json +13 -13
- package/src/audit-logs/MemberPlatformMembershipLogger.ts +1 -1
- package/src/crons/update-cached-balances.ts +1 -2
- package/src/endpoints/admin/organizations/GetOrganizationsEndpoint.ts +2 -2
- package/src/endpoints/auth/CreateAdminEndpoint.ts +4 -15
- package/src/endpoints/global/audit-logs/GetAuditLogsEndpoint.ts +2 -2
- package/src/endpoints/global/events/GetEventNotificationsCountEndpoint.ts +43 -0
- package/src/endpoints/global/events/GetEventNotificationsEndpoint.ts +181 -0
- package/src/endpoints/global/events/GetEventsEndpoint.ts +2 -2
- package/src/endpoints/global/events/PatchEventNotificationsEndpoint.ts +288 -0
- package/src/endpoints/global/events/PatchEventsEndpoint.ts +2 -2
- package/src/endpoints/global/files/UploadFile.ts +56 -4
- package/src/endpoints/global/files/UploadImage.ts +9 -3
- package/src/endpoints/global/members/GetMembersEndpoint.ts +2 -2
- package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.ts +14 -5
- package/src/endpoints/global/platform/GetPlatformAdminsEndpoint.ts +1 -5
- package/src/endpoints/global/platform/PatchPlatformEnpoint.ts +7 -0
- package/src/endpoints/global/registration/GetUserDocumentsEndpoint.ts +1 -1
- package/src/endpoints/global/registration/RegisterMembersEndpoint.test.ts +1756 -164
- package/src/endpoints/global/registration/RegisterMembersEndpoint.ts +2 -2
- package/src/endpoints/global/registration-periods/PatchRegistrationPeriodsEndpoint.ts +48 -2
- package/src/endpoints/organization/dashboard/documents/GetDocumentsEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/payments/GetPaymentsEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/receivable-balances/GetReceivableBalancesEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.ts +8 -0
- package/src/endpoints/organization/dashboard/users/GetOrganizationAdminsEndpoint.ts +3 -3
- package/src/endpoints/organization/dashboard/webshops/GetWebshopOrdersEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/webshops/GetWebshopTicketsEndpoint.ts +2 -2
- package/src/endpoints/organization/webshops/PlaceOrderEndpoint.ts +1 -2
- package/src/helpers/AdminPermissionChecker.ts +80 -2
- package/src/helpers/AuthenticatedStructures.ts +88 -2
- package/src/helpers/FlagMomentCleanup.ts +1 -8
- package/src/helpers/GlobalHelper.ts +15 -0
- package/src/helpers/MembershipCharger.ts +2 -1
- package/src/seeds-temporary/README.md +1 -0
- package/src/services/EventNotificationService.ts +201 -0
- package/src/services/FileSignService.ts +227 -0
- package/src/services/PlatformMembershipService.ts +38 -14
- package/src/sql-filters/event-notifications.ts +39 -0
- package/src/sql-filters/organizations.ts +1 -1
- package/src/sql-sorters/event-notifications.ts +96 -0
- package/src/sql-sorters/events.ts +2 -2
- package/src/sql-sorters/organizations.ts +2 -2
- package/tests/e2e/private-files.test.ts +497 -0
- package/tests/e2e/register.test.ts +762 -0
- package/tests/helpers/TestServer.ts +3 -0
- package/tests/jest.setup.ts +15 -2
- package/tsconfig.json +1 -0
- /package/src/{seeds → seeds-temporary}/1732117645-move-rrn.ts +0 -0
- /package/src/{seeds → seeds-temporary}/1736266448-recall-balance-item-price-paid.ts +0 -0
|
@@ -0,0 +1,762 @@
|
|
|
1
|
+
import { Request } from '@simonbackx/simple-endpoints';
|
|
2
|
+
import { BalanceItemFactory, GroupFactory, MemberFactory, MemberWithRegistrations, Organization, OrganizationFactory, OrganizationRegistrationPeriod, Platform, RegistrationFactory, RegistrationPeriod, RegistrationPeriodFactory, Token, UserFactory } from '@stamhoofd/models';
|
|
3
|
+
import { AdministrationFeeSettings, BalanceItemCartItem, BalanceItemType, DefaultAgeGroup, FreeContributionSettings, GroupOption, GroupOptionMenu, IDRegisterCart, IDRegisterCheckout, IDRegisterItem, PaymentMethod, PermissionLevel, Permissions, PlatformMembershipType, PlatformMembershipTypeConfig, ReceivableBalanceType, ReduceablePrice, RegisterItemOption, Version } from '@stamhoofd/structures';
|
|
4
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
5
|
+
import { GetMemberFamilyEndpoint } from '../../src/endpoints/global/members/GetMemberFamilyEndpoint';
|
|
6
|
+
import { RegisterMembersEndpoint } from '../../src/endpoints/global/registration/RegisterMembersEndpoint';
|
|
7
|
+
import { GetMemberBalanceEndpoint } from '../../src/endpoints/organization/dashboard/payments/GetMemberBalanceEndpoint';
|
|
8
|
+
import { GetReceivableBalanceEndpoint } from '../../src/endpoints/organization/dashboard/receivable-balances/GetReceivableBalanceEndpoint';
|
|
9
|
+
import { testServer } from '../helpers/TestServer';
|
|
10
|
+
|
|
11
|
+
describe('E2E.Register', () => {
|
|
12
|
+
// #region global
|
|
13
|
+
const registerEndpoint = new RegisterMembersEndpoint();
|
|
14
|
+
const memberBalanceEndpoint = new GetMemberBalanceEndpoint();
|
|
15
|
+
const receivableBalancesEndpoint = new GetReceivableBalanceEndpoint();
|
|
16
|
+
const getMemberFamilyEndpoint = new GetMemberFamilyEndpoint();
|
|
17
|
+
|
|
18
|
+
let period: RegistrationPeriod;
|
|
19
|
+
|
|
20
|
+
// #region helpers
|
|
21
|
+
const register = async (body: IDRegisterCheckout, organization: Organization, token: Token) => {
|
|
22
|
+
const request = Request.buildJson('POST', `/v${Version}/members/register`, organization.getApiHost(), body);
|
|
23
|
+
request.headers.authorization = 'Bearer ' + token.accessToken;
|
|
24
|
+
return await testServer.test(registerEndpoint, request);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const getBalance = async (memberId: string, organization: Organization, token: Token) => {
|
|
28
|
+
const request = Request.buildJson('GET', `/v${Version}/organization/members/${memberId}/balance`, organization.getApiHost());
|
|
29
|
+
request.headers.authorization = 'Bearer ' + token.accessToken;
|
|
30
|
+
return await testServer.test(memberBalanceEndpoint, request);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const getReceivableBalance = async (type: ReceivableBalanceType, id: string, organization: Organization, token: Token) => {
|
|
34
|
+
const request = Request.buildJson('GET', `/v${Version}/receivable-balances/${type}/${id}`, organization.getApiHost());
|
|
35
|
+
request.headers.authorization = 'Bearer ' + token.accessToken;
|
|
36
|
+
return await testServer.test(receivableBalancesEndpoint, request);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const getMemberFamily = async (memberId: string, organization: Organization, token: Token) => {
|
|
40
|
+
const request = Request.buildJson('GET', `/v${Version}/organization/members/${memberId}/family`, organization.getApiHost());
|
|
41
|
+
request.headers.authorization = 'Bearer ' + token.accessToken;
|
|
42
|
+
return await testServer.test(getMemberFamilyEndpoint, request);
|
|
43
|
+
};
|
|
44
|
+
// #endregion
|
|
45
|
+
|
|
46
|
+
// #endregion
|
|
47
|
+
|
|
48
|
+
beforeAll(async () => {
|
|
49
|
+
const previousPeriod = await new RegistrationPeriodFactory({
|
|
50
|
+
startDate: new Date(2022, 0, 1),
|
|
51
|
+
endDate: new Date(2022, 11, 31),
|
|
52
|
+
}).create();
|
|
53
|
+
|
|
54
|
+
period = await new RegistrationPeriodFactory({
|
|
55
|
+
startDate: new Date(2023, 0, 1),
|
|
56
|
+
endDate: new Date(2023, 11, 31),
|
|
57
|
+
}).create();
|
|
58
|
+
|
|
59
|
+
period.previousPeriodId = previousPeriod.id;
|
|
60
|
+
await period.save();
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
const initData = async ({ otherMemberAmount = 0, permissionLevel = PermissionLevel.Full }: { otherMemberAmount?: number; permissionLevel?: PermissionLevel } = {}) => {
|
|
64
|
+
const organization = await new OrganizationFactory({ period })
|
|
65
|
+
.create();
|
|
66
|
+
|
|
67
|
+
const user = await new UserFactory({
|
|
68
|
+
organization,
|
|
69
|
+
permissions: Permissions.create({
|
|
70
|
+
level: permissionLevel,
|
|
71
|
+
}),
|
|
72
|
+
})
|
|
73
|
+
.create();
|
|
74
|
+
|
|
75
|
+
const token = await Token.createToken(user);
|
|
76
|
+
|
|
77
|
+
const member = await new MemberFactory({ organization, user })
|
|
78
|
+
.create();
|
|
79
|
+
|
|
80
|
+
const otherMembers: MemberWithRegistrations[] = [];
|
|
81
|
+
|
|
82
|
+
for (let i = 0; i < otherMemberAmount; i++) {
|
|
83
|
+
otherMembers.push(await new MemberFactory({ organization, user })
|
|
84
|
+
.create());
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const group = await new GroupFactory({
|
|
88
|
+
organization,
|
|
89
|
+
price: 25,
|
|
90
|
+
stock: 5,
|
|
91
|
+
})
|
|
92
|
+
.create();
|
|
93
|
+
|
|
94
|
+
const groupPrice = group.settings.prices[0];
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
organization,
|
|
98
|
+
user,
|
|
99
|
+
token,
|
|
100
|
+
member,
|
|
101
|
+
otherMembers,
|
|
102
|
+
group,
|
|
103
|
+
groupPrice,
|
|
104
|
+
};
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
beforeEach(async () => {
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
describe('Register', () => {
|
|
111
|
+
test('Register by member should create balance for member', async () => {
|
|
112
|
+
// #region arrange
|
|
113
|
+
const { organization, group, groupPrice, token, member } = await initData();
|
|
114
|
+
|
|
115
|
+
const body = IDRegisterCheckout.create({
|
|
116
|
+
cart: IDRegisterCart.create({
|
|
117
|
+
items: [
|
|
118
|
+
IDRegisterItem.create({
|
|
119
|
+
id: uuidv4(),
|
|
120
|
+
replaceRegistrationIds: [],
|
|
121
|
+
options: [],
|
|
122
|
+
groupPrice,
|
|
123
|
+
organizationId: organization.id,
|
|
124
|
+
groupId: group.id,
|
|
125
|
+
memberId: member.id,
|
|
126
|
+
}),
|
|
127
|
+
],
|
|
128
|
+
balanceItems: [
|
|
129
|
+
],
|
|
130
|
+
deleteRegistrationIds: [],
|
|
131
|
+
}),
|
|
132
|
+
administrationFee: 0,
|
|
133
|
+
freeContribution: 0,
|
|
134
|
+
paymentMethod: PaymentMethod.PointOfSale,
|
|
135
|
+
totalPrice: 25,
|
|
136
|
+
customer: null,
|
|
137
|
+
});
|
|
138
|
+
// #endregion
|
|
139
|
+
|
|
140
|
+
// #region act and assert
|
|
141
|
+
const balanceBefore = await getBalance(member.id, organization, token);
|
|
142
|
+
expect(balanceBefore).toBeDefined();
|
|
143
|
+
expect(balanceBefore.body.length).toBe(0);
|
|
144
|
+
|
|
145
|
+
await register(body, organization, token);
|
|
146
|
+
|
|
147
|
+
const balance = await getBalance(member.id, organization, token);
|
|
148
|
+
expect(balance).toBeDefined();
|
|
149
|
+
expect(balance.body.length).toBe(1);
|
|
150
|
+
expect(balance.body[0].price).toBe(25);
|
|
151
|
+
expect(balance.body[0].pricePaid).toBe(0);
|
|
152
|
+
// #endregion
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// todo: test max option + allowAmount
|
|
156
|
+
// todo: test stock?
|
|
157
|
+
// todo: test reduced price?
|
|
158
|
+
|
|
159
|
+
test('Should create balance items for options', async () => {
|
|
160
|
+
// #region arrange
|
|
161
|
+
const { organization, group, groupPrice, token, member } = await initData();
|
|
162
|
+
|
|
163
|
+
const option1 = GroupOption.create({
|
|
164
|
+
name: 'option 1',
|
|
165
|
+
price: ReduceablePrice.create({
|
|
166
|
+
price: 5,
|
|
167
|
+
reducedPrice: 3,
|
|
168
|
+
}),
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
const option2 = GroupOption.create({
|
|
172
|
+
name: 'option 2',
|
|
173
|
+
price: ReduceablePrice.create({
|
|
174
|
+
price: 3,
|
|
175
|
+
reducedPrice: 1,
|
|
176
|
+
}),
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
const optionMenu = GroupOptionMenu.create({
|
|
180
|
+
name: 'option menu 1',
|
|
181
|
+
multipleChoice: true,
|
|
182
|
+
options: [option1, option2],
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
group.settings.optionMenus = [
|
|
186
|
+
optionMenu,
|
|
187
|
+
];
|
|
188
|
+
|
|
189
|
+
await group.save();
|
|
190
|
+
|
|
191
|
+
const body = IDRegisterCheckout.create({
|
|
192
|
+
cart: IDRegisterCart.create({
|
|
193
|
+
items: [
|
|
194
|
+
IDRegisterItem.create({
|
|
195
|
+
id: uuidv4(),
|
|
196
|
+
replaceRegistrationIds: [],
|
|
197
|
+
options: [
|
|
198
|
+
RegisterItemOption.create({
|
|
199
|
+
option: option1,
|
|
200
|
+
amount: 2,
|
|
201
|
+
optionMenu,
|
|
202
|
+
}),
|
|
203
|
+
RegisterItemOption.create({
|
|
204
|
+
option: option2,
|
|
205
|
+
amount: 5,
|
|
206
|
+
optionMenu,
|
|
207
|
+
}),
|
|
208
|
+
],
|
|
209
|
+
groupPrice,
|
|
210
|
+
organizationId: organization.id,
|
|
211
|
+
groupId: group.id,
|
|
212
|
+
memberId: member.id,
|
|
213
|
+
}),
|
|
214
|
+
],
|
|
215
|
+
balanceItems: [
|
|
216
|
+
],
|
|
217
|
+
deleteRegistrationIds: [],
|
|
218
|
+
}),
|
|
219
|
+
administrationFee: 0,
|
|
220
|
+
freeContribution: 0,
|
|
221
|
+
paymentMethod: PaymentMethod.PointOfSale,
|
|
222
|
+
totalPrice: 50,
|
|
223
|
+
customer: null,
|
|
224
|
+
});
|
|
225
|
+
// #endregion
|
|
226
|
+
|
|
227
|
+
// #region act and assert
|
|
228
|
+
const balanceBefore = await getBalance(member.id, organization, token);
|
|
229
|
+
expect(balanceBefore).toBeDefined();
|
|
230
|
+
expect(balanceBefore.body.length).toBe(0);
|
|
231
|
+
|
|
232
|
+
await register(body, organization, token);
|
|
233
|
+
|
|
234
|
+
const balance = await getBalance(member.id, organization, token);
|
|
235
|
+
expect(balance).toBeDefined();
|
|
236
|
+
expect(balance.body.length).toBe(3);
|
|
237
|
+
expect(balance.body).toEqual(expect.arrayContaining([
|
|
238
|
+
expect.objectContaining({
|
|
239
|
+
price: 25,
|
|
240
|
+
pricePaid: 0,
|
|
241
|
+
}),
|
|
242
|
+
expect.objectContaining({
|
|
243
|
+
price: 15,
|
|
244
|
+
pricePaid: 0,
|
|
245
|
+
}),
|
|
246
|
+
expect.objectContaining({
|
|
247
|
+
price: 10,
|
|
248
|
+
pricePaid: 0,
|
|
249
|
+
}),
|
|
250
|
+
]));
|
|
251
|
+
// #endregion
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
test('Should reset free contribution if no options on organization', async () => {
|
|
255
|
+
// #region arrange
|
|
256
|
+
const { organization, group, groupPrice, token, member, user } = await initData();
|
|
257
|
+
|
|
258
|
+
const body = IDRegisterCheckout.create({
|
|
259
|
+
cart: IDRegisterCart.create({
|
|
260
|
+
items: [
|
|
261
|
+
IDRegisterItem.create({
|
|
262
|
+
id: uuidv4(),
|
|
263
|
+
replaceRegistrationIds: [],
|
|
264
|
+
options: [],
|
|
265
|
+
groupPrice,
|
|
266
|
+
organizationId: organization.id,
|
|
267
|
+
groupId: group.id,
|
|
268
|
+
memberId: member.id,
|
|
269
|
+
}),
|
|
270
|
+
],
|
|
271
|
+
balanceItems: [
|
|
272
|
+
],
|
|
273
|
+
deleteRegistrationIds: [],
|
|
274
|
+
}),
|
|
275
|
+
administrationFee: 0,
|
|
276
|
+
freeContribution: 31,
|
|
277
|
+
paymentMethod: PaymentMethod.PointOfSale,
|
|
278
|
+
totalPrice: 25,
|
|
279
|
+
customer: null,
|
|
280
|
+
});
|
|
281
|
+
// #endregion
|
|
282
|
+
|
|
283
|
+
// #region act and assert
|
|
284
|
+
const receivableBalanceBefore = await getReceivableBalance(ReceivableBalanceType.user, user.id, organization, token);
|
|
285
|
+
expect(receivableBalanceBefore).toBeDefined();
|
|
286
|
+
expect(receivableBalanceBefore.body.balanceItems.length).toBe(0);
|
|
287
|
+
expect(receivableBalanceBefore.body.amountOpen).toBe(0);
|
|
288
|
+
|
|
289
|
+
await register(body, organization, token);
|
|
290
|
+
|
|
291
|
+
const receivableBalanceAfter = await getReceivableBalance(ReceivableBalanceType.user, user.id, organization, token);
|
|
292
|
+
expect(receivableBalanceAfter).toBeDefined();
|
|
293
|
+
expect(receivableBalanceAfter.body.balanceItems.length).toBe(1);
|
|
294
|
+
expect(receivableBalanceAfter.body.amountOpen).toBe(0);
|
|
295
|
+
expect(receivableBalanceAfter.body.balanceItems).toEqual(expect.arrayContaining([
|
|
296
|
+
expect.objectContaining({
|
|
297
|
+
price: 25,
|
|
298
|
+
pricePaid: 0,
|
|
299
|
+
}),
|
|
300
|
+
]));
|
|
301
|
+
// #endregion
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
test('Should create balance item for free contribution', async () => {
|
|
305
|
+
// #region arrange
|
|
306
|
+
const { organization, group, groupPrice, token, member, user } = await initData();
|
|
307
|
+
|
|
308
|
+
organization.meta.recordsConfiguration.freeContribution = FreeContributionSettings.create({
|
|
309
|
+
description: 'free contribution settings',
|
|
310
|
+
amounts: [30, 20],
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
await organization.save();
|
|
314
|
+
|
|
315
|
+
const body = IDRegisterCheckout.create({
|
|
316
|
+
cart: IDRegisterCart.create({
|
|
317
|
+
items: [
|
|
318
|
+
IDRegisterItem.create({
|
|
319
|
+
id: uuidv4(),
|
|
320
|
+
replaceRegistrationIds: [],
|
|
321
|
+
options: [],
|
|
322
|
+
groupPrice,
|
|
323
|
+
organizationId: organization.id,
|
|
324
|
+
groupId: group.id,
|
|
325
|
+
memberId: member.id,
|
|
326
|
+
}),
|
|
327
|
+
],
|
|
328
|
+
balanceItems: [
|
|
329
|
+
],
|
|
330
|
+
deleteRegistrationIds: [],
|
|
331
|
+
}),
|
|
332
|
+
administrationFee: 0,
|
|
333
|
+
freeContribution: 30,
|
|
334
|
+
paymentMethod: PaymentMethod.PointOfSale,
|
|
335
|
+
totalPrice: 55,
|
|
336
|
+
customer: null,
|
|
337
|
+
});
|
|
338
|
+
// #endregion
|
|
339
|
+
|
|
340
|
+
// #region act and assert
|
|
341
|
+
const receivableBalanceBefore = await getReceivableBalance(ReceivableBalanceType.user, user.id, organization, token);
|
|
342
|
+
expect(receivableBalanceBefore).toBeDefined();
|
|
343
|
+
expect(receivableBalanceBefore.body.balanceItems.length).toBe(0);
|
|
344
|
+
expect(receivableBalanceBefore.body.amountOpen).toBe(0);
|
|
345
|
+
|
|
346
|
+
await register(body, organization, token);
|
|
347
|
+
|
|
348
|
+
const receivableBalanceAfter = await getReceivableBalance(ReceivableBalanceType.user, user.id, organization, token);
|
|
349
|
+
expect(receivableBalanceAfter).toBeDefined();
|
|
350
|
+
expect(receivableBalanceAfter.body.balanceItems.length).toBe(2);
|
|
351
|
+
expect(receivableBalanceAfter.body.amountPending).toBe(55);
|
|
352
|
+
|
|
353
|
+
expect(receivableBalanceAfter.body.balanceItems).toEqual(expect.arrayContaining([
|
|
354
|
+
expect.objectContaining({
|
|
355
|
+
price: 30,
|
|
356
|
+
pricePaid: 0,
|
|
357
|
+
type: BalanceItemType.FreeContribution,
|
|
358
|
+
}),
|
|
359
|
+
expect.objectContaining({
|
|
360
|
+
price: 25,
|
|
361
|
+
pricePaid: 0,
|
|
362
|
+
}),
|
|
363
|
+
]));
|
|
364
|
+
// #endregion
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
test('Should create balance item for free administration fee if register by member', async () => {
|
|
368
|
+
// #region arrange
|
|
369
|
+
const { organization, group, groupPrice, token, member, user } = await initData();
|
|
370
|
+
|
|
371
|
+
organization.meta.registrationPaymentConfiguration.administrationFee = AdministrationFeeSettings.create({
|
|
372
|
+
fixed: 33,
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
await organization.save();
|
|
376
|
+
|
|
377
|
+
const body = IDRegisterCheckout.create({
|
|
378
|
+
cart: IDRegisterCart.create({
|
|
379
|
+
items: [
|
|
380
|
+
IDRegisterItem.create({
|
|
381
|
+
id: uuidv4(),
|
|
382
|
+
replaceRegistrationIds: [],
|
|
383
|
+
options: [],
|
|
384
|
+
groupPrice,
|
|
385
|
+
organizationId: organization.id,
|
|
386
|
+
groupId: group.id,
|
|
387
|
+
memberId: member.id,
|
|
388
|
+
}),
|
|
389
|
+
],
|
|
390
|
+
balanceItems: [
|
|
391
|
+
],
|
|
392
|
+
deleteRegistrationIds: [],
|
|
393
|
+
}),
|
|
394
|
+
administrationFee: 33,
|
|
395
|
+
freeContribution: 0,
|
|
396
|
+
paymentMethod: PaymentMethod.PointOfSale,
|
|
397
|
+
totalPrice: 58,
|
|
398
|
+
});
|
|
399
|
+
// #endregion
|
|
400
|
+
|
|
401
|
+
// #region act and assert
|
|
402
|
+
const receivableBalanceBefore = await getReceivableBalance(ReceivableBalanceType.user, user.id, organization
|
|
403
|
+
, token);
|
|
404
|
+
expect(receivableBalanceBefore).toBeDefined();
|
|
405
|
+
expect(receivableBalanceBefore.body.balanceItems.length).toBe(0);
|
|
406
|
+
expect(receivableBalanceBefore.body.amountOpen).toBe(0);
|
|
407
|
+
|
|
408
|
+
await register(body, organization, token);
|
|
409
|
+
|
|
410
|
+
const receivableBalanceAfter = await getReceivableBalance(ReceivableBalanceType.user, user.id, organization, token);
|
|
411
|
+
expect(receivableBalanceAfter).toBeDefined();
|
|
412
|
+
expect(receivableBalanceAfter.body.balanceItems.length).toBe(2);
|
|
413
|
+
expect(receivableBalanceAfter.body.amountPending).toBe(58);
|
|
414
|
+
|
|
415
|
+
expect(receivableBalanceAfter.body.balanceItems).toEqual(expect.arrayContaining([
|
|
416
|
+
expect.objectContaining({
|
|
417
|
+
price: 25,
|
|
418
|
+
pricePaid: 0,
|
|
419
|
+
type: BalanceItemType.Registration,
|
|
420
|
+
}),
|
|
421
|
+
expect.objectContaining({
|
|
422
|
+
price: 33,
|
|
423
|
+
pricePaid: 0,
|
|
424
|
+
type: BalanceItemType.AdministrationFee,
|
|
425
|
+
}),
|
|
426
|
+
]));
|
|
427
|
+
// #endregion
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
test('Should create balance item for cart item', async () => {
|
|
431
|
+
// #region arrange
|
|
432
|
+
const { organization, group, groupPrice, token, member, user } = await initData();
|
|
433
|
+
|
|
434
|
+
const balanceItem1 = await new BalanceItemFactory({
|
|
435
|
+
organizationId: organization.id,
|
|
436
|
+
memberId: member.id,
|
|
437
|
+
userId: user.id,
|
|
438
|
+
payingOrganizationId: organization.id,
|
|
439
|
+
type: BalanceItemType.Registration,
|
|
440
|
+
amount: 10,
|
|
441
|
+
unitPrice: 2,
|
|
442
|
+
}).create();
|
|
443
|
+
|
|
444
|
+
const cartItem = BalanceItemCartItem.create({
|
|
445
|
+
item: balanceItem1.getStructure(),
|
|
446
|
+
price: 10,
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
const body = IDRegisterCheckout.create({
|
|
450
|
+
cart: IDRegisterCart.create({
|
|
451
|
+
items: [
|
|
452
|
+
IDRegisterItem.create({
|
|
453
|
+
id: uuidv4(),
|
|
454
|
+
replaceRegistrationIds: [],
|
|
455
|
+
options: [],
|
|
456
|
+
groupPrice,
|
|
457
|
+
organizationId: organization.id,
|
|
458
|
+
groupId: group.id,
|
|
459
|
+
memberId: member.id,
|
|
460
|
+
}),
|
|
461
|
+
],
|
|
462
|
+
balanceItems: [
|
|
463
|
+
cartItem,
|
|
464
|
+
],
|
|
465
|
+
deleteRegistrationIds: [],
|
|
466
|
+
}),
|
|
467
|
+
administrationFee: 0,
|
|
468
|
+
freeContribution: 0,
|
|
469
|
+
paymentMethod: PaymentMethod.PointOfSale,
|
|
470
|
+
totalPrice: 35,
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
// #endregion
|
|
474
|
+
|
|
475
|
+
// #region act and assert
|
|
476
|
+
const response = await register(body, organization, token);
|
|
477
|
+
expect(response.body.registrations.length).toBe(1);
|
|
478
|
+
|
|
479
|
+
const receivableBalanceAfter = await getReceivableBalance(ReceivableBalanceType.user, user.id, organization, token);
|
|
480
|
+
|
|
481
|
+
expect(receivableBalanceAfter.body.balanceItems.length).toBe(2);
|
|
482
|
+
expect(receivableBalanceAfter.body.amountPending).toBe(35);
|
|
483
|
+
expect(receivableBalanceAfter.body.amountOpen).toBe(10);
|
|
484
|
+
|
|
485
|
+
expect(receivableBalanceAfter.body.balanceItems).toEqual(expect.arrayContaining([
|
|
486
|
+
expect.objectContaining({
|
|
487
|
+
pricePending: 10,
|
|
488
|
+
}),
|
|
489
|
+
expect.objectContaining({
|
|
490
|
+
pricePending: 25,
|
|
491
|
+
}),
|
|
492
|
+
]));
|
|
493
|
+
// #endregion
|
|
494
|
+
});
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
describe('Delete registrations', () => {
|
|
498
|
+
// todo: should include call to other endpoints?
|
|
499
|
+
test.skip('Should create negative balance items', async () => {
|
|
500
|
+
// #region arrange
|
|
501
|
+
const { member, group: group1, groupPrice: groupPrice1, organization, token } = await initData();
|
|
502
|
+
|
|
503
|
+
const registration = await new RegistrationFactory({
|
|
504
|
+
member,
|
|
505
|
+
group: group1,
|
|
506
|
+
groupPrice: groupPrice1,
|
|
507
|
+
}).create();
|
|
508
|
+
|
|
509
|
+
const group = await new GroupFactory({
|
|
510
|
+
organization,
|
|
511
|
+
price: 30,
|
|
512
|
+
stock: 5,
|
|
513
|
+
}).create();
|
|
514
|
+
|
|
515
|
+
const groupPrice = group.settings.prices[0];
|
|
516
|
+
|
|
517
|
+
const body = IDRegisterCheckout.create({
|
|
518
|
+
cart: IDRegisterCart.create({
|
|
519
|
+
items: [
|
|
520
|
+
IDRegisterItem.create({
|
|
521
|
+
id: uuidv4(),
|
|
522
|
+
replaceRegistrationIds: [],
|
|
523
|
+
options: [],
|
|
524
|
+
groupPrice,
|
|
525
|
+
organizationId: organization.id,
|
|
526
|
+
groupId: group.id,
|
|
527
|
+
memberId: member.id,
|
|
528
|
+
}),
|
|
529
|
+
],
|
|
530
|
+
balanceItems: [],
|
|
531
|
+
deleteRegistrationIds: [registration.id],
|
|
532
|
+
}),
|
|
533
|
+
administrationFee: 0,
|
|
534
|
+
freeContribution: 0,
|
|
535
|
+
paymentMethod: PaymentMethod.PointOfSale,
|
|
536
|
+
totalPrice: 5,
|
|
537
|
+
asOrganizationId: organization.id,
|
|
538
|
+
customer: null,
|
|
539
|
+
});
|
|
540
|
+
// #endregion
|
|
541
|
+
|
|
542
|
+
// #region act and assert
|
|
543
|
+
const response = await register(body, organization, token);
|
|
544
|
+
|
|
545
|
+
throw new Error('not implemented');
|
|
546
|
+
// #endregion
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
test.skip('Should apply cancelation fee', async () => {
|
|
550
|
+
throw new Error('Not implemented');
|
|
551
|
+
});
|
|
552
|
+
|
|
553
|
+
test.skip('Should fail if invalid cancelation fee', async () => {
|
|
554
|
+
throw new Error('Not implemented');
|
|
555
|
+
});
|
|
556
|
+
});
|
|
557
|
+
|
|
558
|
+
describe('Register for group with default age group', () => {
|
|
559
|
+
test('Should create membership', async () => {
|
|
560
|
+
// #region arrange
|
|
561
|
+
const date = new Date('2023-05-14');
|
|
562
|
+
jest.useFakeTimers().setSystemTime(date);
|
|
563
|
+
|
|
564
|
+
try {
|
|
565
|
+
const platformMembershipTypeConfig = PlatformMembershipTypeConfig.create({
|
|
566
|
+
startDate: period.startDate,
|
|
567
|
+
endDate: period.endDate,
|
|
568
|
+
});
|
|
569
|
+
|
|
570
|
+
const platformMembershipType = PlatformMembershipType.create({
|
|
571
|
+
name: 'werkjaar',
|
|
572
|
+
periods: new Map([
|
|
573
|
+
[period.id, platformMembershipTypeConfig],
|
|
574
|
+
]),
|
|
575
|
+
});
|
|
576
|
+
|
|
577
|
+
const platform = await Platform.getShared();
|
|
578
|
+
|
|
579
|
+
platform.config.membershipTypes = [
|
|
580
|
+
platformMembershipType,
|
|
581
|
+
];
|
|
582
|
+
|
|
583
|
+
const defaultAgeGroup = DefaultAgeGroup.create({
|
|
584
|
+
names: ['test groep'],
|
|
585
|
+
defaultMembershipTypeId: platformMembershipType.id,
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
platform.config.defaultAgeGroups = [defaultAgeGroup];
|
|
589
|
+
|
|
590
|
+
await platform.save();
|
|
591
|
+
|
|
592
|
+
const { member, group, groupPrice, organization, token } = await initData();
|
|
593
|
+
|
|
594
|
+
// todo: remove from initData
|
|
595
|
+
member.organizationId = null;
|
|
596
|
+
await member.save();
|
|
597
|
+
|
|
598
|
+
group.defaultAgeGroupId = defaultAgeGroup.id;
|
|
599
|
+
await group.save();
|
|
600
|
+
|
|
601
|
+
const organizationPeriod = new OrganizationRegistrationPeriod();
|
|
602
|
+
organizationPeriod.organizationId = organization.id;
|
|
603
|
+
organizationPeriod.periodId = period.id;
|
|
604
|
+
await organizationPeriod.save();
|
|
605
|
+
|
|
606
|
+
const body = IDRegisterCheckout.create({
|
|
607
|
+
cart: IDRegisterCart.create({
|
|
608
|
+
items: [
|
|
609
|
+
IDRegisterItem.create({
|
|
610
|
+
id: uuidv4(),
|
|
611
|
+
replaceRegistrationIds: [],
|
|
612
|
+
options: [],
|
|
613
|
+
groupPrice: groupPrice,
|
|
614
|
+
organizationId: organization.id,
|
|
615
|
+
groupId: group.id,
|
|
616
|
+
memberId: member.id,
|
|
617
|
+
trial: false,
|
|
618
|
+
}),
|
|
619
|
+
],
|
|
620
|
+
balanceItems: [],
|
|
621
|
+
deleteRegistrationIds: [],
|
|
622
|
+
}),
|
|
623
|
+
administrationFee: 0,
|
|
624
|
+
freeContribution: 0,
|
|
625
|
+
paymentMethod: PaymentMethod.PointOfSale,
|
|
626
|
+
totalPrice: 25,
|
|
627
|
+
asOrganizationId: organization.id,
|
|
628
|
+
customer: null,
|
|
629
|
+
});
|
|
630
|
+
// #endregion
|
|
631
|
+
|
|
632
|
+
// act and assert
|
|
633
|
+
const familyBefore = await getMemberFamily(member.id, organization, token);
|
|
634
|
+
expect(familyBefore).toBeDefined();
|
|
635
|
+
expect(familyBefore.body.members.length).toBe(1);
|
|
636
|
+
expect(familyBefore.body.members[0]).toBeDefined();
|
|
637
|
+
expect(familyBefore.body.members[0].platformMemberships.length).toBe(0);
|
|
638
|
+
|
|
639
|
+
const response = await register(body, organization, token);
|
|
640
|
+
|
|
641
|
+
expect(response.body).toBeDefined();
|
|
642
|
+
expect(response.body.registrations.length).toBe(1);
|
|
643
|
+
|
|
644
|
+
const familyAfter = await getMemberFamily(member.id, organization, token);
|
|
645
|
+
expect(familyAfter).toBeDefined();
|
|
646
|
+
expect(familyAfter.body.members.length).toBe(1);
|
|
647
|
+
expect(familyAfter.body.members[0]).toBeDefined();
|
|
648
|
+
expect(familyAfter.body.members[0].platformMemberships.length).toBe(1);
|
|
649
|
+
expect(familyAfter.body.members[0].platformMemberships[0].membershipTypeId).toBe(platformMembershipType.id);
|
|
650
|
+
}
|
|
651
|
+
finally {
|
|
652
|
+
jest.useFakeTimers().resetAllMocks();
|
|
653
|
+
}
|
|
654
|
+
});
|
|
655
|
+
|
|
656
|
+
test('Should set trial until on membership if trial', async () => {
|
|
657
|
+
// #region arrange
|
|
658
|
+
const date = new Date('2023-05-14');
|
|
659
|
+
jest.useFakeTimers().setSystemTime(date);
|
|
660
|
+
|
|
661
|
+
try {
|
|
662
|
+
const platformMembershipTypeConfig = PlatformMembershipTypeConfig.create({
|
|
663
|
+
startDate: period.startDate,
|
|
664
|
+
endDate: period.endDate,
|
|
665
|
+
trialDays: 10,
|
|
666
|
+
});
|
|
667
|
+
|
|
668
|
+
const platformMembershipType = PlatformMembershipType.create({
|
|
669
|
+
name: 'werkjaar',
|
|
670
|
+
periods: new Map([
|
|
671
|
+
[period.id, platformMembershipTypeConfig],
|
|
672
|
+
]),
|
|
673
|
+
});
|
|
674
|
+
|
|
675
|
+
const platform = await Platform.getShared();
|
|
676
|
+
|
|
677
|
+
platform.config.membershipTypes = [
|
|
678
|
+
platformMembershipType,
|
|
679
|
+
];
|
|
680
|
+
|
|
681
|
+
const defaultAgeGroup = DefaultAgeGroup.create({
|
|
682
|
+
names: ['test groep'],
|
|
683
|
+
defaultMembershipTypeId: platformMembershipType.id,
|
|
684
|
+
});
|
|
685
|
+
|
|
686
|
+
platform.config.defaultAgeGroups = [defaultAgeGroup];
|
|
687
|
+
|
|
688
|
+
await platform.save();
|
|
689
|
+
|
|
690
|
+
const { member, group, groupPrice, organization, token } = await initData();
|
|
691
|
+
|
|
692
|
+
// todo: remove from initData
|
|
693
|
+
member.organizationId = null;
|
|
694
|
+
await member.save();
|
|
695
|
+
|
|
696
|
+
group.settings.trialDays = 5;
|
|
697
|
+
group.defaultAgeGroupId = defaultAgeGroup.id;
|
|
698
|
+
await group.save();
|
|
699
|
+
|
|
700
|
+
const organizationPeriod = new OrganizationRegistrationPeriod();
|
|
701
|
+
organizationPeriod.organizationId = organization.id;
|
|
702
|
+
organizationPeriod.periodId = period.id;
|
|
703
|
+
await organizationPeriod.save();
|
|
704
|
+
|
|
705
|
+
const body = IDRegisterCheckout.create({
|
|
706
|
+
cart: IDRegisterCart.create({
|
|
707
|
+
items: [
|
|
708
|
+
IDRegisterItem.create({
|
|
709
|
+
id: uuidv4(),
|
|
710
|
+
replaceRegistrationIds: [],
|
|
711
|
+
options: [],
|
|
712
|
+
groupPrice: groupPrice,
|
|
713
|
+
organizationId: organization.id,
|
|
714
|
+
groupId: group.id,
|
|
715
|
+
memberId: member.id,
|
|
716
|
+
trial: true,
|
|
717
|
+
}),
|
|
718
|
+
],
|
|
719
|
+
balanceItems: [],
|
|
720
|
+
deleteRegistrationIds: [],
|
|
721
|
+
}),
|
|
722
|
+
administrationFee: 0,
|
|
723
|
+
freeContribution: 0,
|
|
724
|
+
paymentMethod: PaymentMethod.PointOfSale,
|
|
725
|
+
totalPrice: 0,
|
|
726
|
+
asOrganizationId: organization.id,
|
|
727
|
+
customer: null,
|
|
728
|
+
});
|
|
729
|
+
// #endregion
|
|
730
|
+
|
|
731
|
+
// act and assert
|
|
732
|
+
const familyBefore = await getMemberFamily(member.id, organization, token);
|
|
733
|
+
expect(familyBefore).toBeDefined();
|
|
734
|
+
expect(familyBefore.body.members.length).toBe(1);
|
|
735
|
+
expect(familyBefore.body.members[0]).toBeDefined();
|
|
736
|
+
expect(familyBefore.body.members[0].platformMemberships.length).toBe(0);
|
|
737
|
+
|
|
738
|
+
const response = await register(body, organization, token);
|
|
739
|
+
|
|
740
|
+
expect(response.body).toBeDefined();
|
|
741
|
+
expect(response.body.registrations.length).toBe(1);
|
|
742
|
+
|
|
743
|
+
const familyAfter = await getMemberFamily(member.id, organization, token);
|
|
744
|
+
expect(familyAfter).toBeDefined();
|
|
745
|
+
expect(familyAfter.body.members.length).toBe(1);
|
|
746
|
+
expect(familyAfter.body.members[0]).toBeDefined();
|
|
747
|
+
expect(familyAfter.body.members[0].platformMemberships.length).toBe(1);
|
|
748
|
+
expect(familyAfter.body.members[0].platformMemberships[0].membershipTypeId).toBe(platformMembershipType.id);
|
|
749
|
+
|
|
750
|
+
const trialUntil = familyAfter.body.members[0].platformMemberships[0].trialUntil;
|
|
751
|
+
|
|
752
|
+
expect(trialUntil).not.toBeNull();
|
|
753
|
+
expect(trialUntil!.getFullYear()).toBe(2023);
|
|
754
|
+
expect(trialUntil!.getMonth()).toBe(4);
|
|
755
|
+
expect(trialUntil!.getDate()).toBe(24);
|
|
756
|
+
}
|
|
757
|
+
finally {
|
|
758
|
+
jest.useFakeTimers().resetAllMocks();
|
|
759
|
+
}
|
|
760
|
+
});
|
|
761
|
+
});
|
|
762
|
+
});
|