mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-08-20 22:53:49 +00:00
Merge branch '111_exports_suite' into 641_issues_with_children
This commit is contained in:
@@ -62,6 +62,7 @@ class UserRefEventSubscriber implements EventSubscriberInterface
|
||||
&& $period->getUser() !== $this->security->getUser()
|
||||
&& null !== $period->getUser()
|
||||
&& $period->getStep() !== AccompanyingPeriod::STEP_DRAFT
|
||||
&& !$period->isPreventUserIsChangedNotification()
|
||||
) {
|
||||
$this->generateNotificationToUser($period);
|
||||
}
|
||||
|
@@ -11,7 +11,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\PersonBundle\Controller;
|
||||
|
||||
use Chill\MainBundle\Entity\PostalCode;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Form\Type\PickPostalCodeType;
|
||||
use Chill\MainBundle\Form\Type\PickUserDynamicType;
|
||||
use Chill\MainBundle\Pagination\PaginatorFactory;
|
||||
use Chill\MainBundle\Repository\UserRepository;
|
||||
@@ -92,12 +94,14 @@ class ReassignAccompanyingPeriodController extends AbstractController
|
||||
$form->handleRequest($request);
|
||||
|
||||
$userFrom = $form['user']->getData();
|
||||
$postalCodes = $form['postal_code']->getData() instanceof PostalCode ? [$form['postal_code']->getData()] : [];
|
||||
|
||||
$total = $this->accompanyingPeriodACLAwareRepository->countByUserOpenedAccompanyingPeriod($userFrom);
|
||||
$paginator = $this->paginatorFactory->create($total);
|
||||
$periods = $this->accompanyingPeriodACLAwareRepository
|
||||
->findByUserOpenedAccompanyingPeriod(
|
||||
->findByUserAndPostalCodesOpenedAccompanyingPeriod(
|
||||
$userFrom,
|
||||
$postalCodes,
|
||||
['openingDate' => 'ASC'],
|
||||
$paginator->getItemsPerPage(),
|
||||
$paginator->getCurrentPageFirstItemNumber()
|
||||
@@ -123,7 +127,7 @@ class ReassignAccompanyingPeriodController extends AbstractController
|
||||
$period = $this->courseRepository->find($periodId);
|
||||
|
||||
if ($period->getUser() === $userFrom) {
|
||||
$period->setUser($userTo);
|
||||
$period->setUser($userTo, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,7 +152,9 @@ class ReassignAccompanyingPeriodController extends AbstractController
|
||||
{
|
||||
$data = [
|
||||
'user' => null,
|
||||
'postal_code' => null,
|
||||
];
|
||||
|
||||
$builder = $this->formFactory->createBuilder(FormType::class, $data, [
|
||||
'method' => 'get', 'csrf_protection' => false, ]);
|
||||
|
||||
@@ -158,12 +164,17 @@ class ReassignAccompanyingPeriodController extends AbstractController
|
||||
'label' => 'reassign.Current user',
|
||||
'required' => false,
|
||||
'help' => 'reassign.Choose a user and click on "Filter" to apply',
|
||||
])
|
||||
->add('postal_code', PickPostalCodeType::class, [
|
||||
'label' => 'reassign.Filter by postal code',
|
||||
'required' => false,
|
||||
'help' => 'reassign.Filter course which are located inside a postal code',
|
||||
]);
|
||||
|
||||
return $builder->getForm();
|
||||
}
|
||||
|
||||
private function buildReassignForm(array $periodIds, ?User $userFrom): FormInterface
|
||||
private function buildReassignForm(array $periodIds, ?User $userFrom = null): FormInterface
|
||||
{
|
||||
$defaultData = [
|
||||
'userFrom' => $userFrom,
|
||||
|
@@ -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()
|
||||
);
|
||||
|
@@ -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;
|
||||
@@ -265,6 +266,8 @@ class AccompanyingPeriod implements
|
||||
*/
|
||||
private ?Comment $pinnedComment = null;
|
||||
|
||||
private bool $preventUserIsChangedNotification = false;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="text")
|
||||
* @Groups({"read", "write"})
|
||||
@@ -334,6 +337,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})
|
||||
*/
|
||||
@@ -388,7 +397,6 @@ class AccompanyingPeriod implements
|
||||
*/
|
||||
public function __construct(?DateTime $dateOpening = null)
|
||||
{
|
||||
$this->setOpeningDate($dateOpening ?? new DateTime('now'));
|
||||
$this->participations = new ArrayCollection();
|
||||
$this->scopes = new ArrayCollection();
|
||||
$this->socialIssues = new ArrayCollection();
|
||||
@@ -397,6 +405,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'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -964,6 +974,11 @@ class AccompanyingPeriod implements
|
||||
return $this->step;
|
||||
}
|
||||
|
||||
public function getStepHistories(): Collection
|
||||
{
|
||||
return $this->stepHistories;
|
||||
}
|
||||
|
||||
public function getUser(): ?User
|
||||
{
|
||||
return $this->user;
|
||||
@@ -1048,6 +1063,11 @@ class AccompanyingPeriod implements
|
||||
return false;
|
||||
}
|
||||
|
||||
public function isPreventUserIsChangedNotification(): bool
|
||||
{
|
||||
return $this->preventUserIsChangedNotification;
|
||||
}
|
||||
|
||||
public function isRequestorAnonymous(): bool
|
||||
{
|
||||
return $this->requestorAnonymous;
|
||||
@@ -1227,7 +1247,11 @@ class AccompanyingPeriod implements
|
||||
*/
|
||||
public function setOpeningDate($openingDate)
|
||||
{
|
||||
$this->openingDate = $openingDate;
|
||||
if ($this->openingDate !== $openingDate) {
|
||||
$this->openingDate = $openingDate;
|
||||
|
||||
$this->ensureStepContinuity();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
@@ -1326,6 +1350,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;
|
||||
}
|
||||
|
||||
@@ -1343,11 +1375,12 @@ class AccompanyingPeriod implements
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setUser(?User $user): self
|
||||
public function setUser(?User $user, bool $preventNotification = false): self
|
||||
{
|
||||
if ($this->user !== $user) {
|
||||
$this->userPrevious = $this->user;
|
||||
$this->userIsChanged = true;
|
||||
$this->preventUserIsChangedNotification = $preventNotification;
|
||||
|
||||
foreach ($this->userHistories as $history) {
|
||||
if (null === $history->getEndDate()) {
|
||||
@@ -1365,6 +1398,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
|
||||
@@ -1376,6 +1420,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;
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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")
|
||||
|
@@ -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"})
|
||||
@@ -72,6 +77,9 @@ class Evaluation
|
||||
$this->socialActions = new ArrayCollection();
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal do use @see{SocialAction::addEvaluation}
|
||||
*/
|
||||
public function addSocialAction(SocialAction $socialAction): self
|
||||
{
|
||||
if (!$this->socialActions->contains($socialAction)) {
|
||||
@@ -111,6 +119,16 @@ class Evaluation
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
public function isActive(): bool
|
||||
{
|
||||
return $this->active;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*
|
||||
* @internal do use @see{SocialAction::removeEvaluation}
|
||||
*/
|
||||
public function removeSocialAction(SocialAction $socialAction): self
|
||||
{
|
||||
if ($this->socialActions->contains($socialAction)) {
|
||||
@@ -120,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;
|
||||
|
@@ -112,6 +112,7 @@ class SocialAction
|
||||
{
|
||||
if (!$this->evaluations->contains($evaluation)) {
|
||||
$this->evaluations[] = $evaluation;
|
||||
$evaluation->addSocialAction($this);
|
||||
}
|
||||
|
||||
return $this;
|
||||
@@ -332,6 +333,7 @@ class SocialAction
|
||||
public function removeEvaluation(Evaluation $evaluation): self
|
||||
{
|
||||
$this->evaluations->removeElement($evaluation);
|
||||
$evaluation->removeSocialAction($this);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
@@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
|
@@ -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)
|
||||
|
@@ -12,20 +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 LogicException;
|
||||
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(
|
||||
@@ -41,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
|
||||
@@ -95,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;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@@ -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,7 +84,9 @@ 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
|
||||
$qb->addGroupBy('country_of_birth_aggregator');
|
||||
|
@@ -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');
|
||||
|
@@ -50,9 +50,6 @@ class AdministrativeLocationFilter implements FilterInterface
|
||||
{
|
||||
$builder->add('accepted_locations', PickUserLocationType::class, [
|
||||
'label' => 'Accepted locations',
|
||||
'label_attr' => [
|
||||
//'class' => 'd-none'
|
||||
],
|
||||
'multiple' => true,
|
||||
]);
|
||||
}
|
||||
|
@@ -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();
|
||||
}
|
||||
}
|
@@ -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();
|
||||
}
|
||||
}
|
@@ -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'],
|
||||
]);
|
||||
}
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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();
|
||||
}
|
||||
}
|
@@ -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();
|
||||
}
|
||||
}
|
@@ -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'));
|
||||
|
@@ -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')
|
||||
|
@@ -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,
|
||||
]);
|
||||
}
|
||||
|
||||
|
@@ -11,7 +11,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\PersonBundle\Repository;
|
||||
|
||||
use Chill\MainBundle\Entity\Address;
|
||||
use Chill\MainBundle\Entity\Location;
|
||||
use Chill\MainBundle\Entity\PostalCode;
|
||||
use Chill\MainBundle\Entity\Scope;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Entity\UserJob;
|
||||
@@ -19,10 +21,14 @@ use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
||||
use Chill\MainBundle\Security\Resolver\CenterResolverDispatcherInterface;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation;
|
||||
use Chill\PersonBundle\Entity\Household\PersonHouseholdAddress;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
|
||||
use DateTime;
|
||||
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\Query\Expr\Join;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
use function count;
|
||||
@@ -49,7 +55,12 @@ final class AccompanyingPeriodACLAwareRepository implements AccompanyingPeriodAC
|
||||
$this->centerResolverDispatcher = $centerResolverDispatcher;
|
||||
}
|
||||
|
||||
public function buildQueryOpenedAccompanyingCourseByUser(?User $user)
|
||||
/**
|
||||
* @param array|PostalCode[]
|
||||
*
|
||||
* @return QueryBuilder
|
||||
*/
|
||||
public function buildQueryOpenedAccompanyingCourseByUser(?User $user, array $postalCodes = [])
|
||||
{
|
||||
$qb = $this->accompanyingPeriodRepository->createQueryBuilder('ap');
|
||||
|
||||
@@ -65,6 +76,37 @@ final class AccompanyingPeriodACLAwareRepository implements AccompanyingPeriodAC
|
||||
->setParameter('now', new DateTime('now'))
|
||||
->setParameter('draft', AccompanyingPeriod::STEP_DRAFT);
|
||||
|
||||
if ([] !== $postalCodes) {
|
||||
$qb->join('ap.locationHistories', 'location_history')
|
||||
->leftJoin(PersonHouseholdAddress::class, 'person_address', Join::WITH, 'IDENTITY(location_history.personLocation) = IDENTITY(person_address.person)')
|
||||
->join(
|
||||
Address::class,
|
||||
'address',
|
||||
Join::WITH,
|
||||
'COALESCE(IDENTITY(location_history.addressLocation), IDENTITY(person_address.address)) = address.id'
|
||||
)
|
||||
->andWhere(
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->isNull('person_address'),
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->lte('person_address.validFrom', ':now'),
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->isNull('person_address.validTo'),
|
||||
$qb->expr()->lt('person_address.validTo', ':now')
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
->andWhere(
|
||||
$qb->expr()->isNull('location_history.endDate')
|
||||
)
|
||||
->andWhere(
|
||||
$qb->expr()->in('address.postcode', ':postal_codes')
|
||||
)
|
||||
->setParameter('now', new DateTimeImmutable('now'), Types::DATE_IMMUTABLE)
|
||||
->setParameter('postal_codes', $postalCodes);
|
||||
}
|
||||
|
||||
return $qb;
|
||||
}
|
||||
|
||||
@@ -77,6 +119,18 @@ final class AccompanyingPeriodACLAwareRepository implements AccompanyingPeriodAC
|
||||
return $qb->getQuery()->getSingleScalarResult();
|
||||
}
|
||||
|
||||
public function countByUserAndPostalCodesOpenedAccompanyingPeriod(?User $user, array $postalCodes): int
|
||||
{
|
||||
if (null === $user) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return $this->buildQueryOpenedAccompanyingCourseByUser($user, $postalCodes)
|
||||
->select('COUNT(ap)')
|
||||
->getQuery()
|
||||
->getSingleScalarResult();
|
||||
}
|
||||
|
||||
public function countByUserOpenedAccompanyingPeriod(?User $user): int
|
||||
{
|
||||
if (null === $user) {
|
||||
@@ -158,6 +212,24 @@ final class AccompanyingPeriodACLAwareRepository implements AccompanyingPeriodAC
|
||||
return $qb->getQuery()->getResult();
|
||||
}
|
||||
|
||||
public function findByUserAndPostalCodesOpenedAccompanyingPeriod(?User $user, array $postalCodes, array $orderBy = [], int $limit = 0, int $offset = 50): array
|
||||
{
|
||||
if (null === $user) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$qb = $this->buildQueryOpenedAccompanyingCourseByUser($user);
|
||||
|
||||
$qb->setFirstResult($offset)
|
||||
->setMaxResults($limit);
|
||||
|
||||
foreach ($orderBy as $field => $direction) {
|
||||
$qb->addOrderBy('ap.' . $field, $direction);
|
||||
}
|
||||
|
||||
return $qb->getQuery()->getResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array|AccompanyingPeriod[]
|
||||
*/
|
||||
|
@@ -11,6 +11,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\PersonBundle\Repository;
|
||||
|
||||
use Chill\MainBundle\Entity\PostalCode;
|
||||
use Chill\MainBundle\Entity\Scope;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Entity\UserJob;
|
||||
@@ -25,6 +26,11 @@ interface AccompanyingPeriodACLAwareRepositoryInterface
|
||||
*/
|
||||
public function countByUnDispatched(array $jobs, array $services, array $administrativeLocations): int;
|
||||
|
||||
/**
|
||||
* @param array|PostalCode[] $postalCodes
|
||||
*/
|
||||
public function countByUserAndPostalCodesOpenedAccompanyingPeriod(?User $user, array $postalCodes): int;
|
||||
|
||||
public function countByUserOpenedAccompanyingPeriod(?User $user): int;
|
||||
|
||||
public function findByPerson(
|
||||
@@ -43,5 +49,10 @@ interface AccompanyingPeriodACLAwareRepositoryInterface
|
||||
*/
|
||||
public function findByUnDispatched(array $jobs, array $services, array $administrativeLocations, ?int $limit = null, ?int $offset = null): array;
|
||||
|
||||
/**
|
||||
* @param array|PostalCode[] $postalCodes
|
||||
*/
|
||||
public function findByUserAndPostalCodesOpenedAccompanyingPeriod(?User $user, array $postalCodes, array $orderBy = [], int $limit = 0, int $offset = 50): array;
|
||||
|
||||
public function findByUserOpenedAccompanyingPeriod(?User $user, array $orderBy = [], int $limit = 0, int $offset = 50): array;
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
}
|
@@ -13,6 +13,7 @@ namespace Chill\PersonBundle\Repository\SocialWork;
|
||||
|
||||
use Chill\PersonBundle\Entity\SocialWork\Goal;
|
||||
use Chill\PersonBundle\Entity\SocialWork\SocialAction;
|
||||
use DateTime;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
@@ -62,17 +63,21 @@ 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');
|
||||
|
||||
$qb->andWhere(
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->isNull('g.desactivationDate'),
|
||||
$qb->expr()->gt('g.desactivationDate', ':now')
|
||||
)
|
||||
)
|
||||
->setParameter('now', new DateTime('now'));
|
||||
|
||||
foreach ($orderBy as $sort => $order) {
|
||||
$qb->addOrderBy('g.' . $sort, $order);
|
||||
}
|
||||
|
@@ -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');
|
||||
|
@@ -76,7 +76,7 @@
|
||||
<div class="flex-table">
|
||||
{% for period in periods %}
|
||||
{% include '@ChillPerson/AccompanyingPeriod/_list_item.html.twig' with {'period': period,
|
||||
'recordAction': m.period_actions(period), 'itemMeta': m.period_meta(period) } %}
|
||||
'recordAction': m.period_actions(period), 'itemMeta': m.period_meta(period), 'show_address': true } %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
@@ -113,6 +113,16 @@
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if show_address|default(false) and period.location is not null %}
|
||||
<div class="wl-row">
|
||||
<div class="wl-col title"><h3>{{ 'Accompanying course location'|trans }}</h3></div>
|
||||
<div class="wl-col list">
|
||||
<p class="wl-item">
|
||||
{{ period.location|chill_entity_render_string }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
@@ -5,11 +5,13 @@
|
||||
{% block js %}
|
||||
{{ encore_entry_script_tags('mod_set_referrer') }}
|
||||
{{ encore_entry_script_tags('mod_pickentity_type') }}
|
||||
{{ encore_entry_script_tags('mod_pick_postal_code') }}
|
||||
{% endblock %}
|
||||
|
||||
{% block css %}
|
||||
{{ encore_entry_link_tags('mod_set_referrer') }}
|
||||
{{ encore_entry_link_tags('mod_pickentity_type') }}
|
||||
{{ encore_entry_link_tags('mod_pick_postal_code') }}
|
||||
{% endblock %}
|
||||
|
||||
{% macro period_meta(period) %}
|
||||
@@ -48,6 +50,8 @@
|
||||
{{ form_start(form) }}
|
||||
{{ form_label(form.user ) }}
|
||||
{{ form_widget(form.user, {'attr': {'class': 'select2'}}) }}
|
||||
{{ form_label(form.postal_code) }}
|
||||
{{ form_widget(form.postal_code) }}
|
||||
<ul class="record_actions">
|
||||
<li>
|
||||
<button type="submit" class="btn btn-misc">
|
||||
@@ -87,7 +91,7 @@
|
||||
<div class="flex-table">
|
||||
{% for period in periods %}
|
||||
{% include '@ChillPerson/AccompanyingPeriod/_list_item.html.twig' with {'period': period,
|
||||
'recordAction': m.period_actions(period), 'itemMeta': m.period_meta(period) } %}
|
||||
'recordAction': m.period_actions(period), 'itemMeta': m.period_meta(period), 'show_address': true } %}
|
||||
{% else %}
|
||||
{% if userFrom is same as(null) %}
|
||||
<p class="chill-no-data-statement">{{ 'period_by_user_list.Pick a user'|trans }}</p>
|
||||
|
@@ -12,6 +12,7 @@ declare(strict_types=1);
|
||||
namespace Chill\PersonBundle\Service\DocGenerator;
|
||||
|
||||
use Chill\DocGeneratorBundle\Context\DocGeneratorContextWithAdminFormInterface;
|
||||
use Chill\DocGeneratorBundle\Context\DocGeneratorContextWithPublicFormInterface;
|
||||
use Chill\DocGeneratorBundle\Context\Exception\UnexpectedTypeException;
|
||||
use Chill\DocGeneratorBundle\Entity\DocGeneratorTemplate;
|
||||
use Chill\DocGeneratorBundle\Service\Context\BaseContextData;
|
||||
@@ -19,46 +20,72 @@ use Chill\DocStoreBundle\Entity\DocumentCategory;
|
||||
use Chill\DocStoreBundle\Entity\PersonDocument;
|
||||
use Chill\DocStoreBundle\Entity\StoredObject;
|
||||
use Chill\DocStoreBundle\Repository\DocumentCategoryRepository;
|
||||
use Chill\DocStoreBundle\Security\Authorization\PersonDocumentVoter;
|
||||
use Chill\MainBundle\Entity\Scope;
|
||||
use Chill\MainBundle\Form\Type\ScopePickerType;
|
||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface;
|
||||
use Chill\MainBundle\Security\Resolver\CenterResolverManagerInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use DateTime;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use LogicException;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
||||
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
use function array_key_exists;
|
||||
use function count;
|
||||
|
||||
class PersonContext implements DocGeneratorContextWithAdminFormInterface
|
||||
class PersonContext implements DocGeneratorContextWithAdminFormInterface, DocGeneratorContextWithPublicFormInterface
|
||||
{
|
||||
private AuthorizationHelperInterface $authorizationHelper;
|
||||
|
||||
private BaseContextData $baseContextData;
|
||||
|
||||
private CenterResolverManagerInterface $centerResolverManager;
|
||||
|
||||
private DocumentCategoryRepository $documentCategoryRepository;
|
||||
|
||||
private EntityManagerInterface $em;
|
||||
|
||||
private NormalizerInterface $normalizer;
|
||||
|
||||
private Security $security;
|
||||
|
||||
private bool $showScopes;
|
||||
|
||||
private TranslatableStringHelperInterface $translatableStringHelper;
|
||||
|
||||
private TranslatorInterface $translator;
|
||||
|
||||
public function __construct(
|
||||
AuthorizationHelperInterface $authorizationHelper,
|
||||
BaseContextData $baseContextData,
|
||||
CenterResolverManagerInterface $centerResolverManager,
|
||||
DocumentCategoryRepository $documentCategoryRepository,
|
||||
NormalizerInterface $normalizer,
|
||||
TranslatableStringHelperInterface $translatableStringHelper,
|
||||
EntityManagerInterface $em,
|
||||
NormalizerInterface $normalizer,
|
||||
ParameterBagInterface $parameterBag,
|
||||
Security $security,
|
||||
TranslatorInterface $translator,
|
||||
BaseContextData $baseContextData
|
||||
TranslatableStringHelperInterface $translatableStringHelper
|
||||
) {
|
||||
$this->documentCategoryRepository = $documentCategoryRepository;
|
||||
$this->normalizer = $normalizer;
|
||||
$this->translatableStringHelper = $translatableStringHelper;
|
||||
$this->em = $em;
|
||||
$this->authorizationHelper = $authorizationHelper;
|
||||
$this->centerResolverManager = $centerResolverManager;
|
||||
$this->baseContextData = $baseContextData;
|
||||
$this->documentCategoryRepository = $documentCategoryRepository;
|
||||
$this->em = $em;
|
||||
$this->normalizer = $normalizer;
|
||||
$this->security = $security;
|
||||
$this->showScopes = $parameterBag->get('chill_main')['acl']['form_show_scopes'];
|
||||
$this->translator = $translator;
|
||||
$this->translatableStringHelper = $translatableStringHelper;
|
||||
}
|
||||
|
||||
public function adminFormReverseTransform(array $data): array
|
||||
@@ -105,6 +132,18 @@ class PersonContext implements DocGeneratorContextWithAdminFormInterface
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Person $entity
|
||||
*/
|
||||
public function buildPublicForm(FormBuilderInterface $builder, DocGeneratorTemplate $template, $entity): void
|
||||
{
|
||||
$builder->add('scope', ScopePickerType::class, [
|
||||
'center' => $this->centerResolverManager->resolveCenters($entity),
|
||||
'role' => PersonDocumentVoter::CREATE,
|
||||
'label' => 'Scope',
|
||||
]);
|
||||
}
|
||||
|
||||
public function getData(DocGeneratorTemplate $template, $entity, array $contextGenerationData = []): array
|
||||
{
|
||||
if (!$entity instanceof Person) {
|
||||
@@ -156,6 +195,14 @@ class PersonContext implements DocGeneratorContextWithAdminFormInterface
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Person $entity
|
||||
*/
|
||||
public function hasPublicForm(DocGeneratorTemplate $template, $entity): bool
|
||||
{
|
||||
return $this->isScopeNecessary($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Person $entity
|
||||
*/
|
||||
@@ -178,6 +225,36 @@ class PersonContext implements DocGeneratorContextWithAdminFormInterface
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->isScopeNecessary($entity)) {
|
||||
$doc->setScope($contextGenerationData['scope']);
|
||||
} elseif ($this->showScopes) {
|
||||
// in this case, it should have only one scope possible, we get it through AuthorizationHelper::getReachableScopes
|
||||
$scopes = $this->authorizationHelper->getReachableScopes(
|
||||
$this->security->getUser(),
|
||||
PersonDocumentVoter::CREATE,
|
||||
$this->centerResolverManager->resolveCenters($entity)
|
||||
);
|
||||
|
||||
if (1 !== count($scopes)) {
|
||||
throw new LogicException('at this step, it should have only one scope');
|
||||
}
|
||||
|
||||
$doc->setScope($scopes[0]);
|
||||
}
|
||||
|
||||
$this->em->persist($doc);
|
||||
}
|
||||
|
||||
private function isScopeNecessary(Person $person): bool
|
||||
{
|
||||
if ($this->showScopes && 1 < $this->authorizationHelper->getReachableScopes(
|
||||
$this->security->getUser(),
|
||||
PersonDocumentVoter::CREATE,
|
||||
$this->centerResolverManager->resolveCenters($person)
|
||||
)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@@ -193,7 +193,7 @@ final class SocialWorkMetadata implements SocialWorkMetadataInterface
|
||||
/** @var Evaluation $eval */
|
||||
$eval = $this->getOrCreateEntity($this->evaluationRepository, 'title', ['fr' => $evaluationTitle]);
|
||||
$eval->setTitle(['fr' => $evaluationTitle]);
|
||||
$eval->addSocialAction($socialAction);
|
||||
$socialAction->addEvaluation($eval);
|
||||
|
||||
$this->entityManager->persist($eval);
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
@@ -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');
|
||||
|
@@ -39,7 +39,9 @@ final class DurationAggregatorTest extends AbstractAggregatorTest
|
||||
public function getFormData(): array
|
||||
{
|
||||
return [
|
||||
[],
|
||||
['precision' => 'day'],
|
||||
['precision' => 'week'],
|
||||
['precision' => 'month'],
|
||||
];
|
||||
}
|
||||
|
||||
|
@@ -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],
|
||||
];
|
||||
}
|
||||
|
||||
|
@@ -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')],
|
||||
];
|
||||
}
|
||||
|
||||
|
@@ -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
|
||||
{
|
||||
|
@@ -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
|
||||
{
|
||||
|
@@ -0,0 +1,46 @@
|
||||
<?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\Serializer\Normalizer;
|
||||
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\Origin;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @coversNothing
|
||||
*/
|
||||
final class AccompanyingPeriodOriginNormalizerTest extends KernelTestCase
|
||||
{
|
||||
private NormalizerInterface $normalizer;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
self::bootKernel();
|
||||
$this->normalizer = self::$container->get(NormalizerInterface::class);
|
||||
}
|
||||
|
||||
public function testNormalization()
|
||||
{
|
||||
$o = new Origin();
|
||||
|
||||
$normalized = $this->normalizer->normalize(
|
||||
$o,
|
||||
'json',
|
||||
['groups' => ['read']]
|
||||
);
|
||||
|
||||
$this->assertIsArray($normalized);
|
||||
$this->assertArrayHasKey('type', $normalized);
|
||||
$this->assertEquals('origin', $normalized['type']);
|
||||
}
|
||||
}
|
@@ -60,7 +60,7 @@ final class AccompanyingPeriodWorkDocGenNormalizerTest extends KernelTestCase
|
||||
}
|
||||
}
|
||||
|
||||
public function testNormlalize()
|
||||
public function testNormalize()
|
||||
{
|
||||
$work = new AccompanyingPeriodWork();
|
||||
$work
|
||||
|
@@ -0,0 +1,46 @@
|
||||
<?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\Serializer\Normalizer;
|
||||
|
||||
use Chill\PersonBundle\Entity\SocialWork\SocialAction;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @coversNothing
|
||||
*/
|
||||
final class SocialActionNormalizerTest extends KernelTestCase
|
||||
{
|
||||
private NormalizerInterface $normalizer;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
self::bootKernel();
|
||||
$this->normalizer = self::$container->get(NormalizerInterface::class);
|
||||
}
|
||||
|
||||
public function testNormalization()
|
||||
{
|
||||
$sa = new SocialAction();
|
||||
|
||||
$normalized = $this->normalizer->normalize(
|
||||
$sa,
|
||||
'json',
|
||||
['groups' => ['read']]
|
||||
);
|
||||
|
||||
$this->assertIsArray($normalized);
|
||||
$this->assertArrayHasKey('type', $normalized);
|
||||
$this->assertEquals('social_work_social_action', $normalized['type']);
|
||||
}
|
||||
}
|
@@ -0,0 +1,46 @@
|
||||
<?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\Serializer\Normalizer;
|
||||
|
||||
use Chill\PersonBundle\Entity\SocialWork\SocialIssue;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @coversNothing
|
||||
*/
|
||||
final class SocialIssueNormalizerTest extends KernelTestCase
|
||||
{
|
||||
private NormalizerInterface $normalizer;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
self::bootKernel();
|
||||
$this->normalizer = self::$container->get(NormalizerInterface::class);
|
||||
}
|
||||
|
||||
public function testNormalization()
|
||||
{
|
||||
$si = new SocialIssue();
|
||||
|
||||
$normalized = $this->normalizer->normalize(
|
||||
$si,
|
||||
'json',
|
||||
['groups' => ['read']]
|
||||
);
|
||||
|
||||
$this->assertIsArray($normalized);
|
||||
$this->assertArrayHasKey('type', $normalized);
|
||||
$this->assertEquals('social_issue', $normalized['type']);
|
||||
}
|
||||
}
|
@@ -0,0 +1,265 @@
|
||||
<?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 Service\DocGenerator;
|
||||
|
||||
use Chill\DocGeneratorBundle\Entity\DocGeneratorTemplate;
|
||||
use Chill\DocGeneratorBundle\Service\Context\BaseContextData;
|
||||
use Chill\DocStoreBundle\Entity\DocumentCategory;
|
||||
use Chill\DocStoreBundle\Entity\PersonDocument;
|
||||
use Chill\DocStoreBundle\Entity\StoredObject;
|
||||
use Chill\DocStoreBundle\Repository\DocumentCategoryRepository;
|
||||
use Chill\DocStoreBundle\Security\Authorization\PersonDocumentVoter;
|
||||
use Chill\MainBundle\Entity\Center;
|
||||
use Chill\MainBundle\Entity\Scope;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface;
|
||||
use Chill\MainBundle\Security\Resolver\CenterResolverManagerInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\PersonBundle\Service\DocGenerator\PersonContext;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Prophecy\Argument;
|
||||
use Prophecy\Exception\Prediction\FailedPredictionException;
|
||||
use Prophecy\PhpUnit\ProphecyTrait;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
use function count;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @coversNothing
|
||||
*/
|
||||
final class PersonContextTest extends TestCase
|
||||
{
|
||||
use ProphecyTrait;
|
||||
|
||||
/**
|
||||
* Test that the build person context works in the case when 'form_show_scope' is false.
|
||||
*/
|
||||
public function testScopeDoNotShowScopeInForms()
|
||||
{
|
||||
$person = new Person();
|
||||
$docGen = (new DocGeneratorTemplate())
|
||||
->setName(['fr' => 'template']);
|
||||
|
||||
$parameter = new ParameterBag(['chill_main' => ['acl' => ['form_show_scopes' => false]]]);
|
||||
$em = $this->prophesize(EntityManagerInterface::class);
|
||||
$em->persist(Argument::type(PersonDocument::class))
|
||||
->should(static function ($calls, $object, $method) {
|
||||
if (1 !== count($calls)) {
|
||||
throw new FailedPredictionException(sprintf('the persist should be called exactly once, %d receivved', count($calls)));
|
||||
}
|
||||
|
||||
/** @var PersonDocument $personDocument */
|
||||
$personDocument = $calls[0]->getArguments()[0];
|
||||
|
||||
if (null !== $personDocument->getScope()) {
|
||||
throw new FailedPredictionException('the person document should not have any scope');
|
||||
}
|
||||
});
|
||||
|
||||
$personContext = $this->buildPersonContext(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
$em->reveal(),
|
||||
null,
|
||||
$parameter
|
||||
);
|
||||
|
||||
$this->assertFalse($personContext->hasPublicForm($docGen, $person));
|
||||
|
||||
$personContext->storeGenerated(
|
||||
$docGen,
|
||||
new StoredObject(),
|
||||
$person,
|
||||
[]
|
||||
);
|
||||
}
|
||||
|
||||
public function testScopeScopeMustBeShownInFormsAndUserAccessMultipleScope()
|
||||
{
|
||||
$person = new Person();
|
||||
$docGen = (new DocGeneratorTemplate())
|
||||
->setName(['fr' => 'template']);
|
||||
$scope = new Scope();
|
||||
|
||||
$em = $this->prophesize(EntityManagerInterface::class);
|
||||
$em->persist(Argument::type(PersonDocument::class))
|
||||
->should(static function ($calls, $object, $method) use ($scope) {
|
||||
if (1 !== count($calls)) {
|
||||
throw new FailedPredictionException(sprintf('the persist should be called exactly once, %d receivved', count($calls)));
|
||||
}
|
||||
|
||||
/** @var PersonDocument $personDocument */
|
||||
$personDocument = $calls[0]->getArguments()[0];
|
||||
|
||||
if ($personDocument->getScope() !== $scope) {
|
||||
throw new FailedPredictionException('the person document should show the exactly prepared scope');
|
||||
}
|
||||
});
|
||||
|
||||
$authorizationHelper = $this->prophesize(AuthorizationHelperInterface::class);
|
||||
$authorizationHelper->getReachableScopes(Argument::type(UserInterface::class), PersonDocumentVoter::CREATE, Argument::type('array'))
|
||||
->willReturn([$scope, new Scope()]);
|
||||
|
||||
$personContext = $this->buildPersonContext(
|
||||
$authorizationHelper->reveal(),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
$em->reveal(),
|
||||
);
|
||||
|
||||
$this->assertTrue($personContext->hasPublicForm($docGen, $person));
|
||||
|
||||
$personContext->storeGenerated(
|
||||
$docGen,
|
||||
new StoredObject(),
|
||||
$person,
|
||||
['scope' => $scope]
|
||||
);
|
||||
}
|
||||
|
||||
public function testScopeScopeMustBeShownInFormsAndUserAccessOneScope()
|
||||
{
|
||||
$person = new Person();
|
||||
$docGen = (new DocGeneratorTemplate())
|
||||
->setName(['fr' => 'template']);
|
||||
$scope = new Scope();
|
||||
|
||||
$em = $this->prophesize(EntityManagerInterface::class);
|
||||
$em->persist(Argument::type(PersonDocument::class))
|
||||
->should(static function ($calls, $object, $method) use ($scope) {
|
||||
if (1 !== count($calls)) {
|
||||
throw new FailedPredictionException(sprintf('the persist should be called exactly once, %d receivved', count($calls)));
|
||||
}
|
||||
|
||||
/** @var PersonDocument $personDocument */
|
||||
$personDocument = $calls[0]->getArguments()[0];
|
||||
|
||||
if ($personDocument->getScope() !== $scope) {
|
||||
throw new FailedPredictionException('the person document should show the exactly prepared scope');
|
||||
}
|
||||
});
|
||||
|
||||
$authorizationHelper = $this->prophesize(AuthorizationHelperInterface::class);
|
||||
$authorizationHelper->getReachableScopes(Argument::type(UserInterface::class), PersonDocumentVoter::CREATE, Argument::type('array'))
|
||||
->willReturn([$scope]);
|
||||
|
||||
$personContext = $this->buildPersonContext(
|
||||
$authorizationHelper->reveal(),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
$em->reveal(),
|
||||
);
|
||||
|
||||
$this->assertTrue($personContext->hasPublicForm($docGen, $person));
|
||||
|
||||
$personContext->storeGenerated(
|
||||
$docGen,
|
||||
new StoredObject(),
|
||||
$person,
|
||||
['scope' => $scope]
|
||||
);
|
||||
}
|
||||
|
||||
private function buildPersonContext(
|
||||
?AuthorizationHelperInterface $authorizationHelper = null,
|
||||
?BaseContextData $baseContextData = null,
|
||||
?CenterResolverManagerInterface $centerResolverManager = null,
|
||||
?DocumentCategoryRepository $documentCategoryRepository = null,
|
||||
?EntityManagerInterface $em = null,
|
||||
?NormalizerInterface $normalizer = null,
|
||||
?ParameterBagInterface $parameterBag = null,
|
||||
?Security $security = null,
|
||||
?TranslatorInterface $translator = null,
|
||||
?TranslatableStringHelperInterface $translatableStringHelper = null
|
||||
): PersonContext {
|
||||
if (null === $authorizationHelper) {
|
||||
$authorizationHelper = $this->prophesize(AuthorizationHelperInterface::class)->reveal();
|
||||
}
|
||||
|
||||
if (null === $baseContextData) {
|
||||
$baseContextData = $this->prophesize(BaseContextData::class)->reveal();
|
||||
}
|
||||
|
||||
if (null === $centerResolverManager) {
|
||||
$centerResolverManager = $this->prophesize(CenterResolverManagerInterface::class);
|
||||
$centerResolverManager->resolveCenters(Argument::any(), Argument::any())
|
||||
->willReturn([new Center()]);
|
||||
$centerResolverManager = $centerResolverManager->reveal();
|
||||
}
|
||||
|
||||
if (null === $documentCategoryRepository) {
|
||||
$documentCategoryRepository = $this->prophesize(DocumentCategoryRepository::class);
|
||||
$documentCategoryRepository->find(Argument::type('integer'))->willReturn(
|
||||
new DocumentCategory(PersonDocument::class, 1)
|
||||
);
|
||||
$documentCategoryRepository = $documentCategoryRepository->reveal();
|
||||
}
|
||||
|
||||
if (null === $em) {
|
||||
$em = $this->prophesize(EntityManagerInterface::class)->reveal();
|
||||
}
|
||||
|
||||
if (null === $normalizer) {
|
||||
$normalizer = $this->prophesize(NormalizerInterface::class);
|
||||
$normalizer->normalize(Argument::type(Person::class), 'docgen', Argument::any())
|
||||
->willReturn(['type' => 'person']);
|
||||
$normalizer = $normalizer->reveal();
|
||||
}
|
||||
|
||||
if (null === $parameterBag) {
|
||||
$parameterBag = new ParameterBag(['chill_main' => ['acl' => ['form_show_scopes' => true]]]);
|
||||
}
|
||||
|
||||
if (null === $security) {
|
||||
$security = $this->prophesize(Security::class);
|
||||
$security->getUser()->willReturn(new User());
|
||||
$security = $security->reveal();
|
||||
}
|
||||
|
||||
if (null === $translator) {
|
||||
$translator = $this->prophesize(TranslatorInterface::class)->reveal();
|
||||
}
|
||||
|
||||
if (null === $translatableStringHelper) {
|
||||
$translatableStringHelper = $this->prophesize(TranslatableStringHelperInterface::class);
|
||||
// return only the 'fr' key
|
||||
$translatableStringHelper->localize(Argument::type('array'))->will(static function ($args) {
|
||||
return $args[0]['fr'];
|
||||
});
|
||||
$translatableStringHelper = $translatableStringHelper->reveal();
|
||||
}
|
||||
|
||||
return new PersonContext(
|
||||
$authorizationHelper,
|
||||
$baseContextData,
|
||||
$centerResolverManager,
|
||||
$documentCategoryRepository,
|
||||
$em,
|
||||
$normalizer,
|
||||
$parameterBag,
|
||||
$security,
|
||||
$translator,
|
||||
$translatableStringHelper
|
||||
);
|
||||
}
|
||||
}
|
@@ -16,19 +16,17 @@ services:
|
||||
- { name: chill.export, alias: avg_accompanyingcourse_duration }
|
||||
|
||||
## Filters
|
||||
chill.person.export.filter_current_userscope:
|
||||
class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\CurrentUserScopeFilter
|
||||
Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\UserScopeFilter:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
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
|
||||
Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\UserJobFilter:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
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
|
||||
|
@@ -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');
|
||||
}
|
||||
}
|
@@ -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\'
|
||||
'
|
||||
);
|
||||
}
|
||||
}
|
@@ -939,6 +939,8 @@ reassign:
|
||||
All periods on this list will be reassigned to this user, excepted the one you manually reassigned before: Tous les parcours visibles sur cette page seront assignés à cet utilisateur, sauf ceux que vous aurez assigné à un utilisateur manuellement.
|
||||
Reassign: Assigner le référent
|
||||
List periods to be able to reassign them: Choisissez un utilisateur et cliquez sur "Filtrer" pour visualiser ses parcours. Vous pourrez ensuite les réassigner.
|
||||
Filter by postal code: Filtrer par code postal
|
||||
Filter course which are located inside a postal code: Afficher uniquement les parcours localisés auprès de ce code postal (une commune peut comporter plusieurs codes postaux).
|
||||
|
||||
notification:
|
||||
Notify referrer: Notifier le référent
|
||||
@@ -947,14 +949,27 @@ 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
|
||||
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
|
||||
and children: et dérivés
|
||||
|
Reference in New Issue
Block a user