@stamhoofd/backend 2.120.5 → 2.121.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/package.json +12 -12
  2. package/src/audit-logs/RegistrationInvitationLogger.ts +46 -0
  3. package/src/audit-logs/init.ts +2 -0
  4. package/src/crons/index.ts +2 -0
  5. package/src/crons/invoices.ts +166 -0
  6. package/src/crons/mollie-chargebacks.ts +87 -0
  7. package/src/crons.ts +47 -10
  8. package/src/email-recipient-loaders/payments.ts +84 -41
  9. package/src/endpoints/global/groups/GetGroupsCountEndpoint.ts +51 -0
  10. package/src/endpoints/global/platform/PatchPlatformEnpoint.ts +22 -3
  11. package/src/endpoints/global/registration/RegisterMembersEndpoint.ts +4 -0
  12. package/src/endpoints/global/registration-invitations/GetRegistrationInvitationsCountEndpoint.ts +45 -0
  13. package/src/endpoints/global/registration-invitations/GetRegistrationInvitationsEndpoint.test.ts +495 -0
  14. package/src/endpoints/global/registration-invitations/GetRegistrationInvitationsEndpoint.ts +216 -0
  15. package/src/endpoints/global/registration-invitations/PatchRegistrationInvitationsEndpoint.test.ts +405 -0
  16. package/src/endpoints/global/registration-invitations/PatchRegistrationInvitationsEndpoint.ts +168 -0
  17. package/src/endpoints/organization/dashboard/balance-items/PatchBalanceItemsEndpoint.ts +15 -0
  18. package/src/endpoints/{global → organization/dashboard}/billing/DeactivatePackageEndpoint.ts +3 -4
  19. package/src/endpoints/organization/dashboard/billing/DeleteOrganizationMandateEndpoint.ts +62 -0
  20. package/src/endpoints/organization/dashboard/billing/GetOrganizationDetailedPayableBalanceCollectionEndpoint.ts +56 -0
  21. package/src/endpoints/organization/dashboard/billing/GetOrganizationDetailedPayableBalanceEndpoint.ts +42 -19
  22. package/src/endpoints/organization/dashboard/billing/GetOrganizationMandatesEndpoint.ts +64 -0
  23. package/src/endpoints/organization/dashboard/billing/GetPackagesEndpoint.ts +11 -3
  24. package/src/endpoints/organization/dashboard/billing/OrganizationCheckoutEndpoint.ts +308 -0
  25. package/src/endpoints/organization/dashboard/billing/PatchOrganizationMandatesEndpoint.ts +94 -0
  26. package/src/endpoints/organization/dashboard/invoices/GetInvoicesEndpoint.ts +7 -0
  27. package/src/endpoints/organization/dashboard/mollie/CheckMollieEndpoint.ts +5 -4
  28. package/src/endpoints/organization/dashboard/mollie/ConnectMollieEndpoint.ts +7 -2
  29. package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.ts +17 -8
  30. package/src/endpoints/organization/dashboard/payments/PatchPaymentsEndpoint.ts +3 -3
  31. package/src/endpoints/organization/dashboard/receivable-balances/ChargeReceivableBalancesEndpoint.ts +127 -0
  32. package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.ts +13 -4
  33. package/src/endpoints/organization/dashboard/webshops/PatchWebshopEndpoint.ts +7 -1
  34. package/src/endpoints/organization/dashboard/webshops/PatchWebshopOrdersEndpoint.ts +1 -1
  35. package/src/endpoints/organization/shared/ExchangePaymentEndpoint.ts +13 -11
  36. package/src/endpoints/organization/webshops/PlaceOrderEndpoint.ts +14 -19
  37. package/src/helpers/AdminPermissionChecker.ts +11 -3
  38. package/src/helpers/AuthenticatedStructures.ts +94 -6
  39. package/src/helpers/FinancialSupportHelper.ts +21 -0
  40. package/src/helpers/RecordAnswerHelper.test.ts +746 -0
  41. package/src/helpers/RecordAnswerHelper.ts +116 -0
  42. package/src/helpers/StripeHelper.ts +2 -3
  43. package/src/helpers/ViesHelper.ts +7 -3
  44. package/src/seeds/1750090030-records-configuration.ts +68 -3
  45. package/src/seeds/1752848561-groups-registration-periods.ts +26 -2
  46. package/src/seeds/1779121239-default-invoice-email-template.sql +3 -0
  47. package/src/services/BalanceItemService.ts +12 -16
  48. package/src/services/InvoiceService.ts +372 -72
  49. package/src/services/MollieService.ts +537 -0
  50. package/src/services/PaymentMandateService.ts +214 -0
  51. package/src/services/PaymentService.ts +578 -222
  52. package/src/services/PlatformMembershipService.ts +1 -1
  53. package/src/services/RegistrationService.ts +66 -5
  54. package/src/services/STPackageService.ts +0 -7
  55. package/src/services/data/invoice.hbs.html +686 -0
  56. package/src/sql-filters/groups.ts +11 -1
  57. package/src/sql-filters/payments.ts +5 -0
  58. package/src/sql-filters/registration-invitations.ts +90 -0
  59. package/src/sql-sorters/registration-invitations.ts +36 -0
  60. package/vitest.config.js +1 -0
  61. package/src/endpoints/global/billing/ActivatePackagesEndpoint.ts +0 -216
@@ -0,0 +1,746 @@
1
+ import type { AutoEncoderPatchType } from '@simonbackx/simple-encoding';
2
+ import { PatchableArray } from '@simonbackx/simple-encoding';
3
+ import { FileType, RecordCategory, RecordChoice, RecordSettings, RecordType, TranslatedString } from '@stamhoofd/structures';
4
+ import { RecordAnswerHelper } from './RecordAnswerHelper.js';
5
+
6
+ describe('RecordAnswerHelper', () => {
7
+ const record1 = RecordSettings.create({
8
+ name: TranslatedString.create('vraag 1'),
9
+ type: RecordType.Text
10
+ });
11
+
12
+ const record2 = RecordSettings.create({
13
+ name: TranslatedString.create('vraag 2'),
14
+ type: RecordType.Integer
15
+ });
16
+
17
+ const record3 = RecordSettings.create({
18
+ name: TranslatedString.create('vraag 3'),
19
+ type: RecordType.Text
20
+ });
21
+
22
+ const record4 = RecordSettings.create({
23
+ name: TranslatedString.create('vraag 4'),
24
+ type: RecordType.Date
25
+ });
26
+
27
+ const record5 = RecordSettings.create({
28
+ name: TranslatedString.create('vraag 5'),
29
+ type: RecordType.MultipleChoice,
30
+ choices: [
31
+ RecordChoice.create({ name: TranslatedString.create('keuze 1') }),
32
+ RecordChoice.create({ name: TranslatedString.create('keuze 2') }),
33
+ ]
34
+ });
35
+
36
+ const record6 = RecordSettings.create({
37
+ name: TranslatedString.create('vraag 6'),
38
+ type: RecordType.Email
39
+ });
40
+
41
+ const record7 = RecordSettings.create({
42
+ name: TranslatedString.create('vraag 7'),
43
+ type: RecordType.Price
44
+ });
45
+
46
+ const record8 = RecordSettings.create({
47
+ name: TranslatedString.create('vraag 8'),
48
+ type: RecordType.File,
49
+ fileType: FileType.PDF
50
+ });
51
+
52
+ const childCategory1 = RecordCategory.create({
53
+ name: TranslatedString.create('subcategorie 1'),
54
+ records: [record3,record4]
55
+ });
56
+
57
+ const childCategory2 = RecordCategory.create({
58
+ name: TranslatedString.create('subcategorie 2'),
59
+ records: [record5]
60
+ })
61
+
62
+ const recordCategory1 = RecordCategory.create({
63
+ name: TranslatedString.create('categorie 1'),
64
+ records: [ record1, record2 ],
65
+ childCategories: [childCategory1, childCategory2]
66
+ });
67
+
68
+ const recordCategory2 = RecordCategory.create({
69
+ name: TranslatedString.create('categorie 2'),
70
+ records: [record6, record7, record8]
71
+ });
72
+
73
+ const originalRecordCategories = [recordCategory1, recordCategory2];
74
+
75
+ describe('throwIfPatchOrPutIsInvalid', () => {
76
+
77
+ test('patch - happy path should not throw', () => {
78
+ const patches = new PatchableArray<string, RecordCategory, AutoEncoderPatchType<RecordCategory>>();
79
+ const recordPatches1 = new PatchableArray<string, RecordSettings, AutoEncoderPatchType<RecordSettings>>();
80
+
81
+ const recordPatch1 = RecordSettings.patch({
82
+ id: record1.id,
83
+ name: TranslatedString.create('vraag 1 - updated'),
84
+ type: RecordType.Textarea
85
+ });
86
+
87
+ const recordPatch2 = RecordSettings.patch({
88
+ id: record2.id,
89
+ name: TranslatedString.create('vraag 2 - updated'),
90
+ type: RecordType.Integer
91
+ });
92
+
93
+ recordPatches1.addPatch(recordPatch1);
94
+ recordPatches1.addPatch(recordPatch2);
95
+
96
+ const recordPatches2 = new PatchableArray<string, RecordSettings, AutoEncoderPatchType<RecordSettings>>();
97
+
98
+ const recordPatch3 = RecordSettings.patch({
99
+ id: record3.id,
100
+ name: TranslatedString.create('vraag 3 - updated'),
101
+ type: RecordType.Email
102
+ });
103
+
104
+ const recordPatch4 = RecordSettings.patch({
105
+ id: record4.id,
106
+ name: TranslatedString.create('vraag 4 - updated'),
107
+ });
108
+
109
+ recordPatches2.addPatch(recordPatch3);
110
+ recordPatches2.addPatch(recordPatch4);
111
+
112
+ const recordPatches3 = new PatchableArray<string, RecordSettings, AutoEncoderPatchType<RecordSettings>>();
113
+
114
+ const recordPatch5 = RecordSettings.patch({
115
+ id: record5.id,
116
+ name: TranslatedString.create('vraag 5 - updated'),
117
+ });
118
+
119
+ recordPatches3.addPatch(recordPatch5);
120
+
121
+ const recordPatches4 = new PatchableArray<string, RecordSettings, AutoEncoderPatchType<RecordSettings>>();
122
+
123
+ const recordPatch7 = RecordSettings.patch({
124
+ id: record7.id,
125
+ name: TranslatedString.create('vraag 7 - updated'),
126
+ });
127
+
128
+ recordPatches4.addPatch(recordPatch7);
129
+
130
+ const childCategoriesPatch1 = new PatchableArray<string, RecordCategory, AutoEncoderPatchType<RecordCategory>>();
131
+
132
+ childCategoriesPatch1.addPatch(RecordCategory.patch({
133
+ id: childCategory1.id,
134
+ records: recordPatches2
135
+ }));
136
+
137
+ childCategoriesPatch1.addPatch(RecordCategory.patch({
138
+ id: childCategory2.id,
139
+ records: recordPatches3
140
+ }));
141
+
142
+
143
+ const patch1 = RecordCategory.patch({
144
+ id: recordCategory1.id,
145
+ records: recordPatches1,
146
+ childCategories: childCategoriesPatch1
147
+ });
148
+
149
+ const patch2 = RecordCategory.patch({
150
+ id: recordCategory2.id,
151
+ records: recordPatches4
152
+ });
153
+
154
+ patches.addPatch(patch1);
155
+ patches.addPatch(patch2);
156
+
157
+ // should not throw
158
+ expect(() => RecordAnswerHelper.throwIfPatchOrPutIsInvalid(originalRecordCategories, patches)).not.toThrow();
159
+ })
160
+
161
+ describe('patch - should throw if type changes', () => {
162
+ test('case 1 - error in root category', () => {
163
+ const patches = new PatchableArray<string, RecordCategory, AutoEncoderPatchType<RecordCategory>>();
164
+ const recordPatches1 = new PatchableArray<string, RecordSettings, AutoEncoderPatchType<RecordSettings>>();
165
+
166
+ const recordPatch1 = RecordSettings.patch({
167
+ id: record1.id,
168
+ name: TranslatedString.create('vraag 1 - updated'),
169
+ type: RecordType.Textarea
170
+ });
171
+
172
+ const recordPatch2 = RecordSettings.patch({
173
+ id: record2.id,
174
+ name: TranslatedString.create('vraag 2 - updated'),
175
+ // type changed -> should throw
176
+ type: RecordType.Email
177
+ });
178
+
179
+ recordPatches1.addPatch(recordPatch1);
180
+ recordPatches1.addPatch(recordPatch2);
181
+
182
+ const patch1 = RecordCategory.patch({
183
+ id: recordCategory1.id,
184
+ records: recordPatches1,
185
+ });
186
+
187
+ patches.addPatch(patch1);
188
+
189
+ // should throw
190
+ expect(() => RecordAnswerHelper.throwIfPatchOrPutIsInvalid(originalRecordCategories, patches)).toThrow('Cannot change record type from Integer to Email');
191
+ })
192
+
193
+ test('case 2 - error in child category', () => {
194
+ const patches = new PatchableArray<string, RecordCategory, AutoEncoderPatchType<RecordCategory>>();
195
+ const recordPatches1 = new PatchableArray<string, RecordSettings, AutoEncoderPatchType<RecordSettings>>();
196
+
197
+ const recordPatch1 = RecordSettings.patch({
198
+ id: record1.id,
199
+ name: TranslatedString.create('vraag 1 - updated'),
200
+ type: RecordType.Textarea
201
+ });
202
+
203
+ const recordPatch2 = RecordSettings.patch({
204
+ id: record2.id,
205
+ name: TranslatedString.create('vraag 2 - updated'),
206
+ type: RecordType.Integer
207
+ });
208
+
209
+ recordPatches1.addPatch(recordPatch1);
210
+ recordPatches1.addPatch(recordPatch2);
211
+
212
+ const recordPatches2 = new PatchableArray<string, RecordSettings, AutoEncoderPatchType<RecordSettings>>();
213
+
214
+ const recordPatch3 = RecordSettings.patch({
215
+ id: record3.id,
216
+ name: TranslatedString.create('vraag 3 - updated'),
217
+ type: RecordType.Email
218
+ });
219
+
220
+ const recordPatch4 = RecordSettings.patch({
221
+ id: record4.id,
222
+ name: TranslatedString.create('vraag 4 - updated'),
223
+ // changes from date to text -> should throw
224
+ type: RecordType.Text
225
+ });
226
+
227
+ recordPatches2.addPatch(recordPatch3);
228
+ recordPatches2.addPatch(recordPatch4);
229
+
230
+ const recordPatches3 = new PatchableArray<string, RecordSettings, AutoEncoderPatchType<RecordSettings>>();
231
+
232
+ const recordPatch5 = RecordSettings.patch({
233
+ id: record5.id,
234
+ name: TranslatedString.create('vraag 5 - updated'),
235
+ });
236
+
237
+ recordPatches3.addPatch(recordPatch5);
238
+
239
+ const childCategoriesPatch1 = new PatchableArray<string, RecordCategory, AutoEncoderPatchType<RecordCategory>>();
240
+
241
+ childCategoriesPatch1.addPatch(RecordCategory.patch({
242
+ id: childCategory1.id,
243
+ records: recordPatches2
244
+ }));
245
+
246
+ childCategoriesPatch1.addPatch(RecordCategory.patch({
247
+ id: childCategory2.id,
248
+ records: recordPatches3
249
+ }));
250
+
251
+
252
+ const patch1 = RecordCategory.patch({
253
+ id: recordCategory1.id,
254
+ records: recordPatches1,
255
+ childCategories: childCategoriesPatch1
256
+ });
257
+
258
+ patches.addPatch(patch1);
259
+
260
+ // should throw
261
+ expect(() => RecordAnswerHelper.throwIfPatchOrPutIsInvalid(originalRecordCategories, patches)).toThrow('Cannot change record type from Date to Text');
262
+ })
263
+ });
264
+
265
+ test('patch - should throw if file type changes', () => {
266
+ const patches = new PatchableArray<string, RecordCategory, AutoEncoderPatchType<RecordCategory>>();
267
+ const recordPatches1 = new PatchableArray<string, RecordSettings, AutoEncoderPatchType<RecordSettings>>();
268
+
269
+ recordPatches1.addPatch(RecordSettings.patch({
270
+ id: record8.id,
271
+ name: TranslatedString.create('vraag 8 - updated'),
272
+ type: RecordType.File,
273
+ fileType: FileType.Word
274
+ }));
275
+
276
+ patches.addPatch(RecordCategory.patch({
277
+ id: recordCategory2.id,
278
+ records: recordPatches1,
279
+ }));
280
+
281
+ // should throw
282
+ expect(() => RecordAnswerHelper.throwIfPatchOrPutIsInvalid(originalRecordCategories, patches)).toThrow('Cannot change record file type from PDF to Word');
283
+ })
284
+
285
+ test('put - happy path should not throw', () => {
286
+ const newRecord1 = RecordSettings.create({
287
+ id: record1.id,
288
+ name: TranslatedString.create('vraag 1'),
289
+ // type changed but same class -> should not throw
290
+ type: RecordType.Text
291
+ });
292
+
293
+ const newRecord2 = RecordSettings.create({
294
+ id: record2.id,
295
+ name: TranslatedString.create('vraag 2'),
296
+ type: RecordType.Integer
297
+ });
298
+
299
+ const newRecord3 = RecordSettings.create({
300
+ id: record3.id,
301
+ name: TranslatedString.create('vraag 3'),
302
+ type: RecordType.Text
303
+ });
304
+
305
+ const newRecord4 = RecordSettings.create({
306
+ id: record4.id,
307
+ name: TranslatedString.create('vraag 4'),
308
+ type: RecordType.Date
309
+ });
310
+
311
+ const newRecord5 = RecordSettings.create({
312
+ id: record5.id,
313
+ name: TranslatedString.create('vraag 5'),
314
+ type: RecordType.MultipleChoice,
315
+ choices: [
316
+ RecordChoice.create({ name: TranslatedString.create('keuze 1') }),
317
+ RecordChoice.create({ name: TranslatedString.create('keuze 2') }),
318
+ ]
319
+ });
320
+
321
+ const newRecord6 = RecordSettings.create({
322
+ id: record6.id,
323
+ name: TranslatedString.create('vraag 6'),
324
+ type: RecordType.Email
325
+ });
326
+
327
+ const newRecord7 = RecordSettings.create({
328
+ id: record7.id,
329
+ name: TranslatedString.create('vraag 7'),
330
+ type: RecordType.Price
331
+ });
332
+
333
+ const newRecord8 = RecordSettings.create({
334
+ id: record8.id,
335
+ name: TranslatedString.create('vraag 8'),
336
+ type: RecordType.File,
337
+ fileType: FileType.PDF
338
+ });
339
+
340
+ const newChildCategory1 = RecordCategory.create({
341
+ id: childCategory1.id,
342
+ name: TranslatedString.create('subcategorie 1'),
343
+ records: [newRecord3,newRecord4]
344
+ });
345
+
346
+ const newChildCategory2 = RecordCategory.create({
347
+ id: childCategory2.id,
348
+ name: TranslatedString.create('subcategorie 2'),
349
+ records: [newRecord5]
350
+ })
351
+
352
+ const newRecordCategory1 = RecordCategory.create({
353
+ id: recordCategory1.id,
354
+ name: TranslatedString.create('categorie 1'),
355
+ records: [ newRecord1, newRecord2 ],
356
+ childCategories: [newChildCategory1, newChildCategory2]
357
+ });
358
+
359
+ const newRecordCategory2 = RecordCategory.create({
360
+ id: recordCategory2.id,
361
+ name: TranslatedString.create('categorie 2'),
362
+ records: [newRecord6, newRecord7, newRecord8]
363
+ });
364
+
365
+ const newRecordCategories = [newRecordCategory1, newRecordCategory2];
366
+
367
+ // should not throw
368
+ expect(() => RecordAnswerHelper.throwIfPatchOrPutIsInvalid(originalRecordCategories, newRecordCategories)).not.toThrow();
369
+ });
370
+
371
+ describe('put - should throw if type changes', () => {
372
+ test('case 1 - error in root category', () => {
373
+ const newRecord1 = RecordSettings.create({
374
+ id: record1.id,
375
+ name: TranslatedString.create('vraag 1'),
376
+ // type changed but same class -> should not throw
377
+ type: RecordType.Text
378
+ });
379
+
380
+ const newRecord2 = RecordSettings.create({
381
+ id: record2.id,
382
+ name: TranslatedString.create('vraag 2'),
383
+ // type changed from Integer to Email -> should throw
384
+ type: RecordType.Email
385
+ });
386
+
387
+ const newRecord3 = RecordSettings.create({
388
+ id: record3.id,
389
+ name: TranslatedString.create('vraag 3'),
390
+ type: RecordType.Text
391
+ });
392
+
393
+ const newRecord4 = RecordSettings.create({
394
+ id: record4.id,
395
+ name: TranslatedString.create('vraag 4'),
396
+ type: RecordType.Date
397
+ });
398
+
399
+ const newRecord5 = RecordSettings.create({
400
+ id: record5.id,
401
+ name: TranslatedString.create('vraag 5'),
402
+ type: RecordType.MultipleChoice,
403
+ choices: [
404
+ RecordChoice.create({ name: TranslatedString.create('keuze 1') }),
405
+ RecordChoice.create({ name: TranslatedString.create('keuze 2') }),
406
+ ]
407
+ });
408
+
409
+ const newRecord6 = RecordSettings.create({
410
+ id: record6.id,
411
+ name: TranslatedString.create('vraag 6'),
412
+ type: RecordType.Email
413
+ });
414
+
415
+ const newRecord7 = RecordSettings.create({
416
+ id: record7.id,
417
+ name: TranslatedString.create('vraag 7'),
418
+ type: RecordType.Price
419
+ });
420
+
421
+ const newRecord8 = RecordSettings.create({
422
+ id: record8.id,
423
+ name: TranslatedString.create('vraag 8'),
424
+ type: RecordType.File,
425
+ fileType: FileType.PDF
426
+ });
427
+
428
+ const newChildCategory1 = RecordCategory.create({
429
+ id: childCategory1.id,
430
+ name: TranslatedString.create('subcategorie 1'),
431
+ records: [newRecord3,newRecord4]
432
+ });
433
+
434
+ const newChildCategory2 = RecordCategory.create({
435
+ id: childCategory2.id,
436
+ name: TranslatedString.create('subcategorie 2'),
437
+ records: [newRecord5]
438
+ })
439
+
440
+ const newRecordCategory1 = RecordCategory.create({
441
+ id: recordCategory1.id,
442
+ name: TranslatedString.create('categorie 1'),
443
+ records: [ newRecord1, newRecord2 ],
444
+ childCategories: [newChildCategory1, newChildCategory2]
445
+ });
446
+
447
+ const newRecordCategory2 = RecordCategory.create({
448
+ id: recordCategory2.id,
449
+ name: TranslatedString.create('categorie 2'),
450
+ records: [newRecord6, newRecord7, newRecord8]
451
+ });
452
+
453
+ const newRecordCategories = [newRecordCategory1, newRecordCategory2];
454
+
455
+ // should throw
456
+ expect(() => RecordAnswerHelper.throwIfPatchOrPutIsInvalid(originalRecordCategories, newRecordCategories)).toThrow('Cannot change record type from Integer to Email');
457
+ })
458
+
459
+ test('case 2 - error in child category', () => {
460
+ const newRecord1 = RecordSettings.create({
461
+ id: record1.id,
462
+ name: TranslatedString.create('vraag 1'),
463
+ // type changed but same class -> should not throw
464
+ type: RecordType.Text
465
+ });
466
+
467
+ const newRecord2 = RecordSettings.create({
468
+ id: record2.id,
469
+ name: TranslatedString.create('vraag 2'),
470
+ type: RecordType.Integer
471
+ });
472
+
473
+ const newRecord3 = RecordSettings.create({
474
+ id: record3.id,
475
+ name: TranslatedString.create('vraag 3'),
476
+ type: RecordType.Text
477
+ });
478
+
479
+ const newRecord4 = RecordSettings.create({
480
+ id: record4.id,
481
+ name: TranslatedString.create('vraag 4'),
482
+ // type changed from date to text -> should throw
483
+ type: RecordType.Text
484
+ });
485
+
486
+ const newRecord5 = RecordSettings.create({
487
+ id: record5.id,
488
+ name: TranslatedString.create('vraag 5'),
489
+ type: RecordType.MultipleChoice,
490
+ choices: [
491
+ RecordChoice.create({ name: TranslatedString.create('keuze 1') }),
492
+ RecordChoice.create({ name: TranslatedString.create('keuze 2') }),
493
+ ]
494
+ });
495
+
496
+ const newRecord6 = RecordSettings.create({
497
+ id: record6.id,
498
+ name: TranslatedString.create('vraag 6'),
499
+ type: RecordType.Email
500
+ });
501
+
502
+ const newRecord7 = RecordSettings.create({
503
+ id: record7.id,
504
+ name: TranslatedString.create('vraag 7'),
505
+ type: RecordType.Price
506
+ });
507
+
508
+ const newRecord8 = RecordSettings.create({
509
+ id: record8.id,
510
+ name: TranslatedString.create('vraag 8'),
511
+ type: RecordType.File,
512
+ fileType: FileType.PDF
513
+ });
514
+
515
+ const newChildCategory1 = RecordCategory.create({
516
+ id: childCategory1.id,
517
+ name: TranslatedString.create('subcategorie 1'),
518
+ records: [newRecord3,newRecord4]
519
+ });
520
+
521
+ const newChildCategory2 = RecordCategory.create({
522
+ id: childCategory2.id,
523
+ name: TranslatedString.create('subcategorie 2'),
524
+ records: [newRecord5]
525
+ })
526
+
527
+ const newRecordCategory1 = RecordCategory.create({
528
+ id: recordCategory1.id,
529
+ name: TranslatedString.create('categorie 1'),
530
+ records: [ newRecord1, newRecord2 ],
531
+ childCategories: [newChildCategory1, newChildCategory2]
532
+ });
533
+
534
+ const newRecordCategory2 = RecordCategory.create({
535
+ id: recordCategory2.id,
536
+ name: TranslatedString.create('categorie 2'),
537
+ records: [newRecord6, newRecord7, newRecord8]
538
+ });
539
+
540
+ const newRecordCategories = [newRecordCategory1, newRecordCategory2];
541
+
542
+ // should throw
543
+ expect(() => RecordAnswerHelper.throwIfPatchOrPutIsInvalid(originalRecordCategories, newRecordCategories)).toThrow('Cannot change record type from Date to Text');
544
+ })
545
+ });
546
+
547
+ test('combination of patch and put - happy path should not throw', () => {
548
+ const patches = new PatchableArray<string, RecordCategory, AutoEncoderPatchType<RecordCategory>>();
549
+ const recordPatches1 = new PatchableArray<string, RecordSettings, AutoEncoderPatchType<RecordSettings>>();
550
+
551
+ const recordPatch1 = RecordSettings.patch({
552
+ id: record1.id,
553
+ name: TranslatedString.create('vraag 1 - updated'),
554
+ type: RecordType.Textarea
555
+ });
556
+
557
+ const recordPatch2 = RecordSettings.patch({
558
+ id: record2.id,
559
+ name: TranslatedString.create('vraag 2 - updated'),
560
+ type: RecordType.Integer
561
+ });
562
+
563
+ recordPatches1.addPatch(recordPatch1);
564
+ recordPatches1.addPatch(recordPatch2);
565
+
566
+ const recordPatches2 = new PatchableArray<string, RecordSettings, AutoEncoderPatchType<RecordSettings>>();
567
+
568
+ const recordPatch3 = RecordSettings.patch({
569
+ id: record3.id,
570
+ name: TranslatedString.create('vraag 3 - updated'),
571
+ type: RecordType.Email
572
+ });
573
+
574
+ const recordPatch4 = RecordSettings.patch({
575
+ id: record4.id,
576
+ name: TranslatedString.create('vraag 4 - updated'),
577
+ });
578
+
579
+ recordPatches2.addPatch(recordPatch3);
580
+ recordPatches2.addPatch(recordPatch4);
581
+
582
+ const recordPatches3 = new PatchableArray<string, RecordSettings, AutoEncoderPatchType<RecordSettings>>();
583
+
584
+ const recordPatch5 = RecordSettings.patch({
585
+ id: record5.id,
586
+ name: TranslatedString.create('vraag 5 - updated'),
587
+ });
588
+
589
+ recordPatches3.addPatch(recordPatch5);
590
+
591
+ const recordPatches4 = new PatchableArray<string, RecordSettings, AutoEncoderPatchType<RecordSettings>>();
592
+
593
+ const recordPatch7 = RecordSettings.patch({
594
+ id: record7.id,
595
+ name: TranslatedString.create('vraag 7 - updated'),
596
+ });
597
+
598
+ recordPatches4.addPatch(recordPatch7);
599
+
600
+ const childCategoriesPatch1 = new PatchableArray<string, RecordCategory, AutoEncoderPatchType<RecordCategory>>();
601
+
602
+ const newRecord3 = RecordSettings.create({
603
+ id: record3.id,
604
+ name: TranslatedString.create('vraag 3'),
605
+ type: RecordType.Text
606
+ });
607
+
608
+ const newRecord4 = RecordSettings.create({
609
+ id: record4.id,
610
+ name: TranslatedString.create('vraag 4'),
611
+ type: RecordType.Date
612
+ });
613
+
614
+ const newChildCategory1 = RecordCategory.create({
615
+ id: childCategory1.id,
616
+ name: TranslatedString.create('subcategorie 1'),
617
+ records: [newRecord3,newRecord4]
618
+ });
619
+
620
+ childCategoriesPatch1.addPut(newChildCategory1);
621
+
622
+ childCategoriesPatch1.addPatch(RecordCategory.patch({
623
+ id: childCategory2.id,
624
+ records: recordPatches3
625
+ }));
626
+
627
+
628
+ const patch1 = RecordCategory.patch({
629
+ id: recordCategory1.id,
630
+ records: recordPatches1,
631
+ childCategories: childCategoriesPatch1
632
+ });
633
+
634
+ const patch2 = RecordCategory.patch({
635
+ id: recordCategory2.id,
636
+ records: recordPatches4
637
+ });
638
+
639
+ patches.addPatch(patch1);
640
+ patches.addPatch(patch2);
641
+
642
+ // should not throw
643
+ expect(() => RecordAnswerHelper.throwIfPatchOrPutIsInvalid(originalRecordCategories, patches)).not.toThrow();
644
+ })
645
+
646
+ test('combination of patch and put - should throw if type changes', () => {
647
+ const patches = new PatchableArray<string, RecordCategory, AutoEncoderPatchType<RecordCategory>>();
648
+ const recordPatches1 = new PatchableArray<string, RecordSettings, AutoEncoderPatchType<RecordSettings>>();
649
+
650
+ const recordPatch1 = RecordSettings.patch({
651
+ id: record1.id,
652
+ name: TranslatedString.create('vraag 1 - updated'),
653
+ type: RecordType.Textarea
654
+ });
655
+
656
+ const recordPatch2 = RecordSettings.patch({
657
+ id: record2.id,
658
+ name: TranslatedString.create('vraag 2 - updated'),
659
+ type: RecordType.Integer
660
+ });
661
+
662
+ recordPatches1.addPatch(recordPatch1);
663
+ recordPatches1.addPatch(recordPatch2);
664
+
665
+ const recordPatches2 = new PatchableArray<string, RecordSettings, AutoEncoderPatchType<RecordSettings>>();
666
+
667
+ const recordPatch3 = RecordSettings.patch({
668
+ id: record3.id,
669
+ name: TranslatedString.create('vraag 3 - updated'),
670
+ type: RecordType.Email
671
+ });
672
+
673
+ const recordPatch4 = RecordSettings.patch({
674
+ id: record4.id,
675
+ name: TranslatedString.create('vraag 4 - updated'),
676
+ });
677
+
678
+ recordPatches2.addPatch(recordPatch3);
679
+ recordPatches2.addPatch(recordPatch4);
680
+
681
+ const recordPatches3 = new PatchableArray<string, RecordSettings, AutoEncoderPatchType<RecordSettings>>();
682
+
683
+ const recordPatch5 = RecordSettings.patch({
684
+ id: record5.id,
685
+ name: TranslatedString.create('vraag 5 - updated'),
686
+ });
687
+
688
+ recordPatches3.addPatch(recordPatch5);
689
+
690
+ const recordPatches4 = new PatchableArray<string, RecordSettings, AutoEncoderPatchType<RecordSettings>>();
691
+
692
+ const recordPatch7 = RecordSettings.patch({
693
+ id: record7.id,
694
+ name: TranslatedString.create('vraag 7 - updated'),
695
+ });
696
+
697
+ recordPatches4.addPatch(recordPatch7);
698
+
699
+ const childCategoriesPatch1 = new PatchableArray<string, RecordCategory, AutoEncoderPatchType<RecordCategory>>();
700
+
701
+ const newRecord3 = RecordSettings.create({
702
+ id: record3.id,
703
+ name: TranslatedString.create('vraag 3'),
704
+ type: RecordType.Text
705
+ });
706
+
707
+ const newRecord4 = RecordSettings.create({
708
+ id: record4.id,
709
+ name: TranslatedString.create('vraag 4'),
710
+ // type changes from date to text -> should throw
711
+ type: RecordType.Text
712
+ });
713
+
714
+ const newChildCategory1 = RecordCategory.create({
715
+ id: childCategory1.id,
716
+ name: TranslatedString.create('subcategorie 1'),
717
+ records: [newRecord3,newRecord4]
718
+ });
719
+
720
+ childCategoriesPatch1.addPut(newChildCategory1);
721
+
722
+ childCategoriesPatch1.addPatch(RecordCategory.patch({
723
+ id: childCategory2.id,
724
+ records: recordPatches3
725
+ }));
726
+
727
+
728
+ const patch1 = RecordCategory.patch({
729
+ id: recordCategory1.id,
730
+ records: recordPatches1,
731
+ childCategories: childCategoriesPatch1
732
+ });
733
+
734
+ const patch2 = RecordCategory.patch({
735
+ id: recordCategory2.id,
736
+ records: recordPatches4
737
+ });
738
+
739
+ patches.addPatch(patch1);
740
+ patches.addPatch(patch2);
741
+
742
+ // should not throw
743
+ expect(() => RecordAnswerHelper.throwIfPatchOrPutIsInvalid(originalRecordCategories, patches)).toThrow('Cannot change record type from Date to Text');
744
+ })
745
+ })
746
+ })