Merge remote-tracking branch 'origin/111_exports_suite' into calendar/finalization

This commit is contained in:
2022-11-25 15:29:17 +01:00
169 changed files with 7481 additions and 1515 deletions

View File

@@ -97,11 +97,11 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac
$loader->load('services/exports_person.yaml');
if ($container->getParameter('chill_person.accompanying_period') !== 'hidden') {
$loader->load('services/exports_accompanying_period.yaml');
$loader->load('services/exports_accompanying_course.yaml');
$loader->load('services/exports_social_actions.yaml');
$loader->load('services/exports_evaluation.yaml');
}
$loader->load('services/exports_accompanying_course.yaml');
$loader->load('services/exports_social_actions.yaml');
$loader->load('services/exports_evaluation.yaml');
$loader->load('services/exports_household.yaml');
}
@@ -944,10 +944,8 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac
/**
* Add a widget "add a person" on the homepage, automatically.
*
* @param \Chill\PersonBundle\DependencyInjection\containerBuilder $container
*/
protected function prependHomepageWidget(containerBuilder $container)
protected function prependHomepageWidget(ContainerBuilder $container)
{
$container->prependExtensionConfig('chill_main', [
'widgets' => [

View File

@@ -84,7 +84,7 @@ class PersonHouseholdAddress
public function getHousehold(): ?Household
{
return $this->relation;
return $this->household;
}
public function getPerson(): ?Person

View File

@@ -1768,7 +1768,7 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
}
/**
* @param type $spokenLanguages
* @param Collection $spokenLanguages
*/
public function setSpokenLanguages($spokenLanguages): self
{

View File

@@ -0,0 +1,67 @@
<?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\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
class ByActionNumberAggregator implements AggregatorInterface
{
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data): void
{
$qb->addSelect('(SELECT COUNT(acp_by_action_action.id) FROM ' . AccompanyingPeriodWork::class . ' acp_by_action_action WHERE acp_by_action_action.accompanyingPeriod = acp) AS acp_by_action_number_aggregator')
->addGroupBy('acp_by_action_number_aggregator');
}
public function applyOn(): string
{
return Declarations::ACP_TYPE;
}
public function buildForm(FormBuilderInterface $builder): void
{
// No form needed
}
public function getLabels($key, array $values, $data)
{
return static function ($value) {
if ('_header' === $value) {
return 'export.aggregator.course.by_number_of_action.Number of actions';
}
if (null === $value) {
return '';
}
return $value;
};
}
public function getQueryKeys($data): array
{
return ['acp_by_action_number_aggregator'];
}
public function getTitle(): string
{
return 'Group by number of actions';
}
}

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\Aggregator\AccompanyingCourseAggregators;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Repository\UserJobRepository;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use function in_array;
class CreatorJobAggregator implements AggregatorInterface
{
private UserJobRepository $jobRepository;
private TranslatableStringHelper $translatableStringHelper;
public function __construct(
UserJobRepository $jobRepository,
TranslatableStringHelper $translatableStringHelper
) {
$this->jobRepository = $jobRepository;
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('acp_creator', $qb->getAllAliases(), true)) {
$qb->leftJoin('acp.createdBy', 'acp_creator');
}
$qb->addSelect('IDENTITY(acp_creator.userJob) AS acp_creator_job_aggregator')
->addGroupBy('acp_creator_job_aggregator');
}
public function applyOn(): string
{
return Declarations::ACP_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
{
// No form needed
}
public function getLabels($key, array $values, $data)
{
return function ($value): string {
if ('_header' === $value) {
return 'export.aggregator.course.by_creator_job.Creator\'s job';
}
if (null === $value || null === $j = $this->jobRepository->find($value)) {
return '';
}
return $this->translatableStringHelper->localize(
$j->getLabel()
);
};
}
public function getQueryKeys($data): array
{
return ['acp_creator_job_aggregator'];
}
public function getTitle(): string
{
return 'Group by creator job';
}
}

View File

@@ -12,34 +12,37 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators;
use Chill\MainBundle\Entity\Address;
use Chill\MainBundle\Entity\GeographicalUnit;
use Chill\MainBundle\Entity\GeographicalUnitLayer;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Repository\GeographicalUnitLayerRepositoryInterface;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Entity\Household\PersonHouseholdAddress;
use Chill\PersonBundle\Export\Declarations;
use DateTimeImmutable;
use Doctrine\ORM\Query\Expr\Join;
use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface;
use UnexpectedValueException;
use function in_array;
final class GeographicalUnitStatAggregator implements AggregatorInterface
{
private GeographicalUnitLayerRepositoryInterface $geographicalUnitLayerRepository;
private RollingDateConverterInterface $rollingDateConverter;
private TranslatableStringHelperInterface $translatableStringHelper;
public function __construct(
GeographicalUnitLayerRepositoryInterface $geographicalUnitLayerRepository,
TranslatableStringHelperInterface $translatableStringHelper
TranslatableStringHelperInterface $translatableStringHelper,
RollingDateConverterInterface $rollingDateConverter
) {
$this->geographicalUnitLayerRepository = $geographicalUnitLayerRepository;
$this->translatableStringHelper = $translatableStringHelper;
$this->rollingDateConverter = $rollingDateConverter;
}
public function addRole(): ?string
@@ -49,57 +52,50 @@ final class GeographicalUnitStatAggregator implements AggregatorInterface
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('acp_geog_agg_location_history', $qb->getAllAliases(), true)) {
$qb->leftJoin('acp.locationHistories', 'acp_geog_agg_location_history');
$qb->leftJoin('acp.locationHistories', 'acp_geog_agg_location_history');
$qb->andWhere(
$qb->expr()->andX(
'acp_geog_agg_location_history.startDate <= :acp_geog_aggregator_date',
$qb->expr()->orX(
'acp_geog_agg_location_history.endDate IS NULL',
'acp_geog_agg_location_history.endDate > :acp_geog_aggregator_date'
)
$qb->andWhere(
$qb->expr()->andX(
'acp_geog_agg_location_history.startDate <= :acp_geog_aggregator_date',
$qb->expr()->orX(
'acp_geog_agg_location_history.endDate IS NULL',
'acp_geog_agg_location_history.endDate > :acp_geog_aggregator_date'
)
);
$qb->setParameter('acp_geog_aggregator_date', $data['date_calc']);
}
)
);
// link between location history and person
if (!in_array('acp_geog_agg_address_person_location', $qb->getAllAliases(), true)) {
$qb->leftJoin(
PersonHouseholdAddress::class,
'acp_geog_agg_address_person_location',
Join::WITH,
$qb->expr()->andX(
'IDENTITY(acp_geog_agg_address_person_location.person) = IDENTITY(acp_geog_agg_location_history.personLocation)',
'acp_geog_agg_address_person_location.validFrom < :acp_geog_aggregator_date',
$qb->expr()->orX(
'acp_geog_agg_address_person_location.validTo > :acp_geog_aggregator_date',
$qb->expr()->isNull('acp_geog_agg_address_person_location.validTo')
)
$qb->leftJoin(
PersonHouseholdAddress::class,
'acp_geog_agg_address_person_location',
Join::WITH,
$qb->expr()->andX(
'IDENTITY(acp_geog_agg_address_person_location.person) = IDENTITY(acp_geog_agg_location_history.personLocation)',
'acp_geog_agg_address_person_location.validFrom < :acp_geog_aggregator_date',
$qb->expr()->orX(
'acp_geog_agg_address_person_location.validTo > :acp_geog_aggregator_date',
$qb->expr()->isNull('acp_geog_agg_address_person_location.validTo')
)
);
)
);
$qb->setParameter('acp_geog_aggregator_date', $data['date_calc']);
}
$qb->setParameter(
'acp_geog_aggregator_date',
$this->rollingDateConverter->convert($data['date_calc'])
);
// we finally find an address
if (!in_array('acp_geog_agg_address', $qb->getAllAliases(), true)) {
$qb->leftJoin(
Address::class,
'acp_geog_agg_address',
Join::WITH,
'COALESCE(IDENTITY(acp_geog_agg_address_person_location.address), IDENTITY(acp_geog_agg_location_history.addressLocation)) = acp_geog_agg_address.id'
);
}
$qb->leftJoin(
Address::class,
'acp_geog_agg_address',
Join::WITH,
'COALESCE(IDENTITY(acp_geog_agg_address_person_location.address), IDENTITY(acp_geog_agg_location_history.addressLocation)) = acp_geog_agg_address.id'
);
// and we do a join with units
$qb->leftJoin(
GeographicalUnit::class,
'acp_geog_units',
Join::WITH,
'ST_CONTAINS(acp_geog_units.geom, acp_geog_agg_address.point) = TRUE'
'acp_geog_agg_address.geographicalUnits',
'acp_geog_units'
);
$qb->andWhere($qb->expr()->eq('acp_geog_units.layer', ':acp_geog_unit_layer'));
@@ -122,11 +118,10 @@ final class GeographicalUnitStatAggregator implements AggregatorInterface
public function buildForm(FormBuilderInterface $builder)
{
$builder
->add('date_calc', ChillDateType::class, [
->add('date_calc', PickRollingDateType::class, [
'label' => 'Compute geographical location at date',
'required' => true,
'data' => new DateTimeImmutable('today'),
'input' => 'datetime_immutable',
'data' => new RollingDate(RollingDate::T_TODAY),
])
->add('level', EntityType::class, [
'label' => 'Geographical layer',

View File

@@ -12,11 +12,12 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Repository\UserRepository;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\MainBundle\Templating\Entity\UserRender;
use Chill\PersonBundle\Export\Declarations;
use DateTimeImmutable;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
@@ -26,16 +27,20 @@ final class ReferrerAggregator implements AggregatorInterface
private const P = 'acp_ref_agg_date';
private RollingDateConverterInterface $rollingDateConverter;
private UserRender $userRender;
private UserRepository $userRepository;
public function __construct(
UserRepository $userRepository,
UserRender $userRender
UserRender $userRender,
RollingDateConverterInterface $rollingDateConverter
) {
$this->userRepository = $userRepository;
$this->userRender = $userRender;
$this->rollingDateConverter = $rollingDateConverter;
}
public function addRole(): ?string
@@ -61,7 +66,10 @@ final class ReferrerAggregator implements AggregatorInterface
)
)
)
->setParameter(self::P, $data['date_calc']);
->setParameter(
self::P,
$this->rollingDateConverter->convert($data['date_calc'])
);
}
public function applyOn(): string
@@ -72,9 +80,8 @@ final class ReferrerAggregator implements AggregatorInterface
public function buildForm(FormBuilderInterface $builder)
{
$builder
->add('date_calc', ChillDateType::class, [
'input' => 'datetime_immutable',
'data' => new DateTimeImmutable('now'),
->add('date_calc', PickRollingDateType::class, [
'data' => new RollingDate(RollingDate::T_TODAY),
'label' => 'export.aggregator.course.by_referrer.Computation date for referrer',
'required' => true,
]);

View File

@@ -12,11 +12,12 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Repository\ScopeRepositoryInterface;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Export\Declarations;
use DateTimeImmutable;
use Doctrine\ORM\QueryBuilder;
use LogicException;
use Symfony\Component\Form\FormBuilderInterface;
@@ -25,16 +26,20 @@ class ReferrerScopeAggregator implements AggregatorInterface
{
private const SCOPE_KEY = 'acp_agg_refscope_user_history_ref_scope_name';
private RollingDateConverterInterface $rollingDateConverter;
private ScopeRepositoryInterface $scopeRepository;
private TranslatableStringHelperInterface $translatableStringHelper;
public function __construct(
ScopeRepositoryInterface $scopeRepository,
TranslatableStringHelperInterface $translatableStringHelper
TranslatableStringHelperInterface $translatableStringHelper,
RollingDateConverterInterface $rollingDateConverter
) {
$this->scopeRepository = $scopeRepository;
$this->translatableStringHelper = $translatableStringHelper;
$this->rollingDateConverter = $rollingDateConverter;
}
public function addRole(): ?string
@@ -64,7 +69,10 @@ class ReferrerScopeAggregator implements AggregatorInterface
)
)
)
->setParameter($dateCalc, $data['date_calc']);
->setParameter(
$dateCalc,
$this->rollingDateConverter->convert($data['date_calc'])
);
// add groups
$qb
@@ -79,9 +87,8 @@ class ReferrerScopeAggregator implements AggregatorInterface
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('date_calc', ChillDateType::class, [
'input' => 'datetime_immutable',
'data' => new DateTimeImmutable('now'),
$builder->add('date_calc', PickRollingDateType::class, [
'data' => new RollingDate(RollingDate::T_TODAY),
'label' => 'export.aggregator.course.by_user_scope.Computation date for referrer',
'required' => true,
]);

View File

@@ -12,10 +12,11 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Export\Declarations;
use DateTime;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
@@ -27,11 +28,15 @@ final class StepAggregator implements AggregatorInterface
private const P = 'acp_step_agg_date';
private RollingDateConverterInterface $rollingDateConverter;
private TranslatorInterface $translator;
public function __construct(
RollingDateConverterInterface $rollingDateConverter,
TranslatorInterface $translator
) {
$this->rollingDateConverter = $rollingDateConverter;
$this->translator = $translator;
}
@@ -60,7 +65,7 @@ final class StepAggregator implements AggregatorInterface
)
)
)
->setParameter(self::P, $data['on_date'])
->setParameter(self::P, $this->rollingDateConverter->convert($data['on_date']))
->addGroupBy('step_aggregator');
}
@@ -71,8 +76,8 @@ final class StepAggregator implements AggregatorInterface
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('on_date', ChillDateType::class, [
'data' => new DateTime(),
$builder->add('on_date', PickRollingDateType::class, [
'data' => new RollingDate(RollingDate::T_TODAY),
]);
}

View File

@@ -0,0 +1,106 @@
<?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\EvaluationAggregators;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\QueryBuilder;
use LogicException;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
class ByEndDateAggregator implements AggregatorInterface
{
private const CHOICES = [
'by week' => 'week',
'by month' => 'month',
'by year' => 'year',
];
private const DEFAULT_CHOICE = 'year';
private TranslatorInterface $translator;
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
switch ($data['frequency']) {
case 'week':
$fmt = 'YYYY-IW';
break;
case 'month':
$fmt = 'YYYY-MM';
break;
case 'year':
$fmt = 'YYYY';
break;
default:
throw new LogicException(sprintf("The frequency data '%s' is invalid.", $data['frequency']));
}
$qb->addSelect(sprintf("TO_CHAR(workeval.endDate, '%s') AS eval_by_end_date_aggregator", $fmt));
$qb->addGroupBy(' eval_by_end_date_aggregator');
$qb->addOrderBy(' eval_by_end_date_aggregator', 'ASC');
}
public function applyOn(): string
{
return Declarations::EVAL_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('frequency', ChoiceType::class, [
'choices' => self::CHOICES,
'multiple' => false,
'expanded' => true,
'empty_data' => self::DEFAULT_CHOICE,
'data' => self::DEFAULT_CHOICE,
]);
}
public function getLabels($key, array $values, $data)
{
return static function ($value): string {
if ('_header' === $value) {
return 'export.aggregator.eval.by_end_date.End date period';
}
if (null === $value) {
return '';
}
return $value;
};
}
public function getQueryKeys($data): array
{
return ['eval_by_end_date_aggregator'];
}
public function getTitle(): string
{
return 'export.aggregator.eval.by_end_date.Group by end date evaluations';
}
}

View File

@@ -0,0 +1,106 @@
<?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\EvaluationAggregators;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\QueryBuilder;
use LogicException;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
class ByMaxDateAggregator implements AggregatorInterface
{
private const CHOICES = [
'by week' => 'week',
'by month' => 'month',
'by year' => 'year',
];
private const DEFAULT_CHOICE = 'year';
private TranslatorInterface $translator;
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
switch ($data['frequency']) {
case 'week':
$fmt = 'YYYY-IW';
break;
case 'month':
$fmt = 'YYYY-MM';
break;
case 'year':
$fmt = 'YYYY';
break;
default:
throw new LogicException(sprintf("The frequency data '%s' is invalid.", $data['frequency']));
}
$qb->addSelect(sprintf("TO_CHAR(workeval.maxDate, '%s') AS eval_by_max_date_aggregator", $fmt));
$qb->addGroupBy(' eval_by_max_date_aggregator');
$qb->addOrderBy(' eval_by_max_date_aggregator', 'ASC');
}
public function applyOn(): string
{
return Declarations::EVAL_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('frequency', ChoiceType::class, [
'choices' => self::CHOICES,
'multiple' => false,
'expanded' => true,
'empty_data' => self::DEFAULT_CHOICE,
'data' => self::DEFAULT_CHOICE,
]);
}
public function getLabels($key, array $values, $data)
{
return static function ($value): string {
if ('_header' === $value) {
return 'export.aggregator.eval.by_max_date.Max date';
}
if (null === $value) {
return '';
}
return $value;
};
}
public function getQueryKeys($data): array
{
return ['eval_by_max_date_aggregator'];
}
public function getTitle(): string
{
return 'export.aggregator.eval.by_max_date.Group by max date evaluations';
}
}

View File

@@ -0,0 +1,106 @@
<?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\EvaluationAggregators;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\QueryBuilder;
use LogicException;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
class ByStartDateAggregator implements AggregatorInterface
{
private const CHOICES = [
'by week' => 'week',
'by month' => 'month',
'by year' => 'year',
];
private const DEFAULT_CHOICE = 'year';
private TranslatorInterface $translator;
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
switch ($data['frequency']) {
case 'week':
$fmt = 'YYYY-IW';
break;
case 'month':
$fmt = 'YYYY-MM';
break;
case 'year':
$fmt = 'YYYY';
break;
default:
throw new LogicException(sprintf("The frequency data '%s' is invalid.", $data['frequency']));
}
$qb->addSelect(sprintf("TO_CHAR(workeval.startDate, '%s') AS eval_by_start_date_aggregator", $fmt));
$qb->addGroupBy(' eval_by_start_date_aggregator');
$qb->addOrderBy(' eval_by_start_date_aggregator', 'ASC');
}
public function applyOn(): string
{
return Declarations::EVAL_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('frequency', ChoiceType::class, [
'choices' => self::CHOICES,
'multiple' => false,
'expanded' => true,
'empty_data' => self::DEFAULT_CHOICE,
'data' => self::DEFAULT_CHOICE,
]);
}
public function getLabels($key, array $values, $data)
{
return static function ($value): string {
if ('_header' === $value) {
return 'export.aggregator.eval.by_start_date_period.Start date period';
}
if (null === $value) {
return '';
}
return $value;
};
}
public function getQueryKeys($data): array
{
return ['eval_by_start_date_aggregator'];
}
public function getTitle(): string
{
return 'export.aggregator.eval.by_start_date_period.Group by start date evaluations';
}
}

View File

@@ -0,0 +1,81 @@
<?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\EvaluationAggregators;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\QueryBuilder;
use LogicException;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
class HavingEndDateAggregator implements AggregatorInterface
{
private TranslatorInterface $translator;
public function __construct(TranslatorInterface $translator)
{
$this->translator = $translator;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$qb
->addSelect('CASE WHEN workeval.endDate IS NULL THEN true ELSE false END AS eval_enddate_aggregator')
->addGroupBy('eval_enddate_aggregator');
}
public function applyOn(): string
{
return Declarations::EVAL_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
{
// No form needed
}
public function getLabels($key, array $values, $data)
{
return function ($value): string {
if ('_header' === $value) {
return 'export.aggregator.eval.by_end_date.Has end date ?';
}
switch ($value) {
case true:
return $this->translator->trans('export.aggregator.eval.by_end_date.enddate is specified');
case false:
return $this->translator->trans('export.aggregator.eval.by_end_date.enddate is not specified');
default:
throw new LogicException(sprintf('The value %s is not valid', $value));
}
};
}
public function getQueryKeys($data): array
{
return ['eval_enddate_aggregator'];
}
public function getTitle(): string
{
return 'export.aggregator.eval.by_end_date.Group evaluations having end date';
}
}

View File

@@ -12,10 +12,10 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Export\Aggregator\HouseholdAggregators;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\PersonBundle\Export\Declarations;
use DateTime;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Query\Expr;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
@@ -24,12 +24,16 @@ use function in_array;
class ChildrenNumberAggregator implements AggregatorInterface
{
private RollingDateConverterInterface $rollingDateConverter;
private TranslatorInterface $translator;
public function __construct(
TranslatorInterface $translator
TranslatorInterface $translator,
RollingDateConverterInterface $rollingDateConverter
) {
$this->translator = $translator;
$this->rollingDateConverter = $rollingDateConverter;
}
public function addRole(): ?string
@@ -55,7 +59,10 @@ class ChildrenNumberAggregator implements AggregatorInterface
->addSelect('composition_children.numberOfChildren AS childrennumber_aggregator')
->addGroupBy('childrennumber_aggregator');
$qb->setParameter('ondate_composition_children', $data['on_date'], Types::DATE_MUTABLE);
$qb->setParameter(
'ondate_composition_children',
$this->rollingDateConverter->convert($data['on_date'])
);
}
public function applyOn(): string
@@ -65,8 +72,8 @@ class ChildrenNumberAggregator implements AggregatorInterface
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('on_date', ChillDateType::class, [
'data' => new DateTime('now'),
$builder->add('on_date', PickRollingDateType::class, [
'data' => new RollingDate(RollingDate::T_TODAY),
]);
}

View File

@@ -12,12 +12,12 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Export\Aggregator\HouseholdAggregators;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Repository\Household\HouseholdCompositionTypeRepository;
use DateTime;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Query\Expr;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
@@ -25,16 +25,20 @@ use function in_array;
class CompositionAggregator implements AggregatorInterface
{
private RollingDateConverterInterface $rollingDateConverter;
private TranslatableStringHelper $translatableStringHelper;
private HouseholdCompositionTypeRepository $typeRepository;
public function __construct(
HouseholdCompositionTypeRepository $typeRepository,
TranslatableStringHelper $translatableStringHelper
TranslatableStringHelper $translatableStringHelper,
RollingDateConverterInterface $rollingDateConverter
) {
$this->typeRepository = $typeRepository;
$this->translatableStringHelper = $translatableStringHelper;
$this->rollingDateConverter = $rollingDateConverter;
}
public function addRole(): ?string
@@ -60,7 +64,10 @@ class CompositionAggregator implements AggregatorInterface
->addSelect('IDENTITY(composition_type.householdCompositionType) AS composition_aggregator')
->addGroupBy('composition_aggregator');
$qb->setParameter('ondate_composition_type', $data['on_date'], Types::DATE_MUTABLE);
$qb->setParameter(
'ondate_composition_type',
$this->rollingDateConverter->convert($data['on_date'])
);
}
public function applyOn(): string
@@ -70,8 +77,8 @@ class CompositionAggregator implements AggregatorInterface
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('on_date', ChillDateType::class, [
'data' => new DateTime('now'),
$builder->add('on_date', PickRollingDateType::class, [
'data' => new RollingDate(RollingDate::T_TODAY),
]);
}

View File

@@ -9,19 +9,19 @@ declare(strict_types=1);
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators;
namespace Chill\PersonBundle\Export\Aggregator\PersonAggregators;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Entity\Household\HouseholdComposition;
use Chill\PersonBundle\Entity\Household\HouseholdMember;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Repository\Household\HouseholdCompositionTypeRepositoryInterface;
use Doctrine\ORM\Query\Expr\Join;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use function in_array;
class ByHouseholdCompositionAggregator implements AggregatorInterface
{
@@ -29,12 +29,18 @@ class ByHouseholdCompositionAggregator implements AggregatorInterface
private HouseholdCompositionTypeRepositoryInterface $householdCompositionTypeRepository;
private RollingDateConverterInterface $rollingDateConverter;
private TranslatableStringHelperInterface $translatableStringHelper;
public function __construct(HouseholdCompositionTypeRepositoryInterface $householdCompositionTypeRepository, TranslatableStringHelperInterface $translatableStringHelper)
{
public function __construct(
RollingDateConverterInterface $rollingDateConverter,
HouseholdCompositionTypeRepositoryInterface $householdCompositionTypeRepository,
TranslatableStringHelperInterface $translatableStringHelper
) {
$this->householdCompositionTypeRepository = $householdCompositionTypeRepository;
$this->translatableStringHelper = $translatableStringHelper;
$this->rollingDateConverter = $rollingDateConverter;
}
public function addRole(): ?string
@@ -46,30 +52,20 @@ class ByHouseholdCompositionAggregator implements AggregatorInterface
{
$p = self::PREFIX;
if (!in_array('acppart', $qb->getAllAliases(), true)) {
$qb->leftJoin('acp.participations', 'acppart');
}
$qb
->leftJoin(
HouseholdMember::class,
"{$p}_hm",
Join::WITH,
$qb->expr()->orX(
$qb->expr()->isNull("{$p}_hm"),
$qb->expr()->andX(
$qb->expr()->lte("{$p}_hm.startDate", ":{$p}_date"),
$qb->expr()->orX(
$qb->expr()->isNull("{$p}_hm.endDate"),
$qb->expr()->gt("{$p}_hm.endDate", ":{$p}_date")
)
)
)
'person.householdParticipations',
"{$p}_hm"
)
->leftJoin(
HouseholdComposition::class,
"{$p}_compo",
Join::WITH,
$qb->expr()->eq("{$p}_hm.household", "{$p}_compo.household"),
)
->andWhere("{$p}_hm.startDate <= :{$p}_date AND ({$p}_hm.endDate IS NULL OR {$p}_hm.endDate > :{$p}_date)")
->andWhere("{$p}_hm.shareHousehold = 'TRUE'")
->andWhere(
$qb->expr()->orX(
$qb->expr()->isNull("{$p}_compo"),
$qb->expr()->andX(
@@ -82,21 +78,23 @@ class ByHouseholdCompositionAggregator implements AggregatorInterface
)
)
->addSelect("IDENTITY({$p}_compo.householdCompositionType) AS {$p}_select")
->setParameter("{$p}_date", $data['date_calc'])
->setParameter(
"{$p}_date",
$this->rollingDateConverter->convert($data['date_calc'])
)
->addGroupBy("{$p}_select");
}
public function applyOn()
{
return Declarations::ACP_TYPE;
return Declarations::PERSON_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('date_calc', ChillDateType::class, [
'label' => 'export.aggregator.course.by_household_composition.Calc date',
'input_format' => 'datetime_immutable',
'data' => new \DateTimeImmutable('now'),
$builder->add('date_calc', PickRollingDateType::class, [
'label' => 'export.aggregator.person.by_household_composition.Calc date',
'data' => new RollingDate(RollingDate::T_TODAY),
]);
}
@@ -104,7 +102,7 @@ class ByHouseholdCompositionAggregator implements AggregatorInterface
{
return function ($value) {
if ('_header' === $value) {
return 'export.aggregator.course.by_household_composition.Household composition';
return 'export.aggregator.person.by_household_composition.Household composition';
}
if (null === $value) {
@@ -126,6 +124,6 @@ class ByHouseholdCompositionAggregator implements AggregatorInterface
public function getTitle()
{
return 'export.aggregator.course.by_household_composition.Group course by household composition';
return 'export.aggregator.person.by_household_composition.Group course by household composition';
}
}

View File

@@ -11,15 +11,14 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Export\Aggregator\PersonAggregators;
use Chill\MainBundle\Entity\GeographicalUnit;
use Chill\MainBundle\Entity\GeographicalUnitLayer;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Repository\GeographicalUnitLayerRepositoryInterface;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Export\Declarations;
use DateTimeImmutable;
use Doctrine\ORM\Query\Expr\Join;
use Doctrine\ORM\QueryBuilder;
use LogicException;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
@@ -29,14 +28,18 @@ class GeographicalUnitAggregator implements AggregatorInterface
{
private GeographicalUnitLayerRepositoryInterface $geographicalUnitLayerRepository;
private RollingDateConverterInterface $rollingDateConverter;
private TranslatableStringHelperInterface $translatableStringHelper;
public function __construct(
GeographicalUnitLayerRepositoryInterface $geographicalUnitLayerRepository,
TranslatableStringHelperInterface $translatableStringHelper
TranslatableStringHelperInterface $translatableStringHelper,
RollingDateConverterInterface $rollingDateConverter
) {
$this->geographicalUnitLayerRepository = $geographicalUnitLayerRepository;
$this->translatableStringHelper = $translatableStringHelper;
$this->rollingDateConverter = $rollingDateConverter;
}
public function addRole(): ?string
@@ -49,7 +52,7 @@ class GeographicalUnitAggregator implements AggregatorInterface
$qb
->leftJoin('person.householdAddresses', 'person_geog_agg_current_household_address')
->leftJoin('person_geog_agg_current_household_address.address', 'person_geog_agg_address')
->leftJoin(GeographicalUnit::class, 'person_geog_agg_geog_unit', Join::WITH, 'ST_CONTAINS(person_geog_agg_geog_unit.geom, person_geog_agg_address.point) = TRUE')
->leftJoin('person_geog_agg_address.geographicalUnits', 'person_geog_agg_geog_unit')
->andWhere(
$qb->expr()->orX(
$qb->expr()->isNull('person_geog_agg_current_household_address'),
@@ -68,7 +71,10 @@ class GeographicalUnitAggregator implements AggregatorInterface
$qb->expr()->in('person_geog_agg_geog_unit.layer', ':person_geog_agg_layers')
)
)
->setParameter('person_geog_agg_date', $data['date_calc'])
->setParameter(
'person_geog_agg_date',
$this->rollingDateConverter->convert($data['date_calc'])
)
->setParameter('person_geog_agg_layers', $data['level'])
->addSelect('person_geog_agg_geog_unit.unitName AS geog_unit_name')
->addSelect('person_geog_agg_geog_unit.unitRefId AS geog_unit_key')
@@ -84,11 +90,10 @@ class GeographicalUnitAggregator implements AggregatorInterface
public function buildForm(FormBuilderInterface $builder)
{
$builder
->add('date_calc', ChillDateType::class, [
->add('date_calc', PickRollingDateType::class, [
'label' => 'Address valid at this date',
'required' => true,
'data' => new DateTimeImmutable('today'),
'input' => 'datetime_immutable',
'data' => new RollingDate(RollingDate::T_TODAY),
])
->add('level', EntityType::class, [
'label' => 'Geographical layer',

View File

@@ -13,12 +13,13 @@ namespace Chill\PersonBundle\Export\Aggregator\PersonAggregators;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Export\ExportElementValidatedInterface;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\PersonBundle\Entity\Household\HouseholdMember;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Repository\Household\PositionRepository;
use DateTime;
use Doctrine\ORM\Query\Expr;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
@@ -30,6 +31,8 @@ final class HouseholdPositionAggregator implements AggregatorInterface, ExportEl
{
private PositionRepository $positionRepository;
private RollingDateConverterInterface $rollingDateConverter;
private TranslatableStringHelper $translatableStringHelper;
private TranslatorInterface $translator;
@@ -37,11 +40,13 @@ final class HouseholdPositionAggregator implements AggregatorInterface, ExportEl
public function __construct(
TranslatorInterface $translator,
TranslatableStringHelper $translatableStringHelper,
PositionRepository $positionRepository
PositionRepository $positionRepository,
RollingDateConverterInterface $rollingDateConverter
) {
$this->translator = $translator;
$this->positionRepository = $positionRepository;
$this->translatableStringHelper = $translatableStringHelper;
$this->rollingDateConverter = $rollingDateConverter;
}
public function addRole(): ?string
@@ -67,7 +72,10 @@ final class HouseholdPositionAggregator implements AggregatorInterface, ExportEl
)
));
$qb->setParameter('date', $data['date_position']);
$qb->setParameter(
'date',
$this->rollingDateConverter->convert($data['date_position'])
);
$qb->addSelect('IDENTITY(householdmember.position) AS household_position_aggregator');
$qb->addGroupBy('household_position_aggregator');
@@ -80,9 +88,9 @@ final class HouseholdPositionAggregator implements AggregatorInterface, ExportEl
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('date_position', ChillDateType::class, [
$builder->add('date_position', PickRollingDateType::class, [
'label' => 'Household position in relation to this date',
'data' => new DateTime(),
'data' => new RollingDate(RollingDate::T_TODAY),
]);
}

View File

@@ -14,8 +14,11 @@ namespace Chill\PersonBundle\Export\Aggregator\SocialWorkAggregators;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Repository\SocialWork\SocialActionRepository;
use Chill\PersonBundle\Repository\SocialWork\SocialIssueRepository;
use Chill\PersonBundle\Templating\Entity\SocialActionRender;
use Chill\PersonBundle\Templating\Entity\SocialIssueRender;
use Doctrine\ORM\QueryBuilder;
use LogicException;
use Symfony\Component\Form\FormBuilderInterface;
use function in_array;
@@ -25,12 +28,20 @@ final class ActionTypeAggregator implements AggregatorInterface
private SocialActionRepository $socialActionRepository;
private SocialIssueRender $socialIssueRender;
private SocialIssueRepository $socialIssueRepository;
public function __construct(
SocialActionRepository $socialActionRepository,
SocialActionRender $actionRender
SocialActionRender $actionRender,
SocialIssueRender $socialIssueRender,
SocialIssueRepository $socialIssueRepository
) {
$this->socialActionRepository = $socialActionRepository;
$this->actionRender = $actionRender;
$this->socialIssueRender = $socialIssueRender;
$this->socialIssueRepository = $socialIssueRepository;
}
public function addRole(): ?string
@@ -44,8 +55,15 @@ final class ActionTypeAggregator implements AggregatorInterface
$qb->leftJoin('acpw.socialAction', 'acpwsocialaction');
}
$qb->addSelect('acpwsocialaction.id as action_type_aggregator');
$qb->addGroupBy('action_type_aggregator');
if (!in_array('acpwsocialissue', $qb->getAllAliases(), true)) {
$qb->leftJoin('acpwsocialaction.issue', 'acpwsocialissue');
}
$qb
->addSelect('acpwsocialissue.id as social_action_type_aggregator')
->addSelect('acpwsocialaction.id as action_type_aggregator')
->addGroupBy('action_type_aggregator')
->addGroupBy('social_action_type_aggregator');
}
public function applyOn()
@@ -60,24 +78,41 @@ final class ActionTypeAggregator implements AggregatorInterface
public function getLabels($key, array $values, $data)
{
return function ($value): string {
if ('_header' === $value) {
return 'Social Action Type';
}
switch ($key) {
case 'action_type_aggregator':
return function ($value): string {
if ('_header' === $value) {
return 'Social Action Type';
}
if (null === $value) {
return '';
}
if (null === $value || null === $sa = $this->socialActionRepository->find($value)) {
return '';
}
$sa = $this->socialActionRepository->find($value);
return $this->actionRender->renderString($sa, []);
};
return $this->actionRender->renderString($sa, []);
};
case 'social_action_type_aggregator':
return function ($value): string {
if ('_header' === $value) {
return 'Social Issue';
}
if (null === $value || null === $si = $this->socialIssueRepository->find($value)) {
return '';
}
return $this->socialIssueRender->renderString($si, []);
};
default:
throw new LogicException('this key is not supported: ' . $key);
}
}
public function getQueryKeys($data)
{
return ['action_type_aggregator'];
return ['social_action_type_aggregator', 'action_type_aggregator'];
}
public function getTitle()

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\SocialWorkAggregators;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\QueryBuilder;
use LogicException;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
class CurrentActionAggregator implements AggregatorInterface
{
private TranslatorInterface $translator;
public function __construct(TranslatorInterface $translator)
{
$this->translator = $translator;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data): void
{
$qb
->addSelect('
(CASE WHEN acpw.endDate IS NULL THEN true ELSE false END)
AS acpw_current_action_aggregator
')
->addGroupBy('acpw_current_action_aggregator');
}
public function applyOn(): string
{
return Declarations::SOCIAL_WORK_ACTION_TYPE;
}
public function buildForm(FormBuilderInterface $builder): void
{
// No form needed
}
public function getLabels($key, array $values, $data)
{
return function ($value): string {
if ('_header' === $value) {
return 'export.aggregator.course_work.by_current_action.Current action ?';
}
switch ($value) {
case true:
return $this->translator->trans('export.aggregator.course_work.by_current_action.Current action');
case false:
return $this->translator->trans('export.aggregator.course_work.by_current_action.Not current action');
default:
throw new LogicException(sprintf('The value %s is not valid', $value));
}
};
}
public function getQueryKeys($data): array
{
return ['acpw_current_action_aggregator'];
}
public function getTitle(): string
{
return 'export.aggregator.course_work.by_current_action.Group by current actions';
}
}

View File

@@ -15,7 +15,6 @@ use Chill\MainBundle\Export\ExportInterface;
use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\GroupedExportInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation;
use Chill\PersonBundle\Entity\Person\PersonCenterHistory;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
@@ -38,7 +37,7 @@ class CountAccompanyingCourse implements ExportInterface, GroupedExportInterface
public function buildForm(FormBuilderInterface $builder): void
{
// TODO: Implement buildForm() method.
// Nothing to add here
}
public function getAllowedFormattersTypes(): array
@@ -100,11 +99,13 @@ class CountAccompanyingCourse implements ExportInterface, GroupedExportInterface
$qb
->andWhere('acp.step != :count_acp_step')
->leftJoin('acp.participations', 'acppart')
->leftJoin('acppart.person', 'person')
->andWhere('acppart.startDate != acppart.endDate OR acppart.endDate IS NULL')
->andWhere(
$qb->expr()->exists(
'SELECT 1 FROM ' . AccompanyingPeriodParticipation::class . ' acl_count_part
JOIN ' . PersonCenterHistory::class . ' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person)
WHERE acl_count_part.accompanyingPeriod = acp.id AND acl_count_person_history.center IN (:authorized_centers)
'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)
'
)
)
@@ -125,6 +126,7 @@ class CountAccompanyingCourse implements ExportInterface, GroupedExportInterface
{
return [
Declarations::ACP_TYPE,
Declarations::PERSON_TYPE,
];
}
}

View File

@@ -15,25 +15,22 @@ use Chill\MainBundle\Export\ExportInterface;
use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\GroupedExportInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation;
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 LogicException;
use Symfony\Component\Form\FormBuilderInterface;
use function in_array;
class CountEvaluation implements ExportInterface, GroupedExportInterface
{
private EntityRepository $repository;
private EntityManagerInterface $entityManager;
public function __construct(
EntityManagerInterface $em
) {
$this->repository = $em->getRepository(AccompanyingPeriod::class);
$this->entityManager = $em;
}
public function buildForm(FormBuilderInterface $builder)
@@ -96,22 +93,19 @@ class CountEvaluation implements ExportInterface, GroupedExportInterface
return $el['center'];
}, $acl);
$qb = $this->repository->createQueryBuilder('acp');
if (!in_array('acpw', $qb->getAllAliases(), true)) {
$qb->join('acp.works', 'acpw');
}
if (!in_array('workeval', $qb->getAllAliases(), true)) {
$qb->join('acpw.accompanyingPeriodWorkEvaluations', 'workeval');
}
$qb = $this->entityManager->createQueryBuilder();
$qb
->from(AccompanyingPeriod\AccompanyingPeriodWorkEvaluation::class, 'workeval')
->join('workeval.accompanyingPeriodWork', 'acpw')
->join('acpw.accompanyingPeriod', 'acp')
->join('acp.participations', 'acppart')
->join('acppart.person', 'person')
->andWhere('acppart.startDate != acppart.endDate OR acppart.endDate IS NULL')
->andWhere(
$qb->expr()->exists(
'SELECT 1 FROM ' . AccompanyingPeriodParticipation::class . ' acl_count_part
JOIN ' . PersonCenterHistory::class . ' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person)
WHERE acl_count_part.accompanyingPeriod = acp.id AND acl_count_person_history.center IN (:authorized_centers)
'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)
'
)
)
@@ -133,6 +127,7 @@ class CountEvaluation implements ExportInterface, GroupedExportInterface
Declarations::EVAL_TYPE,
Declarations::SOCIAL_WORK_ACTION_TYPE,
Declarations::ACP_TYPE,
Declarations::PERSON_TYPE,
];
}
}

View File

@@ -14,31 +14,42 @@ namespace Chill\PersonBundle\Export\Export;
use Chill\MainBundle\Export\ExportInterface;
use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\GroupedExportInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation;
use Chill\PersonBundle\Entity\Household\HouseholdMember;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\PersonBundle\Entity\Household\Household;
use Chill\PersonBundle\Entity\Person\PersonCenterHistory;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Security\Authorization\HouseholdVoter;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query;
use LogicException;
use Symfony\Component\Form\FormBuilderInterface;
class CountHousehold implements ExportInterface, GroupedExportInterface
{
private EntityRepository $repository;
private const TR_PREFIX = 'export.export.nb_household_with_course.';
private EntityManagerInterface $entityManager;
private RollingDateConverterInterface $rollingDateConverter;
public function __construct(
EntityManagerInterface $em
EntityManagerInterface $em,
RollingDateConverterInterface $rollingDateConverter
) {
$this->repository = $em->getRepository(AccompanyingPeriod::class);
$this->entityManager = $em;
$this->rollingDateConverter = $rollingDateConverter;
}
public function buildForm(FormBuilderInterface $builder)
{
// TODO: Implement buildForm() method.
$builder
->add('calc_date', PickRollingDateType::class, [
'data' => new RollingDate(RollingDate::T_TODAY),
'label' => self::TR_PREFIX . 'Date of calculation of household members',
'required' => false,
]);
}
public function getAllowedFormattersTypes(): array
@@ -48,7 +59,7 @@ class CountHousehold implements ExportInterface, GroupedExportInterface
public function getDescription(): string
{
return 'Count household by various parameters.';
return self::TR_PREFIX . 'Count household with accompanying course by various parameters.';
}
public function getGroup(): string
@@ -58,21 +69,31 @@ class CountHousehold implements ExportInterface, GroupedExportInterface
public function getLabels($key, array $values, $data)
{
if ('export_result' !== $key) {
throw new LogicException("the key {$key} is not used by this export");
}
return static function ($value) use ($key) {
if ('_header' === $value) {
switch ($key) {
case 'household_export_result':
return self::TR_PREFIX . 'Count households';
$labels = array_combine($values, $values);
$labels['_header'] = $this->getTitle();
case 'acp_export_result':
return self::TR_PREFIX . 'Count accompanying periods';
return static function ($value) use ($labels) {
return $labels[$value];
default:
throw new LogicException('Key not supported: ' . $key);
}
}
if (null === $value) {
return '';
}
return $value;
};
}
public function getQueryKeys($data): array
{
return ['export_result'];
return ['household_export_result', 'acp_export_result'];
}
public function getResult($query, $data)
@@ -82,7 +103,7 @@ class CountHousehold implements ExportInterface, GroupedExportInterface
public function getTitle(): string
{
return 'Count households';
return self::TR_PREFIX . 'Count households with accompanying course';
}
public function getType(): string
@@ -96,25 +117,29 @@ class CountHousehold implements ExportInterface, GroupedExportInterface
return $el['center'];
}, $acl);
$qb = $this->repository
->createQueryBuilder('acp')
->join('acp.participations', 'acppart')
// little optimization: we remove joins and make a direct join between participations and household members
->join(HouseholdMember::class, 'member', Query\Expr\Join::WITH, 'IDENTITY(acppart.person) = IDENTITY(member.person)')
->join('member.household', 'household');
$qb = $this->entityManager->createQueryBuilder();
$qb
->from(Household::class, 'household')
->join('household.members', 'hmember')
->join('hmember.person', 'person')
->join('person.accompanyingPeriodParticipations', 'acppart')
->join('acppart.accompanyingPeriod', 'acp')
->andWhere('acppart.startDate != acppart.endDate OR acppart.endDate IS NULL')
->andWhere(
$qb->expr()->exists(
'SELECT 1 FROM ' . AccompanyingPeriodParticipation::class . ' acl_count_part
JOIN ' . PersonCenterHistory::class . ' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person)
WHERE acl_count_part.accompanyingPeriod = acp.id AND acl_count_person_history.center IN (:authorized_centers)
'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);
->andWhere('hmember.startDate <= :count_household_at_date AND (hmember.endDate IS NULL OR hmember.endDate > :count_household_at_date)')
->setParameter('authorized_centers', $centers)
->setParameter('count_household_at_date', $this->rollingDateConverter->convert($data['calc_date']));
$qb->select('COUNT(DISTINCT household.id) AS export_result');
$qb
->select('COUNT(DISTINCT household.id) AS household_export_result')
->addSelect('COUNT(DISTINCT acp.id) AS acp_export_result');
return $qb;
}
@@ -129,6 +154,7 @@ class CountHousehold implements ExportInterface, GroupedExportInterface
return [
Declarations::HOUSEHOLD_TYPE,
Declarations::ACP_TYPE,
Declarations::PERSON_TYPE,
];
}
}

View File

@@ -100,7 +100,7 @@ class CountPerson implements ExportInterface, GroupedExportInterface
$qb = $this->personRepository->createQueryBuilder('person');
$qb->select('COUNT(person.id) AS export_result')
$qb->select('COUNT(DISTINCT person.id) AS export_result')
->andWhere(
$qb->expr()->exists(
'SELECT 1 FROM ' . PersonCenterHistory::class . ' pch WHERE pch.person = person.id AND pch.center IN (:authorized_centers)'

View File

@@ -15,12 +15,10 @@ use Chill\MainBundle\Export\ExportInterface;
use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\GroupedExportInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation;
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 LogicException;
@@ -28,12 +26,12 @@ use Symfony\Component\Form\FormBuilderInterface;
class CountSocialWorkActions implements ExportInterface, GroupedExportInterface
{
protected EntityRepository $repository;
protected EntityManagerInterface $em;
public function __construct(
EntityManagerInterface $em
) {
$this->repository = $em->getRepository(AccompanyingPeriod::class);
$this->em = $em;
}
public function buildForm(FormBuilderInterface $builder): void
@@ -96,15 +94,18 @@ class CountSocialWorkActions implements ExportInterface, GroupedExportInterface
return $el['center'];
}, $acl);
$qb = $this->repository->createQueryBuilder('acp')
->join('acp.works', 'acpw');
$qb = $this->em->createQueryBuilder();
$qb
->from(AccompanyingPeriod\AccompanyingPeriodWork::class, 'acpw')
->join('acpw.accompanyingPeriod', 'acp')
->join('acp.participations', 'acppart')
->join('acppart.person', 'person')
->andWhere('acppart.startDate != acppart.endDate OR acppart.endDate IS NULL')
->andWhere(
$qb->expr()->exists(
'SELECT 1 FROM ' . AccompanyingPeriodParticipation::class . ' acl_count_part
JOIN ' . PersonCenterHistory::class . ' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person)
WHERE acl_count_part.accompanyingPeriod = acp.id AND acl_count_person_history.center IN (:authorized_centers)
'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)
'
)
)
@@ -125,6 +126,7 @@ class CountSocialWorkActions implements ExportInterface, GroupedExportInterface
return [
Declarations::SOCIAL_WORK_ACTION_TYPE,
Declarations::ACP_TYPE,
Declarations::PERSON_TYPE,
];
}
}

View File

@@ -0,0 +1,416 @@
<?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\Entity\Address;
use Chill\MainBundle\Entity\Scope;
use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\GroupedExportInterface;
use Chill\MainBundle\Export\Helper\DateTimeHelper;
use Chill\MainBundle\Export\Helper\ExportAddressHelper;
use Chill\MainBundle\Export\Helper\UserHelper;
use Chill\MainBundle\Export\ListInterface;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation;
use Chill\PersonBundle\Entity\Household\PersonHouseholdAddress;
use Chill\PersonBundle\Entity\Person\PersonCenterHistory;
use Chill\PersonBundle\Entity\SocialWork\SocialIssue;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Repository\PersonRepository;
use Chill\PersonBundle\Repository\SocialWork\SocialIssueRepository;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Chill\PersonBundle\Templating\Entity\PersonRenderInterface;
use Chill\PersonBundle\Templating\Entity\SocialIssueRender;
use Chill\ThirdPartyBundle\Repository\ThirdPartyRepository;
use Chill\ThirdPartyBundle\Templating\Entity\ThirdPartyRender;
use DateTimeImmutable;
use Doctrine\ORM\AbstractQuery;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Query\Expr\Join;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use function strlen;
class ListAccompanyingPeriod implements ListInterface, GroupedExportInterface
{
private const FIELDS = [
'id',
'step',
'stepSince',
'openingDate',
'closingDate',
'referrer',
'referrerSince',
'administrativeLocation',
'locationIsPerson',
'locationIsTemp',
'locationPersonName',
'locationPersonId',
'origin',
'closingMotive',
'confidential',
'emergency',
'intensity',
'job',
'isRequestorPerson',
'isRequestorThirdParty',
'requestorPerson',
'requestorPersonId',
'requestorThirdParty',
'requestorThirdPartyId',
'scopes',
'socialIssues',
'createdAt',
'createdBy',
'updatedAt',
'updatedBy',
];
private ExportAddressHelper $addressHelper;
private DateTimeHelper $dateTimeHelper;
private EntityManagerInterface $entityManager;
private PersonRenderInterface $personRender;
private PersonRepository $personRepository;
private SocialIssueRender $socialIssueRender;
private SocialIssueRepository $socialIssueRepository;
private ThirdPartyRender $thirdPartyRender;
private ThirdPartyRepository $thirdPartyRepository;
private TranslatableStringHelperInterface $translatableStringHelper;
private UserHelper $userHelper;
public function __construct(
ExportAddressHelper $addressHelper,
DateTimeHelper $dateTimeHelper,
EntityManagerInterface $entityManager,
PersonRenderInterface $personRender,
PersonRepository $personRepository,
ThirdPartyRepository $thirdPartyRepository,
ThirdPartyRender $thirdPartyRender,
SocialIssueRepository $socialIssueRepository,
SocialIssueRender $socialIssueRender,
TranslatableStringHelperInterface $translatableStringHelper,
UserHelper $userHelper
) {
$this->addressHelper = $addressHelper;
$this->dateTimeHelper = $dateTimeHelper;
$this->entityManager = $entityManager;
$this->personRender = $personRender;
$this->personRepository = $personRepository;
$this->socialIssueRender = $socialIssueRender;
$this->socialIssueRepository = $socialIssueRepository;
$this->thirdPartyRender = $thirdPartyRender;
$this->thirdPartyRepository = $thirdPartyRepository;
$this->translatableStringHelper = $translatableStringHelper;
$this->userHelper = $userHelper;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder
->add('calc_date', ChillDateType::class, [
'input' => 'datetime_immutable',
'label' => 'export.list.acp.Date of calculation for associated elements',
'help' => 'export.list.acp.The associated referree, localisation, and other elements will be valid at this date',
'required' => true,
]);
}
public function getAllowedFormattersTypes()
{
return [FormatterInterface::TYPE_LIST];
}
public function getDescription()
{
return 'export.list.acp.Generate a list of accompanying periods, filtered on different parameters.';
}
public function getGroup(): string
{
return 'Exports of accompanying courses';
}
public function getLabels($key, array $values, $data)
{
if (substr($key, 0, strlen('address_fields')) === 'address_fields') {
return $this->addressHelper->getLabel($key, $values, $data, 'address_fields');
}
switch ($key) {
case 'stepSince':
case 'openingDate':
case 'closingDate':
case 'referrerSince':
case 'createdAt':
case 'updatedAt':
return $this->dateTimeHelper->getLabel('export.list.acp.' . $key);
case 'origin':
case 'closingMotive':
case 'job':
return function ($value) use ($key) {
if ('_header' === $value) {
return 'export.list.acp.' . $key;
}
if (null === $value) {
return '';
}
return $this->translatableStringHelper->localize(json_decode($value, true));
};
case 'locationPersonName':
case 'requestorPerson':
return function ($value) use ($key) {
if ('_header' === $value) {
return 'export.list.acp.' . $key;
}
if (null === $value || null === $person = $this->personRepository->find($value)) {
return '';
}
return $this->personRender->renderString($person, []);
};
case 'requestorThirdParty':
return function ($value) use ($key) {
if ('_header' === $value) {
return 'export.list.acp.' . $key;
}
if (null === $value || null === $thirdparty = $this->thirdPartyRepository->find($value)) {
return '';
}
return $this->thirdPartyRender->renderString($thirdparty, []);
};
case 'scopes':
return function ($value) use ($key) {
if ('_header' === $value) {
return 'export.list.acp.' . $key;
}
if (null === $value) {
return '';
}
return implode(
'|',
array_map(
fn ($s) => $this->translatableStringHelper->localize($s),
json_decode($value, true)
)
);
};
case 'socialIssues':
return function ($value) use ($key) {
if ('_header' === $value) {
return 'export.list.acp.' . $key;
}
if (null === $value) {
return '';
}
return implode(
'|',
array_map(
fn ($s) => $this->socialIssueRender->renderString($this->socialIssueRepository->find($s), []),
json_decode($value, true)
)
);
};
default:
return static function ($value) use ($key) {
if ('_header' === $value) {
return 'export.list.acp.' . $key;
}
if (null === $value) {
return '';
}
return $value;
};
}
}
public function getQueryKeys($data)
{
return array_merge(
self::FIELDS,
$this->addressHelper->getKeys(ExportAddressHelper::F_ALL, 'address_fields')
);
}
public function getResult($query, $data)
{
return $query->getQuery()->getResult(AbstractQuery::HYDRATE_SCALAR);
}
public function getTitle()
{
return 'export.list.acp.List of accompanying periods';
}
public function getType()
{
return Declarations::PERSON_TYPE;
}
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{
$centers = array_map(static function ($el) {
return $el['center'];
}, $acl);
$qb = $this->entityManager->createQueryBuilder();
$qb
->from(AccompanyingPeriod::class, 'acp')
->andWhere('acp.step != :list_acp_step')
->andWhere(
$qb->expr()->exists(
'SELECT 1 FROM ' . AccompanyingPeriodParticipation::class . ' acl_count_part
JOIN ' . PersonCenterHistory::class . ' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person)
WHERE acl_count_part.accompanyingPeriod = acp.id AND acl_count_person_history.center IN (:authorized_centers)
'
)
)
->setParameter('list_acp_step', AccompanyingPeriod::STEP_DRAFT)
->setParameter('authorized_centers', $centers);
$this->addSelectClauses($qb, $data['calc_date']);
return $qb;
}
public function requiredRole(): string
{
return PersonVoter::LISTS;
}
public function supportsModifiers()
{
return [
Declarations::ACP_TYPE,
];
}
private function addSelectClauses(QueryBuilder $qb, DateTimeImmutable $calcDate): void
{
// add the regular fields
foreach (['id', 'openingDate', 'closingDate', 'confidential', 'emergency', 'intensity', 'createdAt', 'updatedAt'] as $field) {
$qb->addSelect(sprintf('acp.%s AS %s', $field, $field));
}
// add the field which are simple association
foreach (['origin' => 'label', 'closingMotive' => 'name', 'job' => 'label', 'createdBy' => 'label', 'updatedBy' => 'label', 'administrativeLocation' => 'name'] as $entity => $field) {
$qb
->leftJoin(sprintf('acp.%s', $entity), "{$entity}_t")
->addSelect(sprintf('%s_t.%s AS %s', $entity, $field, $entity));
}
// step at date
$qb
->addSelect('stepHistory.step AS step')
->addSelect('stepHistory.startDate AS stepSince')
->leftJoin('acp.stepHistories', 'stepHistory')
->andWhere(
$qb->expr()->andX(
$qb->expr()->lte('stepHistory.startDate', ':calcDate'),
$qb->expr()->orX($qb->expr()->isNull('stepHistory.endDate'), $qb->expr()->gt('stepHistory.endDate', ':calcDate'))
)
);
// referree at date
$qb
->addSelect('referrer_t.label AS referrer')
->addSelect('userHistory.startDate AS referrerSince')
->leftJoin('acp.userHistories', 'userHistory')
->leftJoin('userHistory.user', 'referrer_t')
->andWhere(
$qb->expr()->orX(
$qb->expr()->isNull('userHistory'),
$qb->expr()->andX(
$qb->expr()->lte('userHistory.startDate', ':calcDate'),
$qb->expr()->orX($qb->expr()->isNull('userHistory.endDate'), $qb->expr()->gt('userHistory.endDate', ':calcDate'))
)
)
);
// location of the acp
$qb
->addSelect('CASE WHEN locationHistory.personLocation IS NOT NULL THEN 1 ELSE 0 END AS locationIsPerson')
->addSelect('CASE WHEN locationHistory.personLocation IS NOT NULL THEN 0 ELSE 1 END AS locationIsTemp')
->addSelect('IDENTITY(locationHistory.personLocation) AS locationPersonName')
->addSelect('IDENTITY(locationHistory.personLocation) AS locationPersonId')
->leftJoin('acp.locationHistories', 'locationHistory')
->andWhere(
$qb->expr()->orX(
$qb->expr()->isNull('locationHistory'),
$qb->expr()->andX(
$qb->expr()->lte('locationHistory.startDate', ':calcDate'),
$qb->expr()->orX($qb->expr()->isNull('locationHistory.endDate'), $qb->expr()->gt('locationHistory.endDate', ':calcDate'))
)
)
)
->leftJoin(PersonHouseholdAddress::class, 'personAddress', Join::WITH, 'locationHistory.personLocation = personAddress.person')
->andWhere(
$qb->expr()->orX(
$qb->expr()->isNull('personAddress'),
$qb->expr()->andX(
$qb->expr()->lte('personAddress.validFrom', ':calcDate'),
$qb->expr()->orX($qb->expr()->isNull('personAddress.validTo'), $qb->expr()->gt('personAddress.validTo', ':calcDate'))
)
)
)
->leftJoin(Address::class, 'acp_address', Join::WITH, 'COALESCE(IDENTITY(locationHistory.addressLocation), IDENTITY(personAddress.address)) = acp_address.id');
$this->addressHelper->addSelectClauses(ExportAddressHelper::F_ALL, $qb, 'acp_address', 'address_fields');
// requestor
$qb
->addSelect('CASE WHEN acp.requestorPerson IS NULL THEN 1 ELSE 0 END AS isRequestorPerson')
->addSelect('CASE WHEN acp.requestorPerson IS NULL THEN 0 ELSE 1 END AS isRequestorThirdParty')
->addSelect('IDENTITY(acp.requestorPerson) AS requestorPersonId')
->addSelect('IDENTITY(acp.requestorThirdParty) AS requestorThirdPartyId')
->addSelect('IDENTITY(acp.requestorPerson) AS requestorPerson')
->addSelect('IDENTITY(acp.requestorThirdParty) AS requestorThirdParty');
$qb
// scopes
->addSelect('(SELECT AGGREGATE(scope.name) FROM ' . Scope::class . ' scope WHERE scope MEMBER OF acp.scopes) AS scopes')
// social issues
->addSelect('(SELECT AGGREGATE(socialIssue.id) FROM ' . SocialIssue::class . ' socialIssue WHERE socialIssue MEMBER OF acp.socialIssues) AS socialIssues');
// add parameter
$qb->setParameter('calcDate', $calcDate);
}
}

View File

@@ -17,21 +17,22 @@ use Chill\CustomFieldsBundle\Service\CustomFieldProvider;
use Chill\MainBundle\Export\ExportElementValidatedInterface;
use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\GroupedExportInterface;
use Chill\MainBundle\Export\Helper\ExportAddressHelper;
use Chill\MainBundle\Export\ListInterface;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Export\Helper\ListPersonHelper;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use DateTime;
use DateTimeImmutable;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Query;
use Exception;
use PhpOffice\PhpSpreadsheet\Shared\Date;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Validator\Constraints\Callback;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use function addcslashes;
use function array_key_exists;
@@ -39,7 +40,7 @@ use function array_keys;
use function array_merge;
use function count;
use function in_array;
use function strtolower;
use function strlen;
use function uniqid;
/**
@@ -47,40 +48,35 @@ use function uniqid;
*/
class ListPerson implements ExportElementValidatedInterface, ListInterface, GroupedExportInterface
{
protected CustomFieldProvider $customFieldProvider;
private ExportAddressHelper $addressHelper;
protected EntityManagerInterface $entityManager;
private CustomFieldProvider $customFieldProvider;
protected array $fields = [
'id', 'firstName', 'lastName', 'birthdate',
'placeOfBirth', 'gender', 'memo', 'email', 'phonenumber',
'mobilenumber', 'contactInfo', 'countryOfBirth', 'nationality',
'address_street_address_1', 'address_street_address_2',
'address_valid_from', 'address_postcode_label', 'address_postcode_code',
'address_country_name', 'address_country_code', 'address_isnoaddress',
];
private EntityManagerInterface $entityManager;
protected TranslatableStringHelper $translatableStringHelper;
protected TranslatorInterface $translator;
private ListPersonHelper $listPersonHelper;
private $slugs = [];
private TranslatableStringHelper $translatableStringHelper;
public function __construct(
ExportAddressHelper $addressHelper,
CustomFieldProvider $customFieldProvider,
ListPersonHelper $listPersonHelper,
EntityManagerInterface $em,
TranslatorInterface $translator,
TranslatableStringHelper $translatableStringHelper,
CustomFieldProvider $customFieldProvider
TranslatableStringHelper $translatableStringHelper
) {
$this->entityManager = $em;
$this->translator = $translator;
$this->translatableStringHelper = $translatableStringHelper;
$this->addressHelper = $addressHelper;
$this->customFieldProvider = $customFieldProvider;
$this->listPersonHelper = $listPersonHelper;
$this->entityManager = $em;
$this->translatableStringHelper = $translatableStringHelper;
}
public function buildForm(FormBuilderInterface $builder)
{
$choices = array_combine($this->fields, $this->fields);
$choices = array_combine(ListPersonHelper::FIELDS, ListPersonHelper::FIELDS);
foreach ($this->getCustomFields() as $cf) {
$choices[$this->translatableStringHelper->localize($cf->getName())]
@@ -96,7 +92,7 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou
'label' => 'Fields to include in export',
'choice_attr' => static function (string $val): array {
// add a 'data-display-target' for address fields
if (substr($val, 0, 8) === 'address_') {
if (substr($val, 0, 7) === 'address' || 'center' === $val || 'household' === $val) {
return ['data-display-target' => 'address_date'];
}
@@ -111,17 +107,15 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou
}
},
])],
'data' => array_values($choices),
]);
// add a date field for addresses
$builder->add('address_date', DateType::class, [
'label' => 'Address valid at this date',
'data' => new DateTime(),
'attr' => ['class' => 'datepicker'],
'widget' => 'single_text',
'format' => 'dd-MM-yyyy',
'required' => false,
'block_name' => 'list_export_form_address_date',
$builder->add('address_date', ChillDateType::class, [
'label' => 'Data valid at this date',
'help' => 'Data regarding center, addresses, and so on will be computed at this date',
'data' => new DateTimeImmutable(),
'input' => 'datetime_immutable',
]);
}
@@ -142,113 +136,35 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou
public function getLabels($key, array $values, $data)
{
switch ($key) {
case 'birthdate':
// for birthdate, we have to transform the string into a date
// to format the date correctly.
return static function ($value) {
if ('_header' === $value) {
return 'birthdate';
}
if (empty($value)) {
return '';
}
$date = DateTime::createFromFormat('Y-m-d', $value);
// check that the creation could occurs.
if (false === $date) {
throw new Exception(sprintf('The value %s could '
. 'not be converted to %s', $value, DateTime::class));
}
return $date->format('d-m-Y');
};
case 'gender':
// for gender, we have to translate men/women statement
return function ($value) {
if ('_header' === $value) {
return 'gender';
}
return $this->translator->trans($value);
};
case 'countryOfBirth':
case 'nationality':
$countryRepository = $this->entityManager
->getRepository(\Chill\MainBundle\Entity\Country::class);
// load all countries in a single query
$countryRepository->findBy(['countryCode' => $values]);
return function ($value) use ($key, $countryRepository) {
if ('_header' === $value) {
return strtolower($key);
}
if (null === $value) {
return $this->translator->trans('no data');
}
$country = $countryRepository->find($value);
return $this->translatableStringHelper->localize(
$country->getName()
);
};
case 'address_country_name':
return function ($value) use ($key) {
if ('_header' === $value) {
return strtolower($key);
}
if (null === $value) {
return '';
}
return $this->translatableStringHelper->localize(json_decode($value, true));
};
case 'address_isnoaddress':
return static function (?string $value): string {
if ('_header' === $value) {
return 'address.address_homeless';
}
if (null !== $value) {
return 'X';
}
return '';
};
default:
// for fields which are associated with person
if (in_array($key, $this->fields, true)) {
return static function ($value) use ($key) {
if ('_header' === $value) {
return strtolower($key);
}
return $value;
};
}
return $this->getLabelForCustomField($key, $values, $data);
if (in_array($key, $this->listPersonHelper->getAllPossibleFields(), true)) {
return $this->listPersonHelper->getLabels($key, $values, $data);
}
return $this->getLabelForCustomField($key, $values, $data);
}
public function getQueryKeys($data)
{
$fields = [];
foreach ($data['fields'] as $key) {
if (in_array($key, $this->fields, true)) {
$fields[] = $key;
foreach (ListPersonHelper::FIELDS as $key) {
if (!in_array($key, $data['fields'], true)) {
continue;
}
if (substr($key, 0, strlen('address_fields')) === 'address_fields') {
$fields = array_merge($fields, $this->addressHelper->getKeys(ExportAddressHelper::F_ALL, 'address_fields'));
continue;
}
if ('lifecycleUpdate' === $key) {
$fields = array_merge($fields, ['createdAt', 'createdBy', 'updatedAt', 'updatedBy']);
continue;
}
$fields[] = $key;
}
// add the key from slugs and return
@@ -270,6 +186,9 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou
return Declarations::PERSON_TYPE;
}
/**
* param array{fields: string[], address_date: DateTimeImmutable} $data.
*/
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{
$centers = array_map(static function ($el) {
@@ -284,40 +203,24 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou
$qb = $this->entityManager->createQueryBuilder();
foreach ($this->fields as $f) {
if (in_array($f, $data['fields'], true)) {
switch ($f) {
case 'countryOfBirth':
case 'nationality':
$qb->addSelect(sprintf('IDENTITY(person.%s) as %s', $f, $f));
$qb
->from(Person::class, 'person')
->andWhere(
$qb->expr()->exists(
'SELECT 1 FROM ' . Person\PersonCenterHistory::class . ' pch WHERE pch.person = person.id AND pch.center IN (:authorized_centers)'
)
)
->setParameter('authorized_centers', $centers);
break;
$fields = $data['fields'];
case 'address_street_address_1':
case 'address_street_address_2':
case 'address_valid_from':
case 'address_postcode_label':
case 'address_postcode_code':
case 'address_country_name':
case 'address_country_code':
case 'address_isnoaddress':
$qb->addSelect(sprintf(
'GET_PERSON_ADDRESS_%s(person.id, :address_date) AS %s',
// get the part after address_
strtoupper(substr($f, 8)),
$f
));
$qb->setParameter('address_date', $data['address_date']);
break;
default:
$qb->addSelect(sprintf('person.%s as %s', $f, $f));
}
}
}
$this->listPersonHelper->addSelect($qb, $fields, $data['address_date']);
foreach ($this->getCustomFields() as $cf) {
if (!in_array($cf->getSlug(), $fields, true)) {
continue;
}
$cfType = $this->customFieldProvider->getCustomFieldByType($cf->getType());
if ($cfType instanceof CustomFieldChoice && $cfType->isMultiple($cf)) {
@@ -345,12 +248,6 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou
}
}
$qb
->from('ChillPersonBundle:Person', 'person')
->join('person.center', 'center')
->andWhere('center IN (:authorized_centers)')
->setParameter('authorized_centers', $centers);
return $qb;
}
@@ -368,14 +265,14 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou
{
// get the field starting with address_
$addressFields = array_filter(
$this->fields,
ListPersonHelper::FIELDS,
static fn (string $el): bool => substr($el, 0, 8) === 'address_'
);
// check if there is one field starting with address in data
if (count(array_intersect($data['fields'], $addressFields)) > 0) {
// if a field address is checked, the date must not be empty
if (empty($data['address_date'])) {
if (!$data['address_date'] instanceof DateTimeImmutable) {
$context
->buildViolation('You must set this date if an address is checked')
->atPath('address_date')
@@ -456,7 +353,7 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou
. ' | ' . $label;
}
if ('_other' === $slugChoice && $cfType->isChecked($cf, $choiceSlug, $decoded)) {
if ('_other' === $slugChoice && $cfType->isChecked($cf, $slugChoice, $decoded)) {
return $cfType->extractOtherValue($cf, $decoded);
}

View File

@@ -0,0 +1,220 @@
<?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\ExportElementValidatedInterface;
use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\GroupedExportInterface;
use Chill\MainBundle\Export\Helper\ExportAddressHelper;
use Chill\MainBundle\Export\ListInterface;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Entity\Person\PersonCenterHistory;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Export\Helper\ListPersonHelper;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use DateTimeImmutable;
use Doctrine\ORM\AbstractQuery;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Validator\Constraints\Callback;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use function array_key_exists;
use function count;
use function in_array;
use function strlen;
class ListPersonWithAccompanyingPeriod implements ExportElementValidatedInterface, ListInterface, GroupedExportInterface
{
private ExportAddressHelper $addressHelper;
private EntityManagerInterface $entityManager;
private ListPersonHelper $listPersonHelper;
public function __construct(
ExportAddressHelper $addressHelper,
ListPersonHelper $listPersonHelper,
EntityManagerInterface $em
) {
$this->addressHelper = $addressHelper;
$this->listPersonHelper = $listPersonHelper;
$this->entityManager = $em;
}
public function buildForm(FormBuilderInterface $builder)
{
$choices = array_combine(ListPersonHelper::FIELDS, ListPersonHelper::FIELDS);
// Add a checkbox to select fields
$builder->add('fields', ChoiceType::class, [
'multiple' => true,
'expanded' => true,
'choices' => $choices,
'label' => 'Fields to include in export',
'choice_attr' => static function (string $val): array {
// add a 'data-display-target' for address fields
if (substr($val, 0, 7) === 'address' || 'center' === $val || 'household' === $val) {
return ['data-display-target' => 'address_date'];
}
return [];
},
'constraints' => [new Callback([
'callback' => static function ($selected, ExecutionContextInterface $context) {
if (count($selected) === 0) {
$context->buildViolation('You must select at least one element')
->atPath('fields')
->addViolation();
}
},
])],
'data' => array_values($choices),
]);
// add a date field for addresses
$builder->add('address_date', ChillDateType::class, [
'label' => 'Data valid at this date',
'help' => 'Data regarding center, addresses, and so on will be computed at this date',
'data' => new DateTimeImmutable(),
'input' => 'datetime_immutable',
]);
}
public function getAllowedFormattersTypes()
{
return [FormatterInterface::TYPE_LIST];
}
public function getDescription()
{
return 'export.list.person_with_acp.Create a list of people having an accompaying periods, according to various filters.';
}
public function getGroup(): string
{
return 'Exports of persons';
}
public function getLabels($key, array $values, $data)
{
return $this->listPersonHelper->getLabels($key, $values, $data);
}
public function getQueryKeys($data)
{
$fields = [];
foreach (ListPersonHelper::FIELDS as $key) {
if (!in_array($key, $data['fields'], true)) {
continue;
}
if (substr($key, 0, strlen('address_fields')) === 'address_fields') {
$fields = array_merge($fields, $this->addressHelper->getKeys(ExportAddressHelper::F_ALL, 'address_fields'));
continue;
}
if ('lifecycleUpdate' === $key) {
$fields = array_merge($fields, ['createdAt', 'createdBy', 'updatedAt', 'updatedBy']);
continue;
}
$fields[] = $key;
}
return $fields;
}
public function getResult($query, $data)
{
return $query->getQuery()->getResult(AbstractQuery::HYDRATE_SCALAR);
}
public function getTitle()
{
return 'export.list.person_with_acp.List peoples having an accompanying period';
}
public function getType()
{
return Declarations::PERSON_TYPE;
}
/**
* param array{fields: string[], address_date: DateTimeImmutable} $data.
*/
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{
$centers = array_map(static function ($el) {
return $el['center'];
}, $acl);
// throw an error if any fields are present
if (!array_key_exists('fields', $data)) {
throw new \Doctrine\DBAL\Exception\InvalidArgumentException('any fields '
. 'have been checked');
}
$qb = $this->entityManager->createQueryBuilder();
$qb->from(Person::class, 'person')
->join('person.accompanyingPeriodParticipations', 'acppart')
->join('acppart.accompanyingPeriod', 'acp')
->andWhere($qb->expr()->neq('acp.step', "'" . AccompanyingPeriod::STEP_DRAFT . "'"))
->andWhere(
$qb->expr()->exists(
'SELECT 1 FROM ' . PersonCenterHistory::class . ' pch WHERE pch.person = person.id AND pch.center IN (:authorized_centers)'
)
)->setParameter('authorized_centers', $centers);
$fields = $data['fields'];
$this->listPersonHelper->addSelect($qb, $fields, $data['address_date']);
return $qb;
}
public function requiredRole(): string
{
return PersonVoter::LISTS;
}
public function supportsModifiers()
{
return [Declarations::PERSON_TYPE, Declarations::PERSON_IMPLIED_IN, Declarations::ACP_TYPE];
}
public function validateForm($data, ExecutionContextInterface $context)
{
// get the field starting with address_
$addressFields = array_filter(
ListPersonHelper::FIELDS,
static fn (string $el): bool => substr($el, 0, 8) === 'address_'
);
// check if there is one field starting with address in data
if (count(array_intersect($data['fields'], $addressFields)) > 0) {
// if a field address is checked, the date must not be empty
if (!$data['address_date'] instanceof DateTimeImmutable) {
$context
->buildViolation('You must set this date if an address is checked')
->atPath('address_date')
->addViolation();
}
}
}
}

View File

@@ -16,7 +16,6 @@ use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\GroupedExportInterface;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation;
use Chill\PersonBundle\Entity\Person\PersonCenterHistory;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
@@ -53,7 +52,7 @@ class StatAccompanyingCourseDuration implements ExportInterface, GroupedExportIn
public function getDescription(): string
{
return 'Create an average of accompanying courses duration according to various filters';
return 'Create an average of accompanying courses duration of each person participation to accompanying course, according to filters on persons, accompanying course';
}
public function getGroup(): string
@@ -63,21 +62,37 @@ class StatAccompanyingCourseDuration implements ExportInterface, GroupedExportIn
public function getLabels($key, array $values, $data)
{
if ('export_result' !== $key) {
throw new LogicException("the key {$key} is not used by this export");
}
return static function ($value) use ($key) {
if ('_header' === $value) {
switch ($key) {
case 'avg_export_result':
return 'export.export.acp_stats.avg_duration';
$labels = array_combine($values, $values);
$labels['_header'] = $this->getTitle();
case 'count_acppart_export_result':
return 'export.export.acp_stats.count_participations';
return static function ($value) use ($labels) {
return $labels[$value];
case 'count_acp_export_result':
return 'export.export.acp_stats.count_acps';
case 'count_pers_export_result':
return 'export.export.acp_stats.count_persons';
default:
throw new LogicException('key not supported: ' . $key);
}
}
if (null === $value) {
return '';
}
return $value;
};
}
public function getQueryKeys($data): array
{
return ['export_result'];
return ['avg_export_result', 'count_acp_export_result', 'count_acppart_export_result', 'count_pers_export_result'];
}
public function getResult($query, $data)
@@ -87,7 +102,7 @@ class StatAccompanyingCourseDuration implements ExportInterface, GroupedExportIn
public function getTitle(): string
{
return 'Accompanying courses duration';
return 'Accompanying courses participation duration and number of participations';
}
public function getType(): string
@@ -104,22 +119,28 @@ class StatAccompanyingCourseDuration implements ExportInterface, GroupedExportIn
$qb = $this->repository->createQueryBuilder('acp');
$qb
->select('AVG(
LEAST(acppart.endDate, COALESCE(acp.closingDate, :force_closingDate))
- GREATEST(acppart.startDate, COALESCE(acp.openingDate, :force_closingDate))
) AS avg_export_result')
->addSelect('COUNT(DISTINCT acppart.id) AS count_acppart_export_result')
->addSelect('COUNT(DISTINCT person.id) AS count_pers_export_result')
->addSelect('COUNT(DISTINCT acp.id) AS count_acp_export_result')
->setParameter('force_closingDate', $data['closingdate'])
->leftJoin('acp.participations', 'acppart')
->leftJoin('acppart.person', 'person')
->andWhere('acp.step != :count_acp_step')
->andWhere('acppart.startDate != acppart.endDate OR acppart.endDate IS NULL')
->andWhere(
$qb->expr()->exists(
'SELECT 1 FROM ' . AccompanyingPeriodParticipation::class . ' acl_count_part
JOIN ' . PersonCenterHistory::class . ' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person)
WHERE acl_count_part.accompanyingPeriod = acp.id AND acl_count_person_history.center IN (:authorized_centers)
'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('count_acp_step', AccompanyingPeriod::STEP_DRAFT)
->setParameter('authorized_centers', $centers);
$qb
->select('AVG(
COALESCE(acp.closingDate, :force_closingDate) - acp.openingDate
) AS export_result')
->setParameter('force_closingDate', $data['closingdate']);
return $qb;
}
@@ -132,6 +153,7 @@ class StatAccompanyingCourseDuration implements ExportInterface, GroupedExportIn
{
return [
Declarations::ACP_TYPE,
Declarations::PERSON_TYPE,
];
}
}

View File

@@ -12,16 +12,23 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\PersonBundle\Export\Declarations;
use DateTime;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
class ActiveOnDateFilter implements FilterInterface
{
private RollingDateConverterInterface $rollingDateConverter;
public function __construct(RollingDateConverterInterface $rollingDateConverter)
{
$this->rollingDateConverter = $rollingDateConverter;
}
public function addRole(): ?string
{
return null;
@@ -46,7 +53,10 @@ class ActiveOnDateFilter implements FilterInterface
}
$qb->add('where', $where);
$qb->setParameter('ondate', $data['on_date'], Types::DATE_MUTABLE);
$qb->setParameter(
'ondate',
$this->rollingDateConverter->convert($data['on_date'])
);
}
public function applyOn(): string
@@ -57,15 +67,15 @@ class ActiveOnDateFilter implements FilterInterface
public function buildForm(FormBuilderInterface $builder)
{
$builder
->add('on_date', ChillDateType::class, [
'data' => new DateTime(),
->add('on_date', PickRollingDateType::class, [
'data' => new RollingDate(RollingDate::T_TODAY),
]);
}
public function describeAction($data, $format = 'string'): array
{
return ['Filtered by actives courses: active on %ondate%', [
'%ondate%' => $data['on_date']->format('d-m-Y'),
'%ondate%' => $this->rollingDateConverter->convert($data['on_date'])->format('d-m-Y'),
]];
}

View File

@@ -12,15 +12,22 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\PersonBundle\Export\Declarations;
use DateTime;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
class ActiveOneDayBetweenDatesFilter implements FilterInterface
{
private RollingDateConverterInterface $rollingDateConverter;
public function __construct(RollingDateConverterInterface $rollingDateConverter)
{
$this->rollingDateConverter = $rollingDateConverter;
}
public function addRole(): ?string
{
return null;
@@ -31,8 +38,14 @@ class ActiveOneDayBetweenDatesFilter implements FilterInterface
$clause = "OVERLAPSI (acp.openingDate, acp.closingDate), (:datefrom, :dateto) = 'TRUE'";
$qb->andWhere($clause);
$qb->setParameter('datefrom', $data['date_from'], Types::DATE_MUTABLE);
$qb->setParameter('dateto', $data['date_to'], Types::DATE_MUTABLE);
$qb->setParameter(
'datefrom',
$this->rollingDateConverter->convert($data['date_from'])
);
$qb->setParameter(
'dateto',
$this->rollingDateConverter->convert($data['date_to'])
);
}
public function applyOn(): string
@@ -43,19 +56,19 @@ class ActiveOneDayBetweenDatesFilter implements FilterInterface
public function buildForm(FormBuilderInterface $builder)
{
$builder
->add('date_from', ChillDateType::class, [
'data' => new DateTime(),
->add('date_from', PickRollingDateType::class, [
'data' => new RollingDate(RollingDate::T_YEAR_PREVIOUS_START),
])
->add('date_to', ChillDateType::class, [
'data' => new DateTime(),
->add('date_to', PickRollingDateType::class, [
'data' => new RollingDate(RollingDate::T_TODAY),
]);
}
public function describeAction($data, $format = 'string'): array
{
return ['Filtered by actives courses: at least one day between %datefrom% and %dateto%', [
'%datefrom%' => $data['date_from']->format('d-m-Y'),
'%dateto%' => $data['date_to']->format('d-m-Y'),
'%datefrom%' => $this->rollingDateConverter->convert($data['date_from'])->format('d-m-Y'),
'%dateto%' => $this->rollingDateConverter->convert($data['date_to'])->format('d-m-Y'),
]];
}

View File

@@ -0,0 +1,66 @@
<?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\Export\FilterInterface;
use Chill\MainBundle\Form\Type\PickUserDynamicType;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use function in_array;
class CreatorFilter implements FilterInterface
{
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('acp_creator', $qb->getAllAliases(), true)) {
$qb->join('acp.createdBy', 'acp_creator');
}
$qb
->andWhere($qb->expr()->in('acp_creator', ':creators'))
->setParameter('creators', $data['accepted_creators']);
}
public function applyOn(): string
{
return Declarations::ACP_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder
->add('accepted_creators', PickUserDynamicType::class, [
'multiple' => true,
'label' => false,
]);
}
public function describeAction($data, $format = 'string'): array
{
return [
'Filtered by creator: only %creators%', [
'%creators%' => implode(', ', array_map(static fn (User $u) => $u->getLabel(), $data['accepted_creators'])),
], ];
}
public function getTitle(): string
{
return 'Filter by creator';
}
}

View File

@@ -0,0 +1,94 @@
<?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\UserJob;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Repository\UserJobRepositoryInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface;
use function in_array;
class CreatorJobFilter implements FilterInterface
{
private TranslatableStringHelper $translatableStringHelper;
private UserJobRepositoryInterface $userJobRepository;
public function __construct(
TranslatableStringHelper $translatableStringHelper,
UserJobRepositoryInterface $userJobRepository
) {
$this->translatableStringHelper = $translatableStringHelper;
$this->userJobRepository = $userJobRepository;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('acp_creator', $qb->getAllAliases(), true)) {
$qb->join('acp.createdBy', 'acp_creator');
}
$qb
->andWhere($qb->expr()->in('acp_creator.userJob', ':creator_job'))
->setParameter('creator_job', $data['creator_job']);
}
public function applyOn(): string
{
return Declarations::ACP_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('creator_job', EntityType::class, [
'class' => UserJob::class,
'choices' => $this->userJobRepository->findAllActive(),
'choice_label' => function (UserJob $j) {
return $this->translatableStringHelper->localize(
$j->getLabel()
);
},
'multiple' => true,
'expanded' => true,
'label' => 'Job',
]);
}
public function describeAction($data, $format = 'string'): array
{
$creatorJobs = [];
foreach ($data['creator_job'] as $j) {
$creatorJobs[] = $this->translatableStringHelper->localize(
$j->getLabel()
);
}
return ['export.filter.course.creator_job.Filtered by creator job: only %jobs%', [
'%jobs%' => implode(', ', $creatorJobs),
]];
}
public function getTitle(): string
{
return 'Filter by creator job';
}
}

View File

@@ -12,18 +12,19 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters;
use Chill\MainBundle\Entity\Address;
use Chill\MainBundle\Entity\GeographicalUnit;
use Chill\MainBundle\Entity\GeographicalUnit\SimpleGeographicalUnitDTO;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Repository\GeographicalUnitLayerRepositoryInterface;
use Chill\MainBundle\Repository\GeographicalUnitRepositoryInterface;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Household\PersonHouseholdAddress;
use Chill\PersonBundle\Export\Declarations;
use DateTimeImmutable;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
/**
@@ -31,20 +32,24 @@ use Symfony\Component\Form\FormBuilderInterface;
*/
class GeographicalUnitStatFilter implements FilterInterface
{
private EntityManagerInterface $em;
private GeographicalUnitLayerRepositoryInterface $geographicalUnitLayerRepository;
private GeographicalUnitRepositoryInterface $geographicalUnitRepository;
private RollingDateConverterInterface $rollingDateConverter;
private TranslatableStringHelperInterface $translatableStringHelper;
public function __construct(
EntityManagerInterface $em,
GeographicalUnitRepositoryInterface $geographicalUnitRepository,
TranslatableStringHelperInterface $translatableStringHelper
GeographicalUnitLayerRepositoryInterface $geographicalUnitLayerRepository,
TranslatableStringHelperInterface $translatableStringHelper,
RollingDateConverterInterface $rollingDateConverter
) {
$this->em = $em;
$this->geographicalUnitRepository = $geographicalUnitRepository;
$this->geographicalUnitLayerRepository = $geographicalUnitLayerRepository;
$this->translatableStringHelper = $translatableStringHelper;
$this->rollingDateConverter = $rollingDateConverter;
}
public function addRole(): ?string
@@ -62,7 +67,7 @@ class GeographicalUnitStatFilter implements FilterInterface
WITH IDENTITY(acp_geog_filter_location_history.personLocation) = IDENTITY(acp_geog_filter_address_person_location.person)
LEFT JOIN ' . Address::class . ' acp_geog_filter_address
WITH COALESCE(IDENTITY(acp_geog_filter_address_person_location.address), IDENTITY(acp_geog_filter_location_history.addressLocation)) = acp_geog_filter_address.id
LEFT JOIN ' . GeographicalUnit::class . ' acp_geog_filter_units WITH ST_CONTAINS(acp_geog_units.geom, acp_geog_filter_address.point) = TRUE
LEFT JOIN acp_geog_filter_address.geographicalUnits acp_geog_filter_units
WHERE
(acp_geog_filter_location_history.startDate <= :acp_geog_filter_date AND (
acp_geog_filter_location_history.endDate IS NULL OR acp_geog_filter_location_history.endDate < :acp_geog_filter_date
@@ -77,8 +82,11 @@ class GeographicalUnitStatFilter implements FilterInterface
$qb
->andWhere($qb->expr()->exists($subQueryDql))
->setParameter('acp_geog_filter_date', $data['date_calc'])
->setParameter('acp_geog_filter_units', $data['units']);
->setParameter(
'acp_geog_filter_date',
$this->rollingDateConverter->convert($data['date_calc'])
)
->setParameter('acp_geog_filter_units', array_map(static fn (SimpleGeographicalUnitDTO $unitDTO) => $unitDTO->id, $data['units']));
}
public function applyOn(): string
@@ -89,19 +97,18 @@ class GeographicalUnitStatFilter implements FilterInterface
public function buildForm(FormBuilderInterface $builder)
{
$builder
->add('date_calc', ChillDateType::class, [
->add('date_calc', PickRollingDateType::class, [
'label' => 'Compute geographical location at date',
'required' => true,
'data' => new DateTimeImmutable('today'),
'input' => 'datetime_immutable',
'data' => new RollingDate(RollingDate::T_TODAY),
])
->add('units', EntityType::class, [
->add('units', ChoiceType::class, [
'label' => 'Geographical unit',
'placeholder' => 'Select a geographical unit',
'class' => GeographicalUnit::class,
'choices' => $this->geographicalUnitRepository->findAll(),
'choice_label' => function (GeographicalUnit $item) {
return $this->translatableStringHelper->localize($item->getLayer()->getName()) . ' > ' . $item->getUnitName();
'choice_value' => static fn (SimpleGeographicalUnitDTO $item) => $item->id,
'choice_label' => function (SimpleGeographicalUnitDTO $item) {
return $this->translatableStringHelper->localize($this->geographicalUnitLayerRepository->find($item->layerId)->getName()) . ' > ' . $item->unitName;
},
'attr' => [
'class' => 'select2',
@@ -113,12 +120,12 @@ class GeographicalUnitStatFilter implements FilterInterface
public function describeAction($data, $format = 'string'): array
{
return ['Filtered by geographic unit: computed at %date%, only in %units%', [
'%date%' => $data['date_calc']->format('d-m-Y'),
'%date%' => $this->rollingDateConverter->convert($data['date_calc'])->format('d-m-Y'),
'%units' => implode(
', ',
array_map(
function (GeographicalUnit $item) {
return $this->translatableStringHelper->localize($item->getLayer()->getName()) . ' > ' . $item->getUnitName();
function (SimpleGeographicalUnitDTO $item) {
return $this->translatableStringHelper->localize($this->geographicalUnitLayerRepository->find($item->layerId)->getName()) . ' > ' . $item->unitName;
},
$data['units']
)

View File

@@ -0,0 +1,51 @@
<?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\Export\FilterInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
class HasNoActionFilter implements FilterInterface
{
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data): void
{
$qb->andWhere('NOT EXISTS (SELECT 1 FROM ' . AccompanyingPeriodWork::class . ' work WHERE work.accompanyingPeriod = acp)');
}
public function applyOn(): string
{
return Declarations::ACP_TYPE;
}
public function buildForm(FormBuilderInterface $builder): void
{
// no form
}
public function describeAction($data, $format = 'string'): array
{
return ['Filtered acp which has no actions'];
}
public function getTitle(): string
{
return 'Filter by which has no action';
}
}

View File

@@ -0,0 +1,83 @@
<?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\Export\FilterInterface;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod\UserHistory;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
class HasNoReferrerFilter implements FilterInterface
{
private RollingDateConverterInterface $rollingDateConverter;
public function __construct(
RollingDateConverterInterface $rollingDateConverter
) {
$this->rollingDateConverter = $rollingDateConverter;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$qb
->andWhere('
NOT EXISTS (
SELECT 1 FROM ' . UserHistory::class . ' uh
WHERE uh.startDate <= :has_no_referrer_filter_date
AND (
uh.endDate IS NULL
or uh.endDate > :has_no_referrer_filter_date
)
AND uh.accompanyingPeriod = acp
)
')
->setParameter(
'has_no_referrer_filter_date',
$this->rollingDateConverter->convert($data['calc_date'])
);
}
public function applyOn(): string
{
return Declarations::ACP_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder
->add('calc_date', PickRollingDateType::class, [
'label' => 'Has no referrer on this date',
'data' => new RollingDate(RollingDate::T_TODAY),
]);
}
public function describeAction($data, $format = 'string'): array
{
return ['Filtered acp which has no referrer on date: %date%', [
'%date%' => $this->rollingDateConverter->convert($data['calc_date'])->format('d-m-Y'),
]];
}
public function getTitle(): string
{
return 'Filter by which has no referrer';
}
}

View File

@@ -0,0 +1,116 @@
<?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\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 LogicException;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
class HasTemporaryLocationFilter implements FilterInterface
{
private RollingDateConverterInterface $rollingDateConverter;
public function __construct(
RollingDateConverterInterface $rollingDateConverter
) {
$this->rollingDateConverter = $rollingDateConverter;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$qb
->join('acp.locationHistories', 'acp_having_temporarily_location')
->andWhere('acp_having_temporarily_location.startDate <= :acp_having_temporarily_location_date
AND (acp_having_temporarily_location.endDate IS NULL OR acp_having_temporarily_location.endDate > :acp_having_temporarily_location_date)')
->setParameter(
'acp_having_temporarily_location_date',
$this->rollingDateConverter->convert($data['calc_date'])
);
switch ($data['having_temporarily']) {
case true:
$qb->andWhere('acp_having_temporarily_location.addressLocation IS NOT NULL');
break;
case false:
$qb->andWhere('acp_having_temporarily_location.personLocation IS NOT NULL');
break;
default:
throw new LogicException('value not supported');
}
}
public function applyOn(): string
{
return Declarations::ACP_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder
->add('having_temporarily', ChoiceType::class, [
'choices' => [
'export.filter.course.having_temporarily.Having a temporarily location' => true,
'export.filter.course.having_temporarily.Having a person\'s location' => false,
],
'choice_label' => static function ($choice) {
switch ($choice) {
case true:
return 'export.filter.course.having_temporarily.Having a temporarily location';
case false:
return 'export.filter.course.having_temporarily.Having a person\'s location';
default:
throw new LogicException('this choice is not supported');
}
},
])
->add('calc_date', PickRollingDateType::class, [
'label' => 'export.filter.course.having_temporarily.Calculation date',
'data' => new RollingDate(RollingDate::T_TODAY),
]);
}
public function describeAction($data, $format = 'string'): array
{
switch ($data['having_temporarily']) {
case true:
return ['export.filter.course.having_temporarily.Having a temporarily location', []];
case false:
return ['export.filter.course.having_temporarily.Having a person\'s location', []];
default:
throw new LogicException('value not supported');
}
}
public function getTitle(): string
{
return 'Filter by temporary location';
}
}

View File

@@ -12,15 +12,23 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\PersonBundle\Export\Declarations;
use DateTime;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
class OpenBetweenDatesFilter implements FilterInterface
{
private RollingDateConverterInterface $rollingDateConverter;
public function __construct(RollingDateConverterInterface $rollingDateConverter)
{
$this->rollingDateConverter = $rollingDateConverter;
}
public function addRole(): ?string
{
return null;
@@ -34,8 +42,8 @@ class OpenBetweenDatesFilter implements FilterInterface
);
$qb->andWhere($clause);
$qb->setParameter('datefrom', $data['date_from'], Types::DATE_MUTABLE);
$qb->setParameter('dateto', $data['date_to'], Types::DATE_MUTABLE);
$qb->setParameter('datefrom', $this->rollingDateConverter->convert($data['date_from']), Types::DATE_IMMUTABLE);
$qb->setParameter('dateto', $this->rollingDateConverter->convert($data['date_to']), Types::DATE_IMMUTABLE);
}
public function applyOn(): string
@@ -46,19 +54,19 @@ class OpenBetweenDatesFilter implements FilterInterface
public function buildForm(FormBuilderInterface $builder)
{
$builder
->add('date_from', ChillDateType::class, [
'data' => new DateTime(),
->add('date_from', PickRollingDateType::class, [
'data' => new RollingDate(RollingDate::T_MONTH_PREVIOUS_START),
])
->add('date_to', ChillDateType::class, [
'data' => new DateTime(),
->add('date_to', PickRollingDateType::class, [
'data' => new RollingDate(RollingDate::T_TODAY),
]);
}
public function describeAction($data, $format = 'string'): array
{
return ['Filtered by opening dates: between %datefrom% and %dateto%', [
'%datefrom%' => $data['date_from']->format('d-m-Y'),
'%dateto%' => $data['date_to']->format('d-m-Y'),
'%datefrom%' => $this->rollingDateConverter->convert($data['date_from'])->format('d-m-Y'),
'%dateto%' => $this->rollingDateConverter->convert($data['date_to'])->format('d-m-Y'),
]];
}

View File

@@ -12,11 +12,12 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Form\Type\PickUserDynamicType;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\MainBundle\Templating\Entity\UserRender;
use Chill\PersonBundle\Export\Declarations;
use DateTimeImmutable;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
@@ -28,11 +29,16 @@ class ReferrerFilter implements FilterInterface
private const PU = 'acp_referrer_filter_users';
private RollingDateConverterInterface $rollingDateConverter;
private UserRender $userRender;
public function __construct(UserRender $userRender)
{
public function __construct(
UserRender $userRender,
RollingDateConverterInterface $rollingDateConverter
) {
$this->userRender = $userRender;
$this->rollingDateConverter = $rollingDateConverter;
}
public function addRole(): ?string
@@ -57,7 +63,10 @@ class ReferrerFilter implements FilterInterface
$qb->expr()->in(self::A . '.user', ':' . self::PU)
)
->setParameter(self::PU, $data['accepted_referrers'])
->setParameter(self::P, $data['date_calc']);
->setParameter(
self::P,
$this->rollingDateConverter->convert($data['date_calc'])
);
}
public function applyOn(): string
@@ -71,9 +80,8 @@ class ReferrerFilter implements FilterInterface
->add('accepted_referrers', PickUserDynamicType::class, [
'multiple' => true,
])
->add('date_calc', ChillDateType::class, [
'input' => 'datetime_immutable',
'data' => new DateTimeImmutable('now'),
->add('date_calc', PickRollingDateType::class, [
'data' => new RollingDate(RollingDate::T_TODAY),
'label' => 'export.filter.course.by_referrer.Computation date for referrer',
'required' => true,
]);

View File

@@ -12,7 +12,6 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\PersonBundle\Entity\SocialWork\SocialIssue;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Form\Type\PickSocialIssueType;
@@ -31,15 +30,11 @@ class SocialIssueFilter implements FilterInterface
private SocialIssueRender $socialIssueRender;
private TranslatableStringHelper $translatableStringHelper;
public function __construct(
TranslatorInterface $translator,
TranslatableStringHelper $translatableStringHelper,
SocialIssueRender $socialIssueRender
) {
$this->translator = $translator;
$this->translatableStringHelper = $translatableStringHelper;
$this->socialIssueRender = $socialIssueRender;
}
@@ -59,7 +54,7 @@ class SocialIssueFilter implements FilterInterface
$qb->andWhere($clause)
->setParameter(
'socialissues',
SocialIssue::getDescendantsWithThisForIssues($data['accepted_socialissues'])
SocialIssue::getDescendantsWithThisForIssues($data['accepted_socialissues']->toArray())
);
}

View File

@@ -12,31 +12,43 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters;
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\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use function in_array;
class StepFilter implements FilterInterface
{
private const A = 'acp_filter_bystep_stephistories';
private const DEFAULT_CHOICE = AccompanyingPeriod::STEP_CONFIRMED;
private const P = 'acp_step_filter_date';
private const STEPS = [
'Draft' => AccompanyingPeriod::STEP_DRAFT,
'Confirmed' => AccompanyingPeriod::STEP_CONFIRMED,
'Closed' => AccompanyingPeriod::STEP_CLOSED,
];
private RollingDateConverterInterface $rollingDateConverter;
/**
* @var TranslatorInterface
*/
protected $translator;
private $translator;
public function __construct(TranslatorInterface $translator)
{
public function __construct(
RollingDateConverterInterface $rollingDateConverter,
TranslatorInterface $translator
) {
$this->rollingDateConverter = $rollingDateConverter;
$this->translator = $translator;
}
@@ -47,17 +59,25 @@ class StepFilter implements FilterInterface
public function alterQuery(QueryBuilder $qb, $data)
{
$where = $qb->getDQLPart('where');
$clause = $qb->expr()->eq('acp.step', ':step');
if ($where instanceof Andx) {
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
if (!in_array(self::A, $qb->getAllAliases(), true)) {
$qb->leftJoin('acp.stepHistories', self::A);
}
$qb->add('where', $where);
$qb->setParameter('step', $data['accepted_steps']);
$qb
->andWhere(
$qb->expr()->andX(
$qb->expr()->lte(self::A . '.startDate', ':' . self::P),
$qb->expr()->orX(
$qb->expr()->isNull(self::A . '.endDate'),
$qb->expr()->lt(self::A . '.endDate', ':' . self::P)
)
)
)
->andWhere(
$qb->expr()->in(self::A . '.step', ':acp_filter_by_step_steps')
)
->setParameter(self::P, $this->rollingDateConverter->convert($data['calc_date']))
->setParameter('acp_filter_by_step_steps', $data['accepted_steps']);
}
public function applyOn()
@@ -67,13 +87,18 @@ class StepFilter implements FilterInterface
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('accepted_steps', ChoiceType::class, [
'choices' => self::STEPS,
'multiple' => false,
'expanded' => true,
'empty_data' => self::DEFAULT_CHOICE,
'data' => self::DEFAULT_CHOICE,
]);
$builder
->add('accepted_steps', ChoiceType::class, [
'choices' => self::STEPS,
'multiple' => false,
'expanded' => true,
'empty_data' => self::DEFAULT_CHOICE,
'data' => self::DEFAULT_CHOICE,
])
->add('calc_date', PickRollingDateType::class, [
'label' => 'export.acp.filter.by_step.date_calc',
'data' => new RollingDate(RollingDate::T_TODAY),
]);
}
public function describeAction($data, $format = 'string')

View File

@@ -14,11 +14,12 @@ 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\ChillDateType;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Repository\UserJobRepositoryInterface;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\PersonBundle\Export\Declarations;
use DateTimeImmutable;
use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface;
@@ -34,6 +35,8 @@ class UserJobFilter implements FilterInterface
private const PJ = 'acp_ujob_filter_job';
private RollingDateConverterInterface $rollingDateConverter;
private Security $security;
private TranslatableStringHelper $translatableStringHelper;
@@ -43,11 +46,13 @@ class UserJobFilter implements FilterInterface
public function __construct(
Security $security,
TranslatableStringHelper $translatableStringHelper,
UserJobRepositoryInterface $userJobRepository
UserJobRepositoryInterface $userJobRepository,
RollingDateConverterInterface $rollingDateConverter
) {
$this->security = $security;
$this->translatableStringHelper = $translatableStringHelper;
$this->userJobRepository = $userJobRepository;
$this->rollingDateConverter = $rollingDateConverter;
}
public function addRole(): ?string
@@ -68,7 +73,10 @@ class UserJobFilter implements FilterInterface
)
)
)
->setParameter(self::P, $data['date_calc'])
->setParameter(
self::P,
$this->rollingDateConverter->convert($data['date_calc'])
)
->join(self::A . '.user', self::AU)
->andWhere(
$qb->expr()->in(self::AU . '.userJob', ':' . self::PJ)
@@ -92,9 +100,8 @@ class UserJobFilter implements FilterInterface
'choice_label' => fn (UserJob $job) => $this->translatableStringHelper->localize($job->getLabel()),
'label' => 'Job',
])
->add('date_calc', ChillDateType::class, [
'input' => 'datetime_immutable',
'data' => new DateTimeImmutable('now'),
->add('date_calc', PickRollingDateType::class, [
'data' => new RollingDate(RollingDate::T_TODAY),
'label' => 'export.filter.course.by_user_scope.Computation date for referrer',
'required' => true,
]);

View File

@@ -14,11 +14,12 @@ namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters;
use Chill\MainBundle\Entity\Scope;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Repository\ScopeRepositoryInterface;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\PersonBundle\Export\Declarations;
use DateTimeImmutable;
use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface;
@@ -34,6 +35,8 @@ class UserScopeFilter implements FilterInterface
private const PS = 'acp_uscope_filter_scopes';
private RollingDateConverterInterface $rollingDateConverter;
private ScopeRepositoryInterface $scopeRepository;
private Security $security;
@@ -43,11 +46,13 @@ class UserScopeFilter implements FilterInterface
public function __construct(
ScopeRepositoryInterface $scopeRepository,
Security $security,
TranslatableStringHelper $translatableStringHelper
TranslatableStringHelper $translatableStringHelper,
RollingDateConverterInterface $rollingDateConverter
) {
$this->scopeRepository = $scopeRepository;
$this->security = $security;
$this->translatableStringHelper = $translatableStringHelper;
$this->rollingDateConverter = $rollingDateConverter;
}
public function addRole(): ?string
@@ -68,7 +73,10 @@ class UserScopeFilter implements FilterInterface
)
)
)
->setParameter(self::P, $data['date_calc'])
->setParameter(
self::P,
$this->rollingDateConverter->convert($data['date_calc'])
)
->join(self::A . '.user', self::AU)
->andWhere(
$qb->expr()->in(self::AU . '.mainScope', ':' . self::PS)
@@ -91,9 +99,8 @@ class UserScopeFilter implements FilterInterface
'multiple' => true,
'expanded' => true,
])
->add('date_calc', ChillDateType::class, [
'input' => 'datetime_immutable',
'data' => new DateTimeImmutable('now'),
->add('date_calc', PickRollingDateType::class, [
'data' => new RollingDate(RollingDate::T_TODAY),
'label' => 'export.filter.course.by_user_scope.Computation date for referrer',
'required' => true,
]);

View File

@@ -0,0 +1,80 @@
<?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\EvaluationFilters;
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 Symfony\Component\Form\FormBuilderInterface;
class ByEndDateFilter implements FilterInterface
{
private RollingDateConverterInterface $rollingDateConverter;
public function __construct(RollingDateConverterInterface $rollingDateConverter)
{
$this->rollingDateConverter = $rollingDateConverter;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data): void
{
$qb
->andWhere('workeval.endDate BETWEEN :work_eval_by_end_date_start_date and :work_eval_by_end_date_end_date')
->setParameter(
'work_eval_by_end_date_start_date',
$this->rollingDateConverter->convert($data['start_date'])
)
->setParameter(
'work_eval_by_end_date_end_date',
$this->rollingDateConverter->convert($data['end_date'])
);
}
public function applyOn(): string
{
return Declarations::EVAL_TYPE;
}
public function buildForm(FormBuilderInterface $builder): void
{
$builder
->add('start_date', PickRollingDateType::class, [
'label' => 'start period date',
'data' => new RollingDate(RollingDate::T_YEAR_PREVIOUS_START),
])
->add('end_date', PickRollingDateType::class, [
'label' => 'end period date',
'data' => new RollingDate(RollingDate::T_TODAY),
]);
}
public function describeAction($data, $format = 'string'): array
{
return ['Filtered by end date: between %start_date% and %end_date%', [
'%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 getTitle(): string
{
return 'Filter by end date evaluations';
}
}

View File

@@ -0,0 +1,80 @@
<?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\EvaluationFilters;
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 Symfony\Component\Form\FormBuilderInterface;
class ByStartDateFilter implements FilterInterface
{
private RollingDateConverterInterface $rollingDateConverter;
public function __construct(RollingDateConverterInterface $rollingDateConverter)
{
$this->rollingDateConverter = $rollingDateConverter;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data): void
{
$qb
->andWhere('workeval.startDate BETWEEN :work_eval_by_start_date_start_date and :work_eval_by_start_date_end_date')
->setParameter(
'work_eval_by_start_date_start_date',
$this->rollingDateConverter->convert($data['start_date'])
)
->setParameter(
'work_eval_by_start_date_end_date',
$this->rollingDateConverter->convert($data['end_date'])
);
}
public function applyOn(): string
{
return Declarations::EVAL_TYPE;
}
public function buildForm(FormBuilderInterface $builder): void
{
$builder
->add('start_date', PickRollingDateType::class, [
'label' => 'start period date',
'data' => new RollingDate(RollingDate::T_YEAR_PREVIOUS_START),
])
->add('end_date', PickRollingDateType::class, [
'label' => 'end period date',
'data' => new RollingDate(RollingDate::T_TODAY),
]);
}
public function describeAction($data, $format = 'string'): array
{
return ['Filtered by start date: between %start_date% and %end_date%', [
'%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 getTitle(): string
{
return 'Filter by start date evaluations';
}
}

View File

@@ -0,0 +1,50 @@
<?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\EvaluationFilters;
use Chill\MainBundle\Export\FilterInterface;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
class CurrentEvaluationsFilter implements FilterInterface
{
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data): void
{
$qb->andWhere('workeval.endDate IS NULL');
}
public function applyOn(): string
{
return Declarations::EVAL_TYPE;
}
public function buildForm(FormBuilderInterface $builder): void
{
//no form needed
}
public function describeAction($data, $format = 'string'): array
{
return ['Filtered by current evaluations'];
}
public function getTitle(): string
{
return 'Filter by current evaluations';
}
}

View File

@@ -12,12 +12,12 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Export\Filter\HouseholdFilters;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\PersonBundle\Entity\Household\HouseholdCompositionType;
use Chill\PersonBundle\Export\Declarations;
use DateTime;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Query\Expr;
use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
@@ -26,12 +26,16 @@ use function in_array;
class CompositionFilter implements FilterInterface
{
private RollingDateConverterInterface $rollingDateConverter;
private TranslatableStringHelper $translatableStringHelper;
public function __construct(
TranslatableStringHelper $translatableStringHelper
TranslatableStringHelper $translatableStringHelper,
RollingDateConverterInterface $rollingDateConverter
) {
$this->translatableStringHelper = $translatableStringHelper;
$this->rollingDateConverter = $rollingDateConverter;
}
public function addRole(): ?string
@@ -58,7 +62,10 @@ class CompositionFilter implements FilterInterface
$qb->andWhere($whereClause);
$qb->setParameter('compositions', $data['accepted_composition']);
$qb->setParameter('ondate_composition_type_filter', $data['on_date'], Types::DATE_MUTABLE);
$qb->setParameter(
'ondate_composition_type_filter',
$this->rollingDateConverter->convert($data['on_date'])
);
}
public function applyOn(): string
@@ -79,8 +86,8 @@ class CompositionFilter implements FilterInterface
'multiple' => true,
'expanded' => true,
])
->add('on_date', ChillDateType::class, [
'data' => new DateTime('now'),
->add('on_date', PickRollingDateType::class, [
'data' => new RollingDate(RollingDate::T_TODAY),
]);
}
@@ -96,7 +103,7 @@ class CompositionFilter implements FilterInterface
return ['Filtered by composition: only %compositions% on %ondate%', [
'%compositions%' => implode(', ', $compositions),
'%ondate%' => $data['on_date']->format('d-m-Y'),
'%ondate%' => $this->rollingDateConverter->convert($data['on_date'])->format('d-m-Y'),
]];
}

View File

@@ -1,78 +0,0 @@
<?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\PersonFilters;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\PersonBundle\Export\AbstractAccompanyingPeriodExportElement;
use Chill\PersonBundle\Export\Declarations;
use DateTime;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
class AccompanyingPeriodClosingFilter extends AbstractAccompanyingPeriodExportElement implements FilterInterface
{
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$this->addJoinAccompanyingPeriod($qb);
$clause = $qb->expr()->andX(
$qb->expr()->lte('acp.closingDate', ':date_to'),
$qb->expr()->gte('acp.closingDate', ':date_from')
);
$qb->andWhere($clause);
$qb->setParameter('date_from', $data['date_from'], Types::DATE_MUTABLE);
$qb->setParameter('date_to', $data['date_to'], Types::DATE_MUTABLE);
}
public function applyOn(): string
{
return Declarations::PERSON_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('date_from', ChillDateType::class, [
'label' => 'Having an accompanying period closed after this date',
'data' => new DateTime('-1 month'),
]);
$builder->add('date_to', ChillDateType::class, [
'label' => 'Having an accompanying period closed before this date',
'data' => new DateTime(),
]);
}
public function describeAction($data, $format = 'string')
{
return [
'Filtered by accompanying period: persons having an accompanying period'
. ' closed between the %date_from% and %date_to%',
[
'%date_from%' => $data['date_from']->format('d-m-Y'),
'%date_to%' => $data['date_to']->format('d-m-Y'),
],
];
}
public function getTitle(): string
{
return 'Filter by accompanying period: closed between two dates';
}
}

View File

@@ -1,87 +0,0 @@
<?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\PersonFilters;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\PersonBundle\Export\AbstractAccompanyingPeriodExportElement;
use Chill\PersonBundle\Export\Declarations;
use DateTime;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
class AccompanyingPeriodFilter extends AbstractAccompanyingPeriodExportElement implements FilterInterface
{
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$this->addJoinAccompanyingPeriod($qb);
$clause = $qb->expr()->andX();
$clause->add(
$qb->expr()->lte('acp.openingDate', ':date_to')
);
$clause->add(
$qb->expr()->orX(
$qb->expr()->gte('acp.closingDate', ':date_from'),
$qb->expr()->isNull('acp.closingDate')
)
);
$qb->andWhere($clause);
$qb->setParameter('date_from', $data['date_from'], Types::DATE_MUTABLE);
$qb->setParameter('date_to', $data['date_to'], Types::DATE_MUTABLE);
}
public function applyOn(): string
{
return Declarations::PERSON_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('date_from', ChillDateType::class, [
'label' => 'Having an accompanying period opened after this date',
'data' => new DateTime('-1 month'),
]);
$builder->add('date_to', ChillDateType::class, [
'label' => 'Having an accompanying period ending before this date, or '
. 'still opened at this date',
'data' => new DateTime(),
]);
}
public function describeAction($data, $format = 'string')
{
return [
'Filtered by accompanying period: persons having an accompanying period'
. ' opened after the %date_from% and closed before the %date_to% (or still opened '
. 'at the %date_to%)',
[
'%date_from%' => $data['date_from']->format('d-m-Y'),
'%date_to%' => $data['date_to']->format('d-m-Y'),
],
];
}
public function getTitle(): string
{
return 'Filter by accompanying period: active period';
}
}

View File

@@ -1,78 +0,0 @@
<?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\PersonFilters;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\PersonBundle\Export\AbstractAccompanyingPeriodExportElement;
use Chill\PersonBundle\Export\Declarations;
use DateTime;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
class AccompanyingPeriodOpeningFilter extends AbstractAccompanyingPeriodExportElement implements FilterInterface
{
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$this->addJoinAccompanyingPeriod($qb);
$clause = $qb->expr()->andX(
$qb->expr()->lte('acp.openingDate', ':date_to'),
$qb->expr()->gte('acp.openingDate', ':date_from')
);
$qb->andWhere($clause);
$qb->setParameter('date_from', $data['date_from'], Types::DATE_MUTABLE);
$qb->setParameter('date_to', $data['date_to'], Types::DATE_MUTABLE);
}
public function applyOn(): string
{
return Declarations::PERSON_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('date_from', ChillDateType::class, [
'label' => 'Having an accompanying period opened after this date',
'data' => new DateTime('-1 month'),
]);
$builder->add('date_to', ChillDateType::class, [
'label' => 'Having an accompanying period opened before this date',
'data' => new DateTime(),
]);
}
public function describeAction($data, $format = 'string')
{
return [
'Filtered by accompanying period: persons having an accompanying period'
. ' opened between the %date_from% and %date_to%',
[
'%date_from%' => $data['date_from']->format('d-m-Y'),
'%date_to%' => $data['date_to']->format('d-m-Y'),
],
];
}
public function getTitle(): string
{
return 'Filter by accompanying period: starting between two dates';
}
}

View File

@@ -13,10 +13,11 @@ namespace Chill\PersonBundle\Export\Filter\PersonFilters;
use Chill\MainBundle\Export\ExportElementValidatedInterface;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\PersonBundle\Export\Declarations;
use DateInterval;
use DateTimeImmutable;
use Doctrine\ORM\Query\Expr;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
@@ -25,6 +26,13 @@ use Symfony\Component\Validator\Context\ExecutionContextInterface;
class AgeFilter implements ExportElementValidatedInterface, FilterInterface
{
private RollingDateConverterInterface $rollingDateConverter;
public function __construct(RollingDateConverterInterface $rollingDateConverter)
{
$this->rollingDateConverter = $rollingDateConverter;
}
public function addRole(): ?string
{
return null;
@@ -36,7 +44,7 @@ class AgeFilter implements ExportElementValidatedInterface, FilterInterface
$min = null !== $data['min_age'] ? $data['min_age'] : 0;
$max = null !== $data['max_age'] ? $data['max_age'] : 150;
$calc = $data['date_calc'];
$calc = $this->rollingDateConverter->convert($data['date_calc']);
$minDate = $calc->sub(new DateInterval('P' . $max . 'Y'));
$maxDate = $calc->sub(new DateInterval('P' . $min . 'Y'));
@@ -78,10 +86,9 @@ class AgeFilter implements ExportElementValidatedInterface, FilterInterface
'label' => 'Maximum age',
]);
$builder->add('date_calc', ChillDateType::class, [
$builder->add('date_calc', PickRollingDateType::class, [
'label' => 'Calculate age in relation to this date',
'data' => new DateTimeImmutable('now'),
'input' => 'datetime_immutable',
'data' => new RollingDate(RollingDate::T_TODAY),
]);
}

View File

@@ -13,20 +13,30 @@ namespace Chill\PersonBundle\Export\Filter\PersonFilters;
use Chill\MainBundle\Export\ExportElementValidatedInterface;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\PersonBundle\Export\Declarations;
use DateTime;
use Doctrine\ORM\Query\Expr;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
class BirthdateFilter implements ExportElementValidatedInterface, FilterInterface
{
private RollingDateConverterInterface $rollingDateConverter;
public function __construct(RollingDateConverterInterface $rollingDateConverter)
{
$this->rollingDateConverter = $rollingDateConverter;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(\Doctrine\ORM\QueryBuilder $qb, $data)
public function alterQuery(QueryBuilder $qb, $data)
{
$where = $qb->getDQLPart('where');
$clause = $qb->expr()->between(
@@ -42,8 +52,14 @@ class BirthdateFilter implements ExportElementValidatedInterface, FilterInterfac
}
$qb->add('where', $where);
$qb->setParameter('date_from', $data['date_from']);
$qb->setParameter('date_to', $data['date_to']);
$qb->setParameter(
'date_from',
$this->rollingDateConverter->convert($data['date_from'])
);
$qb->setParameter(
'date_to',
$this->rollingDateConverter->convert($data['date_to'])
);
}
public function applyOn()
@@ -51,16 +67,16 @@ class BirthdateFilter implements ExportElementValidatedInterface, FilterInterfac
return Declarations::PERSON_TYPE;
}
public function buildForm(\Symfony\Component\Form\FormBuilderInterface $builder)
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('date_from', ChillDateType::class, [
$builder->add('date_from', PickRollingDateType::class, [
'label' => 'Born after this date',
'data' => new DateTime(),
'data' => new RollingDate(RollingDate::T_YEAR_PREVIOUS_START),
]);
$builder->add('date_to', ChillDateType::class, [
$builder->add('date_to', PickRollingDateType::class, [
'label' => 'Born before this date',
'data' => new DateTime(),
'data' => new RollingDate(RollingDate::T_TODAY),
]);
}
@@ -68,8 +84,8 @@ class BirthdateFilter implements ExportElementValidatedInterface, FilterInterfac
{
return ['Filtered by person\'s birthdate: '
. 'between %date_from% and %date_to%', [
'%date_from%' => $data['date_from']->format('d-m-Y'),
'%date_to%' => $data['date_to']->format('d-m-Y'),
'%date_from%' => $this->rollingDateConverter->convert($data['date_from'])->format('d-m-Y'),
'%date_to%' => $this->rollingDateConverter->convert($data['date_to'])->format('d-m-Y'),
], ];
}

View File

@@ -0,0 +1,110 @@
<?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\PersonFilters;
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\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Entity\Household\HouseholdComposition;
use Chill\PersonBundle\Entity\Household\HouseholdCompositionType;
use Chill\PersonBundle\Entity\Household\HouseholdMember;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Repository\Household\HouseholdCompositionTypeRepositoryInterface;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface;
class ByHouseholdCompositionFilter implements FilterInterface
{
private HouseholdCompositionTypeRepositoryInterface $householdCompositionTypeRepository;
private RollingDateConverterInterface $rollingDateConverter;
private TranslatableStringHelperInterface $translatableStringHelper;
public function __construct(
HouseholdCompositionTypeRepositoryInterface $householdCompositionTypeRepository,
RollingDateConverterInterface $rollingDateConverter,
TranslatableStringHelperInterface $translatableStringHelper
) {
$this->householdCompositionTypeRepository = $householdCompositionTypeRepository;
$this->rollingDateConverter = $rollingDateConverter;
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$p = 'person_by_household_compo_filter';
$qb
->andWhere(
$qb->expr()->exists(
'SELECT 1 FROM ' . HouseholdMember::class . " {$p}_hm " .
'JOIN ' . HouseholdComposition::class . " {$p}_compo WITH {$p}_hm.household = {$p}_compo.household " .
"WHERE {$p}_hm.person = person AND {$p}_hm.shareHousehold = 'TRUE' " .
"AND ({$p}_hm.startDate <= :{$p}_date AND ({$p}_hm.endDate IS NULL OR {$p}_hm.endDate > :{$p}_date)) " .
"AND ({$p}_compo.startDate <= :{$p}_date AND ({$p}_compo.endDate IS NULL OR {$p}_compo.endDate > :{$p}_date)) " .
"AND {$p}_compo.householdCompositionType IN (:{$p}_accepted)"
)
)
->setParameter("{$p}_accepted", $data['compositions'])
->setParameter("{$p}_date", $this->rollingDateConverter->convert($data['calc_date']), Types::DATE_IMMUTABLE);
}
public function applyOn()
{
return Declarations::PERSON_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder
->add('compositions', EntityType::class, [
'class' => HouseholdCompositionType::class,
'choices' => $this->householdCompositionTypeRepository->findAllActive(),
'choice_label' => fn (HouseholdCompositionType $compositionType) => $this->translatableStringHelper->localize($compositionType->getLabel()),
'label' => 'export.filter.person.by_composition.Accepted compositions',
'multiple' => true,
'expanded' => true,
])
->add('calc_date', PickRollingDateType::class, [
'label' => 'export.filter.person.by_composition.Date calc',
'data' => new RollingDate(RollingDate::T_TODAY),
]);
}
public function describeAction($data, $format = 'string')
{
$compos = array_map(
fn (HouseholdCompositionType $compositionType) => $this->translatableStringHelper->localize($compositionType->getLabel()),
$data['compositions']->toArray()
);
return ['export.filter.person.by_composition.Filtered by composition at %date%: only %compositions%', [
'%compositions%' => implode(', ', $compos),
'%date%' => $this->rollingDateConverter->convert($data['calc_date'])->format('d-m-Y'),
]];
}
public function getTitle()
{
return 'export.filter.person.by_composition.Filter by household composition';
}
}

View File

@@ -12,9 +12,10 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Export\Filter\PersonFilters;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\PersonBundle\Export\Declarations;
use DateTime;
use Doctrine\ORM\Query\Expr;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
@@ -22,6 +23,14 @@ use Symfony\Component\Form\FormBuilderInterface;
class DeadOrAliveFilter implements FilterInterface
{
private RollingDateConverterInterface $rollingDateConverter;
public function __construct(
RollingDateConverterInterface $rollingDateConverter
) {
$this->rollingDateConverter = $rollingDateConverter;
}
public function addRole(): ?string
{
return null;
@@ -32,7 +41,7 @@ class DeadOrAliveFilter implements FilterInterface
$where = $qb->getDQLPart('where');
$personState = $data['person_state'];
$calc = $data['date_calc'];
$calc = $this->rollingDateConverter->convert($data['date_calc']);
if ('alive' === $personState) {
$clause = $qb->expr()->orX(
@@ -91,9 +100,9 @@ class DeadOrAliveFilter implements FilterInterface
'expanded' => true,
]);
$builder->add('date_calc', ChillDateType::class, [
$builder->add('date_calc', PickRollingDateType::class, [
'label' => 'Filter in relation to this date',
'data' => new DateTime('now'),
'data' => new RollingDate(RollingDate::T_TODAY),
]);
}
@@ -101,7 +110,7 @@ class DeadOrAliveFilter implements FilterInterface
{
return ['Filtered by a state of %deadOrAlive%: '
. 'at this date %date_calc%', [
'%date_calc%' => $data['date_calc']->format('d-m-Y'),
'%date_calc%' => $this->rollingDateConverter->convert($data['date_calc'])->format('d-m-Y'),
'%deadOrAlive%' => $data['person_state'],
], ];
}

View File

@@ -13,9 +13,10 @@ namespace Chill\PersonBundle\Export\Filter\PersonFilters;
use Chill\MainBundle\Export\ExportElementValidatedInterface;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\PersonBundle\Export\Declarations;
use DateTime;
use Doctrine\ORM\Query\Expr;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
@@ -23,6 +24,14 @@ use Symfony\Component\Validator\Context\ExecutionContextInterface;
class DeathdateFilter implements ExportElementValidatedInterface, FilterInterface
{
private RollingDateConverterInterface $rollingDateConverter;
public function __construct(
RollingDateConverterInterface $rollingDateConverter
) {
$this->rollingDateConverter = $rollingDateConverter;
}
public function addRole(): ?string
{
return null;
@@ -44,8 +53,14 @@ class DeathdateFilter implements ExportElementValidatedInterface, FilterInterfac
}
$qb->add('where', $where);
$qb->setParameter('date_from', $data['date_from']);
$qb->setParameter('date_to', $data['date_to']);
$qb->setParameter(
'date_from',
$this->rollingDateConverter->convert($data['date_from'])
);
$qb->setParameter(
'date_to',
$this->rollingDateConverter->convert($data['date_to'])
);
}
public function applyOn()
@@ -55,14 +70,14 @@ class DeathdateFilter implements ExportElementValidatedInterface, FilterInterfac
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('date_from', ChillDateType::class, [
$builder->add('date_from', PickRollingDateType::class, [
'label' => 'Death after this date',
'data' => new DateTime(),
'data' => new RollingDate(RollingDate::T_YEAR_PREVIOUS_START),
]);
$builder->add('date_to', ChillDateType::class, [
$builder->add('date_to', PickRollingDateType::class, [
'label' => 'Deathdate before',
'data' => new DateTime(),
'data' => new RollingDate(RollingDate::T_TODAY),
]);
}
@@ -70,8 +85,8 @@ class DeathdateFilter implements ExportElementValidatedInterface, FilterInterfac
{
return ['Filtered by person\'s deathdate: '
. 'between %date_from% and %date_to%', [
'%date_from%' => $data['date_from']->format('d-m-Y'),
'%date_to%' => $data['date_to']->format('d-m-Y'),
'%date_from%' => $this->rollingDateConverter->convert($data['date_from'])->format('d-m-Y'),
'%date_to%' => $this->rollingDateConverter->convert($data['date_to'])->format('d-m-Y'),
], ];
}

View File

@@ -11,29 +11,39 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Export\Filter\PersonFilters;
use Chill\MainBundle\Entity\GeographicalUnit;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Entity\GeographicalUnit\SimpleGeographicalUnitDTO;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Repository\GeographicalUnitLayerRepositoryInterface;
use Chill\MainBundle\Repository\GeographicalUnitRepositoryInterface;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Entity\Household\PersonHouseholdAddress;
use Chill\PersonBundle\Export\Declarations;
use DateTimeImmutable;
use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
class GeographicalUnitFilter implements \Chill\MainBundle\Export\FilterInterface
{
private GeographicalUnitLayerRepositoryInterface $geographicalUnitLayerRepository;
private GeographicalUnitRepositoryInterface $geographicalUnitRepository;
private RollingDateConverterInterface $rollingDateConverter;
private TranslatableStringHelperInterface $translatableStringHelper;
public function __construct(
GeographicalUnitRepositoryInterface $geographicalUnitRepository,
TranslatableStringHelperInterface $translatableStringHelper
GeographicalUnitLayerRepositoryInterface $geographicalUnitLayerRepository,
TranslatableStringHelperInterface $translatableStringHelper,
RollingDateConverterInterface $rollingDateConverter
) {
$this->geographicalUnitRepository = $geographicalUnitRepository;
$this->geographicalUnitLayerRepository = $geographicalUnitLayerRepository;
$this->translatableStringHelper = $translatableStringHelper;
$this->rollingDateConverter = $rollingDateConverter;
}
public function addRole(): ?string
@@ -47,8 +57,7 @@ class GeographicalUnitFilter implements \Chill\MainBundle\Export\FilterInterface
'SELECT 1
FROM ' . PersonHouseholdAddress::class . ' person_filter_geog_person_household_address
JOIN person_filter_geog_person_household_address.address person_filter_geog_address
JOIN ' . GeographicalUnit::class . ' person_filter_geog_unit
WITH ST_CONTAINS(person_filter_geog_unit.geom, person_filter_geog_address.point) = TRUE
JOIN person_filter_geog_address.geographicalUnits person_filter_geog_unit
WHERE
person_filter_geog_person_household_address.validFrom <= :person_filter_geog_date
AND
@@ -64,8 +73,11 @@ class GeographicalUnitFilter implements \Chill\MainBundle\Export\FilterInterface
->andWhere(
$qb->expr()->exists($subQuery)
)
->setParameter('person_filter_geog_date', $data['date_calc'])
->setParameter('person_filter_geog_units', $data['units']);
->setParameter(
'person_filter_geog_date',
$this->rollingDateConverter->convert($data['date_calc'])
)
->setParameter('person_filter_geog_units', array_map(static fn (SimpleGeographicalUnitDTO $unitDTO) => $unitDTO->id, $data['units']));
}
public function applyOn()
@@ -76,19 +88,18 @@ class GeographicalUnitFilter implements \Chill\MainBundle\Export\FilterInterface
public function buildForm(FormBuilderInterface $builder)
{
$builder
->add('date_calc', ChillDateType::class, [
->add('date_calc', PickRollingDateType::class, [
'label' => 'Compute geographical location at date',
'required' => true,
'data' => new DateTimeImmutable('today'),
'input' => 'datetime_immutable',
'data' => new RollingDate(RollingDate::T_TODAY),
])
->add('units', EntityType::class, [
->add('units', ChoiceType::class, [
'label' => 'Geographical unit',
'placeholder' => 'Select a geographical unit',
'class' => GeographicalUnit::class,
'choices' => $this->geographicalUnitRepository->findAll(),
'choice_label' => function (GeographicalUnit $item) {
return $this->translatableStringHelper->localize($item->getLayer()->getName()) . ' > ' . $item->getUnitName();
'choice_value' => static fn (SimpleGeographicalUnitDTO $item) => $item->id,
'choice_label' => function (SimpleGeographicalUnitDTO $item) {
return $this->translatableStringHelper->localize($this->geographicalUnitLayerRepository->find($item->layerId)->getName()) . ' > ' . $item->unitName;
},
'attr' => [
'class' => 'select2',
@@ -102,14 +113,14 @@ class GeographicalUnitFilter implements \Chill\MainBundle\Export\FilterInterface
return [
'exports.by_person.Filtered by person\'s geographical unit (based on address) computed at datecalc, only units',
[
'datecalc' => $data['date_calc']->format('Y-m-d'),
'datecalc' => $this->rollingDateConverter->convert($data['date_calc'])->format('Y-m-d'),
'units' => implode(
', ',
array_map(
function (GeographicalUnit $item) {
return $this->translatableStringHelper->localize($item->getLayer()->getName()) . ' > ' . $item->getUnitName();
function (SimpleGeographicalUnitDTO $item) {
return $this->translatableStringHelper->localize($this->geographicalUnitLayerRepository->find($item->layerId)->getName()) . ' > ' . $item->unitName;
},
$data['units']->toArray()
$data['units']
)
),
],

View File

@@ -12,12 +12,13 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Export\Filter\PersonFilters;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\PersonBundle\Entity\Person\ResidentialAddress;
use Chill\PersonBundle\Export\Declarations;
use Chill\ThirdPartyBundle\Entity\ThirdPartyCategory;
use DateTime;
use Doctrine\ORM\Query\Expr;
use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
@@ -26,11 +27,16 @@ use function in_array;
class ResidentialAddressAtThirdpartyFilter implements FilterInterface
{
private RollingDateConverterInterface $rollingDateConverter;
private TranslatableStringHelper $translatableStringHelper;
public function __construct(TranslatableStringHelper $translatableStringHelper)
{
public function __construct(
RollingDateConverterInterface $rollingDateConverter,
TranslatableStringHelper $translatableStringHelper
) {
$this->translatableStringHelper = $translatableStringHelper;
$this->rollingDateConverter = $rollingDateConverter;
}
public function addRole(): ?string
@@ -77,7 +83,10 @@ class ResidentialAddressAtThirdpartyFilter implements FilterInterface
}
$qb->setParameter('type', $data['thirdparty_cat']);
$qb->setParameter('date', $data['date_calc']);
$qb->setParameter(
'date',
$this->rollingDateConverter->convert($data['date_calc'])
);
$qb->add('where', $where);
}
@@ -98,9 +107,9 @@ class ResidentialAddressAtThirdpartyFilter implements FilterInterface
'expanded' => true,
]);
$builder->add('date_calc', ChillDateType::class, [
$builder->add('date_calc', PickRollingDateType::class, [
'label' => 'Date during which residential address was valid',
'data' => new DateTime('now'),
'data' => new RollingDate(RollingDate::T_TODAY),
]);
}
@@ -108,7 +117,7 @@ class ResidentialAddressAtThirdpartyFilter implements FilterInterface
{
return ['Filtered by person\'s who have a residential address located at a thirdparty of type %thirdparty_type% and valid on %date_calc%', [
'%thirdparty_type%' => $this->translatableStringHelper->localize($data['thirdparty_cat'][0]->getName()),
'%date_calc%' => $data['date_calc']->format('d-m-Y'),
'%date_calc%' => $this->rollingDateConverter->convert($data['date_calc'])->format('d-m-Y'),
]];
}

View File

@@ -12,10 +12,11 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Export\Filter\PersonFilters;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\PersonBundle\Entity\Person\ResidentialAddress;
use Chill\PersonBundle\Export\Declarations;
use DateTime;
use Doctrine\ORM\Query\Expr;
use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\QueryBuilder;
@@ -23,6 +24,14 @@ use function in_array;
class ResidentialAddressAtUserFilter implements FilterInterface
{
private RollingDateConverterInterface $rollingDateConverter;
public function __construct(
RollingDateConverterInterface $rollingDateConverter
) {
$this->rollingDateConverter = $rollingDateConverter;
}
public function addRole(): ?string
{
return null;
@@ -57,7 +66,10 @@ class ResidentialAddressAtUserFilter implements FilterInterface
$where = $qb->expr()->andX($clause);
}
$qb->setParameter('date', $data['date_calc']);
$qb->setParameter(
'date',
$this->rollingDateConverter->convert($data['date_calc'])
);
$qb->add('where', $where);
}
@@ -68,9 +80,9 @@ class ResidentialAddressAtUserFilter implements FilterInterface
public function buildForm(\Symfony\Component\Form\FormBuilderInterface $builder)
{
$builder->add('date_calc', ChillDateType::class, [
$builder->add('date_calc', PickRollingDateType::class, [
'label' => 'Date during which residential address was valid',
'data' => new DateTime('now'),
'data' => new RollingDate(RollingDate::T_TODAY),
]);
}

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\Filter\PersonFilters;
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\Entity\Household\HouseholdComposition;
use Chill\PersonBundle\Entity\Household\HouseholdMember;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
class WithoutHouseholdComposition implements FilterInterface
{
private RollingDateConverterInterface $rollingDateConverter;
public function __construct(
RollingDateConverterInterface $rollingDateConverter
) {
$this->rollingDateConverter = $rollingDateConverter;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$p = 'person_by_household_no_compo_filter';
$qb
->andWhere(
$qb->expr()->not(
$qb->expr()->exists(
'SELECT 1 FROM ' . HouseholdMember::class . " {$p}_hm " .
'JOIN ' . HouseholdComposition::class . " {$p}_compo WITH {$p}_hm.household = {$p}_compo.household " .
"WHERE {$p}_hm.person = person AND {$p}_hm.shareHousehold = 'TRUE' " .
"AND ({$p}_hm.startDate <= :{$p}_date AND ({$p}_hm.endDate IS NULL OR {$p}_hm.endDate > :{$p}_date)) " .
"AND ({$p}_compo.startDate <= :{$p}_date AND ({$p}_compo.endDate IS NULL OR {$p}_compo.endDate > :{$p}_date)) "
)
)
)
->setParameter("{$p}_date", $this->rollingDateConverter->convert($data['calc_date']), Types::DATE_IMMUTABLE);
}
public function applyOn()
{
return Declarations::PERSON_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder
->add('calc_date', PickRollingDateType::class, [
'label' => 'export.filter.person.by_no_composition.Date calc',
'data' => new RollingDate(RollingDate::T_TODAY),
]);
}
public function describeAction($data, $format = 'string')
{
return ['export.filter.person.by_no_composition.Persons filtered by no composition at %date%', [
'%date%' => $this->rollingDateConverter->convert($data['calc_date'])->format('d-m-Y'),
]];
}
public function getTitle()
{
return 'export.filter.person.by_no_composition.Filter persons without household composition';
}
}

View File

@@ -0,0 +1,50 @@
<?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\SocialWorkFilters;
use Chill\MainBundle\Export\FilterInterface;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
class CurrentActionFilter implements FilterInterface
{
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data): void
{
$qb->andWhere('acpw.endDate IS NULL');
}
public function applyOn(): string
{
return Declarations::SOCIAL_WORK_ACTION_TYPE;
}
public function buildForm(FormBuilderInterface $builder): void
{
//no form
}
public function describeAction($data, $format = 'string'): array
{
return ['Filtered by current action'];
}
public function getTitle(): string
{
return 'Filter by current actions';
}
}

View File

@@ -0,0 +1,441 @@
<?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\Helper;
use Chill\MainBundle\Export\Helper\ExportAddressHelper;
use Chill\MainBundle\Repository\CenterRepositoryInterface;
use Chill\MainBundle\Repository\CivilityRepositoryInterface;
use Chill\MainBundle\Repository\CountryRepository;
use Chill\MainBundle\Repository\LanguageRepositoryInterface;
use Chill\MainBundle\Repository\UserRepositoryInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Repository\MaritalStatusRepositoryInterface;
use DateTime;
use DateTimeImmutable;
use Doctrine\ORM\Query;
use Doctrine\ORM\QueryBuilder;
use Exception;
use PhpOffice\PhpSpreadsheet\Shared\Date;
use RuntimeException;
use Symfony\Contracts\Translation\TranslatorInterface;
use function in_array;
use function strlen;
/**
* Helper for list person: provide:.
*
* * the labels;
* * add select statement in query
*
* for some fields
*/
class ListPersonHelper
{
public const FIELDS = [
'id',
'civility',
'firstName',
'lastName',
'birthdate',
'center',
'deathdate',
'placeOfBirth',
'gender',
'genderComment',
'maritalStatus',
'maritalStatusComment',
'maritalStatusDate',
'memo',
'email',
'phonenumber',
'mobilenumber',
'numberOfChildren',
'contactInfo',
'countryOfBirth',
'nationality',
// add full addresses
'address_fields',
// add a list of spoken languages
'spokenLanguages',
// add household id
'household_id',
// add created at, created by, updated at, and updated by
'lifecycleUpdate',
];
private ExportAddressHelper $addressHelper;
private CenterRepositoryInterface $centerRepository;
private CivilityRepositoryInterface $civilityRepository;
private CountryRepository $countryRepository;
private LanguageRepositoryInterface $languageRepository;
private MaritalStatusRepositoryInterface $maritalStatusRepository;
private TranslatableStringHelper $translatableStringHelper;
private TranslatorInterface $translator;
private UserRepositoryInterface $userRepository;
public function __construct(
ExportAddressHelper $addressHelper,
CenterRepositoryInterface $centerRepository,
CivilityRepositoryInterface $civilityRepository,
CountryRepository $countryRepository,
LanguageRepositoryInterface $languageRepository,
MaritalStatusRepositoryInterface $maritalStatusRepository,
TranslatableStringHelper $translatableStringHelper,
TranslatorInterface $translator,
UserRepositoryInterface $userRepository
) {
$this->addressHelper = $addressHelper;
$this->centerRepository = $centerRepository;
$this->civilityRepository = $civilityRepository;
$this->countryRepository = $countryRepository;
$this->languageRepository = $languageRepository;
$this->maritalStatusRepository = $maritalStatusRepository;
$this->translatableStringHelper = $translatableStringHelper;
$this->translator = $translator;
$this->userRepository = $userRepository;
}
/**
* @param array|value-of<self::FIELDS>[] $fields
*/
public function addSelect(QueryBuilder $qb, array $fields, DateTimeImmutable $computedDate): void
{
foreach (ListPersonHelper::FIELDS as $f) {
if (!in_array($f, $fields, true)) {
continue;
}
switch ($f) {
case 'countryOfBirth':
case 'nationality':
$qb->addSelect(sprintf('IDENTITY(person.%s) as %s', $f, $f));
break;
case 'address_fields':
$this->addCurrentAddressAt($qb, $computedDate);
$qb->leftJoin('personHouseholdAddress.address', 'personAddress');
$this->addressHelper->addSelectClauses(ExportAddressHelper::F_ALL, $qb, 'personAddress', 'address_fields');
break;
case 'spokenLanguages':
$qb
->leftJoin('person.spokenLanguages', 'spokenLanguage')
->addSelect('AGGREGATE(spokenLanguage.id) AS spokenLanguages')
->addGroupBy('person');
if (in_array('center', $fields, true)) {
$qb->addGroupBy('center');
}
if (in_array('address_fields', $fields, true)) {
$qb
->addGroupBy('address_fieldsid')
->addGroupBy('address_fieldscountry_t.id')
->addGroupBy('address_fieldspostcode_t.id');
}
if (in_array('household_id', $fields, true)) {
$qb->addGroupBy('household_id');
}
break;
case 'household_id':
$qb
->addSelect('IDENTITY(personHouseholdAddress.household) AS household_id');
$this->addCurrentAddressAt($qb, $computedDate);
break;
case 'center':
$qb
->addSelect('IDENTITY(centerHistory.center) AS center')
->leftJoin('person.centerHistory', 'centerHistory')
->andWhere(
$qb->expr()->orX(
$qb->expr()->isNull('centerHistory'),
$qb->expr()->andX(
$qb->expr()->lte('centerHistory.startDate', ':address_date'),
$qb->expr()->orX(
$qb->expr()->isNull('centerHistory.endDate'),
$qb->expr()->gte('centerHistory.endDate', ':address_date')
)
)
)
)
->setParameter('address_date', $computedDate);
break;
case 'lifecycleUpdate':
$qb
->addSelect('person.createdAt AS createdAt')
->addSelect('IDENTITY(person.createdBy) AS createdBy')
->addSelect('person.updatedAt AS updatedAt')
->addSelect('IDENTITY(person.updatedBy) AS updatedBy');
break;
case 'genderComment':
$qb->addSelect('person.genderComment.comment AS genderComment');
break;
case 'maritalStatus':
$qb->addSelect('IDENTITY(person.maritalStatus) AS maritalStatus');
break;
case 'maritalStatusComment':
$qb->addSelect('person.maritalStatusComment.comment AS maritalStatusComment');
break;
case 'civility':
$qb->addSelect('IDENTITY(person.civility) AS civility');
break;
default:
$qb->addSelect(sprintf('person.%s as %s', $f, $f));
}
}
}
/**
* @return array|string[]
*/
public function getAllPossibleFields(): array
{
return array_merge(
self::FIELDS,
['createdAt', 'createdBy', 'updatedAt', 'updatedBy'],
$this->addressHelper->getKeys(ExportAddressHelper::F_ALL, 'address_fields')
);
}
public function getLabels($key, array $values, $data): callable
{
if (substr($key, 0, strlen('address_fields')) === 'address_fields') {
return $this->addressHelper->getLabel($key, $values, $data, 'address_fields');
}
switch ($key) {
case 'center':
return function ($value) use ($key) {
if ('_header' === $value) {
return $this->translator->trans($key);
}
if (null === $value || null === $center = $this->centerRepository->find($value)) {
return '';
}
return $center->getName();
};
case 'birthdate':
case 'deathdate':
case 'maritalStatusDate':
case 'createdAt':
case 'updatedAt':
// for birthdate, we have to transform the string into a date
// to format the date correctly.
return function ($value) use ($key) {
if ('_header' === $value) {
return $this->translator->trans($key);
}
if (null === $value) {
return '';
}
// warning: won't work with DateTimeImmutable as we reset time a few lines later
$date = DateTime::createFromFormat('Y-m-d', $value);
$hasTime = false;
if (false === $date) {
$date = DateTime::createFromFormat('Y-m-d H:i:s', $value);
$hasTime = true;
}
// check that the creation could occurs.
if (false === $date) {
throw new Exception(sprintf('The value %s could '
. 'not be converted to %s', $value, DateTime::class));
}
if (!$hasTime) {
$date->setTime(0, 0, 0);
}
return $date;
};
case 'createdBy':
case 'updatedBy':
return function ($value) use ($key) {
if ('_header' === $value) {
return $this->translator->trans($key);
}
if (null === $value) {
return '';
}
return $this->userRepository->find($value)->getLabel();
};
case 'civility':
return function ($value) use ($key) {
if ('_header' === $value) {
return $this->translator->trans($key);
}
if (null === $value) {
return '';
}
$civility = $this->civilityRepository->find($value);
if (null === $civility) {
return '';
}
return $this->translatableStringHelper->localize($civility->getName());
};
case 'gender':
// for gender, we have to translate men/women statement
return function ($value) use ($key) {
if ('_header' === $value) {
return $this->translator->trans($key);
}
return $this->translator->trans($value);
};
case 'maritalStatus':
return function ($value) use ($key) {
if ('_header' === $value) {
return $this->translator->trans($key);
}
if (null === $value) {
return '';
}
$maritalStatus = $this->maritalStatusRepository->find($value);
return $this->translatableStringHelper->localize($maritalStatus->getName());
};
case 'spokenLanguages':
return function ($value) use ($key) {
if ('_header' === $value) {
return $this->translator->trans($key);
}
if (null === $value) {
return '';
}
$ids = json_decode($value);
return
implode(
'|',
array_map(function ($id) {
if (null === $id) {
return '';
}
$lang = $this->languageRepository->find($id);
if (null === $lang) {
return null;
}
return $this->translatableStringHelper->localize($lang->getName());
}, $ids)
);
};
case 'countryOfBirth':
case 'nationality':
return function ($value) use ($key) {
if ('_header' === $value) {
return $this->translator->trans($key);
}
if (null === $value) {
return '';
}
$country = $this->countryRepository->find($value);
return $this->translatableStringHelper->localize(
$country->getName()
);
};
default:
if (!in_array($key, self::getAllPossibleFields(), true)) {
throw new RuntimeException("this key is not supported by this helper: {$key}");
}
// for fields which are associated with person
return function ($value) use ($key) {
if ('_header' === $value) {
return $this->translator->trans($key);
}
return $value;
};
}
}
private function addCurrentAddressAt(QueryBuilder $qb, DateTimeImmutable $date): void
{
if (!(in_array('personHouseholdAddress', $qb->getAllAliases(), true))) {
$qb
->leftJoin('person.householdAddresses', 'personHouseholdAddress')
->andWhere(
$qb->expr()->orX(
// no address at this time
$qb->expr()->isNull('personHouseholdAddress'),
// there is one address...
$qb->expr()->andX(
$qb->expr()->lte('personHouseholdAddress.validFrom', ':address_date'),
$qb->expr()->orX(
$qb->expr()->isNull('personHouseholdAddress.validTo'),
$qb->expr()->gt('personHouseholdAddress.validTo', ':address_date')
)
)
)
)
->setParameter('address_date', $date);
}
}
}

View File

@@ -14,9 +14,8 @@ namespace Chill\PersonBundle\Repository;
use Chill\PersonBundle\Entity\MaritalStatus;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\Persistence\ObjectRepository;
class MaritalStatusRepository implements ObjectRepository
class MaritalStatusRepository implements MaritalStatusRepositoryInterface
{
private EntityRepository $repository;

View File

@@ -0,0 +1,27 @@
<?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\Repository;
use Chill\PersonBundle\Entity\MaritalStatus;
interface MaritalStatusRepositoryInterface
{
public function find($id): ?MaritalStatus;
public function findAll(): array;
public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array;
public function findOneBy(array $criteria): ?MaritalStatus;
public function getClassName(): string;
}

View File

@@ -342,7 +342,9 @@ final class AccompanyingCourseApiControllerTest extends WebTestCase
// check that the person id is contained
$participationsPersonsIds = array_map(
static function ($participation) { return $participation->person->id; },
static function ($participation) {
return $participation->person->id;
},
$data->participations
);

View File

@@ -294,23 +294,49 @@ final class PersonControllerUpdateTest extends WebTestCase
public function validTextFieldsProvider()
{
return [
['firstName', 'random Value', static function (Person $person) { return $person->getFirstName(); }],
['lastName', 'random Value', static function (Person $person) { return $person->getLastName(); }],
['firstName', 'random Value', static function (Person $person) {
return $person->getFirstName();
}],
['lastName', 'random Value', static function (Person $person) {
return $person->getLastName();
}],
// reminder: this value is capitalized
['placeOfBirth', 'A PLACE', static function (Person $person) { return $person->getPlaceOfBirth(); }],
['birthdate', '1980-12-15', static function (Person $person) { return $person->getBirthdate()->format('Y-m-d'); }],
['placeOfBirth', 'A PLACE', static function (Person $person) {
return $person->getPlaceOfBirth();
}],
['birthdate', '1980-12-15', static function (Person $person) {
return $person->getBirthdate()->format('Y-m-d');
}],
// TODO test on phonenumber update
// ['phonenumber', '+32123456789', static function (Person $person) { return $person->getPhonenumber(); }],
['memo', 'jfkdlmq jkfldmsq jkmfdsq', static function (Person $person) { return $person->getMemo(); }],
['countryOfBirth', 'BE', static function (Person $person) { return $person->getCountryOfBirth()->getCountryCode(); }],
['nationality', 'FR', static function (Person $person) { return $person->getNationality()->getCountryCode(); }],
['placeOfBirth', '', static function (Person $person) { return $person->getPlaceOfBirth(); }],
['birthdate', '', static function (Person $person) { return $person->getBirthdate(); }],
['memo', 'jfkdlmq jkfldmsq jkmfdsq', static function (Person $person) {
return $person->getMemo();
}],
['countryOfBirth', 'BE', static function (Person $person) {
return $person->getCountryOfBirth()->getCountryCode();
}],
['nationality', 'FR', static function (Person $person) {
return $person->getNationality()->getCountryCode();
}],
['placeOfBirth', '', static function (Person $person) {
return $person->getPlaceOfBirth();
}],
['birthdate', '', static function (Person $person) {
return $person->getBirthdate();
}],
//['phonenumber', '', static function (Person $person) { return $person->getPhonenumber(); }],
['memo', '', static function (Person $person) { return $person->getMemo(); }],
['countryOfBirth', null, static function (Person $person) { return $person->getCountryOfBirth(); }],
['nationality', null, static function (Person $person) { return $person->getNationality(); }],
['gender', Person::FEMALE_GENDER, static function (Person $person) { return $person->getGender(); }],
['memo', '', static function (Person $person) {
return $person->getMemo();
}],
['countryOfBirth', null, static function (Person $person) {
return $person->getCountryOfBirth();
}],
['nationality', null, static function (Person $person) {
return $person->getNationality();
}],
['gender', Person::FEMALE_GENDER, static function (Person $person) {
return $person->getGender();
}],
];
}

View File

@@ -197,12 +197,24 @@ final class PersonControllerUpdateWithHiddenFieldsTest extends WebTestCase
public function validTextFieldsProvider()
{
return [
['firstName', 'random Value', static function (Person $person) { return $person->getFirstName(); }],
['lastName', 'random Value', static function (Person $person) { return $person->getLastName(); }],
['birthdate', '15-12-1980', static function (Person $person) { return $person->getBirthdate()->format('d-m-Y'); }],
['memo', 'jfkdlmq jkfldmsq jkmfdsq', static function (Person $person) { return $person->getMemo(); }],
['birthdate', '', static function (Person $person) { return $person->getBirthdate(); }],
['gender', Person::FEMALE_GENDER, static function (Person $person) { return $person->getGender(); }],
['firstName', 'random Value', static function (Person $person) {
return $person->getFirstName();
}],
['lastName', 'random Value', static function (Person $person) {
return $person->getLastName();
}],
['birthdate', '15-12-1980', static function (Person $person) {
return $person->getBirthdate()->format('d-m-Y');
}],
['memo', 'jfkdlmq jkfldmsq jkmfdsq', static function (Person $person) {
return $person->getMemo();
}],
['birthdate', '', static function (Person $person) {
return $person->getBirthdate();
}],
['gender', Person::FEMALE_GENDER, static function (Person $person) {
return $person->getGender();
}],
];
}

View File

@@ -13,6 +13,8 @@ namespace Export\Aggregator\AccompanyingCourseAggregators;
use Chill\MainBundle\Entity\Scope;
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\Test\Export\AbstractAggregatorTest;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
@@ -40,9 +42,13 @@ final class ReferrerScopeAggregatorTest extends AbstractAggregatorTest
(new Scope())->setName(['fr' => 'scope'])
);
$dateConverter = $this->prophesize(RollingDateConverterInterface::class);
$dateConverter->convert(Argument::type(RollingDate::class))->willReturn(new DateTimeImmutable());
return new ReferrerScopeAggregator(
$scopeRepository->reveal(),
$translatableStringHelper->reveal()
$translatableStringHelper->reveal(),
$dateConverter->reveal()
);
}

View File

@@ -1,90 +0,0 @@
<?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\Tests\Export\Filter\PersonFilters;
use Chill\MainBundle\Test\Export\AbstractFilterTest;
use Chill\PersonBundle\Export\Filter\PersonFilters\AccompanyingPeriodClosingFilter;
use DateTime;
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
/**
* @internal
* @coversNothing
*/
final class AccompanyingPeriodClosingFilterTest extends AbstractFilterTest
{
private AccompanyingPeriodClosingFilter $filter;
protected function setUp(): void
{
self::bootKernel();
try {
$this->filter = self::$container->get('chill.person.export.filter_accompanying_period_closing');
} catch (ServiceNotFoundException $e) {
$this->markTestSkipped('The current configuration does not use accompanying_periods');
}
}
public function getFilter()
{
return $this->filter;
}
public function getFormData(): array
{
return [
[
'date_from' => DateTime::createFromFormat('Y-m-d', '2000-01-01'),
'date_to' => DateTime::createFromFormat('Y-m-d', '2010-01-01'),
],
];
}
public function getQueryBuilders(): array
{
if (null === self::$kernel) {
self::bootKernel();
}
$em = self::$container
->get('doctrine.orm.entity_manager');
return [
$em->createQueryBuilder()
->select('person.firstName')
->from('ChillPersonBundle:Person', 'person'),
$em->createQueryBuilder()
->select('person.firstName')
->from('ChillPersonBundle:Person', 'person')
// add a dummy where clause
->where('person.firstname IS NOT NULL'),
$em->createQueryBuilder()
->select('count(IDENTITY(p))')
->from('ChillPersonBundle:Person', 'person')
// add a dummy where clause
->where('person.firstname IS NOT NULL'),
$em->createQueryBuilder()
->select('count(IDENTITY(p))')
->from('ChillPersonBundle:Person', 'person')
->join('person.accompanyingPeriods', 'accompanying_period')
// add a dummy where clause
->where('person.firstname IS NOT NULL'),
$em->createQueryBuilder()
->select('activity.date AS date')
->select('activity.attendee as attendee')
->from('ChillActivityBundle:Activity', 'activity')
->join('activity.person', 'person')
->join('person.center', 'center'),
];
}
}

View File

@@ -1,90 +0,0 @@
<?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\Tests\Export\Filter\PersonFilters;
use Chill\MainBundle\Test\Export\AbstractFilterTest;
use Chill\PersonBundle\Export\Filter\PersonFilters\AccompanyingPeriodFilter;
use DateTime;
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
/**
* @internal
* @coversNothing
*/
final class AccompanyingPeriodFilterTest extends AbstractFilterTest
{
private AccompanyingPeriodFilter $filter;
protected function setUp(): void
{
self::bootKernel();
try {
$this->filter = self::$container->get('chill.person.export.filter_accompanying_period');
} catch (ServiceNotFoundException $e) {
$this->markTestSkipped('The current configuration does not use accompanying_periods');
}
}
public function getFilter()
{
return $this->filter;
}
public function getFormData()
{
return [
[
'date_from' => DateTime::createFromFormat('Y-m-d', '2000-01-01'),
'date_to' => DateTime::createFromFormat('Y-m-d', '2010-01-01'),
],
];
}
public function getQueryBuilders()
{
if (null === self::$kernel) {
self::bootKernel();
}
$em = self::$container
->get('doctrine.orm.entity_manager');
return [
$em->createQueryBuilder()
->select('person.firstName')
->from('ChillPersonBundle:Person', 'person'),
$em->createQueryBuilder()
->select('person.firstName')
->from('ChillPersonBundle:Person', 'person')
// add a dummy where clause
->where('person.firstname IS NOT NULL'),
$em->createQueryBuilder()
->select('count(IDENTITY(p))')
->from('ChillPersonBundle:Person', 'person')
// add a dummy where clause
->where('person.firstname IS NOT NULL'),
$em->createQueryBuilder()
->select('count(IDENTITY(p))')
->from('ChillPersonBundle:Person', 'person')
->join('person.accompanyingPeriods', 'accompanying_period')
// add a dummy where clause
->where('person.firstname IS NOT NULL'),
$em->createQueryBuilder()
->select('activity.date AS date')
->select('activity.attendee as attendee')
->from('ChillActivityBundle:Activity', 'activity')
->join('activity.person', 'person')
->join('person.center', 'center'),
];
}
}

View File

@@ -1,90 +0,0 @@
<?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\Tests\Export\Filter\PersonFilters;
use Chill\MainBundle\Test\Export\AbstractFilterTest;
use Chill\PersonBundle\Export\Filter\PersonFilters\AccompanyingPeriodOpeningFilter;
use DateTime;
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
/**
* @internal
* @coversNothing
*/
final class AccompanyingPeriodOpeningFilterTest extends AbstractFilterTest
{
private AccompanyingPeriodOpeningFilter $filter;
protected function setUp(): void
{
self::bootKernel();
try {
$this->filter = self::$container->get('chill.person.export.filter_accompanying_period_opening');
} catch (ServiceNotFoundException $e) {
$this->markTestSkipped('The current configuration does not use accompanying_periods');
}
}
public function getFilter()
{
return $this->filter;
}
public function getFormData()
{
return [
[
'date_from' => DateTime::createFromFormat('Y-m-d', '2000-01-01'),
'date_to' => DateTime::createFromFormat('Y-m-d', '2010-01-01'),
],
];
}
public function getQueryBuilders()
{
if (null === self::$kernel) {
self::bootKernel();
}
$em = self::$container
->get('doctrine.orm.entity_manager');
return [
$em->createQueryBuilder()
->select('person.firstName')
->from('ChillPersonBundle:Person', 'person'),
$em->createQueryBuilder()
->select('person.firstName')
->from('ChillPersonBundle:Person', 'person')
// add a dummy where clause
->where('person.firstname IS NOT NULL'),
$em->createQueryBuilder()
->select('count(IDENTITY(p))')
->from('ChillPersonBundle:Person', 'person')
// add a dummy where clause
->where('person.firstname IS NOT NULL'),
$em->createQueryBuilder()
->select('count(IDENTITY(p))')
->from('ChillPersonBundle:Person', 'person')
->join('person.accompanyingPeriods', 'accompanying_period')
// add a dummy where clause
->where('person.firstname IS NOT NULL'),
$em->createQueryBuilder()
->select('activity.date AS date')
->select('activity.attendee as attendee')
->from('ChillActivityBundle:Activity', 'activity')
->join('activity.person', 'person')
->join('person.center', 'center'),
];
}
}

View File

@@ -111,7 +111,9 @@ final class MembersEditorTest extends TestCase
$this->assertCount(1, $notSharing);
$this->assertCount(1, $sharings);
$getPerson = static function (HouseholdMember $m) { return $m->getPerson(); };
$getPerson = static function (HouseholdMember $m) {
return $m->getPerson();
};
$this->assertContains($person, $notSharing->map($getPerson));
}
@@ -151,7 +153,9 @@ final class MembersEditorTest extends TestCase
$this->assertCount(1, $notSharing);
$this->assertCount(0, $sharings);
$getPerson = static function (HouseholdMember $m) { return $m->getPerson(); };
$getPerson = static function (HouseholdMember $m) {
return $m->getPerson();
};
$this->assertContains($person, $notSharing->map($getPerson));
}

View File

@@ -118,7 +118,9 @@ final class RelationshipDocGenNormalizerTest extends TestCase
{
$translatableStringHelper = $this->prophesize(TranslatableStringHelperInterface::class);
$translatableStringHelper->localize(Argument::type('array'))->will(
static function ($args) { return $args[0][array_keys($args[0])[0]]; }
static function ($args) {
return $args[0][array_keys($args[0])[0]];
}
);
$normalizer = new RelationshipDocGenNormalizer(

View File

@@ -103,6 +103,32 @@ services:
tags:
- { name: chill.export_filter, alias: accompanyingcourse_openbetweendates_filter }
chill.person.export.filter_has_temporary_location:
class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\HasTemporaryLocationFilter
tags:
- { name: chill.export_filter, alias: accompanyingcourse_has_temporary_location_filter }
chill.person.export.filter_has_no_referrer:
class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\HasNoReferrerFilter
tags:
- { name: chill.export_filter, alias: accompanyingcourse_has_no_referrer_filter }
chill.person.export.filter_has_no_action:
class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\HasNoActionFilter
tags:
- { name: chill.export_filter, alias: accompanyingcourse_has_no_action_filter }
chill.person.export.filter_creator:
class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\CreatorFilter
tags:
- { name: chill.export_filter, alias: accompanyingcourse_creator_filter }
chill.person.export.filter_creator_job:
class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\CreatorJobFilter
tags:
- { name: chill.export_filter, alias: accompanyingcourse_creator_job_filter }
## Aggregators
chill.person.export.aggregator_referrer_scope:
class: Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\ScopeAggregator
@@ -188,6 +214,10 @@ services:
tags:
- { name: chill.export_aggregator, alias: accompanyingcourse_ref_scope_aggregator }
Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\ByHouseholdCompositionAggregator:
Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\ByActionNumberAggregator:
tags:
- { name: chill.export_aggregator, alias: accompanyingcourse_by_household_compo_aggregator }
- { name: chill.export_aggregator, alias: accompanyingcourse_by_action_number_aggregator }
Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\CreatorJobAggregator:
tags:
- { name: chill.export_aggregator, alias: accompanyingcourse_creator_job_aggregator }

View File

@@ -1,16 +0,0 @@
services:
chill.person.export.filter_accompanying_period:
class: Chill\PersonBundle\Export\Filter\PersonFilters\AccompanyingPeriodFilter
tags:
- { name: chill.export_filter, alias: person_accc_period_filter }
chill.person.export.filter_accompanying_period_opening:
class: Chill\PersonBundle\Export\Filter\PersonFilters\AccompanyingPeriodOpeningFilter
tags:
- { name: chill.export_filter, alias: person_acc_pe_op_filter }
chill.person.export.filter_accompanying_period_closing:
class: Chill\PersonBundle\Export\Filter\PersonFilters\AccompanyingPeriodClosingFilter
tags:
- { name: chill.export_filter, alias: person_acc_pe_cl_filter }

View File

@@ -23,6 +23,31 @@ services:
tags:
- { name: chill.export_filter, alias: accompanyingcourse_maxdate_filter }
Chill\PersonBundle\Export\Filter\EvaluationFilters\ByStartDateFilter:
autowire: true
autoconfigure: true
tags:
- { name: chill.export_filter, alias: evaluation_bystartdate_filter }
Chill\PersonBundle\Export\Filter\EvaluationFilters\ByEndDateFilter:
autowire: true
autoconfigure: true
tags:
- { name: chill.export_filter, alias: evaluation_byenddate_filter }
Chill\PersonBundle\Export\Filter\EvaluationFilters\MaxDateFilter:
autowire: true
autoconfigure: true
tags:
- { name: chill.export_filter, alias: evaluation_bymaxdate_filter }
Chill\PersonBundle\Export\Filter\EvaluationFilters\CurrentEvaluationsFilter:
autowire: true
autoconfigure: true
tags:
- { name: chill.export_filter, alias: evaluation_currentevaluations_filter }
## Aggregators
chill.person.export.aggregator_evaluationtype:
class: Chill\PersonBundle\Export\Aggregator\EvaluationAggregators\EvaluationTypeAggregator
@@ -30,4 +55,28 @@ services:
autoconfigure: true
tags:
- { name: chill.export_aggregator, alias: accompanyingcourse_evaluationtype_aggregator }
Chill\PersonBundle\Export\Aggregator\EvaluationAggregators\ByStartDateAggregator:
autowire: true
autoconfigure: true
tags:
- { name: chill.export_aggregator, alias: evaluation_bystartdate_aggregator }
Chill\PersonBundle\Export\Aggregator\EvaluationAggregators\ByEndDateAggregator:
autowire: true
autoconfigure: true
tags:
- { name: chill.export_aggregator, alias: evaluation_byenddate_aggregator }
Chill\PersonBundle\Export\Aggregator\EvaluationAggregators\ByMaxDateAggregator:
autowire: true
autoconfigure: true
tags:
- { name: chill.export_aggregator, alias: evaluation_bymaxdate_aggregator }
Chill\PersonBundle\Export\Aggregator\EvaluationAggregators\HavingEndDateAggregator:
autowire: true
autoconfigure: true
tags:
- { name: chill.export_aggregator, alias: evaluation_byend_date_aggregator }

View File

@@ -1,4 +1,7 @@
services:
_defaults:
autoconfigure: true
autowire: true
## Indicators
chill.person.export.count_person:
@@ -15,13 +18,24 @@ services:
tags:
- { name: chill.export, alias: count_person_with_accompanying_course }
chill.person.export.list_person:
class: Chill\PersonBundle\Export\Export\ListPerson
Chill\PersonBundle\Export\Export\ListPerson:
autowire: true
autoconfigure: true
tags:
- { name: chill.export, alias: list_person }
Chill\PersonBundle\Export\Export\ListPersonWithAccompanyingPeriod:
autowire: true
autoconfigure: true
tags:
- { name: chill.export, alias: list_person_with_acp }
Chill\PersonBundle\Export\Export\ListAccompanyingPeriod:
autowire: true
autoconfigure: true
tags:
- { name: chill.export, alias: list_acp }
chill.person.export.list_person.duplicate:
class: Chill\PersonBundle\Export\Export\ListPersonDuplicate
arguments:
@@ -96,6 +110,14 @@ services:
tags:
- { name: chill.export_filter, alias: person_geog_filter }
Chill\PersonBundle\Export\Filter\PersonFilters\ByHouseholdCompositionFilter:
tags:
- { name: chill.export_filter, alias: person_by_household_composition_filter }
Chill\PersonBundle\Export\Filter\PersonFilters\WithoutHouseholdComposition:
tags:
- { name: chill.export_filter, alias: person_without_household_composition_filter }
## Aggregators
chill.person.export.aggregator_nationality:
class: Chill\PersonBundle\Export\Aggregator\PersonAggregators\NationalityAggregator
@@ -145,3 +167,7 @@ services:
tags:
- { name: chill.export_aggregator, alias: person_geog_aggregator }
Chill\PersonBundle\Export\Aggregator\PersonAggregators\ByHouseholdCompositionAggregator:
tags:
- { name: chill.export_aggregator, alias: person_household_compo_aggregator }

View File

@@ -37,6 +37,12 @@ services:
tags:
- { name: chill.export_filter, alias: social_work_actions_treatingagent_filter }
Chill\PersonBundle\Export\Filter\SocialWorkFilters\CurrentActionFilter:
autowire: true
autoconfigure: true
tags:
- { name: chill.export_filter, alias: social_work_actions_current_filter }
## AGGREGATORS
chill.person.export.aggregator_action_type:
class: Chill\PersonBundle\Export\Aggregator\SocialWorkAggregators\ActionTypeAggregator
@@ -86,3 +92,9 @@ services:
autoconfigure: true
tags:
- { name: chill.export_aggregator, alias: social_work_actions_goal_result_aggregator }
Chill\PersonBundle\Export\Aggregator\SocialWorkAggregators\CurrentActionAggregator:
autowire: true
autoconfigure: true
tags:
- { name: chill.export_aggregator, alias: social_work_actions_current_aggregator }

View File

@@ -89,6 +89,16 @@ Any person selected: Aucune personne sélectionnée
Create a household and add an address: Ajouter une adresse pour une personne non suivie et seule dans un ménage
A new household will be created. The person will be member of this household.: Un nouveau ménage va être créé. La personne sera membre de ce ménage.
Comment on the gender: Commentaire sur le genre
genderComment: Commentaire sur le genre
maritalStatus: État civil
maritalStatusComment: Commentaire sur l'état civil
maritalStatusDate: Date de l'état civil
memo: Commentaire
numberOfChildren: Nombre d'enfants
contactInfo: Commentaire des contacts
spokenLanguages: Langues parlées
# dédoublonnage
Old person: Doublon
@@ -339,6 +349,8 @@ List peoples: Liste des personnes
Create a list of people according to various filters.: Crée une liste des personnes selon différents filtres.
Fields to include in export: Champs à inclure dans l'export
Address valid at this date: Addresse valide à cette date
Data valid at this date: Données valides à cette date
Data regarding center, addresses, and so on will be computed at this date: Les données concernant le centre, l'adresse, le ménage, sera calculé à cette date.
List duplicates: Liste des doublons
Create a list of duplicate people: Créer la liste des personnes détectées comme doublons.
Count people participating in an accompanying course: Nombre de personnes concernées par un parcours
@@ -347,8 +359,8 @@ Count people participating in an accompanying course by various parameters.: Com
Exports of accompanying courses: Exports des parcours d'accompagnement
Count accompanying courses: Nombre de parcours
Count accompanying courses by various parameters: Compte le nombre de parcours en fonction de différents filtres.
Accompanying courses duration: Durée moyenne des parcours
Create an average of accompanying courses duration according to various filters: Moyenne de la durée des parcours en jours, selon différents filtres.
Accompanying courses participation duration and number of participations: Durée moyenne et nombre des participation des usagers aux parcours
Create an average of accompanying courses duration of each person participation to accompanying course, according to filters on persons, accompanying course: Crée un rapport qui comptabilise la moyenne de la durée de participation de chaque usager concerné aux parcours, avec différents filtres, notamment sur les usagers concernés.
Closingdate to apply: Date de fin à prendre en compte lorsque le parcours n'est pas clotûré
Exports of social work actions: Exports des actions d'accompagnement
@@ -361,8 +373,6 @@ Count evaluations: Nombre d'évaluations
Count evaluation by various parameters.: Compte le nombre d'évaluations selon différents filtres.
Exports of households: Exports des ménages
Count households: Nombre de ménages
Count household by various parameters.: Compte le nombre de ménages impliqués dans un parcours selon différents filtres.
## persons filters
Filter by person gender: Filtrer les personnes par genre
@@ -552,6 +562,32 @@ Date from: Date de début
Date to: Date de fin
"Filtered by opening dates: between %datefrom% and %dateto%": "Filtrer les parcours ouverts entre deux dates: entre le %datefrom% et le %dateto%"
Filter by temporary location: Filtrer les parcours avec une localisation temporaire
Filter by which has no referrer: Filtrer les parcours sans référent
"Filtered acp which has no referrer on date: %date%": "Filtré les parcours sans référent à cette date: %date%"
Has no referrer on this date: N'a pas de référent à cette date
Filter by which has no action: Filtrer les parcours qui nont pas dactions
Filtered acp which has no actions: 'Filtré: uniquement les parcours qui n''ont pas d''actions'
Group by number of actions: Grouper les parcours par nombre dactions
Filter by creator: Filtrer les parcours par créateur
'Filtered by creator: only %creators%': 'Filtré par créateur: uniquement %creators%'
Filter by creator job: Filtrer les parcours par métier du créateur
'Filtered by creator job: only %jobs%': 'Filtré par métier du créateur: uniquement %jobs%'
Group by creator job: Grouper les parcours par métier du créateur
Filter by current actions: Filtrer les actions en cours
Filtered by current action: 'Filtré: uniquement les actions en cours (sans date de fin)'
Filter by start date evaluations: Filtrer les évaluations par date de début
Filter by end date evaluations: Filtrer les évaluations par date de fin
start period date: Date de début de la période
end period date: Date de fin de la période
"Filtered by start date: between %start_date% and %end_date%": "Filtré par la date de début: comprise entre %start_date% et %end_date%"
"Filtered by end date: between %start_date% and %end_date%": "Filtré par la date de fin: comprise entre %start_date% et %end_date%"
Filter by current evaluations: Filtrer les évaluations en cours
"Filtered by current evaluations": "Filtré: uniquement les évaluations en cours"
'Filtered by geographic unit: computed at %date%, only in %units%': 'Filtré par unité géographique: adresse le %date%, seulement les unités %units%'
## social actions filters/aggr
Filter by treating agent scope: Filtrer les actions par service de l'agent traitant
"Filtered by treating agent scope: only %scopes%": "Filtré par service de l'agent traitant: uniquement %scopes%"
@@ -589,6 +625,7 @@ maxdate is specified: la date d'échéance est spécifiée
maxdate is not specified: la date d'échéance n'est pas spécifiée
"Filtered by maxdate: only %choice%": "Filtré par date d'échéance: uniquement si %choice%"
## household filters/aggr
Filter by composition: Filtrer les ménages par composition familiale
Accepted composition: Composition familiale
@@ -716,6 +753,7 @@ socialAction:
defaultNotificationDelay: Délai de notification par défaut
socialIssue: Problématique sociale
household_id: Identifiant du ménage
household:
allowHolder: Peut être titulaire
shareHousehold: Membre du ménage
@@ -951,7 +989,24 @@ notification:
Notify any: Notifier d'autres utilisateurs
export:
export:
acp_stats:
avg_duration: Moyenne de la durée de participation de chaque usager concerné
count_participations: Nombre de participations distinctes
count_persons: Nombre d'usagers concernés distincts
count_acps: Nombre de parcours distincts
nb_household_with_course:
Count households with accompanying course: Nombre de ménages impliqués dans un parcours
Count households: Nombre de ménages
Count accompanying periods: Nombre de parcours
Count household with accompanying course by various parameters.: Compte le nombre de ménages impliqués dans un parcours selon différents filtres.
Date of calculation of household members: Date à laquelle les membres du ménages sont comptabilisés
aggregator:
person:
by_household_composition:
Household composition: Composition du ménage
Group course by household composition: Grouper les personnes par composition familiale
Calc date: Date de calcul de la composition du ménage
course:
by_referrer:
Computation date for referrer: Date à laquelle le référent était actif
@@ -964,16 +1019,97 @@ export:
week: Durée du parcours en semaines
month: Durée du parcours en mois
Precision: Unité de la durée
by_household_composition:
Household composition: Composition du ménage
Group course by household composition: Grouper les parcours par composition familiale des ménages des usagers concernés
Calc date: Date de calcul de la composition du ménage
by_number_of_action:
Number of actions: Nombre d'actions
by_creator_job:
Creator's job: Métier du créateur
course_work:
by_current_action:
Current action ?: Action en cours ?
Group by current actions: Grouper les actions en cours
Current action: Action en cours
Not current action: Action terminée
eval:
by_end_date:
Has end date ?: Évaluation en cours ?
Group evaluations having end date: Grouper les évaluations en cours (avec ou sans date de fin)
enddate is specified: la date de fin est spécifiée
enddate is not specified: la date de fin n'est pas spécifiée
Group by end date evaluations: Grouper les évaluations par semaine/mois/année de la date de fin
End date period: Fin (par periode)
by_start_date_period:
Start date period: Début (par periode)
Group by start date evaluations: Grouper les évaluations par semaine/mois/année de la date de début
by_max_date:
Group by max date evaluations: Grouper les évaluations par semaine/mois/année de la date d'échéance
Max date: Date d'échéance
filter:
person:
by_composition:
Filter by household composition: Filtrer les personnes par composition du ménage
Accepted compositions: Composition de ménages
Date calc: Date de calcul
'Filtered by composition at %date%: only %compositions%': 'Filtré par composition du ménage, le %date%, seulement %compositions%'
by_no_composition:
Filter persons without household composition: Filtrer les personnes sans composition de ménage (ni ménage)
Persons filtered by no composition at %date%: Uniquement les personnes sans composition de ménage à la date du %date%
Date calc: Date de calcul
course:
by_user_scope:
Computation date for referrer: Date à laquelle le référent était actif
by_referrer:
Computation date for referrer: Date à laquelle le référent était actif
having_temporarily:
Having a temporarily location: Ayant une localisation temporaire
Having a person's location: Ayant une localisation auprès d'un usager
Calculation date: Date de la localisation
creator_job:
'Filtered by creator job: only %jobs%': 'Filtré par métier du créateur: seulement %jobs%'
list:
person_with_acp:
List peoples having an accompanying period: Liste des personnes ayant un parcours d'accompagnement
Create a list of people having an accompaying periods, according to various filters.: Génère une liste des personnes ayant un parcours d'accompagnement, selon différents critères liés au parcours ou à l'usager
acp:
List of accompanying periods: Liste de périodes d'accompagnements
Generate a list of accompanying periods, filtered on different parameters.: Génère une liste des périodes d'accompagnement, filtrée sur différents paramètres.
Date of calculation for associated elements: Date de calcul des éléments associés
The associated referree, localisation, and other elements will be valid at this date: Les éléments associés, comme la localisation, le référent et d'autres éléments seront valides à cette date
id: Identifiant du parcours
openingDate: Date d'ouverture du parcours
closingDate: Date de fermeture du parcours
confidential: Confidentiel
emergency: Urgent
intensity: Intensité
createdAt: Créé le
updatedAt: Dernière mise à jour le
acpOrigin: Origine du parcours
acpClosingMotive: Motif de fermeture
acpJob: Métier du parcours
createdBy: Créé par
updatedBy: Dernière modification par
administrativeLocation: Location administrative
step: Etape
stepSince: Dernière modification de l'étape
referrer: Référent
referrerSince: Référent depuis le
locationIsPerson: Parcours localisé auprès d'un usager concerné
locationIsTemp: Parcours avec une localisation temporaire
acpLocationPersonName: Usager auprès duquel le parcours est localisé
locationPersonId: Identifiant de l'usager auprès duquel le parcours est localisé
acpaddress_fieldscountry: Pays de l'adresse
isRequestorPerson: Le demandeur est-il un usager ?
isRequestorThirdParty: Le demandeur est-il un tiers ?
requestorPersonId: Identifiant du demandeur personne
requestorThirdPartyId: Identifiant du tiers
acprequestorPerson: Nom du demandeur personne
acprequestorThirdPaty: Nom du demandeur tiers
scopes: Services
socialIssues: Problématiques sociales
social_action:
and children: et dérivés