From 3f4a42adb2d98d459803e1339b7a23efdb537dd0 Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Wed, 27 Sep 2023 10:05:25 +0200 Subject: [PATCH] [export] fix calendar scope/job Filters query + unit test (partial) --- .../Export/Aggregator/JobAggregator.php | 2 +- .../Export/Aggregator/ScopeAggregator.php | 5 +- .../Export/Filter/JobFilter.php | 73 +++++++++++------- .../Export/Filter/ScopeFilter.php | 74 +++++++++++-------- .../Tests/Export/Filter/JobFilterTest.php | 12 ++- .../Tests/Export/Filter/ScopeFilterTest.php | 2 + .../translations/messages.fr.yml | 2 +- 7 files changed, 111 insertions(+), 59 deletions(-) diff --git a/src/Bundle/ChillCalendarBundle/Export/Aggregator/JobAggregator.php b/src/Bundle/ChillCalendarBundle/Export/Aggregator/JobAggregator.php index 94807d709..4de1f863b 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Aggregator/JobAggregator.php +++ b/src/Bundle/ChillCalendarBundle/Export/Aggregator/JobAggregator.php @@ -71,7 +71,7 @@ final readonly class JobAggregator implements AggregatorInterface public function buildForm(FormBuilderInterface $builder) { $builder->add('job_at', PickRollingDateType::class, [ - 'label' => 'export.aggregator.calendar.agent_job.Calc date', + 'label' => 'export.calendar.agent_job.Calc date', ]); } diff --git a/src/Bundle/ChillCalendarBundle/Export/Aggregator/ScopeAggregator.php b/src/Bundle/ChillCalendarBundle/Export/Aggregator/ScopeAggregator.php index df68b7c42..9e395de80 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Aggregator/ScopeAggregator.php +++ b/src/Bundle/ChillCalendarBundle/Export/Aggregator/ScopeAggregator.php @@ -60,7 +60,8 @@ final readonly class ScopeAggregator implements AggregatorInterface ->addSelect("IDENTITY({$p}_history.scope) AS {$p}_select") ->setParameter( "{$p}_at", - $this->rollingDateConverter->convert($data['scope_at'])) + $this->rollingDateConverter->convert($data['scope_at']) + ) ->addGroupBy("{$p}_select"); } @@ -72,7 +73,7 @@ final readonly class ScopeAggregator implements AggregatorInterface public function buildForm(FormBuilderInterface $builder) { $builder->add('scope_at', PickRollingDateType::class, [ - 'label' => 'export.aggregator.calendar.agent_scope.Calc date', + 'label' => 'export.calendar.agent_scope.Calc date', ]); } public function getFormDefaultData(): array diff --git a/src/Bundle/ChillCalendarBundle/Export/Filter/JobFilter.php b/src/Bundle/ChillCalendarBundle/Export/Filter/JobFilter.php index 1391c4968..ae36fe2f0 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Filter/JobFilter.php +++ b/src/Bundle/ChillCalendarBundle/Export/Filter/JobFilter.php @@ -12,19 +12,28 @@ declare(strict_types=1); namespace Chill\CalendarBundle\Export\Filter; use Chill\CalendarBundle\Export\Declarations; +use Chill\MainBundle\Entity\User\UserJobHistory; use Chill\MainBundle\Entity\UserJob; use Chill\MainBundle\Export\FilterInterface; +use Chill\MainBundle\Form\Type\PickRollingDateType; +use Chill\MainBundle\Service\RollingDate\RollingDate; +use Chill\MainBundle\Service\RollingDate\RollingDateConverter; use Chill\MainBundle\Templating\TranslatableStringHelper; -use Doctrine\ORM\Query\Expr\Andx; +use Doctrine\ORM\Query\Expr; use Doctrine\ORM\QueryBuilder; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Contracts\Translation\TranslatorInterface; -use function in_array; class JobFilter implements FilterInterface { - public function __construct(protected TranslatorInterface $translator, private readonly TranslatableStringHelper $translatableStringHelper) {} + private const PREFIX = 'cal_filter_job'; + + public function __construct( + private readonly RollingDateConverter $rollingDateConverter, + protected TranslatorInterface $translator, + private readonly TranslatableStringHelper $translatableStringHelper + ) {} public function addRole(): ?string { @@ -33,21 +42,29 @@ class JobFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { - if (!in_array('caluser', $qb->getAllAliases(), true)) { - $qb->join('cal.mainUser', 'caluser'); - } + $p = self::PREFIX; - $where = $qb->getDQLPart('where'); - $clause = $qb->expr()->in('caluser.userJob', ':job'); + $qb + ->leftJoin( + "cal.mainUser", + "{$p}_user" + ) + ->leftJoin( + UserJobHistory::class, + "{$p}_history", + Expr\Join::WITH, + $qb->expr()->eq("{$p}_history.user", "{$p}_user") + ) + ->andWhere($qb->expr()->in("{$p}_history.job", ":{$p}_job")) + ->andWhere( + "{$p}_history.startDate <= :{$p}_at AND ({$p}_history.endDate IS NULL OR {$p}_history.endDate > :{$p}_at)" + ) + ->setParameters([ + ["{$p}_job", $data["job"]], + ["{$p}_at", $this->rollingDateConverter->convert($data["job_at"])] + ]) + ; - if ($where instanceof Andx) { - $where->add($clause); - } else { - $where = $qb->expr()->andX($clause); - } - - $qb->add('where', $where); - $qb->setParameter('job', $data['job']); } public function applyOn(): string @@ -57,18 +74,24 @@ class JobFilter implements FilterInterface public function buildForm(FormBuilderInterface $builder) { - $builder->add('job', EntityType::class, [ - 'class' => UserJob::class, - 'choice_label' => fn (UserJob $j) => $this->translatableStringHelper->localize( - $j->getLabel() - ), - 'multiple' => true, - 'expanded' => true, - ]); + $builder + ->add('job', EntityType::class, [ + 'class' => UserJob::class, + 'choice_label' => fn (UserJob $j) => $this->translatableStringHelper->localize( + $j->getLabel() + ), + 'multiple' => true, + 'expanded' => true, + ]) + ->add('job_at', PickRollingDateType::class, [ + 'label' => 'export.calendar.agent_job.Calc date', + ]); } public function getFormDefaultData(): array { - return []; + return [ + 'job_at' => new RollingDate(RollingDate::T_TODAY), + ]; } public function describeAction($data, $format = 'string'): array diff --git a/src/Bundle/ChillCalendarBundle/Export/Filter/ScopeFilter.php b/src/Bundle/ChillCalendarBundle/Export/Filter/ScopeFilter.php index a30638d0d..30f732f9b 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Filter/ScopeFilter.php +++ b/src/Bundle/ChillCalendarBundle/Export/Filter/ScopeFilter.php @@ -13,18 +13,27 @@ namespace Chill\CalendarBundle\Export\Filter; use Chill\CalendarBundle\Export\Declarations; use Chill\MainBundle\Entity\Scope; +use Chill\MainBundle\Entity\User\UserScopeHistory; use Chill\MainBundle\Export\FilterInterface; +use Chill\MainBundle\Form\Type\PickRollingDateType; +use Chill\MainBundle\Service\RollingDate\RollingDate; +use Chill\MainBundle\Service\RollingDate\RollingDateConverter; use Chill\MainBundle\Templating\TranslatableStringHelper; -use Doctrine\ORM\Query\Expr\Andx; +use Doctrine\ORM\Query\Expr; use Doctrine\ORM\QueryBuilder; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Contracts\Translation\TranslatorInterface; -use function in_array; class ScopeFilter implements FilterInterface { - public function __construct(protected TranslatorInterface $translator, private readonly TranslatableStringHelper $translatableStringHelper) {} + private const PREFIX = 'cal_filter_scope'; + + public function __construct( + private RollingDateConverter $rollingDateConverter, + protected TranslatorInterface $translator, + private readonly TranslatableStringHelper $translatableStringHelper + ) {} public function addRole(): ?string { @@ -33,45 +42,52 @@ class ScopeFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { - if (!in_array('caluser', $qb->getAllAliases(), true)) { - $qb->join('cal.mainUser', 'caluser'); - } + $p = self::PREFIX; - $where = $qb->getDQLPart('where'); - $clause = $qb->expr()->in('caluser.mainScope', ':scope'); - - if ($where instanceof Andx) { - $where->add($clause); - } else { - $where = $qb->expr()->andX($clause); - } - - $qb->add('where', $where); - $qb->setParameter('scope', $data['scope']); + $qb + ->leftJoin("cal.mainUser", "{$p}_user") + ->leftJoin( + UserScopeHistory::class, + "{$p}_history", + Expr\Join::WITH, + $qb->expr()->eq("{$p}_history.user", "{$p}_user") + ) + ->andWhere($qb->expr()->in("{$p}_history.scope", ":{$p}_scope")) + ->andWhere( + "{$p}_history.startDate <= :{$p}_at AND ({$p}_history.endDate IS NULL OR {$p}_history.endDate > :{$p}_at)" + ) + ->setParameters([ + ["{$p}_scope", $data["scope"]], + ["{$p}_at", $this->rollingDateConverter->convert($data['scope_at'])] + ]); } - public function applyOn() + public function applyOn(): string { return Declarations::CALENDAR_TYPE; } public function buildForm(FormBuilderInterface $builder) { - $builder->add('scope', EntityType::class, [ - 'class' => Scope::class, - 'choice_label' => fn (Scope $s) => $this->translatableStringHelper->localize( - $s->getName() - ), - 'multiple' => true, - 'expanded' => true, - ]); + $builder + ->add('scope', EntityType::class, [ + 'class' => Scope::class, + 'choice_label' => fn (Scope $s) => $this->translatableStringHelper->localize( + $s->getName() + ), + 'multiple' => true, + 'expanded' => true, + ]) + ->add('scope_at', PickRollingDateType::class, [ + 'label' => 'export.calendar.agent_scope.Calc date', + ]); } public function getFormDefaultData(): array { - return []; + return ['scope_at' => new RollingDate(RollingDate::T_TODAY)]; } - public function describeAction($data, $format = 'string') + public function describeAction($data, $format = 'string'): array { $scopes = []; @@ -86,7 +102,7 @@ class ScopeFilter implements FilterInterface ]]; } - public function getTitle() + public function getTitle(): string { return 'Filter calendars by agent scope'; } diff --git a/src/Bundle/ChillCalendarBundle/Tests/Export/Filter/JobFilterTest.php b/src/Bundle/ChillCalendarBundle/Tests/Export/Filter/JobFilterTest.php index 42629896c..c1305036c 100644 --- a/src/Bundle/ChillCalendarBundle/Tests/Export/Filter/JobFilterTest.php +++ b/src/Bundle/ChillCalendarBundle/Tests/Export/Filter/JobFilterTest.php @@ -21,6 +21,7 @@ namespace Chill\CalendarBundle\Tests\Export\Filter; use Chill\CalendarBundle\Entity\Calendar; use Chill\CalendarBundle\Export\Filter\JobFilter; use Chill\MainBundle\Entity\UserJob; +use Chill\MainBundle\Service\RollingDate\RollingDate; use Chill\MainBundle\Test\Export\AbstractFilterTest; use Doctrine\ORM\EntityManagerInterface; @@ -62,7 +63,16 @@ final class JobFilterTest extends AbstractFilterTest ->setMaxResults(1) ->getResult(); - yield ['job' => $array]; + $data = []; + + foreach ($array as $a) { + $data[] = [ + 'job' => $a, + 'job_at' => new RollingDate(RollingDate::T_FIXED_DATE, \DateTimeImmutable::createFromFormat('Y-m-d', '2020-01-01')), + ]; + } + + return $data; } 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..5d54a091e 100644 --- a/src/Bundle/ChillCalendarBundle/Tests/Export/Filter/ScopeFilterTest.php +++ b/src/Bundle/ChillCalendarBundle/Tests/Export/Filter/ScopeFilterTest.php @@ -21,6 +21,7 @@ namespace Chill\CalendarBundle\Tests\Export\Filter; use Chill\CalendarBundle\Entity\Calendar; use Chill\CalendarBundle\Export\Filter\ScopeFilter; use Chill\MainBundle\Entity\Scope; +use Chill\MainBundle\Service\RollingDate\RollingDate; use Chill\MainBundle\Test\Export\AbstractFilterTest; use Doctrine\ORM\EntityManagerInterface; @@ -67,6 +68,7 @@ final class ScopeFilterTest extends AbstractFilterTest foreach ($array as $a) { $data[] = [ 'scope' => $a, + 'scope_at' => new RollingDate(RollingDate::T_FIXED_DATE, \DateTimeImmutable::createFromFormat('Y-m-d', '2020-01-01')), ]; } diff --git a/src/Bundle/ChillCalendarBundle/translations/messages.fr.yml b/src/Bundle/ChillCalendarBundle/translations/messages.fr.yml index 20e9604db..0da92fd72 100644 --- a/src/Bundle/ChillCalendarBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillCalendarBundle/translations/messages.fr.yml @@ -117,7 +117,7 @@ Group calendars by cancel reason: Grouper les rendez-vous par motif d'annulation Group calendars by month and year: Grouper les rendez-vous par mois et année Group calendars by urgency: Grouper les rendez-vous par urgent ou non -export.aggregator.calendar: +export.calendar: agent_job: Calc date: Date de calcul du métier de l'agent agent_scope: