@stamhoofd/backend 2.58.0 → 2.60.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 (56) hide show
  1. package/index.ts +10 -1
  2. package/package.json +12 -12
  3. package/src/audit-logs/DocumentTemplateLogger.ts +22 -0
  4. package/src/audit-logs/EventLogger.ts +30 -0
  5. package/src/audit-logs/GroupLogger.ts +95 -0
  6. package/src/audit-logs/MemberLogger.ts +24 -0
  7. package/src/audit-logs/MemberPlatformMembershipLogger.ts +60 -0
  8. package/src/audit-logs/MemberResponsibilityRecordLogger.ts +69 -0
  9. package/src/audit-logs/ModelLogger.ts +219 -0
  10. package/src/audit-logs/OrderLogger.ts +57 -0
  11. package/src/audit-logs/OrganizationLogger.ts +16 -0
  12. package/src/audit-logs/OrganizationRegistrationPeriodLogger.ts +77 -0
  13. package/src/audit-logs/PaymentLogger.ts +43 -0
  14. package/src/audit-logs/PlatformLogger.ts +13 -0
  15. package/src/audit-logs/RegistrationLogger.ts +53 -0
  16. package/src/audit-logs/RegistrationPeriodLogger.ts +21 -0
  17. package/src/audit-logs/StripeAccountLogger.ts +47 -0
  18. package/src/audit-logs/WebshopLogger.ts +35 -0
  19. package/src/crons/updateSetupSteps.ts +1 -1
  20. package/src/crons.ts +2 -1
  21. package/src/endpoints/global/events/PatchEventsEndpoint.ts +12 -24
  22. package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.ts +7 -21
  23. package/src/endpoints/global/payments/StripeWebhookEndpoint.ts +5 -13
  24. package/src/endpoints/global/platform/PatchPlatformEnpoint.ts +5 -12
  25. package/src/endpoints/global/registration/PatchUserMembersEndpoint.ts +0 -15
  26. package/src/endpoints/global/registration/RegisterMembersEndpoint.ts +43 -27
  27. package/src/endpoints/global/registration-periods/PatchRegistrationPeriodsEndpoint.ts +0 -19
  28. package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.ts +3 -13
  29. package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.ts +20 -43
  30. package/src/endpoints/organization/dashboard/stripe/ConnectStripeEndpoint.ts +0 -6
  31. package/src/endpoints/organization/dashboard/stripe/DeleteStripeAccountEndpoint.ts +0 -6
  32. package/src/endpoints/organization/dashboard/stripe/UpdateStripeAccountEndpoint.ts +5 -14
  33. package/src/endpoints/organization/dashboard/webshops/PatchWebshopOrdersEndpoint.ts +7 -4
  34. package/src/endpoints/organization/webshops/PlaceOrderEndpoint.ts +8 -2
  35. package/src/helpers/AuthenticatedStructures.ts +16 -1
  36. package/src/helpers/Context.ts +8 -2
  37. package/src/helpers/MemberUserSyncer.ts +45 -40
  38. package/src/helpers/PeriodHelper.ts +5 -5
  39. package/src/helpers/SetupStepUpdater.ts +503 -0
  40. package/src/helpers/TagHelper.ts +23 -20
  41. package/src/seeds/1722344162-update-membership.ts +2 -2
  42. package/src/seeds/1726572303-schedule-stock-updates.ts +2 -1
  43. package/src/seeds/1726847064-setup-steps.ts +1 -1
  44. package/src/seeds/1733319079-fill-paying-organization-ids.ts +68 -0
  45. package/src/services/AuditLogService.ts +81 -296
  46. package/src/services/BalanceItemPaymentService.ts +1 -1
  47. package/src/services/BalanceItemService.ts +14 -5
  48. package/src/services/DocumentService.ts +43 -0
  49. package/src/services/MemberNumberService.ts +120 -0
  50. package/src/services/PaymentService.ts +199 -193
  51. package/src/services/PlatformMembershipService.ts +284 -0
  52. package/src/services/RegistrationService.ts +78 -27
  53. package/src/services/diff.ts +512 -0
  54. package/src/sql-filters/events.ts +13 -1
  55. package/src/helpers/MembershipHelper.ts +0 -54
  56. package/src/services/explainPatch.ts +0 -782
@@ -209,6 +209,18 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
209
209
  });
210
210
  }
211
211
 
212
+ // Who is going to pay?
213
+ let whoWillPayNow: 'member' | 'organization' | 'nobody' = 'member'; // if this is set to 'organization', there will also be created separate balance items so the member can pay back the paying organization
214
+
215
+ if (request.body.asOrganizationId && request.body.asOrganizationId === organization.id) {
216
+ // Will get added to the outstanding amount of the member / already paying organization
217
+ whoWillPayNow = 'nobody';
218
+ }
219
+ else if (request.body.asOrganizationId && request.body.asOrganizationId !== organization.id) {
220
+ // The organization will pay to the organizing organization, and it will get added to the outstanding amount of the member towards the paying organization
221
+ whoWillPayNow = 'organization';
222
+ }
223
+
212
224
  const registrationMemberRelation = new ManyToOneRelation(Member, 'member');
213
225
  registrationMemberRelation.foreignKey = 'memberId';
214
226
 
@@ -264,6 +276,19 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
264
276
  registration.options = item.options;
265
277
  registration.recordAnswers = item.recordAnswers;
266
278
 
279
+ if (whoWillPayNow === 'organization' && request.body.asOrganizationId) {
280
+ registration.payingOrganizationId = request.body.asOrganizationId;
281
+ }
282
+
283
+ if (whoWillPayNow === 'nobody' && item.replaceRegistrations.length > 0) {
284
+ // If the replace registration was paid by an organization
285
+ // Make sure this updated registration will also be paid by the organization, not the member
286
+ const paidAsOrganization = item.replaceRegistrations[0].payingOrganizationId;
287
+ if (paidAsOrganization) {
288
+ registration.payingOrganizationId = paidAsOrganization;
289
+ }
290
+ }
291
+
267
292
  payRegistrations.push({
268
293
  registration,
269
294
  item,
@@ -272,18 +297,6 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
272
297
  registrations.push(registration);
273
298
  }
274
299
 
275
- // Who is going to pay?
276
- let whoWillPayNow: 'member' | 'organization' | 'nobody' = 'member'; // if this is set to 'organization', there will also be created separate balance items so the member can pay back the paying organization
277
-
278
- if (request.body.asOrganizationId && request.body.asOrganizationId === organization.id) {
279
- // Will get added to the outstanding amount of the member
280
- whoWillPayNow = 'nobody';
281
- }
282
- else if (request.body.asOrganizationId && request.body.asOrganizationId !== organization.id) {
283
- // The organization will pay to the organizing organization, and it will get added to the outstanding amount of the member towards the paying organization
284
- whoWillPayNow = 'organization';
285
- }
286
-
287
300
  // Validate payment method
288
301
  if (totalPrice > 0 && whoWillPayNow !== 'nobody') {
289
302
  const allowedPaymentMethods = organization.meta.registrationPaymentConfiguration.getAvailablePaymentMethods({
@@ -369,8 +382,13 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
369
382
  deactivatedRegistrationGroupIds.push(existingRegistration.groupId);
370
383
  }
371
384
 
372
- async function createBalanceItem({ registration, amount, unitPrice, description, type, relations }: { amount?: number; registration: RegistrationWithMemberAndGroup; unitPrice: number; description: string; relations: Map<BalanceItemRelationType, BalanceItemRelation>; type: BalanceItemType }) {
385
+ async function createBalanceItem({ registration, skipZero, amount, unitPrice, description, type, relations }: { amount?: number; skipZero?: boolean; registration: RegistrationWithMemberAndGroup; unitPrice: number; description: string; relations: Map<BalanceItemRelationType, BalanceItemRelation>; type: BalanceItemType }) {
373
386
  // NOTE: We also need to save zero-price balance items because for online payments, we need to know which registrations to activate after payment
387
+ if (skipZero === true) {
388
+ if (unitPrice === 0 || amount === 0) {
389
+ return;
390
+ }
391
+ }
374
392
 
375
393
  // Create balance item
376
394
  const balanceItem = new BalanceItem();
@@ -386,11 +404,11 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
386
404
 
387
405
  // Who is responsible for payment?
388
406
  let balanceItem2: BalanceItem | null = null;
389
- if (whoWillPayNow === 'organization' && request.body.asOrganizationId) {
407
+ if (registration.payingOrganizationId) {
390
408
  // Create a separate balance item for this meber to pay back the paying organization
391
409
  // this is not yet associated with a payment but will be added to the outstanding balance of the member
392
410
 
393
- balanceItem.payingOrganizationId = request.body.asOrganizationId;
411
+ balanceItem.payingOrganizationId = registration.payingOrganizationId;
394
412
 
395
413
  balanceItem2 = new BalanceItem();
396
414
 
@@ -405,7 +423,7 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
405
423
  balanceItem2.type = type;
406
424
 
407
425
  // Who needs to receive this money?
408
- balanceItem2.organizationId = request.body.asOrganizationId;
426
+ balanceItem2.organizationId = registration.payingOrganizationId;
409
427
 
410
428
  // Who is responsible for payment?
411
429
  balanceItem2.memberId = registration.memberId;
@@ -437,26 +455,19 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
437
455
  const { item, registration } = bundle;
438
456
  registration.reservedUntil = null;
439
457
 
440
- /* if (shouldMarkValid) {
441
- await registration.markValid({ skipEmail: bundle.item.replaceRegistrations.length > 0 });
442
- }
443
- else { */
444
458
  // Reserve registration for 30 minutes (if needed)
445
459
  const group = groups.find(g => g.id === registration.groupId);
446
460
 
447
461
  if (group && group.settings.maxMembers !== null) {
448
462
  registration.reservedUntil = new Date(new Date().getTime() + 1000 * 60 * 30);
449
463
  }
464
+
465
+ // Only now save the registration
450
466
  await registration.save();
451
- // }
452
467
 
453
468
  // Note: we should always create the balance items: even when the price is zero
454
469
  // Otherwise we don't know which registrations to activate after payment
455
470
 
456
- if (shouldMarkValid && item.calculatedPrice === 0) {
457
- // continue;
458
- }
459
-
460
471
  // Create balance items
461
472
  const sharedRelations: [BalanceItemRelationType, BalanceItemRelation][] = [
462
473
  [
@@ -490,6 +501,7 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
490
501
  registration,
491
502
  unitPrice: item.groupPrice.price.forMember(item.member),
492
503
  type: BalanceItemType.Registration,
504
+ skipZero: false, // Always create at least one balance item for each registration - even when the price is zero
493
505
  description: `${item.member.patchedMember.name} bij ${item.group.settings.name}`,
494
506
  relations: new Map([
495
507
  ...sharedRelations,
@@ -502,6 +514,7 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
502
514
  registration,
503
515
  amount: option.amount,
504
516
  unitPrice: option.option.price.forMember(item.member),
517
+ skipZero: true, // Do not create for zero option prices
505
518
  type: BalanceItemType.Registration,
506
519
  description: `${option.optionMenu.name}: ${option.option.name}`,
507
520
  relations: new Map([
@@ -583,10 +596,13 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
583
596
  let paymentUrl: string | null = null;
584
597
  let payment: Payment | null = null;
585
598
 
586
- // Delaying markign as valid as late as possible so any errors will prevent creating valid balance items
599
+ // Delay marking as valid as late as possible so any errors will prevent creating valid balance items
600
+ // Keep a copy because createdBalanceItems will be altered - and we don't want to mark added items as valid
601
+ const markValidList = [...createdBalanceItems, ...unrelatedCreatedBalanceItems];
602
+
587
603
  async function markValidIfNeeded() {
588
604
  if (shouldMarkValid) {
589
- for (const balanceItem of [...createdBalanceItems, ...unrelatedCreatedBalanceItems]) {
605
+ for (const balanceItem of markValidList) {
590
606
  // Mark valid
591
607
  await BalanceItemService.markPaid(balanceItem, payment, organization);
592
608
  }
@@ -63,11 +63,6 @@ export class PatchRegistrationPeriodsEndpoint extends Endpoint<Params, Query, Bo
63
63
 
64
64
  await period.save();
65
65
  periods.push(period);
66
-
67
- await AuditLogService.log({
68
- type: AuditLogType.RegistrationPeriodAdded,
69
- period,
70
- });
71
66
  }
72
67
 
73
68
  for (const patch of request.body.getPatches()) {
@@ -80,8 +75,6 @@ export class PatchRegistrationPeriodsEndpoint extends Endpoint<Params, Query, Bo
80
75
  message: 'Registration period not found',
81
76
  });
82
77
  }
83
- const initialStructure = model.getStructure().clone();
84
-
85
78
  if (patch.startDate !== undefined) {
86
79
  model.startDate = patch.startDate;
87
80
  }
@@ -102,13 +95,6 @@ export class PatchRegistrationPeriodsEndpoint extends Endpoint<Params, Query, Bo
102
95
 
103
96
  // Schedule patch of all groups in this period
104
97
  PeriodHelper.updateGroupsInPeriod(model).catch(console.error);
105
-
106
- await AuditLogService.log({
107
- type: AuditLogType.RegistrationPeriodEdited,
108
- period: model,
109
- patch,
110
- oldData: initialStructure,
111
- });
112
98
  }
113
99
 
114
100
  for (const id of request.body.getDeletes()) {
@@ -123,11 +109,6 @@ export class PatchRegistrationPeriodsEndpoint extends Endpoint<Params, Query, Bo
123
109
  }
124
110
 
125
111
  await model.delete();
126
-
127
- await AuditLogService.log({
128
- type: AuditLogType.RegistrationPeriodDeleted,
129
- period: model,
130
- });
131
112
  }
132
113
 
133
114
  // Clear platform cache
@@ -1,8 +1,8 @@
1
1
  import { AutoEncoderPatchType, cloneObject, Decoder, isPatchableArray, ObjectData, PatchableArrayAutoEncoder, patchObject } from '@simonbackx/simple-encoding';
2
2
  import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
3
3
  import { SimpleError, SimpleErrors } from '@simonbackx/simple-errors';
4
- import { Organization, OrganizationRegistrationPeriod, PayconiqPayment, Platform, RegistrationPeriod, SetupStepUpdater, StripeAccount, Webshop } from '@stamhoofd/models';
5
- import { AuditLogType, BuckarooSettings, Company, OrganizationMetaData, OrganizationPatch, Organization as OrganizationStruct, PayconiqAccount, PaymentMethod, PaymentMethodHelper, PermissionLevel, PlatformConfig } from '@stamhoofd/structures';
4
+ import { Organization, OrganizationRegistrationPeriod, PayconiqPayment, Platform, RegistrationPeriod, StripeAccount, Webshop } from '@stamhoofd/models';
5
+ import { BuckarooSettings, Company, OrganizationMetaData, OrganizationPatch, Organization as OrganizationStruct, PayconiqAccount, PaymentMethod, PaymentMethodHelper, PermissionLevel, PlatformConfig } from '@stamhoofd/structures';
6
6
  import { Formatter } from '@stamhoofd/utility';
7
7
 
8
8
  import { AuthenticatedStructures } from '../../../../helpers/AuthenticatedStructures';
@@ -10,7 +10,7 @@ import { BuckarooHelper } from '../../../../helpers/BuckarooHelper';
10
10
  import { Context } from '../../../../helpers/Context';
11
11
  import { TagHelper } from '../../../../helpers/TagHelper';
12
12
  import { ViesHelper } from '../../../../helpers/ViesHelper';
13
- import { AuditLogService } from '../../../../services/AuditLogService';
13
+ import { SetupStepUpdater } from '../../../../helpers/SetupStepUpdater';
14
14
 
15
15
  type Params = Record<string, never>;
16
16
  type Query = undefined;
@@ -63,8 +63,6 @@ export class PatchOrganizationEndpoint extends Endpoint<Params, Query, Body, Res
63
63
  });
64
64
  }
65
65
 
66
- const initialStruct = (await AuthenticatedStructures.organization(organization)).clone();
67
-
68
66
  const errors = new SimpleErrors();
69
67
  let shouldUpdateSetupSteps = false;
70
68
  let updateTags = false;
@@ -387,14 +385,6 @@ export class PatchOrganizationEndpoint extends Endpoint<Params, Query, Body, Res
387
385
  await TagHelper.updateOrganizations();
388
386
  }
389
387
  const struct = await AuthenticatedStructures.organization(organization);
390
-
391
- await AuditLogService.log({
392
- type: AuditLogType.OrganizationSettingsChanged,
393
- organization,
394
- oldData: initialStruct,
395
- patch: struct,
396
- });
397
-
398
388
  return new Response(struct);
399
389
  }
400
390
 
@@ -1,12 +1,12 @@
1
1
  import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
2
- import { AuditLogType, GroupPrivateSettings, Group as GroupStruct, GroupType, OrganizationRegistrationPeriod as OrganizationRegistrationPeriodStruct, PermissionLevel, PermissionsResourceType, ResourcePermissions, Version } from '@stamhoofd/structures';
2
+ import { GroupPrivateSettings, Group as GroupStruct, GroupType, OrganizationRegistrationPeriod as OrganizationRegistrationPeriodStruct, PermissionLevel, PermissionsResourceType, ResourcePermissions, Version } from '@stamhoofd/structures';
3
3
 
4
4
  import { AutoEncoderPatchType, Decoder, PatchableArrayAutoEncoder, PatchableArrayDecoder, StringDecoder } from '@simonbackx/simple-encoding';
5
5
  import { SimpleError } from '@simonbackx/simple-errors';
6
- import { Group, Member, Organization, OrganizationRegistrationPeriod, Platform, RegistrationPeriod, SetupStepUpdater } from '@stamhoofd/models';
6
+ import { Group, Organization, OrganizationRegistrationPeriod, Platform, RegistrationPeriod } from '@stamhoofd/models';
7
7
  import { AuthenticatedStructures } from '../../../../helpers/AuthenticatedStructures';
8
8
  import { Context } from '../../../../helpers/Context';
9
- import { AuditLogService } from '../../../../services/AuditLogService';
9
+ import { SetupStepUpdater } from '../../../../helpers/SetupStepUpdater';
10
10
 
11
11
  type Params = Record<string, never>;
12
12
  type Query = undefined;
@@ -81,6 +81,20 @@ export class PatchOrganizationRegistrationPeriodsEndpoint extends Endpoint<Param
81
81
 
82
82
  let deleteUnreachable = false;
83
83
  const allowedIds: string[] = [];
84
+ let wasInvalid = false;
85
+
86
+ // Check if have an initial invalid state
87
+ if (patch.settings) {
88
+ // Already clean up the categories (not yet delete the groups)
89
+ const groups = await Group.getAll(organization.id, organizationPeriod.periodId);
90
+ await organizationPeriod.cleanCategories(groups);
91
+
92
+ for (const category of organizationPeriod.settings.categories) {
93
+ if (category.groupIds.length && category.categoryIds.length) {
94
+ wasInvalid = true;
95
+ }
96
+ }
97
+ }
84
98
 
85
99
  // #region prevent patch category lock if no full platform access
86
100
  const originalCategories = organizationPeriod.settings.categories;
@@ -90,7 +104,7 @@ export class PatchOrganizationRegistrationPeriodsEndpoint extends Endpoint<Param
90
104
  if (patch.settings.categories) {
91
105
  deleteUnreachable = true;
92
106
  }
93
- organizationPeriod.settings.patchOrPut(patch.settings);
107
+ organizationPeriod.settings = organizationPeriod.settings.patch(patch.settings);
94
108
  }
95
109
  }
96
110
  else {
@@ -123,7 +137,7 @@ export class PatchOrganizationRegistrationPeriodsEndpoint extends Endpoint<Param
123
137
  }
124
138
 
125
139
  // Check if we have a category with groups and categories combined
126
- if (patch.settings) {
140
+ if (patch.settings && !wasInvalid) {
127
141
  for (const category of organizationPeriod.settings.categories) {
128
142
  if (category.groupIds.length && category.categoryIds.length) {
129
143
  throw new SimpleError({
@@ -281,12 +295,6 @@ export class PatchOrganizationRegistrationPeriodsEndpoint extends Endpoint<Param
281
295
 
282
296
  model.deletedAt = new Date();
283
297
  await model.save();
284
- Member.updateMembershipsForGroupId(id);
285
-
286
- await AuditLogService.log({
287
- type: AuditLogType.GroupDeleted,
288
- group: model,
289
- });
290
298
  }
291
299
 
292
300
  static async patchGroup(struct: AutoEncoderPatchType<GroupStruct>, period?: RegistrationPeriod | null) {
@@ -295,12 +303,6 @@ export class PatchOrganizationRegistrationPeriodsEndpoint extends Endpoint<Param
295
303
  if (!model || !await Context.auth.canAccessGroup(model, PermissionLevel.Full)) {
296
304
  throw Context.auth.error('Je hebt geen toegangsrechten om deze groep te wijzigen');
297
305
  }
298
- const originalStruct = (await AuthenticatedStructures.group(model)).clone(); // Clone is required for deep changes
299
-
300
- const previousProperties = {
301
- deletedAt: model.deletedAt,
302
- defaultAgeGroupId: model.defaultAgeGroupId,
303
- };
304
306
 
305
307
  if (struct.settings) {
306
308
  struct.settings.period = undefined; // Not allowed to patch manually
@@ -416,26 +418,8 @@ export class PatchOrganizationRegistrationPeriodsEndpoint extends Endpoint<Param
416
418
  }
417
419
  }
418
420
 
419
- await model.updateOccupancy({
420
- previousProperties,
421
- });
421
+ await model.updateOccupancy();
422
422
  await model.save();
423
-
424
- if (struct.deletedAt !== undefined || struct.defaultAgeGroupId !== undefined) {
425
- Member.updateMembershipsForGroupId(model.id);
426
- }
427
-
428
- if (Object.keys(struct).length === 1 && struct.id) {
429
- // Nothing changed
430
- return;
431
- }
432
-
433
- await AuditLogService.log({
434
- type: AuditLogType.GroupEdited,
435
- group: model,
436
- oldData: originalStruct,
437
- patch,
438
- });
439
423
  }
440
424
 
441
425
  static async createGroup(struct: GroupStruct, organizationId: string, period: RegistrationPeriod, options?: { allowedIds?: string[] }): Promise<Group> {
@@ -515,13 +499,6 @@ export class PatchOrganizationRegistrationPeriodsEndpoint extends Endpoint<Param
515
499
  }
516
500
 
517
501
  await model.save();
518
- await model.updateOccupancy({ isNew: true }); // Force update steps
519
-
520
- await AuditLogService.log({
521
- type: AuditLogType.GroupAdded,
522
- group: model,
523
- });
524
-
525
502
  return model;
526
503
  }
527
504
  }
@@ -92,12 +92,6 @@ export class ConnectMollieEndpoint extends Endpoint<Params, Query, Body, Respons
92
92
  model.setMetaFromStripeAccount(account);
93
93
  await model.save();
94
94
 
95
- // Track audit log
96
- await AuditLogService.log({
97
- type: AuditLogType.StripeAccountAdded,
98
- stripeAccount: model,
99
- });
100
-
101
95
  // Return information about the Stripe Account
102
96
 
103
97
  return new Response(StripeAccountStruct.create(model));
@@ -64,12 +64,6 @@ export class DeleteStripeAccountEndpoint extends Endpoint<Params, Query, Body, R
64
64
  model.status = 'deleted';
65
65
  await model.save();
66
66
 
67
- // Track audit log
68
- await AuditLogService.log({
69
- type: AuditLogType.StripeAccountDeleted,
70
- stripeAccount: model,
71
- });
72
-
73
67
  return new Response(undefined);
74
68
  }
75
69
  }
@@ -1,11 +1,10 @@
1
1
  import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
2
2
  import { StripeAccount } from '@stamhoofd/models';
3
- import { AuditLogType, PermissionLevel, StripeAccount as StripeAccountStruct } from '@stamhoofd/structures';
3
+ import { AuditLogSource, PermissionLevel, StripeAccount as StripeAccountStruct } from '@stamhoofd/structures';
4
4
 
5
5
  import { Context } from '../../../../helpers/Context';
6
6
  import { StripeHelper } from '../../../../helpers/StripeHelper';
7
7
  import { AuditLogService } from '../../../../services/AuditLogService';
8
- import { Model } from '@simonbackx/simple-database';
9
8
 
10
9
  type Params = { id: string };
11
10
  type Body = undefined;
@@ -45,19 +44,11 @@ export class UpdateStripeAccountEndpoint extends Endpoint<Params, Query, Body, R
45
44
  // Get account
46
45
  const stripe = StripeHelper.getInstance();
47
46
  const account = await stripe.accounts.retrieve(model.accountId);
48
- const beforeMeta = model.meta;
49
- model.setMetaFromStripeAccount(account);
50
-
51
- if (await model.save()) {
52
- // Track audit log
53
- await AuditLogService.log({
54
- type: AuditLogType.StripeAccountEdited,
55
- stripeAccount: model,
56
- oldData: beforeMeta,
57
- patch: model.meta,
58
- });
59
- }
60
47
 
48
+ await AuditLogService.setContext({ userId: null, source: AuditLogSource.System }, async () => {
49
+ model.setMetaFromStripeAccount(account);
50
+ await model.save();
51
+ });
61
52
  return new Response(StripeAccountStruct.create(model));
62
53
  }
63
54
  }
@@ -3,9 +3,10 @@ import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-
3
3
  import { SimpleError } from '@simonbackx/simple-errors';
4
4
  import { BalanceItem, BalanceItemPayment, Order, Payment, Token, Webshop } from '@stamhoofd/models';
5
5
  import { QueueHandler } from '@stamhoofd/queues';
6
- import { BalanceItemRelation, BalanceItemRelationType, BalanceItemStatus, BalanceItemType, OrderStatus, PaymentMethod, PaymentStatus, PermissionLevel, PrivateOrder, PrivatePayment, Webshop as WebshopStruct } from '@stamhoofd/structures';
6
+ import { AuditLogSource, BalanceItemRelation, BalanceItemRelationType, BalanceItemStatus, BalanceItemType, OrderStatus, PaymentMethod, PaymentStatus, PermissionLevel, PrivateOrder, PrivatePayment, Webshop as WebshopStruct } from '@stamhoofd/structures';
7
7
 
8
8
  import { Context } from '../../../../helpers/Context';
9
+ import { AuditLogService } from '../../../../services/AuditLogService';
9
10
 
10
11
  type Params = { id: string };
11
12
  type Query = undefined;
@@ -131,7 +132,7 @@ export class PatchWebshopOrdersEndpoint extends Endpoint<Params, Query, Body, Re
131
132
  order.data.validate(webshopGetter.struct, organization.meta, request.i18n, true);
132
133
 
133
134
  try {
134
- await order.updateStock();
135
+ await order.updateStock(null, true);
135
136
  const totalPrice = order.data.totalPrice;
136
137
 
137
138
  if (totalPrice == 0) {
@@ -286,9 +287,11 @@ export class PatchWebshopOrdersEndpoint extends Endpoint<Params, Query, Body, Re
286
287
  }
287
288
  }
288
289
 
290
+ await AuditLogService.setContext({ source: AuditLogSource.System }, async () => {
291
+ await model.setRelation(Order.webshop, webshop).updateStock(previousData, true);
292
+ await model.setRelation(Order.webshop, webshop).updateTickets();
293
+ });
289
294
  await model.save();
290
- await model.setRelation(Order.webshop, webshop).updateStock(previousData);
291
- await model.setRelation(Order.webshop, webshop).updateTickets();
292
295
  }
293
296
 
294
297
  const mapped = orders.map(order => order.setRelation(Order.webshop, webshop));
@@ -6,12 +6,13 @@ import { I18n } from '@stamhoofd/backend-i18n';
6
6
  import { Email } from '@stamhoofd/email';
7
7
  import { BalanceItem, BalanceItemPayment, MolliePayment, MollieToken, Order, PayconiqPayment, Payment, RateLimiter, Webshop, WebshopDiscountCode } from '@stamhoofd/models';
8
8
  import { QueueHandler } from '@stamhoofd/queues';
9
- import { BalanceItemStatus, Order as OrderStruct, OrderData, OrderResponse, Payment as PaymentStruct, PaymentMethod, PaymentMethodHelper, PaymentProvider, PaymentStatus, Version, Webshop as WebshopStruct, WebshopAuthType, BalanceItemType, BalanceItemRelationType, BalanceItemRelation } from '@stamhoofd/structures';
9
+ import { BalanceItemStatus, Order as OrderStruct, OrderData, OrderResponse, Payment as PaymentStruct, PaymentMethod, PaymentMethodHelper, PaymentProvider, PaymentStatus, Version, Webshop as WebshopStruct, WebshopAuthType, BalanceItemType, BalanceItemRelationType, BalanceItemRelation, AuditLogSource } from '@stamhoofd/structures';
10
10
  import { Formatter } from '@stamhoofd/utility';
11
11
 
12
12
  import { BuckarooHelper } from '../../../helpers/BuckarooHelper';
13
13
  import { Context } from '../../../helpers/Context';
14
14
  import { StripeHelper } from '../../../helpers/StripeHelper';
15
+ import { AuditLogService } from '../../../services/AuditLogService';
15
16
 
16
17
  type Params = { id: string };
17
18
  type Query = undefined;
@@ -140,7 +141,12 @@ export class PlaceOrderEndpoint extends Endpoint<Params, Query, Body, ResponseBo
140
141
  order.userId = Context.user?.id ?? null;
141
142
 
142
143
  // Always reserve the stock
143
- await order.updateStock();
144
+ await AuditLogService.setContext({ source: AuditLogSource.System }, async () => {
145
+ await order.updateStock(null, true);
146
+ });
147
+
148
+ await order.save();
149
+
144
150
  return { webshop, order, organization };
145
151
  });
146
152
 
@@ -1,6 +1,6 @@
1
1
  import { SimpleError } from '@simonbackx/simple-errors';
2
2
  import { AuditLog, BalanceItem, CachedBalance, Document, Event, Group, Member, MemberPlatformMembership, MemberResponsibilityRecord, MemberWithRegistrations, Order, Organization, OrganizationRegistrationPeriod, Payment, RegistrationPeriod, Ticket, User, Webshop } from '@stamhoofd/models';
3
- import { AuditLog as AuditLogStruct, Document as DocumentStruct, Event as EventStruct, Group as GroupStruct, MemberPlatformMembership as MemberPlatformMembershipStruct, MemberWithRegistrationsBlob, MembersBlob, NamedObject, OrganizationRegistrationPeriod as OrganizationRegistrationPeriodStruct, Organization as OrganizationStruct, PaymentGeneral, PermissionLevel, Platform, PrivateOrder, PrivateWebshop, ReceivableBalanceObject, ReceivableBalanceObjectContact, ReceivableBalance as ReceivableBalanceStruct, ReceivableBalanceType, TicketPrivate, UserWithMembers, WebshopPreview, Webshop as WebshopStruct } from '@stamhoofd/structures';
3
+ import { AuditLogReplacement, AuditLogReplacementType, AuditLog as AuditLogStruct, Document as DocumentStruct, Event as EventStruct, Group as GroupStruct, MemberPlatformMembership as MemberPlatformMembershipStruct, MemberWithRegistrationsBlob, MembersBlob, NamedObject, OrganizationRegistrationPeriod as OrganizationRegistrationPeriodStruct, Organization as OrganizationStruct, PaymentGeneral, PermissionLevel, Platform, PrivateOrder, PrivateWebshop, ReceivableBalanceObject, ReceivableBalanceObjectContact, ReceivableBalance as ReceivableBalanceStruct, ReceivableBalanceType, TicketPrivate, UserWithMembers, WebshopPreview, Webshop as WebshopStruct } from '@stamhoofd/structures';
4
4
 
5
5
  import { Formatter } from '@stamhoofd/utility';
6
6
  import { Context } from './Context';
@@ -645,6 +645,8 @@ export class AuthenticatedStructures {
645
645
 
646
646
  const userIds = Formatter.uniqueArray(logs.map(l => l.userId).filter(id => id !== null));
647
647
  const users = await User.getByIDs(...userIds);
648
+ const organizationsIds = Formatter.uniqueArray(logs.map(l => l.organizationId).filter(id => id !== null));
649
+ const organizations = await Organization.getByIDs(...organizationsIds);
648
650
 
649
651
  for (const log of logs) {
650
652
  const user = log.userId ? (users.find(u => u.id === log.userId) ?? null) : null;
@@ -673,9 +675,22 @@ export class AuthenticatedStructures {
673
675
  }
674
676
  }
675
677
 
678
+ let replacements = log.replacements;
679
+
680
+ if (log.organizationId && log.organizationId !== Context.organization?.id) {
681
+ replacements = new Map(log.replacements);
682
+ const org = organizations.find(o => o.id === log.organizationId);
683
+ replacements.set('org', AuditLogReplacement.create({
684
+ id: log.organizationId,
685
+ value: org?.name ?? 'verwijderde vereniging',
686
+ type: AuditLogReplacementType.Organization,
687
+ }));
688
+ }
689
+
676
690
  structs.push(
677
691
  AuditLogStruct.create({
678
692
  ...log,
693
+ replacements,
679
694
  user: userStruct,
680
695
  }),
681
696
  );
@@ -46,9 +46,15 @@ export class ContextInstance {
46
46
 
47
47
  static asyncLocalStorage = new AsyncLocalStorage<ContextInstance>();
48
48
 
49
- static get current(): ContextInstance {
49
+ static get optional(): ContextInstance | null {
50
50
  const c = this.asyncLocalStorage.getStore();
51
51
 
52
+ return c ?? null;
53
+ }
54
+
55
+ static get current(): ContextInstance {
56
+ const c = this.optional;
57
+
52
58
  if (!c) {
53
59
  throw new SimpleError({
54
60
  code: 'no_context',
@@ -220,7 +226,7 @@ export class ContextInstance {
220
226
  export const Context = new Proxy(ContextInstance, {
221
227
  get(target, prop, receiver) {
222
228
  const c = target.current[prop];
223
- if (c && typeof c == 'function') {
229
+ if (c && typeof c === 'function') {
224
230
  return c.bind(target.current);
225
231
  }
226
232
  return c;