diff --git a/backend/src/graphql/resolver/AdminResolver.test.ts b/backend/src/graphql/resolver/AdminResolver.test.ts index f0ce064b4..9de1a7116 100644 --- a/backend/src/graphql/resolver/AdminResolver.test.ts +++ b/backend/src/graphql/resolver/AdminResolver.test.ts @@ -1117,7 +1117,9 @@ describe('AdminResolver', () => { }), ).resolves.toEqual( expect.objectContaining({ - errors: [new GraphQLError('Could not find user with email: bob@baumeister.de')], + errors: [ + new GraphQLError('Could not find UserContact with email: bob@baumeister.de'), + ], }), ) }) diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index 7fde128c9..34819ae73 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -75,24 +75,24 @@ export class AdminResolver { { searchText, currentPage = 1, pageSize = 25, filters }: SearchUsersArgs, ): Promise { const userRepository = getCustomRepository(UserRepository) - + /* const filterCriteria: ObjectLiteral[] = [] if (filters) { if (filters.byActivated !== null) { - filterCriteria.push({ emailChecked: filters.byActivated }) + filterCriteria.push({ 'emailContact.emailChecked': filters.byActivated }) } if (filters.byDeleted !== null) { filterCriteria.push({ deletedAt: filters.byDeleted ? Not(IsNull()) : IsNull() }) } } - + */ const userFields = [ 'id', 'firstName', 'lastName', - 'email', - 'emailChecked', + 'emailId', + 'emailContact', 'deletedAt', 'isAdmin', ] @@ -101,7 +101,7 @@ export class AdminResolver { return 'user.' + fieldName }), searchText, - filterCriteria, + filters, currentPage, pageSize, ) @@ -249,7 +249,11 @@ export class AdminResolver { logger.info( `adminCreateContribution(email=${email}, amount=${amount}, memo=${memo}, creationDate=${creationDate})`, ) - const emailContact = await UserContact.findOne({ email }, { withDeleted: true }) + const emailContact = await UserContact.findOne({ + where: { email }, + withDeleted: true, + relations: ['user'], + }) if (!emailContact) { logger.error(`Could not find user with email: ${email}`) throw new Error(`Could not find user with email: ${email}`) @@ -258,6 +262,10 @@ export class AdminResolver { logger.error('This emailContact was deleted. Cannot create a contribution.') throw new Error('This emailContact was deleted. Cannot create a contribution.') } + if (emailContact.user.deletedAt) { + logger.error('This user was deleted. Cannot create a contribution.') + throw new Error('This user was deleted. Cannot create a contribution.') + } if (!emailContact.emailChecked) { logger.error('Contribution could not be saved, Email is not activated') throw new Error('Contribution could not be saved, Email is not activated') @@ -317,12 +325,16 @@ export class AdminResolver { @Args() { id, email, amount, memo, creationDate }: AdminUpdateContributionArgs, @Ctx() context: Context, ): Promise { - const emailContact = await UserContact.findOne({ email }, { withDeleted: true }) + const emailContact = await UserContact.findOne({ + where: { email }, + withDeleted: true, + relations: ['user'], + }) if (!emailContact) { logger.error(`Could not find UserContact with email: ${email}`) throw new Error(`Could not find UserContact with email: ${email}`) } - const user = await dbUser.findOne({ id: emailContact.userId }, { withDeleted: true }) + const user = emailContact.user if (!user) { logger.error(`Could not find User to emailContact: ${email}`) throw new Error(`Could not find User to emailContact: ${email}`) @@ -388,7 +400,11 @@ export class AdminResolver { const userIds = contributions.map((p) => p.userId) const userCreations = await getUserCreations(userIds) - const users = await dbUser.find({ where: { id: In(userIds) }, withDeleted: true }) + const users = await dbUser.find({ + where: { id: In(userIds) }, + withDeleted: true, + relations: ['emailContact'], + }) return contributions.map((contribution) => { const user = users.find((u) => u.id === contribution.userId) diff --git a/backend/src/seeds/factory/creation.ts b/backend/src/seeds/factory/creation.ts index 05be6d28e..7f19e2828 100644 --- a/backend/src/seeds/factory/creation.ts +++ b/backend/src/seeds/factory/creation.ts @@ -24,15 +24,16 @@ export const creationFactory = async ( logger.trace('creationFactory...') await query({ query: login, variables: { email: 'peter@lustig.de', password: 'Aa12345_' } }) logger.trace('creationFactory... after login') - // TODO it would be nice to have this mutation return the id await mutate({ mutation: adminCreateContribution, variables: { ...creation } }) logger.trace('creationFactory... after adminCreateContribution') - const userContact = await UserContact.findOneOrFail({ where: { email: creation.email } }) + const userContact = await UserContact.findOneOrFail({ + where: { email: creation.email }, + relations: ['user'], + }) logger.trace('creationFactory... after UserContact.findOneOrFail userContact=', userContact) - const user = await User.findOneOrFail({ where: { id: userContact.userId } }) - logger.trace('creationFactory... after User.findOneOrFail user=', user) + const user = userContact.user const pendingCreation = await Contribution.findOneOrFail({ where: { userId: user.id, amount: creation.amount }, @@ -42,12 +43,10 @@ export const creationFactory = async ( 'creationFactory... after Contribution.findOneOrFail pendingCreation=', pendingCreation, ) - if (creation.confirmed) { logger.trace('creationFactory... creation.confirmed=', creation.confirmed) await mutate({ mutation: confirmContribution, variables: { id: pendingCreation.id } }) logger.trace('creationFactory... after confirmContribution') - const confirmedCreation = await Contribution.findOneOrFail({ id: pendingCreation.id }) logger.trace( 'creationFactory... after Contribution.findOneOrFail confirmedCreation=', @@ -61,6 +60,7 @@ export const creationFactory = async ( order: { balanceDate: 'DESC' }, }) logger.trace('creationFactory... after Transaction.findOneOrFail transaction=', transaction) + if (transaction.decay.equals(0) && transaction.creationDate) { confirmedCreation.contributionDate = new Date( nMonthsBefore(transaction.creationDate, creation.moveCreationDate), diff --git a/backend/src/typeorm/repository/User.ts b/backend/src/typeorm/repository/User.ts index b347fae40..8b3e29859 100644 --- a/backend/src/typeorm/repository/User.ts +++ b/backend/src/typeorm/repository/User.ts @@ -1,4 +1,5 @@ -import { Brackets, EntityRepository, ObjectLiteral, Repository } from '@dbTools/typeorm' +import SearchUsersFilters from '@/graphql/arg/SearchUsersFilters' +import { Brackets, EntityRepository, IsNull, Not, Repository } from '@dbTools/typeorm' import { User as DbUser } from '@entity/User' @EntityRepository(DbUser) @@ -21,17 +22,18 @@ export class UserRepository extends Repository { async findBySearchCriteriaPagedFiltered( select: string[], searchCriteria: string, - filterCriteria: ObjectLiteral[], + filters: SearchUsersFilters, currentPage: number, pageSize: number, ): Promise<[DbUser[], number]> { const query = this.createQueryBuilder('user') .select(select) + .leftJoinAndSelect('user.emailContact', 'emailContact') .withDeleted() .where( new Brackets((qb) => { qb.where( - 'user.firstName like :name or user.lastName like :lastName or user.email like :email', + 'user.firstName like :name or user.lastName like :lastName or emailContact.email like :email', { name: `%${searchCriteria}%`, lastName: `%${searchCriteria}%`, @@ -40,9 +42,23 @@ export class UserRepository extends Repository { ) }), ) + /* filterCriteria.forEach((filter) => { query.andWhere(filter) }) + */ + if (filters) { + if (filters.byActivated !== null) { + query.andWhere('emailContact.emailChecked = :value', { value: filters.byActivated }) + // filterCriteria.push({ 'emailContact.emailChecked': filters.byActivated }) + } + + if (filters.byDeleted !== null) { + // filterCriteria.push({ deletedAt: filters.byDeleted ? Not(IsNull()) : IsNull() }) + query.andWhere({ deletedAt: filters.byDeleted ? Not(IsNull()) : IsNull() }) + } + } + return query .take(pageSize) .skip((currentPage - 1) * pageSize)