Merge branch '111_exports_suite' into calendar/finalization

This commit is contained in:
2022-10-24 11:10:44 +02:00
158 changed files with 3064 additions and 1522 deletions

View File

@@ -13,6 +13,7 @@ namespace Chill\PersonBundle\Controller;
use Chill\MainBundle\Pagination\PaginatorFactory;
use Chill\MainBundle\Serializer\Model\Collection;
use Chill\PersonBundle\Entity\SocialWork\Evaluation;
use Chill\PersonBundle\Entity\SocialWork\SocialAction;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
@@ -39,9 +40,11 @@ class SocialWorkEvaluationApiController extends AbstractController
*/
public function listEvaluationBySocialAction(SocialAction $action): Response
{
$pagination = $this->paginatorFactory->create($action->getEvaluations()->count());
$evaluations = $action->getEvaluations()->filter(static fn (Evaluation $eval) => $eval->isActive());
$evaluations = $action->getEvaluations()->slice(
$pagination = $this->paginatorFactory->create($evaluations->count());
$evaluations = $evaluations->slice(
$pagination->getCurrentPageFirstItemNumber(),
$pagination->getItemsPerPage()
);

View File

@@ -22,6 +22,7 @@ use Chill\MainBundle\Entity\Scope;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Entity\UserJob;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodLocationHistory;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodStepHistory;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
use Chill\PersonBundle\Entity\AccompanyingPeriod\ClosingMotive;
use Chill\PersonBundle\Entity\AccompanyingPeriod\Comment;
@@ -341,6 +342,12 @@ class AccompanyingPeriod implements
*/
private string $step = self::STEP_DRAFT;
/**
* @ORM\OneToMany(targetEntity=AccompanyingPeriodStepHistory::class,
* mappedBy="period", cascade={"persist", "remove"}, orphanRemoval=true)
*/
private Collection $stepHistories;
/**
* @ORM\Column(type="datetime", nullable=true, options={"default": NULL})
*/
@@ -395,8 +402,7 @@ class AccompanyingPeriod implements
*/
public function __construct(?DateTime $dateOpening = null)
{
$this->setOpeningDate($dateOpening ?? new DateTime('now'));
$this->calendars = new ArrayCollection();
$this->calendars = new ArrayCollection(); // TODO we cannot add a dependency between AccompanyingPeriod and calendars
$this->participations = new ArrayCollection();
$this->scopes = new ArrayCollection();
$this->socialIssues = new ArrayCollection();
@@ -405,6 +411,8 @@ class AccompanyingPeriod implements
$this->resources = new ArrayCollection();
$this->userHistories = new ArrayCollection();
$this->locationHistories = new ArrayCollection();
$this->stepHistories = new ArrayCollection();
$this->setOpeningDate($dateOpening ?? new DateTime('now'));
}
/**
@@ -995,6 +1003,11 @@ class AccompanyingPeriod implements
return $this->step;
}
public function getStepHistories(): Collection
{
return $this->stepHistories;
}
public function getUser(): ?User
{
return $this->user;
@@ -1263,7 +1276,11 @@ class AccompanyingPeriod implements
*/
public function setOpeningDate($openingDate)
{
$this->openingDate = $openingDate;
if ($this->openingDate !== $openingDate) {
$this->openingDate = $openingDate;
$this->ensureStepContinuity();
}
return $this;
}
@@ -1362,6 +1379,14 @@ class AccompanyingPeriod implements
$this->bootPeriod();
}
if (self::STEP_DRAFT !== $this->step && $previous !== $step) {
// we create a new history
$history = new AccompanyingPeriodStepHistory();
$history->setStep($this->step)->setStartDate(new DateTimeImmutable('now'));
$this->addStepHistory($history);
}
return $this;
}
@@ -1402,6 +1427,17 @@ class AccompanyingPeriod implements
return $this;
}
private function addStepHistory(AccompanyingPeriodStepHistory $stepHistory): self
{
if (!$this->stepHistories->contains($stepHistory)) {
$this->stepHistories[] = $stepHistory;
$stepHistory->setPeriod($this);
$this->ensureStepContinuity();
}
return $this;
}
private function bootPeriod(): void
{
// first location history
@@ -1413,6 +1449,43 @@ class AccompanyingPeriod implements
$this->addLocationHistory($locationHistory);
}
private function ensureStepContinuity(): void
{
// ensure continuity of histories
$criteria = new Criteria();
$criteria->orderBy(['startDate' => Criteria::ASC, 'id' => Criteria::ASC]);
/** @var Iterator $steps */
$steps = $this->getStepHistories()->matching($criteria)->getIterator();
$steps->rewind();
// we set the start date of the first step as the opening date, only if it is
// not greater than the end date
/** @var AccompanyingPeriodStepHistory $current */
$current = $steps->current();
if (null === $current) {
return;
}
if ($this->getOpeningDate()->format('Y-m-d') !== $current->getStartDate()->format('Y-m-d')
&& ($this->getOpeningDate() <= $current->getEndDate() || null === $current->getEndDate())) {
$current->setStartDate(DateTimeImmutable::createFromMutable($this->getOpeningDate()));
}
// then we set all the end date to the start date of the next one
do {
/** @var AccompanyingPeriodStepHistory $current */
$current = $steps->current();
$steps->next();
if ($steps->valid()) {
$next = $steps->current();
$current->setEndDate($next->getStartDate());
}
} while ($steps->valid());
}
private function setRequestorPerson(?Person $requestorPerson = null): self
{
$this->requestorPerson = $requestorPerson;

View File

@@ -0,0 +1,115 @@
<?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\Entity\AccompanyingPeriod;
use Chill\MainBundle\Doctrine\Model\TrackCreationInterface;
use Chill\MainBundle\Doctrine\Model\TrackCreationTrait;
use Chill\MainBundle\Doctrine\Model\TrackUpdateInterface;
use Chill\MainBundle\Doctrine\Model\TrackUpdateTrait;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person;
use DateTimeImmutable;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
* @ORM\Table("chill_person_accompanying_period_step_history")
*/
class AccompanyingPeriodStepHistory implements TrackCreationInterface, TrackUpdateInterface
{
use TrackCreationTrait;
use TrackUpdateTrait;
/**
* @ORM\Column(type="date_immutable", nullable=true, options={"default": null})
*/
private ?DateTimeImmutable $endDate = null;
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private ?int $id = null;
/**
* @ORM\ManyToOne(targetEntity=AccompanyingPeriod::class)
*/
private AccompanyingPeriod $period;
/**
* @ORM\Column(type="date_immutable")
*/
private ?DateTimeImmutable $startDate = null;
/**
* @ORM\Column(type="text", nullable=false)
*/
private string $step;
public function getEndDate(): ?DateTimeImmutable
{
return $this->endDate;
}
public function getId(): ?int
{
return $this->id;
}
public function getPeriod(): AccompanyingPeriod
{
return $this->period;
}
public function getStartDate(): ?DateTimeImmutable
{
return $this->startDate;
}
public function getStep(): string
{
return $this->step;
}
public function setEndDate(?DateTimeImmutable $endDate): self
{
$this->endDate = $endDate;
return $this;
}
/**
* @internal use AccompanyingPeriod::addLocationHistory
*/
public function setPeriod(AccompanyingPeriod $period): self
{
$this->period = $period;
return $this;
}
public function setStartDate(?DateTimeImmutable $startDate): self
{
$this->startDate = $startDate;
return $this;
}
public function setStep(string $step): AccompanyingPeriodStepHistory
{
$this->step = $step;
return $this;
}
}

View File

@@ -36,7 +36,7 @@ class Comment implements TrackCreationInterface, TrackUpdateInterface
* inversedBy="comments")
* @ORM\JoinColumn(nullable=false, onDelete="CASCADE")
*/
private ?AccompanyingPeriod $accompanyingPeriod;
private ?AccompanyingPeriod $accompanyingPeriod = null;
/**
* @ORM\Column(type="text")

View File

@@ -1488,7 +1488,7 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
$participation = $this->participationsContainAccompanyingPeriod($accompanyingPeriod);
if (!null === $participation) {
$participation->setEndDate(DateTimeImmutable::class);
$participation->setEndDate(new DateTime());
$this->accompanyingPeriodParticipations->removeElement($participation);
}
}
@@ -1569,8 +1569,6 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
*/
public function setCenter(Center $center): self
{
$this->center = $center;
$modification = new DateTimeImmutable('now');
foreach ($this->centerHistory as $centerHistory) {

View File

@@ -26,6 +26,11 @@ use Symfony\Component\Serializer\Annotation as Serializer;
*/
class Evaluation
{
/**
* @ORM\Column(type="boolean", nullable=false, options={"default": true})
*/
private bool $active = true;
/**
* @ORM\Column(type="dateinterval", nullable=true, options={"default": null})
* @Serializer\Groups({"read"})
@@ -114,6 +119,11 @@ class Evaluation
return $this->url;
}
public function isActive(): bool
{
return $this->active;
}
/**
* @return $this
*
@@ -128,6 +138,13 @@ class Evaluation
return $this;
}
public function setActive(bool $active): Evaluation
{
$this->active = $active;
return $this;
}
public function setDelay(?DateInterval $delay): self
{
$this->delay = $delay;

View File

@@ -228,6 +228,22 @@ class SocialAction
return $descendants;
}
/**
* @param Collection|SocialAction[] $socialActions
*/
public static function getDescendantsWithThisForActions($socialActions): Collection
{
$unique = [];
foreach ($socialActions as $action) {
foreach ($action->getDescendantsWithThis() as $child) {
$unique[spl_object_hash($child)] = $child;
}
}
return new ArrayCollection(array_values($unique));
}
public function getEvaluations(): Collection
{
return $this->evaluations;
@@ -274,6 +290,11 @@ class SocialAction
return $this->title;
}
public function hasChildren(): bool
{
return 0 < $this->getChildren()->count();
}
public function hasParent(): bool
{
return $this->getParent() instanceof self;
@@ -369,6 +390,8 @@ class SocialAction
{
$this->parent = $parent;
$parent->addChild($this);
return $this;
}

View File

@@ -71,11 +71,17 @@ class SocialIssue
$this->socialActions = new ArrayCollection();
}
/**
* @internal use @see{SocialIssue::setParent} instead
*
* @param SocialIssue $child
*
* @return $this
*/
public function addChild(self $child): self
{
if (!$this->children->contains($child)) {
$this->children[] = $child;
$child->setParent($this);
}
return $this;
@@ -215,6 +221,22 @@ class SocialIssue
return $descendants;
}
/**
* @param array|SocialIssue[] $socialIssues
*/
public static function getDescendantsWithThisForIssues(array $socialIssues): Collection
{
$unique = [];
foreach ($socialIssues as $issue) {
foreach ($issue->getDescendantsWithThis() as $child) {
$unique[spl_object_hash($child)] = $child;
}
}
return new ArrayCollection(array_values($unique));
}
public function getId(): ?int
{
return $this->id;
@@ -262,6 +284,11 @@ class SocialIssue
return $this->title;
}
public function hasChildren(): bool
{
return 0 < $this->getChildren()->count();
}
public function hasParent(): bool
{
return null !== $this->parent;
@@ -329,6 +356,8 @@ class SocialIssue
{
$this->parent = $parent;
$parent->addChild($this);
return $this;
}

View File

@@ -0,0 +1,131 @@
<?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\Form\Type\ChillDateType;
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
{
private const PREFIX = 'acp_by_household_compo_agg';
private HouseholdCompositionTypeRepositoryInterface $householdCompositionTypeRepository;
private TranslatableStringHelperInterface $translatableStringHelper;
public function __construct(HouseholdCompositionTypeRepositoryInterface $householdCompositionTypeRepository, TranslatableStringHelperInterface $translatableStringHelper)
{
$this->householdCompositionTypeRepository = $householdCompositionTypeRepository;
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$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")
)
)
)
)
->leftJoin(
HouseholdComposition::class,
"{$p}_compo",
Join::WITH,
$qb->expr()->orX(
$qb->expr()->isNull("{$p}_compo"),
$qb->expr()->andX(
$qb->expr()->lte("{$p}_compo.startDate", ":{$p}_date"),
$qb->expr()->orX(
$qb->expr()->isNull("{$p}_compo.endDate"),
$qb->expr()->gt("{$p}_compo.endDate", ":{$p}_date")
)
)
)
)
->addSelect("IDENTITY({$p}_compo.householdCompositionType) AS {$p}_select")
->setParameter("{$p}_date", $data['date_calc'])
->addGroupBy("{$p}_select");
}
public function applyOn()
{
return Declarations::ACP_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'),
]);
}
public function getLabels($key, array $values, $data)
{
return function ($value) {
if ('_header' === $value) {
return 'export.aggregator.course.by_household_composition.Household composition';
}
if (null === $value) {
return '';
}
if (null === $o = $this->householdCompositionTypeRepository->find($value)) {
return '';
}
return $this->translatableStringHelper->localize($o->getLabel());
};
}
public function getQueryKeys($data)
{
return [self::PREFIX . '_select'];
}
public function getTitle()
{
return 'export.aggregator.course.by_household_composition.Group course by household composition';
}
}

View File

@@ -14,6 +14,7 @@ namespace Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators;
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;
@@ -65,8 +66,6 @@ class ConfidentialAggregator implements AggregatorInterface
default:
throw new LogicException(sprintf('The value %s is not valid', $value));
}
return $value;
};
}

View File

@@ -13,12 +13,21 @@ namespace Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\PersonBundle\Export\Declarations;
use DateTimeImmutable;
use Doctrine\ORM\QueryBuilder;
use LogicException;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
final class DurationAggregator implements AggregatorInterface
{
private const CHOICES = [
'month',
'week',
'day',
];
private TranslatorInterface $translator;
public function __construct(TranslatorInterface $translator)
@@ -33,19 +42,31 @@ final class DurationAggregator implements AggregatorInterface
public function alterQuery(QueryBuilder $qb, $data)
{
$qb->addSelect(
'
(acp.closingDate - acp.openingDate +15) *12/365
AS duration_aggregator'
);
switch ($data['precision']) {
case 'day':
$qb->addSelect('(COALESCE(acp.closingDate, :now) - acp.openingDate) AS duration_aggregator');
// TODO Pour avoir un interval plus précis (nécessaire ?):
// adapter la fonction extract pour pouvoir l'utiliser avec des intervals: extract(month from interval)
// et ajouter une fonction custom qui calcule plus précisément les intervals, comme doctrineum/date-interval
// https://packagist.org/packages/doctrineum/date-interval#3.1.0 (mais composer fait un conflit de dépendance)
break;
$qb->addGroupBy('duration_aggregator');
$qb->addOrderBy('duration_aggregator');
case 'week':
$qb->addSelect('(COALESCE(acp.closingDate, :now) - acp.openingDate) / 7 AS duration_aggregator');
break;
case 'month':
$qb->addSelect('(EXTRACT (MONTH FROM AGE(COALESCE(acp.closingDate, :now), acp.openingDate)) * 12 +
EXTRACT (MONTH FROM AGE(COALESCE(acp.closingDate, :now), acp.openingDate))) AS duration_aggregator');
break;
default:
throw new LogicException('precision not supported: ' . $data['precision']);
}
$qb
->setParameter('now', new DateTimeImmutable('now'))
->addGroupBy('duration_aggregator')
->addOrderBy('duration_aggregator');
}
public function applyOn(): string
@@ -55,25 +76,27 @@ final class DurationAggregator implements AggregatorInterface
public function buildForm(FormBuilderInterface $builder)
{
// no form
$builder->add('precision', ChoiceType::class, [
'choices' => array_combine(self::CHOICES, self::CHOICES),
'label' => 'export.aggregator.course.duration.Precision',
'choice_label' => static fn (string $c) => 'export.aggregator.course.duration.' . $c,
'multiple' => false,
'expanded' => true,
]);
}
public function getLabels($key, array $values, $data)
{
return function ($value): ?string {
return static function ($value) use ($data) {
if ('_header' === $value) {
return 'Rounded month duration';
return 'export.aggregator.course.duration.' . $data['precision'];
}
if (null === $value) {
return $this->translator->trans('current duration'); // when closingDate is null
return 0;
}
if (0 === $value) {
return $this->translator->trans('duration 0 month');
}
return $value . $this->translator->trans(' months');
return $value;
};
}

View File

@@ -14,6 +14,7 @@ namespace Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators;
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;
@@ -65,8 +66,6 @@ class EmergencyAggregator implements AggregatorInterface
default:
throw new LogicException(sprintf('The value %s is not valid', $value));
}
return $value;
};
}

View File

@@ -14,6 +14,7 @@ namespace Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators;
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;

View File

@@ -12,15 +12,20 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Repository\UserRepository;
use Chill\MainBundle\Templating\Entity\UserRender;
use Chill\PersonBundle\Export\Declarations;
use DateTimeImmutable;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use function in_array;
final class ReferrerAggregator implements AggregatorInterface
{
private const A = 'acp_ref_agg_uhistory';
private const P = 'acp_ref_agg_date';
private UserRender $userRender;
private UserRepository $userRepository;
@@ -40,12 +45,23 @@ final class ReferrerAggregator implements AggregatorInterface
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('acpuser', $qb->getAllAliases(), true)) {
$qb->leftJoin('acp.user', 'acpuser');
}
$qb->addSelect('acpuser.id AS referrer_aggregator');
$qb->addGroupBy('referrer_aggregator');
$qb
->addSelect('IDENTITY(' . self::A . '.user) AS referrer_aggregator')
->addGroupBy('referrer_aggregator')
->leftJoin('acp.userHistories', self::A)
->andWhere(
$qb->expr()->orX(
$qb->expr()->isNull(self::A),
$qb->expr()->andX(
$qb->expr()->lte(self::A . '.startDate', ':' . self::P),
$qb->expr()->orX(
$qb->expr()->isNull(self::A . '.endDate'),
$qb->expr()->gt(self::A . '.endDate', ':' . self::P)
)
)
)
)
->setParameter(self::P, $data['date_calc']);
}
public function applyOn(): string
@@ -55,7 +71,13 @@ final class ReferrerAggregator implements AggregatorInterface
public function buildForm(FormBuilderInterface $builder)
{
// no form
$builder
->add('date_calc', ChillDateType::class, [
'input' => 'datetime_immutable',
'data' => new DateTimeImmutable('now'),
'label' => 'export.aggregator.course.by_referrer.Computation date for referrer',
'required' => true,
]);
}
public function getLabels($key, array $values, $data)

View File

@@ -12,19 +12,21 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators;
use Chill\MainBundle\Export\AggregatorInterface;
//use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
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;
use Symfony\Contracts\Translation\TranslatorInterface;
use function in_array;
final class StepAggregator implements AggregatorInterface //, FilterInterface
final class StepAggregator implements AggregatorInterface
{
private const A = 'acpstephistories';
private const P = 'acp_step_agg_date';
private TranslatorInterface $translator;
public function __construct(
@@ -40,30 +42,26 @@ final class StepAggregator implements AggregatorInterface //, FilterInterface
public function alterQuery(QueryBuilder $qb, $data)
{
$qb->addSelect('acp.step AS step_aggregator');
$qb->addGroupBy('step_aggregator');
/*
// add date in where clause
$where = $qb->getDQLPart('where');
$clause = $qb->expr()->andX(
$qb->expr()->lte('acp.openingDate', ':ondate'),
$qb->expr()->orX(
$qb->expr()->gt('acp.closingDate', ':ondate'),
$qb->expr()->isNull('acp.closingDate')
)
);
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('ondate', $data['on_date'], Types::DATE_MUTABLE);
*/
$qb
->addSelect(self::A . '.step AS step_aggregator')
->andWhere(
$qb->expr()->orX(
$qb->expr()->isNull(self::A . '.step'),
$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)
)
)
)
)
->setParameter(self::P, $data['on_date'])
->addGroupBy('step_aggregator');
}
public function applyOn(): string
@@ -94,8 +92,11 @@ final class StepAggregator implements AggregatorInterface //, FilterInterface
case '_header':
return 'Step';
case null:
return '';
default:
throw new LogicException(sprintf('The value %s is not valid', $value));
return $value;
}
};
}

View File

@@ -23,6 +23,7 @@ use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use function in_array;
final class CountryOfBirthAggregator implements AggregatorInterface, ExportElementValidatedInterface
{
@@ -83,16 +84,12 @@ final class CountryOfBirthAggregator implements AggregatorInterface, ExportEleme
. ' is not known.');
}
$qb->leftJoin('person.countryOfBirth', 'countryOfBirth');
if (!in_array('countryOfBirth', $qb->getAllAliases(), true)) {
$qb->leftJoin('person.countryOfBirth', 'countryOfBirth');
}
// add group by
$groupBy = $qb->getDQLPart('groupBy');
if (!empty($groupBy)) {
$qb->addGroupBy('country_of_birth_aggregator');
} else {
$qb->groupBy('country_of_birth_aggregator');
}
$qb->addGroupBy('country_of_birth_aggregator');
}
public function applyOn()

View File

@@ -70,14 +70,7 @@ final class HouseholdPositionAggregator implements AggregatorInterface, ExportEl
$qb->setParameter('date', $data['date_position']);
$qb->addSelect('IDENTITY(householdmember.position) AS household_position_aggregator');
$groupBy = $qb->getDQLPart('groupBy');
if (!empty($groupBy)) {
$qb->addGroupBy('household_position_aggregator');
} else {
$qb->groupBy('household_position_aggregator');
}
$qb->addGroupBy('household_position_aggregator');
}
public function applyOn()

View File

@@ -43,14 +43,7 @@ final class MaritalStatusAggregator implements AggregatorInterface
}
$qb->addSelect('personmarital.id as marital_status_aggregator');
$groupBy = $qb->getDQLPart('groupBy');
if (!empty($groupBy)) {
$qb->addGroupBy('marital_status_aggregator');
} else {
$qb->groupBy('marital_status_aggregator');
}
$qb->addGroupBy('marital_status_aggregator');
}
public function applyOn()

View File

@@ -83,15 +83,7 @@ final class NationalityAggregator implements AggregatorInterface, ExportElementV
}
$qb->leftJoin('person.nationality', 'nationality');
// add group by
$groupBy = $qb->getDQLPart('groupBy');
if (!empty($groupBy)) {
$qb->addGroupBy('nationality_aggregator');
} else {
$qb->groupBy('nationality_aggregator');
}
$qb->addGroupBy('nationality_aggregator');
}
public function applyOn()

View File

@@ -75,9 +75,9 @@ class CountAccompanyingCourse implements ExportInterface, GroupedExportInterface
return ['export_result'];
}
public function getResult($qb, $data)
public function getResult($query, $data)
{
return $qb->getQuery()->getResult(Query::HYDRATE_SCALAR);
return $query->getQuery()->getResult(Query::HYDRATE_SCALAR);
}
public function getTitle(): string
@@ -99,6 +99,7 @@ class CountAccompanyingCourse implements ExportInterface, GroupedExportInterface
$qb = $this->repository->createQueryBuilder('acp');
$qb
->andWhere('acp.step != :count_acp_step')
->andWhere(
$qb->expr()->exists(
'SELECT 1 FROM ' . AccompanyingPeriodParticipation::class . ' acl_count_part
@@ -107,6 +108,7 @@ class CountAccompanyingCourse implements ExportInterface, GroupedExportInterface
'
)
)
->setParameter('count_acp_step', AccompanyingPeriod::STEP_DRAFT)
->setParameter('authorized_centers', $centers);
$qb->select('COUNT(DISTINCT acp.id) AS export_result');

View File

@@ -22,6 +22,7 @@ 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;
@@ -74,9 +75,9 @@ class CountEvaluation implements ExportInterface, GroupedExportInterface
return ['export_result'];
}
public function getResult($qb, $data)
public function getResult($query, $data)
{
return $qb->getQuery()->getResult(Query::HYDRATE_SCALAR);
return $query->getQuery()->getResult(Query::HYDRATE_SCALAR);
}
public function getTitle(): string

View File

@@ -23,6 +23,7 @@ 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
@@ -74,9 +75,9 @@ class CountHousehold implements ExportInterface, GroupedExportInterface
return ['export_result'];
}
public function getResult($qb, $data)
public function getResult($query, $data)
{
return $qb->getQuery()->getResult(Query::HYDRATE_SCALAR);
return $query->getQuery()->getResult(Query::HYDRATE_SCALAR);
}
public function getTitle(): string

View File

@@ -72,9 +72,9 @@ class CountPerson implements ExportInterface, GroupedExportInterface
return ['export_result'];
}
public function getResult($qb, $data)
public function getResult($query, $data)
{
return $qb->getQuery()->getResult(Query::HYDRATE_SCALAR);
return $query->getQuery()->getResult(Query::HYDRATE_SCALAR);
}
public function getTitle()

View File

@@ -21,6 +21,7 @@ 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;
@@ -73,9 +74,9 @@ class CountPersonWithAccompanyingCourse implements ExportInterface, GroupedExpor
return ['export_result'];
}
public function getResult($qb, $data)
public function getResult($query, $data)
{
return $qb->getQuery()->getResult(Query::HYDRATE_SCALAR);
return $query->getQuery()->getResult(Query::HYDRATE_SCALAR);
}
public function getTitle(): string

View File

@@ -75,9 +75,9 @@ class CountSocialWorkActions implements ExportInterface, GroupedExportInterface
return ['export_result'];
}
public function getResult($qb, $data)
public function getResult($query, $data)
{
return $qb->getQuery()->getResult(Query::HYDRATE_SCALAR);
return $query->getQuery()->getResult(Query::HYDRATE_SCALAR);
}
public function getTitle(): string

View File

@@ -120,10 +120,7 @@ class ListPersonDuplicate implements DirectExportInterface, ExportElementValidat
return $response;
}
/**
* @return string
*/
public function getDescription()
public function getDescription(): string
{
return 'Create a list of duplicate people';
}

View File

@@ -25,6 +25,7 @@ use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query;
use Doctrine\ORM\QueryBuilder;
use LogicException;
use Symfony\Component\Form\FormBuilderInterface;
class StatAccompanyingCourseDuration implements ExportInterface, GroupedExportInterface
@@ -79,9 +80,9 @@ class StatAccompanyingCourseDuration implements ExportInterface, GroupedExportIn
return ['export_result'];
}
public function getResult($qb, $data)
public function getResult($query, $data)
{
return $qb->getQuery()->getResult(Query::HYDRATE_SCALAR);
return $query->getQuery()->getResult(Query::HYDRATE_SCALAR);
}
public function getTitle(): string

View File

@@ -11,13 +11,11 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters;
use Chill\MainBundle\Entity\Location;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\PickUserLocationType;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface;
class AdministrativeLocationFilter implements FilterInterface
@@ -37,17 +35,10 @@ class AdministrativeLocationFilter implements FilterInterface
public function alterQuery(QueryBuilder $qb, $data)
{
$where = $qb->getDQLPart('where');
$clause = $qb->expr()->in('acp.administrativeLocation', ':locations');
if ($where instanceof Andx) {
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->setParameter('locations', $data['accepted_locations']);
$qb
->andWhere($clause)
->setParameter('locations', $data['accepted_locations']);
}
public function applyOn(): string
@@ -57,13 +48,9 @@ class AdministrativeLocationFilter implements FilterInterface
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('accepted_locations', EntityType::class, [
'class' => Location::class,
'choice_label' => function (Location $l) {
return $l->getName() . ' (' . $this->translatableStringHelper->localize($l->getLocationType()->getTitle()) . ')';
},
$builder->add('accepted_locations', PickUserLocationType::class, [
'label' => 'Accepted locations',
'multiple' => true,
'expanded' => true,
]);
}
@@ -76,7 +63,7 @@ class AdministrativeLocationFilter implements FilterInterface
}
return ['Filtered by administratives locations: only %locations%', [
'%locations%' => implode(', ou ', $locations),
'%locations%' => implode(', ', $locations),
]];
}

View File

@@ -77,7 +77,7 @@ class ClosingMotiveFilter implements FilterInterface
return [
'Filtered by closingmotive: only %closingmotives%', [
'%closingmotives%' => implode(', ou ', $motives),
'%closingmotives%' => implode(', ', $motives),
], ];
}

View File

@@ -73,15 +73,11 @@ class ConfidentialFilter implements FilterInterface
public function describeAction($data, $format = 'string'): array
{
foreach (self::CHOICES as $k => $v) {
if ($v === $data['accepted_confidentials']) {
$choice = $k;
}
}
return [
'Filtered by confidential: only %confidential%', [
'%confidential%' => $this->translator->trans($choice),
'%confidential%' => $this->translator->trans(
$data['accepted_confidentials'] ? 'is confidential' : 'is not confidential'
),
],
];
}

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\Export\Filter\AccompanyingCourseFilters;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Entity\UserJob;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Security\Core\Security;
class CurrentUserJobFilter implements FilterInterface
{
private Security $security;
private TranslatableStringHelper $translatableStringHelper;
public function __construct(
TranslatableStringHelper $translatableStringHelper,
Security $security
) {
$this->translatableStringHelper = $translatableStringHelper;
$this->security = $security;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$where = $qb->getDQLPart('where');
$clause = $qb->expr()->eq('acp.job', ':userjob');
if ($where instanceof Andx) {
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->setParameter('userjob', $this->getUserJob());
}
public function applyOn()
{
return Declarations::ACP_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
{
}
public function describeAction($data, $format = 'string')
{
return [
'Filtered by user job: only %job%', [
'%job%' => $this->translatableStringHelper->localize(
$this->getUserJob()->getLabel()
),
],
];
}
public function getTitle()
{
return 'Filter by user job';
}
private function getUserJob(): UserJob
{
/** @var User $user */
$user = $this->security->getUser();
return $user->getUserJob();
}
}

View File

@@ -1,95 +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\AccompanyingCourseFilters;
use Chill\MainBundle\Entity\Scope;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Security\Core\Security;
use function in_array;
class CurrentUserScopeFilter implements FilterInterface
{
private Security $security;
private TranslatableStringHelper $translatableStringHelper;
public function __construct(
TranslatableStringHelper $translatableStringHelper,
Security $security
) {
$this->translatableStringHelper = $translatableStringHelper;
$this->security = $security;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('acpscope', $qb->getAllAliases(), true)) {
$qb->join('acp.scopes', 'acpscope');
}
$where = $qb->getDQLPart('where');
$clause = $qb->expr()->eq('acpscope.id', ':userscope');
if ($where instanceof Andx) {
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->setParameter('userscope', $this->getUserMainScope());
}
public function applyOn()
{
return Declarations::ACP_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
{
}
public function describeAction($data, $format = 'string')
{
return [
'Filtered by user main scope: only %scope%', [
'%scope%' => $this->translatableStringHelper->localize(
$this->getUserMainScope()->getName()
),
],
];
}
public function getTitle()
{
return 'Filter by user scope';
}
private function getUserMainScope(): Scope
{
/** @var User $user */
$user = $this->security->getUser();
return $user->getMainScope();
}
}

View File

@@ -73,15 +73,11 @@ class EmergencyFilter implements FilterInterface
public function describeAction($data, $format = 'string'): array
{
foreach (self::CHOICES as $k => $v) {
if ($v === $data['accepted_emergency']) {
$choice = $k;
}
}
return [
'Filtered by emergency: only %emergency%', [
'%emergency%' => $this->translator->trans($choice),
'%emergency%' => $this->translator->trans(
$data['accepted_emergency'] ? 'is emergency' : 'is not emergency'
),
],
];
}

View File

@@ -15,7 +15,7 @@ use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\PersonBundle\Entity\SocialWork\Evaluation;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\Query\Expr\Andx;
use Chill\PersonBundle\Repository\SocialWork\EvaluationRepositoryInterface;
use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface;
@@ -23,11 +23,15 @@ use function in_array;
class EvaluationFilter implements FilterInterface
{
private EvaluationRepositoryInterface $evaluationRepository;
private TranslatableStringHelper $translatableStringHelper;
public function __construct(
EvaluationRepositoryInterface $evaluationRepository,
TranslatableStringHelper $translatableStringHelper
) {
$this->evaluationRepository = $evaluationRepository;
$this->translatableStringHelper = $translatableStringHelper;
}
@@ -50,16 +54,8 @@ class EvaluationFilter implements FilterInterface
$qb->join('workeval.evaluation', 'eval');
}
$where = $qb->getDQLPart('where');
$clause = $qb->expr()->in('eval.id', ':evaluations');
if ($where instanceof Andx) {
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->andWhere($clause);
$qb->setParameter('evaluations', $data['accepted_evaluations']);
}
@@ -72,11 +68,13 @@ class EvaluationFilter implements FilterInterface
{
$builder->add('accepted_evaluations', EntityType::class, [
'class' => Evaluation::class,
'choices' => $this->evaluationRepository->findAllActive(),
'choice_label' => function (Evaluation $ev) {
return $this->translatableStringHelper->localize($ev->getTitle());
},
'multiple' => true,
'expanded' => true,
'expanded' => false,
'attr' => ['class' => 'select2'],
]);
}
@@ -89,7 +87,7 @@ class EvaluationFilter implements FilterInterface
}
return ['Filtered by evaluations: only %evals%', [
'%evals%' => implode(', ou ', $evaluations),
'%evals%' => implode(', ', $evaluations),
]];
}

View File

@@ -73,15 +73,9 @@ class IntensityFilter implements FilterInterface
public function describeAction($data, $format = 'string'): array
{
foreach (self::CHOICES as $k => $v) {
if ($v === $data['accepted_intensities']) {
$choice = $k;
}
}
return [
'Filtered by intensity: only %intensity%', [
'%intensity%' => $this->translator->trans($choice),
'%intensity%' => $this->translator->trans($data['accepted_intensities']),
],
];
}

View File

@@ -76,7 +76,7 @@ class OriginFilter implements FilterInterface
}
return ['Filtered by origins: only %origins%', [
'%origins%' => implode(', ou ', $origins),
'%origins%' => implode(', ', $origins),
]];
}

View File

@@ -11,17 +11,23 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Form\Type\PickUserDynamicType;
use Chill\MainBundle\Templating\Entity\UserRender;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\Query\Expr\Andx;
use DateTimeImmutable;
use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface;
class ReferrerFilter implements FilterInterface
{
private const A = 'acp_referrer_filter_uhistory';
private const P = 'acp_referrer_filter_date';
private const PU = 'acp_referrer_filter_users';
private UserRender $userRender;
public function __construct(UserRender $userRender)
@@ -36,17 +42,22 @@ class ReferrerFilter implements FilterInterface
public function alterQuery(QueryBuilder $qb, $data)
{
$where = $qb->getDQLPart('where');
$clause = $qb->expr()->in('acp.user', ':referrers');
if ($where instanceof Andx) {
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->setParameter('referrers', $data['accepted_referrers']);
$qb
->join('acp.userHistories', self::A)
->andWhere(
$qb->expr()->andX(
$qb->expr()->lte(self::A . '.startDate', ':' . self::P),
$qb->expr()->orX(
$qb->expr()->isNull(self::A . '.endDate'),
$qb->expr()->gt(self::A . '.endDate', ':' . self::P)
)
)
)
->andWhere(
$qb->expr()->in(self::A . '.user', ':' . self::PU)
)
->setParameter(self::PU, $data['accepted_referrers'])
->setParameter(self::P, $data['date_calc']);
}
public function applyOn(): string
@@ -56,14 +67,16 @@ class ReferrerFilter implements FilterInterface
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('accepted_referrers', EntityType::class, [
'class' => User::class,
'choice_label' => function (User $u) {
return $this->userRender->renderString($u, []);
},
'multiple' => true,
'expanded' => true,
]);
$builder
->add('accepted_referrers', PickUserDynamicType::class, [
'multiple' => true,
])
->add('date_calc', ChillDateType::class, [
'input' => 'datetime_immutable',
'data' => new DateTimeImmutable('now'),
'label' => 'export.filter.course.by_referrer.Computation date for referrer',
'required' => true,
]);
}
public function describeAction($data, $format = 'string'): array
@@ -76,7 +89,7 @@ class ReferrerFilter implements FilterInterface
return [
'Filtered by referrer: only %referrers%', [
'%referrers' => implode(', ou ', $users),
'%referrers' => implode(', ', $users),
], ];
}

View File

@@ -15,10 +15,9 @@ use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\PersonBundle\Entity\SocialWork\SocialAction;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Form\Type\PickSocialActionType;
use Chill\PersonBundle\Templating\Entity\SocialActionRender;
use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface;
use function in_array;
@@ -51,17 +50,13 @@ class SocialActionFilter implements FilterInterface
$qb->join('acpw.socialAction', 'acpwsocialaction');
}
$where = $qb->getDQLPart('where');
$clause = $qb->expr()->in('acpwsocialaction.id', ':socialactions');
if ($where instanceof Andx) {
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->setParameter('socialactions', $data['accepted_socialactions']);
$qb->andWhere($clause)
->setParameter(
'socialactions',
SocialAction::getDescendantsWithThisForActions($data['accepted_socialactions'])->toArray()
);
}
public function applyOn(): string
@@ -71,26 +66,25 @@ class SocialActionFilter implements FilterInterface
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('accepted_socialactions', EntityType::class, [
'class' => SocialAction::class,
'choice_label' => function (SocialAction $sa) {
return $this->actionRender->renderString($sa, []);
},
$builder->add('accepted_socialactions', PickSocialActionType::class, [
'multiple' => true,
'expanded' => true,
]);
}
public function describeAction($data, $format = 'string'): array
{
$socialactions = [];
$actions = [];
foreach ($data['accepted_socialactions'] as $sa) {
$socialactions[] = $this->actionRender->renderString($sa, []);
$socialactions = $data['accepted_socialactions'];
foreach ($socialactions as $action) {
$actions[] = $this->actionRender->renderString($action, [
'show_and_children' => true,
]);
}
return ['Filtered by socialactions: only %socialactions%', [
'%socialactions%' => implode(', ou ', $socialactions),
'%socialactions%' => implode(', ', $actions),
]];
}

View File

@@ -15,10 +15,9 @@ 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;
use Chill\PersonBundle\Templating\Entity\SocialIssueRender;
use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use function in_array;
@@ -55,20 +54,13 @@ class SocialIssueFilter implements FilterInterface
$qb->join('acp.socialIssues', 'acpsocialissue');
}
$where = $qb->getDQLPart('where');
$clause = $qb->expr()->in('acpsocialissue.id', ':socialissues');
if ($where instanceof Andx) {
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->setParameter(
'socialissues',
$this->addParentIssues($data['accepted_socialissues'])
);
$qb->andWhere($clause)
->setParameter(
'socialissues',
SocialIssue::getDescendantsWithThisForIssues($data['accepted_socialissues'])
);
}
public function applyOn()
@@ -78,74 +70,31 @@ class SocialIssueFilter implements FilterInterface
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('accepted_socialissues', EntityType::class, [
'class' => SocialIssue::class,
'choice_label' => function ($socialIssue) {
return $this->socialIssueRender->renderString($socialIssue, []);
},
$builder->add('accepted_socialissues', PickSocialIssueType::class, [
'multiple' => true,
'expanded' => true,
]);
}
public function describeAction($data, $format = 'string')
public function describeAction($data, $format = 'string'): array
{
$issues = [];
$socialissues = $this->addParentIssues($data['accepted_socialissues']);
$socialissues = $data['accepted_socialissues'];
foreach ($socialissues as $i) {
if ('null' === $i) {
$issues[] = $this->translator->trans('Not given');
} else {
$issues[] = $this->socialIssueRender->renderString($i, []);
}
foreach ($socialissues as $issue) {
$issues[] = $this->socialIssueRender->renderString($issue, [
'show_and_children' => true,
]);
}
return [
'Filtered by socialissues: only %socialissues%', [
'%socialissues%' => implode(', ou ', $issues),
'%socialissues%' => implode(', ', $issues),
], ];
}
public function getTitle()
public function getTitle(): string
{
return 'Filter by social issue';
}
/**
* "Le filtre retiendra les parcours qui comportent cette problématique,
* ou une problématique parente à celles choisies.".
*
* Add parent of each socialissue selected, and remove duplicates
*
* @param $accepted_issues
*/
private function addParentIssues($accepted_issues): array
{
$array = [];
foreach ($accepted_issues as $i) {
/** @var SocialIssue $i */
if ($i->hasParent()) {
$array[] = $i->getParent();
}
$array[] = $i;
}
return $this->removeDuplicate($array);
}
private function removeDuplicate(array $array): array
{
$ids = array_map(static function ($item) {
return $item->getId();
}, $array);
$unique_ids = array_unique($ids);
return array_values(
array_intersect_key($array, $unique_ids)
);
}
}

View File

@@ -0,0 +1,130 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Entity\UserJob;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Repository\UserJobRepositoryInterface;
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;
use Symfony\Component\Security\Core\Security;
class UserJobFilter implements FilterInterface
{
private const A = 'acp_ujob_filter_uhistory';
private const AU = 'acp_ujob_filter_uhistory_user';
private const P = 'acp_ujob_filter_date';
private const PJ = 'acp_ujob_filter_job';
private Security $security;
private TranslatableStringHelper $translatableStringHelper;
private UserJobRepositoryInterface $userJobRepository;
public function __construct(
Security $security,
TranslatableStringHelper $translatableStringHelper,
UserJobRepositoryInterface $userJobRepository
) {
$this->security = $security;
$this->translatableStringHelper = $translatableStringHelper;
$this->userJobRepository = $userJobRepository;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$qb
->join('acp.userHistories', self::A)
->andWhere(
$qb->expr()->andX(
$qb->expr()->lte(self::A . '.startDate', ':' . self::P),
$qb->expr()->orX(
$qb->expr()->isNull(self::A . '.endDate'),
$qb->expr()->gt(self::A . '.endDate', ':' . self::P)
)
)
)
->setParameter(self::P, $data['date_calc'])
->join(self::A . '.user', self::AU)
->andWhere(
$qb->expr()->in(self::AU . '.userJob', ':' . self::PJ)
)
->setParameter(self::PJ, $data['jobs']);
}
public function applyOn()
{
return Declarations::ACP_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder
->add('jobs', EntityType::class, [
'class' => UserJob::class,
'choices' => $this->userJobRepository->findAllActive(),
'multiple' => true,
'expanded' => true,
'choice_label' => fn (UserJob $job) => $this->translatableStringHelper->localize($job->getLabel()),
'label' => 'Job',
])
->add('date_calc', ChillDateType::class, [
'input' => 'datetime_immutable',
'data' => new DateTimeImmutable('now'),
'label' => 'export.filter.course.by_user_scope.Computation date for referrer',
'required' => true,
]);
}
public function describeAction($data, $format = 'string')
{
return [
'Filtered by user job: only %job%', [
'%job%' => implode(
', ',
array_map(
fn (UserJob $job) => $this->translatableStringHelper->localize($job->getLabel()),
$data['jobs']->toArray()
)
),
],
];
}
public function getTitle()
{
return 'Filter by user job';
}
private function getUserJob(): UserJob
{
/** @var User $user */
$user = $this->security->getUser();
return $user->getUserJob();
}
}

View File

@@ -0,0 +1,129 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters;
use Chill\MainBundle\Entity\Scope;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Repository\ScopeRepositoryInterface;
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;
use Symfony\Component\Security\Core\Security;
class UserScopeFilter implements FilterInterface
{
private const A = 'acp_uscope_filter_uhistory';
private const AU = 'acp_uscope_filter_uhistory_user';
private const P = 'acp_uscope_filter_date';
private const PS = 'acp_uscope_filter_scopes';
private ScopeRepositoryInterface $scopeRepository;
private Security $security;
private TranslatableStringHelper $translatableStringHelper;
public function __construct(
ScopeRepositoryInterface $scopeRepository,
Security $security,
TranslatableStringHelper $translatableStringHelper
) {
$this->scopeRepository = $scopeRepository;
$this->security = $security;
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$qb
->join('acp.userHistories', self::A)
->andWhere(
$qb->expr()->andX(
$qb->expr()->lte(self::A . '.startDate', ':' . self::P),
$qb->expr()->orX(
$qb->expr()->isNull(self::A . '.endDate'),
$qb->expr()->gt(self::A . '.endDate', ':' . self::P)
)
)
)
->setParameter(self::P, $data['date_calc'])
->join(self::A . '.user', self::AU)
->andWhere(
$qb->expr()->in(self::AU . '.mainScope', ':' . self::PS)
)
->setParameter(self::PS, $data['scopes']);
}
public function applyOn()
{
return Declarations::ACP_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder
->add('scopes', EntityType::class, [
'class' => Scope::class,
'choices' => $this->scopeRepository->findAllActive(),
'choice_label' => fn (Scope $s) => $this->translatableStringHelper->localize($s->getName()),
'multiple' => true,
'expanded' => true,
])
->add('date_calc', ChillDateType::class, [
'input' => 'datetime_immutable',
'data' => new DateTimeImmutable('now'),
'label' => 'export.filter.course.by_user_scope.Computation date for referrer',
'required' => true,
]);
}
public function describeAction($data, $format = 'string')
{
return [
'Filtered by user main scope: only %scope%', [
'%scope%' => implode(
', ',
array_map(
fn (Scope $s) => $this->translatableStringHelper->localize($s->getName()),
$data['scopes']->toArray()
)
),
],
];
}
public function getTitle()
{
return 'Filter by user scope';
}
private function getUserMainScope(): Scope
{
/** @var User $user */
$user = $this->security->getUser();
return $user->getMainScope();
}
}

View File

@@ -76,7 +76,7 @@ final class EvaluationTypeFilter implements FilterInterface
}
return ['Filtered by evaluation type: only %evals%', [
'%evals%' => implode(', ou ', $evals),
'%evals%' => implode(', ', $evals),
]];
}

View File

@@ -13,7 +13,6 @@ namespace Chill\PersonBundle\Export\Filter\EvaluationFilters;
use Chill\MainBundle\Export\FilterInterface;
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;
@@ -22,8 +21,8 @@ use Symfony\Contracts\Translation\TranslatorInterface;
class MaxDateFilter implements FilterInterface
{
private const MAXDATE_CHOICES = [
'is specified' => true,
'is not specified' => false,
'maxdate is specified' => true,
'maxdate is not specified' => false,
];
private TranslatorInterface $translator;
@@ -40,21 +39,13 @@ class MaxDateFilter implements FilterInterface
public function alterQuery(QueryBuilder $qb, $data)
{
$where = $qb->getDQLPart('where');
if (true === $data['maxdate']) {
$clause = $qb->expr()->isNotNull('eval.maxDate');
$clause = $qb->expr()->isNotNull('workeval.maxDate');
} else {
$clause = $qb->expr()->isNull('eval.maxDate');
$clause = $qb->expr()->isNull('workeval.maxDate');
}
if ($where instanceof Andx) {
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->andWhere($clause);
}
public function applyOn(): string
@@ -73,19 +64,15 @@ class MaxDateFilter implements FilterInterface
public function describeAction($data, $format = 'string'): array
{
foreach (self::MAXDATE_CHOICES as $k => $v) {
if ($v === $data['maxdate']) {
$choice = $k;
}
}
return ['Filtered by maxdate: only %choice%', [
'%choice%' => $this->translator->trans($choice),
'%choice%' => $this->translator->trans(
$data['maxdate'] ? 'maxdate is specified' : 'maxdate is not specified'
),
]];
}
public function getTitle(): string
{
return 'Filter by maxdate';
return 'Filter evaluations by maxdate mention';
}
}

View File

@@ -95,7 +95,7 @@ class CompositionFilter implements FilterInterface
}
return ['Filtered by composition: only %compositions% on %ondate%', [
'%compositions%' => implode(', ou ', $compositions),
'%compositions%' => implode(', ', $compositions),
'%ondate%' => $data['on_date']->format('d-m-Y'),
]];
}

View File

@@ -35,7 +35,7 @@ class AgeFilter implements ExportElementValidatedInterface, FilterInterface
$where = $qb->getDQLPart('where');
$min = null !== $data['min_age'] ? $data['min_age'] : 0;
$max = null !== $data['max_age'] ? $data['max_age'] : 3000;
$max = null !== $data['max_age'] ? $data['max_age'] : 150;
$calc = $data['date_calc'];
$minDate = $calc->sub(new DateInterval('P' . $max . 'Y'));

View File

@@ -12,12 +12,9 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Export\Filter\PersonFilters;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\PersonBundle\Entity\MaritalStatus;
use Chill\PersonBundle\Export\Declarations;
use DateTime;
use Doctrine\ORM\Query\Expr\Andx;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
class MaritalStatusFilter implements FilterInterface
@@ -37,25 +34,10 @@ class MaritalStatusFilter implements FilterInterface
public function alterQuery(\Doctrine\ORM\QueryBuilder $qb, $data)
{
$where = $qb->getDQLPart('where');
$clause = $qb->expr()->andX(
$qb->expr()->in('person.maritalStatus', ':maritalStatus'),
$qb->expr()->orX(
$qb->expr()->eq('person.maritalStatusDate', ':calc_date'),
$qb->expr()->isNull('person.maritalStatusDate')
)
$qb->andWhere(
$qb->expr()->in('person.maritalStatus', ':maritalStatus')
);
if ($where instanceof Andx) {
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->setParameter('maritalStatus', $data['maritalStatus']);
$qb->setParameter('calc_date', $data['calc_date']);
}
public function applyOn()
@@ -75,11 +57,6 @@ class MaritalStatusFilter implements FilterInterface
'multiple' => true,
'expanded' => true,
]);
$builder->add('calc_date', ChillDateType::class, [
'label' => 'Marital status at this time',
'data' => new DateTime(),
]);
}
public function describeAction($data, $format = 'string')

View File

@@ -90,7 +90,7 @@ class JobFilter implements FilterInterface
}
return ['Filtered by treating agent job: only %jobs%', [
'%jobs%' => implode(', ou ', $userjobs),
'%jobs%' => implode(', ', $userjobs),
]];
}

View File

@@ -81,7 +81,7 @@ class ReferrerFilter implements FilterInterface
return [
'Filtered by treating agent: only %agents%', [
'%agents' => implode(', ou ', $users),
'%agents' => implode(', ', $users),
], ];
}

View File

@@ -90,7 +90,7 @@ class ScopeFilter implements FilterInterface
}
return ['Filtered by treating agent scope: only %scopes%', [
'%scopes%' => implode(', ou ', $scopes),
'%scopes%' => implode(', ', $scopes),
]];
}

View File

@@ -16,6 +16,7 @@ use Chill\MainBundle\Form\Type\TranslatableStringFormType;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\PersonBundle\Entity\SocialWork\Evaluation;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\UrlType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
@@ -52,6 +53,14 @@ class EvaluationType extends AbstractType
->add('notificationDelay', DateIntervalType::class, [
'label' => 'evaluation.notificationDelay',
'required' => false,
])
->add('active', ChoiceType::class, [
'label' => 'active',
'choices' => [
'active' => true,
'inactive' => false,
],
'required' => true,
]);
}

View File

@@ -0,0 +1,57 @@
<?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\Form\Type;
use Chill\PersonBundle\Entity\SocialWork\SocialAction;
use Chill\PersonBundle\Repository\SocialWork\SocialActionRepository;
use Chill\PersonBundle\Templating\Entity\SocialActionRender;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolver;
class PickSocialActionType extends AbstractType
{
private SocialActionRender $actionRender;
private SocialActionRepository $actionRepository;
public function __construct(
SocialActionRender $actionRender,
SocialActionRepository $actionRepository
) {
$this->actionRender = $actionRender;
$this->actionRepository = $actionRepository;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver
->setDefaults([
'class' => SocialAction::class,
'choices' => $this->actionRepository->findAllActive(),
'choice_label' => function (SocialAction $sa) {
return $this->actionRender->renderString($sa, []);
},
'placeholder' => 'Pick a social action',
'required' => false,
'attr' => ['class' => 'select2'],
'label' => 'Social actions',
'multiple' => false,
])
->setAllowedTypes('multiple', ['bool']);
}
public function getParent(): string
{
return EntityType::class;
}
}

View File

@@ -0,0 +1,57 @@
<?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\Form\Type;
use Chill\PersonBundle\Entity\SocialWork\SocialIssue;
use Chill\PersonBundle\Repository\SocialWork\SocialIssueRepository;
use Chill\PersonBundle\Templating\Entity\SocialIssueRender;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolver;
class PickSocialIssueType extends AbstractType
{
private SocialIssueRender $issueRender;
private SocialIssueRepository $issueRepository;
public function __construct(
SocialIssueRender $issueRender,
SocialIssueRepository $issueRepository
) {
$this->issueRender = $issueRender;
$this->issueRepository = $issueRepository;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver
->setDefaults([
'class' => SocialIssue::class,
'choices' => $this->issueRepository->findAllActive(),
'choice_label' => function (SocialIssue $si) {
return $this->issueRender->renderString($si, []);
},
'placeholder' => 'Pick a social issue',
'required' => false,
'attr' => ['class' => 'select2'],
'label' => 'Social issues',
'multiple' => false,
])
->setAllowedTypes('multiple', ['bool']);
}
public function getParent(): string
{
return EntityType::class;
}
}

View File

@@ -15,9 +15,8 @@ use Chill\PersonBundle\Entity\Household\Household;
use Chill\PersonBundle\Entity\Household\HouseholdComposition;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\Persistence\ObjectRepository;
class HouseholdCompositionRepository implements ObjectRepository
final class HouseholdCompositionRepository implements HouseholdCompositionRepositoryInterface
{
private EntityRepository $repository;

View File

@@ -0,0 +1,45 @@
<?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\Household;
use Chill\PersonBundle\Entity\Household\Household;
use Chill\PersonBundle\Entity\Household\HouseholdComposition;
use Doctrine\Persistence\ObjectRepository;
interface HouseholdCompositionRepositoryInterface extends ObjectRepository
{
public function countByHousehold(Household $household): int;
public function find($id): ?HouseholdComposition;
/**
* @return array|HouseholdComposition[]
*/
public function findAll(): array;
/**
* @param int $limit
* @param int $offset
*
* @return array|object[]|HouseholdComposition[]
*/
public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array;
/**
* @return array|HouseholdComposition[]|object[]
*/
public function findByHousehold(Household $household, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array;
public function findOneBy(array $criteria): ?HouseholdComposition;
public function getClassName(): string;
}

View File

@@ -14,9 +14,8 @@ namespace Chill\PersonBundle\Repository\Household;
use Chill\PersonBundle\Entity\Household\HouseholdCompositionType;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\Persistence\ObjectRepository;
class HouseholdCompositionTypeRepository implements ObjectRepository
final class HouseholdCompositionTypeRepository implements HouseholdCompositionTypeRepositoryInterface
{
private EntityRepository $repository;

View File

@@ -0,0 +1,42 @@
<?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\Household;
use Chill\PersonBundle\Entity\Household\HouseholdCompositionType;
use Doctrine\Persistence\ObjectRepository;
interface HouseholdCompositionTypeRepositoryInterface extends ObjectRepository
{
public function find($id): ?HouseholdCompositionType;
/**
* @return array|HouseholdCompositionType[]|object[]
*/
public function findAll(): array;
/**
* @return array|HouseholdCompositionType[]
*/
public function findAllActive(): array;
/**
* @param $limit
* @param $offset
*
* @return array|HouseholdCompositionType[]|object[]
*/
public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array;
public function findOneBy(array $criteria): ?HouseholdCompositionType;
public function getClassName(): string;
}

View File

@@ -14,9 +14,8 @@ namespace Chill\PersonBundle\Repository\SocialWork;
use Chill\PersonBundle\Entity\SocialWork\Evaluation;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\Persistence\ObjectRepository;
final class EvaluationRepository implements ObjectRepository
final class EvaluationRepository implements EvaluationRepositoryInterface
{
private EntityRepository $repository;
@@ -38,6 +37,11 @@ final class EvaluationRepository implements ObjectRepository
return $this->repository->findAll();
}
public function findAllActive(): array
{
return $this->findBy(['active' => true]);
}
/**
* @param mixed|null $limit
* @param mixed|null $offset

View File

@@ -0,0 +1,45 @@
<?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\SocialWork;
use Chill\PersonBundle\Entity\SocialWork\Evaluation;
use Doctrine\Persistence\ObjectRepository;
interface EvaluationRepositoryInterface extends ObjectRepository
{
public function find($id, ?int $lockMode = null, ?int $lockVersion = null): ?Evaluation;
/**
* @return array<int, Evaluation>
*/
public function findAll(): array;
/**
* @return array<int, Evaluation>
*/
public function findAllActive(): array;
/**
* @param mixed|null $limit
* @param mixed|null $offset
*
* @return array<int, Evaluation>
*/
public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array;
public function findOneBy(array $criteria, ?array $orderBy = null): ?Evaluation;
/**
* @return class-string
*/
public function getClassName(): string;
}

View File

@@ -63,13 +63,9 @@ final class GoalRepository implements ObjectRepository
}
/**
* @param mixed|null $orderBy
* @param mixed|null $limit
* @param mixed|null $offset
*
* @return Goal[]
*/
public function findBySocialActionWithDescendants(SocialAction $action, $orderBy = null, $limit = null, $offset = null): array
public function findBySocialActionWithDescendants(SocialAction $action, array $orderBy = [], ?int $limit = null, ?int $offset = null): array
{
$qb = $this->buildQueryBySocialActionWithDescendants($action);
$qb->select('g');

View File

@@ -96,13 +96,9 @@ final class ResultRepository implements ObjectRepository
}
/**
* @param mixed|null $orderBy
* @param mixed|null $limit
* @param mixed|null $offset
*
* @return Result[]
*/
public function findBySocialActionWithDescendants(SocialAction $action, $orderBy = null, $limit = null, $offset = null): array
public function findBySocialActionWithDescendants(SocialAction $action, array $orderBy = [], ?int $limit = null, ?int $offset = null): array
{
$qb = $this->buildQueryBySocialActionWithDescendants($action);
$qb->select('r');

View File

@@ -12,6 +12,7 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Repository\SocialWork;
use Chill\PersonBundle\Entity\SocialWork\SocialAction;
use DateTime;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\QueryBuilder;
@@ -44,6 +45,14 @@ final class SocialActionRepository implements ObjectRepository
return $this->repository->findAll();
}
/**
* @return array|SocialAction[]
*/
public function findAllActive(): array
{
return $this->buildQueryWithDesactivatedDateCriteria()->getQuery()->getResult();
}
/**
* @param mixed|null $limit
* @param mixed|null $offset
@@ -67,4 +76,16 @@ final class SocialActionRepository implements ObjectRepository
{
return SocialAction::class;
}
private function buildQueryWithDesactivatedDateCriteria(): QueryBuilder
{
$qb = $this->repository->createQueryBuilder('sa');
$qb->where('sa.desactivationDate is null')
->orWhere('sa.desactivationDate > :now')
->orderBy('sa.ordering', 'ASC')
->setParameter('now', new DateTime('now'));
return $qb;
}
}

View File

@@ -12,8 +12,10 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Repository\SocialWork;
use Chill\PersonBundle\Entity\SocialWork\SocialIssue;
use DateTime;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\QueryBuilder;
use Doctrine\Persistence\ObjectRepository;
final class SocialIssueRepository implements ObjectRepository
@@ -38,6 +40,14 @@ final class SocialIssueRepository implements ObjectRepository
return $this->repository->findAll();
}
/**
* @return array|SocialIssue[]
*/
public function findAllActive(): array
{
return $this->buildQueryWithDesactivatedDateCriteria()->getQuery()->getResult();
}
/**
* @param mixed|null $limit
* @param mixed|null $offset
@@ -61,4 +71,16 @@ final class SocialIssueRepository implements ObjectRepository
{
return SocialIssue::class;
}
private function buildQueryWithDesactivatedDateCriteria(): QueryBuilder
{
$qb = $this->repository->createQueryBuilder('si');
$qb->where('si.desactivationDate is null')
->orWhere('si.desactivationDate > :now')
->orderBy('si.ordering', 'ASC')
->setParameter('now', new DateTime('now'));
return $qb;
}
}

View File

@@ -15,16 +15,20 @@ use Chill\MainBundle\Templating\Entity\ChillEntityRenderInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\PersonBundle\Entity\SocialWork\SocialAction;
use Symfony\Component\Templating\EngineInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use function array_merge;
use function array_reverse;
use function implode;
class SocialActionRender implements ChillEntityRenderInterface
{
public const AND_CHILDREN_MENTION = 'show_and_children_mention';
public const DEFAULT_ARGS = [
self::SEPARATOR_KEY => ' > ',
self::NO_BADGE => false,
self::SHOW_AND_CHILDREN => false,
self::AND_CHILDREN_MENTION => 'social_action.and children',
];
/**
@@ -34,14 +38,26 @@ class SocialActionRender implements ChillEntityRenderInterface
public const SEPARATOR_KEY = 'default.separator';
/**
* Show a mention "and children" on each SocialAction, if the social action
* has at least one child.
*/
public const SHOW_AND_CHILDREN = 'show_and_children';
private EngineInterface $engine;
private TranslatableStringHelper $translatableStringHelper;
public function __construct(TranslatableStringHelper $translatableStringHelper, EngineInterface $engine)
{
private TranslatorInterface $translator;
public function __construct(
TranslatableStringHelper $translatableStringHelper,
EngineInterface $engine,
TranslatorInterface $translator
) {
$this->translatableStringHelper = $translatableStringHelper;
$this->engine = $engine;
$this->translator = $translator;
}
public function renderBox($socialAction, array $options): string
@@ -72,7 +88,13 @@ class SocialActionRender implements ChillEntityRenderInterface
$titles = array_reverse($titles);
return implode($options[self::SEPARATOR_KEY], $titles);
$title = implode($options[self::SEPARATOR_KEY], $titles);
if ($options[self::SHOW_AND_CHILDREN] && $socialAction->hasChildren()) {
$title .= ' (' . $this->translator->trans($options[self::AND_CHILDREN_MENTION]) . ')';
}
return $title;
}
public function supports($entity, array $options): bool

View File

@@ -15,26 +15,42 @@ use Chill\MainBundle\Templating\Entity\ChillEntityRenderInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\PersonBundle\Entity\SocialWork\SocialIssue;
use Symfony\Component\Templating\EngineInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use function array_reverse;
use function implode;
final class SocialIssueRender implements ChillEntityRenderInterface
{
public const AND_CHILDREN_MENTION = 'show_and_children_mention';
public const DEFAULT_ARGS = [
self::SEPARATOR_KEY => ' > ',
self::SHOW_AND_CHILDREN => false,
self::AND_CHILDREN_MENTION => 'social_issue.and children',
];
public const SEPARATOR_KEY = 'default.separator';
/**
* Show a mention "and children" on each SocialIssue, if the social issue
* has at least one child.
*/
public const SHOW_AND_CHILDREN = 'show_and_children';
private EngineInterface $engine;
private TranslatableStringHelper $translatableStringHelper;
public function __construct(TranslatableStringHelper $translatableStringHelper, EngineInterface $engine)
{
private TranslatorInterface $translator;
public function __construct(
TranslatableStringHelper $translatableStringHelper,
EngineInterface $engine,
TranslatorInterface $translator
) {
$this->translatableStringHelper = $translatableStringHelper;
$this->engine = $engine;
$this->translator = $translator;
}
/**
@@ -78,7 +94,13 @@ final class SocialIssueRender implements ChillEntityRenderInterface
$titles = array_reverse($titles);
return implode($options[self::SEPARATOR_KEY], $titles);
$title = implode($options[self::SEPARATOR_KEY], $titles);
if ($options[self::SHOW_AND_CHILDREN] && $socialIssue->hasChildren()) {
$title .= ' (' . $this->translator->trans($options[self::AND_CHILDREN_MENTION]) . ')';
}
return $title;
}
public function supports($entity, array $options): bool

View File

@@ -0,0 +1,86 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Tests\Controller;
use Chill\MainBundle\Test\PrepareClientTrait;
use Chill\PersonBundle\Entity\SocialWork\Evaluation;
use Chill\PersonBundle\Entity\SocialWork\SocialAction;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
/**
* @internal
* @coversNothing
*/
final class SocialWorkEvaluationApiControllerTest extends WebTestCase
{
use PrepareClientTrait;
private EntityManagerInterface $em;
private ?Evaluation $evaluationToReset = null;
protected function tearDown(): void
{
if (null === $this->evaluationToReset) {
return;
}
self::bootKernel();
$em = self::$container->get(EntityManagerInterface::class);
$evaluation = $em->find(Evaluation::class, $this->evaluationToReset->getId());
$evaluation->setActive(true);
$em->flush();
}
public function dataGenerateSocialActionWithEvaluations(): iterable
{
self::bootKernel();
$this->em = self::$container->get(EntityManagerInterface::class);
/** @var SocialAction $socialAction */
$socialAction = $this->em->createQuery(
'SELECT s FROM ' . SocialAction::class . ' s WHERE SIZE(s.evaluations) >= 2'
)
->setMaxResults(1)
->getSingleResult();
// set the first evaluation as inactive and save
$this->evaluationToReset = $socialAction->getEvaluations()->first();
$this->evaluationToReset->setActive(false);
$this->em->flush();
yield [$socialAction, $this->evaluationToReset];
}
/**
* @dataProvider dataGenerateSocialActionWithEvaluations
*/
public function testListEvaluationBySocialAction(SocialAction $action, Evaluation $inactiveEvaluation): void
{
$client = $this->getClientAuthenticated();
$client->request('GET', sprintf('/api/1.0/person/social-work/evaluation/by-social-action/%d.json', $action->getId()));
$this->assertResponseIsSuccessful();
$content = json_decode($client->getResponse()->getContent(), true);
$ids = array_map(static fn (array $item) => $item['id'], $content['results']);
$this->assertNotContains($inactiveEvaluation->getId(), $ids);
}
}

View File

@@ -29,6 +29,38 @@ use function count;
*/
final class AccompanyingPeriodTest extends \PHPUnit\Framework\TestCase
{
public function testChangeStepKeepHistory()
{
$period = new AccompanyingPeriod();
$this->assertCount(0, $period->getStepHistories(), 'at initialization, period should not have any step history');
$period->setStep(AccompanyingPeriod::STEP_DRAFT);
$this->assertCount(0, $period->getStepHistories(), 're applying a draft should not create a history');
$period->setStep(AccompanyingPeriod::STEP_CONFIRMED);
$this->assertCount(1, $period->getStepHistories());
$this->assertEquals(AccompanyingPeriod::STEP_CONFIRMED, $period->getStepHistories()->first()->getStep());
$period->setOpeningDate($aMonthAgo = new DateTime('1 month ago'));
$this->assertCount(1, $period->getStepHistories());
$this->assertEquals($aMonthAgo, $period->getStepHistories()->first()->getStartDate(), 'when changing the opening date, the start date of the first history should change');
$period->setOpeningDate($tenDaysAgo = new DateTime('10 days ago'));
$this->assertCount(1, $period->getStepHistories());
$this->assertEquals($tenDaysAgo, $period->getStepHistories()->first()->getStartDate(), 'when changing the opening date, the start date of the first history should change');
$period->setStep(AccompanyingPeriod::STEP_CLOSED);
$this->assertCount(2, $period->getStepHistories());
$period->setOpeningDate($tomorrow = new DateTime('tomorrow'));
$this->assertEquals($tenDaysAgo, $period->getStepHistories()->first()->getStartDate(), 'when changing the opening date to a later one and no history after, start date should change');
}
public function testClosingEqualOpening()
{
$datetime = new DateTime('now');

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\Tests\Entity\SocialWork;
use Chill\PersonBundle\Entity\SocialWork\SocialAction;
use PHPUnit\Framework\TestCase;
/**
* @internal
* @coversNothing
*/
final class SocialActionTest extends TestCase
{
public function testGetDescendantsWithThisForActions()
{
$parentA = new SocialAction();
$childA = (new SocialAction())->setParent($parentA);
$grandChildA = (new SocialAction())->setParent($childA);
$grandGrandChildA = (new SocialAction())->setParent($grandChildA);
$unrelatedA = new SocialAction();
$parentB = new SocialAction();
$childB = (new SocialAction())->setParent($parentB);
$grandChildB = (new SocialAction())->setParent($childB);
$grandGrandChildB = (new SocialAction())->setParent($grandChildB);
$unrelatedB = new SocialAction();
$actual = SocialAction::getDescendantsWithThisForActions([$parentA, $parentB]);
$this->assertContains($parentA, $actual);
$this->assertContains($parentB, $actual);
$this->assertContains($childA, $actual);
$this->assertContains($childB, $actual);
$this->assertContains($grandChildA, $actual);
$this->assertContains($grandChildB, $actual);
$this->assertContains($grandGrandChildA, $actual);
$this->assertContains($grandGrandChildB, $actual);
$this->assertCount(8, $actual);
$this->assertNotContains($unrelatedA, $actual);
$this->assertNotContains($unrelatedB, $actual);
}
}

View File

@@ -55,6 +55,35 @@ final class SocialIssueTest extends TestCase
$this->assertCount(0, $unrelated->getAncestors(false));
}
public function testGetDescendantsWithThisForIssues()
{
$parentA = new SocialIssue();
$childA = (new SocialIssue())->setParent($parentA);
$grandChildA = (new SocialIssue())->setParent($childA);
$grandGrandChildA = (new SocialIssue())->setParent($grandChildA);
$unrelatedA = new SocialIssue();
$parentB = new SocialIssue();
$childB = (new SocialIssue())->setParent($parentB);
$grandChildB = (new SocialIssue())->setParent($childB);
$grandGrandChildB = (new SocialIssue())->setParent($grandChildB);
$unrelatedB = new SocialIssue();
$actual = SocialIssue::getDescendantsWithThisForIssues([$parentA, $parentB]);
$this->assertContains($parentA, $actual);
$this->assertContains($parentB, $actual);
$this->assertContains($childA, $actual);
$this->assertContains($childB, $actual);
$this->assertContains($grandChildA, $actual);
$this->assertContains($grandChildB, $actual);
$this->assertContains($grandGrandChildA, $actual);
$this->assertContains($grandGrandChildB, $actual);
$this->assertCount(8, $actual);
$this->assertNotContains($unrelatedA, $actual);
$this->assertNotContains($unrelatedB, $actual);
}
public function testIsDescendantOf()
{
$parent = new SocialIssue();

View File

@@ -39,7 +39,9 @@ final class DurationAggregatorTest extends AbstractAggregatorTest
public function getFormData(): array
{
return [
[],
['precision' => 'day'],
['precision' => 'week'],
['precision' => 'month'],
];
}

View File

@@ -14,6 +14,7 @@ namespace Chill\PersonBundle\Tests\Export\Aggregator\AccompanyingCourseAggregato
use Chill\MainBundle\Test\Export\AbstractAggregatorTest;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\GeographicalUnitStatAggregator;
use DateTimeImmutable;
use Doctrine\ORM\EntityManagerInterface;
/**
@@ -38,8 +39,9 @@ final class GeographicalUnitStatAggregatorTest extends AbstractAggregatorTest
public function getFormData(): array
{
// TODO: add geographical unit stat into fixtures and provide a level
return [
[],
['date_calc' => new DateTimeImmutable('today'), 'level' => null],
];
}

View File

@@ -14,6 +14,7 @@ namespace Chill\PersonBundle\Tests\Export\Aggregator\AccompanyingCourseAggregato
use Chill\MainBundle\Test\Export\AbstractAggregatorTest;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\ReferrerAggregator;
use DateTimeImmutable;
use Doctrine\ORM\EntityManagerInterface;
/**
@@ -39,7 +40,7 @@ final class ReferrerAggregatorTest extends AbstractAggregatorTest
public function getFormData(): array
{
return [
[],
['date_calc' => new DateTimeImmutable('now')],
];
}

View File

@@ -13,6 +13,7 @@ namespace Export\Export;
use Chill\MainBundle\Test\Export\AbstractExportTest;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Export\Export\CountSocialWorkActions;
/**
* @internal

View File

@@ -12,7 +12,7 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Tests\Export\Filter\AccompanyingCourseFilters;
use Chill\MainBundle\Test\Export\AbstractFilterTest;
use Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\CurrentUserJobFilter;
use Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\UserJobFilter;
use Doctrine\ORM\EntityManagerInterface;
/**
@@ -21,7 +21,7 @@ use Doctrine\ORM\EntityManagerInterface;
*/
final class CurrentUserJobFilterTest extends AbstractFilterTest
{
private CurrentUserJobFilter $filter;
private UserJobFilter $filter;
protected function setUp(): void
{

View File

@@ -12,7 +12,7 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Tests\Export\Filter\AccompanyingCourseFilters;
use Chill\MainBundle\Test\Export\AbstractFilterTest;
use Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\CurrentUserScopeFilter;
use Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\UserScopeFilter;
use Doctrine\ORM\EntityManagerInterface;
/**
@@ -21,7 +21,7 @@ use Doctrine\ORM\EntityManagerInterface;
*/
final class CurrentUserScopeFilterTest extends AbstractFilterTest
{
private CurrentUserScopeFilter $filter;
private UserScopeFilter $filter;
protected function setUp(): void
{

View File

@@ -1,264 +1,193 @@
services:
_defaults:
autowire: true
autoconfigure: true
## Indicators
chill.person.export.count_accompanyingcourse:
class: Chill\PersonBundle\Export\Export\CountAccompanyingCourse
autowire: true
autoconfigure: true
tags:
- { name: chill.export, alias: count_accompanyingcourse }
chill.person.export.avg_accompanyingcourse_duration:
class: Chill\PersonBundle\Export\Export\StatAccompanyingCourseDuration
autowire: true
autoconfigure: true
tags:
- { name: chill.export, alias: avg_accompanyingcourse_duration }
## Filters
chill.person.export.filter_current_userscope:
class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\CurrentUserScopeFilter
autowire: true
autoconfigure: true
Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\UserScopeFilter:
tags:
- { name: chill.export_filter, alias: accompanyingcourse_current_userscope_filter }
- { name: chill.export_filter, alias: accompanyingcourse_userscope_filter }
chill.person.export.filter_current_userjob:
class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\CurrentUserJobFilter
autowire: true
autoconfigure: true
Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\UserJobFilter:
tags:
- { name: chill.export_filter, alias: accompanyingcourse_current_userjob_filter }
- { name: chill.export_filter, alias: accompanyingcourse_userjob_filter }
chill.person.export.filter_socialissue:
class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\SocialIssueFilter
autowire: true
autoconfigure: true
tags:
- { name: chill.export_filter, alias: accompanyingcourse_socialissue_filter }
chill.person.export.filter_step:
class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\StepFilter
autowire: true
autoconfigure: true
tags:
- { name: chill.export_filter, alias: accompanyingcourse_step_filter }
chill.person.export.filter_geographicalunitstat:
class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\GeographicalUnitStatFilter
autowire: true
autoconfigure: true
tags:
- { name: chill.export_filter, alias: accompanyingcourse_geographicalunitstat_filter }
chill.person.export.filter_socialaction:
class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\SocialActionFilter
autowire: true
autoconfigure: true
tags:
- { name: chill.export_filter, alias: accompanyingcourse_socialaction_filter }
chill.person.export.filter_evaluation:
class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\EvaluationFilter
autowire: true
autoconfigure: true
tags:
- { name: chill.export_filter, alias: accompanyingcourse_evaluation_filter }
chill.person.export.filter_origin:
class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\OriginFilter
autowire: true
autoconfigure: true
tags:
- { name: chill.export_filter, alias: accompanyingcourse_origin_filter }
chill.person.export.filter_closingmotive:
class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\ClosingMotiveFilter
autowire: true
autoconfigure: true
tags:
- { name: chill.export_filter, alias: accompanyingcourse_closingmotive_filter }
chill.person.export.filter_administrative_location:
class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\AdministrativeLocationFilter
autowire: true
autoconfigure: true
tags:
- { name: chill.export_filter, alias: accompanyingcourse_administrative_location_filter }
chill.person.export.filter_requestor:
class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\RequestorFilter
autowire: true
autoconfigure: true
tags:
- { name: chill.export_filter, alias: accompanyingcourse_requestor_filter }
chill.person.export.filter_confidential:
class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\ConfidentialFilter
autowire: true
autoconfigure: true
tags:
- { name: chill.export_filter, alias: accompanyingcourse_confidential_filter }
chill.person.export.filter_emergency:
class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\EmergencyFilter
autowire: true
autoconfigure: true
tags:
- { name: chill.export_filter, alias: accompanyingcourse_emergency_filter }
chill.person.export.filter_intensity:
class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\IntensityFilter
autowire: true
autoconfigure: true
tags:
- { name: chill.export_filter, alias: accompanyingcourse_intensity_filter }
chill.person.export.filter_activeondate:
class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\ActiveOnDateFilter
autowire: true
autoconfigure: true
tags:
- { name: chill.export_filter, alias: accompanyingcourse_activeondate_filter }
chill.person.export.filter_activeonedaybetweendates:
class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\ActiveOneDayBetweenDatesFilter
autowire: true
autoconfigure: true
tags:
- { name: chill.export_filter, alias: accompanyingcourse_activeonedaybetweendates_filter }
chill.person.export.filter_referrer:
class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\ReferrerFilter
autowire: true
autoconfigure: true
tags:
- { name: chill.export_filter, alias: accompanyingcourse_referrer_filter }
chill.person.export.filter_openbetweendates:
class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\OpenBetweenDatesFilter
autowire: true
autoconfigure: true
tags:
- { name: chill.export_filter, alias: accompanyingcourse_openbetweendates_filter }
## Aggregators
chill.person.export.aggregator_referrer_scope:
class: Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\ScopeAggregator
autowire: true
autoconfigure: true
tags:
- { name: chill.export_aggregator, alias: accompanyingcourse_scope_aggregator }
chill.person.export.aggregator_referrer_job:
class: Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\JobAggregator
autowire: true
autoconfigure: true
tags:
- { name: chill.export_aggregator, alias: accompanyingcourse_referrer_job_aggregator }
chill.person.export.aggregator_socialissue:
class: Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\SocialIssueAggregator
autowire: true
autoconfigure: true
tags:
- { name: chill.export_aggregator, alias: accompanyingcourse_socialissue_aggregator }
chill.person.export.aggregator_step:
class: Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\StepAggregator
autowire: true
autoconfigure: true
tags:
- { name: chill.export_aggregator, alias: accompanyingcourse_step_aggregator }
chill.person.export.aggregator_geographicalunitstat:
class: Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\GeographicalUnitStatAggregator
autowire: true
autoconfigure: true
tags:
- { name: chill.export_aggregator, alias: accompanyingcourse_geographicalunitstat_aggregator }
chill.person.export.aggregator_socialaction:
class: Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\SocialActionAggregator
autowire: true
autoconfigure: true
tags:
- { name: chill.export_aggregator, alias: accompanyingcourse_socialaction_aggregator }
chill.person.export.aggregator_evaluation:
class: Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\EvaluationAggregator
autowire: true
autoconfigure: true
tags:
- { name: chill.export_aggregator, alias: accompanyingcourse_evaluation_aggregator }
chill.person.export.aggregator_origin:
class: Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\OriginAggregator
autowire: true
autoconfigure: true
tags:
- { name: chill.export_aggregator, alias: accompanyingcourse_origin_aggregator }
chill.person.export.aggregator_closingmotive:
class: Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\ClosingMotiveAggregator
autowire: true
autoconfigure: true
tags:
- { name: chill.export_aggregator, alias: accompanyingcourse_closingmotive_aggregator }
chill.person.export.aggregator_administrative_location:
class: Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\AdministrativeLocationAggregator
autowire: true
autoconfigure: true
tags:
- { name: chill.export_aggregator, alias: accompanyingcourse_administrative_location_aggregator }
chill.person.export.aggregator_requestor:
class: Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\RequestorAggregator
autowire: true
autoconfigure: true
tags:
- { name: chill.export_aggregator, alias: accompanyingcourse_requestor_aggregator }
chill.person.export.aggregator_confidential:
class: Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\ConfidentialAggregator
autowire: true
autoconfigure: true
tags:
- { name: chill.export_aggregator, alias: accompanyingcourse_confidential_aggregator }
chill.person.export.aggregator_emergency:
class: Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\EmergencyAggregator
autowire: true
autoconfigure: true
tags:
- { name: chill.export_aggregator, alias: accompanyingcourse_emergency_aggregator }
chill.person.export.aggregator_intensity:
class: Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\IntensityAggregator
autowire: true
autoconfigure: true
tags:
- { name: chill.export_aggregator, alias: accompanyingcourse_intensity_aggregator }
chill.person.export.aggregator_referrer:
class: Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\ReferrerAggregator
autowire: true
autoconfigure: true
tags:
- { name: chill.export_aggregator, alias: accompanyingcourse_referrer_aggregator }
chill.person.export.aggregator_duration:
class: Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\DurationAggregator
autowire: true
autoconfigure: true
tags:
- { name: chill.export_aggregator, alias: accompanyingcourse_duration_aggregator }
Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\ReferrerScopeAggregator:
autoconfigure: true
autowire: true
tags:
- { name: chill.export_aggregator, alias: accompanyingcourse_ref_scope_aggregator }
Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\ByHouseholdCompositionAggregator:
tags:
- { name: chill.export_aggregator, alias: accompanyingcourse_by_household_compo_aggregator }

View File

@@ -0,0 +1,33 @@
<?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\Migrations\Person;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20221013131221 extends AbstractMigration
{
public function down(Schema $schema): void
{
$this->addSql('ALTER TABLE chill_person_social_work_evaluation DROP active');
}
public function getDescription(): string
{
return 'Add an active column on evaluation';
}
public function up(Schema $schema): void
{
$this->addSql('ALTER TABLE chill_person_social_work_evaluation ADD active BOOLEAN DEFAULT true NOT NULL');
}
}

View File

@@ -0,0 +1,69 @@
<?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\Migrations\Person;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20221014115500 extends AbstractMigration
{
public function down(Schema $schema): void
{
$this->addSql('DROP SEQUENCE chill_person_accompanying_period_step_history_id_seq CASCADE');
$this->addSql('DROP TABLE chill_person_accompanying_period_step_history');
}
public function getDescription(): string
{
return 'Add step history on accompanying periods';
}
public function up(Schema $schema): void
{
$this->addSql('CREATE SEQUENCE chill_person_accompanying_period_step_history_id_seq INCREMENT BY 1 MINVALUE 1 START 1');
$this->addSql('CREATE TABLE chill_person_accompanying_period_step_history (id INT NOT NULL, period_id INT DEFAULT NULL,
endDate DATE DEFAULT NULL, startDate DATE NOT NULL, step TEXT NOT NULL, createdAt TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL,
updatedAt TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, createdBy_id INT DEFAULT NULL,
updatedBy_id INT DEFAULT NULL, PRIMARY KEY(id))
');
$this->addSql('ALTER TABLE chill_person_accompanying_period_step_history ADD CHECK (startDate <= endDate)');
$this->addSql('ALTER TABLE chill_person_accompanying_period_step_history ADD CONSTRAINT ' .
'chill_internal_acp_steps_not_overlaps EXCLUDE USING GIST(
-- extension btree_gist required to include comparaison with integer
period_id WITH =,
daterange(startDate, endDate, \'[)\') WITH &&
)
INITIALLY DEFERRED');
$this->addSql('CREATE INDEX IDX_84D514ACEC8B7ADE ON chill_person_accompanying_period_step_history (period_id)');
$this->addSql('CREATE INDEX IDX_84D514AC3174800F ON chill_person_accompanying_period_step_history (createdBy_id)');
$this->addSql('COMMENT ON COLUMN chill_person_accompanying_period_step_history.endDate IS \'(DC2Type:date_immutable)\'');
$this->addSql('COMMENT ON COLUMN chill_person_accompanying_period_step_history.startDate IS \'(DC2Type:date_immutable)\'');
$this->addSql('COMMENT ON COLUMN chill_person_accompanying_period_step_history.createdAt IS \'(DC2Type:datetime_immutable)\'');
$this->addSql('COMMENT ON COLUMN chill_person_accompanying_period_step_history.updatedAt IS \'(DC2Type:datetime_immutable)\'');
$this->addSql('ALTER TABLE chill_person_accompanying_period_step_history ADD CONSTRAINT FK_84D514ACEC8B7ADE FOREIGN KEY (period_id) REFERENCES chill_person_accompanying_period (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
$this->addSql('ALTER TABLE chill_person_accompanying_period_step_history ADD CONSTRAINT FK_84D514AC3174800F FOREIGN KEY (createdBy_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
$this->addSql('CREATE INDEX IDX_84D514AC65FF1AEC ON chill_person_accompanying_period_step_history (updatedBy_id)');
// fill the tables with current state
$this->addSql(
'INSERT INTO chill_person_accompanying_period_step_history (id, period_id, startDate, endDate, step, createdAt, updatedAt)
SELECT nextval(\'chill_person_accompanying_period_step_history_id_seq\'), id, openingDate, null, step, NOW(), NOW() FROM chill_person_accompanying_period WHERE step = \'CONFIRMED\'
UNION
SELECT nextval(\'chill_person_accompanying_period_step_history_id_seq\'), id, openingDate, closingDate, \'CONFIRMED\', NOW(), NOW() FROM chill_person_accompanying_period WHERE step = \'CLOSED\'
UNION
SELECT nextval(\'chill_person_accompanying_period_step_history_id_seq\'), id, closingDate, null, \'CLOSED\', NOW(), NOW() FROM chill_person_accompanying_period WHERE step = \'CLOSED\'
'
);
}
}

View File

@@ -205,9 +205,11 @@ Resources: Interlocuteurs privilégiés
Any requestor to this accompanying course: Aucun demandeur pour ce parcours
Social action: Action d'accompagnement
Social actions: Actions d'accompagnement
Pick a social action: Choisir une action d'accompagnement
Last social actions: Les dernières actions d'accompagnement
Social issue: Problématique sociale
Social issues: Problématiques sociales
Pick a social issue: Choisir une problématique sociale
Last events on accompanying course: Dernières actions de suivi
Edit & activate accompanying course: Modifier et valider
See accompanying periods: Voir toutes les périodes d'accompagnement
@@ -581,10 +583,10 @@ Accepted evaluationtype: Évaluations
Group by evaluation type: Grouper les évaluations par type
Evaluation type: Type d'évaluation
Filter by maxdate: Filtrer les évaluations par date d'échéance
Filter evaluations by maxdate mention: Filtrer les évaluations qui possèdent une date d'échéance
Maxdate: ''
is specified: La date d'échéance est spécifiée
is not specified: La date d'échéance n'est pas spécifiée
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
@@ -951,7 +953,30 @@ notification:
export:
aggregator:
course:
by_referrer:
Computation date for referrer: Date à laquelle le référent était actif
by_user_scope:
Group course by referrer's scope: Grouper les parcours par service du référent
Computation date for referrer: Date à laquelle le référent était actif
Referrer's scope: Service du référent de parcours
duration:
day: Durée du parcours en jours
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
filter:
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
social_action:
and children: et dérivés
social_issue:
and children: et dérivés