From 49d2e98a1a82d8a25c1bed0a9f73b52b279fe8bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Mon, 26 Sep 2022 21:11:01 +0200 Subject: [PATCH 1/4] [person] Feature: add a person's center history The association between Person and Center is now stored in a dedicated Entity: `PersonCenterHistory`, which have a date interval (start date and endDate). The SQL counterpart is a table, with a constraint which ensure that no person might be associated with two center at the same time. For ease, a view is created to get the current center associated with the person. The dedicated migration creates also: * indexes for a rapid search for person at current date; * and populate the table from current data, setting the startdate to the person's creation date and time if any, `NOW()` unless. The `Person` entity is also updated to use the information from the PersonCenterHistory classes, but this commit does not yet delete the `Center` column. --- .../ChillPersonBundle/Entity/Person.php | 64 +++++++- .../Entity/Person/PersonCenterCurrent.php | 112 ++++++++++++++ .../Entity/Person/PersonCenterHistory.php | 142 ++++++++++++++++++ .../Person/PersonCenterHistoryInterface.php | 10 ++ .../Person/PersonCenterHistoryRepository.php | 48 ++++++ .../Tests/Entity/PersonTest.php | 26 ++++ .../migrations/Version20220926154347.php | 69 +++++++++ 7 files changed, 468 insertions(+), 3 deletions(-) create mode 100644 src/Bundle/ChillPersonBundle/Entity/Person/PersonCenterCurrent.php create mode 100644 src/Bundle/ChillPersonBundle/Entity/Person/PersonCenterHistory.php create mode 100644 src/Bundle/ChillPersonBundle/Repository/Person/PersonCenterHistoryInterface.php create mode 100644 src/Bundle/ChillPersonBundle/Repository/Person/PersonCenterHistoryRepository.php create mode 100644 src/Bundle/ChillPersonBundle/migrations/Version20220926154347.php diff --git a/src/Bundle/ChillPersonBundle/Entity/Person.php b/src/Bundle/ChillPersonBundle/Entity/Person.php index 3a197e414..5d9f6a2cb 100644 --- a/src/Bundle/ChillPersonBundle/Entity/Person.php +++ b/src/Bundle/ChillPersonBundle/Entity/Person.php @@ -27,6 +27,8 @@ use Chill\MainBundle\Validation\Constraint\PhonenumberConstraint; use Chill\PersonBundle\Entity\Household\Household; use Chill\PersonBundle\Entity\Household\HouseholdMember; use Chill\PersonBundle\Entity\Household\PersonHouseholdAddress; +use Chill\PersonBundle\Entity\Person\PersonCenterCurrent; +use Chill\PersonBundle\Entity\Person\PersonCenterHistory; use Chill\PersonBundle\Entity\Person\PersonCurrentAddress; use Chill\PersonBundle\Entity\Person\PersonResource; use Chill\PersonBundle\Validator\Constraints\Household\HouseholdMembershipSequential; @@ -180,9 +182,21 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI * The person's center. * * @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\Center") + * @deprecated */ private ?Center $center = null; + /** + * @ORM\OneToMany(targetEntity=PersonCenterHistory::class, mappedBy="person") + * @var Collection|PersonCenterHistory[] + */ + private Collection $centerHistory; + + /** + * @ORM\OneToOne(targetEntity=PersonCenterCurrent::class, mappedBy="person") + */ + private ?PersonCenterCurrent $centerCurrent = null; + /** * Array where customfield's data are stored. * @@ -523,6 +537,7 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI $this->budgetResources = new ArrayCollection(); $this->budgetCharges = new ArrayCollection(); $this->resources = new ArrayCollection(); + $this->centerHistory = new ArrayCollection(); } /** @@ -897,7 +912,7 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI public function getCenter(): ?Center { - return $this->center; + return null === $this->centerCurrent ? null : $this->centerCurrent->getCenter(); } public function getCFData(): ?array @@ -1510,17 +1525,60 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI return $this; } + /** + * Associate the center with the person. The association start on 'now'. + * + * @param Center $center + * @return $this + */ public function setCenter(Center $center): self { $this->center = $center; + $modification = new DateTimeImmutable('now'); + + foreach ($this->centerHistory as $centerHistory) { + if (null === $centerHistory->getEndDate()) { + $centerHistory->setEndDate($modification); + } + } + + $this->centerHistory[] = $new = new PersonCenterHistory($this, $center, $modification); + $this->centerCurrent = new PersonCenterCurrent($new); + return $this; } /** - * @return Report + * @return Collection */ - public function setCFData(?array $cFData) + public function getCenterHistory(): Collection + { + return $this->centerHistory; + } + + /** + * @param Collection $centerHistory + * @return Person + */ + public function setCenterHistory(Collection $centerHistory): Person + { + $this->centerHistory = $centerHistory; + return $this; + } + + /** + * @return PersonCenterCurrent|null + */ + public function getCenterCurrent(): ?PersonCenterCurrent + { + return $this->centerCurrent; + } + + /** + * @return Person + */ + public function setCFData(?array $cFData): self { $this->cFData = $cFData; diff --git a/src/Bundle/ChillPersonBundle/Entity/Person/PersonCenterCurrent.php b/src/Bundle/ChillPersonBundle/Entity/Person/PersonCenterCurrent.php new file mode 100644 index 000000000..690ff025e --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Entity/Person/PersonCenterCurrent.php @@ -0,0 +1,112 @@ +person = $history->getPerson(); + $this->center = $history->getCenter(); + $this->startDate = $history->getStartDate(); + $this->endDate = $history->getEndDate(); + $this->id = $history->getId(); + } + + /** + * The id will be the same as the current @link{PersonCenterHistory::class} + * + * @return int + */ + public function getId(): int + { + return $this->id; + } + + /** + * @return Person + */ + public function getPerson(): Person + { + return $this->person; + } + + /** + * @return Center + */ + public function getCenter(): Center + { + return $this->center; + } + + /** + * @return \DateTimeImmutable + */ + public function getStartDate(): \DateTimeImmutable + { + return $this->startDate; + } + + /** + * @return \DateTimeImmutable|null + */ + public function getEndDate(): ?\DateTimeImmutable + { + return $this->endDate; + } + +} diff --git a/src/Bundle/ChillPersonBundle/Entity/Person/PersonCenterHistory.php b/src/Bundle/ChillPersonBundle/Entity/Person/PersonCenterHistory.php new file mode 100644 index 000000000..6bfb28cc6 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Entity/Person/PersonCenterHistory.php @@ -0,0 +1,142 @@ +person = $person; + $this->center = $center; + $this->startDate = $startDate; + } + + + /** + * @return int|null + */ + public function getId(): ?int + { + return $this->id; + } + + /** + * @return Person|null + */ + public function getPerson(): ?Person + { + return $this->person; + } + + /** + * @param Person|null $person + */ + public function setPerson(?Person $person): self + { + $this->person = $person; + + return $this; + } + + /** + * @return Center|null + */ + public function getCenter(): ?Center + { + return $this->center; + } + + /** + * @param Center|null $center + */ + public function setCenter(?Center $center): self + { + $this->center = $center; + + return $this; + } + + /** + * @return \DateTimeImmutable|null + */ + public function getStartDate(): ?\DateTimeImmutable + { + return $this->startDate; + } + + /** + * @param \DateTimeImmutable|null $startDate + */ + public function setStartDate(?\DateTimeImmutable $startDate): self + { + $this->startDate = $startDate; + return $this; + } + + /** + * @return \DateTimeImmutable|null + */ + public function getEndDate(): ?\DateTimeImmutable + { + return $this->endDate; + } + + /** + * @param \DateTimeImmutable|null $endDate + */ + public function setEndDate(?\DateTimeImmutable $endDate): self + { + $this->endDate = $endDate; + + return $this; + } +} diff --git a/src/Bundle/ChillPersonBundle/Repository/Person/PersonCenterHistoryInterface.php b/src/Bundle/ChillPersonBundle/Repository/Person/PersonCenterHistoryInterface.php new file mode 100644 index 000000000..089add06a --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Repository/Person/PersonCenterHistoryInterface.php @@ -0,0 +1,10 @@ +repository = $em->getRepository($this->getClassName()); + } + + public function find($id): ?PersonCenterHistory + { + return $this->repository->find($id); + } + + /** + * @return array|PersonCenterHistory[] + */ + public function findAll(): array + { + return $this->repository->findAll(); + } + + /** + * @return array|PersonCenterHistory[] + */ + public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array + { + return $this->repository->findBy($criteria, $orderBy, $limit, $offset); + } + + public function findOneBy(array $criteria): ?PersonCenterHistory + { + return $this->repository->findOneBy($criteria); + } + + public function getClassName(): string + { + return PersonCenterHistory::class; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Entity/PersonTest.php b/src/Bundle/ChillPersonBundle/Tests/Entity/PersonTest.php index 18149c824..4633c3fcb 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Entity/PersonTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Entity/PersonTest.php @@ -11,6 +11,7 @@ declare(strict_types=1); namespace Chill\PersonBundle\Tests\Entity; +use Chill\MainBundle\Entity\Center; use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Entity\Household\Household; use Chill\PersonBundle\Entity\Household\HouseholdMember; @@ -191,4 +192,29 @@ final class PersonTest extends \PHPUnit\Framework\TestCase $this->assertEquals($r['result'], Person::ERROR_ADDIND_PERIOD_AFTER_AN_OPEN_PERIOD); } + + public function testSetCenter() + { + $person = new Person(); + $centerA = new Center(); + $centerB = new Center(); + + $this->assertCount(0, $person->getCenterHistory()); + $this->assertNull($person->getCenter()); + $this->assertNull($person->getCenterCurrent()); + + $person->setCenter($centerA); + + $this->assertCount(1, $person->getCenterHistory()); + $this->assertSame($centerA, $person->getCenter()); + $this->assertInstanceOf(Person\PersonCenterCurrent::class, $person->getCenterCurrent()); + $this->assertSame($centerA, $person->getCenterCurrent()->getCenter()); + + $person->setCenter($centerB); + + $this->assertCount(2, $person->getCenterHistory()); + $this->assertSame($centerB, $person->getCenter()); + $this->assertInstanceOf(Person\PersonCenterCurrent::class, $person->getCenterCurrent()); + $this->assertSame($centerB, $person->getCenterCurrent()->getCenter()); + } } diff --git a/src/Bundle/ChillPersonBundle/migrations/Version20220926154347.php b/src/Bundle/ChillPersonBundle/migrations/Version20220926154347.php new file mode 100644 index 000000000..b308c22e3 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/migrations/Version20220926154347.php @@ -0,0 +1,69 @@ +addSql('CREATE SEQUENCE chill_person_person_center_history_id_seq INCREMENT BY 1 MINVALUE 1 START 1'); + $this->addSql('CREATE TABLE chill_person_person_center_history ( + id INT NOT NULL, person_id INT DEFAULT NULL, center_id INT DEFAULT NULL, + startDate DATE NOT NULL, endDate DATE DEFAULT NULL, createdAt TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, + updatedAt TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, createdBy_id INT DEFAULT NULL, + updatedBy_id INT DEFAULT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE INDEX IDX_CACA67FA217BBB47 ON chill_person_person_center_history (person_id)'); + $this->addSql('CREATE INDEX IDX_CACA67FA5932F377 ON chill_person_person_center_history (center_id)'); + $this->addSql('CREATE INDEX IDX_CACA67FA3174800F ON chill_person_person_center_history (createdBy_id)'); + $this->addSql('CREATE INDEX IDX_CACA67FA65FF1AEC ON chill_person_person_center_history (updatedBy_id)'); + $this->addSql('COMMENT ON COLUMN chill_person_person_center_history.startDate IS \'(DC2Type:date_immutable)\''); + $this->addSql('COMMENT ON COLUMN chill_person_person_center_history.endDate IS \'(DC2Type:date_immutable)\''); + $this->addSql('COMMENT ON COLUMN chill_person_person_center_history.createdAt IS \'(DC2Type:datetime_immutable)\''); + $this->addSql('COMMENT ON COLUMN chill_person_person_center_history.updatedAt IS \'(DC2Type:datetime_immutable)\''); + $this->addSql('ALTER TABLE chill_person_person_center_history ADD CONSTRAINT FK_CACA67FA217BBB47 FOREIGN KEY (person_id) REFERENCES chill_person_person (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE chill_person_person_center_history ADD CONSTRAINT FK_CACA67FA5932F377 FOREIGN KEY (center_id) REFERENCES centers (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE chill_person_person_center_history ADD CONSTRAINT FK_CACA67FA3174800F FOREIGN KEY (createdBy_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE chill_person_person_center_history ADD CONSTRAINT FK_CACA67FA65FF1AEC FOREIGN KEY (updatedBy_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + + // check consistency of history on database side + $this->addSql('ALTER TABLE chill_person_person_center_history ADD CHECK (startdate <= enddate)'); + $this->addSql('ALTER TABLE chill_person_person_center_history ADD CONSTRAINT ' . + 'chill_internal_person_person_center_history_not_overlaps EXCLUDE USING GIST( + -- extension btree_gist required to include comparaison with integer + person_id WITH =, + daterange(startdate, enddate, \'[)\') WITH && + ) + INITIALLY DEFERRED'); + + // create index on search by person and date + $this->addSql('CREATE INDEX chill_internal_person_person_center_history_by_date ON chill_person_person_center_history (person_id DESC, startdate DESC, enddate DESC NULLS FIRST)'); + + // create a view to get the current center on a person + $this->addSql( + 'CREATE VIEW view_chill_person_person_center_history_current AS + SELECT id, person_id, center_id, startDate, endDate, createdAt, updatedAt, createdBy_id, updatedBy_id + FROM chill_person_person_center_history WHERE startDate <= NOW() AND (enddate IS NULL OR enddate > NOW())' + ); + + $this->addSql('INSERT INTO chill_person_person_center_history (id, person_id, center_id, startdate) + SELECT nextval(\'chill_person_person_center_history_id_seq\'), id, center_id, COALESCE(createdat, NOW()) + FROM chill_person_person WHERE center_id IS NOT NULL'); + } + + public function down(Schema $schema): void + { + $this->addSql('DROP VIEW view_chill_person_person_center_history_current'); + $this->addSql('DROP SEQUENCE chill_person_person_center_history_id_seq CASCADE'); + $this->addSql('DROP TABLE chill_person_person_center_history'); + } +} From 451f7f4230d907928966c05fc6e62fca96d9fd72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Tue, 27 Sep 2022 09:41:41 +0200 Subject: [PATCH 2/4] [person][Search] Feature: use the current center history when searching for person --- .../Repository/PersonACLAwareRepository.php | 8 +- .../PersonACLAwareRepositoryTest.php | 79 +++++++++++++++++++ 2 files changed, 84 insertions(+), 3 deletions(-) create mode 100644 src/Bundle/ChillPersonBundle/Tests/Repository/PersonACLAwareRepositoryTest.php diff --git a/src/Bundle/ChillPersonBundle/Repository/PersonACLAwareRepository.php b/src/Bundle/ChillPersonBundle/Repository/PersonACLAwareRepository.php index d5dc3cd7d..96662b66c 100644 --- a/src/Bundle/ChillPersonBundle/Repository/PersonACLAwareRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/PersonACLAwareRepository.php @@ -16,6 +16,7 @@ use Chill\MainBundle\Repository\CountryRepository; use Chill\MainBundle\Search\ParsingException; use Chill\MainBundle\Search\SearchApiQuery; use Chill\MainBundle\Security\Authorization\AuthorizationHelper; +use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface; use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Security\Authorization\PersonVoter; use DateTimeInterface; @@ -34,7 +35,7 @@ use function implode; final class PersonACLAwareRepository implements PersonACLAwareRepositoryInterface { - private AuthorizationHelper $authorizationHelper; + private AuthorizationHelperInterface $authorizationHelper; private CountryRepository $countryRepository; @@ -46,7 +47,7 @@ final class PersonACLAwareRepository implements PersonACLAwareRepositoryInterfac Security $security, EntityManagerInterface $em, CountryRepository $countryRepository, - AuthorizationHelper $authorizationHelper + AuthorizationHelperInterface $authorizationHelper ) { $this->security = $security; $this->em = $em; @@ -310,9 +311,10 @@ final class PersonACLAwareRepository implements PersonACLAwareRepositoryInterfac } return $query + ->setFromClause($query->getFromClause() . ' JOIN view_chill_person_person_center_history_current vcppchc ON vcppchc.person_id = person.id', $query->getFromParams()) ->andWhereClause( strtr( - 'person.center_id IN ({{ center_ids }})', + 'vcppchc.center_id IN ({{ center_ids }})', [ '{{ center_ids }}' => implode( ', ', diff --git a/src/Bundle/ChillPersonBundle/Tests/Repository/PersonACLAwareRepositoryTest.php b/src/Bundle/ChillPersonBundle/Tests/Repository/PersonACLAwareRepositoryTest.php new file mode 100644 index 000000000..4540e3716 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Repository/PersonACLAwareRepositoryTest.php @@ -0,0 +1,79 @@ +entityManager = self::$container->get(EntityManagerInterface::class); + $this->countryRepository = self::$container->get(CountryRepository::class); + $this->centerRepository = self::$container->get(CenterRepositoryInterface::class); + + } + + public function testCountByCriteria() + { + $user = new User(); + + $authorizationHelper = $this->prophesize(AuthorizationHelperInterface::class); + $authorizationHelper->getReachableCenters(Argument::exact($user), Argument::exact(PersonVoter::SEE)) + ->willReturn($this->centerRepository->findAll()); + + $security = $this->prophesize(Security::class); + $security->getUser()->willReturn($user); + + $repository = new PersonACLAwareRepository($security->reveal(), $this->entityManager, $this->countryRepository, + $authorizationHelper->reveal()); + + $number = $repository->countBySearchCriteria('diallo'); + + $this->assertGreaterThan(0, $number); + } + + public function testFindByCriteria() + { + $user = new User(); + + $authorizationHelper = $this->prophesize(AuthorizationHelperInterface::class); + $authorizationHelper->getReachableCenters(Argument::exact($user), Argument::exact(PersonVoter::SEE)) + ->willReturn($this->centerRepository->findAll()); + + $security = $this->prophesize(Security::class); + $security->getUser()->willReturn($user); + + $repository = new PersonACLAwareRepository($security->reveal(), $this->entityManager, $this->countryRepository, + $authorizationHelper->reveal()); + + $results = $repository->findBySearchCriteria(0, 5, false, 'diallo'); + + $this->assertGreaterThan(0, count($results)); + $this->assertContainsOnlyInstancesOf(Person::class, $results); + foreach ($results as $person) { + $this->assertStringContainsString('diallo', strtolower($person->getFirstName() . ' ' . $person->getLastName())); + } + } +} From f39b0ee0029324fd2a8614b6aac61187c4c7ba23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Tue, 27 Sep 2022 16:09:09 +0200 Subject: [PATCH 3/4] [task] Feature: use the resolution of center with person's center history --- .../SingleTaskAclAwareRepository.php | 32 ++- .../SingleTaskACLAwareRepositoryTest.php | 211 ++++++++++++++++++ 2 files changed, 231 insertions(+), 12 deletions(-) create mode 100644 src/Bundle/ChillTaskBundle/Tests/Repository/SingleTaskACLAwareRepositoryTest.php diff --git a/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepository.php b/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepository.php index d2dd7fc18..dca83eb71 100644 --- a/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepository.php +++ b/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepository.php @@ -13,6 +13,7 @@ namespace Chill\TaskBundle\Repository; use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface; use Chill\MainBundle\Security\Resolver\CenterResolverDispatcherInterface; +use Chill\MainBundle\Security\Resolver\CenterResolverManagerInterface; use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Entity\Person; use Chill\TaskBundle\Entity\SingleTask; @@ -31,14 +32,14 @@ final class SingleTaskAclAwareRepository implements SingleTaskAclAwareRepository { private AuthorizationHelperInterface $authorizationHelper; - private CenterResolverDispatcherInterface $centerResolverDispatcher; + private CenterResolverManagerInterface $centerResolverDispatcher; private EntityManagerInterface $em; private Security $security; public function __construct( - CenterResolverDispatcherInterface $centerResolverDispatcher, + CenterResolverManagerInterface $centerResolverDispatcher, EntityManagerInterface $em, Security $security, AuthorizationHelperInterface $authorizationHelper @@ -304,14 +305,18 @@ final class SingleTaskAclAwareRepository implements SingleTaskAclAwareRepository QueryBuilder $qb, $entity ): QueryBuilder { - $scopes = $this->authorizationHelper->getReachableScopes( - $this->security->getUser(), - TaskVoter::SHOW, - $this->centerResolverDispatcher->resolveCenter($entity) - ); + foreach ($this->centerResolverDispatcher->resolveCenters($entity) as $center) { + $scopes = $this->authorizationHelper->getReachableScopes( + $this->security->getUser(), + TaskVoter::SHOW, + $center + ); - return $qb->andWhere($qb->expr()->in('t.circle', ':scopes')) - ->setParameter('scopes', $scopes); + $qb->andWhere($qb->expr()->in('t.circle', ':scopes')) + ->setParameter('scopes', $scopes); + } + + return $qb; } private function addACLGlobal( @@ -329,7 +334,10 @@ final class SingleTaskAclAwareRepository implements SingleTaskAclAwareRepository $qb->leftJoin('t.person', 'person') ->leftJoin('t.course', 'course') ->leftJoin('course.participations', 'participation') - ->leftJoin('participation.person', 'person_p'); + ->leftJoin('participation.person', 'person_p') + ->leftJoin('person.centerCurrent', 'center_current_person') + ->leftJoin('person_p.centerCurrent', 'center_current_participation') + ; $qb->distinct(true); $k = 0; @@ -344,8 +352,8 @@ final class SingleTaskAclAwareRepository implements SingleTaskAclAwareRepository $and = $qb->expr()->andX( $qb->expr()->orX( - $qb->expr()->eq('person.center', ':center_' . $k), - $qb->expr()->eq('person_p.center', ':center_' . $k) + $qb->expr()->eq('center_current_person.center', ':center_' . $k), + $qb->expr()->eq('center_current_participation.center', ':center_' . $k) ), $qb->expr()->in('t.circle', ':scopes_' . $k) ); diff --git a/src/Bundle/ChillTaskBundle/Tests/Repository/SingleTaskACLAwareRepositoryTest.php b/src/Bundle/ChillTaskBundle/Tests/Repository/SingleTaskACLAwareRepositoryTest.php new file mode 100644 index 000000000..f4c54b6cc --- /dev/null +++ b/src/Bundle/ChillTaskBundle/Tests/Repository/SingleTaskACLAwareRepositoryTest.php @@ -0,0 +1,211 @@ +em = self::$container->get(EntityManagerInterface::class); + $this->userRepository = self::$container->get(UserRepository::class); + $this->centerRepository = self::$container->get(CenterRepositoryInterface::class); + $this->scopeRepository = self::$container->get(ScopeRepository::class); + $this->personRepository = self::$container->get(PersonRepository::class); + } + + public function testCountByPerson(): void + { + $centerA = $this->centerRepository->findOneBy(['name' => 'Center A']); + $user = new User(); + $scopes = $this->scopeRepository->findAll(); + $person = $this->getRandomPerson($this->em); + + $security = $this->prophesize(Security::class); + $security->getUser()->willReturn($user); + + $centerResolverDispatcher = $this->prophesize(CenterResolverManagerInterface::class); + $centerResolverDispatcher->resolveCenters(Argument::type(Person::class), Argument::any()) + ->willReturn([$centerA]); + + $authorizationHelper = $this->prophesize(AuthorizationHelperInterface::class); + $authorizationHelper->getReachableScopes(Argument::exact($user), Argument::exact(TaskVoter::SHOW), Argument::exact($centerA)) + ->willReturn($scopes); + + $repository = new SingleTaskAclAwareRepository( + $centerResolverDispatcher->reveal(), + $this->em, + $security->reveal(), + $authorizationHelper->reveal() + ); + + $nb = $repository->countByPerson($person, null, []); + + $this->assertGreaterThanOrEqual(0, $nb); + } + + public function testFindByPerson(): void + { + $centerA = $this->centerRepository->findOneBy(['name' => 'Center A']); + $user = new User(); + $scopes = $this->scopeRepository->findAll(); + $person = $this->getRandomPerson($this->em); + + $security = $this->prophesize(Security::class); + $security->getUser()->willReturn($user); + + $centerResolverDispatcher = $this->prophesize(CenterResolverManagerInterface::class); + $centerResolverDispatcher->resolveCenters(Argument::type(Person::class), Argument::any()) + ->willReturn([$centerA]); + + $authorizationHelper = $this->prophesize(AuthorizationHelperInterface::class); + $authorizationHelper->getReachableScopes(Argument::exact($user), Argument::exact(TaskVoter::SHOW), Argument::exact($centerA)) + ->willReturn($scopes); + + $repository = new SingleTaskAclAwareRepository( + $centerResolverDispatcher->reveal(), + $this->em, + $security->reveal(), + $authorizationHelper->reveal() + ); + + $tasks = $repository->findByPerson($person, null, []); + + $this->assertGreaterThanOrEqual(0, count($tasks)); + } + + public function testFindByAllViewable(): void + { + $centerA = $this->centerRepository->findOneBy(['name' => 'Center A']); + $user = new User(); + $scopes = $this->scopeRepository->findAll(); + + $security = $this->prophesize(Security::class); + $security->getUser()->willReturn($user); + + $centerResolverDispatcher = $this->prophesize(CenterResolverManagerInterface::class); + $centerResolverDispatcher->resolveCenters(Argument::type(Person::class), Argument::any()) + ->willReturn([$centerA]); + + $authorizationHelper = $this->prophesize(AuthorizationHelperInterface::class); + $authorizationHelper->getReachableCenters(Argument::exact($user), Argument::exact(TaskVoter::SHOW)) + ->willReturn([$centerA]); + $authorizationHelper->getReachableScopes(Argument::exact($user), Argument::exact(TaskVoter::SHOW), Argument::exact($centerA)) + ->willReturn($scopes); + + $repository = new SingleTaskAclAwareRepository( + $centerResolverDispatcher->reveal(), + $this->em, + $security->reveal(), + $authorizationHelper->reveal() + ); + + $tasks = $repository->findByAllViewable(null, []); + + $this->assertGreaterThanOrEqual(0, count($tasks)); + } + + public function testCountByAllViewable(): void + { + $centerA = $this->centerRepository->findOneBy(['name' => 'Center A']); + $user = new User(); + $scopes = $this->scopeRepository->findAll(); + + $security = $this->prophesize(Security::class); + $security->getUser()->willReturn($user); + + $centerResolverDispatcher = $this->prophesize(CenterResolverManagerInterface::class); + $centerResolverDispatcher->resolveCenters(Argument::type(Person::class), Argument::any()) + ->willReturn([$centerA]); + + $authorizationHelper = $this->prophesize(AuthorizationHelperInterface::class); + $authorizationHelper->getReachableCenters(Argument::exact($user), Argument::exact(TaskVoter::SHOW)) + ->willReturn([$centerA]); + $authorizationHelper->getReachableScopes(Argument::exact($user), Argument::exact(TaskVoter::SHOW), Argument::exact($centerA)) + ->willReturn($scopes); + + $repository = new SingleTaskAclAwareRepository( + $centerResolverDispatcher->reveal(), + $this->em, + $security->reveal(), + $authorizationHelper->reveal() + ); + + $nb = $repository->countByAllViewable(null, []); + + $this->assertGreaterThanOrEqual(0, $nb); + } + + public function testFindByCourse(): void + { + $centerA = $this->centerRepository->findOneBy(['name' => 'Center A']); + $user = new User(); + $scopes = $this->scopeRepository->findAll(); + /** @var Person $person */ + $person = $this->em->createQuery( + 'SELECT p FROM '.Person::class.' p JOIN p.centerCurrent cc + WHERE SIZE(p.accompanyingPeriodParticipations) > 0 + AND cc.center = :center' + ) + ->setParameter('center', $centerA) + ->setMaxResults(1) + ->getSingleResult() + ; + $period = $person->getAccompanyingPeriodParticipations()->first()->getAccompanyingPeriod(); + + $security = $this->prophesize(Security::class); + $security->getUser()->willReturn($user); + + $centerResolverDispatcher = $this->prophesize(CenterResolverManagerInterface::class); + $centerResolverDispatcher->resolveCenters(Argument::type(AccompanyingPeriod::class), Argument::any()) + ->willReturn([$centerA]); + + $authorizationHelper = $this->prophesize(AuthorizationHelperInterface::class); + $authorizationHelper->getReachableScopes(Argument::exact($user), Argument::exact(TaskVoter::SHOW), Argument::any()) + ->willReturn($scopes); + + $repository = new SingleTaskAclAwareRepository( + $centerResolverDispatcher->reveal(), + $this->em, + $security->reveal(), + $authorizationHelper->reveal() + ); + + $tasks = $repository->findByCourse($period); + + $this->assertGreaterThanOrEqual(0, count($tasks)); + } +} \ No newline at end of file From 32f252149bd43a034eda338715692357477cb1b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 28 Sep 2022 15:43:31 +0200 Subject: [PATCH 4/4] [export] Feature: rely on person's center history to build export's query --- .../LinkedToACP/AvgActivityDuration.php | 18 +++++++++ .../LinkedToACP/AvgActivityVisitDuration.php | 18 +++++++++ .../Export/LinkedToACP/CountActivity.php | 24 ++++++++++-- .../LinkedToACP/SumActivityDuration.php | 24 ++++++++++-- .../LinkedToACP/SumActivityVisitDuration.php | 24 ++++++++++-- .../Export/LinkedToPerson/CountActivity.php | 21 +++++++--- .../LinkedToPerson/StatActivityDuration.php | 21 ++++++++-- .../Export/Export/CountAccompanyingCourse.php | 20 +++++++++- .../Export/Export/CountEvaluation.php | 18 +++++++++ .../Export/Export/CountHousehold.php | 39 ++++++++++++------- .../Export/Export/CountPerson.php | 11 ++++-- .../CountPersonWithAccompanyingCourse.php | 11 ++++++ .../Export/Export/CountSocialWorkActions.php | 25 +++++++++--- .../Export/StatAccompanyingCourseDuration.php | 24 +++++++++--- 14 files changed, 247 insertions(+), 51 deletions(-) diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityDuration.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityDuration.php index 3fc975147..f45adc0b3 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityDuration.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityDuration.php @@ -17,6 +17,8 @@ use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter; use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\GroupedExportInterface; +use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation; +use Chill\PersonBundle\Entity\Person\PersonCenterHistory; use Chill\PersonBundle\Export\Declarations as PersonDeclarations; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; @@ -83,6 +85,10 @@ class AvgActivityDuration implements ExportInterface, GroupedExportInterface public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) { + $centers = array_map(static function ($el) { + return $el['center']; + }, $acl); + $qb = $this->repository->createQueryBuilder('activity'); $qb @@ -90,6 +96,18 @@ class AvgActivityDuration implements ExportInterface, GroupedExportInterface ->select('AVG(activity.durationTime) as export_avg_activity_duration') ->andWhere($qb->expr()->isNotNull('activity.durationTime')); + $qb + ->andWhere( + $qb->expr()->exists( + 'SELECT 1 FROM '.AccompanyingPeriodParticipation::class.' acl_count_part + JOIN '.PersonCenterHistory::class.' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person) + WHERE acl_count_part.accompanyingPeriod = acp.id AND acl_count_person_history.center IN (:authorized_centers) + ' + ) + ) + ->setParameter('authorized_centers', $centers) + ; + return $qb; } diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityVisitDuration.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityVisitDuration.php index 69533a09d..661f169dd 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityVisitDuration.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityVisitDuration.php @@ -17,6 +17,8 @@ use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter; use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\GroupedExportInterface; +use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation; +use Chill\PersonBundle\Entity\Person\PersonCenterHistory; use Chill\PersonBundle\Export\Declarations as PersonDeclarations; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; @@ -84,6 +86,10 @@ class AvgActivityVisitDuration implements ExportInterface, GroupedExportInterfac public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) { + $centers = array_map(static function ($el) { + return $el['center']; + }, $acl); + $qb = $this->repository->createQueryBuilder('activity'); $qb @@ -92,6 +98,18 @@ class AvgActivityVisitDuration implements ExportInterface, GroupedExportInterfac ->andWhere($qb->expr()->isNotNull('activity.travelTime')) ; + $qb + ->andWhere( + $qb->expr()->exists( + 'SELECT 1 FROM '.AccompanyingPeriodParticipation::class.' acl_count_part + JOIN '.PersonCenterHistory::class.' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person) + WHERE acl_count_part.accompanyingPeriod = acp.id AND acl_count_person_history.center IN (:authorized_centers) + ' + ) + ) + ->setParameter('authorized_centers', $centers) + ; + return $qb; } diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/CountActivity.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/CountActivity.php index 2162b8b58..46d2f3c33 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/CountActivity.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/CountActivity.php @@ -17,6 +17,8 @@ use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter; use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\GroupedExportInterface; +use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation; +use Chill\PersonBundle\Entity\Person\PersonCenterHistory; use Chill\PersonBundle\Export\Declarations as PersonDeclarations; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; @@ -84,11 +86,25 @@ class CountActivity implements ExportInterface, GroupedExportInterface public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) { - $qb = $this->repository->createQueryBuilder('activity'); + $centers = array_map(static function ($el) { + return $el['center']; + }, $acl); - if (!in_array('acp', $qb->getAllAliases(), true)) { - $qb->join('activity.accompanyingPeriod', 'acp'); - } + $qb = $this->repository + ->createQueryBuilder('activity') + ->join('activity.accompanyingPeriod', 'acp'); + + $qb + ->andWhere( + $qb->expr()->exists( + 'SELECT 1 FROM '.AccompanyingPeriodParticipation::class.' acl_count_part + JOIN '.PersonCenterHistory::class.' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person) + WHERE acl_count_part.accompanyingPeriod = acp.id AND acl_count_person_history.center IN (:authorized_centers) + ' + ) + ) + ->setParameter('authorized_centers', $centers) + ; $qb->select('COUNT(DISTINCT activity.id) as export_count_activity'); diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityDuration.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityDuration.php index e9dbaff29..405b54885 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityDuration.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityDuration.php @@ -17,6 +17,8 @@ use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter; use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\GroupedExportInterface; +use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation; +use Chill\PersonBundle\Entity\Person\PersonCenterHistory; use Chill\PersonBundle\Export\Declarations as PersonDeclarations; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; @@ -84,15 +86,29 @@ class SumActivityDuration implements ExportInterface, GroupedExportInterface public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) { - $qb = $this->repository->createQueryBuilder('activity'); + $centers = array_map(static function ($el) { + return $el['center']; + }, $acl); - if (!in_array('acp', $qb->getAllAliases(), true)) { - $qb->join('activity.accompanyingPeriod', 'acp'); - } + $qb = $this->repository + ->createQueryBuilder('activity') + ->join('activity.accompanyingPeriod', 'acp'); $qb->select('SUM(activity.durationTime) as export_sum_activity_duration') ->andWhere($qb->expr()->isNotNull('activity.durationTime')); + $qb + ->andWhere( + $qb->expr()->exists( + 'SELECT 1 FROM '.AccompanyingPeriodParticipation::class.' acl_count_part + JOIN '.PersonCenterHistory::class.' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person) + WHERE acl_count_part.accompanyingPeriod = acp.id AND acl_count_person_history.center IN (:authorized_centers) + ' + ) + ) + ->setParameter('authorized_centers', $centers) + ; + return $qb; } diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityVisitDuration.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityVisitDuration.php index d41994562..40020cb4b 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityVisitDuration.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityVisitDuration.php @@ -17,6 +17,8 @@ use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter; use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\GroupedExportInterface; +use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation; +use Chill\PersonBundle\Entity\Person\PersonCenterHistory; use Chill\PersonBundle\Export\Declarations as PersonDeclarations; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; @@ -84,15 +86,29 @@ class SumActivityVisitDuration implements ExportInterface, GroupedExportInterfac public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) { - $qb = $this->repository->createQueryBuilder('activity'); + $centers = array_map(static function ($el) { + return $el['center']; + }, $acl); - if (!in_array('acp', $qb->getAllAliases(), true)) { - $qb->join('activity.accompanyingPeriod', 'acp'); - } + $qb = $this->repository + ->createQueryBuilder('activity') + ->join('activity.accompanyingPeriod', 'acp'); $qb->select('SUM(activity.travelTime) as export_sum_activity_visit_duration') ->andWhere($qb->expr()->isNotNull('activity.travelTime')); + $qb + ->andWhere( + $qb->expr()->exists( + 'SELECT 1 FROM '.AccompanyingPeriodParticipation::class.' acl_count_part + JOIN '.PersonCenterHistory::class.' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person) + WHERE acl_count_part.accompanyingPeriod = acp.id AND acl_count_person_history.center IN (:authorized_centers) + ' + ) + ) + ->setParameter('authorized_centers', $centers) + ; + return $qb; } diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/CountActivity.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/CountActivity.php index f12507df8..116fa4b6a 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/CountActivity.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/CountActivity.php @@ -84,16 +84,25 @@ class CountActivity implements ExportInterface, GroupedExportInterface { $centers = array_map(static fn ($el) => $el['center'], $acl); - $qb = $this->activityRepository->createQueryBuilder('activity'); - - if (!in_array('person', $qb->getAllAliases(), true)) { - $qb->join('activity.person', 'person'); - } + $qb = $this->activityRepository + ->createQueryBuilder('activity') + ->join('activity.person', 'person') + ->join('person.centerHistory', 'centerHistory') + ; $qb->select('COUNT(activity.id) as export_count_activity'); $qb - ->where($qb->expr()->in('person.center', ':centers')) + ->where( + $qb->expr()->andX( + $qb->expr()->lte('centerHistory.startDate', 'activity.date'), + $qb->expr()->orX( + $qb->expr()->isNull('centerHistory.endDate'), + $qb->expr()->gt('centerHistory.endDate', 'activity.date') + ) + ) + ) + ->andWhere($qb->expr()->in('centerHistory.center', ':centers')) ->setParameter('centers', $centers); return $qb; diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/StatActivityDuration.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/StatActivityDuration.php index 9013b4648..f1228842f 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/StatActivityDuration.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/StatActivityDuration.php @@ -119,11 +119,24 @@ class StatActivityDuration implements ExportInterface, GroupedExportInterface $select = 'SUM(activity.durationTime) AS export_stat_activity'; } - return $qb->select($select) + $qb->select($select) ->join('activity.person', 'person') - ->join('actperson.center', 'actcenter') - ->where($qb->expr()->in('actcenter', ':centers')) - ->setParameter(':centers', $centers); + ->join('person.centerHistory', 'centerHistory'); + + $qb + ->where( + $qb->expr()->andX( + $qb->expr()->lte('centerHistory.startDate', 'activity.date'), + $qb->expr()->orX( + $qb->expr()->isNull('centerHistory.endDate'), + $qb->expr()->gt('centerHistory.endDate', 'activity.date') + ) + ) + ) + ->andWhere($qb->expr()->in('centerHistory.center', ':centers')) + ->setParameter('centers', $centers); + + return $qb; } public function requiredRole(): string diff --git a/src/Bundle/ChillPersonBundle/Export/Export/CountAccompanyingCourse.php b/src/Bundle/ChillPersonBundle/Export/Export/CountAccompanyingCourse.php index 11a10436e..02682c3a9 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/CountAccompanyingCourse.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/CountAccompanyingCourse.php @@ -15,6 +15,8 @@ use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\GroupedExportInterface; use Chill\PersonBundle\Entity\AccompanyingPeriod; +use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation; +use Chill\PersonBundle\Entity\Person\PersonCenterHistory; use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter; use Doctrine\ORM\EntityManagerInterface; @@ -90,9 +92,25 @@ class CountAccompanyingCourse implements ExportInterface, GroupedExportInterface public function initiateQuery(array $requiredModifiers, array $acl, array $data = []): QueryBuilder { + $centers = array_map(static function ($el) { + return $el['center']; + }, $acl); + $qb = $this->repository->createQueryBuilder('acp'); - $qb->select('COUNT(acp.id) AS export_result'); + $qb + ->andWhere( + $qb->expr()->exists( + 'SELECT 1 FROM '.AccompanyingPeriodParticipation::class.' acl_count_part + JOIN '.PersonCenterHistory::class.' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person) + WHERE acl_count_part.accompanyingPeriod = acp.id AND acl_count_person_history.center IN (:authorized_centers) + ' + ) + ) + ->setParameter('authorized_centers', $centers) + ; + + $qb->select('COUNT(DISTINCT acp.id) AS export_result'); return $qb; } diff --git a/src/Bundle/ChillPersonBundle/Export/Export/CountEvaluation.php b/src/Bundle/ChillPersonBundle/Export/Export/CountEvaluation.php index 634b351cf..7ff5349f6 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/CountEvaluation.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/CountEvaluation.php @@ -15,6 +15,8 @@ use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\GroupedExportInterface; use Chill\PersonBundle\Entity\AccompanyingPeriod; +use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation; +use Chill\PersonBundle\Entity\Person\PersonCenterHistory; use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter; use Doctrine\ORM\EntityManagerInterface; @@ -88,6 +90,10 @@ class CountEvaluation implements ExportInterface, GroupedExportInterface public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) { + $centers = array_map(static function ($el) { + return $el['center']; + }, $acl); + $qb = $this->repository->createQueryBuilder('acp'); if (!in_array('acpw', $qb->getAllAliases(), true)) { @@ -98,6 +104,18 @@ class CountEvaluation implements ExportInterface, GroupedExportInterface $qb->join('acpw.accompanyingPeriodWorkEvaluations', 'workeval'); } + $qb + ->andWhere( + $qb->expr()->exists( + 'SELECT 1 FROM '.AccompanyingPeriodParticipation::class.' acl_count_part + JOIN '.PersonCenterHistory::class.' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person) + WHERE acl_count_part.accompanyingPeriod = acp.id AND acl_count_person_history.center IN (:authorized_centers) + ' + ) + ) + ->setParameter('authorized_centers', $centers) + ; + $qb->select('COUNT(DISTINCT workeval.id) AS export_result'); return $qb; diff --git a/src/Bundle/ChillPersonBundle/Export/Export/CountHousehold.php b/src/Bundle/ChillPersonBundle/Export/Export/CountHousehold.php index 106084e4f..e1c7adf88 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/CountHousehold.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/CountHousehold.php @@ -15,6 +15,9 @@ use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\GroupedExportInterface; use Chill\PersonBundle\Entity\AccompanyingPeriod; +use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation; +use Chill\PersonBundle\Entity\Household\HouseholdMember; +use Chill\PersonBundle\Entity\Person\PersonCenterHistory; use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Security\Authorization\HouseholdVoter; use Doctrine\ORM\EntityManagerInterface; @@ -88,23 +91,29 @@ class CountHousehold implements ExportInterface, GroupedExportInterface public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) { - $qb = $this->repository->createQueryBuilder('acp'); + $centers = array_map(static function ($el) { + return $el['center']; + }, $acl); - if (!in_array('acppart', $qb->getAllAliases(), true)) { - $qb->join('acp.participations', 'acppart'); - } + $qb = $this->repository + ->createQueryBuilder('acp') + ->join('acp.participations', 'acppart') + // little optimization: we remove joins and make a direct join between participations and household members + ->join(HouseholdMember::class, 'member', Query\Expr\Join::WITH, 'IDENTITY(acppart.person) = IDENTITY(member.person)') + ->join('member.household', 'household') + ; - if (!in_array('partperson', $qb->getAllAliases(), true)) { - $qb->join('acppart.person', 'partperson'); - } - - if (!in_array('member', $qb->getAllAliases(), true)) { - $qb->join('partperson.householdParticipations', 'member'); - } - - if (!in_array('household', $qb->getAllAliases(), true)) { - $qb->join('member.household', 'household'); - } + $qb + ->andWhere( + $qb->expr()->exists( + 'SELECT 1 FROM '.AccompanyingPeriodParticipation::class.' acl_count_part + JOIN '.PersonCenterHistory::class.' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person) + WHERE acl_count_part.accompanyingPeriod = acp.id AND acl_count_person_history.center IN (:authorized_centers) + ' + ) + ) + ->setParameter('authorized_centers', $centers) + ; $qb->select('COUNT(DISTINCT household.id) AS export_result'); diff --git a/src/Bundle/ChillPersonBundle/Export/Export/CountPerson.php b/src/Bundle/ChillPersonBundle/Export/Export/CountPerson.php index 18cefc6bb..3b2e47bdd 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/CountPerson.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/CountPerson.php @@ -14,9 +14,11 @@ namespace Chill\PersonBundle\Export\Export; use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\GroupedExportInterface; +use Chill\PersonBundle\Entity\Person\PersonCenterHistory; use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Repository\PersonRepository; use Chill\PersonBundle\Security\Authorization\PersonVoter; +use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Query; use Doctrine\ORM\QueryBuilder; use LogicException; @@ -100,9 +102,12 @@ class CountPerson implements ExportInterface, GroupedExportInterface $qb = $this->personRepository->createQueryBuilder('person'); $qb->select('COUNT(person.id) AS export_result') - ->join('person.center', 'center') - ->andWhere('center IN (:authorized_centers)') - ->setParameter('authorized_centers', $centers); + ->andWhere( + $qb->expr()->exists( + 'SELECT 1 FROM '.PersonCenterHistory::class.' pch WHERE pch.person = person.id AND pch.center IN (:authorized_centers)' + ) + ) + ->setParameter('authorized_centers', $centers); return $qb; } diff --git a/src/Bundle/ChillPersonBundle/Export/Export/CountPersonWithAccompanyingCourse.php b/src/Bundle/ChillPersonBundle/Export/Export/CountPersonWithAccompanyingCourse.php index 057611032..fa28e2abe 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/CountPersonWithAccompanyingCourse.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/CountPersonWithAccompanyingCourse.php @@ -15,6 +15,7 @@ use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\GroupedExportInterface; use Chill\PersonBundle\Entity\AccompanyingPeriod; +use Chill\PersonBundle\Entity\Person\PersonCenterHistory; use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter; use Doctrine\ORM\EntityManagerInterface; @@ -88,6 +89,10 @@ class CountPersonWithAccompanyingCourse implements ExportInterface, GroupedExpor public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) { + $centers = array_map(static function ($el) { + return $el['center']; + }, $acl); + $qb = $this->repository->createQueryBuilder('acp'); if (!in_array('acppart', $qb->getAllAliases(), true)) { @@ -98,6 +103,12 @@ class CountPersonWithAccompanyingCourse implements ExportInterface, GroupedExpor $qb->join('acppart.person', 'person'); } + $qb->andWhere( + $qb->expr()->exists( + 'SELECT 1 FROM '.PersonCenterHistory::class.' pch WHERE pch.person = person.id AND pch.center IN (:authorized_centers)' + ) + )->setParameter('authorized_centers', $centers); + $qb->select('COUNT(DISTINCT person.id) AS export_result'); return $qb; diff --git a/src/Bundle/ChillPersonBundle/Export/Export/CountSocialWorkActions.php b/src/Bundle/ChillPersonBundle/Export/Export/CountSocialWorkActions.php index 2ad6e9f1b..4ba071d3b 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/CountSocialWorkActions.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/CountSocialWorkActions.php @@ -15,6 +15,8 @@ use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\GroupedExportInterface; use Chill\PersonBundle\Entity\AccompanyingPeriod; +use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation; +use Chill\PersonBundle\Entity\Person\PersonCenterHistory; use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter; use Doctrine\ORM\EntityManagerInterface; @@ -90,13 +92,26 @@ class CountSocialWorkActions implements ExportInterface, GroupedExportInterface public function initiateQuery(array $requiredModifiers, array $acl, array $data = []): QueryBuilder { - $qb = $this->repository->createQueryBuilder('acp'); + $centers = array_map(static function ($el) { + return $el['center']; + }, $acl); - if (!in_array('acpw', $qb->getAllAliases(), true)) { - $qb->join('acp.works', 'acpw'); - } + $qb = $this->repository->createQueryBuilder('acp') + ->join('acp.works', 'acpw'); - $qb->select('COUNT(acpw.id) as export_result'); + $qb + ->andWhere( + $qb->expr()->exists( + 'SELECT 1 FROM '.AccompanyingPeriodParticipation::class.' acl_count_part + JOIN '.PersonCenterHistory::class.' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person) + WHERE acl_count_part.accompanyingPeriod = acp.id AND acl_count_person_history.center IN (:authorized_centers) + ' + ) + ) + ->setParameter('authorized_centers', $centers) + ; + + $qb->select('COUNT(DISTINCT acpw.id) as export_result'); return $qb; } diff --git a/src/Bundle/ChillPersonBundle/Export/Export/StatAccompanyingCourseDuration.php b/src/Bundle/ChillPersonBundle/Export/Export/StatAccompanyingCourseDuration.php index b805005be..3b2036797 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/StatAccompanyingCourseDuration.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/StatAccompanyingCourseDuration.php @@ -16,6 +16,8 @@ use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\GroupedExportInterface; use Chill\MainBundle\Form\Type\ChillDateType; use Chill\PersonBundle\Entity\AccompanyingPeriod; +use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation; +use Chill\PersonBundle\Entity\Person\PersonCenterHistory; use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter; use DateTime; @@ -94,15 +96,27 @@ class StatAccompanyingCourseDuration implements ExportInterface, GroupedExportIn public function initiateQuery(array $requiredModifiers, array $acl, array $data = []): QueryBuilder { + $centers = array_map(static function ($el) { + return $el['center']; + }, $acl); + $qb = $this->repository->createQueryBuilder('acp'); + $qb + ->andWhere( + $qb->expr()->exists( + 'SELECT 1 FROM '.AccompanyingPeriodParticipation::class.' acl_count_part + JOIN '.PersonCenterHistory::class.' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person) + WHERE acl_count_part.accompanyingPeriod = acp.id AND acl_count_person_history.center IN (:authorized_centers) + ' + ) + ) + ->setParameter('authorized_centers', $centers) + ; + $qb ->select('AVG( - ( CASE - WHEN acp.closingDate IS NOT NULL - THEN acp.closingDate - ELSE :force_closingDate - END ) - acp.openingDate + COALESCE(acp.closingDate, :force_closingDate) - acp.openingDate ) AS export_result') ->setParameter('force_closingDate', $data['closingdate']);