diff --git a/.changes/unreleased/Feature-20231011-155115.yaml b/.changes/unreleased/Feature-20231011-155115.yaml new file mode 100644 index 000000000..77aac142b --- /dev/null +++ b/.changes/unreleased/Feature-20231011-155115.yaml @@ -0,0 +1,5 @@ +kind: Feature +body: '[export] add an aggregator for activities: group by job scope aggregator' +time: 2023-10-11T15:51:15.022779832+02:00 +custom: + Issue: "" diff --git a/exports_alias_conventions.csv b/exports_alias_conventions.csv deleted file mode 100644 index ab32cda8e..000000000 --- a/exports_alias_conventions.csv +++ /dev/null @@ -1,63 +0,0 @@ -Entity,Join,Attribute,Alias -AccompanyingPeriod::class,,,acp -,AccompanyingPeriodWork::class,acp.works,acpw -,AccompanyingPeriodParticipation::class,acp.participations,acppart -,Location::class,acp.administrativeLocation,acploc -,ClosingMotive::class,acp.closingMotive,acpmotive -,UserJob::class,acp.job,acpjob -,Origin::class,acp.origin,acporigin -,Scope::class,acp.scopes,acpscope -,SocialIssue::class,acp.socialIssues,acpsocialissue -,User::class,acp.user,acpuser -AccompanyingPeriodWork::class,,,acpw -,AccompanyingPeriodWorkEvaluation::class,acpw.accompanyingPeriodWorkEvaluations,workeval -,User::class,acpw.referrers,acpwuser -,SocialAction::class,acpw.socialAction,acpwsocialaction -,Goal::class,acpw.goals,goal -,Result::class,acpw.results,result -AccompanyingPeriodParticipation::class,,,acppart -,Person::class,acppart.person,partperson -AccompanyingPeriodWorkEvaluation::class,,,workeval -,Evaluation::class,workeval.evaluation,eval -Goal::class,,,goal -,Result::class,goal.results,goalresult -Person::class,,,person -,Center::class,person.center,center -,HouseholdMember::class,partperson.householdParticipations,householdmember -,MaritalStatus::class,person.maritalStatus,personmarital -,VendeePerson::class,,vp -,VendeePersonMineur::class,,vpm -ResidentialAddress::class,,,resaddr -,ThirdParty::class,resaddr.hostThirdParty,tparty -ThirdParty::class,,,tparty -,ThirdPartyCategory::class,tparty.categories,tpartycat -HouseholdMember::class,,,householdmember -,Household::class,householdmember.household,household -,Person::class,householdmember.person,memberperson -,,memberperson.center,membercenter -Household::class,,,household -,HouseholdComposition::class,household.compositions,composition -Activity::class,,,activity -,Person::class,activity.person,actperson -,AccompanyingPeriod::class,activity.accompanyingPeriod,acp -,Person::class,activity_person_having_activity.person,person_person_having_activity -,ActivityReason::class,activity_person_having_activity.reasons,reasons_person_having_activity -,ActivityType::class,activity.activityType,acttype -,Location::class,activity.location,actloc -,SocialAction::class,activity.socialActions,actsocialaction -,SocialIssue::class,activity.socialIssues,actsocialssue -,ThirdParty::class,activity.thirdParties,acttparty -,User::class,activity.user,actuser -,User::class,activity.users,actusers -,ActivityReason::class,activity.reasons,actreasons -,Center::class,actperson.center,actcenter -ActivityReason::class,,,actreasons -,ActivityReasonCategory::class,actreason.category,actreasoncat -Calendar::class,,,cal -,CancelReason::class,cal.cancelReason,calcancel -,Location::class,cal.location,calloc -,User::class,cal.user,caluser -VendeePerson::class,,,vp -,SituationProfessionelle::class,vp.situationProfessionelle,vpprof -,StatutLogement::class,vp.statutLogement,vplog -,TempsDeTravail::class,vp.tempsDeTravail,vptt diff --git a/exports_alias_conventions.md b/exports_alias_conventions.md index 64df91030..eb0545702 100644 --- a/exports_alias_conventions.md +++ b/exports_alias_conventions.md @@ -21,7 +21,6 @@ These are alias conventions : | | AccompanyingPeriodInfo::class | not existing (using custom WITH clause) | acpinfo | | AccompanyingPeriodWork::class | | | acpw | | | AccompanyingPeriodWorkEvaluation::class | acpw.accompanyingPeriodWorkEvaluations | workeval | -| | User::class | acpw.referrers | acpwuser | | | SocialAction::class | acpw.socialAction | acpwsocialaction | | | Goal::class | acpw.goals | goal | | | Result::class | acpw.results | result | diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/CreatorScopeAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/CreatorScopeAggregator.php index 2c053b035..e2357bd7d 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/CreatorScopeAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/CreatorScopeAggregator.php @@ -93,7 +93,7 @@ class CreatorScopeAggregator implements AggregatorInterface public function getQueryKeys($data): array { - return ['_select']; + return [self::PREFIX . '_select']; } public function getTitle(): string diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/JobScopeAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/JobScopeAggregator.php new file mode 100644 index 000000000..251f67b36 --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/JobScopeAggregator.php @@ -0,0 +1,104 @@ +leftJoin("activity.createdBy", "{$p}_user") + ->leftJoin( + UserJobHistory::class, + "{$p}_history", + Join::WITH, + $qb->expr()->eq("{$p}_history.user", "{$p}_user") + ) + // job_at based on activity.date + ->andWhere( + $qb->expr()->andX( + $qb->expr()->lte("{$p}_history.startDate", "activity.date"), + $qb->expr()->orX( + $qb->expr()->isNull("{$p}_history.endDate"), + $qb->expr()->gt("{$p}_history.endDate", "activity.date") + ) + ) + ) + ->addSelect("IDENTITY({$p}_history.job) AS {$p}_select") + ->addGroupBy("{$p}_select"); + } + + public function applyOn(): string + { + return Declarations::ACTIVITY; + } + + public function buildForm(FormBuilderInterface $builder) {} + + public function getFormDefaultData(): array + { + return []; + } + + public function getLabels($key, array $values, $data) + { + return function ($value): string { + if ('_header' === $value) { + return 'Scope'; + } + + if (null === $value || '' === $value) { + return ''; + } + + $s = $this->scopeRepository->find($value); + + return $this->translatableStringHelper->localize( + $s->getName() + ); + }; + } + + public function getQueryKeys($data): array + { + return [self::PREFIX . '_select']; + } + + public function getTitle(): string + { + return 'export.aggregator.activity.by_creator_job.Group activity by creator job'; + } +} diff --git a/src/Bundle/ChillActivityBundle/Export/Export/ListActivityHelper.php b/src/Bundle/ChillActivityBundle/Export/Export/ListActivityHelper.php index 84d94c000..ea43d387d 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/ListActivityHelper.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/ListActivityHelper.php @@ -57,7 +57,7 @@ class ListActivityHelper ->addSelect('AGGREGATE(actPerson.id) AS personsNames') ->leftJoin('activity.users', 'users_u') ->addSelect('AGGREGATE(users_u.id) AS usersIds') - ->addSelect('AGGREGATE(users_u.id) AS usersNames') + ->addSelect('AGGREGATE(JSON_BUILD_OBJECT(\'uid\', users_u.id, \'d\', activity.date)) AS usersNames') ->leftJoin('activity.thirdParties', 'thirdparty') ->addSelect('AGGREGATE(thirdparty.id) AS thirdPartiesIds') ->addSelect('AGGREGATE(thirdparty.id) AS thirdPartiesNames') @@ -68,9 +68,9 @@ class ListActivityHelper ->leftJoin('activity.location', 'location') ->addSelect('location.name AS locationName') ->addSelect('activity.sentReceived') - ->addSelect('IDENTITY(activity.createdBy) AS createdBy') + ->addSelect('JSON_BUILD_OBJECT(\'uid\', IDENTITY(activity.createdBy), \'d\', activity.createdAt) AS createdBy') ->addSelect('activity.createdAt') - ->addSelect('IDENTITY(activity.updatedBy) AS updatedBy') + ->addSelect('JSON_BUILD_OBJECT(\'uid\', IDENTITY(activity.updatedBy), \'d\', activity.updatedAt) AS updatedBy') ->addSelect('activity.updatedAt') ->addGroupBy('activity.id') ->addGroupBy('location.id'); diff --git a/src/Bundle/ChillActivityBundle/Export/Filter/UserScopeFilter.php b/src/Bundle/ChillActivityBundle/Export/Filter/UserScopeFilter.php index ec87ce954..d33ae1a10 100644 --- a/src/Bundle/ChillActivityBundle/Export/Filter/UserScopeFilter.php +++ b/src/Bundle/ChillActivityBundle/Export/Filter/UserScopeFilter.php @@ -59,9 +59,10 @@ class UserScopeFilter implements FilterInterface ->andWhere( $qb->expr()->in("{$p}_history.scope", ":{$p}_scopes") ) - ->setParameters([ - "{$p}_scopes" => $data["scopes"], - ]); + ->setParameter( + "{$p}_scopes", + $data["scopes"], + ); } public function applyOn(): string diff --git a/src/Bundle/ChillActivityBundle/Export/Filter/UsersJobFilter.php b/src/Bundle/ChillActivityBundle/Export/Filter/UsersJobFilter.php index b49397521..d787f30ea 100644 --- a/src/Bundle/ChillActivityBundle/Export/Filter/UsersJobFilter.php +++ b/src/Bundle/ChillActivityBundle/Export/Filter/UsersJobFilter.php @@ -51,9 +51,10 @@ class UsersJobFilter implements FilterInterface . "AND {$p}_history.job IN ( :{$p}_jobs )" ) ) - ->setParameters([ - "{$p}_jobs" => $data["jobs"], - ]); + ->setParameter( + "{$p}_jobs", + $data["jobs"] + ); } public function applyOn() diff --git a/src/Bundle/ChillActivityBundle/Export/Filter/UsersScopeFilter.php b/src/Bundle/ChillActivityBundle/Export/Filter/UsersScopeFilter.php index 3391f92cf..a1bcb958b 100644 --- a/src/Bundle/ChillActivityBundle/Export/Filter/UsersScopeFilter.php +++ b/src/Bundle/ChillActivityBundle/Export/Filter/UsersScopeFilter.php @@ -53,9 +53,10 @@ class UsersScopeFilter implements FilterInterface . "AND {$p}_history.scope IN ( :{$p}_scopes )" ) ) - ->setParameters([ - "{$p}_scopes" => $data["scopes"], - ]); + ->setParameter( + "{$p}_scopes", + $data["scopes"] + ); } public function applyOn(): string diff --git a/src/Bundle/ChillActivityBundle/Repository/ActivityACLAwareRepository.php b/src/Bundle/ChillActivityBundle/Repository/ActivityACLAwareRepository.php index f9db5c158..d61e0999e 100644 --- a/src/Bundle/ChillActivityBundle/Repository/ActivityACLAwareRepository.php +++ b/src/Bundle/ChillActivityBundle/Repository/ActivityACLAwareRepository.php @@ -122,9 +122,30 @@ final readonly class ActivityACLAwareRepository implements ActivityACLAwareRepos ->leftJoin('a.user', 'activity_u') ->andWhere( $qb->expr()->orX( - 'creator.userJob IN (:jobs)', - 'activity_u.userJob IN (:jobs)', - 'EXISTS (SELECT 1 FROM ' . User::class . ' activity_user WHERE activity_user MEMBER OF a.users AND activity_user.userJob IN (:jobs))' + $qb->expr()->exists( + sprintf( + "SELECT 1 FROM %s ujh_creator WHERE ujh_creator.user = a.createdBy " + . "AND ujh_creator.job IN (:jobs) AND a.createdAt > ujh_creator.startDate " + . "AND (ujh_creator.endDate IS NULL or ujh_creator.endDate > a.date)", + User\UserJobHistory::class + ) + ), + $qb->expr()->exists( + sprintf( + "SELECT 1 FROM %s ujh_u WHERE ujh_u.user = a.user " + . "AND ujh_u.job IN (:jobs) AND a.createdAt > ujh_u.startDate " + . "AND (ujh_u.endDate IS NULL or ujh_u.endDate > a.date)", + User\UserJobHistory::class + ) + ), + $qb->expr()->exists( + sprintf( + "SELECT 1 FROM %s ujh_users WHERE ujh_users.user MEMBER OF a.users " + . "AND ujh_users.job IN (:jobs) AND a.createdAt > ujh_users.startDate " + . "AND (ujh_users.endDate IS NULL or ujh_users.endDate > a.date)", + User\UserJobHistory::class + ) + ), ) ) ->setParameter('jobs', $jobs); @@ -172,16 +193,17 @@ final readonly class ActivityACLAwareRepository implements ActivityACLAwareRepos return $qb->getQuery()->getResult(); } - public function findUserJobByAssociated(Person|AccompanyingPeriod $associated): array + public function findUserJobByAssociated(AccompanyingPeriod|Person $associated): array { $in = $this->em->createQueryBuilder(); - $in->select('IDENTITY(u.userJob)') - ->from(User::class, 'u') + $in->select('IDENTITY(u.job)') + ->distinct() + ->from(User\UserJobHistory::class, 'u') ->join( Activity::class, 'a', Join::WITH, - 'a.createdBy = u OR a.user = u OR u MEMBER OF a.users' + 'a.createdBy = u.user OR a.user = u.user OR u.user MEMBER OF a.users AND a.date >= u.startDate ANd (u.endDate IS NULL or u.endDate > a.date)' ); if ($associated instanceof Person) { diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/JobScopeAggregatorTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/JobScopeAggregatorTest.php new file mode 100644 index 000000000..3771e6e32 --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/JobScopeAggregatorTest.php @@ -0,0 +1,60 @@ +aggregator = self::$container->get(JobScopeAggregator::class); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getQueryBuilders(): array + { + self::bootKernel(); + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(activity.id)') + ->from(Activity::class, 'activity') + ->leftJoin('activity.accompanyingPeriod', 'acp') + ->leftJoin('activity.user', 'actuser'), + ]; + } +} diff --git a/src/Bundle/ChillActivityBundle/config/services/export.yaml b/src/Bundle/ChillActivityBundle/config/services/export.yaml index 563ea7bf5..09a5227eb 100644 --- a/src/Bundle/ChillActivityBundle/config/services/export.yaml +++ b/src/Bundle/ChillActivityBundle/config/services/export.yaml @@ -177,6 +177,10 @@ services: tags: - { name: chill.export_aggregator, alias: activity_creator_scope_aggregator } + Chill\ActivityBundle\Export\Aggregator\JobScopeAggregator: + tags: + - { name: chill.export_aggregator, alias: activity_creator_job_aggregator } + Chill\ActivityBundle\Export\Aggregator\ActivityUsersAggregator: tags: - { name: chill.export_aggregator, alias: activity_users_aggregator } diff --git a/src/Bundle/ChillActivityBundle/translations/messages.fr.yml b/src/Bundle/ChillActivityBundle/translations/messages.fr.yml index 3496f7a52..8deebbb8c 100644 --- a/src/Bundle/ChillActivityBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillActivityBundle/translations/messages.fr.yml @@ -395,6 +395,9 @@ export: by_creator_scope: Group activity by creator scope: Grouper les échanges par service du créateur de l'échange Calc date: Date de calcul du service du créateur de l'échange + by_creator_job: + Group activity by creator job: Grouper les échanges par service du créateur de l'échange + Calc date: Date de calcul du service du créateur de l'échange generic_doc: filter: diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Export/ListAsideActivity.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Export/ListAsideActivity.php index f01c3efc2..0981c6bf8 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Export/Export/ListAsideActivity.php +++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Export/ListAsideActivity.php @@ -182,7 +182,10 @@ final readonly class ListAsideActivity implements ListInterface, GroupedExportIn { $qb = $this->em->createQueryBuilder() ->from(AsideActivity::class, 'aside') - ->leftJoin('aside.agent', 'agent'); + ->leftJoin('aside.agent', 'agent') + ->leftJoin('agent.scopeHistories', 'scopeHistories') + ->andWhere('scopeHistories.startDate <= aside.date AND (scopeHistories.endDate IS NULL or scopeHistories.endDate > aside.date)') + ; $qb ->addSelect('aside.id AS id') @@ -190,7 +193,7 @@ final readonly class ListAsideActivity implements ListInterface, GroupedExportIn ->addSelect('aside.updatedAt AS updatedAt') ->addSelect('IDENTITY(aside.agent) AS agent_id') ->addSelect('IDENTITY(aside.createdBy) AS creator_id') - ->addSelect('IDENTITY(agent.mainScope) AS main_scope') /// + ->addSelect('IDENTITY(scopeHistories.scope) AS main_scope') ->addSelect('IDENTITY(agent.mainCenter) AS main_center') ->addSelect('IDENTITY(aside.type) AS aside_activity_type') ->addSelect('aside.date') diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByUserJobFilter.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByUserJobFilter.php index d76d6d126..69aaa376f 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByUserJobFilter.php +++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByUserJobFilter.php @@ -51,9 +51,10 @@ class ByUserJobFilter implements FilterInterface . "AND {$p}_history.job IN ( :{$p}_jobs )" ) ) - ->setParameters([ - "{$p}_jobs" => $data["jobs"], - ]); + ->setParameter( + "{$p}_jobs", + $data["jobs"], + ); } public function applyOn(): string diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByUserScopeFilter.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByUserScopeFilter.php index 7df450bde..bcaabdcea 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByUserScopeFilter.php +++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByUserScopeFilter.php @@ -53,9 +53,10 @@ class ByUserScopeFilter implements FilterInterface . "AND {$p}_history.scope IN ( :{$p}_scopes )" ) ) - ->setParameters([ - "{$p}_scopes" => $data["scopes"], - ]); + ->setParameter( + "{$p}_scopes", + $data["scopes"], + ); } public function applyOn(): string diff --git a/src/Bundle/ChillAsideActivityBundle/src/Tests/Export/Export/ListAsideActivityTest.php b/src/Bundle/ChillAsideActivityBundle/src/Tests/Export/Export/ListAsideActivityTest.php new file mode 100644 index 000000000..6bd29ef1d --- /dev/null +++ b/src/Bundle/ChillAsideActivityBundle/src/Tests/Export/Export/ListAsideActivityTest.php @@ -0,0 +1,43 @@ +listAsideActivity = self::$container->get(ListAsideActivity::class); + } + + public function testExecuteQuery(): void + { + $qb = $this->listAsideActivity->initiateQuery([], [], []) + ->setMaxResults(1); + + $results = $qb->getQuery()->getResult(AbstractQuery::HYDRATE_ARRAY); + + self::assertIsArray($results, "smoke test: test that the result is an array"); + } +} diff --git a/src/Bundle/ChillBudgetBundle/Repository/ChargeRepository.php b/src/Bundle/ChillBudgetBundle/Repository/ChargeRepository.php index 9b89010dc..475657f68 100644 --- a/src/Bundle/ChillBudgetBundle/Repository/ChargeRepository.php +++ b/src/Bundle/ChillBudgetBundle/Repository/ChargeRepository.php @@ -34,7 +34,7 @@ class ChargeRepository extends ServiceEntityRepository /** * @return Charge[] */ - public function findAllByEntity(Person|Household $entity): array + public function findAllByEntity(Household|Person $entity): array { $qb = $this->createQueryBuilder('c'); diff --git a/src/Bundle/ChillBudgetBundle/Repository/ResourceRepository.php b/src/Bundle/ChillBudgetBundle/Repository/ResourceRepository.php index 836f13e4b..6ecf13d28 100644 --- a/src/Bundle/ChillBudgetBundle/Repository/ResourceRepository.php +++ b/src/Bundle/ChillBudgetBundle/Repository/ResourceRepository.php @@ -35,7 +35,7 @@ class ResourceRepository extends ServiceEntityRepository /** * @return Resource[] */ - public function findAllByEntity(Person|Household $entity): array + public function findAllByEntity(Household|Person $entity): array { $qb = $this->createQueryBuilder('r'); @@ -47,7 +47,7 @@ class ResourceRepository extends ServiceEntityRepository return $qb->getQuery()->getResult(); } - public function findByEntityAndDate(Person|Household $entity, DateTime $date, $sort = null) + public function findByEntityAndDate(Household|Person $entity, DateTime $date, $sort = null) { $qb = $this->createQueryBuilder('c'); diff --git a/src/Bundle/ChillCalendarBundle/Export/Filter/JobFilter.php b/src/Bundle/ChillCalendarBundle/Export/Filter/JobFilter.php index 1b666019b..93def5631 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Filter/JobFilter.php +++ b/src/Bundle/ChillCalendarBundle/Export/Filter/JobFilter.php @@ -22,13 +22,12 @@ use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Contracts\Translation\TranslatorInterface; -class JobFilter implements FilterInterface +final readonly class JobFilter implements FilterInterface { private const PREFIX = 'cal_filter_job'; public function __construct( - protected TranslatorInterface $translator, - private readonly TranslatableStringHelper $translatableStringHelper + private TranslatableStringHelper $translatableStringHelper ) {} public function addRole(): ?string @@ -59,9 +58,10 @@ class JobFilter implements FilterInterface ) ) ->andWhere($qb->expr()->in("{$p}_history.job", ":{$p}_job")) - ->setParameters([ - "{$p}_job" => $data["job"], - ]); + ->setParameter( + "{$p}_job", + $data["job"] + ); } diff --git a/src/Bundle/ChillCalendarBundle/Export/Filter/ScopeFilter.php b/src/Bundle/ChillCalendarBundle/Export/Filter/ScopeFilter.php index 61ec8d50d..e04bd922b 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Filter/ScopeFilter.php +++ b/src/Bundle/ChillCalendarBundle/Export/Filter/ScopeFilter.php @@ -59,9 +59,10 @@ class ScopeFilter implements FilterInterface ) ) ->andWhere($qb->expr()->in("{$p}_history.scope", ":{$p}_scope")) - ->setParameters([ - "{$p}_scope" => $data["scope"], - ]); + ->setParameter( + "{$p}_scope", + $data["scope"] + ); } public function applyOn(): string diff --git a/src/Bundle/ChillCalendarBundle/RemoteCalendar/Connector/MSGraph/MSUserAbsenceReader.php b/src/Bundle/ChillCalendarBundle/RemoteCalendar/Connector/MSGraph/MSUserAbsenceReader.php index a81ef34c3..740666cd6 100644 --- a/src/Bundle/ChillCalendarBundle/RemoteCalendar/Connector/MSGraph/MSUserAbsenceReader.php +++ b/src/Bundle/ChillCalendarBundle/RemoteCalendar/Connector/MSGraph/MSUserAbsenceReader.php @@ -33,7 +33,7 @@ final readonly class MSUserAbsenceReader implements MSUserAbsenceReaderInterface /** * @throw UserAbsenceSyncException when the data cannot be reached or is not valid from microsoft */ - public function isUserAbsent(User $user): bool|null + public function isUserAbsent(User $user): null|bool { $id = $this->mapCalendarToUser->getUserId($user); diff --git a/src/Bundle/ChillCalendarBundle/RemoteCalendar/Connector/MSGraph/MSUserAbsenceReaderInterface.php b/src/Bundle/ChillCalendarBundle/RemoteCalendar/Connector/MSGraph/MSUserAbsenceReaderInterface.php index a918bb7ea..f67562e27 100644 --- a/src/Bundle/ChillCalendarBundle/RemoteCalendar/Connector/MSGraph/MSUserAbsenceReaderInterface.php +++ b/src/Bundle/ChillCalendarBundle/RemoteCalendar/Connector/MSGraph/MSUserAbsenceReaderInterface.php @@ -18,5 +18,5 @@ interface MSUserAbsenceReaderInterface /** * @throw UserAbsenceSyncException when the data cannot be reached or is not valid from microsoft */ - public function isUserAbsent(User $user): bool|null; + public function isUserAbsent(User $user): null|bool; } diff --git a/src/Bundle/ChillCalendarBundle/Tests/Export/Filter/JobFilterTest.php b/src/Bundle/ChillCalendarBundle/Tests/Export/Filter/JobFilterTest.php index a395062db..d571508c8 100644 --- a/src/Bundle/ChillCalendarBundle/Tests/Export/Filter/JobFilterTest.php +++ b/src/Bundle/ChillCalendarBundle/Tests/Export/Filter/JobFilterTest.php @@ -22,6 +22,7 @@ use Chill\CalendarBundle\Entity\Calendar; use Chill\CalendarBundle\Export\Filter\JobFilter; use Chill\MainBundle\Entity\UserJob; use Chill\MainBundle\Test\Export\AbstractFilterTest; +use Doctrine\Common\Collections\ArrayCollection; use Doctrine\ORM\EntityManagerInterface; /** @@ -62,15 +63,11 @@ final class JobFilterTest extends AbstractFilterTest ->setMaxResults(1) ->getResult(); - $data = []; - - foreach ($array as $a) { - $data[] = [ - 'job' => $a, - ]; - } - - return $data; + return [ + [ + 'job' => new ArrayCollection($array) + ] + ]; } public function getQueryBuilders(): array diff --git a/src/Bundle/ChillCalendarBundle/Tests/Export/Filter/ScopeFilterTest.php b/src/Bundle/ChillCalendarBundle/Tests/Export/Filter/ScopeFilterTest.php index eef7d1362..06d36846b 100644 --- a/src/Bundle/ChillCalendarBundle/Tests/Export/Filter/ScopeFilterTest.php +++ b/src/Bundle/ChillCalendarBundle/Tests/Export/Filter/ScopeFilterTest.php @@ -22,6 +22,7 @@ use Chill\CalendarBundle\Entity\Calendar; use Chill\CalendarBundle\Export\Filter\ScopeFilter; use Chill\MainBundle\Entity\Scope; use Chill\MainBundle\Test\Export\AbstractFilterTest; +use Doctrine\Common\Collections\ArrayCollection; use Doctrine\ORM\EntityManagerInterface; /** @@ -62,15 +63,11 @@ final class ScopeFilterTest extends AbstractFilterTest ->setMaxResults(1) ->getResult(); - $data = []; - - foreach ($array as $a) { - $data[] = [ - 'scope' => $a, - ]; - } - - return $data; + return [ + [ + 'scope' => new ArrayCollection($array) + ] + ]; } public function getQueryBuilders(): array diff --git a/src/Bundle/ChillCustomFieldsBundle/CustomFields/CustomFieldChoice.php b/src/Bundle/ChillCustomFieldsBundle/CustomFields/CustomFieldChoice.php index 27a4f930f..2ee0a9c48 100644 --- a/src/Bundle/ChillCustomFieldsBundle/CustomFields/CustomFieldChoice.php +++ b/src/Bundle/ChillCustomFieldsBundle/CustomFields/CustomFieldChoice.php @@ -366,7 +366,7 @@ class CustomFieldChoice extends AbstractCustomField * If the value had an 'allow_other' = true option, the returned value * **is not** the content of the _other field, but the `_other` string. */ - private function guessValue(array|string|null $value) + private function guessValue(null|array|string $value) { if (null === $value) { return null; diff --git a/src/Bundle/ChillCustomFieldsBundle/Entity/CustomFieldsGroup.php b/src/Bundle/ChillCustomFieldsBundle/Entity/CustomFieldsGroup.php index 7ede1a08a..41fc535fe 100644 --- a/src/Bundle/ChillCustomFieldsBundle/Entity/CustomFieldsGroup.php +++ b/src/Bundle/ChillCustomFieldsBundle/Entity/CustomFieldsGroup.php @@ -138,7 +138,7 @@ class CustomFieldsGroup /** * Get name. */ - public function getName(?string $language = null): string|array + public function getName(?string $language = null): array|string { //TODO set this in a service, PLUS twig function if (null !== $language) { diff --git a/src/Bundle/ChillEventBundle/Controller/ParticipationController.php b/src/Bundle/ChillEventBundle/Controller/ParticipationController.php index 126178a9e..54dc763ef 100644 --- a/src/Bundle/ChillEventBundle/Controller/ParticipationController.php +++ b/src/Bundle/ChillEventBundle/Controller/ParticipationController.php @@ -44,7 +44,7 @@ class ParticipationController extends AbstractController * @return Response|\Symfony\Component\HttpFoundation\RedirectResponse * @\Symfony\Component\Routing\Annotation\Route(path="/{_locale}/event/participation/create", name="chill_event_participation_create") */ - public function createAction(Request $request): \Symfony\Component\HttpFoundation\Response|\Symfony\Component\HttpFoundation\RedirectResponse + public function createAction(Request $request): \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response { // test the request is correct try { @@ -162,7 +162,7 @@ class ParticipationController extends AbstractController /** * @return Response|\Symfony\Component\HttpFoundation\RedirectResponse */ - public function createMultiple(Request $request): \Symfony\Component\HttpFoundation\Response|\Symfony\Component\HttpFoundation\RedirectResponse + public function createMultiple(Request $request): \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response { $participations = $this->handleRequest($request, new Participation(), true); @@ -205,7 +205,7 @@ class ParticipationController extends AbstractController /** * @return Response|\Symfony\Component\HttpFoundation\RedirectResponse */ - public function createSingle(Request $request): \Symfony\Component\HttpFoundation\Response|\Symfony\Component\HttpFoundation\RedirectResponse + public function createSingle(Request $request): \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response { $participation = $this->handleRequest($request, new Participation(), false); @@ -249,7 +249,7 @@ class ParticipationController extends AbstractController * @return Response|\Symfony\Component\HttpFoundation\RedirectResponse * @\Symfony\Component\Routing\Annotation\Route(path="/{_locale}/event/participation/{participation_id}/delete", name="chill_event_participation_delete", requirements={"participation_id"="\d+"}, methods={"GET", "DELETE"}) */ - public function deleteAction($participation_id, Request $request): \Symfony\Component\HttpFoundation\Response|\Symfony\Component\HttpFoundation\RedirectResponse + public function deleteAction($participation_id, Request $request): \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response { $em = $this->getDoctrine()->getManager(); $participation = $em->getRepository(\Chill\EventBundle\Entity\Participation::class)->findOneBy([ @@ -330,7 +330,7 @@ class ParticipationController extends AbstractController * @return Response|\Symfony\Component\HttpFoundation\RedirectResponse * @\Symfony\Component\Routing\Annotation\Route(path="/{_locale}/event/participation/{event_id}/edit_multiple", name="chill_event_participation_edit_multiple") */ - public function editMultipleAction($event_id): \Symfony\Component\HttpFoundation\Response|\Symfony\Component\HttpFoundation\RedirectResponse + public function editMultipleAction($event_id): \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response { $event = $this->getDoctrine()->getRepository(\Chill\EventBundle\Entity\Event::class) ->find($event_id); @@ -388,7 +388,7 @@ class ParticipationController extends AbstractController * @return Response|\Symfony\Component\HttpFoundation\RedirectResponse * @\Symfony\Component\Routing\Annotation\Route(path="/{_locale}/event/participation/new", name="chill_event_participation_new") */ - public function newAction(Request $request): \Symfony\Component\HttpFoundation\Response|\Symfony\Component\HttpFoundation\RedirectResponse + public function newAction(Request $request): \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response { // test the request is correct try { @@ -560,7 +560,7 @@ class ParticipationController extends AbstractController Request $request, Participation $participation, bool $multiple = false - ): \Chill\EventBundle\Entity\Participation|array { + ): array|\Chill\EventBundle\Entity\Participation { $em = $this->getDoctrine()->getManager(); if ($em->contains($participation)) { @@ -637,7 +637,7 @@ class ParticipationController extends AbstractController * * @return Response|\Symfony\Component\HttpFoundation\RedirectResponse */ - protected function newMultiple(Request $request): \Symfony\Component\HttpFoundation\Response|\Symfony\Component\HttpFoundation\RedirectResponse + protected function newMultiple(Request $request): \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response { $participations = $this->handleRequest($request, new Participation(), true); $ignoredParticipations = $newParticipations = []; diff --git a/src/Bundle/ChillEventBundle/Entity/Event.php b/src/Bundle/ChillEventBundle/Entity/Event.php index 17f7899fc..b2e00c24e 100644 --- a/src/Bundle/ChillEventBundle/Entity/Event.php +++ b/src/Bundle/ChillEventBundle/Entity/Event.php @@ -135,7 +135,7 @@ class Event implements HasCenterInterface, HasScopeInterface return $this->id; } - public function getModerator(): User|null + public function getModerator(): null|User { return $this->moderator; } diff --git a/src/Bundle/ChillMainBundle/Command/LoadPostalCodesCommand.php b/src/Bundle/ChillMainBundle/Command/LoadPostalCodesCommand.php index 6b6bfa8e0..c68cf6762 100644 --- a/src/Bundle/ChillMainBundle/Command/LoadPostalCodesCommand.php +++ b/src/Bundle/ChillMainBundle/Command/LoadPostalCodesCommand.php @@ -96,7 +96,7 @@ class LoadPostalCodesCommand extends Command try { $this->addPostalCode($row, $output); ++$num; - } catch (ExistingPostalCodeException|CountryCodeNotFoundException|PostalCodeNotValidException $ex) { + } catch (CountryCodeNotFoundException|ExistingPostalCodeException|PostalCodeNotValidException $ex) { $output->writeln(' on line ' . $line . ' : ' . $ex->getMessage() . ''); } ++$line; diff --git a/src/Bundle/ChillMainBundle/Controller/PasswordController.php b/src/Bundle/ChillMainBundle/Controller/PasswordController.php index 1c1d11f84..dc0856cc0 100644 --- a/src/Bundle/ChillMainBundle/Controller/PasswordController.php +++ b/src/Bundle/ChillMainBundle/Controller/PasswordController.php @@ -99,7 +99,7 @@ class PasswordController extends AbstractController * @return Response|\Symfony\Component\HttpFoundation\RedirectResponse * @\Symfony\Component\Routing\Annotation\Route(path="/public/{_locale}/password/recover", name="password_recover") */ - public function recoverAction(Request $request): \Symfony\Component\HttpFoundation\Response|\Symfony\Component\HttpFoundation\RedirectResponse + public function recoverAction(Request $request): \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response { if (false === $this->isGranted(PasswordRecoverVoter::ASK_TOKEN)) { return new Response($this->translator->trans('You are not allowed ' @@ -168,7 +168,7 @@ class PasswordController extends AbstractController * @return Response|\Symfony\Component\HttpFoundation\RedirectResponse * @\Symfony\Component\Routing\Annotation\Route(path="/public/{_locale}/password/request-recover", name="password_request_recover") */ - public function requestRecoverAction(Request $request): \Symfony\Component\HttpFoundation\Response|\Symfony\Component\HttpFoundation\RedirectResponse + public function requestRecoverAction(Request $request): \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response { if (false === $this->isGranted(PasswordRecoverVoter::ASK_TOKEN)) { return new Response($this->translator->trans('You are not allowed ' diff --git a/src/Bundle/ChillMainBundle/Controller/UserExportController.php b/src/Bundle/ChillMainBundle/Controller/UserExportController.php index 04778dfae..fe318c4ae 100644 --- a/src/Bundle/ChillMainBundle/Controller/UserExportController.php +++ b/src/Bundle/ChillMainBundle/Controller/UserExportController.php @@ -50,7 +50,7 @@ final readonly class UserExportController fn (string $e) => $this->translator->trans('admin.users.export.' . $e), [ 'id', - 'username', + // 'username', 'email', 'enabled', 'civility_id', @@ -59,10 +59,10 @@ final readonly class UserExportController 'label', 'mainCenter_id' , 'mainCenter_name', - 'mainScope_id', /// - 'mainScope_name', /// - 'userJob_id', /// - 'userJob_name', /// + 'mainScope_id', + 'mainScope_name', + 'userJob_id', + 'userJob_name', 'currentLocation_id', 'currentLocation_name', 'mainLocation_id', diff --git a/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php b/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php index 4d81c3980..72848303f 100644 --- a/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php +++ b/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php @@ -33,6 +33,7 @@ use Chill\MainBundle\Doctrine\DQL\Greatest; use Chill\MainBundle\Doctrine\DQL\JsonAggregate; use Chill\MainBundle\Doctrine\DQL\JsonbArrayLength; use Chill\MainBundle\Doctrine\DQL\JsonbExistsInArray; +use Chill\MainBundle\Doctrine\DQL\JsonBuildObject; use Chill\MainBundle\Doctrine\DQL\JsonExtract; use Chill\MainBundle\Doctrine\DQL\Least; use Chill\MainBundle\Doctrine\DQL\OverlapsI; @@ -255,6 +256,7 @@ class ChillMainExtension extends Extension implements 'AGGREGATE' => JsonAggregate::class, 'REPLACE' => Replace::class, 'JSON_EXTRACT' => JsonExtract::class, + 'JSON_BUILD_OBJECT' => JsonBuildObject::class, ], 'numeric_functions' => [ 'JSONB_EXISTS_IN_ARRAY' => JsonbExistsInArray::class, diff --git a/src/Bundle/ChillMainBundle/Doctrine/DQL/Extract.php b/src/Bundle/ChillMainBundle/Doctrine/DQL/Extract.php index a63f04164..6fc763c6d 100644 --- a/src/Bundle/ChillMainBundle/Doctrine/DQL/Extract.php +++ b/src/Bundle/ChillMainBundle/Doctrine/DQL/Extract.php @@ -29,7 +29,7 @@ class Extract extends FunctionNode { private string $field; - private \Doctrine\ORM\Query\AST\Node|string|null $value = null; + private null|\Doctrine\ORM\Query\AST\Node|string $value = null; //private PathExpression $value; //private FunctionNode $value; //private DateDiffFunction $value; diff --git a/src/Bundle/ChillMainBundle/Doctrine/DQL/JsonBuildObject.php b/src/Bundle/ChillMainBundle/Doctrine/DQL/JsonBuildObject.php new file mode 100644 index 000000000..4a8c3485a --- /dev/null +++ b/src/Bundle/ChillMainBundle/Doctrine/DQL/JsonBuildObject.php @@ -0,0 +1,53 @@ + $expr->dispatch($sqlWalker), $this->exprs)) . ')'; + } + + public function parse(Parser $parser) + { + $lexer = $parser->getLexer(); + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->exprs[] = $parser->ArithmeticPrimary(); + + while (Lexer::T_COMMA === $lexer->lookahead['type']) { + $parser->match(Lexer::T_COMMA); + $this->exprs[] = $parser->ArithmeticPrimary(); + } + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} diff --git a/src/Bundle/ChillMainBundle/Doctrine/DQL/JsonExtract.php b/src/Bundle/ChillMainBundle/Doctrine/DQL/JsonExtract.php index 95d851790..7d93071b7 100644 --- a/src/Bundle/ChillMainBundle/Doctrine/DQL/JsonExtract.php +++ b/src/Bundle/ChillMainBundle/Doctrine/DQL/JsonExtract.php @@ -18,7 +18,7 @@ use Doctrine\ORM\Query\SqlWalker; class JsonExtract extends FunctionNode { - private \Doctrine\ORM\Query\AST\Node|string|null $element = null; + private null|\Doctrine\ORM\Query\AST\Node|string $element = null; private ?\Doctrine\ORM\Query\AST\ArithmeticExpression $keyToExtract = null; diff --git a/src/Bundle/ChillMainBundle/Doctrine/DQL/ToChar.php b/src/Bundle/ChillMainBundle/Doctrine/DQL/ToChar.php index ef150867e..a5e73209d 100644 --- a/src/Bundle/ChillMainBundle/Doctrine/DQL/ToChar.php +++ b/src/Bundle/ChillMainBundle/Doctrine/DQL/ToChar.php @@ -23,7 +23,7 @@ class ToChar extends FunctionNode { private ?\Doctrine\ORM\Query\AST\ArithmeticExpression $datetime = null; - private \Doctrine\ORM\Query\AST\Node|string|null $fmt = null; + private null|\Doctrine\ORM\Query\AST\Node|string $fmt = null; public function getSql(SqlWalker $sqlWalker) { diff --git a/src/Bundle/ChillMainBundle/Entity/User.php b/src/Bundle/ChillMainBundle/Entity/User.php index 33c8be166..5df70134b 100644 --- a/src/Bundle/ChillMainBundle/Entity/User.php +++ b/src/Bundle/ChillMainBundle/Entity/User.php @@ -292,9 +292,7 @@ class User implements UserInterface, \Stringable $sortedScopeHistories = $scopeHistories->toArray(); - usort($sortedScopeHistories, function ($a, $b) { - return $a->getStartDate() < $b->getStartDate() ? 1 : -1; - }); + usort($sortedScopeHistories, fn ($a, $b) => $a->getStartDate() < $b->getStartDate() ? 1 : -1); return new ArrayCollection($sortedScopeHistories); } @@ -346,9 +344,7 @@ class User implements UserInterface, \Stringable $sortedJobHistories = $jobHistories->toArray(); - usort($sortedJobHistories, function ($a, $b) { - return $a->getStartDate() < $b->getStartDate() ? 1 : -1; - }); + usort($sortedJobHistories, fn ($a, $b) => $a->getStartDate() < $b->getStartDate() ? 1 : -1); return new ArrayCollection($sortedJobHistories); } diff --git a/src/Bundle/ChillMainBundle/Export/ExportFormHelper.php b/src/Bundle/ChillMainBundle/Export/ExportFormHelper.php index e21ea7f4d..8d9320424 100644 --- a/src/Bundle/ChillMainBundle/Export/ExportFormHelper.php +++ b/src/Bundle/ChillMainBundle/Export/ExportFormHelper.php @@ -28,7 +28,7 @@ final readonly class ExportFormHelper private FormFactoryInterface $formFactory, ) {} - public function getDefaultData(string $step, ExportInterface|DirectExportInterface $export, array $options = []): array + public function getDefaultData(string $step, DirectExportInterface|ExportInterface $export, array $options = []): array { return match ($step) { 'centers', 'generate_centers' => ['centers' => $this->authorizationHelper->getReachableCenters($export->requiredRole())], @@ -45,7 +45,7 @@ final readonly class ExportFormHelper return $formatter->getFormDefaultData($options['aggregator_aliases']); } - private function getDefaultDataStepExport(ExportInterface|DirectExportInterface $export, array $options): array + private function getDefaultDataStepExport(DirectExportInterface|ExportInterface $export, array $options): array { $data = [ ExportType::EXPORT_KEY => $export->getFormDefaultData(), diff --git a/src/Bundle/ChillMainBundle/Export/ExportManager.php b/src/Bundle/ChillMainBundle/Export/ExportManager.php index 612ff4140..dae3f7de3 100644 --- a/src/Bundle/ChillMainBundle/Export/ExportManager.php +++ b/src/Bundle/ChillMainBundle/Export/ExportManager.php @@ -101,7 +101,7 @@ class ExportManager * * @return FilterInterface[] a \Generator that contains filters. The key is the filter's alias */ - public function &getFiltersApplyingOn(ExportInterface|DirectExportInterface $export, ?array $centers = null): iterable + public function &getFiltersApplyingOn(DirectExportInterface|ExportInterface $export, ?array $centers = null): iterable { if ($export instanceof DirectExportInterface) { return; @@ -124,7 +124,7 @@ class ExportManager * * @return null|iterable a \Generator that contains aggretagors. The key is the filter's alias */ - public function &getAggregatorsApplyingOn(ExportInterface|DirectExportInterface $export, ?array $centers = null): ?iterable + public function &getAggregatorsApplyingOn(DirectExportInterface|ExportInterface $export, ?array $centers = null): ?iterable { if ($export instanceof ListInterface || $export instanceof DirectExportInterface) { return; @@ -307,7 +307,7 @@ class ExportManager * * @throws RuntimeException */ - public function getExport($alias): ExportInterface|DirectExportInterface + public function getExport($alias): DirectExportInterface|ExportInterface { if (!array_key_exists($alias, $this->exports)) { throw new RuntimeException("The export with alias {$alias} is not known."); @@ -453,7 +453,7 @@ class ExportManager * */ public function isGrantedForElement( - ExportInterface|DirectExportInterface|ModifierInterface $element, + DirectExportInterface|ExportInterface|ModifierInterface $element, \Chill\MainBundle\Export\DirectExportInterface|\Chill\MainBundle\Export\ExportInterface $export = null, ?array $centers = null ): bool { diff --git a/src/Bundle/ChillMainBundle/Export/Helper/ExportAddressHelper.php b/src/Bundle/ChillMainBundle/Export/Helper/ExportAddressHelper.php index 01bd7f2fc..a3e4bae86 100644 --- a/src/Bundle/ChillMainBundle/Export/Helper/ExportAddressHelper.php +++ b/src/Bundle/ChillMainBundle/Export/Helper/ExportAddressHelper.php @@ -281,9 +281,9 @@ class ExportAddressHelper }; case 'country': - return function ($value) use ($key) { + return function ($value) use ($sanitizedKey, $translationPrefix) { if ('_header' === $value) { - return 'export.list.acp' . $key; + return $translationPrefix . $sanitizedKey; } if (null === $value) { diff --git a/src/Bundle/ChillMainBundle/Export/Helper/UserHelper.php b/src/Bundle/ChillMainBundle/Export/Helper/UserHelper.php index 996f8d925..44c2f260f 100644 --- a/src/Bundle/ChillMainBundle/Export/Helper/UserHelper.php +++ b/src/Bundle/ChillMainBundle/Export/Helper/UserHelper.php @@ -20,21 +20,64 @@ class UserHelper { public function __construct(private readonly UserRender $userRender, private readonly UserRepositoryInterface $userRepository) {} + /** + * Return a callable that will transform a value into a string representing a user + * + * The callable may receive as argument: + * + * - an int or a string, the id of the user; + * - a string containing a json which will be decoded, and will have this structure: array{uid: int, d: string}. The job and scopes will be shown at this date + * + * @param string $key the key of the content + * @param array $values the list of values + * @param string $header the header's content + */ public function getLabel($key, array $values, string $header): callable { - return function ($value) use ($header) { + return function (null|int|string $value) use ($header) { if ('_header' === $value) { return $header; } - if (null === $value || null === $user = $this->userRepository->find($value)) { + if (null === $value) { return ''; } - return $this->userRender->renderString($user, []); + if (is_numeric($value)) { + $uid = $value; + $date = null; + } else { + $decode = json_decode($value, true, 512, JSON_THROW_ON_ERROR); + $uid = $decode['uid']; + + if (null === $uid) { + return ''; + } + + $date = new \DateTimeImmutable($decode['d']); + } + + if (null === $user = $this->userRepository->find($uid)) { + return ''; + } + + return $this->userRender->renderString($user, ['at' => $date]); }; } + /** + * Return a callable that will transform a value into a string representing a user + * + * The callable may receive as argument: + * + * - an int or a string, the id of the user; + * - a string containing a json which will be decoded, and will have this structure: array{uid: int, d: string}. The job and scopes will be shown at this date * @param $key + * + * @param string $key the key of the element + * @param array $values a list of values + * @param string $header the header's content + * @return callable + */ public function getLabelMulti($key, array $values, string $header): callable { return function ($value) use ($header) { @@ -46,31 +89,36 @@ class UserHelper return ''; } - $decoded = json_decode((string) $value, null, 512, JSON_THROW_ON_ERROR); + $decoded = json_decode((string) $value, true, 512, JSON_THROW_ON_ERROR); if (0 === count($decoded)) { return ''; } + $asStrings = []; - return - implode( - '|', - array_map( - function (int $userId) { - $user = $this->userRepository->find($userId); + foreach ($decoded as $userId) { + if (is_array($userId)) { + $uid = $userId['uid']; + $date = new \DateTimeImmutable($userId['d']); + } else { + $uid = $userId; + $date = null; + } - if (null === $user) { - return ''; - } + if (null === $uid) { + continue; + } - return $this->userRender->renderString($user, []); - }, - array_unique( - array_filter($decoded, static fn (?int $userId) => null !== $userId), - SORT_NUMERIC - ) - ) - ); + $user = $this->userRepository->find($uid); + + if (null === $user) { + continue; + } + + $asStrings[$uid] = $this->userRender->renderString($user, ['absence' => false, 'at' => $date]); + } + + return implode('|', $asStrings); }; } } diff --git a/src/Bundle/ChillMainBundle/Repository/UserRepository.php b/src/Bundle/ChillMainBundle/Repository/UserRepository.php index a59ddc399..17775f475 100644 --- a/src/Bundle/ChillMainBundle/Repository/UserRepository.php +++ b/src/Bundle/ChillMainBundle/Repository/UserRepository.php @@ -13,7 +13,8 @@ namespace Chill\MainBundle\Repository; use Chill\MainBundle\Entity\GroupCenter; use Chill\MainBundle\Entity\User; -use Doctrine\ORM\AbstractQuery; +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Exception; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; use Doctrine\ORM\NoResultException; @@ -27,7 +28,11 @@ final readonly class UserRepository implements UserRepositoryInterface { private EntityRepository $repository; - public function __construct(private EntityManagerInterface $entityManager) + private const FIELDS = ['id', 'email', 'enabled', 'civility_id', 'civility_abbreviation', 'civility_name', 'label', 'mainCenter_id', + 'mainCenter_name', 'mainScope_id', 'mainScope_name', 'userJob_id', 'userJob_name', 'currentLocation_id', 'currentLocation_name', + 'mainLocation_id', 'mainLocation_name']; + + public function __construct(private EntityManagerInterface $entityManager, private Connection $connection) { $this->repository = $entityManager->getRepository(User::class); } @@ -75,48 +80,55 @@ final readonly class UserRepository implements UserRepositoryInterface } /** - * @param string $lang + * @throws Exception */ public function findAllAsArray(string $lang): iterable { - $dql = sprintf(<<<'DQL' + $sql = sprintf(<<<'SQL' SELECT - u.id AS id, + u.id, u.username AS username, - u.email, + u.email AS email, u.enabled, - IDENTITY(u.civility) AS civility_id, - JSON_EXTRACT(civility.abbreviation, :lang) AS civility_abbreviation, - JSON_EXTRACT(civility.name, :lang) AS civility_name, + u.civility_id, + civility.abbreviation->>:lang AS civility_abbreviation, + civility.name->>:lang AS civility_name, u.label, mainCenter.id AS mainCenter_id, mainCenter.name AS mainCenter_name, - IDENTITY(u.mainScope) AS mainScope_id, - JSON_EXTRACT(mainScope.name, :lang) AS mainScope_name, - IDENTITY(u.userJob) AS userJob_id, - JSON_EXTRACT(userJob.label, :lang) AS userJob_name, + mainScope.id AS mainScope_id, + mainScope.name->>:lang AS mainScope_name, + userJob.id AS userJob_id, + userJob.label->>:lang AS userJob_name, currentLocation.id AS currentLocation_id, currentLocation.name AS currentLocation_name, mainLocation.id AS mainLocation_id, mainLocation.name AS mainLocation_name, u.absenceStart - FROM Chill\MainBundle\Entity\User u - LEFT JOIN u.civility civility - LEFT JOIN u.currentLocation currentLocation - LEFT JOIN u.mainLocation mainLocation - LEFT JOIN u.mainCenter mainCenter - LEFT JOIN u.mainScope mainScope - LEFT JOIN u.userJob userJob - ORDER BY u.label - DQL); /// mainScope userJob + FROM users u + LEFT JOIN chill_main_civility civility ON u.civility_id = civility.id + LEFT JOIN centers mainCenter ON u.maincenter_id = mainCenter.id + LEFT JOIN chill_main_user_job_history userJobHistory ON u.id = userJobHistory.user_id + LEFT JOIN chill_main_user_job userJob ON userJobHistory.job_id = userJob.id AND tstzrange(userJobHistory.startdate, userJobHistory.enddate) @> NOW() + LEFT JOIN chill_main_user_scope_history userScopeHistory ON u.id = userScopeHistory.user_id AND tstzrange(userScopeHistory.startdate, userScopeHistory.enddate) @> NOW() + LEFT JOIN scopes mainScope ON userScopeHistory.scope_id = mainScope.id + LEFT JOIN chill_main_location currentLocation ON u.currentlocation_id = currentLocation.id + LEFT JOIN chill_main_location mainLocation ON u.mainlocation_id = mainLocation.id + ORDER BY u.label, u.id + SQL); - $query = $this->entityManager->createQuery($dql) - ->setHydrationMode(AbstractQuery::HYDRATE_ARRAY) - ->setParameter('lang', $lang) - ; + $query = $this->connection->prepare($sql); - foreach ($query->toIterable() as $u) { - yield $u; + foreach ($query->executeQuery(['lang' => $lang])->iterateAssociative() as $u) { + $converted = []; + foreach (self::FIELDS as $f) { + $converted[$f] = $u[strtolower($f)]; + } + + $converted['absenceStart'] = null !== $u['absencestart'] ? new \DateTimeImmutable($u['absencestart']) : null; + + /** @phpstan-ignore-next-line phpstan does not take into account that all required keys will be present */ + yield $converted; } } diff --git a/src/Bundle/ChillMainBundle/Resources/views/Entity/user.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Entity/user.html.twig index ad05a5a2c..c95308610 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/Entity/user.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/Entity/user.html.twig @@ -1,10 +1,10 @@ {{- user.label }} - {%- if opts['user_job'] and user.userJob is not null %} - ({{ user.userJob.label|localize_translatable_string }}) + {%- if opts['user_job'] and user.userJob(opts['at']) is not null %} + ({{ user.userJob(opts['at']).label|localize_translatable_string }}) {%- endif -%} - {%- if opts['main_scope'] and user.mainScope is not null %} - ({{ user.mainScope.name|localize_translatable_string }}) + {%- if opts['main_scope'] and user.mainScope(opts['at']) is not null %} + ({{ user.mainScope(opts['at']).name|localize_translatable_string }}) {%- endif -%} {%- if opts['absence'] and user.isAbsent %} {{ 'absence.A'|trans }} diff --git a/src/Bundle/ChillMainBundle/Security/Authorization/AuthorizationHelper.php b/src/Bundle/ChillMainBundle/Security/Authorization/AuthorizationHelper.php index 574d98b90..413492915 100644 --- a/src/Bundle/ChillMainBundle/Security/Authorization/AuthorizationHelper.php +++ b/src/Bundle/ChillMainBundle/Security/Authorization/AuthorizationHelper.php @@ -66,7 +66,7 @@ class AuthorizationHelper implements AuthorizationHelperInterface * * @return User[] */ - public function findUsersReaching(string $role, array|\Chill\MainBundle\Entity\Center $center, array|\Chill\MainBundle\Entity\Scope|null $scope = null, bool $onlyEnabled = true): array + public function findUsersReaching(string $role, array|\Chill\MainBundle\Entity\Center $center, null|array|\Chill\MainBundle\Entity\Scope $scope = null, bool $onlyEnabled = true): array { return $this->userACLAwareRepository ->findUsersByReachedACL($role, $center, $scope, $onlyEnabled); @@ -130,7 +130,7 @@ class AuthorizationHelper implements AuthorizationHelperInterface * @param Center|Center[] $center * @return Scope[] */ - public function getReachableCircles(UserInterface $user, string $role, \Chill\MainBundle\Entity\Center|array $center) + public function getReachableCircles(UserInterface $user, string $role, array|\Chill\MainBundle\Entity\Center $center) { $scopes = []; @@ -162,7 +162,7 @@ class AuthorizationHelper implements AuthorizationHelperInterface /** * Return all reachable scope for a given user, center and role. */ - public function getReachableScopes(UserInterface $user, string $role, Center|array $center): array + public function getReachableScopes(UserInterface $user, string $role, array|Center $center): array { return $this->getReachableCircles($user, $role, $center); } @@ -172,7 +172,7 @@ class AuthorizationHelper implements AuthorizationHelperInterface * * @param Center|Center[] $center May be an array of center */ - public function userCanReachCenter(User $user, \Chill\MainBundle\Entity\Center|array $center): bool + public function userCanReachCenter(User $user, array|\Chill\MainBundle\Entity\Center $center): bool { if ($center instanceof Traversable) { foreach ($center as $c) { diff --git a/src/Bundle/ChillMainBundle/Security/Authorization/AuthorizationHelperInterface.php b/src/Bundle/ChillMainBundle/Security/Authorization/AuthorizationHelperInterface.php index 1dc9668ec..2a8c6587c 100644 --- a/src/Bundle/ChillMainBundle/Security/Authorization/AuthorizationHelperInterface.php +++ b/src/Bundle/ChillMainBundle/Security/Authorization/AuthorizationHelperInterface.php @@ -29,5 +29,5 @@ interface AuthorizationHelperInterface * @param Center|array
$center * @return list */ - public function getReachableScopes(UserInterface $user, string $role, Center|array $center): array; + public function getReachableScopes(UserInterface $user, string $role, array|Center $center): array; } diff --git a/src/Bundle/ChillMainBundle/Security/Resolver/ScopeResolverDispatcher.php b/src/Bundle/ChillMainBundle/Security/Resolver/ScopeResolverDispatcher.php index afec7c6ad..a4a464e45 100644 --- a/src/Bundle/ChillMainBundle/Security/Resolver/ScopeResolverDispatcher.php +++ b/src/Bundle/ChillMainBundle/Security/Resolver/ScopeResolverDispatcher.php @@ -32,7 +32,7 @@ final readonly class ScopeResolverDispatcher return false; } - public function resolveScope(mixed $entity, ?array $options = []): iterable|\Chill\MainBundle\Entity\Scope|null + public function resolveScope(mixed $entity, ?array $options = []): null|\Chill\MainBundle\Entity\Scope|iterable { foreach ($this->resolvers as $resolver) { if ($resolver->supports($entity, $options)) { diff --git a/src/Bundle/ChillMainBundle/Serializer/Normalizer/DiscriminatedObjectDenormalizer.php b/src/Bundle/ChillMainBundle/Serializer/Normalizer/DiscriminatedObjectDenormalizer.php index 58dc7e839..4149e387b 100644 --- a/src/Bundle/ChillMainBundle/Serializer/Normalizer/DiscriminatedObjectDenormalizer.php +++ b/src/Bundle/ChillMainBundle/Serializer/Normalizer/DiscriminatedObjectDenormalizer.php @@ -45,7 +45,7 @@ class DiscriminatedObjectDenormalizer implements ContextAwareDenormalizerInterfa if ($this->denormalizer->supportsDenormalization($data, $localType, $format)) { try { return $this->denormalizer->denormalize($data, $localType, $format, $context); - } catch (RuntimeException|NotNormalizableValueException $e) { + } catch (NotNormalizableValueException|RuntimeException $e) { $lastException = $e; } } diff --git a/src/Bundle/ChillMainBundle/Service/EntityInfo/ViewEntityInfoManager.php b/src/Bundle/ChillMainBundle/Service/EntityInfo/ViewEntityInfoManager.php index 5057d76e5..c5ef96d0b 100644 --- a/src/Bundle/ChillMainBundle/Service/EntityInfo/ViewEntityInfoManager.php +++ b/src/Bundle/ChillMainBundle/Service/EntityInfo/ViewEntityInfoManager.php @@ -12,6 +12,7 @@ declare(strict_types=1); namespace Chill\MainBundle\Service\EntityInfo; use Doctrine\DBAL\Connection; +use Psr\Log\LoggerInterface; class ViewEntityInfoManager { @@ -21,6 +22,7 @@ class ViewEntityInfoManager */ private readonly iterable $vienEntityInfoProviders, private readonly Connection $connection, + private readonly LoggerInterface $logger, ) {} public function synchronizeOnDB(): void @@ -28,6 +30,8 @@ class ViewEntityInfoManager $this->connection->transactional(function (Connection $conn): void { foreach ($this->vienEntityInfoProviders as $viewProvider) { foreach ($this->createOrReplaceViewSQL($viewProvider, $viewProvider->getViewName()) as $sql) { + $this->logger->debug("Will execute create view sql", ['sql' => $sql]); + $this->logger->debug($sql); $conn->executeQuery($sql); } } @@ -41,7 +45,7 @@ class ViewEntityInfoManager { return [ "DROP VIEW IF EXISTS {$viewName}", - sprintf("CREATE VIEW {$viewName} AS %s", $viewProvider->getViewQuery()) + sprintf("CREATE OR REPLACE VIEW {$viewName} AS %s", $viewProvider->getViewQuery()) ]; } } diff --git a/src/Bundle/ChillMainBundle/Templating/Entity/UserRender.php b/src/Bundle/ChillMainBundle/Templating/Entity/UserRender.php index 0878453b2..c26664bf1 100644 --- a/src/Bundle/ChillMainBundle/Templating/Entity/UserRender.php +++ b/src/Bundle/ChillMainBundle/Templating/Entity/UserRender.php @@ -28,10 +28,14 @@ class UserRender implements ChillEntityRenderInterface 'main_scope' => true, 'user_job' => true, 'absence' => true, + 'at' => null, ]; public function __construct(private readonly TranslatableStringHelper $translatableStringHelper, private readonly \Twig\Environment $engine, private readonly TranslatorInterface $translator) {} + /** + * @param mixed $entity + */ public function renderBox($entity, array $options): string { $opts = array_merge(self::DEFAULT_OPTIONS, $options); @@ -42,20 +46,23 @@ class UserRender implements ChillEntityRenderInterface ]); } + /** + * @param mixed $entity + */ public function renderString($entity, array $options): string { $opts = array_merge(self::DEFAULT_OPTIONS, $options); $str = $entity->getLabel(); - if (null !== $entity->getUserJob() && $opts['user_job']) { + if (null !== $entity->getUserJob($opts['at']) && $opts['user_job']) { $str .= ' (' . $this->translatableStringHelper - ->localize($entity->getUserJob()->getLabel()) . ')'; + ->localize($entity->getUserJob($opts['at'])->getLabel()) . ')'; } - if (null !== $entity->getMainScope() && $opts['main_scope']) { + if (null !== $entity->getMainScope($opts['at']) && $opts['main_scope']) { $str .= ' (' . $this->translatableStringHelper - ->localize($entity->getMainScope()->getName()) . ')'; + ->localize($entity->getMainScope($opts['at'])->getName()) . ')'; } if ($entity->isAbsent() && $opts['absence']) { diff --git a/src/Bundle/ChillMainBundle/Tests/Doctrine/DQL/JsonBuildObjectTest.php b/src/Bundle/ChillMainBundle/Tests/Doctrine/DQL/JsonBuildObjectTest.php new file mode 100644 index 000000000..d2897546c --- /dev/null +++ b/src/Bundle/ChillMainBundle/Tests/Doctrine/DQL/JsonBuildObjectTest.php @@ -0,0 +1,61 @@ +entityManager = self::$container->get(EntityManagerInterface::class); + } + + /** + * @dataProvider provideQueries + */ + public function testQuery(string $sql, array $params, array $paramType): void + { + $query = $this->entityManager->createQuery($sql); + + foreach ($params as $k => $v) { + $query->setParameter($k, $v, $paramType[$k]); + } + $query->setMaxResults(1); + + $result = $query->getResult(AbstractQuery::HYDRATE_ARRAY); + + self::assertIsArray($result); + } + + public function provideQueries(): iterable + { + yield ["SELECT JSON_BUILD_OBJECT(1, 2, 3, 4) FROM " . Address::class . " a", [], []]; + yield ["SELECT JSON_BUILD_OBJECT('st', a.street, 'sn', a.streetNumber) FROM " . Address::class . ' a', [], []]; + // next query make the test fails. But we do not need it for now. + //yield ["SELECT JSON_BUILD_OBJECT(a.street, :param), LOWER(:param) FROM " . Address::class . " a", ['param' => 1], ['param' => Types::INTEGER]]; + } + +} diff --git a/src/Bundle/ChillPersonBundle/Doctrine/DQL/AddressPart.php b/src/Bundle/ChillPersonBundle/Doctrine/DQL/AddressPart.php index 229a69f2c..38d13ea43 100644 --- a/src/Bundle/ChillPersonBundle/Doctrine/DQL/AddressPart.php +++ b/src/Bundle/ChillPersonBundle/Doctrine/DQL/AddressPart.php @@ -40,7 +40,7 @@ abstract class AddressPart extends FunctionNode 'country_id', ]; - private \Doctrine\ORM\Query\AST\Node|string|null $date = null; + private null|\Doctrine\ORM\Query\AST\Node|string $date = null; /** * @var \Doctrine\ORM\Query\AST\Node diff --git a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php index 2224f307b..e074f53ef 100644 --- a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php +++ b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php @@ -977,7 +977,7 @@ class AccompanyingPeriod implements /** * @Groups({"read"}) */ - public function getRequestor(): Person|ThirdParty|null + public function getRequestor(): null|Person|ThirdParty { return $this->requestorPerson ?? $this->requestorThirdParty; } diff --git a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWork.php b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWork.php index 611f1763e..0c17276ae 100644 --- a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWork.php +++ b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWork.php @@ -156,8 +156,7 @@ class AccompanyingPeriodWork implements AccompanyingPeriodLinkedWithSocialIssues /** * @var Collection - * @ORM\OneToMany(targetEntity=AccompanyingPeriodWorkReferrerHistory::class, cascade={"persist", "remove"}, mappedBy="accompanyingPeriodWork") - * @ORM\JoinTable(name="chill_person_accompanying_period_work_referrer") + * @ORM\OneToMany(targetEntity=AccompanyingPeriodWorkReferrerHistory::class, cascade={"persist", "remove"}, mappedBy="accompanyingPeriodWork", orphanRemoval=true) */ private Collection $referrersHistory; @@ -357,9 +356,17 @@ class AccompanyingPeriodWork implements AccompanyingPeriodLinkedWithSocialIssues */ public function getReferrers(): ReadableCollection { - return $this->referrersHistory->map(fn (AccompanyingPeriodWorkReferrerHistory $h) => $h->getUser()); + return $this->referrersHistory + ->filter(fn (AccompanyingPeriodWorkReferrerHistory $h) => null === $h->getEndDate()) + ->map(fn (AccompanyingPeriodWorkReferrerHistory $h) => $h->getUser()); } + public function getReferrersHistory(): Collection + { + return $this->referrersHistory; + } + + /** * @return Collection */ @@ -442,6 +449,7 @@ class AccompanyingPeriodWork implements AccompanyingPeriodLinkedWithSocialIssues if ($history->isDateRangeEmpty()) { $history->removeAccompanyingPeriodWork(); + $this->referrersHistory->removeElement($history); } } } diff --git a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWorkReferrerHistory.php b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWorkReferrerHistory.php index 48b75d3e4..057b5a409 100644 --- a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWorkReferrerHistory.php +++ b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWorkReferrerHistory.php @@ -38,29 +38,24 @@ class AccompanyingPeriodWorkReferrerHistory implements TrackCreationInterface, T * @var \DateTimeImmutable|null * @ORM\Column(type="date_immutable", nullable=true, options={"default": null}) */ - private ?\DateTimeImmutable $endDate; - - /** - * @var AccompanyingPeriodWork|null - * @ORM\ManyToOne(targetEntity=AccompanyingPeriodWork::class, inversedBy="referrersHistory") - */ - private ?AccompanyingPeriodWork $accompanyingPeriodWork; + private ?\DateTimeImmutable $endDate = null; public function __construct( - AccompanyingPeriodWork $accompanyingPeriodWork, + /** + * @ORM\ManyToOne(targetEntity=AccompanyingPeriodWork::class, inversedBy="referrersHistory") + */ + private ?AccompanyingPeriodWork $accompanyingPeriodWork, /** * @var User * @ORM\ManyToOne(targetEntity=User::class) */ - private readonly User $user, + private User $user, /** * @var \DateTimeImmutable * @ORM\Column(type="date_immutable", nullable=false) */ - private readonly \DateTimeImmutable $startDate, - ) { - $this->accompanyingPeriodWork = $accompanyingPeriodWork; - } + private \DateTimeImmutable $startDate + ) {} public function getId(): ?int { diff --git a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/Resource.php b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/Resource.php index c9f1e9279..abe171df1 100644 --- a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/Resource.php +++ b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/Resource.php @@ -96,7 +96,7 @@ class Resource /** * @Groups({"read"}) */ - public function getResource(): \Chill\PersonBundle\Entity\Person|\Chill\ThirdPartyBundle\Entity\ThirdParty|null + public function getResource(): null|\Chill\PersonBundle\Entity\Person|\Chill\ThirdPartyBundle\Entity\ThirdParty { return $this->person ?? $this->thirdParty; } diff --git a/src/Bundle/ChillPersonBundle/Entity/Person.php b/src/Bundle/ChillPersonBundle/Entity/Person.php index 832afb987..3265f09e1 100644 --- a/src/Bundle/ChillPersonBundle/Entity/Person.php +++ b/src/Bundle/ChillPersonBundle/Entity/Person.php @@ -619,7 +619,7 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI * @return true | array True if the accompanying periods are not collapsing, * an array with data for displaying the error */ - public function checkAccompanyingPeriodsAreNotCollapsing(): bool|array + public function checkAccompanyingPeriodsAreNotCollapsing(): array|bool { $periods = $this->getAccompanyingPeriodsOrdered(); $periodsNbr = count($periods); diff --git a/src/Bundle/ChillPersonBundle/Entity/SocialWork/SocialAction.php b/src/Bundle/ChillPersonBundle/Entity/SocialWork/SocialAction.php index a5475263e..f98964fcb 100644 --- a/src/Bundle/ChillPersonBundle/Entity/SocialWork/SocialAction.php +++ b/src/Bundle/ChillPersonBundle/Entity/SocialWork/SocialAction.php @@ -156,7 +156,7 @@ class SocialAction * * @return Collection|SocialAction[] a list with the elements of the given list which are parent of other elements in the given list */ - public static function findAncestorSocialActions(\Doctrine\Common\Collections\Collection|array $socialActions): Collection + public static function findAncestorSocialActions(array|\Doctrine\Common\Collections\Collection $socialActions): Collection { $ancestors = new ArrayCollection(); @@ -236,7 +236,7 @@ class SocialAction /** * @param Collection|SocialAction[] $socialActions */ - public static function getDescendantsWithThisForActions(\Doctrine\Common\Collections\Collection|array $socialActions): Collection + public static function getDescendantsWithThisForActions(array|\Doctrine\Common\Collections\Collection $socialActions): Collection { $unique = []; @@ -420,7 +420,7 @@ class SocialAction return $this; } - public static function filterRemoveDeactivatedActions(ReadableCollection|array $actions, \DateTime $comparisonDate): ReadableCollection|array + public static function filterRemoveDeactivatedActions(array|ReadableCollection $actions, \DateTime $comparisonDate): array|ReadableCollection { $filterFn = fn (SocialAction $socialAction) => !$socialAction->isDesactivated($comparisonDate); diff --git a/src/Bundle/ChillPersonBundle/Entity/SocialWork/SocialIssue.php b/src/Bundle/ChillPersonBundle/Entity/SocialWork/SocialIssue.php index 5c9f1a9c4..000d83397 100644 --- a/src/Bundle/ChillPersonBundle/Entity/SocialWork/SocialIssue.php +++ b/src/Bundle/ChillPersonBundle/Entity/SocialWork/SocialIssue.php @@ -110,7 +110,7 @@ class SocialIssue * * @return Collection|SocialIssue[] */ - public static function findAncestorSocialIssues(\Doctrine\Common\Collections\Collection|array $socialIssues): Collection + public static function findAncestorSocialIssues(array|\Doctrine\Common\Collections\Collection $socialIssues): Collection { $ancestors = new ArrayCollection(); diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/JobWorkingOnCourseAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/JobWorkingOnCourseAggregator.php index ad38b4a6b..b42bb0cd5 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/JobWorkingOnCourseAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/JobWorkingOnCourseAggregator.php @@ -81,7 +81,7 @@ final readonly class JobWorkingOnCourseAggregator implements AggregatorInterface public function getLabels($key, array $values, $data): \Closure { - return function (int|string|null $jobId) { + return function (null|int|string $jobId) { if (null === $jobId || '' === $jobId) { return ''; } diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ScopeWorkingOnCourseAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ScopeWorkingOnCourseAggregator.php index fbc4947bf..f0dcff515 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ScopeWorkingOnCourseAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ScopeWorkingOnCourseAggregator.php @@ -81,7 +81,7 @@ final readonly class ScopeWorkingOnCourseAggregator implements AggregatorInterfa public function getLabels($key, array $values, $data): \Closure { - return function (int|string|null $scopeId) { + return function (null|int|string $scopeId) { if (null === $scopeId || '' === $scopeId) { return ''; } diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/UserWorkingOnCourseAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/UserWorkingOnCourseAggregator.php index 862a2de79..b36795cdd 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/UserWorkingOnCourseAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/UserWorkingOnCourseAggregator.php @@ -41,7 +41,7 @@ final readonly class UserWorkingOnCourseAggregator implements AggregatorInterfac public function getLabels($key, array $values, $data): \Closure { - return function (int|string|null $userId) { + return function (null|int|string $userId) { if (null === $userId || '' === $userId) { return ''; } diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/CenterAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/CenterAggregator.php index 99187264a..c6d8cfae0 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/CenterAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/CenterAggregator.php @@ -46,7 +46,7 @@ final readonly class CenterAggregator implements AggregatorInterface public function getLabels($key, array $values, $data): Closure { - return function (int|string|null $value) { + return function (null|int|string $value) { if (null === $value || '' === $value) { return ''; } diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/JobAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/JobAggregator.php index 6012bbcab..c95d4ef37 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/JobAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/JobAggregator.php @@ -11,13 +11,16 @@ declare(strict_types=1); namespace Chill\PersonBundle\Export\Aggregator\SocialWorkAggregators; +use Chill\MainBundle\Entity\Scope; use Chill\MainBundle\Entity\User\UserJobHistory; +use Chill\MainBundle\Entity\UserJob; use Chill\MainBundle\Export\AggregatorInterface; use Chill\MainBundle\Form\Type\PickRollingDateType; use Chill\MainBundle\Repository\UserJobRepository; use Chill\MainBundle\Service\RollingDate\RollingDate; use Chill\MainBundle\Service\RollingDate\RollingDateConverter; use Chill\MainBundle\Templating\TranslatableStringHelper; +use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkReferrerHistory; use Chill\PersonBundle\Export\Declarations; use Doctrine\ORM\Query\Expr; use Doctrine\ORM\QueryBuilder; @@ -29,7 +32,6 @@ final readonly class JobAggregator implements AggregatorInterface private const PREFIX = 'acp_work_action_agg_user_job'; public function __construct( - private RollingDateConverter $rollingDateConverter, private UserJobRepository $jobRepository, private TranslatableStringHelper $translatableStringHelper ) {} @@ -44,27 +46,16 @@ final readonly class JobAggregator implements AggregatorInterface $p = self::PREFIX; $qb - ->leftJoin("acpw.referrers", "{$p}_user") ->leftJoin( - UserJobHistory::class, - "{$p}_history", + UserJob::class, + "{$p}_job", Expr\Join::WITH, - $qb->expr()->eq("{$p}_history.user", "{$p}_user") - ) - ->andWhere( - $qb->expr()->andX( - $qb->expr()->lte("{$p}_history.startDate", ":{$p}_at"), - $qb->expr()->orX( - $qb->expr()->isNull("{$p}_history.endDate"), - $qb->expr()->gt("{$p}_history.endDate", ":{$p}_at") - ) - ) - ) - ->addSelect("IDENTITY({$p}_history.job) as {$p}_select") - ->setParameter( - "{$p}_at", - $this->rollingDateConverter->convert($data['job_at']) + 'EXISTS (SELECT 1 FROM ' . AccompanyingPeriodWorkReferrerHistory::class . " {$p}_ref_history + JOIN {$p}_ref_history.user {$p}_ref_history_user JOIN {$p}_ref_history_user.jobHistories {$p}_job_history + WHERE {$p}_ref_history.accompanyingPeriodWork = acpw AND IDENTITY({$p}_job_history.job) = {$p}_job.id AND {$p}_job_history.startDate <= {$p}_ref_history.startDate + AND ({$p}_job_history.endDate IS NULL or {$p}_job_history.endDate >= {$p}_ref_history.startDate))" ) + ->addSelect("{$p}_job.id as {$p}_select") ->addGroupBy("{$p}_select"); } @@ -73,17 +64,11 @@ final readonly class JobAggregator implements AggregatorInterface return Declarations::SOCIAL_WORK_ACTION_TYPE; } - public function buildForm(FormBuilderInterface $builder) - { - $builder->add('job_at', PickRollingDateType::class, [ - 'label' => 'export.aggregator.course_work.by_agent_job.Calc date', - 'required' => true - ]); - } + public function buildForm(FormBuilderInterface $builder) {} public function getFormDefaultData(): array { - return ['job_at' => new RollingDate(RollingDate::T_TODAY)]; + return []; } public function getLabels($key, array $values, $data) diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ReferrerAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ReferrerAggregator.php index 6af106715..99f766ebc 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ReferrerAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ReferrerAggregator.php @@ -12,7 +12,10 @@ declare(strict_types=1); namespace Chill\PersonBundle\Export\Aggregator\SocialWorkAggregators; use Chill\MainBundle\Export\AggregatorInterface; +use Chill\MainBundle\Form\Type\PickRollingDateType; use Chill\MainBundle\Repository\UserRepository; +use Chill\MainBundle\Service\RollingDate\RollingDate; +use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface; use Chill\MainBundle\Templating\Entity\UserRender; use Chill\PersonBundle\Export\Declarations; use Doctrine\ORM\QueryBuilder; @@ -21,7 +24,13 @@ use function in_array; final readonly class ReferrerAggregator implements AggregatorInterface { - public function __construct(private UserRepository $userRepository, private UserRender $userRender) {} + private const PREFIX = 'acpw_referrer_aggregator'; + + public function __construct( + private UserRepository $userRepository, + private UserRender $userRender, + private RollingDateConverterInterface $rollingDateConverter + ) {} public function addRole(): ?string { @@ -30,11 +39,16 @@ final readonly class ReferrerAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { - if (!in_array('acpwuser', $qb->getAllAliases(), true)) { - $qb->leftJoin('acpw.referrers', 'acpwuser'); - } + $p = self::PREFIX; - $qb->addSelect('acpwuser.id AS referrer_aggregator'); + $qb + ->leftJoin('acpw.referrersHistory', $p . "_acpwusers_history") + ->andWhere("{$p}_acpwusers_history.startDate <= :{$p}_calc_date AND ({$p}_acpwusers_history.endDate IS NULL or {$p}_acpwusers_history.endDate > :{$p}_calc_date)"); + + $qb->setParameter("{$p}_calc_date", $this->rollingDateConverter->convert( + $data['referrer_at'] ?? new RollingDate(RollingDate::T_TODAY) + )); + $qb->addSelect("IDENTITY({$p}_acpwusers_history.user) AS referrer_aggregator"); $qb->addGroupBy('referrer_aggregator'); } @@ -45,11 +59,16 @@ final readonly class ReferrerAggregator implements AggregatorInterface public function buildForm(FormBuilderInterface $builder) { - // no form + $builder->add('referrer_at', PickRollingDateType::class, [ + 'label' => 'export.aggregator.course_work.by_treating_agent.Calc date' + ]); } + public function getFormDefaultData(): array { - return []; + return [ + 'referrer_at' => new RollingDate(RollingDate::T_TODAY), + ]; } public function getLabels($key, array $values, $data) @@ -65,7 +84,7 @@ final readonly class ReferrerAggregator implements AggregatorInterface $r = $this->userRepository->find($value); - return $this->userRender->renderString($r, []); + return $this->userRender->renderString($r, ['absence' => false, 'user_job' => false, 'main_scope' => false]); }; } @@ -76,6 +95,6 @@ final readonly class ReferrerAggregator implements AggregatorInterface public function getTitle(): string { - return 'Group by treating agent'; + return 'export.aggregator.course_work.by_treating_agent.Group by treating agent'; } } diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ScopeAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ScopeAggregator.php index b6c799079..6fc158dad 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ScopeAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ScopeAggregator.php @@ -11,13 +11,16 @@ declare(strict_types=1); namespace Chill\PersonBundle\Export\Aggregator\SocialWorkAggregators; +use Chill\MainBundle\Entity\Scope; use Chill\MainBundle\Entity\User\UserScopeHistory; +use Chill\MainBundle\Entity\UserJob; use Chill\MainBundle\Export\AggregatorInterface; use Chill\MainBundle\Form\Type\PickRollingDateType; use Chill\MainBundle\Repository\ScopeRepository; use Chill\MainBundle\Service\RollingDate\RollingDate; use Chill\MainBundle\Service\RollingDate\RollingDateConverter; use Chill\MainBundle\Templating\TranslatableStringHelper; +use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkReferrerHistory; use Chill\PersonBundle\Export\Declarations; use Doctrine\ORM\Query\Expr; use Doctrine\ORM\QueryBuilder; @@ -29,7 +32,6 @@ final readonly class ScopeAggregator implements AggregatorInterface private const PREFIX = 'acp_work_action_agg_user_scope'; public function __construct( - private RollingDateConverter $rollingDateConverter, private ScopeRepository $scopeRepository, private TranslatableStringHelper $translatableStringHelper ) {} @@ -44,27 +46,16 @@ final readonly class ScopeAggregator implements AggregatorInterface $p = self::PREFIX; $qb - ->leftJoin("acpw.referrers", "{$p}_user") ->leftJoin( - UserScopeHistory::class, - "{$p}_history", + Scope::class, + "{$p}_scope", Expr\Join::WITH, - $qb->expr()->eq("{$p}_history.user", "{$p}_user") - ) - ->andWhere( - $qb->expr()->andX( - $qb->expr()->lte("{$p}_history.startDate", ":{$p}_at"), - $qb->expr()->orX( - $qb->expr()->isNull("{$p}_history.endDate"), - $qb->expr()->gt("{$p}_history.endDate", ":{$p}_at") - ) - ) - ) - ->addSelect("IDENTITY({$p}_history.scope) as {$p}_select") - ->setParameter( - "{$p}_at", - $this->rollingDateConverter->convert($data['scope_at']) + 'EXISTS (SELECT 1 FROM ' . AccompanyingPeriodWorkReferrerHistory::class . " {$p}_ref_history + JOIN {$p}_ref_history.user {$p}_ref_history_user JOIN {$p}_ref_history_user.scopeHistories {$p}_scope_history + WHERE {$p}_ref_history.accompanyingPeriodWork = acpw AND IDENTITY({$p}_scope_history.scope) = {$p}_scope.id AND {$p}_scope_history.startDate <= {$p}_ref_history.startDate + AND ({$p}_scope_history.endDate IS NULL or {$p}_scope_history.endDate >= {$p}_ref_history.startDate))" ) + ->addSelect("{$p}_scope.id as {$p}_select") ->addGroupBy("{$p}_select"); } @@ -73,17 +64,11 @@ final readonly class ScopeAggregator implements AggregatorInterface return Declarations::SOCIAL_WORK_ACTION_TYPE; } - public function buildForm(FormBuilderInterface $builder) - { - $builder->add('scope_at', PickRollingDateType::class, [ - 'label' => 'export.aggregator.course_work.by_agent_scope.Calc date', - 'required' => true, - ]); - } + public function buildForm(FormBuilderInterface $builder) {} public function getFormDefaultData(): array { - return ['scope_at' => new RollingDate(RollingDate::T_TODAY)]; + return []; } public function getLabels($key, array $values, $data) diff --git a/src/Bundle/ChillPersonBundle/Export/Export/ListAccompanyingPeriodWork.php b/src/Bundle/ChillPersonBundle/Export/Export/ListAccompanyingPeriodWork.php index 0b34899b9..3923936b8 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/ListAccompanyingPeriodWork.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/ListAccompanyingPeriodWork.php @@ -24,6 +24,7 @@ use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface; use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork; use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation; use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkGoal; +use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkReferrerHistory; use Chill\PersonBundle\Entity\AccompanyingPeriod\UserHistory; use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Entity\Person\PersonCenterHistory; @@ -69,6 +70,7 @@ class ListAccompanyingPeriodWork implements ListInterface, GroupedExportInterfac 'personsName', 'thirdParties', 'handlingThierParty', + //'acpwReferrers', 'referrers', 'createdAt', 'createdBy', @@ -76,7 +78,20 @@ class ListAccompanyingPeriodWork implements ListInterface, GroupedExportInterfac 'updatedBy', ]; - public function __construct(private readonly EntityManagerInterface $entityManager, private readonly DateTimeHelper $dateTimeHelper, private readonly UserHelper $userHelper, private readonly LabelPersonHelper $personHelper, private readonly LabelThirdPartyHelper $thirdPartyHelper, private readonly TranslatableStringExportLabelHelper $translatableStringExportLabelHelper, private readonly SocialIssueRender $socialIssueRender, private readonly SocialIssueRepository $socialIssueRepository, private readonly SocialActionRender $socialActionRender, private readonly RollingDateConverterInterface $rollingDateConverter, private readonly AggregateStringHelper $aggregateStringHelper, private readonly SocialActionRepository $socialActionRepository) {} + public function __construct( + private readonly EntityManagerInterface $entityManager, + private readonly DateTimeHelper $dateTimeHelper, + private readonly UserHelper $userHelper, + private readonly LabelPersonHelper $personHelper, + private readonly LabelThirdPartyHelper $thirdPartyHelper, + private readonly TranslatableStringExportLabelHelper $translatableStringExportLabelHelper, + private readonly SocialIssueRender $socialIssueRender, + private readonly SocialIssueRepository $socialIssueRepository, + private readonly SocialActionRender $socialActionRender, + private readonly RollingDateConverterInterface $rollingDateConverter, + private readonly AggregateStringHelper $aggregateStringHelper, + private readonly SocialActionRepository $socialActionRepository + ) {} public function buildForm(FormBuilderInterface $builder) { @@ -141,6 +156,7 @@ class ListAccompanyingPeriodWork implements ListInterface, GroupedExportInterfac }, 'createdBy', 'updatedBy', 'acp_user' => $this->userHelper->getLabel($key, $values, 'export.list.acpw.' . $key), 'referrers' => $this->userHelper->getLabel($key, $values, 'export.list.acpw.' . $key), + //'acpwReferrers' => $this->userHelper->getLabelMulti($key, $values, 'export.list.acpw.' . $key), 'personsName' => $this->personHelper->getLabelMulti($key, $values, 'export.list.acpw.' . $key), 'handlingThierParty' => $this->thirdPartyHelper->getLabel($key, $values, 'export.list.acpw.' . $key), 'thirdParties' => $this->thirdPartyHelper->getLabelMulti($key, $values, 'export.list.acpw.' . $key), @@ -167,7 +183,7 @@ class ListAccompanyingPeriodWork implements ListInterface, GroupedExportInterfac public function getResult($query, $data) { - return $query->getQuery()->getResult(AbstractQuery::HYDRATE_SCALAR); + return dump($query->getQuery()->getResult(AbstractQuery::HYDRATE_SCALAR)); } public function getTitle(): string @@ -269,9 +285,20 @@ class ListAccompanyingPeriodWork implements ListInterface, GroupedExportInterfac // referrers => at date XXXX $qb - ->addSelect('(SELECT IDENTITY(history.user) FROM ' . UserHistory::class . ' history ' . + ->addSelect('(SELECT JSON_BUILD_OBJECT(\'uid\', IDENTITY(history.user), \'d\', history.startDate) FROM ' . UserHistory::class . ' history ' . 'WHERE history.accompanyingPeriod = acp AND history.startDate <= :calcDate AND (history.endDate IS NULL OR history.endDate > :calcDate)) AS referrers'); + /* + // acpwReferrers at date XXX + $qb + ->addSelect('( + SELECT IDENTITY(acpw_ref_history.accompanyingPeriodWork) AS acpw_ref_history_id, + JSON_BUILD_OBJECT(\'uid\', IDENTITY(acpw_ref_history.user), \'d\', acpw_ref_history.startDate) + FROM ' . AccompanyingPeriodWorkReferrerHistory::class . ' acpw_ref_history ' . + 'WHERE acpw_ref_history.accompanyingPeriodWork = acpw AND acpw_ref_history.startDate <= :calcDate AND (acpw_ref_history.endDate IS NULL or acpw_ref_history.endDate > :calcDate) GROUP BY acpw_ref_history_id) AS acpwReferrers' + ); + */ + // thirdparties $qb ->addSelect('(SELECT AGGREGATE(tp.id) FROM ' . ThirdParty::class . ' tp ' diff --git a/src/Bundle/ChillPersonBundle/Export/Export/ListEvaluation.php b/src/Bundle/ChillPersonBundle/Export/Export/ListEvaluation.php index 34646dbd0..253b4f34c 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/ListEvaluation.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/ListEvaluation.php @@ -132,7 +132,7 @@ class ListEvaluation implements ListInterface, GroupedExportInterface ); }, 'createdBy', 'updatedBy', 'acpw_acp_user' => $this->userHelper->getLabel($key, $values, 'export.list.eval.' . $key), - 'acpw_referrers' => $this->userHelper->getLabel($key, $values, 'export.list.eval.' . $key), + 'acpw_referrers' => $this->userHelper->getLabelMulti($key, $values, 'export.list.eval.' . $key), 'acpw_persons_id' => $this->aggregateStringHelper->getLabelMulti($key, $values, 'export.list.eval.' . $key), 'acpw_persons' => $this->personHelper->getLabelMulti($key, $values, 'export.list.eval.' . $key), 'eval_title' => $this->translatableStringExportLabelHelper @@ -252,8 +252,8 @@ class ListEvaluation implements ListInterface, GroupedExportInterface // referrers => at date XXXX $qb - ->addSelect('(SELECT IDENTITY(history.user) FROM ' . UserHistory::class . ' history ' . - 'WHERE history.accompanyingPeriod = acp AND history.startDate <= :calc_date AND (history.endDate IS NULL OR history.endDate > :calc_date)) AS acpw_referrers'); + ->addSelect('(SELECT JSON_BUILD_OBJECT(\'uid\', IDENTITY(history.user), \'d\', history.startDate) FROM ' . UserHistory::class . ' history ' . + 'WHERE history.accompanyingPeriod = acp AND history.startDate <= :calc_date AND (history.endDate IS NULL OR history.endDate > :calc_date)) AS referrers'); // persons $qb diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorJobFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorJobFilter.php index 7053654df..10467f911 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorJobFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorJobFilter.php @@ -72,9 +72,10 @@ class CreatorJobFilter implements FilterInterface ) ) ->andWhere($qb->expr()->in("{$p}_history.job", ":{$p}_jobs")) - ->setParameters([ - "{$p}_jobs" => $data["creator_job"], - ]); + ->setParameter( + "{$p}_jobs", + $data["creator_job"], + ); } public function applyOn(): string diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/JobWorkingOnCourseFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/JobWorkingOnCourseFilter.php index 393a726fc..7024d837d 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/JobWorkingOnCourseFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/JobWorkingOnCourseFilter.php @@ -63,11 +63,15 @@ readonly class JobWorkingOnCourseFilter implements FilterInterface . "AND {$p}_info.infoDate >= :{$p}_start and {$p}_info.infoDate < :{$p}_end" ) ) - ->setParameters([ - "{$p}_jobs" => $data['jobs'], - "{$p}_start" => $this->rollingDateConverter->convert($data['start_date']), - "{$p}_end" => $this->rollingDateConverter->convert($data['end_date']) - ]) + ->setParameter("{$p}_jobs", $data['jobs']) + ->setParameter( + "{$p}_start", + $this->rollingDateConverter->convert($data['start_date']), + ) + ->setParameter( + "{$p}_end", + $this->rollingDateConverter->convert($data['end_date']) + ) ; } diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ScopeWorkingOnCourseFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ScopeWorkingOnCourseFilter.php index 97f592c3d..491122968 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ScopeWorkingOnCourseFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ScopeWorkingOnCourseFilter.php @@ -63,11 +63,15 @@ readonly class ScopeWorkingOnCourseFilter implements FilterInterface . "AND {$p}_info.infoDate >= :{$p}_start AND {$p}_info.infoDate < :{$p}_end" ) ) - ->setParameters([ - "{$p}_scopes" => $data["scopes"], - "{$p}_start" => $this->rollingDateConverter->convert($data["start_date"]), - "{$p}_end" => $this->rollingDateConverter->convert($data["end_date"]) - ]) + ->setParameter("{$p}_scopes", $data["scopes"]) + ->setParameter( + "{$p}_start", + $this->rollingDateConverter->convert($data["start_date"]), + ) + ->setParameter( + "{$p}_end", + $this->rollingDateConverter->convert($data["end_date"]) + ) ; } diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/UserJobFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/UserJobFilter.php index 04d1b2cb8..dc55fd454 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/UserJobFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/UserJobFilter.php @@ -72,9 +72,10 @@ class UserJobFilter implements FilterInterface ) ) ->andWhere($qb->expr()->in("{$p}_jobHistory.job", ":{$p}_job")) - ->setParameters([ - "{$p}_job" => $data["jobs"], - ]) + ->setParameter( + "{$p}_job", + $data["jobs"], + ) ; } diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/UserScopeFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/UserScopeFilter.php index 2abad7823..3e53f49f1 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/UserScopeFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/UserScopeFilter.php @@ -72,9 +72,10 @@ class UserScopeFilter implements FilterInterface ) ) ->andWhere($qb->expr()->in("{$p}_scopeHistory.scope", ":{$p}_scopes")) - ->setParameters([ - "{$p}_scopes" => $data["scopes"], - ]) + ->setParameter( + "{$p}_scopes", + $data["scopes"], + ) ; } diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/JobFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/JobFilter.php index 2ad01c3ff..c0de87d65 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/JobFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/JobFilter.php @@ -18,6 +18,7 @@ use Chill\MainBundle\Form\Type\PickRollingDateType; use Chill\MainBundle\Service\RollingDate\RollingDate; use Chill\MainBundle\Service\RollingDate\RollingDateConverter; use Chill\MainBundle\Templating\TranslatableStringHelper; +use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkReferrerHistory; use Chill\PersonBundle\Export\Declarations; use Doctrine\ORM\Query\Expr; use Doctrine\ORM\Query\Expr\Andx; @@ -32,7 +33,6 @@ class JobFilter implements FilterInterface private const PREFIX = 'acp_work_action_filter_user_job'; public function __construct( - private readonly RollingDateConverter $rollingDateConverter, protected TranslatorInterface $translator, private readonly TranslatableStringHelper $translatableStringHelper ) {} @@ -46,30 +46,14 @@ class JobFilter implements FilterInterface { $p = self::PREFIX; - $qb - ->leftJoin("acpw.referrers", "{$p}_user") - ->leftJoin( - UserJobHistory::class, - "{$p}_history", - Expr\Join::WITH, - $qb->expr()->eq("{$p}_history.user", "{$p}_user") - ) - ->andWhere( - $qb->expr()->andX( - $qb->expr()->lte("{$p}_history.startDate", ":{$p}_at"), - $qb->expr()->orX( - $qb->expr()->isNull("{$p}_history.endDate"), - $qb->expr()->gt("{$p}_history.endDate", ":{$p}_at") - ) - ) - ) - ->andWhere( - $qb->expr()->in("{$p}_history.job", ":{$p}_job") - ) - ->setParameters([ - "{$p}_job" => $data["job"], - "{$p}_at" => $this->rollingDateConverter->convert($data['job_at']) - ]); + $qb->andWhere( + 'EXISTS (SELECT 1 FROM ' . AccompanyingPeriodWorkReferrerHistory::class . " {$p}_ref_history + JOIN {$p}_ref_history.user {$p}_ref_history_user JOIN {$p}_ref_history_user.jobHistories {$p}_job_history + WHERE {$p}_ref_history.accompanyingPeriodWork = acpw AND {$p}_job_history.job IN (:{$p}_job) AND {$p}_job_history.startDate <= {$p}_ref_history.startDate + AND ({$p}_job_history.endDate IS NULL or {$p}_job_history.endDate >= {$p}_ref_history.startDate))" + ); + + $qb->setParameter("{$p}_job", $data["job"]); } public function applyOn(): string @@ -88,10 +72,6 @@ class JobFilter implements FilterInterface 'multiple' => true, 'expanded' => true, ]) - ->add('job_at', PickRollingDateType::class, [ - 'label' => 'export.filter.work.by_user_job.Calc date', - 'required' => true, - ]) ; } diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/ReferrerFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/ReferrerFilter.php index bddcfbf9b..2410a5d08 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/ReferrerFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/ReferrerFilter.php @@ -12,7 +12,10 @@ declare(strict_types=1); namespace Chill\PersonBundle\Export\Filter\SocialWorkFilters; use Chill\MainBundle\Export\FilterInterface; +use Chill\MainBundle\Form\Type\PickRollingDateType; use Chill\MainBundle\Form\Type\PickUserDynamicType; +use Chill\MainBundle\Service\RollingDate\RollingDate; +use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface; use Chill\MainBundle\Templating\Entity\UserRender; use Chill\PersonBundle\Export\Declarations; use Doctrine\ORM\Query\Expr\Andx; @@ -20,8 +23,12 @@ use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; use function in_array; -class ReferrerFilter implements FilterInterface +final readonly class ReferrerFilter implements FilterInterface { + private const PREFIX = 'acpw_referrer_filter'; + + public function __construct(private RollingDateConverterInterface $rollingDateConverter) {} + public function addRole(): ?string { return null; @@ -29,21 +36,19 @@ class ReferrerFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { - if (!in_array('acpwuser', $qb->getAllAliases(), true)) { - $qb->join('acpw.referrers', 'acpwuser'); - } + $p = self::PREFIX; - $where = $qb->getDQLPart('where'); - $clause = $qb->expr()->in('acpwuser', ':agents'); + $qb + ->leftJoin('acpw.referrersHistory', $p . "_acpwusers_history") + ->andWhere("{$p}_acpwusers_history.startDate <= :{$p}_calc_date AND ({$p}_acpwusers_history.endDate IS NULL or {$p}_acpwusers_history.endDate > :{$p}_calc_date)") + ->andWhere("{$p}_acpwusers_history.user IN (:{$p}_agents)"); - if ($where instanceof Andx) { - $where->add($clause); - } else { - $where = $qb->expr()->andX($clause); - } - - $qb->add('where', $where); - $qb->setParameter('agents', $data['accepted_agents']); + $qb + ->setParameter("{$p}_agents", $data['accepted_agents']) + ->setParameter("{$p}_calc_date", $this->rollingDateConverter->convert( + $data['agent_at'] ?? new RollingDate(RollingDate::T_TODAY) + )) + ; } public function applyOn(): string @@ -53,13 +58,23 @@ class ReferrerFilter implements FilterInterface public function buildForm(FormBuilderInterface $builder) { - $builder->add('accepted_agents', PickUserDynamicType::class, [ - 'multiple' => true, - ]); + $builder + ->add('accepted_agents', PickUserDynamicType::class, [ + 'multiple' => true, + 'label' => 'export.filter.work.by_treating_agent.Accepted agents' + ]) + ->add('agent_at', PickRollingDateType::class, [ + 'label' => 'export.filter.work.by_treating_agent.Calc date', + 'help' => 'export.filter.work.by_treating_agent.calc_date_help', + ]) + ; } public function getFormDefaultData(): array { - return []; + return [ + 'accepted_agents' => [], + 'agent_at' => new RollingDate(RollingDate::T_TODAY), + ]; } public function describeAction($data, $format = 'string'): array @@ -71,13 +86,14 @@ class ReferrerFilter implements FilterInterface } return [ - 'Filtered by treating agent: only %agents%', [ - '%agents' => implode(', ', $users), + 'exports.filter.work.by_treating_agent.Filtered by treating agent at date', [ + 'agents' => implode(', ', $users), + 'agent_at' => $this->rollingDateConverter->convert($data['agent_at'] ?? new RollingDate(RollingDate::T_TODAY)), ], ]; } public function getTitle(): string { - return 'Filter by treating agent'; + return 'export.filter.work.by_treating_agent.Filter by treating agent'; } } diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/ScopeFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/ScopeFilter.php index c5041aa6c..ce9b4e585 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/ScopeFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/ScopeFilter.php @@ -18,6 +18,7 @@ use Chill\MainBundle\Form\Type\PickRollingDateType; use Chill\MainBundle\Service\RollingDate\RollingDate; use Chill\MainBundle\Service\RollingDate\RollingDateConverter; use Chill\MainBundle\Templating\TranslatableStringHelper; +use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkReferrerHistory; use Chill\PersonBundle\Export\Declarations; use Doctrine\ORM\Query\Expr; use Doctrine\ORM\QueryBuilder; @@ -30,7 +31,6 @@ class ScopeFilter implements FilterInterface private const PREFIX = 'acp_work_action_filter_user_scope'; public function __construct( - private readonly RollingDateConverter $rollingDateConverter, protected TranslatorInterface $translator, private readonly TranslatableStringHelper $translatableStringHelper ) {} @@ -44,30 +44,13 @@ class ScopeFilter implements FilterInterface { $p = self::PREFIX; - $qb - ->leftJoin("acpw.referrers", "{$p}_user") - ->leftJoin( - UserScopeHistory::class, - "{$p}_history", - Expr\Join::WITH, - $qb->expr()->eq("{$p}_history.user", "{$p}_user") - ) - ->andWhere( - $qb->expr()->andX( - $qb->expr()->lte("{$p}_history.startDate", ":{$p}_at"), - $qb->expr()->orX( - $qb->expr()->isNull("{$p}_history.endDate"), - $qb->expr()->gt("{$p}_history.endDate", ":{$p}_at") - ) - ) - ) - ->andWhere( - $qb->expr()->in("{$p}_history.scope", ":{$p}_scope") - ) - ->setParameters([ - "{$p}_scope" => $data["scope"], - "{$p}_at" => $this->rollingDateConverter->convert($data['scope_at']) - ]); + $qb->andWhere( + 'EXISTS (SELECT 1 FROM ' . AccompanyingPeriodWorkReferrerHistory::class . " {$p}_ref_history + JOIN {$p}_ref_history.user {$p}_ref_history_user JOIN {$p}_ref_history_user.scopeHistories {$p}_scope_history + WHERE {$p}_ref_history.accompanyingPeriodWork = acpw AND {$p}_scope_history.scope IN (:{$p}_scope) AND {$p}_scope_history.startDate <= {$p}_ref_history.startDate + AND ({$p}_scope_history.endDate IS NULL or {$p}_scope_history.endDate >= {$p}_ref_history.startDate))" + ) + ->setParameter("{$p}_scope", $data["scope"]); } public function applyOn() @@ -86,10 +69,6 @@ class ScopeFilter implements FilterInterface 'multiple' => true, 'expanded' => true, ]) - ->add('scope_at', PickRollingDateType::class, [ - 'label' => 'export.filter.work.by_user_scope.Calc date', - 'required' => true, - ]) ; } diff --git a/src/Bundle/ChillPersonBundle/Export/Helper/ListAccompanyingPeriodHelper.php b/src/Bundle/ChillPersonBundle/Export/Helper/ListAccompanyingPeriodHelper.php index 7349c5457..1531b0489 100644 --- a/src/Bundle/ChillPersonBundle/Export/Helper/ListAccompanyingPeriodHelper.php +++ b/src/Bundle/ChillPersonBundle/Export/Helper/ListAccompanyingPeriodHelper.php @@ -15,6 +15,7 @@ use Chill\MainBundle\Entity\Address; use Chill\MainBundle\Entity\Scope; use Chill\MainBundle\Export\Helper\DateTimeHelper; use Chill\MainBundle\Export\Helper\ExportAddressHelper; +use Chill\MainBundle\Export\Helper\UserHelper; use Chill\MainBundle\Templating\TranslatableStringHelperInterface; use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Entity\Household\PersonHouseholdAddress; @@ -59,8 +60,10 @@ final readonly class ListAccompanyingPeriodHelper 'scopes', 'socialIssues', 'acpCreatedAt', + 'acpCreatedBy_id', 'acpCreatedBy', 'acpUpdatedAt', + 'acpUpdatedBy_id', 'acpUpdatedBy', ]; @@ -75,6 +78,7 @@ final readonly class ListAccompanyingPeriodHelper private SocialIssueRender $socialIssueRender, private TranslatableStringHelperInterface $translatableStringHelper, private TranslatorInterface $translator, + private UserHelper $userHelper, ) {} public function getQueryKeys($data) @@ -104,6 +108,7 @@ final readonly class ListAccompanyingPeriodHelper return $this->translatableStringHelper->localize(json_decode((string) $value, true, 512, JSON_THROW_ON_ERROR)); }, + 'acpCreatedBy', 'acpUpdatedBy', 'referrer' => $this->userHelper->getLabel($key, $values, 'export.list.acp.' . $key), 'locationPersonName', 'requestorPerson' => function ($value) use ($key) { if ('_header' === $value) { return 'export.list.acp.' . $key; @@ -204,11 +209,11 @@ final readonly class ListAccompanyingPeriodHelper // add the field which are simple association $qb - ->leftJoin('acp.createdBy', "acp_created_by_t") - ->addSelect('acp_created_by_t.label AS acpCreatedBy'); + ->addSelect('IDENTITY(acp.createdBy) AS acpCreatedBy_id') + ->addSelect('JSON_BUILD_OBJECT(\'uid\', IDENTITY(acp.createdBy), \'d\', acp.createdAt) AS acpCreatedBy'); $qb - ->leftJoin('acp.updatedBy', "acp_updated_by_t") - ->addSelect('acp_updated_by_t.label AS acpUpdatedBy'); + ->addSelect('IDENTITY(acp.updatedBy) AS acpUpdatedBy_id') + ->addSelect('JSON_BUILD_OBJECT(\'uid\', IDENTITY(acp.updatedBy), \'d\', acp.updatedAt) AS acpUpdatedBy'); foreach (['origin' => 'label', 'closingMotive' => 'name', 'job' => 'label', 'administrativeLocation' => 'name'] as $entity => $field) { $qb @@ -230,7 +235,7 @@ final readonly class ListAccompanyingPeriodHelper // referree at date $qb - ->addSelect('referrer_t.label AS referrer') + ->addSelect('JSON_BUILD_OBJECT(\'uid\', IDENTITY(userHistory.user), \'d\', userHistory.startDate) AS referrer') ->addSelect('userHistory.startDate AS referrerSince') ->leftJoin('acp.userHistories', 'userHistory') ->leftJoin('userHistory.user', 'referrer_t') diff --git a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkEvaluationRepository.php b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkEvaluationRepository.php index 28cbca270..fb1bb4ffa 100644 --- a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkEvaluationRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkEvaluationRepository.php @@ -12,6 +12,7 @@ declare(strict_types=1); namespace Chill\PersonBundle\Repository\AccompanyingPeriod; use Chill\MainBundle\Entity\User; +use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork; use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation; use DateTimeImmutable; use Doctrine\ORM\EntityManagerInterface; @@ -91,7 +92,9 @@ class AccompanyingPeriodWorkEvaluationRepository implements ObjectRepository $qb->expr()->gte(':now', $qb->expr()->diff('e.maxDate', 'e.warningInterval')), $qb->expr()->orX( $qb->expr()->eq('period.user', ':user'), - $qb->expr()->isMemberOf(':user', 'work.referrers') + $qb->expr()->exists( + 'SELECT 1 FROM ' . AccompanyingPeriodWork::class . ' subw JOIN subw.referrersHistory subw_ref_history WHERE subw.id = work.id AND subw_ref_history.user = :user' + ) ) ) ) diff --git a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php index 04ce8928e..4cbbab9fc 100644 --- a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php @@ -132,10 +132,10 @@ final readonly class AccompanyingPeriodWorkRepository implements ObjectRepositor // set limit and offset $sql .= " ORDER BY - CASE WHEN enddate IS NULL THEN '-infinity'::timestamp ELSE 'infinity'::timestamp END ASC, - startdate DESC, - enddate DESC, - id DESC"; + CASE WHEN w.enddate IS NULL THEN '-infinity'::timestamp ELSE 'infinity'::timestamp END ASC, + w.startdate DESC, + w.enddate DESC, + w.id DESC"; $sql .= " LIMIT :limit OFFSET :offset"; @@ -239,7 +239,9 @@ final readonly class AccompanyingPeriodWorkRepository implements ObjectRepositor $qb->expr()->lte('w.startDate', ':until'), $qb->expr()->orX( $qb->expr()->eq('period.user', ':user'), - $qb->expr()->isMemberOf(':user', 'w.referrers') + $qb->expr()->exists( + 'SELECT 1 FROM ' . AccompanyingPeriodWork::class . ' subw JOIN subw.referrersHistory subw_ref_history WHERE subw.id = w.id AND subw_ref_history.user = :user and subw.ref_history.endDate IS NULL' + ) ) ) ) diff --git a/src/Bundle/ChillPersonBundle/Serializer/Normalizer/AccompanyingPeriodWorkNormalizer.php b/src/Bundle/ChillPersonBundle/Serializer/Normalizer/AccompanyingPeriodWorkNormalizer.php index f6faeed12..86e94f52e 100644 --- a/src/Bundle/ChillPersonBundle/Serializer/Normalizer/AccompanyingPeriodWorkNormalizer.php +++ b/src/Bundle/ChillPersonBundle/Serializer/Normalizer/AccompanyingPeriodWorkNormalizer.php @@ -37,7 +37,7 @@ class AccompanyingPeriodWorkNormalizer implements ContextAwareNormalizerInterfac * * @throws ExceptionInterface */ - public function normalize($object, ?string $format = null, array $context = []): array|\ArrayObject|bool|float|int|string|null + public function normalize($object, ?string $format = null, array $context = []): null|array|\ArrayObject|bool|float|int|string { $initial = $this->normalizer->normalize($object, $format, array_merge( $context, diff --git a/src/Bundle/ChillPersonBundle/Service/EntityInfo/AccompanyingPeriodInfoQueryPart/AccompanyingPeriodWorkEndQueryPartForAccompanyingPeriodInfo.php b/src/Bundle/ChillPersonBundle/Service/EntityInfo/AccompanyingPeriodInfoQueryPart/AccompanyingPeriodWorkEndQueryPartForAccompanyingPeriodInfo.php index 8ba9b9640..f0b38b635 100644 --- a/src/Bundle/ChillPersonBundle/Service/EntityInfo/AccompanyingPeriodInfoQueryPart/AccompanyingPeriodWorkEndQueryPartForAccompanyingPeriodInfo.php +++ b/src/Bundle/ChillPersonBundle/Service/EntityInfo/AccompanyingPeriodInfoQueryPart/AccompanyingPeriodWorkEndQueryPartForAccompanyingPeriodInfo.php @@ -54,7 +54,7 @@ class AccompanyingPeriodWorkEndQueryPartForAccompanyingPeriodInfo implements Acc public function getFromStatement(): string { return 'chill_person_accompanying_period_work w - LEFT JOIN chill_person_accompanying_period_work_referrer cpapwr on w.id = cpapwr.accompanyingperiodwork_id'; + LEFT JOIN chill_person_accompanying_period_work_referrer cpapwr ON w.id = cpapwr.accompanyingperiodwork_id AND daterange(cpapwr.startDate, cpapwr.endDate) @> w.endDate'; } public function getWhereClause(): string diff --git a/src/Bundle/ChillPersonBundle/Service/EntityInfo/AccompanyingPeriodInfoQueryPart/AccompanyingPeriodWorkEvaluationWarningDateQueryPartForAccompanyingPeriodInfo.php b/src/Bundle/ChillPersonBundle/Service/EntityInfo/AccompanyingPeriodInfoQueryPart/AccompanyingPeriodWorkEvaluationCreationQueryPartForAccompanyingPeriodInfo.php similarity index 73% rename from src/Bundle/ChillPersonBundle/Service/EntityInfo/AccompanyingPeriodInfoQueryPart/AccompanyingPeriodWorkEvaluationWarningDateQueryPartForAccompanyingPeriodInfo.php rename to src/Bundle/ChillPersonBundle/Service/EntityInfo/AccompanyingPeriodInfoQueryPart/AccompanyingPeriodWorkEvaluationCreationQueryPartForAccompanyingPeriodInfo.php index a71fe4d29..53b5e31ad 100644 --- a/src/Bundle/ChillPersonBundle/Service/EntityInfo/AccompanyingPeriodInfoQueryPart/AccompanyingPeriodWorkEvaluationWarningDateQueryPartForAccompanyingPeriodInfo.php +++ b/src/Bundle/ChillPersonBundle/Service/EntityInfo/AccompanyingPeriodInfoQueryPart/AccompanyingPeriodWorkEvaluationCreationQueryPartForAccompanyingPeriodInfo.php @@ -11,10 +11,11 @@ declare(strict_types=1); namespace Chill\PersonBundle\Service\EntityInfo\AccompanyingPeriodInfoQueryPart; +use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork; use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation; use Chill\PersonBundle\Service\EntityInfo\AccompanyingPeriodInfoUnionQueryPartInterface; -class AccompanyingPeriodWorkEvaluationWarningDateQueryPartForAccompanyingPeriodInfo implements AccompanyingPeriodInfoUnionQueryPartInterface +class AccompanyingPeriodWorkEvaluationCreationQueryPartForAccompanyingPeriodInfo implements AccompanyingPeriodInfoUnionQueryPartInterface { public function getAccompanyingPeriodIdColumn(): string { @@ -33,12 +34,12 @@ class AccompanyingPeriodWorkEvaluationWarningDateQueryPartForAccompanyingPeriodI public function getUserIdColumn(): string { - return 'cpapwr.user_id'; + return 'e.createdBy_id'; } public function getDateTimeColumn(): string { - return 'e.maxDate'; + return 'e.createdAt'; } public function getMetadataColumn(): string @@ -48,18 +49,18 @@ class AccompanyingPeriodWorkEvaluationWarningDateQueryPartForAccompanyingPeriodI public function getDiscriminator(): string { - return 'accompanying_period_work_evaluation_max'; + return 'accompanying_period_work_evaluation_creation'; } public function getFromStatement(): string { return 'chill_person_accompanying_period_work_evaluation e JOIN chill_person_accompanying_period_work cpapw ON cpapw.id = e.accompanyingperiodwork_id - LEFT JOIN chill_person_accompanying_period_work_referrer cpapwr ON cpapw.id = cpapwr.accompanyingperiodwork_id'; + LEFT JOIN chill_person_accompanying_period_work_referrer cpapwr ON cpapw.id = cpapwr.accompanyingperiodwork_id AND daterange(cpapwr.startDate, cpapwr.endDate) @> e.startDate'; } public function getWhereClause(): string { - return 'e.maxDate IS NOT NULL'; + return 'e.createdAt IS NOT NULL'; } } diff --git a/src/Bundle/ChillPersonBundle/Service/EntityInfo/AccompanyingPeriodInfoQueryPart/AccompanyingPeriodWorkEvaluationMaxQueryPartForAccompanyingPeriodInfo.php b/src/Bundle/ChillPersonBundle/Service/EntityInfo/AccompanyingPeriodInfoQueryPart/AccompanyingPeriodWorkEvaluationMaxQueryPartForAccompanyingPeriodInfo.php index b89f77460..843602249 100644 --- a/src/Bundle/ChillPersonBundle/Service/EntityInfo/AccompanyingPeriodInfoQueryPart/AccompanyingPeriodWorkEvaluationMaxQueryPartForAccompanyingPeriodInfo.php +++ b/src/Bundle/ChillPersonBundle/Service/EntityInfo/AccompanyingPeriodInfoQueryPart/AccompanyingPeriodWorkEvaluationMaxQueryPartForAccompanyingPeriodInfo.php @@ -56,7 +56,7 @@ class AccompanyingPeriodWorkEvaluationMaxQueryPartForAccompanyingPeriodInfo impl { return 'chill_person_accompanying_period_work_evaluation e JOIN chill_person_accompanying_period_work cpapw ON cpapw.id = e.accompanyingperiodwork_id - LEFT JOIN chill_person_accompanying_period_work_referrer cpapwr ON cpapw.id = cpapwr.accompanyingperiodwork_id'; + LEFT JOIN chill_person_accompanying_period_work_referrer cpapwr ON cpapw.id = cpapwr.accompanyingperiodwork_id AND daterange(cpapwr.startDate, cpapwr.endDate) @> e.maxDate'; } public function getWhereClause(): string diff --git a/src/Bundle/ChillPersonBundle/Service/EntityInfo/AccompanyingPeriodInfoQueryPart/AccompanyingPeriodWorkEvaluationStartQueryPartForAccompanyingPeriodInfo.php b/src/Bundle/ChillPersonBundle/Service/EntityInfo/AccompanyingPeriodInfoQueryPart/AccompanyingPeriodWorkEvaluationStartQueryPartForAccompanyingPeriodInfo.php index 8ea3e579c..cc82fc9ab 100644 --- a/src/Bundle/ChillPersonBundle/Service/EntityInfo/AccompanyingPeriodInfoQueryPart/AccompanyingPeriodWorkEvaluationStartQueryPartForAccompanyingPeriodInfo.php +++ b/src/Bundle/ChillPersonBundle/Service/EntityInfo/AccompanyingPeriodInfoQueryPart/AccompanyingPeriodWorkEvaluationStartQueryPartForAccompanyingPeriodInfo.php @@ -56,11 +56,11 @@ class AccompanyingPeriodWorkEvaluationStartQueryPartForAccompanyingPeriodInfo im { return 'chill_person_accompanying_period_work_evaluation e JOIN chill_person_accompanying_period_work cpapw ON cpapw.id = e.accompanyingperiodwork_id - LEFT JOIN chill_person_accompanying_period_work_referrer cpapwr ON cpapw.id = cpapwr.accompanyingperiodwork_id'; + LEFT JOIN chill_person_accompanying_period_work_referrer cpapwr ON cpapw.id = cpapwr.accompanyingperiodwork_id AND daterange(cpapwr.startDate, cpapwr.endDate) @> e.startDate'; } public function getWhereClause(): string { - return ''; + return 'e.startDate IS NOT NULL'; } } diff --git a/src/Bundle/ChillPersonBundle/Service/EntityInfo/AccompanyingPeriodInfoQueryPart/AccompanyingPeriodWorkNewReferrerQueryPartForAccompanyingPeriodInfo.php b/src/Bundle/ChillPersonBundle/Service/EntityInfo/AccompanyingPeriodInfoQueryPart/AccompanyingPeriodWorkNewReferrerQueryPartForAccompanyingPeriodInfo.php new file mode 100644 index 000000000..2208c2975 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Service/EntityInfo/AccompanyingPeriodInfoQueryPart/AccompanyingPeriodWorkNewReferrerQueryPartForAccompanyingPeriodInfo.php @@ -0,0 +1,64 @@ + w.startDate'; } public function getWhereClause(): string diff --git a/src/Bundle/ChillPersonBundle/Tests/Action/Remove/PersonMoveTest.php b/src/Bundle/ChillPersonBundle/Tests/Action/Remove/PersonMoveTest.php index ce3d8a3d1..64826930f 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Action/Remove/PersonMoveTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Action/Remove/PersonMoveTest.php @@ -24,6 +24,7 @@ use Chill\PersonBundle\Entity\Household\HouseholdMember; use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Entity\Relationships\Relation; use Chill\PersonBundle\Entity\Relationships\Relationship; +use Chill\PersonBundle\Repository\Person\PersonCenterHistoryInterface; use Chill\PersonBundle\Repository\PersonRepository; use Doctrine\DBAL\Connection; use Doctrine\ORM\EntityManagerInterface; @@ -44,6 +45,8 @@ class PersonMoveTest extends KernelTestCase private CenterRepositoryInterface $centerRepository; + private PersonCenterHistoryInterface $personCenterHistory; + /** * @var list */ @@ -56,6 +59,7 @@ class PersonMoveTest extends KernelTestCase $this->personMoveManager = self::$container->get(PersonMoveManager::class); $this->eventDispatcher = self::$container->get(EventDispatcherInterface::class); $this->centerRepository = self::$container->get(CenterRepositoryInterface::class); + $this->personCenterHistory = self::$container->get(PersonCenterHistoryInterface::class); } public static function tearDownAfterClass(): void @@ -146,18 +150,22 @@ class PersonMoveTest extends KernelTestCase $personB = $this->em->find(Person::class, $personB->getId()); $message = 'Move persons with overlapping center histories'; - $this->em->refresh($personB); - self::assertCount(0, $personsByIdOfA); self::assertNotNull($personB?->getId(), $message); - $centerHistoriesB = $personB->getCenterHistory(); - $oldestDate = new \DateTimeImmutable('2023-01-01'); + $centersHistories = $this->personCenterHistory->findBy(['person' => $personB]); - $this->em->refresh($centerHistoriesB->first()); + // compute the oldest center history + $oldestCenterHistory = null; + foreach ($centersHistories as $centerHistory) { + $this->em->refresh($centerHistory); + if (null === $oldestCenterHistory || ($oldestCenterHistory instanceof Person\PersonCenterHistory && $oldestCenterHistory->getStartDate() >= $centerHistory->getStartDate())) { + $oldestCenterHistory = $centerHistory; + } + } - self::assertCount(2, $centerHistoriesB); - self::assertEquals($oldestDate, $centerHistoriesB->first()->getStartDate()); + self::assertCount(2, $centersHistories); + self::assertEquals('2023-01-01', $oldestCenterHistory?->getStartDate()->format('Y-m-d')); self::$entitiesToDelete[] = [Person::class, $personA]; self::$entitiesToDelete[] = [Person::class, $personB]; @@ -218,11 +226,11 @@ class PersonMoveTest extends KernelTestCase $this->em->persist($memberA); $this->em->persist($memberB); - self::$entitiesToDelete[] = [Person::class, $personA]; - self::$entitiesToDelete[] = [Person::class, $personB]; self::$entitiesToDelete[] = [HouseholdMember::class, $memberA]; self::$entitiesToDelete[] = [HouseholdMember::class, $memberB]; self::$entitiesToDelete[] = [Household::class, $household]; + self::$entitiesToDelete[] = [Person::class, $personA]; + self::$entitiesToDelete[] = [Person::class, $personB]; yield [$personA, $personB, "move 2 people having the same household at the same time"]; diff --git a/src/Bundle/ChillPersonBundle/Tests/Controller/AccompanyingCourseApiControllerTest.php b/src/Bundle/ChillPersonBundle/Tests/Controller/AccompanyingCourseApiControllerTest.php index 64b78f49e..4f2d19f54 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Controller/AccompanyingCourseApiControllerTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Controller/AccompanyingCourseApiControllerTest.php @@ -52,16 +52,6 @@ final class AccompanyingCourseApiControllerTest extends WebTestCase private ?int $personId = null; - private KernelBrowser $client; - - /** - * Setup before each test method (see phpunit doc). - */ - protected function setUp(): void - { - $this->client = $this->getClientAuthenticated(); - } - protected function tearDown(): void { self::ensureKernelShutdown(); @@ -427,8 +417,9 @@ final class AccompanyingCourseApiControllerTest extends WebTestCase */ public function testAccompanyingCourseShow(int $personId, int $periodId) { - $c = $this->client->request(Request::METHOD_GET, sprintf('/api/1.0/person/accompanying-course/%d.json', $periodId)); - $response = $this->client->getResponse(); + $client = $this->getClientAuthenticated(); + $client->request(Request::METHOD_GET, sprintf('/api/1.0/person/accompanying-course/%d.json', $periodId)); + $response = $client->getResponse(); $this->assertTrue(in_array($response->getStatusCode(), [200, 422], true)); @@ -548,12 +539,13 @@ final class AccompanyingCourseApiControllerTest extends WebTestCase */ public function testReferralAvailable(int $personId, int $periodId) { - $this->client->request( + $client = $this->getClientAuthenticated(); + $client->request( Request::METHOD_POST, sprintf('/api/1.0/person/accompanying-course/%d/referrers-suggested.json', $periodId) ); - $this->assertTrue(in_array($this->client->getResponse()->getStatusCode(), [200, 422], true)); + $this->assertTrue(in_array($client->getResponse()->getStatusCode(), [200, 422], true)); } /** @@ -732,8 +724,9 @@ final class AccompanyingCourseApiControllerTest extends WebTestCase public function testShow404() { - $this->client->request(Request::METHOD_GET, sprintf('/api/1.0/person/accompanying-course/%d.json', 99999)); - $response = $this->client->getResponse(); + $client = $this->getClientAuthenticated(); + $client->request(Request::METHOD_GET, sprintf('/api/1.0/person/accompanying-course/%d.json', 99999)); + $response = $client->getResponse(); $this->assertEquals(404, $response->getStatusCode(), "Test that the response of rest api has a status code 'not found' (404)"); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Controller/HouseholdApiControllerTest.php b/src/Bundle/ChillPersonBundle/Tests/Controller/HouseholdApiControllerTest.php index 2e1c58c64..e98539de5 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Controller/HouseholdApiControllerTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Controller/HouseholdApiControllerTest.php @@ -137,20 +137,17 @@ final class HouseholdApiControllerTest extends WebTestCase $qb = self::$container->get(EntityManagerInterface::class) ->createQueryBuilder(); - $period = $qb - ->select('ap') - ->from(AccompanyingPeriod::class, 'ap') + $personIds = $qb + ->select("p.id AS pid") + ->from(Person::class, 'p') ->where( - $qb->expr()->gte('SIZE(ap.participations)', 2) + $qb->expr()->gte('SIZE(p.accompanyingPeriodParticipations)', 2) ) ->getQuery() ->setMaxResults(1) ->getSingleResult(); - $person = $period->getParticipations() - ->first()->getPerson(); - - yield [$person->getId()]; + yield [$personIds['pid']]; self::ensureKernelShutdown(); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Entity/AccompanyingPeriod/AccompanyingPeriodWorkTest.php b/src/Bundle/ChillPersonBundle/Tests/Entity/AccompanyingPeriod/AccompanyingPeriodWorkTest.php new file mode 100644 index 000000000..6771b0305 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Entity/AccompanyingPeriod/AccompanyingPeriodWorkTest.php @@ -0,0 +1,96 @@ +getReferrers()); + + $work->addReferrer($userA); + + self::assertCount(1, $work->getReferrers()); + self::assertContains($userA, $work->getReferrers()); + + $work->addReferrer($userB); + + self::assertCount(2, $work->getReferrers()); + self::assertContains($userA, $work->getReferrers()); + self::assertContains($userB, $work->getReferrers()); + + $work->addReferrer($userC); + $work->removeReferrer($userB); + + self::assertCount(2, $work->getReferrers()); + self::assertContains($userA, $work->getReferrers()); + self::assertNotContains($userB, $work->getReferrers()); + self::assertContains($userC, $work->getReferrers()); + + $work->removeReferrer($userA); + self::assertNotContains($userA, $work->getReferrers()); + self::assertNotContains($userB, $work->getReferrers()); + self::assertContains($userC, $work->getReferrers()); + } + + public function testReferrerHistoryOnDifferentDays(): void + { + + $work = new AccompanyingPeriodWork(); + $userA = new User(); + $userB = new User(); + $userC = new User(); + + $work->addReferrer($userA); + + $historyA = $work->getReferrersHistory()->first(); + $reflection = new \ReflectionClass($historyA); + $startDateReflection = $reflection->getProperty('startDate'); + $startDateReflection->setAccessible(true); + $startDateReflection->setValue($historyA, new \DateTimeImmutable('1 year ago')); + + $work->addReferrer($userB); + $work->addReferrer($userC); + + $work->removeReferrer($userB); + $work->removeReferrer($userA); + + self::assertCount(1, $work->getReferrers()); + self::assertNotContains($userA, $work->getReferrers()); + self::assertNotContains($userB, $work->getReferrers()); + self::assertContains($userC, $work->getReferrers()); + + self::assertCount(2, $work->getReferrersHistory()); + + $historyA = $work->getReferrersHistory() + ->filter(fn (AccompanyingPeriodWorkReferrerHistory $h) => $userA === $h->getUser()) + ->first(); + + self::assertNotFalse($historyA); + self::assertSame($userA, $historyA->getUser()); + self::assertEquals((new \DateTimeImmutable())->format('Y-m-d'), $historyA->getEndDate()->format('Y-m-d')); + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/JobAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/JobAggregatorTest.php index 5a20fc3b1..dfd6db053 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/JobAggregatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/JobAggregatorTest.php @@ -40,9 +40,7 @@ final class JobAggregatorTest extends AbstractAggregatorTest public function getFormData(): array { return [ - [ - 'job_at' => new RollingDate(RollingDate::T_FIXED_DATE, \DateTimeImmutable::createFromFormat('Y-m-d', '2020-01-01')), - ], + [], ]; } @@ -57,7 +55,6 @@ final class JobAggregatorTest extends AbstractAggregatorTest ->select('count(acp.id)') ->from(AccompanyingPeriod::class, 'acp') ->join('acp.works', 'acpw') - ->join('acpw.referrers', 'acpwuser'), ]; } } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ReferrerAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ReferrerAggregatorTest.php index 21f185469..19ca8b615 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ReferrerAggregatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ReferrerAggregatorTest.php @@ -11,6 +11,7 @@ declare(strict_types=1); namespace Chill\PersonBundle\Tests\Export\Aggregator\SocialWorkAggregators; +use Chill\MainBundle\Service\RollingDate\RollingDate; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork; use Chill\PersonBundle\Export\Aggregator\SocialWorkAggregators\ReferrerAggregator; @@ -39,7 +40,10 @@ final class ReferrerAggregatorTest extends AbstractAggregatorTest public function getFormData(): array { return [ - [], + [], // there are previous saved export which does not contains any data + [ + 'referrer_at' => new RollingDate(RollingDate::T_TODAY) + ] ]; } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ScopeAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ScopeAggregatorTest.php index 0bb9fba13..3cd8c93fe 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ScopeAggregatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ScopeAggregatorTest.php @@ -40,9 +40,7 @@ final class ScopeAggregatorTest extends AbstractAggregatorTest public function getFormData(): array { return [ - [ - 'scope_at' => new RollingDate(RollingDate::T_FIXED_DATE, \DateTimeImmutable::createFromFormat('Y-m-d', '2020-01-01')), - ], + [], ]; } @@ -57,7 +55,6 @@ final class ScopeAggregatorTest extends AbstractAggregatorTest ->select('count(acp.id)') ->from(AccompanyingPeriod::class, 'acp') ->join('acp.works', 'acpw') - ->join('acpw.referrers', 'acpwuser'), ]; } } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Export/ListAccompanyingPeriodTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Export/ListAccompanyingPeriodTest.php new file mode 100644 index 000000000..24a49bab3 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Export/ListAccompanyingPeriodTest.php @@ -0,0 +1,53 @@ +listAccompanyingPeriod = self::$container->get(ListAccompanyingPeriod::class); + $this->centerRepository = self::$container->get(CenterRepositoryInterface::class); + } + + public function testQuery(): void + { + $centers = $this->centerRepository->findAll(); + + $query = $this->listAccompanyingPeriod->initiateQuery([], array_map(fn (Center $c) => ['center' => $c ], $centers), $exportOpts = ['calc_date' => new RollingDate(RollingDate::T_TODAY)]); + + $query->setMaxResults(1); + + $actual = $query->getQuery()->getResult(AbstractQuery::HYDRATE_ARRAY); + + self::assertIsArray($actual); + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Export/ListAccompanyingPeriodWorkTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Export/ListAccompanyingPeriodWorkTest.php new file mode 100644 index 000000000..3467f15a7 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Export/ListAccompanyingPeriodWorkTest.php @@ -0,0 +1,49 @@ +listAccompanyingPeriodWork = self::$container->get(ListAccompanyingPeriodWork::class); + $this->centerRepository = self::$container->get(CenterRepositoryInterface::class); + } + + public function testQuery(): void + { + $centers = $this->centerRepository->findAll(); + + $query = $this->listAccompanyingPeriodWork->initiateQuery([], array_map(fn (Center $c) => ['center' => $c], $centers), ['calc_date' => new RollingDate(RollingDate::T_TODAY)]); + $query->setMaxResults(1); + + self::assertIsArray($query->getQuery()->getResult(AbstractQuery::HYDRATE_ARRAY)); + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Export/ListEvaluationTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Export/ListEvaluationTest.php new file mode 100644 index 000000000..d7db24a82 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Export/ListEvaluationTest.php @@ -0,0 +1,49 @@ +listEvaluation = self::$container->get(ListEvaluation::class); + $this->centerRepository = self::$container->get(CenterRepositoryInterface::class); + } + + public function testQuery(): void + { + $centers = $this->centerRepository->findAll(); + + $query = $this->listEvaluation->initiateQuery([], array_map(fn (Center $c) => ['center' => $c], $centers), ['calc_date' => new RollingDate(RollingDate::T_TODAY)]); + $query->setMaxResults(1); + + self::assertIsArray($query->getQuery()->getResult(AbstractQuery::HYDRATE_ARRAY)); + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/JobFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/JobFilterTest.php index f5836c18c..e8ed85ad4 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/JobFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/JobFilterTest.php @@ -51,7 +51,6 @@ final class JobFilterTest extends AbstractFilterTest return [ [ 'job' => new ArrayCollection($jobs), - 'job_at' => new RollingDate(RollingDate::T_FIXED_DATE, \DateTimeImmutable::createFromFormat('Y-m-d', '2020-01-01')) ] ]; } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/ReferrerFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/ReferrerFilterTest.php index 68fab1250..a77c6d9c9 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/ReferrerFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/ReferrerFilterTest.php @@ -12,6 +12,7 @@ declare(strict_types=1); namespace Chill\PersonBundle\Tests\Export\Filter\SocialWorkFilters; use Chill\MainBundle\Entity\User; +use Chill\MainBundle\Service\RollingDate\RollingDate; use Chill\MainBundle\Test\Export\AbstractFilterTest; use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Export\Filter\SocialWorkFilters\ReferrerFilter; @@ -47,8 +48,13 @@ final class ReferrerFilterTest extends AbstractFilterTest $data = []; foreach ($users as $u) { + $data[] = [ + 'accepted_agents' => $u, // some saved export does not have the parameter "agent_at" + ]; + $data[] = [ 'accepted_agents' => $u, + 'agent_at' => new RollingDate(RollingDate::T_TODAY) ]; } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/ScopeFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/ScopeFilterTest.php index f2ffce2e4..c3e29f71e 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/ScopeFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/ScopeFilterTest.php @@ -50,7 +50,6 @@ final class ScopeFilterTest extends AbstractFilterTest return [ [ 'scope' => $scopes, - 'scope_at' => new RollingDate(RollingDate::T_FIXED_DATE, \DateTimeImmutable::createFromFormat('Y-m-d', '2020-01-01')) ] ]; } diff --git a/src/Bundle/ChillPersonBundle/Tests/Repository/AccompanyingPeriodACLAwareRepositoryTest.php b/src/Bundle/ChillPersonBundle/Tests/Repository/AccompanyingPeriodACLAwareRepositoryTest.php index 050a839e6..83335c02a 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Repository/AccompanyingPeriodACLAwareRepositoryTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Repository/AccompanyingPeriodACLAwareRepositoryTest.php @@ -518,7 +518,7 @@ class AccompanyingPeriodACLAwareRepositoryTest extends KernelTestCase /** * @param array $scopes */ - private function buildPeriod(Person $person, array $scopes, User|null $creator, bool $confirm): AccompanyingPeriod + private function buildPeriod(Person $person, array $scopes, null|User $creator, bool $confirm): AccompanyingPeriod { $period = new AccompanyingPeriod(); $period->addPerson($person); diff --git a/src/Bundle/ChillPersonBundle/Tests/Repository/UserRepositoryTest.php b/src/Bundle/ChillPersonBundle/Tests/Repository/UserRepositoryTest.php new file mode 100644 index 000000000..5a99b8e4c --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Repository/UserRepositoryTest.php @@ -0,0 +1,44 @@ +userRepository = self::$container->get(UserRepository::class); + } + + public function testFindAllAsArray(): void + { + $userIterator = $this->userRepository->findAllAsArray('fr'); + + self::assertIsIterable($userIterator); + $i = 0; + foreach ($userIterator as $u) { + self::assertIsArray($u); + $i++; + } + self::assertGreaterThan(0, $i); + } +} diff --git a/src/Bundle/ChillPersonBundle/translations/messages+intl-icu.fr.yaml b/src/Bundle/ChillPersonBundle/translations/messages+intl-icu.fr.yaml index 250bca9c7..a8d2080fb 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages+intl-icu.fr.yaml +++ b/src/Bundle/ChillPersonBundle/translations/messages+intl-icu.fr.yaml @@ -135,7 +135,11 @@ exports: by_person: Filtered by person\'s geographical unit (based on address) computed at date, only units: "Filtré par zone géographique sur base de l'adresse, calculé à {datecalc, date, short}, seulement les zones suivantes: {units}" - + filter: + work: + by_treating_agent: + Filtered by treating agent at date: >- + Les agents traitant au { agent_at, date, medium }, seulement {agents} 'total persons matching the search pattern': >- { total, plural, =0 {Aucun usager ne correspond aux termes de recherche} diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index 639de0443..e142e0f8e 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -588,10 +588,6 @@ Filter by current evaluations: Filtrer les évaluations en cours ## social actions filters/aggr Filter by scope: Filtrer par service -Filter by treating agent: Filtrer les actions par agent traitant -Accepted agents: Agent traitant -"Filtered by treating agent: only %agents%": "Filtré par agent traitant: uniquement %agents%" -Group by treating agent: Grouper les actions par agent traitant Group social work actions by action type: Grouper les actions par type Group social work actions by goal: Grouper les actions par objectif @@ -1040,6 +1036,9 @@ export: Group course by scope: Grouper les parcours par service course_work: + by_treating_agent: + Calc date: Référent à la date + Group by treating agent: Grouper les actions par agent traitant by_current_action: Current action ?: Action en cours ? Group by current actions: Grouper les actions en cours @@ -1160,6 +1159,12 @@ export: Filter by treating agent scope: Filtrer les actions par service de l'agent traitant "Filtered by treating agent scope: only %scopes%": "Filtré par service de l'agent traitant: uniquement %scopes%" Calc date: Date de calcul du service de l'agent traitant + by_treating_agent: + Filter by treating agent: Filtrer les actions par agent traitant + Accepted agents: Agent traitant + Calc date: Date à laquelle l'agent est en situation de désignation sur l'action + calc_date_help: Il s'agit de la date à laquelle l'agent est actif comme agent traitant de l'action, et non la date à la quelle l'agent est désigné comme agent traitant. + "Filtered by treating agent: only %agents%": "Filtré par agent traitant: uniquement %agents%" list: person_with_acp: @@ -1187,7 +1192,9 @@ export: origin: Origine du parcours acpClosingMotive: Motif de fermeture acpJob: Métier du parcours + acpCreatedBy_id: Créé par (identifiant) acpCreatedBy: Créé par + acpUpdatedBy_id: Dernière modificaton par (identifiant) acpUpdatedBy: Dernière modification par administrativeLocation: Location administrative step: Etape diff --git a/src/Bundle/ChillReportBundle/DataFixtures/ORM/LoadReports.php b/src/Bundle/ChillReportBundle/DataFixtures/ORM/LoadReports.php index 80c1edd87..3593b612c 100644 --- a/src/Bundle/ChillReportBundle/DataFixtures/ORM/LoadReports.php +++ b/src/Bundle/ChillReportBundle/DataFixtures/ORM/LoadReports.php @@ -165,7 +165,7 @@ final class LoadReports extends AbstractFixture implements OrderedFixtureInterfa * * @return string|string[] */ - private function getRandomChoice(CustomField $field): string|array + private function getRandomChoice(CustomField $field): array|string { $choices = $field->getOptions()['choices']; $multiple = $field->getOptions()['multiple']; diff --git a/src/Bundle/ChillTaskBundle/Security/Authorization/AuthorizationEvent.php b/src/Bundle/ChillTaskBundle/Security/Authorization/AuthorizationEvent.php index 203963d80..de8b52c4c 100644 --- a/src/Bundle/ChillTaskBundle/Security/Authorization/AuthorizationEvent.php +++ b/src/Bundle/ChillTaskBundle/Security/Authorization/AuthorizationEvent.php @@ -27,7 +27,7 @@ class AuthorizationEvent extends \Symfony\Contracts\EventDispatcher\Event protected $vote; public function __construct( - private readonly Person|AbstractTask|AccompanyingPeriod|null $subject, + private readonly null|AbstractTask|AccompanyingPeriod|Person $subject, private readonly string $attribute, private readonly TokenInterface $token ) {} diff --git a/src/Bundle/ChillWopiBundle/src/Controller/Convert.php b/src/Bundle/ChillWopiBundle/src/Controller/Convert.php index 665386ffe..c4702c2b2 100644 --- a/src/Bundle/ChillWopiBundle/src/Controller/Convert.php +++ b/src/Bundle/ChillWopiBundle/src/Controller/Convert.php @@ -78,7 +78,7 @@ class Convert return new Response($response->getContent(), Response::HTTP_OK, [ 'Content-Type' => 'application/pdf', ]); - } catch (ClientExceptionInterface|TransportExceptionInterface|RedirectionExceptionInterface|ServerExceptionInterface $exception) { + } catch (ClientExceptionInterface|RedirectionExceptionInterface|ServerExceptionInterface|TransportExceptionInterface $exception) { return $this->onConversionFailed($url, $exception->getResponse()); } }