@stamhoofd/backend 2.105.0 → 2.106.1
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/crons.ts +39 -5
- package/src/endpoints/global/members/GetMembersEndpoint.test.ts +953 -47
- package/src/endpoints/global/members/GetMembersEndpoint.ts +1 -1
- package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.test.ts +142 -0
- package/src/endpoints/global/registration/GetRegistrationsEndpoint.ts +1 -1
- package/src/endpoints/global/registration/RegisterMembersEndpoint.test.ts +163 -8
- package/src/endpoints/global/registration/RegisterMembersEndpoint.ts +2 -0
- package/src/endpoints/organization/dashboard/billing/GetPackagesEndpoint.test.ts +108 -0
- package/src/endpoints/organization/dashboard/billing/GetPackagesEndpoint.ts +40 -0
- package/src/endpoints/organization/dashboard/webshops/PatchWebshopOrdersEndpoint.ts +8 -1
- package/src/endpoints/organization/webshops/PlaceOrderEndpoint.ts +8 -1
- package/src/helpers/AdminPermissionChecker.ts +30 -6
- package/src/helpers/AuthenticatedStructures.ts +2 -2
- package/src/helpers/MemberUserSyncer.test.ts +400 -1
- package/src/helpers/MemberUserSyncer.ts +15 -10
- package/src/helpers/ServiceFeeHelper.ts +63 -0
- package/src/helpers/StripeHelper.ts +7 -4
- package/src/helpers/StripePayoutChecker.ts +1 -1
- package/src/seeds/0000000001-development-user.ts +2 -2
- package/src/seeds/0000000004-single-organization.ts +60 -0
- package/src/seeds/1754560914-groups-prices.test.ts +3023 -0
- package/src/seeds/1754560914-groups-prices.ts +408 -0
- package/src/seeds/{1722344162-sync-member-users.ts → 1761665607-sync-member-users.ts} +1 -1
- package/src/sql-filters/members.ts +1 -1
- package/tests/init/initAdmin.ts +19 -5
- package/tests/init/initPermissionRole.ts +14 -4
- package/tests/init/initPlatformRecordCategory.ts +8 -0
|
@@ -0,0 +1,3023 @@
|
|
|
1
|
+
import { Group, GroupFactory, Organization, OrganizationFactory, OrganizationRegistrationPeriod, OrganizationRegistrationPeriodFactory, RegistrationPeriod, RegistrationPeriodFactory } from '@stamhoofd/models';
|
|
2
|
+
import { GroupCategory, GroupCategorySettings, GroupPriceDiscountType, GroupStatus, OldGroupPrice, OldGroupPrices, TranslatedString } from '@stamhoofd/structures';
|
|
3
|
+
import { migratePrices } from './1754560914-groups-prices';
|
|
4
|
+
|
|
5
|
+
describe('migration.migratePrices', () => {
|
|
6
|
+
/**
|
|
7
|
+
* Test case 1 description:
|
|
8
|
+
* An organization with 3 groups. Group 1 and 2 are in the same category. Only group 1 has prices set with a discount if family members are in the same category.
|
|
9
|
+
* The tests checks if the prices and bundle discounts for each group are migrated correctly. See each test for a more detailed description.
|
|
10
|
+
*/
|
|
11
|
+
describe('Case 1 - family members in category', () => {
|
|
12
|
+
let period: RegistrationPeriod;
|
|
13
|
+
let organization: Organization;
|
|
14
|
+
let organizationPeriod: OrganizationRegistrationPeriod;
|
|
15
|
+
let group1: Group;
|
|
16
|
+
let group2: Group;
|
|
17
|
+
let group3: Group;
|
|
18
|
+
|
|
19
|
+
beforeAll(async () => {
|
|
20
|
+
const startDate = new Date(2025, 0, 1);
|
|
21
|
+
const endDate = new Date(2025, 11, 31);
|
|
22
|
+
period = await new RegistrationPeriodFactory({ startDate, endDate }).create();
|
|
23
|
+
organization = await new OrganizationFactory({ period }).create();
|
|
24
|
+
period.organizationId = organization.id;
|
|
25
|
+
await period.save();
|
|
26
|
+
organizationPeriod = await new OrganizationRegistrationPeriodFactory({ organization, period }).create();
|
|
27
|
+
|
|
28
|
+
group1 = await new GroupFactory({ organization, period }).create();
|
|
29
|
+
group1.settings.prices = [];
|
|
30
|
+
group1.settings.name = new TranslatedString('group1');
|
|
31
|
+
|
|
32
|
+
// initial price with discount if family members in same category
|
|
33
|
+
const oldPrices1 = OldGroupPrices.create({
|
|
34
|
+
startDate: null,
|
|
35
|
+
sameMemberOnlyDiscount: false,
|
|
36
|
+
onlySameGroup: false,
|
|
37
|
+
prices: [
|
|
38
|
+
OldGroupPrice.create({
|
|
39
|
+
price: 30,
|
|
40
|
+
reducedPrice: 20,
|
|
41
|
+
}),
|
|
42
|
+
OldGroupPrice.create({
|
|
43
|
+
price: 25,
|
|
44
|
+
reducedPrice: 15,
|
|
45
|
+
}),
|
|
46
|
+
OldGroupPrice.create({
|
|
47
|
+
price: 20,
|
|
48
|
+
reducedPrice: 10,
|
|
49
|
+
}),
|
|
50
|
+
],
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// price with discount after startDate 2025-03-01 if family members in same category
|
|
54
|
+
const oldPrices2 = OldGroupPrices.create({
|
|
55
|
+
startDate: new Date(2025, 2, 1),
|
|
56
|
+
sameMemberOnlyDiscount: false,
|
|
57
|
+
onlySameGroup: false,
|
|
58
|
+
prices: [
|
|
59
|
+
OldGroupPrice.create({
|
|
60
|
+
price: 300,
|
|
61
|
+
reducedPrice: 200,
|
|
62
|
+
}),
|
|
63
|
+
OldGroupPrice.create({
|
|
64
|
+
price: 200,
|
|
65
|
+
reducedPrice: 100,
|
|
66
|
+
}),
|
|
67
|
+
],
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// add both prices with discounts to group 1
|
|
71
|
+
group1.settings.oldPrices = [oldPrices2, oldPrices1];
|
|
72
|
+
|
|
73
|
+
await group1.save();
|
|
74
|
+
|
|
75
|
+
group2 = await new GroupFactory({ organization, period }).create();
|
|
76
|
+
group2.settings.prices = [];
|
|
77
|
+
// do not set old prices for group 2 (should be set to 0 automatically in migration)
|
|
78
|
+
group2.settings.oldPrices = [];
|
|
79
|
+
group2.settings.name = new TranslatedString('group2');
|
|
80
|
+
await group2.save();
|
|
81
|
+
|
|
82
|
+
group3 = await new GroupFactory({ organization, period }).create();
|
|
83
|
+
group3.settings.prices = [];
|
|
84
|
+
// do not set old prices for group 3 (should be set to 0 automatically in migration)
|
|
85
|
+
group3.settings.oldPrices = [];
|
|
86
|
+
group3.settings.name = new TranslatedString('group3');
|
|
87
|
+
await group3.save();
|
|
88
|
+
|
|
89
|
+
// add group 1 and to 2 to same category, add group 3 to different category
|
|
90
|
+
organizationPeriod.settings.categories = [
|
|
91
|
+
GroupCategory.create({
|
|
92
|
+
settings: GroupCategorySettings.create({ name: 'category1' }),
|
|
93
|
+
groupIds: [group1.id, group2.id],
|
|
94
|
+
}),
|
|
95
|
+
GroupCategory.create({
|
|
96
|
+
settings: GroupCategorySettings.create({ name: 'category2' }),
|
|
97
|
+
groupIds: [group3.id],
|
|
98
|
+
}),
|
|
99
|
+
];
|
|
100
|
+
|
|
101
|
+
await organizationPeriod.save();
|
|
102
|
+
|
|
103
|
+
await migratePrices();
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
afterAll(async () => {
|
|
107
|
+
await group1.delete();
|
|
108
|
+
await group2.delete();
|
|
109
|
+
await group3.delete();
|
|
110
|
+
|
|
111
|
+
await organizationPeriod.delete();
|
|
112
|
+
period.organizationId = null;
|
|
113
|
+
await period.save();
|
|
114
|
+
|
|
115
|
+
await organization.delete();
|
|
116
|
+
await period.delete();
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test('organization period', async () => {
|
|
120
|
+
// check organization registration period
|
|
121
|
+
const orgPeriod = await OrganizationRegistrationPeriod.getByID(organizationPeriod.id);
|
|
122
|
+
|
|
123
|
+
// the organization period should have 1 bundle discount for family members in category 1
|
|
124
|
+
expect(orgPeriod!.settings.bundleDiscounts).toHaveLength(1);
|
|
125
|
+
|
|
126
|
+
expect(orgPeriod!.settings.bundleDiscounts).toEqual(
|
|
127
|
+
expect.arrayContaining([
|
|
128
|
+
expect.objectContaining({
|
|
129
|
+
// the discount should be for family members in category 1
|
|
130
|
+
countWholeFamily: true,
|
|
131
|
+
countPerGroup: false,
|
|
132
|
+
// should contain the differences for oldPrices1
|
|
133
|
+
discounts: expect.arrayContaining([
|
|
134
|
+
expect.objectContaining({
|
|
135
|
+
type: GroupPriceDiscountType.Fixed,
|
|
136
|
+
value: expect.objectContaining({
|
|
137
|
+
price: 5,
|
|
138
|
+
reducedPrice: 5,
|
|
139
|
+
}),
|
|
140
|
+
}),
|
|
141
|
+
expect.objectContaining({
|
|
142
|
+
type: GroupPriceDiscountType.Fixed,
|
|
143
|
+
value: expect.objectContaining({
|
|
144
|
+
price: 10,
|
|
145
|
+
reducedPrice: 10,
|
|
146
|
+
}),
|
|
147
|
+
}),
|
|
148
|
+
]) }),
|
|
149
|
+
]),
|
|
150
|
+
);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
test('group 1', async () => {
|
|
154
|
+
const g1 = await Group.getByID(group1.id);
|
|
155
|
+
|
|
156
|
+
// check prices (should be equal to old prices)
|
|
157
|
+
expect(g1!.settings.prices).toHaveLength(2);
|
|
158
|
+
expect(g1!.settings.prices[0].price.price).toBe(30);
|
|
159
|
+
expect(g1!.settings.prices[0].price.reducedPrice).toBe(20);
|
|
160
|
+
expect(g1!.settings.prices[1].price.price).toBe(300);
|
|
161
|
+
expect(g1!.settings.prices[1].price.reducedPrice).toBe(200);
|
|
162
|
+
|
|
163
|
+
// check bundle discounts (each price should have 1 bundle discount for family members in same category)
|
|
164
|
+
expect(g1!.settings.prices[0].bundleDiscounts.size).toBe(1);
|
|
165
|
+
expect(g1!.settings.prices[1].bundleDiscounts.size).toBe(1);
|
|
166
|
+
|
|
167
|
+
// custom discount for bundle discount of the first price should be null because the discounts are not different than the discounts for the bundle discount that is configured in the settings of the organization (which are the discounts for oldPrices1)
|
|
168
|
+
expect([...g1!.settings.prices[0].bundleDiscounts.values()]).toEqual(
|
|
169
|
+
expect.arrayContaining([
|
|
170
|
+
expect.objectContaining({ customDiscounts: null }),
|
|
171
|
+
]),
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
// custom discount for bundle discount of second price should not be null because the discounts are different than the bundle discount that is configured in the settings of the organization (which are the discounts for oldPrices1), it should contain the discount for oldPrices2
|
|
175
|
+
expect([...g1!.settings.prices[1].bundleDiscounts.values()]).toEqual(
|
|
176
|
+
expect.arrayContaining([
|
|
177
|
+
expect.objectContaining({ customDiscounts: expect.arrayContaining([
|
|
178
|
+
expect.objectContaining({
|
|
179
|
+
type: GroupPriceDiscountType.Fixed,
|
|
180
|
+
value: expect.objectContaining({
|
|
181
|
+
price: 100,
|
|
182
|
+
reducedPrice: 100,
|
|
183
|
+
}),
|
|
184
|
+
}),
|
|
185
|
+
]) }),
|
|
186
|
+
]),
|
|
187
|
+
);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
test('group 2', async () => {
|
|
191
|
+
// group 2
|
|
192
|
+
const g2 = await Group.getByID(group2.id);
|
|
193
|
+
|
|
194
|
+
// check prices (the price should be 0 because there were no old prices configured)
|
|
195
|
+
expect(g2!.settings.prices).toHaveLength(1);
|
|
196
|
+
expect(g2!.settings.prices[0].price.price).toBe(0);
|
|
197
|
+
expect(g2!.settings.prices[0].price.reducedPrice).toBeNull();
|
|
198
|
+
|
|
199
|
+
// check bundle discounts (each price should have 1 bundle discount for family members in same category)
|
|
200
|
+
expect(g2!.settings.prices[0].bundleDiscounts.size).toBe(1);
|
|
201
|
+
|
|
202
|
+
// The custom discounts should be 0 because there were no prices for the group. It should only be linked to the bundle discount because the discount for group 1 should be applied if a member inscribes for group 2.
|
|
203
|
+
expect([...g2!.settings.prices[0].bundleDiscounts.values()]).toEqual(
|
|
204
|
+
expect.arrayContaining([
|
|
205
|
+
expect.objectContaining({ customDiscounts: expect.arrayContaining([
|
|
206
|
+
expect.objectContaining({
|
|
207
|
+
type: GroupPriceDiscountType.Fixed,
|
|
208
|
+
value: expect.objectContaining({
|
|
209
|
+
price: 0,
|
|
210
|
+
reducedPrice: null,
|
|
211
|
+
}),
|
|
212
|
+
}),
|
|
213
|
+
]) }),
|
|
214
|
+
]),
|
|
215
|
+
);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
test('group 3', async () => {
|
|
219
|
+
// group 3
|
|
220
|
+
const g3 = await Group.getByID(group3.id);
|
|
221
|
+
|
|
222
|
+
// check prices (the price should be 0 because there were no old prices configured)
|
|
223
|
+
expect(g3!.settings.oldPrices).toHaveLength(0);
|
|
224
|
+
expect(g3!.settings.prices).toHaveLength(1);
|
|
225
|
+
expect(g3!.settings.prices[0].price.price).toBe(0);
|
|
226
|
+
expect(g3!.settings.prices[0].price.reducedPrice).toBeNull();
|
|
227
|
+
|
|
228
|
+
// check bundle discounts (should have no bundle discount because the group is in another category)
|
|
229
|
+
expect(g3!.settings.prices[0].bundleDiscounts.size).toBe(0);
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Test case 2 description:
|
|
235
|
+
* An organization with 3 groups. Group 1 and 2 are in the same category. Only group 1 has prices set with a discount if same members are in the same category.
|
|
236
|
+
* The tests checks if the prices and bundle discounts for each group are migrated correctly. See each test for a more detailed description.
|
|
237
|
+
*/
|
|
238
|
+
describe('Case 2 - same members in category', () => {
|
|
239
|
+
let period: RegistrationPeriod;
|
|
240
|
+
let organization: Organization;
|
|
241
|
+
let organizationPeriod: OrganizationRegistrationPeriod;
|
|
242
|
+
let group1: Group;
|
|
243
|
+
let group2: Group;
|
|
244
|
+
let group3: Group;
|
|
245
|
+
|
|
246
|
+
beforeAll(async () => {
|
|
247
|
+
const startDate = new Date(2025, 0, 1);
|
|
248
|
+
const endDate = new Date(2025, 11, 31);
|
|
249
|
+
period = await new RegistrationPeriodFactory({ startDate, endDate }).create();
|
|
250
|
+
organization = await new OrganizationFactory({ period }).create();
|
|
251
|
+
period.organizationId = organization.id;
|
|
252
|
+
await period.save();
|
|
253
|
+
organizationPeriod = await new OrganizationRegistrationPeriodFactory({ organization, period }).create();
|
|
254
|
+
|
|
255
|
+
group1 = await new GroupFactory({ organization, period }).create();
|
|
256
|
+
group1.settings.prices = [];
|
|
257
|
+
group1.settings.name = new TranslatedString('group1');
|
|
258
|
+
|
|
259
|
+
// initial price with discount if same members in same category
|
|
260
|
+
const oldPrices1 = OldGroupPrices.create({
|
|
261
|
+
startDate: null,
|
|
262
|
+
sameMemberOnlyDiscount: true,
|
|
263
|
+
onlySameGroup: false,
|
|
264
|
+
prices: [
|
|
265
|
+
OldGroupPrice.create({
|
|
266
|
+
price: 30,
|
|
267
|
+
reducedPrice: 20,
|
|
268
|
+
}),
|
|
269
|
+
OldGroupPrice.create({
|
|
270
|
+
price: 25,
|
|
271
|
+
reducedPrice: 15,
|
|
272
|
+
}),
|
|
273
|
+
OldGroupPrice.create({
|
|
274
|
+
price: 20,
|
|
275
|
+
reducedPrice: 10,
|
|
276
|
+
}),
|
|
277
|
+
],
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
// price with discount after startDate 2025-03-01 if same members in same category
|
|
281
|
+
const oldPrices2 = OldGroupPrices.create({
|
|
282
|
+
startDate: new Date(2025, 2, 1),
|
|
283
|
+
sameMemberOnlyDiscount: true,
|
|
284
|
+
onlySameGroup: false,
|
|
285
|
+
prices: [
|
|
286
|
+
OldGroupPrice.create({
|
|
287
|
+
price: 300,
|
|
288
|
+
reducedPrice: 200,
|
|
289
|
+
}),
|
|
290
|
+
OldGroupPrice.create({
|
|
291
|
+
price: 200,
|
|
292
|
+
reducedPrice: 100,
|
|
293
|
+
}),
|
|
294
|
+
],
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
group1.settings.oldPrices = [oldPrices2, oldPrices1];
|
|
298
|
+
|
|
299
|
+
await group1.save();
|
|
300
|
+
|
|
301
|
+
group2 = await new GroupFactory({ organization, period }).create();
|
|
302
|
+
group2.settings.prices = [];
|
|
303
|
+
group2.settings.oldPrices = [];
|
|
304
|
+
group2.settings.name = new TranslatedString('group2');
|
|
305
|
+
await group2.save();
|
|
306
|
+
|
|
307
|
+
group3 = await new GroupFactory({ organization, period }).create();
|
|
308
|
+
group3.settings.prices = [];
|
|
309
|
+
group3.settings.oldPrices = [];
|
|
310
|
+
group3.settings.name = new TranslatedString('group3');
|
|
311
|
+
await group3.save();
|
|
312
|
+
|
|
313
|
+
organizationPeriod.settings.categories = [
|
|
314
|
+
GroupCategory.create({
|
|
315
|
+
settings: GroupCategorySettings.create({ name: 'category1' }),
|
|
316
|
+
groupIds: [group1.id, group2.id],
|
|
317
|
+
}),
|
|
318
|
+
GroupCategory.create({
|
|
319
|
+
settings: GroupCategorySettings.create({ name: 'category2' }),
|
|
320
|
+
groupIds: [group3.id],
|
|
321
|
+
}),
|
|
322
|
+
];
|
|
323
|
+
|
|
324
|
+
await organizationPeriod.save();
|
|
325
|
+
|
|
326
|
+
await migratePrices();
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
afterAll(async () => {
|
|
330
|
+
await group1.delete();
|
|
331
|
+
await group2.delete();
|
|
332
|
+
await group3.delete();
|
|
333
|
+
|
|
334
|
+
await organizationPeriod.delete();
|
|
335
|
+
period.organizationId = null;
|
|
336
|
+
await period.save();
|
|
337
|
+
|
|
338
|
+
await organization.delete();
|
|
339
|
+
await period.delete();
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
test('organization period', async () => {
|
|
343
|
+
// check organization registration period
|
|
344
|
+
const orgPeriod = await OrganizationRegistrationPeriod.getByID(organizationPeriod.id);
|
|
345
|
+
|
|
346
|
+
// the organization period should have 1 bundle discount for same members in category 1
|
|
347
|
+
expect(orgPeriod!.settings.bundleDiscounts).toHaveLength(1);
|
|
348
|
+
|
|
349
|
+
expect(orgPeriod!.settings.bundleDiscounts).toEqual(
|
|
350
|
+
expect.arrayContaining([
|
|
351
|
+
expect.objectContaining({
|
|
352
|
+
// the discount should be for same members in category 1
|
|
353
|
+
countWholeFamily: false,
|
|
354
|
+
countPerGroup: false,
|
|
355
|
+
discounts: expect.arrayContaining([
|
|
356
|
+
// should contain the differences for oldPrices1
|
|
357
|
+
expect.objectContaining({
|
|
358
|
+
type: GroupPriceDiscountType.Fixed,
|
|
359
|
+
value: expect.objectContaining({
|
|
360
|
+
price: 5,
|
|
361
|
+
reducedPrice: 5,
|
|
362
|
+
}),
|
|
363
|
+
}),
|
|
364
|
+
expect.objectContaining({
|
|
365
|
+
type: GroupPriceDiscountType.Fixed,
|
|
366
|
+
value: expect.objectContaining({
|
|
367
|
+
price: 10,
|
|
368
|
+
reducedPrice: 10,
|
|
369
|
+
}),
|
|
370
|
+
}),
|
|
371
|
+
]) }),
|
|
372
|
+
]),
|
|
373
|
+
);
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
test('group 1', async () => {
|
|
377
|
+
const g1 = await Group.getByID(group1.id);
|
|
378
|
+
|
|
379
|
+
// check prices (should be equal to old prices)
|
|
380
|
+
expect(g1!.settings.prices).toHaveLength(2);
|
|
381
|
+
expect(g1!.settings.prices[0].price.price).toBe(30);
|
|
382
|
+
expect(g1!.settings.prices[0].price.reducedPrice).toBe(20);
|
|
383
|
+
expect(g1!.settings.prices[1].price.price).toBe(300);
|
|
384
|
+
expect(g1!.settings.prices[1].price.reducedPrice).toBe(200);
|
|
385
|
+
|
|
386
|
+
// check bundle discounts (each price should have 1 bundle discount for same members in same category)
|
|
387
|
+
expect(g1!.settings.prices[0].bundleDiscounts.size).toBe(1);
|
|
388
|
+
expect(g1!.settings.prices[1].bundleDiscounts.size).toBe(1);
|
|
389
|
+
|
|
390
|
+
// custom discount for bundle discount of the first price should be null because the discounts are not different than the discounts for the bundle discount that is configured in the settings of the organization (which are the discounts for oldPrices1)
|
|
391
|
+
expect([...g1!.settings.prices[0].bundleDiscounts.values()]).toEqual(
|
|
392
|
+
expect.arrayContaining([
|
|
393
|
+
expect.objectContaining({ customDiscounts: null }),
|
|
394
|
+
]),
|
|
395
|
+
);
|
|
396
|
+
|
|
397
|
+
// custom discount for bundle discount of second price should not be null because the discounts are different than the bundle discount that is configured in the settings of the organization (which are the discounts for oldPrices1), it should contain the discount for oldPrices2
|
|
398
|
+
expect([...g1!.settings.prices[1].bundleDiscounts.values()]).toEqual(
|
|
399
|
+
expect.arrayContaining([
|
|
400
|
+
expect.objectContaining({ customDiscounts: expect.arrayContaining([
|
|
401
|
+
expect.objectContaining({
|
|
402
|
+
type: GroupPriceDiscountType.Fixed,
|
|
403
|
+
value: expect.objectContaining({
|
|
404
|
+
price: 100,
|
|
405
|
+
reducedPrice: 100,
|
|
406
|
+
}),
|
|
407
|
+
}),
|
|
408
|
+
]) }),
|
|
409
|
+
]),
|
|
410
|
+
);
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
test('group 2', async () => {
|
|
414
|
+
// group 2
|
|
415
|
+
const g2 = await Group.getByID(group2.id);
|
|
416
|
+
|
|
417
|
+
// check prices (the price should be 0 because there were no old prices configured)
|
|
418
|
+
expect(g2!.settings.prices).toHaveLength(1);
|
|
419
|
+
expect(g2!.settings.prices[0].price.price).toBe(0);
|
|
420
|
+
expect(g2!.settings.prices[0].price.reducedPrice).toBeNull();
|
|
421
|
+
|
|
422
|
+
// check bundle discounts (each price should have 1 bundle discount for same members in same category)
|
|
423
|
+
expect(g2!.settings.prices[0].bundleDiscounts.size).toBe(1);
|
|
424
|
+
|
|
425
|
+
// The custom discounts should be 0 because there were no prices for the group. It should only be linked to the bundle discount because the discount for group 1 should be applied if a member inscribes for group 2.
|
|
426
|
+
expect([...g2!.settings.prices[0].bundleDiscounts.values()]).toEqual(
|
|
427
|
+
expect.arrayContaining([
|
|
428
|
+
expect.objectContaining({ customDiscounts: expect.arrayContaining([
|
|
429
|
+
expect.objectContaining({
|
|
430
|
+
type: GroupPriceDiscountType.Fixed,
|
|
431
|
+
value: expect.objectContaining({
|
|
432
|
+
price: 0,
|
|
433
|
+
reducedPrice: null,
|
|
434
|
+
}),
|
|
435
|
+
}),
|
|
436
|
+
]) }),
|
|
437
|
+
]),
|
|
438
|
+
);
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
test('group 3', async () => {
|
|
442
|
+
// group 3
|
|
443
|
+
const g3 = await Group.getByID(group3.id);
|
|
444
|
+
|
|
445
|
+
// check prices (the price should be 0 because there were no old prices configured)
|
|
446
|
+
expect(g3!.settings.oldPrices).toHaveLength(0);
|
|
447
|
+
expect(g3!.settings.prices).toHaveLength(1);
|
|
448
|
+
expect(g3!.settings.prices[0].price.price).toBe(0);
|
|
449
|
+
expect(g3!.settings.prices[0].price.reducedPrice).toBeNull();
|
|
450
|
+
|
|
451
|
+
// check bundle discounts (should have no bundle discount because the group is in another category)
|
|
452
|
+
expect(g3!.settings.prices[0].bundleDiscounts.size).toBe(0);
|
|
453
|
+
});
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* Test case 3 description:
|
|
458
|
+
* An organization with 3 groups. Group 1 and 2 are in the same category. Only group 1 has prices set with a discount if same members are in the same category and a discount if family members are in the same category (after 2025-03-01).
|
|
459
|
+
* The tests checks if the prices and bundle discounts for each group are migrated correctly. See each test for a more detailed description.
|
|
460
|
+
*/
|
|
461
|
+
describe('Case 3 - combination of family members in category and same members in category', () => {
|
|
462
|
+
let period: RegistrationPeriod;
|
|
463
|
+
let organization: Organization;
|
|
464
|
+
let organizationPeriod: OrganizationRegistrationPeriod;
|
|
465
|
+
let group1: Group;
|
|
466
|
+
let group2: Group;
|
|
467
|
+
let group3: Group;
|
|
468
|
+
|
|
469
|
+
beforeAll(async () => {
|
|
470
|
+
const startDate = new Date(2025, 0, 1);
|
|
471
|
+
const endDate = new Date(2025, 11, 31);
|
|
472
|
+
period = await new RegistrationPeriodFactory({ startDate, endDate }).create();
|
|
473
|
+
organization = await new OrganizationFactory({ period }).create();
|
|
474
|
+
period.organizationId = organization.id;
|
|
475
|
+
await period.save();
|
|
476
|
+
organizationPeriod = await new OrganizationRegistrationPeriodFactory({ organization, period }).create();
|
|
477
|
+
|
|
478
|
+
group1 = await new GroupFactory({ organization, period }).create();
|
|
479
|
+
group1.settings.prices = [];
|
|
480
|
+
group1.settings.name = new TranslatedString('group1');
|
|
481
|
+
|
|
482
|
+
// initial price with discount if same members in same category
|
|
483
|
+
const oldPrices1 = OldGroupPrices.create({
|
|
484
|
+
startDate: null,
|
|
485
|
+
sameMemberOnlyDiscount: true,
|
|
486
|
+
onlySameGroup: false,
|
|
487
|
+
prices: [
|
|
488
|
+
OldGroupPrice.create({
|
|
489
|
+
price: 30,
|
|
490
|
+
reducedPrice: 20,
|
|
491
|
+
}),
|
|
492
|
+
OldGroupPrice.create({
|
|
493
|
+
price: 25,
|
|
494
|
+
reducedPrice: 15,
|
|
495
|
+
}),
|
|
496
|
+
OldGroupPrice.create({
|
|
497
|
+
price: 20,
|
|
498
|
+
reducedPrice: 10,
|
|
499
|
+
}),
|
|
500
|
+
],
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
// price with discount after startDate 2025-03-01 if family members in same category
|
|
504
|
+
const oldPrices2 = OldGroupPrices.create({
|
|
505
|
+
startDate: new Date(2025, 2, 1),
|
|
506
|
+
sameMemberOnlyDiscount: false,
|
|
507
|
+
onlySameGroup: false,
|
|
508
|
+
prices: [
|
|
509
|
+
OldGroupPrice.create({
|
|
510
|
+
price: 300,
|
|
511
|
+
reducedPrice: 200,
|
|
512
|
+
}),
|
|
513
|
+
OldGroupPrice.create({
|
|
514
|
+
price: 200,
|
|
515
|
+
reducedPrice: 100,
|
|
516
|
+
}),
|
|
517
|
+
],
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
group1.settings.oldPrices = [oldPrices2, oldPrices1];
|
|
521
|
+
|
|
522
|
+
await group1.save();
|
|
523
|
+
|
|
524
|
+
group2 = await new GroupFactory({ organization, period }).create();
|
|
525
|
+
group2.settings.prices = [];
|
|
526
|
+
group2.settings.oldPrices = [];
|
|
527
|
+
group2.settings.name = new TranslatedString('group2');
|
|
528
|
+
await group2.save();
|
|
529
|
+
|
|
530
|
+
group3 = await new GroupFactory({ organization, period }).create();
|
|
531
|
+
group3.settings.prices = [];
|
|
532
|
+
group3.settings.oldPrices = [];
|
|
533
|
+
group3.settings.name = new TranslatedString('group3');
|
|
534
|
+
await group3.save();
|
|
535
|
+
|
|
536
|
+
organizationPeriod.settings.categories = [
|
|
537
|
+
GroupCategory.create({
|
|
538
|
+
settings: GroupCategorySettings.create({ name: 'category1' }),
|
|
539
|
+
groupIds: [group1.id, group2.id],
|
|
540
|
+
}),
|
|
541
|
+
GroupCategory.create({
|
|
542
|
+
settings: GroupCategorySettings.create({ name: 'category2' }),
|
|
543
|
+
groupIds: [group3.id],
|
|
544
|
+
}),
|
|
545
|
+
];
|
|
546
|
+
|
|
547
|
+
await organizationPeriod.save();
|
|
548
|
+
|
|
549
|
+
await migratePrices();
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
afterAll(async () => {
|
|
553
|
+
await group1.delete();
|
|
554
|
+
await group2.delete();
|
|
555
|
+
await group3.delete();
|
|
556
|
+
|
|
557
|
+
await organizationPeriod.delete();
|
|
558
|
+
period.organizationId = null;
|
|
559
|
+
await period.save();
|
|
560
|
+
|
|
561
|
+
await organization.delete();
|
|
562
|
+
await period.delete();
|
|
563
|
+
});
|
|
564
|
+
|
|
565
|
+
test('organization period', async () => {
|
|
566
|
+
// check organization registration period
|
|
567
|
+
const orgPeriod = await OrganizationRegistrationPeriod.getByID(organizationPeriod.id);
|
|
568
|
+
|
|
569
|
+
// the organization period should have 2 bundle discounts: 1 for family members in category 1 and 1 for same members in category 1
|
|
570
|
+
expect(orgPeriod!.settings.bundleDiscounts).toHaveLength(2);
|
|
571
|
+
|
|
572
|
+
expect(orgPeriod!.settings.bundleDiscounts).toEqual(
|
|
573
|
+
expect.arrayContaining([
|
|
574
|
+
expect.objectContaining({
|
|
575
|
+
// the discount should be for same members in category 1
|
|
576
|
+
countWholeFamily: false,
|
|
577
|
+
countPerGroup: false,
|
|
578
|
+
// should contain the differences for oldPrices1
|
|
579
|
+
discounts: expect.arrayContaining([
|
|
580
|
+
expect.objectContaining({
|
|
581
|
+
type: GroupPriceDiscountType.Fixed,
|
|
582
|
+
value: expect.objectContaining({
|
|
583
|
+
price: 5,
|
|
584
|
+
reducedPrice: 5,
|
|
585
|
+
}),
|
|
586
|
+
}),
|
|
587
|
+
expect.objectContaining({
|
|
588
|
+
type: GroupPriceDiscountType.Fixed,
|
|
589
|
+
value: expect.objectContaining({
|
|
590
|
+
price: 10,
|
|
591
|
+
reducedPrice: 10,
|
|
592
|
+
}),
|
|
593
|
+
}),
|
|
594
|
+
]) }),
|
|
595
|
+
expect.objectContaining({
|
|
596
|
+
// the discount should be for family members in category 1
|
|
597
|
+
countWholeFamily: true,
|
|
598
|
+
countPerGroup: false,
|
|
599
|
+
discounts: expect.arrayContaining([
|
|
600
|
+
// should contain the differences for oldPrices2
|
|
601
|
+
expect.objectContaining({
|
|
602
|
+
type: GroupPriceDiscountType.Fixed,
|
|
603
|
+
value: expect.objectContaining({
|
|
604
|
+
price: 100,
|
|
605
|
+
reducedPrice: 100,
|
|
606
|
+
}),
|
|
607
|
+
}),
|
|
608
|
+
]) }),
|
|
609
|
+
]),
|
|
610
|
+
);
|
|
611
|
+
});
|
|
612
|
+
|
|
613
|
+
test('group 1', async () => {
|
|
614
|
+
const g1 = await Group.getByID(group1.id);
|
|
615
|
+
|
|
616
|
+
// check prices (should be equal to old prices)
|
|
617
|
+
expect(g1!.settings.prices).toHaveLength(2);
|
|
618
|
+
expect(g1!.settings.prices[0].price.price).toBe(30);
|
|
619
|
+
expect(g1!.settings.prices[0].price.reducedPrice).toBe(20);
|
|
620
|
+
expect(g1!.settings.prices[1].price.price).toBe(300);
|
|
621
|
+
expect(g1!.settings.prices[1].price.reducedPrice).toBe(200);
|
|
622
|
+
|
|
623
|
+
// check bundle discounts
|
|
624
|
+
expect(g1!.settings.prices[0].bundleDiscounts.size).toBe(2);
|
|
625
|
+
expect([...g1!.settings.prices[0].bundleDiscounts.values()]).toEqual(
|
|
626
|
+
expect.arrayContaining([
|
|
627
|
+
// is default discount for category
|
|
628
|
+
expect.objectContaining({ customDiscounts: null }),
|
|
629
|
+
// is connected to family discount, but zero discount
|
|
630
|
+
expect.objectContaining({ customDiscounts: expect.arrayContaining([
|
|
631
|
+
expect.objectContaining({
|
|
632
|
+
type: GroupPriceDiscountType.Fixed,
|
|
633
|
+
value: expect.objectContaining({
|
|
634
|
+
price: 0,
|
|
635
|
+
reducedPrice: null,
|
|
636
|
+
}),
|
|
637
|
+
}),
|
|
638
|
+
]) }),
|
|
639
|
+
]),
|
|
640
|
+
);
|
|
641
|
+
expect(g1!.settings.prices[1].bundleDiscounts.size).toBe(2);
|
|
642
|
+
expect([...g1!.settings.prices[1].bundleDiscounts.values()]).toEqual(
|
|
643
|
+
expect.arrayContaining([
|
|
644
|
+
// is default discount for category
|
|
645
|
+
expect.objectContaining({ customDiscounts: null }),
|
|
646
|
+
// is connected to member only discount, but zero discount
|
|
647
|
+
expect.objectContaining({ customDiscounts: expect.arrayContaining([
|
|
648
|
+
expect.objectContaining({
|
|
649
|
+
type: GroupPriceDiscountType.Fixed,
|
|
650
|
+
value: expect.objectContaining({
|
|
651
|
+
price: 0,
|
|
652
|
+
reducedPrice: null,
|
|
653
|
+
}),
|
|
654
|
+
}),
|
|
655
|
+
]) }),
|
|
656
|
+
]),
|
|
657
|
+
);
|
|
658
|
+
});
|
|
659
|
+
|
|
660
|
+
test('group 2', async () => {
|
|
661
|
+
// group 2
|
|
662
|
+
const g2 = await Group.getByID(group2.id);
|
|
663
|
+
|
|
664
|
+
// check prices (the price should be 0 because there were no old prices configured)
|
|
665
|
+
expect(g2!.settings.prices).toHaveLength(1);
|
|
666
|
+
expect(g2!.settings.prices[0].price.price).toBe(0);
|
|
667
|
+
expect(g2!.settings.prices[0].price.reducedPrice).toBeNull();
|
|
668
|
+
|
|
669
|
+
// check bundle discounts (each price should have 2 bundle discounts for same members in same category and for family members in same category)
|
|
670
|
+
expect(g2!.settings.prices[0].bundleDiscounts.size).toBe(2);
|
|
671
|
+
|
|
672
|
+
// The custom discounts should be 0 because there were no prices for the group. It should only be linked to the 2 bundle discounts because the discount for group 1 should be applied if a member inscribes for group 2.
|
|
673
|
+
expect([...g2!.settings.prices[0].bundleDiscounts.values()]).toEqual(
|
|
674
|
+
expect.arrayContaining([
|
|
675
|
+
expect.objectContaining({ customDiscounts: expect.arrayContaining([
|
|
676
|
+
expect.objectContaining({
|
|
677
|
+
type: GroupPriceDiscountType.Fixed,
|
|
678
|
+
value: expect.objectContaining({
|
|
679
|
+
price: 0,
|
|
680
|
+
reducedPrice: null,
|
|
681
|
+
}),
|
|
682
|
+
}),
|
|
683
|
+
]) }),
|
|
684
|
+
expect.objectContaining({ customDiscounts: expect.arrayContaining([
|
|
685
|
+
expect.objectContaining({
|
|
686
|
+
type: GroupPriceDiscountType.Fixed,
|
|
687
|
+
value: expect.objectContaining({
|
|
688
|
+
price: 0,
|
|
689
|
+
reducedPrice: null,
|
|
690
|
+
}),
|
|
691
|
+
}),
|
|
692
|
+
]) }),
|
|
693
|
+
]),
|
|
694
|
+
);
|
|
695
|
+
});
|
|
696
|
+
|
|
697
|
+
test('group 3', async () => {
|
|
698
|
+
// group 3
|
|
699
|
+
const g3 = await Group.getByID(group3.id);
|
|
700
|
+
|
|
701
|
+
// check prices (the price should be 0 because there were no old prices configured)
|
|
702
|
+
expect(g3!.settings.oldPrices).toHaveLength(0);
|
|
703
|
+
expect(g3!.settings.prices).toHaveLength(1);
|
|
704
|
+
expect(g3!.settings.prices[0].price.price).toBe(0);
|
|
705
|
+
expect(g3!.settings.prices[0].price.reducedPrice).toBeNull();
|
|
706
|
+
|
|
707
|
+
// check bundle discounts (should have no bundle discount because the group is in another category)
|
|
708
|
+
expect(g3!.settings.prices[0].bundleDiscounts.size).toBe(0);
|
|
709
|
+
});
|
|
710
|
+
});
|
|
711
|
+
|
|
712
|
+
/**
|
|
713
|
+
* Test case 4 description:
|
|
714
|
+
* An organization with 4 groups. Group 1 and 2 are in the same category. Group 3 and 4 are also in the same category. Group 1, group 2 and group 3 have prices set (a combination of different discounts). Group 1 also has a discount only for the same group. Group 4 has no prices set.
|
|
715
|
+
* The tests checks if the prices and bundle discounts for each group are migrated correctly. See each test for a more detailed description.
|
|
716
|
+
*/
|
|
717
|
+
describe('Case 4 - combination of different discounts and multiple group categories', () => {
|
|
718
|
+
let period: RegistrationPeriod;
|
|
719
|
+
let organization: Organization;
|
|
720
|
+
let organizationPeriod: OrganizationRegistrationPeriod;
|
|
721
|
+
let group1: Group;
|
|
722
|
+
let group2: Group;
|
|
723
|
+
let group3: Group;
|
|
724
|
+
let group4: Group;
|
|
725
|
+
|
|
726
|
+
beforeAll(async () => {
|
|
727
|
+
const startDate = new Date(2025, 0, 1);
|
|
728
|
+
const endDate = new Date(2025, 11, 31);
|
|
729
|
+
period = await new RegistrationPeriodFactory({ startDate, endDate }).create();
|
|
730
|
+
organization = await new OrganizationFactory({ period }).create();
|
|
731
|
+
period.organizationId = organization.id;
|
|
732
|
+
await period.save();
|
|
733
|
+
organizationPeriod = await new OrganizationRegistrationPeriodFactory({ organization, period }).create();
|
|
734
|
+
|
|
735
|
+
group1 = await new GroupFactory({ organization, period }).create();
|
|
736
|
+
group1.settings.prices = [];
|
|
737
|
+
group1.settings.name = new TranslatedString('group1');
|
|
738
|
+
|
|
739
|
+
// price with discount if same members in same category
|
|
740
|
+
const oldPrices1 = OldGroupPrices.create({
|
|
741
|
+
startDate: null,
|
|
742
|
+
sameMemberOnlyDiscount: true,
|
|
743
|
+
onlySameGroup: false,
|
|
744
|
+
prices: [
|
|
745
|
+
OldGroupPrice.create({
|
|
746
|
+
price: 30,
|
|
747
|
+
reducedPrice: 20,
|
|
748
|
+
}),
|
|
749
|
+
OldGroupPrice.create({
|
|
750
|
+
price: 25,
|
|
751
|
+
reducedPrice: 15,
|
|
752
|
+
}),
|
|
753
|
+
OldGroupPrice.create({
|
|
754
|
+
price: 20,
|
|
755
|
+
reducedPrice: 10,
|
|
756
|
+
}),
|
|
757
|
+
],
|
|
758
|
+
});
|
|
759
|
+
|
|
760
|
+
// price with discount after startDate 2025-03-01 if family members in same category
|
|
761
|
+
const oldPrices2 = OldGroupPrices.create({
|
|
762
|
+
startDate: new Date(2025, 2, 1),
|
|
763
|
+
sameMemberOnlyDiscount: false,
|
|
764
|
+
onlySameGroup: false,
|
|
765
|
+
prices: [
|
|
766
|
+
OldGroupPrice.create({
|
|
767
|
+
price: 300,
|
|
768
|
+
reducedPrice: 200,
|
|
769
|
+
}),
|
|
770
|
+
OldGroupPrice.create({
|
|
771
|
+
price: 200,
|
|
772
|
+
reducedPrice: 100,
|
|
773
|
+
}),
|
|
774
|
+
],
|
|
775
|
+
});
|
|
776
|
+
|
|
777
|
+
// price with discount after startDate 2025-07-05 if same members in same GROUP
|
|
778
|
+
const oldPrices3 = OldGroupPrices.create({
|
|
779
|
+
startDate: new Date(2025, 6, 5),
|
|
780
|
+
sameMemberOnlyDiscount: true,
|
|
781
|
+
onlySameGroup: true,
|
|
782
|
+
prices: [
|
|
783
|
+
OldGroupPrice.create({
|
|
784
|
+
price: 32,
|
|
785
|
+
reducedPrice: 21,
|
|
786
|
+
}),
|
|
787
|
+
OldGroupPrice.create({
|
|
788
|
+
price: 26,
|
|
789
|
+
reducedPrice: 16,
|
|
790
|
+
}),
|
|
791
|
+
OldGroupPrice.create({
|
|
792
|
+
price: 21,
|
|
793
|
+
reducedPrice: 11,
|
|
794
|
+
}),
|
|
795
|
+
],
|
|
796
|
+
});
|
|
797
|
+
|
|
798
|
+
group1.settings.oldPrices = [oldPrices2, oldPrices1, oldPrices3];
|
|
799
|
+
|
|
800
|
+
await group1.save();
|
|
801
|
+
|
|
802
|
+
group2 = await new GroupFactory({ organization, period }).create();
|
|
803
|
+
group2.settings.prices = [];
|
|
804
|
+
|
|
805
|
+
// price with discount if family members in same category
|
|
806
|
+
const oldPrices_g2_1 = OldGroupPrices.create({
|
|
807
|
+
startDate: null,
|
|
808
|
+
sameMemberOnlyDiscount: false,
|
|
809
|
+
onlySameGroup: false,
|
|
810
|
+
prices: [
|
|
811
|
+
OldGroupPrice.create({
|
|
812
|
+
price: 32,
|
|
813
|
+
reducedPrice: 22,
|
|
814
|
+
}),
|
|
815
|
+
OldGroupPrice.create({
|
|
816
|
+
price: 27,
|
|
817
|
+
reducedPrice: 17,
|
|
818
|
+
}),
|
|
819
|
+
OldGroupPrice.create({
|
|
820
|
+
price: 22,
|
|
821
|
+
reducedPrice: 12,
|
|
822
|
+
}),
|
|
823
|
+
],
|
|
824
|
+
});
|
|
825
|
+
|
|
826
|
+
// price with discount after startDate 2025-08-02 if family members in same category
|
|
827
|
+
const oldPrices_g2_2 = OldGroupPrices.create({
|
|
828
|
+
startDate: new Date(2025, 7, 2),
|
|
829
|
+
sameMemberOnlyDiscount: false,
|
|
830
|
+
onlySameGroup: false,
|
|
831
|
+
prices: [
|
|
832
|
+
OldGroupPrice.create({
|
|
833
|
+
price: 33,
|
|
834
|
+
reducedPrice: 23,
|
|
835
|
+
}),
|
|
836
|
+
OldGroupPrice.create({
|
|
837
|
+
price: 23,
|
|
838
|
+
reducedPrice: 13,
|
|
839
|
+
}),
|
|
840
|
+
],
|
|
841
|
+
});
|
|
842
|
+
group2.settings.oldPrices = [oldPrices_g2_1, oldPrices_g2_2];
|
|
843
|
+
group2.settings.name = new TranslatedString('group2');
|
|
844
|
+
await group2.save();
|
|
845
|
+
|
|
846
|
+
group3 = await new GroupFactory({ organization, period }).create();
|
|
847
|
+
group3.settings.prices = [];
|
|
848
|
+
|
|
849
|
+
// price with discount if same members in same category
|
|
850
|
+
const oldPrices_g3_1 = OldGroupPrices.create({
|
|
851
|
+
startDate: null,
|
|
852
|
+
sameMemberOnlyDiscount: true,
|
|
853
|
+
onlySameGroup: false,
|
|
854
|
+
prices: [
|
|
855
|
+
OldGroupPrice.create({
|
|
856
|
+
price: 29,
|
|
857
|
+
reducedPrice: 19,
|
|
858
|
+
}),
|
|
859
|
+
OldGroupPrice.create({
|
|
860
|
+
price: 28,
|
|
861
|
+
reducedPrice: 18,
|
|
862
|
+
}),
|
|
863
|
+
],
|
|
864
|
+
});
|
|
865
|
+
group3.settings.oldPrices = [oldPrices_g3_1];
|
|
866
|
+
group3.settings.name = new TranslatedString('group3');
|
|
867
|
+
await group3.save();
|
|
868
|
+
|
|
869
|
+
group4 = await new GroupFactory({ organization, period }).create();
|
|
870
|
+
group4.settings.prices = [];
|
|
871
|
+
group4.settings.oldPrices = [];
|
|
872
|
+
group4.settings.name = new TranslatedString('group4');
|
|
873
|
+
await group4.save();
|
|
874
|
+
|
|
875
|
+
organizationPeriod.settings.categories = [
|
|
876
|
+
GroupCategory.create({
|
|
877
|
+
settings: GroupCategorySettings.create({ name: 'category1' }),
|
|
878
|
+
groupIds: [group1.id, group2.id],
|
|
879
|
+
}),
|
|
880
|
+
GroupCategory.create({
|
|
881
|
+
settings: GroupCategorySettings.create({ name: 'category2' }),
|
|
882
|
+
groupIds: [group3.id, group4.id],
|
|
883
|
+
}),
|
|
884
|
+
];
|
|
885
|
+
|
|
886
|
+
await organizationPeriod.save();
|
|
887
|
+
|
|
888
|
+
await migratePrices();
|
|
889
|
+
});
|
|
890
|
+
|
|
891
|
+
afterAll(async () => {
|
|
892
|
+
await group1.delete();
|
|
893
|
+
await group2.delete();
|
|
894
|
+
await group3.delete();
|
|
895
|
+
await group4.delete();
|
|
896
|
+
|
|
897
|
+
await organizationPeriod.delete();
|
|
898
|
+
period.organizationId = null;
|
|
899
|
+
await period.save();
|
|
900
|
+
|
|
901
|
+
await organization.delete();
|
|
902
|
+
await period.delete();
|
|
903
|
+
});
|
|
904
|
+
|
|
905
|
+
test('organization period', async () => {
|
|
906
|
+
// check organization registration period
|
|
907
|
+
const orgPeriod = await OrganizationRegistrationPeriod.getByID(organizationPeriod.id);
|
|
908
|
+
expect(orgPeriod!.settings.bundleDiscounts).toHaveLength(4);
|
|
909
|
+
|
|
910
|
+
// should have 4 different bundle discounts:
|
|
911
|
+
// - oldPrices1
|
|
912
|
+
// - oldPrices2
|
|
913
|
+
// - oldPrices3
|
|
914
|
+
// - oldPrices_g3_1
|
|
915
|
+
expect([...orgPeriod!.settings.bundleDiscounts.values()]).toEqual(
|
|
916
|
+
expect.arrayContaining([
|
|
917
|
+
expect.objectContaining({ countWholeFamily: false, countPerGroup: false,
|
|
918
|
+
// should contain discounts for oldPrices1
|
|
919
|
+
discounts: expect.arrayContaining([
|
|
920
|
+
expect.objectContaining({
|
|
921
|
+
type: GroupPriceDiscountType.Fixed,
|
|
922
|
+
value: expect.objectContaining({
|
|
923
|
+
price: 5,
|
|
924
|
+
reducedPrice: 5,
|
|
925
|
+
}),
|
|
926
|
+
}),
|
|
927
|
+
expect.objectContaining({
|
|
928
|
+
type: GroupPriceDiscountType.Fixed,
|
|
929
|
+
value: expect.objectContaining({
|
|
930
|
+
price: 10,
|
|
931
|
+
reducedPrice: 10,
|
|
932
|
+
}),
|
|
933
|
+
}),
|
|
934
|
+
]),
|
|
935
|
+
}),
|
|
936
|
+
expect.objectContaining({ countWholeFamily: true, countPerGroup: false,
|
|
937
|
+
// should contain discounts for oldPrices2
|
|
938
|
+
discounts: expect.arrayContaining([
|
|
939
|
+
expect.objectContaining({
|
|
940
|
+
type: GroupPriceDiscountType.Fixed,
|
|
941
|
+
value: expect.objectContaining({
|
|
942
|
+
price: 100,
|
|
943
|
+
reducedPrice: 100,
|
|
944
|
+
}),
|
|
945
|
+
}),
|
|
946
|
+
]),
|
|
947
|
+
}),
|
|
948
|
+
expect.objectContaining({ countWholeFamily: false, countPerGroup: true,
|
|
949
|
+
// should contain discounts for oldPrices3
|
|
950
|
+
discounts: expect.arrayContaining([
|
|
951
|
+
expect.objectContaining({
|
|
952
|
+
type: GroupPriceDiscountType.Fixed,
|
|
953
|
+
value: expect.objectContaining({
|
|
954
|
+
price: 6,
|
|
955
|
+
reducedPrice: 5,
|
|
956
|
+
}),
|
|
957
|
+
}),
|
|
958
|
+
expect.objectContaining({
|
|
959
|
+
type: GroupPriceDiscountType.Fixed,
|
|
960
|
+
value: expect.objectContaining({
|
|
961
|
+
price: 11,
|
|
962
|
+
reducedPrice: 10,
|
|
963
|
+
}),
|
|
964
|
+
}),
|
|
965
|
+
]),
|
|
966
|
+
}),
|
|
967
|
+
expect.objectContaining({ countWholeFamily: false, countPerGroup: false,
|
|
968
|
+
// should contain discounts for oldPrices_g3_1
|
|
969
|
+
discounts: expect.arrayContaining([
|
|
970
|
+
expect.objectContaining({
|
|
971
|
+
type: GroupPriceDiscountType.Fixed,
|
|
972
|
+
value: expect.objectContaining({
|
|
973
|
+
price: 1,
|
|
974
|
+
reducedPrice: 1,
|
|
975
|
+
}),
|
|
976
|
+
}),
|
|
977
|
+
]),
|
|
978
|
+
}),
|
|
979
|
+
// see test case 5 for a case where countWholeFamily is true and countPerGroup is true
|
|
980
|
+
]),
|
|
981
|
+
);
|
|
982
|
+
});
|
|
983
|
+
|
|
984
|
+
test('group 1', async () => {
|
|
985
|
+
const g1 = await Group.getByID(group1.id);
|
|
986
|
+
|
|
987
|
+
// check prices (should be equal to old prices)
|
|
988
|
+
expect(g1!.settings.prices).toHaveLength(3);
|
|
989
|
+
expect(g1!.settings.prices[0].price.price).toBe(30);
|
|
990
|
+
expect(g1!.settings.prices[0].price.reducedPrice).toBe(20);
|
|
991
|
+
expect(g1!.settings.prices[1].price.price).toBe(300);
|
|
992
|
+
expect(g1!.settings.prices[1].price.reducedPrice).toBe(200);
|
|
993
|
+
expect(g1!.settings.prices[2].price.price).toBe(32);
|
|
994
|
+
expect(g1!.settings.prices[2].price.reducedPrice).toBe(21);
|
|
995
|
+
|
|
996
|
+
// // check bundle discounts
|
|
997
|
+
expect(g1!.settings.prices[0].bundleDiscounts.size).toBe(2);
|
|
998
|
+
expect([...g1!.settings.prices[0].bundleDiscounts.values()]).toEqual(
|
|
999
|
+
expect.arrayContaining([
|
|
1000
|
+
// is default discount for category
|
|
1001
|
+
expect.objectContaining({ customDiscounts: null }),
|
|
1002
|
+
// is connected to family discount, but zero discount
|
|
1003
|
+
expect.objectContaining({ customDiscounts: expect.arrayContaining([
|
|
1004
|
+
expect.objectContaining({
|
|
1005
|
+
type: GroupPriceDiscountType.Fixed,
|
|
1006
|
+
value: expect.objectContaining({
|
|
1007
|
+
price: 0,
|
|
1008
|
+
reducedPrice: null,
|
|
1009
|
+
}),
|
|
1010
|
+
}),
|
|
1011
|
+
]) }),
|
|
1012
|
+
]),
|
|
1013
|
+
);
|
|
1014
|
+
expect(g1!.settings.prices[1].bundleDiscounts.size).toBe(2);
|
|
1015
|
+
expect([...g1!.settings.prices[1].bundleDiscounts.values()]).toEqual(
|
|
1016
|
+
expect.arrayContaining([
|
|
1017
|
+
// is default discount for category
|
|
1018
|
+
expect.objectContaining({ customDiscounts: null }),
|
|
1019
|
+
// is connected to member only discount, but zero discount
|
|
1020
|
+
expect.objectContaining({ customDiscounts: expect.arrayContaining([
|
|
1021
|
+
expect.objectContaining({
|
|
1022
|
+
type: GroupPriceDiscountType.Fixed,
|
|
1023
|
+
value: expect.objectContaining({
|
|
1024
|
+
price: 0,
|
|
1025
|
+
reducedPrice: null,
|
|
1026
|
+
}),
|
|
1027
|
+
}),
|
|
1028
|
+
]) }),
|
|
1029
|
+
]),
|
|
1030
|
+
);
|
|
1031
|
+
|
|
1032
|
+
expect(g1!.settings.prices[2].bundleDiscounts.size).toBe(3);
|
|
1033
|
+
expect([...g1!.settings.prices[2].bundleDiscounts.values()]).toEqual(
|
|
1034
|
+
expect.arrayContaining([
|
|
1035
|
+
// is default discount for category
|
|
1036
|
+
expect.objectContaining({ customDiscounts: null }),
|
|
1037
|
+
// is connected to member only discount, but zero discount
|
|
1038
|
+
expect.objectContaining({ customDiscounts: expect.arrayContaining([
|
|
1039
|
+
expect.objectContaining({
|
|
1040
|
+
type: GroupPriceDiscountType.Fixed,
|
|
1041
|
+
value: expect.objectContaining({
|
|
1042
|
+
price: 0,
|
|
1043
|
+
reducedPrice: null,
|
|
1044
|
+
}),
|
|
1045
|
+
}),
|
|
1046
|
+
]) }),
|
|
1047
|
+
// is default discount for member in group
|
|
1048
|
+
expect.objectContaining({ customDiscounts: null }),
|
|
1049
|
+
]),
|
|
1050
|
+
);
|
|
1051
|
+
});
|
|
1052
|
+
|
|
1053
|
+
test('group 2', async () => {
|
|
1054
|
+
// group 2
|
|
1055
|
+
const g2 = await Group.getByID(group2.id);
|
|
1056
|
+
|
|
1057
|
+
// check prices (should be equal to old prices)
|
|
1058
|
+
expect(g2!.settings.prices).toHaveLength(2);
|
|
1059
|
+
expect(g2!.settings.prices[0].price.price).toBe(32);
|
|
1060
|
+
expect(g2!.settings.prices[0].price.reducedPrice).toBe(22);
|
|
1061
|
+
expect(g2!.settings.prices[1].price.price).toBe(33);
|
|
1062
|
+
expect(g2!.settings.prices[1].price.reducedPrice).toBe(23);
|
|
1063
|
+
|
|
1064
|
+
// check bundle discounts (each price should have 2 bundle discounts for same members in same category and for family members in same category)
|
|
1065
|
+
expect(g2!.settings.prices[0].bundleDiscounts.size).toBe(2);
|
|
1066
|
+
|
|
1067
|
+
// There should be 2 custom discounts because the old prices of group 2 are different than the prices of the bundle discounts in the settings of the period.
|
|
1068
|
+
expect([...g2!.settings.prices[0].bundleDiscounts.values()]).toEqual(
|
|
1069
|
+
expect.arrayContaining([
|
|
1070
|
+
// is connected to family discount, but different discount than group 1
|
|
1071
|
+
expect.objectContaining({ customDiscounts: expect.arrayContaining([
|
|
1072
|
+
expect.objectContaining({
|
|
1073
|
+
type: GroupPriceDiscountType.Fixed,
|
|
1074
|
+
value: expect.objectContaining({
|
|
1075
|
+
price: 5,
|
|
1076
|
+
reducedPrice: 5,
|
|
1077
|
+
}),
|
|
1078
|
+
}),
|
|
1079
|
+
]) }),
|
|
1080
|
+
// is connected to family discount, but different discount than group 1
|
|
1081
|
+
expect.objectContaining({ customDiscounts: expect.arrayContaining([
|
|
1082
|
+
expect.objectContaining({
|
|
1083
|
+
type: GroupPriceDiscountType.Fixed,
|
|
1084
|
+
value: expect.objectContaining({
|
|
1085
|
+
price: 10,
|
|
1086
|
+
reducedPrice: 10,
|
|
1087
|
+
}),
|
|
1088
|
+
}),
|
|
1089
|
+
]) }),
|
|
1090
|
+
]),
|
|
1091
|
+
);
|
|
1092
|
+
});
|
|
1093
|
+
|
|
1094
|
+
test('group 3', async () => {
|
|
1095
|
+
// group 3
|
|
1096
|
+
const g3 = await Group.getByID(group3.id);
|
|
1097
|
+
|
|
1098
|
+
// check prices (should be equal to old prices)
|
|
1099
|
+
expect(g3!.settings.prices).toHaveLength(1);
|
|
1100
|
+
expect(g3!.settings.prices[0].price.price).toBe(29);
|
|
1101
|
+
expect(g3!.settings.prices[0].price.reducedPrice).toBe(19);
|
|
1102
|
+
|
|
1103
|
+
// check bundle discounts (should be only 1 bundle discount because the group is in another category and group 3 only has 1 discount configured in the old prices)
|
|
1104
|
+
expect(g3!.settings.prices[0].bundleDiscounts.size).toBe(1);
|
|
1105
|
+
|
|
1106
|
+
// There should be no custom discounts because the discounts are the same as default
|
|
1107
|
+
expect([...g3!.settings.prices[0].bundleDiscounts.values()]).toEqual(
|
|
1108
|
+
expect.arrayContaining([
|
|
1109
|
+
expect.objectContaining({ customDiscounts: null }),
|
|
1110
|
+
]),
|
|
1111
|
+
);
|
|
1112
|
+
});
|
|
1113
|
+
|
|
1114
|
+
test('group 4', async () => {
|
|
1115
|
+
// group 4
|
|
1116
|
+
const g4 = await Group.getByID(group4.id);
|
|
1117
|
+
|
|
1118
|
+
// check prices (should be equal to old prices)
|
|
1119
|
+
expect(g4!.settings.prices).toHaveLength(1);
|
|
1120
|
+
expect(g4!.settings.prices[0].price.price).toBe(0);
|
|
1121
|
+
expect(g4!.settings.prices[0].price.reducedPrice).toBeNull();
|
|
1122
|
+
|
|
1123
|
+
// Should have 1 bundle discount because group 4 is in the same category as group 3 and group 3 has a discount for family members in the same category. The custom discounts should be 0 because there is no discount for this group, it is only linked.
|
|
1124
|
+
expect(g4!.settings.prices[0].bundleDiscounts.size).toBe(1);
|
|
1125
|
+
expect([...g4!.settings.prices[0].bundleDiscounts.values()]).toEqual(
|
|
1126
|
+
expect.arrayContaining([
|
|
1127
|
+
expect.objectContaining({ customDiscounts: expect.arrayContaining([
|
|
1128
|
+
expect.objectContaining({
|
|
1129
|
+
type: GroupPriceDiscountType.Fixed,
|
|
1130
|
+
value: expect.objectContaining({
|
|
1131
|
+
price: 0,
|
|
1132
|
+
reducedPrice: null,
|
|
1133
|
+
}),
|
|
1134
|
+
}),
|
|
1135
|
+
]) }),
|
|
1136
|
+
]),
|
|
1137
|
+
);
|
|
1138
|
+
});
|
|
1139
|
+
});
|
|
1140
|
+
|
|
1141
|
+
/**
|
|
1142
|
+
* Test case 5 description:
|
|
1143
|
+
* An organization with 3 groups. Group 1 and 2 are in the same category. Only group 1 has prices set with a discount if family members are in the same category and a discount if family members are in the same group.
|
|
1144
|
+
* The tests checks if the prices and bundle discounts for each group are migrated correctly. See each test for a more detailed description.
|
|
1145
|
+
*/
|
|
1146
|
+
describe('Case 5 - family members in same group', () => {
|
|
1147
|
+
let period: RegistrationPeriod;
|
|
1148
|
+
let organization: Organization;
|
|
1149
|
+
let organizationPeriod: OrganizationRegistrationPeriod;
|
|
1150
|
+
let group1: Group;
|
|
1151
|
+
let group2: Group;
|
|
1152
|
+
let group3: Group;
|
|
1153
|
+
|
|
1154
|
+
beforeAll(async () => {
|
|
1155
|
+
const startDate = new Date(2025, 0, 1);
|
|
1156
|
+
const endDate = new Date(2025, 11, 31);
|
|
1157
|
+
period = await new RegistrationPeriodFactory({ startDate, endDate }).create();
|
|
1158
|
+
organization = await new OrganizationFactory({ period }).create();
|
|
1159
|
+
period.organizationId = organization.id;
|
|
1160
|
+
await period.save();
|
|
1161
|
+
organizationPeriod = await new OrganizationRegistrationPeriodFactory({ organization, period }).create();
|
|
1162
|
+
|
|
1163
|
+
group1 = await new GroupFactory({ organization, period }).create();
|
|
1164
|
+
group1.settings.prices = [];
|
|
1165
|
+
group1.settings.name = new TranslatedString('group1');
|
|
1166
|
+
|
|
1167
|
+
// initial price with discount if family members in same category
|
|
1168
|
+
const oldPrices1 = OldGroupPrices.create({
|
|
1169
|
+
startDate: null,
|
|
1170
|
+
sameMemberOnlyDiscount: false,
|
|
1171
|
+
onlySameGroup: true,
|
|
1172
|
+
prices: [
|
|
1173
|
+
OldGroupPrice.create({
|
|
1174
|
+
price: 30,
|
|
1175
|
+
reducedPrice: 20,
|
|
1176
|
+
}),
|
|
1177
|
+
OldGroupPrice.create({
|
|
1178
|
+
price: 25,
|
|
1179
|
+
reducedPrice: 15,
|
|
1180
|
+
}),
|
|
1181
|
+
OldGroupPrice.create({
|
|
1182
|
+
price: 20,
|
|
1183
|
+
reducedPrice: 10,
|
|
1184
|
+
}),
|
|
1185
|
+
],
|
|
1186
|
+
});
|
|
1187
|
+
|
|
1188
|
+
// price with discount after startDate 2025-03-01 if family members in same category
|
|
1189
|
+
const oldPrices2 = OldGroupPrices.create({
|
|
1190
|
+
startDate: new Date(2025, 2, 1),
|
|
1191
|
+
sameMemberOnlyDiscount: false,
|
|
1192
|
+
onlySameGroup: false,
|
|
1193
|
+
prices: [
|
|
1194
|
+
OldGroupPrice.create({
|
|
1195
|
+
price: 300,
|
|
1196
|
+
reducedPrice: 200,
|
|
1197
|
+
}),
|
|
1198
|
+
OldGroupPrice.create({
|
|
1199
|
+
price: 200,
|
|
1200
|
+
reducedPrice: 100,
|
|
1201
|
+
}),
|
|
1202
|
+
],
|
|
1203
|
+
});
|
|
1204
|
+
|
|
1205
|
+
// add both prices with discounts to group 1
|
|
1206
|
+
group1.settings.oldPrices = [oldPrices2, oldPrices1];
|
|
1207
|
+
|
|
1208
|
+
await group1.save();
|
|
1209
|
+
|
|
1210
|
+
group2 = await new GroupFactory({ organization, period }).create();
|
|
1211
|
+
group2.settings.prices = [];
|
|
1212
|
+
// do not set old prices for group 2 (should be set to 0 automatically in migration)
|
|
1213
|
+
group2.settings.oldPrices = [];
|
|
1214
|
+
group2.settings.name = new TranslatedString('group2');
|
|
1215
|
+
await group2.save();
|
|
1216
|
+
|
|
1217
|
+
group3 = await new GroupFactory({ organization, period }).create();
|
|
1218
|
+
group3.settings.prices = [];
|
|
1219
|
+
// do not set old prices for group 3 (should be set to 0 automatically in migration)
|
|
1220
|
+
group3.settings.oldPrices = [];
|
|
1221
|
+
group3.settings.name = new TranslatedString('group3');
|
|
1222
|
+
await group3.save();
|
|
1223
|
+
|
|
1224
|
+
// add group 1 and to 2 to same category, add group 3 to different category
|
|
1225
|
+
organizationPeriod.settings.categories = [
|
|
1226
|
+
GroupCategory.create({
|
|
1227
|
+
settings: GroupCategorySettings.create({ name: 'category1' }),
|
|
1228
|
+
groupIds: [group1.id, group2.id],
|
|
1229
|
+
}),
|
|
1230
|
+
GroupCategory.create({
|
|
1231
|
+
settings: GroupCategorySettings.create({ name: 'category2' }),
|
|
1232
|
+
groupIds: [group3.id],
|
|
1233
|
+
}),
|
|
1234
|
+
];
|
|
1235
|
+
|
|
1236
|
+
await organizationPeriod.save();
|
|
1237
|
+
|
|
1238
|
+
await migratePrices();
|
|
1239
|
+
});
|
|
1240
|
+
|
|
1241
|
+
afterAll(async () => {
|
|
1242
|
+
await group1.delete();
|
|
1243
|
+
await group2.delete();
|
|
1244
|
+
await group3.delete();
|
|
1245
|
+
|
|
1246
|
+
await organizationPeriod.delete();
|
|
1247
|
+
period.organizationId = null;
|
|
1248
|
+
await period.save();
|
|
1249
|
+
|
|
1250
|
+
await organization.delete();
|
|
1251
|
+
await period.delete();
|
|
1252
|
+
});
|
|
1253
|
+
|
|
1254
|
+
test('organization period', async () => {
|
|
1255
|
+
// check organization registration period
|
|
1256
|
+
const orgPeriod = await OrganizationRegistrationPeriod.getByID(organizationPeriod.id);
|
|
1257
|
+
|
|
1258
|
+
// the organization period should have 1 bundle discount for family members in category 1
|
|
1259
|
+
expect(orgPeriod!.settings.bundleDiscounts).toHaveLength(2);
|
|
1260
|
+
|
|
1261
|
+
expect(orgPeriod!.settings.bundleDiscounts).toEqual(
|
|
1262
|
+
expect.arrayContaining([
|
|
1263
|
+
expect.objectContaining({
|
|
1264
|
+
// the discount should be for family members in group 1
|
|
1265
|
+
countWholeFamily: true,
|
|
1266
|
+
countPerGroup: true,
|
|
1267
|
+
// should contain the differences for oldPrices1
|
|
1268
|
+
discounts: expect.arrayContaining([
|
|
1269
|
+
expect.objectContaining({
|
|
1270
|
+
type: GroupPriceDiscountType.Fixed,
|
|
1271
|
+
value: expect.objectContaining({
|
|
1272
|
+
price: 5,
|
|
1273
|
+
reducedPrice: 5,
|
|
1274
|
+
}),
|
|
1275
|
+
}),
|
|
1276
|
+
expect.objectContaining({
|
|
1277
|
+
type: GroupPriceDiscountType.Fixed,
|
|
1278
|
+
value: expect.objectContaining({
|
|
1279
|
+
price: 10,
|
|
1280
|
+
reducedPrice: 10,
|
|
1281
|
+
}),
|
|
1282
|
+
}),
|
|
1283
|
+
]) }),
|
|
1284
|
+
expect.objectContaining({
|
|
1285
|
+
// the discount should be for family members in category 1
|
|
1286
|
+
countWholeFamily: true,
|
|
1287
|
+
countPerGroup: false,
|
|
1288
|
+
// should contain the differences for oldPrices1
|
|
1289
|
+
discounts: expect.arrayContaining([
|
|
1290
|
+
expect.objectContaining({
|
|
1291
|
+
type: GroupPriceDiscountType.Fixed,
|
|
1292
|
+
value: expect.objectContaining({
|
|
1293
|
+
price: 100,
|
|
1294
|
+
reducedPrice: 100,
|
|
1295
|
+
}),
|
|
1296
|
+
}),
|
|
1297
|
+
]) }),
|
|
1298
|
+
]),
|
|
1299
|
+
);
|
|
1300
|
+
});
|
|
1301
|
+
|
|
1302
|
+
test('group 1', async () => {
|
|
1303
|
+
const g1 = await Group.getByID(group1.id);
|
|
1304
|
+
|
|
1305
|
+
// check prices (should be equal to old prices)
|
|
1306
|
+
expect(g1!.settings.prices).toHaveLength(2);
|
|
1307
|
+
expect(g1!.settings.prices[0].price.price).toBe(30);
|
|
1308
|
+
expect(g1!.settings.prices[0].price.reducedPrice).toBe(20);
|
|
1309
|
+
expect(g1!.settings.prices[1].price.price).toBe(300);
|
|
1310
|
+
expect(g1!.settings.prices[1].price.reducedPrice).toBe(200);
|
|
1311
|
+
|
|
1312
|
+
// check bundle discounts
|
|
1313
|
+
// a bundle discount for family members in the same category with 0 discount (only linked) and a bundle discount for family members in the same group
|
|
1314
|
+
expect(g1!.settings.prices[0].bundleDiscounts.size).toBe(2);
|
|
1315
|
+
// a bundle discount for family members in the same category
|
|
1316
|
+
expect(g1!.settings.prices[1].bundleDiscounts.size).toBe(1);
|
|
1317
|
+
|
|
1318
|
+
// custom discount for bundle discount of the first price should be null because the discounts are not different than the discounts for the bundle discount that is configured in the settings of the organization (which are the discounts for oldPrices1)
|
|
1319
|
+
expect([...g1!.settings.prices[0].bundleDiscounts.values()]).toEqual(
|
|
1320
|
+
expect.arrayContaining([
|
|
1321
|
+
// a bundle discount for family members in the same category with 0 discount (only linked)
|
|
1322
|
+
expect.objectContaining({ customDiscounts:
|
|
1323
|
+
expect.arrayContaining([
|
|
1324
|
+
expect.objectContaining({
|
|
1325
|
+
type: GroupPriceDiscountType.Fixed,
|
|
1326
|
+
value: expect.objectContaining({
|
|
1327
|
+
price: 0,
|
|
1328
|
+
reducedPrice: null,
|
|
1329
|
+
}),
|
|
1330
|
+
}),
|
|
1331
|
+
]),
|
|
1332
|
+
}),
|
|
1333
|
+
// A bundle discount for family members in the same group. No custom discount because the discount is the same as the bundle discount on the organization period.
|
|
1334
|
+
expect.objectContaining({ customDiscounts: null }),
|
|
1335
|
+
]),
|
|
1336
|
+
);
|
|
1337
|
+
|
|
1338
|
+
// custom discount for bundle discount of second price should be null because the discounts are the same as the bundle discount that is configured in the settings of the organization (which are the discounts for oldPrices2)
|
|
1339
|
+
expect([...g1!.settings.prices[1].bundleDiscounts.values()]).toEqual(
|
|
1340
|
+
expect.arrayContaining([
|
|
1341
|
+
expect.objectContaining({ customDiscounts: null }),
|
|
1342
|
+
]),
|
|
1343
|
+
);
|
|
1344
|
+
});
|
|
1345
|
+
|
|
1346
|
+
test('group 2', async () => {
|
|
1347
|
+
// group 2
|
|
1348
|
+
const g2 = await Group.getByID(group2.id);
|
|
1349
|
+
|
|
1350
|
+
// check prices (the price should be 0 because there were no old prices configured)
|
|
1351
|
+
expect(g2!.settings.prices).toHaveLength(1);
|
|
1352
|
+
expect(g2!.settings.prices[0].price.price).toBe(0);
|
|
1353
|
+
expect(g2!.settings.prices[0].price.reducedPrice).toBeNull();
|
|
1354
|
+
|
|
1355
|
+
// check bundle discounts (each price should have 1 bundle discount for family members in same category)
|
|
1356
|
+
expect(g2!.settings.prices[0].bundleDiscounts.size).toBe(1);
|
|
1357
|
+
|
|
1358
|
+
// The custom discounts should be 0 because there were no prices for the group. It should only be linked to the bundle discount because the discount for group 1 should be applied if a member inscribes for group 2.
|
|
1359
|
+
expect([...g2!.settings.prices[0].bundleDiscounts.values()]).toEqual(
|
|
1360
|
+
expect.arrayContaining([
|
|
1361
|
+
expect.objectContaining({ customDiscounts: expect.arrayContaining([
|
|
1362
|
+
expect.objectContaining({
|
|
1363
|
+
type: GroupPriceDiscountType.Fixed,
|
|
1364
|
+
value: expect.objectContaining({
|
|
1365
|
+
price: 0,
|
|
1366
|
+
reducedPrice: null,
|
|
1367
|
+
}),
|
|
1368
|
+
}),
|
|
1369
|
+
]) }),
|
|
1370
|
+
]),
|
|
1371
|
+
);
|
|
1372
|
+
});
|
|
1373
|
+
|
|
1374
|
+
test('group 3', async () => {
|
|
1375
|
+
// group 3
|
|
1376
|
+
const g3 = await Group.getByID(group3.id);
|
|
1377
|
+
|
|
1378
|
+
// check prices (the price should be 0 because there were no old prices configured)
|
|
1379
|
+
expect(g3!.settings.prices).toHaveLength(1);
|
|
1380
|
+
expect(g3!.settings.prices[0].price.price).toBe(0);
|
|
1381
|
+
expect(g3!.settings.prices[0].price.reducedPrice).toBeNull();
|
|
1382
|
+
|
|
1383
|
+
// check bundle discounts (should have no bundle discount because the group is in another category)
|
|
1384
|
+
expect(g3!.settings.prices[0].bundleDiscounts.size).toBe(0);
|
|
1385
|
+
});
|
|
1386
|
+
});
|
|
1387
|
+
|
|
1388
|
+
describe('Old group price with single price should not result in an empty custom discount but in a custom discount of 0', () => {
|
|
1389
|
+
// The main purpose of this test is to check that the custom discount is not an array without items because the calculation of the bundle discount would fail.
|
|
1390
|
+
|
|
1391
|
+
let period: RegistrationPeriod;
|
|
1392
|
+
let organization: Organization;
|
|
1393
|
+
let organizationPeriod: OrganizationRegistrationPeriod;
|
|
1394
|
+
let group1: Group;
|
|
1395
|
+
|
|
1396
|
+
afterEach(async () => {
|
|
1397
|
+
if (group1) {
|
|
1398
|
+
await group1.delete();
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1401
|
+
if (organizationPeriod) {
|
|
1402
|
+
await organizationPeriod.delete();
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
if (period) {
|
|
1406
|
+
period.organizationId = null;
|
|
1407
|
+
await period.save();
|
|
1408
|
+
}
|
|
1409
|
+
|
|
1410
|
+
if (organization) {
|
|
1411
|
+
await organization.delete();
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
if (period) {
|
|
1415
|
+
await period.delete();
|
|
1416
|
+
}
|
|
1417
|
+
});
|
|
1418
|
+
|
|
1419
|
+
// case where the discount is for family members in the same category
|
|
1420
|
+
test('family member discount for category', async () => {
|
|
1421
|
+
// arrange
|
|
1422
|
+
const startDate = new Date(2025, 0, 1);
|
|
1423
|
+
const endDate = new Date(2025, 11, 31);
|
|
1424
|
+
period = await new RegistrationPeriodFactory({ startDate, endDate }).create();
|
|
1425
|
+
organization = await new OrganizationFactory({ period }).create();
|
|
1426
|
+
period.organizationId = organization.id;
|
|
1427
|
+
await period.save();
|
|
1428
|
+
organizationPeriod = await new OrganizationRegistrationPeriodFactory({ organization, period }).create();
|
|
1429
|
+
|
|
1430
|
+
group1 = await new GroupFactory({ organization, period }).create();
|
|
1431
|
+
group1.settings.prices = [];
|
|
1432
|
+
group1.settings.name = new TranslatedString('group1');
|
|
1433
|
+
|
|
1434
|
+
// initial price with discount if family members in same category
|
|
1435
|
+
const oldPrices1 = OldGroupPrices.create({
|
|
1436
|
+
startDate: null,
|
|
1437
|
+
sameMemberOnlyDiscount: false,
|
|
1438
|
+
onlySameGroup: false,
|
|
1439
|
+
prices: [
|
|
1440
|
+
OldGroupPrice.create({
|
|
1441
|
+
price: 30,
|
|
1442
|
+
reducedPrice: 20,
|
|
1443
|
+
}),
|
|
1444
|
+
OldGroupPrice.create({
|
|
1445
|
+
price: 25,
|
|
1446
|
+
reducedPrice: 15,
|
|
1447
|
+
}),
|
|
1448
|
+
OldGroupPrice.create({
|
|
1449
|
+
price: 20,
|
|
1450
|
+
reducedPrice: 10,
|
|
1451
|
+
}),
|
|
1452
|
+
],
|
|
1453
|
+
});
|
|
1454
|
+
|
|
1455
|
+
// price with discount after startDate 2025-03-01 if family members in same category
|
|
1456
|
+
const oldPrices2 = OldGroupPrices.create({
|
|
1457
|
+
startDate: new Date(2025, 2, 1),
|
|
1458
|
+
sameMemberOnlyDiscount: false,
|
|
1459
|
+
onlySameGroup: false,
|
|
1460
|
+
prices: [
|
|
1461
|
+
OldGroupPrice.create({
|
|
1462
|
+
price: 300,
|
|
1463
|
+
reducedPrice: 200,
|
|
1464
|
+
}),
|
|
1465
|
+
],
|
|
1466
|
+
});
|
|
1467
|
+
|
|
1468
|
+
// add both prices with discounts to group 1
|
|
1469
|
+
group1.settings.oldPrices = [oldPrices2, oldPrices1];
|
|
1470
|
+
|
|
1471
|
+
await group1.save();
|
|
1472
|
+
|
|
1473
|
+
// add group 1 and to 2 to same category, add group 3 to different category
|
|
1474
|
+
organizationPeriod.settings.categories = [
|
|
1475
|
+
GroupCategory.create({
|
|
1476
|
+
settings: GroupCategorySettings.create({ name: 'category1' }),
|
|
1477
|
+
groupIds: [group1.id],
|
|
1478
|
+
}),
|
|
1479
|
+
];
|
|
1480
|
+
|
|
1481
|
+
await organizationPeriod.save();
|
|
1482
|
+
|
|
1483
|
+
// act
|
|
1484
|
+
await migratePrices();
|
|
1485
|
+
const g1 = await Group.getByID(group1.id);
|
|
1486
|
+
|
|
1487
|
+
// check prices (should be equal to old prices)
|
|
1488
|
+
expect(g1!.settings.prices).toHaveLength(2);
|
|
1489
|
+
expect(g1!.settings.prices[0].price.price).toBe(30);
|
|
1490
|
+
expect(g1!.settings.prices[0].price.reducedPrice).toBe(20);
|
|
1491
|
+
expect(g1!.settings.prices[1].price.price).toBe(300);
|
|
1492
|
+
expect(g1!.settings.prices[1].price.reducedPrice).toBe(200);
|
|
1493
|
+
|
|
1494
|
+
// check bundle discounts (each price should have 1 bundle discount for family members in same category)
|
|
1495
|
+
expect(g1!.settings.prices[0].bundleDiscounts.size).toBe(1);
|
|
1496
|
+
expect(g1!.settings.prices[1].bundleDiscounts.size).toBe(1);
|
|
1497
|
+
|
|
1498
|
+
// custom discount for bundle discount of the first price should be null because the discounts are not different than the discounts for the bundle discount that is configured in the settings of the organization (which are the discounts for oldPrices1)
|
|
1499
|
+
expect([...g1!.settings.prices[0].bundleDiscounts.values()]).toEqual(
|
|
1500
|
+
expect.arrayContaining([
|
|
1501
|
+
expect.objectContaining({ customDiscounts: null }),
|
|
1502
|
+
]),
|
|
1503
|
+
);
|
|
1504
|
+
|
|
1505
|
+
// custom discount for bundle discount of second price should not be null because the discounts are different than the bundle discount that is configured in the settings of the organization (which are the discounts for oldPrices1), it should contain the discount for oldPrices2 which is 0 because there is only one price
|
|
1506
|
+
expect([...g1!.settings.prices[1].bundleDiscounts.values()]).toEqual(
|
|
1507
|
+
expect.arrayContaining([
|
|
1508
|
+
expect.objectContaining({ customDiscounts: expect.arrayContaining([
|
|
1509
|
+
expect.objectContaining({
|
|
1510
|
+
type: GroupPriceDiscountType.Fixed,
|
|
1511
|
+
value: expect.objectContaining({
|
|
1512
|
+
price: 0,
|
|
1513
|
+
reducedPrice: null,
|
|
1514
|
+
}),
|
|
1515
|
+
}),
|
|
1516
|
+
]) }),
|
|
1517
|
+
]),
|
|
1518
|
+
);
|
|
1519
|
+
});
|
|
1520
|
+
|
|
1521
|
+
// case where the discount is for same members in the same category
|
|
1522
|
+
test('same member discount for category', async () => {
|
|
1523
|
+
// arrange
|
|
1524
|
+
const startDate = new Date(2025, 0, 1);
|
|
1525
|
+
const endDate = new Date(2025, 11, 31);
|
|
1526
|
+
period = await new RegistrationPeriodFactory({ startDate, endDate }).create();
|
|
1527
|
+
organization = await new OrganizationFactory({ period }).create();
|
|
1528
|
+
period.organizationId = organization.id;
|
|
1529
|
+
await period.save();
|
|
1530
|
+
organizationPeriod = await new OrganizationRegistrationPeriodFactory({ organization, period }).create();
|
|
1531
|
+
|
|
1532
|
+
group1 = await new GroupFactory({ organization, period }).create();
|
|
1533
|
+
group1.settings.prices = [];
|
|
1534
|
+
group1.settings.name = new TranslatedString('group1');
|
|
1535
|
+
|
|
1536
|
+
// initial price with discount if same members in same category
|
|
1537
|
+
const oldPrices1 = OldGroupPrices.create({
|
|
1538
|
+
startDate: null,
|
|
1539
|
+
sameMemberOnlyDiscount: true,
|
|
1540
|
+
onlySameGroup: false,
|
|
1541
|
+
prices: [
|
|
1542
|
+
OldGroupPrice.create({
|
|
1543
|
+
price: 30,
|
|
1544
|
+
reducedPrice: 20,
|
|
1545
|
+
}),
|
|
1546
|
+
OldGroupPrice.create({
|
|
1547
|
+
price: 25,
|
|
1548
|
+
reducedPrice: 15,
|
|
1549
|
+
}),
|
|
1550
|
+
OldGroupPrice.create({
|
|
1551
|
+
price: 20,
|
|
1552
|
+
reducedPrice: 10,
|
|
1553
|
+
}),
|
|
1554
|
+
],
|
|
1555
|
+
});
|
|
1556
|
+
|
|
1557
|
+
// price with discount after startDate 2025-03-01 if same members in same category
|
|
1558
|
+
const oldPrices2 = OldGroupPrices.create({
|
|
1559
|
+
startDate: new Date(2025, 2, 1),
|
|
1560
|
+
sameMemberOnlyDiscount: true,
|
|
1561
|
+
onlySameGroup: false,
|
|
1562
|
+
prices: [
|
|
1563
|
+
OldGroupPrice.create({
|
|
1564
|
+
price: 300,
|
|
1565
|
+
reducedPrice: 200,
|
|
1566
|
+
}),
|
|
1567
|
+
],
|
|
1568
|
+
});
|
|
1569
|
+
|
|
1570
|
+
// add both prices with discounts to group 1
|
|
1571
|
+
group1.settings.oldPrices = [oldPrices2, oldPrices1];
|
|
1572
|
+
|
|
1573
|
+
await group1.save();
|
|
1574
|
+
|
|
1575
|
+
// add group 1 and to 2 to same category, add group 3 to different category
|
|
1576
|
+
organizationPeriod.settings.categories = [
|
|
1577
|
+
GroupCategory.create({
|
|
1578
|
+
settings: GroupCategorySettings.create({ name: 'category1' }),
|
|
1579
|
+
groupIds: [group1.id],
|
|
1580
|
+
}),
|
|
1581
|
+
];
|
|
1582
|
+
|
|
1583
|
+
await organizationPeriod.save();
|
|
1584
|
+
|
|
1585
|
+
// act
|
|
1586
|
+
await migratePrices();
|
|
1587
|
+
const g1 = await Group.getByID(group1.id);
|
|
1588
|
+
|
|
1589
|
+
// check prices (should be equal to old prices)
|
|
1590
|
+
expect(g1!.settings.prices).toHaveLength(2);
|
|
1591
|
+
expect(g1!.settings.prices[0].price.price).toBe(30);
|
|
1592
|
+
expect(g1!.settings.prices[0].price.reducedPrice).toBe(20);
|
|
1593
|
+
expect(g1!.settings.prices[1].price.price).toBe(300);
|
|
1594
|
+
expect(g1!.settings.prices[1].price.reducedPrice).toBe(200);
|
|
1595
|
+
|
|
1596
|
+
// check bundle discounts (each price should have 1 bundle discount for family members in same category)
|
|
1597
|
+
expect(g1!.settings.prices[0].bundleDiscounts.size).toBe(1);
|
|
1598
|
+
expect(g1!.settings.prices[1].bundleDiscounts.size).toBe(1);
|
|
1599
|
+
|
|
1600
|
+
// custom discount for bundle discount of the first price should be null because the discounts are not different than the discounts for the bundle discount that is configured in the settings of the organization (which are the discounts for oldPrices1)
|
|
1601
|
+
expect([...g1!.settings.prices[0].bundleDiscounts.values()]).toEqual(
|
|
1602
|
+
expect.arrayContaining([
|
|
1603
|
+
expect.objectContaining({ customDiscounts: null }),
|
|
1604
|
+
]),
|
|
1605
|
+
);
|
|
1606
|
+
|
|
1607
|
+
// custom discount for bundle discount of second price should not be null because the discounts are different than the bundle discount that is configured in the settings of the organization (which are the discounts for oldPrices1), it should contain the discount for oldPrices2 which is 0 because there is only one price
|
|
1608
|
+
expect([...g1!.settings.prices[1].bundleDiscounts.values()]).toEqual(
|
|
1609
|
+
expect.arrayContaining([
|
|
1610
|
+
expect.objectContaining({ customDiscounts: expect.arrayContaining([
|
|
1611
|
+
expect.objectContaining({
|
|
1612
|
+
type: GroupPriceDiscountType.Fixed,
|
|
1613
|
+
value: expect.objectContaining({
|
|
1614
|
+
price: 0,
|
|
1615
|
+
reducedPrice: null,
|
|
1616
|
+
}),
|
|
1617
|
+
}),
|
|
1618
|
+
]) }),
|
|
1619
|
+
]),
|
|
1620
|
+
);
|
|
1621
|
+
});
|
|
1622
|
+
|
|
1623
|
+
// case where the discount is for family members in the same group
|
|
1624
|
+
test('family member discount for group', async () => {
|
|
1625
|
+
// arrange
|
|
1626
|
+
const startDate = new Date(2025, 0, 1);
|
|
1627
|
+
const endDate = new Date(2025, 11, 31);
|
|
1628
|
+
period = await new RegistrationPeriodFactory({ startDate, endDate }).create();
|
|
1629
|
+
organization = await new OrganizationFactory({ period }).create();
|
|
1630
|
+
period.organizationId = organization.id;
|
|
1631
|
+
await period.save();
|
|
1632
|
+
organizationPeriod = await new OrganizationRegistrationPeriodFactory({ organization, period }).create();
|
|
1633
|
+
|
|
1634
|
+
group1 = await new GroupFactory({ organization, period }).create();
|
|
1635
|
+
group1.settings.prices = [];
|
|
1636
|
+
group1.settings.name = new TranslatedString('group1');
|
|
1637
|
+
|
|
1638
|
+
// initial price with discount if same members in same category
|
|
1639
|
+
const oldPrices1 = OldGroupPrices.create({
|
|
1640
|
+
startDate: null,
|
|
1641
|
+
sameMemberOnlyDiscount: true,
|
|
1642
|
+
onlySameGroup: true,
|
|
1643
|
+
prices: [
|
|
1644
|
+
OldGroupPrice.create({
|
|
1645
|
+
price: 30,
|
|
1646
|
+
reducedPrice: 20,
|
|
1647
|
+
}),
|
|
1648
|
+
OldGroupPrice.create({
|
|
1649
|
+
price: 25,
|
|
1650
|
+
reducedPrice: 15,
|
|
1651
|
+
}),
|
|
1652
|
+
OldGroupPrice.create({
|
|
1653
|
+
price: 20,
|
|
1654
|
+
reducedPrice: 10,
|
|
1655
|
+
}),
|
|
1656
|
+
],
|
|
1657
|
+
});
|
|
1658
|
+
|
|
1659
|
+
// price with discount after startDate 2025-03-01 if same members in same category
|
|
1660
|
+
const oldPrices2 = OldGroupPrices.create({
|
|
1661
|
+
startDate: new Date(2025, 2, 1),
|
|
1662
|
+
sameMemberOnlyDiscount: true,
|
|
1663
|
+
onlySameGroup: true,
|
|
1664
|
+
prices: [
|
|
1665
|
+
OldGroupPrice.create({
|
|
1666
|
+
price: 300,
|
|
1667
|
+
reducedPrice: 200,
|
|
1668
|
+
}),
|
|
1669
|
+
],
|
|
1670
|
+
});
|
|
1671
|
+
|
|
1672
|
+
// add both prices with discounts to group 1
|
|
1673
|
+
group1.settings.oldPrices = [oldPrices2, oldPrices1];
|
|
1674
|
+
|
|
1675
|
+
await group1.save();
|
|
1676
|
+
|
|
1677
|
+
// add group 1 and to 2 to same category, add group 3 to different category
|
|
1678
|
+
organizationPeriod.settings.categories = [
|
|
1679
|
+
GroupCategory.create({
|
|
1680
|
+
settings: GroupCategorySettings.create({ name: 'category1' }),
|
|
1681
|
+
groupIds: [group1.id],
|
|
1682
|
+
}),
|
|
1683
|
+
];
|
|
1684
|
+
|
|
1685
|
+
await organizationPeriod.save();
|
|
1686
|
+
|
|
1687
|
+
// act
|
|
1688
|
+
await migratePrices();
|
|
1689
|
+
const g1 = await Group.getByID(group1.id);
|
|
1690
|
+
|
|
1691
|
+
// check prices (should be equal to old prices)
|
|
1692
|
+
expect(g1!.settings.prices).toHaveLength(2);
|
|
1693
|
+
expect(g1!.settings.prices[0].price.price).toBe(30);
|
|
1694
|
+
expect(g1!.settings.prices[0].price.reducedPrice).toBe(20);
|
|
1695
|
+
expect(g1!.settings.prices[1].price.price).toBe(300);
|
|
1696
|
+
expect(g1!.settings.prices[1].price.reducedPrice).toBe(200);
|
|
1697
|
+
|
|
1698
|
+
// check bundle discounts
|
|
1699
|
+
// first price should have 1 bundle discount for same members in same group
|
|
1700
|
+
expect(g1!.settings.prices[0].bundleDiscounts.size).toBe(1);
|
|
1701
|
+
// second price should not have bundle discount because the group price has only one price
|
|
1702
|
+
expect(g1!.settings.prices[1].bundleDiscounts.size).toBe(0);
|
|
1703
|
+
|
|
1704
|
+
// custom discount for bundle discount of the first price should be null because the discounts are not different than the discounts for the bundle discount that is configured in the settings of the organization (which are the discounts for oldPrices1)
|
|
1705
|
+
expect([...g1!.settings.prices[0].bundleDiscounts.values()]).toEqual(
|
|
1706
|
+
expect.arrayContaining([
|
|
1707
|
+
expect.objectContaining({ customDiscounts: null }),
|
|
1708
|
+
]),
|
|
1709
|
+
);
|
|
1710
|
+
});
|
|
1711
|
+
});
|
|
1712
|
+
|
|
1713
|
+
describe('Groups with same discounts in old prices should have bundle discounts without custom discounts', () => {
|
|
1714
|
+
let period: RegistrationPeriod;
|
|
1715
|
+
let organization: Organization;
|
|
1716
|
+
let organizationPeriod: OrganizationRegistrationPeriod;
|
|
1717
|
+
let group1: Group;
|
|
1718
|
+
let group2: Group;
|
|
1719
|
+
|
|
1720
|
+
afterEach(async () => {
|
|
1721
|
+
if (group1) {
|
|
1722
|
+
await group1.delete();
|
|
1723
|
+
}
|
|
1724
|
+
|
|
1725
|
+
if (group2) {
|
|
1726
|
+
await group2.delete();
|
|
1727
|
+
}
|
|
1728
|
+
|
|
1729
|
+
if (organizationPeriod) {
|
|
1730
|
+
await organizationPeriod.delete();
|
|
1731
|
+
}
|
|
1732
|
+
|
|
1733
|
+
if (period) {
|
|
1734
|
+
period.organizationId = null;
|
|
1735
|
+
await period.save();
|
|
1736
|
+
}
|
|
1737
|
+
|
|
1738
|
+
if (organization) {
|
|
1739
|
+
await organization.delete();
|
|
1740
|
+
}
|
|
1741
|
+
|
|
1742
|
+
if (period) {
|
|
1743
|
+
await period.delete();
|
|
1744
|
+
}
|
|
1745
|
+
});
|
|
1746
|
+
|
|
1747
|
+
// case where the discount is for family members in the same category
|
|
1748
|
+
test('family member discount for category', async () => {
|
|
1749
|
+
// arrange
|
|
1750
|
+
const startDate = new Date(2025, 0, 1);
|
|
1751
|
+
const endDate = new Date(2025, 11, 31);
|
|
1752
|
+
period = await new RegistrationPeriodFactory({ startDate, endDate }).create();
|
|
1753
|
+
organization = await new OrganizationFactory({ period }).create();
|
|
1754
|
+
period.organizationId = organization.id;
|
|
1755
|
+
await period.save();
|
|
1756
|
+
organizationPeriod = await new OrganizationRegistrationPeriodFactory({ organization, period }).create();
|
|
1757
|
+
|
|
1758
|
+
group1 = await new GroupFactory({ organization, period }).create();
|
|
1759
|
+
group1.settings.prices = [];
|
|
1760
|
+
group1.settings.name = new TranslatedString('group1');
|
|
1761
|
+
|
|
1762
|
+
// initial price with discount if family members in same category
|
|
1763
|
+
const oldPrices1 = OldGroupPrices.create({
|
|
1764
|
+
startDate: null,
|
|
1765
|
+
sameMemberOnlyDiscount: false,
|
|
1766
|
+
onlySameGroup: false,
|
|
1767
|
+
prices: [
|
|
1768
|
+
OldGroupPrice.create({
|
|
1769
|
+
price: 30,
|
|
1770
|
+
reducedPrice: 20,
|
|
1771
|
+
}),
|
|
1772
|
+
OldGroupPrice.create({
|
|
1773
|
+
price: 25,
|
|
1774
|
+
reducedPrice: 15,
|
|
1775
|
+
}),
|
|
1776
|
+
OldGroupPrice.create({
|
|
1777
|
+
price: 20,
|
|
1778
|
+
reducedPrice: 10,
|
|
1779
|
+
}),
|
|
1780
|
+
],
|
|
1781
|
+
});
|
|
1782
|
+
|
|
1783
|
+
group1.settings.oldPrices = [oldPrices1];
|
|
1784
|
+
|
|
1785
|
+
await group1.save();
|
|
1786
|
+
|
|
1787
|
+
group2 = await new GroupFactory({ organization, period }).create();
|
|
1788
|
+
group2.settings.prices = [];
|
|
1789
|
+
group2.settings.name = new TranslatedString('group2');
|
|
1790
|
+
|
|
1791
|
+
// initial price with discount if family members in same category (same discounts as oldPrices1)
|
|
1792
|
+
const oldPrices2 = OldGroupPrices.create({
|
|
1793
|
+
startDate: null,
|
|
1794
|
+
sameMemberOnlyDiscount: false,
|
|
1795
|
+
onlySameGroup: false,
|
|
1796
|
+
prices: [
|
|
1797
|
+
OldGroupPrice.create({
|
|
1798
|
+
price: 30,
|
|
1799
|
+
reducedPrice: 20,
|
|
1800
|
+
}),
|
|
1801
|
+
OldGroupPrice.create({
|
|
1802
|
+
price: 25,
|
|
1803
|
+
reducedPrice: 15,
|
|
1804
|
+
}),
|
|
1805
|
+
OldGroupPrice.create({
|
|
1806
|
+
price: 20,
|
|
1807
|
+
reducedPrice: 10,
|
|
1808
|
+
}),
|
|
1809
|
+
],
|
|
1810
|
+
});
|
|
1811
|
+
group2.settings.oldPrices = [oldPrices2];
|
|
1812
|
+
|
|
1813
|
+
await group2.save();
|
|
1814
|
+
|
|
1815
|
+
// add group 1 and to 2 to same category
|
|
1816
|
+
organizationPeriod.settings.categories = [
|
|
1817
|
+
GroupCategory.create({
|
|
1818
|
+
settings: GroupCategorySettings.create({ name: 'category1' }),
|
|
1819
|
+
groupIds: [group1.id, group2.id],
|
|
1820
|
+
}),
|
|
1821
|
+
];
|
|
1822
|
+
|
|
1823
|
+
await organizationPeriod.save();
|
|
1824
|
+
|
|
1825
|
+
// act
|
|
1826
|
+
await migratePrices();
|
|
1827
|
+
const g1 = await Group.getByID(group1.id);
|
|
1828
|
+
|
|
1829
|
+
// test group 1
|
|
1830
|
+
|
|
1831
|
+
// check prices (should be equal to old prices)
|
|
1832
|
+
expect(g1!.settings.prices).toHaveLength(1);
|
|
1833
|
+
expect(g1!.settings.prices[0].price.price).toBe(30);
|
|
1834
|
+
expect(g1!.settings.prices[0].price.reducedPrice).toBe(20);
|
|
1835
|
+
|
|
1836
|
+
// check bundle discounts (each price should have 1 bundle discount for family members in same category)
|
|
1837
|
+
expect(g1!.settings.prices[0].bundleDiscounts.size).toBe(1);
|
|
1838
|
+
|
|
1839
|
+
// custom discount for bundle discount of the first price should be null because the discounts are not different than the discounts for the bundle discount that is configured in the settings of the organization (which are the discounts for oldPrices1)
|
|
1840
|
+
expect([...g1!.settings.prices[0].bundleDiscounts.values()]).toEqual(
|
|
1841
|
+
expect.arrayContaining([
|
|
1842
|
+
expect.objectContaining({ customDiscounts: null }),
|
|
1843
|
+
]),
|
|
1844
|
+
);
|
|
1845
|
+
|
|
1846
|
+
// test group 2
|
|
1847
|
+
const g2 = await Group.getByID(group2.id);
|
|
1848
|
+
|
|
1849
|
+
// check prices (should be equal to old prices)
|
|
1850
|
+
expect(g2!.settings.prices).toHaveLength(1);
|
|
1851
|
+
expect(g2!.settings.prices[0].price.price).toBe(30);
|
|
1852
|
+
expect(g2!.settings.prices[0].price.reducedPrice).toBe(20);
|
|
1853
|
+
|
|
1854
|
+
// check bundle discounts (each price should have 1 bundle discount for family members in same category)
|
|
1855
|
+
expect(g2!.settings.prices[0].bundleDiscounts.size).toBe(1);
|
|
1856
|
+
|
|
1857
|
+
// custom discount for bundle discount of the second price should be null because the discounts are not different than the discounts for the bundle discount that is configured in the settings of the organization (which are the discounts for oldPrices1)
|
|
1858
|
+
expect([...g2!.settings.prices[0].bundleDiscounts.values()]).toEqual(
|
|
1859
|
+
expect.arrayContaining([
|
|
1860
|
+
expect.objectContaining({ customDiscounts: null }),
|
|
1861
|
+
]),
|
|
1862
|
+
);
|
|
1863
|
+
});
|
|
1864
|
+
});
|
|
1865
|
+
|
|
1866
|
+
describe('Groups with different discounts in old prices should have bundle discounts with custom discounts', () => {
|
|
1867
|
+
let period: RegistrationPeriod;
|
|
1868
|
+
let organization: Organization;
|
|
1869
|
+
let organizationPeriod: OrganizationRegistrationPeriod;
|
|
1870
|
+
let group1: Group;
|
|
1871
|
+
let group2: Group;
|
|
1872
|
+
|
|
1873
|
+
afterEach(async () => {
|
|
1874
|
+
if (group1) {
|
|
1875
|
+
await group1.delete();
|
|
1876
|
+
}
|
|
1877
|
+
|
|
1878
|
+
if (group2) {
|
|
1879
|
+
await group2.delete();
|
|
1880
|
+
}
|
|
1881
|
+
|
|
1882
|
+
if (organizationPeriod) {
|
|
1883
|
+
await organizationPeriod.delete();
|
|
1884
|
+
}
|
|
1885
|
+
|
|
1886
|
+
if (period) {
|
|
1887
|
+
period.organizationId = null;
|
|
1888
|
+
await period.save();
|
|
1889
|
+
}
|
|
1890
|
+
|
|
1891
|
+
if (organization) {
|
|
1892
|
+
await organization.delete();
|
|
1893
|
+
}
|
|
1894
|
+
|
|
1895
|
+
if (period) {
|
|
1896
|
+
await period.delete();
|
|
1897
|
+
}
|
|
1898
|
+
});
|
|
1899
|
+
|
|
1900
|
+
test('group 2 has same discount count but other price', async () => {
|
|
1901
|
+
// arrange
|
|
1902
|
+
const startDate = new Date(2025, 0, 1);
|
|
1903
|
+
const endDate = new Date(2025, 11, 31);
|
|
1904
|
+
period = await new RegistrationPeriodFactory({ startDate, endDate }).create();
|
|
1905
|
+
organization = await new OrganizationFactory({ period }).create();
|
|
1906
|
+
period.organizationId = organization.id;
|
|
1907
|
+
await period.save();
|
|
1908
|
+
organizationPeriod = await new OrganizationRegistrationPeriodFactory({ organization, period }).create();
|
|
1909
|
+
|
|
1910
|
+
group1 = await new GroupFactory({ organization, period }).create();
|
|
1911
|
+
group1.settings.prices = [];
|
|
1912
|
+
group1.settings.name = new TranslatedString('group1');
|
|
1913
|
+
|
|
1914
|
+
// initial price with discount if family members in same category
|
|
1915
|
+
const oldPrices1 = OldGroupPrices.create({
|
|
1916
|
+
startDate: null,
|
|
1917
|
+
sameMemberOnlyDiscount: false,
|
|
1918
|
+
onlySameGroup: false,
|
|
1919
|
+
prices: [
|
|
1920
|
+
OldGroupPrice.create({
|
|
1921
|
+
price: 30,
|
|
1922
|
+
reducedPrice: 20,
|
|
1923
|
+
}),
|
|
1924
|
+
OldGroupPrice.create({
|
|
1925
|
+
price: 25,
|
|
1926
|
+
reducedPrice: 15,
|
|
1927
|
+
}),
|
|
1928
|
+
OldGroupPrice.create({
|
|
1929
|
+
price: 20,
|
|
1930
|
+
reducedPrice: 10,
|
|
1931
|
+
}),
|
|
1932
|
+
],
|
|
1933
|
+
});
|
|
1934
|
+
|
|
1935
|
+
group1.settings.oldPrices = [oldPrices1];
|
|
1936
|
+
|
|
1937
|
+
await group1.save();
|
|
1938
|
+
|
|
1939
|
+
group2 = await new GroupFactory({ organization, period }).create();
|
|
1940
|
+
group2.settings.prices = [];
|
|
1941
|
+
group2.settings.name = new TranslatedString('group2');
|
|
1942
|
+
|
|
1943
|
+
// initial price with discount if family members in same category (different discount as oldPrices1)
|
|
1944
|
+
const oldPrices2 = OldGroupPrices.create({
|
|
1945
|
+
startDate: null,
|
|
1946
|
+
sameMemberOnlyDiscount: false,
|
|
1947
|
+
onlySameGroup: false,
|
|
1948
|
+
prices: [
|
|
1949
|
+
OldGroupPrice.create({
|
|
1950
|
+
price: 30,
|
|
1951
|
+
reducedPrice: 20,
|
|
1952
|
+
}),
|
|
1953
|
+
OldGroupPrice.create({
|
|
1954
|
+
price: 25,
|
|
1955
|
+
reducedPrice: 15,
|
|
1956
|
+
}),
|
|
1957
|
+
OldGroupPrice.create({
|
|
1958
|
+
// the only difference with oldPrices1!!!
|
|
1959
|
+
price: 21,
|
|
1960
|
+
reducedPrice: 10,
|
|
1961
|
+
}),
|
|
1962
|
+
],
|
|
1963
|
+
});
|
|
1964
|
+
group2.settings.oldPrices = [oldPrices2];
|
|
1965
|
+
|
|
1966
|
+
await group2.save();
|
|
1967
|
+
|
|
1968
|
+
// add group 1 and to 2 to same category
|
|
1969
|
+
organizationPeriod.settings.categories = [
|
|
1970
|
+
GroupCategory.create({
|
|
1971
|
+
settings: GroupCategorySettings.create({ name: 'category1' }),
|
|
1972
|
+
groupIds: [group1.id, group2.id],
|
|
1973
|
+
}),
|
|
1974
|
+
];
|
|
1975
|
+
|
|
1976
|
+
await organizationPeriod.save();
|
|
1977
|
+
|
|
1978
|
+
// act
|
|
1979
|
+
await migratePrices();
|
|
1980
|
+
const g1 = await Group.getByID(group1.id);
|
|
1981
|
+
|
|
1982
|
+
// test group 1
|
|
1983
|
+
|
|
1984
|
+
// check prices (should be equal to old prices)
|
|
1985
|
+
expect(g1!.settings.prices).toHaveLength(1);
|
|
1986
|
+
expect(g1!.settings.prices[0].price.price).toBe(30);
|
|
1987
|
+
expect(g1!.settings.prices[0].price.reducedPrice).toBe(20);
|
|
1988
|
+
|
|
1989
|
+
// check bundle discounts (each price should have 1 bundle discount for family members in same category)
|
|
1990
|
+
expect(g1!.settings.prices[0].bundleDiscounts.size).toBe(1);
|
|
1991
|
+
|
|
1992
|
+
// custom discount for bundle discount of the first price should be null because the discounts are not different than the discounts for the bundle discount that is configured in the settings of the organization (which are the discounts for oldPrices1)
|
|
1993
|
+
expect([...g1!.settings.prices[0].bundleDiscounts.values()]).toEqual(
|
|
1994
|
+
expect.arrayContaining([
|
|
1995
|
+
expect.objectContaining({ customDiscounts: null }),
|
|
1996
|
+
]),
|
|
1997
|
+
);
|
|
1998
|
+
|
|
1999
|
+
// test group 2
|
|
2000
|
+
const g2 = await Group.getByID(group2.id);
|
|
2001
|
+
|
|
2002
|
+
// check prices (should be equal to old prices)
|
|
2003
|
+
expect(g2!.settings.prices).toHaveLength(1);
|
|
2004
|
+
expect(g2!.settings.prices[0].price.price).toBe(30);
|
|
2005
|
+
expect(g2!.settings.prices[0].price.reducedPrice).toBe(20);
|
|
2006
|
+
|
|
2007
|
+
// check bundle discounts (each price should have 1 bundle discount for family members in same category)
|
|
2008
|
+
expect(g2!.settings.prices[0].bundleDiscounts.size).toBe(1);
|
|
2009
|
+
|
|
2010
|
+
// custom discount for bundle discount of the second price should not be null because the discounts are different than the discounts for the bundle discount that is configured in the settings of the organization (which are the discounts for oldPrices1)
|
|
2011
|
+
expect([...g2!.settings.prices[0].bundleDiscounts.values()]).toEqual(
|
|
2012
|
+
expect.arrayContaining([
|
|
2013
|
+
expect.objectContaining({ customDiscounts: expect.arrayContaining([
|
|
2014
|
+
expect.objectContaining({
|
|
2015
|
+
type: GroupPriceDiscountType.Fixed,
|
|
2016
|
+
value: expect.objectContaining({
|
|
2017
|
+
price: 5,
|
|
2018
|
+
reducedPrice: 5,
|
|
2019
|
+
}),
|
|
2020
|
+
}),
|
|
2021
|
+
expect.objectContaining({
|
|
2022
|
+
type: GroupPriceDiscountType.Fixed,
|
|
2023
|
+
value: expect.objectContaining({
|
|
2024
|
+
price: 9,
|
|
2025
|
+
reducedPrice: 10,
|
|
2026
|
+
}),
|
|
2027
|
+
}),
|
|
2028
|
+
]) }),
|
|
2029
|
+
]),
|
|
2030
|
+
);
|
|
2031
|
+
});
|
|
2032
|
+
|
|
2033
|
+
test('group 2 has extra discount', async () => {
|
|
2034
|
+
// arrange
|
|
2035
|
+
const startDate = new Date(2025, 0, 1);
|
|
2036
|
+
const endDate = new Date(2025, 11, 31);
|
|
2037
|
+
period = await new RegistrationPeriodFactory({ startDate, endDate }).create();
|
|
2038
|
+
organization = await new OrganizationFactory({ period }).create();
|
|
2039
|
+
period.organizationId = organization.id;
|
|
2040
|
+
await period.save();
|
|
2041
|
+
organizationPeriod = await new OrganizationRegistrationPeriodFactory({ organization, period }).create();
|
|
2042
|
+
|
|
2043
|
+
group1 = await new GroupFactory({ organization, period }).create();
|
|
2044
|
+
group1.settings.prices = [];
|
|
2045
|
+
group1.settings.name = new TranslatedString('group1');
|
|
2046
|
+
|
|
2047
|
+
// initial price with discount if family members in same category
|
|
2048
|
+
const oldPrices1 = OldGroupPrices.create({
|
|
2049
|
+
startDate: null,
|
|
2050
|
+
sameMemberOnlyDiscount: false,
|
|
2051
|
+
onlySameGroup: false,
|
|
2052
|
+
prices: [
|
|
2053
|
+
OldGroupPrice.create({
|
|
2054
|
+
price: 30,
|
|
2055
|
+
reducedPrice: 20,
|
|
2056
|
+
}),
|
|
2057
|
+
OldGroupPrice.create({
|
|
2058
|
+
price: 25,
|
|
2059
|
+
reducedPrice: 15,
|
|
2060
|
+
}),
|
|
2061
|
+
OldGroupPrice.create({
|
|
2062
|
+
price: 20,
|
|
2063
|
+
reducedPrice: 10,
|
|
2064
|
+
}),
|
|
2065
|
+
],
|
|
2066
|
+
});
|
|
2067
|
+
|
|
2068
|
+
group1.settings.oldPrices = [oldPrices1];
|
|
2069
|
+
|
|
2070
|
+
await group1.save();
|
|
2071
|
+
|
|
2072
|
+
group2 = await new GroupFactory({ organization, period }).create();
|
|
2073
|
+
group2.settings.prices = [];
|
|
2074
|
+
group2.settings.name = new TranslatedString('group2');
|
|
2075
|
+
|
|
2076
|
+
// initial price with discount if family members in same category (different discount as oldPrices1)
|
|
2077
|
+
const oldPrices2 = OldGroupPrices.create({
|
|
2078
|
+
startDate: null,
|
|
2079
|
+
sameMemberOnlyDiscount: false,
|
|
2080
|
+
onlySameGroup: false,
|
|
2081
|
+
prices: [
|
|
2082
|
+
OldGroupPrice.create({
|
|
2083
|
+
price: 30,
|
|
2084
|
+
reducedPrice: 20,
|
|
2085
|
+
}),
|
|
2086
|
+
OldGroupPrice.create({
|
|
2087
|
+
price: 25,
|
|
2088
|
+
reducedPrice: 15,
|
|
2089
|
+
}),
|
|
2090
|
+
OldGroupPrice.create({
|
|
2091
|
+
price: 20,
|
|
2092
|
+
reducedPrice: 10,
|
|
2093
|
+
}),
|
|
2094
|
+
// extra discount
|
|
2095
|
+
OldGroupPrice.create({
|
|
2096
|
+
price: 19,
|
|
2097
|
+
reducedPrice: 9,
|
|
2098
|
+
}),
|
|
2099
|
+
],
|
|
2100
|
+
});
|
|
2101
|
+
group2.settings.oldPrices = [oldPrices2];
|
|
2102
|
+
|
|
2103
|
+
await group2.save();
|
|
2104
|
+
|
|
2105
|
+
// add group 1 and to 2 to same category
|
|
2106
|
+
organizationPeriod.settings.categories = [
|
|
2107
|
+
GroupCategory.create({
|
|
2108
|
+
settings: GroupCategorySettings.create({ name: 'category1' }),
|
|
2109
|
+
groupIds: [group1.id, group2.id],
|
|
2110
|
+
}),
|
|
2111
|
+
];
|
|
2112
|
+
|
|
2113
|
+
await organizationPeriod.save();
|
|
2114
|
+
|
|
2115
|
+
// act
|
|
2116
|
+
await migratePrices();
|
|
2117
|
+
const g1 = await Group.getByID(group1.id);
|
|
2118
|
+
|
|
2119
|
+
// test group 1
|
|
2120
|
+
|
|
2121
|
+
// check prices (should be equal to old prices)
|
|
2122
|
+
expect(g1!.settings.prices).toHaveLength(1);
|
|
2123
|
+
expect(g1!.settings.prices[0].price.price).toBe(30);
|
|
2124
|
+
expect(g1!.settings.prices[0].price.reducedPrice).toBe(20);
|
|
2125
|
+
|
|
2126
|
+
// check bundle discounts (each price should have 1 bundle discount for family members in same category)
|
|
2127
|
+
expect(g1!.settings.prices[0].bundleDiscounts.size).toBe(1);
|
|
2128
|
+
|
|
2129
|
+
// custom discount for bundle discount of the first price should be null because the discounts are not different than the discounts for the bundle discount that is configured in the settings of the organization (which are the discounts for oldPrices1)
|
|
2130
|
+
expect([...g1!.settings.prices[0].bundleDiscounts.values()]).toEqual(
|
|
2131
|
+
expect.arrayContaining([
|
|
2132
|
+
expect.objectContaining({ customDiscounts: null }),
|
|
2133
|
+
]),
|
|
2134
|
+
);
|
|
2135
|
+
|
|
2136
|
+
// test group 2
|
|
2137
|
+
const g2 = await Group.getByID(group2.id);
|
|
2138
|
+
|
|
2139
|
+
// check prices (should be equal to old prices)
|
|
2140
|
+
expect(g2!.settings.prices).toHaveLength(1);
|
|
2141
|
+
expect(g2!.settings.prices[0].price.price).toBe(30);
|
|
2142
|
+
expect(g2!.settings.prices[0].price.reducedPrice).toBe(20);
|
|
2143
|
+
|
|
2144
|
+
// check bundle discounts (each price should have 1 bundle discount for family members in same category)
|
|
2145
|
+
expect(g2!.settings.prices[0].bundleDiscounts.size).toBe(1);
|
|
2146
|
+
|
|
2147
|
+
// custom discount for bundle discount of the second price should not be null because the discounts are different than the discounts for the bundle discount that is configured in the settings of the organization (which are the discounts for oldPrices1)
|
|
2148
|
+
expect([...g2!.settings.prices[0].bundleDiscounts.values()]).toEqual(
|
|
2149
|
+
expect.arrayContaining([
|
|
2150
|
+
expect.objectContaining({ customDiscounts: expect.arrayContaining([
|
|
2151
|
+
expect.objectContaining({
|
|
2152
|
+
type: GroupPriceDiscountType.Fixed,
|
|
2153
|
+
value: expect.objectContaining({
|
|
2154
|
+
price: 5,
|
|
2155
|
+
reducedPrice: 5,
|
|
2156
|
+
}),
|
|
2157
|
+
}),
|
|
2158
|
+
expect.objectContaining({
|
|
2159
|
+
type: GroupPriceDiscountType.Fixed,
|
|
2160
|
+
value: expect.objectContaining({
|
|
2161
|
+
price: 10,
|
|
2162
|
+
reducedPrice: 10,
|
|
2163
|
+
}),
|
|
2164
|
+
}),
|
|
2165
|
+
expect.objectContaining({
|
|
2166
|
+
type: GroupPriceDiscountType.Fixed,
|
|
2167
|
+
value: expect.objectContaining({
|
|
2168
|
+
price: 11,
|
|
2169
|
+
reducedPrice: 11,
|
|
2170
|
+
}),
|
|
2171
|
+
}),
|
|
2172
|
+
]) }),
|
|
2173
|
+
]),
|
|
2174
|
+
);
|
|
2175
|
+
});
|
|
2176
|
+
|
|
2177
|
+
test('group 2 has one less discount', async () => {
|
|
2178
|
+
// arrange
|
|
2179
|
+
const startDate = new Date(2025, 0, 1);
|
|
2180
|
+
const endDate = new Date(2025, 11, 31);
|
|
2181
|
+
period = await new RegistrationPeriodFactory({ startDate, endDate }).create();
|
|
2182
|
+
organization = await new OrganizationFactory({ period }).create();
|
|
2183
|
+
period.organizationId = organization.id;
|
|
2184
|
+
await period.save();
|
|
2185
|
+
organizationPeriod = await new OrganizationRegistrationPeriodFactory({ organization, period }).create();
|
|
2186
|
+
|
|
2187
|
+
group1 = await new GroupFactory({ organization, period }).create();
|
|
2188
|
+
group1.settings.prices = [];
|
|
2189
|
+
group1.settings.name = new TranslatedString('group1');
|
|
2190
|
+
|
|
2191
|
+
// initial price with discount if family members in same category
|
|
2192
|
+
const oldPrices1 = OldGroupPrices.create({
|
|
2193
|
+
startDate: null,
|
|
2194
|
+
sameMemberOnlyDiscount: false,
|
|
2195
|
+
onlySameGroup: false,
|
|
2196
|
+
prices: [
|
|
2197
|
+
OldGroupPrice.create({
|
|
2198
|
+
price: 30,
|
|
2199
|
+
reducedPrice: 20,
|
|
2200
|
+
}),
|
|
2201
|
+
OldGroupPrice.create({
|
|
2202
|
+
price: 25,
|
|
2203
|
+
reducedPrice: 15,
|
|
2204
|
+
}),
|
|
2205
|
+
OldGroupPrice.create({
|
|
2206
|
+
price: 20,
|
|
2207
|
+
reducedPrice: 10,
|
|
2208
|
+
}),
|
|
2209
|
+
],
|
|
2210
|
+
});
|
|
2211
|
+
|
|
2212
|
+
group1.settings.oldPrices = [oldPrices1];
|
|
2213
|
+
|
|
2214
|
+
await group1.save();
|
|
2215
|
+
|
|
2216
|
+
group2 = await new GroupFactory({ organization, period }).create();
|
|
2217
|
+
group2.settings.prices = [];
|
|
2218
|
+
group2.settings.name = new TranslatedString('group2');
|
|
2219
|
+
|
|
2220
|
+
// initial price with discount if family members in same category (different discount as oldPrices1)
|
|
2221
|
+
const oldPrices2 = OldGroupPrices.create({
|
|
2222
|
+
startDate: null,
|
|
2223
|
+
sameMemberOnlyDiscount: false,
|
|
2224
|
+
onlySameGroup: false,
|
|
2225
|
+
prices: [
|
|
2226
|
+
OldGroupPrice.create({
|
|
2227
|
+
price: 30,
|
|
2228
|
+
reducedPrice: 20,
|
|
2229
|
+
}),
|
|
2230
|
+
OldGroupPrice.create({
|
|
2231
|
+
price: 25,
|
|
2232
|
+
reducedPrice: 15,
|
|
2233
|
+
}),
|
|
2234
|
+
// one less discount
|
|
2235
|
+
],
|
|
2236
|
+
});
|
|
2237
|
+
group2.settings.oldPrices = [oldPrices2];
|
|
2238
|
+
|
|
2239
|
+
await group2.save();
|
|
2240
|
+
|
|
2241
|
+
// add group 1 and to 2 to same category
|
|
2242
|
+
organizationPeriod.settings.categories = [
|
|
2243
|
+
GroupCategory.create({
|
|
2244
|
+
settings: GroupCategorySettings.create({ name: 'category1' }),
|
|
2245
|
+
groupIds: [group1.id, group2.id],
|
|
2246
|
+
}),
|
|
2247
|
+
];
|
|
2248
|
+
|
|
2249
|
+
await organizationPeriod.save();
|
|
2250
|
+
|
|
2251
|
+
// act
|
|
2252
|
+
await migratePrices();
|
|
2253
|
+
const g1 = await Group.getByID(group1.id);
|
|
2254
|
+
|
|
2255
|
+
// test group 1
|
|
2256
|
+
|
|
2257
|
+
// check prices (should be equal to old prices)
|
|
2258
|
+
expect(g1!.settings.prices).toHaveLength(1);
|
|
2259
|
+
expect(g1!.settings.prices[0].price.price).toBe(30);
|
|
2260
|
+
expect(g1!.settings.prices[0].price.reducedPrice).toBe(20);
|
|
2261
|
+
|
|
2262
|
+
// check bundle discounts (each price should have 1 bundle discount for family members in same category)
|
|
2263
|
+
expect(g1!.settings.prices[0].bundleDiscounts.size).toBe(1);
|
|
2264
|
+
|
|
2265
|
+
// custom discount for bundle discount of the first price should be null because the discounts are not different than the discounts for the bundle discount that is configured in the settings of the organization (which are the discounts for oldPrices1)
|
|
2266
|
+
expect([...g1!.settings.prices[0].bundleDiscounts.values()]).toEqual(
|
|
2267
|
+
expect.arrayContaining([
|
|
2268
|
+
expect.objectContaining({ customDiscounts: null }),
|
|
2269
|
+
]),
|
|
2270
|
+
);
|
|
2271
|
+
|
|
2272
|
+
// test group 2
|
|
2273
|
+
const g2 = await Group.getByID(group2.id);
|
|
2274
|
+
|
|
2275
|
+
// check prices (should be equal to old prices)
|
|
2276
|
+
expect(g2!.settings.prices).toHaveLength(1);
|
|
2277
|
+
expect(g2!.settings.prices[0].price.price).toBe(30);
|
|
2278
|
+
expect(g2!.settings.prices[0].price.reducedPrice).toBe(20);
|
|
2279
|
+
|
|
2280
|
+
// check bundle discounts (each price should have 1 bundle discount for family members in same category)
|
|
2281
|
+
expect(g2!.settings.prices[0].bundleDiscounts.size).toBe(1);
|
|
2282
|
+
|
|
2283
|
+
// custom discount for bundle discount of the second price should not be null because the discounts are different than the discounts for the bundle discount that is configured in the settings of the organization (which are the discounts for oldPrices1)
|
|
2284
|
+
expect([...g2!.settings.prices[0].bundleDiscounts.values()]).toEqual(
|
|
2285
|
+
expect.arrayContaining([
|
|
2286
|
+
expect.objectContaining({ customDiscounts: expect.arrayContaining([
|
|
2287
|
+
expect.objectContaining({
|
|
2288
|
+
type: GroupPriceDiscountType.Fixed,
|
|
2289
|
+
value: expect.objectContaining({
|
|
2290
|
+
price: 5,
|
|
2291
|
+
reducedPrice: 5,
|
|
2292
|
+
}),
|
|
2293
|
+
}),
|
|
2294
|
+
]) }),
|
|
2295
|
+
]),
|
|
2296
|
+
);
|
|
2297
|
+
});
|
|
2298
|
+
});
|
|
2299
|
+
|
|
2300
|
+
test('Groups that are in no category should not have bundle discounts from other groups that also do not have a category', async () => {
|
|
2301
|
+
// arrange
|
|
2302
|
+
const startDate = new Date(2025, 0, 1);
|
|
2303
|
+
const endDate = new Date(2025, 11, 31);
|
|
2304
|
+
const period = await new RegistrationPeriodFactory({ startDate, endDate }).create();
|
|
2305
|
+
const organization = await new OrganizationFactory({ period }).create();
|
|
2306
|
+
period.organizationId = organization.id;
|
|
2307
|
+
await period.save();
|
|
2308
|
+
const organizationPeriod = await new OrganizationRegistrationPeriodFactory({ organization, period }).create();
|
|
2309
|
+
|
|
2310
|
+
const group1 = await new GroupFactory({ organization, period }).create();
|
|
2311
|
+
group1.settings.prices = [];
|
|
2312
|
+
group1.settings.name = new TranslatedString('group1');
|
|
2313
|
+
|
|
2314
|
+
// initial price with discount if family members in same category
|
|
2315
|
+
const oldPrices1 = OldGroupPrices.create({
|
|
2316
|
+
startDate: null,
|
|
2317
|
+
sameMemberOnlyDiscount: false,
|
|
2318
|
+
onlySameGroup: false,
|
|
2319
|
+
prices: [
|
|
2320
|
+
OldGroupPrice.create({
|
|
2321
|
+
price: 30,
|
|
2322
|
+
reducedPrice: 20,
|
|
2323
|
+
}),
|
|
2324
|
+
OldGroupPrice.create({
|
|
2325
|
+
price: 25,
|
|
2326
|
+
reducedPrice: 15,
|
|
2327
|
+
}),
|
|
2328
|
+
OldGroupPrice.create({
|
|
2329
|
+
price: 20,
|
|
2330
|
+
reducedPrice: 10,
|
|
2331
|
+
}),
|
|
2332
|
+
],
|
|
2333
|
+
});
|
|
2334
|
+
|
|
2335
|
+
group1.settings.oldPrices = [oldPrices1];
|
|
2336
|
+
|
|
2337
|
+
await group1.save();
|
|
2338
|
+
|
|
2339
|
+
const group2 = await new GroupFactory({ organization, period }).create();
|
|
2340
|
+
group2.settings.prices = [];
|
|
2341
|
+
group2.settings.name = new TranslatedString('group2');
|
|
2342
|
+
|
|
2343
|
+
// initial price with discount if same members in same category
|
|
2344
|
+
const oldPrices2 = OldGroupPrices.create({
|
|
2345
|
+
startDate: null,
|
|
2346
|
+
sameMemberOnlyDiscount: true,
|
|
2347
|
+
onlySameGroup: false,
|
|
2348
|
+
prices: [
|
|
2349
|
+
OldGroupPrice.create({
|
|
2350
|
+
price: 30,
|
|
2351
|
+
reducedPrice: 20,
|
|
2352
|
+
}),
|
|
2353
|
+
OldGroupPrice.create({
|
|
2354
|
+
price: 25,
|
|
2355
|
+
reducedPrice: 15,
|
|
2356
|
+
}),
|
|
2357
|
+
OldGroupPrice.create({
|
|
2358
|
+
price: 20,
|
|
2359
|
+
reducedPrice: 10,
|
|
2360
|
+
}),
|
|
2361
|
+
],
|
|
2362
|
+
});
|
|
2363
|
+
group2.settings.oldPrices = [oldPrices2];
|
|
2364
|
+
|
|
2365
|
+
await group2.save();
|
|
2366
|
+
|
|
2367
|
+
// add group 1 and to 2 to same category
|
|
2368
|
+
organizationPeriod.settings.categories = [
|
|
2369
|
+
GroupCategory.create({
|
|
2370
|
+
settings: GroupCategorySettings.create({ name: 'category1' }),
|
|
2371
|
+
// the groups are not in a category
|
|
2372
|
+
groupIds: [],
|
|
2373
|
+
}),
|
|
2374
|
+
];
|
|
2375
|
+
|
|
2376
|
+
await organizationPeriod.save();
|
|
2377
|
+
|
|
2378
|
+
// act
|
|
2379
|
+
await migratePrices();
|
|
2380
|
+
const g1 = await Group.getByID(group1.id);
|
|
2381
|
+
|
|
2382
|
+
// test group 1
|
|
2383
|
+
|
|
2384
|
+
// check prices (should be equal to old prices)
|
|
2385
|
+
expect(g1!.settings.prices).toHaveLength(1);
|
|
2386
|
+
expect(g1!.settings.prices[0].price.price).toBe(30);
|
|
2387
|
+
expect(g1!.settings.prices[0].price.reducedPrice).toBe(20);
|
|
2388
|
+
|
|
2389
|
+
// check bundle discounts (each price should have 1 bundle discount for family members in same category)
|
|
2390
|
+
expect(g1!.settings.prices[0].bundleDiscounts.size).toBe(1);
|
|
2391
|
+
|
|
2392
|
+
// custom discount for bundle discount of the first price should be null because the discounts are not different than the discounts for the bundle discount that is configured in the settings of the organization (which are the discounts for oldPrices1)
|
|
2393
|
+
expect([...g1!.settings.prices[0].bundleDiscounts.values()]).toEqual(
|
|
2394
|
+
expect.arrayContaining([
|
|
2395
|
+
expect.objectContaining({ customDiscounts: null }),
|
|
2396
|
+
]),
|
|
2397
|
+
);
|
|
2398
|
+
|
|
2399
|
+
// test group 2
|
|
2400
|
+
const g2 = await Group.getByID(group2.id);
|
|
2401
|
+
|
|
2402
|
+
// check prices (should be equal to old prices)
|
|
2403
|
+
expect(g2!.settings.prices).toHaveLength(1);
|
|
2404
|
+
expect(g2!.settings.prices[0].price.price).toBe(30);
|
|
2405
|
+
expect(g2!.settings.prices[0].price.reducedPrice).toBe(20);
|
|
2406
|
+
|
|
2407
|
+
// check bundle discounts (each price should have 1 bundle discount for family members in same category)
|
|
2408
|
+
expect(g2!.settings.prices[0].bundleDiscounts.size).toBe(1);
|
|
2409
|
+
|
|
2410
|
+
// custom discount for bundle discount of the second price should be null because the group is not in a category and thus should not be linked to the bundle discount of the other group
|
|
2411
|
+
expect([...g2!.settings.prices[0].bundleDiscounts.values()]).toEqual(
|
|
2412
|
+
expect.arrayContaining([
|
|
2413
|
+
expect.objectContaining({ customDiscounts: null }),
|
|
2414
|
+
]),
|
|
2415
|
+
);
|
|
2416
|
+
|
|
2417
|
+
// cleanup
|
|
2418
|
+
await group1.delete();
|
|
2419
|
+
await group2.delete();
|
|
2420
|
+
await organizationPeriod.delete();
|
|
2421
|
+
period.organizationId = null;
|
|
2422
|
+
await period.save();
|
|
2423
|
+
await organization.delete();
|
|
2424
|
+
await period.delete();
|
|
2425
|
+
});
|
|
2426
|
+
|
|
2427
|
+
test('Groups that are archived should not have bundle discounts from other groups that also do not have a category', async () => {
|
|
2428
|
+
// arrange
|
|
2429
|
+
const startDate = new Date(2025, 0, 1);
|
|
2430
|
+
const endDate = new Date(2025, 11, 31);
|
|
2431
|
+
const period = await new RegistrationPeriodFactory({ startDate, endDate }).create();
|
|
2432
|
+
const organization = await new OrganizationFactory({ period }).create();
|
|
2433
|
+
period.organizationId = organization.id;
|
|
2434
|
+
await period.save();
|
|
2435
|
+
const organizationPeriod = await new OrganizationRegistrationPeriodFactory({ organization, period }).create();
|
|
2436
|
+
|
|
2437
|
+
const group1 = await new GroupFactory({ organization, period }).create();
|
|
2438
|
+
// archived group
|
|
2439
|
+
group1.status = GroupStatus.Archived;
|
|
2440
|
+
group1.settings.prices = [];
|
|
2441
|
+
group1.settings.name = new TranslatedString('group1');
|
|
2442
|
+
|
|
2443
|
+
// initial price with discount if family members in same category
|
|
2444
|
+
const oldPrices1 = OldGroupPrices.create({
|
|
2445
|
+
startDate: null,
|
|
2446
|
+
sameMemberOnlyDiscount: false,
|
|
2447
|
+
onlySameGroup: false,
|
|
2448
|
+
prices: [
|
|
2449
|
+
OldGroupPrice.create({
|
|
2450
|
+
price: 30,
|
|
2451
|
+
reducedPrice: 20,
|
|
2452
|
+
}),
|
|
2453
|
+
OldGroupPrice.create({
|
|
2454
|
+
price: 25,
|
|
2455
|
+
reducedPrice: 15,
|
|
2456
|
+
}),
|
|
2457
|
+
OldGroupPrice.create({
|
|
2458
|
+
price: 20,
|
|
2459
|
+
reducedPrice: 10,
|
|
2460
|
+
}),
|
|
2461
|
+
],
|
|
2462
|
+
});
|
|
2463
|
+
|
|
2464
|
+
group1.settings.oldPrices = [oldPrices1];
|
|
2465
|
+
|
|
2466
|
+
await group1.save();
|
|
2467
|
+
|
|
2468
|
+
const group2 = await new GroupFactory({ organization, period }).create();
|
|
2469
|
+
// archived group
|
|
2470
|
+
group2.status = GroupStatus.Archived;
|
|
2471
|
+
group2.settings.prices = [];
|
|
2472
|
+
group2.settings.name = new TranslatedString('group2');
|
|
2473
|
+
|
|
2474
|
+
// initial price with discount if same members in same category
|
|
2475
|
+
const oldPrices2 = OldGroupPrices.create({
|
|
2476
|
+
startDate: null,
|
|
2477
|
+
sameMemberOnlyDiscount: true,
|
|
2478
|
+
onlySameGroup: false,
|
|
2479
|
+
prices: [
|
|
2480
|
+
OldGroupPrice.create({
|
|
2481
|
+
price: 30,
|
|
2482
|
+
reducedPrice: 20,
|
|
2483
|
+
}),
|
|
2484
|
+
OldGroupPrice.create({
|
|
2485
|
+
price: 25,
|
|
2486
|
+
reducedPrice: 15,
|
|
2487
|
+
}),
|
|
2488
|
+
OldGroupPrice.create({
|
|
2489
|
+
price: 20,
|
|
2490
|
+
reducedPrice: 10,
|
|
2491
|
+
}),
|
|
2492
|
+
],
|
|
2493
|
+
});
|
|
2494
|
+
group2.settings.oldPrices = [oldPrices2];
|
|
2495
|
+
|
|
2496
|
+
await group2.save();
|
|
2497
|
+
|
|
2498
|
+
// add group 1 and to 2 to same category
|
|
2499
|
+
organizationPeriod.settings.categories = [
|
|
2500
|
+
GroupCategory.create({
|
|
2501
|
+
settings: GroupCategorySettings.create({ name: 'category1' }),
|
|
2502
|
+
groupIds: [group1.id, group2.id],
|
|
2503
|
+
}),
|
|
2504
|
+
];
|
|
2505
|
+
|
|
2506
|
+
await organizationPeriod.save();
|
|
2507
|
+
|
|
2508
|
+
// act
|
|
2509
|
+
await migratePrices();
|
|
2510
|
+
const g1 = await Group.getByID(group1.id);
|
|
2511
|
+
|
|
2512
|
+
// test group 1
|
|
2513
|
+
|
|
2514
|
+
// check prices (should be equal to old prices)
|
|
2515
|
+
expect(g1!.settings.prices).toHaveLength(1);
|
|
2516
|
+
expect(g1!.settings.prices[0].price.price).toBe(30);
|
|
2517
|
+
expect(g1!.settings.prices[0].price.reducedPrice).toBe(20);
|
|
2518
|
+
|
|
2519
|
+
// check bundle discounts (each price should have 1 bundle discount for family members in same category)
|
|
2520
|
+
expect(g1!.settings.prices[0].bundleDiscounts.size).toBe(1);
|
|
2521
|
+
|
|
2522
|
+
// custom discount for bundle discount of the first price should be null because the discounts are not different than the discounts for the bundle discount that is configured in the settings of the organization (which are the discounts for oldPrices1)
|
|
2523
|
+
expect([...g1!.settings.prices[0].bundleDiscounts.values()]).toEqual(
|
|
2524
|
+
expect.arrayContaining([
|
|
2525
|
+
expect.objectContaining({ customDiscounts: null }),
|
|
2526
|
+
]),
|
|
2527
|
+
);
|
|
2528
|
+
|
|
2529
|
+
// test group 2
|
|
2530
|
+
const g2 = await Group.getByID(group2.id);
|
|
2531
|
+
|
|
2532
|
+
// check prices (should be equal to old prices)
|
|
2533
|
+
expect(g2!.settings.prices).toHaveLength(1);
|
|
2534
|
+
expect(g2!.settings.prices[0].price.price).toBe(30);
|
|
2535
|
+
expect(g2!.settings.prices[0].price.reducedPrice).toBe(20);
|
|
2536
|
+
|
|
2537
|
+
// check bundle discounts (each price should have 1 bundle discount for family members in same category)
|
|
2538
|
+
expect(g2!.settings.prices[0].bundleDiscounts.size).toBe(1);
|
|
2539
|
+
|
|
2540
|
+
// custom discount for bundle discount of the second price should be null because the group is archived and thus should not be linked to the bundle discount of the other group, even if they are in the same category still
|
|
2541
|
+
expect([...g2!.settings.prices[0].bundleDiscounts.values()]).toEqual(
|
|
2542
|
+
expect.arrayContaining([
|
|
2543
|
+
expect.objectContaining({ customDiscounts: null }),
|
|
2544
|
+
]),
|
|
2545
|
+
);
|
|
2546
|
+
|
|
2547
|
+
// cleanup
|
|
2548
|
+
await group1.delete();
|
|
2549
|
+
await group2.delete();
|
|
2550
|
+
await organizationPeriod.delete();
|
|
2551
|
+
period.organizationId = null;
|
|
2552
|
+
await period.save();
|
|
2553
|
+
await organization.delete();
|
|
2554
|
+
await period.delete();
|
|
2555
|
+
});
|
|
2556
|
+
|
|
2557
|
+
describe('Bundle discount should be reused if multiple groups have the same discount and onlySameGroup is true', () => {
|
|
2558
|
+
let period: RegistrationPeriod;
|
|
2559
|
+
let organization: Organization;
|
|
2560
|
+
let organizationPeriod: OrganizationRegistrationPeriod;
|
|
2561
|
+
let group1: Group;
|
|
2562
|
+
let group2: Group;
|
|
2563
|
+
|
|
2564
|
+
afterEach(async () => {
|
|
2565
|
+
if (group1) {
|
|
2566
|
+
await group1.delete();
|
|
2567
|
+
}
|
|
2568
|
+
|
|
2569
|
+
if (group2) {
|
|
2570
|
+
await group2.delete();
|
|
2571
|
+
}
|
|
2572
|
+
|
|
2573
|
+
if (organizationPeriod) {
|
|
2574
|
+
await organizationPeriod.delete();
|
|
2575
|
+
}
|
|
2576
|
+
|
|
2577
|
+
if (period) {
|
|
2578
|
+
period.organizationId = null;
|
|
2579
|
+
await period.save();
|
|
2580
|
+
}
|
|
2581
|
+
|
|
2582
|
+
if (organization) {
|
|
2583
|
+
await organization.delete();
|
|
2584
|
+
}
|
|
2585
|
+
|
|
2586
|
+
if (period) {
|
|
2587
|
+
await period.delete();
|
|
2588
|
+
}
|
|
2589
|
+
});
|
|
2590
|
+
|
|
2591
|
+
test('customDiscounts should be null if same discounts', async () => {
|
|
2592
|
+
// arrange
|
|
2593
|
+
const startDate = new Date(2025, 0, 1);
|
|
2594
|
+
const endDate = new Date(2025, 11, 31);
|
|
2595
|
+
period = await new RegistrationPeriodFactory({ startDate, endDate }).create();
|
|
2596
|
+
organization = await new OrganizationFactory({ period }).create();
|
|
2597
|
+
period.organizationId = organization.id;
|
|
2598
|
+
await period.save();
|
|
2599
|
+
organizationPeriod = await new OrganizationRegistrationPeriodFactory({ organization, period }).create();
|
|
2600
|
+
|
|
2601
|
+
group1 = await new GroupFactory({ organization, period }).create();
|
|
2602
|
+
group1.settings.prices = [];
|
|
2603
|
+
group1.settings.name = new TranslatedString('group1');
|
|
2604
|
+
|
|
2605
|
+
// initial price with discount if family members in same category
|
|
2606
|
+
const oldPrices1 = OldGroupPrices.create({
|
|
2607
|
+
startDate: null,
|
|
2608
|
+
sameMemberOnlyDiscount: false,
|
|
2609
|
+
onlySameGroup: true,
|
|
2610
|
+
prices: [
|
|
2611
|
+
OldGroupPrice.create({
|
|
2612
|
+
price: 30,
|
|
2613
|
+
reducedPrice: 20,
|
|
2614
|
+
}),
|
|
2615
|
+
OldGroupPrice.create({
|
|
2616
|
+
price: 25,
|
|
2617
|
+
reducedPrice: 15,
|
|
2618
|
+
}),
|
|
2619
|
+
OldGroupPrice.create({
|
|
2620
|
+
price: 20,
|
|
2621
|
+
reducedPrice: 10,
|
|
2622
|
+
}),
|
|
2623
|
+
],
|
|
2624
|
+
});
|
|
2625
|
+
|
|
2626
|
+
group1.settings.oldPrices = [oldPrices1];
|
|
2627
|
+
|
|
2628
|
+
await group1.save();
|
|
2629
|
+
|
|
2630
|
+
group2 = await new GroupFactory({ organization, period }).create();
|
|
2631
|
+
group2.settings.prices = [];
|
|
2632
|
+
group2.settings.name = new TranslatedString('group2');
|
|
2633
|
+
|
|
2634
|
+
// initial price with discount if family members in same category (same discount as oldPrices1)
|
|
2635
|
+
const oldPrices2 = OldGroupPrices.create({
|
|
2636
|
+
startDate: null,
|
|
2637
|
+
sameMemberOnlyDiscount: false,
|
|
2638
|
+
onlySameGroup: true,
|
|
2639
|
+
prices: [
|
|
2640
|
+
OldGroupPrice.create({
|
|
2641
|
+
price: 30,
|
|
2642
|
+
reducedPrice: 20,
|
|
2643
|
+
}),
|
|
2644
|
+
OldGroupPrice.create({
|
|
2645
|
+
price: 25,
|
|
2646
|
+
reducedPrice: 15,
|
|
2647
|
+
}),
|
|
2648
|
+
OldGroupPrice.create({
|
|
2649
|
+
price: 20,
|
|
2650
|
+
reducedPrice: 10,
|
|
2651
|
+
}),
|
|
2652
|
+
],
|
|
2653
|
+
});
|
|
2654
|
+
group2.settings.oldPrices = [oldPrices2];
|
|
2655
|
+
|
|
2656
|
+
await group2.save();
|
|
2657
|
+
|
|
2658
|
+
// add group 1 and to 2 to different category (should not make a difference)
|
|
2659
|
+
organizationPeriod.settings.categories = [
|
|
2660
|
+
GroupCategory.create({
|
|
2661
|
+
settings: GroupCategorySettings.create({ name: 'category1' }),
|
|
2662
|
+
groupIds: [group1.id],
|
|
2663
|
+
}),
|
|
2664
|
+
GroupCategory.create({
|
|
2665
|
+
settings: GroupCategorySettings.create({ name: 'category2' }),
|
|
2666
|
+
groupIds: [group2.id],
|
|
2667
|
+
}),
|
|
2668
|
+
];
|
|
2669
|
+
|
|
2670
|
+
await organizationPeriod.save();
|
|
2671
|
+
|
|
2672
|
+
// act
|
|
2673
|
+
await migratePrices();
|
|
2674
|
+
const g1 = await Group.getByID(group1.id);
|
|
2675
|
+
|
|
2676
|
+
// test group 1
|
|
2677
|
+
|
|
2678
|
+
// check prices (should be equal to old prices)
|
|
2679
|
+
expect(g1!.settings.prices).toHaveLength(1);
|
|
2680
|
+
expect(g1!.settings.prices[0].price.price).toBe(30);
|
|
2681
|
+
expect(g1!.settings.prices[0].price.reducedPrice).toBe(20);
|
|
2682
|
+
|
|
2683
|
+
// check bundle discounts (each price should have 1 bundle discount for family members in same category)
|
|
2684
|
+
expect(g1!.settings.prices[0].bundleDiscounts.size).toBe(1);
|
|
2685
|
+
|
|
2686
|
+
// custom discount for bundle discount of the first price should be null because the discounts are not different than the discounts for the bundle discount that is configured in the settings of the organization (which are the discounts for oldPrices1)
|
|
2687
|
+
expect([...g1!.settings.prices[0].bundleDiscounts.values()]).toEqual(
|
|
2688
|
+
expect.arrayContaining([
|
|
2689
|
+
expect.objectContaining({ customDiscounts: null }),
|
|
2690
|
+
]),
|
|
2691
|
+
);
|
|
2692
|
+
|
|
2693
|
+
// test group 2
|
|
2694
|
+
const g2 = await Group.getByID(group2.id);
|
|
2695
|
+
|
|
2696
|
+
// check prices (should be equal to old prices)
|
|
2697
|
+
expect(g2!.settings.prices).toHaveLength(1);
|
|
2698
|
+
expect(g2!.settings.prices[0].price.price).toBe(30);
|
|
2699
|
+
expect(g2!.settings.prices[0].price.reducedPrice).toBe(20);
|
|
2700
|
+
|
|
2701
|
+
// check bundle discounts (each price should have 1 bundle discount for family members in same category)
|
|
2702
|
+
expect(g2!.settings.prices[0].bundleDiscounts.size).toBe(1);
|
|
2703
|
+
|
|
2704
|
+
// custom discount for bundle discount of the second price should be null because the discounts are equal to the discounts for the bundle discount that is configured in the settings of the organization (which are the discounts for oldPrices1)
|
|
2705
|
+
expect([...g2!.settings.prices[0].bundleDiscounts.values()]).toEqual(
|
|
2706
|
+
expect.arrayContaining([
|
|
2707
|
+
expect.objectContaining({ customDiscounts: null }),
|
|
2708
|
+
]),
|
|
2709
|
+
);
|
|
2710
|
+
});
|
|
2711
|
+
|
|
2712
|
+
test('should set customDiscounts if different discounts', async () => {
|
|
2713
|
+
// arrange
|
|
2714
|
+
const startDate = new Date(2025, 0, 1);
|
|
2715
|
+
const endDate = new Date(2025, 11, 31);
|
|
2716
|
+
period = await new RegistrationPeriodFactory({ startDate, endDate }).create();
|
|
2717
|
+
organization = await new OrganizationFactory({ period }).create();
|
|
2718
|
+
period.organizationId = organization.id;
|
|
2719
|
+
await period.save();
|
|
2720
|
+
organizationPeriod = await new OrganizationRegistrationPeriodFactory({ organization, period }).create();
|
|
2721
|
+
|
|
2722
|
+
group1 = await new GroupFactory({ organization, period }).create();
|
|
2723
|
+
group1.settings.prices = [];
|
|
2724
|
+
group1.settings.name = new TranslatedString('group1');
|
|
2725
|
+
|
|
2726
|
+
// initial price with discount if family members in same category
|
|
2727
|
+
const oldPrices1 = OldGroupPrices.create({
|
|
2728
|
+
startDate: null,
|
|
2729
|
+
sameMemberOnlyDiscount: false,
|
|
2730
|
+
onlySameGroup: true,
|
|
2731
|
+
prices: [
|
|
2732
|
+
OldGroupPrice.create({
|
|
2733
|
+
price: 30,
|
|
2734
|
+
reducedPrice: 20,
|
|
2735
|
+
}),
|
|
2736
|
+
OldGroupPrice.create({
|
|
2737
|
+
price: 25,
|
|
2738
|
+
reducedPrice: 15,
|
|
2739
|
+
}),
|
|
2740
|
+
OldGroupPrice.create({
|
|
2741
|
+
price: 20,
|
|
2742
|
+
reducedPrice: 10,
|
|
2743
|
+
}),
|
|
2744
|
+
],
|
|
2745
|
+
});
|
|
2746
|
+
|
|
2747
|
+
group1.settings.oldPrices = [oldPrices1];
|
|
2748
|
+
|
|
2749
|
+
await group1.save();
|
|
2750
|
+
|
|
2751
|
+
group2 = await new GroupFactory({ organization, period }).create();
|
|
2752
|
+
group2.settings.prices = [];
|
|
2753
|
+
group2.settings.name = new TranslatedString('group2');
|
|
2754
|
+
|
|
2755
|
+
// initial price with discount if family members in same category (different discount as oldPrices1)
|
|
2756
|
+
const oldPrices2 = OldGroupPrices.create({
|
|
2757
|
+
startDate: null,
|
|
2758
|
+
sameMemberOnlyDiscount: false,
|
|
2759
|
+
onlySameGroup: true,
|
|
2760
|
+
prices: [
|
|
2761
|
+
OldGroupPrice.create({
|
|
2762
|
+
price: 30,
|
|
2763
|
+
reducedPrice: 20,
|
|
2764
|
+
}),
|
|
2765
|
+
OldGroupPrice.create({
|
|
2766
|
+
price: 25,
|
|
2767
|
+
reducedPrice: 15,
|
|
2768
|
+
}),
|
|
2769
|
+
OldGroupPrice.create({
|
|
2770
|
+
// only difference
|
|
2771
|
+
price: 21,
|
|
2772
|
+
reducedPrice: 10,
|
|
2773
|
+
}),
|
|
2774
|
+
],
|
|
2775
|
+
});
|
|
2776
|
+
group2.settings.oldPrices = [oldPrices2];
|
|
2777
|
+
|
|
2778
|
+
await group2.save();
|
|
2779
|
+
|
|
2780
|
+
// add group 1 and to 2 to different category (should not make a difference)
|
|
2781
|
+
organizationPeriod.settings.categories = [
|
|
2782
|
+
GroupCategory.create({
|
|
2783
|
+
settings: GroupCategorySettings.create({ name: 'category1' }),
|
|
2784
|
+
groupIds: [group1.id],
|
|
2785
|
+
}),
|
|
2786
|
+
GroupCategory.create({
|
|
2787
|
+
settings: GroupCategorySettings.create({ name: 'category2' }),
|
|
2788
|
+
groupIds: [group2.id],
|
|
2789
|
+
}),
|
|
2790
|
+
];
|
|
2791
|
+
|
|
2792
|
+
await organizationPeriod.save();
|
|
2793
|
+
|
|
2794
|
+
// act
|
|
2795
|
+
await migratePrices();
|
|
2796
|
+
const g1 = await Group.getByID(group1.id);
|
|
2797
|
+
|
|
2798
|
+
// test group 1
|
|
2799
|
+
|
|
2800
|
+
// check prices (should be equal to old prices)
|
|
2801
|
+
expect(g1!.settings.prices).toHaveLength(1);
|
|
2802
|
+
expect(g1!.settings.prices[0].price.price).toBe(30);
|
|
2803
|
+
expect(g1!.settings.prices[0].price.reducedPrice).toBe(20);
|
|
2804
|
+
|
|
2805
|
+
// check bundle discounts (each price should have 1 bundle discount for family members in same category)
|
|
2806
|
+
expect(g1!.settings.prices[0].bundleDiscounts.size).toBe(1);
|
|
2807
|
+
|
|
2808
|
+
// custom discount for bundle discount of the first price should be null because the discounts are not different than the discounts for the bundle discount that is configured in the settings of the organization (which are the discounts for oldPrices1)
|
|
2809
|
+
expect([...g1!.settings.prices[0].bundleDiscounts.values()]).toEqual(
|
|
2810
|
+
expect.arrayContaining([
|
|
2811
|
+
expect.objectContaining({ customDiscounts: null }),
|
|
2812
|
+
]),
|
|
2813
|
+
);
|
|
2814
|
+
|
|
2815
|
+
// test group 2
|
|
2816
|
+
const g2 = await Group.getByID(group2.id);
|
|
2817
|
+
|
|
2818
|
+
// check prices (should be equal to old prices)
|
|
2819
|
+
expect(g2!.settings.prices).toHaveLength(1);
|
|
2820
|
+
expect(g2!.settings.prices[0].price.price).toBe(30);
|
|
2821
|
+
expect(g2!.settings.prices[0].price.reducedPrice).toBe(20);
|
|
2822
|
+
|
|
2823
|
+
// check bundle discounts (each price should have 1 bundle discount for family members in same category)
|
|
2824
|
+
expect(g2!.settings.prices[0].bundleDiscounts.size).toBe(1);
|
|
2825
|
+
|
|
2826
|
+
// custom discount for bundle discount of the second price should not be null because the discounts are different than the discounts for the bundle discount that is configured in the settings of the organization (which are the discounts for oldPrices1)
|
|
2827
|
+
expect([...g2!.settings.prices[0].bundleDiscounts.values()]).toEqual(
|
|
2828
|
+
expect.arrayContaining([
|
|
2829
|
+
expect.objectContaining({ customDiscounts: expect.arrayContaining([
|
|
2830
|
+
expect.objectContaining({
|
|
2831
|
+
type: GroupPriceDiscountType.Fixed,
|
|
2832
|
+
value: expect.objectContaining({
|
|
2833
|
+
price: 5,
|
|
2834
|
+
reducedPrice: 5,
|
|
2835
|
+
}),
|
|
2836
|
+
}),
|
|
2837
|
+
expect.objectContaining({
|
|
2838
|
+
type: GroupPriceDiscountType.Fixed,
|
|
2839
|
+
value: expect.objectContaining({
|
|
2840
|
+
price: 9,
|
|
2841
|
+
reducedPrice: 10,
|
|
2842
|
+
}),
|
|
2843
|
+
}),
|
|
2844
|
+
]) }),
|
|
2845
|
+
]),
|
|
2846
|
+
);
|
|
2847
|
+
});
|
|
2848
|
+
|
|
2849
|
+
test('customDiscounts should not be reused if sameMemberOnlyDiscount is different', async () => {
|
|
2850
|
+
// arrange
|
|
2851
|
+
const startDate = new Date(2025, 0, 1);
|
|
2852
|
+
const endDate = new Date(2025, 11, 31);
|
|
2853
|
+
period = await new RegistrationPeriodFactory({ startDate, endDate }).create();
|
|
2854
|
+
organization = await new OrganizationFactory({ period }).create();
|
|
2855
|
+
period.organizationId = organization.id;
|
|
2856
|
+
await period.save();
|
|
2857
|
+
organizationPeriod = await new OrganizationRegistrationPeriodFactory({ organization, period }).create();
|
|
2858
|
+
|
|
2859
|
+
group1 = await new GroupFactory({ organization, period }).create();
|
|
2860
|
+
group1.settings.prices = [];
|
|
2861
|
+
group1.settings.name = new TranslatedString('group1');
|
|
2862
|
+
|
|
2863
|
+
// initial price with discount if family members in same category
|
|
2864
|
+
const oldPrices1 = OldGroupPrices.create({
|
|
2865
|
+
startDate: null,
|
|
2866
|
+
sameMemberOnlyDiscount: false,
|
|
2867
|
+
onlySameGroup: true,
|
|
2868
|
+
prices: [
|
|
2869
|
+
OldGroupPrice.create({
|
|
2870
|
+
price: 30,
|
|
2871
|
+
reducedPrice: 20,
|
|
2872
|
+
}),
|
|
2873
|
+
OldGroupPrice.create({
|
|
2874
|
+
price: 25,
|
|
2875
|
+
reducedPrice: 15,
|
|
2876
|
+
}),
|
|
2877
|
+
OldGroupPrice.create({
|
|
2878
|
+
price: 20,
|
|
2879
|
+
reducedPrice: 10,
|
|
2880
|
+
}),
|
|
2881
|
+
],
|
|
2882
|
+
});
|
|
2883
|
+
|
|
2884
|
+
group1.settings.oldPrices = [oldPrices1];
|
|
2885
|
+
|
|
2886
|
+
await group1.save();
|
|
2887
|
+
|
|
2888
|
+
group2 = await new GroupFactory({ organization, period }).create();
|
|
2889
|
+
group2.settings.prices = [];
|
|
2890
|
+
group2.settings.name = new TranslatedString('group2');
|
|
2891
|
+
|
|
2892
|
+
// initial price with discount if family members in same category (same discount as oldPrices1)
|
|
2893
|
+
const oldPrices2 = OldGroupPrices.create({
|
|
2894
|
+
startDate: null,
|
|
2895
|
+
sameMemberOnlyDiscount: true,
|
|
2896
|
+
onlySameGroup: true,
|
|
2897
|
+
prices: [
|
|
2898
|
+
OldGroupPrice.create({
|
|
2899
|
+
price: 30,
|
|
2900
|
+
reducedPrice: 20,
|
|
2901
|
+
}),
|
|
2902
|
+
OldGroupPrice.create({
|
|
2903
|
+
price: 25,
|
|
2904
|
+
reducedPrice: 15,
|
|
2905
|
+
}),
|
|
2906
|
+
OldGroupPrice.create({
|
|
2907
|
+
price: 20,
|
|
2908
|
+
reducedPrice: 10,
|
|
2909
|
+
}),
|
|
2910
|
+
],
|
|
2911
|
+
});
|
|
2912
|
+
group2.settings.oldPrices = [oldPrices2];
|
|
2913
|
+
|
|
2914
|
+
await group2.save();
|
|
2915
|
+
|
|
2916
|
+
// add group 1 and to 2 to different category (should not make a difference)
|
|
2917
|
+
organizationPeriod.settings.categories = [
|
|
2918
|
+
GroupCategory.create({
|
|
2919
|
+
settings: GroupCategorySettings.create({ name: 'category1' }),
|
|
2920
|
+
groupIds: [group1.id],
|
|
2921
|
+
}),
|
|
2922
|
+
GroupCategory.create({
|
|
2923
|
+
settings: GroupCategorySettings.create({ name: 'category2' }),
|
|
2924
|
+
groupIds: [group2.id],
|
|
2925
|
+
}),
|
|
2926
|
+
];
|
|
2927
|
+
|
|
2928
|
+
await organizationPeriod.save();
|
|
2929
|
+
|
|
2930
|
+
// act
|
|
2931
|
+
await migratePrices();
|
|
2932
|
+
const g1 = await Group.getByID(group1.id);
|
|
2933
|
+
|
|
2934
|
+
// test group 1
|
|
2935
|
+
|
|
2936
|
+
// check prices (should be equal to old prices)
|
|
2937
|
+
expect(g1!.settings.prices).toHaveLength(1);
|
|
2938
|
+
expect(g1!.settings.prices[0].price.price).toBe(30);
|
|
2939
|
+
expect(g1!.settings.prices[0].price.reducedPrice).toBe(20);
|
|
2940
|
+
|
|
2941
|
+
// check bundle discounts (each price should have 1 bundle discount for family members in same category)
|
|
2942
|
+
expect(g1!.settings.prices[0].bundleDiscounts.size).toBe(1);
|
|
2943
|
+
|
|
2944
|
+
// custom discount for bundle discount of the first price should be null because the discounts are not different than the discounts for the bundle discount that is configured in the settings of the organization (which are the discounts for oldPrices1)
|
|
2945
|
+
expect([...g1!.settings.prices[0].bundleDiscounts.values()]).toEqual(
|
|
2946
|
+
expect.arrayContaining([
|
|
2947
|
+
expect.objectContaining({ customDiscounts: null }),
|
|
2948
|
+
]),
|
|
2949
|
+
);
|
|
2950
|
+
|
|
2951
|
+
// test group 2
|
|
2952
|
+
const g2 = await Group.getByID(group2.id);
|
|
2953
|
+
|
|
2954
|
+
// check prices (should be equal to old prices)
|
|
2955
|
+
expect(g2!.settings.prices).toHaveLength(1);
|
|
2956
|
+
expect(g2!.settings.prices[0].price.price).toBe(30);
|
|
2957
|
+
expect(g2!.settings.prices[0].price.reducedPrice).toBe(20);
|
|
2958
|
+
|
|
2959
|
+
// check bundle discounts (each price should have 1 bundle discount for family members in same category)
|
|
2960
|
+
expect(g2!.settings.prices[0].bundleDiscounts.size).toBe(1);
|
|
2961
|
+
|
|
2962
|
+
// custom discount for bundle discount of the second price should be null because the discounts are equal to the discounts for the bundle discount that is configured in the settings of the organization (which are the discounts for oldPrices1)
|
|
2963
|
+
expect([...g2!.settings.prices[0].bundleDiscounts.values()]).toEqual(
|
|
2964
|
+
expect.arrayContaining([
|
|
2965
|
+
expect.objectContaining({ customDiscounts: null }),
|
|
2966
|
+
]),
|
|
2967
|
+
);
|
|
2968
|
+
|
|
2969
|
+
// check organization registration period
|
|
2970
|
+
const orgPeriod = await OrganizationRegistrationPeriod.getByID(organizationPeriod.id);
|
|
2971
|
+
|
|
2972
|
+
// the organization period should have 2 bundle discounts: 1 for family members in category 1 and 1 for same members in category 1
|
|
2973
|
+
expect(orgPeriod!.settings.bundleDiscounts).toHaveLength(2);
|
|
2974
|
+
|
|
2975
|
+
expect(orgPeriod!.settings.bundleDiscounts).toEqual(
|
|
2976
|
+
expect.arrayContaining([
|
|
2977
|
+
// bundle discount of oldPrices1 of group1
|
|
2978
|
+
expect.objectContaining({
|
|
2979
|
+
countWholeFamily: true,
|
|
2980
|
+
countPerGroup: true,
|
|
2981
|
+
// should contain the differences for oldPrices1
|
|
2982
|
+
discounts: expect.arrayContaining([
|
|
2983
|
+
expect.objectContaining({
|
|
2984
|
+
type: GroupPriceDiscountType.Fixed,
|
|
2985
|
+
value: expect.objectContaining({
|
|
2986
|
+
price: 5,
|
|
2987
|
+
reducedPrice: 5,
|
|
2988
|
+
}),
|
|
2989
|
+
}),
|
|
2990
|
+
expect.objectContaining({
|
|
2991
|
+
type: GroupPriceDiscountType.Fixed,
|
|
2992
|
+
value: expect.objectContaining({
|
|
2993
|
+
price: 10,
|
|
2994
|
+
reducedPrice: 10,
|
|
2995
|
+
}),
|
|
2996
|
+
}),
|
|
2997
|
+
]) }),
|
|
2998
|
+
// bundle discount of oldPrices2 of group 2
|
|
2999
|
+
expect.objectContaining({
|
|
3000
|
+
countWholeFamily: false,
|
|
3001
|
+
countPerGroup: true,
|
|
3002
|
+
// should contain the differences for oldPrices1
|
|
3003
|
+
discounts: expect.arrayContaining([
|
|
3004
|
+
expect.objectContaining({
|
|
3005
|
+
type: GroupPriceDiscountType.Fixed,
|
|
3006
|
+
value: expect.objectContaining({
|
|
3007
|
+
price: 5,
|
|
3008
|
+
reducedPrice: 5,
|
|
3009
|
+
}),
|
|
3010
|
+
}),
|
|
3011
|
+
expect.objectContaining({
|
|
3012
|
+
type: GroupPriceDiscountType.Fixed,
|
|
3013
|
+
value: expect.objectContaining({
|
|
3014
|
+
price: 10,
|
|
3015
|
+
reducedPrice: 10,
|
|
3016
|
+
}),
|
|
3017
|
+
}),
|
|
3018
|
+
]) }),
|
|
3019
|
+
]),
|
|
3020
|
+
);
|
|
3021
|
+
});
|
|
3022
|
+
});
|
|
3023
|
+
});
|