@stamhoofd/backend 2.129.1 → 2.129.2

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stamhoofd/backend",
3
- "version": "2.129.1",
3
+ "version": "2.129.2",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "exports": {
@@ -57,20 +57,20 @@
57
57
  "@simonbackx/simple-endpoints": "1.21.1",
58
58
  "@simonbackx/simple-errors": "1.5.0",
59
59
  "@simonbackx/simple-logging": "1.0.1",
60
- "@stamhoofd/backend-env": "2.129.1",
61
- "@stamhoofd/backend-i18n": "2.129.1",
62
- "@stamhoofd/backend-middleware": "2.129.1",
63
- "@stamhoofd/crons": "2.129.1",
64
- "@stamhoofd/email": "2.129.1",
65
- "@stamhoofd/excel-writer": "2.129.1",
66
- "@stamhoofd/logging": "2.129.1",
67
- "@stamhoofd/models": "2.129.1",
68
- "@stamhoofd/object-differ": "2.129.1",
69
- "@stamhoofd/queues": "2.129.1",
70
- "@stamhoofd/sql": "2.129.1",
71
- "@stamhoofd/structures": "2.129.1",
72
- "@stamhoofd/types": "2.129.1",
73
- "@stamhoofd/utility": "2.129.1",
60
+ "@stamhoofd/backend-env": "2.129.2",
61
+ "@stamhoofd/backend-i18n": "2.129.2",
62
+ "@stamhoofd/backend-middleware": "2.129.2",
63
+ "@stamhoofd/crons": "2.129.2",
64
+ "@stamhoofd/email": "2.129.2",
65
+ "@stamhoofd/excel-writer": "2.129.2",
66
+ "@stamhoofd/logging": "2.129.2",
67
+ "@stamhoofd/models": "2.129.2",
68
+ "@stamhoofd/object-differ": "2.129.2",
69
+ "@stamhoofd/queues": "2.129.2",
70
+ "@stamhoofd/sql": "2.129.2",
71
+ "@stamhoofd/structures": "2.129.2",
72
+ "@stamhoofd/types": "2.129.2",
73
+ "@stamhoofd/utility": "2.129.2",
74
74
  "archiver": "7.0.1",
75
75
  "axios": "1.16.0",
76
76
  "base-x": "3.0.11",
@@ -91,7 +91,7 @@
91
91
  "stripe": "16.12.0"
92
92
  },
93
93
  "devDependencies": {
94
- "@stamhoofd/test-utils": "2.129.1",
94
+ "@stamhoofd/test-utils": "2.129.2",
95
95
  "@types/cookie": "0.6.0",
96
96
  "@types/luxon": "3.7.1",
97
97
  "@types/mailparser": "3.4.6",
@@ -107,5 +107,5 @@
107
107
  "publishConfig": {
108
108
  "access": "public"
109
109
  },
110
- "gitHead": "fed4dfbbbb768c9d57c476b93a3a42dfac6dd69b"
110
+ "gitHead": "099a48481fdbd3d661793675ab9e2c24a594b130"
111
111
  }
@@ -44,6 +44,6 @@ async function createStripeInvoices() {
44
44
  const invoicer = new StripeInvoicer({
45
45
  secretKey: STAMHOOFD.STRIPE_SECRET_KEY,
46
46
  });
47
- await invoicer.generateAllInvoices(membershipOrganization, { forceLast: Formatter.dateIso(today) === '2026-07-03' });
47
+ await invoicer.generateAllInvoices(membershipOrganization, { forceLast: Formatter.dateIso(today) === '2026-07-04' });
48
48
  lastStripeInvoice = new Date();
49
49
  }
@@ -7,7 +7,7 @@ import { SimpleError } from '@simonbackx/simple-errors';
7
7
  import type { MemberWithUsersRegistrationsAndGroups, Registration } from '@stamhoofd/models';
8
8
  import { AuditLog, BalanceItem, Document, Group, Member, MemberFactory, MemberPlatformMembership, MemberResponsibilityRecord, Organization, Platform, RateLimiter, RegistrationPeriod, User } from '@stamhoofd/models';
9
9
  import type { MemberResponsibility, MembersBlob } from '@stamhoofd/structures';
10
- import { AuditLogReplacement, AuditLogReplacementType, AuditLogSource, AuditLogType, EmergencyContact, GroupType, MemberDetails, MemberWithRegistrationsBlob, Parent, PermissionLevel, PlatformMembershipTypeBehaviour, SetupStepType } from '@stamhoofd/structures';
10
+ import { AuditLogReplacement, AuditLogReplacementType, AuditLogSource, AuditLogType, BooleanStatus, EmergencyContact, GroupType, MemberDetails, MemberWithRegistrationsBlob, Parent, PermissionLevel, PlatformMembershipTypeBehaviour, SetupStepType } from '@stamhoofd/structures';
11
11
  import { Formatter } from '@stamhoofd/utility';
12
12
 
13
13
  import { Email } from '@stamhoofd/email';
@@ -1110,6 +1110,15 @@ export class PatchOrganizationMembersEndpoint extends Endpoint<Params, Query, Bo
1110
1110
  // Merge data
1111
1111
  // NOTE: We use mergeTwoMembers instead of mergeMultipleMembers, because we should never safe 'member' , because that one does not exist in the database
1112
1112
  await mergeTwoMembers(duplicate, member);
1113
+
1114
+ // Make sure the user keeps access to this member
1115
+ // const user = Context.user;
1116
+
1117
+ // const { userEmails, parentEmails } = MemberUserSyncer.getMemberAccessEmails(duplicate.details);
1118
+ // if (user && !duplicate.details.calculatedParentsHaveAccess && parentEmails.includes(user.email.toLocaleLowerCase()) && !userEmails.includes(user.email.toLocaleLowerCase())) {
1119
+ // console.log('Setting parents have access to true to maintain access for parent that used security code');
1120
+ // duplicate.details.parentsHaveAccess = BooleanStatus.create({ value: true });
1121
+ // }
1113
1122
  return duplicate;
1114
1123
  }
1115
1124
  }
@@ -778,5 +778,139 @@ describe('member merge', () => {
778
778
  expect(m1.details.email).toBe('example@example.com');
779
779
  expect(m1.details.alternativeEmails).toEqual([]);
780
780
  });
781
+
782
+ describe('Parents have access get merged', () => {
783
+ test('False other', () => {
784
+ const m1 = new Member();
785
+ const m2 = new Member();
786
+
787
+ const base = MemberDetails.create({
788
+ email: 'example@example.com',
789
+ firstName: 'John',
790
+ lastName: 'Doe',
791
+ });
792
+
793
+ const other = MemberDetails.create({
794
+ email: 'example@example.com',
795
+ firstName: 'John',
796
+ lastName: 'Doe',
797
+ parentsHaveAccess: BooleanStatus.create({ value: false }),
798
+ });
799
+
800
+ m1.details = base.clone();
801
+ m2.details = other.clone();
802
+
803
+ mergeMemberDetails(m1, m2);
804
+
805
+ expect(m1.details.email).toBe('example@example.com');
806
+ expect(m1.details.parentsHaveAccess?.value).toEqual(false);
807
+ });
808
+ test('True other', () => {
809
+ const m1 = new Member();
810
+ const m2 = new Member();
811
+
812
+ const base = MemberDetails.create({
813
+ email: 'example@example.com',
814
+ firstName: 'John',
815
+ lastName: 'Doe',
816
+ });
817
+
818
+ const other = MemberDetails.create({
819
+ email: 'example@example.com',
820
+ firstName: 'John',
821
+ lastName: 'Doe',
822
+ parentsHaveAccess: BooleanStatus.create({ value: true }),
823
+ });
824
+
825
+ m1.details = base.clone();
826
+ m2.details = other.clone();
827
+
828
+ mergeMemberDetails(m1, m2);
829
+
830
+ expect(m1.details.email).toBe('example@example.com');
831
+ expect(m1.details.parentsHaveAccess?.value).toEqual(true);
832
+ });
833
+
834
+ test('Both different other prefer newest other', () => {
835
+ const m1 = new Member();
836
+ const m2 = new Member();
837
+
838
+ const base = MemberDetails.create({
839
+ email: 'example@example.com',
840
+ firstName: 'John',
841
+ lastName: 'Doe',
842
+ parentsHaveAccess: BooleanStatus.create({ value: true, date: new Date(0) }),
843
+ });
844
+
845
+ const other = MemberDetails.create({
846
+ email: 'example@example.com',
847
+ firstName: 'John',
848
+ lastName: 'Doe',
849
+ parentsHaveAccess: BooleanStatus.create({ value: false, date: new Date(1) }),
850
+ });
851
+
852
+ m1.details = base.clone();
853
+ m2.details = other.clone();
854
+
855
+ mergeMemberDetails(m1, m2);
856
+
857
+ expect(m1.details.email).toBe('example@example.com');
858
+ expect(m1.details.parentsHaveAccess?.value).toEqual(false);
859
+ });
860
+
861
+ test('Both different other prefer newest base', () => {
862
+ const m1 = new Member();
863
+ const m2 = new Member();
864
+
865
+ const base = MemberDetails.create({
866
+ email: 'example@example.com',
867
+ firstName: 'John',
868
+ lastName: 'Doe',
869
+ parentsHaveAccess: BooleanStatus.create({ value: true, date: new Date(1) }),
870
+ });
871
+
872
+ const other = MemberDetails.create({
873
+ email: 'example@example.com',
874
+ firstName: 'John',
875
+ lastName: 'Doe',
876
+ parentsHaveAccess: BooleanStatus.create({ value: false, date: new Date(0) }),
877
+ });
878
+
879
+ m1.details = base.clone();
880
+ m2.details = other.clone();
881
+
882
+ mergeMemberDetails(m1, m2);
883
+
884
+ expect(m1.details.email).toBe('example@example.com');
885
+ expect(m1.details.parentsHaveAccess?.value).toEqual(true);
886
+ });
887
+
888
+ test('Both different other prefer newest base 2', () => {
889
+ const m1 = new Member();
890
+ const m2 = new Member();
891
+
892
+ const base = MemberDetails.create({
893
+ email: 'example@example.com',
894
+ firstName: 'John',
895
+ lastName: 'Doe',
896
+ parentsHaveAccess: BooleanStatus.create({ value: false, date: new Date(1) }),
897
+ });
898
+
899
+ const other = MemberDetails.create({
900
+ email: 'example@example.com',
901
+ firstName: 'John',
902
+ lastName: 'Doe',
903
+ parentsHaveAccess: BooleanStatus.create({ value: true, date: new Date(0) }),
904
+ });
905
+
906
+ m1.details = base.clone();
907
+ m2.details = other.clone();
908
+
909
+ mergeMemberDetails(m1, m2);
910
+
911
+ expect(m1.details.email).toBe('example@example.com');
912
+ expect(m1.details.parentsHaveAccess?.value).toEqual(false);
913
+ });
914
+ });
781
915
  });
782
916
  });
@@ -306,6 +306,12 @@ export function mergeMemberDetails(base: Member, other: Member): void {
306
306
  'dataPermissions',
307
307
  );
308
308
 
309
+ mergeBooleanStatusIfBaseNotSet(
310
+ baseDetails,
311
+ otherDetails,
312
+ 'parentsHaveAccess',
313
+ );
314
+
309
315
  // address
310
316
  mergeAddress(baseDetails, otherDetails, baseDetails);
311
317
 
@@ -693,7 +693,7 @@ describe('Helpers.MemberUserSyncer', () => {
693
693
  ]);
694
694
  });
695
695
 
696
- test('Parents are removed if member turns 18', async () => {
696
+ test.skip('Parents are removed if member turns 18', async () => {
697
697
  const member = await new MemberFactory({
698
698
  details: MemberDetails.create({
699
699
  firstName: 'John',
@@ -132,7 +132,8 @@ export class MemberUserSyncerStatic {
132
132
  // This makes sure we don't inherit permissions and aren't counted as 'being' the member
133
133
  await this.linkUser(user.email, member, true);
134
134
  }
135
- } else if (!member.details.calculatedParentsHaveAccess && parentEmails.includes(user.email.toLocaleLowerCase()) && !userEmails.includes(user.email.toLocaleLowerCase())) {
135
+ } else if (member.details.parentsHaveAccess?.value === false && parentEmails.includes(user.email.toLocaleLowerCase()) && !userEmails.includes(user.email.toLocaleLowerCase())) {
136
+ // Only remove if manually set to false for now
136
137
  await this.unlinkUser(user, member);
137
138
  }
138
139
  }