@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 +17 -17
- package/src/crons/stripe-invoices.ts +1 -1
- package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.ts +10 -1
- package/src/helpers/MemberMerger.test.ts +134 -0
- package/src/helpers/MemberMerger.ts +6 -0
- package/src/helpers/MemberUserSyncer.test.ts +1 -1
- package/src/helpers/MemberUserSyncer.ts +2 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stamhoofd/backend",
|
|
3
|
-
"version": "2.129.
|
|
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.
|
|
61
|
-
"@stamhoofd/backend-i18n": "2.129.
|
|
62
|
-
"@stamhoofd/backend-middleware": "2.129.
|
|
63
|
-
"@stamhoofd/crons": "2.129.
|
|
64
|
-
"@stamhoofd/email": "2.129.
|
|
65
|
-
"@stamhoofd/excel-writer": "2.129.
|
|
66
|
-
"@stamhoofd/logging": "2.129.
|
|
67
|
-
"@stamhoofd/models": "2.129.
|
|
68
|
-
"@stamhoofd/object-differ": "2.129.
|
|
69
|
-
"@stamhoofd/queues": "2.129.
|
|
70
|
-
"@stamhoofd/sql": "2.129.
|
|
71
|
-
"@stamhoofd/structures": "2.129.
|
|
72
|
-
"@stamhoofd/types": "2.129.
|
|
73
|
-
"@stamhoofd/utility": "2.129.
|
|
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.
|
|
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": "
|
|
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-
|
|
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 (
|
|
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
|
}
|