From 4a5ac170ba30f548edaee801f08395ffa086cda0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Thu, 29 Jun 2023 13:16:40 +0200 Subject: [PATCH 1/3] [export] add dates for filter "user working on course" --- .../unreleased/Feature-20230629-131558.yaml | 6 +++ .../UserWorkingOnCourseFilter.php | 40 ++++++++++++++----- .../translations/messages.fr.yml | 4 +- 3 files changed, 40 insertions(+), 10 deletions(-) create mode 100644 .changes/unreleased/Feature-20230629-131558.yaml diff --git a/.changes/unreleased/Feature-20230629-131558.yaml b/.changes/unreleased/Feature-20230629-131558.yaml new file mode 100644 index 000000000..42d731c4f --- /dev/null +++ b/.changes/unreleased/Feature-20230629-131558.yaml @@ -0,0 +1,6 @@ +kind: Feature +body: '[export] on "filter by user working" on accompanying period, add two dates + to filters intervention within a period' +time: 2023-06-29T13:15:58.070316708+02:00 +custom: + Issue: "113" diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/UserWorkingOnCourseFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/UserWorkingOnCourseFilter.php index d078443af..1f9bfc61a 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/UserWorkingOnCourseFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/UserWorkingOnCourseFilter.php @@ -13,7 +13,10 @@ namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters; use Chill\MainBundle\Entity\User; use Chill\MainBundle\Export\FilterInterface; +use Chill\MainBundle\Form\Type\PickRollingDateType; use Chill\MainBundle\Form\Type\PickUserDynamicType; +use Chill\MainBundle\Service\RollingDate\RollingDate; +use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface; use Chill\MainBundle\Templating\Entity\UserRender; use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Export\Declarations; @@ -27,11 +30,9 @@ use Symfony\Component\Form\FormBuilderInterface; */ readonly class UserWorkingOnCourseFilter implements FilterInterface { - private const AI_ALIAS = 'user_working_on_course_filter_acc_info'; - private const AI_USERS = 'user_working_on_course_filter_users'; - public function __construct( private UserRender $userRender, + private RollingDateConverterInterface $rollingDateConverter, ) { } @@ -40,11 +41,23 @@ readonly class UserWorkingOnCourseFilter implements FilterInterface $builder ->add('users', PickUserDynamicType::class, [ 'multiple' => true, - ]); + ]) + ->add('start_date', PickRollingDateType::class, [ + 'label' => 'export.filter.course.by_user_working.User working after' + ]) + ->add('end_date', PickRollingDateType::class, [ + 'label' => 'export.filter.course.by_user_working.User working before' + ]) + ; } + public function getFormDefaultData(): array { - return []; + return [ + 'users' => [], + 'start_date' => new RollingDate(RollingDate::T_YEAR_CURRENT_START), + 'end_date' => new RollingDate(RollingDate::T_TODAY), + ]; } public function getTitle(): string @@ -55,7 +68,7 @@ readonly class UserWorkingOnCourseFilter implements FilterInterface public function describeAction($data, $format = 'string'): array { return [ - 'export.filter.course.by_user_working.Filtered by user working on course: only %users%', [ + 'export.filter.course.by_user_working.Filtered by user working on course: only %users%, between %start_date% and %end_date%', [ '%users%' => implode( ', ', array_map( @@ -63,6 +76,8 @@ readonly class UserWorkingOnCourseFilter implements FilterInterface $data['users'] ) ), + '%start_date%' => $this->rollingDateConverter->convert($data['start_date'])?->format('d-m-Y'), + '%end_date%' => $this->rollingDateConverter->convert($data['end_date'])?->format('d-m-Y'), ], ]; } @@ -74,14 +89,21 @@ readonly class UserWorkingOnCourseFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data): void { + $ai_alias = 'user_working_on_course_filter_acc_info'; + $ai_users = 'user_working_on_course_filter_users'; + $start = 'acp_use_work_on_start'; + $end = 'acp_use_work_on_end'; + $qb ->andWhere( $qb->expr()->exists( - "SELECT 1 FROM " . AccompanyingPeriod\AccompanyingPeriodInfo::class . " " . self::AI_ALIAS . " " . - "WHERE " . self::AI_ALIAS . ".user IN (:" . self::AI_USERS .") AND IDENTITY(" . self::AI_ALIAS . ".accompanyingPeriod) = acp.id" + "SELECT 1 FROM " . AccompanyingPeriod\AccompanyingPeriodInfo::class . " {$ai_alias} " . + "WHERE {$ai_alias}.user IN (:{$ai_users}) AND IDENTITY({$ai_alias}.accompanyingPeriod) = acp.id AND {$ai_alias}.infoDate >= :{$start} and {$ai_alias}.infoDate < :{$end}" ) ) - ->setParameter(self::AI_USERS, $data['users']) + ->setParameter($ai_users, $data['users']) + ->setParameter($start, $this->rollingDateConverter->convert($data['start_date'])) + ->setParameter($end, $this->rollingDateConverter->convert($data['end_date'])) ; } diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index 01383c050..464e8fa67 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -1082,7 +1082,9 @@ export: Only course with events between %startDate% and %endDate%: Seulement les parcours ayant reçu une intervention entre le %startDate% et le %endDate% by_user_working: title: Filter les parcours par intervenant - 'Filtered by user working on course: only %users%': 'Filtré par intervenants sur le parcours: seulement %users%' + 'Filtered by user working on course: only %users%, between %start_date% and %end_date%': 'Filtré par intervenants sur le parcours: seulement %users%, entre le %start_date% et le %end_date%' + User working after: Intervention après le + User working before: Intervention avant le by_step: Filter by step: Filtrer les parcours par statut du parcours Filter by step between dates: Filtrer les parcours par statut du parcours entre deux dates From 393e59e22bdab368ca6ca53fea6e079a8f1a19ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Thu, 29 Jun 2023 16:00:50 +0200 Subject: [PATCH 2/3] DX: Rolling date: allow to receive a null parameter (RollingDate) When receiving a null parameter (a null rolling date), it will return null --- .changes/unreleased/DX-20230629-160029.yaml | 5 +++++ .../Service/RollingDate/RollingDateConverter.php | 6 +++++- .../Service/RollingDate/RollingDateConverterInterface.php | 6 +++++- 3 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 .changes/unreleased/DX-20230629-160029.yaml diff --git a/.changes/unreleased/DX-20230629-160029.yaml b/.changes/unreleased/DX-20230629-160029.yaml new file mode 100644 index 000000000..b83befec6 --- /dev/null +++ b/.changes/unreleased/DX-20230629-160029.yaml @@ -0,0 +1,5 @@ +kind: DX +body: 'Rolling Date: can receive a null parameter' +time: 2023-06-29T16:00:29.664814895+02:00 +custom: + Issue: "" diff --git a/src/Bundle/ChillMainBundle/Service/RollingDate/RollingDateConverter.php b/src/Bundle/ChillMainBundle/Service/RollingDate/RollingDateConverter.php index 026ff7a8a..72ad89c58 100644 --- a/src/Bundle/ChillMainBundle/Service/RollingDate/RollingDateConverter.php +++ b/src/Bundle/ChillMainBundle/Service/RollingDate/RollingDateConverter.php @@ -18,8 +18,12 @@ use UnexpectedValueException; class RollingDateConverter implements RollingDateConverterInterface { - public function convert(RollingDate $rollingDate): DateTimeImmutable + public function convert(?RollingDate $rollingDate): ?DateTimeImmutable { + if (null === $rollingDate) { + return null; + } + switch ($rollingDate->getRoll()) { case RollingDate::T_MONTH_CURRENT_START: return $this->toBeginOfMonth($rollingDate->getPivotDate()); diff --git a/src/Bundle/ChillMainBundle/Service/RollingDate/RollingDateConverterInterface.php b/src/Bundle/ChillMainBundle/Service/RollingDate/RollingDateConverterInterface.php index b20a5ced2..6c7d9a5bd 100644 --- a/src/Bundle/ChillMainBundle/Service/RollingDate/RollingDateConverterInterface.php +++ b/src/Bundle/ChillMainBundle/Service/RollingDate/RollingDateConverterInterface.php @@ -15,5 +15,9 @@ use DateTimeImmutable; interface RollingDateConverterInterface { - public function convert(RollingDate $rollingDate): DateTimeImmutable; + /** + * @param RollingDate|null $rollingDate + * @return ($rollingDate is null ? null : DateTimeImmutable) + */ + public function convert(?RollingDate $rollingDate): ?DateTimeImmutable; } From 5a395b160fd990d423887a6451a15f1f52768f6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Thu, 29 Jun 2023 17:39:26 +0200 Subject: [PATCH 3/3] [export] add aggregator and filter related to AccompanyingPeriodInfo + Center aggregator for Person see #113 --- .../unreleased/Feature-20230629-173445.yaml | 5 + .../unreleased/Feature-20230629-173509.yaml | 5 + .../unreleased/Feature-20230629-173544.yaml | 5 + .../unreleased/Feature-20230629-173617.yaml | 5 + .../unreleased/Feature-20230629-173822.yaml | 5 + .../unreleased/Feature-20230629-173844.yaml | 5 + exports_alias_conventions.md | 3 + .../JobWorkingOnCourseAggregator.php | 100 +++++++++++++ .../ScopeWorkingOnCourseAggregator.php | 101 ++++++++++++++ .../UserWorkingOnCourseAggregator.php | 100 +++++++++++++ .../PersonAggregators/CenterAggregator.php | 103 ++++++++++++++ .../JobWorkingOnCourseFilter.php | 130 +++++++++++++++++ .../OpenBetweenDatesFilter.php | 2 +- .../ScopeWorkingOnCourseFilter.php | 132 ++++++++++++++++++ .../services/exports_accompanying_course.yaml | 20 +++ .../config/services/exports_person.yaml | 5 + .../translations/messages.fr.yml | 28 +++- 17 files changed, 751 insertions(+), 3 deletions(-) create mode 100644 .changes/unreleased/Feature-20230629-173445.yaml create mode 100644 .changes/unreleased/Feature-20230629-173509.yaml create mode 100644 .changes/unreleased/Feature-20230629-173544.yaml create mode 100644 .changes/unreleased/Feature-20230629-173617.yaml create mode 100644 .changes/unreleased/Feature-20230629-173822.yaml create mode 100644 .changes/unreleased/Feature-20230629-173844.yaml create mode 100644 src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/JobWorkingOnCourseAggregator.php create mode 100644 src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ScopeWorkingOnCourseAggregator.php create mode 100644 src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/UserWorkingOnCourseAggregator.php create mode 100644 src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/CenterAggregator.php create mode 100644 src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/JobWorkingOnCourseFilter.php create mode 100644 src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ScopeWorkingOnCourseFilter.php diff --git a/.changes/unreleased/Feature-20230629-173445.yaml b/.changes/unreleased/Feature-20230629-173445.yaml new file mode 100644 index 000000000..2d3642a85 --- /dev/null +++ b/.changes/unreleased/Feature-20230629-173445.yaml @@ -0,0 +1,5 @@ +kind: Feature +body: '[export] Add an aggregator by user''s job working on a course' +time: 2023-06-29T17:34:45.278993433+02:00 +custom: + Issue: "113" diff --git a/.changes/unreleased/Feature-20230629-173509.yaml b/.changes/unreleased/Feature-20230629-173509.yaml new file mode 100644 index 000000000..95fd23458 --- /dev/null +++ b/.changes/unreleased/Feature-20230629-173509.yaml @@ -0,0 +1,5 @@ +kind: Feature +body: '[export] add an aggregator by user''s scope working on a course' +time: 2023-06-29T17:35:09.548758741+02:00 +custom: + Issue: "113" diff --git a/.changes/unreleased/Feature-20230629-173544.yaml b/.changes/unreleased/Feature-20230629-173544.yaml new file mode 100644 index 000000000..94e79bc6d --- /dev/null +++ b/.changes/unreleased/Feature-20230629-173544.yaml @@ -0,0 +1,5 @@ +kind: Feature +body: '[export] on aggregator "user working on a course"' +time: 2023-06-29T17:35:44.998468724+02:00 +custom: + Issue: "" diff --git a/.changes/unreleased/Feature-20230629-173617.yaml b/.changes/unreleased/Feature-20230629-173617.yaml new file mode 100644 index 000000000..445b85cff --- /dev/null +++ b/.changes/unreleased/Feature-20230629-173617.yaml @@ -0,0 +1,5 @@ +kind: Feature +body: '[export] add a center aggregator for Person' +time: 2023-06-29T17:36:17.635876613+02:00 +custom: + Issue: "113" diff --git a/.changes/unreleased/Feature-20230629-173822.yaml b/.changes/unreleased/Feature-20230629-173822.yaml new file mode 100644 index 000000000..6a1569d00 --- /dev/null +++ b/.changes/unreleased/Feature-20230629-173822.yaml @@ -0,0 +1,5 @@ +kind: Feature +body: '[export] add a filter on "job working on a course"' +time: 2023-06-29T17:38:22.682951416+02:00 +custom: + Issue: "113" diff --git a/.changes/unreleased/Feature-20230629-173844.yaml b/.changes/unreleased/Feature-20230629-173844.yaml new file mode 100644 index 000000000..f307ebf57 --- /dev/null +++ b/.changes/unreleased/Feature-20230629-173844.yaml @@ -0,0 +1,5 @@ +kind: Feature +body: '[export] Add a filter on "scope working on a course"' +time: 2023-06-29T17:38:44.238287822+02:00 +custom: + Issue: "113" diff --git a/exports_alias_conventions.md b/exports_alias_conventions.md index fd7844691..64df91030 100644 --- a/exports_alias_conventions.md +++ b/exports_alias_conventions.md @@ -18,6 +18,7 @@ These are alias conventions : | | SocialIssue::class | acp.socialIssues | acpsocialissue | | | User::class | acp.user | acpuser | | | AccompanyingPeriopStepHistory::class | acp.stepHistories | acpstephistories | +| | AccompanyingPeriodInfo::class | not existing (using custom WITH clause) | acpinfo | | AccompanyingPeriodWork::class | | | acpw | | | AccompanyingPeriodWorkEvaluation::class | acpw.accompanyingPeriodWorkEvaluations | workeval | | | User::class | acpw.referrers | acpwuser | @@ -28,6 +29,8 @@ These are alias conventions : | | Person::class | acppart.person | partperson | | AccompanyingPeriodWorkEvaluation::class | | | workeval | | | Evaluation::class | workeval.evaluation | eval | +| AccompanyingPeriodInfo::class | | | acpinfo | +| | User::class | acpinfo.user | acpinfo_user | | Goal::class | | | goal | | | Result::class | goal.results | goalresult | | Person::class | | | person | diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/JobWorkingOnCourseAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/JobWorkingOnCourseAggregator.php new file mode 100644 index 000000000..e93300e85 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/JobWorkingOnCourseAggregator.php @@ -0,0 +1,100 @@ +userJobRepository->find((int) $jobId)) { + return ''; + } + + return $this->translatableStringHelper->localize($job->getLabel()); + }; + } + + public function getQueryKeys($data) + { + return [self::COLUMN_NAME]; + } + + public function getTitle() + { + return 'export.aggregator.course.by_job_working.title'; + } + + public function addRole(): ?string + { + return null; + } + + public function alterQuery(QueryBuilder $qb, $data) + { + if (!in_array('acpinfo', $qb->getAllAliases(), true)) { + $qb->leftJoin( + AccompanyingPeriodInfo::class, + 'acpinfo', + Join::WITH, + 'acp.id = IDENTITY(acpinfo.accompanyingPeriod)' + ); + } + + if (!in_array('acpinfo_user', $qb->getAllAliases(), true)) { + $qb->leftJoin('acpinfo.user', 'acpinfo_user'); + } + + $qb->addSelect('IDENTITY(acpinfo_user.userJob) AS ' . self::COLUMN_NAME); + $qb->addGroupBy(self::COLUMN_NAME); + } + + public function applyOn() + { + return Declarations::ACP_TYPE; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ScopeWorkingOnCourseAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ScopeWorkingOnCourseAggregator.php new file mode 100644 index 000000000..b9f493af9 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ScopeWorkingOnCourseAggregator.php @@ -0,0 +1,101 @@ +scopeRepository->find((int) $scopeId)) { + return ''; + } + + return $this->translatableStringHelper->localize($scope->getName()); + }; + } + + public function getQueryKeys($data) + { + return [self::COLUMN_NAME]; + } + + public function getTitle() + { + return 'export.aggregator.course.by_scope_working.title'; + } + + public function addRole(): ?string + { + return null; + } + + public function alterQuery(QueryBuilder $qb, $data) + { + if (!in_array('acpinfo', $qb->getAllAliases(), true)) { + $qb->leftJoin( + AccompanyingPeriodInfo::class, + 'acpinfo', + Join::WITH, + 'acp.id = IDENTITY(acpinfo.accompanyingPeriod)' + ); + } + + if (!in_array('acpinfo_user', $qb->getAllAliases(), true)) { + $qb->leftJoin('acpinfo.user', 'acpinfo_user'); + } + + $qb->addSelect('IDENTITY(acpinfo_user.mainScope) AS ' . self::COLUMN_NAME); + $qb->addGroupBy(self::COLUMN_NAME); + } + + public function applyOn() + { + return Declarations::ACP_TYPE; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/UserWorkingOnCourseAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/UserWorkingOnCourseAggregator.php new file mode 100644 index 000000000..b4941fa01 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/UserWorkingOnCourseAggregator.php @@ -0,0 +1,100 @@ +userRepository->find((int) $userId)) { + return ''; + } + + return $this->userRender->renderString($user, []); + }; + } + + public function getQueryKeys($data) + { + return [self::COLUMN_NAME]; + } + + public function getTitle() + { + return 'export.aggregator.course.by_user_working.title'; + } + + public function addRole(): ?string + { + return null; + } + + public function alterQuery(QueryBuilder $qb, $data) + { + if (!in_array('acpinfo', $qb->getAllAliases(), true)) { + $qb->leftJoin( + AccompanyingPeriodInfo::class, + 'acpinfo', + Join::WITH, + 'acp.id = IDENTITY(acpinfo.accompanyingPeriod)' + ); + } + + if (!in_array('acpinfo_user', $qb->getAllAliases(), true)) { + $qb->leftJoin('acpinfo.user', 'acpinfo_user'); + } + + $qb->addSelect('acpinfo_user.id AS ' . self::COLUMN_NAME); + $qb->addGroupBy('acpinfo_user.id'); + } + + public function applyOn() + { + return Declarations::ACP_TYPE; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/CenterAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/CenterAggregator.php new file mode 100644 index 000000000..9be0b0c7e --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/CenterAggregator.php @@ -0,0 +1,103 @@ +add('at_date', PickRollingDateType::class, [ + 'label' => 'export.aggregator.person.by_center.at_date', + ]); + } + + public function getFormDefaultData(): array + { + return [ + 'at_date' => new RollingDate(RollingDate::T_TODAY) + ]; + } + + public function getLabels($key, array $values, $data): Closure + { + return function (int|string|null $value) { + if (null === $value || '' === $value) { + return ''; + } + + if ('_header' === $value) { + return 'export.aggregator.person.by_center.center'; + } + + return (string) $this->centerRepository->find((int) $value)?->getName(); + }; + } + + public function getQueryKeys($data) + { + return [self::COLUMN_NAME]; + } + + public function getTitle() + { + return 'export.aggregator.person.by_center.title'; + } + + public function addRole(): ?string + { + return null; + } + + public function alterQuery(QueryBuilder $qb, $data) + { + $alias = 'pers_center_agg'; + $atDate = 'pers_center_agg_at_date'; + + $qb->leftJoin('person.centerHistory', $alias); + $qb + ->andWhere( + $qb->expr()->lte($alias.'.startDate', ':'.$atDate), + )->andWhere( + $qb->expr()->orX( + $qb->expr()->isNull($alias.'.endDate'), + $qb->expr()->gt($alias.'.endDate', ':'.$atDate) + ) + ); + $qb->setParameter($atDate, $this->rollingDateConverter->convert($data['at_date'])); + + $qb->addSelect("IDENTITY({$alias}.center) AS " . self::COLUMN_NAME); + $qb->addGroupBy(self::COLUMN_NAME); + } + + public function applyOn() + { + return Declarations::PERSON_TYPE; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/JobWorkingOnCourseFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/JobWorkingOnCourseFilter.php new file mode 100644 index 000000000..63a668b6d --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/JobWorkingOnCourseFilter.php @@ -0,0 +1,130 @@ +userJobRepository->findAllActive(); + usort($jobs, fn (UserJob $a, UserJob $b) => $this->translatableStringHelper->localize($a->getLabel()) <=> $this->translatableStringHelper->localize($b->getLabel())); + + $builder + ->add('jobs', EntityType::class, [ + 'class' => UserJob::class, + 'choices' => $jobs, + 'choice_label' => fn (UserJob $userJob) => $this->translatableStringHelper->localize($userJob->getLabel()), + 'multiple' => true, + 'expanded' => true, + ]) + ->add('start_date', PickRollingDateType::class, [ + 'label' => 'export.filter.course.by_job_working.Job working after' + ]) + ->add('end_date', PickRollingDateType::class, [ + 'label' => 'export.filter.course.by_job_working.Job working before' + ]) + ; + } + + public function getFormDefaultData(): array + { + return [ + 'jobs' => [], + 'start_date' => new RollingDate(RollingDate::T_YEAR_CURRENT_START), + 'end_date' => new RollingDate(RollingDate::T_TODAY), + ]; + } + + public function getTitle(): string + { + return 'export.filter.course.by_job_working.title'; + } + + public function describeAction($data, $format = 'string'): array + { + return [ + 'export.filter.course.by_job_working.Filtered by job working on course: only %jobs%, between %start_date% and %end_date%', [ + '%jobs%' => implode( + ', ', + array_map( + fn (UserJob $userJob) => $this->translatableStringHelper->localize($userJob->getLabel()), + $data['jobs'] + ) + ), + '%start_date%' => $this->rollingDateConverter->convert($data['start_date'])?->format('d-m-Y'), + '%end_date%' => $this->rollingDateConverter->convert($data['end_date'])?->format('d-m-Y'), + ], + ]; + } + + public function addRole(): ?string + { + return null; + } + + public function alterQuery(QueryBuilder $qb, $data): void + { + $ai_alias = 'jobs_working_on_course_filter_acc_info'; + $ai_user_alias = 'jobs_working_on_course_filter_user'; + $ai_jobs = 'jobs_working_on_course_filter_jobs'; + $start = 'acp_jobs_work_on_start'; + $end = 'acp_jobs_work_on_end'; + + $qb + ->andWhere( + $qb->expr()->exists( + "SELECT 1 FROM " . AccompanyingPeriod\AccompanyingPeriodInfo::class . " {$ai_alias} JOIN {$ai_alias}.user {$ai_user_alias} " . + "WHERE IDENTITY({$ai_alias}.accompanyingPeriod) = acp.id + AND {$ai_user_alias}.userJob IN (:{$ai_jobs}) + AND {$ai_alias}.infoDate >= :{$start} and {$ai_alias}.infoDate < :{$end} + " + ) + ) + ->setParameter($ai_jobs, $data['jobs']) + ->setParameter($start, $this->rollingDateConverter->convert($data['start_date'])) + ->setParameter($end, $this->rollingDateConverter->convert($data['end_date'])) + ; + } + + public function applyOn(): string + { + return Declarations::ACP_TYPE; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/OpenBetweenDatesFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/OpenBetweenDatesFilter.php index e9413083f..69fdd7bc0 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/OpenBetweenDatesFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/OpenBetweenDatesFilter.php @@ -38,7 +38,7 @@ class OpenBetweenDatesFilter implements FilterInterface { $clause = $qb->expr()->andX( $qb->expr()->gte('acp.openingDate', ':datefrom'), - $qb->expr()->lte('acp.openingDate', ':dateto') + $qb->expr()->lt('acp.openingDate', ':dateto') ); $qb->andWhere($clause); diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ScopeWorkingOnCourseFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ScopeWorkingOnCourseFilter.php new file mode 100644 index 000000000..b9787bf52 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ScopeWorkingOnCourseFilter.php @@ -0,0 +1,132 @@ +scopeRepository->findAllActive(); + usort($scopes, fn (Scope $a, Scope $b) => $this->translatableStringHelper->localize($a->getName()) <=> $this->translatableStringHelper->localize($b->getName())); + + $builder + ->add('scopes', EntityType::class, [ + 'class' => Scope::class, + 'choices' => $scopes, + 'choice_label' => fn (Scope $scope) => $this->translatableStringHelper->localize($scope->getName()), + 'multiple' => true, + 'expanded' => true, + ]) + ->add('start_date', PickRollingDateType::class, [ + 'label' => 'export.filter.course.by_scope_working.Scope working after' + ]) + ->add('end_date', PickRollingDateType::class, [ + 'label' => 'export.filter.course.by_scope_working.Scope working before' + ]) + ; + } + + public function getFormDefaultData(): array + { + return [ + 'scopes' => [], + 'start_date' => new RollingDate(RollingDate::T_YEAR_CURRENT_START), + 'end_date' => new RollingDate(RollingDate::T_TODAY), + ]; + } + + public function getTitle(): string + { + return 'export.filter.course.by_scope_working.title'; + } + + public function describeAction($data, $format = 'string'): array + { + return [ + 'export.filter.course.by_scope_working.Filtered by scope working on course: only %scopes%, between %start_date% and %end_date%', [ + '%scopes%' => implode( + ', ', + array_map( + fn (Scope $scope) => $this->translatableStringHelper->localize($scope->getName()), + $data['scopes'] + ) + ), + '%start_date%' => $this->rollingDateConverter->convert($data['start_date'])?->format('d-m-Y'), + '%end_date%' => $this->rollingDateConverter->convert($data['end_date'])?->format('d-m-Y'), + ], + ]; + } + + public function addRole(): ?string + { + return null; + } + + public function alterQuery(QueryBuilder $qb, $data): void + { + $ai_alias = 'scopes_working_on_course_filter_acc_info'; + $ai_user_alias = 'scopes_working_on_course_filter_user'; + $ai_scopes = 'scopes_working_on_course_filter_scopes'; + $start = 'acp_scopes_work_on_start'; + $end = 'acp_scopes_work_on_end'; + + $qb + ->andWhere( + $qb->expr()->exists( + "SELECT 1 FROM " . AccompanyingPeriod\AccompanyingPeriodInfo::class . " {$ai_alias} JOIN {$ai_alias}.user {$ai_user_alias} " . + "WHERE IDENTITY({$ai_alias}.accompanyingPeriod) = acp.id + AND {$ai_user_alias}.mainScope IN (:{$ai_scopes}) + AND {$ai_alias}.infoDate >= :{$start} and {$ai_alias}.infoDate < :{$end} + " + ) + ) + ->setParameter($ai_scopes, $data['scopes']) + ->setParameter($start, $this->rollingDateConverter->convert($data['start_date'])) + ->setParameter($end, $this->rollingDateConverter->convert($data['end_date'])) + ; + } + + public function applyOn(): string + { + return Declarations::ACP_TYPE; + } +} diff --git a/src/Bundle/ChillPersonBundle/config/services/exports_accompanying_course.yaml b/src/Bundle/ChillPersonBundle/config/services/exports_accompanying_course.yaml index c299bbaaa..c5a2dba68 100644 --- a/src/Bundle/ChillPersonBundle/config/services/exports_accompanying_course.yaml +++ b/src/Bundle/ChillPersonBundle/config/services/exports_accompanying_course.yaml @@ -135,6 +135,14 @@ services: tags: - { name: chill.export_filter, alias: accompanyingcourse_user_working_on_filter } + Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\JobWorkingOnCourseFilter: + tags: + - { name: chill.export_filter, alias: accompanyingcourse_job_working_on_filter } + + Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\ScopeWorkingOnCourseFilter: + tags: + - { name: chill.export_filter, alias: accompanyingcourse_scope_working_on_filter } + Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\HavingAnAccompanyingPeriodInfoWithinDatesFilter: tags: - { name: chill.export_filter, alias: accompanyingcourse_info_within_filter } @@ -231,3 +239,15 @@ services: Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\CreatorJobAggregator: tags: - { name: chill.export_aggregator, alias: accompanyingcourse_creator_job_aggregator } + + Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\UserWorkingOnCourseAggregator: + tags: + - { name: chill.export_aggregator, alias: accompanyingcourse_user_working_on_course_aggregator } + + Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\JobWorkingOnCourseAggregator: + tags: + - { name: chill.export_aggregator, alias: accompanyingcourse_job_working_on_course_aggregator } + + Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\ScopeWorkingOnCourseAggregator: + tags: + - { name: chill.export_aggregator, alias: accompanyingcourse_scope_working_on_course_aggregator } diff --git a/src/Bundle/ChillPersonBundle/config/services/exports_person.yaml b/src/Bundle/ChillPersonBundle/config/services/exports_person.yaml index bf372ea06..64360aee9 100644 --- a/src/Bundle/ChillPersonBundle/config/services/exports_person.yaml +++ b/src/Bundle/ChillPersonBundle/config/services/exports_person.yaml @@ -177,3 +177,8 @@ services: tags: - { name: chill.export_aggregator, alias: person_household_compo_aggregator } + Chill\PersonBundle\Export\Aggregator\PersonAggregators\CenterAggregator: + tags: + - { name: chill.export_aggregator, alias: person_center_aggregator } + + diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index 464e8fa67..2f7800e2b 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -372,7 +372,7 @@ Count people participating in an accompanying course by various parameters.: Com Exports of accompanying courses: Exports des parcours d'accompagnement Count accompanying courses: Nombre de parcours Count accompanying courses by various parameters: Compte le nombre de parcours en fonction de différents filtres. -Accompanying courses participation duration and number of participations: Durée moyenne et nombre des participation des usagers aux parcours +Accompanying courses participation duration and number of participations: Durée moyenne et nombre des participations des usagers aux parcours Create an average of accompanying courses duration of each person participation to accompanying course, according to filters on persons, accompanying course: Crée un rapport qui comptabilise la moyenne de la durée de participation de chaque usager concerné aux parcours, avec différents filtres, notamment sur les usagers concernés. Closingdate to apply: Date de fin à prendre en compte lorsque le parcours n'est pas clotûré @@ -1016,6 +1016,11 @@ export: Household composition: Composition du ménage Group course by household composition: Grouper les usagers par composition familiale Calc date: Date de calcul de la composition du ménage + by_center: + title: Grouper les usagers par centre + at_date: Date de calcul du centre + center: Centre de l'usager + course: by_referrer: Computation date for referrer: Date à laquelle le référent était actif @@ -1032,6 +1037,15 @@ export: Number of actions: Nombre d'actions by_creator_job: Creator's job: Métier du créateur + by_user_working: + title: Grouper les parcours par intervenant + user: Intervenant + by_job_working: + title: Grouper les parcours par métier de l'intervenant + job: Métier de l'intervenant + by_scope_working: + title: Grouper les parcours par service de l'intervenant + scope: Service de l'intervenant course_work: by_current_action: Current action ?: Action en cours ? @@ -1081,10 +1095,20 @@ export: end_date: Fin de la période Only course with events between %startDate% and %endDate%: Seulement les parcours ayant reçu une intervention entre le %startDate% et le %endDate% by_user_working: - title: Filter les parcours par intervenant + title: Filter les parcours par intervenant, entre deux dates 'Filtered by user working on course: only %users%, between %start_date% and %end_date%': 'Filtré par intervenants sur le parcours: seulement %users%, entre le %start_date% et le %end_date%' User working after: Intervention après le User working before: Intervention avant le + by_job_working: + title: Filtrer les parcours par métier de l'intervenant, entre deux dates + 'Filtered by job working on course: only %jobs%, between %start_date% and %end_date%': 'Filtré par métier des intervenants sur le parcours: seulement %jobs%, entre le %start_date% et le %end_date%' + Job working after: Intervention après le + Job working before: Intervention avant le + by_scope_working: + title: Filtrer les parcours par service de l'intervenant, entre deux dates + 'Filtered by scope working on course: only %scopes%, between %start_date% and %end_date%': 'Filtré par service des intervenants sur le parcours: seulement %scopes%, entre le %start_date% et le %end_date%' + Scope working after: Intervention après le + Scope working before: Intervention avant le by_step: Filter by step: Filtrer les parcours par statut du parcours Filter by step between dates: Filtrer les parcours par statut du parcours entre deux dates