diff --git a/src/modules/authentication/api/authentication.controller.integration-spec.ts b/src/modules/authentication/api/authentication.controller.integration-spec.ts index 24214d9dd..2e094e8e7 100644 --- a/src/modules/authentication/api/authentication.controller.integration-spec.ts +++ b/src/modules/authentication/api/authentication.controller.integration-spec.ts @@ -26,6 +26,8 @@ import { RolleRepo } from '../../rolle/repo/rolle.repo.js'; import { OrganisationRepository } from '../../organisation/persistence/organisation.repository.js'; import { KeycloakConfig } from '../../../shared/config/keycloak.config.js'; import { KeycloakUserService } from '../../keycloak-administration/index.js'; +import { TimeLimitOccasion } from '../../person/domain/time-limit-occasion.enums.js'; +import PersonTimeLimitService from '../../person/domain/person-time-limit-info.service.js'; describe('AuthenticationController', () => { let module: TestingModule; @@ -38,7 +40,7 @@ describe('AuthenticationController', () => { let rolleRepoMock: DeepMocked; const keycloakUserServiceMock: DeepMocked = createMock(); let keyCloakConfig: KeycloakConfig; - + const personTimeLimitServiceMock: DeepMocked = createMock(); beforeAll(async () => { module = await Test.createTestingModule({ imports: [ @@ -76,6 +78,10 @@ describe('AuthenticationController', () => { provide: KeycloakUserService, useValue: keycloakUserServiceMock, }, + { + provide: PersonTimeLimitService, + useValue: personTimeLimitServiceMock, + }, ], }).compile(); @@ -275,6 +281,13 @@ describe('AuthenticationController', () => { value: person.updatedAt, }); + personTimeLimitServiceMock.getPersonTimeLimitInfo.mockResolvedValueOnce([ + { + occasion: TimeLimitOccasion.KOPERS, + deadline: faker.date.future(), + }, + ]); + const requestMock: Request = setupRequest(); const result: UserinfoResponse = await authController.info(permissions, requestMock); diff --git a/src/modules/authentication/api/authentication.controller.ts b/src/modules/authentication/api/authentication.controller.ts index bba9d1122..78a6da663 100644 --- a/src/modules/authentication/api/authentication.controller.ts +++ b/src/modules/authentication/api/authentication.controller.ts @@ -31,6 +31,9 @@ import { AuthenticationExceptionFilter } from './authentication-exception-filter import { KeycloakUserService } from '../../keycloak-administration/index.js'; import { DomainError } from '../../../shared/error/domain.error.js'; import { getLowestStepUpLevel } from '../passport/oidc.strategy.js'; +import { PersonTimeLimitInfo } from '../../person/domain/person-time-limit-info.js'; +import { PersonTimeLimitInfoResponse } from './person-time-limit-info.reponse.js'; +import PersonTimeLimitService from '../../person/domain/person-time-limit-info.service.js'; @UseFilters(new AuthenticationExceptionFilter()) @ApiTags('auth') @@ -47,6 +50,7 @@ export class AuthenticationController { @Inject(OIDC_CLIENT) private client: Client, private readonly logger: ClassLogger, private keycloakUserService: KeycloakUserService, + private readonly personTimeLimitService: PersonTimeLimitService, ) { const frontendConfig: FrontendConfig = configService.getOrThrow('FRONTEND'); const keycloakConfig: KeycloakConfig = configService.getOrThrow('KEYCLOAK'); @@ -132,10 +136,18 @@ export class AuthenticationController { if (lastPasswordChange.ok) userinfoExtension.password_updated_at = lastPasswordChange.value; } + const timeLimitInfos: PersonTimeLimitInfo[] = await this.personTimeLimitService.getPersonTimeLimitInfo( + permissions.personFields.id, + ); + const timeLimitInfosResponse: PersonTimeLimitInfoResponse[] = timeLimitInfos.map( + (info: PersonTimeLimitInfo) => new PersonTimeLimitInfoResponse(info.occasion, info.deadline), + ); + return new UserinfoResponse( permissions, rolleFieldsResponse, req.passportUser?.stepUpLevel ?? getLowestStepUpLevel(), + timeLimitInfosResponse, userinfoExtension, ); } diff --git a/src/modules/authentication/api/person-time-limit-info.reponse.ts b/src/modules/authentication/api/person-time-limit-info.reponse.ts new file mode 100644 index 000000000..a3632ef32 --- /dev/null +++ b/src/modules/authentication/api/person-time-limit-info.reponse.ts @@ -0,0 +1,15 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { TimeLimitOccasion } from '../../person/domain/time-limit-occasion.enums.js'; + +export class PersonTimeLimitInfoResponse { + @ApiProperty() + public occasion: TimeLimitOccasion; + + @ApiProperty() + public deadline: string; + + public constructor(occasion: TimeLimitOccasion, deadline: Date) { + this.occasion = occasion; + this.deadline = deadline.toISOString(); + } +} diff --git a/src/modules/authentication/api/userinfo.response.spec.ts b/src/modules/authentication/api/userinfo.response.spec.ts index 0c6119e73..ff5aedc2a 100644 --- a/src/modules/authentication/api/userinfo.response.spec.ts +++ b/src/modules/authentication/api/userinfo.response.spec.ts @@ -9,6 +9,8 @@ import { RolleRepo } from '../../rolle/repo/rolle.repo.js'; import { PersonenkontextRolleFieldsResponse } from './personen-kontext-rolle-fields.response.js'; import { createMock } from '@golevelup/ts-jest'; import { StepUpLevel } from '../passport/oidc.strategy.js'; +import { PersonTimeLimitInfoResponse } from './person-time-limit-info.reponse.js'; +import { TimeLimitOccasion } from '../../person/domain/time-limit-occasion.enums.js'; describe('UserinfoResponse', () => { const permissions: PersonPermissions = new PersonPermissions( @@ -22,8 +24,15 @@ describe('UserinfoResponse', () => { rolle: { systemrechte: [faker.string.alpha()], serviceProviderIds: [faker.string.uuid()] }, }; + const personTimeLimtit: PersonTimeLimitInfoResponse = { + occasion: TimeLimitOccasion.KOPERS, + deadline: faker.date.future().toISOString(), + }; + it('constructs the object without optional extension', () => { - const userinfoResponse: UserinfoResponse = new UserinfoResponse(permissions, [pk], StepUpLevel.SILVER); + const userinfoResponse: UserinfoResponse = new UserinfoResponse(permissions, [pk], StepUpLevel.SILVER, [ + personTimeLimtit, + ]); expect(userinfoResponse).toBeDefined(); expect(userinfoResponse.password_updated_at).toBeUndefined(); }); @@ -34,6 +43,7 @@ describe('UserinfoResponse', () => { permissions, [pk], StepUpLevel.SILVER, + [personTimeLimtit], extension, ); expect(userinfoResponse).toBeDefined(); diff --git a/src/modules/authentication/api/userinfo.response.ts b/src/modules/authentication/api/userinfo.response.ts index 260798a24..d2d856047 100644 --- a/src/modules/authentication/api/userinfo.response.ts +++ b/src/modules/authentication/api/userinfo.response.ts @@ -2,6 +2,7 @@ import { ApiProperty } from '@nestjs/swagger'; import { PersonPermissions } from '../domain/person-permissions.js'; import { PersonenkontextRolleFieldsResponse } from './personen-kontext-rolle-fields.response.js'; import { StepUpLevel } from '../passport/oidc.strategy.js'; +import { PersonTimeLimitInfoResponse } from './person-time-limit-info.reponse.js'; export type UserinfoExtension = { password_updated_at?: Date; @@ -74,10 +75,14 @@ export class UserinfoResponse { @ApiProperty({ nullable: false }) public acr: StepUpLevel; + @ApiProperty({ type: PersonTimeLimitInfoResponse, isArray: true }) + public timeLimits: PersonTimeLimitInfoResponse[]; + public constructor( info: PersonPermissions, personenkontexte: PersonenkontextRolleFieldsResponse[], acr: StepUpLevel, + timeLimits: PersonTimeLimitInfoResponse[], extension?: UserinfoExtension, ) { this.sub = info.personFields.keycloakUserId!; @@ -93,5 +98,6 @@ export class UserinfoResponse { this.personenkontexte = personenkontexte; this.password_updated_at = extension?.password_updated_at?.toISOString(); this.acr = acr; + this.timeLimits = timeLimits; } } diff --git a/src/modules/person/api/person.controller.ts b/src/modules/person/api/person.controller.ts index c0c565335..69cbcae05 100644 --- a/src/modules/person/api/person.controller.ts +++ b/src/modules/person/api/person.controller.ts @@ -207,6 +207,7 @@ export class PersonController { const personEmailResponse: Option = await this.emailRepo.getEmailAddressAndStatusForPerson( personResult.value, ); + const response: PersonendatensatzResponse = new PersonendatensatzResponse( personResult.value, false, diff --git a/src/modules/person/domain/person-time-limit-info.service.spec.ts b/src/modules/person/domain/person-time-limit-info.service.spec.ts new file mode 100644 index 000000000..d415df71b --- /dev/null +++ b/src/modules/person/domain/person-time-limit-info.service.spec.ts @@ -0,0 +1,120 @@ +import { DeepMocked, createMock } from '@golevelup/ts-jest'; +import { TestingModule, Test } from '@nestjs/testing'; +import { PersonRepository } from '../../person/persistence/person.repository.js'; +import PersonTimeLimitService from './person-time-limit-info.service.js'; +import { DBiamPersonenkontextService } from '../../personenkontext/domain/dbiam-personenkontext.service.js'; +import { Person } from '../../person/domain/person.js'; +import { DoFactory } from '../../../../test/utils/do-factory.js'; +import { TimeLimitOccasion } from '../domain/time-limit-occasion.enums.js'; +import { Personenkontext } from '../../personenkontext/domain/personenkontext.js'; +import { PersonTimeLimitInfo } from './person-time-limit-info.js'; +import { KOPERS_DEADLINE_IN_DAYS } from './person-time-limit.js'; + +describe('PersonTimeLimitService', () => { + let module: TestingModule; + let sut: PersonTimeLimitService; + let personRepoMock: DeepMocked; + let dBiamPersonenkontextServiceMock: DeepMocked; + + beforeAll(async () => { + module = await Test.createTestingModule({ + providers: [ + PersonTimeLimitService, + { + provide: PersonRepository, + useValue: createMock(), + }, + { + provide: DBiamPersonenkontextService, + useValue: createMock(), + }, + ], + }).compile(); + sut = module.get(PersonTimeLimitService); + personRepoMock = module.get(PersonRepository); + dBiamPersonenkontextServiceMock = module.get(DBiamPersonenkontextService); + }); + + afterAll(async () => { + await module.close(); + }); + + beforeEach(() => { + jest.resetAllMocks(); + }); + + it('should be defined', () => { + expect(sut).toBeDefined(); + }); + + describe('getPersonTimeLimitInfo', () => { + it('should return PersonTimeLimitInfo array', async () => { + const person: Person = DoFactory.createPerson(true); + person.personalnummer = undefined; + personRepoMock.findById.mockResolvedValue(person); + + const pesonenkontext: Personenkontext = DoFactory.createPersonenkontext(true); + dBiamPersonenkontextServiceMock.getKopersPersonenkontexte.mockResolvedValue([pesonenkontext]); + + const result: PersonTimeLimitInfo[] = await sut.getPersonTimeLimitInfo(person.id); + + const expectedDeadline: Date = new Date(pesonenkontext.createdAt); + expectedDeadline.setDate(expectedDeadline.getDate() + KOPERS_DEADLINE_IN_DAYS); + + expect(result).toEqual([ + { + occasion: TimeLimitOccasion.KOPERS, + deadline: expectedDeadline, + }, + ]); + }); + + it.each([ + { + personenkontextDates: ['2021-01-02', '2021-01-01'], + expectedDate: '2021-01-01', + }, + { + personenkontextDates: ['2021-01-01', '2021-01-02'], + expectedDate: '2021-01-01', + }, + ])( + 'should return PersonTimeLimitInfo array with earliest Koperslock', + async ({ + personenkontextDates, + expectedDate, + }: { + personenkontextDates: string[]; + expectedDate: string; + }) => { + const person: Person = DoFactory.createPerson(true); + person.personalnummer = undefined; + personRepoMock.findById.mockResolvedValue(person); + + const personenkontexte: Personenkontext[] = personenkontextDates.map((date: string) => + DoFactory.createPersonenkontext(true, { createdAt: new Date(date) }), + ); + dBiamPersonenkontextServiceMock.getKopersPersonenkontexte.mockResolvedValue(personenkontexte); + + const result: PersonTimeLimitInfo[] = await sut.getPersonTimeLimitInfo(person.id); + + const expectedDeadline: Date = new Date(expectedDate); + expectedDeadline.setDate(expectedDeadline.getDate() + KOPERS_DEADLINE_IN_DAYS); + + expect(result).toEqual([ + { + occasion: TimeLimitOccasion.KOPERS, + deadline: expectedDeadline, + }, + ]); + }, + ); + + it('should return empty array when person isnt found ', async () => { + personRepoMock.findById.mockResolvedValue(null); + const result: PersonTimeLimitInfo[] = await sut.getPersonTimeLimitInfo(''); + + expect(result).toEqual([]); + }); + }); +}); diff --git a/src/modules/person/domain/person-time-limit-info.service.ts b/src/modules/person/domain/person-time-limit-info.service.ts new file mode 100644 index 000000000..63e19bd2f --- /dev/null +++ b/src/modules/person/domain/person-time-limit-info.service.ts @@ -0,0 +1,39 @@ +import { Injectable } from '@nestjs/common'; +import { Personenkontext } from '../../personenkontext/domain/personenkontext.js'; +import { DBiamPersonenkontextService } from '../../personenkontext/domain/dbiam-personenkontext.service.js'; +import { PersonRepository } from '../../person/persistence/person.repository.js'; +import { Person } from '../../person/domain/person.js'; +import { PersonTimeLimitInfo } from './person-time-limit-info.js'; +import { TimeLimitOccasion } from './time-limit-occasion.enums.js'; +import { KOPERS_DEADLINE_IN_DAYS } from './person-time-limit.js'; + +@Injectable() +export default class PersonTimeLimitService { + public constructor( + private readonly personRepository: PersonRepository, + private readonly dBiamPersonenkontextService: DBiamPersonenkontextService, + ) {} + + public async getPersonTimeLimitInfo(personId: string): Promise { + const person: Option> = await this.personRepository.findById(personId); + if (!person) { + return []; + } + const lockInfos: PersonTimeLimitInfo[] = []; + if (!person.personalnummer) { + const kopersKontexte: Personenkontext[] = + await this.dBiamPersonenkontextService.getKopersPersonenkontexte(person.id); + if (kopersKontexte.length > 0) { + const earliestKopersKontext: Personenkontext = kopersKontexte.reduce( + (prev: Personenkontext, current: Personenkontext) => + prev.createdAt < current.createdAt ? prev : current, + kopersKontexte[0]!, + ); + const kopersdeadline: Date = new Date(earliestKopersKontext.createdAt); + kopersdeadline.setDate(kopersdeadline.getDate() + KOPERS_DEADLINE_IN_DAYS); + lockInfos.push(new PersonTimeLimitInfo(TimeLimitOccasion.KOPERS, kopersdeadline)); + } + } + return lockInfos; + } +} diff --git a/src/modules/person/domain/person-time-limit-info.ts b/src/modules/person/domain/person-time-limit-info.ts new file mode 100644 index 000000000..d2b53c73b --- /dev/null +++ b/src/modules/person/domain/person-time-limit-info.ts @@ -0,0 +1,8 @@ +import { TimeLimitOccasion } from './time-limit-occasion.enums.js'; + +export class PersonTimeLimitInfo { + public constructor( + public readonly occasion: TimeLimitOccasion, + public readonly deadline: Date, + ) {} +} diff --git a/src/modules/person/domain/person-time-limit.ts b/src/modules/person/domain/person-time-limit.ts new file mode 100644 index 000000000..938b68853 --- /dev/null +++ b/src/modules/person/domain/person-time-limit.ts @@ -0,0 +1 @@ +export const KOPERS_DEADLINE_IN_DAYS: number = 56; diff --git a/src/modules/person/domain/time-limit-occasion.enums.ts b/src/modules/person/domain/time-limit-occasion.enums.ts new file mode 100644 index 000000000..d725c64f4 --- /dev/null +++ b/src/modules/person/domain/time-limit-occasion.enums.ts @@ -0,0 +1,3 @@ +export enum TimeLimitOccasion { + KOPERS = 'KOPERS', +} diff --git a/src/modules/person/persistence/person.repository.ts b/src/modules/person/persistence/person.repository.ts index 2a44931d3..9b85e60d1 100644 --- a/src/modules/person/persistence/person.repository.ts +++ b/src/modules/person/persistence/person.repository.ts @@ -38,6 +38,7 @@ import { PersonalNummerForPersonWithTrailingSpaceError } from '../domain/persona import { VornameForPersonWithTrailingSpaceError } from '../domain/vorname-with-trailing-space.error.js'; import { SystemConfig } from '../../../shared/config/system.config.js'; import { UserLock } from '../../keycloak-administration/domain/user-lock.js'; +import { KOPERS_DEADLINE_IN_DAYS } from '../domain/person-time-limit.js'; /** * Return email-address for person, if an enabled email-address exists, return it. @@ -762,7 +763,7 @@ export class PersonRepository { public async getKoPersUserLockList(): Promise<[PersonID, string][]> { const daysAgo: Date = new Date(); - daysAgo.setDate(daysAgo.getDate() - 56); + daysAgo.setDate(daysAgo.getDate() - KOPERS_DEADLINE_IN_DAYS); const filters: QBFilterQuery = { $and: [ @@ -770,7 +771,7 @@ export class PersonRepository { { personenKontexte: { $some: { - createdAt: { $lte: daysAgo }, // Check that createdAt is older than 56 days + createdAt: { $lte: daysAgo }, // Check that createdAt is older than KOPERS_DEADLINE_IN_DAYS rolleId: { merkmale: { merkmal: RollenMerkmal.KOPERS_PFLICHT }, }, diff --git a/src/modules/person/person.module.ts b/src/modules/person/person.module.ts index 45afb2505..a58a244b9 100644 --- a/src/modules/person/person.module.ts +++ b/src/modules/person/person.module.ts @@ -1,4 +1,4 @@ -import { Module } from '@nestjs/common'; +import { forwardRef, Module } from '@nestjs/common'; import { LoggerModule } from '../../core/logging/logger.module.js'; import { PersonService } from './domain/person.service.js'; import { UsernameGeneratorService } from './domain/username-generator.service.js'; @@ -10,8 +10,15 @@ import { RolleFactory } from '../rolle/domain/rolle.factory.js'; import { ServiceProviderRepo } from '../service-provider/repo/service-provider.repo.js'; import { OrganisationRepository } from '../organisation/persistence/organisation.repository.js'; import { EventModule } from '../../core/eventbus/event.module.js'; +import PersonTimeLimitService from './domain/person-time-limit-info.service.js'; +import { PersonenKontextModule } from '../personenkontext/personenkontext.module.js'; @Module({ - imports: [KeycloakAdministrationModule, LoggerModule.register(PersonModule.name), EventModule], + imports: [ + KeycloakAdministrationModule, + LoggerModule.register(PersonModule.name), + EventModule, + forwardRef(() => PersonenKontextModule), + ], providers: [ PersonRepository, PersonService, @@ -21,7 +28,8 @@ import { EventModule } from '../../core/eventbus/event.module.js'; OrganisationRepository, RolleFactory, ServiceProviderRepo, + PersonTimeLimitService, ], - exports: [PersonService, PersonFactory, PersonRepository], + exports: [PersonService, PersonFactory, PersonRepository, PersonTimeLimitService], }) export class PersonModule {} diff --git a/src/modules/personenkontext/domain/dbiam-personenkontext.service.spec.ts b/src/modules/personenkontext/domain/dbiam-personenkontext.service.spec.ts index 5f22acb14..90a666650 100644 --- a/src/modules/personenkontext/domain/dbiam-personenkontext.service.spec.ts +++ b/src/modules/personenkontext/domain/dbiam-personenkontext.service.spec.ts @@ -114,4 +114,95 @@ describe('DBiamPersonenkontextService', () => { }); }); }); + + describe('getKopersPersonenkontexte', () => { + describe('when a person has a personenkontext with a rolle with koperspflichtig merkmale', () => { + it('should return the personenkontext', async () => { + const personenkontexte: Personenkontext[] = [ + personenkontextFactory.construct('1', faker.date.past(), faker.date.recent(), '', '1', '1', '1'), + personenkontextFactory.construct('2', faker.date.past(), faker.date.recent(), '', '1', '2', '1'), + personenkontextFactory.construct('3', faker.date.past(), faker.date.recent(), '', '1', '1', '2'), + ]; + dbiamPersonenKontextRepoMock.findByPerson.mockResolvedValue(personenkontexte); + + const mapRollen: Map> = new Map(); + mapRollen.set( + '1', + DoFactory.createRolle(true, { + rollenart: RollenArt.LEHR, + merkmale: [RollenMerkmal.KOPERS_PFLICHT], + id: '1', + }), + ); + mapRollen.set('2', DoFactory.createRolle(true, { rollenart: RollenArt.LEIT, merkmale: [], id: '2' })); + rolleRepoMock.findByIds.mockResolvedValueOnce(mapRollen); + + const result: Personenkontext[] = await sut.getKopersPersonenkontexte('1'); + expect(result[0]).toEqual(personenkontexte[0]); + }); + }); + + describe('when a person has multiple personenkontexts with a rolle with koperspflichtig merkmale', () => { + it('should return the personenkontext', async () => { + const personenkontexte: Personenkontext[] = [ + personenkontextFactory.construct('1', faker.date.past(), faker.date.recent(), '', '1', '1', '1'), + personenkontextFactory.construct('2', faker.date.past(), faker.date.recent(), '', '1', '2', '2'), + personenkontextFactory.construct('3', faker.date.past(), faker.date.recent(), '', '1', '1', '3'), + ]; + dbiamPersonenKontextRepoMock.findByPerson.mockResolvedValue(personenkontexte); + + const mapRollen: Map> = new Map(); + mapRollen.set( + '1', + DoFactory.createRolle(true, { + rollenart: RollenArt.LEHR, + merkmale: [RollenMerkmal.KOPERS_PFLICHT], + id: '1', + }), + ); + mapRollen.set( + '2', + DoFactory.createRolle(true, { + rollenart: RollenArt.LEHR, + merkmale: [RollenMerkmal.KOPERS_PFLICHT], + id: '2', + }), + ); + rolleRepoMock.findByIds.mockResolvedValueOnce(mapRollen); + + const result: Personenkontext[] = await sut.getKopersPersonenkontexte('1'); + expect(result.length).toBe(2); + expect(result[0]).toEqual(personenkontexte[0]); + expect(result[1]).toEqual(personenkontexte[1]); + }); + }); + + describe('when a person has no personenkontext with a rolle with koperspflichtig merkmale', () => { + it('should return undefined', async () => { + const personenkontexte: Personenkontext[] = [ + personenkontextFactory.construct('1', faker.date.past(), faker.date.recent(), '', '1', '1', '1'), + personenkontextFactory.construct('2', faker.date.past(), faker.date.recent(), '', '1', '2', '1'), + personenkontextFactory.construct('3', faker.date.past(), faker.date.recent(), '', '1', '1', '2'), + ]; + dbiamPersonenKontextRepoMock.findByPerson.mockResolvedValue(personenkontexte); + + const mapRollen: Map> = new Map(); + + mapRollen.set( + '1', + DoFactory.createRolle(true, { + rollenart: RollenArt.LERN, + merkmale: [], + id: '1', + }), + ); + + mapRollen.set('2', DoFactory.createRolle(true, { rollenart: RollenArt.LEIT, merkmale: [], id: '2' })); + rolleRepoMock.findByIds.mockResolvedValueOnce(mapRollen); + + const result: Personenkontext[] = await sut.getKopersPersonenkontexte('1'); + expect(result.length).toBe(0); + }); + }); + }); }); diff --git a/src/modules/personenkontext/domain/dbiam-personenkontext.service.ts b/src/modules/personenkontext/domain/dbiam-personenkontext.service.ts index 61e577553..8c6d2c4e9 100644 --- a/src/modules/personenkontext/domain/dbiam-personenkontext.service.ts +++ b/src/modules/personenkontext/domain/dbiam-personenkontext.service.ts @@ -64,4 +64,18 @@ export class DBiamPersonenkontextService { rolle.merkmale.includes(RollenMerkmal.KOPERS_PFLICHT), ); } + + public async getKopersPersonenkontexte(personId: string): Promise[]> { + const personenkontexte: Personenkontext[] = await this.dBiamPersonenkontextRepo.findByPerson(personId); + const uniqueRolleIds: Set = new Set(personenkontexte.map((pk: Personenkontext) => pk.rolleId)); + const foundRollen: Map> = await this.rolleRepo.findByIds(Array.from(uniqueRolleIds)); + + const kopersRolle: Rolle[] = Array.from(foundRollen.values()).filter((rolle: Rolle) => + rolle.merkmale.includes(RollenMerkmal.KOPERS_PFLICHT), + ); + + return personenkontexte.filter((pk: Personenkontext) => + kopersRolle.some((rolle: Rolle) => rolle.id === pk.rolleId), + ); + } } diff --git a/src/modules/personenkontext/personenkontext.module.ts b/src/modules/personenkontext/personenkontext.module.ts index d717c0429..1479f88e2 100644 --- a/src/modules/personenkontext/personenkontext.module.ts +++ b/src/modules/personenkontext/personenkontext.module.ts @@ -1,4 +1,4 @@ -import { Module } from '@nestjs/common'; +import { forwardRef, Module } from '@nestjs/common'; import { LoggerModule } from '../../core/logging/logger.module.js'; import { PersonenkontextRepo } from '../personenkontext/persistence/personenkontext.repo.js'; import { PersonenkontextService } from '../personenkontext/domain/personenkontext.service.js'; @@ -17,7 +17,7 @@ import { PersonenkontextWorkflowFactory } from './domain/personenkontext-workflo @Module({ imports: [ EventModule, - PersonModule, + forwardRef(() => PersonModule), RolleModule, OrganisationModule, LoggerModule.register(PersonenKontextModule.name),