@stamhoofd/backend 2.58.0 → 2.59.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 (48) hide show
  1. package/index.ts +6 -1
  2. package/package.json +12 -12
  3. package/src/audit-logs/EventLogger.ts +30 -0
  4. package/src/audit-logs/GroupLogger.ts +95 -0
  5. package/src/audit-logs/MemberLogger.ts +24 -0
  6. package/src/audit-logs/MemberPlatformMembershipLogger.ts +57 -0
  7. package/src/audit-logs/MemberResponsibilityRecordLogger.ts +69 -0
  8. package/src/audit-logs/ModelLogger.ts +218 -0
  9. package/src/audit-logs/OrderLogger.ts +57 -0
  10. package/src/audit-logs/OrganizationLogger.ts +26 -0
  11. package/src/audit-logs/OrganizationRegistrationPeriodLogger.ts +77 -0
  12. package/src/audit-logs/PaymentLogger.ts +43 -0
  13. package/src/audit-logs/PlatformLogger.ts +13 -0
  14. package/src/audit-logs/RegistrationLogger.ts +53 -0
  15. package/src/audit-logs/RegistrationPeriodLogger.ts +21 -0
  16. package/src/audit-logs/StripeAccountLogger.ts +47 -0
  17. package/src/audit-logs/WebshopLogger.ts +35 -0
  18. package/src/crons.ts +2 -1
  19. package/src/endpoints/global/events/PatchEventsEndpoint.ts +12 -24
  20. package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.ts +4 -18
  21. package/src/endpoints/global/payments/StripeWebhookEndpoint.ts +5 -13
  22. package/src/endpoints/global/platform/PatchPlatformEnpoint.ts +3 -11
  23. package/src/endpoints/global/registration/PatchUserMembersEndpoint.ts +0 -15
  24. package/src/endpoints/global/registration/RegisterMembersEndpoint.ts +5 -2
  25. package/src/endpoints/global/registration-periods/PatchRegistrationPeriodsEndpoint.ts +0 -19
  26. package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.ts +1 -12
  27. package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.ts +18 -33
  28. package/src/endpoints/organization/dashboard/stripe/ConnectStripeEndpoint.ts +0 -6
  29. package/src/endpoints/organization/dashboard/stripe/DeleteStripeAccountEndpoint.ts +0 -6
  30. package/src/endpoints/organization/dashboard/stripe/UpdateStripeAccountEndpoint.ts +5 -14
  31. package/src/endpoints/organization/dashboard/webshops/PatchWebshopOrdersEndpoint.ts +7 -4
  32. package/src/endpoints/organization/webshops/PlaceOrderEndpoint.ts +8 -2
  33. package/src/helpers/AuthenticatedStructures.ts +16 -1
  34. package/src/helpers/Context.ts +8 -2
  35. package/src/helpers/MemberUserSyncer.ts +45 -40
  36. package/src/helpers/PeriodHelper.ts +3 -4
  37. package/src/helpers/TagHelper.ts +23 -20
  38. package/src/seeds/1722344162-update-membership.ts +2 -2
  39. package/src/seeds/1726572303-schedule-stock-updates.ts +2 -1
  40. package/src/services/AuditLogService.ts +83 -295
  41. package/src/services/BalanceItemPaymentService.ts +1 -1
  42. package/src/services/BalanceItemService.ts +14 -5
  43. package/src/services/MemberNumberService.ts +120 -0
  44. package/src/services/PaymentService.ts +199 -193
  45. package/src/services/PlatformMembershipService.ts +284 -0
  46. package/src/services/RegistrationService.ts +76 -27
  47. package/src/services/explainPatch.ts +110 -41
  48. package/src/helpers/MembershipHelper.ts +0 -54
@@ -22,8 +22,8 @@ function createStringChangeHandler(key: string) {
22
22
  return [
23
23
  AuditLogPatchItem.create({
24
24
  key: getAutoEncoderKey(key),
25
- oldValue: getAutoEncoderValue(oldValue) || getAutoEncoderName(oldValue) || undefined,
26
- value: getAutoEncoderValue(value) || getAutoEncoderName(value) || undefined,
25
+ oldValue: getAutoEncoderValue(oldValue, key) || getAutoEncoderName(oldValue) || undefined,
26
+ value: getAutoEncoderValue(value, key) || getAutoEncoderName(value) || undefined,
27
27
  }).autoType(),
28
28
  ];
29
29
  };
@@ -58,12 +58,11 @@ function createIntegerChangeHandler(key: string) {
58
58
  return [];
59
59
  }
60
60
 
61
- const formatter: (typeof Formatter.price | typeof Formatter.integer) = key.toLowerCase().includes('price') ? Formatter.price.bind(Formatter) : Formatter.integer.bind(Formatter);
62
61
  return [
63
62
  AuditLogPatchItem.create({
64
63
  key: getAutoEncoderKey(key),
65
- oldValue: oldValue !== null ? AuditLogReplacement.string(formatter(oldValue)) : undefined,
66
- value: value !== null ? AuditLogReplacement.string(formatter(value)) : undefined,
64
+ oldValue: oldValue !== null ? (getAutoEncoderValue(oldValue, key) || undefined) : undefined,
65
+ value: value !== null ? (getAutoEncoderValue(value, key) || undefined) : undefined,
67
66
  }).autoType(),
68
67
  ];
69
68
  };
@@ -85,7 +84,7 @@ function createDateChangeHandler(key: string) {
85
84
  let dno = oldValue ? Formatter.dateNumber(oldValue, true) : undefined;
86
85
  let dn = value ? Formatter.dateNumber(value, true) : undefined;
87
86
 
88
- if (dno && dn && (dno === dn || (Formatter.time(oldValue!) !== Formatter.time(value!)))) {
87
+ if (dno && dn && (dno === dn || (Formatter.time(oldValue!) !== Formatter.time(value!))) && key !== 'birthDay') {
89
88
  // Add time
90
89
  dno += ' ' + Formatter.time(oldValue!);
91
90
  dn += ' ' + Formatter.time(value!);
@@ -157,7 +156,7 @@ function getAutoEncoderName(autoEncoder: unknown): AuditLogReplacement | null {
157
156
  }
158
157
  return null;
159
158
  }
160
- function getAutoEncoderPutValue(autoEncoder: unknown): AuditLogReplacement | null {
159
+ function getAutoEncoderPutValue(autoEncoder: unknown, key?: string): AuditLogReplacement | null {
161
160
  if (typeof autoEncoder === 'object' && autoEncoder !== null && 'getPutValue' in autoEncoder && typeof autoEncoder.getPutValue === 'function') {
162
161
  const name = autoEncoder.getPutValue();
163
162
  if (typeof name === 'string') {
@@ -167,14 +166,18 @@ function getAutoEncoderPutValue(autoEncoder: unknown): AuditLogReplacement | nul
167
166
  return name;
168
167
  }
169
168
  }
170
- return getAutoEncoderValue(autoEncoder);
169
+ return getAutoEncoderValue(autoEncoder, key);
171
170
  }
172
171
 
173
- function getAutoEncoderValue(autoEncoder: unknown): AuditLogReplacement | null {
172
+ function getAutoEncoderValue(autoEncoder: unknown, key?: string): AuditLogReplacement | null {
174
173
  if (typeof autoEncoder === 'string') {
175
174
  if (isUuid(autoEncoder)) {
176
175
  return AuditLogReplacement.uuid(autoEncoder);
177
176
  }
177
+ if (key && key === 'status') {
178
+ // Will be an enum
179
+ return AuditLogReplacement.key(autoEncoder);
180
+ }
178
181
  return AuditLogReplacement.string(autoEncoder);
179
182
  }
180
183
 
@@ -187,6 +190,9 @@ function getAutoEncoderValue(autoEncoder: unknown): AuditLogReplacement | null {
187
190
  }
188
191
 
189
192
  if (typeof autoEncoder === 'number') {
193
+ if (key && (key.toLowerCase().includes('price') || key.toLowerCase().includes('fee'))) {
194
+ return AuditLogReplacement.string(Formatter.price(autoEncoder));
195
+ }
190
196
  return AuditLogReplacement.string(Formatter.integer(autoEncoder));
191
197
  }
192
198
 
@@ -246,6 +252,10 @@ function findOriginalById(id: unknown, oldArray: unknown[]): unknown | null {
246
252
  return id ? oldArray.find(v => getId(v) === id) : null;
247
253
  }
248
254
 
255
+ function findOriginalIndexById(id: unknown, oldArray: unknown[]): number {
256
+ return id ? oldArray.findIndex(v => getId(v) === id) : -1;
257
+ }
258
+
249
259
  function getId(object: unknown): string | number | null {
250
260
  const id = getOptionalId(object);
251
261
  if (typeof id !== 'string' && typeof id !== 'number') {
@@ -262,6 +272,10 @@ function findOriginal(put: unknown, oldArray: unknown[]): unknown | null {
262
272
  return findOriginalById(getId(put), oldArray);
263
273
  }
264
274
 
275
+ function findIndex(put: unknown, oldArray: unknown[]): number {
276
+ return findOriginalIndexById(getId(put), oldArray);
277
+ }
278
+
265
279
  function processPut(key: string, put: unknown, original: unknown | null, createdIdSet?: Set<string>): AuditLogPatchItem[] {
266
280
  const items: AuditLogPatchItem[] = [];
267
281
  const keySingular = getKeySingular(key);
@@ -269,10 +283,11 @@ function processPut(key: string, put: unknown, original: unknown | null, created
269
283
 
270
284
  // Added a new parent
271
285
  if (!original) {
286
+ const n = getAutoEncoderName(put);
272
287
  items.push(
273
288
  AuditLogPatchItem.create({
274
- key: AuditLogReplacement.key(keySingular).append(getAutoEncoderName(put)),
275
- value: v || undefined,
289
+ key: AuditLogReplacement.key(keySingular).append(n),
290
+ value: (n?.toString() !== v?.toString()) ? (v || undefined) : undefined,
276
291
  type: AuditLogPatchItemType.Added,
277
292
  }),
278
293
  );
@@ -345,10 +360,10 @@ function processPatch(key: string, patch: unknown, original: unknown | null): Au
345
360
  ov = null;
346
361
  v = null;
347
362
 
348
- if (l.length === 0) {
349
- // Probably no change
350
- return [];
351
- }
363
+ // if (l.length === 0) {
364
+ // Probably no change
365
+ return [];
366
+ // }
352
367
  }
353
368
 
354
369
  // Simplify changes by providing one change instead of for all keys
@@ -380,15 +395,16 @@ function processDelete(key: string, deletedItem: unknown, createdIdSet?: Set<str
380
395
  }
381
396
 
382
397
  const v = getAutoEncoderPutValue(deletedItem);
398
+ const n = getAutoEncoderName(deletedItem);
383
399
 
384
400
  const keySingular = getKeySingular(key);
385
- const k = AuditLogReplacement.key(keySingular).append(getAutoEncoderName(deletedItem));
401
+ const k = AuditLogReplacement.key(keySingular).append(n);
386
402
 
387
403
  return [
388
404
  AuditLogPatchItem.create({
389
405
  key: k,
390
406
  type: AuditLogPatchItemType.Removed,
391
- oldValue: v ?? undefined,
407
+ oldValue: (n?.toString() !== v?.toString()) ? (v || undefined) : undefined,
392
408
  }),
393
409
  ];
394
410
  }
@@ -404,16 +420,25 @@ function createArrayChangeHandler(key: string) {
404
420
  if (!isPatchableArray(value)) {
405
421
  if (Array.isArray(value)) {
406
422
  // Search for puts
407
- for (const newItem of value) {
408
- const original = findOriginal(newItem, oldValue);
423
+ let orderChanged = false;
424
+ let added = 0;
425
+ for (const [index, newItem] of value.entries()) {
426
+ const originalIndex = findIndex(newItem, oldValue);
409
427
 
410
- if (!original) {
428
+ if (originalIndex === -1) {
411
429
  // Has been added
412
- items.push(...processPut(key, newItem, original));
430
+ items.push(...processPut(key, newItem, null));
431
+ added++;
413
432
  }
414
433
  else {
415
434
  // Has been overwritten
435
+ const original = oldValue[originalIndex];
416
436
  items.push(...processPatch(key, newItem, original));
437
+
438
+ if ((index - added) !== originalIndex) {
439
+ // Order has changed
440
+ orderChanged = true;
441
+ }
417
442
  }
418
443
  }
419
444
 
@@ -423,8 +448,20 @@ function createArrayChangeHandler(key: string) {
423
448
  if (!newItem) {
424
449
  // Has been deleted
425
450
  items.push(...processDelete(key, original));
451
+
452
+ orderChanged = false; // ignore order changed as delete will have set it to true
426
453
  }
427
454
  }
455
+
456
+ if (orderChanged) {
457
+ // Check if order has changed
458
+ items.push(
459
+ AuditLogPatchItem.create({
460
+ key: getAutoEncoderKey(key),
461
+ type: AuditLogPatchItemType.Reordered,
462
+ }),
463
+ );
464
+ }
428
465
  }
429
466
  // Not supported
430
467
  return items;
@@ -524,10 +561,10 @@ function createMapChangeHandler(key?: string) {
524
561
  ov = null;
525
562
  nv = null;
526
563
 
527
- if (c.length === 0) {
528
- // Probably no change
529
- continue;
530
- }
564
+ // if (c.length === 0) {
565
+ // Probably no change
566
+ continue;
567
+ // }
531
568
  }
532
569
 
533
570
  // Simplify change
@@ -587,32 +624,32 @@ function createMapChangeHandler(key?: string) {
587
624
  };
588
625
  }
589
626
 
590
- function createUnknownChangeHandler(key: string) {
627
+ export function createUnknownChangeHandler(key: string) {
591
628
  return (oldValue: unknown, value: unknown) => {
592
- if (typeof value !== 'object' && value !== null) {
629
+ if (oldValue === value) {
593
630
  return [];
594
631
  }
595
632
 
596
- if (oldValue === value) {
597
- return [];
633
+ if ((Array.isArray(oldValue) || Array.isArray(value)) && (!oldValue || Array.isArray(oldValue)) && (Array.isArray(value) || !value)) {
634
+ return createArrayChangeHandler(key)(oldValue, value);
598
635
  }
599
636
 
600
- if (!oldValue && getAutoEncoderValue(value)) {
637
+ if (!oldValue && oldValue !== 0 && getAutoEncoderValue(value, key)) {
601
638
  // Simplify addition
602
639
  return [
603
640
  AuditLogPatchItem.create({
604
641
  key: getAutoEncoderKey(key),
605
- value: getAutoEncoderPutValue(value) || undefined,
642
+ value: getAutoEncoderPutValue(value, key) || undefined,
606
643
  type: AuditLogPatchItemType.Added,
607
644
  }),
608
645
  ];
609
646
  }
610
647
 
611
- if (oldValue && value === null) {
648
+ if ((oldValue || oldValue === 0) && (value === null || value === undefined)) {
612
649
  return [
613
650
  AuditLogPatchItem.create({
614
651
  key: getAutoEncoderKey(key),
615
- oldValue: getAutoEncoderPutValue(oldValue) || undefined,
652
+ oldValue: getAutoEncoderPutValue(oldValue, key) || undefined,
616
653
  type: AuditLogPatchItemType.Removed,
617
654
  }),
618
655
  ];
@@ -623,10 +660,10 @@ function createUnknownChangeHandler(key: string) {
623
660
  return i;
624
661
  });
625
662
 
626
- let v = getAutoEncoderValue(value);
627
- let ov = getAutoEncoderValue(oldValue);
663
+ let v = getAutoEncoderValue(value, key);
664
+ let ov = getAutoEncoderValue(oldValue, key);
628
665
 
629
- if (oldValue && value && getAutoEncoderValue(value) && items.length === 0 && value instanceof AutoEncoder && value.isPatch()) {
666
+ if (oldValue !== undefined && oldValue !== null && value !== undefined && value !== null && getAutoEncoderValue(value, key) && items.length === 0 && value instanceof AutoEncoder && value.isPatch()) {
630
667
  return [
631
668
  AuditLogPatchItem.create({
632
669
  key: getAutoEncoderKey(key),
@@ -643,10 +680,10 @@ function createUnknownChangeHandler(key: string) {
643
680
  v = null;
644
681
  ov = null;
645
682
 
646
- if (items.length === 0) {
647
- // Probably no change
648
- return [];
649
- }
683
+ // if (items.length === 0) {
684
+ // Probably no change
685
+ return [];
686
+ // }
650
687
  }
651
688
 
652
689
  return [
@@ -694,7 +731,39 @@ function getExplainerForField(field: Field<any>) {
694
731
  }
695
732
 
696
733
  if (field.decoder instanceof ArrayDecoder) {
697
- return createArrayChangeHandler(field.property);
734
+ const handler = createArrayChangeHandler(field.property);
735
+
736
+ if (field.decoder.decoder instanceof EnumDecoder) {
737
+ // Map values to keys
738
+ return (oldValue: unknown, value: unknown) => {
739
+ const items = handler(oldValue, value);
740
+
741
+ for (const item of items) {
742
+ if (item.oldValue && !item.oldValue.type) {
743
+ item.oldValue.type = AuditLogReplacementType.Key;
744
+ }
745
+ if (item.value && !item.value.type) {
746
+ item.value.type = AuditLogReplacementType.Key;
747
+ }
748
+
749
+ // If item.key is an array that ends with a 'value', also change it
750
+ if (item.key.type === AuditLogReplacementType.Array) {
751
+ const lastKeyItem = item.key.values[item.key.values.length - 1];
752
+ if (!lastKeyItem.type) {
753
+ lastKeyItem.type = AuditLogReplacementType.Key;
754
+ }
755
+ }
756
+ else {
757
+ if (!item.key.type) {
758
+ item.key.type = AuditLogReplacementType.Key;
759
+ }
760
+ }
761
+ }
762
+ return items;
763
+ };
764
+ }
765
+
766
+ return handler;
698
767
  }
699
768
 
700
769
  if (field.decoder instanceof MapDecoder) {
@@ -1,54 +0,0 @@
1
- import { logger } from '@simonbackx/simple-logging';
2
- import { Member } from '@stamhoofd/models';
3
- import { QueueHandler } from '@stamhoofd/queues';
4
-
5
- export class MembershipHelper {
6
- static async updateAll() {
7
- console.log('Scheduling updateAllMemberships');
8
-
9
- let c = 0;
10
- let id: string = '';
11
- const tag = 'updateAllMemberships';
12
- const batch = 100;
13
-
14
- QueueHandler.cancel(tag);
15
-
16
- await QueueHandler.schedule(tag, async () => {
17
- console.log('Starting updateAllMemberships');
18
- await logger.setContext({ tags: ['silent-seed', 'seed'] }, async () => {
19
- while (true) {
20
- const rawMembers = await Member.where({
21
- id: {
22
- value: id,
23
- sign: '>',
24
- },
25
- }, { limit: batch, sort: ['id'] });
26
-
27
- if (rawMembers.length === 0) {
28
- break;
29
- }
30
-
31
- const promises: Promise<any>[] = [];
32
-
33
- for (const member of rawMembers) {
34
- promises.push((async () => {
35
- await Member.updateMembershipsForId(member.id, true);
36
- c++;
37
-
38
- if (c % 10000 === 0) {
39
- process.stdout.write(c + ' members updated\n');
40
- }
41
- })());
42
- }
43
-
44
- await Promise.all(promises);
45
- id = rawMembers[rawMembers.length - 1].id;
46
-
47
- if (rawMembers.length < batch) {
48
- break;
49
- }
50
- }
51
- });
52
- });
53
- }
54
- }