Merge branch '244-record-closing-motive-on-acc-period-closing' into 'master'

[Export] Ajout de la comptabilisation du nombre de changements de statuts du parcours

Closes #244

See merge request Chill-Projet/chill-bundles!650
This commit is contained in:
2024-02-07 15:09:09 +00:00
24 changed files with 1206 additions and 78 deletions

View File

@@ -0,0 +1,84 @@
<?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\AccompanyingPeriodStepHistoryAggregators;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Repository\AccompanyingPeriod\ClosingMotiveRepositoryInterface;
use Chill\PersonBundle\Templating\Entity\ClosingMotiveRender;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
final readonly class ByClosingMotiveAggregator implements AggregatorInterface
{
private const KEY = 'acpstephistory_by_closing_motive_agg';
public function __construct(
private ClosingMotiveRepositoryInterface $closingMotiveRepository,
private ClosingMotiveRender $closingMotiveRender,
) {
}
public function buildForm(FormBuilderInterface $builder)
{
// nothing to add here
}
public function getFormDefaultData(): array
{
return [];
}
public function getLabels($key, array $values, mixed $data)
{
return function (int|string|null $value): string {
if ('_header' === $value) {
return 'export.aggregator.step_history.by_closing_motive.header';
}
if (null === $value || '' === $value || null === $closingMotive = $this->closingMotiveRepository->find((int) $value)) {
return '';
}
return $this->closingMotiveRender->renderString($closingMotive, []);
};
}
public function getQueryKeys($data)
{
return [
self::KEY,
];
}
public function getTitle()
{
return 'export.aggregator.step_history.by_closing_motive.title';
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$qb
->addSelect('IDENTITY(acpstephistory.closingMotive) AS '.self::KEY)
->addGroupBy(self::KEY);
}
public function applyOn()
{
return Declarations::ACP_STEP_HISTORY;
}
}

View File

@@ -0,0 +1,90 @@
<?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\AccompanyingPeriodStepHistoryAggregators;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Export\Enum\DateGroupingChoiceEnum;
use Chill\PersonBundle\Tests\Export\Aggregator\AccompanyingPeriodStepHistoryAggregators\ByDateAggregatorTest;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
/**
* @see ByDateAggregatorTest
*/
final readonly class ByDateAggregator implements AggregatorInterface
{
private const KEY = 'acpstephistory_by_date_agg';
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('frequency', ChoiceType::class, [
'choices' => array_combine(
array_map(fn (DateGroupingChoiceEnum $c) => 'export.enum.frequency.'.$c->value, DateGroupingChoiceEnum::cases()),
array_map(fn (DateGroupingChoiceEnum $c) => $c->value, DateGroupingChoiceEnum::cases()),
),
'label' => 'export.aggregator.course.by_opening_date.frequency',
'multiple' => false,
'expanded' => true,
]);
}
public function getFormDefaultData(): array
{
return ['frequency' => DateGroupingChoiceEnum::YEAR->value];
}
public function getLabels($key, array $values, mixed $data)
{
return function (?string $value): string {
if ('_header' === $value) {
return 'export.aggregator.step_history.by_date.header';
}
if (null === $value || '' === $value) {
return '';
}
return $value;
};
}
public function getQueryKeys($data)
{
return [self::KEY];
}
public function getTitle()
{
return 'export.aggregator.step_history.by_date.title';
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$p = self::KEY;
$qb->addSelect(sprintf("TO_CHAR(acpstephistory.startDate, '%s') AS {$p}", $data['frequency']));
$qb->addGroupBy($p);
$qb->addOrderBy($p, 'DESC');
}
public function applyOn()
{
return Declarations::ACP_STEP_HISTORY;
}
}

View File

@@ -0,0 +1,86 @@
<?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\AccompanyingPeriodStepHistoryAggregators;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Tests\Export\Aggregator\AccompanyingPeriodStepHistoryAggregators\ByStepAggregatorTest;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
* @see ByStepAggregatorTest
*/
final readonly class ByStepAggregator implements AggregatorInterface
{
private const KEY = 'acpstephistory_step_agg';
public function __construct(
private TranslatorInterface $translator
) {
}
public function buildForm(FormBuilderInterface $builder)
{
// nothing in this form
}
public function getFormDefaultData(): array
{
return [];
}
public function getLabels($key, array $values, mixed $data)
{
return function (?string $step): string {
if ('_header' === $step) {
return 'export.aggregator.step_history.by_step.header';
}
if (null === $step || '' === $step) {
return '';
}
return $this->translator->trans('accompanying_period.'.$step);
};
}
public function getQueryKeys($data)
{
return [
self::KEY,
];
}
public function getTitle()
{
return 'export.aggregator.step_history.by_step.title';
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$qb
->addSelect('acpstephistory.step AS '.self::KEY)
->addGroupBy(self::KEY);
}
public function applyOn()
{
return Declarations::ACP_STEP_HISTORY;
}
}

View File

@@ -18,6 +18,8 @@ abstract class Declarations
{
final public const ACP_TYPE = 'accompanying_period';
final public const ACP_STEP_HISTORY = 'accompanying_period_step_history';
final public const EVAL_TYPE = 'evaluation';
final public const HOUSEHOLD_TYPE = 'household';

View File

@@ -0,0 +1,139 @@
<?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\Export;
use Chill\MainBundle\Export\AccompanyingCourseExportHelper;
use Chill\MainBundle\Export\ExportInterface;
use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\GroupedExportInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person\PersonCenterHistory;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\Form\FormBuilderInterface;
class CountAccompanyingCourseStepHistory implements ExportInterface, GroupedExportInterface
{
protected EntityRepository $repository;
private readonly bool $filterStatsByCenters;
public function __construct(
private readonly EntityManagerInterface $em,
ParameterBagInterface $parameterBag,
) {
$this->repository = $em->getRepository(AccompanyingPeriod::class);
$this->filterStatsByCenters = $parameterBag->get('chill_main')['acl']['filter_stats_by_center'];
}
public function buildForm(FormBuilderInterface $builder): void
{
// Nothing to add here
}
public function getFormDefaultData(): array
{
return [];
}
public function getAllowedFormattersTypes(): array
{
return [FormatterInterface::TYPE_TABULAR];
}
public function getDescription(): string
{
return 'export.export.acp_closing.description';
}
public function getGroup(): string
{
return 'Exports of accompanying courses';
}
public function getLabels($key, array $values, $data)
{
if ('export_result' !== $key) {
throw new \LogicException("the key {$key} is not used by this export");
}
return fn ($value) => '_header' === $value ? $this->getTitle() : $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 'export.export.acp_closing.title';
}
public function getType(): string
{
return Declarations::ACP_TYPE;
}
public function initiateQuery(array $requiredModifiers, array $acl, array $data = []): QueryBuilder
{
$centers = array_map(static fn ($el) => $el['center'], $acl);
$qb = $this->em->createQueryBuilder()
->select('COUNT(DISTINCT acpstephistory.id) As export_result')
->from(AccompanyingPeriod\AccompanyingPeriodStepHistory::class, 'acpstephistory')
->join('acpstephistory.period', 'acp');
$qb
->leftJoin('acp.participations', 'acppart')
->leftJoin('acppart.person', 'person');
if ($this->filterStatsByCenters) {
$qb
->andWhere(
$qb->expr()->exists(
'SELECT 1 FROM '.PersonCenterHistory::class.' acl_count_person_history WHERE acl_count_person_history.person = person
AND acl_count_person_history.center IN (:authorized_centers)
'
)
)
->setParameter('authorized_centers', $centers);
}
AccompanyingCourseExportHelper::addClosingMotiveExclusionClause($qb);
return $qb;
}
public function requiredRole(): string
{
return AccompanyingPeriodVoter::STATS;
}
public function supportsModifiers(): array
{
return [
Declarations::ACP_TYPE,
Declarations::PERSON_TYPE,
Declarations::ACP_STEP_HISTORY,
];
}
}

View File

@@ -0,0 +1,96 @@
<?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\AccompanyingPeriodStepHistoryFilters;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\QueryBuilder;
use src\Bundle\ChillPersonBundle\Tests\Export\Filter\AccompanyingPeriodStepHistoryFilters\ByDateFilterTest;
use Symfony\Component\Form\FormBuilderInterface;
/**
* Class ByDateFilter
* Implements the FilterInterface.
*
* This class represents a filter that filters data based on a date range on the start date.
* It allows the user to select a start date and an end date to filter the data.
*
* @see ByDateFilterTest
*/
final readonly class ByDateFilter implements FilterInterface
{
public function __construct(
private RollingDateConverterInterface $rollingDateConverter
) {
}
public function getTitle()
{
return 'export.filter.step_history.by_date.title';
}
public function buildForm(FormBuilderInterface $builder)
{
$builder
->add('start_date', PickRollingDateType::class, [
'label' => 'export.filter.step_history.by_date.start_date_label',
])
->add('end_date', PickRollingDateType::class, [
'label' => 'export.filter.step_history.by_date.end_date_label',
]);
}
public function getFormDefaultData(): array
{
return [
'start_date' => new RollingDate(RollingDate::T_YEAR_CURRENT_START),
'end_date' => new RollingDate(RollingDate::T_TODAY),
];
}
public function describeAction($data, $format = 'string')
{
return [
'exports.filter.step_history.by_date.description',
[
'start_date' => $this->rollingDateConverter->convert($data['start_date']),
'end_date' => $this->rollingDateConverter->convert($data['end_date']),
],
];
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$startDate = 'acp_step_history_by_date_start_filter';
$endDate = 'acp_step_history_by_date_end_filter';
$qb
->andWhere(
"acpstephistory.startDate >= :{$startDate} AND (acpstephistory.endDate < :{$endDate} OR acpstephistory.endDate IS NULL)"
)
->setParameter($startDate, $this->rollingDateConverter->convert($data['start_date']))
->setParameter($endDate, $this->rollingDateConverter->convert($data['end_date']));
}
public function applyOn()
{
return Declarations::ACP_STEP_HISTORY;
}
}

View File

@@ -0,0 +1,87 @@
<?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\AccompanyingPeriodStepHistoryFilters;
use Chill\MainBundle\Export\FilterInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
final readonly class ByStepFilter implements FilterInterface
{
public function __construct(
private TranslatorInterface $translator,
) {
}
public function getTitle()
{
return 'export.filter.step_history.by_step.title';
}
public function buildForm(FormBuilderInterface $builder)
{
$steps = [
AccompanyingPeriod::STEP_CONFIRMED,
AccompanyingPeriod::STEP_CONFIRMED_INACTIVE_SHORT,
AccompanyingPeriod::STEP_CONFIRMED_INACTIVE_LONG,
AccompanyingPeriod::STEP_CLOSED,
];
$builder->add('steps', ChoiceType::class, [
'choices' => array_combine(
array_map(static fn (string $step): string => 'accompanying_period.'.$step, $steps),
$steps
),
'label' => 'export.filter.step_history.by_step.pick_steps',
'multiple' => true,
'expanded' => true,
]);
}
public function getFormDefaultData(): array
{
return [
'steps' => [],
];
}
public function describeAction($data, $format = 'string')
{
return [
'export.filter.step_history.by_step.description',
[
'%steps%' => implode(', ', array_map(fn (string $step) => $this->translator->trans('accompanying_period.'.$step), $data['steps'])),
],
];
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$qb
->andWhere('acpstephistory.step IN (:acpstephistory_by_step_filter_steps)')
->setParameter('acpstephistory_by_step_filter_steps', $data['steps']);
}
public function applyOn()
{
return Declarations::ACP_STEP_HISTORY;
}
}