@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,408 @@
|
|
|
1
|
+
import { Migration } from '@simonbackx/simple-database';
|
|
2
|
+
import { Group, OrganizationRegistrationPeriod } from '@stamhoofd/models';
|
|
3
|
+
import { BundleDiscount, BundleDiscountGroupPriceSettings, GroupCategory, GroupPrice, GroupPriceDiscount, GroupPriceDiscountType, GroupStatus, GroupType, OldGroupPrice, OldGroupPrices, ReduceablePrice, TranslatedString } from '@stamhoofd/structures';
|
|
4
|
+
import { Formatter } from '@stamhoofd/utility';
|
|
5
|
+
|
|
6
|
+
const UNNASSIGNED_KEY = 'unassigned';
|
|
7
|
+
|
|
8
|
+
export default new Migration(async () => {
|
|
9
|
+
if (STAMHOOFD.environment === 'test') {
|
|
10
|
+
console.log('skipped in tests');
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (STAMHOOFD.platformName.toLowerCase() !== 'stamhoofd') {
|
|
15
|
+
console.log('skipped for platform (only runs for Stamhoofd): ' + STAMHOOFD.platformName);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
await migratePrices();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
export async function migratePrices() {
|
|
23
|
+
for await (const period of OrganizationRegistrationPeriod.select().all()) {
|
|
24
|
+
// groups
|
|
25
|
+
const allGroups = await Group.select()
|
|
26
|
+
.where('periodId', period.periodId)
|
|
27
|
+
.andWhere('organizationId', period.organizationId)
|
|
28
|
+
.fetch();
|
|
29
|
+
|
|
30
|
+
if (allGroups.every(g => g.settings.prices.length > 0)) {
|
|
31
|
+
// already migrated
|
|
32
|
+
console.log('Skipping period (already migrated): ' + period.id);
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// make sure bundle discounts are empty (just in case, if previous migration failed)
|
|
37
|
+
period.settings.bundleDiscounts = [];
|
|
38
|
+
|
|
39
|
+
const filteredGroups: Group[] = [];
|
|
40
|
+
const archivedGroups: Group[] = [];
|
|
41
|
+
|
|
42
|
+
const sameGroupDiscountForFamily: BundleDiscount | null = null;
|
|
43
|
+
const sameGroupDiscountForMember: BundleDiscount | null = null;
|
|
44
|
+
|
|
45
|
+
// filter relevant groups, cleanup other groups
|
|
46
|
+
for (const group of allGroups) {
|
|
47
|
+
// make sure prices are empty (just in case, if previous migration failed)
|
|
48
|
+
group.settings.prices = [GroupPrice.create({})];
|
|
49
|
+
|
|
50
|
+
if (group.type !== GroupType.Membership || group.deletedAt !== null) {
|
|
51
|
+
group.settings.prices = [
|
|
52
|
+
GroupPrice.create({
|
|
53
|
+
name: new TranslatedString('Standaard tarief'),
|
|
54
|
+
startDate: null,
|
|
55
|
+
endDate: null,
|
|
56
|
+
price: ReduceablePrice.create({
|
|
57
|
+
price: 0,
|
|
58
|
+
reducedPrice: null,
|
|
59
|
+
}),
|
|
60
|
+
}),
|
|
61
|
+
];
|
|
62
|
+
}
|
|
63
|
+
else if (group.status === GroupStatus.Archived) {
|
|
64
|
+
archivedGroups.push(group);
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
filteredGroups.push(group);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// group by category
|
|
72
|
+
const categoryMap = createCategoryMap(filteredGroups, archivedGroups, period.settings.categories);
|
|
73
|
+
const allBundleDiscounts: BundleDiscount[] = [];
|
|
74
|
+
|
|
75
|
+
// loop categories
|
|
76
|
+
for (const [categoryId, groups] of categoryMap.entries()) {
|
|
77
|
+
const category: GroupCategory | undefined = period.settings.categories.find(c => c.id === categoryId)!;
|
|
78
|
+
const isUnassigned = categoryId === UNNASSIGNED_KEY;
|
|
79
|
+
let categoryDiscountForFamily: BundleDiscount | null = null;
|
|
80
|
+
let categoryDiscountForMember: BundleDiscount | null = null;
|
|
81
|
+
|
|
82
|
+
// first find category discounts
|
|
83
|
+
if (!isUnassigned) {
|
|
84
|
+
for (const group of groups) {
|
|
85
|
+
if (group.settings.prices.length > 1 || (group.settings.prices.length === 1 && group.settings.prices[0].bundleDiscounts.size > 0)) {
|
|
86
|
+
// should never happen because prices are reset
|
|
87
|
+
throw new Error('Prices are not empty: ' + group.id);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// sorted old prices
|
|
91
|
+
const oldPricesArray = group.settings.oldPrices.slice().sort((a, b) => {
|
|
92
|
+
if (a.startDate === null) {
|
|
93
|
+
return -1;
|
|
94
|
+
}
|
|
95
|
+
if (b.startDate === null) {
|
|
96
|
+
return 1;
|
|
97
|
+
}
|
|
98
|
+
return a.startDate.getTime() - b.startDate.getTime();
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
for (const oldPrices of oldPricesArray) {
|
|
102
|
+
if (oldPrices.prices.length < 2) {
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (oldPrices.onlySameGroup) {
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const countWholeFamily = !oldPrices.sameMemberOnlyDiscount;
|
|
111
|
+
const isCategoryDiscount = !oldPrices.onlySameGroup;
|
|
112
|
+
|
|
113
|
+
if (isCategoryDiscount) {
|
|
114
|
+
if (countWholeFamily) {
|
|
115
|
+
if (!categoryDiscountForFamily) {
|
|
116
|
+
categoryDiscountForFamily = createBundleDiscount(oldPrices, category, allBundleDiscounts);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
else if (!categoryDiscountForMember) {
|
|
120
|
+
categoryDiscountForMember = createBundleDiscount(oldPrices, category, allBundleDiscounts);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (categoryDiscountForFamily && categoryDiscountForMember) {
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// migrate prices for group
|
|
132
|
+
for (const group of groups) {
|
|
133
|
+
// sorted old prices
|
|
134
|
+
const oldPricesArray = group.settings.oldPrices.slice().sort((a, b) => {
|
|
135
|
+
if (a.startDate === null) {
|
|
136
|
+
return -1;
|
|
137
|
+
}
|
|
138
|
+
if (b.startDate === null) {
|
|
139
|
+
return 1;
|
|
140
|
+
}
|
|
141
|
+
return a.startDate.getTime() - b.startDate.getTime();
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
if (oldPricesArray.length === 0) {
|
|
145
|
+
oldPricesArray.push(OldGroupPrices.create({
|
|
146
|
+
startDate: null,
|
|
147
|
+
prices: [],
|
|
148
|
+
sameMemberOnlyDiscount: false,
|
|
149
|
+
onlySameGroup: true,
|
|
150
|
+
}));
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const prices: GroupPrice[] = [];
|
|
154
|
+
|
|
155
|
+
// loop different tarriffs (with different start dates)
|
|
156
|
+
for (let i = 0; i < oldPricesArray.length; i++) {
|
|
157
|
+
const oldPrices: OldGroupPrices = oldPricesArray[i];
|
|
158
|
+
const isCategoryDiscount = !oldPrices.onlySameGroup;
|
|
159
|
+
const next: OldGroupPrices | undefined = oldPricesArray[i + 1];
|
|
160
|
+
|
|
161
|
+
const firstPrice = oldPrices.prices[0] ?? OldGroupPrice.create({
|
|
162
|
+
price: 0,
|
|
163
|
+
reducedPrice: null,
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
const groupPrice = GroupPrice.create({
|
|
167
|
+
name: new TranslatedString(oldPrices.startDate === null
|
|
168
|
+
? 'Standaard tarief'
|
|
169
|
+
: `Vanaf ${formatDate(oldPrices.startDate)}`),
|
|
170
|
+
startDate: oldPrices.startDate ? new Date(oldPrices.startDate) : null,
|
|
171
|
+
endDate: next?.startDate ? new Date(next.startDate.getTime() - 1) : null,
|
|
172
|
+
price: ReduceablePrice.create({
|
|
173
|
+
price: firstPrice.price,
|
|
174
|
+
reducedPrice: firstPrice.reducedPrice,
|
|
175
|
+
}),
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
const countWholeFamily = !oldPrices.sameMemberOnlyDiscount;
|
|
179
|
+
|
|
180
|
+
const discounts = createDiscounts(oldPrices);
|
|
181
|
+
|
|
182
|
+
if (categoryDiscountForFamily) {
|
|
183
|
+
// discount should be zero if discount is not a category discount (group discount) or if the discount is not for family members (but should be linked however)
|
|
184
|
+
const isZeroDiscount = !isCategoryDiscount || !countWholeFamily;
|
|
185
|
+
|
|
186
|
+
// set custom discounts if discounts are different
|
|
187
|
+
let customDiscounts: GroupPriceDiscount[] | undefined = discounts;
|
|
188
|
+
|
|
189
|
+
if (isZeroDiscount) {
|
|
190
|
+
customDiscounts = [GroupPriceDiscount.create({
|
|
191
|
+
type: GroupPriceDiscountType.Fixed,
|
|
192
|
+
value: ReduceablePrice.create({
|
|
193
|
+
price: 0,
|
|
194
|
+
reducedPrice: null,
|
|
195
|
+
}) })];
|
|
196
|
+
}
|
|
197
|
+
else if (areDiscountsEqual(categoryDiscountForFamily.discounts, discounts)) {
|
|
198
|
+
customDiscounts = undefined;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// add discount
|
|
202
|
+
groupPrice.bundleDiscounts.set(
|
|
203
|
+
categoryDiscountForFamily.id, BundleDiscountGroupPriceSettings.create({
|
|
204
|
+
name: categoryDiscountForFamily.name,
|
|
205
|
+
customDiscounts,
|
|
206
|
+
}));
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (categoryDiscountForMember) {
|
|
210
|
+
// discount should be zero if discount is not a category discount (group discount) or if the discount is for family members (but should be linked however)
|
|
211
|
+
const isZeroDiscount = !isCategoryDiscount || countWholeFamily;
|
|
212
|
+
|
|
213
|
+
// set custom discounts if discounts are different
|
|
214
|
+
let customDiscounts: GroupPriceDiscount[] | undefined = discounts;
|
|
215
|
+
|
|
216
|
+
if (isZeroDiscount) {
|
|
217
|
+
customDiscounts = [GroupPriceDiscount.create({
|
|
218
|
+
type: GroupPriceDiscountType.Fixed,
|
|
219
|
+
value: ReduceablePrice.create({
|
|
220
|
+
price: 0,
|
|
221
|
+
reducedPrice: null,
|
|
222
|
+
}) })];
|
|
223
|
+
}
|
|
224
|
+
else if (areDiscountsEqual(categoryDiscountForMember.discounts, discounts)) {
|
|
225
|
+
customDiscounts = undefined;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// add discount
|
|
229
|
+
groupPrice.bundleDiscounts.set(
|
|
230
|
+
categoryDiscountForMember.id, BundleDiscountGroupPriceSettings.create({
|
|
231
|
+
name: categoryDiscountForMember.name,
|
|
232
|
+
customDiscounts,
|
|
233
|
+
}));
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// in other cases the bundle discount will have been added already (as a category discount)
|
|
237
|
+
if (oldPrices.prices.length > 1 && (oldPrices.onlySameGroup || isUnassigned)) {
|
|
238
|
+
let bundleDiscount: BundleDiscount | undefined = undefined;
|
|
239
|
+
let customDiscounts: GroupPriceDiscount[] | undefined = undefined;
|
|
240
|
+
|
|
241
|
+
// reuse existing bundle discount if only same group
|
|
242
|
+
if (oldPrices.onlySameGroup) {
|
|
243
|
+
// search if equal bundle discounts exist
|
|
244
|
+
const equalBundleDiscount = allBundleDiscounts.find(bd => bd.countPerGroup && bd.countWholeFamily === countWholeFamily);
|
|
245
|
+
|
|
246
|
+
if (equalBundleDiscount) {
|
|
247
|
+
bundleDiscount = equalBundleDiscount;
|
|
248
|
+
|
|
249
|
+
// set custom discounts if discounts are different
|
|
250
|
+
if (!areDiscountsEqual(bundleDiscount.discounts, discounts)) {
|
|
251
|
+
customDiscounts = discounts;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if (!bundleDiscount) {
|
|
257
|
+
bundleDiscount = createBundleDiscount(oldPrices, category, allBundleDiscounts);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
groupPrice.bundleDiscounts.set(bundleDiscount.id, BundleDiscountGroupPriceSettings.create({
|
|
261
|
+
name: bundleDiscount.name,
|
|
262
|
+
customDiscounts,
|
|
263
|
+
}));
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
prices.push(groupPrice);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
group.settings.prices = prices;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// set bundle discounts on period and save
|
|
274
|
+
if (allBundleDiscounts.length) {
|
|
275
|
+
period.settings.bundleDiscounts = [...allBundleDiscounts];
|
|
276
|
+
await period.save();
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// save groups
|
|
280
|
+
for (const group of allGroups) {
|
|
281
|
+
await group.save();
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function createCategoryMap(groups: Group[], archivedGroups: Group[], categories: GroupCategory[]) {
|
|
287
|
+
// sort groups per category
|
|
288
|
+
const categoryMap = new Map<string, Group[]>();
|
|
289
|
+
const foundGroups = new Set<string>();
|
|
290
|
+
|
|
291
|
+
for (const category of categories) {
|
|
292
|
+
for (const groupId of category.groupIds) {
|
|
293
|
+
const group = groups.find(g => g.id === groupId);
|
|
294
|
+
if (group) {
|
|
295
|
+
foundGroups.add(group.id);
|
|
296
|
+
const otherGroups = categoryMap.get(category.id);
|
|
297
|
+
if (otherGroups) {
|
|
298
|
+
otherGroups.push(group);
|
|
299
|
+
}
|
|
300
|
+
else {
|
|
301
|
+
categoryMap.set(category.id, [group]);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const unassignedGroups = groups.filter(g => !foundGroups.has(g.id));
|
|
308
|
+
// add archived groups to unassigned groups
|
|
309
|
+
unassignedGroups.push(...archivedGroups);
|
|
310
|
+
|
|
311
|
+
if (unassignedGroups.length) {
|
|
312
|
+
categoryMap.set(UNNASSIGNED_KEY, unassignedGroups);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
return categoryMap;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function createDiscounts(oldPrices: OldGroupPrices): GroupPriceDiscount[] {
|
|
319
|
+
if (oldPrices.prices.length < 2) {
|
|
320
|
+
return [GroupPriceDiscount.create({
|
|
321
|
+
type: GroupPriceDiscountType.Fixed,
|
|
322
|
+
value: ReduceablePrice.create({
|
|
323
|
+
price: 0,
|
|
324
|
+
reducedPrice: null,
|
|
325
|
+
}) })];
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const discounts: GroupPriceDiscount[] = [];
|
|
329
|
+
const firstPrice = oldPrices.prices[0];
|
|
330
|
+
const baseReducedPrice = firstPrice.reducedPrice === null ? firstPrice.price : firstPrice.reducedPrice;
|
|
331
|
+
|
|
332
|
+
// skip first one
|
|
333
|
+
for (let i = 1; i < oldPrices.prices.length; i++) {
|
|
334
|
+
const oldGroupPrice = oldPrices.prices[i];
|
|
335
|
+
|
|
336
|
+
const reducedPrice: number = oldGroupPrice.reducedPrice === null ? oldGroupPrice.price : oldGroupPrice.reducedPrice;
|
|
337
|
+
|
|
338
|
+
const discount = GroupPriceDiscount.create({
|
|
339
|
+
type: GroupPriceDiscountType.Fixed,
|
|
340
|
+
value: ReduceablePrice.create({
|
|
341
|
+
price: Math.max(0, firstPrice.price - oldGroupPrice.price),
|
|
342
|
+
reducedPrice: Math.max(0, baseReducedPrice - reducedPrice),
|
|
343
|
+
}),
|
|
344
|
+
});
|
|
345
|
+
discounts.push(discount);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
return discounts;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function createBundleDiscount(oldPrices: OldGroupPrices, category: GroupCategory | undefined, allBundleDiscounts: BundleDiscount[]): BundleDiscount {
|
|
352
|
+
if (!oldPrices.onlySameGroup && oldPrices.prices.length < 2) {
|
|
353
|
+
throw new Error('Not enough prices');
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const countWholeFamily = !oldPrices.sameMemberOnlyDiscount;
|
|
357
|
+
const countPerGroup = oldPrices.onlySameGroup;
|
|
358
|
+
|
|
359
|
+
const discounts = createDiscounts(oldPrices);
|
|
360
|
+
|
|
361
|
+
const baseNameText = countWholeFamily ? 'Korting voor extra gezinslid' : 'Korting voor meerdere inschrijvingen';
|
|
362
|
+
const nameText = oldPrices.onlySameGroup || !category ? baseNameText : `${category.settings.name} - ${baseNameText}`;
|
|
363
|
+
|
|
364
|
+
const bundleDiscount = BundleDiscount.create({
|
|
365
|
+
name: new TranslatedString(nameText),
|
|
366
|
+
discounts,
|
|
367
|
+
countWholeFamily,
|
|
368
|
+
countPerGroup,
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
allBundleDiscounts.push(bundleDiscount);
|
|
372
|
+
|
|
373
|
+
return bundleDiscount;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// copied from v1 code
|
|
377
|
+
function formatDate(date: Date) {
|
|
378
|
+
const time = Formatter.time(date);
|
|
379
|
+
if (time === '0:00') {
|
|
380
|
+
return Formatter.date(date);
|
|
381
|
+
}
|
|
382
|
+
return Formatter.dateTime(date);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
function areDiscountsEqual(a: GroupPriceDiscount[], b: GroupPriceDiscount[]) {
|
|
386
|
+
if (a.length !== b.length) {
|
|
387
|
+
return false;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
for (let i = 0; i < a.length; i++) {
|
|
391
|
+
const discountA = a[i];
|
|
392
|
+
const discountB = b[i];
|
|
393
|
+
|
|
394
|
+
if (discountA.type !== discountB.type) {
|
|
395
|
+
return false;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
if (discountA.value.price !== discountB.value.price) {
|
|
399
|
+
return false;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
if (discountA.value.reducedPrice !== discountB.value.reducedPrice) {
|
|
403
|
+
return false;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
return true;
|
|
408
|
+
}
|
|
@@ -497,7 +497,7 @@ export const memberFilterCompilers: SQLFilterDefinitions = {
|
|
|
497
497
|
if (!result.canAccess) {
|
|
498
498
|
throw new SimpleError({
|
|
499
499
|
code: 'permission_denied',
|
|
500
|
-
message: 'No permissions
|
|
500
|
+
message: 'No permissions to filter on record ' + key,
|
|
501
501
|
human: result.record ? $t(`3560487e-3f2c-4cc9-ad7f-4e9a0fc1bbb8`, { recordName: result.record.name }) : $t(`Je hebt niet voldoende toegangsrechten om te filteren op dit gegevensveld`),
|
|
502
502
|
statusCode: 400,
|
|
503
503
|
});
|
package/tests/init/initAdmin.ts
CHANGED
|
@@ -1,12 +1,26 @@
|
|
|
1
1
|
import { Organization, Token, UserFactory } from '@stamhoofd/models';
|
|
2
|
-
import { PermissionLevel, Permissions } from '@stamhoofd/structures';
|
|
2
|
+
import { AccessRight, PermissionLevel, PermissionRole, Permissions } from '@stamhoofd/structures';
|
|
3
|
+
import { initPermissionRole } from './initPermissionRole';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* You cannot assign access rights directy to a user, but it can be done using roles. So when setting accessRights, a role wil be created and assigned to the user.
|
|
7
|
+
*/
|
|
8
|
+
export async function initAdmin({ organization, permissions, accessRights }: { organization: Organization; permissions?: Permissions; accessRights?: AccessRight[] }) {
|
|
9
|
+
permissions = permissions ?? Permissions.create({
|
|
10
|
+
level: accessRights === undefined ? PermissionLevel.Full : PermissionLevel.None,
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
if (accessRights) {
|
|
14
|
+
const role = await initPermissionRole({
|
|
15
|
+
organization,
|
|
16
|
+
accessRights,
|
|
17
|
+
});
|
|
18
|
+
permissions.roles.push(PermissionRole.create(role));
|
|
19
|
+
}
|
|
3
20
|
|
|
4
|
-
export async function initAdmin({ organization, permissions }: { organization: Organization; permissions?: Permissions }) {
|
|
5
21
|
const admin = await new UserFactory({
|
|
6
22
|
organization,
|
|
7
|
-
permissions
|
|
8
|
-
level: PermissionLevel.Full,
|
|
9
|
-
}),
|
|
23
|
+
permissions,
|
|
10
24
|
}).create();
|
|
11
25
|
|
|
12
26
|
const adminToken = await Token.createToken(admin);
|
|
@@ -1,12 +1,22 @@
|
|
|
1
|
-
import { Organization } from '@stamhoofd/models';
|
|
1
|
+
import { Organization, Platform } from '@stamhoofd/models';
|
|
2
2
|
import { AccessRight, PermissionRoleDetailed } from '@stamhoofd/structures';
|
|
3
3
|
|
|
4
|
-
export async function initPermissionRole(
|
|
4
|
+
export async function initPermissionRole(
|
|
5
|
+
{ organization, accessRights }:
|
|
6
|
+
{ organization?: Organization; accessRights?: AccessRight[] },
|
|
7
|
+
): Promise<PermissionRoleDetailed> {
|
|
5
8
|
const role = PermissionRoleDetailed.create({
|
|
6
9
|
name: 'Test role',
|
|
7
10
|
accessRights,
|
|
8
11
|
});
|
|
9
|
-
organization
|
|
10
|
-
|
|
12
|
+
if (organization) {
|
|
13
|
+
organization.privateMeta.roles.push(role);
|
|
14
|
+
await organization.save();
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
const platform = await Platform.getForEditing();
|
|
18
|
+
platform.privateConfig.roles.push(role);
|
|
19
|
+
await platform.save();
|
|
20
|
+
}
|
|
11
21
|
return role;
|
|
12
22
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Platform } from '@stamhoofd/models';
|
|
2
|
+
import { RecordCategory } from '@stamhoofd/structures';
|
|
3
|
+
|
|
4
|
+
export async function initPlatformRecordCategory({ recordCategory }: { recordCategory: RecordCategory }): Promise<void> {
|
|
5
|
+
const platform = await Platform.getForEditing();
|
|
6
|
+
platform.config.recordsConfiguration.recordCategories.push(recordCategory);
|
|
7
|
+
await platform.save();
|
|
8
|
+
}
|