Merge branch '113-add-missing-filters' into 'master'

Add missing aggregators and filters

Closes #113

See merge request Chill-Projet/chill-bundles!567
This commit is contained in:
Julien Fastré 2023-07-05 20:08:20 +00:00
commit 0361743ae0
22 changed files with 806 additions and 15 deletions

View File

@ -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: ""

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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: ""

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -18,6 +18,7 @@ These are alias conventions :
| | SocialIssue::class | acp.socialIssues | acpsocialissue | | | SocialIssue::class | acp.socialIssues | acpsocialissue |
| | User::class | acp.user | acpuser | | | User::class | acp.user | acpuser |
| | AccompanyingPeriopStepHistory::class | acp.stepHistories | acpstephistories | | | AccompanyingPeriopStepHistory::class | acp.stepHistories | acpstephistories |
| | AccompanyingPeriodInfo::class | not existing (using custom WITH clause) | acpinfo |
| AccompanyingPeriodWork::class | | | acpw | | AccompanyingPeriodWork::class | | | acpw |
| | AccompanyingPeriodWorkEvaluation::class | acpw.accompanyingPeriodWorkEvaluations | workeval | | | AccompanyingPeriodWorkEvaluation::class | acpw.accompanyingPeriodWorkEvaluations | workeval |
| | User::class | acpw.referrers | acpwuser | | | User::class | acpw.referrers | acpwuser |
@ -28,6 +29,8 @@ These are alias conventions :
| | Person::class | acppart.person | partperson | | | Person::class | acppart.person | partperson |
| AccompanyingPeriodWorkEvaluation::class | | | workeval | | AccompanyingPeriodWorkEvaluation::class | | | workeval |
| | Evaluation::class | workeval.evaluation | eval | | | Evaluation::class | workeval.evaluation | eval |
| AccompanyingPeriodInfo::class | | | acpinfo |
| | User::class | acpinfo.user | acpinfo_user |
| Goal::class | | | goal | | Goal::class | | | goal |
| | Result::class | goal.results | goalresult | | | Result::class | goal.results | goalresult |
| Person::class | | | person | | Person::class | | | person |

View File

@ -18,8 +18,12 @@ use UnexpectedValueException;
class RollingDateConverter implements RollingDateConverterInterface class RollingDateConverter implements RollingDateConverterInterface
{ {
public function convert(RollingDate $rollingDate): DateTimeImmutable public function convert(?RollingDate $rollingDate): ?DateTimeImmutable
{ {
if (null === $rollingDate) {
return null;
}
switch ($rollingDate->getRoll()) { switch ($rollingDate->getRoll()) {
case RollingDate::T_MONTH_CURRENT_START: case RollingDate::T_MONTH_CURRENT_START:
return $this->toBeginOfMonth($rollingDate->getPivotDate()); return $this->toBeginOfMonth($rollingDate->getPivotDate());

View File

@ -15,5 +15,9 @@ use DateTimeImmutable;
interface RollingDateConverterInterface interface RollingDateConverterInterface
{ {
public function convert(RollingDate $rollingDate): DateTimeImmutable; /**
* @param RollingDate|null $rollingDate
* @return ($rollingDate is null ? null : DateTimeImmutable)
*/
public function convert(?RollingDate $rollingDate): ?DateTimeImmutable;
} }

View File

@ -0,0 +1,100 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Repository\UserJobRepositoryInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodInfo;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\Query\Expr\Join;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
final readonly class JobWorkingOnCourseAggregator implements AggregatorInterface
{
private const COLUMN_NAME = 'user_working_on_course_job_id';
public function __construct(
private UserJobRepositoryInterface $userJobRepository,
private TranslatableStringHelperInterface $translatableStringHelper,
) {
}
public function buildForm(FormBuilderInterface $builder)
{
// nothing to add here
}
public function getFormDefaultData(): array
{
return [];
}
public function getLabels($key, array $values, $data): \Closure
{
return function (int|string|null $jobId) {
if (null === $jobId || '' === $jobId) {
return '';
}
if ('_header' === $jobId) {
return 'export.aggregator.course.by_job_working.job';
}
if (null === $job = $this->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;
}
}

View File

@ -0,0 +1,101 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Repository\ScopeRepositoryInterface;
use Chill\MainBundle\Repository\UserJobRepositoryInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodInfo;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\Query\Expr\Join;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
final readonly class ScopeWorkingOnCourseAggregator implements AggregatorInterface
{
private const COLUMN_NAME = 'user_working_on_course_scope_id';
public function __construct(
private ScopeRepositoryInterface $scopeRepository,
private TranslatableStringHelperInterface $translatableStringHelper,
) {
}
public function buildForm(FormBuilderInterface $builder)
{
// nothing to add here
}
public function getFormDefaultData(): array
{
return [];
}
public function getLabels($key, array $values, $data): \Closure
{
return function (int|string|null $scopeId) {
if (null === $scopeId || '' === $scopeId) {
return '';
}
if ('_header' === $scopeId) {
return 'export.aggregator.course.by_scope_working.scope';
}
if (null === $scope = $this->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;
}
}

View File

@ -0,0 +1,100 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Repository\UserRepositoryInterface;
use Chill\MainBundle\Templating\Entity\UserRender;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodInfo;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\Query\Expr\Join;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
final readonly class UserWorkingOnCourseAggregator implements AggregatorInterface
{
private const COLUMN_NAME = 'user_working_on_course_user_id';
public function __construct(
private UserRender $userRender,
private UserRepositoryInterface $userRepository,
) {
}
public function buildForm(FormBuilderInterface $builder)
{
// nothing to add here
}
public function getFormDefaultData(): array
{
return [];
}
public function getLabels($key, array $values, $data): \Closure
{
return function (int|string|null $userId) {
if (null === $userId || '' === $userId) {
return '';
}
if ('_header' === $userId) {
return 'export.aggregator.course.by_user_working.user';
}
if (null === $user = $this->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;
}
}

View File

@ -0,0 +1,103 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Export\Aggregator\PersonAggregators;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Repository\CenterRepositoryInterface;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\PersonBundle\Export\Declarations;
use Closure;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
final readonly class CenterAggregator implements AggregatorInterface
{
private const COLUMN_NAME = 'person_center_aggregator';
public function __construct(
private CenterRepositoryInterface $centerRepository,
private RollingDateConverterInterface $rollingDateConverter,
) {
}
public function buildForm(FormBuilderInterface $builder)
{
$builder->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;
}
}

View File

@ -0,0 +1,130 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Entity\UserJob;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Form\Type\PickUserDynamicType;
use Chill\MainBundle\Repository\UserJobRepositoryInterface;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\MainBundle\Templating\Entity\UserRender;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface;
/**
* Filter course where a user with the given job is "working" on it
*
* Makes use of AccompanyingPeriodInfo
*/
readonly class JobWorkingOnCourseFilter implements FilterInterface
{
public function __construct(
private UserJobRepositoryInterface $userJobRepository,
private RollingDateConverterInterface $rollingDateConverter,
private TranslatableStringHelperInterface $translatableStringHelper,
) {
}
public function buildForm(FormBuilderInterface $builder): void
{
$jobs = $this->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;
}
}

View File

@ -38,7 +38,7 @@ class OpenBetweenDatesFilter implements FilterInterface
{ {
$clause = $qb->expr()->andX( $clause = $qb->expr()->andX(
$qb->expr()->gte('acp.openingDate', ':datefrom'), $qb->expr()->gte('acp.openingDate', ':datefrom'),
$qb->expr()->lte('acp.openingDate', ':dateto') $qb->expr()->lt('acp.openingDate', ':dateto')
); );
$qb->andWhere($clause); $qb->andWhere($clause);

View File

@ -0,0 +1,132 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters;
use Chill\MainBundle\Entity\Scope;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Entity\UserJob;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Form\Type\PickUserDynamicType;
use Chill\MainBundle\Repository\ScopeRepositoryInterface;
use Chill\MainBundle\Repository\UserJobRepositoryInterface;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\MainBundle\Templating\Entity\UserRender;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface;
/**
* Filter course where a user with the given scope is "working" on it
*
* Makes use of AccompanyingPeriodInfo
*/
readonly class ScopeWorkingOnCourseFilter implements FilterInterface
{
public function __construct(
private ScopeRepositoryInterface $scopeRepository,
private RollingDateConverterInterface $rollingDateConverter,
private TranslatableStringHelperInterface $translatableStringHelper,
) {
}
public function buildForm(FormBuilderInterface $builder): void
{
$scopes = $this->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;
}
}

View File

@ -13,7 +13,10 @@ namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters;
use Chill\MainBundle\Entity\User; use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Form\Type\PickUserDynamicType; 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\MainBundle\Templating\Entity\UserRender;
use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Export\Declarations;
@ -27,11 +30,9 @@ use Symfony\Component\Form\FormBuilderInterface;
*/ */
readonly class UserWorkingOnCourseFilter implements FilterInterface 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( public function __construct(
private UserRender $userRender, private UserRender $userRender,
private RollingDateConverterInterface $rollingDateConverter,
) { ) {
} }
@ -40,11 +41,23 @@ readonly class UserWorkingOnCourseFilter implements FilterInterface
$builder $builder
->add('users', PickUserDynamicType::class, [ ->add('users', PickUserDynamicType::class, [
'multiple' => true, '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 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 public function getTitle(): string
@ -55,7 +68,7 @@ readonly class UserWorkingOnCourseFilter implements FilterInterface
public function describeAction($data, $format = 'string'): array public function describeAction($data, $format = 'string'): array
{ {
return [ 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( '%users%' => implode(
', ', ', ',
array_map( array_map(
@ -63,6 +76,8 @@ readonly class UserWorkingOnCourseFilter implements FilterInterface
$data['users'] $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 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 $qb
->andWhere( ->andWhere(
$qb->expr()->exists( $qb->expr()->exists(
"SELECT 1 FROM " . AccompanyingPeriod\AccompanyingPeriodInfo::class . " " . self::AI_ALIAS . " " . "SELECT 1 FROM " . AccompanyingPeriod\AccompanyingPeriodInfo::class . " {$ai_alias} " .
"WHERE " . self::AI_ALIAS . ".user IN (:" . self::AI_USERS .") AND IDENTITY(" . self::AI_ALIAS . ".accompanyingPeriod) = acp.id" "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']))
; ;
} }

View File

@ -135,6 +135,14 @@ services:
tags: tags:
- { name: chill.export_filter, alias: accompanyingcourse_user_working_on_filter } - { 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: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\HavingAnAccompanyingPeriodInfoWithinDatesFilter:
tags: tags:
- { name: chill.export_filter, alias: accompanyingcourse_info_within_filter } - { name: chill.export_filter, alias: accompanyingcourse_info_within_filter }
@ -231,3 +239,15 @@ services:
Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\CreatorJobAggregator: Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\CreatorJobAggregator:
tags: tags:
- { name: chill.export_aggregator, alias: accompanyingcourse_creator_job_aggregator } - { 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 }

View File

@ -177,3 +177,8 @@ services:
tags: tags:
- { name: chill.export_aggregator, alias: person_household_compo_aggregator } - { name: chill.export_aggregator, alias: person_household_compo_aggregator }
Chill\PersonBundle\Export\Aggregator\PersonAggregators\CenterAggregator:
tags:
- { name: chill.export_aggregator, alias: person_center_aggregator }

View File

@ -372,7 +372,7 @@ Count people participating in an accompanying course by various parameters.: Com
Exports of accompanying courses: Exports des parcours d'accompagnement Exports of accompanying courses: Exports des parcours d'accompagnement
Count accompanying courses: Nombre de parcours Count accompanying courses: Nombre de parcours
Count accompanying courses by various parameters: Compte le nombre de parcours en fonction de différents filtres. 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. 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é 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 Household composition: Composition du ménage
Group course by household composition: Grouper les usagers par composition familiale Group course by household composition: Grouper les usagers par composition familiale
Calc date: Date de calcul de la composition du ménage 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: course:
by_referrer: by_referrer:
Computation date for referrer: Date à laquelle le référent était actif Computation date for referrer: Date à laquelle le référent était actif
@ -1032,6 +1037,15 @@ export:
Number of actions: Nombre d'actions Number of actions: Nombre d'actions
by_creator_job: by_creator_job:
Creator's job: Métier du créateur 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: course_work:
by_current_action: by_current_action:
Current action ?: Action en cours ? Current action ?: Action en cours ?
@ -1081,8 +1095,20 @@ export:
end_date: Fin de la période 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% 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: 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%': '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_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: by_step:
Filter by step: Filtrer les parcours par statut du parcours 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 Filter by step between dates: Filtrer les parcours par statut du parcours entre deux dates