diff --git a/.changes/unreleased/Feature-20231204-181156.yaml b/.changes/unreleased/Feature-20231204-181156.yaml new file mode 100644 index 000000000..a3d3ce620 --- /dev/null +++ b/.changes/unreleased/Feature-20231204-181156.yaml @@ -0,0 +1,5 @@ +kind: Feature +body: 'Export: add dates on the filter "filter course by activity type"' +time: 2023-12-04T18:11:56.906524311+01:00 +custom: + Issue: "235" diff --git a/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/ActivityTypeFilter.php b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/ActivityTypeFilter.php index 31a29c2eb..7c6eb0bb2 100644 --- a/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/ActivityTypeFilter.php +++ b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/ActivityTypeFilter.php @@ -15,17 +15,22 @@ use Chill\ActivityBundle\Entity\Activity; use Chill\ActivityBundle\Entity\ActivityType; use Chill\ActivityBundle\Repository\ActivityTypeRepositoryInterface; use Chill\MainBundle\Export\FilterInterface; +use Chill\MainBundle\Form\Type\PickRollingDateType; +use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface; use Chill\MainBundle\Templating\TranslatableStringHelperInterface; use Chill\PersonBundle\Export\Declarations; use Doctrine\ORM\QueryBuilder; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\FormBuilderInterface; -class ActivityTypeFilter implements FilterInterface +final readonly class ActivityTypeFilter implements FilterInterface { + private const BASE_EXISTS = 'SELECT 1 FROM '.Activity::class.' act_type_filter_activity WHERE act_type_filter_activity.accompanyingPeriod = acp'; + public function __construct( - private readonly ActivityTypeRepositoryInterface $activityTypeRepository, - private readonly TranslatableStringHelperInterface $translatableStringHelper + private ActivityTypeRepositoryInterface $activityTypeRepository, + private TranslatableStringHelperInterface $translatableStringHelper, + private RollingDateConverterInterface $rollingDateConverter, ) {} public function addRole(): ?string @@ -35,13 +40,26 @@ class ActivityTypeFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { - $qb->andWhere( - $qb->expr()->exists( - 'SELECT 1 FROM '.Activity::class.' act_type_filter_activity - WHERE act_type_filter_activity.activityType IN (:act_type_filter_activity_types) AND act_type_filter_activity.accompanyingPeriod = acp' - ) - ); - $qb->setParameter('act_type_filter_activity_types', $data['accepted_activitytypes']); + $exists = self::BASE_EXISTS; + + if (count($data['accepted_activitytypes']) > 0) { + $exists .= ' AND act_type_filter_activity.activityType IN (:act_type_filter_activity_types)'; + $qb->setParameter('act_type_filter_activity_types', $data['accepted_activitytypes']); + } + + if (null !== $data['date_after']) { + $exists .= ' AND act_type_filter_activity.date >= :act_type_filter_activity_date_after'; + $qb->setParameter('act_type_filter_activity_date_after', $this->rollingDateConverter->convert($data['date_after'])); + } + + if (null !== $data['date_before']) { + $exists .= ' AND act_type_filter_activity.date >= :act_type_filter_activity_date_before'; + $qb->setParameter('act_type_filter_activity_date_before', $this->rollingDateConverter->convert($data['date_before'])); + } + + if (self::BASE_EXISTS !== $exists) { + $qb->andWhere($qb->expr()->exists($exists)); + } } public function applyOn() @@ -60,11 +78,27 @@ class ActivityTypeFilter implements FilterInterface 'multiple' => true, 'expanded' => true, ]); + + $builder->add('date_after', PickRollingDateType::class, [ + 'label' => 'export.filter.activity.acp_by_activity_type.activity after', + 'help' => 'export.filter.activity.acp_by_activity_type.activity after help', + 'required' => false, + ]); + + $builder->add('date_before', PickRollingDateType::class, [ + 'label' => 'export.filter.activity.acp_by_activity_type.activity before', + 'help' => 'export.filter.activity.acp_by_activity_type.activity before help', + 'required' => false, + ]); } public function getFormDefaultData(): array { - return []; + return [ + 'accepted_activitytypes' => [], + 'date_after' => null, + 'date_before' => null, + ]; } public function describeAction($data, $format = 'string'): array @@ -75,8 +109,12 @@ class ActivityTypeFilter implements FilterInterface $types[] = $this->translatableStringHelper->localize($aty->getName()); } - return ['export.filter.activity.acp_by_activity_type.acp_containing_at_least_one_%activitytypes%', [ - '%activitytypes%' => implode(', ', $types), + return ['export.filter.activity.acp_by_activity_type.acp_containing_at_least_one_activitytypes', [ + 'activitytypes' => implode(', ', $types), + 'has_date_after' => null !== $data['date_after'] ? 1 : 0, + 'date_after' => $this->rollingDateConverter->convert($data['date_after']), + 'has_date_before' => null !== $data['date_before'] ? 1 : 0, + 'date_before' => $this->rollingDateConverter->convert($data['date_before']), ]]; } diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/ActivityTypeFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/ActivityTypeFilterTest.php index 347d7466f..3701459da 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/ActivityTypeFilterTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/ActivityTypeFilterTest.php @@ -13,6 +13,7 @@ namespace Chill\ActivityBundle\Tests\Export\Filter\ACPFilters; use Chill\ActivityBundle\Entity\Activity; use Chill\ActivityBundle\Entity\ActivityType; +use Chill\MainBundle\Service\RollingDate\RollingDate; use Chill\MainBundle\Test\Export\AbstractFilterTest; use Chill\PersonBundle\Entity\AccompanyingPeriod; use Doctrine\Common\Collections\ArrayCollection; @@ -55,8 +56,30 @@ final class ActivityTypeFilterTest extends AbstractFilterTest $data = []; foreach ($array as $a) { + $data[] = [ + 'accepted_activitytypes' => [], + 'date_after' => new RollingDate(RollingDate::T_YEAR_PREVIOUS_START), + 'date_before' => new RollingDate(RollingDate::T_TODAY), + ]; $data[] = [ 'accepted_activitytypes' => new ArrayCollection([$a]), + 'date_after' => null, + 'date_before' => null, + ]; + $data[] = [ + 'accepted_activitytypes' => [$a], + 'date_after' => null, + 'date_before' => null, + ]; + $data[] = [ + 'accepted_activitytypes' => [$a], + 'date_after' => new RollingDate(RollingDate::T_YEAR_PREVIOUS_START), + 'date_before' => new RollingDate(RollingDate::T_TODAY), + ]; + $data[] = [ + 'accepted_activitytypes' => [], + 'date_after' => null, + 'date_before' => null, ]; } diff --git a/src/Bundle/ChillActivityBundle/translations/messages+intl-icu.fr.yml b/src/Bundle/ChillActivityBundle/translations/messages+intl-icu.fr.yml index 7c02e29c6..0807861cc 100644 --- a/src/Bundle/ChillActivityBundle/translations/messages+intl-icu.fr.yml +++ b/src/Bundle/ChillActivityBundle/translations/messages+intl-icu.fr.yml @@ -3,7 +3,12 @@ export: activity: course_having_activity_between_date: Only course having an activity between from and to: Seulement les parcours ayant reçu au moins un échange entre le {from, date, short} et le {to, date, short} - person_between_dates: + + acp_by_activity_type: + 'acp_containing_at_least_one_activitytypes': >- + Parcours filtrés: uniquement ceux qui contiennent au moins un échange d'un des types suivants: {activitytypes} + {has_date_after, select, 1 {, après le {date_after, date}} other {}} + {has_date_before, select, 1 {, avant le {date_before, date}} other {}} describe_action_with_no_subject: >- Filtré par personne ayant eu un échange entre le {date_from, date} et le {date_to, date} describe_action_with_subject: >- diff --git a/src/Bundle/ChillActivityBundle/translations/messages.fr.yml b/src/Bundle/ChillActivityBundle/translations/messages.fr.yml index 036f75ce3..60b84d614 100644 --- a/src/Bundle/ChillActivityBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillActivityBundle/translations/messages.fr.yml @@ -371,7 +371,10 @@ export: Receiving an activity after: Ayant reçu un échange après le Receiving an activity before: Ayant reçu un échange avant le acp_by_activity_type: - 'acp_containing_at_least_one_%activitytypes%': 'Parcours filtrés: uniquement ceux qui contiennent au moins un échange d''un des types suivants: %activitytypes%' + 'activity after': Échanges après le + activity after help: Si laissé vide, ne sera pas pris en compte + activity before: Echanges avant le + activity before help: Si laissé vide, ne sera pas pris en compte person_between_dates: Implied in an activity after this date: Impliqué dans un échange après cette date Implied in an activity before this date: Impliqué dans un échange avant cette date