diff --git a/.changes/unreleased/Feature-20231114-143558.yaml b/.changes/unreleased/Feature-20231114-143558.yaml new file mode 100644 index 000000000..d95691b73 --- /dev/null +++ b/.changes/unreleased/Feature-20231114-143558.yaml @@ -0,0 +1,6 @@ +kind: Feature +body: |+ + Add 3 new filters and 3 new aggregators for work action creator (with jobs and scopes) +time: 2023-11-14T14:35:58.099418749+01:00 +custom: + Issue: "204" diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 32b7e5221..dc3e39778 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -11,6 +11,10 @@ cache: services: - name: postgis/postgis:14-3.3-alpine alias: db + command: + - postgres + - "-c" + - max_connections=1000 - name: redis alias: redis diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/CreatorAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/CreatorAggregator.php new file mode 100644 index 000000000..f7c203a4e --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/CreatorAggregator.php @@ -0,0 +1,82 @@ +addSelect("IDENTITY(acpw.createdBy) AS {$p}_select") + ->addGroupBy("{$p}_select"); + } + + public function applyOn(): string + { + return Declarations::SOCIAL_WORK_ACTION_TYPE; + } + + public function buildForm(FormBuilderInterface $builder) {} + + public function getFormDefaultData(): array + { + return []; + } + + public function getLabels($key, array $values, mixed $data) + { + return function ($value): string { + if ('_header' === $value) { + return 'export.aggregator.course_work.by_creator.Creator'; + } + + if (null === $value || '' === $value) { + return ''; + } + + $r = $this->userRepository->find($value); + + return $this->userRender->renderString($r, ['absence' => false, 'user_job' => false, 'main_scope' => false]); + }; + } + + public function getQueryKeys($data): array + { + return [self::PREFIX.'_select']; + } + + public function getTitle(): string + { + return 'export.aggregator.course_work.by_creator.title'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/CreatorJobAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/CreatorJobAggregator.php new file mode 100644 index 000000000..1f7fdca5a --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/CreatorJobAggregator.php @@ -0,0 +1,99 @@ +leftJoin( + UserJobHistory::class, + "{$p}_history", + Join::WITH, + $qb->expr()->andX( + $qb->expr()->eq("{$p}_history.user", 'acpw.createdBy'), + $qb->expr()->andX( + $qb->expr()->lte("{$p}_history.startDate", 'acpw.createdAt'), + $qb->expr()->orX( + $qb->expr()->isNull("{$p}_history.endDate"), + $qb->expr()->gt("{$p}_history.endDate", 'acpw.createdAt') + ) + ) + ) + ) + ->addSelect("IDENTITY({$p}_history.job) AS {$p}_select") + ->addGroupBy("{$p}_select"); + } + + public function applyOn(): string + { + return Declarations::SOCIAL_WORK_ACTION_TYPE; + } + + public function buildForm(FormBuilderInterface $builder) {} + + public function getFormDefaultData(): array + { + return []; + } + + public function getLabels($key, array $values, mixed $data) + { + return function ($value): string { + if ('_header' === $value) { + return 'export.aggregator.course_work.by_creator_job.Creator\'s job'; + } + + if (null === $value || '' === $value || null === $j = $this->jobRepository->find($value)) { + return ''; + } + + return $this->translatableStringHelper->localize( + $j->getLabel() + ); + }; + } + + public function getQueryKeys($data): array + { + return [self::PREFIX.'_select']; + } + + public function getTitle(): string + { + return 'export.aggregator.course_work.by_creator_job.title'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/CreatorScopeAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/CreatorScopeAggregator.php new file mode 100644 index 000000000..c57607693 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/CreatorScopeAggregator.php @@ -0,0 +1,99 @@ +leftJoin( + UserScopeHistory::class, + "{$p}_history", + Join::WITH, + $qb->expr()->andX( + $qb->expr()->eq("{$p}_history.user", 'acpw.createdBy'), + $qb->expr()->andX( + $qb->expr()->lte("{$p}_history.startDate", 'acpw.createdAt'), + $qb->expr()->orX( + $qb->expr()->isNull("{$p}_history.endDate"), + $qb->expr()->gt("{$p}_history.endDate", 'acpw.createdAt') + ) + ) + ) + ) + ->addSelect("IDENTITY({$p}_history.scope) AS {$p}_select") + ->addGroupBy("{$p}_select"); + } + + public function applyOn(): string + { + return Declarations::SOCIAL_WORK_ACTION_TYPE; + } + + public function buildForm(FormBuilderInterface $builder) {} + + public function getFormDefaultData(): array + { + return []; + } + + public function getLabels($key, array $values, mixed $data) + { + return function ($value): string { + if ('_header' === $value) { + return 'export.aggregator.course_work.by_creator_scope.Creator\'s scope'; + } + + if (null === $value || '' === $value || null === $s = $this->scopeRepository->find($value)) { + return ''; + } + + return $this->translatableStringHelper->localize( + $s->getName() + ); + }; + } + + public function getQueryKeys($data): array + { + return [self::PREFIX.'_select']; + } + + public function getTitle(): string + { + return 'export.aggregator.course_work.by_creator_scope.title'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/CreatorFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/CreatorFilter.php new file mode 100644 index 000000000..100bcfdc4 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/CreatorFilter.php @@ -0,0 +1,74 @@ +leftJoin('acpw.createdBy', "{$p}_creator") + ->andWhere($qb->expr()->in("{$p}_creator", ":{$p}_creators")) + ->setParameter("{$p}_creators", $data['creators']); + } + + public function applyOn(): string + { + return Declarations::SOCIAL_WORK_ACTION_TYPE; + } + + public function buildForm(FormBuilderInterface $builder) + { + $builder + ->add('creators', PickUserDynamicType::class, [ + 'multiple' => true, + 'label' => 'export.filter.work.by_creator.Creators', + ]); + } + + public function describeAction($data, $format = 'string'): array + { + return [ + 'export.filter.work.by_creator.Filtered by creator: only %creators%', [ + '%creators%' => implode(', ', array_map(static fn (User $u) => $u->getLabel(), $data['creators'])), + ], + ]; + } + + public function getFormDefaultData(): array + { + return [ + 'creators' => [], + ]; + } + + public function getTitle(): string + { + return 'export.filter.work.by_creator.title'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/CreatorJobFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/CreatorJobFilter.php new file mode 100644 index 000000000..2b7a6ef07 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/CreatorJobFilter.php @@ -0,0 +1,107 @@ +leftJoin( + UserJobHistory::class, + "{$p}_history", + Join::WITH, + $qb->expr()->andX( + $qb->expr()->eq("{$p}_history.user", 'acpw.createdBy'), + $qb->expr()->andX( + $qb->expr()->lte("{$p}_history.startDate", 'acpw.createdAt'), + $qb->expr()->orX( + $qb->expr()->isNull("{$p}_history.endDate"), + $qb->expr()->gt("{$p}_history.endDate", 'acpw.createdAt') + ) + ) + ) + ) + ->andWhere($qb->expr()->in("{$p}_history.job", ":{$p}_jobs")) + ->setParameter("{$p}_jobs", $data['jobs']); + } + + public function applyOn(): string + { + return Declarations::SOCIAL_WORK_ACTION_TYPE; + } + + public function buildForm(FormBuilderInterface $builder) + { + $builder + ->add('jobs', EntityType::class, [ + 'class' => UserJob::class, + 'choices' => $this->userJobRepository->findAllActive(), + 'multiple' => true, + 'expanded' => true, + 'choice_label' => fn (UserJob $job) => $this->translatableStringHelper->localize($job->getLabel()), + 'label' => 'Job', + ]); + } + + public function describeAction($data, $format = 'string'): array + { + $creatorJobs = []; + + foreach ($data['jobs'] as $j) { + $creatorJobs[] = $this->translatableStringHelper->localize( + $j->getLabel() + ); + } + + return ['export.filter.work.by_creator_job.Filtered by creator job: only %jobs%', [ + '%jobs%' => implode(', ', $creatorJobs), + ]]; + } + + public function getFormDefaultData(): array + { + return [ + 'jobs' => [], + ]; + } + + public function getTitle(): string + { + return 'export.filter.work.by_creator_job.title'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/CreatorScopeFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/CreatorScopeFilter.php new file mode 100644 index 000000000..16ed52755 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/CreatorScopeFilter.php @@ -0,0 +1,107 @@ +leftJoin( + UserScopeHistory::class, + "{$p}_history", + Join::WITH, + $qb->expr()->andX( + $qb->expr()->eq("{$p}_history.user", 'acpw.createdBy'), + $qb->expr()->andX( + $qb->expr()->lte("{$p}_history.startDate", 'acpw.createdAt'), + $qb->expr()->orX( + $qb->expr()->isNull("{$p}_history.endDate"), + $qb->expr()->gt("{$p}_history.endDate", 'acpw.createdAt') + ) + ) + ) + ) + ->andWhere($qb->expr()->in("{$p}_history.scope", ":{$p}_scopes")) + ->setParameter("{$p}_scopes", $data['scopes']); + } + + public function applyOn(): string + { + return Declarations::SOCIAL_WORK_ACTION_TYPE; + } + + public function buildForm(FormBuilderInterface $builder) + { + $builder + ->add('scopes', EntityType::class, [ + 'class' => Scope::class, + 'choices' => $this->scopeRepository->findAllActive(), + 'choice_label' => fn (Scope $s) => $this->translatableStringHelper->localize($s->getName()), + 'multiple' => true, + 'expanded' => true, + 'label' => 'Scope', + ]); + } + + public function describeAction($data, $format = 'string'): array + { + $creatorScopes = []; + + foreach ($data['scopes'] as $s) { + $creatorScopes[] = $this->translatableStringHelper->localize( + $s->getName() + ); + } + + return ['export.filter.work.by_creator_scope.Filtered by creator scope: only %scopes%', [ + '%scopes%' => implode(', ', $creatorScopes), + ]]; + } + + public function getFormDefaultData(): array + { + return [ + 'scopes' => [], + ]; + } + + public function getTitle(): string + { + return 'export.filter.work.by_creator_scope.title'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/CreatorAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/CreatorAggregatorTest.php new file mode 100644 index 000000000..18921db7b --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/CreatorAggregatorTest.php @@ -0,0 +1,60 @@ +aggregator = self::$container->get(CreatorAggregator::class); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData() + { + return [ + [], + ]; + } + + public function getQueryBuilders() + { + self::bootKernel(); + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(acp.id)') + ->from(AccompanyingPeriod::class, 'acp') + ->join('acp.works', 'acpw'), + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/CreatorJobAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/CreatorJobAggregatorTest.php new file mode 100644 index 000000000..98699db0e --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/CreatorJobAggregatorTest.php @@ -0,0 +1,60 @@ +aggregator = self::$container->get(CreatorJobAggregator::class); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData() + { + return [ + [], + ]; + } + + public function getQueryBuilders() + { + self::bootKernel(); + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(acp.id)') + ->from(AccompanyingPeriod::class, 'acp') + ->join('acp.works', 'acpw'), + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/CreatorScopeAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/CreatorScopeAggregatorTest.php new file mode 100644 index 000000000..df424792c --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/CreatorScopeAggregatorTest.php @@ -0,0 +1,60 @@ +aggregator = self::$container->get(CreatorScopeAggregator::class); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData() + { + return [ + [], + ]; + } + + public function getQueryBuilders() + { + self::bootKernel(); + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(acp.id)') + ->from(AccompanyingPeriod::class, 'acp') + ->join('acp.works', 'acpw'), + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/CreatorFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/CreatorFilterTest.php new file mode 100644 index 000000000..a8ba33350 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/CreatorFilterTest.php @@ -0,0 +1,70 @@ +filter = self::$container->get(CreatorFilter::class); + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData() + { + self::bootKernel(); + + $em = self::$container->get(EntityManagerInterface::class); + + $creators = $em->createQuery('SELECT u FROM '.User::class.' u') + ->setMaxResults(1) + ->getResult(); + + return [ + [ + 'creators' => $creators, + ], + ]; + } + + public function getQueryBuilders() + { + self::bootKernel(); + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('acpw.id') + ->from(AccompanyingPeriodWork::class, 'acpw'), + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/CreatorJobFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/CreatorJobFilterTest.php new file mode 100644 index 000000000..51a4a288e --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/CreatorJobFilterTest.php @@ -0,0 +1,70 @@ +filter = self::$container->get(CreatorJobFilter::class); + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData() + { + self::bootKernel(); + $em = self::$container->get(EntityManagerInterface::class); + + $jobs = $em->createQuery('SELECT j FROM '.UserJob::class.' j') + ->setMaxResults(1) + ->getResult(); + + return [ + [ + 'jobs' => new ArrayCollection($jobs), + ], + ]; + } + + public function getQueryBuilders() + { + self::bootKernel(); + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('acpw.id') + ->from(AccompanyingPeriodWork::class, 'acpw'), + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/CreatorScopeFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/CreatorScopeFilterTest.php new file mode 100644 index 000000000..cac5856bd --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/CreatorScopeFilterTest.php @@ -0,0 +1,69 @@ +filter = self::$container->get(CreatorScopeFilter::class); + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData() + { + self::bootKernel(); + $em = self::$container->get(EntityManagerInterface::class); + + $scopes = $em->createQuery('SELECT s FROM '.Scope::class.' s') + ->setMaxResults(1) + ->getResult(); + + return [ + [ + 'scopes' => $scopes, + ], + ]; + } + + public function getQueryBuilders() + { + self::bootKernel(); + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('acpw.id') + ->from(AccompanyingPeriodWork::class, 'acpw'), + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/config/services/exports_social_actions.yaml b/src/Bundle/ChillPersonBundle/config/services/exports_social_actions.yaml index bda76f4a7..1987568e3 100644 --- a/src/Bundle/ChillPersonBundle/config/services/exports_social_actions.yaml +++ b/src/Bundle/ChillPersonBundle/config/services/exports_social_actions.yaml @@ -67,6 +67,19 @@ services: tags: - { name: chill.export_filter, alias: social_work_actions_end_btw_dates_filter } + Chill\PersonBundle\Export\Filter\SocialWorkFilters\CreatorFilter: + tags: + - { name: chill.export_filter, alias: social_work_actions_creator_filter } + + Chill\PersonBundle\Export\Filter\SocialWorkFilters\CreatorJobFilter: + tags: + - { name: chill.export_filter, alias: social_work_actions_creator_job_filter } + + Chill\PersonBundle\Export\Filter\SocialWorkFilters\CreatorScopeFilter: + tags: + - { name: chill.export_filter, alias: social_work_actions_creator_scope_filter } + + ## AGGREGATORS chill.person.export.aggregator_action_type: class: Chill\PersonBundle\Export\Aggregator\SocialWorkAggregators\ActionTypeAggregator @@ -110,6 +123,18 @@ services: tags: - { name: chill.export_aggregator, alias: accompanyingcourse_handling3party_aggregator } + Chill\PersonBundle\Export\Aggregator\SocialWorkAggregators\CreatorAggregator: + tags: + - { name: chill.export_aggregator, alias: social_work_actions_creator_aggregator } + + Chill\PersonBundle\Export\Aggregator\SocialWorkAggregators\CreatorJobAggregator: + tags: + - { name: chill.export_aggregator, alias: social_work_actions_creator_job_aggregator } + + Chill\PersonBundle\Export\Aggregator\SocialWorkAggregators\CreatorScopeAggregator: + tags: + - { name: chill.export_aggregator, alias: social_work_actions_creator_scope_aggregator } + Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\HandlingThirdPartyFilter: tags: - { name: chill.export_filter, alias: 'acpw_handling3party_filter'} diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index d55ccdfaf..08f76e485 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -573,7 +573,7 @@ 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 actions without end date: Filtre les actions sans date de fin (ouvertes) +Filter actions without end date: Filtrer les actions sans date de fin (ouvertes) Filtered actions without end date: 'Filtré: uniquement les actions sans date de fin (ouvertes)' 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 @@ -1095,6 +1095,15 @@ export: by_handling_third_party: title: Grouper les actions par tiers traitant header: Tiers traitant + by_creator: + title: Grouper les actions par créateur + Creator: Créateur de l'action + by_creator_job: + title: Grouper les actions par métier du créateur + Creator's job: Métier du créateur + by_creator_scope: + title: Grouper les actions par service du créateur + Creator's scope: Service du créateur eval: by_end_date: @@ -1198,14 +1207,14 @@ export: work: start_between_dates: - title: Filtre les actions d'accompagnement dont la date d'ouverture est entre deux dates + title: Filtrer les actions dont la date d'ouverture est entre deux dates start_date: Date de début end_date: Date de fin keep_null: Conserver les actions dont la date de début n'est pas indiquée keep_null_help: Si coché, les actions dont la date de début est vide seront prises en compte. Si non coché, elles ne seront pas comptabilisée. Only where start date is between %startDate% and %endDate%: Seulement les actions dont la date de début est entre le %startDate% et le %endDate% end_between_dates: - title: Filtre les actions d'accompagnement dont la date de clotûre est entre deux dates (ou l'action est toujours ouverte) + title: Filtrer les actions dont la date de clotûre est entre deux dates (ou l'action est toujours ouverte) start_date: Date de début end_date: Date de fin keep_null: Conserver les actions dont la date de fin n'est pas indiquée (actions en cours) @@ -1229,6 +1238,16 @@ export: title: Filtrer les actions par tiers traitant Only 3 parties %3parties%: "Seulement les actions d'accompagnement qui ont pour tiers traitant: %3parties%" pick_3parties: Tiers traitants des actions + by_creator: + title: Filtrer les actions par créateur + Creators: Créateur de l'action + "Filtered by creator: only %creators%": "Filtré par créateur de l'action: uniquement %creators%" + by_creator_job: + title: Filtrer les actions par métier du créateur + "Filtered by creator job: only %jobs%": "Filtré par métier du créateur: uniquement %jobs%" + by_creator_scope: + title: Filtrer les actions par service du créateur + "Filtered by creator scope: only %scopes%": "Filtré par service du créateur: uniquement %scopes%" list: person_with_acp: