From 43d9ba1ba56701c207eca52749b26092ddd8002b Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Fri, 28 Oct 2022 13:07:24 +0200 Subject: [PATCH 01/73] =?UTF-8?q?Create=2016=20new=20filters/aggregators?= =?UTF-8?q?=20in=20Bundles:=20Vend=C3=A9e,=20Person,=20Activity?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Basic file creation, with methods, namespace, and title translation --- .../ByActivityNumberAggregator.php | 60 +++++++++++++++++++ .../Aggregator/SentReceivedAggregator.php | 60 +++++++++++++++++++ .../Filter/ACPFilters/HasNoActivityFilter.php | 55 +++++++++++++++++ .../config/services/export.yaml | 12 ++++ .../translations/messages.fr.yml | 4 ++ .../ByActionNumberAggregator.php | 60 +++++++++++++++++++ .../CreatorJobAggregator.php | 60 +++++++++++++++++++ .../CreatorFilter.php | 55 +++++++++++++++++ .../CreatorJobFilter.php | 55 +++++++++++++++++ .../HasNoActionFilter.php | 55 +++++++++++++++++ .../HasNoReferrerFilter.php | 55 +++++++++++++++++ .../HasTemporaryLocationFilter.php | 55 +++++++++++++++++ .../services/exports_accompanying_course.yaml | 34 +++++++++++ .../translations/messages.fr.yml | 8 +++ 14 files changed, 628 insertions(+) create mode 100644 src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByActivityNumberAggregator.php create mode 100644 src/Bundle/ChillActivityBundle/Export/Aggregator/SentReceivedAggregator.php create mode 100644 src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/HasNoActivityFilter.php create mode 100644 src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ByActionNumberAggregator.php create mode 100644 src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/CreatorJobAggregator.php create mode 100644 src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorFilter.php create mode 100644 src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorJobFilter.php create mode 100644 src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasNoActionFilter.php create mode 100644 src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasNoReferrerFilter.php create mode 100644 src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasTemporaryLocationFilter.php diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByActivityNumberAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByActivityNumberAggregator.php new file mode 100644 index 000000000..ae7492724 --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByActivityNumberAggregator.php @@ -0,0 +1,60 @@ +addSelect('AS _aggregator') + ->addGroupBy('_aggregator'); + } + + public function applyOn(): string + { + return Declarations::ACP_TYPE; + } + + public function buildForm(FormBuilderInterface $builder) + { + // No form needed + } + + public function getLabels($key, array $values, $data) + { + return function ($value): string { + if ('_header' === $value) { + return ''; + } + }; + } + + public function getQueryKeys($data): array + { + return ['_aggregator']; + } + + public function getTitle(): string + { + return 'Group acp by activity number'; + } +} diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/SentReceivedAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/SentReceivedAggregator.php new file mode 100644 index 000000000..bde6e7f06 --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/SentReceivedAggregator.php @@ -0,0 +1,60 @@ +addSelect('AS _aggregator') + ->addGroupBy('_aggregator'); + } + + public function applyOn(): string + { + return Declarations::ACP_TYPE; + } + + public function buildForm(FormBuilderInterface $builder) + { + // No form needed + } + + public function getLabels($key, array $values, $data) + { + return function ($value): string { + if ('_header' === $value) { + return ''; + } + }; + } + + public function getQueryKeys($data): array + { + return ['_aggregator']; + } + + public function getTitle(): string + { + return 'Group activity by sentreceived'; + } +} diff --git a/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/HasNoActivityFilter.php b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/HasNoActivityFilter.php new file mode 100644 index 000000000..53abd2550 --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/HasNoActivityFilter.php @@ -0,0 +1,55 @@ +andWhere( + $qb->expr()->in('', ':') + ) + ->setParameter('', $data[]); + } + + public function applyOn(): string + { + return Declarations::ACP_TYPE; + } + + public function buildForm(FormBuilderInterface $builder) + { + $builder->add(); + } + + public function describeAction($data, $format = 'string'): array + { + return ['', [ + ]]; + } + + public function getTitle(): string + { + return 'Filter acp which has no activity'; + } +} \ No newline at end of file diff --git a/src/Bundle/ChillActivityBundle/config/services/export.yaml b/src/Bundle/ChillActivityBundle/config/services/export.yaml index 224075e6f..57153a193 100644 --- a/src/Bundle/ChillActivityBundle/config/services/export.yaml +++ b/src/Bundle/ChillActivityBundle/config/services/export.yaml @@ -120,6 +120,10 @@ services: tags: - { name: chill.export_filter, alias: 'activity_usersscope_filter' } + Chill\ActivityBundle\Export\Filter\ACPFilters\HasNoActivityFilter: + tags: + - { name: chill.export_filter, alias: 'accompanyingcourse_has_no_activity_filter' } + ## Aggregators Chill\ActivityBundle\Export\Aggregator\PersonAggregators\ActivityReasonAggregator: tags: @@ -179,3 +183,11 @@ services: Chill\ActivityBundle\Export\Aggregator\ActivityUsersJobAggregator: tags: - { name: chill.export_aggregator, alias: activity_users_job_aggregator } + + Chill\ActivityBundle\Export\Aggregator\ACPAggregators\ByActivityNumberAggregator: + tags: + - { name: chill.export_aggregator, alias: accompanyingcourse_by_activity_number_aggregator } + + Chill\ActivityBundle\Export\Aggregator\SentReceivedAggregator: + tags: + - { name: chill.export_aggregator, alias: activity_sentreceived_aggregator } diff --git a/src/Bundle/ChillActivityBundle/translations/messages.fr.yml b/src/Bundle/ChillActivityBundle/translations/messages.fr.yml index 6873b3736..d3ac822cb 100644 --- a/src/Bundle/ChillActivityBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillActivityBundle/translations/messages.fr.yml @@ -276,6 +276,10 @@ Creators: Créateurs Filter activity by userscope: Filtrer les activités par service du créateur 'Filtered activity by userscope: only %scopes%': "Filtré par service du créateur: uniquement %scopes%" Accepted userscope: Services + +Filter acp which has no activity: Filtrer les parcours qui n’ont pas d’activité +Group acp by activity number: Grouper les parcours par nombre d’activité +Group activity by sentreceived: Grouper les activités par envoyé / reçu #aggregators Activity type: Type d'activité diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ByActionNumberAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ByActionNumberAggregator.php new file mode 100644 index 000000000..c3f94b947 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ByActionNumberAggregator.php @@ -0,0 +1,60 @@ +addSelect('AS _aggregator') + ->addGroupBy('_aggregator'); + } + + public function applyOn(): string + { + return Declarations::ACP_TYPE; + } + + public function buildForm(FormBuilderInterface $builder) + { + // No form needed + } + + public function getLabels($key, array $values, $data) + { + return function ($value): string { + if ('_header' === $value) { + return ''; + } + }; + } + + public function getQueryKeys($data): array + { + return ['_aggregator']; + } + + public function getTitle(): string + { + return 'Group by number of actions'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/CreatorJobAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/CreatorJobAggregator.php new file mode 100644 index 000000000..0eb98b0d4 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/CreatorJobAggregator.php @@ -0,0 +1,60 @@ +addSelect('AS _aggregator') + ->addGroupBy('_aggregator'); + } + + public function applyOn(): string + { + return Declarations::ACP_TYPE; + } + + public function buildForm(FormBuilderInterface $builder) + { + // No form needed + } + + public function getLabels($key, array $values, $data) + { + return function ($value): string { + if ('_header' === $value) { + return ''; + } + }; + } + + public function getQueryKeys($data): array + { + return ['_aggregator']; + } + + public function getTitle(): string + { + return 'Group by creator job'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorFilter.php new file mode 100644 index 000000000..7dd78d0b4 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorFilter.php @@ -0,0 +1,55 @@ +andWhere( + $qb->expr()->in('', ':') + ) + ->setParameter('', $data[]); + } + + public function applyOn(): string + { + return Declarations::ACP_TYPE; + } + + public function buildForm(FormBuilderInterface $builder) + { + $builder->add(); + } + + public function describeAction($data, $format = 'string'): array + { + return ['', [ + ]]; + } + + public function getTitle(): string + { + return 'Filter by creator'; + } +} \ No newline at end of file diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorJobFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorJobFilter.php new file mode 100644 index 000000000..021d2227c --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorJobFilter.php @@ -0,0 +1,55 @@ +andWhere( + $qb->expr()->in('', ':') + ) + ->setParameter('', $data[]); + } + + public function applyOn(): string + { + return Declarations::ACP_TYPE; + } + + public function buildForm(FormBuilderInterface $builder) + { + $builder->add(); + } + + public function describeAction($data, $format = 'string'): array + { + return ['', [ + ]]; + } + + public function getTitle(): string + { + return 'Filter by creator job'; + } +} \ No newline at end of file diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasNoActionFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasNoActionFilter.php new file mode 100644 index 000000000..c6271b555 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasNoActionFilter.php @@ -0,0 +1,55 @@ +andWhere( + $qb->expr()->in('', ':') + ) + ->setParameter('', $data[]); + } + + public function applyOn(): string + { + return Declarations::ACP_TYPE; + } + + public function buildForm(FormBuilderInterface $builder) + { + $builder->add(); + } + + public function describeAction($data, $format = 'string'): array + { + return ['', [ + ]]; + } + + public function getTitle(): string + { + return 'Filter by which has no action'; + } +} \ No newline at end of file diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasNoReferrerFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasNoReferrerFilter.php new file mode 100644 index 000000000..f6ec58104 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasNoReferrerFilter.php @@ -0,0 +1,55 @@ +andWhere( + $qb->expr()->in('', ':') + ) + ->setParameter('', $data[]); + } + + public function applyOn(): string + { + return Declarations::ACP_TYPE; + } + + public function buildForm(FormBuilderInterface $builder) + { + $builder->add(); + } + + public function describeAction($data, $format = 'string'): array + { + return ['', [ + ]]; + } + + public function getTitle(): string + { + return 'Filter by which has no referrer'; + } +} \ No newline at end of file diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasTemporaryLocationFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasTemporaryLocationFilter.php new file mode 100644 index 000000000..18a6c3918 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasTemporaryLocationFilter.php @@ -0,0 +1,55 @@ +andWhere( + $qb->expr()->in('', ':') + ) + ->setParameter('', $data[]); + } + + public function applyOn(): string + { + return Declarations::ACP_TYPE; + } + + public function buildForm(FormBuilderInterface $builder) + { + $builder->add(); + } + + public function describeAction($data, $format = 'string'): array + { + return ['', [ + ]]; + } + + public function getTitle(): string + { + return 'Filter by temporary location'; + } +} \ No newline at end of file diff --git a/src/Bundle/ChillPersonBundle/config/services/exports_accompanying_course.yaml b/src/Bundle/ChillPersonBundle/config/services/exports_accompanying_course.yaml index 04d5d8371..71eecf4ff 100644 --- a/src/Bundle/ChillPersonBundle/config/services/exports_accompanying_course.yaml +++ b/src/Bundle/ChillPersonBundle/config/services/exports_accompanying_course.yaml @@ -103,6 +103,32 @@ services: tags: - { name: chill.export_filter, alias: accompanyingcourse_openbetweendates_filter } + chill.person.export.filter_has_temporary_location: + class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\HasTemporaryLocationFilter + tags: + - { name: chill.export_filter, alias: accompanyingcourse_has_temporary_location_filter } + + chill.person.export.filter_has_no_referrer: + class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\HasNoReferrerFilter + tags: + - { name: chill.export_filter, alias: accompanyingcourse_has_no_referrer_filter } + + chill.person.export.filter_has_no_action: + class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\HasNoActionFilter + tags: + - { name: chill.export_filter, alias: accompanyingcourse_has_no_action_filter } + + chill.person.export.filter_creator: + class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\CreatorFilter + tags: + - { name: chill.export_filter, alias: accompanyingcourse_creator_filter } + + chill.person.export.filter_creator_job: + class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\CreatorJobFilter + tags: + - { name: chill.export_filter, alias: accompanyingcourse_creator_job_filter } + + ## Aggregators chill.person.export.aggregator_referrer_scope: class: Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\ScopeAggregator @@ -191,3 +217,11 @@ services: Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\ByHouseholdCompositionAggregator: tags: - { name: chill.export_aggregator, alias: accompanyingcourse_by_household_compo_aggregator } + + Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\ByActionNumberAggregator: + tags: + - { name: chill.export_aggregator, alias: accompanyingcourse_by_action_number_aggregator } + + Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\CreatorJobAggregator: + tags: + - { name: chill.export_aggregator, alias: accompanyingcourse_creator_job_aggregator } diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index f3fd6c430..e39602dd3 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -552,6 +552,14 @@ Date from: Date de début Date to: Date de fin "Filtered by opening dates: between %datefrom% and %dateto%": "Filtrer les parcours ouverts entre deux dates: entre le %datefrom% et le %dateto%" +Filter by temporary location: Filtrer les parcours avec une localisation temporaire +Filter by which has no referrer: Filtrer les parcours sans référent +Filter by which has no action: Filtrer les parcours qui n’ont pas d’actions +Group by number of actions: Grouper les parcours par nombre d’actions +Filter by creator: Filtrer les parcours par créateur +Filter by creator job: Filtrer les parcours par métier du créateur +Group by creator job: Grouper les parcours par métier du créateur + ## social actions filters/aggr 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%" From f1fb77187fc56c937d4b8230ed0a99d3038d80bf Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Fri, 28 Oct 2022 16:26:11 +0200 Subject: [PATCH 02/73] create 13 new export/filters/aggregators in Person and AsideActivity Bundles * minor corrections on last commit * modify related files (declaration, messages.fr, repository) * yaml service declaration --- .../Aggregator/SentReceivedAggregator.php | 4 +- .../Aggregator/ByActivityTypeAggregator.php | 60 ++++++++++ .../src/Export/Declarations.php | 20 ++++ .../src/Export/Export/CountAsideActivity.php | 111 ++++++++++++++++++ .../Export/Filter/ByActivityTypeFilter.php | 55 +++++++++ .../src/Export/Filter/ByDateFilter.php | 55 +++++++++ .../Repository/AsideActivityRepository.php | 48 ++------ .../src/config/services.yaml | 30 +++++ .../src/translations/messages.fr.yml | 7 ++ .../ByEndDateAggregator.php | 60 ++++++++++ .../ByMaxDateAggregator.php | 60 ++++++++++ .../ByStartDateAggregator.php | 60 ++++++++++ .../CurrentActionAggregator.php | 60 ++++++++++ .../EvaluationFilters/ByEndDateFilter.php | 55 +++++++++ .../EvaluationFilters/ByMaxDateFilter.php | 55 +++++++++ .../EvaluationFilters/ByStartDateFilter.php | 55 +++++++++ .../CurrentEvaluationsFilter.php | 55 +++++++++ .../SocialWorkFilters/CurrentActionFilter.php | 55 +++++++++ .../config/services/exports_evaluation.yaml | 44 ++++++- .../services/exports_social_actions.yaml | 4 + .../translations/messages.fr.yml | 10 ++ 21 files changed, 923 insertions(+), 40 deletions(-) create mode 100644 src/Bundle/ChillAsideActivityBundle/src/Export/Aggregator/ByActivityTypeAggregator.php create mode 100644 src/Bundle/ChillAsideActivityBundle/src/Export/Declarations.php create mode 100644 src/Bundle/ChillAsideActivityBundle/src/Export/Export/CountAsideActivity.php create mode 100644 src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByActivityTypeFilter.php create mode 100644 src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByDateFilter.php create mode 100644 src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByEndDateAggregator.php create mode 100644 src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByMaxDateAggregator.php create mode 100644 src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByStartDateAggregator.php create mode 100644 src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/CurrentActionAggregator.php create mode 100644 src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByEndDateFilter.php create mode 100644 src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByMaxDateFilter.php create mode 100644 src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByStartDateFilter.php create mode 100644 src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/CurrentEvaluationsFilter.php create mode 100644 src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/CurrentActionFilter.php diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/SentReceivedAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/SentReceivedAggregator.php index bde6e7f06..85c37454f 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/SentReceivedAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/SentReceivedAggregator.php @@ -12,7 +12,7 @@ declare(strict_types=1); namespace Chill\ActivityBundle\Export\Aggregator; use Chill\MainBundle\Export\AggregatorInterface; -use Chill\PersonBundle\Export\Declarations; +use Chill\ActivityBundle\Export\Declarations; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; @@ -31,7 +31,7 @@ class SentReceivedAggregator implements AggregatorInterface public function applyOn(): string { - return Declarations::ACP_TYPE; + return Declarations::ACTIVITY; } public function buildForm(FormBuilderInterface $builder) diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Aggregator/ByActivityTypeAggregator.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Aggregator/ByActivityTypeAggregator.php new file mode 100644 index 000000000..36b643d53 --- /dev/null +++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Aggregator/ByActivityTypeAggregator.php @@ -0,0 +1,60 @@ +addSelect('AS _aggregator') + ->addGroupBy('_aggregator'); + } + + public function applyOn(): string + { + return Declarations::ASIDE_ACTIVITY_TYPE; + } + + public function buildForm(FormBuilderInterface $builder) + { + // No form needed + } + + public function getLabels($key, array $values, $data) + { + return function ($value): string { + if ('_header' === $value) { + return ''; + } + }; + } + + public function getQueryKeys($data): array + { + return ['_aggregator']; + } + + public function getTitle(): string + { + return 'Group by aside activity type'; + } +} diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Declarations.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Declarations.php new file mode 100644 index 000000000..0262516aa --- /dev/null +++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Declarations.php @@ -0,0 +1,20 @@ +repository = $repository; + } + + public function buildForm(FormBuilderInterface $builder) + { + // TODO: Implement buildForm() method. + } + + public function getAllowedFormattersTypes(): array + { + return [FormatterInterface::TYPE_TABULAR]; + } + + public function getDescription(): string + { + return 'Count aside activities by various parameters.'; + } + + public function getGroup(): string + { + return 'Exports of aside activities'; + } + + public function getLabels($key, array $values, $data) + { + if ('export_result' !== $key) { + throw new LogicException("the key {$key} is not used by this export"); + } + + $labels = array_combine($values, $values); + $labels['_header'] = $this->getTitle(); + + return static function ($value) use ($labels) { + return $labels[$value]; + }; + } + + public function getQueryKeys($data): array + { + return ['export_result']; + } + + public function getResult($query, $data) + { + return $query->getQuery()->getResult(Query::HYDRATE_SCALAR); + } + + public function getTitle(): string + { + return 'Count aside activities'; + } + + public function getType(): string + { + return Declarations::ASIDE_ACTIVITY_TYPE; + } + + public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) + { + $qb = $this->repository->createQueryBuilder('aside'); + + $qb->andWhere(); + + $qb->select('COUNT() AS export_result'); + + return $qb; + } + + public function requiredRole(): string + { + return ''; + } + + public function supportsModifiers(): array + { + return []; + } +} + + + + diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByActivityTypeFilter.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByActivityTypeFilter.php new file mode 100644 index 000000000..60ca914b2 --- /dev/null +++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByActivityTypeFilter.php @@ -0,0 +1,55 @@ +andWhere( + $qb->expr()->in('', ':') + ) + ->setParameter('', $data[]); + } + + public function applyOn(): string + { + return Declarations::ASIDE_ACTIVITY_TYPE; + } + + public function buildForm(FormBuilderInterface $builder) + { + $builder->add(); + } + + public function describeAction($data, $format = 'string'): array + { + return ['', [ + ]]; + } + + public function getTitle(): string + { + return 'Filter by aside activity type'; + } +} \ No newline at end of file diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByDateFilter.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByDateFilter.php new file mode 100644 index 000000000..dc6b89662 --- /dev/null +++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByDateFilter.php @@ -0,0 +1,55 @@ +andWhere( + $qb->expr()->in('', ':') + ) + ->setParameter('', $data[]); + } + + public function applyOn(): string + { + return Declarations::ASIDE_ACTIVITY_TYPE; + } + + public function buildForm(FormBuilderInterface $builder) + { + $builder->add(); + } + + public function describeAction($data, $format = 'string'): array + { + return ['', [ + ]]; + } + + public function getTitle(): string + { + return 'Filter by aside activity date'; + } +} \ No newline at end of file diff --git a/src/Bundle/ChillAsideActivityBundle/src/Repository/AsideActivityRepository.php b/src/Bundle/ChillAsideActivityBundle/src/Repository/AsideActivityRepository.php index f2ad9c072..b3dc46f87 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Repository/AsideActivityRepository.php +++ b/src/Bundle/ChillAsideActivityBundle/src/Repository/AsideActivityRepository.php @@ -12,46 +12,20 @@ declare(strict_types=1); namespace Chill\AsideActivityBundle\Repository; use Chill\AsideActivityBundle\Entity\AsideActivity; -use Doctrine\ORM\EntityManagerInterface; -use Doctrine\ORM\EntityRepository; -use Doctrine\Persistence\ObjectRepository; +use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; +use Doctrine\Persistence\ManagerRegistry; -final class AsideActivityRepository implements ObjectRepository +/** + * @method AsideActivity|null find($id, $lockMode = null, $lockVersion = null) + * @method AsideActivity|null findOneBy(array $criteria, array $orderBy = null) + * @method AsideActivity[] findAll() + * @method AsideActivity[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) + */ +final class AsideActivityRepository extends ServiceEntityRepository { - private EntityRepository $repository; - - public function __construct(EntityManagerInterface $entityManager) + public function __construct(ManagerRegistry $registry) { - $this->repository = $entityManager->getRepository(AsideActivity::class); - } - - public function find($id): ?AsideActivity - { - return $this->repository->find($id); - } - - /** - * @return AsideActivity[] - */ - public function findAll(): array - { - return $this->repository->findAll(); - } - - /** - * @param mixed|null $limit - * @param mixed|null $offset - * - * @return AsideActivity[] - */ - public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array - { - return $this->repository->findBy($criteria, $orderBy, $limit, $offset); - } - - public function findOneBy(array $criteria): ?AsideActivity - { - return $this->repository->findOneBy($criteria); + parent::__construct($registry, AsideActivity::class); } public function getClassName(): string diff --git a/src/Bundle/ChillAsideActivityBundle/src/config/services.yaml b/src/Bundle/ChillAsideActivityBundle/src/config/services.yaml index 34bb6da33..2a7c30d7c 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/config/services.yaml +++ b/src/Bundle/ChillAsideActivityBundle/src/config/services.yaml @@ -20,3 +20,33 @@ services: resource: "../Controller" autowire: true autoconfigure: true + + + ## Exports + + # indicators + Chill\AsideActivityBundle\Export\Export\CountAsideActivity: + autowire: true + autoconfigure: true + tags: + - { name: chill.export, alias: count_asideactivity } + + # filters + Chill\AsideActivityBundle\Export\Filter\ByDateFilter: + autowire: true + autoconfigure: true + tags: + - { name: chill.export_filter, alias: asideactivity_bydate_filter } + + Chill\AsideActivityBundle\Export\Filter\ByActivityTypeFilter: + autowire: true + autoconfigure: true + tags: + - { name: chill.export_filter, alias: asideactivity_activitytype_filter } + + # aggregators + Chill\AsideActivityBundle\Export\Aggregator\ByActivityTypeAggregator: + autowire: true + autoconfigure: true + tags: + - { name: chill.export_aggregator, alias: asideactivity_activitytype_aggregator } diff --git a/src/Bundle/ChillAsideActivityBundle/src/translations/messages.fr.yml b/src/Bundle/ChillAsideActivityBundle/src/translations/messages.fr.yml index 0a7be1fcd..5b298ed68 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/translations/messages.fr.yml +++ b/src/Bundle/ChillAsideActivityBundle/src/translations/messages.fr.yml @@ -166,3 +166,10 @@ Aside activities: Activités annexes Aside activity types: Types d'activités annexes Aside activity type configuration: Configuration des categories d'activités annexes Aside activity configuration: Configuration des activités annexes + +# exports +Count aside activities: Nombre d'activités annexes +Count aside activities by various parameters.: Compte le nombre d'activités annexes selon divers critères +Filter by aside activity date: Filtrer les activités annexes par date +Filter by aside activity type: Filtrer les activités annexes par type d'activité +Group by aside activity type: Grouper les activités annexes par type d'activité \ No newline at end of file diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByEndDateAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByEndDateAggregator.php new file mode 100644 index 000000000..46c3b460f --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByEndDateAggregator.php @@ -0,0 +1,60 @@ +addSelect('AS _aggregator') + ->addGroupBy('_aggregator'); + } + + public function applyOn(): string + { + return Declarations::EVAL_TYPE; + } + + public function buildForm(FormBuilderInterface $builder) + { + // No form needed + } + + public function getLabels($key, array $values, $data) + { + return function ($value): string { + if ('_header' === $value) { + return ''; + } + }; + } + + public function getQueryKeys($data): array + { + return ['_aggregator']; + } + + public function getTitle(): string + { + return 'Group by end date evaluations'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByMaxDateAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByMaxDateAggregator.php new file mode 100644 index 000000000..689b6fea3 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByMaxDateAggregator.php @@ -0,0 +1,60 @@ +addSelect('AS _aggregator') + ->addGroupBy('_aggregator'); + } + + public function applyOn(): string + { + return Declarations::EVAL_TYPE; + } + + public function buildForm(FormBuilderInterface $builder) + { + // No form needed + } + + public function getLabels($key, array $values, $data) + { + return function ($value): string { + if ('_header' === $value) { + return ''; + } + }; + } + + public function getQueryKeys($data): array + { + return ['_aggregator']; + } + + public function getTitle(): string + { + return 'Group by max date evaluations'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByStartDateAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByStartDateAggregator.php new file mode 100644 index 000000000..656054250 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByStartDateAggregator.php @@ -0,0 +1,60 @@ +addSelect('AS _aggregator') + ->addGroupBy('_aggregator'); + } + + public function applyOn(): string + { + return Declarations::EVAL_TYPE; + } + + public function buildForm(FormBuilderInterface $builder) + { + // No form needed + } + + public function getLabels($key, array $values, $data) + { + return function ($value): string { + if ('_header' === $value) { + return ''; + } + }; + } + + public function getQueryKeys($data): array + { + return ['_aggregator']; + } + + public function getTitle(): string + { + return 'Group by start date evaluations'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/CurrentActionAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/CurrentActionAggregator.php new file mode 100644 index 000000000..c69cda35d --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/CurrentActionAggregator.php @@ -0,0 +1,60 @@ +addSelect('AS _aggregator') + ->addGroupBy('_aggregator'); + } + + public function applyOn(): string + { + return Declarations::SOCIAL_WORK_ACTION_TYPE; + } + + public function buildForm(FormBuilderInterface $builder) + { + // No form needed + } + + public function getLabels($key, array $values, $data) + { + return function ($value): string { + if ('_header' === $value) { + return ''; + } + }; + } + + public function getQueryKeys($data): array + { + return ['_aggregator']; + } + + public function getTitle(): string + { + return 'Group by current actions'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByEndDateFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByEndDateFilter.php new file mode 100644 index 000000000..45fddd9c1 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByEndDateFilter.php @@ -0,0 +1,55 @@ +andWhere( + $qb->expr()->in('', ':') + ) + ->setParameter('', $data[]); + } + + public function applyOn(): string + { + return Declarations::EVAL_TYPE; + } + + public function buildForm(FormBuilderInterface $builder) + { + $builder->add(); + } + + public function describeAction($data, $format = 'string'): array + { + return ['', [ + ]]; + } + + public function getTitle(): string + { + return 'Filter by end date evaluations'; + } +} \ No newline at end of file diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByMaxDateFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByMaxDateFilter.php new file mode 100644 index 000000000..f262a0cf4 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByMaxDateFilter.php @@ -0,0 +1,55 @@ +andWhere( + $qb->expr()->in('', ':') + ) + ->setParameter('', $data[]); + } + + public function applyOn(): string + { + return Declarations::EVAL_TYPE; + } + + public function buildForm(FormBuilderInterface $builder) + { + $builder->add(); + } + + public function describeAction($data, $format = 'string'): array + { + return ['', [ + ]]; + } + + public function getTitle(): string + { + return 'Filter by max date evaluations'; + } +} \ No newline at end of file diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByStartDateFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByStartDateFilter.php new file mode 100644 index 000000000..1d694d27c --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByStartDateFilter.php @@ -0,0 +1,55 @@ +andWhere( + $qb->expr()->in('', ':') + ) + ->setParameter('', $data[]); + } + + public function applyOn(): string + { + return Declarations::EVAL_TYPE; + } + + public function buildForm(FormBuilderInterface $builder) + { + $builder->add(); + } + + public function describeAction($data, $format = 'string'): array + { + return ['', [ + ]]; + } + + public function getTitle(): string + { + return 'Filter by start date evaluations'; + } +} \ No newline at end of file diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/CurrentEvaluationsFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/CurrentEvaluationsFilter.php new file mode 100644 index 000000000..19b7ac726 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/CurrentEvaluationsFilter.php @@ -0,0 +1,55 @@ +andWhere( + $qb->expr()->in('', ':') + ) + ->setParameter('', $data[]); + } + + public function applyOn(): string + { + return Declarations::EVAL_TYPE; + } + + public function buildForm(FormBuilderInterface $builder) + { + $builder->add(); + } + + public function describeAction($data, $format = 'string'): array + { + return ['', [ + ]]; + } + + public function getTitle(): string + { + return 'Filter by current evaluations'; + } +} \ No newline at end of file diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/CurrentActionFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/CurrentActionFilter.php new file mode 100644 index 000000000..c77f9c662 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/CurrentActionFilter.php @@ -0,0 +1,55 @@ +andWhere( + $qb->expr()->in('', ':') + ) + ->setParameter('', $data[]); + } + + public function applyOn(): string + { + return Declarations::SOCIAL_WORK_ACTION_TYPE; + } + + public function buildForm(FormBuilderInterface $builder) + { + $builder->add(); + } + + public function describeAction($data, $format = 'string'): array + { + return ['', [ + ]]; + } + + public function getTitle(): string + { + return 'Filter by current actions'; + } +} \ No newline at end of file diff --git a/src/Bundle/ChillPersonBundle/config/services/exports_evaluation.yaml b/src/Bundle/ChillPersonBundle/config/services/exports_evaluation.yaml index e21e0b6c1..bffde2abc 100644 --- a/src/Bundle/ChillPersonBundle/config/services/exports_evaluation.yaml +++ b/src/Bundle/ChillPersonBundle/config/services/exports_evaluation.yaml @@ -23,6 +23,31 @@ services: tags: - { name: chill.export_filter, alias: accompanyingcourse_maxdate_filter } + Chill\PersonBundle\Export\Filter\EvaluationFilters\ByStartDateFilter: + autowire: true + autoconfigure: true + tags: + - { name: chill.export_filter, alias: evaluation_bystartdate_filter } + + Chill\PersonBundle\Export\Filter\EvaluationFilters\ByEndDateFilter: + autowire: true + autoconfigure: true + tags: + - { name: chill.export_filter, alias: evaluation_byenddate_filter } + + Chill\PersonBundle\Export\Filter\EvaluationFilters\ByMaxDateFilter: + autowire: true + autoconfigure: true + tags: + - { name: chill.export_filter, alias: evaluation_bymaxdate_filter } + + Chill\PersonBundle\Export\Filter\EvaluationFilters\CurrentEvaluationsFilter: + autowire: true + autoconfigure: true + tags: + - { name: chill.export_filter, alias: evaluation_currentevaluations_filter } + + ## Aggregators chill.person.export.aggregator_evaluationtype: class: Chill\PersonBundle\Export\Aggregator\EvaluationAggregators\EvaluationTypeAggregator @@ -30,4 +55,21 @@ services: autoconfigure: true tags: - { name: chill.export_aggregator, alias: accompanyingcourse_evaluationtype_aggregator } - \ No newline at end of file + + Chill\PersonBundle\Export\Aggregator\EvaluationAggregators\ByStartDateAggregator: + autowire: true + autoconfigure: true + tags: + - { name: chill.export_aggregator, alias: evaluation_bystartdate_aggregator } + + Chill\PersonBundle\Export\Aggregator\EvaluationAggregators\ByEndDateAggregator: + autowire: true + autoconfigure: true + tags: + - { name: chill.export_aggregator, alias: evaluation_byenddate_aggregator } + + Chill\PersonBundle\Export\Aggregator\EvaluationAggregators\ByMaxDateAggregator: + autowire: true + autoconfigure: true + tags: + - { name: chill.export_aggregator, alias: evaluation_bymaxdate_aggregator } diff --git a/src/Bundle/ChillPersonBundle/config/services/exports_social_actions.yaml b/src/Bundle/ChillPersonBundle/config/services/exports_social_actions.yaml index 450899659..28401c3b3 100644 --- a/src/Bundle/ChillPersonBundle/config/services/exports_social_actions.yaml +++ b/src/Bundle/ChillPersonBundle/config/services/exports_social_actions.yaml @@ -37,6 +37,8 @@ services: tags: - { name: chill.export_filter, alias: social_work_actions_treatingagent_filter } + Chill\PersonBundle\Export\Filter\SocialWorkFilters\CurrentActionFilter: + ## AGGREGATORS chill.person.export.aggregator_action_type: class: Chill\PersonBundle\Export\Aggregator\SocialWorkAggregators\ActionTypeAggregator @@ -86,3 +88,5 @@ services: autoconfigure: true tags: - { name: chill.export_aggregator, alias: social_work_actions_goal_result_aggregator } + + Chill\PersonBundle\Export\Aggregator\SocialWorkAggregators\CurrentActionAggregator: \ No newline at end of file diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index e39602dd3..9243a51f8 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -559,6 +559,16 @@ Group by number of actions: Grouper les parcours par nombre d’actions Filter by creator: Filtrer les parcours par créateur Filter by creator job: Filtrer les parcours par métier du créateur Group by creator job: Grouper les parcours par métier du créateur + +Filter by current actions: Filtrer les actions en cours +Group by current actions: Grouper les actions en cours +Filter by start date evaluations: Filtrer les évaluations par date de début +Filter by end date evaluations: Filtrer les évaluations par date de fin +Filter by max date evaluations: Filtrer les évaluations par date d'échéance +Filter by current evaluations: Filtrer les évaluations en cours +Group by start date evaluations: Grouper les évaluations par semaine/mois/année de la date de début +Group by end date evaluations: Grouper les évaluations par semaine/mois/année de la date de fin +Group by max date evaluations: Grouper les évaluations par semaine/mois/année de la date d'échéance ## social actions filters/aggr Filter by treating agent scope: Filtrer les actions par service de l'agent traitant From fe120086c7450bea169d3f0869474f4cfd0e3914 Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Fri, 28 Oct 2022 16:59:11 +0200 Subject: [PATCH 03/73] define queryKey in 11 aggregators --- .../ACPAggregators/ByActivityNumberAggregator.php | 6 +++--- .../Export/Aggregator/SentReceivedAggregator.php | 6 +++--- .../src/Export/Aggregator/ByActivityTypeAggregator.php | 6 +++--- .../ByActionNumberAggregator.php | 6 +++--- .../AccompanyingCourseAggregators/CreatorJobAggregator.php | 6 +++--- .../EvaluationAggregators/ByEndDateAggregator.php | 6 +++--- .../EvaluationAggregators/ByMaxDateAggregator.php | 6 +++--- .../EvaluationAggregators/ByStartDateAggregator.php | 6 +++--- .../SocialWorkAggregators/CurrentActionAggregator.php | 6 +++--- 9 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByActivityNumberAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByActivityNumberAggregator.php index ae7492724..44a3fd8f0 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByActivityNumberAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByActivityNumberAggregator.php @@ -25,8 +25,8 @@ class ByActivityNumberAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { - $qb->addSelect('AS _aggregator') - ->addGroupBy('_aggregator'); + $qb->addSelect('AS activity_by_number_aggregator') + ->addGroupBy('activity_by_number_aggregator'); } public function applyOn(): string @@ -50,7 +50,7 @@ class ByActivityNumberAggregator implements AggregatorInterface public function getQueryKeys($data): array { - return ['_aggregator']; + return ['activity_by_number_aggregator']; } public function getTitle(): string diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/SentReceivedAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/SentReceivedAggregator.php index 85c37454f..bf848d5e1 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/SentReceivedAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/SentReceivedAggregator.php @@ -25,8 +25,8 @@ class SentReceivedAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { - $qb->addSelect('AS _aggregator') - ->addGroupBy('_aggregator'); + $qb->addSelect('AS activity_sent_received_aggregator') + ->addGroupBy('activity_sent_received_aggregator'); } public function applyOn(): string @@ -50,7 +50,7 @@ class SentReceivedAggregator implements AggregatorInterface public function getQueryKeys($data): array { - return ['_aggregator']; + return ['activity_sent_received_aggregator']; } public function getTitle(): string diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Aggregator/ByActivityTypeAggregator.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Aggregator/ByActivityTypeAggregator.php index 36b643d53..bbbdb6284 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Export/Aggregator/ByActivityTypeAggregator.php +++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Aggregator/ByActivityTypeAggregator.php @@ -25,8 +25,8 @@ class ByActivityTypeAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { - $qb->addSelect('AS _aggregator') - ->addGroupBy('_aggregator'); + $qb->addSelect('AS aside_by_activity_type_aggregator') + ->addGroupBy('aside_by_activity_type_aggregator'); } public function applyOn(): string @@ -50,7 +50,7 @@ class ByActivityTypeAggregator implements AggregatorInterface public function getQueryKeys($data): array { - return ['_aggregator']; + return ['aside_by_activity_type_aggregator']; } public function getTitle(): string diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ByActionNumberAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ByActionNumberAggregator.php index c3f94b947..3c16aa161 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ByActionNumberAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ByActionNumberAggregator.php @@ -25,8 +25,8 @@ class ByActionNumberAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { - $qb->addSelect('AS _aggregator') - ->addGroupBy('_aggregator'); + $qb->addSelect('AS acp_by_action_number_aggregator') + ->addGroupBy('acp_by_action_number_aggregator'); } public function applyOn(): string @@ -50,7 +50,7 @@ class ByActionNumberAggregator implements AggregatorInterface public function getQueryKeys($data): array { - return ['_aggregator']; + return ['acp_by_action_number_aggregator']; } public function getTitle(): string diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/CreatorJobAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/CreatorJobAggregator.php index 0eb98b0d4..1449091e7 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/CreatorJobAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/CreatorJobAggregator.php @@ -25,8 +25,8 @@ class CreatorJobAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { - $qb->addSelect('AS _aggregator') - ->addGroupBy('_aggregator'); + $qb->addSelect('AS acp_creator_job_aggregator') + ->addGroupBy('acp_creator_job_aggregator'); } public function applyOn(): string @@ -50,7 +50,7 @@ class CreatorJobAggregator implements AggregatorInterface public function getQueryKeys($data): array { - return ['_aggregator']; + return ['acp_creator_job_aggregator']; } public function getTitle(): string diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByEndDateAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByEndDateAggregator.php index 46c3b460f..9c90c8dd3 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByEndDateAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByEndDateAggregator.php @@ -25,8 +25,8 @@ class ByEndDateAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { - $qb->addSelect('AS _aggregator') - ->addGroupBy('_aggregator'); + $qb->addSelect('AS eval_by_end_date_aggregator') + ->addGroupBy('eval_by_end_date_aggregator'); } public function applyOn(): string @@ -50,7 +50,7 @@ class ByEndDateAggregator implements AggregatorInterface public function getQueryKeys($data): array { - return ['_aggregator']; + return ['eval_by_end_date_aggregator']; } public function getTitle(): string diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByMaxDateAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByMaxDateAggregator.php index 689b6fea3..ded9fb685 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByMaxDateAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByMaxDateAggregator.php @@ -25,8 +25,8 @@ class ByMaxDateAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { - $qb->addSelect('AS _aggregator') - ->addGroupBy('_aggregator'); + $qb->addSelect('AS eval_by_max_date_aggregator') + ->addGroupBy('eval_by_max_date_aggregator'); } public function applyOn(): string @@ -50,7 +50,7 @@ class ByMaxDateAggregator implements AggregatorInterface public function getQueryKeys($data): array { - return ['_aggregator']; + return ['eval_by_max_date_aggregator']; } public function getTitle(): string diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByStartDateAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByStartDateAggregator.php index 656054250..121171d8c 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByStartDateAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByStartDateAggregator.php @@ -25,8 +25,8 @@ class ByStartDateAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { - $qb->addSelect('AS _aggregator') - ->addGroupBy('_aggregator'); + $qb->addSelect('AS eval_by_start_date_aggregator') + ->addGroupBy('eval_by_start_date_aggregator'); } public function applyOn(): string @@ -50,7 +50,7 @@ class ByStartDateAggregator implements AggregatorInterface public function getQueryKeys($data): array { - return ['_aggregator']; + return ['eval_by_start_date_aggregator']; } public function getTitle(): string diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/CurrentActionAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/CurrentActionAggregator.php index c69cda35d..9a9c56afc 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/CurrentActionAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/CurrentActionAggregator.php @@ -25,8 +25,8 @@ class CurrentActionAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { - $qb->addSelect('AS _aggregator') - ->addGroupBy('_aggregator'); + $qb->addSelect('AS acpw_current_action_aggregator') + ->addGroupBy('acpw_current_action_aggregator'); } public function applyOn(): string @@ -50,7 +50,7 @@ class CurrentActionAggregator implements AggregatorInterface public function getQueryKeys($data): array { - return ['_aggregator']; + return ['acpw_current_action_aggregator']; } public function getTitle(): string From 8527c1907313f91d0d76a4adde1de369078518ad Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Mon, 31 Oct 2022 11:18:29 +0100 Subject: [PATCH 04/73] acp HasNoAction Filter (UNTESTED) --- .../HasNoActionFilter.php | 15 +++++++-------- .../translations/messages.fr.yml | 1 + 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasNoActionFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasNoActionFilter.php index c6271b555..a26628e95 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasNoActionFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasNoActionFilter.php @@ -25,11 +25,11 @@ class HasNoActionFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { - $qb - ->andWhere( - $qb->expr()->in('', ':') - ) - ->setParameter('', $data[]); + if (!in_array('acpw', $qb->getAllAliases(), true)) { + $qb->join('acp.works', 'acpw'); + } + + $qb->andWhere('COUNT(acp.works) IS NULL'); } public function applyOn(): string @@ -39,13 +39,12 @@ class HasNoActionFilter implements FilterInterface public function buildForm(FormBuilderInterface $builder) { - $builder->add(); + // no form } public function describeAction($data, $format = 'string'): array { - return ['', [ - ]]; + return ['Filtered acp which has no actions']; } public function getTitle(): string diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index 9243a51f8..be47a06c2 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -555,6 +555,7 @@ Date to: Date de fin Filter by temporary location: Filtrer les parcours avec une localisation temporaire Filter by which has no referrer: Filtrer les parcours sans référent Filter by which has no action: Filtrer les parcours qui n’ont pas d’actions +Filtered acp which has no actions: Filtré les parcours qui n'ont pas d'actions Group by number of actions: Grouper les parcours par nombre d’actions Filter by creator: Filtrer les parcours par créateur Filter by creator job: Filtrer les parcours par métier du créateur From a0fc9d4de5996b497c231f7d52d1b6fc6afcb0b3 Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Mon, 31 Oct 2022 11:43:41 +0100 Subject: [PATCH 05/73] SocialWork: Current actions filter + aggregator --- .../CurrentActionAggregator.php | 17 ++++++++++++++++- .../SocialWorkFilters/CurrentActionFilter.php | 11 +++-------- .../translations/messages.fr.yml | 3 +++ 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/CurrentActionAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/CurrentActionAggregator.php index 9a9c56afc..219aaf9cd 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/CurrentActionAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/CurrentActionAggregator.php @@ -25,7 +25,11 @@ class CurrentActionAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { - $qb->addSelect('AS acpw_current_action_aggregator') + $qb + ->addSelect(' + (CASE true WHEN acpw.startDate IS NULL ELSE false END) + AS acpw_current_action_aggregator + ') ->addGroupBy('acpw_current_action_aggregator'); } @@ -45,6 +49,17 @@ class CurrentActionAggregator implements AggregatorInterface if ('_header' === $value) { return ''; } + switch ($value) { + case true: + return 'Current action'; + + case false: + return 'Not current action'; + + default: + throw new \LogicException(sprintf('The value %s is not valid', $value)); + } + }; } diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/CurrentActionFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/CurrentActionFilter.php index c77f9c662..ecbe6086a 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/CurrentActionFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/CurrentActionFilter.php @@ -25,11 +25,7 @@ class CurrentActionFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { - $qb - ->andWhere( - $qb->expr()->in('', ':') - ) - ->setParameter('', $data[]); + $qb->andWhere('acpw.startDate IS NULL'); } public function applyOn(): string @@ -39,13 +35,12 @@ class CurrentActionFilter implements FilterInterface public function buildForm(FormBuilderInterface $builder) { - $builder->add(); + //no form } public function describeAction($data, $format = 'string'): array { - return ['', [ - ]]; + return ['Filtered by current action']; } public function getTitle(): string diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index be47a06c2..1be565938 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -562,7 +562,10 @@ Filter by creator job: Filtrer les parcours par métier du créateur Group by creator job: Grouper les parcours par métier du créateur Filter by current actions: Filtrer les actions en cours +Filtered by current action: Filtré les actions en cours Group by current actions: Grouper les actions en cours +Current action: Actions en cours +Not current action: Actions terminées Filter by start date evaluations: Filtrer les évaluations par date de début Filter by end date evaluations: Filtrer les évaluations par date de fin Filter by max date evaluations: Filtrer les évaluations par date d'échéance From 441c5c8da7b4be9a07bf8cd6100bcbbb8fc9b20d Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Mon, 31 Oct 2022 14:58:25 +0100 Subject: [PATCH 06/73] remove ByMaxDateFilter (evaluation) --- .../EvaluationFilters/ByMaxDateFilter.php | 55 ------------------- 1 file changed, 55 deletions(-) delete mode 100644 src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByMaxDateFilter.php diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByMaxDateFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByMaxDateFilter.php deleted file mode 100644 index f262a0cf4..000000000 --- a/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByMaxDateFilter.php +++ /dev/null @@ -1,55 +0,0 @@ -andWhere( - $qb->expr()->in('', ':') - ) - ->setParameter('', $data[]); - } - - public function applyOn(): string - { - return Declarations::EVAL_TYPE; - } - - public function buildForm(FormBuilderInterface $builder) - { - $builder->add(); - } - - public function describeAction($data, $format = 'string'): array - { - return ['', [ - ]]; - } - - public function getTitle(): string - { - return 'Filter by max date evaluations'; - } -} \ No newline at end of file From 05f9a83bed50e5e88864047b9eabd1b37dc62119 Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Mon, 31 Oct 2022 15:15:35 +0100 Subject: [PATCH 07/73] Evaluation HavingEndDate Aggregator --- .../HavingEndDateAggregator.php | 75 +++++++++++++++++++ .../translations/messages.fr.yml | 4 + 2 files changed, 79 insertions(+) create mode 100644 src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/HavingEndDateAggregator.php diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/HavingEndDateAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/HavingEndDateAggregator.php new file mode 100644 index 000000000..8def8cf88 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/HavingEndDateAggregator.php @@ -0,0 +1,75 @@ +addSelect(' + CASE true WHEN workeval.endDAte IS NULL ELSE false END + AS eval_enddate_aggregator + ') + ->addGroupBy('eval_enddate_aggregator'); + } + + public function applyOn(): string + { + return Declarations::EVAL_TYPE; + } + + public function buildForm(FormBuilderInterface $builder) + { + // No form needed + } + + public function getLabels($key, array $values, $data) + { + return function ($value): string { + if ('_header' === $value) { + return ''; + } + switch ($value) { + case true: + return 'enddate is specified'; + + case false: + return 'enddate is not specified'; + + default: + throw new \LogicException(sprintf('The value %s is not valid', $value)); + } + + }; + } + + public function getQueryKeys($data): array + { + return ['eval_enddate_aggregator']; + } + + public function getTitle(): string + { + return 'Group evaluations having end date'; + } +} diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index 1be565938..bab2845ed 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -611,6 +611,10 @@ maxdate is specified: la date d'échéance est spécifiée maxdate is not specified: la date d'échéance n'est pas spécifiée "Filtered by maxdate: only %choice%": "Filtré par date d'échéance: uniquement si %choice%" +Group evaluations having end date: Grouper les évaluations qui ont une date de fin +enddate is specified: la date de fin est spécifiée +enddate is not specified: la date de fin n'est pas spécifiée + ## household filters/aggr Filter by composition: Filtrer les ménages par composition familiale Accepted composition: Composition familiale From d48474bbc901d9ca236acff95d424bfe2a9f17e7 Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Mon, 31 Oct 2022 15:32:34 +0100 Subject: [PATCH 08/73] HasNoReferrer filter --- .../HasNoReferrerFilter.php | 29 +++++++++++++++---- .../translations/messages.fr.yml | 2 ++ 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasNoReferrerFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasNoReferrerFilter.php index f6ec58104..c37d423f8 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasNoReferrerFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasNoReferrerFilter.php @@ -12,9 +12,13 @@ declare(strict_types=1); namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters; use Chill\MainBundle\Export\FilterInterface; +use Chill\MainBundle\Form\Type\ChillDateType; +use Chill\PersonBundle\Entity\AccompanyingPeriod\UserHistory; use Chill\PersonBundle\Export\Declarations; +use Doctrine\DBAL\Types\Types; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; +use DateTime; class HasNoReferrerFilter implements FilterInterface { @@ -26,10 +30,18 @@ class HasNoReferrerFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { $qb - ->andWhere( - $qb->expr()->in('', ':') - ) - ->setParameter('', $data[]); + ->andWhere(' + NOT EXISTS ( + SELECT 1 FROM '. UserHistory::class .' uh + WHERE uh.startDate < :date + AND ( + uh.endDate IS NULL + or uh.endDate > :date + ) + AND uh.accompanyingPeriod = acp + ) + ') + ->setParameter('date', $data['calc_date'], Types::DATE_IMMUTABLE); } public function applyOn(): string @@ -39,12 +51,17 @@ class HasNoReferrerFilter implements FilterInterface public function buildForm(FormBuilderInterface $builder) { - $builder->add(); + $builder + ->add('calc_date', ChillDateType::class, [ + 'label' => 'Has no referrer on this date', + 'data' => new DateTime(), + ]); } public function describeAction($data, $format = 'string'): array { - return ['', [ + return ['Filtered acp which has no referrer on date: %date%', [ + '%date%' => $data['calc_date']->format('d-m-Y'), ]]; } diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index bab2845ed..d2f8410ed 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -554,6 +554,8 @@ Date to: Date de fin Filter by temporary location: Filtrer les parcours avec une localisation temporaire Filter by which has no referrer: Filtrer les parcours sans référent +"Filtered acp which has no referrer on date: %date%": "Filtré les parcours sans référent à cette date: %date%" +Has no referrer on this date: N'a pas de référent à cette date Filter by which has no action: Filtrer les parcours qui n’ont pas d’actions Filtered acp which has no actions: Filtré les parcours qui n'ont pas d'actions Group by number of actions: Grouper les parcours par nombre d’actions From e3846829e18511a3bc8dd5fd0c540892bf729a0f Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Mon, 31 Oct 2022 15:46:29 +0100 Subject: [PATCH 09/73] ByStartDateFilter evaluation --- .../EvaluationFilters/ByStartDateFilter.php | 24 ++++++++++++++----- .../translations/messages.fr.yml | 3 +++ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByStartDateFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByStartDateFilter.php index 1d694d27c..afa3c212a 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByStartDateFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByStartDateFilter.php @@ -12,9 +12,12 @@ declare(strict_types=1); namespace Chill\PersonBundle\Export\Filter\EvaluationFilters; use Chill\MainBundle\Export\FilterInterface; +use Chill\MainBundle\Form\Type\ChillDateType; use Chill\PersonBundle\Export\Declarations; +use Doctrine\DBAL\Types\Types; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; +use DateTime; class ByStartDateFilter implements FilterInterface { @@ -26,10 +29,9 @@ class ByStartDateFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { $qb - ->andWhere( - $qb->expr()->in('', ':') - ) - ->setParameter('', $data[]); + ->andWhere('workeval.startDate IS BETWEEN :start_date and :end_date') + ->setParameter('start_date', $data['start_date'], Types::DATE_IMMUTABLE) + ->setParameter('end_date', $data['end_date'], Types::DATE_IMMUTABLE); } public function applyOn(): string @@ -39,12 +41,22 @@ class ByStartDateFilter implements FilterInterface public function buildForm(FormBuilderInterface $builder) { - $builder->add(); + $builder + ->add('start_date', ChillDateType::class, [ + 'label' => 'start period date', + 'data' => new DateTime(), + ]) + ->add('end_date', ChillDateType::class, [ + 'label' => 'end period date', + 'data' => new DateTime(), + ]); } public function describeAction($data, $format = 'string'): array { - return ['', [ + return ['Filtered by start date: between %start_date% and %end_date%', [ + '%start_date%' => $data['start_date']->format('d-m-Y'), + '%end_date%' => $data['end_date']->format('d-m-Y'), ]]; } diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index d2f8410ed..7bd0294b8 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -569,6 +569,9 @@ Group by current actions: Grouper les actions en cours Current action: Actions en cours Not current action: Actions terminées Filter by start date evaluations: Filtrer les évaluations par date de début +start period date: Date de début de la période +end period date: Date de fin de la période +"Filtered by start date: between %start_date% and %end_date%": "Filtré par la date de début: entre %start_date% et %end_date%" Filter by end date evaluations: Filtrer les évaluations par date de fin Filter by max date evaluations: Filtrer les évaluations par date d'échéance Filter by current evaluations: Filtrer les évaluations en cours From 791ea25e4f69f25f1e794bb74995df2ce27d05ea Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Mon, 31 Oct 2022 15:51:35 +0100 Subject: [PATCH 10/73] ByEndDateFilter evaluation --- .../EvaluationFilters/ByEndDateFilter.php | 24 ++++++++++++++----- .../translations/messages.fr.yml | 6 ++--- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByEndDateFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByEndDateFilter.php index 45fddd9c1..d40170447 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByEndDateFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByEndDateFilter.php @@ -12,9 +12,12 @@ declare(strict_types=1); namespace Chill\PersonBundle\Export\Filter\EvaluationFilters; use Chill\MainBundle\Export\FilterInterface; +use Chill\MainBundle\Form\Type\ChillDateType; use Chill\PersonBundle\Export\Declarations; +use Doctrine\DBAL\Types\Types; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; +use DateTime; class ByEndDateFilter implements FilterInterface { @@ -26,10 +29,9 @@ class ByEndDateFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { $qb - ->andWhere( - $qb->expr()->in('', ':') - ) - ->setParameter('', $data[]); + ->andWhere('workeval.endDate IS BETWEEN :start_date and :end_date') + ->setParameter('start_date', $data['start_date'], Types::DATE_IMMUTABLE) + ->setParameter('end_date', $data['end_date'], Types::DATE_IMMUTABLE); } public function applyOn(): string @@ -39,12 +41,22 @@ class ByEndDateFilter implements FilterInterface public function buildForm(FormBuilderInterface $builder) { - $builder->add(); + $builder + ->add('start_date', ChillDateType::class, [ + 'label' => 'start period date', + 'data' => new DateTime(), + ]) + ->add('end_date', ChillDateType::class, [ + 'label' => 'end period date', + 'data' => new DateTime(), + ]); } public function describeAction($data, $format = 'string'): array { - return ['', [ + return ['Filtered by end date: between %start_date% and %end_date%', [ + '%start_date%' => $data['start_date']->format('d-m-Y'), + '%end_date%' => $data['end_date']->format('d-m-Y'), ]]; } diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index 7bd0294b8..3e3ec4a2c 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -569,11 +569,11 @@ Group by current actions: Grouper les actions en cours Current action: Actions en cours Not current action: Actions terminées Filter by start date evaluations: Filtrer les évaluations par date de début +Filter by end date evaluations: Filtrer les évaluations par date de fin start period date: Date de début de la période end period date: Date de fin de la période -"Filtered by start date: between %start_date% and %end_date%": "Filtré par la date de début: entre %start_date% et %end_date%" -Filter by end date evaluations: Filtrer les évaluations par date de fin -Filter by max date evaluations: Filtrer les évaluations par date d'échéance +"Filtered by start date: between %start_date% and %end_date%": "Filtré par la date de début: comprise entre %start_date% et %end_date%" +"Filtered by end date: between %start_date% and %end_date%": "Filtré par la date de fin: comprise entre %start_date% et %end_date%" Filter by current evaluations: Filtrer les évaluations en cours Group by start date evaluations: Grouper les évaluations par semaine/mois/année de la date de début Group by end date evaluations: Grouper les évaluations par semaine/mois/année de la date de fin From 68b9c171a6afbb1d5e9eff4833ae8b905126c034 Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Mon, 31 Oct 2022 15:54:41 +0100 Subject: [PATCH 11/73] CurrentEvaluationsFilter --- .../EvaluationFilters/CurrentEvaluationsFilter.php | 11 +++-------- .../ChillPersonBundle/translations/messages.fr.yml | 1 + 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/CurrentEvaluationsFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/CurrentEvaluationsFilter.php index 19b7ac726..293e551f1 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/CurrentEvaluationsFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/CurrentEvaluationsFilter.php @@ -25,11 +25,7 @@ class CurrentEvaluationsFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { - $qb - ->andWhere( - $qb->expr()->in('', ':') - ) - ->setParameter('', $data[]); + $qb->andWhere('workeval.endDate IS NULL'); } public function applyOn(): string @@ -39,13 +35,12 @@ class CurrentEvaluationsFilter implements FilterInterface public function buildForm(FormBuilderInterface $builder) { - $builder->add(); + //no form needed } public function describeAction($data, $format = 'string'): array { - return ['', [ - ]]; + return ['Filtered by current evaluations']; } public function getTitle(): string diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index 3e3ec4a2c..46cd974d3 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -575,6 +575,7 @@ end period date: Date de fin de la période "Filtered by start date: between %start_date% and %end_date%": "Filtré par la date de début: comprise entre %start_date% et %end_date%" "Filtered by end date: between %start_date% and %end_date%": "Filtré par la date de fin: comprise entre %start_date% et %end_date%" Filter by current evaluations: Filtrer les évaluations en cours +"Filtered by current evaluations": "Filtré selon les évaluations en cours" Group by start date evaluations: Grouper les évaluations par semaine/mois/année de la date de début Group by end date evaluations: Grouper les évaluations par semaine/mois/année de la date de fin Group by max date evaluations: Grouper les évaluations par semaine/mois/année de la date d'échéance From f8b5078997be88e3c8afd92d9e96cec1132ab33e Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Mon, 31 Oct 2022 16:10:24 +0100 Subject: [PATCH 12/73] HasNoActivityFilter (in activity) --- .../Filter/ACPFilters/HasNoActivityFilter.php | 23 +++++++++++++------ .../translations/messages.fr.yml | 1 + 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/HasNoActivityFilter.php b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/HasNoActivityFilter.php index 53abd2550..13aaf475a 100644 --- a/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/HasNoActivityFilter.php +++ b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/HasNoActivityFilter.php @@ -11,10 +11,12 @@ declare(strict_types=1); namespace Chill\ActivityBundle\Export\Filter\ACPFilters; +use Chill\ActivityBundle\Entity\Activity; use Chill\MainBundle\Export\FilterInterface; use Chill\PersonBundle\Export\Declarations; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; +use Doctrine\ORM\Query\Expr; class HasNoActivityFilter implements FilterInterface { @@ -25,11 +27,19 @@ class HasNoActivityFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { + if (!in_array('activity', $qb->getAllAliases(), true)) { + $qb->join(Activity::class, 'activity', Expr\Join::WITH, 'activity.accompanyingPeriod = acp'); + } + $qb - ->andWhere( - $qb->expr()->in('', ':') - ) - ->setParameter('', $data[]); + //->andWhere('COUNT(acp.activities) IS NULL') + //TODO check this: + ->andWhere(' + NOT EXISTS ( + SELECT 1 FROM '. Activity::class .' activity + WHERE activity.accompanyingPeriod = acp + ) + '); } public function applyOn(): string @@ -39,13 +49,12 @@ class HasNoActivityFilter implements FilterInterface public function buildForm(FormBuilderInterface $builder) { - $builder->add(); + //no form needed } public function describeAction($data, $format = 'string'): array { - return ['', [ - ]]; + return ['Filtered acp which has no activities']; } public function getTitle(): string diff --git a/src/Bundle/ChillActivityBundle/translations/messages.fr.yml b/src/Bundle/ChillActivityBundle/translations/messages.fr.yml index d3ac822cb..e3fcfd2b2 100644 --- a/src/Bundle/ChillActivityBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillActivityBundle/translations/messages.fr.yml @@ -278,6 +278,7 @@ Filter activity by userscope: Filtrer les activités par service du créateur Accepted userscope: Services Filter acp which has no activity: Filtrer les parcours qui n’ont pas d’activité +Filtered acp which has no activities: Filtrés les parcours qui n'ont pas d'activités Group acp by activity number: Grouper les parcours par nombre d’activité Group activity by sentreceived: Grouper les activités par envoyé / reçu From ed04c276c23de6817e6960bd9337bfce8bce8485 Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Mon, 31 Oct 2022 16:18:10 +0100 Subject: [PATCH 13/73] SentReceivedAggregator (in activity) --- .../Export/Aggregator/SentReceivedAggregator.php | 16 +++++++++++++--- .../CurrentActionAggregator.php | 1 - 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/SentReceivedAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/SentReceivedAggregator.php index bf848d5e1..b5300834c 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/SentReceivedAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/SentReceivedAggregator.php @@ -25,8 +25,8 @@ class SentReceivedAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { - $qb->addSelect('AS activity_sent_received_aggregator') - ->addGroupBy('activity_sent_received_aggregator'); + $qb->addSelect('activity.sentReceived AS activity_sentreceived_aggregator') + ->addGroupBy('activity_sentreceived_aggregator'); } public function applyOn(): string @@ -45,12 +45,22 @@ class SentReceivedAggregator implements AggregatorInterface if ('_header' === $value) { return ''; } + switch ($value) { + case 'sent': + return 'is sent'; + + case 'received': + return 'is received'; + + default: + throw new \LogicException(sprintf('The value %s is not valid', $value)); + } }; } public function getQueryKeys($data): array { - return ['activity_sent_received_aggregator']; + return ['activity_sentreceived_aggregator']; } public function getTitle(): string diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/CurrentActionAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/CurrentActionAggregator.php index 219aaf9cd..d9120d121 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/CurrentActionAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/CurrentActionAggregator.php @@ -59,7 +59,6 @@ class CurrentActionAggregator implements AggregatorInterface default: throw new \LogicException(sprintf('The value %s is not valid', $value)); } - }; } From 7dc34cb3572542f82de57c9d7027f447cd05f572 Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Wed, 2 Nov 2022 10:15:08 +0100 Subject: [PATCH 14/73] correct config file --- .../ChillPersonBundle/config/services/exports_evaluation.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Bundle/ChillPersonBundle/config/services/exports_evaluation.yaml b/src/Bundle/ChillPersonBundle/config/services/exports_evaluation.yaml index bffde2abc..a389c1f80 100644 --- a/src/Bundle/ChillPersonBundle/config/services/exports_evaluation.yaml +++ b/src/Bundle/ChillPersonBundle/config/services/exports_evaluation.yaml @@ -35,7 +35,7 @@ services: tags: - { name: chill.export_filter, alias: evaluation_byenddate_filter } - Chill\PersonBundle\Export\Filter\EvaluationFilters\ByMaxDateFilter: + Chill\PersonBundle\Export\Filter\EvaluationFilters\MaxDateFilter: autowire: true autoconfigure: true tags: From dc149c7fd5b681f7a4bf61ed5c3964f447a341ce Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Wed, 2 Nov 2022 10:26:44 +0100 Subject: [PATCH 15/73] correct namespace and use statements --- .../src/Export/Aggregator/ByActivityTypeAggregator.php | 2 +- src/Bundle/ChillAsideActivityBundle/src/Export/Declarations.php | 2 +- .../src/Export/Export/CountAsideActivity.php | 2 +- .../src/Export/Filter/ByActivityTypeFilter.php | 2 +- .../ChillAsideActivityBundle/src/Export/Filter/ByDateFilter.php | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Aggregator/ByActivityTypeAggregator.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Aggregator/ByActivityTypeAggregator.php index bbbdb6284..d15d107aa 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Export/Aggregator/ByActivityTypeAggregator.php +++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Aggregator/ByActivityTypeAggregator.php @@ -12,7 +12,7 @@ declare(strict_types=1); namespace Chill\AsideActivityBundle\Export\Aggregator; use Chill\MainBundle\Export\AggregatorInterface; -use ChillAsideActivityBundle\Export\Declarations; +use Chill\AsideActivityBundle\Export\Declarations; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Declarations.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Declarations.php index 0262516aa..7ddbec6de 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Export/Declarations.php +++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Declarations.php @@ -9,7 +9,7 @@ declare(strict_types=1); * the LICENSE file that was distributed with this source code. */ -namespace ChillAsideActivityBundle\Export; +namespace Chill\AsideActivityBundle\Export; /** * This class declare constants used for the export framework. diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Export/CountAsideActivity.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Export/CountAsideActivity.php index 7af39568e..d6c391121 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Export/Export/CountAsideActivity.php +++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Export/CountAsideActivity.php @@ -15,7 +15,7 @@ use Chill\AsideActivityBundle\Repository\AsideActivityRepository; use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\GroupedExportInterface; -use ChillAsideActivityBundle\Export\Declarations; +use Chill\AsideActivityBundle\Export\Declarations; use Symfony\Component\Form\FormBuilderInterface; use Doctrine\ORM\Query; use LogicException; diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByActivityTypeFilter.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByActivityTypeFilter.php index 60ca914b2..53bd904fa 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByActivityTypeFilter.php +++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByActivityTypeFilter.php @@ -12,7 +12,7 @@ declare(strict_types=1); namespace Chill\AsideActivityBundle\Export\Filter; use Chill\MainBundle\Export\FilterInterface; -use ChillAsideActivityBundle\Export\Declarations; +use Chill\AsideActivityBundle\Export\Declarations; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByDateFilter.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByDateFilter.php index dc6b89662..dbab304d2 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByDateFilter.php +++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByDateFilter.php @@ -12,7 +12,7 @@ declare(strict_types=1); namespace Chill\AsideActivityBundle\Export\Filter; use Chill\MainBundle\Export\FilterInterface; -use ChillAsideActivityBundle\Export\Declarations; +use Chill\AsideActivityBundle\Export\Declarations; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; From 1228a0323ce54bf051d4a0da89d9511ecc95c563 Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Wed, 2 Nov 2022 10:28:52 +0100 Subject: [PATCH 16/73] php csfixes --- .../ACPAggregators/ByActivityNumberAggregator.php | 4 ++-- .../Export/Aggregator/SentReceivedAggregator.php | 10 ++++++---- .../Export/Filter/ACPFilters/HasNoActivityFilter.php | 7 ++++--- .../Export/Filter/UsersJobFilter.php | 4 ++-- .../Export/Aggregator/ByActivityTypeAggregator.php | 6 +++--- .../src/Export/Declarations.php | 2 +- .../src/Export/Export/CountAsideActivity.php | 8 ++------ .../src/Export/Filter/ByActivityTypeFilter.php | 4 ++-- .../src/Export/Filter/ByDateFilter.php | 4 ++-- .../ByActionNumberAggregator.php | 4 ++-- .../ByHouseholdCompositionAggregator.php | 3 ++- .../CreatorJobAggregator.php | 4 ++-- .../EvaluationAggregators/ByEndDateAggregator.php | 4 ++-- .../EvaluationAggregators/ByMaxDateAggregator.php | 4 ++-- .../EvaluationAggregators/ByStartDateAggregator.php | 4 ++-- .../HavingEndDateAggregator.php | 9 +++++---- .../CurrentActionAggregator.php | 10 ++++++---- .../AccompanyingCourseFilters/CreatorFilter.php | 2 +- .../AccompanyingCourseFilters/CreatorJobFilter.php | 2 +- .../AccompanyingCourseFilters/HasNoActionFilter.php | 3 ++- .../HasNoReferrerFilter.php | 12 ++++++------ .../HasTemporaryLocationFilter.php | 2 +- .../Filter/EvaluationFilters/ByEndDateFilter.php | 4 ++-- .../Filter/EvaluationFilters/ByStartDateFilter.php | 4 ++-- .../EvaluationFilters/CurrentEvaluationsFilter.php | 2 +- .../Filter/SocialWorkFilters/CurrentActionFilter.php | 2 +- 26 files changed, 64 insertions(+), 60 deletions(-) diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByActivityNumberAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByActivityNumberAggregator.php index 44a3fd8f0..de35f75cc 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByActivityNumberAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByActivityNumberAggregator.php @@ -2,7 +2,7 @@ declare(strict_types=1); -/** +/* * Chill is a software for social workers * * For the full copyright and license information, please view @@ -41,7 +41,7 @@ class ByActivityNumberAggregator implements AggregatorInterface public function getLabels($key, array $values, $data) { - return function ($value): string { + return static function ($value): string { if ('_header' === $value) { return ''; } diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/SentReceivedAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/SentReceivedAggregator.php index b5300834c..2a6dec7f7 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/SentReceivedAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/SentReceivedAggregator.php @@ -2,7 +2,7 @@ declare(strict_types=1); -/** +/* * Chill is a software for social workers * * For the full copyright and license information, please view @@ -11,9 +11,10 @@ declare(strict_types=1); namespace Chill\ActivityBundle\Export\Aggregator; -use Chill\MainBundle\Export\AggregatorInterface; use Chill\ActivityBundle\Export\Declarations; +use Chill\MainBundle\Export\AggregatorInterface; use Doctrine\ORM\QueryBuilder; +use LogicException; use Symfony\Component\Form\FormBuilderInterface; class SentReceivedAggregator implements AggregatorInterface @@ -41,10 +42,11 @@ class SentReceivedAggregator implements AggregatorInterface public function getLabels($key, array $values, $data) { - return function ($value): string { + return static function ($value): string { if ('_header' === $value) { return ''; } + switch ($value) { case 'sent': return 'is sent'; @@ -53,7 +55,7 @@ class SentReceivedAggregator implements AggregatorInterface return 'is received'; default: - throw new \LogicException(sprintf('The value %s is not valid', $value)); + throw new LogicException(sprintf('The value %s is not valid', $value)); } }; } diff --git a/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/HasNoActivityFilter.php b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/HasNoActivityFilter.php index 13aaf475a..508644a07 100644 --- a/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/HasNoActivityFilter.php +++ b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/HasNoActivityFilter.php @@ -14,9 +14,10 @@ namespace Chill\ActivityBundle\Export\Filter\ACPFilters; use Chill\ActivityBundle\Entity\Activity; use Chill\MainBundle\Export\FilterInterface; use Chill\PersonBundle\Export\Declarations; +use Doctrine\ORM\Query\Expr; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; -use Doctrine\ORM\Query\Expr; +use function in_array; class HasNoActivityFilter implements FilterInterface { @@ -36,7 +37,7 @@ class HasNoActivityFilter implements FilterInterface //TODO check this: ->andWhere(' NOT EXISTS ( - SELECT 1 FROM '. Activity::class .' activity + SELECT 1 FROM ' . Activity::class . ' activity WHERE activity.accompanyingPeriod = acp ) '); @@ -61,4 +62,4 @@ class HasNoActivityFilter implements FilterInterface { return 'Filter acp which has no activity'; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillActivityBundle/Export/Filter/UsersJobFilter.php b/src/Bundle/ChillActivityBundle/Export/Filter/UsersJobFilter.php index dcdacd84a..b52ef441c 100644 --- a/src/Bundle/ChillActivityBundle/Export/Filter/UsersJobFilter.php +++ b/src/Bundle/ChillActivityBundle/Export/Filter/UsersJobFilter.php @@ -39,9 +39,9 @@ class UsersJobFilter implements FilterInterface $qb ->andWhere( $qb->expr()->exists( - 'SELECT 1 FROM ' . Activity::class . ' activity_users_job_filter_act + 'SELECT 1 FROM ' . Activity::class . ' activity_users_job_filter_act JOIN activity_users_job_filter_act.users users WHERE users.userJob IN (:activity_users_job_filter_jobs) AND activity_users_job_filter_act = activity ' - ) + ) ) ->setParameter('activity_users_job_filter_jobs', $data['jobs']); } diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Aggregator/ByActivityTypeAggregator.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Aggregator/ByActivityTypeAggregator.php index d15d107aa..488885bde 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Export/Aggregator/ByActivityTypeAggregator.php +++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Aggregator/ByActivityTypeAggregator.php @@ -2,7 +2,7 @@ declare(strict_types=1); -/** +/* * Chill is a software for social workers * * For the full copyright and license information, please view @@ -11,8 +11,8 @@ declare(strict_types=1); namespace Chill\AsideActivityBundle\Export\Aggregator; -use Chill\MainBundle\Export\AggregatorInterface; use Chill\AsideActivityBundle\Export\Declarations; +use Chill\MainBundle\Export\AggregatorInterface; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; @@ -41,7 +41,7 @@ class ByActivityTypeAggregator implements AggregatorInterface public function getLabels($key, array $values, $data) { - return function ($value): string { + return static function ($value): string { if ('_header' === $value) { return ''; } diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Declarations.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Declarations.php index 7ddbec6de..8c808ea37 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Export/Declarations.php +++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Declarations.php @@ -17,4 +17,4 @@ namespace Chill\AsideActivityBundle\Export; abstract class Declarations { public const ASIDE_ACTIVITY_TYPE = 'aside_activity'; -} \ No newline at end of file +} diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Export/CountAsideActivity.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Export/CountAsideActivity.php index d6c391121..d1d69f0c8 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Export/Export/CountAsideActivity.php +++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Export/CountAsideActivity.php @@ -11,14 +11,14 @@ declare(strict_types=1); namespace Chill\AsideActivityBundle\Export\Export; +use Chill\AsideActivityBundle\Export\Declarations; use Chill\AsideActivityBundle\Repository\AsideActivityRepository; use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\GroupedExportInterface; -use Chill\AsideActivityBundle\Export\Declarations; -use Symfony\Component\Form\FormBuilderInterface; use Doctrine\ORM\Query; use LogicException; +use Symfony\Component\Form\FormBuilderInterface; class CountAsideActivity implements ExportInterface, GroupedExportInterface { @@ -105,7 +105,3 @@ class CountAsideActivity implements ExportInterface, GroupedExportInterface return []; } } - - - - diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByActivityTypeFilter.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByActivityTypeFilter.php index 53bd904fa..4f256538d 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByActivityTypeFilter.php +++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByActivityTypeFilter.php @@ -11,8 +11,8 @@ declare(strict_types=1); namespace Chill\AsideActivityBundle\Export\Filter; -use Chill\MainBundle\Export\FilterInterface; use Chill\AsideActivityBundle\Export\Declarations; +use Chill\MainBundle\Export\FilterInterface; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; @@ -52,4 +52,4 @@ class ByActivityTypeFilter implements FilterInterface { return 'Filter by aside activity type'; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByDateFilter.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByDateFilter.php index dbab304d2..d8fec664a 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByDateFilter.php +++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByDateFilter.php @@ -11,8 +11,8 @@ declare(strict_types=1); namespace Chill\AsideActivityBundle\Export\Filter; -use Chill\MainBundle\Export\FilterInterface; use Chill\AsideActivityBundle\Export\Declarations; +use Chill\MainBundle\Export\FilterInterface; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; @@ -52,4 +52,4 @@ class ByDateFilter implements FilterInterface { return 'Filter by aside activity date'; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ByActionNumberAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ByActionNumberAggregator.php index 3c16aa161..0f51b6feb 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ByActionNumberAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ByActionNumberAggregator.php @@ -2,7 +2,7 @@ declare(strict_types=1); -/** +/* * Chill is a software for social workers * * For the full copyright and license information, please view @@ -41,7 +41,7 @@ class ByActionNumberAggregator implements AggregatorInterface public function getLabels($key, array $values, $data) { - return function ($value): string { + return static function ($value): string { if ('_header' === $value) { return ''; } diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ByHouseholdCompositionAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ByHouseholdCompositionAggregator.php index e96c8a228..f299762e6 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ByHouseholdCompositionAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ByHouseholdCompositionAggregator.php @@ -18,6 +18,7 @@ use Chill\PersonBundle\Entity\Household\HouseholdComposition; use Chill\PersonBundle\Entity\Household\HouseholdMember; use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Repository\Household\HouseholdCompositionTypeRepositoryInterface; +use DateTimeImmutable; use Doctrine\ORM\Query\Expr\Join; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; @@ -96,7 +97,7 @@ class ByHouseholdCompositionAggregator implements AggregatorInterface $builder->add('date_calc', ChillDateType::class, [ 'label' => 'export.aggregator.course.by_household_composition.Calc date', 'input_format' => 'datetime_immutable', - 'data' => new \DateTimeImmutable('now'), + 'data' => new DateTimeImmutable('now'), ]); } diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/CreatorJobAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/CreatorJobAggregator.php index 1449091e7..c473bf7a1 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/CreatorJobAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/CreatorJobAggregator.php @@ -2,7 +2,7 @@ declare(strict_types=1); -/** +/* * Chill is a software for social workers * * For the full copyright and license information, please view @@ -41,7 +41,7 @@ class CreatorJobAggregator implements AggregatorInterface public function getLabels($key, array $values, $data) { - return function ($value): string { + return static function ($value): string { if ('_header' === $value) { return ''; } diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByEndDateAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByEndDateAggregator.php index 9c90c8dd3..40b035a02 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByEndDateAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByEndDateAggregator.php @@ -2,7 +2,7 @@ declare(strict_types=1); -/** +/* * Chill is a software for social workers * * For the full copyright and license information, please view @@ -41,7 +41,7 @@ class ByEndDateAggregator implements AggregatorInterface public function getLabels($key, array $values, $data) { - return function ($value): string { + return static function ($value): string { if ('_header' === $value) { return ''; } diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByMaxDateAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByMaxDateAggregator.php index ded9fb685..36ab0f50b 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByMaxDateAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByMaxDateAggregator.php @@ -2,7 +2,7 @@ declare(strict_types=1); -/** +/* * Chill is a software for social workers * * For the full copyright and license information, please view @@ -41,7 +41,7 @@ class ByMaxDateAggregator implements AggregatorInterface public function getLabels($key, array $values, $data) { - return function ($value): string { + return static function ($value): string { if ('_header' === $value) { return ''; } diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByStartDateAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByStartDateAggregator.php index 121171d8c..e64245d53 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByStartDateAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByStartDateAggregator.php @@ -2,7 +2,7 @@ declare(strict_types=1); -/** +/* * Chill is a software for social workers * * For the full copyright and license information, please view @@ -41,7 +41,7 @@ class ByStartDateAggregator implements AggregatorInterface public function getLabels($key, array $values, $data) { - return function ($value): string { + return static function ($value): string { if ('_header' === $value) { return ''; } diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/HavingEndDateAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/HavingEndDateAggregator.php index 8def8cf88..af801b74d 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/HavingEndDateAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/HavingEndDateAggregator.php @@ -2,7 +2,7 @@ declare(strict_types=1); -/** +/* * Chill is a software for social workers * * For the full copyright and license information, please view @@ -14,6 +14,7 @@ namespace Chill\PersonBundle\Export\Aggregator\EvaluationAggregators; use Chill\MainBundle\Export\AggregatorInterface; use Chill\PersonBundle\Export\Declarations; use Doctrine\ORM\QueryBuilder; +use LogicException; use Symfony\Component\Form\FormBuilderInterface; class HavingEndDateAggregator implements AggregatorInterface @@ -45,10 +46,11 @@ class HavingEndDateAggregator implements AggregatorInterface public function getLabels($key, array $values, $data) { - return function ($value): string { + return static function ($value): string { if ('_header' === $value) { return ''; } + switch ($value) { case true: return 'enddate is specified'; @@ -57,9 +59,8 @@ class HavingEndDateAggregator implements AggregatorInterface return 'enddate is not specified'; default: - throw new \LogicException(sprintf('The value %s is not valid', $value)); + throw new LogicException(sprintf('The value %s is not valid', $value)); } - }; } diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/CurrentActionAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/CurrentActionAggregator.php index d9120d121..a77838167 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/CurrentActionAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/CurrentActionAggregator.php @@ -2,7 +2,7 @@ declare(strict_types=1); -/** +/* * Chill is a software for social workers * * For the full copyright and license information, please view @@ -14,6 +14,7 @@ namespace Chill\PersonBundle\Export\Aggregator\SocialWorkAggregators; use Chill\MainBundle\Export\AggregatorInterface; use Chill\PersonBundle\Export\Declarations; use Doctrine\ORM\QueryBuilder; +use LogicException; use Symfony\Component\Form\FormBuilderInterface; class CurrentActionAggregator implements AggregatorInterface @@ -27,7 +28,7 @@ class CurrentActionAggregator implements AggregatorInterface { $qb ->addSelect(' - (CASE true WHEN acpw.startDate IS NULL ELSE false END) + (CASE true WHEN acpw.startDate IS NULL ELSE false END) AS acpw_current_action_aggregator ') ->addGroupBy('acpw_current_action_aggregator'); @@ -45,10 +46,11 @@ class CurrentActionAggregator implements AggregatorInterface public function getLabels($key, array $values, $data) { - return function ($value): string { + return static function ($value): string { if ('_header' === $value) { return ''; } + switch ($value) { case true: return 'Current action'; @@ -57,7 +59,7 @@ class CurrentActionAggregator implements AggregatorInterface return 'Not current action'; default: - throw new \LogicException(sprintf('The value %s is not valid', $value)); + throw new LogicException(sprintf('The value %s is not valid', $value)); } }; } diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorFilter.php index 7dd78d0b4..c7e110f62 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorFilter.php @@ -52,4 +52,4 @@ class CreatorFilter implements FilterInterface { return 'Filter by creator'; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorJobFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorJobFilter.php index 021d2227c..6f094fe97 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorJobFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorJobFilter.php @@ -52,4 +52,4 @@ class CreatorJobFilter implements FilterInterface { return 'Filter by creator job'; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasNoActionFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasNoActionFilter.php index a26628e95..771b8d063 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasNoActionFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasNoActionFilter.php @@ -15,6 +15,7 @@ use Chill\MainBundle\Export\FilterInterface; use Chill\PersonBundle\Export\Declarations; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; +use function in_array; class HasNoActionFilter implements FilterInterface { @@ -51,4 +52,4 @@ class HasNoActionFilter implements FilterInterface { return 'Filter by which has no action'; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasNoReferrerFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasNoReferrerFilter.php index c37d423f8..b18bcd1de 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasNoReferrerFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasNoReferrerFilter.php @@ -15,10 +15,10 @@ use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Form\Type\ChillDateType; use Chill\PersonBundle\Entity\AccompanyingPeriod\UserHistory; use Chill\PersonBundle\Export\Declarations; +use DateTime; use Doctrine\DBAL\Types\Types; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; -use DateTime; class HasNoReferrerFilter implements FilterInterface { @@ -32,12 +32,12 @@ class HasNoReferrerFilter implements FilterInterface $qb ->andWhere(' NOT EXISTS ( - SELECT 1 FROM '. UserHistory::class .' uh - WHERE uh.startDate < :date + SELECT 1 FROM ' . UserHistory::class . ' uh + WHERE uh.startDate < :date AND ( - uh.endDate IS NULL + uh.endDate IS NULL or uh.endDate > :date - ) + ) AND uh.accompanyingPeriod = acp ) ') @@ -69,4 +69,4 @@ class HasNoReferrerFilter implements FilterInterface { return 'Filter by which has no referrer'; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasTemporaryLocationFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasTemporaryLocationFilter.php index 18a6c3918..85d8d93b6 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasTemporaryLocationFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasTemporaryLocationFilter.php @@ -52,4 +52,4 @@ class HasTemporaryLocationFilter implements FilterInterface { return 'Filter by temporary location'; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByEndDateFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByEndDateFilter.php index d40170447..5c509d50c 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByEndDateFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByEndDateFilter.php @@ -14,10 +14,10 @@ namespace Chill\PersonBundle\Export\Filter\EvaluationFilters; use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Form\Type\ChillDateType; use Chill\PersonBundle\Export\Declarations; +use DateTime; use Doctrine\DBAL\Types\Types; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; -use DateTime; class ByEndDateFilter implements FilterInterface { @@ -64,4 +64,4 @@ class ByEndDateFilter implements FilterInterface { return 'Filter by end date evaluations'; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByStartDateFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByStartDateFilter.php index afa3c212a..e21a1ea68 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByStartDateFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByStartDateFilter.php @@ -14,10 +14,10 @@ namespace Chill\PersonBundle\Export\Filter\EvaluationFilters; use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Form\Type\ChillDateType; use Chill\PersonBundle\Export\Declarations; +use DateTime; use Doctrine\DBAL\Types\Types; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; -use DateTime; class ByStartDateFilter implements FilterInterface { @@ -64,4 +64,4 @@ class ByStartDateFilter implements FilterInterface { return 'Filter by start date evaluations'; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/CurrentEvaluationsFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/CurrentEvaluationsFilter.php index 293e551f1..50c95600e 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/CurrentEvaluationsFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/CurrentEvaluationsFilter.php @@ -47,4 +47,4 @@ class CurrentEvaluationsFilter implements FilterInterface { return 'Filter by current evaluations'; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/CurrentActionFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/CurrentActionFilter.php index ecbe6086a..cbf958182 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/CurrentActionFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/CurrentActionFilter.php @@ -47,4 +47,4 @@ class CurrentActionFilter implements FilterInterface { return 'Filter by current actions'; } -} \ No newline at end of file +} From 4af261a36631058588ed1f43687143165c15d6f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Tue, 25 Oct 2022 09:25:30 +0200 Subject: [PATCH 17/73] Fixed: correctly show datetime in spreadsheet list --- .../Formatter/SpreadsheetListFormatter.php | 32 +++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/src/Bundle/ChillMainBundle/Export/Formatter/SpreadsheetListFormatter.php b/src/Bundle/ChillMainBundle/Export/Formatter/SpreadsheetListFormatter.php index ab9c2e893..0e5e339ea 100644 --- a/src/Bundle/ChillMainBundle/Export/Formatter/SpreadsheetListFormatter.php +++ b/src/Bundle/ChillMainBundle/Export/Formatter/SpreadsheetListFormatter.php @@ -20,11 +20,12 @@ use PhpOffice\PhpSpreadsheet\Shared\Date; use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Style\NumberFormat; use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; +use RuntimeException; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\HttpFoundation\Response; -use Symfony\Contracts\Translation\TranslatorInterface; +use Symfony\Contracts\Translation\TranslatorInterface; use function array_key_exists; use function array_keys; use function array_map; @@ -80,7 +81,7 @@ class SpreadsheetListFormatter implements FormatterInterface * * @uses appendAggregatorForm * - * @param type $exportAlias + * @param string $exportAlias */ public function buildForm( FormBuilderInterface $builder, @@ -144,8 +145,6 @@ class SpreadsheetListFormatter implements FormatterInterface $i = 1; foreach ($result as $row) { - $line = []; - if (true === $this->formatterData['numerotation']) { $worksheet->setCellValue('A' . ($i + 1), (string) $i); } @@ -155,13 +154,22 @@ class SpreadsheetListFormatter implements FormatterInterface foreach ($row as $key => $value) { $row = $a . ($i + 1); - if ($value instanceof DateTimeInterface) { - $worksheet->setCellValue($row, Date::PHPToExcel($value)); - $worksheet->getStyle($row) - ->getNumberFormat() - ->setFormatCode(NumberFormat::FORMAT_DATE_DDMMYYYY); + $formattedValue = $this->getLabel($key, $value); + + if ($formattedValue instanceof DateTimeInterface) { + $worksheet->setCellValue($row, Date::PHPToExcel($formattedValue)); + + if ($formattedValue->format('His') === '000000') { + $worksheet->getStyle($row) + ->getNumberFormat() + ->setFormatCode(NumberFormat::FORMAT_DATE_DDMMYYYY); + } else { + $worksheet->getStyle($row) + ->getNumberFormat() + ->setFormatCode(NumberFormat::FORMAT_DATE_DATETIME); + } } else { - $worksheet->setCellValue($row, $this->getLabel($key, $value)); + $worksheet->setCellValue($row, $formattedValue); } ++$a; } @@ -259,6 +267,10 @@ class SpreadsheetListFormatter implements FormatterInterface foreach ($keys as $key) { // get an array with all values for this key if possible $values = array_map(static function ($v) use ($key) { + if (!array_key_exists($key, $v)) { + throw new RuntimeException(sprintf('This key does not exists: %s. Available keys are %s', $key, implode(', ', array_keys($v)))); + } + return $v[$key]; }, $this->result); // store the label in the labelsCache property From 333c305eef548fe0fc4270f1098f9c1fc8a9a977 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Tue, 25 Oct 2022 10:23:02 +0200 Subject: [PATCH 18/73] Fixed: [export][list person] use address from household for person list --- exports_alias_conventions.md | 133 +++++----- .../Export/Helper/ExportAddressHelper.php | 247 ++++++++++++++++++ .../config/services/export.yaml | 3 + .../translations/messages.fr.yml | 167 ++++++------ .../ChillPersonBundle/Entity/Person.php | 2 +- .../Export/Export/ListPerson.php | 200 +++++++------- 6 files changed, 515 insertions(+), 237 deletions(-) create mode 100644 src/Bundle/ChillMainBundle/Export/Helper/ExportAddressHelper.php diff --git a/exports_alias_conventions.md b/exports_alias_conventions.md index 62fc745d8..fd7844691 100644 --- a/exports_alias_conventions.md +++ b/exports_alias_conventions.md @@ -5,69 +5,70 @@ Add condition with distinct alias on each export join clauses (Indicators + Filt These are alias conventions : -| 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 | -| | AccompanyingPeriopStepHistory::class | acp.stepHistories | acpstephistories | -| 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 | -| | Person::class | activity.createdBy | actcreator | -| 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 | +| 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 | +| | AccompanyingPeriopStepHistory::class | acp.stepHistories | acpstephistories | +| 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 | +| | CurrentPersonAddress::class | person.currentPersonAddress | currentPersonAddress (on a given date) | +| 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 | +| | Person::class | activity.createdBy | actcreator | +| 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/src/Bundle/ChillMainBundle/Export/Helper/ExportAddressHelper.php b/src/Bundle/ChillMainBundle/Export/Helper/ExportAddressHelper.php new file mode 100644 index 000000000..09aaa7a1d --- /dev/null +++ b/src/Bundle/ChillMainBundle/Export/Helper/ExportAddressHelper.php @@ -0,0 +1,247 @@ + self::COUNTRY, + 'postal_code' => self::POSTAL_CODE, + 'street' => self::STREET, + 'building' => self::BUILDING, + 'string' => self::STRING, + 'geom' => self::GEOM, + 'attributes' => self::ATTRIBUTES, + ]; + + private const COLUMN_MAPPING = [ + 'country' => ['country'], + 'postal_code' => ['postcode_code', 'postcode_name'], + 'street' => ['street', 'streetNumber'], + 'building' => ['buildingName', 'corridor', 'distribution', 'extra', 'flat', 'floor'], + 'string' => ['_as_string'], + 'attributes' => ['isNoAddress', 'confidential', 'id'], + 'geom' => ['_lat', '_lon'], + ]; + + private AddressRender $addressRender; + + private AddressRepository $addressRepository; + + private PropertyAccessor $propertyAccess; + + private TranslatableStringHelperInterface $translatableStringHelper; + + public function __construct( + AddressRepository $addressRepository, + TranslatableStringHelperInterface $translatableStringHelper, + AddressRender $addressRender + ) { + $this->addressRepository = $addressRepository; + $this->propertyAccess = PropertyAccess::createPropertyAccessor(); + $this->translatableStringHelper = $translatableStringHelper; + $this->addressRender = $addressRender; + } + + /** + * @return array|string[] + */ + public function getKeys(int $params, string $prefix = ''): array + { + $prefixes = []; + + foreach (self::ALL as $key => $bitmask) { + if (($params & $bitmask) === $bitmask) { + $prefixes = array_merge( + $prefixes, + array_map( + static function ($item) use ($prefix) { return $prefix . $item; }, + self::COLUMN_MAPPING[$key] + ) + ); + } + } + + return $prefixes; + } + + public function getLabel($key, array $values, $data, string $prefix = '', string $translationPrefix = 'export.address_helper.'): callable + { + $sanitizedKey = substr($key, strlen($prefix)); + + switch ($sanitizedKey) { + case 'id': + case 'street': + case 'streetNumber': + case 'buildingName': + case 'corridor': + case 'distribution': + case 'extra': + case 'flat': + case 'floor': + return function ($value) use ($sanitizedKey, $translationPrefix) { + if ('_header' === $value) { + return $translationPrefix . $sanitizedKey; + } + + if (null === $value) { + return ''; + } + + $address = $this->addressRepository->find($value); + + return $this->propertyAccess->getValue($address, $sanitizedKey); + }; + + case '_lat': + case '_lon': + return function ($value) use ($sanitizedKey, $translationPrefix) { + if ('_header' === $value) { + return $translationPrefix . $sanitizedKey; + } + + if (null === $value) { + return ''; + } + + $address = $this->addressRepository->find($value); + $geom = $address->getPoint(); + + if (null === $geom) { + return ''; + } + + switch ($sanitizedKey) { + case '_lat': + return $geom->getLat(); + + case '_lon': + return $geom->getLon(); + + default: + throw new LogicException('only _lat or _lon accepted, given: ' . $sanitizedKey); + } + }; + + case 'isNoAddress': + case 'confidential': + return function ($value) use ($sanitizedKey, $translationPrefix) { + if ('_header' === $value) { + return $translationPrefix . $sanitizedKey; + } + + if (null === $value) { + return ''; + } + + $address = $this->addressRepository->find($value); + + switch ($val = $this->propertyAccess->getValue($address, $sanitizedKey)) { + case null: + return ''; + + case true: + return 1; + + case false: + return 0; + + default: + throw new LogicException('this value is not supported for ' . $sanitizedKey . ': ' . $val); + } + }; + + case 'country': + return function ($value) use ($sanitizedKey, $translationPrefix) { + if ('_header' === $value) { + return $translationPrefix . $sanitizedKey; + } + + if (null === $value) { + return ''; + } + + $address = $this->addressRepository->find($value); + + return $this->translatableStringHelper->localize($address->getPostcode()->getCountry()->getName()); + }; + + case '_as_string': + return function ($value) use ($sanitizedKey, $translationPrefix) { + if ('_header' === $value) { + return $translationPrefix . $sanitizedKey; + } + + if (null === $value) { + return ''; + } + + $address = $this->addressRepository->find($value); + + return $this->addressRender->renderString($address, []); + }; + + case 'postcode_code': + case 'postcode_name': + return function ($value) use ($sanitizedKey, $translationPrefix) { + if ('_header' === $value) { + return $translationPrefix . $sanitizedKey; + } + + if (null === $value) { + return ''; + } + + $address = $this->addressRepository->find($value); + + switch ($sanitizedKey) { + case 'postcode_code': + return $address->getPostcode()->getCode(); + + case 'postcode_name': + return $address->getPostcode()->getName(); + + default: + throw new LogicException('this key is not supported: ' . $sanitizedKey); + } + }; + + default: + throw new LogicException('this key is not supported: ' . $sanitizedKey); + } + } +} diff --git a/src/Bundle/ChillMainBundle/config/services/export.yaml b/src/Bundle/ChillMainBundle/config/services/export.yaml index d330f30f5..ea7328839 100644 --- a/src/Bundle/ChillMainBundle/config/services/export.yaml +++ b/src/Bundle/ChillMainBundle/config/services/export.yaml @@ -3,6 +3,9 @@ services: autowire: true autoconfigure: true + Chill\MainBundle\Export\Helper\: + resource: '../../Export/Helper' + chill.main.export_element_validator: class: Chill\MainBundle\Validator\Constraints\Export\ExportElementConstraintValidator tags: diff --git a/src/Bundle/ChillMainBundle/translations/messages.fr.yml b/src/Bundle/ChillMainBundle/translations/messages.fr.yml index b4ce910c4..01fcf0b8c 100644 --- a/src/Bundle/ChillMainBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillMainBundle/translations/messages.fr.yml @@ -79,17 +79,17 @@ Postal code: Code postal Valid from: Valide à partir du Choose a postal code: Choisir un code postal address: - address_homeless: L'adresse est-elle celle d'un domicile fixe ? - real address: Adresse d'un domicile - consider homeless: Cette adresse est incomplète + address_homeless: L'adresse est-elle celle d'un domicile fixe ? + real address: Adresse d'un domicile + consider homeless: Cette adresse est incomplète address more: - floor: ét - corridor: coul - steps: esc - flat: appart - buildingName: résidence - extra: "" - distribution: cedex + floor: ét + corridor: coul + steps: esc + flat: appart + buildingName: résidence + extra: "" + distribution: cedex Create a new address: Créer une nouvelle adresse Create an address: Créer une adresse Update address: Modifier l'adresse @@ -125,7 +125,7 @@ Location and location type: Localisations et types de localisation Back to the admin: Menu d'administration "Administration interface": Interface d'administration Welcome to the admin section !: > - Bienvenue dans l'interface d'administration ! + Bienvenue dans l'interface d'administration ! #permissions Permissions Menu: Gestion des droits @@ -334,69 +334,69 @@ Impersonate: Incarner l'utilisateur Impersonate mode: Mode fantôme crud: - # general items - new: - button_action_form: Créer - link_edit: Modifier - save_and_close: Créer & fermer - save_and_show: Créer & voir - save_and_new: Créer & nouveau - success: Les données ont été créées - edit: - button_action_form: Enregistrer - back_to_view: Voir - save_and_close: Enregistrer & fermer - save_and_show: Enregistrer & voir - success: Les données ont été modifiées - delete: - success: Les données ont été supprimées - link_to_form: Supprimer - default: - success: Les données ont été enregistrées - view: - link_duplicate: Dupliquer - admin_user: - index: - title: Utilisateurs - add_new: Créer - title_edit: Modifier un utilisateur - title_new: Créer un utilisateur - admin_user_job: - index: - title: Métiers - add_new: Créer - title_new: Nouveau métier - title_edit: Modifier un métier - main_location_type: - index: - title: Liste des types de localisations - add_new: Ajouter un type de localisation - title_new: Nouveau type de localisation - title_edit: Modifier un type de localisation - main_location: - index: - title: Liste des localisations - add_new: Ajouter une localisation - title_new: Nouvelle localisation - title_edit: Modifier une localisation - main_language: - index: - title: Liste des langues - add_new: Ajouter une langue - title_new: Nouvelle langue - title_edit: Modifier une langue - main_country: - index: - title: Liste des pays - add_new: Ajouter un pays - title_new: Nouveau pays - title_edit: Modifier un pays - main_civility: - index: - title: Liste des civilités - add_new: Ajouter une civilité - title_new: Nouvelle civilité - title_edit: Modifier une civilité + # general items + new: + button_action_form: Créer + link_edit: Modifier + save_and_close: Créer & fermer + save_and_show: Créer & voir + save_and_new: Créer & nouveau + success: Les données ont été créées + edit: + button_action_form: Enregistrer + back_to_view: Voir + save_and_close: Enregistrer & fermer + save_and_show: Enregistrer & voir + success: Les données ont été modifiées + delete: + success: Les données ont été supprimées + link_to_form: Supprimer + default: + success: Les données ont été enregistrées + view: + link_duplicate: Dupliquer + admin_user: + index: + title: Utilisateurs + add_new: Créer + title_edit: Modifier un utilisateur + title_new: Créer un utilisateur + admin_user_job: + index: + title: Métiers + add_new: Créer + title_new: Nouveau métier + title_edit: Modifier un métier + main_location_type: + index: + title: Liste des types de localisations + add_new: Ajouter un type de localisation + title_new: Nouveau type de localisation + title_edit: Modifier un type de localisation + main_location: + index: + title: Liste des localisations + add_new: Ajouter une localisation + title_new: Nouvelle localisation + title_edit: Modifier une localisation + main_language: + index: + title: Liste des langues + add_new: Ajouter une langue + title_new: Nouvelle langue + title_edit: Modifier une langue + main_country: + index: + title: Liste des pays + add_new: Ajouter un pays + title_new: Nouveau pays + title_edit: Modifier un pays + main_civility: + index: + title: Liste des civilités + add_new: Ajouter une civilité + title_new: Nouvelle civilité + title_edit: Modifier une civilité No entities: Aucun élément @@ -515,3 +515,22 @@ notification: Remove an email: Supprimer l'adresse email Email with access link: Adresse email ayant reçu un lien d'accès +export: + address_helper: + id: Identifiant de l'adresse + street: Voie + streetNumber: Numéro de voie + buildingName: Résidence + corridor: Couloir + distribution: Distribution + extra: Extra + flat: Appartement + floor: Étage + postcode_code: Code postal + postcode_name: Libellé du code postal + country: Pays + _as_string: Adresse formattée + confidential: Adresse confidentielle ? + isNoAddress: Adresse incomplète ? + _lat: Latitude + _lon: Longitude diff --git a/src/Bundle/ChillPersonBundle/Entity/Person.php b/src/Bundle/ChillPersonBundle/Entity/Person.php index 0d6e3e493..48864120d 100644 --- a/src/Bundle/ChillPersonBundle/Entity/Person.php +++ b/src/Bundle/ChillPersonBundle/Entity/Person.php @@ -1760,7 +1760,7 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI } /** - * @param type $spokenLanguages + * @param Collection $spokenLanguages */ public function setSpokenLanguages($spokenLanguages): self { diff --git a/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php b/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php index 5f5d95034..e799a53e4 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php @@ -17,28 +17,33 @@ use Chill\CustomFieldsBundle\Service\CustomFieldProvider; use Chill\MainBundle\Export\ExportElementValidatedInterface; use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\GroupedExportInterface; +use Chill\MainBundle\Export\Helper\ExportAddressHelper; use Chill\MainBundle\Export\ListInterface; +use Chill\MainBundle\Form\Type\ChillDateType; +use Chill\MainBundle\Repository\CountryRepository; use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Security\Authorization\PersonVoter; use DateTime; +use DateTimeImmutable; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Query; use Exception; +use PhpOffice\PhpSpreadsheet\Shared\Date; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; -use Symfony\Component\Form\Extension\Core\Type\DateType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Validator\Constraints\Callback; use Symfony\Component\Validator\Context\ExecutionContextInterface; -use Symfony\Contracts\Translation\TranslatorInterface; +use Symfony\Contracts\Translation\TranslatorInterface; use function addcslashes; use function array_key_exists; use function array_keys; use function array_merge; use function count; use function in_array; +use function strlen; use function strtolower; use function uniqid; @@ -47,31 +52,40 @@ use function uniqid; */ class ListPerson implements ExportElementValidatedInterface, ListInterface, GroupedExportInterface { - protected CustomFieldProvider $customFieldProvider; - - protected EntityManagerInterface $entityManager; - - protected array $fields = [ - 'id', 'firstName', 'lastName', 'birthdate', + public const FIELDS = [ + 'id', + 'firstName', + 'lastName', + 'birthdate', 'placeOfBirth', 'gender', 'memo', 'email', 'phonenumber', 'mobilenumber', 'contactInfo', 'countryOfBirth', 'nationality', - 'address_street_address_1', 'address_street_address_2', - 'address_valid_from', 'address_postcode_label', 'address_postcode_code', - 'address_country_name', 'address_country_code', 'address_isnoaddress', + 'address', ]; - protected TranslatableStringHelper $translatableStringHelper; + private ExportAddressHelper $addressHelper; - protected TranslatorInterface $translator; + private CountryRepository $countryRepository; + + private CustomFieldProvider $customFieldProvider; + + private EntityManagerInterface $entityManager; private $slugs = []; + private TranslatableStringHelper $translatableStringHelper; + + private TranslatorInterface $translator; + public function __construct( + CountryRepository $countryRepository, + ExportAddressHelper $addressHelper, EntityManagerInterface $em, TranslatorInterface $translator, TranslatableStringHelper $translatableStringHelper, CustomFieldProvider $customFieldProvider ) { + $this->addressHelper = $addressHelper; + $this->countryRepository = $countryRepository; $this->entityManager = $em; $this->translator = $translator; $this->translatableStringHelper = $translatableStringHelper; @@ -80,7 +94,7 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou public function buildForm(FormBuilderInterface $builder) { - $choices = array_combine($this->fields, $this->fields); + $choices = array_combine(self::FIELDS, self::FIELDS); foreach ($this->getCustomFields() as $cf) { $choices[$this->translatableStringHelper->localize($cf->getName())] @@ -96,7 +110,7 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou 'label' => 'Fields to include in export', 'choice_attr' => static function (string $val): array { // add a 'data-display-target' for address fields - if (substr($val, 0, 8) === 'address_') { + if (substr($val, 0, 7) === 'address') { return ['data-display-target' => 'address_date']; } @@ -111,17 +125,14 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou } }, ])], + 'data' => array_values($choices), ]); // add a date field for addresses - $builder->add('address_date', DateType::class, [ + $builder->add('address_date', ChillDateType::class, [ 'label' => 'Address valid at this date', - 'data' => new DateTime(), - 'attr' => ['class' => 'datepicker'], - 'widget' => 'single_text', - 'format' => 'dd-MM-yyyy', - 'required' => false, - 'block_name' => 'list_export_form_address_date', + 'data' => new DateTimeImmutable(), + 'input' => 'datetime_immutable', ]); } @@ -142,6 +153,10 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou public function getLabels($key, array $values, $data) { + if (substr($key, 0, strlen('address')) === 'address') { + return $this->addressHelper->getLabel($key, $values, $data, 'address_'); + } + switch ($key) { case 'birthdate': // for birthdate, we have to transform the string into a date @@ -151,10 +166,11 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou return 'birthdate'; } - if (empty($value)) { + if (null === $value) { return ''; } + // warning: won't work with DateTimeImmutable as we reset time a few lines later $date = DateTime::createFromFormat('Y-m-d', $value); // check that the creation could occurs. if (false === $date) { @@ -162,7 +178,9 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou . 'not be converted to %s', $value, DateTime::class)); } - return $date->format('d-m-Y'); + $date->setTime(0, 0, 0); + + return $date; }; case 'gender': @@ -177,29 +195,6 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou case 'countryOfBirth': case 'nationality': - $countryRepository = $this->entityManager - ->getRepository(\Chill\MainBundle\Entity\Country::class); - - // load all countries in a single query - $countryRepository->findBy(['countryCode' => $values]); - - return function ($value) use ($key, $countryRepository) { - if ('_header' === $value) { - return strtolower($key); - } - - if (null === $value) { - return $this->translator->trans('no data'); - } - - $country = $countryRepository->find($value); - - return $this->translatableStringHelper->localize( - $country->getName() - ); - }; - - case 'address_country_name': return function ($value) use ($key) { if ('_header' === $value) { return strtolower($key); @@ -209,25 +204,16 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou return ''; } - return $this->translatableStringHelper->localize(json_decode($value, true)); - }; + $country = $this->countryRepository->find($value); - case 'address_isnoaddress': - return static function (?string $value): string { - if ('_header' === $value) { - return 'address.address_homeless'; - } - - if (null !== $value) { - return 'X'; - } - - return ''; + return $this->translatableStringHelper->localize( + $country->getName() + ); }; default: // for fields which are associated with person - if (in_array($key, $this->fields, true)) { + if (in_array($key, self::FIELDS, true)) { return static function ($value) use ($key) { if ('_header' === $value) { return strtolower($key); @@ -246,9 +232,13 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou $fields = []; foreach ($data['fields'] as $key) { - if (in_array($key, $this->fields, true)) { - $fields[] = $key; + if (substr($key, 0, strlen('address')) === 'address') { + $fields = array_merge($fields, $this->addressHelper->getKeys(0b01111111, 'address_')); + + continue; } + + $fields[] = $key; } // add the key from slugs and return @@ -270,6 +260,9 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou return Declarations::PERSON_TYPE; } + /** + * @param array{fields: string[], address_date: DateTimeImmutable} $data + */ public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) { $centers = array_map(static function ($el) { @@ -284,36 +277,57 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou $qb = $this->entityManager->createQueryBuilder(); - foreach ($this->fields as $f) { - if (in_array($f, $data['fields'], true)) { - switch ($f) { - case 'countryOfBirth': - case 'nationality': - $qb->addSelect(sprintf('IDENTITY(person.%s) as %s', $f, $f)); + $qb + ->from(Person::class, 'person') + ->andWhere( + $qb->expr()->exists( + 'SELECT 1 FROM ' . Person\PersonCenterHistory::class . ' pch WHERE pch.person = person.id AND pch.center IN (:authorized_centers)' + ) + ) + ->setParameter('authorized_centers', $centers); - break; + foreach (self::FIELDS as $f) { + if (!in_array($f, $data['fields'], true)) { + continue; + } - case 'address_street_address_1': - case 'address_street_address_2': - case 'address_valid_from': - case 'address_postcode_label': - case 'address_postcode_code': - case 'address_country_name': - case 'address_country_code': - case 'address_isnoaddress': - $qb->addSelect(sprintf( - 'GET_PERSON_ADDRESS_%s(person.id, :address_date) AS %s', - // get the part after address_ - strtoupper(substr($f, 8)), - $f - )); - $qb->setParameter('address_date', $data['address_date']); + switch ($f) { + case 'countryOfBirth': + case 'nationality': + $qb->addSelect(sprintf('IDENTITY(person.%s) as %s', $f, $f)); - break; + break; - default: - $qb->addSelect(sprintf('person.%s as %s', $f, $f)); - } + case 'address': + foreach ($this->addressHelper->getKeys(0b01111111, 'address_') as $key) { + $qb + ->addSelect(sprintf('IDENTITY(currentPersonAddress.address) AS %s', $key)); + } + + if (!(in_array('currentPersonAddress', $qb->getAllAliases(), true))) { + $qb + ->leftJoin('person.currentPersonAddress', 'currentPersonAddress') + ->andWhere( + $qb->expr()->orX( + // no address at this time + $qb->expr()->isNull('currentPersonAddress'), + // there is one address... + $qb->expr()->andX( + $qb->expr()->lte('currentPersonAddress.validFrom', ':address_date'), + $qb->expr()->orX( + $qb->expr()->isNull('currentPersonAddress.validTo'), + $qb->expr()->gt('currentPersonAddress.validTo', ':address_date') + ) + ) + ) + ) + ->setParameter('address_date', $data['address_date']); + } + + break; + + default: + $qb->addSelect(sprintf('person.%s as %s', $f, $f)); } } @@ -345,12 +359,6 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou } } - $qb - ->from('ChillPersonBundle:Person', 'person') - ->join('person.center', 'center') - ->andWhere('center IN (:authorized_centers)') - ->setParameter('authorized_centers', $centers); - return $qb; } @@ -368,7 +376,7 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou { // get the field starting with address_ $addressFields = array_filter( - $this->fields, + self::FIELDS, static fn (string $el): bool => substr($el, 0, 8) === 'address_' ); From f434cc5c0251a3d2e2f919653d6b12c008d77049 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 26 Oct 2022 12:47:32 +0200 Subject: [PATCH 19/73] Fixed: [list person] fix list person and add new fields --- phpstan-critical.neon | 10 - phpstan-types.neon | 5 - .../Export/Helper/ExportAddressHelper.php | 32 +- .../Repository/CivilityRepository.php | 43 ++- .../CivilityRepositoryInterface.php | 34 ++ .../Repository/LanguageRepository.php | 7 +- .../LanguageRepositoryInterface.php | 37 ++ .../translations/messages.fr.yml | 4 + .../Household/PersonHouseholdAddress.php | 2 +- .../Export/Export/ListPerson.php | 330 +++++++++++++++--- .../Repository/MaritalStatusRepository.php | 3 +- .../MaritalStatusRepositoryInterface.php | 27 ++ .../translations/messages.fr.yml | 15 + 13 files changed, 457 insertions(+), 92 deletions(-) create mode 100644 src/Bundle/ChillMainBundle/Repository/CivilityRepositoryInterface.php create mode 100644 src/Bundle/ChillMainBundle/Repository/LanguageRepositoryInterface.php create mode 100644 src/Bundle/ChillPersonBundle/Repository/MaritalStatusRepositoryInterface.php diff --git a/phpstan-critical.neon b/phpstan-critical.neon index 1dc516834..bfbb2dc7c 100644 --- a/phpstan-critical.neon +++ b/phpstan-critical.neon @@ -5,11 +5,6 @@ parameters: count: 1 path: src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php - - - message: "#^Access to an undefined property Chill\\\\PersonBundle\\\\Entity\\\\Household\\\\PersonHouseholdAddress\\:\\:\\$relation\\.$#" - count: 1 - path: src/Bundle/ChillPersonBundle/Entity/Household/PersonHouseholdAddress.php - - message: "#^Access to an undefined property Chill\\\\PersonBundle\\\\Entity\\\\AccompanyingPeriod\\:\\:\\$work\\.$#" count: 1 @@ -30,11 +25,6 @@ parameters: count: 1 path: src/Bundle/ChillPersonBundle/Serializer/Normalizer/MembersEditorNormalizer.php - - - message: "#^Undefined variable\\: \\$choiceSlug$#" - count: 1 - path: src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php - - message: "#^Undefined variable\\: \\$choiceSlug$#" count: 1 diff --git a/phpstan-types.neon b/phpstan-types.neon index a0493ce0b..bf2ece912 100644 --- a/phpstan-types.neon +++ b/phpstan-types.neon @@ -340,11 +340,6 @@ parameters: count: 1 path: src/Bundle/ChillPersonBundle/Export/Aggregator/NationalityAggregator.php - - - message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" - count: 2 - path: src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php - - message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" count: 1 diff --git a/src/Bundle/ChillMainBundle/Export/Helper/ExportAddressHelper.php b/src/Bundle/ChillMainBundle/Export/Helper/ExportAddressHelper.php index 09aaa7a1d..6c6fcb76e 100644 --- a/src/Bundle/ChillMainBundle/Export/Helper/ExportAddressHelper.php +++ b/src/Bundle/ChillMainBundle/Export/Helper/ExportAddressHelper.php @@ -20,32 +20,32 @@ use Symfony\Component\PropertyAccess\PropertyAccessor; use function strlen; /** - * Helps to load addresses and format them in list + * Helps to load addresses and format them in list. */ class ExportAddressHelper { - public const ATTRIBUTES = 0b01000000; + public const F_AS_STRING = 0b00010000; - public const BUILDING = 0b00001000; + public const F_ATTRIBUTES = 0b01000000; - public const COUNTRY = 0b00000001; + public const F_BUILDING = 0b00001000; - public const GEOM = 0b00100000; + public const F_COUNTRY = 0b00000001; - public const POSTAL_CODE = 0b00000010; + public const F_GEOM = 0b00100000; - public const STREET = 0b00000100; + public const F_POSTAL_CODE = 0b00000010; - public const STRING = 0b00010000; + public const F_STREET = 0b00000100; private const ALL = [ - 'country' => self::COUNTRY, - 'postal_code' => self::POSTAL_CODE, - 'street' => self::STREET, - 'building' => self::BUILDING, - 'string' => self::STRING, - 'geom' => self::GEOM, - 'attributes' => self::ATTRIBUTES, + 'country' => self::F_COUNTRY, + 'postal_code' => self::F_POSTAL_CODE, + 'street' => self::F_STREET, + 'building' => self::F_BUILDING, + 'string' => self::F_AS_STRING, + 'geom' => self::F_GEOM, + 'attributes' => self::F_ATTRIBUTES, ]; private const COLUMN_MAPPING = [ @@ -78,6 +78,8 @@ class ExportAddressHelper } /** + * @param self::F_* $params + * * @return array|string[] */ public function getKeys(int $params, string $prefix = ''): array diff --git a/src/Bundle/ChillMainBundle/Repository/CivilityRepository.php b/src/Bundle/ChillMainBundle/Repository/CivilityRepository.php index 385488839..70b4a2bc4 100644 --- a/src/Bundle/ChillMainBundle/Repository/CivilityRepository.php +++ b/src/Bundle/ChillMainBundle/Repository/CivilityRepository.php @@ -12,19 +12,40 @@ declare(strict_types=1); namespace Chill\MainBundle\Repository; use Chill\MainBundle\Entity\Civility; -use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; -use Doctrine\Persistence\ManagerRegistry; +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\EntityRepository; -/** - * @method Civility|null find($id, $lockMode = null, $lockVersion = null) - * @method Civility|null findOneBy(array $criteria, array $orderBy = null) - * @method Civility[] findAll() - * @method Civility[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) - */ -class CivilityRepository extends ServiceEntityRepository +class CivilityRepository implements CivilityRepositoryInterface { - public function __construct(ManagerRegistry $registry) + private EntityRepository $repository; + + public function __construct(EntityManagerInterface $entityManager) { - parent::__construct($registry, Civility::class); + $this->repository = $entityManager->getRepository($this->getClassName()); + } + + public function find($id): ?Civility + { + return $this->repository->find($id); + } + + public function findAll(): array + { + return $this->repository->findAll(); + } + + public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array + { + return $this->repository->findBy($criteria, $orderBy, $limit, $offset); + } + + public function findOneBy(array $criteria): ?Civility + { + return $this->repository->findOneBy($criteria); + } + + public function getClassName(): string + { + return Civility::class; } } diff --git a/src/Bundle/ChillMainBundle/Repository/CivilityRepositoryInterface.php b/src/Bundle/ChillMainBundle/Repository/CivilityRepositoryInterface.php new file mode 100644 index 000000000..5d687ac7e --- /dev/null +++ b/src/Bundle/ChillMainBundle/Repository/CivilityRepositoryInterface.php @@ -0,0 +1,34 @@ +repository = $entityManager->getRepository(Language::class); + $this->repository = $entityManager->getRepository($this->getClassName()); } public function find($id, $lockMode = null, $lockVersion = null): ?Language @@ -54,7 +53,7 @@ final class LanguageRepository implements ObjectRepository return $this->repository->findOneBy($criteria, $orderBy); } - public function getClassName() + public function getClassName(): string { return Language::class; } diff --git a/src/Bundle/ChillMainBundle/Repository/LanguageRepositoryInterface.php b/src/Bundle/ChillMainBundle/Repository/LanguageRepositoryInterface.php new file mode 100644 index 000000000..397b264e4 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Repository/LanguageRepositoryInterface.php @@ -0,0 +1,37 @@ +relation; + return $this->household; } public function getPerson(): ?Person diff --git a/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php b/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php index e799a53e4..631e3b69a 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php @@ -20,15 +20,20 @@ use Chill\MainBundle\Export\GroupedExportInterface; use Chill\MainBundle\Export\Helper\ExportAddressHelper; use Chill\MainBundle\Export\ListInterface; use Chill\MainBundle\Form\Type\ChillDateType; +use Chill\MainBundle\Repository\CivilityRepositoryInterface; use Chill\MainBundle\Repository\CountryRepository; +use Chill\MainBundle\Repository\LanguageRepositoryInterface; +use Chill\MainBundle\Repository\UserRepositoryInterface; use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Export\Declarations; +use Chill\PersonBundle\Repository\MaritalStatusRepositoryInterface; use Chill\PersonBundle\Security\Authorization\PersonVoter; use DateTime; use DateTimeImmutable; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Query; +use Doctrine\ORM\QueryBuilder; use Exception; use PhpOffice\PhpSpreadsheet\Shared\Date; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; @@ -44,7 +49,6 @@ use function array_merge; use function count; use function in_array; use function strlen; -use function strtolower; use function uniqid; /** @@ -54,42 +58,89 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou { public const FIELDS = [ 'id', + 'civility', 'firstName', 'lastName', 'birthdate', - 'placeOfBirth', 'gender', 'memo', 'email', 'phonenumber', - 'mobilenumber', 'contactInfo', 'countryOfBirth', 'nationality', - 'address', + 'center', + 'deathdate', + 'placeOfBirth', + 'gender', + 'genderComment', + 'maritalStatus', + 'maritalStatusComment', + 'maritalStatusDate', + 'memo', + 'email', + 'phonenumber', + 'mobilenumber', + 'numberOfChildren', + 'contactInfo', + 'countryOfBirth', + 'nationality', + // add full addresses + 'address_fields', + // add a list of spoken languages + 'spokenLanguages', + // add household id + 'household_id', + // add created at, created by, updated at, and updated by + 'lifecycleUpdate', ]; + private const HELPER_ATTRIBUTES = + ExportAddressHelper::F_ATTRIBUTES | + ExportAddressHelper::F_BUILDING | + ExportAddressHelper::F_COUNTRY | + ExportAddressHelper::F_GEOM | + ExportAddressHelper::F_POSTAL_CODE | + ExportAddressHelper::F_STREET | + ExportAddressHelper::F_AS_STRING; + private ExportAddressHelper $addressHelper; + private CivilityRepositoryInterface $civilityRepository; + private CountryRepository $countryRepository; private CustomFieldProvider $customFieldProvider; private EntityManagerInterface $entityManager; + private LanguageRepositoryInterface $languageRepository; + + private MaritalStatusRepositoryInterface $maritalStatusRepository; + private $slugs = []; private TranslatableStringHelper $translatableStringHelper; private TranslatorInterface $translator; + private UserRepositoryInterface $userRepository; + public function __construct( - CountryRepository $countryRepository, ExportAddressHelper $addressHelper, + CivilityRepositoryInterface $civilityRepository, + CountryRepository $countryRepository, + CustomFieldProvider $customFieldProvider, EntityManagerInterface $em, - TranslatorInterface $translator, + LanguageRepositoryInterface $languageRepository, + MaritalStatusRepositoryInterface $maritalStatusRepository, TranslatableStringHelper $translatableStringHelper, - CustomFieldProvider $customFieldProvider + TranslatorInterface $translator, + UserRepositoryInterface $userRepository ) { $this->addressHelper = $addressHelper; + $this->civilityRepository = $civilityRepository; $this->countryRepository = $countryRepository; $this->entityManager = $em; + $this->languageRepository = $languageRepository; + $this->maritalStatusRepository = $maritalStatusRepository; $this->translator = $translator; $this->translatableStringHelper = $translatableStringHelper; $this->customFieldProvider = $customFieldProvider; + $this->userRepository = $userRepository; } public function buildForm(FormBuilderInterface $builder) @@ -110,7 +161,7 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou 'label' => 'Fields to include in export', 'choice_attr' => static function (string $val): array { // add a 'data-display-target' for address fields - if (substr($val, 0, 7) === 'address') { + if (substr($val, 0, 7) === 'address' || 'center' === $val || 'household' === $val) { return ['data-display-target' => 'address_date']; } @@ -130,7 +181,8 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou // add a date field for addresses $builder->add('address_date', ChillDateType::class, [ - 'label' => 'Address valid at this date', + 'label' => 'Data valid at this date', + 'help' => 'Data regarding center, addresses, and so on will be computed at this date', 'data' => new DateTimeImmutable(), 'input' => 'datetime_immutable', ]); @@ -153,17 +205,21 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou public function getLabels($key, array $values, $data) { - if (substr($key, 0, strlen('address')) === 'address') { - return $this->addressHelper->getLabel($key, $values, $data, 'address_'); + if (substr($key, 0, strlen('address_fields')) === 'address_fields') { + return $this->addressHelper->getLabel($key, $values, $data, 'address_fields'); } switch ($key) { case 'birthdate': + case 'deathdate': + case 'maritalStatusDate': + case 'createdAt': + case 'updatedAt': // for birthdate, we have to transform the string into a date // to format the date correctly. - return static function ($value) { + return function ($value) use ($key) { if ('_header' === $value) { - return 'birthdate'; + return $this->translator->trans($key); } if (null === $value) { @@ -172,32 +228,120 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou // warning: won't work with DateTimeImmutable as we reset time a few lines later $date = DateTime::createFromFormat('Y-m-d', $value); + $hasTime = false; + + if (false === $date) { + $date = DateTime::createFromFormat('Y-m-d H:i:s', $value); + $hasTime = true; + } + // check that the creation could occurs. if (false === $date) { throw new Exception(sprintf('The value %s could ' . 'not be converted to %s', $value, DateTime::class)); } - $date->setTime(0, 0, 0); + if (!$hasTime) { + $date->setTime(0, 0, 0); + } return $date; }; + case 'createdBy': + case 'updatedBy': + return function ($value) use ($key) { + if ('_header' === $value) { + return $this->translator->trans($key); + } + + if (null === $value) { + return ''; + } + + return $this->userRepository->find($value)->getLabel(); + }; + + case 'civility': + return function ($value) use ($key) { + if ('_header' === $value) { + return $this->translator->trans($key); + } + + if (null === $value) { + return ''; + } + + $civility = $this->civilityRepository->find($value); + + if (null === $civility) { + return ''; + } + + return $this->translatableStringHelper->localize($civility->getName()); + }; + case 'gender': // for gender, we have to translate men/women statement - return function ($value) { + return function ($value) use ($key) { if ('_header' === $value) { - return 'gender'; + return $this->translator->trans($key); } return $this->translator->trans($value); }; + case 'maritalStatus': + return function ($value) use ($key) { + if ('_header' === $value) { + return $this->translator->trans($key); + } + + if (null === $value) { + return ''; + } + + $maritalStatus = $this->maritalStatusRepository->find($value); + + return $this->translatableStringHelper->localize($maritalStatus->getName()); + }; + + case 'spokenLanguages': + return function ($value) use ($key) { + if ('_header' === $value) { + return $this->translator->trans($key); + } + + if (null === $value) { + return ''; + } + + $ids = json_decode($value); + + return + implode( + '|', + array_map(function ($id) { + if (null === $id) { + return ''; + } + + $lang = $this->languageRepository->find($id); + + if (null === $lang) { + return null; + } + + return $this->translatableStringHelper->localize($lang->getName()); + }, $ids) + ); + }; + case 'countryOfBirth': case 'nationality': return function ($value) use ($key) { if ('_header' === $value) { - return strtolower($key); + return $this->translator->trans($key); } if (null === $value) { @@ -214,9 +358,9 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou default: // for fields which are associated with person if (in_array($key, self::FIELDS, true)) { - return static function ($value) use ($key) { + return function ($value) use ($key) { if ('_header' === $value) { - return strtolower($key); + return $this->translator->trans($key); } return $value; @@ -231,9 +375,19 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou { $fields = []; - foreach ($data['fields'] as $key) { - if (substr($key, 0, strlen('address')) === 'address') { - $fields = array_merge($fields, $this->addressHelper->getKeys(0b01111111, 'address_')); + foreach (self::FIELDS as $key) { + if (!in_array($key, $data['fields'], true)) { + continue; + } + + if (substr($key, 0, strlen('address_fields')) === 'address_fields') { + $fields = array_merge($fields, $this->addressHelper->getKeys(self::HELPER_ATTRIBUTES, 'address_fields')); + + continue; + } + + if ('lifecycleUpdate' === $key) { + $fields = array_merge($fields, ['createdAt', 'createdBy', 'updatedAt', 'updatedBy']); continue; } @@ -286,8 +440,10 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou ) ->setParameter('authorized_centers', $centers); + $fields = $data['fields']; + foreach (self::FIELDS as $f) { - if (!in_array($f, $data['fields'], true)) { + if (!in_array($f, $fields, true)) { continue; } @@ -298,31 +454,90 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou break; - case 'address': - foreach ($this->addressHelper->getKeys(0b01111111, 'address_') as $key) { + case 'address_fields': + foreach ($this->addressHelper->getKeys(self::HELPER_ATTRIBUTES, 'address_fields') as $key) { $qb - ->addSelect(sprintf('IDENTITY(currentPersonAddress.address) AS %s', $key)); + ->addSelect(sprintf('IDENTITY(personHouseholdAddress.address) AS %s', $key)); } - if (!(in_array('currentPersonAddress', $qb->getAllAliases(), true))) { - $qb - ->leftJoin('person.currentPersonAddress', 'currentPersonAddress') - ->andWhere( - $qb->expr()->orX( - // no address at this time - $qb->expr()->isNull('currentPersonAddress'), - // there is one address... - $qb->expr()->andX( - $qb->expr()->lte('currentPersonAddress.validFrom', ':address_date'), - $qb->expr()->orX( - $qb->expr()->isNull('currentPersonAddress.validTo'), - $qb->expr()->gt('currentPersonAddress.validTo', ':address_date') - ) + $this->addCurrentAddressAt($qb, $data['address_date']); + + break; + + case 'spokenLanguages': + $qb + ->leftJoin('person.spokenLanguages', 'spokenLanguage') + ->addSelect('AGGREGATE(spokenLanguage.id) AS spokenLanguages') + ->addGroupBy('person'); + + if (in_array('center', $fields, true)) { + $qb->addGroupBy('center'); + } + + if (in_array('address_fields', $fields, true)) { + $qb->addGroupBy('address_fieldsid'); + } + + if (in_array('household_id', $fields, true)) { + $qb->addGroupBy('household_id'); + } + + break; + + case 'household_id': + $qb + ->addSelect('IDENTITY(personHouseholdAddress.household) AS household_id'); + + $this->addCurrentAddressAt($qb, $data['address_date']); + + break; + + case 'center': + $qb + ->addSelect('IDENTITY(centerHistory.center) AS center') + ->leftJoin('person.centerHistory', 'centerHistory') + ->andWhere( + $qb->expr()->orX( + $qb->expr()->isNull('centerHistory'), + $qb->expr()->andX( + $qb->expr()->lte('centerHistory.startDate', ':address_date'), + $qb->expr()->orX( + $qb->expr()->isNull('centerHistory.endDate'), + $qb->expr()->gte('centerHistory.endDate', ':address_date') ) ) ) - ->setParameter('address_date', $data['address_date']); - } + ) + ->setParameter('address_date', $data['address_date']); + + break; + + case 'lifecycleUpdate': + $qb + ->addSelect('person.createdAt AS createdAt') + ->addSelect('IDENTITY(person.createdBy) AS createdBy') + ->addSelect('person.updatedAt AS updatedAt') + ->addSelect('IDENTITY(person.updatedBy) AS updatedBy'); + + break; + + case 'genderComment': + $qb->addSelect('person.genderComment.comment AS genderComment'); + + break; + + case 'maritalStatus': + $qb->addSelect('IDENTITY(person.maritalStatus) AS maritalStatus'); + + break; + + case 'maritalStatusComment': + $qb->addSelect('person.maritalStatusComment.comment AS maritalStatusComment'); + + break; + + case 'civility': + $qb->addSelect('IDENTITY(person.civility) AS civility'); break; @@ -332,6 +547,10 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou } foreach ($this->getCustomFields() as $cf) { + if (!in_array($cf->getSlug(), $fields, true)) { + continue; + } + $cfType = $this->customFieldProvider->getCustomFieldByType($cf->getType()); if ($cfType instanceof CustomFieldChoice && $cfType->isMultiple($cf)) { @@ -383,7 +602,7 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou // check if there is one field starting with address in data if (count(array_intersect($data['fields'], $addressFields)) > 0) { // if a field address is checked, the date must not be empty - if (empty($data['address_date'])) { + if (!$data['address_date'] instanceof DateTimeImmutable) { $context ->buildViolation('You must set this date if an address is checked') ->atPath('address_date') @@ -392,6 +611,29 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou } } + private function addCurrentAddressAt(QueryBuilder $qb, DateTimeImmutable $date): void + { + if (!(in_array('personHouseholdAddress', $qb->getAllAliases(), true))) { + $qb + ->leftJoin('person.householdAddresses', 'personHouseholdAddress') + ->andWhere( + $qb->expr()->orX( + // no address at this time + $qb->expr()->isNull('personHouseholdAddress'), + // there is one address... + $qb->expr()->andX( + $qb->expr()->lte('personHouseholdAddress.validFrom', ':address_date'), + $qb->expr()->orX( + $qb->expr()->isNull('personHouseholdAddress.validTo'), + $qb->expr()->gt('personHouseholdAddress.validTo', ':address_date') + ) + ) + ) + ) + ->setParameter('address_date', $date); + } + } + private function DQLToSlug($cleanedSlug) { return $this->slugs[$cleanedSlug]['slug']; @@ -464,7 +706,7 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou . ' | ' . $label; } - if ('_other' === $slugChoice && $cfType->isChecked($cf, $choiceSlug, $decoded)) { + if ('_other' === $slugChoice && $cfType->isChecked($cf, $slugChoice, $decoded)) { return $cfType->extractOtherValue($cf, $decoded); } diff --git a/src/Bundle/ChillPersonBundle/Repository/MaritalStatusRepository.php b/src/Bundle/ChillPersonBundle/Repository/MaritalStatusRepository.php index 1df23fa31..8697f9346 100644 --- a/src/Bundle/ChillPersonBundle/Repository/MaritalStatusRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/MaritalStatusRepository.php @@ -14,9 +14,8 @@ namespace Chill\PersonBundle\Repository; use Chill\PersonBundle\Entity\MaritalStatus; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; -use Doctrine\Persistence\ObjectRepository; -class MaritalStatusRepository implements ObjectRepository +class MaritalStatusRepository implements MaritalStatusRepositoryInterface { private EntityRepository $repository; diff --git a/src/Bundle/ChillPersonBundle/Repository/MaritalStatusRepositoryInterface.php b/src/Bundle/ChillPersonBundle/Repository/MaritalStatusRepositoryInterface.php new file mode 100644 index 000000000..1f51060e9 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Repository/MaritalStatusRepositoryInterface.php @@ -0,0 +1,27 @@ + Date: Wed, 26 Oct 2022 13:30:16 +0200 Subject: [PATCH 20/73] DX: Refactor ListPerson to extract methods linked to label and select --- .../Export/Export/ListPerson.php | 383 +--------------- .../Export/Helper/ListPersonHelper.php | 428 ++++++++++++++++++ 2 files changed, 443 insertions(+), 368 deletions(-) create mode 100644 src/Bundle/ChillPersonBundle/Export/Helper/ListPersonHelper.php diff --git a/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php b/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php index 631e3b69a..eb746e122 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php @@ -20,28 +20,20 @@ use Chill\MainBundle\Export\GroupedExportInterface; use Chill\MainBundle\Export\Helper\ExportAddressHelper; use Chill\MainBundle\Export\ListInterface; use Chill\MainBundle\Form\Type\ChillDateType; -use Chill\MainBundle\Repository\CivilityRepositoryInterface; -use Chill\MainBundle\Repository\CountryRepository; -use Chill\MainBundle\Repository\LanguageRepositoryInterface; -use Chill\MainBundle\Repository\UserRepositoryInterface; use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Export\Declarations; -use Chill\PersonBundle\Repository\MaritalStatusRepositoryInterface; +use Chill\PersonBundle\Export\Helper\ListPersonHelper; use Chill\PersonBundle\Security\Authorization\PersonVoter; -use DateTime; use DateTimeImmutable; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Query; -use Doctrine\ORM\QueryBuilder; -use Exception; use PhpOffice\PhpSpreadsheet\Shared\Date; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Validator\Constraints\Callback; use Symfony\Component\Validator\Context\ExecutionContextInterface; -use Symfony\Contracts\Translation\TranslatorInterface; use function addcslashes; use function array_key_exists; use function array_keys; @@ -56,96 +48,35 @@ use function uniqid; */ class ListPerson implements ExportElementValidatedInterface, ListInterface, GroupedExportInterface { - public const FIELDS = [ - 'id', - 'civility', - 'firstName', - 'lastName', - 'birthdate', - 'center', - 'deathdate', - 'placeOfBirth', - 'gender', - 'genderComment', - 'maritalStatus', - 'maritalStatusComment', - 'maritalStatusDate', - 'memo', - 'email', - 'phonenumber', - 'mobilenumber', - 'numberOfChildren', - 'contactInfo', - 'countryOfBirth', - 'nationality', - // add full addresses - 'address_fields', - // add a list of spoken languages - 'spokenLanguages', - // add household id - 'household_id', - // add created at, created by, updated at, and updated by - 'lifecycleUpdate', - ]; - - private const HELPER_ATTRIBUTES = - ExportAddressHelper::F_ATTRIBUTES | - ExportAddressHelper::F_BUILDING | - ExportAddressHelper::F_COUNTRY | - ExportAddressHelper::F_GEOM | - ExportAddressHelper::F_POSTAL_CODE | - ExportAddressHelper::F_STREET | - ExportAddressHelper::F_AS_STRING; - private ExportAddressHelper $addressHelper; - private CivilityRepositoryInterface $civilityRepository; - - private CountryRepository $countryRepository; - private CustomFieldProvider $customFieldProvider; private EntityManagerInterface $entityManager; - private LanguageRepositoryInterface $languageRepository; - - private MaritalStatusRepositoryInterface $maritalStatusRepository; + private ListPersonHelper $listPersonHelper; private $slugs = []; private TranslatableStringHelper $translatableStringHelper; - private TranslatorInterface $translator; - - private UserRepositoryInterface $userRepository; - public function __construct( ExportAddressHelper $addressHelper, - CivilityRepositoryInterface $civilityRepository, - CountryRepository $countryRepository, CustomFieldProvider $customFieldProvider, + ListPersonHelper $listPersonHelper, EntityManagerInterface $em, - LanguageRepositoryInterface $languageRepository, - MaritalStatusRepositoryInterface $maritalStatusRepository, - TranslatableStringHelper $translatableStringHelper, - TranslatorInterface $translator, - UserRepositoryInterface $userRepository + TranslatableStringHelper $translatableStringHelper ) { $this->addressHelper = $addressHelper; - $this->civilityRepository = $civilityRepository; - $this->countryRepository = $countryRepository; - $this->entityManager = $em; - $this->languageRepository = $languageRepository; - $this->maritalStatusRepository = $maritalStatusRepository; - $this->translator = $translator; - $this->translatableStringHelper = $translatableStringHelper; $this->customFieldProvider = $customFieldProvider; - $this->userRepository = $userRepository; + $this->listPersonHelper = $listPersonHelper; + $this->entityManager = $em; + $this->translatableStringHelper = $translatableStringHelper; } public function buildForm(FormBuilderInterface $builder) { - $choices = array_combine(self::FIELDS, self::FIELDS); + $choices = array_combine(ListPersonHelper::FIELDS, ListPersonHelper::FIELDS); foreach ($this->getCustomFields() as $cf) { $choices[$this->translatableStringHelper->localize($cf->getName())] @@ -205,183 +136,24 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou public function getLabels($key, array $values, $data) { - if (substr($key, 0, strlen('address_fields')) === 'address_fields') { - return $this->addressHelper->getLabel($key, $values, $data, 'address_fields'); + if (in_array($key, $this->listPersonHelper->getAllPossibleFields(), true)) { + return $this->listPersonHelper->getLabels($key, $values, $data); } - switch ($key) { - case 'birthdate': - case 'deathdate': - case 'maritalStatusDate': - case 'createdAt': - case 'updatedAt': - // for birthdate, we have to transform the string into a date - // to format the date correctly. - return function ($value) use ($key) { - if ('_header' === $value) { - return $this->translator->trans($key); - } - - if (null === $value) { - return ''; - } - - // warning: won't work with DateTimeImmutable as we reset time a few lines later - $date = DateTime::createFromFormat('Y-m-d', $value); - $hasTime = false; - - if (false === $date) { - $date = DateTime::createFromFormat('Y-m-d H:i:s', $value); - $hasTime = true; - } - - // check that the creation could occurs. - if (false === $date) { - throw new Exception(sprintf('The value %s could ' - . 'not be converted to %s', $value, DateTime::class)); - } - - if (!$hasTime) { - $date->setTime(0, 0, 0); - } - - return $date; - }; - - case 'createdBy': - case 'updatedBy': - return function ($value) use ($key) { - if ('_header' === $value) { - return $this->translator->trans($key); - } - - if (null === $value) { - return ''; - } - - return $this->userRepository->find($value)->getLabel(); - }; - - case 'civility': - return function ($value) use ($key) { - if ('_header' === $value) { - return $this->translator->trans($key); - } - - if (null === $value) { - return ''; - } - - $civility = $this->civilityRepository->find($value); - - if (null === $civility) { - return ''; - } - - return $this->translatableStringHelper->localize($civility->getName()); - }; - - case 'gender': - // for gender, we have to translate men/women statement - return function ($value) use ($key) { - if ('_header' === $value) { - return $this->translator->trans($key); - } - - return $this->translator->trans($value); - }; - - case 'maritalStatus': - return function ($value) use ($key) { - if ('_header' === $value) { - return $this->translator->trans($key); - } - - if (null === $value) { - return ''; - } - - $maritalStatus = $this->maritalStatusRepository->find($value); - - return $this->translatableStringHelper->localize($maritalStatus->getName()); - }; - - case 'spokenLanguages': - return function ($value) use ($key) { - if ('_header' === $value) { - return $this->translator->trans($key); - } - - if (null === $value) { - return ''; - } - - $ids = json_decode($value); - - return - implode( - '|', - array_map(function ($id) { - if (null === $id) { - return ''; - } - - $lang = $this->languageRepository->find($id); - - if (null === $lang) { - return null; - } - - return $this->translatableStringHelper->localize($lang->getName()); - }, $ids) - ); - }; - - case 'countryOfBirth': - case 'nationality': - return function ($value) use ($key) { - if ('_header' === $value) { - return $this->translator->trans($key); - } - - if (null === $value) { - return ''; - } - - $country = $this->countryRepository->find($value); - - return $this->translatableStringHelper->localize( - $country->getName() - ); - }; - - default: - // for fields which are associated with person - if (in_array($key, self::FIELDS, true)) { - return function ($value) use ($key) { - if ('_header' === $value) { - return $this->translator->trans($key); - } - - return $value; - }; - } - - return $this->getLabelForCustomField($key, $values, $data); - } + return $this->getLabelForCustomField($key, $values, $data); } public function getQueryKeys($data) { $fields = []; - foreach (self::FIELDS as $key) { + foreach (ListPersonHelper::FIELDS as $key) { if (!in_array($key, $data['fields'], true)) { continue; } if (substr($key, 0, strlen('address_fields')) === 'address_fields') { - $fields = array_merge($fields, $this->addressHelper->getKeys(self::HELPER_ATTRIBUTES, 'address_fields')); + $fields = array_merge($fields, $this->addressHelper->getKeys(ListPersonHelper::HELPER_ATTRIBUTES, 'address_fields')); continue; } @@ -442,109 +214,7 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou $fields = $data['fields']; - foreach (self::FIELDS as $f) { - if (!in_array($f, $fields, true)) { - continue; - } - - switch ($f) { - case 'countryOfBirth': - case 'nationality': - $qb->addSelect(sprintf('IDENTITY(person.%s) as %s', $f, $f)); - - break; - - case 'address_fields': - foreach ($this->addressHelper->getKeys(self::HELPER_ATTRIBUTES, 'address_fields') as $key) { - $qb - ->addSelect(sprintf('IDENTITY(personHouseholdAddress.address) AS %s', $key)); - } - - $this->addCurrentAddressAt($qb, $data['address_date']); - - break; - - case 'spokenLanguages': - $qb - ->leftJoin('person.spokenLanguages', 'spokenLanguage') - ->addSelect('AGGREGATE(spokenLanguage.id) AS spokenLanguages') - ->addGroupBy('person'); - - if (in_array('center', $fields, true)) { - $qb->addGroupBy('center'); - } - - if (in_array('address_fields', $fields, true)) { - $qb->addGroupBy('address_fieldsid'); - } - - if (in_array('household_id', $fields, true)) { - $qb->addGroupBy('household_id'); - } - - break; - - case 'household_id': - $qb - ->addSelect('IDENTITY(personHouseholdAddress.household) AS household_id'); - - $this->addCurrentAddressAt($qb, $data['address_date']); - - break; - - case 'center': - $qb - ->addSelect('IDENTITY(centerHistory.center) AS center') - ->leftJoin('person.centerHistory', 'centerHistory') - ->andWhere( - $qb->expr()->orX( - $qb->expr()->isNull('centerHistory'), - $qb->expr()->andX( - $qb->expr()->lte('centerHistory.startDate', ':address_date'), - $qb->expr()->orX( - $qb->expr()->isNull('centerHistory.endDate'), - $qb->expr()->gte('centerHistory.endDate', ':address_date') - ) - ) - ) - ) - ->setParameter('address_date', $data['address_date']); - - break; - - case 'lifecycleUpdate': - $qb - ->addSelect('person.createdAt AS createdAt') - ->addSelect('IDENTITY(person.createdBy) AS createdBy') - ->addSelect('person.updatedAt AS updatedAt') - ->addSelect('IDENTITY(person.updatedBy) AS updatedBy'); - - break; - - case 'genderComment': - $qb->addSelect('person.genderComment.comment AS genderComment'); - - break; - - case 'maritalStatus': - $qb->addSelect('IDENTITY(person.maritalStatus) AS maritalStatus'); - - break; - - case 'maritalStatusComment': - $qb->addSelect('person.maritalStatusComment.comment AS maritalStatusComment'); - - break; - - case 'civility': - $qb->addSelect('IDENTITY(person.civility) AS civility'); - - break; - - default: - $qb->addSelect(sprintf('person.%s as %s', $f, $f)); - } - } + $this->listPersonHelper->addSelect($qb, $fields, $data['address_date']); foreach ($this->getCustomFields() as $cf) { if (!in_array($cf->getSlug(), $fields, true)) { @@ -595,7 +265,7 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou { // get the field starting with address_ $addressFields = array_filter( - self::FIELDS, + ListPersonHelper::FIELDS, static fn (string $el): bool => substr($el, 0, 8) === 'address_' ); @@ -611,29 +281,6 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou } } - private function addCurrentAddressAt(QueryBuilder $qb, DateTimeImmutable $date): void - { - if (!(in_array('personHouseholdAddress', $qb->getAllAliases(), true))) { - $qb - ->leftJoin('person.householdAddresses', 'personHouseholdAddress') - ->andWhere( - $qb->expr()->orX( - // no address at this time - $qb->expr()->isNull('personHouseholdAddress'), - // there is one address... - $qb->expr()->andX( - $qb->expr()->lte('personHouseholdAddress.validFrom', ':address_date'), - $qb->expr()->orX( - $qb->expr()->isNull('personHouseholdAddress.validTo'), - $qb->expr()->gt('personHouseholdAddress.validTo', ':address_date') - ) - ) - ) - ) - ->setParameter('address_date', $date); - } - } - private function DQLToSlug($cleanedSlug) { return $this->slugs[$cleanedSlug]['slug']; diff --git a/src/Bundle/ChillPersonBundle/Export/Helper/ListPersonHelper.php b/src/Bundle/ChillPersonBundle/Export/Helper/ListPersonHelper.php new file mode 100644 index 000000000..7a14e0446 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Helper/ListPersonHelper.php @@ -0,0 +1,428 @@ +addressHelper = $addressHelper; + $this->civilityRepository = $civilityRepository; + $this->countryRepository = $countryRepository; + $this->languageRepository = $languageRepository; + $this->maritalStatusRepository = $maritalStatusRepository; + $this->translatableStringHelper = $translatableStringHelper; + $this->translator = $translator; + $this->userRepository = $userRepository; + } + + /** + * @param array|value-of[] $fields + */ + public function addSelect(QueryBuilder $qb, array $fields, DateTimeImmutable $computedDate): void + { + foreach (ListPersonHelper::FIELDS as $f) { + if (!in_array($f, $fields, true)) { + continue; + } + + switch ($f) { + case 'countryOfBirth': + case 'nationality': + $qb->addSelect(sprintf('IDENTITY(person.%s) as %s', $f, $f)); + + break; + + case 'address_fields': + foreach ($this->addressHelper->getKeys(ListPersonHelper::HELPER_ATTRIBUTES, 'address_fields') as $key) { + $qb + ->addSelect(sprintf('IDENTITY(personHouseholdAddress.address) AS %s', $key)); + } + + $this->addCurrentAddressAt($qb, $computedDate); + + break; + + case 'spokenLanguages': + $qb + ->leftJoin('person.spokenLanguages', 'spokenLanguage') + ->addSelect('AGGREGATE(spokenLanguage.id) AS spokenLanguages') + ->addGroupBy('person'); + + if (in_array('center', $fields, true)) { + $qb->addGroupBy('center'); + } + + if (in_array('address_fields', $fields, true)) { + $qb->addGroupBy('address_fieldsid'); + } + + if (in_array('household_id', $fields, true)) { + $qb->addGroupBy('household_id'); + } + + break; + + case 'household_id': + $qb + ->addSelect('IDENTITY(personHouseholdAddress.household) AS household_id'); + + $this->addCurrentAddressAt($qb, $computedDate); + + break; + + case 'center': + $qb + ->addSelect('IDENTITY(centerHistory.center) AS center') + ->leftJoin('person.centerHistory', 'centerHistory') + ->andWhere( + $qb->expr()->orX( + $qb->expr()->isNull('centerHistory'), + $qb->expr()->andX( + $qb->expr()->lte('centerHistory.startDate', ':address_date'), + $qb->expr()->orX( + $qb->expr()->isNull('centerHistory.endDate'), + $qb->expr()->gte('centerHistory.endDate', ':address_date') + ) + ) + ) + ) + ->setParameter('address_date', $computedDate); + + break; + + case 'lifecycleUpdate': + $qb + ->addSelect('person.createdAt AS createdAt') + ->addSelect('IDENTITY(person.createdBy) AS createdBy') + ->addSelect('person.updatedAt AS updatedAt') + ->addSelect('IDENTITY(person.updatedBy) AS updatedBy'); + + break; + + case 'genderComment': + $qb->addSelect('person.genderComment.comment AS genderComment'); + + break; + + case 'maritalStatus': + $qb->addSelect('IDENTITY(person.maritalStatus) AS maritalStatus'); + + break; + + case 'maritalStatusComment': + $qb->addSelect('person.maritalStatusComment.comment AS maritalStatusComment'); + + break; + + case 'civility': + $qb->addSelect('IDENTITY(person.civility) AS civility'); + + break; + + default: + $qb->addSelect(sprintf('person.%s as %s', $f, $f)); + } + } + } + + /** + * @return array|string[] + */ + public function getAllPossibleFields(): array + { + return array_merge( + self::FIELDS, + ['createdAt', 'createdBy', 'updatedAt', 'updatedBy'], + $this->addressHelper->getKeys(self::HELPER_ATTRIBUTES, 'address_fields') + ); + } + + public function getLabels($key, array $values, $data): callable + { + if (substr($key, 0, strlen('address_fields')) === 'address_fields') { + return $this->addressHelper->getLabel($key, $values, $data, 'address_fields'); + } + + switch ($key) { + case 'birthdate': + case 'deathdate': + case 'maritalStatusDate': + case 'createdAt': + case 'updatedAt': + // for birthdate, we have to transform the string into a date + // to format the date correctly. + return function ($value) use ($key) { + if ('_header' === $value) { + return $this->translator->trans($key); + } + + if (null === $value) { + return ''; + } + + // warning: won't work with DateTimeImmutable as we reset time a few lines later + $date = DateTime::createFromFormat('Y-m-d', $value); + $hasTime = false; + + if (false === $date) { + $date = DateTime::createFromFormat('Y-m-d H:i:s', $value); + $hasTime = true; + } + + // check that the creation could occurs. + if (false === $date) { + throw new Exception(sprintf('The value %s could ' + . 'not be converted to %s', $value, DateTime::class)); + } + + if (!$hasTime) { + $date->setTime(0, 0, 0); + } + + return $date; + }; + + case 'createdBy': + case 'updatedBy': + return function ($value) use ($key) { + if ('_header' === $value) { + return $this->translator->trans($key); + } + + if (null === $value) { + return ''; + } + + return $this->userRepository->find($value)->getLabel(); + }; + + case 'civility': + return function ($value) use ($key) { + if ('_header' === $value) { + return $this->translator->trans($key); + } + + if (null === $value) { + return ''; + } + + $civility = $this->civilityRepository->find($value); + + if (null === $civility) { + return ''; + } + + return $this->translatableStringHelper->localize($civility->getName()); + }; + + case 'gender': + // for gender, we have to translate men/women statement + return function ($value) use ($key) { + if ('_header' === $value) { + return $this->translator->trans($key); + } + + return $this->translator->trans($value); + }; + + case 'maritalStatus': + return function ($value) use ($key) { + if ('_header' === $value) { + return $this->translator->trans($key); + } + + if (null === $value) { + return ''; + } + + $maritalStatus = $this->maritalStatusRepository->find($value); + + return $this->translatableStringHelper->localize($maritalStatus->getName()); + }; + + case 'spokenLanguages': + return function ($value) use ($key) { + if ('_header' === $value) { + return $this->translator->trans($key); + } + + if (null === $value) { + return ''; + } + + $ids = json_decode($value); + + return + implode( + '|', + array_map(function ($id) { + if (null === $id) { + return ''; + } + + $lang = $this->languageRepository->find($id); + + if (null === $lang) { + return null; + } + + return $this->translatableStringHelper->localize($lang->getName()); + }, $ids) + ); + }; + + case 'countryOfBirth': + case 'nationality': + return function ($value) use ($key) { + if ('_header' === $value) { + return $this->translator->trans($key); + } + + if (null === $value) { + return ''; + } + + $country = $this->countryRepository->find($value); + + return $this->translatableStringHelper->localize( + $country->getName() + ); + }; + + default: + // for fields which are associated with person + if (in_array($key, ListPersonHelper::FIELDS, true)) { + return function ($value) use ($key) { + if ('_header' === $value) { + return $this->translator->trans($key); + } + + return $value; + }; + } + } + } + + private function addCurrentAddressAt(QueryBuilder $qb, DateTimeImmutable $date): void + { + if (!(in_array('personHouseholdAddress', $qb->getAllAliases(), true))) { + $qb + ->leftJoin('person.householdAddresses', 'personHouseholdAddress') + ->andWhere( + $qb->expr()->orX( + // no address at this time + $qb->expr()->isNull('personHouseholdAddress'), + // there is one address... + $qb->expr()->andX( + $qb->expr()->lte('personHouseholdAddress.validFrom', ':address_date'), + $qb->expr()->orX( + $qb->expr()->isNull('personHouseholdAddress.validTo'), + $qb->expr()->gt('personHouseholdAddress.validTo', ':address_date') + ) + ) + ) + ) + ->setParameter('address_date', $date); + } + } +} From cf7d0c1bdb7176b163d84f7077016458a1198551 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 26 Oct 2022 14:57:10 +0200 Subject: [PATCH 21/73] Fixed: [export][acl][Social issue filter] fix type for retrieving data from form --- .../Filter/AccompanyingCourseFilters/SocialIssueFilter.php | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/SocialIssueFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/SocialIssueFilter.php index 141a1a2db..917e129bf 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/SocialIssueFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/SocialIssueFilter.php @@ -12,7 +12,6 @@ declare(strict_types=1); namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters; use Chill\MainBundle\Export\FilterInterface; -use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\PersonBundle\Entity\SocialWork\SocialIssue; use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Form\Type\PickSocialIssueType; @@ -31,15 +30,11 @@ class SocialIssueFilter implements FilterInterface private SocialIssueRender $socialIssueRender; - private TranslatableStringHelper $translatableStringHelper; - public function __construct( TranslatorInterface $translator, - TranslatableStringHelper $translatableStringHelper, SocialIssueRender $socialIssueRender ) { $this->translator = $translator; - $this->translatableStringHelper = $translatableStringHelper; $this->socialIssueRender = $socialIssueRender; } @@ -59,7 +54,7 @@ class SocialIssueFilter implements FilterInterface $qb->andWhere($clause) ->setParameter( 'socialissues', - SocialIssue::getDescendantsWithThisForIssues($data['accepted_socialissues']) + SocialIssue::getDescendantsWithThisForIssues($data['accepted_socialissues']->toArray()) ); } From 4e55f1ede77cac731d77fcfc515733d37b95400f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 26 Oct 2022 14:58:28 +0200 Subject: [PATCH 22/73] DX: ensure that the return type is correct in ListPersonHelper --- .../Export/Helper/ListPersonHelper.php | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Export/Helper/ListPersonHelper.php b/src/Bundle/ChillPersonBundle/Export/Helper/ListPersonHelper.php index 7a14e0446..69f797c3d 100644 --- a/src/Bundle/ChillPersonBundle/Export/Helper/ListPersonHelper.php +++ b/src/Bundle/ChillPersonBundle/Export/Helper/ListPersonHelper.php @@ -25,6 +25,7 @@ use Doctrine\ORM\Query; use Doctrine\ORM\QueryBuilder; use Exception; use PhpOffice\PhpSpreadsheet\Shared\Date; +use RuntimeException; use Symfony\Contracts\Translation\TranslatorInterface; use function in_array; use function strlen; @@ -390,16 +391,18 @@ class ListPersonHelper }; default: - // for fields which are associated with person - if (in_array($key, ListPersonHelper::FIELDS, true)) { - return function ($value) use ($key) { - if ('_header' === $value) { - return $this->translator->trans($key); - } - - return $value; - }; + if (!in_array($key, self::getAllPossibleFields(), true)) { + throw new RuntimeException("this key is not supported by this helper: {$key}"); } + + // for fields which are associated with person + return function ($value) use ($key) { + if ('_header' === $value) { + return $this->translator->trans($key); + } + + return $value; + }; } } From d75ec92417d0f82b12aae3e8a33f9244326638ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 26 Oct 2022 14:59:06 +0200 Subject: [PATCH 23/73] Feature: [export] add a list of people having an accompanying period --- .../ListPersonWithAccompanyingPeriod.php | 218 ++++++++++++++++++ .../config/services/exports_person.yaml | 9 +- .../translations/messages.fr.yml | 5 +- 3 files changed, 228 insertions(+), 4 deletions(-) create mode 100644 src/Bundle/ChillPersonBundle/Export/Export/ListPersonWithAccompanyingPeriod.php diff --git a/src/Bundle/ChillPersonBundle/Export/Export/ListPersonWithAccompanyingPeriod.php b/src/Bundle/ChillPersonBundle/Export/Export/ListPersonWithAccompanyingPeriod.php new file mode 100644 index 000000000..ce97ce662 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Export/ListPersonWithAccompanyingPeriod.php @@ -0,0 +1,218 @@ +addressHelper = $addressHelper; + $this->listPersonHelper = $listPersonHelper; + $this->entityManager = $em; + } + + public function buildForm(FormBuilderInterface $builder) + { + $choices = array_combine(ListPersonHelper::FIELDS, ListPersonHelper::FIELDS); + + // Add a checkbox to select fields + $builder->add('fields', ChoiceType::class, [ + 'multiple' => true, + 'expanded' => true, + 'choices' => $choices, + 'label' => 'Fields to include in export', + 'choice_attr' => static function (string $val): array { + // add a 'data-display-target' for address fields + if (substr($val, 0, 7) === 'address' || 'center' === $val || 'household' === $val) { + return ['data-display-target' => 'address_date']; + } + + return []; + }, + 'constraints' => [new Callback([ + 'callback' => static function ($selected, ExecutionContextInterface $context) { + if (count($selected) === 0) { + $context->buildViolation('You must select at least one element') + ->atPath('fields') + ->addViolation(); + } + }, + ])], + 'data' => array_values($choices), + ]); + + // add a date field for addresses + $builder->add('address_date', ChillDateType::class, [ + 'label' => 'Data valid at this date', + 'help' => 'Data regarding center, addresses, and so on will be computed at this date', + 'data' => new DateTimeImmutable(), + 'input' => 'datetime_immutable', + ]); + } + + public function getAllowedFormattersTypes() + { + return [FormatterInterface::TYPE_LIST]; + } + + public function getDescription() + { + return 'export.list.person_with_acp.Create a list of people having an accompaying periods, according to various filters.'; + } + + public function getGroup(): string + { + return 'Exports of persons'; + } + + public function getLabels($key, array $values, $data) + { + return $this->listPersonHelper->getLabels($key, $values, $data); + } + + public function getQueryKeys($data) + { + $fields = []; + + foreach (ListPersonHelper::FIELDS as $key) { + if (!in_array($key, $data['fields'], true)) { + continue; + } + + if (substr($key, 0, strlen('address_fields')) === 'address_fields') { + $fields = array_merge($fields, $this->addressHelper->getKeys(ListPersonHelper::HELPER_ATTRIBUTES, 'address_fields')); + + continue; + } + + if ('lifecycleUpdate' === $key) { + $fields = array_merge($fields, ['createdAt', 'createdBy', 'updatedAt', 'updatedBy']); + + continue; + } + + $fields[] = $key; + } + + return $fields; + } + + public function getResult($query, $data) + { + return $query->getQuery()->getResult(AbstractQuery::HYDRATE_SCALAR); + } + + public function getTitle() + { + return 'export.list.person_with_acp.List peoples having an accompanying period'; + } + + public function getType() + { + return Declarations::PERSON_TYPE; + } + + /** + * @param array{fields: string[], address_date: DateTimeImmutable} $data + */ + public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) + { + $centers = array_map(static function ($el) { + return $el['center']; + }, $acl); + + // throw an error if any fields are present + if (!array_key_exists('fields', $data)) { + throw new \Doctrine\DBAL\Exception\InvalidArgumentException('any fields ' + . 'have been checked'); + } + + $qb = $this->entityManager->createQueryBuilder(); + + $qb->from(Person::class, 'person') + ->join('person.accompanyingPeriodParticipations', 'acppart') + ->join('acppart.accompanyingPeriod', 'acp') + ->andWhere( + $qb->expr()->exists( + 'SELECT 1 FROM ' . PersonCenterHistory::class . ' pch WHERE pch.person = person.id AND pch.center IN (:authorized_centers)' + ) + )->setParameter('authorized_centers', $centers); + + $fields = $data['fields']; + + $this->listPersonHelper->addSelect($qb, $fields, $data['address_date']); + + return $qb; + } + + public function requiredRole(): string + { + return PersonVoter::LISTS; + } + + public function supportsModifiers() + { + return [Declarations::PERSON_TYPE, Declarations::PERSON_IMPLIED_IN, Declarations::ACP_TYPE]; + } + + public function validateForm($data, ExecutionContextInterface $context) + { + // get the field starting with address_ + $addressFields = array_filter( + ListPersonHelper::FIELDS, + static fn (string $el): bool => substr($el, 0, 8) === 'address_' + ); + + // check if there is one field starting with address in data + if (count(array_intersect($data['fields'], $addressFields)) > 0) { + // if a field address is checked, the date must not be empty + if (!$data['address_date'] instanceof DateTimeImmutable) { + $context + ->buildViolation('You must set this date if an address is checked') + ->atPath('address_date') + ->addViolation(); + } + } + } +} diff --git a/src/Bundle/ChillPersonBundle/config/services/exports_person.yaml b/src/Bundle/ChillPersonBundle/config/services/exports_person.yaml index ffffaf175..b9030ec62 100644 --- a/src/Bundle/ChillPersonBundle/config/services/exports_person.yaml +++ b/src/Bundle/ChillPersonBundle/config/services/exports_person.yaml @@ -15,13 +15,18 @@ services: tags: - { name: chill.export, alias: count_person_with_accompanying_course } - chill.person.export.list_person: - class: Chill\PersonBundle\Export\Export\ListPerson + Chill\PersonBundle\Export\Export\ListPerson: autowire: true autoconfigure: true tags: - { name: chill.export, alias: list_person } + Chill\PersonBundle\Export\Export\ListPersonWithAccompanyingPeriod: + autowire: true + autoconfigure: true + tags: + - { name: chill.export, alias: list_person_with_acp } + chill.person.export.list_person.duplicate: class: Chill\PersonBundle\Export\Export\ListPersonDuplicate arguments: diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index 0b71d9698..5f43608ce 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -1019,8 +1019,9 @@ export: Computation date for referrer: Date à laquelle le référent était actif by_referrer: Computation date for referrer: Date à laquelle le référent était actif - lists: - + list: + person_with_acp.List peoples having an accompanying period: Liste des personnes ayant un parcours d'accompagnement + Create a list of people having an accompaying periods, according to various filters.: Génère une liste des personnes ayant un parcours d'accompagnement, selon différents critères liés au parcours ou à l'usager social_action: and children: et dérivés From 6ffe99a2be1196ead6eec156a99bce5bda0059f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Fri, 28 Oct 2022 22:25:53 +0200 Subject: [PATCH 24/73] Feature: [export] Add a list of accompanying periods --- .../ChillMainExtension.php | 4 + .../ChillMainBundle/Doctrine/DQL/STX.php | 37 ++ .../ChillMainBundle/Doctrine/DQL/STY.php | 37 ++ .../Export/Helper/DateTimeHelper.php | 60 +++ .../Export/Helper/ExportAddressHelper.php | 174 +++++--- .../Export/Helper/UserHelper.php | 43 ++ .../Export/Export/CountAccompanyingCourse.php | 2 +- .../Export/Export/ListAccompanyingPeriod.php | 416 ++++++++++++++++++ .../Export/Export/ListPerson.php | 2 +- .../ListPersonWithAccompanyingPeriod.php | 4 +- .../Export/Helper/ListPersonHelper.php | 42 +- .../config/services/exports_person.yaml | 6 + .../translations/messages.fr.yml | 44 +- 13 files changed, 778 insertions(+), 93 deletions(-) create mode 100644 src/Bundle/ChillMainBundle/Doctrine/DQL/STX.php create mode 100644 src/Bundle/ChillMainBundle/Doctrine/DQL/STY.php create mode 100644 src/Bundle/ChillMainBundle/Export/Helper/DateTimeHelper.php create mode 100644 src/Bundle/ChillMainBundle/Export/Helper/UserHelper.php create mode 100644 src/Bundle/ChillPersonBundle/Export/Export/ListAccompanyingPeriod.php diff --git a/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php b/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php index 17c6e0b64..7c63263ba 100644 --- a/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php +++ b/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php @@ -34,6 +34,8 @@ use Chill\MainBundle\Doctrine\DQL\Replace; use Chill\MainBundle\Doctrine\DQL\Similarity; use Chill\MainBundle\Doctrine\DQL\STContains; use Chill\MainBundle\Doctrine\DQL\StrictWordSimilarityOPS; +use Chill\MainBundle\Doctrine\DQL\STX; +use Chill\MainBundle\Doctrine\DQL\STY; use Chill\MainBundle\Doctrine\DQL\ToChar; use Chill\MainBundle\Doctrine\DQL\Unaccent; use Chill\MainBundle\Doctrine\ORM\Hydration\FlatHierarchyEntityHydrator; @@ -242,6 +244,8 @@ class ChillMainExtension extends Extension implements 'STRICT_WORD_SIMILARITY_OPS' => StrictWordSimilarityOPS::class, 'ST_CONTAINS' => STContains::class, 'JSONB_ARRAY_LENGTH' => JsonbArrayLength::class, + 'ST_X' => STX::class, + 'ST_Y' => STY::class, ], 'datetime_functions' => [ 'EXTRACT' => Extract::class, diff --git a/src/Bundle/ChillMainBundle/Doctrine/DQL/STX.php b/src/Bundle/ChillMainBundle/Doctrine/DQL/STX.php new file mode 100644 index 000000000..d5d8d0a3f --- /dev/null +++ b/src/Bundle/ChillMainBundle/Doctrine/DQL/STX.php @@ -0,0 +1,37 @@ +field->dispatch($sqlWalker)); + } + + public function parse(Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->field = $parser->ArithmeticExpression(); + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} diff --git a/src/Bundle/ChillMainBundle/Doctrine/DQL/STY.php b/src/Bundle/ChillMainBundle/Doctrine/DQL/STY.php new file mode 100644 index 000000000..e827da543 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Doctrine/DQL/STY.php @@ -0,0 +1,37 @@ +field->dispatch($sqlWalker)); + } + + public function parse(Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->field = $parser->ArithmeticExpression(); + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} diff --git a/src/Bundle/ChillMainBundle/Export/Helper/DateTimeHelper.php b/src/Bundle/ChillMainBundle/Export/Helper/DateTimeHelper.php new file mode 100644 index 000000000..86a2458b2 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Export/Helper/DateTimeHelper.php @@ -0,0 +1,60 @@ +translator = $translator; + } + + public function getLabel($header): callable + { + return function ($value) use ($header) { + if ('_header' === $value) { + return $this->translator->trans($header); + } + + if (null === $value) { + return ''; + } + + // warning: won't work with DateTimeImmutable as we reset time a few lines later + $date = DateTime::createFromFormat('Y-m-d', $value); + $hasTime = false; + + if (false === $date) { + $date = DateTime::createFromFormat('Y-m-d H:i:s', $value); + $hasTime = true; + } + + // check that the creation could occurs. + if (false === $date) { + throw new Exception(sprintf('The value %s could ' + . 'not be converted to %s', $value, DateTime::class)); + } + + if (!$hasTime) { + $date->setTime(0, 0, 0); + } + + return $date; + }; + } +} diff --git a/src/Bundle/ChillMainBundle/Export/Helper/ExportAddressHelper.php b/src/Bundle/ChillMainBundle/Export/Helper/ExportAddressHelper.php index 6c6fcb76e..41443e3d8 100644 --- a/src/Bundle/ChillMainBundle/Export/Helper/ExportAddressHelper.php +++ b/src/Bundle/ChillMainBundle/Export/Helper/ExportAddressHelper.php @@ -14,9 +14,11 @@ namespace Chill\MainBundle\Export\Helper; use Chill\MainBundle\Repository\AddressRepository; use Chill\MainBundle\Templating\Entity\AddressRender; use Chill\MainBundle\Templating\TranslatableStringHelperInterface; +use Doctrine\ORM\QueryBuilder; use LogicException; use Symfony\Component\PropertyAccess\PropertyAccess; use Symfony\Component\PropertyAccess\PropertyAccessor; +use function in_array; use function strlen; /** @@ -24,6 +26,10 @@ use function strlen; */ class ExportAddressHelper { + public const F_ALL = + self::F_ATTRIBUTES | self::F_BUILDING | self::F_COUNTRY | + self::F_GEOM | self::F_POSTAL_CODE | self::F_STREET; + public const F_AS_STRING = 0b00010000; public const F_ATTRIBUTES = 0b01000000; @@ -77,6 +83,87 @@ class ExportAddressHelper $this->addressRender = $addressRender; } + public function addSelectClauses(int $params, QueryBuilder $queryBuilder, $entityName = 'address', $prefix = 'add') + { + foreach (self::ALL as $key => $bitmask) { + if (($params & $bitmask) === $bitmask) { + foreach (self::COLUMN_MAPPING[$key] as $field) { + switch ($field) { + case 'id': + case '_as_string': + $queryBuilder->addSelect(sprintf('%s.id AS %s%s', $entityName, $prefix, $field)); + + break; + + case 'street': + case 'streetNumber': + case 'building': + case 'floor': + case 'corridor': + case 'steps': + case 'buildingName': + case 'flat': + case 'distribution': + case 'extra': + $queryBuilder->addSelect(sprintf('%s.%s AS %s%s', $entityName, $field, $prefix, $field)); + + break; + + case 'country': + case 'postcode_name': + case 'postcode_code': + $postCodeAlias = sprintf('%spostcode_t', $prefix); + + if (!in_array($postCodeAlias, $queryBuilder->getAllAliases(), true)) { + $queryBuilder->leftJoin($entityName . '.postcode', $postCodeAlias); + } + + if ('postcode_name' === $field) { + $queryBuilder->addSelect(sprintf('%s.%s AS %s%s', $postCodeAlias, 'name', $prefix, $field)); + + break; + } + + if ('postcode_code' === $field) { + $queryBuilder->addSelect(sprintf('%s.%s AS %s%s', $postCodeAlias, 'code', $prefix, $field)); + + break; + } + + $countryAlias = sprintf('%scountry_t', $prefix); + + if (!in_array($countryAlias, $queryBuilder->getAllAliases(), true)) { + $queryBuilder->leftJoin(sprintf('%s.country', $postCodeAlias), $countryAlias); + } + + $queryBuilder->addSelect(sprintf('%s.%s AS %s%s', $countryAlias, 'name', $prefix, $field)); + + break; + + case 'isNoAddress': + case 'confidential': + $queryBuilder->addSelect(sprintf('CASE WHEN %s.%s = \'TRUE\' THEN 1 ELSE 0 END AS %s%s', $entityName, $field, $prefix, $field)); + + break; + + case '_lat': + $queryBuilder->addSelect(sprintf('ST_Y(%s.point) AS %s%s', $entityName, $prefix, $field)); + + break; + + case '_lon': + $queryBuilder->addSelect(sprintf('ST_X(%s.point) AS %s%s', $entityName, $prefix, $field)); + + break; + + default: + throw new LogicException('This key is not supported: ' . $key); + } + } + } + } + } + /** * @param self::F_* $params * @@ -115,23 +202,11 @@ class ExportAddressHelper case 'extra': case 'flat': case 'floor': - return function ($value) use ($sanitizedKey, $translationPrefix) { - if ('_header' === $value) { - return $translationPrefix . $sanitizedKey; - } - - if (null === $value) { - return ''; - } - - $address = $this->addressRepository->find($value); - - return $this->propertyAccess->getValue($address, $sanitizedKey); - }; - case '_lat': case '_lon': - return function ($value) use ($sanitizedKey, $translationPrefix) { + case 'postcode_code': + case 'postcode_name': + return static function ($value) use ($sanitizedKey, $translationPrefix) { if ('_header' === $value) { return $translationPrefix . $sanitizedKey; } @@ -140,28 +215,25 @@ class ExportAddressHelper return ''; } - $address = $this->addressRepository->find($value); - $geom = $address->getPoint(); + return $value; + }; - if (null === $geom) { + case 'country': + return function ($value) use ($key) { + if ('_header' === $value) { + return 'export.list.acp' . $key; + } + + if (null === $value) { return ''; } - switch ($sanitizedKey) { - case '_lat': - return $geom->getLat(); - - case '_lon': - return $geom->getLon(); - - default: - throw new LogicException('only _lat or _lon accepted, given: ' . $sanitizedKey); - } + return $this->translatableStringHelper->localize(json_decode($value, true)); }; case 'isNoAddress': case 'confidential': - return function ($value) use ($sanitizedKey, $translationPrefix) { + return static function ($value) use ($sanitizedKey, $translationPrefix) { if ('_header' === $value) { return $translationPrefix . $sanitizedKey; } @@ -170,9 +242,7 @@ class ExportAddressHelper return ''; } - $address = $this->addressRepository->find($value); - - switch ($val = $this->propertyAccess->getValue($address, $sanitizedKey)) { + switch ($value) { case null: return ''; @@ -187,21 +257,6 @@ class ExportAddressHelper } }; - case 'country': - return function ($value) use ($sanitizedKey, $translationPrefix) { - if ('_header' === $value) { - return $translationPrefix . $sanitizedKey; - } - - if (null === $value) { - return ''; - } - - $address = $this->addressRepository->find($value); - - return $this->translatableStringHelper->localize($address->getPostcode()->getCountry()->getName()); - }; - case '_as_string': return function ($value) use ($sanitizedKey, $translationPrefix) { if ('_header' === $value) { @@ -217,31 +272,6 @@ class ExportAddressHelper return $this->addressRender->renderString($address, []); }; - case 'postcode_code': - case 'postcode_name': - return function ($value) use ($sanitizedKey, $translationPrefix) { - if ('_header' === $value) { - return $translationPrefix . $sanitizedKey; - } - - if (null === $value) { - return ''; - } - - $address = $this->addressRepository->find($value); - - switch ($sanitizedKey) { - case 'postcode_code': - return $address->getPostcode()->getCode(); - - case 'postcode_name': - return $address->getPostcode()->getName(); - - default: - throw new LogicException('this key is not supported: ' . $sanitizedKey); - } - }; - default: throw new LogicException('this key is not supported: ' . $sanitizedKey); } diff --git a/src/Bundle/ChillMainBundle/Export/Helper/UserHelper.php b/src/Bundle/ChillMainBundle/Export/Helper/UserHelper.php new file mode 100644 index 000000000..98c4c5579 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Export/Helper/UserHelper.php @@ -0,0 +1,43 @@ +userRender = $userRender; + $this->userRepository = $userRepository; + } + + public function getLabel($key, array $values, string $header): callable + { + return function ($value) use ($header) { + if ('_header' === $value) { + return $header; + } + + if (null === $value || null === $user = $this->userRepository->find($value)) { + return ''; + } + + return $this->userRender->renderString($user, []); + }; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Export/CountAccompanyingCourse.php b/src/Bundle/ChillPersonBundle/Export/Export/CountAccompanyingCourse.php index 8c8744dfd..d07ee3639 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/CountAccompanyingCourse.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/CountAccompanyingCourse.php @@ -38,7 +38,7 @@ class CountAccompanyingCourse implements ExportInterface, GroupedExportInterface public function buildForm(FormBuilderInterface $builder): void { - // TODO: Implement buildForm() method. + // Nothing to add here } public function getAllowedFormattersTypes(): array diff --git a/src/Bundle/ChillPersonBundle/Export/Export/ListAccompanyingPeriod.php b/src/Bundle/ChillPersonBundle/Export/Export/ListAccompanyingPeriod.php new file mode 100644 index 000000000..28ad94bd3 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Export/ListAccompanyingPeriod.php @@ -0,0 +1,416 @@ +addressHelper = $addressHelper; + $this->dateTimeHelper = $dateTimeHelper; + $this->entityManager = $entityManager; + $this->personRender = $personRender; + $this->personRepository = $personRepository; + $this->socialIssueRender = $socialIssueRender; + $this->socialIssueRepository = $socialIssueRepository; + $this->thirdPartyRender = $thirdPartyRender; + $this->thirdPartyRepository = $thirdPartyRepository; + $this->translatableStringHelper = $translatableStringHelper; + $this->userHelper = $userHelper; + } + + public function buildForm(FormBuilderInterface $builder) + { + $builder + ->add('calc_date', ChillDateType::class, [ + 'input' => 'datetime_immutable', + 'label' => 'export.list.acp.Date of calculation for associated elements', + 'help' => 'export.list.acp.The associated referree, localisation, and other elements will be valid at this date', + 'required' => true, + ]); + } + + public function getAllowedFormattersTypes() + { + return [FormatterInterface::TYPE_LIST]; + } + + public function getDescription() + { + return 'export.list.acp.Generate a list of accompanying periods, filtered on different parameters.'; + } + + public function getGroup(): string + { + return 'Exports of accompanying courses'; + } + + public function getLabels($key, array $values, $data) + { + if (substr($key, 0, strlen('address_fields')) === 'address_fields') { + return $this->addressHelper->getLabel($key, $values, $data, 'address_fields'); + } + + switch ($key) { + case 'stepSince': + case 'openingDate': + case 'closingDate': + case 'referrerSince': + case 'createdAt': + case 'updatedAt': + return $this->dateTimeHelper->getLabel('export.list.acp.' . $key); + + case 'origin': + case 'closingMotive': + case 'job': + return function ($value) use ($key) { + if ('_header' === $value) { + return 'export.list.acp.' . $key; + } + + if (null === $value) { + return ''; + } + + return $this->translatableStringHelper->localize(json_decode($value, true)); + }; + + case 'locationPersonName': + case 'requestorPerson': + return function ($value) use ($key) { + if ('_header' === $value) { + return 'export.list.acp.' . $key; + } + + if (null === $value || null === $person = $this->personRepository->find($value)) { + return ''; + } + + return $this->personRender->renderString($person, []); + }; + + case 'requestorThirdParty': + return function ($value) use ($key) { + if ('_header' === $value) { + return 'export.list.acp.' . $key; + } + + if (null === $value || null === $thirdparty = $this->thirdPartyRepository->find($value)) { + return ''; + } + + return $this->thirdPartyRender->renderString($thirdparty, []); + }; + + case 'scopes': + return function ($value) use ($key) { + if ('_header' === $value) { + return 'export.list.acp.' . $key; + } + + if (null === $value) { + return ''; + } + + return implode( + '|', + array_map( + fn ($s) => $this->translatableStringHelper->localize($s), + json_decode($value, true) + ) + ); + }; + + case 'socialIssues': + return function ($value) use ($key) { + if ('_header' === $value) { + return 'export.list.acp.' . $key; + } + + if (null === $value) { + return ''; + } + + return implode( + '|', + array_map( + fn ($s) => $this->socialIssueRender->renderString($this->socialIssueRepository->find($s), []), + json_decode($value, true) + ) + ); + }; + + default: + return static function ($value) use ($key) { + if ('_header' === $value) { + return 'export.list.acp.' . $key; + } + + if (null === $value) { + return ''; + } + + return $value; + }; + } + } + + public function getQueryKeys($data) + { + return array_merge( + self::FIELDS, + $this->addressHelper->getKeys(ExportAddressHelper::F_ALL, 'address_fields') + ); + } + + public function getResult($query, $data) + { + return $query->getQuery()->getResult(AbstractQuery::HYDRATE_SCALAR); + } + + public function getTitle() + { + return 'export.list.acp.List of accompanying periods'; + } + + public function getType() + { + return Declarations::PERSON_TYPE; + } + + public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) + { + $centers = array_map(static function ($el) { + return $el['center']; + }, $acl); + + $qb = $this->entityManager->createQueryBuilder(); + + $qb + ->from(AccompanyingPeriod::class, 'acp') + ->andWhere('acp.step != :list_acp_step') + ->andWhere( + $qb->expr()->exists( + 'SELECT 1 FROM ' . AccompanyingPeriodParticipation::class . ' acl_count_part + JOIN ' . PersonCenterHistory::class . ' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person) + WHERE acl_count_part.accompanyingPeriod = acp.id AND acl_count_person_history.center IN (:authorized_centers) + ' + ) + ) + ->setParameter('list_acp_step', AccompanyingPeriod::STEP_DRAFT) + ->setParameter('authorized_centers', $centers); + + $this->addSelectClauses($qb, $data['calc_date']); + + return $qb; + } + + public function requiredRole(): string + { + return PersonVoter::LISTS; + } + + public function supportsModifiers() + { + return [ + Declarations::ACP_TYPE, + ]; + } + + private function addSelectClauses(QueryBuilder $qb, DateTimeImmutable $calcDate): void + { + // add the regular fields + foreach (['id', 'openingDate', 'closingDate', 'confidential', 'emergency', 'intensity', 'createdAt', 'updatedAt'] as $field) { + $qb->addSelect(sprintf('acp.%s AS %s', $field, $field)); + } + + // add the field which are simple association + foreach (['origin' => 'label', 'closingMotive' => 'name', 'job' => 'label', 'createdBy' => 'label', 'updatedBy' => 'label', 'administrativeLocation' => 'name'] as $entity => $field) { + $qb + ->leftJoin(sprintf('acp.%s', $entity), "{$entity}_t") + ->addSelect(sprintf('%s_t.%s AS %s', $entity, $field, $entity)); + } + + // step at date + $qb + ->addSelect('stepHistory.step AS step') + ->addSelect('stepHistory.startDate AS stepSince') + ->leftJoin('acp.stepHistories', 'stepHistory') + ->andWhere( + $qb->expr()->andX( + $qb->expr()->lte('stepHistory.startDate', ':calcDate'), + $qb->expr()->orX($qb->expr()->isNull('stepHistory.endDate'), $qb->expr()->gt('stepHistory.endDate', ':calcDate')) + ) + ); + + // referree at date + $qb + ->addSelect('referrer_t.label AS referrer') + ->addSelect('userHistory.startDate AS referrerSince') + ->leftJoin('acp.userHistories', 'userHistory') + ->leftJoin('userHistory.user', 'referrer_t') + ->andWhere( + $qb->expr()->orX( + $qb->expr()->isNull('userHistory'), + $qb->expr()->andX( + $qb->expr()->lte('userHistory.startDate', ':calcDate'), + $qb->expr()->orX($qb->expr()->isNull('userHistory.endDate'), $qb->expr()->gt('userHistory.endDate', ':calcDate')) + ) + ) + ); + + // location of the acp + $qb + ->addSelect('CASE WHEN locationHistory.personLocation IS NOT NULL THEN 1 ELSE 0 END AS locationIsPerson') + ->addSelect('CASE WHEN locationHistory.personLocation IS NOT NULL THEN 0 ELSE 1 END AS locationIsTemp') + ->addSelect('IDENTITY(locationHistory.personLocation) AS locationPersonName') + ->addSelect('IDENTITY(locationHistory.personLocation) AS locationPersonId') + ->leftJoin('acp.locationHistories', 'locationHistory') + ->andWhere( + $qb->expr()->orX( + $qb->expr()->isNull('locationHistory'), + $qb->expr()->andX( + $qb->expr()->lte('locationHistory.startDate', ':calcDate'), + $qb->expr()->orX($qb->expr()->isNull('locationHistory.endDate'), $qb->expr()->gt('locationHistory.endDate', ':calcDate')) + ) + ) + ) + ->leftJoin(PersonHouseholdAddress::class, 'personAddress', Join::WITH, 'locationHistory.personLocation = personAddress.person') + ->andWhere( + $qb->expr()->orX( + $qb->expr()->isNull('personAddress'), + $qb->expr()->andX( + $qb->expr()->lte('personAddress.validFrom', ':calcDate'), + $qb->expr()->orX($qb->expr()->isNull('personAddress.validTo'), $qb->expr()->gt('personAddress.validTo', ':calcDate')) + ) + ) + ) + ->leftJoin(Address::class, 'acp_address', Join::WITH, 'COALESCE(IDENTITY(locationHistory.addressLocation), IDENTITY(personAddress.address)) = acp_address.id'); + + $this->addressHelper->addSelectClauses(ExportAddressHelper::F_ALL, $qb, 'acp_address', 'address_fields'); + + // requestor + $qb + ->addSelect('CASE WHEN acp.requestorPerson IS NULL THEN 1 ELSE 0 END AS isRequestorPerson') + ->addSelect('CASE WHEN acp.requestorPerson IS NULL THEN 0 ELSE 1 END AS isRequestorThirdParty') + ->addSelect('IDENTITY(acp.requestorPerson) AS requestorPersonId') + ->addSelect('IDENTITY(acp.requestorThirdParty) AS requestorThirdPartyId') + ->addSelect('IDENTITY(acp.requestorPerson) AS requestorPerson') + ->addSelect('IDENTITY(acp.requestorThirdParty) AS requestorThirdParty'); + + $qb + // scopes + ->addSelect('(SELECT AGGREGATE(scope.name) FROM ' . Scope::class . ' scope WHERE scope MEMBER OF acp.scopes) AS scopes') + // social issues + ->addSelect('(SELECT AGGREGATE(socialIssue.id) FROM ' . SocialIssue::class . ' socialIssue WHERE socialIssue MEMBER OF acp.socialIssues) AS socialIssues'); + + // add parameter + $qb->setParameter('calcDate', $calcDate); + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php b/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php index eb746e122..d1a23a570 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php @@ -153,7 +153,7 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou } if (substr($key, 0, strlen('address_fields')) === 'address_fields') { - $fields = array_merge($fields, $this->addressHelper->getKeys(ListPersonHelper::HELPER_ATTRIBUTES, 'address_fields')); + $fields = array_merge($fields, $this->addressHelper->getKeys(ExportAddressHelper::F_ALL, 'address_fields')); continue; } diff --git a/src/Bundle/ChillPersonBundle/Export/Export/ListPersonWithAccompanyingPeriod.php b/src/Bundle/ChillPersonBundle/Export/Export/ListPersonWithAccompanyingPeriod.php index ce97ce662..534eaecc6 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/ListPersonWithAccompanyingPeriod.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/ListPersonWithAccompanyingPeriod.php @@ -17,6 +17,7 @@ use Chill\MainBundle\Export\GroupedExportInterface; use Chill\MainBundle\Export\Helper\ExportAddressHelper; use Chill\MainBundle\Export\ListInterface; use Chill\MainBundle\Form\Type\ChillDateType; +use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Entity\Person\PersonCenterHistory; use Chill\PersonBundle\Export\Declarations; @@ -121,7 +122,7 @@ class ListPersonWithAccompanyingPeriod implements ExportElementValidatedInterfac } if (substr($key, 0, strlen('address_fields')) === 'address_fields') { - $fields = array_merge($fields, $this->addressHelper->getKeys(ListPersonHelper::HELPER_ATTRIBUTES, 'address_fields')); + $fields = array_merge($fields, $this->addressHelper->getKeys(ExportAddressHelper::F_ALL, 'address_fields')); continue; } @@ -173,6 +174,7 @@ class ListPersonWithAccompanyingPeriod implements ExportElementValidatedInterfac $qb->from(Person::class, 'person') ->join('person.accompanyingPeriodParticipations', 'acppart') ->join('acppart.accompanyingPeriod', 'acp') + ->andWhere($qb->expr()->neq('acp.step', "'" . AccompanyingPeriod::STEP_DRAFT . "'")) ->andWhere( $qb->expr()->exists( 'SELECT 1 FROM ' . PersonCenterHistory::class . ' pch WHERE pch.person = person.id AND pch.center IN (:authorized_centers)' diff --git a/src/Bundle/ChillPersonBundle/Export/Helper/ListPersonHelper.php b/src/Bundle/ChillPersonBundle/Export/Helper/ListPersonHelper.php index 69f797c3d..de8870674 100644 --- a/src/Bundle/ChillPersonBundle/Export/Helper/ListPersonHelper.php +++ b/src/Bundle/ChillPersonBundle/Export/Helper/ListPersonHelper.php @@ -12,6 +12,7 @@ declare(strict_types=1); namespace Chill\PersonBundle\Export\Helper; use Chill\MainBundle\Export\Helper\ExportAddressHelper; +use Chill\MainBundle\Repository\CenterRepositoryInterface; use Chill\MainBundle\Repository\CivilityRepositoryInterface; use Chill\MainBundle\Repository\CountryRepository; use Chill\MainBundle\Repository\LanguageRepositoryInterface; @@ -72,16 +73,10 @@ class ListPersonHelper 'lifecycleUpdate', ]; - public const HELPER_ATTRIBUTES = ExportAddressHelper::F_ATTRIBUTES | - ExportAddressHelper::F_BUILDING | - ExportAddressHelper::F_COUNTRY | - ExportAddressHelper::F_GEOM | - ExportAddressHelper::F_POSTAL_CODE | - ExportAddressHelper::F_STREET | - ExportAddressHelper::F_AS_STRING; - private ExportAddressHelper $addressHelper; + private CenterRepositoryInterface $centerRepository; + private CivilityRepositoryInterface $civilityRepository; private CountryRepository $countryRepository; @@ -98,6 +93,7 @@ class ListPersonHelper public function __construct( ExportAddressHelper $addressHelper, + CenterRepositoryInterface $centerRepository, CivilityRepositoryInterface $civilityRepository, CountryRepository $countryRepository, LanguageRepositoryInterface $languageRepository, @@ -107,6 +103,7 @@ class ListPersonHelper UserRepositoryInterface $userRepository ) { $this->addressHelper = $addressHelper; + $this->centerRepository = $centerRepository; $this->civilityRepository = $civilityRepository; $this->countryRepository = $countryRepository; $this->languageRepository = $languageRepository; @@ -134,12 +131,9 @@ class ListPersonHelper break; case 'address_fields': - foreach ($this->addressHelper->getKeys(ListPersonHelper::HELPER_ATTRIBUTES, 'address_fields') as $key) { - $qb - ->addSelect(sprintf('IDENTITY(personHouseholdAddress.address) AS %s', $key)); - } - $this->addCurrentAddressAt($qb, $computedDate); + $qb->leftJoin('personHouseholdAddress.address', 'personAddress'); + $this->addressHelper->addSelectClauses(ExportAddressHelper::F_ALL, $qb, 'personAddress', 'address_fields'); break; @@ -154,7 +148,10 @@ class ListPersonHelper } if (in_array('address_fields', $fields, true)) { - $qb->addGroupBy('address_fieldsid'); + $qb + ->addGroupBy('address_fieldsid') + ->addGroupBy('address_fieldscountry_t.id') + ->addGroupBy('address_fieldspostcode_t.id'); } if (in_array('household_id', $fields, true)) { @@ -234,7 +231,7 @@ class ListPersonHelper return array_merge( self::FIELDS, ['createdAt', 'createdBy', 'updatedAt', 'updatedBy'], - $this->addressHelper->getKeys(self::HELPER_ATTRIBUTES, 'address_fields') + $this->addressHelper->getKeys(ExportAddressHelper::F_ALL, 'address_fields') ); } @@ -245,6 +242,19 @@ class ListPersonHelper } switch ($key) { + case 'center': + return function ($value) use ($key) { + if ('_header' === $value) { + return $this->translator->trans($key); + } + + if (null === $value || null === $center = $this->centerRepository->find($value)) { + return ''; + } + + return $center->getName(); + }; + case 'birthdate': case 'deathdate': case 'maritalStatusDate': @@ -413,7 +423,7 @@ class ListPersonHelper ->leftJoin('person.householdAddresses', 'personHouseholdAddress') ->andWhere( $qb->expr()->orX( - // no address at this time + // no address at this time $qb->expr()->isNull('personHouseholdAddress'), // there is one address... $qb->expr()->andX( diff --git a/src/Bundle/ChillPersonBundle/config/services/exports_person.yaml b/src/Bundle/ChillPersonBundle/config/services/exports_person.yaml index b9030ec62..b2b182c5b 100644 --- a/src/Bundle/ChillPersonBundle/config/services/exports_person.yaml +++ b/src/Bundle/ChillPersonBundle/config/services/exports_person.yaml @@ -27,6 +27,12 @@ services: tags: - { name: chill.export, alias: list_person_with_acp } + Chill\PersonBundle\Export\Export\ListAccompanyingPeriod: + autowire: true + autoconfigure: true + tags: + - { name: chill.export, alias: list_acp } + chill.person.export.list_person.duplicate: class: Chill\PersonBundle\Export\Export\ListPersonDuplicate arguments: diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index 5f43608ce..319231489 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -1020,8 +1020,48 @@ export: by_referrer: Computation date for referrer: Date à laquelle le référent était actif list: - person_with_acp.List peoples having an accompanying period: Liste des personnes ayant un parcours d'accompagnement - Create a list of people having an accompaying periods, according to various filters.: Génère une liste des personnes ayant un parcours d'accompagnement, selon différents critères liés au parcours ou à l'usager + person_with_acp: + List peoples having an accompanying period: Liste des personnes ayant un parcours d'accompagnement + Create a list of people having an accompaying periods, according to various filters.: Génère une liste des personnes ayant un parcours d'accompagnement, selon différents critères liés au parcours ou à l'usager + acp: + List of accompanying periods: Liste de périodes d'accompagnements + Generate a list of accompanying periods, filtered on different parameters.: Génère une liste des périodes d'accompagnement, filtrée sur différents paramètres. + Date of calculation for associated elements: Date de calcul des éléments associés + The associated referree, localisation, and other elements will be valid at this date: Les éléments associés, comme la localisation, le référent et d'autres éléments seront valides à cette date + id: Identifiant du parcours + openingDate: Date d'ouverture du parcours + closingDate: Date de fermeture du parcours + confidential: Confidentiel + emergency: Urgent + intensity: Intensité + createdAt: Créé le + updatedAt: Dernière mise à jour le + acpOrigin: Origine du parcours + acpClosingMotive: Motif de fermeture + acpJob: Métier du parcours + createdBy: Créé par + updatedBy: Dernière modification par + administrativeLocation: Location administrative + step: Etape + stepSince: Dernière modification de l'étape + referrer: Référent + referrerSince: Référent depuis le + locationIsPerson: Parcours localisé auprès d'un usager concerné + locationIsTemp: Parcours avec une localisation temporaire + acpLocationPersonName: Usager auprès duquel le parcours est localisé + locationPersonId: Identifiant de l'usager auprès duquel le parcours est localisé + acpaddress_fieldscountry: Pays de l'adresse + isRequestorPerson: Le demandeur est-il un usager ? + isRequestorThirdParty: Le demandeur est-il un tiers ? + requestorPersonId: Identifiant du demandeur personne + requestorThirdPartyId: Identifiant du tiers + acprequestorPerson: Nom du demandeur personne + acprequestorThirdPaty: Nom du demandeur tiers + scopes: Services + socialIssues: Problématiques sociales + + + social_action: and children: et dérivés From c331f9420174cae5e17204410e27e1d5d638dd44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Mon, 31 Oct 2022 17:53:25 +0100 Subject: [PATCH 25/73] fix cs --- .../ChillActivityBundle/Export/Filter/UsersJobFilter.php | 4 ++-- .../ByHouseholdCompositionAggregator.php | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Bundle/ChillActivityBundle/Export/Filter/UsersJobFilter.php b/src/Bundle/ChillActivityBundle/Export/Filter/UsersJobFilter.php index dcdacd84a..b52ef441c 100644 --- a/src/Bundle/ChillActivityBundle/Export/Filter/UsersJobFilter.php +++ b/src/Bundle/ChillActivityBundle/Export/Filter/UsersJobFilter.php @@ -39,9 +39,9 @@ class UsersJobFilter implements FilterInterface $qb ->andWhere( $qb->expr()->exists( - 'SELECT 1 FROM ' . Activity::class . ' activity_users_job_filter_act + 'SELECT 1 FROM ' . Activity::class . ' activity_users_job_filter_act JOIN activity_users_job_filter_act.users users WHERE users.userJob IN (:activity_users_job_filter_jobs) AND activity_users_job_filter_act = activity ' - ) + ) ) ->setParameter('activity_users_job_filter_jobs', $data['jobs']); } diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ByHouseholdCompositionAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ByHouseholdCompositionAggregator.php index e96c8a228..f299762e6 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ByHouseholdCompositionAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ByHouseholdCompositionAggregator.php @@ -18,6 +18,7 @@ use Chill\PersonBundle\Entity\Household\HouseholdComposition; use Chill\PersonBundle\Entity\Household\HouseholdMember; use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Repository\Household\HouseholdCompositionTypeRepositoryInterface; +use DateTimeImmutable; use Doctrine\ORM\Query\Expr\Join; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; @@ -96,7 +97,7 @@ class ByHouseholdCompositionAggregator implements AggregatorInterface $builder->add('date_calc', ChillDateType::class, [ 'label' => 'export.aggregator.course.by_household_composition.Calc date', 'input_format' => 'datetime_immutable', - 'data' => new \DateTimeImmutable('now'), + 'data' => new DateTimeImmutable('now'), ]); } From afa6dfd77c92fd1c6cf8f3a3aa6bbc72595e01d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Mon, 31 Oct 2022 18:00:41 +0100 Subject: [PATCH 26/73] fix psalm errors --- .../ChillMainBundle/Export/Helper/ExportAddressHelper.php | 2 +- .../ChillMainBundle/Tests/Export/ExportManagerTest.php | 6 ++++-- src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php | 2 +- .../Export/Export/ListPersonWithAccompanyingPeriod.php | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Bundle/ChillMainBundle/Export/Helper/ExportAddressHelper.php b/src/Bundle/ChillMainBundle/Export/Helper/ExportAddressHelper.php index 41443e3d8..268abc3ea 100644 --- a/src/Bundle/ChillMainBundle/Export/Helper/ExportAddressHelper.php +++ b/src/Bundle/ChillMainBundle/Export/Helper/ExportAddressHelper.php @@ -253,7 +253,7 @@ class ExportAddressHelper return 0; default: - throw new LogicException('this value is not supported for ' . $sanitizedKey . ': ' . $val); + throw new LogicException('this value is not supported for ' . $sanitizedKey . ': ' . $value); } }; diff --git a/src/Bundle/ChillMainBundle/Tests/Export/ExportManagerTest.php b/src/Bundle/ChillMainBundle/Tests/Export/ExportManagerTest.php index b3f5eac85..701b3d14e 100644 --- a/src/Bundle/ChillMainBundle/Tests/Export/ExportManagerTest.php +++ b/src/Bundle/ChillMainBundle/Tests/Export/ExportManagerTest.php @@ -680,10 +680,12 @@ final class ExportManagerTest extends KernelTestCase return new ExportManager( $logger ?? self::$container->get('logger'), - $em ?? self::$container->get('doctrine.orm.entity_manager'), $authorizationChecker ?? self::$container->get('security.authorization_checker'), $authorizationHelper ?? self::$container->get('chill.main.security.authorization.helper'), - $tokenStorage + $tokenStorage, + [], + [], + [] ); } } diff --git a/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php b/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php index d1a23a570..549fe1c4d 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php @@ -187,7 +187,7 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou } /** - * @param array{fields: string[], address_date: DateTimeImmutable} $data + * param array{fields: string[], address_date: DateTimeImmutable} $data */ public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) { diff --git a/src/Bundle/ChillPersonBundle/Export/Export/ListPersonWithAccompanyingPeriod.php b/src/Bundle/ChillPersonBundle/Export/Export/ListPersonWithAccompanyingPeriod.php index 534eaecc6..7ddc6c9d3 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/ListPersonWithAccompanyingPeriod.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/ListPersonWithAccompanyingPeriod.php @@ -155,7 +155,7 @@ class ListPersonWithAccompanyingPeriod implements ExportElementValidatedInterfac } /** - * @param array{fields: string[], address_date: DateTimeImmutable} $data + * param array{fields: string[], address_date: DateTimeImmutable} $data */ public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) { From acc9523ff5edff313bb19bfb1f300ef9487e5ec3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Mon, 31 Oct 2022 18:22:01 +0100 Subject: [PATCH 27/73] DX: repairs code style, and psalm types --- phpstan-types.neon | 20 ------------------- .../Export/Helper/ExportAddressHelper.php | 7 +------ .../Export/Export/ListPerson.php | 2 +- .../ListPersonWithAccompanyingPeriod.php | 2 +- 4 files changed, 3 insertions(+), 28 deletions(-) diff --git a/phpstan-types.neon b/phpstan-types.neon index bf2ece912..1aae06880 100644 --- a/phpstan-types.neon +++ b/phpstan-types.neon @@ -10,16 +10,6 @@ parameters: count: 1 path: src/Bundle/ChillActivityBundle/Entity/ActivityReasonCategory.php - - - message: "#^Method Chill\\\\ActivityBundle\\\\Export\\\\Export\\\\StatActivityDuration\\:\\:getDescription\\(\\) should return string but return statement is missing\\.$#" - count: 1 - path: src/Bundle/ChillActivityBundle/Export/Export/StatActivityDuration.php - - - - message: "#^Method Chill\\\\ActivityBundle\\\\Export\\\\Export\\\\StatActivityDuration\\:\\:getTitle\\(\\) should return string but return statement is missing\\.$#" - count: 1 - path: src/Bundle/ChillActivityBundle/Export/Export/StatActivityDuration.php - - message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" count: 1 @@ -330,16 +320,6 @@ parameters: count: 6 path: src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php - - - message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" - count: 1 - path: src/Bundle/ChillPersonBundle/Export/Aggregator/CountryOfBirthAggregator.php - - - - message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" - count: 1 - path: src/Bundle/ChillPersonBundle/Export/Aggregator/NationalityAggregator.php - - message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" count: 1 diff --git a/src/Bundle/ChillMainBundle/Export/Helper/ExportAddressHelper.php b/src/Bundle/ChillMainBundle/Export/Helper/ExportAddressHelper.php index 268abc3ea..2f9a270ff 100644 --- a/src/Bundle/ChillMainBundle/Export/Helper/ExportAddressHelper.php +++ b/src/Bundle/ChillMainBundle/Export/Helper/ExportAddressHelper.php @@ -58,7 +58,7 @@ class ExportAddressHelper 'country' => ['country'], 'postal_code' => ['postcode_code', 'postcode_name'], 'street' => ['street', 'streetNumber'], - 'building' => ['buildingName', 'corridor', 'distribution', 'extra', 'flat', 'floor'], + 'building' => ['buildingName', 'corridor', 'distribution', 'extra', 'flat', 'floor', 'steps'], 'string' => ['_as_string'], 'attributes' => ['isNoAddress', 'confidential', 'id'], 'geom' => ['_lat', '_lon'], @@ -97,7 +97,6 @@ class ExportAddressHelper case 'street': case 'streetNumber': - case 'building': case 'floor': case 'corridor': case 'steps': @@ -238,10 +237,6 @@ class ExportAddressHelper return $translationPrefix . $sanitizedKey; } - if (null === $value) { - return ''; - } - switch ($value) { case null: return ''; diff --git a/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php b/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php index 549fe1c4d..acaeb498c 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php @@ -187,7 +187,7 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou } /** - * param array{fields: string[], address_date: DateTimeImmutable} $data + * param array{fields: string[], address_date: DateTimeImmutable} $data. */ public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) { diff --git a/src/Bundle/ChillPersonBundle/Export/Export/ListPersonWithAccompanyingPeriod.php b/src/Bundle/ChillPersonBundle/Export/Export/ListPersonWithAccompanyingPeriod.php index 7ddc6c9d3..bab67fb39 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/ListPersonWithAccompanyingPeriod.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/ListPersonWithAccompanyingPeriod.php @@ -155,7 +155,7 @@ class ListPersonWithAccompanyingPeriod implements ExportElementValidatedInterfac } /** - * param array{fields: string[], address_date: DateTimeImmutable} $data + * param array{fields: string[], address_date: DateTimeImmutable} $data. */ public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) { From 8d4ec5d6756c049de96017e8f88f6af56bc0e9a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 2 Nov 2022 11:12:55 +0100 Subject: [PATCH 28/73] fix code style --- .../ACPAggregators/ByActivityNumberAggregator.php | 4 ++-- .../Export/Aggregator/SentReceivedAggregator.php | 10 ++++++---- .../Export/Filter/ACPFilters/HasNoActivityFilter.php | 7 ++++--- .../Export/Aggregator/ByActivityTypeAggregator.php | 4 ++-- .../src/Export/Declarations.php | 2 +- .../src/Export/Export/CountAsideActivity.php | 6 +----- .../src/Export/Filter/ByActivityTypeFilter.php | 2 +- .../src/Export/Filter/ByDateFilter.php | 2 +- .../ByActionNumberAggregator.php | 4 ++-- .../CreatorJobAggregator.php | 4 ++-- .../EvaluationAggregators/ByEndDateAggregator.php | 4 ++-- .../EvaluationAggregators/ByMaxDateAggregator.php | 4 ++-- .../EvaluationAggregators/ByStartDateAggregator.php | 4 ++-- .../HavingEndDateAggregator.php | 9 +++++---- .../CurrentActionAggregator.php | 10 ++++++---- .../AccompanyingCourseFilters/CreatorFilter.php | 2 +- .../AccompanyingCourseFilters/CreatorJobFilter.php | 2 +- .../AccompanyingCourseFilters/HasNoActionFilter.php | 3 ++- .../HasNoReferrerFilter.php | 12 ++++++------ .../HasTemporaryLocationFilter.php | 2 +- .../Filter/EvaluationFilters/ByEndDateFilter.php | 4 ++-- .../Filter/EvaluationFilters/ByStartDateFilter.php | 4 ++-- .../EvaluationFilters/CurrentEvaluationsFilter.php | 2 +- .../Filter/SocialWorkFilters/CurrentActionFilter.php | 2 +- 24 files changed, 56 insertions(+), 53 deletions(-) diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByActivityNumberAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByActivityNumberAggregator.php index 44a3fd8f0..de35f75cc 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByActivityNumberAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByActivityNumberAggregator.php @@ -2,7 +2,7 @@ declare(strict_types=1); -/** +/* * Chill is a software for social workers * * For the full copyright and license information, please view @@ -41,7 +41,7 @@ class ByActivityNumberAggregator implements AggregatorInterface public function getLabels($key, array $values, $data) { - return function ($value): string { + return static function ($value): string { if ('_header' === $value) { return ''; } diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/SentReceivedAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/SentReceivedAggregator.php index b5300834c..2a6dec7f7 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/SentReceivedAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/SentReceivedAggregator.php @@ -2,7 +2,7 @@ declare(strict_types=1); -/** +/* * Chill is a software for social workers * * For the full copyright and license information, please view @@ -11,9 +11,10 @@ declare(strict_types=1); namespace Chill\ActivityBundle\Export\Aggregator; -use Chill\MainBundle\Export\AggregatorInterface; use Chill\ActivityBundle\Export\Declarations; +use Chill\MainBundle\Export\AggregatorInterface; use Doctrine\ORM\QueryBuilder; +use LogicException; use Symfony\Component\Form\FormBuilderInterface; class SentReceivedAggregator implements AggregatorInterface @@ -41,10 +42,11 @@ class SentReceivedAggregator implements AggregatorInterface public function getLabels($key, array $values, $data) { - return function ($value): string { + return static function ($value): string { if ('_header' === $value) { return ''; } + switch ($value) { case 'sent': return 'is sent'; @@ -53,7 +55,7 @@ class SentReceivedAggregator implements AggregatorInterface return 'is received'; default: - throw new \LogicException(sprintf('The value %s is not valid', $value)); + throw new LogicException(sprintf('The value %s is not valid', $value)); } }; } diff --git a/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/HasNoActivityFilter.php b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/HasNoActivityFilter.php index 13aaf475a..508644a07 100644 --- a/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/HasNoActivityFilter.php +++ b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/HasNoActivityFilter.php @@ -14,9 +14,10 @@ namespace Chill\ActivityBundle\Export\Filter\ACPFilters; use Chill\ActivityBundle\Entity\Activity; use Chill\MainBundle\Export\FilterInterface; use Chill\PersonBundle\Export\Declarations; +use Doctrine\ORM\Query\Expr; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; -use Doctrine\ORM\Query\Expr; +use function in_array; class HasNoActivityFilter implements FilterInterface { @@ -36,7 +37,7 @@ class HasNoActivityFilter implements FilterInterface //TODO check this: ->andWhere(' NOT EXISTS ( - SELECT 1 FROM '. Activity::class .' activity + SELECT 1 FROM ' . Activity::class . ' activity WHERE activity.accompanyingPeriod = acp ) '); @@ -61,4 +62,4 @@ class HasNoActivityFilter implements FilterInterface { return 'Filter acp which has no activity'; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Aggregator/ByActivityTypeAggregator.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Aggregator/ByActivityTypeAggregator.php index bbbdb6284..2104db81e 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Export/Aggregator/ByActivityTypeAggregator.php +++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Aggregator/ByActivityTypeAggregator.php @@ -2,7 +2,7 @@ declare(strict_types=1); -/** +/* * Chill is a software for social workers * * For the full copyright and license information, please view @@ -41,7 +41,7 @@ class ByActivityTypeAggregator implements AggregatorInterface public function getLabels($key, array $values, $data) { - return function ($value): string { + return static function ($value): string { if ('_header' === $value) { return ''; } diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Declarations.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Declarations.php index 0262516aa..925169a68 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Export/Declarations.php +++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Declarations.php @@ -17,4 +17,4 @@ namespace ChillAsideActivityBundle\Export; abstract class Declarations { public const ASIDE_ACTIVITY_TYPE = 'aside_activity'; -} \ No newline at end of file +} diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Export/CountAsideActivity.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Export/CountAsideActivity.php index 7af39568e..01b6a5204 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Export/Export/CountAsideActivity.php +++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Export/CountAsideActivity.php @@ -16,9 +16,9 @@ use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\GroupedExportInterface; use ChillAsideActivityBundle\Export\Declarations; -use Symfony\Component\Form\FormBuilderInterface; use Doctrine\ORM\Query; use LogicException; +use Symfony\Component\Form\FormBuilderInterface; class CountAsideActivity implements ExportInterface, GroupedExportInterface { @@ -105,7 +105,3 @@ class CountAsideActivity implements ExportInterface, GroupedExportInterface return []; } } - - - - diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByActivityTypeFilter.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByActivityTypeFilter.php index 60ca914b2..46d84c064 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByActivityTypeFilter.php +++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByActivityTypeFilter.php @@ -52,4 +52,4 @@ class ByActivityTypeFilter implements FilterInterface { return 'Filter by aside activity type'; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByDateFilter.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByDateFilter.php index dc6b89662..ad876571e 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByDateFilter.php +++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByDateFilter.php @@ -52,4 +52,4 @@ class ByDateFilter implements FilterInterface { return 'Filter by aside activity date'; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ByActionNumberAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ByActionNumberAggregator.php index 3c16aa161..0f51b6feb 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ByActionNumberAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ByActionNumberAggregator.php @@ -2,7 +2,7 @@ declare(strict_types=1); -/** +/* * Chill is a software for social workers * * For the full copyright and license information, please view @@ -41,7 +41,7 @@ class ByActionNumberAggregator implements AggregatorInterface public function getLabels($key, array $values, $data) { - return function ($value): string { + return static function ($value): string { if ('_header' === $value) { return ''; } diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/CreatorJobAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/CreatorJobAggregator.php index 1449091e7..c473bf7a1 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/CreatorJobAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/CreatorJobAggregator.php @@ -2,7 +2,7 @@ declare(strict_types=1); -/** +/* * Chill is a software for social workers * * For the full copyright and license information, please view @@ -41,7 +41,7 @@ class CreatorJobAggregator implements AggregatorInterface public function getLabels($key, array $values, $data) { - return function ($value): string { + return static function ($value): string { if ('_header' === $value) { return ''; } diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByEndDateAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByEndDateAggregator.php index 9c90c8dd3..40b035a02 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByEndDateAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByEndDateAggregator.php @@ -2,7 +2,7 @@ declare(strict_types=1); -/** +/* * Chill is a software for social workers * * For the full copyright and license information, please view @@ -41,7 +41,7 @@ class ByEndDateAggregator implements AggregatorInterface public function getLabels($key, array $values, $data) { - return function ($value): string { + return static function ($value): string { if ('_header' === $value) { return ''; } diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByMaxDateAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByMaxDateAggregator.php index ded9fb685..36ab0f50b 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByMaxDateAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByMaxDateAggregator.php @@ -2,7 +2,7 @@ declare(strict_types=1); -/** +/* * Chill is a software for social workers * * For the full copyright and license information, please view @@ -41,7 +41,7 @@ class ByMaxDateAggregator implements AggregatorInterface public function getLabels($key, array $values, $data) { - return function ($value): string { + return static function ($value): string { if ('_header' === $value) { return ''; } diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByStartDateAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByStartDateAggregator.php index 121171d8c..e64245d53 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByStartDateAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByStartDateAggregator.php @@ -2,7 +2,7 @@ declare(strict_types=1); -/** +/* * Chill is a software for social workers * * For the full copyright and license information, please view @@ -41,7 +41,7 @@ class ByStartDateAggregator implements AggregatorInterface public function getLabels($key, array $values, $data) { - return function ($value): string { + return static function ($value): string { if ('_header' === $value) { return ''; } diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/HavingEndDateAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/HavingEndDateAggregator.php index 8def8cf88..af801b74d 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/HavingEndDateAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/HavingEndDateAggregator.php @@ -2,7 +2,7 @@ declare(strict_types=1); -/** +/* * Chill is a software for social workers * * For the full copyright and license information, please view @@ -14,6 +14,7 @@ namespace Chill\PersonBundle\Export\Aggregator\EvaluationAggregators; use Chill\MainBundle\Export\AggregatorInterface; use Chill\PersonBundle\Export\Declarations; use Doctrine\ORM\QueryBuilder; +use LogicException; use Symfony\Component\Form\FormBuilderInterface; class HavingEndDateAggregator implements AggregatorInterface @@ -45,10 +46,11 @@ class HavingEndDateAggregator implements AggregatorInterface public function getLabels($key, array $values, $data) { - return function ($value): string { + return static function ($value): string { if ('_header' === $value) { return ''; } + switch ($value) { case true: return 'enddate is specified'; @@ -57,9 +59,8 @@ class HavingEndDateAggregator implements AggregatorInterface return 'enddate is not specified'; default: - throw new \LogicException(sprintf('The value %s is not valid', $value)); + throw new LogicException(sprintf('The value %s is not valid', $value)); } - }; } diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/CurrentActionAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/CurrentActionAggregator.php index d9120d121..a77838167 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/CurrentActionAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/CurrentActionAggregator.php @@ -2,7 +2,7 @@ declare(strict_types=1); -/** +/* * Chill is a software for social workers * * For the full copyright and license information, please view @@ -14,6 +14,7 @@ namespace Chill\PersonBundle\Export\Aggregator\SocialWorkAggregators; use Chill\MainBundle\Export\AggregatorInterface; use Chill\PersonBundle\Export\Declarations; use Doctrine\ORM\QueryBuilder; +use LogicException; use Symfony\Component\Form\FormBuilderInterface; class CurrentActionAggregator implements AggregatorInterface @@ -27,7 +28,7 @@ class CurrentActionAggregator implements AggregatorInterface { $qb ->addSelect(' - (CASE true WHEN acpw.startDate IS NULL ELSE false END) + (CASE true WHEN acpw.startDate IS NULL ELSE false END) AS acpw_current_action_aggregator ') ->addGroupBy('acpw_current_action_aggregator'); @@ -45,10 +46,11 @@ class CurrentActionAggregator implements AggregatorInterface public function getLabels($key, array $values, $data) { - return function ($value): string { + return static function ($value): string { if ('_header' === $value) { return ''; } + switch ($value) { case true: return 'Current action'; @@ -57,7 +59,7 @@ class CurrentActionAggregator implements AggregatorInterface return 'Not current action'; default: - throw new \LogicException(sprintf('The value %s is not valid', $value)); + throw new LogicException(sprintf('The value %s is not valid', $value)); } }; } diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorFilter.php index 7dd78d0b4..c7e110f62 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorFilter.php @@ -52,4 +52,4 @@ class CreatorFilter implements FilterInterface { return 'Filter by creator'; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorJobFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorJobFilter.php index 021d2227c..6f094fe97 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorJobFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorJobFilter.php @@ -52,4 +52,4 @@ class CreatorJobFilter implements FilterInterface { return 'Filter by creator job'; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasNoActionFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasNoActionFilter.php index a26628e95..771b8d063 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasNoActionFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasNoActionFilter.php @@ -15,6 +15,7 @@ use Chill\MainBundle\Export\FilterInterface; use Chill\PersonBundle\Export\Declarations; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; +use function in_array; class HasNoActionFilter implements FilterInterface { @@ -51,4 +52,4 @@ class HasNoActionFilter implements FilterInterface { return 'Filter by which has no action'; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasNoReferrerFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasNoReferrerFilter.php index c37d423f8..b18bcd1de 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasNoReferrerFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasNoReferrerFilter.php @@ -15,10 +15,10 @@ use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Form\Type\ChillDateType; use Chill\PersonBundle\Entity\AccompanyingPeriod\UserHistory; use Chill\PersonBundle\Export\Declarations; +use DateTime; use Doctrine\DBAL\Types\Types; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; -use DateTime; class HasNoReferrerFilter implements FilterInterface { @@ -32,12 +32,12 @@ class HasNoReferrerFilter implements FilterInterface $qb ->andWhere(' NOT EXISTS ( - SELECT 1 FROM '. UserHistory::class .' uh - WHERE uh.startDate < :date + SELECT 1 FROM ' . UserHistory::class . ' uh + WHERE uh.startDate < :date AND ( - uh.endDate IS NULL + uh.endDate IS NULL or uh.endDate > :date - ) + ) AND uh.accompanyingPeriod = acp ) ') @@ -69,4 +69,4 @@ class HasNoReferrerFilter implements FilterInterface { return 'Filter by which has no referrer'; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasTemporaryLocationFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasTemporaryLocationFilter.php index 18a6c3918..85d8d93b6 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasTemporaryLocationFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasTemporaryLocationFilter.php @@ -52,4 +52,4 @@ class HasTemporaryLocationFilter implements FilterInterface { return 'Filter by temporary location'; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByEndDateFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByEndDateFilter.php index d40170447..5c509d50c 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByEndDateFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByEndDateFilter.php @@ -14,10 +14,10 @@ namespace Chill\PersonBundle\Export\Filter\EvaluationFilters; use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Form\Type\ChillDateType; use Chill\PersonBundle\Export\Declarations; +use DateTime; use Doctrine\DBAL\Types\Types; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; -use DateTime; class ByEndDateFilter implements FilterInterface { @@ -64,4 +64,4 @@ class ByEndDateFilter implements FilterInterface { return 'Filter by end date evaluations'; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByStartDateFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByStartDateFilter.php index afa3c212a..e21a1ea68 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByStartDateFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByStartDateFilter.php @@ -14,10 +14,10 @@ namespace Chill\PersonBundle\Export\Filter\EvaluationFilters; use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Form\Type\ChillDateType; use Chill\PersonBundle\Export\Declarations; +use DateTime; use Doctrine\DBAL\Types\Types; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; -use DateTime; class ByStartDateFilter implements FilterInterface { @@ -64,4 +64,4 @@ class ByStartDateFilter implements FilterInterface { return 'Filter by start date evaluations'; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/CurrentEvaluationsFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/CurrentEvaluationsFilter.php index 293e551f1..50c95600e 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/CurrentEvaluationsFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/CurrentEvaluationsFilter.php @@ -47,4 +47,4 @@ class CurrentEvaluationsFilter implements FilterInterface { return 'Filter by current evaluations'; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/CurrentActionFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/CurrentActionFilter.php index ecbe6086a..cbf958182 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/CurrentActionFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/CurrentActionFilter.php @@ -47,4 +47,4 @@ class CurrentActionFilter implements FilterInterface { return 'Filter by current actions'; } -} \ No newline at end of file +} From 355436aa8242b0a09b1bf7666e726c6b7218a9a3 Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Wed, 2 Nov 2022 11:15:54 +0100 Subject: [PATCH 29/73] acp creator filter --- .../CreatorFilter.php | 40 +++++++++++++++---- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorFilter.php index c7e110f62..1ac6d5390 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorFilter.php @@ -12,9 +12,12 @@ declare(strict_types=1); namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters; use Chill\MainBundle\Export\FilterInterface; +use Chill\MainBundle\Form\Type\PickUserDynamicType; use Chill\PersonBundle\Export\Declarations; +use Doctrine\ORM\Query\Expr\Andx; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; +use function in_array; class CreatorFilter implements FilterInterface { @@ -25,11 +28,21 @@ class CreatorFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { - $qb - ->andWhere( - $qb->expr()->in('', ':') - ) - ->setParameter('', $data[]); + if (!in_array('acp_creator', $qb->getAllAliases(), true)) { + $qb->join('acp.createdBy', 'acp_creator'); + } + + $where = $qb->getDQLPart('where'); + $clause = $qb->expr()->in('acp_creator', ':creators'); + + if ($where instanceof Andx) { + $where->add($clause); + } else { + $where = $qb->expr()->andX($clause); + } + + $qb->add('where', $where); + $qb->setParameter('creators', $data['accepted_creators']); } public function applyOn(): string @@ -39,13 +52,24 @@ class CreatorFilter implements FilterInterface public function buildForm(FormBuilderInterface $builder) { - $builder->add(); + $builder + ->add('accepted_creators', PickUserDynamicType::class, [ + 'multiple' => true, + ]); } public function describeAction($data, $format = 'string'): array { - return ['', [ - ]]; + $creators = []; + + foreach ($data['accepted_creators'] as $c) { + $creators[] = $c; + } + + return [ + 'Filtered by creator: only %creators%', [ + '%creators%' => implode(', ', $creators), + ], ]; } public function getTitle(): string From edd1557f35e609b728db5c047db00a6ec553bdbf Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Wed, 2 Nov 2022 11:32:55 +0100 Subject: [PATCH 30/73] comment out builder method --- .../src/Export/Filter/ByActivityTypeFilter.php | 2 +- .../ChillAsideActivityBundle/src/Export/Filter/ByDateFilter.php | 2 +- .../AccompanyingCourseFilters/HasTemporaryLocationFilter.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByActivityTypeFilter.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByActivityTypeFilter.php index 4f256538d..354bd9f3c 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByActivityTypeFilter.php +++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByActivityTypeFilter.php @@ -39,7 +39,7 @@ class ByActivityTypeFilter implements FilterInterface public function buildForm(FormBuilderInterface $builder) { - $builder->add(); + //$builder->add(); } public function describeAction($data, $format = 'string'): array diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByDateFilter.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByDateFilter.php index d8fec664a..5a63e8e94 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByDateFilter.php +++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByDateFilter.php @@ -39,7 +39,7 @@ class ByDateFilter implements FilterInterface public function buildForm(FormBuilderInterface $builder) { - $builder->add(); + //$builder->add(); } public function describeAction($data, $format = 'string'): array diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasTemporaryLocationFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasTemporaryLocationFilter.php index 85d8d93b6..b674f1881 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasTemporaryLocationFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasTemporaryLocationFilter.php @@ -39,7 +39,7 @@ class HasTemporaryLocationFilter implements FilterInterface public function buildForm(FormBuilderInterface $builder) { - $builder->add(); + //$builder->add(); } public function describeAction($data, $format = 'string'): array From 749facb9b0e226c7e8afbfe354f31b5ee8b28160 Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Wed, 2 Nov 2022 11:55:36 +0100 Subject: [PATCH 31/73] creator job filter --- .../CreatorJobFilter.php | 55 ++++++++++++++++--- 1 file changed, 48 insertions(+), 7 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorJobFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorJobFilter.php index 6f094fe97..71fe3250f 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorJobFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorJobFilter.php @@ -11,13 +11,26 @@ declare(strict_types=1); namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters; +use Chill\MainBundle\Entity\UserJob; use Chill\MainBundle\Export\FilterInterface; +use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\PersonBundle\Export\Declarations; +use Doctrine\ORM\Query\Expr\Andx; use Doctrine\ORM\QueryBuilder; +use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\FormBuilderInterface; +use function in_array; class CreatorJobFilter implements FilterInterface { + private TranslatableStringHelper $translatableStringHelper; + + public function __construct( + TranslatableStringHelper $translatableStringHelper + ) { + $this->translatableStringHelper = $translatableStringHelper; + } + public function addRole(): ?string { return null; @@ -25,11 +38,21 @@ class CreatorJobFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { - $qb - ->andWhere( - $qb->expr()->in('', ':') - ) - ->setParameter('', $data[]); + if (!in_array('acp_creator', $qb->getAllAliases(), true)) { + $qb->join('acp.createdBy', 'acp_creator'); + } + + $where = $qb->getDQLPart('where'); + $clause = $qb->expr()->in('acp_creator.userJob', ':creator_job'); + + if ($where instanceof Andx) { + $where->add($clause); + } else { + $where = $qb->expr()->andX($clause); + } + + $qb->add('where', $where); + $qb->setParameter('creator_job', $data['creator_job']); } public function applyOn(): string @@ -39,12 +62,30 @@ class CreatorJobFilter implements FilterInterface public function buildForm(FormBuilderInterface $builder) { - $builder->add(); + $builder->add('creator_job', EntityType::class, [ + 'class' => UserJob::class, + 'choice_label' => function (UserJob $j) { + return $this->translatableStringHelper->localize( + $j->getLabel() + ); + }, + 'multiple' => true, + 'expanded' => true, + ]); } public function describeAction($data, $format = 'string'): array { - return ['', [ + $creatorJobs = []; + + foreach ($data['creator_job'] as $j) { + $creatorJobs[] = $this->translatableStringHelper->localize( + $j->getLabel() + ); + } + + return ['Filtered by creator job: only %jobs%', [ + '%jobs%' => implode(', ', $creatorJobs), ]]; } From 32cdf56fbd0a5c6933a70333c5a0917cfc52742c Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Wed, 2 Nov 2022 12:00:29 +0100 Subject: [PATCH 32/73] add translations creator filters --- src/Bundle/ChillPersonBundle/translations/messages.fr.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index 319231489..134a97414 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -572,9 +572,11 @@ Filter by which has no action: Filtrer les parcours qui n’ont pas d’actions Filtered acp which has no actions: Filtré les parcours qui n'ont pas d'actions Group by number of actions: Grouper les parcours par nombre d’actions Filter by creator: Filtrer les parcours par créateur +'Filtered by creator: only %creators%': 'Filtré par créateur: uniquement %creators%' Filter by creator job: Filtrer les parcours par métier du créateur +'Filtered by creator job: only %jobs%': 'Filtré par métier du créateur: uniquement %jobs%' Group by creator job: Grouper les parcours par métier du créateur - + Filter by current actions: Filtrer les actions en cours Filtered by current action: Filtré les actions en cours Group by current actions: Grouper les actions en cours From 17a7b053cf821f39c95a5baa75decfa568e8c811 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 2 Nov 2022 11:32:21 +0100 Subject: [PATCH 33/73] DX: remove unused variable --- src/Bundle/ChillCalendarBundle/Menu/UserMenuBuilder.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Bundle/ChillCalendarBundle/Menu/UserMenuBuilder.php b/src/Bundle/ChillCalendarBundle/Menu/UserMenuBuilder.php index 9da2f4ffd..90743d1ba 100644 --- a/src/Bundle/ChillCalendarBundle/Menu/UserMenuBuilder.php +++ b/src/Bundle/ChillCalendarBundle/Menu/UserMenuBuilder.php @@ -54,8 +54,6 @@ class UserMenuBuilder implements LocalMenuBuilderInterface public function buildMenu($menuId, MenuItem $menu, array $parameters) { - $user = $this->tokenStorage->getToken()->getUser(); - if ($this->authorizationChecker->isGranted('ROLE_USER')) { $menu->addChild('My calendar list', [ 'route' => 'chill_calendar_calendar_list', From e46b285d3e594c0fd23a14c571641be3175b2632 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 2 Nov 2022 11:34:50 +0100 Subject: [PATCH 34/73] Fixed: handle null value in SentReceivedAggregator --- .../Export/Aggregator/SentReceivedAggregator.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/SentReceivedAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/SentReceivedAggregator.php index 2a6dec7f7..24000096f 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/SentReceivedAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/SentReceivedAggregator.php @@ -48,6 +48,9 @@ class SentReceivedAggregator implements AggregatorInterface } switch ($value) { + case null: + return ''; + case 'sent': return 'is sent'; From be75cdce550e9aa55b218fb29ee7db04330d4b03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 2 Nov 2022 12:55:10 +0100 Subject: [PATCH 35/73] Feature: [export] Finalize HasTemporaryLocation filter on accompanying course --- .../HasTemporaryLocationFilter.php | 64 +++++++++++++++++-- .../translations/messages.fr.yml | 6 +- 2 files changed, 62 insertions(+), 8 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasTemporaryLocationFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasTemporaryLocationFilter.php index b674f1881..2ae1ad2a0 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasTemporaryLocationFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasTemporaryLocationFilter.php @@ -12,8 +12,12 @@ declare(strict_types=1); namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters; use Chill\MainBundle\Export\FilterInterface; +use Chill\MainBundle\Form\Type\ChillDateType; use Chill\PersonBundle\Export\Declarations; +use DateTimeImmutable; use Doctrine\ORM\QueryBuilder; +use LogicException; +use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\FormBuilderInterface; class HasTemporaryLocationFilter implements FilterInterface @@ -26,10 +30,25 @@ class HasTemporaryLocationFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { $qb - ->andWhere( - $qb->expr()->in('', ':') - ) - ->setParameter('', $data[]); + ->join('acp.locationHistories', 'acp_having_temporarily_location') + ->andWhere('acp_having_temporarily_location.startDate <= :acp_having_temporarily_location_date + AND (acp_having_temporarily_location.endDate IS NULL OR acp_having_temporarily_location.endDate > :acp_having_temporarily_location_date)') + ->setParameter('acp_having_temporarily_location_date', $data['calc_date']); + + switch ($data['having_temporarily']) { + case true: + $qb->andWhere('acp_having_temporarily_location.addressLocation IS NOT NULL'); + + break; + + case false: + $qb->andWhere('acp_having_temporarily_location.personLocation IS NOT NULL'); + + break; + + default: + throw new LogicException('value not supported'); + } } public function applyOn(): string @@ -39,13 +58,44 @@ class HasTemporaryLocationFilter implements FilterInterface public function buildForm(FormBuilderInterface $builder) { - //$builder->add(); + $builder + ->add('having_temporarily', ChoiceType::class, [ + 'choices' => [ + 'export.filter.course.having_temporarily.Having a temporarily location' => true, + 'export.filter.course.having_temporarily.Having a person\'s location' => false, + ], + 'choice_label' => static function ($choice) { + switch ($choice) { + case true: + return 'export.filter.course.having_temporarily.Having a temporarily location'; + + case false: + return 'export.filter.course.having_temporarily.Having a person\'s location'; + + default: + throw new LogicException('this choice is not supported'); + } + }, + ]) + ->add('calc_date', ChillDateType::class, [ + 'label' => 'export.filter.course.having_temporarily.Calculation date', + 'input' => 'datetime_immutable', + 'data' => new DateTimeImmutable(), + ]); } public function describeAction($data, $format = 'string'): array { - return ['', [ - ]]; + switch ($data['having_temporarily']) { + case true: + return ['export.filter.course.having_temporarily.Having a temporarily location', []]; + + case false: + return ['export.filter.course.having_temporarily.Having a person\'s location', []]; + + default: + throw new LogicException('value not supported'); + } } public function getTitle(): string diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index 319231489..a48d5ea46 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -574,7 +574,7 @@ Group by number of actions: Grouper les parcours par nombre d’actions Filter by creator: Filtrer les parcours par créateur Filter by creator job: Filtrer les parcours par métier du créateur Group by creator job: Grouper les parcours par métier du créateur - + Filter by current actions: Filtrer les actions en cours Filtered by current action: Filtré les actions en cours Group by current actions: Grouper les actions en cours @@ -1019,6 +1019,10 @@ export: Computation date for referrer: Date à laquelle le référent était actif by_referrer: Computation date for referrer: Date à laquelle le référent était actif + having_temporarily: + Having a temporarily location: Ayant une localisation temporaire + Having a person's location: Ayant une localisation auprès d'un usager + Calculation date: Date de la localisation list: person_with_acp: List peoples having an accompanying period: Liste des personnes ayant un parcours d'accompagnement From be215bd1356fa511ae6f3686df3aed24d8162803 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 2 Nov 2022 13:05:12 +0100 Subject: [PATCH 36/73] DX: simplify creatorJobFilter --- .../CreatorJobFilter.php | 25 ++++++++----------- .../translations/messages.fr.yml | 2 ++ 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorJobFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorJobFilter.php index 71fe3250f..c8b7ccc55 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorJobFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorJobFilter.php @@ -13,9 +13,9 @@ namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters; use Chill\MainBundle\Entity\UserJob; use Chill\MainBundle\Export\FilterInterface; +use Chill\MainBundle\Repository\UserJobRepositoryInterface; use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\PersonBundle\Export\Declarations; -use Doctrine\ORM\Query\Expr\Andx; use Doctrine\ORM\QueryBuilder; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\FormBuilderInterface; @@ -25,10 +25,14 @@ class CreatorJobFilter implements FilterInterface { private TranslatableStringHelper $translatableStringHelper; + private UserJobRepositoryInterface $userJobRepository; + public function __construct( - TranslatableStringHelper $translatableStringHelper + TranslatableStringHelper $translatableStringHelper, + UserJobRepositoryInterface $userJobRepository ) { $this->translatableStringHelper = $translatableStringHelper; + $this->userJobRepository = $userJobRepository; } public function addRole(): ?string @@ -42,17 +46,9 @@ class CreatorJobFilter implements FilterInterface $qb->join('acp.createdBy', 'acp_creator'); } - $where = $qb->getDQLPart('where'); - $clause = $qb->expr()->in('acp_creator.userJob', ':creator_job'); - - if ($where instanceof Andx) { - $where->add($clause); - } else { - $where = $qb->expr()->andX($clause); - } - - $qb->add('where', $where); - $qb->setParameter('creator_job', $data['creator_job']); + $qb + ->andWhere($qb->expr()->in('acp_creator.userJob', ':creator_job')) + ->setParameter('creator_job', $data['creator_job']); } public function applyOn(): string @@ -64,6 +60,7 @@ class CreatorJobFilter implements FilterInterface { $builder->add('creator_job', EntityType::class, [ 'class' => UserJob::class, + 'choices' => $this->userJobRepository->findAllActive(), 'choice_label' => function (UserJob $j) { return $this->translatableStringHelper->localize( $j->getLabel() @@ -84,7 +81,7 @@ class CreatorJobFilter implements FilterInterface ); } - return ['Filtered by creator job: only %jobs%', [ + return ['export.filter.course.creator_job.Filtered by creator job: only %jobs%', [ '%jobs%' => implode(', ', $creatorJobs), ]]; } diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index a48d5ea46..3821253ed 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -1023,6 +1023,8 @@ export: Having a temporarily location: Ayant une localisation temporaire Having a person's location: Ayant une localisation auprès d'un usager Calculation date: Date de la localisation + creator_job: + 'Filtered by creator job: only %jobs%': 'Filtré par métier du créateur: seulement %jobs%' list: person_with_acp: List peoples having an accompanying period: Liste des personnes ayant un parcours d'accompagnement From 50e2f4a147eef0d62a478c39353e40490fdcffc8 Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Wed, 2 Nov 2022 13:14:38 +0100 Subject: [PATCH 37/73] setup aside activity export --- .../DependencyInjection/ChillAsideActivityExtension.php | 1 + .../src/config/services/export.yaml | 9 +++++++++ 2 files changed, 10 insertions(+) create mode 100644 src/Bundle/ChillAsideActivityBundle/src/config/services/export.yaml diff --git a/src/Bundle/ChillAsideActivityBundle/src/DependencyInjection/ChillAsideActivityExtension.php b/src/Bundle/ChillAsideActivityBundle/src/DependencyInjection/ChillAsideActivityExtension.php index e81b887c0..ef6c5d69b 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/DependencyInjection/ChillAsideActivityExtension.php +++ b/src/Bundle/ChillAsideActivityBundle/src/DependencyInjection/ChillAsideActivityExtension.php @@ -30,6 +30,7 @@ final class ChillAsideActivityExtension extends Extension implements PrependExte $loader->load('services.yaml'); $loader->load('services/form.yaml'); $loader->load('services/menu.yaml'); + $loader->load('services/export.yaml'); } public function prepend(ContainerBuilder $container) diff --git a/src/Bundle/ChillAsideActivityBundle/src/config/services/export.yaml b/src/Bundle/ChillAsideActivityBundle/src/config/services/export.yaml new file mode 100644 index 000000000..579791d41 --- /dev/null +++ b/src/Bundle/ChillAsideActivityBundle/src/config/services/export.yaml @@ -0,0 +1,9 @@ +services: + _defaults: + autowire: true + autoconfigure: true + + ## Indicators + Chill\AsideActivityBundle\Export\Export\CountAsideActivity: + tags: + - { name: chill.export, alias: 'count_aside_activity' } From 094a912e427b13ebb05356234dce2c88b1139528 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 2 Nov 2022 13:14:48 +0100 Subject: [PATCH 38/73] Fixed: fix date's type in DateType input, and prevent collision on parameter name --- .../AccompanyingCourseFilters/HasNoReferrerFilter.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasNoReferrerFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasNoReferrerFilter.php index b18bcd1de..303c789af 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasNoReferrerFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasNoReferrerFilter.php @@ -15,7 +15,7 @@ use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Form\Type\ChillDateType; use Chill\PersonBundle\Entity\AccompanyingPeriod\UserHistory; use Chill\PersonBundle\Export\Declarations; -use DateTime; +use DateTimeImmutable; use Doctrine\DBAL\Types\Types; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; @@ -33,15 +33,15 @@ class HasNoReferrerFilter implements FilterInterface ->andWhere(' NOT EXISTS ( SELECT 1 FROM ' . UserHistory::class . ' uh - WHERE uh.startDate < :date + WHERE uh.startDate <= :has_no_referrer_filter_date AND ( uh.endDate IS NULL - or uh.endDate > :date + or uh.endDate > :has_no_referrer_filter_date ) AND uh.accompanyingPeriod = acp ) ') - ->setParameter('date', $data['calc_date'], Types::DATE_IMMUTABLE); + ->setParameter('has_no_referrer_filter_date', $data['calc_date'], Types::DATE_IMMUTABLE); } public function applyOn(): string @@ -54,7 +54,8 @@ class HasNoReferrerFilter implements FilterInterface $builder ->add('calc_date', ChillDateType::class, [ 'label' => 'Has no referrer on this date', - 'data' => new DateTime(), + 'data' => new DateTimeImmutable(), + 'input' => 'datetime_immutable', ]); } From 5acf2a198680c2f3faa79d669853af5c5751952d Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Wed, 2 Nov 2022 13:17:03 +0100 Subject: [PATCH 39/73] creator job aggregator --- .../CreatorJobAggregator.php | 33 +++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/CreatorJobAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/CreatorJobAggregator.php index c473bf7a1..3bae64fec 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/CreatorJobAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/CreatorJobAggregator.php @@ -12,12 +12,27 @@ declare(strict_types=1); namespace Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators; use Chill\MainBundle\Export\AggregatorInterface; +use Chill\MainBundle\Repository\UserJobRepository; +use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\PersonBundle\Export\Declarations; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; +use function in_array; class CreatorJobAggregator implements AggregatorInterface { + private UserJobRepository $jobRepository; + + private TranslatableStringHelper $translatableStringHelper; + + public function __construct( + UserJobRepository $jobRepository, + TranslatableStringHelper $translatableStringHelper + ) { + $this->jobRepository = $jobRepository; + $this->translatableStringHelper = $translatableStringHelper; + } + public function addRole(): ?string { return null; @@ -25,7 +40,11 @@ class CreatorJobAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { - $qb->addSelect('AS acp_creator_job_aggregator') + if (!in_array('acp_creator', $qb->getAllAliases(), true)) { + $qb->leftJoin('acp.createdBy', 'acp_creator'); + } + + $qb->addSelect('IDENTITY(acp_creator.userJob) AS acp_creator_job_aggregator') ->addGroupBy('acp_creator_job_aggregator'); } @@ -41,10 +60,20 @@ class CreatorJobAggregator implements AggregatorInterface public function getLabels($key, array $values, $data) { - return static function ($value): string { + return function ($value): string { if ('_header' === $value) { + return 'Job'; + } + + if (null === $value) { return ''; } + + $j = $this->jobRepository->find($value); + + return $this->translatableStringHelper->localize( + $j->getLabel() + ); }; } From 84e5be6a600f0a549a866c0f7273c0523f671521 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 2 Nov 2022 13:21:26 +0100 Subject: [PATCH 40/73] DX: review for hasNoActivityFilter --- .../Export/Filter/ACPFilters/HasNoActivityFilter.php | 10 +--------- .../ChillActivityBundle/translations/messages.fr.yml | 2 +- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/HasNoActivityFilter.php b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/HasNoActivityFilter.php index 508644a07..570f42ae0 100644 --- a/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/HasNoActivityFilter.php +++ b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/HasNoActivityFilter.php @@ -14,10 +14,8 @@ namespace Chill\ActivityBundle\Export\Filter\ACPFilters; use Chill\ActivityBundle\Entity\Activity; use Chill\MainBundle\Export\FilterInterface; use Chill\PersonBundle\Export\Declarations; -use Doctrine\ORM\Query\Expr; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; -use function in_array; class HasNoActivityFilter implements FilterInterface { @@ -28,13 +26,7 @@ class HasNoActivityFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { - if (!in_array('activity', $qb->getAllAliases(), true)) { - $qb->join(Activity::class, 'activity', Expr\Join::WITH, 'activity.accompanyingPeriod = acp'); - } - $qb - //->andWhere('COUNT(acp.activities) IS NULL') - //TODO check this: ->andWhere(' NOT EXISTS ( SELECT 1 FROM ' . Activity::class . ' activity @@ -55,7 +47,7 @@ class HasNoActivityFilter implements FilterInterface public function describeAction($data, $format = 'string'): array { - return ['Filtered acp which has no activities']; + return ['Filtered acp which has no activities', []]; } public function getTitle(): string diff --git a/src/Bundle/ChillActivityBundle/translations/messages.fr.yml b/src/Bundle/ChillActivityBundle/translations/messages.fr.yml index e3fcfd2b2..1d5e7f1f2 100644 --- a/src/Bundle/ChillActivityBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillActivityBundle/translations/messages.fr.yml @@ -278,7 +278,7 @@ Filter activity by userscope: Filtrer les activités par service du créateur Accepted userscope: Services Filter acp which has no activity: Filtrer les parcours qui n’ont pas d’activité -Filtered acp which has no activities: Filtrés les parcours qui n'ont pas d'activités +Filtered acp which has no activities: Filtrer les parcours sans activité associée Group acp by activity number: Grouper les parcours par nombre d’activité Group activity by sentreceived: Grouper les activités par envoyé / reçu From 24873f0cc29092ff7a8f7f35145c85ac6cc9bfa9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 2 Nov 2022 13:33:33 +0100 Subject: [PATCH 41/73] Feature: [export] add aggregator by "number of activity" for acp --- .../ByActivityNumberAggregator.php | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByActivityNumberAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByActivityNumberAggregator.php index de35f75cc..5c6656009 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByActivityNumberAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByActivityNumberAggregator.php @@ -11,6 +11,7 @@ declare(strict_types=1); namespace Chill\ActivityBundle\Export\Aggregator\ACPAggregators; +use Chill\ActivityBundle\Entity\Activity; use Chill\MainBundle\Export\AggregatorInterface; use Chill\PersonBundle\Export\Declarations; use Doctrine\ORM\QueryBuilder; @@ -23,9 +24,10 @@ class ByActivityNumberAggregator implements AggregatorInterface return null; } - public function alterQuery(QueryBuilder $qb, $data) + public function alterQuery(QueryBuilder $qb, $data): void { - $qb->addSelect('AS activity_by_number_aggregator') + $qb + ->addSelect('(SELECT COUNT(activity.id) FROM ' . Activity::class . ' activity WHERE activity.accompanyingPeriod = acp) AS activity_by_number_aggregator') ->addGroupBy('activity_by_number_aggregator'); } @@ -34,17 +36,23 @@ class ByActivityNumberAggregator implements AggregatorInterface return Declarations::ACP_TYPE; } - public function buildForm(FormBuilderInterface $builder) + public function buildForm(FormBuilderInterface $builder): void { // No form needed } public function getLabels($key, array $values, $data) { - return static function ($value): string { + return static function ($value) { if ('_header' === $value) { return ''; } + + if (null === $value) { + return ''; + } + + return $value; }; } From 51ac4e0938038acf775e10de292438700a5eba10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 2 Nov 2022 13:44:04 +0100 Subject: [PATCH 42/73] Feature: [export] filter accompanying period which has no action - work --- .../AccompanyingCourseFilters/HasNoActionFilter.php | 12 ++++-------- .../ChillPersonBundle/translations/messages.fr.yml | 2 +- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasNoActionFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasNoActionFilter.php index 771b8d063..54c28d027 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasNoActionFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasNoActionFilter.php @@ -12,10 +12,10 @@ declare(strict_types=1); namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters; use Chill\MainBundle\Export\FilterInterface; +use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork; use Chill\PersonBundle\Export\Declarations; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; -use function in_array; class HasNoActionFilter implements FilterInterface { @@ -24,13 +24,9 @@ class HasNoActionFilter implements FilterInterface return null; } - public function alterQuery(QueryBuilder $qb, $data) + public function alterQuery(QueryBuilder $qb, $data): void { - if (!in_array('acpw', $qb->getAllAliases(), true)) { - $qb->join('acp.works', 'acpw'); - } - - $qb->andWhere('COUNT(acp.works) IS NULL'); + $qb->andWhere('NOT EXISTS (SELECT 1 FROM ' . AccompanyingPeriodWork::class . ' work WHERE work.accompanyingPeriod = acp)'); } public function applyOn(): string @@ -38,7 +34,7 @@ class HasNoActionFilter implements FilterInterface return Declarations::ACP_TYPE; } - public function buildForm(FormBuilderInterface $builder) + public function buildForm(FormBuilderInterface $builder): void { // no form } diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index 3821253ed..4b1919f8d 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -569,7 +569,7 @@ Filter by which has no referrer: Filtrer les parcours sans référent "Filtered acp which has no referrer on date: %date%": "Filtré les parcours sans référent à cette date: %date%" Has no referrer on this date: N'a pas de référent à cette date Filter by which has no action: Filtrer les parcours qui n’ont pas d’actions -Filtered acp which has no actions: Filtré les parcours qui n'ont pas d'actions +Filtered acp which has no actions: 'Filtré: uniquement les parcours qui n''ont pas d''actions' Group by number of actions: Grouper les parcours par nombre d’actions Filter by creator: Filtrer les parcours par créateur Filter by creator job: Filtrer les parcours par métier du créateur From c872ca7204805cd0462790f71eb9944b09a1add4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 2 Nov 2022 14:05:16 +0100 Subject: [PATCH 43/73] Feature: [export] group accompanying period by number of actions --- .../ByActionNumberAggregator.php | 15 +++++++++++---- .../translations/messages.fr.yml | 2 ++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ByActionNumberAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ByActionNumberAggregator.php index 0f51b6feb..2dffccbbb 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ByActionNumberAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ByActionNumberAggregator.php @@ -12,6 +12,7 @@ declare(strict_types=1); namespace Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators; use Chill\MainBundle\Export\AggregatorInterface; +use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork; use Chill\PersonBundle\Export\Declarations; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; @@ -23,9 +24,9 @@ class ByActionNumberAggregator implements AggregatorInterface return null; } - public function alterQuery(QueryBuilder $qb, $data) + public function alterQuery(QueryBuilder $qb, $data): void { - $qb->addSelect('AS acp_by_action_number_aggregator') + $qb->addSelect('(SELECT COUNT(acp_by_action_action.id) FROM ' . AccompanyingPeriodWork::class . ' acp_by_action_action WHERE acp_by_action_action.accompanyingPeriod = acp) AS acp_by_action_number_aggregator') ->addGroupBy('acp_by_action_number_aggregator'); } @@ -34,17 +35,23 @@ class ByActionNumberAggregator implements AggregatorInterface return Declarations::ACP_TYPE; } - public function buildForm(FormBuilderInterface $builder) + public function buildForm(FormBuilderInterface $builder): void { // No form needed } public function getLabels($key, array $values, $data) { - return static function ($value): string { + return static function ($value) { if ('_header' === $value) { + return 'export.aggregator.course.by_number_of_action.Number of actions'; + } + + if (null === $value) { return ''; } + + return $value; }; } diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index 4b1919f8d..8d5e055cd 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -1013,6 +1013,8 @@ export: Household composition: Composition du ménage Group course by household composition: Grouper les parcours par composition familiale des ménages des usagers concernés Calc date: Date de calcul de la composition du ménage + by_number_of_action: + Number of actions: Nombre d'actions filter: course: by_user_scope: From 60f93f7f12ae2522c6a567b7aaa2e7d2c27f988a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 2 Nov 2022 14:06:10 +0100 Subject: [PATCH 44/73] DX: more code style fixes --- .../Controller/ActivityControllerTest.php | 8 ++- .../Tests/Form/ActivityTypeTest.php | 4 +- .../Config/ConfigRepository.php | 16 ++++-- .../Service/Summary/SummaryBudget.php | 4 +- .../ParticipationControllerTest.php | 12 +++-- .../Export/Helper/ExportAddressHelper.php | 4 +- .../EntityToJsonTransformer.php | 4 +- .../Form/Type/ScopePickerType.php | 4 +- .../Tests/Entity/NotificationTest.php | 4 +- .../Tests/Form/Type/ScopePickerTypeTest.php | 4 +- .../Authorization/AuthorizationHelperTest.php | 4 +- .../Resolver/DefaultScopeResolverTest.php | 4 +- .../Resolver/ScopeResolverDispatcherTest.php | 4 +- .../NotificationOnTransition.php | 4 +- ...cialIssueConsistencyEntityListenerTest.php | 2 +- .../AccompanyingCourseApiControllerTest.php | 4 +- .../Controller/PersonControllerUpdateTest.php | 52 ++++++++++++++----- ...onControllerUpdateWithHiddenFieldsTest.php | 24 ++++++--- .../Tests/Household/MembersEditorTest.php | 8 ++- .../RelationshipDocGenNormalizerTest.php | 4 +- .../Tests/Timeline/TimelineProviderTest.php | 4 +- 21 files changed, 131 insertions(+), 47 deletions(-) diff --git a/src/Bundle/ChillActivityBundle/Tests/Controller/ActivityControllerTest.php b/src/Bundle/ChillActivityBundle/Tests/Controller/ActivityControllerTest.php index 55a1442f1..8a30a6c9b 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Controller/ActivityControllerTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Controller/ActivityControllerTest.php @@ -369,8 +369,12 @@ final class ActivityControllerTest extends WebTestCase $center ); $reachableScopesId = array_intersect( - array_map(static function ($s) { return $s->getId(); }, $reachableScopesDelete), - array_map(static function ($s) { return $s->getId(); }, $reachableScopesUpdate) + array_map(static function ($s) { + return $s->getId(); + }, $reachableScopesDelete), + array_map(static function ($s) { + return $s->getId(); + }, $reachableScopesUpdate) ); if (count($reachableScopesId) === 0) { diff --git a/src/Bundle/ChillActivityBundle/Tests/Form/ActivityTypeTest.php b/src/Bundle/ChillActivityBundle/Tests/Form/ActivityTypeTest.php index d0b4b5bb2..b6cb6fc7b 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Form/ActivityTypeTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Form/ActivityTypeTest.php @@ -188,7 +188,9 @@ final class ActivityTypeTest extends KernelTestCase // map all the values in an array $values = array_map( - static function ($choice) { return $choice->value; }, + static function ($choice) { + return $choice->value; + }, $view['activity']['durationTime']->vars['choices'] ); diff --git a/src/Bundle/ChillBudgetBundle/Config/ConfigRepository.php b/src/Bundle/ChillBudgetBundle/Config/ConfigRepository.php index 2497bcf21..f28a83a4d 100644 --- a/src/Bundle/ChillBudgetBundle/Config/ConfigRepository.php +++ b/src/Bundle/ChillBudgetBundle/Config/ConfigRepository.php @@ -31,7 +31,9 @@ class ConfigRepository public function getChargesKeys(bool $onlyActive = false): array { - return array_map(static function ($element) { return $element['key']; }, $this->getCharges($onlyActive)); + return array_map(static function ($element) { + return $element['key']; + }, $this->getCharges($onlyActive)); } /** @@ -50,7 +52,9 @@ class ConfigRepository public function getResourcesKeys(bool $onlyActive = false): array { - return array_map(static function ($element) { return $element['key']; }, $this->getResources($onlyActive)); + return array_map(static function ($element) { + return $element['key']; + }, $this->getResources($onlyActive)); } /** @@ -70,14 +74,18 @@ class ConfigRepository private function getCharges(bool $onlyActive = false): array { return $onlyActive ? - array_filter($this->charges, static function ($el) { return $el['active']; }) + array_filter($this->charges, static function ($el) { + return $el['active']; + }) : $this->charges; } private function getResources(bool $onlyActive = false): array { return $onlyActive ? - array_filter($this->resources, static function ($el) { return $el['active']; }) + array_filter($this->resources, static function ($el) { + return $el['active']; + }) : $this->resources; } diff --git a/src/Bundle/ChillBudgetBundle/Service/Summary/SummaryBudget.php b/src/Bundle/ChillBudgetBundle/Service/Summary/SummaryBudget.php index 6ae7e049b..f02d2e64a 100644 --- a/src/Bundle/ChillBudgetBundle/Service/Summary/SummaryBudget.php +++ b/src/Bundle/ChillBudgetBundle/Service/Summary/SummaryBudget.php @@ -61,7 +61,9 @@ class SummaryBudget implements SummaryBudgetInterface ]; } - $personIds = $household->getCurrentPersons()->map(static function (Person $p) { return $p->getId(); }); + $personIds = $household->getCurrentPersons()->map(static function (Person $p) { + return $p->getId(); + }); $ids = implode(', ', array_fill(0, count($personIds), '?')); $parameters = [...$personIds, $household->getId()]; diff --git a/src/Bundle/ChillEventBundle/Tests/Controller/ParticipationControllerTest.php b/src/Bundle/ChillEventBundle/Tests/Controller/ParticipationControllerTest.php index dfb782a7d..fa1ecabbe 100644 --- a/src/Bundle/ChillEventBundle/Tests/Controller/ParticipationControllerTest.php +++ b/src/Bundle/ChillEventBundle/Tests/Controller/ParticipationControllerTest.php @@ -239,7 +239,9 @@ final class ParticipationControllerTest extends WebTestCase $this->personsIdsCache = array_merge( $this->personsIdsCache, $event->getParticipations()->map( - static function ($p) { return $p->getPerson()->getId(); } + static function ($p) { + return $p->getPerson()->getId(); + } ) ->toArray() ); @@ -303,7 +305,9 @@ final class ParticipationControllerTest extends WebTestCase $event = $this->getRandomEventWithMultipleParticipations(); $persons_id = implode(',', $event->getParticipations()->map( - static function ($p) { return $p->getPerson()->getId(); } + static function ($p) { + return $p->getPerson()->getId(); + } )->toArray()); $crawler = $this->client->request( @@ -329,7 +333,9 @@ final class ParticipationControllerTest extends WebTestCase $nbParticipations = $event->getParticipations()->count(); // get the persons_id participating on this event $persons_id = $event->getParticipations()->map( - static function ($p) { return $p->getPerson()->getId(); } + static function ($p) { + return $p->getPerson()->getId(); + } )->toArray(); // exclude the existing persons_ids from the new person $this->personsIdsCache = array_merge($this->personsIdsCache, $persons_id); diff --git a/src/Bundle/ChillMainBundle/Export/Helper/ExportAddressHelper.php b/src/Bundle/ChillMainBundle/Export/Helper/ExportAddressHelper.php index 2f9a270ff..7d4245aa6 100644 --- a/src/Bundle/ChillMainBundle/Export/Helper/ExportAddressHelper.php +++ b/src/Bundle/ChillMainBundle/Export/Helper/ExportAddressHelper.php @@ -177,7 +177,9 @@ class ExportAddressHelper $prefixes = array_merge( $prefixes, array_map( - static function ($item) use ($prefix) { return $prefix . $item; }, + static function ($item) use ($prefix) { + return $prefix . $item; + }, self::COLUMN_MAPPING[$key] ) ); diff --git a/src/Bundle/ChillMainBundle/Form/Type/DataTransformer/EntityToJsonTransformer.php b/src/Bundle/ChillMainBundle/Form/Type/DataTransformer/EntityToJsonTransformer.php index ecd668d2b..46fa8799f 100644 --- a/src/Bundle/ChillMainBundle/Form/Type/DataTransformer/EntityToJsonTransformer.php +++ b/src/Bundle/ChillMainBundle/Form/Type/DataTransformer/EntityToJsonTransformer.php @@ -51,7 +51,9 @@ class EntityToJsonTransformer implements DataTransformerInterface } return array_map( - function ($item) { return $this->denormalizeOne($item); }, + function ($item) { + return $this->denormalizeOne($item); + }, $denormalized ); } diff --git a/src/Bundle/ChillMainBundle/Form/Type/ScopePickerType.php b/src/Bundle/ChillMainBundle/Form/Type/ScopePickerType.php index 1cba1ba58..f65677dc7 100644 --- a/src/Bundle/ChillMainBundle/Form/Type/ScopePickerType.php +++ b/src/Bundle/ChillMainBundle/Form/Type/ScopePickerType.php @@ -66,7 +66,9 @@ class ScopePickerType extends AbstractType $options['role'] instanceof Role ? $options['role']->getRole() : $options['role'], $options['center'] ), - static function (Scope $s) { return $s->isActive(); } + static function (Scope $s) { + return $s->isActive(); + } ); if (0 === count($items)) { diff --git a/src/Bundle/ChillMainBundle/Tests/Entity/NotificationTest.php b/src/Bundle/ChillMainBundle/Tests/Entity/NotificationTest.php index f5e203f78..9246cf1e6 100644 --- a/src/Bundle/ChillMainBundle/Tests/Entity/NotificationTest.php +++ b/src/Bundle/ChillMainBundle/Tests/Entity/NotificationTest.php @@ -134,7 +134,9 @@ final class NotificationTest extends KernelTestCase $this->assertEquals($senderId, $notification->getSender()->getId()); $this->assertCount(count($addressesIds), $notification->getUnreadBy()); - $unreadIds = $notification->getUnreadBy()->map(static function (User $u) { return $u->getId(); }); + $unreadIds = $notification->getUnreadBy()->map(static function (User $u) { + return $u->getId(); + }); foreach ($addressesIds as $addresseeId) { $this->assertContains($addresseeId, $unreadIds); diff --git a/src/Bundle/ChillMainBundle/Tests/Form/Type/ScopePickerTypeTest.php b/src/Bundle/ChillMainBundle/Tests/Form/Type/ScopePickerTypeTest.php index d64f68951..ce41c9a21 100644 --- a/src/Bundle/ChillMainBundle/Tests/Form/Type/ScopePickerTypeTest.php +++ b/src/Bundle/ChillMainBundle/Tests/Form/Type/ScopePickerTypeTest.php @@ -96,7 +96,9 @@ final class ScopePickerTypeTest extends TypeTestCase $translatableStringHelper = $this->prophesize(TranslatableStringHelperInterface::class); $translatableStringHelper->localize(Argument::type('array'))->will( - static function ($args) { return $args[0]['fr']; } + static function ($args) { + return $args[0]['fr']; + } ); $type = new ScopePickerType( diff --git a/src/Bundle/ChillMainBundle/Tests/Security/Authorization/AuthorizationHelperTest.php b/src/Bundle/ChillMainBundle/Tests/Security/Authorization/AuthorizationHelperTest.php index 02d319042..cc5a49096 100644 --- a/src/Bundle/ChillMainBundle/Tests/Security/Authorization/AuthorizationHelperTest.php +++ b/src/Bundle/ChillMainBundle/Tests/Security/Authorization/AuthorizationHelperTest.php @@ -211,7 +211,9 @@ final class AuthorizationHelperTest extends KernelTestCase $centerA ); - $usernames = array_map(static function (User $u) { return $u->getUsername(); }, $users); + $usernames = array_map(static function (User $u) { + return $u->getUsername(); + }, $users); $this->assertContains('center a_social', $usernames); } diff --git a/src/Bundle/ChillMainBundle/Tests/Security/Resolver/DefaultScopeResolverTest.php b/src/Bundle/ChillMainBundle/Tests/Security/Resolver/DefaultScopeResolverTest.php index 7e6710a20..30d50fca5 100644 --- a/src/Bundle/ChillMainBundle/Tests/Security/Resolver/DefaultScopeResolverTest.php +++ b/src/Bundle/ChillMainBundle/Tests/Security/Resolver/DefaultScopeResolverTest.php @@ -33,7 +33,7 @@ final class DefaultScopeResolverTest extends TestCase public function testHasScopeInterface() { $scope = new Scope(); - $entity = new class($scope) implements HasScopeInterface { + $entity = new class ($scope) implements HasScopeInterface { public function __construct(Scope $scope) { $this->scope = $scope; @@ -52,7 +52,7 @@ final class DefaultScopeResolverTest extends TestCase public function testHasScopesInterface() { - $entity = new class($scopeA = new Scope(), $scopeB = new Scope()) implements HasScopesInterface { + $entity = new class ($scopeA = new Scope(), $scopeB = new Scope()) implements HasScopesInterface { public function __construct(Scope $scopeA, Scope $scopeB) { $this->scopes = [$scopeA, $scopeB]; diff --git a/src/Bundle/ChillMainBundle/Tests/Security/Resolver/ScopeResolverDispatcherTest.php b/src/Bundle/ChillMainBundle/Tests/Security/Resolver/ScopeResolverDispatcherTest.php index e2c5d3879..31e5229bb 100644 --- a/src/Bundle/ChillMainBundle/Tests/Security/Resolver/ScopeResolverDispatcherTest.php +++ b/src/Bundle/ChillMainBundle/Tests/Security/Resolver/ScopeResolverDispatcherTest.php @@ -34,7 +34,7 @@ final class ScopeResolverDispatcherTest extends TestCase public function testHasScopeInterface() { $scope = new Scope(); - $entity = new class($scope) implements HasScopeInterface { + $entity = new class ($scope) implements HasScopeInterface { public function __construct(Scope $scope) { $this->scope = $scope; @@ -52,7 +52,7 @@ final class ScopeResolverDispatcherTest extends TestCase public function testHasScopesInterface() { - $entity = new class($scopeA = new Scope(), $scopeB = new Scope()) implements HasScopesInterface { + $entity = new class ($scopeA = new Scope(), $scopeB = new Scope()) implements HasScopesInterface { public function __construct(Scope $scopeA, Scope $scopeB) { $this->scopes = [$scopeA, $scopeB]; diff --git a/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/NotificationOnTransition.php b/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/NotificationOnTransition.php index 608ae6fa1..7d2cadc37 100644 --- a/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/NotificationOnTransition.php +++ b/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/NotificationOnTransition.php @@ -107,7 +107,9 @@ class NotificationOnTransition implements EventSubscriberInterface 'dest' => $subscriber, 'place' => $place, 'workflow' => $workflow, - 'is_dest' => in_array($subscriber->getId(), array_map(static function (User $u) { return $u->getId(); }, $entityWorkflow->futureDestUsers), true), + 'is_dest' => in_array($subscriber->getId(), array_map(static function (User $u) { + return $u->getId(); + }, $entityWorkflow->futureDestUsers), true), ]; $notification = new Notification(); diff --git a/src/Bundle/ChillPersonBundle/Tests/AccompanyingPeriod/SocialIssueConsistency/AccompanyingPeriodSocialIssueConsistencyEntityListenerTest.php b/src/Bundle/ChillPersonBundle/Tests/AccompanyingPeriod/SocialIssueConsistency/AccompanyingPeriodSocialIssueConsistencyEntityListenerTest.php index 0c8c3e0c3..8ac092647 100644 --- a/src/Bundle/ChillPersonBundle/Tests/AccompanyingPeriod/SocialIssueConsistency/AccompanyingPeriodSocialIssueConsistencyEntityListenerTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/AccompanyingPeriod/SocialIssueConsistency/AccompanyingPeriodSocialIssueConsistencyEntityListenerTest.php @@ -114,7 +114,7 @@ final class AccompanyingPeriodSocialIssueConsistencyEntityListenerTest extends T protected function generateClass(AccompanyingPeriod $period, Collection $socialIssues): AccompanyingPeriodLinkedWithSocialIssuesEntityInterface { - return new class($period, $socialIssues) implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterface { + return new class ($period, $socialIssues) implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterface { public Collection $socialIssues; public AccompanyingPeriod $period; diff --git a/src/Bundle/ChillPersonBundle/Tests/Controller/AccompanyingCourseApiControllerTest.php b/src/Bundle/ChillPersonBundle/Tests/Controller/AccompanyingCourseApiControllerTest.php index 30c8e2a4c..154dbd884 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Controller/AccompanyingCourseApiControllerTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Controller/AccompanyingCourseApiControllerTest.php @@ -342,7 +342,9 @@ final class AccompanyingCourseApiControllerTest extends WebTestCase // check that the person id is contained $participationsPersonsIds = array_map( - static function ($participation) { return $participation->person->id; }, + static function ($participation) { + return $participation->person->id; + }, $data->participations ); diff --git a/src/Bundle/ChillPersonBundle/Tests/Controller/PersonControllerUpdateTest.php b/src/Bundle/ChillPersonBundle/Tests/Controller/PersonControllerUpdateTest.php index 8b2f4c141..bb70866e2 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Controller/PersonControllerUpdateTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Controller/PersonControllerUpdateTest.php @@ -294,23 +294,49 @@ final class PersonControllerUpdateTest extends WebTestCase public function validTextFieldsProvider() { return [ - ['firstName', 'random Value', static function (Person $person) { return $person->getFirstName(); }], - ['lastName', 'random Value', static function (Person $person) { return $person->getLastName(); }], + ['firstName', 'random Value', static function (Person $person) { + return $person->getFirstName(); + }], + ['lastName', 'random Value', static function (Person $person) { + return $person->getLastName(); + }], // reminder: this value is capitalized - ['placeOfBirth', 'A PLACE', static function (Person $person) { return $person->getPlaceOfBirth(); }], - ['birthdate', '1980-12-15', static function (Person $person) { return $person->getBirthdate()->format('Y-m-d'); }], + ['placeOfBirth', 'A PLACE', static function (Person $person) { + return $person->getPlaceOfBirth(); + }], + ['birthdate', '1980-12-15', static function (Person $person) { + return $person->getBirthdate()->format('Y-m-d'); + }], // TODO test on phonenumber update // ['phonenumber', '+32123456789', static function (Person $person) { return $person->getPhonenumber(); }], - ['memo', 'jfkdlmq jkfldmsq jkmfdsq', static function (Person $person) { return $person->getMemo(); }], - ['countryOfBirth', 'BE', static function (Person $person) { return $person->getCountryOfBirth()->getCountryCode(); }], - ['nationality', 'FR', static function (Person $person) { return $person->getNationality()->getCountryCode(); }], - ['placeOfBirth', '', static function (Person $person) { return $person->getPlaceOfBirth(); }], - ['birthdate', '', static function (Person $person) { return $person->getBirthdate(); }], + ['memo', 'jfkdlmq jkfldmsq jkmfdsq', static function (Person $person) { + return $person->getMemo(); + }], + ['countryOfBirth', 'BE', static function (Person $person) { + return $person->getCountryOfBirth()->getCountryCode(); + }], + ['nationality', 'FR', static function (Person $person) { + return $person->getNationality()->getCountryCode(); + }], + ['placeOfBirth', '', static function (Person $person) { + return $person->getPlaceOfBirth(); + }], + ['birthdate', '', static function (Person $person) { + return $person->getBirthdate(); + }], //['phonenumber', '', static function (Person $person) { return $person->getPhonenumber(); }], - ['memo', '', static function (Person $person) { return $person->getMemo(); }], - ['countryOfBirth', null, static function (Person $person) { return $person->getCountryOfBirth(); }], - ['nationality', null, static function (Person $person) { return $person->getNationality(); }], - ['gender', Person::FEMALE_GENDER, static function (Person $person) { return $person->getGender(); }], + ['memo', '', static function (Person $person) { + return $person->getMemo(); + }], + ['countryOfBirth', null, static function (Person $person) { + return $person->getCountryOfBirth(); + }], + ['nationality', null, static function (Person $person) { + return $person->getNationality(); + }], + ['gender', Person::FEMALE_GENDER, static function (Person $person) { + return $person->getGender(); + }], ]; } diff --git a/src/Bundle/ChillPersonBundle/Tests/Controller/PersonControllerUpdateWithHiddenFieldsTest.php b/src/Bundle/ChillPersonBundle/Tests/Controller/PersonControllerUpdateWithHiddenFieldsTest.php index 7423b5388..403f89dd4 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Controller/PersonControllerUpdateWithHiddenFieldsTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Controller/PersonControllerUpdateWithHiddenFieldsTest.php @@ -197,12 +197,24 @@ final class PersonControllerUpdateWithHiddenFieldsTest extends WebTestCase public function validTextFieldsProvider() { return [ - ['firstName', 'random Value', static function (Person $person) { return $person->getFirstName(); }], - ['lastName', 'random Value', static function (Person $person) { return $person->getLastName(); }], - ['birthdate', '15-12-1980', static function (Person $person) { return $person->getBirthdate()->format('d-m-Y'); }], - ['memo', 'jfkdlmq jkfldmsq jkmfdsq', static function (Person $person) { return $person->getMemo(); }], - ['birthdate', '', static function (Person $person) { return $person->getBirthdate(); }], - ['gender', Person::FEMALE_GENDER, static function (Person $person) { return $person->getGender(); }], + ['firstName', 'random Value', static function (Person $person) { + return $person->getFirstName(); + }], + ['lastName', 'random Value', static function (Person $person) { + return $person->getLastName(); + }], + ['birthdate', '15-12-1980', static function (Person $person) { + return $person->getBirthdate()->format('d-m-Y'); + }], + ['memo', 'jfkdlmq jkfldmsq jkmfdsq', static function (Person $person) { + return $person->getMemo(); + }], + ['birthdate', '', static function (Person $person) { + return $person->getBirthdate(); + }], + ['gender', Person::FEMALE_GENDER, static function (Person $person) { + return $person->getGender(); + }], ]; } diff --git a/src/Bundle/ChillPersonBundle/Tests/Household/MembersEditorTest.php b/src/Bundle/ChillPersonBundle/Tests/Household/MembersEditorTest.php index 9c0e884a5..a58560bdd 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Household/MembersEditorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Household/MembersEditorTest.php @@ -111,7 +111,9 @@ final class MembersEditorTest extends TestCase $this->assertCount(1, $notSharing); $this->assertCount(1, $sharings); - $getPerson = static function (HouseholdMember $m) { return $m->getPerson(); }; + $getPerson = static function (HouseholdMember $m) { + return $m->getPerson(); + }; $this->assertContains($person, $notSharing->map($getPerson)); } @@ -151,7 +153,9 @@ final class MembersEditorTest extends TestCase $this->assertCount(1, $notSharing); $this->assertCount(0, $sharings); - $getPerson = static function (HouseholdMember $m) { return $m->getPerson(); }; + $getPerson = static function (HouseholdMember $m) { + return $m->getPerson(); + }; $this->assertContains($person, $notSharing->map($getPerson)); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Serializer/Normalizer/RelationshipDocGenNormalizerTest.php b/src/Bundle/ChillPersonBundle/Tests/Serializer/Normalizer/RelationshipDocGenNormalizerTest.php index a6bab133a..05d2eace1 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Serializer/Normalizer/RelationshipDocGenNormalizerTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Serializer/Normalizer/RelationshipDocGenNormalizerTest.php @@ -118,7 +118,9 @@ final class RelationshipDocGenNormalizerTest extends TestCase { $translatableStringHelper = $this->prophesize(TranslatableStringHelperInterface::class); $translatableStringHelper->localize(Argument::type('array'))->will( - static function ($args) { return $args[0][array_keys($args[0])[0]]; } + static function ($args) { + return $args[0][array_keys($args[0])[0]]; + } ); $normalizer = new RelationshipDocGenNormalizer( diff --git a/src/Bundle/ChillReportBundle/Tests/Timeline/TimelineProviderTest.php b/src/Bundle/ChillReportBundle/Tests/Timeline/TimelineProviderTest.php index e76688357..6091c51a8 100644 --- a/src/Bundle/ChillReportBundle/Tests/Timeline/TimelineProviderTest.php +++ b/src/Bundle/ChillReportBundle/Tests/Timeline/TimelineProviderTest.php @@ -67,7 +67,9 @@ final class TimelineProviderTest extends WebTestCase self::$em ->getRepository(\Chill\MainBundle\Entity\Scope::class) ->findAll(), - static function (Scope $scope) { return $scope->getName()['en'] === 'social'; } + static function (Scope $scope) { + return $scope->getName()['en'] === 'social'; + } ); $report = (new Report()) From 9cf06c147fdbc7138bd173f0d58cfd19b4231925 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 2 Nov 2022 14:25:52 +0100 Subject: [PATCH 45/73] Feature: [export][activity] Add an aggregator to group by sent / received --- .../Aggregator/SentReceivedAggregator.php | 28 +++++++++++++------ .../translations/messages.fr.yml | 10 +++++-- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/SentReceivedAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/SentReceivedAggregator.php index 24000096f..3396faa80 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/SentReceivedAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/SentReceivedAggregator.php @@ -16,15 +16,27 @@ use Chill\MainBundle\Export\AggregatorInterface; use Doctrine\ORM\QueryBuilder; use LogicException; use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Contracts\Translation\TranslatorInterface; class SentReceivedAggregator implements AggregatorInterface { + private TranslatorInterface $translator; + + /** + * @param TranslatorInterface $translator + */ + public function __construct(TranslatorInterface $translator) + { + $this->translator = $translator; + } + + public function addRole(): ?string { return null; } - public function alterQuery(QueryBuilder $qb, $data) + public function alterQuery(QueryBuilder $qb, $data): void { $qb->addSelect('activity.sentReceived AS activity_sentreceived_aggregator') ->addGroupBy('activity_sentreceived_aggregator'); @@ -35,16 +47,16 @@ class SentReceivedAggregator implements AggregatorInterface return Declarations::ACTIVITY; } - public function buildForm(FormBuilderInterface $builder) + public function buildForm(FormBuilderInterface $builder): void { // No form needed } - public function getLabels($key, array $values, $data) + public function getLabels($key, array $values, $data): callable { - return static function ($value): string { + return function (?string $value): string { if ('_header' === $value) { - return ''; + return 'export.aggregator.activity.by_sent_received.Sent or received'; } switch ($value) { @@ -52,10 +64,10 @@ class SentReceivedAggregator implements AggregatorInterface return ''; case 'sent': - return 'is sent'; + return $this->translator->trans('export.aggregator.activity.by_sent_received.is sent'); case 'received': - return 'is received'; + return $this->translator->trans('export.aggregator.activity.by_sent_received.is received'); default: throw new LogicException(sprintf('The value %s is not valid', $value)); @@ -70,6 +82,6 @@ class SentReceivedAggregator implements AggregatorInterface public function getTitle(): string { - return 'Group activity by sentreceived'; + return 'export.aggregator.activity.by_sent_received.Group activity by sentreceived'; } } diff --git a/src/Bundle/ChillActivityBundle/translations/messages.fr.yml b/src/Bundle/ChillActivityBundle/translations/messages.fr.yml index 1d5e7f1f2..c3d56185a 100644 --- a/src/Bundle/ChillActivityBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillActivityBundle/translations/messages.fr.yml @@ -260,8 +260,6 @@ activity is not emergency: l'activité n'est pas urgente Filter activity by sentreceived: Filtrer les activités par envoyé/reçu 'Filtered activity by sentreceived: only %sentreceived%': "Filtré par envoyé/reçu: uniquement %sentreceived%" Accepted sentreceived: '' -is sent: envoyé -is received: reçu Filter activity by linked socialaction: Filtrer les activités par action liée 'Filtered activity by linked socialaction: only %actions%': "Filtré par action liée: uniquement %actions%" Filter activity by linked socialissue: Filtrer les activités par problématique liée @@ -280,7 +278,6 @@ Accepted userscope: Services Filter acp which has no activity: Filtrer les parcours qui n’ont pas d’activité Filtered acp which has no activities: Filtrer les parcours sans activité associée Group acp by activity number: Grouper les parcours par nombre d’activité -Group activity by sentreceived: Grouper les activités par envoyé / reçu #aggregators Activity type: Type d'activité @@ -337,3 +334,10 @@ export: by_usersscope: Filter by users scope: Filtrer les activités par services d'au moins un utilisateur participant 'Filtered activity by users scope: only %scopes%': 'Filtré par service d''au moins un utilisateur participant: seulement %scopes%' + aggregator: + activity: + by_sent_received: + Sent or received: Envoyé ou reçu + is sent: envoyé + is received: reçu + Group activity by sentreceived: Grouper les activités par envoyé / reçu From 1bf7f9945cac5bbbcbe3f5b73fabe02ff0ea36dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 2 Nov 2022 14:39:36 +0100 Subject: [PATCH 46/73] Feature: [export][action] Filter 'active' action: which does not have an end date --- .../Export/Filter/SocialWorkFilters/CurrentActionFilter.php | 6 +++--- .../config/services/exports_social_actions.yaml | 6 +++++- src/Bundle/ChillPersonBundle/translations/messages.fr.yml | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/CurrentActionFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/CurrentActionFilter.php index cbf958182..cbdb64d8d 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/CurrentActionFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/CurrentActionFilter.php @@ -23,9 +23,9 @@ class CurrentActionFilter implements FilterInterface return null; } - public function alterQuery(QueryBuilder $qb, $data) + public function alterQuery(QueryBuilder $qb, $data): void { - $qb->andWhere('acpw.startDate IS NULL'); + $qb->andWhere('acpw.endDate IS NULL'); } public function applyOn(): string @@ -33,7 +33,7 @@ class CurrentActionFilter implements FilterInterface return Declarations::SOCIAL_WORK_ACTION_TYPE; } - public function buildForm(FormBuilderInterface $builder) + public function buildForm(FormBuilderInterface $builder): void { //no form } diff --git a/src/Bundle/ChillPersonBundle/config/services/exports_social_actions.yaml b/src/Bundle/ChillPersonBundle/config/services/exports_social_actions.yaml index 28401c3b3..8b2d9d2b8 100644 --- a/src/Bundle/ChillPersonBundle/config/services/exports_social_actions.yaml +++ b/src/Bundle/ChillPersonBundle/config/services/exports_social_actions.yaml @@ -38,6 +38,10 @@ services: - { name: chill.export_filter, alias: social_work_actions_treatingagent_filter } Chill\PersonBundle\Export\Filter\SocialWorkFilters\CurrentActionFilter: + autowire: true + autoconfigure: true + tags: + - { name: chill.export_filter, alias: social_work_actions_current_filter } ## AGGREGATORS chill.person.export.aggregator_action_type: @@ -89,4 +93,4 @@ services: tags: - { name: chill.export_aggregator, alias: social_work_actions_goal_result_aggregator } - Chill\PersonBundle\Export\Aggregator\SocialWorkAggregators\CurrentActionAggregator: \ No newline at end of file + Chill\PersonBundle\Export\Aggregator\SocialWorkAggregators\CurrentActionAggregator: diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index 8d5e055cd..df4b0fab2 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -576,7 +576,7 @@ Filter by creator job: Filtrer les parcours par métier du créateur Group by creator job: Grouper les parcours par métier du créateur Filter by current actions: Filtrer les actions en cours -Filtered by current action: Filtré les actions en cours +Filtered by current action: 'Filtré: uniquement les actions en cours (sans date de fin)' Group by current actions: Grouper les actions en cours Current action: Actions en cours Not current action: Actions terminées From cda25d3459d05d5f7dccead62a22f9c26416e5ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 2 Nov 2022 14:40:17 +0100 Subject: [PATCH 47/73] DX: more cs --- .../Export/Aggregator/SentReceivedAggregator.php | 4 ---- .../Tests/Security/Resolver/DefaultScopeResolverTest.php | 4 ++-- .../Tests/Security/Resolver/ScopeResolverDispatcherTest.php | 4 ++-- ...mpanyingPeriodSocialIssueConsistencyEntityListenerTest.php | 2 +- 4 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/SentReceivedAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/SentReceivedAggregator.php index 3396faa80..7d77a7c69 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/SentReceivedAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/SentReceivedAggregator.php @@ -22,15 +22,11 @@ class SentReceivedAggregator implements AggregatorInterface { private TranslatorInterface $translator; - /** - * @param TranslatorInterface $translator - */ public function __construct(TranslatorInterface $translator) { $this->translator = $translator; } - public function addRole(): ?string { return null; diff --git a/src/Bundle/ChillMainBundle/Tests/Security/Resolver/DefaultScopeResolverTest.php b/src/Bundle/ChillMainBundle/Tests/Security/Resolver/DefaultScopeResolverTest.php index 30d50fca5..7e6710a20 100644 --- a/src/Bundle/ChillMainBundle/Tests/Security/Resolver/DefaultScopeResolverTest.php +++ b/src/Bundle/ChillMainBundle/Tests/Security/Resolver/DefaultScopeResolverTest.php @@ -33,7 +33,7 @@ final class DefaultScopeResolverTest extends TestCase public function testHasScopeInterface() { $scope = new Scope(); - $entity = new class ($scope) implements HasScopeInterface { + $entity = new class($scope) implements HasScopeInterface { public function __construct(Scope $scope) { $this->scope = $scope; @@ -52,7 +52,7 @@ final class DefaultScopeResolverTest extends TestCase public function testHasScopesInterface() { - $entity = new class ($scopeA = new Scope(), $scopeB = new Scope()) implements HasScopesInterface { + $entity = new class($scopeA = new Scope(), $scopeB = new Scope()) implements HasScopesInterface { public function __construct(Scope $scopeA, Scope $scopeB) { $this->scopes = [$scopeA, $scopeB]; diff --git a/src/Bundle/ChillMainBundle/Tests/Security/Resolver/ScopeResolverDispatcherTest.php b/src/Bundle/ChillMainBundle/Tests/Security/Resolver/ScopeResolverDispatcherTest.php index 31e5229bb..e2c5d3879 100644 --- a/src/Bundle/ChillMainBundle/Tests/Security/Resolver/ScopeResolverDispatcherTest.php +++ b/src/Bundle/ChillMainBundle/Tests/Security/Resolver/ScopeResolverDispatcherTest.php @@ -34,7 +34,7 @@ final class ScopeResolverDispatcherTest extends TestCase public function testHasScopeInterface() { $scope = new Scope(); - $entity = new class ($scope) implements HasScopeInterface { + $entity = new class($scope) implements HasScopeInterface { public function __construct(Scope $scope) { $this->scope = $scope; @@ -52,7 +52,7 @@ final class ScopeResolverDispatcherTest extends TestCase public function testHasScopesInterface() { - $entity = new class ($scopeA = new Scope(), $scopeB = new Scope()) implements HasScopesInterface { + $entity = new class($scopeA = new Scope(), $scopeB = new Scope()) implements HasScopesInterface { public function __construct(Scope $scopeA, Scope $scopeB) { $this->scopes = [$scopeA, $scopeB]; diff --git a/src/Bundle/ChillPersonBundle/Tests/AccompanyingPeriod/SocialIssueConsistency/AccompanyingPeriodSocialIssueConsistencyEntityListenerTest.php b/src/Bundle/ChillPersonBundle/Tests/AccompanyingPeriod/SocialIssueConsistency/AccompanyingPeriodSocialIssueConsistencyEntityListenerTest.php index 8ac092647..0c8c3e0c3 100644 --- a/src/Bundle/ChillPersonBundle/Tests/AccompanyingPeriod/SocialIssueConsistency/AccompanyingPeriodSocialIssueConsistencyEntityListenerTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/AccompanyingPeriod/SocialIssueConsistency/AccompanyingPeriodSocialIssueConsistencyEntityListenerTest.php @@ -114,7 +114,7 @@ final class AccompanyingPeriodSocialIssueConsistencyEntityListenerTest extends T protected function generateClass(AccompanyingPeriod $period, Collection $socialIssues): AccompanyingPeriodLinkedWithSocialIssuesEntityInterface { - return new class ($period, $socialIssues) implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterface { + return new class($period, $socialIssues) implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterface { public Collection $socialIssues; public AccompanyingPeriod $period; From e07d2ab467933f15194b5aa7b3c9e7c6e8716cd5 Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Wed, 2 Nov 2022 14:41:25 +0100 Subject: [PATCH 48/73] voter aside activity created --- .../ChillAsideActivityExtension.php | 1 + .../src/Security/AsideActivityVoter.php | 79 +++++++++++++++++++ .../src/config/services/security.yaml | 7 ++ 3 files changed, 87 insertions(+) create mode 100644 src/Bundle/ChillAsideActivityBundle/src/Security/AsideActivityVoter.php create mode 100644 src/Bundle/ChillAsideActivityBundle/src/config/services/security.yaml diff --git a/src/Bundle/ChillAsideActivityBundle/src/DependencyInjection/ChillAsideActivityExtension.php b/src/Bundle/ChillAsideActivityBundle/src/DependencyInjection/ChillAsideActivityExtension.php index ef6c5d69b..927de1845 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/DependencyInjection/ChillAsideActivityExtension.php +++ b/src/Bundle/ChillAsideActivityBundle/src/DependencyInjection/ChillAsideActivityExtension.php @@ -30,6 +30,7 @@ final class ChillAsideActivityExtension extends Extension implements PrependExte $loader->load('services.yaml'); $loader->load('services/form.yaml'); $loader->load('services/menu.yaml'); + $loader->load('services/security.yaml'); $loader->load('services/export.yaml'); } diff --git a/src/Bundle/ChillAsideActivityBundle/src/Security/AsideActivityVoter.php b/src/Bundle/ChillAsideActivityBundle/src/Security/AsideActivityVoter.php new file mode 100644 index 000000000..6b308b5f2 --- /dev/null +++ b/src/Bundle/ChillAsideActivityBundle/src/Security/AsideActivityVoter.php @@ -0,0 +1,79 @@ +voterHelper = $voterHelperFactory + ->generate(self::class) + ->addCheckFor(Center::class, [self::STATS]) + ->build(); + } + + /** + * @return string[] + */ + public function getRoles(): array + { + return $this->getAttributes(); + } + + /** + * @return string[][] + */ + public function getRolesWithHierarchy(): array + { + return ['Aside activity' => $this->getRoles()]; + } + + /** + * @return string[] + */ + public function getRolesWithoutScope(): array + { + return $this->getAttributes(); + } + + protected function supports($attribute, $subject) + { + return $this->voterHelper->supports($attribute, $subject); + } + + protected function voteOnAttribute($attribute, $subject, TokenInterface $token): bool + { + if (!$token->getUser() instanceof User) { + return false; + } + + return $this->voterHelper->voteOnAttribute($attribute, $subject, $token); + } + + private function getAttributes(): array + { + return [self::STATS]; + } +} diff --git a/src/Bundle/ChillAsideActivityBundle/src/config/services/security.yaml b/src/Bundle/ChillAsideActivityBundle/src/config/services/security.yaml new file mode 100644 index 000000000..eb3327959 --- /dev/null +++ b/src/Bundle/ChillAsideActivityBundle/src/config/services/security.yaml @@ -0,0 +1,7 @@ +services: + Chill\AsideActivityBundle\Security\AsideActivityVoter: + autowire: true + autoconfigure: true + tags: + - { name: security.voter } + - { name: chill.role } \ No newline at end of file From 9c3533d1060f02385ac2c89aa6db9324a4f55f1b Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Wed, 2 Nov 2022 14:42:44 +0100 Subject: [PATCH 49/73] count aside activity --- .../src/Export/Export/CountAsideActivity.php | 8 +++----- .../src/translations/messages.fr.yml | 1 + 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Export/CountAsideActivity.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Export/CountAsideActivity.php index d1d69f0c8..304e0a283 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Export/Export/CountAsideActivity.php +++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Export/CountAsideActivity.php @@ -13,6 +13,7 @@ namespace Chill\AsideActivityBundle\Export\Export; use Chill\AsideActivityBundle\Export\Declarations; use Chill\AsideActivityBundle\Repository\AsideActivityRepository; +use Chill\AsideActivityBundle\Security\AsideActivityVoter; use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\GroupedExportInterface; @@ -32,7 +33,6 @@ class CountAsideActivity implements ExportInterface, GroupedExportInterface public function buildForm(FormBuilderInterface $builder) { - // TODO: Implement buildForm() method. } public function getAllowedFormattersTypes(): array @@ -88,16 +88,14 @@ class CountAsideActivity implements ExportInterface, GroupedExportInterface { $qb = $this->repository->createQueryBuilder('aside'); - $qb->andWhere(); - - $qb->select('COUNT() AS export_result'); + $qb->select('COUNT(DISTINCT aside.id) AS export_result'); return $qb; } public function requiredRole(): string { - return ''; + return AsideActivityVoter::STATS; } public function supportsModifiers(): array diff --git a/src/Bundle/ChillAsideActivityBundle/src/translations/messages.fr.yml b/src/Bundle/ChillAsideActivityBundle/src/translations/messages.fr.yml index 5b298ed68..ed54fd488 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/translations/messages.fr.yml +++ b/src/Bundle/ChillAsideActivityBundle/src/translations/messages.fr.yml @@ -168,6 +168,7 @@ Aside activity type configuration: Configuration des categories d'activités ann Aside activity configuration: Configuration des activités annexes # exports +Exports of aside activities: Exports des activités annexes Count aside activities: Nombre d'activités annexes Count aside activities by various parameters.: Compte le nombre d'activités annexes selon divers critères Filter by aside activity date: Filtrer les activités annexes par date From 63d38e35f729d1646b93ed141a2bff7d257872e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 2 Nov 2022 14:55:12 +0100 Subject: [PATCH 50/73] Feature: [export][work] Group by current action --- .../CurrentActionAggregator.php | 24 ++++++++++++------- .../services/exports_social_actions.yaml | 4 ++++ .../translations/messages.fr.yml | 9 ++++--- 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/CurrentActionAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/CurrentActionAggregator.php index a77838167..58fcb2874 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/CurrentActionAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/CurrentActionAggregator.php @@ -16,19 +16,27 @@ use Chill\PersonBundle\Export\Declarations; use Doctrine\ORM\QueryBuilder; use LogicException; use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Contracts\Translation\TranslatorInterface; class CurrentActionAggregator implements AggregatorInterface { + private TranslatorInterface $translator; + + public function __construct(TranslatorInterface $translator) + { + $this->translator = $translator; + } + public function addRole(): ?string { return null; } - public function alterQuery(QueryBuilder $qb, $data) + public function alterQuery(QueryBuilder $qb, $data): void { $qb ->addSelect(' - (CASE true WHEN acpw.startDate IS NULL ELSE false END) + (CASE WHEN acpw.endDate IS NULL THEN true ELSE false END) AS acpw_current_action_aggregator ') ->addGroupBy('acpw_current_action_aggregator'); @@ -39,24 +47,24 @@ class CurrentActionAggregator implements AggregatorInterface return Declarations::SOCIAL_WORK_ACTION_TYPE; } - public function buildForm(FormBuilderInterface $builder) + public function buildForm(FormBuilderInterface $builder): void { // No form needed } public function getLabels($key, array $values, $data) { - return static function ($value): string { + return function ($value): string { if ('_header' === $value) { - return ''; + return 'export.aggregator.course_work.by_current_action.Current action ?'; } switch ($value) { case true: - return 'Current action'; + return $this->translator->trans('export.aggregator.course_work.by_current_action.Current action'); case false: - return 'Not current action'; + return $this->translator->trans('export.aggregator.course_work.by_current_action.Not current action'); default: throw new LogicException(sprintf('The value %s is not valid', $value)); @@ -71,6 +79,6 @@ class CurrentActionAggregator implements AggregatorInterface public function getTitle(): string { - return 'Group by current actions'; + return 'export.aggregator.course_work.by_current_action.Group by current actions'; } } diff --git a/src/Bundle/ChillPersonBundle/config/services/exports_social_actions.yaml b/src/Bundle/ChillPersonBundle/config/services/exports_social_actions.yaml index 8b2d9d2b8..13c51762f 100644 --- a/src/Bundle/ChillPersonBundle/config/services/exports_social_actions.yaml +++ b/src/Bundle/ChillPersonBundle/config/services/exports_social_actions.yaml @@ -94,3 +94,7 @@ services: - { name: chill.export_aggregator, alias: social_work_actions_goal_result_aggregator } Chill\PersonBundle\Export\Aggregator\SocialWorkAggregators\CurrentActionAggregator: + autowire: true + autoconfigure: true + tags: + - { name: chill.export_aggregator, alias: social_work_actions_current_aggregator } diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index df4b0fab2..0833f57e3 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -577,9 +577,6 @@ Group by creator job: Grouper les parcours par métier du créateur Filter by current actions: Filtrer les actions en cours Filtered by current action: 'Filtré: uniquement les actions en cours (sans date de fin)' -Group by current actions: Grouper les actions en cours -Current action: Actions en cours -Not current action: Actions terminées Filter by start date evaluations: Filtrer les évaluations par date de début Filter by end date evaluations: Filtrer les évaluations par date de fin start period date: Date de début de la période @@ -1015,6 +1012,12 @@ export: Calc date: Date de calcul de la composition du ménage by_number_of_action: Number of actions: Nombre d'actions + course_work: + by_current_action: + Current action ?: Action en cours ? + Group by current actions: Grouper les actions en cours + Current action: Action en cours + Not current action: Action terminée filter: course: by_user_scope: From d8104c4443b948d0433ff438f88279c3ebe33c57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 2 Nov 2022 15:08:04 +0100 Subject: [PATCH 51/73] DX: prevent collision in parameter name in evaluation / by start date filter --- .../EvaluationFilters/ByStartDateFilter.php | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByStartDateFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByStartDateFilter.php index e21a1ea68..f12b5e805 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByStartDateFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByStartDateFilter.php @@ -14,7 +14,7 @@ namespace Chill\PersonBundle\Export\Filter\EvaluationFilters; use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Form\Type\ChillDateType; use Chill\PersonBundle\Export\Declarations; -use DateTime; +use DateTimeImmutable; use Doctrine\DBAL\Types\Types; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; @@ -26,12 +26,12 @@ class ByStartDateFilter implements FilterInterface return null; } - public function alterQuery(QueryBuilder $qb, $data) + public function alterQuery(QueryBuilder $qb, $data): void { $qb - ->andWhere('workeval.startDate IS BETWEEN :start_date and :end_date') - ->setParameter('start_date', $data['start_date'], Types::DATE_IMMUTABLE) - ->setParameter('end_date', $data['end_date'], Types::DATE_IMMUTABLE); + ->andWhere('workeval.startDate BETWEEN :work_eval_by_start_date_start_date and :work_eval_by_start_date_end_date') + ->setParameter('work_eval_by_start_date_start_date', $data['start_date'], Types::DATE_IMMUTABLE) + ->setParameter('work_eval_by_start_date_end_date', $data['end_date'], Types::DATE_IMMUTABLE); } public function applyOn(): string @@ -39,16 +39,18 @@ class ByStartDateFilter implements FilterInterface return Declarations::EVAL_TYPE; } - public function buildForm(FormBuilderInterface $builder) + public function buildForm(FormBuilderInterface $builder): void { $builder ->add('start_date', ChillDateType::class, [ 'label' => 'start period date', - 'data' => new DateTime(), + 'data' => new DateTimeImmutable('1 year ago'), + 'input' => 'datetime_immutable', ]) ->add('end_date', ChillDateType::class, [ 'label' => 'end period date', - 'data' => new DateTime(), + 'data' => new DateTimeImmutable(), + 'input' => 'datetime_immutable', ]); } From 2d40c38c88fed58648f713bbd47a2dac1ca22784 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 2 Nov 2022 15:56:48 +0100 Subject: [PATCH 52/73] Feature: [export][evaluation] add by end date filter --- .../EvaluationFilters/ByEndDateFilter.php | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByEndDateFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByEndDateFilter.php index 5c509d50c..f828da109 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByEndDateFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByEndDateFilter.php @@ -14,7 +14,7 @@ namespace Chill\PersonBundle\Export\Filter\EvaluationFilters; use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Form\Type\ChillDateType; use Chill\PersonBundle\Export\Declarations; -use DateTime; +use DateTimeImmutable; use Doctrine\DBAL\Types\Types; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; @@ -26,12 +26,12 @@ class ByEndDateFilter implements FilterInterface return null; } - public function alterQuery(QueryBuilder $qb, $data) + public function alterQuery(QueryBuilder $qb, $data): void { $qb - ->andWhere('workeval.endDate IS BETWEEN :start_date and :end_date') - ->setParameter('start_date', $data['start_date'], Types::DATE_IMMUTABLE) - ->setParameter('end_date', $data['end_date'], Types::DATE_IMMUTABLE); + ->andWhere('workeval.endDate BETWEEN :work_eval_by_end_date_start_date and :work_eval_by_end_date_end_date') + ->setParameter('work_eval_by_end_date_start_date', $data['start_date'], Types::DATE_IMMUTABLE) + ->setParameter('work_eval_by_end_date_end_date', $data['end_date'], Types::DATE_IMMUTABLE); } public function applyOn(): string @@ -39,16 +39,18 @@ class ByEndDateFilter implements FilterInterface return Declarations::EVAL_TYPE; } - public function buildForm(FormBuilderInterface $builder) + public function buildForm(FormBuilderInterface $builder): void { $builder ->add('start_date', ChillDateType::class, [ 'label' => 'start period date', - 'data' => new DateTime(), + 'data' => new DateTimeImmutable('1 year ago'), + 'input' => 'datetime_immutable', ]) ->add('end_date', ChillDateType::class, [ 'label' => 'end period date', - 'data' => new DateTime(), + 'data' => new DateTimeImmutable(), + 'input' => 'datetime_immutable', ]); } From f0b2ec13486f971ec4e62b0fc3418380a547b85d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 2 Nov 2022 16:17:10 +0100 Subject: [PATCH 53/73] Feature: [export][evaluation] filter by current evaluation (without any end date) --- .../Filter/EvaluationFilters/CurrentEvaluationsFilter.php | 4 ++-- src/Bundle/ChillPersonBundle/translations/messages.fr.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/CurrentEvaluationsFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/CurrentEvaluationsFilter.php index 50c95600e..140f6c3cb 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/CurrentEvaluationsFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/CurrentEvaluationsFilter.php @@ -23,7 +23,7 @@ class CurrentEvaluationsFilter implements FilterInterface return null; } - public function alterQuery(QueryBuilder $qb, $data) + public function alterQuery(QueryBuilder $qb, $data): void { $qb->andWhere('workeval.endDate IS NULL'); } @@ -33,7 +33,7 @@ class CurrentEvaluationsFilter implements FilterInterface return Declarations::EVAL_TYPE; } - public function buildForm(FormBuilderInterface $builder) + public function buildForm(FormBuilderInterface $builder): void { //no form needed } diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index 0833f57e3..3764325af 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -584,7 +584,7 @@ end period date: Date de fin de la période "Filtered by start date: between %start_date% and %end_date%": "Filtré par la date de début: comprise entre %start_date% et %end_date%" "Filtered by end date: between %start_date% and %end_date%": "Filtré par la date de fin: comprise entre %start_date% et %end_date%" Filter by current evaluations: Filtrer les évaluations en cours -"Filtered by current evaluations": "Filtré selon les évaluations en cours" +"Filtered by current evaluations": "Filtré: uniquement les évaluations en cours" Group by start date evaluations: Grouper les évaluations par semaine/mois/année de la date de début Group by end date evaluations: Grouper les évaluations par semaine/mois/année de la date de fin Group by max date evaluations: Grouper les évaluations par semaine/mois/année de la date d'échéance From 4ca10ce38d11f991a0d80e7d264253fbe2374097 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 2 Nov 2022 16:26:56 +0100 Subject: [PATCH 54/73] Feature: [export][evaluation] Adding group by evaluation having end date --- .../HavingEndDateAggregator.php | 23 +++++++++++-------- .../config/services/exports_evaluation.yaml | 7 ++++++ .../translations/messages.fr.yml | 9 +++++--- 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/HavingEndDateAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/HavingEndDateAggregator.php index af801b74d..e33bb326f 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/HavingEndDateAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/HavingEndDateAggregator.php @@ -16,9 +16,17 @@ use Chill\PersonBundle\Export\Declarations; use Doctrine\ORM\QueryBuilder; use LogicException; use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Contracts\Translation\TranslatorInterface; class HavingEndDateAggregator implements AggregatorInterface { + private TranslatorInterface $translator; + + public function __construct(TranslatorInterface $translator) + { + $this->translator = $translator; + } + public function addRole(): ?string { return null; @@ -27,10 +35,7 @@ class HavingEndDateAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { $qb - ->addSelect(' - CASE true WHEN workeval.endDAte IS NULL ELSE false END - AS eval_enddate_aggregator - ') + ->addSelect('CASE WHEN workeval.endDate IS NULL THEN true ELSE false END AS eval_enddate_aggregator') ->addGroupBy('eval_enddate_aggregator'); } @@ -46,17 +51,17 @@ class HavingEndDateAggregator implements AggregatorInterface public function getLabels($key, array $values, $data) { - return static function ($value): string { + return function ($value): string { if ('_header' === $value) { - return ''; + return 'export.aggregator.eval.by_end_date.Has end date ?'; } switch ($value) { case true: - return 'enddate is specified'; + return $this->translator->trans('export.aggregator.eval.by_end_date.enddate is specified'); case false: - return 'enddate is not specified'; + return $this->translator->trans('export.aggregator.eval.by_end_date.enddate is not specified'); default: throw new LogicException(sprintf('The value %s is not valid', $value)); @@ -71,6 +76,6 @@ class HavingEndDateAggregator implements AggregatorInterface public function getTitle(): string { - return 'Group evaluations having end date'; + return 'export.aggregator.eval.by_end_date.Group evaluations having end date'; } } diff --git a/src/Bundle/ChillPersonBundle/config/services/exports_evaluation.yaml b/src/Bundle/ChillPersonBundle/config/services/exports_evaluation.yaml index a389c1f80..f58b7112b 100644 --- a/src/Bundle/ChillPersonBundle/config/services/exports_evaluation.yaml +++ b/src/Bundle/ChillPersonBundle/config/services/exports_evaluation.yaml @@ -73,3 +73,10 @@ services: autoconfigure: true tags: - { name: chill.export_aggregator, alias: evaluation_bymaxdate_aggregator } + + Chill\PersonBundle\Export\Aggregator\EvaluationAggregators\HavingEndDateAggregator: + autowire: true + autoconfigure: true + tags: + - { name: chill.export_aggregator, alias: evaluation_byend_date_aggregator } + diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index 3764325af..89808d900 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -626,9 +626,6 @@ maxdate is specified: la date d'échéance est spécifiée maxdate is not specified: la date d'échéance n'est pas spécifiée "Filtered by maxdate: only %choice%": "Filtré par date d'échéance: uniquement si %choice%" -Group evaluations having end date: Grouper les évaluations qui ont une date de fin -enddate is specified: la date de fin est spécifiée -enddate is not specified: la date de fin n'est pas spécifiée ## household filters/aggr Filter by composition: Filtrer les ménages par composition familiale @@ -1018,6 +1015,12 @@ export: Group by current actions: Grouper les actions en cours Current action: Action en cours Not current action: Action terminée + eval: + by_end_date: + Has end date ?: Évaluation en cours ? + Group evaluations having end date: Grouper les évaluations en cours (avec ou sans date de fin) + enddate is specified: la date de fin est spécifiée + enddate is not specified: la date de fin n'est pas spécifiée filter: course: by_user_scope: From 0255f12fa913c39ce5efbd4da9a63b23d5cf7061 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 2 Nov 2022 16:42:23 +0100 Subject: [PATCH 55/73] Feature: [export][evaluation] Group evaluation by start date --- .../ByStartDateAggregator.php | 54 +++++++++++++++++-- .../translations/messages.fr.yml | 4 +- 2 files changed, 53 insertions(+), 5 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByStartDateAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByStartDateAggregator.php index e64245d53..a41fe2451 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByStartDateAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByStartDateAggregator.php @@ -14,10 +14,23 @@ namespace Chill\PersonBundle\Export\Aggregator\EvaluationAggregators; use Chill\MainBundle\Export\AggregatorInterface; use Chill\PersonBundle\Export\Declarations; use Doctrine\ORM\QueryBuilder; +use LogicException; +use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Contracts\Translation\TranslatorInterface; class ByStartDateAggregator implements AggregatorInterface { + private const CHOICES = [ + 'by month' => 'month', + 'by week' => 'week', + 'by year' => 'year', + ]; + + private const DEFAULT_CHOICE = 'year'; + + private TranslatorInterface $translator; + public function addRole(): ?string { return null; @@ -25,8 +38,29 @@ class ByStartDateAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { - $qb->addSelect('AS eval_by_start_date_aggregator') - ->addGroupBy('eval_by_start_date_aggregator'); + switch ($data['frequency']) { + case 'month': + $fmt = 'YYYY-MM'; + + break; + + case 'week': + $fmt = 'YYYY-IW'; + + break; + + case 'year': + $fmt = 'YYYY'; + + break; + + default: + throw new LogicException(sprintf("The frequency data '%s' is invalid.", $data['frequency'])); + } + + $qb->addSelect(sprintf("TO_CHAR(workeval.startDate, '%s') AS eval_by_start_date_aggregator", $fmt)); + $qb->addGroupBy(' eval_by_start_date_aggregator'); + $qb->addOrderBy(' eval_by_start_date_aggregator', 'ASC'); } public function applyOn(): string @@ -36,15 +70,27 @@ class ByStartDateAggregator implements AggregatorInterface public function buildForm(FormBuilderInterface $builder) { - // No form needed + $builder->add('frequency', ChoiceType::class, [ + 'choices' => self::CHOICES, + 'multiple' => false, + 'expanded' => true, + 'empty_data' => self::DEFAULT_CHOICE, + 'data' => self::DEFAULT_CHOICE, + ]); } public function getLabels($key, array $values, $data) { return static function ($value): string { if ('_header' === $value) { + return 'export.aggregator.eval.by_start_date_period.Start date period'; + } + + if (null === $value) { return ''; } + + return $value; }; } @@ -55,6 +101,6 @@ class ByStartDateAggregator implements AggregatorInterface public function getTitle(): string { - return 'Group by start date evaluations'; + return 'export.aggregator.eval.by_start_date_period.Group by start date evaluations'; } } diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index 89808d900..9f6eda592 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -585,7 +585,6 @@ end period date: Date de fin de la période "Filtered by end date: between %start_date% and %end_date%": "Filtré par la date de fin: comprise entre %start_date% et %end_date%" Filter by current evaluations: Filtrer les évaluations en cours "Filtered by current evaluations": "Filtré: uniquement les évaluations en cours" -Group by start date evaluations: Grouper les évaluations par semaine/mois/année de la date de début Group by end date evaluations: Grouper les évaluations par semaine/mois/année de la date de fin Group by max date evaluations: Grouper les évaluations par semaine/mois/année de la date d'échéance @@ -1021,6 +1020,9 @@ export: Group evaluations having end date: Grouper les évaluations en cours (avec ou sans date de fin) enddate is specified: la date de fin est spécifiée enddate is not specified: la date de fin n'est pas spécifiée + by_start_date_period: + Start date period: Début (par periode) + Group by start date evaluations: Grouper les évaluations par semaine/mois/année de la date de début filter: course: by_user_scope: From 481be3299756e084951b20f382fefda3841e56aa Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Wed, 2 Nov 2022 20:02:46 +0100 Subject: [PATCH 56/73] filters and aggregator aside activity --- .../Aggregator/ByActivityTypeAggregator.php | 32 ++++++- .../src/Export/Export/CountAsideActivity.php | 6 +- .../Export/Filter/ByActivityTypeFilter.php | 57 ++++++++++-- .../src/Export/Filter/ByDateFilter.php | 92 +++++++++++++++++-- .../AsideActivityCategoryRepository.php | 5 + .../src/config/services/export.yaml | 18 ++++ .../src/translations/messages.fr.yml | 22 +++-- 7 files changed, 202 insertions(+), 30 deletions(-) diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Aggregator/ByActivityTypeAggregator.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Aggregator/ByActivityTypeAggregator.php index 488885bde..6c91c9336 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Export/Aggregator/ByActivityTypeAggregator.php +++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Aggregator/ByActivityTypeAggregator.php @@ -12,12 +12,24 @@ declare(strict_types=1); namespace Chill\AsideActivityBundle\Export\Aggregator; use Chill\AsideActivityBundle\Export\Declarations; +use Chill\AsideActivityBundle\Repository\AsideActivityCategoryRepository; use Chill\MainBundle\Export\AggregatorInterface; +use Chill\MainBundle\Templating\TranslatableStringHelper; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; class ByActivityTypeAggregator implements AggregatorInterface { + private AsideActivityCategoryRepository $asideActivityCategoryRepository; + + private TranslatableStringHelper $translatableStringHelper; + + public function __construct(AsideActivityCategoryRepository $asideActivityCategoryRepository, TranslatableStringHelper $translatableStringHelper) + { + $this->asideActivityCategoryRepository = $asideActivityCategoryRepository; + $this->translatableStringHelper = $translatableStringHelper; + } + public function addRole(): ?string { return null; @@ -25,8 +37,8 @@ class ByActivityTypeAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { - $qb->addSelect('AS aside_by_activity_type_aggregator') - ->addGroupBy('aside_by_activity_type_aggregator'); + $qb->addSelect('IDENTITY(aside.type) AS by_aside_activity_type_aggregator') + ->addGroupBy('by_aside_activity_type_aggregator'); } public function applyOn(): string @@ -41,20 +53,30 @@ class ByActivityTypeAggregator implements AggregatorInterface public function getLabels($key, array $values, $data) { - return static function ($value): string { + $this->asideActivityCategoryRepository->findBy(['id' => $values]); + + return function ($value): string { if ('_header' === $value) { + return 'export.aggregator.Aside activity type'; + } + + if (null === $value) { return ''; } + + $t = $this->asideActivityCategoryRepository->find($value); + + return $this->translatableStringHelper->localize($t->getTitle()); }; } public function getQueryKeys($data): array { - return ['aside_by_activity_type_aggregator']; + return ['by_aside_activity_type_aggregator']; } public function getTitle(): string { - return 'Group by aside activity type'; + return 'export.aggregator.Group by aside activity type'; } } diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Export/CountAsideActivity.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Export/CountAsideActivity.php index 304e0a283..6abcd5ec8 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Export/Export/CountAsideActivity.php +++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Export/CountAsideActivity.php @@ -42,12 +42,12 @@ class CountAsideActivity implements ExportInterface, GroupedExportInterface public function getDescription(): string { - return 'Count aside activities by various parameters.'; + return 'export.Count aside activities by various parameters.'; } public function getGroup(): string { - return 'Exports of aside activities'; + return 'export.Exports of aside activities'; } public function getLabels($key, array $values, $data) @@ -76,7 +76,7 @@ class CountAsideActivity implements ExportInterface, GroupedExportInterface public function getTitle(): string { - return 'Count aside activities'; + return 'export.Count aside activities'; } public function getType(): string diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByActivityTypeFilter.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByActivityTypeFilter.php index 354bd9f3c..85b327795 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByActivityTypeFilter.php +++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByActivityTypeFilter.php @@ -11,13 +11,34 @@ declare(strict_types=1); namespace Chill\AsideActivityBundle\Export\Filter; +use Chill\AsideActivityBundle\Entity\AsideActivityCategory; use Chill\AsideActivityBundle\Export\Declarations; +use Chill\AsideActivityBundle\Repository\AsideActivityCategoryRepository; +use Chill\AsideActivityBundle\Templating\Entity\CategoryRender; use Chill\MainBundle\Export\FilterInterface; +use Chill\MainBundle\Templating\TranslatableStringHelperInterface; use Doctrine\ORM\QueryBuilder; +use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\FormBuilderInterface; class ByActivityTypeFilter implements FilterInterface { + private AsideActivityCategoryRepository $asideActivityTypeRepository; + + private CategoryRender $categoryRender; + + private TranslatableStringHelperInterface $translatableStringHelper; + + public function __construct( + CategoryRender $categoryRender, + TranslatableStringHelperInterface $translatableStringHelper, + AsideActivityCategoryRepository $asideActivityTypeRepository + ) { + $this->categoryRender = $categoryRender; + $this->asideActivityTypeRepository = $asideActivityTypeRepository; + $this->translatableStringHelper = $translatableStringHelper; + } + public function addRole(): ?string { return null; @@ -25,11 +46,10 @@ class ByActivityTypeFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { - $qb - ->andWhere( - $qb->expr()->in('', ':') - ) - ->setParameter('', $data[]); + $clause = $qb->expr()->in('aside.type', ':types'); + + $qb->andWhere($clause); + $qb->setParameter('types', $data['types']); } public function applyOn(): string @@ -39,17 +59,38 @@ class ByActivityTypeFilter implements FilterInterface public function buildForm(FormBuilderInterface $builder) { - //$builder->add(); + $builder + ->add('types', EntityType::class, [ + 'class' => AsideActivityCategory::class, + 'choices' => $this->asideActivityTypeRepository->findAllActive(), + 'required' => false, + 'multiple' => true, + 'expanded' => false, + 'attr' => [ + 'class' => 'select2', + ], + 'choice_label' => function (AsideActivityCategory $category) { + $options = []; + + return $this->categoryRender->renderString($category, $options); + }, + ]); } public function describeAction($data, $format = 'string'): array { - return ['', [ + $types = array_map( + fn (AsideActivityCategory $t): string => $this->translatableStringHelper->localize($t->getName()), + $this->asideActivityTypeRepository->findBy(['id' => $data['types']->toArray()]) + ); + + return ['export.filter.Filtered by aside activity type: only %type%', [ + '%type%' => implode(', ', $types), ]]; } public function getTitle(): string { - return 'Filter by aside activity type'; + return 'export.filter.Filter by aside activity type'; } } diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByDateFilter.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByDateFilter.php index 5a63e8e94..a7595281d 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByDateFilter.php +++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByDateFilter.php @@ -13,11 +13,26 @@ namespace Chill\AsideActivityBundle\Export\Filter; use Chill\AsideActivityBundle\Export\Declarations; use Chill\MainBundle\Export\FilterInterface; +use Chill\MainBundle\Form\Type\ChillDateType; +use Chill\MainBundle\Form\Type\Export\FilterType; +use DateTime; +use Doctrine\ORM\Query\Expr\Andx; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\FormError; +use Symfony\Component\Form\FormEvent; +use Symfony\Component\Form\FormEvents; +use Symfony\Contracts\Translation\TranslatorInterface; class ByDateFilter implements FilterInterface { + protected TranslatorInterface $translator; + + public function __construct(TranslatorInterface $translator) + { + $this->translator = $translator; + } + public function addRole(): ?string { return null; @@ -25,11 +40,22 @@ class ByDateFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { - $qb - ->andWhere( - $qb->expr()->in('', ':') - ) - ->setParameter('', $data[]); + $where = $qb->getDQLPart('where'); + $clause = $qb->expr()->between( + 'aside.date', + ':date_from', + ':date_to' + ); + + if ($where instanceof Andx) { + $where->add($clause); + } else { + $where = $qb->expr()->andX($clause); + } + + $qb->add('where', $where); + $qb->setParameter('date_from', $data['date_from']); + $qb->setParameter('date_to', $data['date_to']); } public function applyOn(): string @@ -39,17 +65,67 @@ class ByDateFilter implements FilterInterface public function buildForm(FormBuilderInterface $builder) { - //$builder->add(); + $builder + ->add('date_from', ChillDateType::class, [ + 'label' => 'export.filter.Aside activities after this date', + 'data' => new DateTime(), + ]) + ->add('date_to', ChillDateType::class, [ + 'label' => 'export.filter.Aside activities before this date', + 'data' => new DateTime(), + ]); + + $builder->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) { + /** @var \Symfony\Component\Form\FormInterface $filterForm */ + $filterForm = $event->getForm()->getParent(); + $enabled = $filterForm->get(FilterType::ENABLED_FIELD)->getData(); + + if (true === $enabled) { + // if the filter is enabled, add some validation + $form = $event->getForm(); + $date_from = $form->get('date_from')->getData(); + $date_to = $form->get('date_to')->getData(); + + // check that fields are not empty + if (null === $date_from) { + $form->get('date_from')->addError(new FormError( + $this->translator->trans('This field ' + . 'should not be empty') + )); + } + + if (null === $date_to) { + $form->get('date_to')->addError(new FormError( + $this->translator->trans('This field ' + . 'should not be empty') + )); + } + + // check that date_from is before date_to + if ( + (null !== $date_from && null !== $date_to) + && $date_from >= $date_to + ) { + $form->get('date_to')->addError(new FormError( + $this->translator->trans('export.filter.This date should be after ' + . 'the date given in "Implied in an aside activity after ' + . 'this date" field') + )); + } + } + }); } public function describeAction($data, $format = 'string'): array { - return ['', [ + return ['export.filter.Filtered by aside activities between %dateFrom% and %dateTo%', [ + '%dateFrom%' => $data['date_from']->format('d-m-Y'), + '%dateTo%' => $data['date_to']->format('d-m-Y'), ]]; } public function getTitle(): string { - return 'Filter by aside activity date'; + return 'export.filter.Filter by aside activity date'; } } diff --git a/src/Bundle/ChillAsideActivityBundle/src/Repository/AsideActivityCategoryRepository.php b/src/Bundle/ChillAsideActivityBundle/src/Repository/AsideActivityCategoryRepository.php index 85ea9faf4..918cec586 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Repository/AsideActivityCategoryRepository.php +++ b/src/Bundle/ChillAsideActivityBundle/src/Repository/AsideActivityCategoryRepository.php @@ -38,6 +38,11 @@ class AsideActivityCategoryRepository implements ObjectRepository return $this->repository->findAll(); } + public function findAllActive(): array + { + return $this->repository->findBy(['isActive' => true]); + } + /** * @param mixed|null $limit * @param mixed|null $offset diff --git a/src/Bundle/ChillAsideActivityBundle/src/config/services/export.yaml b/src/Bundle/ChillAsideActivityBundle/src/config/services/export.yaml index 579791d41..1b6b05e1c 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/config/services/export.yaml +++ b/src/Bundle/ChillAsideActivityBundle/src/config/services/export.yaml @@ -7,3 +7,21 @@ services: Chill\AsideActivityBundle\Export\Export\CountAsideActivity: tags: - { name: chill.export, alias: 'count_aside_activity' } + + ## Filters + chill.aside_activity.export.date_filter: + class: Chill\AsideActivityBundle\Export\Filter\ByDateFilter + tags: + - { name: chill.export_filter, alias: 'aside_activity_date_filter' } + + chill.aside_activity.export.type_filter: + class: Chill\AsideActivityBundle\Export\Filter\ByActivityTypeFilter + tags: + - { name: chill.export_filter, alias: 'aside_activity_type_filter' } + + ## Aggregators + + chill.aside_activity.export.type_aggregator: + class: Chill\AsideActivityBundle\Export\Aggregator\ByActivityTypeAggregator + tags: + - { name: chill.export_aggregator, alias: activity_type_aggregator } \ No newline at end of file diff --git a/src/Bundle/ChillAsideActivityBundle/src/translations/messages.fr.yml b/src/Bundle/ChillAsideActivityBundle/src/translations/messages.fr.yml index ed54fd488..35a4d6a22 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/translations/messages.fr.yml +++ b/src/Bundle/ChillAsideActivityBundle/src/translations/messages.fr.yml @@ -168,9 +168,19 @@ Aside activity type configuration: Configuration des categories d'activités ann Aside activity configuration: Configuration des activités annexes # exports -Exports of aside activities: Exports des activités annexes -Count aside activities: Nombre d'activités annexes -Count aside activities by various parameters.: Compte le nombre d'activités annexes selon divers critères -Filter by aside activity date: Filtrer les activités annexes par date -Filter by aside activity type: Filtrer les activités annexes par type d'activité -Group by aside activity type: Grouper les activités annexes par type d'activité \ No newline at end of file +export: + Exports of aside activities: Exports des activités annexes + Count aside activities: Nombre d'activités annexes + Count aside activities by various parameters.: Compte le nombre d'activités annexes selon divers critères + filter: + Filter by aside activity date: Filtrer les activités annexes par date + Filter by aside activity type: Filtrer les activités annexes par type d'activité + 'Filtered by aside activity type: only %type%': "Filtré par type d'activité annexe: uniquement %type%" + This date should be after the date given in "Implied in an aside activity after this date" field: Cette date devrait être postérieure à la date donnée dans le champ "activités annexes après cette date" + Aside activities after this date: Actvitités annexes après cette date + Aside activities before this date: Actvitités annexes avant cette date + aggregator: + Group by aside activity type: Grouper les activités annexes par type d'activité + Aside activity type: Type d'activité annexe + + From d3f03fb27cfc8f37fd1e6b7eb6cbf8108c65c2dc Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Wed, 2 Nov 2022 20:06:03 +0100 Subject: [PATCH 57/73] evaluations aggregators --- .../ByEndDateAggregator.php | 54 +++++++++++++++++-- .../ByMaxDateAggregator.php | 54 +++++++++++++++++-- .../ByStartDateAggregator.php | 10 ++-- .../translations/messages.fr.yml | 8 ++- 4 files changed, 111 insertions(+), 15 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByEndDateAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByEndDateAggregator.php index 40b035a02..2f4275c49 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByEndDateAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByEndDateAggregator.php @@ -14,10 +14,23 @@ namespace Chill\PersonBundle\Export\Aggregator\EvaluationAggregators; use Chill\MainBundle\Export\AggregatorInterface; use Chill\PersonBundle\Export\Declarations; use Doctrine\ORM\QueryBuilder; +use LogicException; +use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Contracts\Translation\TranslatorInterface; class ByEndDateAggregator implements AggregatorInterface { + private const CHOICES = [ + 'by week' => 'week', + 'by month' => 'month', + 'by year' => 'year', + ]; + + private const DEFAULT_CHOICE = 'year'; + + private TranslatorInterface $translator; + public function addRole(): ?string { return null; @@ -25,8 +38,29 @@ class ByEndDateAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { - $qb->addSelect('AS eval_by_end_date_aggregator') - ->addGroupBy('eval_by_end_date_aggregator'); + switch ($data['frequency']) { + case 'week': + $fmt = 'YYYY-IW'; + + break; + + case 'month': + $fmt = 'YYYY-MM'; + + break; + + case 'year': + $fmt = 'YYYY'; + + break; + + default: + throw new LogicException(sprintf("The frequency data '%s' is invalid.", $data['frequency'])); + } + + $qb->addSelect(sprintf("TO_CHAR(workeval.endDate, '%s') AS eval_by_end_date_aggregator", $fmt)); + $qb->addGroupBy(' eval_by_end_date_aggregator'); + $qb->addOrderBy(' eval_by_end_date_aggregator', 'ASC'); } public function applyOn(): string @@ -36,15 +70,27 @@ class ByEndDateAggregator implements AggregatorInterface public function buildForm(FormBuilderInterface $builder) { - // No form needed + $builder->add('frequency', ChoiceType::class, [ + 'choices' => self::CHOICES, + 'multiple' => false, + 'expanded' => true, + 'empty_data' => self::DEFAULT_CHOICE, + 'data' => self::DEFAULT_CHOICE, + ]); } public function getLabels($key, array $values, $data) { return static function ($value): string { if ('_header' === $value) { + return 'export.aggregator.eval.by_end_date.End date period'; + } + + if (null === $value) { return ''; } + + return $value; }; } @@ -55,6 +101,6 @@ class ByEndDateAggregator implements AggregatorInterface public function getTitle(): string { - return 'Group by end date evaluations'; + return 'export.aggregator.eval.by_end_date.Group by end date evaluations'; } } diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByMaxDateAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByMaxDateAggregator.php index 36ab0f50b..9283fd9dc 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByMaxDateAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByMaxDateAggregator.php @@ -14,10 +14,23 @@ namespace Chill\PersonBundle\Export\Aggregator\EvaluationAggregators; use Chill\MainBundle\Export\AggregatorInterface; use Chill\PersonBundle\Export\Declarations; use Doctrine\ORM\QueryBuilder; +use LogicException; +use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Contracts\Translation\TranslatorInterface; class ByMaxDateAggregator implements AggregatorInterface { + private const CHOICES = [ + 'by week' => 'week', + 'by month' => 'month', + 'by year' => 'year', + ]; + + private const DEFAULT_CHOICE = 'year'; + + private TranslatorInterface $translator; + public function addRole(): ?string { return null; @@ -25,8 +38,29 @@ class ByMaxDateAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { - $qb->addSelect('AS eval_by_max_date_aggregator') - ->addGroupBy('eval_by_max_date_aggregator'); + switch ($data['frequency']) { + case 'week': + $fmt = 'YYYY-IW'; + + break; + + case 'month': + $fmt = 'YYYY-MM'; + + break; + + case 'year': + $fmt = 'YYYY'; + + break; + + default: + throw new LogicException(sprintf("The frequency data '%s' is invalid.", $data['frequency'])); + } + + $qb->addSelect(sprintf("TO_CHAR(workeval.maxDate, '%s') AS eval_by_max_date_aggregator", $fmt)); + $qb->addGroupBy(' eval_by_max_date_aggregator'); + $qb->addOrderBy(' eval_by_max_date_aggregator', 'ASC'); } public function applyOn(): string @@ -36,15 +70,27 @@ class ByMaxDateAggregator implements AggregatorInterface public function buildForm(FormBuilderInterface $builder) { - // No form needed + $builder->add('frequency', ChoiceType::class, [ + 'choices' => self::CHOICES, + 'multiple' => false, + 'expanded' => true, + 'empty_data' => self::DEFAULT_CHOICE, + 'data' => self::DEFAULT_CHOICE, + ]); } public function getLabels($key, array $values, $data) { return static function ($value): string { if ('_header' === $value) { + return 'export.aggregator.eval.by_max_date.Max date'; + } + + if (null === $value) { return ''; } + + return $value; }; } @@ -55,6 +101,6 @@ class ByMaxDateAggregator implements AggregatorInterface public function getTitle(): string { - return 'Group by max date evaluations'; + return 'export.aggregator.eval.by_max_date.Group by max date evaluations'; } } diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByStartDateAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByStartDateAggregator.php index a41fe2451..cd183b25e 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByStartDateAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByStartDateAggregator.php @@ -22,8 +22,8 @@ use Symfony\Contracts\Translation\TranslatorInterface; class ByStartDateAggregator implements AggregatorInterface { private const CHOICES = [ - 'by month' => 'month', 'by week' => 'week', + 'by month' => 'month', 'by year' => 'year', ]; @@ -39,13 +39,13 @@ class ByStartDateAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { switch ($data['frequency']) { - case 'month': - $fmt = 'YYYY-MM'; + case 'week': + $fmt = 'YYYY-IW'; break; - case 'week': - $fmt = 'YYYY-IW'; + case 'month': + $fmt = 'YYYY-MM'; break; diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index 7780c76dd..dca6c91f6 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -587,8 +587,6 @@ end period date: Date de fin de la période "Filtered by end date: between %start_date% and %end_date%": "Filtré par la date de fin: comprise entre %start_date% et %end_date%" Filter by current evaluations: Filtrer les évaluations en cours "Filtered by current evaluations": "Filtré: uniquement les évaluations en cours" -Group by end date evaluations: Grouper les évaluations par semaine/mois/année de la date de fin -Group by max date evaluations: Grouper les évaluations par semaine/mois/année de la date d'échéance ## social actions filters/aggr Filter by treating agent scope: Filtrer les actions par service de l'agent traitant @@ -1022,9 +1020,15 @@ export: Group evaluations having end date: Grouper les évaluations en cours (avec ou sans date de fin) enddate is specified: la date de fin est spécifiée enddate is not specified: la date de fin n'est pas spécifiée + Group by end date evaluations: Grouper les évaluations par semaine/mois/année de la date de fin + End date period: Fin (par periode) by_start_date_period: Start date period: Début (par periode) Group by start date evaluations: Grouper les évaluations par semaine/mois/année de la date de début + by_max_date: + Group by max date evaluations: Grouper les évaluations par semaine/mois/année de la date d'échéance + Max date: Date d'échéance + filter: course: by_user_scope: From 03e765f811336e61dc0786b7be7218d38dbbed98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Thu, 3 Nov 2022 12:39:19 +0100 Subject: [PATCH 58/73] DX: [export] review on CreatorFilter, CreatorJobAggregator, CreatorJobFilter --- .../CreatorJobAggregator.php | 6 ++--- .../CreatorFilter.php | 25 +++++-------------- .../CreatorJobFilter.php | 1 + .../translations/messages.fr.yml | 2 ++ 4 files changed, 11 insertions(+), 23 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/CreatorJobAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/CreatorJobAggregator.php index 3bae64fec..6c958ca6c 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/CreatorJobAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/CreatorJobAggregator.php @@ -62,15 +62,13 @@ class CreatorJobAggregator implements AggregatorInterface { return function ($value): string { if ('_header' === $value) { - return 'Job'; + return 'export.aggregator.course.by_creator_job.Creator\'s job'; } - if (null === $value) { + if (null === $value || null === $j = $this->jobRepository->find($value)) { return ''; } - $j = $this->jobRepository->find($value); - return $this->translatableStringHelper->localize( $j->getLabel() ); diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorFilter.php index 1ac6d5390..b13947e60 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorFilter.php @@ -11,10 +11,10 @@ declare(strict_types=1); namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters; +use Chill\MainBundle\Entity\User; use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Form\Type\PickUserDynamicType; use Chill\PersonBundle\Export\Declarations; -use Doctrine\ORM\Query\Expr\Andx; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; use function in_array; @@ -32,17 +32,9 @@ class CreatorFilter implements FilterInterface $qb->join('acp.createdBy', 'acp_creator'); } - $where = $qb->getDQLPart('where'); - $clause = $qb->expr()->in('acp_creator', ':creators'); - - if ($where instanceof Andx) { - $where->add($clause); - } else { - $where = $qb->expr()->andX($clause); - } - - $qb->add('where', $where); - $qb->setParameter('creators', $data['accepted_creators']); + $qb + ->andWhere($qb->expr()->in('acp_creator', ':creators')) + ->setParameter('creators', $data['accepted_creators']); } public function applyOn(): string @@ -55,20 +47,15 @@ class CreatorFilter implements FilterInterface $builder ->add('accepted_creators', PickUserDynamicType::class, [ 'multiple' => true, + 'label' => false, ]); } public function describeAction($data, $format = 'string'): array { - $creators = []; - - foreach ($data['accepted_creators'] as $c) { - $creators[] = $c; - } - return [ 'Filtered by creator: only %creators%', [ - '%creators%' => implode(', ', $creators), + '%creators%' => implode(', ', array_map(static fn (User $u) => $u->getLabel(), $data['accepted_creators'])), ], ]; } diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorJobFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorJobFilter.php index c8b7ccc55..62cf49d6d 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorJobFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorJobFilter.php @@ -68,6 +68,7 @@ class CreatorJobFilter implements FilterInterface }, 'multiple' => true, 'expanded' => true, + 'label' => 'Job', ]); } diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index dca6c91f6..bdedea895 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -1008,6 +1008,8 @@ export: Calc date: Date de calcul de la composition du ménage by_number_of_action: Number of actions: Nombre d'actions + by_creator_job: + Creator's job: Métier du créateur course_work: by_current_action: Current action ?: Action en cours ? From 6fd75a175f2984c4891517f7f30a663435cd1c28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Thu, 3 Nov 2022 13:59:16 +0100 Subject: [PATCH 59/73] Fixed: [export] improve memory footprint for filter by Geographical Unit --- .../SimpleGeographicalUnitDTO.php | 52 +++++++++++++++++++ .../Export/Helper/ExportAddressHelper.php | 1 + .../Repository/GeographicalUnitRepository.php | 2 +- .../GeographicalUnitStatFilter.php | 24 +++++---- .../PersonFilters/GeographicalUnitFilter.php | 24 +++++---- 5 files changed, 84 insertions(+), 19 deletions(-) create mode 100644 src/Bundle/ChillMainBundle/Entity/GeographicalUnit/SimpleGeographicalUnitDTO.php diff --git a/src/Bundle/ChillMainBundle/Entity/GeographicalUnit/SimpleGeographicalUnitDTO.php b/src/Bundle/ChillMainBundle/Entity/GeographicalUnit/SimpleGeographicalUnitDTO.php new file mode 100644 index 000000000..34f16a0fb --- /dev/null +++ b/src/Bundle/ChillMainBundle/Entity/GeographicalUnit/SimpleGeographicalUnitDTO.php @@ -0,0 +1,52 @@ +id = $id; + $this->unitName = $unitName; + $this->unitRefId = $unitRefId; + $this->layerId = $layerId; + } +} diff --git a/src/Bundle/ChillMainBundle/Export/Helper/ExportAddressHelper.php b/src/Bundle/ChillMainBundle/Export/Helper/ExportAddressHelper.php index 7d4245aa6..701f9ed07 100644 --- a/src/Bundle/ChillMainBundle/Export/Helper/ExportAddressHelper.php +++ b/src/Bundle/ChillMainBundle/Export/Helper/ExportAddressHelper.php @@ -205,6 +205,7 @@ class ExportAddressHelper case 'floor': case '_lat': case '_lon': + case 'steps': case 'postcode_code': case 'postcode_name': return static function ($value) use ($sanitizedKey, $translationPrefix) { diff --git a/src/Bundle/ChillMainBundle/Repository/GeographicalUnitRepository.php b/src/Bundle/ChillMainBundle/Repository/GeographicalUnitRepository.php index 17d29356b..b422b8649 100644 --- a/src/Bundle/ChillMainBundle/Repository/GeographicalUnitRepository.php +++ b/src/Bundle/ChillMainBundle/Repository/GeographicalUnitRepository.php @@ -41,7 +41,7 @@ class GeographicalUnitRepository implements GeographicalUnitRepositoryInterface { return $this->repository ->createQueryBuilder('gu') - ->select('PARTIAL gu.{id,unitName,unitRefId,layer}') + ->select(sprintf('NEW %s(gu.id, gu.unitName, gu.unitRefId, IDENTITY(gu.layer))', GeographicalUnit\SimpleGeographicalUnitDTO::class)) ->addOrderBy('IDENTITY(gu.layer)') ->addOrderBy(('gu.unitName')) ->getQuery() diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/GeographicalUnitStatFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/GeographicalUnitStatFilter.php index a1347ca99..ee420e63e 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/GeographicalUnitStatFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/GeographicalUnitStatFilter.php @@ -13,8 +13,10 @@ namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters; use Chill\MainBundle\Entity\Address; use Chill\MainBundle\Entity\GeographicalUnit; +use Chill\MainBundle\Entity\GeographicalUnit\SimpleGeographicalUnitDTO; use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Form\Type\ChillDateType; +use Chill\MainBundle\Repository\GeographicalUnitLayerRepositoryInterface; use Chill\MainBundle\Repository\GeographicalUnitRepositoryInterface; use Chill\MainBundle\Templating\TranslatableStringHelperInterface; use Chill\PersonBundle\Entity\AccompanyingPeriod; @@ -23,7 +25,7 @@ use Chill\PersonBundle\Export\Declarations; use DateTimeImmutable; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\QueryBuilder; -use Symfony\Bridge\Doctrine\Form\Type\EntityType; +use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\FormBuilderInterface; /** @@ -33,6 +35,8 @@ class GeographicalUnitStatFilter implements FilterInterface { private EntityManagerInterface $em; + private GeographicalUnitLayerRepositoryInterface $geographicalUnitLayerRepository; + private GeographicalUnitRepositoryInterface $geographicalUnitRepository; private TranslatableStringHelperInterface $translatableStringHelper; @@ -40,10 +44,12 @@ class GeographicalUnitStatFilter implements FilterInterface public function __construct( EntityManagerInterface $em, GeographicalUnitRepositoryInterface $geographicalUnitRepository, + GeographicalUnitLayerRepositoryInterface $geographicalUnitLayerRepository, TranslatableStringHelperInterface $translatableStringHelper ) { $this->em = $em; $this->geographicalUnitRepository = $geographicalUnitRepository; + $this->geographicalUnitLayerRepository = $geographicalUnitLayerRepository; $this->translatableStringHelper = $translatableStringHelper; } @@ -62,7 +68,7 @@ class GeographicalUnitStatFilter implements FilterInterface WITH IDENTITY(acp_geog_filter_location_history.personLocation) = IDENTITY(acp_geog_filter_address_person_location.person) LEFT JOIN ' . Address::class . ' acp_geog_filter_address WITH COALESCE(IDENTITY(acp_geog_filter_address_person_location.address), IDENTITY(acp_geog_filter_location_history.addressLocation)) = acp_geog_filter_address.id - LEFT JOIN ' . GeographicalUnit::class . ' acp_geog_filter_units WITH ST_CONTAINS(acp_geog_units.geom, acp_geog_filter_address.point) = TRUE + LEFT JOIN ' . GeographicalUnit::class . ' acp_geog_filter_units WITH ST_CONTAINS(acp_geog_filter_units.geom, acp_geog_filter_address.point) = TRUE WHERE (acp_geog_filter_location_history.startDate <= :acp_geog_filter_date AND ( acp_geog_filter_location_history.endDate IS NULL OR acp_geog_filter_location_history.endDate < :acp_geog_filter_date @@ -78,7 +84,7 @@ class GeographicalUnitStatFilter implements FilterInterface $qb ->andWhere($qb->expr()->exists($subQueryDql)) ->setParameter('acp_geog_filter_date', $data['date_calc']) - ->setParameter('acp_geog_filter_units', $data['units']); + ->setParameter('acp_geog_filter_units', array_map(static fn (SimpleGeographicalUnitDTO $unitDTO) => $unitDTO->id, $data['units'])); } public function applyOn(): string @@ -95,13 +101,13 @@ class GeographicalUnitStatFilter implements FilterInterface 'data' => new DateTimeImmutable('today'), 'input' => 'datetime_immutable', ]) - ->add('units', EntityType::class, [ + ->add('units', ChoiceType::class, [ 'label' => 'Geographical unit', 'placeholder' => 'Select a geographical unit', - 'class' => GeographicalUnit::class, 'choices' => $this->geographicalUnitRepository->findAll(), - 'choice_label' => function (GeographicalUnit $item) { - return $this->translatableStringHelper->localize($item->getLayer()->getName()) . ' > ' . $item->getUnitName(); + 'choice_value' => static fn (SimpleGeographicalUnitDTO $item) => $item->id, + 'choice_label' => function (SimpleGeographicalUnitDTO $item) { + return $this->translatableStringHelper->localize($this->geographicalUnitLayerRepository->find($item->layerId)->getName()) . ' > ' . $item->unitName; }, 'attr' => [ 'class' => 'select2', @@ -117,8 +123,8 @@ class GeographicalUnitStatFilter implements FilterInterface '%units' => implode( ', ', array_map( - function (GeographicalUnit $item) { - return $this->translatableStringHelper->localize($item->getLayer()->getName()) . ' > ' . $item->getUnitName(); + function (SimpleGeographicalUnitDTO $item) { + return $this->translatableStringHelper->localize($this->geographicalUnitLayerRepository->find($item->layerId)->getName()) . ' > ' . $item->unitName; }, $data['units'] ) diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/GeographicalUnitFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/GeographicalUnitFilter.php index 96a9f5a1c..03d9ab568 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/GeographicalUnitFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/GeographicalUnitFilter.php @@ -12,27 +12,33 @@ declare(strict_types=1); namespace Chill\PersonBundle\Export\Filter\PersonFilters; use Chill\MainBundle\Entity\GeographicalUnit; +use Chill\MainBundle\Entity\GeographicalUnit\SimpleGeographicalUnitDTO; use Chill\MainBundle\Form\Type\ChillDateType; +use Chill\MainBundle\Repository\GeographicalUnitLayerRepositoryInterface; use Chill\MainBundle\Repository\GeographicalUnitRepositoryInterface; use Chill\MainBundle\Templating\TranslatableStringHelperInterface; use Chill\PersonBundle\Entity\Household\PersonHouseholdAddress; use Chill\PersonBundle\Export\Declarations; use DateTimeImmutable; use Doctrine\ORM\QueryBuilder; -use Symfony\Bridge\Doctrine\Form\Type\EntityType; +use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\FormBuilderInterface; class GeographicalUnitFilter implements \Chill\MainBundle\Export\FilterInterface { + private GeographicalUnitLayerRepositoryInterface $geographicalUnitLayerRepository; + private GeographicalUnitRepositoryInterface $geographicalUnitRepository; private TranslatableStringHelperInterface $translatableStringHelper; public function __construct( GeographicalUnitRepositoryInterface $geographicalUnitRepository, + GeographicalUnitLayerRepositoryInterface $geographicalUnitLayerRepository, TranslatableStringHelperInterface $translatableStringHelper ) { $this->geographicalUnitRepository = $geographicalUnitRepository; + $this->geographicalUnitLayerRepository = $geographicalUnitLayerRepository; $this->translatableStringHelper = $translatableStringHelper; } @@ -65,7 +71,7 @@ class GeographicalUnitFilter implements \Chill\MainBundle\Export\FilterInterface $qb->expr()->exists($subQuery) ) ->setParameter('person_filter_geog_date', $data['date_calc']) - ->setParameter('person_filter_geog_units', $data['units']); + ->setParameter('person_filter_geog_units', array_map(static fn (SimpleGeographicalUnitDTO $unitDTO) => $unitDTO->id, $data['units'])); } public function applyOn() @@ -82,13 +88,13 @@ class GeographicalUnitFilter implements \Chill\MainBundle\Export\FilterInterface 'data' => new DateTimeImmutable('today'), 'input' => 'datetime_immutable', ]) - ->add('units', EntityType::class, [ + ->add('units', ChoiceType::class, [ 'label' => 'Geographical unit', 'placeholder' => 'Select a geographical unit', - 'class' => GeographicalUnit::class, 'choices' => $this->geographicalUnitRepository->findAll(), - 'choice_label' => function (GeographicalUnit $item) { - return $this->translatableStringHelper->localize($item->getLayer()->getName()) . ' > ' . $item->getUnitName(); + 'choice_value' => static fn (SimpleGeographicalUnitDTO $item) => $item->id, + 'choice_label' => function (SimpleGeographicalUnitDTO $item) { + return $this->translatableStringHelper->localize($this->geographicalUnitLayerRepository->find($item->layerId)->getName()) . ' > ' . $item->unitName; }, 'attr' => [ 'class' => 'select2', @@ -106,10 +112,10 @@ class GeographicalUnitFilter implements \Chill\MainBundle\Export\FilterInterface 'units' => implode( ', ', array_map( - function (GeographicalUnit $item) { - return $this->translatableStringHelper->localize($item->getLayer()->getName()) . ' > ' . $item->getUnitName(); + function (SimpleGeographicalUnitDTO $item) { + return $this->translatableStringHelper->localize($this->geographicalUnitLayerRepository->find($item->layerId)->getName()) . ' > ' . $item->unitName; }, - $data['units']->toArray() + $data['units'] ) ), ], From 5489178e4b9c70d3de9e49489a6c352977029818 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Mon, 7 Nov 2022 14:06:47 +0100 Subject: [PATCH 60/73] DX: Create a RollingDate data transfer object and Form type --- .../Form/DataMapper/RollingDateDataMapper.php | 45 ++++++++ .../Form/Type/PickRollingDateType.php | 50 +++++++++ .../Service/RollingDate/RollingDate.php | 101 ++++++++++++++++++ .../Form/Type/PickRollingDateTypeTest.php | 53 +++++++++ .../translations/messages.fr.yml | 19 ++++ 5 files changed, 268 insertions(+) create mode 100644 src/Bundle/ChillMainBundle/Form/DataMapper/RollingDateDataMapper.php create mode 100644 src/Bundle/ChillMainBundle/Form/Type/PickRollingDateType.php create mode 100644 src/Bundle/ChillMainBundle/Service/RollingDate/RollingDate.php create mode 100644 src/Bundle/ChillMainBundle/Tests/Form/Type/PickRollingDateTypeTest.php diff --git a/src/Bundle/ChillMainBundle/Form/DataMapper/RollingDateDataMapper.php b/src/Bundle/ChillMainBundle/Form/DataMapper/RollingDateDataMapper.php new file mode 100644 index 000000000..21d0c3fde --- /dev/null +++ b/src/Bundle/ChillMainBundle/Form/DataMapper/RollingDateDataMapper.php @@ -0,0 +1,45 @@ +setData($viewData->getRoll()); + $forms['fixedDate']->setData($viewData->getFixedDate()); + } + + public function mapFormsToData($forms, &$viewData): void + { + $forms = iterator_to_array($forms); + + $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 new file mode 100644 index 000000000..a54dc778b --- /dev/null +++ b/src/Bundle/ChillMainBundle/Form/Type/PickRollingDateType.php @@ -0,0 +1,50 @@ +add('roll', ChoiceType::class, [ + 'choices' => array_combine( + array_map(static fn (string $item) => 'rolling_date.' . $item, RollingDate::ALL_T), + RollingDate::ALL_T + ), + 'multiple' => false, + 'expanded' => false, + 'label' => 'rolling_date.roll_movement', + ]) + ->add('fixedDate', ChillDateType::class, [ + 'input' => 'datetime_immutable', + 'label' => 'rolling_date.fixed_date_date', + ]); + + $builder->setDataMapper(new RollingDateDataMapper()); + } + + public function configureOptions(OptionsResolver $resolver) + { + $resolver->setDefaults([ + 'class' => RollingDate::class, + 'empty_data' => new RollingDate(RollingDate::T_TODAY), + ]); + } +} diff --git a/src/Bundle/ChillMainBundle/Service/RollingDate/RollingDate.php b/src/Bundle/ChillMainBundle/Service/RollingDate/RollingDate.php new file mode 100644 index 000000000..cc00c7777 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Service/RollingDate/RollingDate.php @@ -0,0 +1,101 @@ +roll = $roll; + $this->pivotDate = $pivotDate ?? new DateTimeImmutable('now'); + $this->fixedDate = $fixedDate; + } + + public function getFixedDate(): DateTimeImmutable + { + return $this->fixedDate; + } + + public function getPivotDate(): ?DateTimeImmutable + { + return $this->pivotDate; + } + + public function getRoll(): string + { + return $this->roll; + } +} diff --git a/src/Bundle/ChillMainBundle/Tests/Form/Type/PickRollingDateTypeTest.php b/src/Bundle/ChillMainBundle/Tests/Form/Type/PickRollingDateTypeTest.php new file mode 100644 index 000000000..771dd0310 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Tests/Form/Type/PickRollingDateTypeTest.php @@ -0,0 +1,53 @@ + 'year_previous_start', + 'fixedDate' => null, + ]; + + $form = $this->factory->create(PickRollingDateType::class); + + $form->submit($formData); + + $this->assertTrue($form->isSynchronized()); + + /** @var RollingDate $rollingDate */ + $rollingDate = $form->getData(); + + $this->assertInstanceOf(RollingDate::class, $rollingDate); + $this->assertEquals(RollingDate::T_YEAR_PREVIOUS_START, $rollingDate->getRoll()); + } + + protected function getExtensions(): array + { + $type = new PickRollingDateType(); + + return [ + new PreloadedExtension([$type], []), + ]; + } +} diff --git a/src/Bundle/ChillMainBundle/translations/messages.fr.yml b/src/Bundle/ChillMainBundle/translations/messages.fr.yml index 3e229b8d4..c532ba30f 100644 --- a/src/Bundle/ChillMainBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillMainBundle/translations/messages.fr.yml @@ -538,3 +538,22 @@ export: isNoAddress: Adresse incomplète ? _lat: Latitude _lon: Longitude + +rolling_date: + year_previous_start: Début de l'année précédente + quarter_previous_start: Début du trimestre précédent + month_previous_start: Début du mois précédent + week_previous_start: Début de la semaine précédente + year_current_start: Début de l'année courante + quarter_current_start: Début du trimestre courant + month_current_start: Début du mois courant + week_current_start: Début de la semaine courante + today: Aujourd'hui (aucune modification de la date courante) + year_next_start: Début de l'année suivante + quarter_next_start: Début du trimestre suivante + month_next_start: Début du mois suivant + week_next_start: Début de la semaine suivante + fixed_date: Date fixe + roll_movement: Modification par rapport à aujourd'hui + fixed_date_date: Date fixe + From c7f740d5d7e48a3cc1af34bc36b7f546f30f604b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Mon, 7 Nov 2022 17:42:00 +0100 Subject: [PATCH 61/73] DX: Add rolling date converter + unit test --- .../Service/RollingDate/RollingDate.php | 2 +- .../RollingDate/RollingDateConverter.php | 141 ++++++++++++++++++ .../RollingDate/RollingDateConverterTest.php | 101 +++++++++++++ 3 files changed, 243 insertions(+), 1 deletion(-) create mode 100644 src/Bundle/ChillMainBundle/Service/RollingDate/RollingDateConverter.php create mode 100644 src/Bundle/ChillMainBundle/Tests/Services/RollingDate/RollingDateConverterTest.php diff --git a/src/Bundle/ChillMainBundle/Service/RollingDate/RollingDate.php b/src/Bundle/ChillMainBundle/Service/RollingDate/RollingDate.php index cc00c7777..7294e6816 100644 --- a/src/Bundle/ChillMainBundle/Service/RollingDate/RollingDate.php +++ b/src/Bundle/ChillMainBundle/Service/RollingDate/RollingDate.php @@ -77,7 +77,7 @@ class RollingDate * @param DateTimeImmutable|null $pivotDate Will be "now" if null is given * @param DateTimeImmutable|null $fixedDate Only to insert if $roll equals @see{self::T_FIXED_DATE} */ - public function __construct(string $roll, ?DateTimeImmutable $pivotDate = null, ?DateTimeImmutable $fixedDate = null) + public function __construct(string $roll, ?DateTimeImmutable $fixedDate = null, ?DateTimeImmutable $pivotDate = null) { $this->roll = $roll; $this->pivotDate = $pivotDate ?? new DateTimeImmutable('now'); diff --git a/src/Bundle/ChillMainBundle/Service/RollingDate/RollingDateConverter.php b/src/Bundle/ChillMainBundle/Service/RollingDate/RollingDateConverter.php new file mode 100644 index 000000000..e3cfb8e89 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Service/RollingDate/RollingDateConverter.php @@ -0,0 +1,141 @@ +getRoll()) { + case RollingDate::T_MONTH_CURRENT_START: + return $this->toBeginOfMonth($rollingDate->getPivotDate()); + + case RollingDate::T_MONTH_NEXT_START: + return $this->toBeginOfMonth($rollingDate->getPivotDate()->add(new DateInterval('P1M'))); + + case RollingDate::T_MONTH_PREVIOUS_START: + return $this->toBeginOfMonth($rollingDate->getPivotDate()->sub(new DateInterval('P1M'))); + + case RollingDate::T_QUARTER_CURRENT_START: + return $this->toBeginOfQuarter($rollingDate->getPivotDate()); + + case RollingDate::T_QUARTER_NEXT_START: + return $this->toBeginOfQuarter($rollingDate->getPivotDate()->add(new DateInterval('P3M'))); + + case RollingDate::T_QUARTER_PREVIOUS_START: + return $this->toBeginOfQuarter($rollingDate->getPivotDate()->sub(new DateInterval('P3M'))); + + case RollingDate::T_WEEK_CURRENT_START: + return $this->toBeginOfWeek($rollingDate->getPivotDate()); + + case RollingDate::T_WEEK_NEXT_START: + return $this->toBeginOfWeek($rollingDate->getPivotDate()->add(new DateInterval('P1W'))); + + case RollingDate::T_WEEK_PREVIOUS_START: + return $this->toBeginOfWeek($rollingDate->getPivotDate()->sub(new DateInterval('P1W'))); + + case RollingDate::T_YEAR_CURRENT_START: + return $this->toBeginOfYear($rollingDate->getPivotDate()); + + case RollingDate::T_YEAR_PREVIOUS_START: + return $this->toBeginOfYear($rollingDate->getPivotDate()->sub(new DateInterval('P1Y'))); + + case RollingDate::T_YEAR_NEXT_START: + return $this->toBeginOfYear($rollingDate->getPivotDate()->add(new DateInterval('P1Y'))); + + case RollingDate::T_TODAY: + return $rollingDate->getPivotDate(); + + case RollingDate::T_FIXED_DATE: + if (null === $rollingDate->getFixedDate()) { + throw new \LogicException("You must provide a fixed date when selecting a fixed date"); + } + return $rollingDate->getFixedDate(); + + default: + throw new UnexpectedValueException(sprintf('%s rolling operation not supported', $rollingDate->getRoll())); + } + } + + private function toBeginOfMonth(DateTimeImmutable $date): DateTimeImmutable + { + return DateTimeImmutable::createFromFormat( + 'Y-m-d His', + sprintf('%s-%s-01 000000', $date->format('Y'), $date->format('m')) + ); + } + + private function toBeginOfQuarter(DateTimeImmutable $date): DateTimeImmutable + { + switch ((int) $date->format('n')) { + case 1: + case 2: + case 3: + $month = '01'; + + break; + + case 4: + case 5: + case 6: + $month = '04'; + + break; + + case 7: + case 8: + case 9: + $month = '07'; + + break; + + case 10: + case 11: + case 12: + $month = '10'; + + break; + + default: + throw new LogicException('this month is not valid: ' . $date->format('n')); + } + + return DateTimeImmutable::createFromFormat( + 'Y-m-d His', + sprintf('%s-%s-01 000000', $date->format('Y'), $month) + ); + } + + private function toBeginOfWeek(DateTimeImmutable $date): DateTimeImmutable + { + if (1 === $dayOfWeek = (int) $date->format('N')) { + return $date->setTime(0, 0, 0); + } + + return $date + ->sub(new DateInterval('P' . ($dayOfWeek - 1) . 'D')) + ->setTime(0, 0, 0); + } + + private function toBeginOfYear(DateTimeImmutable $date): DateTimeImmutable + { + return DateTimeImmutable::createFromFormat( + 'Y-m-d His', + sprintf('%s-01-01 000000', $date->format('Y')) + ); + } +} diff --git a/src/Bundle/ChillMainBundle/Tests/Services/RollingDate/RollingDateConverterTest.php b/src/Bundle/ChillMainBundle/Tests/Services/RollingDate/RollingDateConverterTest.php new file mode 100644 index 000000000..6df2c889b --- /dev/null +++ b/src/Bundle/ChillMainBundle/Tests/Services/RollingDate/RollingDateConverterTest.php @@ -0,0 +1,101 @@ +converter = new RollingDateConverter(); + } + + public function generateDataConversionDate(): iterable + { + $format = 'Y-m-d His'; + + yield [RollingDate::T_MONTH_CURRENT_START, '2022-11-01 000000', $format]; + + yield [RollingDate::T_MONTH_NEXT_START, '2022-12-01 000000', $format]; + + yield [RollingDate::T_MONTH_PREVIOUS_START, '2022-10-01 000000', $format]; + + yield [RollingDate::T_QUARTER_CURRENT_START, '2022-10-01 000000', $format]; + + yield [RollingDate::T_QUARTER_NEXT_START, '2023-01-01 000000', $format]; + + yield [RollingDate::T_QUARTER_PREVIOUS_START, '2022-07-01 000000', $format]; + + yield [RollingDate::T_TODAY, '2022-11-07 000000', $format]; + + yield [RollingDate::T_WEEK_CURRENT_START, '2022-11-07 000000', $format]; + + yield [RollingDate::T_WEEK_NEXT_START, '2022-11-14 000000', $format]; + + yield [RollingDate::T_WEEK_PREVIOUS_START, '2022-10-31 000000', $format]; + + yield [RollingDate::T_YEAR_CURRENT_START, '2022-01-01 000000', $format]; + + yield [RollingDate::T_YEAR_NEXT_START, '2023-01-01 000000', $format]; + + yield [RollingDate::T_YEAR_PREVIOUS_START, '2021-01-01 000000', $format]; + } + + public function testConversionFixedDate() + { + $rollingDate = new RollingDate(RollingDate::T_FIXED_DATE, new DateTimeImmutable('2022-01-01')); + + $this->assertEquals( + '2022-01-01', + $this->converter->convert($rollingDate)->format('Y-m-d') + ); + } + + public function testConvertOnDateNow() + { + $rollingDate = new RollingDate(RollingDate::T_YEAR_PREVIOUS_START); + + $actual = $this->converter->convert($rollingDate); + + $this->assertEquals( + (int) (new DateTimeImmutable('now'))->format('Y') - 1, + (int) $actual->format('Y') + ); + $this->assertEquals(1, (int) $actual->format('m')); + $this->assertEquals(1, (int) $actual->format('d')); + } + + /** + * @dataProvider generateDataConversionDate + */ + public function testConvertOnPivotDate(string $roll, string $expectedDateTime, string $format) + { + $pivot = DateTimeImmutable::createFromFormat('Y-m-d His', '2022-11-07 000000'); + $rollingDate = new RollingDate($roll, null, $pivot); + + $this->assertEquals( + DateTime::createFromFormat($format, $expectedDateTime), + $this->converter->convert($rollingDate) + ); + } +} From 4e3c93cdd3b0e244491f8456f58ed85463af1cdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Mon, 7 Nov 2022 17:56:30 +0100 Subject: [PATCH 62/73] DX: Add validation to PickRolingDateType It is not possible to pick a fixed date when not setting a fixed date --- .../Form/Type/PickRollingDateType.php | 17 +++++++++++++++++ .../Service/RollingDate/RollingDate.php | 4 ++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/Bundle/ChillMainBundle/Form/Type/PickRollingDateType.php b/src/Bundle/ChillMainBundle/Form/Type/PickRollingDateType.php index a54dc778b..a3013f792 100644 --- a/src/Bundle/ChillMainBundle/Form/Type/PickRollingDateType.php +++ b/src/Bundle/ChillMainBundle/Form/Type/PickRollingDateType.php @@ -17,6 +17,8 @@ use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +use Symfony\Component\Validator\Constraints\Callback; +use Symfony\Component\Validator\Context\ExecutionContextInterface; class PickRollingDateType extends AbstractType { @@ -45,6 +47,21 @@ class PickRollingDateType extends AbstractType $resolver->setDefaults([ 'class' => RollingDate::class, 'empty_data' => new RollingDate(RollingDate::T_TODAY), + 'constraints' => [ + new Callback([$this, 'validate']) + ] ]); } + + public function validate($data, ExecutionContextInterface $context, $payload): void + { + /** @var RollingDate $data */ + if (RollingDate::T_FIXED_DATE === $data->getRoll() && null === $data->getFixedDate()) { + $context + ->buildViolation('rolling_date.When fixed date is selected, you must provide a date') + ->atPath('roll') + ->addViolation() + ; + } + } } diff --git a/src/Bundle/ChillMainBundle/Service/RollingDate/RollingDate.php b/src/Bundle/ChillMainBundle/Service/RollingDate/RollingDate.php index 7294e6816..e8427c62f 100644 --- a/src/Bundle/ChillMainBundle/Service/RollingDate/RollingDate.php +++ b/src/Bundle/ChillMainBundle/Service/RollingDate/RollingDate.php @@ -84,12 +84,12 @@ class RollingDate $this->fixedDate = $fixedDate; } - public function getFixedDate(): DateTimeImmutable + public function getFixedDate(): ?DateTimeImmutable { return $this->fixedDate; } - public function getPivotDate(): ?DateTimeImmutable + public function getPivotDate(): DateTimeImmutable { return $this->pivotDate; } From 0756bec5806bc5b44bc3409c3613487f4cd9c204 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Mon, 7 Nov 2022 18:07:26 +0100 Subject: [PATCH 63/73] DX: fix cs --- .../Service/RollingDate/RollingDateConverter.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Bundle/ChillMainBundle/Service/RollingDate/RollingDateConverter.php b/src/Bundle/ChillMainBundle/Service/RollingDate/RollingDateConverter.php index e3cfb8e89..9ec78c509 100644 --- a/src/Bundle/ChillMainBundle/Service/RollingDate/RollingDateConverter.php +++ b/src/Bundle/ChillMainBundle/Service/RollingDate/RollingDateConverter.php @@ -62,8 +62,9 @@ class RollingDateConverter case RollingDate::T_FIXED_DATE: if (null === $rollingDate->getFixedDate()) { - throw new \LogicException("You must provide a fixed date when selecting a fixed date"); + throw new LogicException('You must provide a fixed date when selecting a fixed date'); } + return $rollingDate->getFixedDate(); default: From 8159f91e2519e6774696742a2cba559f25627ef6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Mon, 7 Nov 2022 18:19:35 +0100 Subject: [PATCH 64/73] DX: create interface for RollingDateConverterInterface --- .../RollingDate/RollingDateConverter.php | 2 +- .../RollingDateConverterInterface.php | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 src/Bundle/ChillMainBundle/Service/RollingDate/RollingDateConverterInterface.php diff --git a/src/Bundle/ChillMainBundle/Service/RollingDate/RollingDateConverter.php b/src/Bundle/ChillMainBundle/Service/RollingDate/RollingDateConverter.php index 9ec78c509..026ff7a8a 100644 --- a/src/Bundle/ChillMainBundle/Service/RollingDate/RollingDateConverter.php +++ b/src/Bundle/ChillMainBundle/Service/RollingDate/RollingDateConverter.php @@ -16,7 +16,7 @@ use DateTimeImmutable; use LogicException; use UnexpectedValueException; -class RollingDateConverter +class RollingDateConverter implements RollingDateConverterInterface { public function convert(RollingDate $rollingDate): DateTimeImmutable { diff --git a/src/Bundle/ChillMainBundle/Service/RollingDate/RollingDateConverterInterface.php b/src/Bundle/ChillMainBundle/Service/RollingDate/RollingDateConverterInterface.php new file mode 100644 index 000000000..b20a5ced2 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Service/RollingDate/RollingDateConverterInterface.php @@ -0,0 +1,19 @@ + Date: Mon, 7 Nov 2022 18:20:22 +0100 Subject: [PATCH 65/73] Feature: [export] Add a rolling date on Acp "by step" filter --- .../ChillMainBundle/config/services.yaml | 5 ++ .../AccompanyingCourseFilters/StepFilter.php | 59 +++++++++++++------ 2 files changed, 45 insertions(+), 19 deletions(-) diff --git a/src/Bundle/ChillMainBundle/config/services.yaml b/src/Bundle/ChillMainBundle/config/services.yaml index 040db1a9f..1c56c1ad5 100644 --- a/src/Bundle/ChillMainBundle/config/services.yaml +++ b/src/Bundle/ChillMainBundle/config/services.yaml @@ -105,3 +105,8 @@ services: resource: '../Service/Import/' autowire: true autoconfigure: true + + Chill\MainBundle\Service\RollingDate\: + resource: '../Service/RollingDate/' + autowire: true + autoconfigure: true diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/StepFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/StepFilter.php index 2eb1b90b3..0c9b28e8c 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/StepFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/StepFilter.php @@ -12,31 +12,40 @@ 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\Export\Declarations; -use Doctrine\ORM\Query\Expr\Andx; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Contracts\Translation\TranslatorInterface; +use function in_array; class StepFilter implements FilterInterface { + private const A = 'acp_filter_bystep_stephistories'; + private const DEFAULT_CHOICE = AccompanyingPeriod::STEP_CONFIRMED; + private const P = 'acp_step_filter_date'; + private const STEPS = [ 'Draft' => AccompanyingPeriod::STEP_DRAFT, 'Confirmed' => AccompanyingPeriod::STEP_CONFIRMED, 'Closed' => AccompanyingPeriod::STEP_CLOSED, ]; + private RollingDateConverterInterface $rollingDateConverter; + /** * @var TranslatorInterface */ - protected $translator; + private $translator; - public function __construct(TranslatorInterface $translator) + public function __construct(RollingDateConverterInterface $rollingDateConverter, TranslatorInterface $translator) { + $this->rollingDateConverter = $rollingDateConverter; $this->translator = $translator; } @@ -47,17 +56,25 @@ class StepFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { - $where = $qb->getDQLPart('where'); - $clause = $qb->expr()->eq('acp.step', ':step'); - - if ($where instanceof Andx) { - $where->add($clause); - } else { - $where = $qb->expr()->andX($clause); + if (!in_array(self::A, $qb->getAllAliases(), true)) { + $qb->leftJoin('acp.stepHistories', self::A); } - $qb->add('where', $where); - $qb->setParameter('step', $data['accepted_steps']); + $qb + ->andWhere( + $qb->expr()->andX( + $qb->expr()->lte(self::A . '.startDate', ':' . self::P), + $qb->expr()->orX( + $qb->expr()->isNull(self::A . '.endDate'), + $qb->expr()->lt(self::A . '.endDate', ':' . self::P) + ) + ) + ) + ->andWhere( + $qb->expr()->in(self::A . '.step', ':acp_filter_by_step_steps') + ) + ->setParameter(self::P, $this->rollingDateConverter->convert($data['calc_date'])) + ->setParameter('acp_filter_by_step_steps', $data['accepted_steps']); } public function applyOn() @@ -67,13 +84,17 @@ class StepFilter implements FilterInterface public function buildForm(FormBuilderInterface $builder) { - $builder->add('accepted_steps', ChoiceType::class, [ - 'choices' => self::STEPS, - 'multiple' => false, - 'expanded' => true, - 'empty_data' => self::DEFAULT_CHOICE, - 'data' => self::DEFAULT_CHOICE, - ]); + $builder + ->add('accepted_steps', ChoiceType::class, [ + 'choices' => self::STEPS, + 'multiple' => false, + 'expanded' => true, + 'empty_data' => self::DEFAULT_CHOICE, + 'data' => self::DEFAULT_CHOICE, + ]) + ->add('calc_date', PickRollingDateType::class, [ + 'label' => 'export.acp.filter.by_step.date_calc', + ]); } public function describeAction($data, $format = 'string') From 50db96ffbc3e5f8d3be3fc63cdd473b470823859 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Mon, 7 Nov 2022 18:20:34 +0100 Subject: [PATCH 66/73] Fix validation of fixed date --- .../ChillMainBundle/Form/Type/PickRollingDateType.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Bundle/ChillMainBundle/Form/Type/PickRollingDateType.php b/src/Bundle/ChillMainBundle/Form/Type/PickRollingDateType.php index a3013f792..2c90379f2 100644 --- a/src/Bundle/ChillMainBundle/Form/Type/PickRollingDateType.php +++ b/src/Bundle/ChillMainBundle/Form/Type/PickRollingDateType.php @@ -48,8 +48,8 @@ class PickRollingDateType extends AbstractType 'class' => RollingDate::class, 'empty_data' => new RollingDate(RollingDate::T_TODAY), 'constraints' => [ - new Callback([$this, 'validate']) - ] + new Callback([$this, 'validate']), + ], ]); } @@ -60,8 +60,7 @@ class PickRollingDateType extends AbstractType $context ->buildViolation('rolling_date.When fixed date is selected, you must provide a date') ->atPath('roll') - ->addViolation() - ; + ->addViolation(); } } } From 48da6d0a92920a2c5128d9e78b7f7e1e35ca77e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Mon, 7 Nov 2022 21:11:20 +0100 Subject: [PATCH 67/73] Feature: Apply rolling date on StepAggregator --- .../StepAggregator.php | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/StepAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/StepAggregator.php index fc2fcc7b2..e17e9f9a5 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/StepAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/StepAggregator.php @@ -12,10 +12,11 @@ declare(strict_types=1); namespace Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators; use Chill\MainBundle\Export\AggregatorInterface; -use Chill\MainBundle\Form\Type\ChillDateType; +use Chill\MainBundle\Form\Type\PickRollingDateType; +use Chill\MainBundle\Service\RollingDate\RollingDate; +use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface; use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Export\Declarations; -use DateTime; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Contracts\Translation\TranslatorInterface; @@ -27,11 +28,15 @@ final class StepAggregator implements AggregatorInterface private const P = 'acp_step_agg_date'; + private RollingDateConverterInterface $rollingDateConverter; + private TranslatorInterface $translator; public function __construct( + RollingDateConverterInterface $rollingDateConverter, TranslatorInterface $translator ) { + $this->rollingDateConverter = $rollingDateConverter; $this->translator = $translator; } @@ -60,7 +65,7 @@ final class StepAggregator implements AggregatorInterface ) ) ) - ->setParameter(self::P, $data['on_date']) + ->setParameter(self::P, $this->rollingDateConverter->convert($data['on_date'])) ->addGroupBy('step_aggregator'); } @@ -71,8 +76,8 @@ final class StepAggregator implements AggregatorInterface public function buildForm(FormBuilderInterface $builder) { - $builder->add('on_date', ChillDateType::class, [ - 'data' => new DateTime(), + $builder->add('on_date', PickRollingDateType::class, [ + 'data' => new RollingDate(RollingDate::T_TODAY), ]); } From 8aeeab9e6b876f26b27b831954c80d9ec206c795 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Mon, 7 Nov 2022 21:18:56 +0100 Subject: [PATCH 68/73] Feature: Apply rolling "open between dates filters" (accompanying period) --- .../OpenBetweenDatesFilter.php | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/OpenBetweenDatesFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/OpenBetweenDatesFilter.php index 5b8335c55..07ca1de75 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/OpenBetweenDatesFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/OpenBetweenDatesFilter.php @@ -12,15 +12,23 @@ declare(strict_types=1); namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters; use Chill\MainBundle\Export\FilterInterface; -use Chill\MainBundle\Form\Type\ChillDateType; +use Chill\MainBundle\Form\Type\PickRollingDateType; +use Chill\MainBundle\Service\RollingDate\RollingDate; +use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface; use Chill\PersonBundle\Export\Declarations; -use DateTime; use Doctrine\DBAL\Types\Types; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; class OpenBetweenDatesFilter implements FilterInterface { + private RollingDateConverterInterface $rollingDateConverter; + + public function __construct(RollingDateConverterInterface $rollingDateConverter) + { + $this->rollingDateConverter = $rollingDateConverter; + } + public function addRole(): ?string { return null; @@ -34,8 +42,8 @@ class OpenBetweenDatesFilter implements FilterInterface ); $qb->andWhere($clause); - $qb->setParameter('datefrom', $data['date_from'], Types::DATE_MUTABLE); - $qb->setParameter('dateto', $data['date_to'], Types::DATE_MUTABLE); + $qb->setParameter('datefrom', $this->rollingDateConverter->convert($data['date_from']), Types::DATE_IMMUTABLE); + $qb->setParameter('dateto', $this->rollingDateConverter->convert($data['date_to']), Types::DATE_IMMUTABLE); } public function applyOn(): string @@ -46,19 +54,19 @@ class OpenBetweenDatesFilter implements FilterInterface public function buildForm(FormBuilderInterface $builder) { $builder - ->add('date_from', ChillDateType::class, [ - 'data' => new DateTime(), + ->add('date_from', PickRollingDateType::class, [ + 'data' => new RollingDate(RollingDate::T_MONTH_PREVIOUS_START), ]) - ->add('date_to', ChillDateType::class, [ - 'data' => new DateTime(), + ->add('date_to', PickRollingDateType::class, [ + 'data' => new RollingDate(RollingDate::T_TODAY), ]); } public function describeAction($data, $format = 'string'): array { return ['Filtered by opening dates: between %datefrom% and %dateto%', [ - '%datefrom%' => $data['date_from']->format('d-m-Y'), - '%dateto%' => $data['date_to']->format('d-m-Y'), + '%datefrom%' => $this->rollingDateConverter->convert($data['date_from'])->format('d-m-Y'), + '%dateto%' => $this->rollingDateConverter->convert($data['date_to'])->format('d-m-Y'), ]]; } From ccb2cd029563df5f194e3e8ba4a1126164c7e2a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Tue, 8 Nov 2022 10:20:38 +0100 Subject: [PATCH 69/73] Feature: [saved export] Create a "saved export" --- .../Controller/ExportController.php | 115 +++++++++++---- .../ChillMainBundle/Entity/SavedExport.php | 133 ++++++++++++++++++ .../ChillMainBundle/Form/SavedExportType.php | 40 ++++++ .../Resources/views/Export/download.html.twig | 4 + .../Resources/views/SavedExport/new.html.twig | 21 +++ .../migrations/Version20221107212201.php | 43 ++++++ .../translations/messages.fr.yml | 2 + 7 files changed, 329 insertions(+), 29 deletions(-) create mode 100644 src/Bundle/ChillMainBundle/Entity/SavedExport.php create mode 100644 src/Bundle/ChillMainBundle/Form/SavedExportType.php create mode 100644 src/Bundle/ChillMainBundle/Resources/views/SavedExport/new.html.twig create mode 100644 src/Bundle/ChillMainBundle/migrations/Version20221107212201.php diff --git a/src/Bundle/ChillMainBundle/Controller/ExportController.php b/src/Bundle/ChillMainBundle/Controller/ExportController.php index 96c9ed08d..9037ae141 100644 --- a/src/Bundle/ChillMainBundle/Controller/ExportController.php +++ b/src/Bundle/ChillMainBundle/Controller/ExportController.php @@ -11,11 +11,15 @@ declare(strict_types=1); namespace Chill\MainBundle\Controller; +use Chill\MainBundle\Entity\SavedExport; +use Chill\MainBundle\Entity\User; use Chill\MainBundle\Export\ExportManager; +use Chill\MainBundle\Form\SavedExportType; use Chill\MainBundle\Form\Type\Export\ExportType; use Chill\MainBundle\Form\Type\Export\FormatterType; use Chill\MainBundle\Form\Type\Export\PickCenterType; use Chill\MainBundle\Redis\ChillRedis; +use Doctrine\ORM\EntityManagerInterface; use LogicException; use Psr\Log\LoggerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; @@ -26,6 +30,8 @@ use Symfony\Component\Form\FormInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Session\SessionInterface; +use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; +use Symfony\Component\Routing\Annotation\Route; use Symfony\Contracts\Translation\TranslatorInterface; use function count; @@ -38,35 +44,37 @@ use function unserialize; */ class ExportController extends AbstractController { + private EntityManagerInterface $entityManager; + /** * @var ExportManager */ - protected $exportManager; + private $exportManager; /** * @var FormFactoryInterface */ - protected $formFactory; + private $formFactory; /** * @var LoggerInterface */ - protected $logger; + private $logger; /** * @var ChillRedis */ - protected $redis; + private $redis; /** * @var SessionInterface */ - protected $session; + private $session; /** * @var TranslatorInterface */ - protected $translator; + private $translator; public function __construct( ChillRedis $chillRedis, @@ -74,8 +82,10 @@ class ExportController extends AbstractController FormFactoryInterface $formFactory, LoggerInterface $logger, SessionInterface $session, - TranslatorInterface $translator + TranslatorInterface $translator, + EntityManagerInterface $entityManager ) { + $this->entityManager = $entityManager; $this->redis = $chillRedis; $this->exportManager = $exportManager; $this->formFactory = $formFactory; @@ -203,6 +213,46 @@ class ExportController extends AbstractController } } + /** + * @Route("/{_locale}/export/save-from-key/{alias}/{key}", name="chill_main_export_save_from_key") + */ + public function saveFromKey(string $alias, string $key, Request $request): Response + { + $this->denyAccessUnlessGranted('ROLE_USER'); + $user = $this->getUser(); + + if (!$user instanceof User) { + throw new AccessDeniedHttpException(); + } + + $data = $this->rebuildRawData($key); + + $savedExport = new SavedExport(); + $savedExport + ->setOptions($data) + ->setExportAlias($alias) + ->setUser($user); + + $form = $this->createForm(SavedExportType::class, $savedExport); + + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + $this->entityManager->persist($savedExport); + $this->entityManager->flush(); + + return $this->redirectToRoute('chill_main_export_index'); + } + + return $this->render( + '@ChillMain/SavedExport/new.html.twig', + [ + 'form' => $form->createView(), + 'saved_export' => $savedExport, + ] + ); + } + /** * create a form to show on different steps. * @@ -418,28 +468,7 @@ class ExportController extends AbstractController protected function rebuildData($key) { - if (null === $key) { - throw $this->createNotFoundException('key does not exists'); - } - - if ($this->redis->exists($key) !== 1) { - $this->addFlash('error', $this->translator->trans('This report is not available any more')); - - throw $this->createNotFoundException('key does not exists'); - } - - $serialized = $this->redis->get($key); - - if (false === $serialized) { - throw new LogicException('the key could not be reached from redis'); - } - - $rawData = unserialize($serialized); - - $this->logger->notice('[export] choices for an export unserialized', [ - 'key' => $key, - 'rawData' => json_encode($rawData), - ]); + $rawData = $this->rebuildRawData($key); $alias = $rawData['alias']; @@ -585,4 +614,32 @@ class ExportController extends AbstractController throw new LogicException("the step {$step} is not defined."); } } + + private function rebuildRawData(string $key): array + { + if (null === $key) { + throw $this->createNotFoundException('key does not exists'); + } + + if ($this->redis->exists($key) !== 1) { + $this->addFlash('error', $this->translator->trans('This report is not available any more')); + + throw $this->createNotFoundException('key does not exists'); + } + + $serialized = $this->redis->get($key); + + if (false === $serialized) { + throw new LogicException('the key could not be reached from redis'); + } + + $rawData = unserialize($serialized); + + $this->logger->notice('[export] choices for an export unserialized', [ + 'key' => $key, + 'rawData' => json_encode($rawData), + ]); + + return $rawData; + } } diff --git a/src/Bundle/ChillMainBundle/Entity/SavedExport.php b/src/Bundle/ChillMainBundle/Entity/SavedExport.php new file mode 100644 index 000000000..3ac361e81 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Entity/SavedExport.php @@ -0,0 +1,133 @@ +id = Uuid::uuid4(); + } + + public function getDescription(): string + { + return $this->description; + } + + public function getExportAlias(): string + { + return $this->exportAlias; + } + + public function getId(): UuidInterface + { + return $this->id; + } + + public function getOptions(): array + { + return $this->options; + } + + public function getTitle(): string + { + return $this->title; + } + + public function getUser(): User + { + return $this->user; + } + + public function setDescription(string $description): SavedExport + { + $this->description = $description; + + return $this; + } + + public function setExportAlias(string $exportAlias): SavedExport + { + $this->exportAlias = $exportAlias; + + return $this; + } + + public function setOptions(array $options): SavedExport + { + $this->options = $options; + + return $this; + } + + public function setTitle(string $title): SavedExport + { + $this->title = $title; + + return $this; + } + + public function setUser(User $user): SavedExport + { + $this->user = $user; + + return $this; + } +} diff --git a/src/Bundle/ChillMainBundle/Form/SavedExportType.php b/src/Bundle/ChillMainBundle/Form/SavedExportType.php new file mode 100644 index 000000000..16aa4e42e --- /dev/null +++ b/src/Bundle/ChillMainBundle/Form/SavedExportType.php @@ -0,0 +1,40 @@ +add('title', TextType::class, [ + 'required' => true, + ]) + ->add('description', ChillTextareaType::class, [ + 'required' => false, + ]); + } + + public function configureOptions(OptionsResolver $resolver) + { + $resolver->setDefaults([ + 'class' => SavedExport::class, + ]); + } +} diff --git a/src/Bundle/ChillMainBundle/Resources/views/Export/download.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Export/download.html.twig index 84eb85100..a15e60088 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/Export/download.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/Export/download.html.twig @@ -49,5 +49,9 @@ window.addEventListener("DOMContentLoaded", function(e) { data-download-text="{{ "Download your report"|trans|escape('html_attr') }}" >{{ "Waiting for your report"|trans ~ '...' }} + + {% endblock content %} \ No newline at end of file diff --git a/src/Bundle/ChillMainBundle/Resources/views/SavedExport/new.html.twig b/src/Bundle/ChillMainBundle/Resources/views/SavedExport/new.html.twig new file mode 100644 index 000000000..037adb0fc --- /dev/null +++ b/src/Bundle/ChillMainBundle/Resources/views/SavedExport/new.html.twig @@ -0,0 +1,21 @@ +{% extends "@ChillMain/layout.html.twig" %} + +{% block title %}{{ 'saved_export.New'|trans }}{% endblock %} + +{% block content %} +

{{ block('title') }}

+ + {{ form_start(form) }} + {{ form_row(form.title) }} + {{ form_row(form.description) }} + + + {{ form_end(form) }} +{% endblock %} \ No newline at end of file diff --git a/src/Bundle/ChillMainBundle/migrations/Version20221107212201.php b/src/Bundle/ChillMainBundle/migrations/Version20221107212201.php new file mode 100644 index 000000000..58285c333 --- /dev/null +++ b/src/Bundle/ChillMainBundle/migrations/Version20221107212201.php @@ -0,0 +1,43 @@ +addSql('DROP TABLE chill_main_saved_export'); + } + + public function getDescription(): string + { + return 'Create table for saved exports'; + } + + public function up(Schema $schema): void + { + $this->addSql('CREATE TABLE chill_main_saved_export (id UUID NOT NULL, user_id INT DEFAULT NULL, description TEXT DEFAULT \'\' NOT NULL, exportAlias TEXT DEFAULT \'\' NOT NULL, options JSONB DEFAULT \'[]\' NOT NULL, title TEXT DEFAULT \'\' NOT NULL, createdAt TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, updatedAt TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, createdBy_id INT DEFAULT NULL, updatedBy_id INT DEFAULT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE INDEX IDX_C2029B22A76ED395 ON chill_main_saved_export (user_id)'); + $this->addSql('CREATE INDEX IDX_C2029B223174800F ON chill_main_saved_export (createdBy_id)'); + $this->addSql('CREATE INDEX IDX_C2029B2265FF1AEC ON chill_main_saved_export (updatedBy_id)'); + $this->addSql('COMMENT ON COLUMN chill_main_saved_export.id IS \'(DC2Type:uuid)\''); + $this->addSql('COMMENT ON COLUMN chill_main_saved_export.options IS \'(DC2Type:json)\''); + $this->addSql('COMMENT ON COLUMN chill_main_saved_export.createdAt IS \'(DC2Type:datetime_immutable)\''); + $this->addSql('COMMENT ON COLUMN chill_main_saved_export.updatedAt IS \'(DC2Type:datetime_immutable)\''); + $this->addSql('ALTER TABLE chill_main_saved_export ADD CONSTRAINT FK_C2029B22A76ED395 FOREIGN KEY (user_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE chill_main_saved_export ADD CONSTRAINT FK_C2029B223174800F FOREIGN KEY (createdBy_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE chill_main_saved_export ADD CONSTRAINT FK_C2029B2265FF1AEC FOREIGN KEY (updatedBy_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + } +} diff --git a/src/Bundle/ChillMainBundle/translations/messages.fr.yml b/src/Bundle/ChillMainBundle/translations/messages.fr.yml index c532ba30f..42f1c8004 100644 --- a/src/Bundle/ChillMainBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillMainBundle/translations/messages.fr.yml @@ -557,3 +557,5 @@ rolling_date: roll_movement: Modification par rapport à aujourd'hui fixed_date_date: Date fixe +saved_export: + New: Nouveau rapport enregistré From aec4c52567efd99858ec80d478a5eb041504bb8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Tue, 8 Nov 2022 16:57:49 +0100 Subject: [PATCH 70/73] Feature: [saved export] First list of saved exports --- .../Controller/SavedExportController.php | 90 +++++++++++++++++++ .../Repository/SavedExportRepository.php | 82 +++++++++++++++++ .../SavedExportRepositoryInterface.php | 40 +++++++++ .../views/SavedExport/index.html.twig | 49 ++++++++++ .../translations/messages.fr.yml | 1 + 5 files changed, 262 insertions(+) create mode 100644 src/Bundle/ChillMainBundle/Controller/SavedExportController.php create mode 100644 src/Bundle/ChillMainBundle/Repository/SavedExportRepository.php create mode 100644 src/Bundle/ChillMainBundle/Repository/SavedExportRepositoryInterface.php create mode 100644 src/Bundle/ChillMainBundle/Resources/views/SavedExport/index.html.twig diff --git a/src/Bundle/ChillMainBundle/Controller/SavedExportController.php b/src/Bundle/ChillMainBundle/Controller/SavedExportController.php new file mode 100644 index 000000000..051fa5231 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Controller/SavedExportController.php @@ -0,0 +1,90 @@ +exportManager = $exportManager; + $this->savedExportRepository = $savedExportRepository; + $this->security = $security; + $this->templating = $templating; + $this->translator = $translator; + } + + /** + * @Route("/{_locale}/exports/saved/my", name="chill_main_export_saved_list_my") + */ + public function list(): Response + { + $user = $this->security->getUser(); + + if (!$this->security->isGranted('ROLE_USER') || !$user instanceof User) { + throw new AccessDeniedHttpException(); + } + + $exports = $this->savedExportRepository->findByUser($user, ['title' => 'ASC']); + + // group by center + /** @var array $exportsGrouped */ + $exportsGrouped = []; + + foreach ($exports as $savedExport) { + $export = $this->exportManager->getExport($savedExport->getExportAlias()); + + $exportsGrouped[ + $export instanceof GroupedExportInterface + ? $this->translator->trans($export->getGroup()) : '_' + ][] = ['saved' => $savedExport, 'export' => $export]; + } + + ksort($exportsGrouped); + + return new Response( + $this->templating->render( + '@ChillMain/SavedExport/index.html.twig', + [ + 'grouped_exports' => $exportsGrouped, + ] + ) + ); + } +} diff --git a/src/Bundle/ChillMainBundle/Repository/SavedExportRepository.php b/src/Bundle/ChillMainBundle/Repository/SavedExportRepository.php new file mode 100644 index 000000000..0011c5c43 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Repository/SavedExportRepository.php @@ -0,0 +1,82 @@ + + */ +class SavedExportRepository implements SavedExportRepositoryInterface +{ + private EntityRepository $repository; + + public function __construct(EntityManagerInterface $entityManager) + { + $this->repository = $entityManager->getRepository($this->getClassName()); + } + + public function find($id): ?SavedExport + { + return $this->repository->find($id); + } + + /** + * @return array|SavedExport[] + */ + public function findAll(): array + { + return $this->repository->findAll(); + } + + public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array + { + return $this->repository->findBy($criteria, $orderBy, $limit, $offset); + } + + public function findByUser(User $user, ?array $orderBy = [], ?int $limit = null, ?int $offset = null): array + { + $qb = $this->repository->createQueryBuilder('se'); + + $qb + ->where($qb->expr()->eq('se.user', ':user')) + ->setParameter('user', $user); + + if (null !== $limit) { + $qb->setMaxResults($limit); + } + + if (null !== $offset) { + $qb->setFirstResult($offset); + } + + foreach ($orderBy as $field => $order) { + $qb->addOrderBy('se.' . $field, $order); + } + + return $qb->getQuery()->getResult(); + } + + public function findOneBy(array $criteria): ?SavedExport + { + return $this->repository->findOneBy($criteria); + } + + public function getClassName(): string + { + return SavedExport::class; + } +} diff --git a/src/Bundle/ChillMainBundle/Repository/SavedExportRepositoryInterface.php b/src/Bundle/ChillMainBundle/Repository/SavedExportRepositoryInterface.php new file mode 100644 index 000000000..3b168505f --- /dev/null +++ b/src/Bundle/ChillMainBundle/Repository/SavedExportRepositoryInterface.php @@ -0,0 +1,40 @@ + + */ +interface SavedExportRepositoryInterface extends ObjectRepository +{ + public function find($id): ?SavedExport; + + /** + * @return array|SavedExport[] + */ + public function findAll(): array; + + public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array; + + /** + * @return array|SavedExport[] + */ + public function findByUser(User $user, ?array $orderBy = [], ?int $limit = null, ?int $offset = null): array; + + public function findOneBy(array $criteria): ?SavedExport; + + public function getClassName(): string; +} diff --git a/src/Bundle/ChillMainBundle/Resources/views/SavedExport/index.html.twig b/src/Bundle/ChillMainBundle/Resources/views/SavedExport/index.html.twig new file mode 100644 index 000000000..071a177a5 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Resources/views/SavedExport/index.html.twig @@ -0,0 +1,49 @@ +{% extends "@ChillMain/layout.html.twig" %} + +{% block title %}{{ 'saved_export.My saved exports'|trans }}{% endblock %} + +{% block content %} +
+

{{ block('title') }}

+ +
+ + {% for group, saveds in grouped_exports %} + {% if group != '_' %} +

{{ group }}

+
+ {% for s in saveds %} +
+

{{ s.saved.title }}

+

{{ s.export.title|trans }}

+
+ {{ s.saved.description|chill_markdown_to_html }} +
+
+ {% endfor %} +
+ {% endif %} + {% endfor %} + + {% if grouped_exports|keys|length > 1 and grouped_exports['_']|length > 0 %} +

{{ 'Ungrouped exports'|trans }}

+ {% endif %} + +
+ {% for saveds in grouped_exports['_']|default([]) %} + {% for s in saveds %} +
+
+

{{ s.saved.title }}

+

{{ s.export.title|trans }}

+
+ {{ s.saved.description|chill_markdown_to_html }} +
+
+
+ {% endfor %} + {% endfor %} +
+
+
+{% endblock %} diff --git a/src/Bundle/ChillMainBundle/translations/messages.fr.yml b/src/Bundle/ChillMainBundle/translations/messages.fr.yml index 42f1c8004..498f185b6 100644 --- a/src/Bundle/ChillMainBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillMainBundle/translations/messages.fr.yml @@ -559,3 +559,4 @@ rolling_date: saved_export: New: Nouveau rapport enregistré + My saved exports: Mes rapports enregistrés From 79e9906a053813b3e7c33ba5e95977de3eaf36bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Tue, 8 Nov 2022 18:02:26 +0100 Subject: [PATCH 71/73] =?UTF-8?q?Feature:=20[saved=20export]=20G=C3=A9n?= =?UTF-8?q?=C3=A9rate=20a=20report=20from=20a=20saved=20export?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controller/ExportController.php | 24 +++++++++++- .../Resources/views/Export/download.html.twig | 13 +++++-- .../views/SavedExport/index.html.twig | 14 ++++++- .../Authorization/SavedExportVoter.php | 39 +++++++++++++++++++ .../config/services/security.yaml | 2 + 5 files changed, 86 insertions(+), 6 deletions(-) create mode 100644 src/Bundle/ChillMainBundle/Security/Authorization/SavedExportVoter.php diff --git a/src/Bundle/ChillMainBundle/Controller/ExportController.php b/src/Bundle/ChillMainBundle/Controller/ExportController.php index 9037ae141..293a3c778 100644 --- a/src/Bundle/ChillMainBundle/Controller/ExportController.php +++ b/src/Bundle/ChillMainBundle/Controller/ExportController.php @@ -19,21 +19,24 @@ use Chill\MainBundle\Form\Type\Export\ExportType; use Chill\MainBundle\Form\Type\Export\FormatterType; use Chill\MainBundle\Form\Type\Export\PickCenterType; use Chill\MainBundle\Redis\ChillRedis; +use Chill\MainBundle\Security\Authorization\SavedExportVoter; use Doctrine\ORM\EntityManagerInterface; use LogicException; use Psr\Log\LoggerInterface; +use RedisException; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\Form\Extension\Core\Type\FormType; use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\Form\FormFactoryInterface; use Symfony\Component\Form\FormInterface; +use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Session\SessionInterface; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\Routing\Annotation\Route; -use Symfony\Contracts\Translation\TranslatorInterface; +use Symfony\Contracts\Translation\TranslatorInterface; use function count; use function serialize; use function unserialize; @@ -152,6 +155,25 @@ class ExportController extends AbstractController ); } + /** + * @Route("/{_locale}/exports/generate-from-saved/{id}", name="chill_main_export_generate_from_saved") + * + * @throws RedisException + */ + public function generateFromSavedExport(SavedExport $savedExport): RedirectResponse + { + $this->denyAccessUnlessGranted(SavedExportVoter::GENERATE, $savedExport); + + $key = md5(uniqid((string) mt_rand(), false)); + + $this->redis->setEx($key, 3600, serialize($savedExport->getOptions())); + + return $this->redirectToRoute( + 'chill_main_export_download', + ['alias' => $savedExport->getExportAlias(), 'key' => $key, 'prevent_save' => true] + ); + } + /** * Render the list of available exports. */ diff --git a/src/Bundle/ChillMainBundle/Resources/views/Export/download.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Export/download.html.twig index a15e60088..1e69c5a49 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/Export/download.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/Export/download.html.twig @@ -49,9 +49,14 @@ window.addEventListener("DOMContentLoaded", function(e) { data-download-text="{{ "Download your report"|trans|escape('html_attr') }}" >{{ "Waiting for your report"|trans ~ '...' }} - + + {% endblock content %} \ No newline at end of file diff --git a/src/Bundle/ChillMainBundle/Resources/views/SavedExport/index.html.twig b/src/Bundle/ChillMainBundle/Resources/views/SavedExport/index.html.twig index 071a177a5..5cd697bb4 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/SavedExport/index.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/SavedExport/index.html.twig @@ -15,10 +15,16 @@ {% for s in saveds %}

{{ s.saved.title }}

-

{{ s.export.title|trans }}

+

{{ s.export.title|trans }}

{{ s.saved.description|chill_markdown_to_html }}
+ +
    +
  • +
  • +
  • +
{% endfor %} @@ -39,6 +45,12 @@
{{ s.saved.description|chill_markdown_to_html }}
+ +
    +
  • +
  • +
  • +
{% endfor %} diff --git a/src/Bundle/ChillMainBundle/Security/Authorization/SavedExportVoter.php b/src/Bundle/ChillMainBundle/Security/Authorization/SavedExportVoter.php new file mode 100644 index 000000000..bc74e2d45 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Security/Authorization/SavedExportVoter.php @@ -0,0 +1,39 @@ +getUser() === $token->getUser(); + + default: + throw new UnexpectedValueException('attribute not supported: ' . $attribute); + } + } +} diff --git a/src/Bundle/ChillMainBundle/config/services/security.yaml b/src/Bundle/ChillMainBundle/config/services/security.yaml index 3347871e3..824144470 100644 --- a/src/Bundle/ChillMainBundle/config/services/security.yaml +++ b/src/Bundle/ChillMainBundle/config/services/security.yaml @@ -50,6 +50,8 @@ services: tags: - { name: security.voter } + Chill\MainBundle\Security\Authorization\SavedExportVoter: ~ + Chill\MainBundle\Security\PasswordRecover\TokenManager: arguments: $secret: '%kernel.secret%' From 43791badd51e16a41b99ecf6ac3204fa11b5c3cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Tue, 8 Nov 2022 19:24:22 +0100 Subject: [PATCH 72/73] Feature: [saved export] Edit and delete saved exports --- .../Controller/ExportController.php | 6 +- .../Controller/SavedExportController.php | 97 ++++++++++++++++++- .../views/SavedExport/delete.html.twig | 25 +++++ .../views/SavedExport/edit.html.twig | 21 ++++ .../views/SavedExport/index.html.twig | 12 ++- .../Authorization/SavedExportVoter.php | 15 ++- .../translations/messages.fr.yml | 6 ++ 7 files changed, 175 insertions(+), 7 deletions(-) create mode 100644 src/Bundle/ChillMainBundle/Resources/views/SavedExport/delete.html.twig create mode 100644 src/Bundle/ChillMainBundle/Resources/views/SavedExport/edit.html.twig diff --git a/src/Bundle/ChillMainBundle/Controller/ExportController.php b/src/Bundle/ChillMainBundle/Controller/ExportController.php index 293a3c778..84bf80c6b 100644 --- a/src/Bundle/ChillMainBundle/Controller/ExportController.php +++ b/src/Bundle/ChillMainBundle/Controller/ExportController.php @@ -170,7 +170,11 @@ class ExportController extends AbstractController return $this->redirectToRoute( 'chill_main_export_download', - ['alias' => $savedExport->getExportAlias(), 'key' => $key, 'prevent_save' => true] + [ + 'alias' => $savedExport->getExportAlias(), + 'key' => $key, 'prevent_save' => true, + 'returnPath' => $this->generateUrl('chill_main_export_saved_list_my'), + ] ); } diff --git a/src/Bundle/ChillMainBundle/Controller/SavedExportController.php b/src/Bundle/ChillMainBundle/Controller/SavedExportController.php index 051fa5231..197fe253d 100644 --- a/src/Bundle/ChillMainBundle/Controller/SavedExportController.php +++ b/src/Bundle/ChillMainBundle/Controller/SavedExportController.php @@ -16,38 +16,132 @@ use Chill\MainBundle\Entity\User; use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\ExportManager; use Chill\MainBundle\Export\GroupedExportInterface; +use Chill\MainBundle\Form\SavedExportType; use Chill\MainBundle\Repository\SavedExportRepositoryInterface; +use Chill\MainBundle\Security\Authorization\SavedExportVoter; +use Doctrine\ORM\EntityManagerInterface; +use Symfony\Component\Form\Extension\Core\Type\SubmitType; +use Symfony\Component\Form\FormFactoryInterface; +use Symfony\Component\HttpFoundation\RedirectResponse; +use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\Session\SessionInterface; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\Routing\Annotation\Route; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Security\Core\Security; use Symfony\Component\Templating\EngineInterface; use Symfony\Contracts\Translation\TranslatorInterface; +use function count; class SavedExportController { + private EntityManagerInterface $entityManager; + private ExportManager $exportManager; + private FormFactoryInterface $formFactory; + private SavedExportRepositoryInterface $savedExportRepository; private Security $security; + private SessionInterface $session; + private EngineInterface $templating; private TranslatorInterface $translator; + private UrlGeneratorInterface $urlGenerator; + public function __construct( EngineInterface $templating, + EntityManagerInterface $entityManager, ExportManager $exportManager, + FormFactoryInterface $formBuilder, SavedExportRepositoryInterface $savedExportRepository, Security $security, - TranslatorInterface $translator + SessionInterface $session, + TranslatorInterface $translator, + UrlGeneratorInterface $urlGenerator ) { $this->exportManager = $exportManager; + $this->entityManager = $entityManager; + $this->formFactory = $formBuilder; $this->savedExportRepository = $savedExportRepository; $this->security = $security; + $this->session = $session; $this->templating = $templating; $this->translator = $translator; + $this->urlGenerator = $urlGenerator; + } + + /** + * @Route("/{_locale}/exports/saved/{id}/delete", name="chill_main_export_saved_delete") + */ + public function delete(SavedExport $savedExport, Request $request): Response + { + if (!$this->security->isGranted(SavedExportVoter::DELETE, $savedExport)) { + throw new AccessDeniedHttpException(); + } + + $form = $this->formFactory->create(); + $form->add('submit', SubmitType::class, ['label' => 'Delete']); + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + $this->entityManager->remove($savedExport); + $this->entityManager->flush(); + + $this->session->getFlashBag()->add('success', $this->translator->trans('saved_export.Export is deleted')); + + return new RedirectResponse( + $this->urlGenerator->generate('chill_main_export_saved_list_my') + ); + } + + return new Response( + $this->templating->render( + '@ChillMain/SavedExport/delete.html.twig', + [ + 'saved_export' => $savedExport, + 'delete_form' => $form->createView(), + ] + ) + ); + } + + /** + * @Route("/{_locale}/exports/saved/{id}/edit", name="chill_main_export_saved_edit") + */ + public function edit(SavedExport $savedExport, Request $request): Response + { + if (!$this->security->isGranted(SavedExportVoter::EDIT, $savedExport)) { + throw new AccessDeniedHttpException(); + } + + $form = $this->formFactory->create(SavedExportType::class, $savedExport); + + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + $this->entityManager->flush(); + + $this->session->getFlashBag()->add('success', $this->translator->trans('saved_export.Saved export is saved!')); + + return new RedirectResponse( + $this->urlGenerator->generate('chill_main_export_saved_list_my') + ); + } + + return new Response( + $this->templating->render( + '@ChillMain/SavedExport/edit.html.twig', + [ + 'form' => $form->createView(), + ] + ) + ); } /** @@ -83,6 +177,7 @@ class SavedExportController '@ChillMain/SavedExport/index.html.twig', [ 'grouped_exports' => $exportsGrouped, + 'total' => count($exports), ] ) ); diff --git a/src/Bundle/ChillMainBundle/Resources/views/SavedExport/delete.html.twig b/src/Bundle/ChillMainBundle/Resources/views/SavedExport/delete.html.twig new file mode 100644 index 000000000..5d90699df --- /dev/null +++ b/src/Bundle/ChillMainBundle/Resources/views/SavedExport/delete.html.twig @@ -0,0 +1,25 @@ +{% extends '@ChillMain/layout.html.twig' %} + +{% block title 'saved_export.Delete saved ?'|trans %} + +{% block display_content %} +
+

{{ saved_export.title }}

+

{{ saved_export.description|chill_markdown_to_html }}

+ +
+{% endblock %} + +{% block content %} +
+ {{ include('@ChillMain/Util/confirmation_template.html.twig', + { + 'title' : 'saved_export.Delete saved ?'|trans, + 'confirm_question' : 'saved_export.Are you sure you want to delete this saved ?'|trans, + 'display_content' : block('display_content'), + 'cancel_route' : 'chill_main_export_saved_list_my', + 'cancel_parameters' : {}, + 'form' : delete_form + } ) }} +
+{% endblock %} diff --git a/src/Bundle/ChillMainBundle/Resources/views/SavedExport/edit.html.twig b/src/Bundle/ChillMainBundle/Resources/views/SavedExport/edit.html.twig new file mode 100644 index 000000000..937d42201 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Resources/views/SavedExport/edit.html.twig @@ -0,0 +1,21 @@ +{% extends "@ChillMain/layout.html.twig" %} + +{% block title %}{{ 'saved_export.Edit'|trans }}{% endblock %} + +{% block content %} +

{{ block('title') }}

+ + {{ form_start(form) }} + {{ form_row(form.title) }} + {{ form_row(form.description) }} + + + {{ form_end(form) }} +{% endblock %} \ No newline at end of file diff --git a/src/Bundle/ChillMainBundle/Resources/views/SavedExport/index.html.twig b/src/Bundle/ChillMainBundle/Resources/views/SavedExport/index.html.twig index 5cd697bb4..bfe377f6c 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/SavedExport/index.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/SavedExport/index.html.twig @@ -8,6 +8,10 @@
+ {% if total == 0 %} +

{{ 'saved_export.Any saved export'|trans }}

+ {% endif %} + {% for group, saveds in grouped_exports %} {% if group != '_' %}

{{ group }}

@@ -21,8 +25,8 @@
    -
  • -
  • +
  • +
@@ -47,8 +51,8 @@
    -
  • -
  • +
  • +
diff --git a/src/Bundle/ChillMainBundle/Security/Authorization/SavedExportVoter.php b/src/Bundle/ChillMainBundle/Security/Authorization/SavedExportVoter.php index bc74e2d45..667bced46 100644 --- a/src/Bundle/ChillMainBundle/Security/Authorization/SavedExportVoter.php +++ b/src/Bundle/ChillMainBundle/Security/Authorization/SavedExportVoter.php @@ -15,20 +15,33 @@ use Chill\MainBundle\Entity\SavedExport; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authorization\Voter\Voter; use UnexpectedValueException; +use function in_array; class SavedExportVoter extends Voter { + public const DELETE = 'CHLL_MAIN_EXPORT_SAVED_DELETE'; + + public const EDIT = 'CHLL_MAIN_EXPORT_SAVED_EDIT'; + public const GENERATE = 'CHLL_MAIN_EXPORT_SAVED_GENERATE'; + private const ALL = [ + self::DELETE, + self::EDIT, + self::GENERATE, + ]; + protected function supports($attribute, $subject): bool { - return $subject instanceof SavedExport && self::GENERATE === $attribute; + return $subject instanceof SavedExport && in_array($attribute, self::ALL, true); } protected function voteOnAttribute($attribute, $subject, TokenInterface $token): bool { /** @var SavedExport $subject */ switch ($attribute) { + case self::DELETE: + case self::EDIT: case self::GENERATE: return $subject->getUser() === $token->getUser(); diff --git a/src/Bundle/ChillMainBundle/translations/messages.fr.yml b/src/Bundle/ChillMainBundle/translations/messages.fr.yml index 498f185b6..1100d8ee3 100644 --- a/src/Bundle/ChillMainBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillMainBundle/translations/messages.fr.yml @@ -558,5 +558,11 @@ rolling_date: fixed_date_date: Date fixe saved_export: + Any saved export: Aucun rapport enregistré New: Nouveau rapport enregistré + Edit: Modifier un rapport enregistré + Delete saved ?: Supprimer un rapport enregistré ? + Are you sure you want to delete this saved ?: Êtes-vous sûr·e de vouloir supprimer ce rapport ? My saved exports: Mes rapports enregistrés + Export is deleted: Le rapport est supprimé + Saved export is saved!: Le rapport est enregistré \ No newline at end of file From be38251a13d0a336f2eedb04c88877141fa5b1e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Tue, 8 Nov 2022 19:59:43 +0100 Subject: [PATCH 73/73] Feature: [saved export] Switch between list of export and saved export thanks to nav at the list's top --- .../Resources/views/Export/_navbar.html.twig | 12 ++++ .../Resources/views/Export/layout.html.twig | 42 ++++++++----- .../views/SavedExport/index.html.twig | 59 ++++++++++++------- .../translations/messages.fr.yml | 3 +- 4 files changed, 78 insertions(+), 38 deletions(-) create mode 100644 src/Bundle/ChillMainBundle/Resources/views/Export/_navbar.html.twig diff --git a/src/Bundle/ChillMainBundle/Resources/views/Export/_navbar.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Export/_navbar.html.twig new file mode 100644 index 000000000..a7d7216b4 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Resources/views/Export/_navbar.html.twig @@ -0,0 +1,12 @@ + \ No newline at end of file diff --git a/src/Bundle/ChillMainBundle/Resources/views/Export/layout.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Export/layout.html.twig index 9cf53993d..5ce433535 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/Export/layout.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/Export/layout.html.twig @@ -22,9 +22,11 @@ {% block content %} + + {{ include('@ChillMain/Export/_navbar.html.twig', {'current' : 'common'}) }} +
-

{{ 'Exports list'|trans }}

- +
{% for group, exports in grouped_exports %}{% if group != '_' %} @@ -32,13 +34,17 @@
{% for export_alias, export in exports %}
-

{{ export.title|trans }}

-

{{ export.description|trans }}

-

- - {{ 'Create an export'|trans }} - -

+
+
+

{{ export.title|trans }}

+

{{ export.description|trans }}

+

+ + {{ 'Create an export'|trans }} + +

+
+
{% endfor %}
@@ -52,13 +58,17 @@ {% for export_alias,export in grouped_exports['_'] %}
-

{{ export.title|trans }}

-

{{ export.description|trans }}

-

- - {{ 'Create an export'|trans }} - -

+
+
+

{{ export.title|trans }}

+

{{ export.description|trans }}

+

+ + {{ 'Create an export'|trans }} + +

+
+
{% endfor %} diff --git a/src/Bundle/ChillMainBundle/Resources/views/SavedExport/index.html.twig b/src/Bundle/ChillMainBundle/Resources/views/SavedExport/index.html.twig index bfe377f6c..17792ecd2 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/SavedExport/index.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/SavedExport/index.html.twig @@ -4,7 +4,8 @@ {% block content %}
-

{{ block('title') }}

+ + {{ include('@ChillMain/Export/_navbar.html.twig', {'current' : 'my'}) }}
@@ -18,17 +19,25 @@
{% for s in saveds %}
-

{{ s.saved.title }}

-

{{ s.export.title|trans }}

-
- {{ s.saved.description|chill_markdown_to_html }} -
+
+
+

{{ s.saved.title }}

+

{{ s.export.title|trans }}

-
    -
  • -
  • -
  • -
+
+ {{ s.saved.description|chill_markdown_to_html }} +
+ +
{{ 'saved_export.Created on %date%'|trans({'%date%': s.saved.createdAt|format_datetime('long', 'short')}) }}
+ +
    +
  • +
  • +
  • +
+ +
+
{% endfor %}
@@ -44,17 +53,25 @@ {% for s in saveds %}
-

{{ s.saved.title }}

-

{{ s.export.title|trans }}

-
- {{ s.saved.description|chill_markdown_to_html }} -
+
+
+

{{ s.saved.title }}

+

{{ s.export.title|trans }}

-
    -
  • -
  • -
  • -
+
+ {{ s.saved.description|chill_markdown_to_html }} +
+ +
{{ 'saved_export.Created on %date%'|trans({'%date%': s.saved.createdAt|format_datetime('long', 'short')}) }}
+ +
    +
  • +
  • +
  • +
+ +
+
{% endfor %} diff --git a/src/Bundle/ChillMainBundle/translations/messages.fr.yml b/src/Bundle/ChillMainBundle/translations/messages.fr.yml index 1100d8ee3..58b3e976e 100644 --- a/src/Bundle/ChillMainBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillMainBundle/translations/messages.fr.yml @@ -565,4 +565,5 @@ saved_export: Are you sure you want to delete this saved ?: Êtes-vous sûr·e de vouloir supprimer ce rapport ? My saved exports: Mes rapports enregistrés Export is deleted: Le rapport est supprimé - Saved export is saved!: Le rapport est enregistré \ No newline at end of file + Saved export is saved!: Le rapport est enregistré + Created on %date%: Créé le %date% \ No newline at end of file