Merge branch '282-add-dates-job-filter' into 'master'

Add dates ranges to job and scope filter and aggregator (accompanying course's exports)

Closes #282

See merge request Chill-Projet/chill-bundles!699
This commit is contained in:
Julien Fastré 2024-06-18 07:54:49 +00:00
commit e95f9e9846
30 changed files with 472 additions and 153 deletions

View File

@ -0,0 +1,6 @@
kind: Feature
body: '[export] add start date and end date on filters "filter course by referrer
job" and "filter course by referrer scope"'
time: 2024-06-17T14:25:05.041546007+02:00
custom:
Issue: "282"

View File

@ -0,0 +1,5 @@
kind: Feature
body: '[export] the aggregator "Group by referrer" now accept a date range.'
time: 2024-06-17T15:22:19.030556768+02:00
custom:
Issue: "282"

View File

@ -0,0 +1,5 @@
kind: Feature
body: '[export] add date range on "group course by scope''s referrer"'
time: 2024-06-17T16:21:10.342069726+02:00
custom:
Issue: "282"

View File

@ -0,0 +1,5 @@
kind: Feature
body: '[export] add date range on "group course by jobs''s referrer"'
time: 2024-06-17T17:14:14.737439251+02:00
custom:
Issue: "282"

View File

@ -60,7 +60,7 @@ final class TranslatableActivityTypeTest extends KernelTestCase
$this->assertInstanceOf( $this->assertInstanceOf(
ActivityType::class, ActivityType::class,
$form->getData()['type'], $form->getData()['type'],
'The data is an instance of Chill\\ActivityBundle\\Entity\\ActivityType' 'The data is an instance of Chill\ActivityBundle\Entity\ActivityType'
); );
$this->assertEquals($type->getId(), $form->getData()['type']->getId()); $this->assertEquals($type->getId(), $form->getData()['type']->getId());

View File

@ -37,12 +37,12 @@ class RemoteEventConverter
* valid when the remote string contains also a timezone, like in * valid when the remote string contains also a timezone, like in
* lastModifiedDate. * lastModifiedDate.
*/ */
final public const REMOTE_DATETIMEZONE_FORMAT = 'Y-m-d\\TH:i:s.u?P'; final public const REMOTE_DATETIMEZONE_FORMAT = 'Y-m-d\TH:i:s.u?P';
/** /**
* Same as above, but sometimes the date is expressed with only 6 milliseconds. * Same as above, but sometimes the date is expressed with only 6 milliseconds.
*/ */
final public const REMOTE_DATETIMEZONE_FORMAT_ALT = 'Y-m-d\\TH:i:s.uP'; final public const REMOTE_DATETIMEZONE_FORMAT_ALT = 'Y-m-d\TH:i:s.uP';
private const REMOTE_DATE_FORMAT = 'Y-m-d\TH:i:s.u0'; private const REMOTE_DATE_FORMAT = 'Y-m-d\TH:i:s.u0';

View File

@ -43,7 +43,7 @@ class ShortMessageCompilerPass implements CompilerPassInterface
$defaultTransporter = new Reference(NullShortMessageSender::class); $defaultTransporter = new Reference(NullShortMessageSender::class);
} elseif ('ovh' === $dsn['scheme']) { } elseif ('ovh' === $dsn['scheme']) {
if (!class_exists('\\'.\Ovh\Api::class)) { if (!class_exists('\\'.\Ovh\Api::class)) {
throw new RuntimeException('Class \\Ovh\\Api not found'); throw new RuntimeException('Class \Ovh\Api not found');
} }
foreach (['user', 'host', 'pass'] as $component) { foreach (['user', 'host', 'pass'] as $component) {

View File

@ -0,0 +1,25 @@
<?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\MainBundle\Export;
/**
* Transform data from filter.
*
* This interface defines a method for transforming filter's form data before it is processed.
*
* You can implement this interface on @see{FilterInterface} or @see{AggregatorInterface}, to allow to transform existing data in saved exports
* and replace it with some default values, or new default values.
*/
interface DataTransformerInterface
{
public function transformData(?array $before): array;
}

View File

@ -190,7 +190,7 @@ class ExportManager
// throw an error if the export require other modifier, which is // throw an error if the export require other modifier, which is
// not allowed when the export return a `NativeQuery` // not allowed when the export return a `NativeQuery`
if (\count($export->supportsModifiers()) > 0) { if (\count($export->supportsModifiers()) > 0) {
throw new \LogicException("The export with alias `{$exportAlias}` return ".'a `\\Doctrine\\ORM\\NativeQuery` and supports modifiers, which is not allowed. Either the method `supportsModifiers` should return an empty array, or return a `Doctrine\\ORM\\QueryBuilder`'); throw new \LogicException("The export with alias `{$exportAlias}` return ".'a `\Doctrine\ORM\NativeQuery` and supports modifiers, which is not allowed. Either the method `supportsModifiers` should return an empty array, or return a `Doctrine\ORM\QueryBuilder`');
} }
} elseif ($query instanceof QueryBuilder) { } elseif ($query instanceof QueryBuilder) {
// handle filters // handle filters
@ -203,7 +203,7 @@ class ExportManager
'dql' => $query->getDQL(), 'dql' => $query->getDQL(),
]); ]);
} else { } else {
throw new \UnexpectedValueException('The method `intiateQuery` should return a `\\Doctrine\\ORM\\NativeQuery` or a `Doctrine\\ORM\\QueryBuilder` object.'); throw new \UnexpectedValueException('The method `intiateQuery` should return a `\Doctrine\ORM\NativeQuery` or a `Doctrine\ORM\QueryBuilder` object.');
} }
$result = $export->getResult($query, $data[ExportType::EXPORT_KEY]); $result = $export->getResult($query, $data[ExportType::EXPORT_KEY]);

View File

@ -32,6 +32,9 @@ interface FilterInterface extends ModifierInterface
/** /**
* Get the default data, that can be use as "data" for the form. * Get the default data, that can be use as "data" for the form.
*
* In case of adding new parameters to a filter, you can implement a @see{DataTransformerFilterInterface} to
* transforme the filters's data saved in an export to the desired state.
*/ */
public function getFormDefaultData(): array; public function getFormDefaultData(): array;

View File

@ -11,7 +11,9 @@ declare(strict_types=1);
namespace Chill\MainBundle\Form\Type\Export; namespace Chill\MainBundle\Form\Type\Export;
use Chill\MainBundle\Export\DataTransformerInterface;
use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\CallbackTransformer;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType; use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\FormType; use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
@ -19,11 +21,7 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
class AggregatorType extends AbstractType class AggregatorType extends AbstractType
{ {
public function __construct() public function buildForm(FormBuilderInterface $builder, array $options): void
{
}
public function buildForm(FormBuilderInterface $builder, array $options)
{ {
$exportManager = $options['export_manager']; $exportManager = $options['export_manager'];
$aggregator = $exportManager->getAggregator($options['aggregator_alias']); $aggregator = $exportManager->getAggregator($options['aggregator_alias']);
@ -34,17 +32,24 @@ class AggregatorType extends AbstractType
'required' => false, 'required' => false,
]); ]);
$filterFormBuilder = $builder->create('form', FormType::class, [ $aggregatorFormBuilder = $builder->create('form', FormType::class, [
'compound' => true, 'compound' => true,
'required' => false, 'required' => false,
'error_bubbling' => false, 'error_bubbling' => false,
]); ]);
$aggregator->buildForm($filterFormBuilder); $aggregator->buildForm($aggregatorFormBuilder);
$builder->add($filterFormBuilder); if ($aggregator instanceof DataTransformerInterface) {
$aggregatorFormBuilder->addViewTransformer(new CallbackTransformer(
fn (?array $data) => $data,
fn (?array $data) => $aggregator->transformData($data),
));
} }
public function configureOptions(OptionsResolver $resolver) $builder->add($aggregatorFormBuilder);
}
public function configureOptions(OptionsResolver $resolver): void
{ {
$resolver->setRequired('aggregator_alias') $resolver->setRequired('aggregator_alias')
->setRequired('export_manager') ->setRequired('export_manager')

View File

@ -11,8 +11,10 @@ declare(strict_types=1);
namespace Chill\MainBundle\Form\Type\Export; namespace Chill\MainBundle\Form\Type\Export;
use Chill\MainBundle\Export\DataTransformerInterface;
use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Export\FilterInterface;
use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\CallbackTransformer;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType; use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\FormType; use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
@ -43,6 +45,13 @@ class FilterType extends AbstractType
]); ]);
$filter->buildForm($filterFormBuilder); $filter->buildForm($filterFormBuilder);
if ($filter instanceof DataTransformerInterface) {
$filterFormBuilder->addViewTransformer(new CallbackTransformer(
fn (?array $data) => $data,
fn (?array $data) => $filter->transformData($data),
));
}
$builder->add($filterFormBuilder); $builder->add($filterFormBuilder);
} }

View File

@ -14,9 +14,9 @@ namespace Chill\MainBundle\Search\Utils;
class ExtractDateFromPattern class ExtractDateFromPattern
{ {
private const DATE_PATTERN = [ private const DATE_PATTERN = [
['([12]\\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01]))', 'Y-m-d'], // 1981-05-12 ['([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))', 'Y-m-d'], // 1981-05-12
['((0[1-9]|[12]\\d|3[01])\\/(0[1-9]|1[0-2])\\/([12]\\d{3}))', 'd/m/Y'], // 15/12/1980 ['((0[1-9]|[12]\d|3[01])\/(0[1-9]|1[0-2])\/([12]\d{3}))', 'd/m/Y'], // 15/12/1980
['((0[1-9]|[12]\\d|3[01])-(0[1-9]|1[0-2])-([12]\\d{3}))', 'd-m-Y'], // 15/12/1980 ['((0[1-9]|[12]\d|3[01])-(0[1-9]|1[0-2])-([12]\d{3}))', 'd-m-Y'], // 15/12/1980
]; ];
public function extractDates(string $subject): SearchExtractionResult public function extractDates(string $subject): SearchExtractionResult

View File

@ -16,7 +16,7 @@ use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
class ExtractPhonenumberFromPattern class ExtractPhonenumberFromPattern
{ {
private const PATTERN = '([\\+]{0,1}[0-9\\ ]{5,})'; private const PATTERN = '([\+]{0,1}[0-9\ ]{5,})';
private readonly string $defaultCarrierCode; private readonly string $defaultCarrierCode;

View File

@ -338,15 +338,11 @@ abstract class AbstractAggregatorTest extends KernelTestCase
.'is a string or an be converted to a string', $key) .'is a string or an be converted to a string', $key)
); );
$this->assertTrue( $head = \call_user_func($closure, '_header');
// conditions
\is_string((string) \call_user_func($closure, '_header')) self::assertIsString($head);
&& !empty(\call_user_func($closure, '_header')) self::assertNotEquals('', $head);
&& '_header' !== \call_user_func($closure, '_header'), self::assertNotEquals('_header', $head);
// message
sprintf('Test that the callable return by `getLabels` for key %s '
.'can provide an header', $key)
);
} }
} }

View File

@ -12,6 +12,7 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators; namespace Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators;
use Chill\MainBundle\Export\AggregatorInterface; use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Export\DataTransformerInterface;
use Chill\MainBundle\Form\Type\PickRollingDateType; use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Repository\UserRepository; use Chill\MainBundle\Repository\UserRepository;
use Chill\MainBundle\Service\RollingDate\RollingDate; use Chill\MainBundle\Service\RollingDate\RollingDate;
@ -21,14 +22,17 @@ use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\QueryBuilder; use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
final readonly class ReferrerAggregator implements AggregatorInterface final readonly class ReferrerAggregator implements AggregatorInterface, DataTransformerInterface
{ {
private const A = 'acp_ref_agg_uhistory'; private const A = 'acp_ref_agg_uhistory';
private const P = 'acp_ref_agg_date'; private const P = 'acp_ref_agg_date';
public function __construct(private UserRepository $userRepository, private UserRender $userRender, private RollingDateConverterInterface $rollingDateConverter) public function __construct(
{ private UserRepository $userRepository,
private UserRender $userRender,
private RollingDateConverterInterface $rollingDateConverter
) {
} }
public function addRole(): ?string public function addRole(): ?string
@ -46,18 +50,16 @@ final readonly class ReferrerAggregator implements AggregatorInterface
$qb->expr()->orX( $qb->expr()->orX(
$qb->expr()->isNull(self::A), $qb->expr()->isNull(self::A),
$qb->expr()->andX( $qb->expr()->andX(
$qb->expr()->lte(self::A.'.startDate', ':'.self::P), $qb->expr()->lt(self::A.'.startDate', ':'.self::P.'_end_date'),
$qb->expr()->orX( $qb->expr()->orX(
$qb->expr()->isNull(self::A.'.endDate'), $qb->expr()->isNull(self::A.'.endDate'),
$qb->expr()->gt(self::A.'.endDate', ':'.self::P) $qb->expr()->gte(self::A.'.endDate', ':'.self::P.'_start_date')
) )
) )
) )
) )
->setParameter( ->setParameter(':'.self::P.'_end_date', $this->rollingDateConverter->convert($data['end_date']))
self::P, ->setParameter(':'.self::P.'_start_date', $this->rollingDateConverter->convert($data['end_date']));
$this->rollingDateConverter->convert($data['date_calc'])
);
} }
public function applyOn(): string public function applyOn(): string
@ -68,15 +70,37 @@ final readonly class ReferrerAggregator implements AggregatorInterface
public function buildForm(FormBuilderInterface $builder) public function buildForm(FormBuilderInterface $builder)
{ {
$builder $builder
->add('date_calc', PickRollingDateType::class, [ ->add('start_date', PickRollingDateType::class, [
'label' => 'export.aggregator.course.by_referrer.Computation date for referrer', 'label' => 'export.aggregator.course.by_referrer.Referrer after',
'required' => true,
])
->add('end_date', PickRollingDateType::class, [
'label' => 'export.aggregator.course.by_referrer.Until',
'required' => true, 'required' => true,
]); ]);
} }
public function getFormDefaultData(): array public function getFormDefaultData(): array
{ {
return ['date_calc' => new RollingDate(RollingDate::T_TODAY)]; return [
'start_date' => new RollingDate(RollingDate::T_YEAR_CURRENT_START),
'end_date' => new RollingDate(RollingDate::T_TODAY),
];
}
public function transformData(?array $before): array
{
$default = $this->getFormDefaultData();
$data = [];
if (null === $before) {
return $default;
}
$data['start_date'] = $before['date_calc'] ?? $before['start_date'] ?? $default['start_date'];
$data['end_date'] = $before['date_calc'] ?? $before['end_date'] ?? $default['end_date'];
return $data;
} }
public function getLabels($key, array $values, $data) public function getLabels($key, array $values, $data)

View File

@ -13,20 +13,25 @@ namespace Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators;
use Chill\MainBundle\Entity\User\UserScopeHistory; use Chill\MainBundle\Entity\User\UserScopeHistory;
use Chill\MainBundle\Export\AggregatorInterface; use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Export\DataTransformerInterface;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Repository\ScopeRepositoryInterface; use Chill\MainBundle\Repository\ScopeRepositoryInterface;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface; use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\Query\Expr\Join; use Doctrine\ORM\Query\Expr\Join;
use Doctrine\ORM\QueryBuilder; use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
readonly class ReferrerScopeAggregator implements AggregatorInterface readonly class ReferrerScopeAggregator implements AggregatorInterface, DataTransformerInterface
{ {
private const PREFIX = 'acp_agg_referrer_scope'; private const PREFIX = 'acp_agg_referrer_scope';
public function __construct( public function __construct(
private ScopeRepositoryInterface $scopeRepository, private ScopeRepositoryInterface $scopeRepository,
private TranslatableStringHelperInterface $translatableStringHelper, private TranslatableStringHelperInterface $translatableStringHelper,
private RollingDateConverterInterface $rollingDateConverter,
) { ) {
} }
@ -47,11 +52,16 @@ readonly class ReferrerScopeAggregator implements AggregatorInterface
$qb->expr()->andX( $qb->expr()->andX(
$qb->expr()->eq("{$p}_userHistory.accompanyingPeriod", 'acp.id'), $qb->expr()->eq("{$p}_userHistory.accompanyingPeriod", 'acp.id'),
$qb->expr()->andX( $qb->expr()->andX(
// check that the user is referrer when the accompanying period is opened
$qb->expr()->gte('COALESCE(acp.closingDate, CURRENT_TIMESTAMP())', "{$p}_userHistory.startDate"), $qb->expr()->gte('COALESCE(acp.closingDate, CURRENT_TIMESTAMP())', "{$p}_userHistory.startDate"),
$qb->expr()->orX( $qb->expr()->orX(
$qb->expr()->isNull("{$p}_userHistory.endDate"), $qb->expr()->isNull("{$p}_userHistory.endDate"),
$qb->expr()->lt('COALESCE(acp.closingDate, CURRENT_TIMESTAMP())', "{$p}_userHistory.endDate") $qb->expr()->lt('COALESCE(acp.closingDate, CURRENT_TIMESTAMP())', "{$p}_userHistory.endDate")
) )
),
$qb->expr()->andX(
"{$p}_userHistory.startDate <= :{$p}_endDate",
"COALESCE({$p}_userHistory.endDate, CURRENT_TIMESTAMP()) > :{$p}_startDate"
) )
) )
) )
@ -67,9 +77,15 @@ readonly class ReferrerScopeAggregator implements AggregatorInterface
$qb->expr()->isNull("{$p}_scopeHistory.endDate"), $qb->expr()->isNull("{$p}_scopeHistory.endDate"),
$qb->expr()->gt("{$p}_scopeHistory.endDate", "{$p}_userHistory.startDate") $qb->expr()->gt("{$p}_scopeHistory.endDate", "{$p}_userHistory.startDate")
) )
),
$qb->expr()->andX(
"{$p}_scopeHistory.startDate <= :{$p}_endDate",
"COALESCE({$p}_scopeHistory.endDate, CURRENT_TIMESTAMP()) > :{$p}_startDate"
) )
) )
) )
->setParameter("{$p}_startDate", $this->rollingDateConverter->convert($data['start_date']))
->setParameter("{$p}_endDate", $this->rollingDateConverter->convert($data['end_date']))
->addSelect("IDENTITY({$p}_scopeHistory.scope) AS {$p}_select") ->addSelect("IDENTITY({$p}_scopeHistory.scope) AS {$p}_select")
->addGroupBy("{$p}_select"); ->addGroupBy("{$p}_select");
} }
@ -81,11 +97,34 @@ readonly class ReferrerScopeAggregator implements AggregatorInterface
public function buildForm(FormBuilderInterface $builder) public function buildForm(FormBuilderInterface $builder)
{ {
$builder
->add('start_date', PickRollingDateType::class, [
'label' => 'export.aggregator.course.by_referrer_scope.Referrer and scope after',
'required' => true,
])
->add('end_date', PickRollingDateType::class, [
'label' => 'export.aggregator.course.by_referrer_scope.Until',
'required' => true,
]);
} }
public function getFormDefaultData(): array public function getFormDefaultData(): array
{ {
return []; return [
'start_date' => new RollingDate(RollingDate::T_YEAR_CURRENT_START),
'end_date' => new RollingDate(RollingDate::T_TODAY),
];
}
public function transformData(?array $before): array
{
$default = $this->getFormDefaultData();
$data = [];
$data['start_date'] = $before['start_date'] ?? new RollingDate(RollingDate::T_FIXED_DATE, new \DateTimeImmutable('1970-01-01'));
$data['end_date'] = $before['end_date'] ?? $default['end_date'];
return $data;
} }
public function getLabels($key, array $values, $data) public function getLabels($key, array $values, $data)

View File

@ -13,20 +13,25 @@ namespace Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators;
use Chill\MainBundle\Entity\User\UserJobHistory; use Chill\MainBundle\Entity\User\UserJobHistory;
use Chill\MainBundle\Export\AggregatorInterface; use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Export\DataTransformerInterface;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Repository\UserJobRepository; use Chill\MainBundle\Repository\UserJobRepository;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\Query\Expr\Join; use Doctrine\ORM\Query\Expr\Join;
use Doctrine\ORM\QueryBuilder; use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
final readonly class UserJobAggregator implements AggregatorInterface final readonly class UserJobAggregator implements AggregatorInterface, DataTransformerInterface
{ {
private const PREFIX = 'acp_agg_user_job'; private const PREFIX = 'acp_agg_user_job';
public function __construct( public function __construct(
private UserJobRepository $jobRepository, private UserJobRepository $jobRepository,
private TranslatableStringHelper $translatableStringHelper private TranslatableStringHelper $translatableStringHelper,
private RollingDateConverterInterface $rollingDateConverter,
) { ) {
} }
@ -52,6 +57,10 @@ final readonly class UserJobAggregator implements AggregatorInterface
$qb->expr()->isNull("{$p}_userHistory.endDate"), $qb->expr()->isNull("{$p}_userHistory.endDate"),
$qb->expr()->lt('COALESCE(acp.closingDate, CURRENT_TIMESTAMP())', "{$p}_userHistory.endDate") $qb->expr()->lt('COALESCE(acp.closingDate, CURRENT_TIMESTAMP())', "{$p}_userHistory.endDate")
) )
),
$qb->expr()->andX(
"{$p}_userHistory.startDate <= :{$p}_endDate",
"COALESCE({$p}_userHistory.endDate, CURRENT_TIMESTAMP()) > :{$p}_startDate"
) )
) )
) )
@ -67,9 +76,15 @@ final readonly class UserJobAggregator implements AggregatorInterface
$qb->expr()->isNull("{$p}_jobHistory.endDate"), $qb->expr()->isNull("{$p}_jobHistory.endDate"),
$qb->expr()->gt("{$p}_jobHistory.endDate", "{$p}_userHistory.startDate") $qb->expr()->gt("{$p}_jobHistory.endDate", "{$p}_userHistory.startDate")
) )
),
$qb->expr()->andX(
"{$p}_jobHistory.startDate <= :{$p}_endDate",
"COALESCE({$p}_jobHistory.endDate, CURRENT_TIMESTAMP()) > :{$p}_startDate"
) )
) )
) )
->setParameter("{$p}_startDate", $this->rollingDateConverter->convert($data['start_date']))
->setParameter("{$p}_endDate", $this->rollingDateConverter->convert($data['end_date']))
->addSelect("IDENTITY({$p}_jobHistory.job) AS {$p}_select") ->addSelect("IDENTITY({$p}_jobHistory.job) AS {$p}_select")
->addGroupBy("{$p}_select"); ->addGroupBy("{$p}_select");
} }
@ -81,11 +96,34 @@ final readonly class UserJobAggregator implements AggregatorInterface
public function buildForm(FormBuilderInterface $builder) public function buildForm(FormBuilderInterface $builder)
{ {
$builder
->add('start_date', PickRollingDateType::class, [
'label' => 'export.aggregator.course.by_referrer_job.Referrer and job after',
'required' => true,
])
->add('end_date', PickRollingDateType::class, [
'label' => 'export.aggregator.course.by_referrer_job.Until',
'required' => true,
]);
} }
public function getFormDefaultData(): array public function getFormDefaultData(): array
{ {
return []; return [
'start_date' => new RollingDate(RollingDate::T_YEAR_CURRENT_START),
'end_date' => new RollingDate(RollingDate::T_TODAY),
];
}
public function transformData(?array $before): array
{
$default = $this->getFormDefaultData();
$data = [];
$data['start_date'] = $before['start_date'] ?? new RollingDate(RollingDate::T_FIXED_DATE, new \DateTimeImmutable('1970-01-01'));
$data['end_date'] = $before['end_date'] ?? $default['end_date'];
return $data;
} }
public function getLabels($key, array $values, $data) public function getLabels($key, array $values, $data)

View File

@ -13,23 +13,28 @@ namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters;
use Chill\MainBundle\Entity\User\UserJobHistory; use Chill\MainBundle\Entity\User\UserJobHistory;
use Chill\MainBundle\Entity\UserJob; use Chill\MainBundle\Entity\UserJob;
use Chill\MainBundle\Export\DataTransformerInterface;
use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Repository\UserJobRepositoryInterface; use Chill\MainBundle\Repository\UserJobRepositoryInterface;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\PersonBundle\Entity\AccompanyingPeriod\UserHistory;
use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Export\Declarations;
use Doctrine\Common\Collections\Collection; use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Query\Expr\Join;
use Doctrine\ORM\QueryBuilder; use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
class UserJobFilter implements FilterInterface final readonly class UserJobFilter implements FilterInterface, DataTransformerInterface
{ {
private const PREFIX = 'acp_filter_user_job'; private const PREFIX = 'acp_filter_user_job';
public function __construct( public function __construct(
private readonly TranslatableStringHelper $translatableStringHelper, private TranslatableStringHelper $translatableStringHelper,
private readonly UserJobRepositoryInterface $userJobRepository, private UserJobRepositoryInterface $userJobRepository,
private RollingDateConverterInterface $rollingDateConverter,
) { ) {
} }
@ -42,42 +47,31 @@ class UserJobFilter implements FilterInterface
{ {
$p = self::PREFIX; $p = self::PREFIX;
$qb $qb->andWhere(
->leftJoin( $qb->expr()->exists(
'acp.userHistories', sprintf(
"{$p}_userHistory", <<<DQL
Join::WITH, SELECT 1
$qb->expr()->andX( FROM %s {$p}_userHistory
$qb->expr()->eq("{$p}_userHistory.accompanyingPeriod", 'acp.id'), JOIN %s {$p}_userJobHistory
$qb->expr()->andX( WITH
$qb->expr()->gte('COALESCE(acp.closingDate, CURRENT_TIMESTAMP())', "{$p}_userHistory.startDate"), {$p}_userHistory.user = {$p}_userJobHistory.user
$qb->expr()->orX( AND OVERLAPSI({$p}_userHistory.startDate, {$p}_userHistory.endDate),({$p}_userJobHistory.startDate, {$p}_userJobHistory.endDate) = TRUE
$qb->expr()->isNull("{$p}_userHistory.endDate"), WHERE {$p}_userHistory.accompanyingPeriod = acp
$qb->expr()->lt('COALESCE(acp.closingDate, CURRENT_TIMESTAMP())', "{$p}_userHistory.endDate") AND {$p}_userHistory.startDate <= :{$p}_endDate
) AND ({$p}_userHistory.endDate IS NULL OR {$p}_userHistory.endDate > :{$p}_startDate)
) AND {$p}_userJobHistory.startDate <= :{$p}_endDate
) AND ({$p}_userJobHistory.endDate IS NULL OR {$p}_userJobHistory.endDate > :{$p}_startDate)
) AND {$p}_userJobHistory.job IN (:{$p}_jobs)
->leftJoin( DQL,
UserHistory::class,
UserJobHistory::class, UserJobHistory::class,
"{$p}_jobHistory", ),
Join::WITH,
$qb->expr()->andX(
$qb->expr()->eq("{$p}_jobHistory.user", "{$p}_userHistory.user"),
$qb->expr()->andX(
$qb->expr()->lte("{$p}_jobHistory.startDate", "{$p}_userHistory.startDate"),
$qb->expr()->orX(
$qb->expr()->isNull("{$p}_jobHistory".'.endDate'),
$qb->expr()->gt("{$p}_jobHistory.endDate", "{$p}_userHistory.startDate")
) )
) )
) ->setParameter("{$p}_jobs", $data['jobs'])
) ->setParameter("{$p}_startDate", $this->rollingDateConverter->convert($data['start_date']))
->andWhere($qb->expr()->in("{$p}_jobHistory.job", ":{$p}_job")) ->setParameter("{$p}_endDate", $this->rollingDateConverter->convert($data['end_date']))
->setParameter(
"{$p}_job",
$data['jobs'],
)
; ;
} }
@ -96,20 +90,29 @@ class UserJobFilter implements FilterInterface
'expanded' => true, 'expanded' => true,
'choice_label' => fn (UserJob $job) => $this->translatableStringHelper->localize($job->getLabel()), 'choice_label' => fn (UserJob $job) => $this->translatableStringHelper->localize($job->getLabel()),
'label' => 'Job', 'label' => 'Job',
]); ])
->add('start_date', PickRollingDateType::class, [
'label' => 'export.filter.course.by_user_job.Start from',
])
->add('end_date', PickRollingDateType::class, [
'label' => 'export.filter.course.by_user_job.Until',
])
;
} }
public function describeAction($data, $format = 'string') public function describeAction($data, $format = 'string')
{ {
return [ return [
'export.filter.course.by_user_job.Filtered by user job: only %job%', [ 'exports.filter.course.by_user_job.Filtered by user job: only job', [
'%job%' => implode( 'job' => implode(
', ', ', ',
array_map( array_map(
fn (UserJob $job) => $this->translatableStringHelper->localize($job->getLabel()), fn (UserJob $job) => $this->translatableStringHelper->localize($job->getLabel()),
$data['jobs'] instanceof Collection ? $data['jobs']->toArray() : $data['jobs'] $data['jobs'] instanceof Collection ? $data['jobs']->toArray() : $data['jobs']
) )
), ),
'startDate' => $this->rollingDateConverter->convert($data['start_date']),
'endDate' => $this->rollingDateConverter->convert($data['end_date']),
], ],
]; ];
} }
@ -118,9 +121,30 @@ class UserJobFilter implements FilterInterface
{ {
return [ return [
'jobs' => [], 'jobs' => [],
'start_date' => new RollingDate(RollingDate::T_YEAR_CURRENT_START),
'end_date' => new RollingDate(RollingDate::T_TODAY),
]; ];
} }
public function transformData(?array $before): array
{
$default = $this->getFormDefaultData();
if (null === $before) {
return $default;
}
if (!array_key_exists('start_date', $before) || null === $before['start_date']) {
$before['start_date'] = $default['start_date'];
}
if (!array_key_exists('end_date', $before) || null === $before['end_date']) {
$before['end_date'] = $default['end_date'];
}
return $before;
}
public function getTitle(): string public function getTitle(): string
{ {
return 'export.filter.course.by_user_job.Filter by user job'; return 'export.filter.course.by_user_job.Filter by user job';

View File

@ -13,23 +13,28 @@ namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters;
use Chill\MainBundle\Entity\Scope; use Chill\MainBundle\Entity\Scope;
use Chill\MainBundle\Entity\User\UserScopeHistory; use Chill\MainBundle\Entity\User\UserScopeHistory;
use Chill\MainBundle\Export\DataTransformerInterface;
use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Repository\ScopeRepositoryInterface; use Chill\MainBundle\Repository\ScopeRepositoryInterface;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\PersonBundle\Entity\AccompanyingPeriod\UserHistory;
use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Export\Declarations;
use Doctrine\Common\Collections\Collection; use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Query\Expr\Join;
use Doctrine\ORM\QueryBuilder; use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
class UserScopeFilter implements FilterInterface final readonly class UserScopeFilter implements FilterInterface, DataTransformerInterface
{ {
private const PREFIX = 'acp_filter_main_scope'; private const PREFIX = 'acp_filter_main_scope';
public function __construct( public function __construct(
private readonly ScopeRepositoryInterface $scopeRepository, private ScopeRepositoryInterface $scopeRepository,
private readonly TranslatableStringHelper $translatableStringHelper, private TranslatableStringHelper $translatableStringHelper,
private RollingDateConverterInterface $rollingDateConverter,
) { ) {
} }
@ -38,47 +43,35 @@ class UserScopeFilter implements FilterInterface
return null; return null;
} }
public function alterQuery(QueryBuilder $qb, $data) public function alterQuery(QueryBuilder $qb, $data): void
{ {
$p = self::PREFIX; $p = self::PREFIX;
$qb $qb->andWhere(
->join( $qb->expr()->exists(
'acp.userHistories', sprintf(
"{$p}_userHistory", <<<DQL
Join::WITH, SELECT 1
$qb->expr()->andX( FROM %s {$p}_userHistory
$qb->expr()->eq("{$p}_userHistory.accompanyingPeriod", 'acp.id'), JOIN %s {$p}_userScopeHistory
$qb->expr()->andX( WITH
$qb->expr()->gte('COALESCE(acp.closingDate, CURRENT_TIMESTAMP())', "{$p}_userHistory.startDate"), {$p}_userHistory.user = {$p}_userScopeHistory.user
$qb->expr()->orX( AND OVERLAPSI({$p}_userHistory.startDate, {$p}_userHistory.endDate),({$p}_userScopeHistory.startDate, {$p}_userScopeHistory.endDate) = TRUE
$qb->expr()->isNull("{$p}_userHistory.endDate"), WHERE {$p}_userHistory.accompanyingPeriod = acp
$qb->expr()->lt('COALESCE(acp.closingDate, CURRENT_TIMESTAMP())', "{$p}_userHistory.endDate") AND {$p}_userHistory.startDate <= :{$p}_endDate
) AND ({$p}_userHistory.endDate IS NULL OR {$p}_userHistory.endDate > :{$p}_startDate)
) AND {$p}_userScopeHistory.startDate <= :{$p}_endDate
) AND ({$p}_userScopeHistory.endDate IS NULL OR {$p}_userScopeHistory.endDate > :{$p}_startDate)
) AND {$p}_userScopeHistory.scope IN (:{$p}_scopes)
->join( DQL,
UserHistory::class,
UserScopeHistory::class, UserScopeHistory::class,
"{$p}_scopeHistory", ),
Join::WITH,
$qb->expr()->andX(
$qb->expr()->eq("{$p}_scopeHistory.user", "{$p}_userHistory.user"),
$qb->expr()->andX(
$qb->expr()->lte("{$p}_scopeHistory.startDate", "{$p}_userHistory.startDate"),
$qb->expr()->orX(
$qb->expr()->isNull("{$p}_scopeHistory.endDate"),
$qb->expr()->gt("{$p}_scopeHistory.endDate", "{$p}_userHistory.startDate")
) )
) )
) ->setParameter("{$p}_scopes", $data['scopes'])
) ->setParameter("{$p}_startDate", $this->rollingDateConverter->convert($data['start_date']))
->andWhere($qb->expr()->in("{$p}_scopeHistory.scope", ":{$p}_scopes")) ->setParameter("{$p}_endDate", $this->rollingDateConverter->convert($data['end_date']));
->setParameter(
"{$p}_scopes",
$data['scopes'],
)
;
} }
public function applyOn(): string public function applyOn(): string
@ -95,20 +88,28 @@ class UserScopeFilter implements FilterInterface
'choice_label' => fn (Scope $s) => $this->translatableStringHelper->localize($s->getName()), 'choice_label' => fn (Scope $s) => $this->translatableStringHelper->localize($s->getName()),
'multiple' => true, 'multiple' => true,
'expanded' => true, 'expanded' => true,
])
->add('start_date', PickRollingDateType::class, [
'label' => 'export.filter.course.by_user_scope.Start from',
])
->add('end_date', PickRollingDateType::class, [
'label' => 'export.filter.course.by_user_scope.Until',
]); ]);
} }
public function describeAction($data, $format = 'string') public function describeAction($data, $format = 'string')
{ {
return [ return [
'export.filter.course.by_user_scope.Filtered by user main scope: only %scope%', [ 'exports.filter.course.by_user_scope.Filtered by user main scope: only scopes', [
'%scope%' => implode( 'scopes' => implode(
', ', ', ',
array_map( array_map(
fn (Scope $s) => $this->translatableStringHelper->localize($s->getName()), fn (Scope $s) => $this->translatableStringHelper->localize($s->getName()),
$data['scopes'] instanceof Collection ? $data['scopes']->toArray() : $data['scopes'] $data['scopes'] instanceof Collection ? $data['scopes']->toArray() : $data['scopes']
) )
), ),
'startDate' => $this->rollingDateConverter->convert($data['start_date']),
'endDate' => $this->rollingDateConverter->convert($data['end_date']),
], ],
]; ];
} }
@ -117,9 +118,30 @@ class UserScopeFilter implements FilterInterface
{ {
return [ return [
'scopes' => [], 'scopes' => [],
'start_date' => new RollingDate(RollingDate::T_YEAR_CURRENT_START),
'end_date' => new RollingDate(RollingDate::T_TODAY),
]; ];
} }
public function transformData(?array $before): array
{
$default = $this->getFormDefaultData();
if (null === $before) {
return $default;
}
if (!array_key_exists('start_date', $before) || null === $before['start_date']) {
$before['start_date'] = $default['start_date'];
}
if (!array_key_exists('end_date', $before) || null === $before['end_date']) {
$before['end_date'] = $default['end_date'];
}
return $before;
}
public function getTitle(): string public function getTitle(): string
{ {
return 'export.filter.course.by_user_scope.Filter by user scope'; return 'export.filter.course.by_user_scope.Filter by user scope';

View File

@ -74,7 +74,7 @@ final class AccompanyingCourseControllerTest extends WebTestCase
$this->assertResponseRedirects(); $this->assertResponseRedirects();
$location = $this->client->getResponse()->headers->get('Location'); $location = $this->client->getResponse()->headers->get('Location');
$this->assertEquals(1, \preg_match('|^\\/[^\\/]+\\/parcours/([\\d]+)/edit$|', (string) $location)); $this->assertEquals(1, \preg_match('|^\/[^\/]+\/parcours/([\d]+)/edit$|', (string) $location));
} }
/** /**
@ -93,7 +93,7 @@ final class AccompanyingCourseControllerTest extends WebTestCase
$location = $this->client->getResponse()->headers->get('Location'); $location = $this->client->getResponse()->headers->get('Location');
$matches = []; $matches = [];
$this->assertEquals(1, \preg_match('|^\\/[^\\/]+\\/parcours/([\\d]+)/edit$|', (string) $location, $matches)); $this->assertEquals(1, \preg_match('|^\/[^\/]+\/parcours/([\d]+)/edit$|', (string) $location, $matches));
$id = $matches[1]; $id = $matches[1];
$period = self::$container->get(EntityManagerInterface::class) $period = self::$container->get(EntityManagerInterface::class)

View File

@ -33,7 +33,40 @@ final class ReferrerAggregatorTest extends AbstractAggregatorTest
$this->aggregator = self::$container->get('chill.person.export.aggregator_referrer'); $this->aggregator = self::$container->get('chill.person.export.aggregator_referrer');
} }
public function getAggregator() /**
* @dataProvider provideBeforeData
*/
public function testDataTransformer(?array $before, array $expected): void
{
$actual = $this->getAggregator()->transformData($before);
self::assertEqualsCanonicalizing(array_keys($expected), array_keys($actual));
foreach (['start_date', 'end_date'] as $key) {
self::assertInstanceOf(RollingDate::class, $actual[$key]);
self::assertEquals($expected[$key]->getRoll(), $actual[$key]->getRoll(), "Check that the roll is the same for {$key}");
}
}
public function provideBeforeData(): iterable
{
yield [
['date_calc' => new RollingDate(RollingDate::T_TODAY)],
['start_date' => new RollingDate(RollingDate::T_TODAY), 'end_date' => new RollingDate(RollingDate::T_TODAY)],
];
yield [
['start_date' => new RollingDate(RollingDate::T_WEEK_CURRENT_START), 'end_date' => new RollingDate(RollingDate::T_TODAY)],
['start_date' => new RollingDate(RollingDate::T_WEEK_CURRENT_START), 'end_date' => new RollingDate(RollingDate::T_TODAY)],
];
yield [
null,
// this is the default configuration
['start_date' => new RollingDate(RollingDate::T_YEAR_CURRENT_START), 'end_date' => new RollingDate(RollingDate::T_TODAY)],
];
}
public function getAggregator(): ReferrerAggregator
{ {
return $this->aggregator; return $this->aggregator;
} }
@ -41,7 +74,10 @@ final class ReferrerAggregatorTest extends AbstractAggregatorTest
public function getFormData(): array public function getFormData(): array
{ {
return [ return [
['date_calc' => new RollingDate(RollingDate::T_TODAY)], [
'start_date' => new RollingDate(RollingDate::T_YEAR_CURRENT_START),
'end_date' => new RollingDate(RollingDate::T_TODAY),
],
]; ];
} }

View File

@ -14,6 +14,7 @@ namespace Export\Aggregator\AccompanyingCourseAggregators;
use Chill\MainBundle\Entity\Scope; use Chill\MainBundle\Entity\Scope;
use Chill\MainBundle\Repository\ScopeRepositoryInterface; use Chill\MainBundle\Repository\ScopeRepositoryInterface;
use Chill\MainBundle\Service\RollingDate\RollingDate; use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverter;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface; use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface; use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\MainBundle\Test\Export\AbstractAggregatorTest; use Chill\MainBundle\Test\Export\AbstractAggregatorTest;
@ -48,16 +49,46 @@ final class ReferrerScopeAggregatorTest extends AbstractAggregatorTest
return new ReferrerScopeAggregator( return new ReferrerScopeAggregator(
$scopeRepository->reveal(), $scopeRepository->reveal(),
$translatableStringHelper->reveal(), $translatableStringHelper->reveal(),
$dateConverter->reveal() new RollingDateConverter(),
); );
} }
public function getFormData() public function getFormData()
{ {
return [ return [
[ ['start_date' => new RollingDate(RollingDate::T_WEEK_CURRENT_START), 'end_date' => new RollingDate(RollingDate::T_TODAY)],
'date_calc' => new RollingDate(RollingDate::T_TODAY), ];
], }
/**
* @dataProvider provideBeforeData
*/
public function testDataTransformer(?array $before, array $expected): void
{
$actual = $this->getAggregator()->transformData($before);
self::assertEqualsCanonicalizing(array_keys($expected), array_keys($actual));
foreach (['start_date', 'end_date'] as $key) {
self::assertInstanceOf(RollingDate::class, $actual[$key]);
self::assertEquals($expected[$key]->getRoll(), $actual[$key]->getRoll(), "Check that the roll is the same for {$key}");
}
}
public function provideBeforeData(): iterable
{
yield [
null,
['start_date' => new RollingDate(RollingDate::T_FIXED_DATE, new \DateTimeImmutable('1970-01-01')), 'end_date' => new RollingDate(RollingDate::T_TODAY)],
];
yield [
[],
['start_date' => new RollingDate(RollingDate::T_FIXED_DATE, new \DateTimeImmutable('1970-01-01')), 'end_date' => new RollingDate(RollingDate::T_TODAY)],
];
yield [
['start_date' => new RollingDate(RollingDate::T_WEEK_CURRENT_START), 'end_date' => new RollingDate(RollingDate::T_TODAY)],
['start_date' => new RollingDate(RollingDate::T_WEEK_CURRENT_START), 'end_date' => new RollingDate(RollingDate::T_TODAY)],
]; ];
} }

View File

@ -33,7 +33,39 @@ final class UserJobAggregatorTest extends AbstractAggregatorTest
$this->aggregator = self::$container->get('chill.person.export.aggregator_referrer_job'); $this->aggregator = self::$container->get('chill.person.export.aggregator_referrer_job');
} }
public function getAggregator() /**
* @dataProvider provideBeforeData
*/
public function testDataTransformer(?array $before, array $expected): void
{
$actual = $this->getAggregator()->transformData($before);
self::assertEqualsCanonicalizing(array_keys($expected), array_keys($actual));
foreach (['start_date', 'end_date'] as $key) {
self::assertInstanceOf(RollingDate::class, $actual[$key]);
self::assertEquals($expected[$key]->getRoll(), $actual[$key]->getRoll(), "Check that the roll is the same for {$key}");
}
}
public function provideBeforeData(): iterable
{
yield [
null,
['start_date' => new RollingDate(RollingDate::T_FIXED_DATE, new \DateTimeImmutable('1970-01-01')), 'end_date' => new RollingDate(RollingDate::T_TODAY)],
];
yield [
[],
['start_date' => new RollingDate(RollingDate::T_FIXED_DATE, new \DateTimeImmutable('1970-01-01')), 'end_date' => new RollingDate(RollingDate::T_TODAY)],
];
yield [
['start_date' => new RollingDate(RollingDate::T_WEEK_CURRENT_START), 'end_date' => new RollingDate(RollingDate::T_TODAY)],
['start_date' => new RollingDate(RollingDate::T_WEEK_CURRENT_START), 'end_date' => new RollingDate(RollingDate::T_TODAY)],
];
}
public function getAggregator(): UserJobAggregator
{ {
return $this->aggregator; return $this->aggregator;
} }
@ -41,9 +73,7 @@ final class UserJobAggregatorTest extends AbstractAggregatorTest
public function getFormData(): array public function getFormData(): array
{ {
return [ return [
[ ['start_date' => new RollingDate(RollingDate::T_WEEK_CURRENT_START), 'end_date' => new RollingDate(RollingDate::T_TODAY)],
'job_at' => new RollingDate(RollingDate::T_FIXED_DATE, \DateTimeImmutable::createFromFormat('Y-m-d', '2020-01-01')),
],
]; ];
} }

View File

@ -50,12 +50,14 @@ final class UserJobFilterTest extends AbstractFilterTest
yield [ yield [
'jobs' => new ArrayCollection($jobs), 'jobs' => new ArrayCollection($jobs),
'date_calc' => new RollingDate(RollingDate::T_TODAY), 'start_date' => new RollingDate(RollingDate::T_YEAR_CURRENT_START),
'end_date' => new RollingDate(RollingDate::T_TODAY),
]; ];
yield [ yield [
'jobs' => $jobs, 'jobs' => $jobs,
'date_calc' => new RollingDate(RollingDate::T_TODAY), 'start_date' => new RollingDate(RollingDate::T_YEAR_CURRENT_START),
'end_date' => new RollingDate(RollingDate::T_TODAY),
]; ];
} }

View File

@ -50,11 +50,13 @@ final class UserScopeFilterTest extends AbstractFilterTest
return [ return [
[ [
'date_calc' => new RollingDate(RollingDate::T_TODAY), 'start_date' => new RollingDate(RollingDate::T_YEAR_CURRENT_START),
'end_date' => new RollingDate(RollingDate::T_TODAY),
'scopes' => new ArrayCollection($scopes), 'scopes' => new ArrayCollection($scopes),
], ],
[ [
'date_calc' => new RollingDate(RollingDate::T_TODAY), 'start_date' => new RollingDate(RollingDate::T_YEAR_CURRENT_START),
'end_date' => new RollingDate(RollingDate::T_TODAY),
'scopes' => $scopes, 'scopes' => $scopes,
], ],
]; ];

View File

@ -143,6 +143,10 @@ exports:
by_referrer_between_dates: by_referrer_between_dates:
description: >- description: >-
Filtré par référent du parcours, entre deux dates: depuis le {start_date, date, medium}, jusqu'au {end_date, date, medium}, seulement {agents} Filtré par référent du parcours, entre deux dates: depuis le {start_date, date, medium}, jusqu'au {end_date, date, medium}, seulement {agents}
by_user_job:
"Filtered by user job: only job": "Filtré par métier du référent entre le {startDate, date, short} et le {endDate, date, short}: uniquement {job}"
by_user_scope:
"Filtered by user main scope: only scopes": "Filtré par service du référent entre le {startDate, date, short} et le {endDate, date, short}: uniquement {scopes}"
work: work:
by_treating_agent: by_treating_agent:
Filtered by treating agent at date: >- Filtered by treating agent at date: >-

View File

@ -1058,9 +1058,15 @@ export:
by-user: by-user:
title: Grouper les parcours par usager participant title: Grouper les parcours par usager participant
header: Usager participant header: Usager participant
by_referrer: by_referrer:
Computation date for referrer: Date à laquelle le référent était actif Referrer after: Référent après le
Until: Jusqu'au
by_referrer_scope:
Referrer and scope after: Référent et service après le
Until: Jusqu'au
by_referrer_job:
Referrer and job after: Référent et métier après le
Until: Jusqu'au
by_user_scope: by_user_scope:
Group course by referrer's scope: Grouper les parcours par service du référent Group course by referrer's scope: Grouper les parcours par service du référent
Referrer's scope: Service du référent de parcours Referrer's scope: Service du référent de parcours
@ -1215,7 +1221,8 @@ export:
'Filtered by steps: only %step% and between %date_from% and %date_to%': 'Filtré par statut: seulement %step%, entre %date_from% et %date_to%' 'Filtered by steps: only %step% and between %date_from% and %date_to%': 'Filtré par statut: seulement %step%, entre %date_from% et %date_to%'
by_user_scope: by_user_scope:
Filter by user scope: Filtrer les parcours par service du référent Filter by user scope: Filtrer les parcours par service du référent
"Filtered by user main scope: only %scope%": "Filtré par service du référent: uniquement %scope%" Start from: Référent et service depuis le
Until: Jusqu'au
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
by_referrer_between_dates: by_referrer_between_dates:
@ -1232,7 +1239,8 @@ export:
'Filtered by creator job: only %jobs%': "Filtré par métier du créateur: uniquement %jobs%" 'Filtered by creator job: only %jobs%': "Filtré par métier du créateur: uniquement %jobs%"
by_user_job: by_user_job:
Filter by user job: Filtrer les parcours par métier du référent Filter by user job: Filtrer les parcours par métier du référent
"Filtered by user job: only %job%": "Filtré par métier du référent: uniquement %job%" Start from: Référent et métier depuis le
Until: Jusqu'au
by_social_action: by_social_action:
title: Filtrer les parcours par action d'accompagnement title: Filtrer les parcours par action d'accompagnement
Accepted socialactions: Actions d'accompagnement Accepted socialactions: Actions d'accompagnement

View File

@ -41,7 +41,7 @@ final class ChillReportExtensionTest extends KernelTestCase
} }
if (!$reportFounded) { if (!$reportFounded) {
throw new \Exception('Class Chill\\ReportBundle\\Entity\\Report not found in chill_custom_fields.customizables_entities', 1); throw new \Exception('Class Chill\ReportBundle\Entity\Report not found in chill_custom_fields.customizables_entities', 1);
} }
} }
} }

View File

@ -33,10 +33,10 @@ return static function (ContainerConfigurator $container) {
->autoconfigure(); ->autoconfigure();
$services $services
->load('Chill\\WopiBundle\\Service\\', __DIR__.'/../../Service'); ->load('Chill\WopiBundle\Service\\', __DIR__.'/../../Service');
$services $services
->load('Chill\\WopiBundle\\Controller\\', __DIR__.'/../../Controller') ->load('Chill\WopiBundle\Controller\\', __DIR__.'/../../Controller')
->tag('controller.service_arguments'); ->tag('controller.service_arguments');
$services $services