diff --git a/.changes/unreleased/Feature-20231115-132233.yaml b/.changes/unreleased/Feature-20231115-132233.yaml new file mode 100644 index 000000000..5d3accbd4 --- /dev/null +++ b/.changes/unreleased/Feature-20231115-132233.yaml @@ -0,0 +1,6 @@ +kind: Feature +body: 'Export: add clauses on the social work start date and end date within the filter + "Filter accompanying period by accompanying period work"' +time: 2023-11-15T13:22:33.19215851+01:00 +custom: + Issue: "203" diff --git a/src/Bundle/ChillMainBundle/Form/DataMapper/RollingDateDataMapper.php b/src/Bundle/ChillMainBundle/Form/DataMapper/RollingDateDataMapper.php index 7628d6aa3..6a139de7b 100644 --- a/src/Bundle/ChillMainBundle/Form/DataMapper/RollingDateDataMapper.php +++ b/src/Bundle/ChillMainBundle/Form/DataMapper/RollingDateDataMapper.php @@ -37,9 +37,13 @@ class RollingDateDataMapper implements DataMapperInterface { $forms = iterator_to_array($forms); - $viewData = new RollingDate( - $forms['roll']->getData() ?? RollingDate::T_TODAY, - $forms['fixedDate']->getData() - ); + if (null === $forms['roll']->getData()) { + $viewData = null; + } else { + $viewData = new RollingDate( + $forms['roll']->getData(), + $forms['fixedDate']->getData() + ); + } } } diff --git a/src/Bundle/ChillMainBundle/Form/Type/PickRollingDateType.php b/src/Bundle/ChillMainBundle/Form/Type/PickRollingDateType.php index 8ceb08578..ca10530e8 100644 --- a/src/Bundle/ChillMainBundle/Form/Type/PickRollingDateType.php +++ b/src/Bundle/ChillMainBundle/Form/Type/PickRollingDateType.php @@ -54,7 +54,7 @@ class PickRollingDateType extends AbstractType { $resolver->setDefaults([ 'class' => RollingDate::class, - 'empty_data' => new RollingDate(RollingDate::T_TODAY), + 'empty_data' => null, 'constraints' => [ new Callback($this->validate(...)), ], @@ -66,6 +66,10 @@ class PickRollingDateType extends AbstractType public function validate($data, ExecutionContextInterface $context, $payload): void { + if (null === $data) { + return; + } + /** @var RollingDate $data */ if (RollingDate::T_FIXED_DATE === $data->getRoll() && null === $data->getFixedDate()) { $context diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/SocialActionFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/SocialActionFilter.php index cba86c257..dc5ffa042 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/SocialActionFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/SocialActionFilter.php @@ -12,6 +12,8 @@ declare(strict_types=1); namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters; use Chill\MainBundle\Export\FilterInterface; +use Chill\MainBundle\Form\Type\PickRollingDateType; +use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface; use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Entity\SocialWork\SocialAction; use Chill\PersonBundle\Export\Declarations; @@ -19,10 +21,17 @@ use Chill\PersonBundle\Form\Type\PickSocialActionType; use Chill\PersonBundle\Templating\Entity\SocialActionRender; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Contracts\Translation\TranslatorInterface; final readonly class SocialActionFilter implements FilterInterface { - public function __construct(private SocialActionRender $actionRender) {} + private const PREFIX = 'acp_by_social_action_filter'; + + public function __construct( + private SocialActionRender $actionRender, + private RollingDateConverterInterface $rollingDateConverter, + private TranslatorInterface $translator + ) {} public function addRole(): ?string { @@ -31,17 +40,40 @@ final readonly class SocialActionFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { - $qb->andWhere( - $qb->expr()->exists( - sprintf( - 'SELECT 1 FROM %s acp_by_social_action_filter WHERE acp_by_social_action_filter.socialAction ' - .'IN (:acp_by_social_action_filter_actions) AND acp_by_social_action_filter.accompanyingPeriod = acp', - AccompanyingPeriod\AccompanyingPeriodWork::class - ) - ) - ); + $p = self::PREFIX; - $qb->setParameter('acp_by_social_action_filter_actions', SocialAction::getDescendantsWithThisForActions($data['accepted_socialactions'])); + $dql = + sprintf( + 'SELECT 1 FROM %s acp_by_social_action_filter WHERE acp_by_social_action_filter.accompanyingPeriod = acp ', + AccompanyingPeriod\AccompanyingPeriodWork::class + ); + + if (0 < count($data['accepted_socialactions'])) { + $dql .= 'AND acp_by_social_action_filter.socialAction IN (:acp_by_social_action_filter_actions)'; + $qb->setParameter("{$p}_actions", SocialAction::getDescendantsWithThisForActions($data['accepted_socialactions'])); + } + + if (null !== ($data['start_date_after'] ?? null)) { + $dql .= " AND acp_by_social_action_filter.startDate > :{$p}_start_date_after"; + $qb->setParameter("{$p}_start_date_after", $this->rollingDateConverter->convert($data['start_date_after'])); + } + + if (null !== ($data['start_date_before'] ?? null)) { + $dql .= " AND acp_by_social_action_filter.startDate <= :{$p}_start_date_before"; + $qb->setParameter("{$p}_start_date_before", $this->rollingDateConverter->convert($data['start_date_before'])); + } + + if (null !== ($data['end_date_after'] ?? null)) { + $dql .= " AND acp_by_social_action_filter.endDate > :{$p}_end_date_after OR acp_by_social_action_filter.endDate IS NULL"; + $qb->setParameter("{$p}_end_date_after", $this->rollingDateConverter->convert($data['end_date_after'])); + } + + if (null !== ($data['end_date_before'] ?? null)) { + $dql .= " AND acp_by_social_action_filter.endDate <= :{$p}_end_date_before OR acp_by_social_action_filter.endDate IS NULL"; + $qb->setParameter("{$p}_end_date_before", $this->rollingDateConverter->convert($data['end_date_before'])); + } + + $qb->andWhere($qb->expr()->exists($dql)); } public function applyOn(): string @@ -51,14 +83,44 @@ final readonly class SocialActionFilter implements FilterInterface public function buildForm(FormBuilderInterface $builder) { - $builder->add('accepted_socialactions', PickSocialActionType::class, [ - 'multiple' => true, - ]); + $builder + ->add('accepted_socialactions', PickSocialActionType::class, [ + 'multiple' => true, + 'label' => 'export.filter.course.by_social_action.Accepted socialactions', + 'help' => 'export.filter.course.by_social_action.accepted socialations help', + ]) + ->add('start_date_after', PickRollingDateType::class, [ + 'label' => 'export.filter.course.by_social_action.start date after', + 'help' => 'export.filter.course.by_social_action.start date after help', + 'required' => false, + ]) + ->add('start_date_before', PickRollingDateType::class, [ + 'label' => 'export.filter.course.by_social_action.start date before', + 'help' => 'export.filter.course.by_social_action.start date before help', + 'required' => false, + ]) + ->add('end_date_after', PickRollingDateType::class, [ + 'label' => 'export.filter.course.by_social_action.end date after', + 'help' => 'export.filter.course.by_social_action.end date after help', + 'required' => false, + ]) + ->add('end_date_before', PickRollingDateType::class, [ + 'label' => 'export.filter.course.by_social_action.end date before', + 'help' => 'export.filter.course.by_social_action.end date before help', + 'required' => false, + ]) + ; } public function getFormDefaultData(): array { - return []; + return [ + 'accepted_social_actions' => [], + 'start_date_after' => null, + 'start_date_before' => null, + 'end_date_after' => null, + 'end_date_before' => null, + ]; } public function describeAction($data, $format = 'string'): array @@ -73,13 +135,17 @@ final readonly class SocialActionFilter implements FilterInterface ]); } - return ['Filtered by socialactions: only %socialactions%', [ + return ['export.filter.course.by_social_action.Filtered by socialactions: only %socialactions%', [ '%socialactions%' => implode(', ', $actions), + '%start_date_after%' => null === ($data['start_date_after'] ?? null) ? '('.$this->translator->trans('export.filter.course.by_social_action.date ignored').')' : $this->rollingDateConverter->convert($data['start_date_after'])->format('d-m-Y'), + '%start_date_before%' => null === ($data['start_date_before'] ?? null) ? '('.$this->translator->trans('export.filter.course.by_social_action.date ignored').')' : $this->rollingDateConverter->convert($data['start_date_before'])->format('d-m-Y'), + '%end_date_after%' => null === ($data['end_date_after'] ?? null) ? '('.$this->translator->trans('export.filter.course.by_social_action.date ignored').')' : $this->rollingDateConverter->convert($data['end_date_after'])->format('d-m-Y'), + '%end_date_before%' => null === ($data['end_date_before'] ?? null) ? '('.$this->translator->trans('export.filter.course.by_social_action.date ignored').')' : $this->rollingDateConverter->convert($data['end_date_before'])->format('d-m-Y'), ]]; } public function getTitle(): string { - return 'Filter by socialaction'; + return 'export.filter.course.by_social_action.title'; } } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/SocialActionFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/SocialActionFilterTest.php index 66768d966..65078dad2 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/SocialActionFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/SocialActionFilterTest.php @@ -11,6 +11,7 @@ declare(strict_types=1); namespace Chill\PersonBundle\Tests\Export\Filter\AccompanyingCourseFilters; +use Chill\MainBundle\Service\RollingDate\RollingDate; use Chill\MainBundle\Test\Export\AbstractFilterTest; use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Entity\SocialWork\SocialAction; @@ -50,7 +51,36 @@ final class SocialActionFilterTest extends AbstractFilterTest ->setMaxResults(1) ->getResult(); - yield ['accepted_socialactions' => $array]; + return [ + [ + 'accepted_socialactions' => $array, + 'start_date_after' => null, + 'start_date_before' => null, + 'end_date_after' => null, + 'end_date_before' => null, + ], + [ + 'accepted_socialactions' => $array, + 'start_date_after' => new RollingDate(RollingDate::T_YEAR_PREVIOUS_START), + 'start_date_before' => new RollingDate(RollingDate::T_TODAY), + 'end_date_after' => new RollingDate(RollingDate::T_YEAR_PREVIOUS_START), + 'end_date_before' => new RollingDate(RollingDate::T_TODAY), + ], + [ + 'accepted_socialactions' => [], + 'start_date_after' => new RollingDate(RollingDate::T_YEAR_PREVIOUS_START), + 'start_date_before' => new RollingDate(RollingDate::T_TODAY), + 'end_date_after' => new RollingDate(RollingDate::T_YEAR_PREVIOUS_START), + 'end_date_before' => new RollingDate(RollingDate::T_TODAY), + ], + [ + 'accepted_socialactions' => [], + 'start_date_after' => null, + 'start_date_before' => null, + 'end_date_after' => null, + 'end_date_before' => null, + ], + ]; } public function getQueryBuilders(): array diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index f45a07c5d..d55ccdfaf 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -483,9 +483,6 @@ Select a geographical layer: Choisir une couche géographique Group people by geographical unit based on his address: Grouper les usagers par zone géographique (sur base de l'adresse) Filter by person's geographical unit (based on address): Filtrer les usagers par zone géographique (sur base de l'adresse) -Filter by socialaction: Filtrer les parcours par action d'accompagnement -Accepted socialactions: Actions d'accompagnement -"Filtered by socialactions: only %socialactions%": "Filtré par action d'accompagnement: uniquement %socialactions%" Group by social action: Grouper les parcours par action d'accompagnement Filter by type of action, goals and results: "Filtrer les actions par type, objectif et résultat" @@ -1184,6 +1181,20 @@ export: by_user_job: Filter by user job: Filtrer les parcours par métier du référent "Filtered by user job: only %job%": "Filtré par métier du référent: uniquement %job%" + by_social_action: + title: Filtrer les parcours par action d'accompagnement + Accepted socialactions: Actions d'accompagnement + accepted socialations help: Si laissé vide, tous les types d'action seront pris en compte + "Filtered by socialactions: only %socialactions%": "Filtré par action d'accompagnement: uniquement %socialactions%, date d'ouverture après le %start_date_after%, et avant le %start_date_before%, date de fermeture après le %end_date_after% et avant le %end_date_before%" + start date after: Date de début de l'action après le + start date after help: Sera ignoré si laissé vide + start date before: Date de début de l'action avant le + start date before help: Sera ignoré si laissé vide + end date after: Date de fin de l'action après le + end date after help: Sera ignoré si laissé vide. Les actions sans date de fin seront toujours prises en compte. + end date before: Date de fin de l'action avant le + end date before help: Sera ignoré si laissé vide. Les actions sans date de fin seront toujours prises en compte. + date ignored: clause de date ignorée work: start_between_dates: