diff --git a/src/modules/person/api/personenuebersicht/dbiam-personenuebersicht.controller.ts b/src/modules/person/api/personenuebersicht/dbiam-personenuebersicht.controller.ts index 4ba7b94c1..725f16009 100644 --- a/src/modules/person/api/personenuebersicht/dbiam-personenuebersicht.controller.ts +++ b/src/modules/person/api/personenuebersicht/dbiam-personenuebersicht.controller.ts @@ -92,23 +92,30 @@ export class DBiamPersonenuebersichtController { this.config, ); - persons.forEach((person: Person) => { - const personenKontexte: Personenkontext[] = allPersonenKontexte.get(person.id) ?? []; - const personenUebersichtenResult: [DBiamPersonenzuordnungResponse[], Date?] | EntityNotFoundError = - dbiamPersonenUebersicht.createZuordnungenForKontexte(personenKontexte, allRollen, allOrganisations); - if (personenUebersichtenResult instanceof EntityNotFoundError) { - throw SchulConnexErrorMapper.mapSchulConnexErrorToHttpException( - SchulConnexErrorMapper.mapDomainErrorToSchulConnexError(personenUebersichtenResult), + await Promise.all( + persons.map(async (person: Person) => { + const personenKontexte: Personenkontext[] = allPersonenKontexte.get(person.id) ?? []; + const personenUebersichtenResult: [DBiamPersonenzuordnungResponse[], Date?] | EntityNotFoundError = + await dbiamPersonenUebersicht.createZuordnungenForKontexte( + personenKontexte, + allRollen, + allOrganisations, + ); + + if (personenUebersichtenResult instanceof EntityNotFoundError) { + throw SchulConnexErrorMapper.mapSchulConnexErrorToHttpException( + SchulConnexErrorMapper.mapDomainErrorToSchulConnexError(personenUebersichtenResult), + ); + } + items.push( + new DBiamPersonenuebersichtResponse( + person, + personenUebersichtenResult[0], + personenUebersichtenResult[1], + ), ); - } - items.push( - new DBiamPersonenuebersichtResponse( - person, - personenUebersichtenResult[0], - personenUebersichtenResult[1], - ), - ); - }); + }), + ); } return { diff --git a/src/modules/person/api/personenuebersicht/dbiam-personenzuordnung.response.ts b/src/modules/person/api/personenuebersicht/dbiam-personenzuordnung.response.ts index 55f061ab5..49c018135 100644 --- a/src/modules/person/api/personenuebersicht/dbiam-personenzuordnung.response.ts +++ b/src/modules/person/api/personenuebersicht/dbiam-personenzuordnung.response.ts @@ -40,11 +40,15 @@ export class DBiamPersonenzuordnungResponse { @ApiProperty({ enum: RollenMerkmal, enumName: RollenMerkmalTypName, nullable: true }) public readonly merkmale?: RollenMerkmal[]; + @ApiProperty({ type: [String] }) + public readonly admins?: string[]; + public constructor( personenkontext: Personenkontext, organisation: OrganisationDo, rolle: Rolle, editable: boolean, + admins: string[] | undefined, ) { //use Organisation Aggregate as soon as there is one this.sskId = personenkontext.organisationId; @@ -58,5 +62,6 @@ export class DBiamPersonenzuordnungResponse { this.editable = editable; this.merkmale = rolle.merkmale; this.befristung = personenkontext.befristung; + this.admins = admins; } } diff --git a/src/modules/person/api/personenuebersicht/dbiam-personuebersicht.controller.mocked.spec.ts b/src/modules/person/api/personenuebersicht/dbiam-personuebersicht.controller.mocked.spec.ts index 4c843ff7e..f8797c32d 100644 --- a/src/modules/person/api/personenuebersicht/dbiam-personuebersicht.controller.mocked.spec.ts +++ b/src/modules/person/api/personenuebersicht/dbiam-personuebersicht.controller.mocked.spec.ts @@ -257,7 +257,7 @@ describe('Personenuebersicht API Mocked', () => { ); jest.spyOn(DbiamPersonenuebersicht.prototype, 'createZuordnungenForKontexte').mockImplementation(() => { - return new EntityNotFoundError(); + return Promise.resolve(new EntityNotFoundError('Some message here')); }); personRepositoryMock.findByIds.mockResolvedValueOnce([person]); diff --git a/src/modules/person/domain/dbiam-personenuebersicht.spec.ts b/src/modules/person/domain/dbiam-personenuebersicht.spec.ts index 5936caea9..9c15c183a 100644 --- a/src/modules/person/domain/dbiam-personenuebersicht.spec.ts +++ b/src/modules/person/domain/dbiam-personenuebersicht.spec.ts @@ -115,11 +115,12 @@ describe('DbiamPersonenUebersicht', () => { }); describe('when most recent PK is last or later in list', () => { - it('should set lastModifiedZuordnungen to value of latest PK updatedAt timestamp', () => { + it('should set lastModifiedZuordnungen to value of latest PK updatedAt timestamp', async () => { personenkontexte = [personenkontextPast, personenkontextRecent]; const res: [DBiamPersonenzuordnungResponse[], Date?] | EntityNotFoundError = - sut.createZuordnungenForKontexte(personenkontexte, rolleMap, orgaMap, undefined); + await sut.createZuordnungenForKontexte(personenkontexte, rolleMap, orgaMap, undefined); + if (res instanceof EntityNotFoundError) throw res; const [responses, lastModified]: [DBiamPersonenzuordnungResponse[], Date?] = res; @@ -130,11 +131,11 @@ describe('DbiamPersonenUebersicht', () => { }); describe('when most recentPK is first in list', () => { - it('should set lastModifiedZuordnungen to value of latest PK updatedAt timestamp', () => { + it('should set lastModifiedZuordnungen to value of latest PK updatedAt timestamp', async () => { personenkontexte = [personenkontextRecent, personenkontextPast]; const res: [DBiamPersonenzuordnungResponse[], Date?] | EntityNotFoundError = - sut.createZuordnungenForKontexte(personenkontexte, rolleMap, orgaMap, undefined); + await sut.createZuordnungenForKontexte(personenkontexte, rolleMap, orgaMap, undefined); if (res instanceof EntityNotFoundError) throw res; const [responses, lastModified]: [DBiamPersonenzuordnungResponse[], Date?] = res; diff --git a/src/modules/person/domain/dbiam-personenuebersicht.ts b/src/modules/person/domain/dbiam-personenuebersicht.ts index a39bd62a4..9656766fb 100644 --- a/src/modules/person/domain/dbiam-personenuebersicht.ts +++ b/src/modules/person/domain/dbiam-personenuebersicht.ts @@ -4,7 +4,7 @@ import { DBiamPersonenkontextRepo } from '../../personenkontext/persistence/dbia import { Person } from './person.js'; import { EntityNotFoundError } from '../../../shared/error/index.js'; import { OrganisationID, PersonID, RolleID } from '../../../shared/types/index.js'; -import { RollenSystemRecht } from '../../rolle/domain/rolle.enums.js'; +import { RollenArt, RollenSystemRecht } from '../../rolle/domain/rolle.enums.js'; import { Personenkontext } from '../../personenkontext/domain/personenkontext.js'; import { Rolle } from '../../rolle/domain/rolle.js'; import { DBiamPersonenzuordnungResponse } from '../api/personenuebersicht/dbiam-personenzuordnung.response.js'; @@ -71,7 +71,7 @@ export class DbiamPersonenuebersicht { ); //use Organisation Aggregate as soon as there is one const result: [DBiamPersonenzuordnungResponse[], Date?] | EntityNotFoundError = - this.createZuordnungenForKontexte( + await this.createZuordnungenForKontexte( personenKontexte, rollenForKontexte, organisationsForKontexte, @@ -84,43 +84,48 @@ export class DbiamPersonenuebersicht { return new DBiamPersonenuebersichtResponse(person, result[0], result[1]); } - public createZuordnungenForKontexte( + public async createZuordnungenForKontexte( kontexte: Personenkontext[], rollen: Map>, organisations: Map>, organisationIds?: string[], - ): [DBiamPersonenzuordnungResponse[], Date?] | EntityNotFoundError { - const personenUebersichten: DBiamPersonenzuordnungResponse[] = []; + ): Promise<[DBiamPersonenzuordnungResponse[], Date?] | EntityNotFoundError> { + const personenUebersichtenPromises: Promise[] = []; let lastModifiedZuordnungen: Date | undefined = undefined; + for (const pk of kontexte) { const rolle: Rolle | undefined = rollen.get(pk.rolleId); const organisation: Organisation | undefined = organisations.get(pk.organisationId); + if (!rolle) { return new EntityNotFoundError('Rolle', pk.rolleId); } if (!organisation) { return new EntityNotFoundError('Organisation', pk.organisationId); } - // if permissions should not be checked or admin has PERSONEN_VERWALTEN at ROOT level - if (organisationIds === undefined) { - personenUebersichten.push(new DBiamPersonenzuordnungResponse(pk, organisation, rolle, true)); - } else { - // if permissions should be checked - if (organisationIds.some((orgaId: OrganisationID) => orgaId === organisation.id)) { - personenUebersichten.push(new DBiamPersonenzuordnungResponse(pk, organisation, rolle, true)); - } else { - personenUebersichten.push(new DBiamPersonenzuordnungResponse(pk, organisation, rolle, false)); - } - } - if (lastModifiedZuordnungen == undefined) { + const hasAccess: boolean = + organisationIds === undefined || organisationIds.some((orgaId: string) => orgaId === organisation.id); + + const zuordnungPromise: Promise = + (async (): Promise => { + let admins: string[] | undefined = undefined; + if (rolle.rollenart !== RollenArt.LEIT) { + admins = await this.personRepository.findOrganisationAdminsByOrganisationId(pk.organisationId); + } + + return new DBiamPersonenzuordnungResponse(pk, organisation, rolle, hasAccess, admins); + })(); + + personenUebersichtenPromises.push(zuordnungPromise); + + if (!lastModifiedZuordnungen || lastModifiedZuordnungen.getTime() < pk.updatedAt.getTime()) { lastModifiedZuordnungen = pk.updatedAt; - } else { - lastModifiedZuordnungen = - lastModifiedZuordnungen.getTime() < pk.updatedAt.getTime() ? pk.updatedAt : lastModifiedZuordnungen; } } + const personenUebersichten: DBiamPersonenzuordnungResponse[] = await Promise.all(personenUebersichtenPromises); + return [personenUebersichten, lastModifiedZuordnungen]; } } diff --git a/src/modules/person/persistence/person.repository.integration-spec.ts b/src/modules/person/persistence/person.repository.integration-spec.ts index ba6b2f8c6..b5a96439f 100644 --- a/src/modules/person/persistence/person.repository.integration-spec.ts +++ b/src/modules/person/persistence/person.repository.integration-spec.ts @@ -2330,5 +2330,33 @@ describe('PersonRepository Integration', () => { expect(personsWithOrgList).not.toContain(person4.id); }); }); + describe('findOrganisationAdminsByOrganisationId', () => { + it('should return a list of admins', async () => { + const personEntity1: PersonEntity = new PersonEntity(); + const person1: Person = DoFactory.createPerson(true); + await em.persistAndFlush(personEntity1.assign(mapAggregateToData(person1))); + person1.id = personEntity1.id; + + const rolle1: Rolle = DoFactory.createRolle(false, { + name: 'rolle1', + rollenart: RollenArt.LEIT, + merkmale: [RollenMerkmal.KOPERS_PFLICHT], + }); + const rolle1Result: Rolle | DomainError = await rolleRepo.save(rolle1); + if (rolle1Result instanceof DomainError) throw Error(); + + const personenKontext1: Personenkontext = DoFactory.createPersonenkontext(false, { + personId: person1.id, + rolleId: rolle1Result.id, + }); + await dbiamPersonenkontextRepoInternal.save(personenKontext1); + //get admins + const admins: string[] = await sut.findOrganisationAdminsByOrganisationId( + personenKontext1.organisationId, + ); + + expect(admins).toContain(person1.vorname + ' ' + person1.familienname); + }); + }); }); }); diff --git a/src/modules/person/persistence/person.repository.ts b/src/modules/person/persistence/person.repository.ts index 33769a99e..996cfcd6d 100644 --- a/src/modules/person/persistence/person.repository.ts +++ b/src/modules/person/persistence/person.repository.ts @@ -842,4 +842,19 @@ export class PersonRepository { const personEntities: PersonEntity[] = await this.em.find(PersonEntity, filters); return personEntities.map((person: PersonEntity) => person.id); } + + public async findOrganisationAdminsByOrganisationId(organisation_id: string): Promise { + const filters: QBFilterQuery = { + personenKontexte: { + $some: { + organisationId: organisation_id, + rolleId: { + rollenart: 'LEIT', + }, + }, + }, + }; + const admins: PersonEntity[] = await this.em.find(PersonEntity, filters); + return admins.map((admin: PersonEntity) => admin.vorname + ' ' + admin.familienname); + } }