Merge remote-tracking branch 'origin/master' into cire16

This commit is contained in:
2022-12-22 10:22:58 +01:00
801 changed files with 39243 additions and 6591 deletions

View File

@@ -13,12 +13,13 @@ namespace Chill\PersonBundle\Controller;
use Chill\MainBundle\CRUD\Controller\ApiController;
use Chill\MainBundle\Entity\Address;
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
use Chill\PersonBundle\Config\ConfigPersonAltNamesHelper;
use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Chill\PersonBundle\Security\AuthorizedCenterOnPersonCreationInterface;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
@@ -26,16 +27,37 @@ use function in_array;
class PersonApiController extends ApiController
{
private AuthorizationHelper $authorizationHelper;
private AuthorizedCenterOnPersonCreationInterface $authorizedCenterOnPersonCreation;
private ConfigPersonAltNamesHelper $configPersonAltNameHelper;
private bool $showCenters;
public function __construct(
AuthorizationHelper $authorizationHelper,
ConfigPersonAltNamesHelper $configPersonAltNameHelper
AuthorizedCenterOnPersonCreationInterface $authorizedCenterOnPersonCreation,
ConfigPersonAltNamesHelper $configPersonAltNameHelper,
ParameterBagInterface $parameterBag
) {
$this->authorizationHelper = $authorizationHelper;
$this->authorizedCenterOnPersonCreation = $authorizedCenterOnPersonCreation;
$this->configPersonAltNameHelper = $configPersonAltNameHelper;
$this->showCenters = $parameterBag->get('chill_main')['acl']['form_show_centers'];
}
/**
* @Route("/api/1.0/person/creation/authorized-centers",
* name="chill_person_person_creation_authorized_centers"
* )
*/
public function authorizedCentersForCreation(): Response
{
$centers = $this->authorizedCenterOnPersonCreation->getCenters();
return $this->json(
['showCenters' => $this->showCenters, 'centers' => $centers],
Response::HTTP_OK,
[],
['gropus' => ['read']]
);
}
/**

View File

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

View File

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

View File

@@ -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;
@@ -52,7 +53,7 @@ use Symfony\Component\Validator\GroupSequenceProviderInterface;
use UnexpectedValueException;
use function in_array;
use function array_key_exists;
use const SORT_REGULAR;
/**
@@ -129,6 +130,11 @@ class AccompanyingPeriod implements
*/
private ?Location $administrativeLocation = null;
/**
* @ORM\OneToMany(targetEntity="Chill\CalendarBundle\Entity\Calendar", mappedBy="accompanyingPeriod")
*/
private Collection $calendars;
/**
* @var DateTime
*
@@ -336,6 +342,12 @@ class AccompanyingPeriod implements
*/
private string $step = self::STEP_DRAFT;
/**
* @ORM\OneToMany(targetEntity=AccompanyingPeriodStepHistory::class,
* mappedBy="period", cascade={"persist", "remove"}, orphanRemoval=true)
*/
private Collection $stepHistories;
/**
* @ORM\Column(type="datetime", nullable=true, options={"default": NULL})
*/
@@ -390,7 +402,7 @@ class AccompanyingPeriod implements
*/
public function __construct(?DateTime $dateOpening = null)
{
$this->setOpeningDate($dateOpening ?? new DateTime('now'));
$this->calendars = new ArrayCollection(); // TODO we cannot add a dependency between AccompanyingPeriod and calendars
$this->participations = new ArrayCollection();
$this->scopes = new ArrayCollection();
$this->socialIssues = new ArrayCollection();
@@ -399,6 +411,8 @@ class AccompanyingPeriod implements
$this->resources = new ArrayCollection();
$this->userHistories = new ArrayCollection();
$this->locationHistories = new ArrayCollection();
$this->stepHistories = new ArrayCollection();
$this->setOpeningDate($dateOpening ?? new DateTime('now'));
}
/**
@@ -614,6 +628,11 @@ class AccompanyingPeriod implements
);
}
public function getCalendars(): Collection
{
return $this->calendars;
}
public function getCenter(): ?Center
{
if ($this->getPersons()->count() === 0) {
@@ -625,16 +644,18 @@ class AccompanyingPeriod implements
public function getCenters(): ?iterable
{
$centers = [];
foreach ($this->getPersons() as $person) {
if (
!in_array($person->getCenter(), $centers ?? [], true)
&& null !== $person->getCenter()
null !== $person->getCenter()
&& !array_key_exists(spl_object_hash($person->getCenter()), $centers)
) {
$centers[] = $person->getCenter();
$centers[spl_object_hash($person->getCenter())] = $person->getCenter();
}
}
return $centers ?? null;
return array_values($centers);
}
/**
@@ -767,6 +788,24 @@ class AccompanyingPeriod implements
return 'none';
}
public function getNextCalendarsForPerson(Person $person, $limit = 5): Collection
{
$today = new DateTimeImmutable('today');
$criteria = Criteria::create()
->where(Criteria::expr()->gte('startDate', $today))
//->andWhere(Criteria::expr()->memberOf('persons', $person))
->orderBy(['startDate' => 'DESC'])
->setMaxResults($limit * 2);
return $this->calendars->matching($criteria)
->matching(
// due to a bug, filter two times
Criteria::create()
->where(Criteria::expr()->memberOf('persons', $person))
->setMaxResults($limit)
);
}
/**
* Get openingDate.
*
@@ -966,6 +1005,11 @@ class AccompanyingPeriod implements
return $this->step;
}
public function getStepHistories(): Collection
{
return $this->stepHistories;
}
public function getUser(): ?User
{
return $this->user;
@@ -1234,7 +1278,11 @@ class AccompanyingPeriod implements
*/
public function setOpeningDate($openingDate)
{
$this->openingDate = $openingDate;
if ($this->openingDate !== $openingDate) {
$this->openingDate = $openingDate;
$this->ensureStepContinuity();
}
return $this;
}
@@ -1333,6 +1381,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;
}
@@ -1373,6 +1429,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
@@ -1384,6 +1451,43 @@ class AccompanyingPeriod implements
$this->addLocationHistory($locationHistory);
}
private function ensureStepContinuity(): void
{
// ensure continuity of histories
$criteria = new Criteria();
$criteria->orderBy(['startDate' => Criteria::ASC, 'id' => Criteria::ASC]);
/** @var Iterator $steps */
$steps = $this->getStepHistories()->matching($criteria)->getIterator();
$steps->rewind();
// we set the start date of the first step as the opening date, only if it is
// not greater than the end date
/** @var AccompanyingPeriodStepHistory $current */
$current = $steps->current();
if (null === $current) {
return;
}
if ($this->getOpeningDate()->format('Y-m-d') !== $current->getStartDate()->format('Y-m-d')
&& ($this->getOpeningDate() <= $current->getEndDate() || null === $current->getEndDate())) {
$current->setStartDate(DateTimeImmutable::createFromMutable($this->getOpeningDate()));
}
// then we set all the end date to the start date of the next one
do {
/** @var AccompanyingPeriodStepHistory $current */
$current = $steps->current();
$steps->next();
if ($steps->valid()) {
$next = $steps->current();
$current->setEndDate($next->getStartDate());
}
} while ($steps->valid());
}
private function setRequestorPerson(?Person $requestorPerson = null): self
{
$this->requestorPerson = $requestorPerson;

View File

@@ -0,0 +1,115 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\MainBundle\Doctrine\Model\TrackCreationInterface;
use Chill\MainBundle\Doctrine\Model\TrackCreationTrait;
use Chill\MainBundle\Doctrine\Model\TrackUpdateInterface;
use Chill\MainBundle\Doctrine\Model\TrackUpdateTrait;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person;
use DateTimeImmutable;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
* @ORM\Table("chill_person_accompanying_period_step_history")
*/
class AccompanyingPeriodStepHistory implements TrackCreationInterface, TrackUpdateInterface
{
use TrackCreationTrait;
use TrackUpdateTrait;
/**
* @ORM\Column(type="date_immutable", nullable=true, options={"default": null})
*/
private ?DateTimeImmutable $endDate = null;
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private ?int $id = null;
/**
* @ORM\ManyToOne(targetEntity=AccompanyingPeriod::class)
*/
private AccompanyingPeriod $period;
/**
* @ORM\Column(type="date_immutable")
*/
private ?DateTimeImmutable $startDate = null;
/**
* @ORM\Column(type="text", nullable=false)
*/
private string $step;
public function getEndDate(): ?DateTimeImmutable
{
return $this->endDate;
}
public function getId(): ?int
{
return $this->id;
}
public function getPeriod(): AccompanyingPeriod
{
return $this->period;
}
public function getStartDate(): ?DateTimeImmutable
{
return $this->startDate;
}
public function getStep(): string
{
return $this->step;
}
public function setEndDate(?DateTimeImmutable $endDate): self
{
$this->endDate = $endDate;
return $this;
}
/**
* @internal use AccompanyingPeriod::addLocationHistory
*/
public function setPeriod(AccompanyingPeriod $period): self
{
$this->period = $period;
return $this;
}
public function setStartDate(?DateTimeImmutable $startDate): self
{
$this->startDate = $startDate;
return $this;
}
public function setStep(string $step): AccompanyingPeriodStepHistory
{
$this->step = $step;
return $this;
}
}

View File

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

View File

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

View File

@@ -27,6 +27,8 @@ use Chill\MainBundle\Validation\Constraint\PhonenumberConstraint;
use Chill\PersonBundle\Entity\Household\Household;
use Chill\PersonBundle\Entity\Household\HouseholdMember;
use Chill\PersonBundle\Entity\Household\PersonHouseholdAddress;
use Chill\PersonBundle\Entity\Person\PersonCenterCurrent;
use Chill\PersonBundle\Entity\Person\PersonCenterHistory;
use Chill\PersonBundle\Entity\Person\PersonCurrentAddress;
use Chill\PersonBundle\Entity\Person\PersonResource;
use Chill\PersonBundle\Validator\Constraints\Household\HouseholdMembershipSequential;
@@ -44,6 +46,7 @@ use libphonenumber\PhoneNumber;
use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use UnexpectedValueException;
use function count;
use function in_array;
@@ -176,13 +179,35 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
*/
private Collection $budgetResources;
/**
* @ORM\ManyToMany(
* targetEntity="Chill\CalendarBundle\Entity\Calendar",
* mappedBy="persons"
* )
*/
private Collection $calendars;
/**
* The person's center.
*
* @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\Center")
*
* @deprecated
*/
private ?Center $center = null;
/**
* @ORM\OneToOne(targetEntity=PersonCenterCurrent::class, mappedBy="person")
*/
private ?PersonCenterCurrent $centerCurrent = null;
/**
* @ORM\OneToMany(targetEntity=PersonCenterHistory::class, mappedBy="person", cascade={"persist"})
*
* @var Collection|PersonCenterHistory[]
*/
private Collection $centerHistory;
/**
* Array where customfield's data are stored.
*
@@ -523,6 +548,7 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
$this->budgetResources = new ArrayCollection();
$this->budgetCharges = new ArrayCollection();
$this->resources = new ArrayCollection();
$this->centerHistory = new ArrayCollection();
}
/**
@@ -897,7 +923,33 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
public function getCenter(): ?Center
{
return $this->center;
if (null !== $this->centerCurrent) {
return $this->centerCurrent->getCenter();
}
if (null === $currentCenterHistory = $this->getCurrentCenterHistory()) {
return null;
}
return $currentCenterHistory->getCenter();
}
public function getCenterCurrent(): ?PersonCenterCurrent
{
if (null !== $this->centerCurrent) {
return $this->centerCurrent;
}
if (null === $currentCenterHistory = $this->getCurrentCenterHistory()) {
return null;
}
return new PersonCenterCurrent($currentCenterHistory);
}
public function getCenterHistory(): Collection
{
return $this->centerHistory;
}
public function getCFData(): ?array
@@ -1436,7 +1488,7 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
$participation = $this->participationsContainAccompanyingPeriod($accompanyingPeriod);
if (!null === $participation) {
$participation->setEndDate(DateTimeImmutable::class);
$participation->setEndDate(new DateTime());
$this->accompanyingPeriodParticipations->removeElement($participation);
}
}
@@ -1510,17 +1562,41 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
return $this;
}
public function setCenter(Center $center): self
/**
* Associate the center with the person. The association start on 'now'.
*
* @return $this
*/
public function setCenter(?Center $center): self
{
$this->center = $center;
$modification = new DateTimeImmutable('now');
foreach ($this->centerHistory as $centerHistory) {
if (null === $centerHistory->getEndDate()) {
$centerHistory->setEndDate($modification);
}
}
if (null === $center) {
return $this;
}
$this->centerHistory[] = new PersonCenterHistory($this, $center, $modification);
return $this;
}
public function setCenterHistory(Collection $centerHistory): Person
{
$this->centerHistory = $centerHistory;
return $this;
}
/**
* @return Report
* @return Person
*/
public function setCFData(?array $cFData)
public function setCFData(?array $cFData): self
{
$this->cFData = $cFData;
@@ -1696,7 +1772,7 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
}
/**
* @param type $spokenLanguages
* @param Collection $spokenLanguages
*/
public function setSpokenLanguages($spokenLanguages): self
{
@@ -1719,6 +1795,34 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
return $this;
}
private function getCurrentCenterHistory(): ?PersonCenterHistory
{
if (0 === $this->centerHistory->count()) {
return null;
}
$criteria = Criteria::create();
$now = new DateTimeImmutable('now');
$criteria->where(Criteria::expr()->lte('startDate', $now))
->andWhere(Criteria::expr()->orX(
Criteria::expr()->isNull('endDate'),
Criteria::expr()->gt('endDate', $now)
));
$histories = $this->centerHistory->matching($criteria);
switch ($histories->count()) {
case 0:
return null;
case 1:
return $histories->first();
default:
throw new UnexpectedValueException('It should not contains more than one center at a time');
}
}
/**
* This private function scan accompanyingPeriodParticipations Collection,
* searching for a given AccompanyingPeriod.

View File

@@ -0,0 +1,100 @@
<?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\Person;
use Chill\MainBundle\Entity\Center;
use Chill\PersonBundle\Entity\Person;
use DateTimeImmutable;
use Doctrine\ORM\Mapping as ORM;
/**
* Associate a Person with the current center.
*
* The process of selecting the current center is done on database side,
* using a SQL view.
*
* @ORM\Entity(readOnly=true)
* @ORM\Table(name="view_chill_person_person_center_history_current")
* @psalm-internal Chill\PersonBundle\Entity
*/
class PersonCenterCurrent
{
/**
* @ORM\ManyToOne(targetEntity=Center::class)
*/
private Center $center;
/**
* @ORM\Column(type="date_immutable", nullable=true, options={"default": null})
*/
private ?DateTimeImmutable $endDate = null;
/**
* @ORM\Id
* @ORM\Column(type="integer")
*/
private ?int $id = null;
/**
* @ORM\ManyToOne(targetEntity=Person::class, inversedBy="centerCurrent")
*/
private Person $person;
/**
* @ORM\Column(type="date_immutable", nullable=false)
*/
private DateTimeImmutable $startDate;
/**
* Populate the properties person, center, start and end date from history.
*
* The creator and updatedby are not filled.
*
* @internal Should not be instantied, unless inside Person entity
*/
public function __construct(PersonCenterHistory $history)
{
$this->person = $history->getPerson();
$this->center = $history->getCenter();
$this->startDate = $history->getStartDate();
$this->endDate = $history->getEndDate();
$this->id = $history->getId();
}
public function getCenter(): Center
{
return $this->center;
}
public function getEndDate(): ?DateTimeImmutable
{
return $this->endDate;
}
/**
* The id will be the same as the current @see{PersonCenterHistory::class}.
*/
public function getId(): int
{
return $this->id;
}
public function getPerson(): Person
{
return $this->person;
}
public function getStartDate(): DateTimeImmutable
{
return $this->startDate;
}
}

View File

@@ -0,0 +1,121 @@
<?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\Person;
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\MainBundle\Entity\Center;
use Chill\PersonBundle\Entity\Person;
use DateTimeImmutable;
use Doctrine\ORM\Mapping as ORM;
/**
* Associate a Person with a Center. The association may change on date intervals.
*
* @ORM\Entity
* @ORM\Table(name="chill_person_person_center_history")
*/
class PersonCenterHistory implements TrackCreationInterface, TrackUpdateInterface
{
use TrackCreationTrait;
use TrackUpdateTrait;
/**
* @ORM\ManyToOne(targetEntity=Center::class)
*/
private ?Center $center = null;
/**
* @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=Person::class, inversedBy="centerHistory")
*/
private ?Person $person = null;
/**
* @ORM\Column(type="date_immutable", nullable=false)
*/
private ?DateTimeImmutable $startDate = null;
public function __construct(?Person $person = null, ?Center $center = null, ?DateTimeImmutable $startDate = null)
{
$this->person = $person;
$this->center = $center;
$this->startDate = $startDate;
}
public function getCenter(): ?Center
{
return $this->center;
}
public function getEndDate(): ?DateTimeImmutable
{
return $this->endDate;
}
public function getId(): ?int
{
return $this->id;
}
public function getPerson(): ?Person
{
return $this->person;
}
public function getStartDate(): ?DateTimeImmutable
{
return $this->startDate;
}
public function setCenter(?Center $center): self
{
$this->center = $center;
return $this;
}
public function setEndDate(?DateTimeImmutable $endDate): self
{
$this->endDate = $endDate;
return $this;
}
public function setPerson(?Person $person): self
{
$this->person = $person;
return $this;
}
public function setStartDate(?DateTimeImmutable $startDate): self
{
$this->startDate = $startDate;
return $this;
}
}

View File

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

View File

@@ -62,7 +62,7 @@ class SocialAction
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
private ?int $id = null;
/**
* @ORM\ManyToOne(targetEntity=SocialIssue::class, inversedBy="socialActions")
@@ -228,6 +228,22 @@ class SocialAction
return $descendants;
}
/**
* @param Collection|SocialAction[] $socialActions
*/
public static function getDescendantsWithThisForActions($socialActions): Collection
{
$unique = [];
foreach ($socialActions as $action) {
foreach ($action->getDescendantsWithThis() as $child) {
$unique[spl_object_hash($child)] = $child;
}
}
return new ArrayCollection(array_values($unique));
}
public function getEvaluations(): Collection
{
return $this->evaluations;
@@ -274,6 +290,11 @@ class SocialAction
return $this->title;
}
public function hasChildren(): bool
{
return 0 < $this->getChildren()->count();
}
public function hasParent(): bool
{
return $this->getParent() instanceof self;
@@ -369,6 +390,8 @@ class SocialAction
{
$this->parent = $parent;
$parent->addChild($this);
return $this;
}

View File

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

View File

@@ -25,23 +25,17 @@ class AbstractAccompanyingPeriodExportElement
*/
protected function addJoinAccompanyingPeriod(QueryBuilder $query): void
{
if (false === $this->havingAccompanyingPeriodInJoin($query)) {
if (false === in_array('person', $query->getAllAliases(), true)) {
throw new LogicException("the alias 'person' does not exists in "
. 'query builder');
}
if (false === in_array('person', $query->getAllAliases(), true)) {
throw new LogicException("the alias 'person' does not exists in "
. 'query builder');
}
$query->join('person.accompanyingPeriods', 'accompanying_period');
if (!in_array('acppart', $query->getAllAliases(), true)) {
$query->join('person.accompanyingPeriodParticipations', 'acppart');
}
if (!in_array('acp', $query->getAllAliases(), true)) {
$query->join('acppart.accompanyingPeriod', 'acp');
}
}
/**
* Return true if "accompanying_period" alias is present in the query alises.
*/
protected function havingAccompanyingPeriodInJoin(QueryBuilder $query): bool
{
$joins = $query->getDQLPart('join') ?? [];
return in_array('accompanying_period', $query->getAllAliases(), true);
}
}

View File

@@ -17,6 +17,7 @@ use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use function in_array;
class AdministrativeLocationAggregator implements AggregatorInterface
{
@@ -32,24 +33,19 @@ class AdministrativeLocationAggregator implements AggregatorInterface
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole()
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$qb->join('acp.administrativeLocation', 'al');
if (!in_array('acploc', $qb->getAllAliases(), true)) {
$qb->leftJoin('acp.administrativeLocation', 'acploc');
}
$qb->addSelect('IDENTITY(acp.administrativeLocation) AS location_aggregator');
$groupBy = $qb->getDQLPart('groupBy');
if (!empty($groupBy)) {
$qb->addGroupBy('location_aggregator');
} else {
$qb->groupBy('location_aggregator');
}
$qb->addGroupBy('location_aggregator');
}
public function applyOn(): string
@@ -69,6 +65,10 @@ class AdministrativeLocationAggregator implements AggregatorInterface
return 'Administrative location';
}
if (null === $value) {
return '';
}
$l = $this->locationRepository->find($value);
return $l->getName() . ' (' . $this->translatableStringHelper->localize($l->getLocationType()->getTitle()) . ')';

View File

@@ -0,0 +1,67 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
class ByActionNumberAggregator implements AggregatorInterface
{
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data): void
{
$qb->addSelect('(SELECT COUNT(acp_by_action_action.id) FROM ' . AccompanyingPeriodWork::class . ' acp_by_action_action WHERE acp_by_action_action.accompanyingPeriod = acp) AS acp_by_action_number_aggregator')
->addGroupBy('acp_by_action_number_aggregator');
}
public function applyOn(): string
{
return Declarations::ACP_TYPE;
}
public function buildForm(FormBuilderInterface $builder): void
{
// No form needed
}
public function getLabels($key, array $values, $data)
{
return static function ($value) {
if ('_header' === $value) {
return 'export.aggregator.course.by_number_of_action.Number of actions';
}
if (null === $value) {
return '';
}
return $value;
};
}
public function getQueryKeys($data): array
{
return ['acp_by_action_number_aggregator'];
}
public function getTitle(): string
{
return 'Group by number of actions';
}
}

View File

@@ -13,44 +13,34 @@ namespace Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\PersonBundle\Entity\AccompanyingPeriod\ClosingMotive;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\EntityManagerInterface;
use Chill\PersonBundle\Repository\AccompanyingPeriod\ClosingMotiveRepositoryInterface;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
class ClosingMotiveAggregator implements AggregatorInterface
{
private EntityManagerInterface $em;
private ClosingMotiveRepositoryInterface $motiveRepository;
private TranslatableStringHelper $translatableStringHelper;
public function __construct(
EntityManagerInterface $em,
ClosingMotiveRepositoryInterface $motiveRepository,
TranslatableStringHelper $translatableStringHelper
) {
$this->motiveRepository = $em->getRepository(ClosingMotive::class);
$this->motiveRepository = $motiveRepository;
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole()
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$qb->join('acp.closingMotive', 'cm');
$qb->addSelect('IDENTITY(acp.closingMotive) AS closingmotive_aggregator');
$groupBy = $qb->getDQLPart('groupBy');
if (!empty($groupBy)) {
$qb->addGroupBy('closingmotive_aggregator');
} else {
$qb->groupBy('closingmotive_aggregator');
}
$qb->addGroupBy('closingmotive_aggregator');
}
public function applyOn(): string
@@ -70,6 +60,10 @@ class ClosingMotiveAggregator implements AggregatorInterface
return 'Closing motive';
}
if (null === $value) {
return '';
}
$cm = $this->motiveRepository->find($value);
return $this->translatableStringHelper->localize(

View File

@@ -14,6 +14,7 @@ namespace Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\QueryBuilder;
use LogicException;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
@@ -27,7 +28,7 @@ class ConfidentialAggregator implements AggregatorInterface
$this->translator = $translator;
}
public function addRole()
public function addRole(): ?string
{
return null;
}
@@ -35,14 +36,7 @@ class ConfidentialAggregator implements AggregatorInterface
public function alterQuery(QueryBuilder $qb, $data)
{
$qb->addSelect('acp.confidential AS confidential_aggregator');
$groupBy = $qb->getDQLPart('groupBy');
if (!empty($groupBy)) {
$qb->addGroupBy('confidential_aggregator');
} else {
$qb->groupBy('confidential_aggregator');
}
$qb->addGroupBy('confidential_aggregator');
}
public function applyOn(): string
@@ -72,8 +66,6 @@ class ConfidentialAggregator implements AggregatorInterface
default:
throw new LogicException(sprintf('The value %s is not valid', $value));
}
return $value;
};
}

View File

@@ -0,0 +1,87 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Repository\UserJobRepository;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use function in_array;
class CreatorJobAggregator implements AggregatorInterface
{
private UserJobRepository $jobRepository;
private TranslatableStringHelper $translatableStringHelper;
public function __construct(
UserJobRepository $jobRepository,
TranslatableStringHelper $translatableStringHelper
) {
$this->jobRepository = $jobRepository;
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('acp_creator', $qb->getAllAliases(), true)) {
$qb->leftJoin('acp.createdBy', 'acp_creator');
}
$qb->addSelect('IDENTITY(acp_creator.userJob) AS acp_creator_job_aggregator')
->addGroupBy('acp_creator_job_aggregator');
}
public function applyOn(): string
{
return Declarations::ACP_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
{
// No form needed
}
public function getLabels($key, array $values, $data)
{
return function ($value): string {
if ('_header' === $value) {
return 'export.aggregator.course.by_creator_job.Creator\'s job';
}
if (null === $value || null === $j = $this->jobRepository->find($value)) {
return '';
}
return $this->translatableStringHelper->localize(
$j->getLabel()
);
};
}
public function getQueryKeys($data): array
{
return ['acp_creator_job_aggregator'];
}
public function getTitle(): string
{
return 'Group by creator job';
}
}

View File

@@ -13,21 +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;
/**
* Les regroupements seront un nombre de mois, arrondi à l'unité la plus proche (donc
* - au dela de 15 jours => 1 mois,
* - jusqu'à 45 jours => 1 mois,
* 15 | 45 | 75
* --+----o----+----o----+----
* | 30 | 60 |
* etc.).
*/
class DurationAggregator implements AggregatorInterface
final class DurationAggregator implements AggregatorInterface
{
private const CHOICES = [
'month',
'week',
'day',
];
private TranslatorInterface $translator;
public function __construct(TranslatorInterface $translator)
@@ -35,60 +35,38 @@ class DurationAggregator implements AggregatorInterface
$this->translator = $translator;
}
public function addRole()
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$qb
// OUI
->addSelect(
'
(acp.closingDate - acp.openingDate +15) *12/365
AS duration_aggregator'
)
//->addSelect('DATE_DIFF(acp.closingDate, acp.openingDate) AS duration_aggregator')
//->addSelect('EXTRACT(month FROM acp.openingDate) AS duration_aggregator')
//->addSelect("DATE_SUB(acp.openingDate, 6, 'day') AS duration_aggregator")
switch ($data['precision']) {
case 'day':
$qb->addSelect('(COALESCE(acp.closingDate, :now) - acp.openingDate) AS duration_aggregator');
// TODO adapter la fonction extract pour l'utiliser avec des intervals: extract(month from interval)
// et ajouter une fonction custom qui calcule les intervals, comme doctrineum/date-interval
// https://packagist.org/packages/doctrineum/date-interval#3.1.0
// (composer fait un conflit de dépendance)
break;
//->addSelect("
// EXTRACT(
// month FROM
// DATE_INTERVAL(acp.closingDate, acp.openingDate)
// )
// AS duration_aggregator")
case 'week':
$qb->addSelect('(COALESCE(acp.closingDate, :now) - acp.openingDate) / 7 AS duration_aggregator');
// NON
//->addSelect("BETWEEN acp.openingDate AND acp.closingDate AS duration_aggregator")
//->addSelect("EXTRACT(month FROM DATE_SUB(acp.openingDate, 6, 'day')) AS duration_aggregator")
//->addSelect('EXTRACT(month FROM DATE_DIFF(acp.closingDate, acp.openingDate)) AS duration_aggregator')
/*
->addSelect('
( CASE
WHEN EXTRACT(day FROM DATE_DIFF(acp.closingDate, acp.openingDate)) > 15
THEN EXTRACT(month FROM DATE_DIFF(acp.closingDate, acp.openingDate)) +1
ELSE EXTRACT(month FROM DATE_DIFF(acp.closingDate, acp.openingDate))
END ) AS duration_aggregator
')
*/
;
break;
$groupBy = $qb->getDQLPart('groupBy');
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');
if (!empty($groupBy)) {
$qb->addGroupBy('duration_aggregator');
} else {
$qb->groupBy('duration_aggregator');
break;
default:
throw new LogicException('precision not supported: ' . $data['precision']);
}
$qb->orderBy('duration_aggregator');
$qb
->setParameter('now', new DateTimeImmutable('now'))
->addGroupBy('duration_aggregator')
->addOrderBy('duration_aggregator');
}
public function applyOn(): string
@@ -98,25 +76,27 @@ 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 $this->translator->trans('Rounded month duration');
return 'export.aggregator.course.duration.' . $data['precision'];
}
if (null === $value) {
return $this->translator->trans('current duration'); // when closingDate is null
return 0;
}
if (0 === $value) {
return $this->translator->trans('duration 0 month');
}
return '' . $value . $this->translator->trans(' months');
return $value;
};
}

View File

@@ -14,6 +14,7 @@ namespace Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\QueryBuilder;
use LogicException;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
@@ -27,7 +28,7 @@ class EmergencyAggregator implements AggregatorInterface
$this->translator = $translator;
}
public function addRole()
public function addRole(): ?string
{
return null;
}
@@ -35,14 +36,7 @@ class EmergencyAggregator implements AggregatorInterface
public function alterQuery(QueryBuilder $qb, $data)
{
$qb->addSelect('acp.emergency AS emergency_aggregator');
$groupBy = $qb->getDQLPart('groupBy');
if (!empty($groupBy)) {
$qb->addGroupBy('emergency_aggregator');
} else {
$qb->groupBy('emergency_aggregator');
}
$qb->addGroupBy('emergency_aggregator');
}
public function applyOn(): string
@@ -72,8 +66,6 @@ class EmergencyAggregator implements AggregatorInterface
default:
throw new LogicException(sprintf('The value %s is not valid', $value));
}
return $value;
};
}

View File

@@ -33,7 +33,7 @@ final class EvaluationAggregator implements AggregatorInterface
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole()
public function addRole(): ?string
{
return null;
}
@@ -41,19 +41,15 @@ final class EvaluationAggregator implements AggregatorInterface
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('acpw', $qb->getAllAliases(), true)) {
$qb->join('acp.works', 'acpw');
$qb->leftJoin('acp.works', 'acpw');
}
$qb->join('acpw.accompanyingPeriodWorkEvaluations', 'we');
$qb->addSelect('IDENTITY(we.evaluation) AS evaluation_aggregator');
$groupby = $qb->getDQLPart('groupBy');
if (!empty($groupBy)) {
$qb->addGroupBy('evaluation_aggregator');
} else {
$qb->groupBy('evaluation_aggregator');
if (!in_array('workeval', $qb->getAllAliases(), true)) {
$qb->leftJoin('acpw.accompanyingPeriodWorkEvaluations', 'workeval');
}
$qb->addSelect('IDENTITY(workeval.evaluation) AS evaluation_aggregator');
$qb->addGroupBy('evaluation_aggregator');
}
public function applyOn(): string
@@ -73,6 +69,10 @@ final class EvaluationAggregator implements AggregatorInterface
return 'Evaluation';
}
if (null === $value) {
return '';
}
$e = $this->evaluationRepository->find($value);
return $this->translatableStringHelper->localize(

View File

@@ -0,0 +1,180 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators;
use Chill\MainBundle\Entity\Address;
use Chill\MainBundle\Entity\GeographicalUnitLayer;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Repository\GeographicalUnitLayerRepositoryInterface;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Entity\Household\PersonHouseholdAddress;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\Query\Expr\Join;
use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface;
use UnexpectedValueException;
final class GeographicalUnitStatAggregator implements AggregatorInterface
{
private GeographicalUnitLayerRepositoryInterface $geographicalUnitLayerRepository;
private RollingDateConverterInterface $rollingDateConverter;
private TranslatableStringHelperInterface $translatableStringHelper;
public function __construct(
GeographicalUnitLayerRepositoryInterface $geographicalUnitLayerRepository,
TranslatableStringHelperInterface $translatableStringHelper,
RollingDateConverterInterface $rollingDateConverter
) {
$this->geographicalUnitLayerRepository = $geographicalUnitLayerRepository;
$this->translatableStringHelper = $translatableStringHelper;
$this->rollingDateConverter = $rollingDateConverter;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$qb->leftJoin('acp.locationHistories', 'acp_geog_agg_location_history');
$qb->andWhere(
$qb->expr()->andX(
'acp_geog_agg_location_history.startDate <= :acp_geog_aggregator_date',
$qb->expr()->orX(
'acp_geog_agg_location_history.endDate IS NULL',
'acp_geog_agg_location_history.endDate > :acp_geog_aggregator_date'
)
)
);
// link between location history and person
$qb->leftJoin(
PersonHouseholdAddress::class,
'acp_geog_agg_address_person_location',
Join::WITH,
$qb->expr()->andX(
'IDENTITY(acp_geog_agg_address_person_location.person) = IDENTITY(acp_geog_agg_location_history.personLocation)',
'acp_geog_agg_address_person_location.validFrom < :acp_geog_aggregator_date',
$qb->expr()->orX(
'acp_geog_agg_address_person_location.validTo > :acp_geog_aggregator_date',
$qb->expr()->isNull('acp_geog_agg_address_person_location.validTo')
)
)
);
$qb->setParameter(
'acp_geog_aggregator_date',
$this->rollingDateConverter->convert($data['date_calc'])
);
// we finally find an address
$qb->leftJoin(
Address::class,
'acp_geog_agg_address',
Join::WITH,
'COALESCE(IDENTITY(acp_geog_agg_address_person_location.address), IDENTITY(acp_geog_agg_location_history.addressLocation)) = acp_geog_agg_address.id'
);
// and we do a join with units
$qb->leftJoin(
'acp_geog_agg_address.geographicalUnits',
'acp_geog_units'
);
$qb->andWhere($qb->expr()->eq('acp_geog_units.layer', ':acp_geog_unit_layer'));
$qb->setParameter('acp_geog_unit_layer', $data['level']);
// we add group by
$qb
->addSelect('acp_geog_units.unitName AS acp_geog_agg_unitname')
->addSelect('acp_geog_units.unitRefId AS acp_geog_agg_unitrefid')
->addGroupBy('acp_geog_agg_unitname')
->addGroupBy('acp_geog_agg_unitrefid');
}
public function applyOn(): string
{
return Declarations::ACP_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder
->add('date_calc', PickRollingDateType::class, [
'label' => 'Compute geographical location at date',
'required' => true,
'data' => new RollingDate(RollingDate::T_TODAY),
])
->add('level', EntityType::class, [
'label' => 'Geographical layer',
'placeholder' => 'Select a geographical layer',
'class' => GeographicalUnitLayer::class,
'choices' => $this->geographicalUnitLayerRepository->findAllHavingUnits(),
'choice_label' => function (GeographicalUnitLayer $item) {
return $this->translatableStringHelper->localize($item->getName());
},
]);
}
public function getLabels($key, array $values, $data)
{
switch ($key) {
case 'acp_geog_agg_unitname':
return static function ($value): string {
if ('_header' === $value) {
return 'acp_geog_agg_unitname';
}
if (null === $value) {
return '';
}
return $value;
};
case 'acp_geog_agg_unitrefid':
return static function ($value): string {
if ('_header' === $value) {
return 'acp_geog_agg_unitrefid';
}
if (null === $value) {
return '';
}
return $value;
};
default:
throw new UnexpectedValueException('this value should not happens');
}
}
public function getQueryKeys($data): array
{
return ['acp_geog_agg_unitname', 'acp_geog_agg_unitrefid'];
}
public function getTitle(): string
{
return 'Group by geographical unit';
}
}

View File

@@ -14,6 +14,7 @@ namespace Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\QueryBuilder;
use LogicException;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
@@ -27,7 +28,7 @@ class IntensityAggregator implements AggregatorInterface
$this->translator = $translator;
}
public function addRole()
public function addRole(): ?string
{
return null;
}
@@ -35,14 +36,7 @@ class IntensityAggregator implements AggregatorInterface
public function alterQuery(QueryBuilder $qb, $data)
{
$qb->addSelect('acp.intensity AS intensity_aggregator');
$groupBy = $qb->getDQLPart('groupBy');
if (!empty($groupBy)) {
$qb->addGroupBy('intensity_aggregator');
} else {
$qb->groupBy('intensity_aggregator');
}
$qb->addGroupBy('intensity_aggregator');
}
public function applyOn(): string

View File

@@ -17,6 +17,7 @@ use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use function in_array;
final class JobAggregator implements AggregatorInterface
{
@@ -32,24 +33,19 @@ final class JobAggregator implements AggregatorInterface
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole()
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$qb->join('acp.job', 'j');
if (!in_array('acpjob', $qb->getAllAliases(), true)) {
$qb->leftJoin('acp.job', 'acpjob');
}
$qb->addSelect('IDENTITY(acp.job) AS job_aggregator');
$groupBy = $qb->getDQLPart('groupBy');
if (!empty($groupBy)) {
$qb->addGroupBy('job_aggregator');
} else {
$qb->groupBy('job_aggregator');
}
$qb->addGroupBy('job_aggregator');
}
public function applyOn(): string
@@ -69,6 +65,10 @@ final class JobAggregator implements AggregatorInterface
return 'Job';
}
if (null === $value) {
return '';
}
$j = $this->jobRepository->find($value);
return $this->translatableStringHelper->localize(

View File

@@ -19,6 +19,7 @@ use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use function in_array;
final class OriginAggregator implements AggregatorInterface
{
@@ -34,24 +35,19 @@ final class OriginAggregator implements AggregatorInterface
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole()
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$qb->join('acp.origin', 'o');
$qb->addSelect('o.id AS origin_aggregator');
$groupby = $qb->getDQLPart('groupBy');
if (!empty($groupBy)) {
$qb->addGroupBy('origin_aggregator');
} else {
$qb->groupBy('origin_aggregator');
if (!in_array('acporigin', $qb->getAllAliases(), true)) {
$qb->leftJoin('acp.origin', 'acporigin');
}
$qb->addSelect('acporigin.id AS origin_aggregator');
$qb->addGroupBy('origin_aggregator');
}
public function applyOn(): string
@@ -71,6 +67,10 @@ final class OriginAggregator implements AggregatorInterface
return 'Origin';
}
if (null === $value) {
return '';
}
$o = $this->repository->find($value);
return $this->translatableStringHelper->localize(

View File

@@ -12,7 +12,10 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Repository\UserRepository;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\MainBundle\Templating\Entity\UserRender;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\QueryBuilder;
@@ -20,36 +23,53 @@ use Symfony\Component\Form\FormBuilderInterface;
final class ReferrerAggregator implements AggregatorInterface
{
private const A = 'acp_ref_agg_uhistory';
private const P = 'acp_ref_agg_date';
private RollingDateConverterInterface $rollingDateConverter;
private UserRender $userRender;
private UserRepository $userRepository;
public function __construct(
UserRepository $userRepository,
UserRender $userRender
UserRender $userRender,
RollingDateConverterInterface $rollingDateConverter
) {
$this->userRepository = $userRepository;
$this->userRender = $userRender;
$this->rollingDateConverter = $rollingDateConverter;
}
public function addRole()
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$qb->join('acp.user', 'u');
$qb->addSelect('u.id AS referrer_aggregator');
$groupBy = $qb->getDQLPart('groupBy');
if (!empty($groupBy)) {
$qb->addGroupBy('referrer_aggregator');
} else {
$qb->groupBy('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,
$this->rollingDateConverter->convert($data['date_calc'])
);
}
public function applyOn(): string
@@ -59,7 +79,12 @@ final class ReferrerAggregator implements AggregatorInterface
public function buildForm(FormBuilderInterface $builder)
{
// no form
$builder
->add('date_calc', PickRollingDateType::class, [
'data' => new RollingDate(RollingDate::T_TODAY),
'label' => 'export.aggregator.course.by_referrer.Computation date for referrer',
'required' => true,
]);
}
public function getLabels($key, array $values, $data)
@@ -69,6 +94,10 @@ final class ReferrerAggregator implements AggregatorInterface
return 'Referrer';
}
if (null === $value) {
return '';
}
$r = $this->userRepository->find($value);
return $this->userRender->renderString($r, []);

View File

@@ -0,0 +1,127 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Repository\ScopeRepositoryInterface;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\QueryBuilder;
use LogicException;
use Symfony\Component\Form\FormBuilderInterface;
class ReferrerScopeAggregator implements AggregatorInterface
{
private const SCOPE_KEY = 'acp_agg_refscope_user_history_ref_scope_name';
private RollingDateConverterInterface $rollingDateConverter;
private ScopeRepositoryInterface $scopeRepository;
private TranslatableStringHelperInterface $translatableStringHelper;
public function __construct(
ScopeRepositoryInterface $scopeRepository,
TranslatableStringHelperInterface $translatableStringHelper,
RollingDateConverterInterface $rollingDateConverter
) {
$this->scopeRepository = $scopeRepository;
$this->translatableStringHelper = $translatableStringHelper;
$this->rollingDateConverter = $rollingDateConverter;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$userHistory = 'acp_agg_refscope_user_history';
$ref = 'acp_agg_refscope_user_history_ref';
$scopeName = self::SCOPE_KEY;
$dateCalc = 'acp_agg_refscope_user_history_date_calc';
$qb
->leftJoin('acp.userHistories', $userHistory)
->leftJoin($userHistory . '.user', $ref)
->andWhere(
$qb->expr()->orX(
$qb->expr()->isNull($userHistory),
$qb->expr()->andX(
$qb->expr()->lte($userHistory . '.startDate', ':' . $dateCalc),
$qb->expr()->orX(
$qb->expr()->isNull($userHistory . '.endDate'),
$qb->expr()->lt($userHistory . '.endDate', ':' . $dateCalc)
)
)
)
)
->setParameter(
$dateCalc,
$this->rollingDateConverter->convert($data['date_calc'])
);
// add groups
$qb
->addSelect('IDENTITY(' . $ref . '.mainScope) AS ' . $scopeName)
->addGroupBy($scopeName);
}
public function applyOn()
{
return Declarations::ACP_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('date_calc', PickRollingDateType::class, [
'data' => new RollingDate(RollingDate::T_TODAY),
'label' => 'export.aggregator.course.by_user_scope.Computation date for referrer',
'required' => true,
]);
}
public function getLabels($key, array $values, $data)
{
return function ($value) {
if ('_header' === $value) {
return 'export.aggregator.course.by_user_scope.Referrer\'s scope';
}
if (null === $value) {
return '';
}
$scope = $this->scopeRepository->find($value);
if (null === $scope) {
throw new LogicException('no scope found with this id: ' . $value);
}
return $this->translatableStringHelper->localize($scope->getName());
};
}
public function getQueryKeys($data)
{
return [self::SCOPE_KEY];
}
public function getTitle()
{
return 'export.aggregator.course.by_user_scope.Group course by referrer\'s scope';
}
}

View File

@@ -0,0 +1,93 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use function in_array;
final class RequestorAggregator implements AggregatorInterface
{
private TranslatorInterface $translator;
public function __construct(
TranslatorInterface $translator
) {
$this->translator = $translator;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('acppart', $qb->getAllAliases(), true)) {
$qb->join('acp.participations', 'acppart');
}
$qb->addSelect("
( CASE
WHEN acp.requestorPerson IS NOT NULL
THEN
( CASE
WHEN acp.requestorPerson = acppart.person
THEN 'is person concerned'
ELSE 'is other person'
END )
ELSE
( CASE
WHEN acp.requestorThirdParty IS NOT NULL
THEN 'is thirdparty'
ELSE 'no requestor'
END )
END ) AS requestor_aggregator
");
$qb->addGroupBy('requestor_aggregator');
}
public function applyOn(): string
{
return Declarations::ACP_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
{
// no form
}
public function getLabels($key, array $values, $data)
{
return function ($value): string {
if ('_header' === $value) {
return 'Requestor';
}
return $this->translator->trans($value);
};
}
public function getQueryKeys($data): array
{
return ['requestor_aggregator'];
}
public function getTitle(): string
{
return 'Group by requestor';
}
}

View File

@@ -17,6 +17,7 @@ use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use function in_array;
final class ScopeAggregator implements AggregatorInterface
{
@@ -32,24 +33,19 @@ final class ScopeAggregator implements AggregatorInterface
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole()
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$qb->join('acp.scopes', 's');
$qb->addSelect('s.id as scope_aggregator');
$groupBy = $qb->getDQLPart('groupBy');
if (!empty($groupBy)) {
$qb->addGroupBy('scope_aggregator');
} else {
$qb->groupBy('scope_aggregator');
if (!in_array('acpscope', $qb->getAllAliases(), true)) {
$qb->leftJoin('acp.scopes', 'acpscope');
}
$qb->addSelect('acpscope.id as scope_aggregator');
$qb->addGroupBy('scope_aggregator');
}
public function applyOn(): string
@@ -69,6 +65,10 @@ final class ScopeAggregator implements AggregatorInterface
return 'Scope';
}
if (null === $value) {
return '';
}
$s = $this->scopeRepository->find($value);
return $this->translatableStringHelper->localize(
@@ -84,6 +84,6 @@ final class ScopeAggregator implements AggregatorInterface
public function getTitle(): string
{
return 'Group by user scope';
return 'Group course by scope';
}
}

View File

@@ -33,7 +33,7 @@ final class SocialActionAggregator implements AggregatorInterface
$this->actionRepository = $actionRepository;
}
public function addRole()
public function addRole(): ?string
{
return null;
}
@@ -41,18 +41,12 @@ final class SocialActionAggregator implements AggregatorInterface
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('acpw', $qb->getAllAliases(), true)) {
// here, we will only see accompanying period linked with a socialAction
$qb->join('acp.works', 'acpw');
}
$qb->addSelect('IDENTITY(acpw.socialAction) AS socialaction_aggregator'); // DISTINCT ??
$groupby = $qb->getDQLPart('groupBy');
if (!empty($groupBy)) {
$qb->addGroupBy('socialaction_aggregator');
} else {
$qb->groupBy('socialaction_aggregator');
}
$qb->addSelect('IDENTITY(acpw.socialAction) AS socialaction_aggregator');
$qb->addGroupBy('socialaction_aggregator');
}
public function applyOn(): string
@@ -72,6 +66,10 @@ final class SocialActionAggregator implements AggregatorInterface
return 'Social action';
}
if (null === $value) {
return '';
}
$sa = $this->actionRepository->find($value);
return $this->actionRender->renderString($sa, []);

View File

@@ -17,6 +17,7 @@ use Chill\PersonBundle\Repository\SocialWork\SocialIssueRepository;
use Chill\PersonBundle\Templating\Entity\SocialIssueRender;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use function in_array;
final class SocialIssueAggregator implements AggregatorInterface
{
@@ -32,23 +33,20 @@ final class SocialIssueAggregator implements AggregatorInterface
$this->issueRender = $issueRender;
}
public function addRole()
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$qb->join('acp.socialIssues', 'si');
$qb->addSelect('si.id as socialissue_aggregator');
$groupBy = $qb->getDQLPart('groupBy');
if (!empty($groupBy)) {
$qb->addGroupBy('socialissue_aggregator');
} else {
$qb->groupBy('socialissue_aggregator');
if (!in_array('acpsocialissue', $qb->getAllAliases(), true)) {
// we will see accompanying period linked with social issues
$qb->join('acp.socialIssues', 'acpsocialissue');
}
$qb->addSelect('acpsocialissue.id as socialissue_aggregator');
$qb->addGroupBy('socialissue_aggregator');
}
public function applyOn(): string

View File

@@ -12,63 +12,61 @@ 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\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Export\Declarations;
use DateTime;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use function in_array;
final class StepAggregator implements AggregatorInterface //, FilterInterface
final class StepAggregator implements AggregatorInterface
{
private const A = 'acpstephistories';
private const P = 'acp_step_agg_date';
private RollingDateConverterInterface $rollingDateConverter;
private TranslatorInterface $translator;
public function __construct(
RollingDateConverterInterface $rollingDateConverter,
TranslatorInterface $translator
) {
$this->rollingDateConverter = $rollingDateConverter;
$this->translator = $translator;
}
public function addRole()
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$qb->addSelect('acp.step AS step_aggregator');
$groupBy = $qb->getDQLPart('groupBy');
if (!empty($groupBy)) {
$qb->addGroupBy('step_aggregator');
} else {
$qb->groupBy('step_aggregator');
if (!in_array(self::A, $qb->getAllAliases(), true)) {
$qb->leftJoin('acp.stepHistories', self::A);
}
// 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')
$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)
)
)
)
)
);
if ($where instanceof Andx) {
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->setParameter('ondate', $data['on_date'], Types::DATE_MUTABLE);
->setParameter(self::P, $this->rollingDateConverter->convert($data['on_date']))
->addGroupBy('step_aggregator');
}
public function applyOn(): string
@@ -78,8 +76,8 @@ final class StepAggregator implements AggregatorInterface //, FilterInterface
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('on_date', ChillDateType::class, [
'data' => new DateTime(),
$builder->add('on_date', PickRollingDateType::class, [
'data' => new RollingDate(RollingDate::T_TODAY),
]);
}
@@ -99,8 +97,11 @@ final class StepAggregator implements AggregatorInterface //, FilterInterface
case '_header':
return 'Step';
case null:
return '';
default:
throw new LogicException(sprintf('The value %s is not valid', $value));
return $value;
}
};
}

View File

@@ -0,0 +1,106 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Export\Aggregator\EvaluationAggregators;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\QueryBuilder;
use LogicException;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
class ByEndDateAggregator implements AggregatorInterface
{
private const CHOICES = [
'by week' => 'week',
'by month' => 'month',
'by year' => 'year',
];
private const DEFAULT_CHOICE = 'year';
private TranslatorInterface $translator;
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
switch ($data['frequency']) {
case 'week':
$fmt = 'YYYY-IW';
break;
case 'month':
$fmt = 'YYYY-MM';
break;
case 'year':
$fmt = 'YYYY';
break;
default:
throw new LogicException(sprintf("The frequency data '%s' is invalid.", $data['frequency']));
}
$qb->addSelect(sprintf("TO_CHAR(workeval.endDate, '%s') AS eval_by_end_date_aggregator", $fmt));
$qb->addGroupBy(' eval_by_end_date_aggregator');
$qb->addOrderBy(' eval_by_end_date_aggregator', 'ASC');
}
public function applyOn(): string
{
return Declarations::EVAL_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('frequency', ChoiceType::class, [
'choices' => self::CHOICES,
'multiple' => false,
'expanded' => true,
'empty_data' => self::DEFAULT_CHOICE,
'data' => self::DEFAULT_CHOICE,
]);
}
public function getLabels($key, array $values, $data)
{
return static function ($value): string {
if ('_header' === $value) {
return 'export.aggregator.eval.by_end_date.End date period';
}
if (null === $value) {
return '';
}
return $value;
};
}
public function getQueryKeys($data): array
{
return ['eval_by_end_date_aggregator'];
}
public function getTitle(): string
{
return 'export.aggregator.eval.by_end_date.Group by end date evaluations';
}
}

View File

@@ -0,0 +1,106 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Export\Aggregator\EvaluationAggregators;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\QueryBuilder;
use LogicException;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
class ByMaxDateAggregator implements AggregatorInterface
{
private const CHOICES = [
'by week' => 'week',
'by month' => 'month',
'by year' => 'year',
];
private const DEFAULT_CHOICE = 'year';
private TranslatorInterface $translator;
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
switch ($data['frequency']) {
case 'week':
$fmt = 'YYYY-IW';
break;
case 'month':
$fmt = 'YYYY-MM';
break;
case 'year':
$fmt = 'YYYY';
break;
default:
throw new LogicException(sprintf("The frequency data '%s' is invalid.", $data['frequency']));
}
$qb->addSelect(sprintf("TO_CHAR(workeval.maxDate, '%s') AS eval_by_max_date_aggregator", $fmt));
$qb->addGroupBy(' eval_by_max_date_aggregator');
$qb->addOrderBy(' eval_by_max_date_aggregator', 'ASC');
}
public function applyOn(): string
{
return Declarations::EVAL_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('frequency', ChoiceType::class, [
'choices' => self::CHOICES,
'multiple' => false,
'expanded' => true,
'empty_data' => self::DEFAULT_CHOICE,
'data' => self::DEFAULT_CHOICE,
]);
}
public function getLabels($key, array $values, $data)
{
return static function ($value): string {
if ('_header' === $value) {
return 'export.aggregator.eval.by_max_date.Max date';
}
if (null === $value) {
return '';
}
return $value;
};
}
public function getQueryKeys($data): array
{
return ['eval_by_max_date_aggregator'];
}
public function getTitle(): string
{
return 'export.aggregator.eval.by_max_date.Group by max date evaluations';
}
}

View File

@@ -0,0 +1,106 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Export\Aggregator\EvaluationAggregators;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\QueryBuilder;
use LogicException;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
class ByStartDateAggregator implements AggregatorInterface
{
private const CHOICES = [
'by week' => 'week',
'by month' => 'month',
'by year' => 'year',
];
private const DEFAULT_CHOICE = 'year';
private TranslatorInterface $translator;
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
switch ($data['frequency']) {
case 'week':
$fmt = 'YYYY-IW';
break;
case 'month':
$fmt = 'YYYY-MM';
break;
case 'year':
$fmt = 'YYYY';
break;
default:
throw new LogicException(sprintf("The frequency data '%s' is invalid.", $data['frequency']));
}
$qb->addSelect(sprintf("TO_CHAR(workeval.startDate, '%s') AS eval_by_start_date_aggregator", $fmt));
$qb->addGroupBy(' eval_by_start_date_aggregator');
$qb->addOrderBy(' eval_by_start_date_aggregator', 'ASC');
}
public function applyOn(): string
{
return Declarations::EVAL_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('frequency', ChoiceType::class, [
'choices' => self::CHOICES,
'multiple' => false,
'expanded' => true,
'empty_data' => self::DEFAULT_CHOICE,
'data' => self::DEFAULT_CHOICE,
]);
}
public function getLabels($key, array $values, $data)
{
return static function ($value): string {
if ('_header' === $value) {
return 'export.aggregator.eval.by_start_date_period.Start date period';
}
if (null === $value) {
return '';
}
return $value;
};
}
public function getQueryKeys($data): array
{
return ['eval_by_start_date_aggregator'];
}
public function getTitle(): string
{
return 'export.aggregator.eval.by_start_date_period.Group by start date evaluations';
}
}

View File

@@ -32,22 +32,15 @@ class EvaluationTypeAggregator implements AggregatorInterface
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole()
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$qb->addSelect('IDENTITY(eval.evaluation) AS evaluationtype_aggregator');
$groupBy = $qb->getDQLPart('groupBy');
if (!empty($groupBy)) {
$qb->addGroupBy('evaluationtype_aggregator');
} else {
$qb->groupBy('evaluationtype_aggregator');
}
$qb->addSelect('IDENTITY(workeval.evaluation) AS eval_evaluationtype_aggregator');
$qb->addGroupBy('eval_evaluationtype_aggregator');
}
public function applyOn(): string
@@ -67,6 +60,10 @@ class EvaluationTypeAggregator implements AggregatorInterface
return 'Evaluation type';
}
if (null === $value) {
return '';
}
$ev = $this->evaluationRepository->find($value);
return $this->translatableStringHelper->localize($ev->getTitle());
@@ -75,7 +72,7 @@ class EvaluationTypeAggregator implements AggregatorInterface
public function getQueryKeys($data): array
{
return ['evaluationtype_aggregator'];
return ['eval_evaluationtype_aggregator'];
}
public function getTitle(): string

View File

@@ -0,0 +1,81 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Export\Aggregator\EvaluationAggregators;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\QueryBuilder;
use LogicException;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
class HavingEndDateAggregator implements AggregatorInterface
{
private TranslatorInterface $translator;
public function __construct(TranslatorInterface $translator)
{
$this->translator = $translator;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$qb
->addSelect('CASE WHEN workeval.endDate IS NULL THEN true ELSE false END AS eval_enddate_aggregator')
->addGroupBy('eval_enddate_aggregator');
}
public function applyOn(): string
{
return Declarations::EVAL_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
{
// No form needed
}
public function getLabels($key, array $values, $data)
{
return function ($value): string {
if ('_header' === $value) {
return 'export.aggregator.eval.by_end_date.Has end date ?';
}
switch ($value) {
case true:
return $this->translator->trans('export.aggregator.eval.by_end_date.enddate is specified');
case false:
return $this->translator->trans('export.aggregator.eval.by_end_date.enddate is not specified');
default:
throw new LogicException(sprintf('The value %s is not valid', $value));
}
};
}
public function getQueryKeys($data): array
{
return ['eval_enddate_aggregator'];
}
public function getTitle(): string
{
return 'export.aggregator.eval.by_end_date.Group evaluations having end date';
}
}

View File

@@ -12,9 +12,11 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Export\Aggregator\HouseholdAggregators;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\PersonBundle\Export\Declarations;
use DateTime;
use Doctrine\ORM\Query\Expr;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
@@ -22,44 +24,44 @@ use function in_array;
class ChildrenNumberAggregator implements AggregatorInterface
{
private RollingDateConverterInterface $rollingDateConverter;
private TranslatorInterface $translator;
public function __construct(
TranslatorInterface $translator
TranslatorInterface $translator,
RollingDateConverterInterface $rollingDateConverter
) {
$this->translator = $translator;
$this->rollingDateConverter = $rollingDateConverter;
}
public function addRole()
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('composition', $qb->getAllAliases(), true)) {
$qb->join('household.compositions', 'composition');
if (!in_array('composition_children', $qb->getAllAliases(), true)) {
$clause = $qb->expr()->andX(
$qb->expr()->lte('composition_children.startDate', ':ondate_composition_children'),
$qb->expr()->orX(
$qb->expr()->gt('composition_children.endDate', ':ondate_composition_children'),
$qb->expr()->isNull('composition_children.endDate')
)
);
$qb->leftJoin('household.compositions', 'composition_children', Expr\Join::WITH, $clause);
}
$qb->addSelect('composition.numberOfChildren AS childrennumber_aggregator');
$qb
->addSelect('composition_children.numberOfChildren AS childrennumber_aggregator')
->addGroupBy('childrennumber_aggregator');
$groupBy = $qb->getDQLPart('groupBy');
if (!empty($groupBy)) {
$qb->addGroupBy('childrennumber_aggregator');
} else {
$qb->groupBy('childrennumber_aggregator');
}
// add date in where clause
$where = $qb->getDQLPart('where');
$clause = $qb->expr()->andX(
$qb->expr()->lte('composition.startDate', ':ondate'),
$qb->expr()->orX(
$qb->expr()->gt('composition.endDate', ':ondate'),
$qb->expr()->isNull('composition.endDate')
)
$qb->setParameter(
'ondate_composition_children',
$this->rollingDateConverter->convert($data['on_date'])
);
}
@@ -70,24 +72,23 @@ class ChildrenNumberAggregator implements AggregatorInterface
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('on_date', ChillDateType::class, [
'data' => new DateTime('now'),
$builder->add('on_date', PickRollingDateType::class, [
'data' => new RollingDate(RollingDate::T_TODAY),
]);
}
public function getLabels($key, array $values, $data)
{
return function ($value): string {
return static function ($value): string {
if ('_header' === $value) {
return 'Number of children';
}
return $this->translator->trans(
'household_composition.numberOfChildren children in household',
[
'numberOfChildren' => $value,
]
);
if (null === $value) {
return '';
}
return $value;
};
}

View File

@@ -12,71 +12,62 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Export\Aggregator\HouseholdAggregators;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Repository\Household\HouseholdCompositionTypeRepository;
use DateTime;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\Query\Expr;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use function in_array;
class CompositionAggregator implements AggregatorInterface
{
private RollingDateConverterInterface $rollingDateConverter;
private TranslatableStringHelper $translatableStringHelper;
private HouseholdCompositionTypeRepository $typeRepository;
public function __construct(
HouseholdCompositionTypeRepository $typeRepository,
TranslatableStringHelper $translatableStringHelper
TranslatableStringHelper $translatableStringHelper,
RollingDateConverterInterface $rollingDateConverter
) {
$this->typeRepository = $typeRepository;
$this->translatableStringHelper = $translatableStringHelper;
$this->rollingDateConverter = $rollingDateConverter;
}
public function addRole()
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('composition', $qb->getAllAliases(), true)) {
$qb->join('household.compositions', 'composition');
if (!in_array('composition_type', $qb->getAllAliases(), true)) {
$clause = $qb->expr()->andX(
$qb->expr()->lte('composition_type.startDate', ':ondate_composition_type'),
$qb->expr()->orX(
$qb->expr()->gt('composition_type.endDate', ':ondate_composition_type'),
$qb->expr()->isNull('composition_type.endDate')
)
);
$qb->leftJoin('household.compositions', 'composition_type', Expr\Join::WITH, $clause);
}
$qb->addSelect('IDENTITY(composition.householdCompositionType) AS composition_aggregator');
$qb
->addSelect('IDENTITY(composition_type.householdCompositionType) AS composition_aggregator')
->addGroupBy('composition_aggregator');
$groupBy = $qb->getDQLPart('groupBy');
if (!empty($groupBy)) {
$qb->addGroupBy('composition_aggregator');
} else {
$qb->groupBy('composition_aggregator');
}
// add date in where clause
$where = $qb->getDQLPart('where');
$clause = $qb->expr()->andX(
$qb->expr()->lte('composition.startDate', ':ondate'),
$qb->expr()->orX(
$qb->expr()->gt('composition.endDate', ':ondate'),
$qb->expr()->isNull('composition.endDate')
)
$qb->setParameter(
'ondate_composition_type',
$this->rollingDateConverter->convert($data['on_date'])
);
if ($where instanceof Andx) {
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->setParameter('ondate', $data['on_date'], Types::DATE_MUTABLE);
}
public function applyOn(): string
@@ -86,8 +77,8 @@ class CompositionAggregator implements AggregatorInterface
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('on_date', ChillDateType::class, [
'data' => new DateTime('now'),
$builder->add('on_date', PickRollingDateType::class, [
'data' => new RollingDate(RollingDate::T_TODAY),
]);
}
@@ -98,6 +89,10 @@ class CompositionAggregator implements AggregatorInterface
return 'Composition';
}
if (null === $value) {
return '';
}
$c = $this->typeRepository->find($value);
return $this->translatableStringHelper->localize(

View File

@@ -29,7 +29,7 @@ final class AgeAggregator implements AggregatorInterface, ExportElementValidated
$this->translator = $translator;
}
public function addRole()
public function addRole(): ?string
{
return null;
}

View File

@@ -0,0 +1,129 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Export\Aggregator\PersonAggregators;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Entity\Household\HouseholdComposition;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Repository\Household\HouseholdCompositionTypeRepositoryInterface;
use Doctrine\ORM\Query\Expr\Join;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
class ByHouseholdCompositionAggregator implements AggregatorInterface
{
private const PREFIX = 'acp_by_household_compo_agg';
private HouseholdCompositionTypeRepositoryInterface $householdCompositionTypeRepository;
private RollingDateConverterInterface $rollingDateConverter;
private TranslatableStringHelperInterface $translatableStringHelper;
public function __construct(
RollingDateConverterInterface $rollingDateConverter,
HouseholdCompositionTypeRepositoryInterface $householdCompositionTypeRepository,
TranslatableStringHelperInterface $translatableStringHelper
) {
$this->householdCompositionTypeRepository = $householdCompositionTypeRepository;
$this->translatableStringHelper = $translatableStringHelper;
$this->rollingDateConverter = $rollingDateConverter;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$p = self::PREFIX;
$qb
->leftJoin(
'person.householdParticipations',
"{$p}_hm"
)
->leftJoin(
HouseholdComposition::class,
"{$p}_compo",
Join::WITH,
$qb->expr()->eq("{$p}_hm.household", "{$p}_compo.household"),
)
->andWhere("{$p}_hm.startDate <= :{$p}_date AND ({$p}_hm.endDate IS NULL OR {$p}_hm.endDate > :{$p}_date)")
->andWhere("{$p}_hm.shareHousehold = 'TRUE'")
->andWhere(
$qb->expr()->orX(
$qb->expr()->isNull("{$p}_compo"),
$qb->expr()->andX(
$qb->expr()->lte("{$p}_compo.startDate", ":{$p}_date"),
$qb->expr()->orX(
$qb->expr()->isNull("{$p}_compo.endDate"),
$qb->expr()->gt("{$p}_compo.endDate", ":{$p}_date")
)
)
)
)
->addSelect("IDENTITY({$p}_compo.householdCompositionType) AS {$p}_select")
->setParameter(
"{$p}_date",
$this->rollingDateConverter->convert($data['date_calc'])
)
->addGroupBy("{$p}_select");
}
public function applyOn()
{
return Declarations::PERSON_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('date_calc', PickRollingDateType::class, [
'label' => 'export.aggregator.person.by_household_composition.Calc date',
'data' => new RollingDate(RollingDate::T_TODAY),
]);
}
public function getLabels($key, array $values, $data)
{
return function ($value) {
if ('_header' === $value) {
return 'export.aggregator.person.by_household_composition.Household composition';
}
if (null === $value) {
return '';
}
if (null === $o = $this->householdCompositionTypeRepository->find($value)) {
return '';
}
return $this->translatableStringHelper->localize($o->getLabel());
};
}
public function getQueryKeys($data)
{
return [self::PREFIX . '_select'];
}
public function getTitle()
{
return 'export.aggregator.person.by_household_composition.Group course by household composition';
}
}

View File

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

View File

@@ -28,7 +28,7 @@ final class GenderAggregator implements AggregatorInterface
$this->translator = $translator;
}
public function addRole()
public function addRole(): ?string
{
return null;
}
@@ -62,6 +62,9 @@ final class GenderAggregator implements AggregatorInterface
case Person::BOTH_GENDER:
return $this->translator->trans('both');
case Person::NO_INFORMATION:
return $this->translator->trans('unknown');
case null:
return $this->translator->trans('Not given');

View File

@@ -0,0 +1,157 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Export\Aggregator\PersonAggregators;
use Chill\MainBundle\Entity\GeographicalUnitLayer;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Repository\GeographicalUnitLayerRepositoryInterface;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\QueryBuilder;
use LogicException;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface;
class GeographicalUnitAggregator implements AggregatorInterface
{
private GeographicalUnitLayerRepositoryInterface $geographicalUnitLayerRepository;
private RollingDateConverterInterface $rollingDateConverter;
private TranslatableStringHelperInterface $translatableStringHelper;
public function __construct(
GeographicalUnitLayerRepositoryInterface $geographicalUnitLayerRepository,
TranslatableStringHelperInterface $translatableStringHelper,
RollingDateConverterInterface $rollingDateConverter
) {
$this->geographicalUnitLayerRepository = $geographicalUnitLayerRepository;
$this->translatableStringHelper = $translatableStringHelper;
$this->rollingDateConverter = $rollingDateConverter;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data): void
{
$qb
->leftJoin('person.householdAddresses', 'person_geog_agg_current_household_address')
->leftJoin('person_geog_agg_current_household_address.address', 'person_geog_agg_address')
->leftJoin('person_geog_agg_address.geographicalUnits', 'person_geog_agg_geog_unit')
->andWhere(
$qb->expr()->orX(
$qb->expr()->isNull('person_geog_agg_current_household_address'),
$qb->expr()->andX(
$qb->expr()->lte('person_geog_agg_current_household_address.validFrom', ':person_geog_agg_date'),
$qb->expr()->orX(
$qb->expr()->isNull('person_geog_agg_current_household_address.validTo'),
$qb->expr()->gt('person_geog_agg_current_household_address.validTo', ':person_geog_agg_date')
)
)
)
)
->andWhere(
$qb->expr()->orX(
$qb->expr()->isNull('person_geog_agg_geog_unit'),
$qb->expr()->in('person_geog_agg_geog_unit.layer', ':person_geog_agg_layers')
)
)
->setParameter(
'person_geog_agg_date',
$this->rollingDateConverter->convert($data['date_calc'])
)
->setParameter('person_geog_agg_layers', $data['level'])
->addSelect('person_geog_agg_geog_unit.unitName AS geog_unit_name')
->addSelect('person_geog_agg_geog_unit.unitRefId AS geog_unit_key')
->addGroupBy('geog_unit_name')
->addGroupBy('geog_unit_key');
}
public function applyOn()
{
return Declarations::PERSON_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder
->add('date_calc', PickRollingDateType::class, [
'label' => 'Address valid at this date',
'required' => true,
'data' => new RollingDate(RollingDate::T_TODAY),
])
->add('level', EntityType::class, [
'label' => 'Geographical layer',
'placeholder' => 'Select a geographical layer',
'class' => GeographicalUnitLayer::class,
'choices' => $this->geographicalUnitLayerRepository->findAllHavingUnits(),
'choice_label' => function (GeographicalUnitLayer $item) {
return $this->translatableStringHelper->localize($item->getName());
},
]);
}
public static function getDefaultAlias(): string
{
return 'person_geog_agg';
}
public function getLabels($key, array $values, $data)
{
switch ($key) {
case 'geog_unit_name':
return static function ($value): string {
if ('_header' === $value) {
return 'acp_geog_agg_unitname';
}
if (null === $value) {
return '';
}
return $value;
};
case 'geog_unit_key':
return static function ($value): string {
if ('_header' === $value) {
return 'acp_geog_agg_unitrefid';
}
if (null === $value) {
return '';
}
return $value;
};
default:
throw new LogicException('key not supported');
}
}
public function getQueryKeys($data)
{
return ['geog_unit_name', 'geog_unit_key'];
}
public function getTitle()
{
return 'Group people by geographical unit based on his address';
}
}

View File

@@ -13,74 +13,84 @@ namespace Chill\PersonBundle\Export\Aggregator\PersonAggregators;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Export\ExportElementValidatedInterface;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\PersonBundle\Entity\Household\HouseholdMember;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Repository\Household\PositionRepository;
use DateTime;
use Doctrine\ORM\Query\Expr;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use function in_array;
final class HouseholdPositionAggregator implements AggregatorInterface, ExportElementValidatedInterface
{
private PositionRepository $positionRepository;
private RollingDateConverterInterface $rollingDateConverter;
private TranslatableStringHelper $translatableStringHelper;
private TranslatorInterface $translator;
public function __construct(TranslatorInterface $translator, TranslatableStringHelper $translatableStringHelper, PositionRepository $positionRepository)
{
public function __construct(
TranslatorInterface $translator,
TranslatableStringHelper $translatableStringHelper,
PositionRepository $positionRepository,
RollingDateConverterInterface $rollingDateConverter
) {
$this->translator = $translator;
$this->positionRepository = $positionRepository;
$this->translatableStringHelper = $translatableStringHelper;
$this->rollingDateConverter = $rollingDateConverter;
}
public function addRole()
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$qb->resetDQLPart('from');
$qb->from('ChillPersonBundle:Household\HouseholdMember', 'hm');
if (!in_array('householdmember', $qb->getAllAliases(), true)) {
$qb->join(HouseholdMember::class, 'householdmember', Expr\Join::WITH, 'householdmember.person = person');
}
$qb->join('hm.person', 'person');
$qb->join('person.center', 'center');
if (!in_array('center', $qb->getAllAliases(), true)) {
$qb->join('person.center', 'center');
}
$qb->andWhere($qb->expr()->andX(
$qb->expr()->lte('hm.startDate', ':date'),
$qb->expr()->lte('householdmember.startDate', ':date'),
$qb->expr()->orX(
$qb->expr()->isNull('hm.endDate'),
$qb->expr()->gte('hm.endDate', ':date')
$qb->expr()->isNull('householdmember.endDate'),
$qb->expr()->gte('householdmember.endDate', ':date')
)
));
$qb->setParameter('date', $data['date_position']);
$qb->setParameter(
'date',
$this->rollingDateConverter->convert($data['date_position'])
);
$qb->addSelect('IDENTITY(hm.position) AS household_position_aggregator');
$groupBy = $qb->getDQLPart('groupBy');
if (!empty($groupBy)) {
$qb->addGroupBy('household_position_aggregator');
} else {
$qb->groupBy('household_position_aggregator');
}
$qb->addSelect('IDENTITY(householdmember.position) AS household_position_aggregator');
$qb->addGroupBy('household_position_aggregator');
}
public function applyOn()
{
return 'person';
return Declarations::PERSON_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('date_position', ChillDateType::class, [
$builder->add('date_position', PickRollingDateType::class, [
'label' => 'Household position in relation to this date',
'data' => new DateTime(),
'data' => new RollingDate(RollingDate::T_TODAY),
]);
}

View File

@@ -17,6 +17,7 @@ use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Repository\MaritalStatusRepository;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use function in_array;
final class MaritalStatusAggregator implements AggregatorInterface
{
@@ -30,23 +31,19 @@ final class MaritalStatusAggregator implements AggregatorInterface
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole()
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$qb->join('person.maritalStatus', 'ms');
$qb->addSelect('ms.id as marital_status_aggregator');
$groupBy = $qb->getDQLPart('groupBy');
if (!empty($groupBy)) {
$qb->addGroupBy('marital_status_aggregator');
} else {
$qb->groupBy('marital_status_aggregator');
if (!in_array('personmarital', $qb->getAllAliases(), true)) {
$qb->join('person.maritalStatus', 'personmarital');
}
$qb->addSelect('personmarital.id as marital_status_aggregator');
$qb->addGroupBy('marital_status_aggregator');
}
public function applyOn()
@@ -56,6 +53,7 @@ final class MaritalStatusAggregator implements AggregatorInterface
public function buildForm(FormBuilderInterface $builder)
{
// no form
}
public function getLabels($key, array $values, $data)

View File

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

View File

@@ -12,41 +12,58 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Export\Aggregator\SocialWorkAggregators;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Repository\SocialWork\SocialActionRepository;
use Chill\PersonBundle\Repository\SocialWork\SocialIssueRepository;
use Chill\PersonBundle\Templating\Entity\SocialActionRender;
use Chill\PersonBundle\Templating\Entity\SocialIssueRender;
use Doctrine\ORM\QueryBuilder;
use LogicException;
use Symfony\Component\Form\FormBuilderInterface;
use function in_array;
final class ActionTypeAggregator implements AggregatorInterface
{
private SocialActionRender $actionRender;
private SocialActionRepository $socialActionRepository;
private TranslatableStringHelper $translatableStringHelper;
private SocialIssueRender $socialIssueRender;
public function __construct(SocialActionRepository $socialActionRepository, TranslatableStringHelper $translatableStringHelper)
{
private SocialIssueRepository $socialIssueRepository;
public function __construct(
SocialActionRepository $socialActionRepository,
SocialActionRender $actionRender,
SocialIssueRender $socialIssueRender,
SocialIssueRepository $socialIssueRepository
) {
$this->socialActionRepository = $socialActionRepository;
$this->translatableStringHelper = $translatableStringHelper;
$this->actionRender = $actionRender;
$this->socialIssueRender = $socialIssueRender;
$this->socialIssueRepository = $socialIssueRepository;
}
public function addRole()
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$qb->join('acpw.socialAction', 'sa');
$qb->addSelect('sa.id as action_type_aggregator');
$groupBy = $qb->getDQLPart('groupBy');
if (!empty($groupBy)) {
$qb->addGroupBy('action_type_aggregator');
} else {
$qb->groupBy('action_type_aggregator');
if (!in_array('acpwsocialaction', $qb->getAllAliases(), true)) {
$qb->leftJoin('acpw.socialAction', 'acpwsocialaction');
}
if (!in_array('acpwsocialissue', $qb->getAllAliases(), true)) {
$qb->leftJoin('acpwsocialaction.issue', 'acpwsocialissue');
}
$qb
->addSelect('acpwsocialissue.id as social_action_type_aggregator')
->addSelect('acpwsocialaction.id as action_type_aggregator')
->addGroupBy('action_type_aggregator')
->addGroupBy('social_action_type_aggregator');
}
public function applyOn()
@@ -61,21 +78,41 @@ final class ActionTypeAggregator implements AggregatorInterface
public function getLabels($key, array $values, $data)
{
//TODO certain social actions have the same title as other, but are linked to different social issues, should we make a visual distinction?
return function ($value): string {
if ('_header' === $value) {
return 'Social Action Type';
}
switch ($key) {
case 'action_type_aggregator':
return function ($value): string {
if ('_header' === $value) {
return 'Social Action Type';
}
$sa = $this->socialActionRepository->find($value);
if (null === $value || null === $sa = $this->socialActionRepository->find($value)) {
return '';
}
return $this->translatableStringHelper->localize($sa->getTitle());
};
return $this->actionRender->renderString($sa, []);
};
case 'social_action_type_aggregator':
return function ($value): string {
if ('_header' === $value) {
return 'Social Issue';
}
if (null === $value || null === $si = $this->socialIssueRepository->find($value)) {
return '';
}
return $this->socialIssueRender->renderString($si, []);
};
default:
throw new LogicException('this key is not supported: ' . $key);
}
}
public function getQueryKeys($data)
{
return ['action_type_aggregator'];
return ['social_action_type_aggregator', 'action_type_aggregator'];
}
public function getTitle()

View File

@@ -0,0 +1,84 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Export\Aggregator\SocialWorkAggregators;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\QueryBuilder;
use LogicException;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
class CurrentActionAggregator implements AggregatorInterface
{
private TranslatorInterface $translator;
public function __construct(TranslatorInterface $translator)
{
$this->translator = $translator;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data): void
{
$qb
->addSelect('
(CASE WHEN acpw.endDate IS NULL THEN true ELSE false END)
AS acpw_current_action_aggregator
')
->addGroupBy('acpw_current_action_aggregator');
}
public function applyOn(): string
{
return Declarations::SOCIAL_WORK_ACTION_TYPE;
}
public function buildForm(FormBuilderInterface $builder): void
{
// No form needed
}
public function getLabels($key, array $values, $data)
{
return function ($value): string {
if ('_header' === $value) {
return 'export.aggregator.course_work.by_current_action.Current action ?';
}
switch ($value) {
case true:
return $this->translator->trans('export.aggregator.course_work.by_current_action.Current action');
case false:
return $this->translator->trans('export.aggregator.course_work.by_current_action.Not current action');
default:
throw new LogicException(sprintf('The value %s is not valid', $value));
}
};
}
public function getQueryKeys($data): array
{
return ['acpw_current_action_aggregator'];
}
public function getTitle(): string
{
return 'export.aggregator.course_work.by_current_action.Group by current actions';
}
}

View File

@@ -17,6 +17,7 @@ use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Repository\SocialWork\GoalRepository;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use function in_array;
final class GoalAggregator implements AggregatorInterface
{
@@ -30,26 +31,22 @@ final class GoalAggregator implements AggregatorInterface
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole()
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$qb->join('acpw.goals', 'g');
$qb->addSelect('g.id as goal_aggregator');
$groupBy = $qb->getDQLPart('groupBy');
if (!empty($groupBy)) {
$qb->addGroupBy('goal_aggregator');
} else {
$qb->groupBy('goal_aggregator');
if (!in_array('goal', $qb->getAllAliases(), true)) {
$qb->leftJoin('acpw.goals', 'goal');
}
$qb->addSelect('IDENTITY(goal.goal) as acpw_goal_aggregator');
$qb->addGroupBy('acpw_goal_aggregator');
}
public function applyOn()
public function applyOn(): string
{
return Declarations::SOCIAL_WORK_ACTION_TYPE;
}
@@ -66,18 +63,24 @@ final class GoalAggregator implements AggregatorInterface
return 'Goal Type';
}
if (null === $value) {
return '';
}
$g = $this->goalRepository->find($value);
return $this->translatableStringHelper->localize($g->getTitle());
return $this->translatableStringHelper->localize(
$g->getTitle()
);
};
}
public function getQueryKeys($data)
public function getQueryKeys($data): array
{
return ['goal_aggregator'];
return ['acpw_goal_aggregator'];
}
public function getTitle()
public function getTitle(): string
{
return 'Group social work actions by goal';
}

View File

@@ -0,0 +1,120 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Export\Aggregator\SocialWorkAggregators;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Repository\SocialWork\GoalRepository;
use Chill\PersonBundle\Repository\SocialWork\ResultRepository;
use Doctrine\ORM\QueryBuilder;
use LogicException;
use Symfony\Component\Form\FormBuilderInterface;
use function in_array;
class GoalResultAggregator implements AggregatorInterface
{
private GoalRepository $goalRepository;
private ResultRepository $resultRepository;
private TranslatableStringHelper $translatableStringHelper;
public function __construct(
ResultRepository $resultRepository,
GoalRepository $goalRepository,
TranslatableStringHelper $translatableStringHelper
) {
$this->resultRepository = $resultRepository;
$this->goalRepository = $goalRepository;
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('goal', $qb->getAllAliases(), true)) {
$qb->leftJoin('acpw.goals', 'goal');
}
if (!in_array('goalresult', $qb->getAllAliases(), true)) {
$qb->leftJoin('goal.results', 'goalresult');
}
$qb->addSelect('IDENTITY(goal.goal) as goal_aggregator');
$qb->addSelect('goalresult.id as result_aggregator');
$qb->addGroupBy('goal_aggregator')->addGroupBy('result_aggregator');
}
public function applyOn(): string
{
return Declarations::SOCIAL_WORK_ACTION_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
{
// no form
}
public function getLabels($key, array $values, $data)
{
return function ($value) use ($key): string {
if (null === $value) {
return '';
}
switch ($key) {
case 'goal_aggregator':
if ('_header' === $value) {
return 'Goal Type';
}
$g = $this->goalRepository->find($value);
return $this->translatableStringHelper->localize(
$g->getTitle()
);
case 'result_aggregator':
if ('_header' === $value) {
return 'Result Type';
}
$r = $this->resultRepository->find($value);
return $this->translatableStringHelper->localize(
$r->getTitle()
);
default:
throw new LogicException();
}
};
}
public function getQueryKeys($data): array
{
return [
'goal_aggregator',
'result_aggregator',
];
}
public function getTitle(): string
{
return 'Group social work actions by goal and result';
}
}

View File

@@ -17,6 +17,7 @@ use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use function in_array;
final class JobAggregator implements AggregatorInterface
{
@@ -32,24 +33,19 @@ final class JobAggregator implements AggregatorInterface
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole()
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$qb->join('acpw.referrers', 'u');
$qb->addSelect('IDENTITY(u.userJob) as job_aggregator');
$groupBy = $qb->getDQLPart('groupBy');
if (!empty($groupBy)) {
$qb->addGroupBy('job_aggregator');
} else {
$qb->groupBy('job_aggregator');
if (!in_array('acpwuser', $qb->getAllAliases(), true)) {
$qb->leftJoin('acpw.referrers', 'acpwuser');
}
$qb->addSelect('IDENTITY(acpwuser.userJob) as job_aggregator')
->addGroupBy('job_aggregator');
}
public function applyOn(): string
@@ -69,6 +65,10 @@ final class JobAggregator implements AggregatorInterface
return 'Job';
}
if (null === $value) {
return '';
}
$j = $this->jobRepository->find($value);
return $this->translatableStringHelper->localize(

View File

@@ -17,6 +17,7 @@ use Chill\MainBundle\Templating\Entity\UserRender;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use function in_array;
final class ReferrerAggregator implements AggregatorInterface
{
@@ -32,24 +33,19 @@ final class ReferrerAggregator implements AggregatorInterface
$this->userRender = $userRender;
}
public function addRole()
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$qb->join('acpw.referrers', 'u');
$qb->addSelect('u.id AS referrer_aggregator');
$groupBy = $qb->getDQLPart('groupBy');
if (!empty($groupBy)) {
$qb->addGroupBy('referrer_aggregator');
} else {
$qb->groupBy('referrer_aggregator');
if (!in_array('acpwuser', $qb->getAllAliases(), true)) {
$qb->leftJoin('acpw.referrers', 'acpwuser');
}
$qb->addSelect('acpwuser.id AS referrer_aggregator');
$qb->addGroupBy('referrer_aggregator');
}
public function applyOn(): string
@@ -69,6 +65,10 @@ final class ReferrerAggregator implements AggregatorInterface
return 'Referrer';
}
if (null === $value) {
return '';
}
$r = $this->userRepository->find($value);
return $this->userRender->renderString($r, []);

View File

@@ -17,6 +17,7 @@ use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Repository\SocialWork\ResultRepository;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use function in_array;
final class ResultAggregator implements AggregatorInterface
{
@@ -30,28 +31,22 @@ final class ResultAggregator implements AggregatorInterface
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole()
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$qb->join('acpw.results', 'res');
$qb->join('acpw.goals', 'g');
$qb->join('g.results', 'gres');
$qb->addSelect('res.id, IDENTITY(g.results) as result_aggregator');
$groupBy = $qb->getDQLPart('groupBy');
if (!empty($groupBy)) {
$qb->addGroupBy('result_aggregator');
} else {
$qb->groupBy('result_aggregator');
if (!in_array('result', $qb->getAllAliases(), true)) {
$qb->leftJoin('acpw.results', 'result');
}
$qb->addSelect('result.id AS acpw_result_aggregator');
$qb->addGroupBy('acpw_result_aggregator');
}
public function applyOn()
public function applyOn(): string
{
return Declarations::SOCIAL_WORK_ACTION_TYPE;
}
@@ -68,18 +63,24 @@ final class ResultAggregator implements AggregatorInterface
return 'Result Type';
}
$g = $this->resultRepository->find($value);
if (null === $value) {
return '';
}
return $this->translatableStringHelper->localize($g->getTitle());
$r = $this->resultRepository->find($value);
return $this->translatableStringHelper->localize(
$r->getTitle()
);
};
}
public function getQueryKeys($data)
public function getQueryKeys($data): array
{
return ['result_aggregator'];
return ['acpw_result_aggregator'];
}
public function getTitle()
public function getTitle(): string
{
return 'Group social work actions by result';
}

View File

@@ -17,6 +17,7 @@ use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use function in_array;
final class ScopeAggregator implements AggregatorInterface
{
@@ -32,24 +33,19 @@ final class ScopeAggregator implements AggregatorInterface
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole()
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$qb->join('acpw.referrers', 'u');
$qb->addSelect('IDENTITY(u.mainScope) as scope_aggregator');
$groupBy = $qb->getDQLPart('groupBy');
if (!empty($groupBy)) {
$qb->addGroupBy('scope_aggregator');
} else {
$qb->groupBy('scope_aggregator');
if (!in_array('acpwuser', $qb->getAllAliases(), true)) {
$qb->leftJoin('acpw.referrers', 'acpwuser');
}
$qb->addSelect('IDENTITY(acpwuser.mainScope) as scope_aggregator');
$qb->addGroupBy('scope_aggregator');
}
public function applyOn(): string
@@ -69,6 +65,10 @@ final class ScopeAggregator implements AggregatorInterface
return 'Scope';
}
if (null === $value) {
return '';
}
$s = $this->scopeRepository->find($value);
return $this->translatableStringHelper->localize(

View File

@@ -15,6 +15,7 @@ use Chill\MainBundle\Export\ExportInterface;
use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\GroupedExportInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person\PersonCenterHistory;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
use Doctrine\ORM\EntityManagerInterface;
@@ -23,7 +24,6 @@ use Doctrine\ORM\Query;
use Doctrine\ORM\QueryBuilder;
use LogicException;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Security\Core\Role\Role;
class CountAccompanyingCourse implements ExportInterface, GroupedExportInterface
{
@@ -37,7 +37,7 @@ class CountAccompanyingCourse implements ExportInterface, GroupedExportInterface
public function buildForm(FormBuilderInterface $builder): void
{
// TODO: Implement buildForm() method.
// Nothing to add here
}
public function getAllowedFormattersTypes(): array
@@ -74,9 +74,9 @@ class CountAccompanyingCourse implements ExportInterface, GroupedExportInterface
return ['export_result'];
}
public function getResult($qb, $data)
public function getResult($query, $data)
{
return $qb->getQuery()->getResult(Query::HYDRATE_SCALAR);
return $query->getQuery()->getResult(Query::HYDRATE_SCALAR);
}
public function getTitle(): string
@@ -91,22 +91,42 @@ class CountAccompanyingCourse implements ExportInterface, GroupedExportInterface
public function initiateQuery(array $requiredModifiers, array $acl, array $data = []): QueryBuilder
{
$centers = array_map(static function ($el) {
return $el['center'];
}, $acl);
$qb = $this->repository->createQueryBuilder('acp');
$qb->select('COUNT(acp.id) AS export_result');
$qb
->andWhere('acp.step != :count_acp_step')
->leftJoin('acp.participations', 'acppart')
->leftJoin('acppart.person', 'person')
->andWhere('acppart.startDate != acppart.endDate OR acppart.endDate IS NULL')
->andWhere(
$qb->expr()->exists(
'SELECT 1 FROM ' . PersonCenterHistory::class . ' acl_count_person_history WHERE acl_count_person_history.person = person
AND acl_count_person_history.center IN (:authorized_centers)
'
)
)
->setParameter('count_acp_step', AccompanyingPeriod::STEP_DRAFT)
->setParameter('authorized_centers', $centers);
$qb->select('COUNT(DISTINCT acp.id) AS export_result');
return $qb;
}
public function requiredRole(): Role
public function requiredRole(): string
{
return new Role(AccompanyingPeriodVoter::STATS);
return AccompanyingPeriodVoter::STATS;
}
public function supportsModifiers(): array
{
return [
Declarations::ACP_TYPE,
Declarations::PERSON_TYPE,
];
}
}

View File

@@ -15,22 +15,22 @@ use Chill\MainBundle\Export\ExportInterface;
use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\GroupedExportInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person\PersonCenterHistory;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query;
use LogicException;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Security\Core\Role\Role;
class CountEvaluation implements ExportInterface, GroupedExportInterface
{
private EntityRepository $repository;
private EntityManagerInterface $entityManager;
public function __construct(
EntityManagerInterface $em
) {
$this->repository = $em->getRepository(AccompanyingPeriod::class);
$this->entityManager = $em;
}
public function buildForm(FormBuilderInterface $builder)
@@ -72,9 +72,9 @@ class CountEvaluation implements ExportInterface, GroupedExportInterface
return ['export_result'];
}
public function getResult($qb, $data)
public function getResult($query, $data)
{
return $qb->getQuery()->getResult(Query::HYDRATE_SCALAR);
return $query->getQuery()->getResult(Query::HYDRATE_SCALAR);
}
public function getTitle(): string
@@ -89,26 +89,45 @@ class CountEvaluation implements ExportInterface, GroupedExportInterface
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{
$qb = $this->repository->createQueryBuilder('acp')
->join('acp.works', 'acpw')
->join('acpw.accompanyingPeriodWorkEvaluations', 'eval');
$centers = array_map(static function ($el) {
return $el['center'];
}, $acl);
$qb->select('COUNT(eval.id) AS export_result');
$qb = $this->entityManager->createQueryBuilder();
$qb
->from(AccompanyingPeriod\AccompanyingPeriodWorkEvaluation::class, 'workeval')
->join('workeval.accompanyingPeriodWork', 'acpw')
->join('acpw.accompanyingPeriod', 'acp')
->join('acp.participations', 'acppart')
->join('acppart.person', 'person')
->andWhere('acppart.startDate != acppart.endDate OR acppart.endDate IS NULL')
->andWhere(
$qb->expr()->exists(
'SELECT 1 FROM ' . PersonCenterHistory::class . ' acl_count_person_history WHERE acl_count_person_history.person = person
AND acl_count_person_history.center IN (:authorized_centers)
'
)
)
->setParameter('authorized_centers', $centers);
$qb->select('COUNT(DISTINCT workeval.id) AS export_result');
return $qb;
}
public function requiredRole()
public function requiredRole(): string
{
return new Role(AccompanyingPeriodVoter::STATS);
return AccompanyingPeriodVoter::STATS;
}
public function supportsModifiers(): array
{
return [
Declarations::EVAL_TYPE,
//Declarations::ACP_TYPE,
//Declarations::SOCIAL_WORK_ACTION_TYPE,
Declarations::SOCIAL_WORK_ACTION_TYPE,
Declarations::ACP_TYPE,
Declarations::PERSON_TYPE,
];
}
}

View File

@@ -14,28 +14,42 @@ namespace Chill\PersonBundle\Export\Export;
use Chill\MainBundle\Export\ExportInterface;
use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\GroupedExportInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\PersonBundle\Entity\Household\Household;
use Chill\PersonBundle\Entity\Person\PersonCenterHistory;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
use Chill\PersonBundle\Security\Authorization\HouseholdVoter;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query;
use LogicException;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Security\Core\Role\Role;
class CountHousehold implements ExportInterface, GroupedExportInterface
{
private EntityRepository $repository;
private const TR_PREFIX = 'export.export.nb_household_with_course.';
private EntityManagerInterface $entityManager;
private RollingDateConverterInterface $rollingDateConverter;
public function __construct(
EntityManagerInterface $em
EntityManagerInterface $em,
RollingDateConverterInterface $rollingDateConverter
) {
$this->repository = $em->getRepository(AccompanyingPeriod::class);
$this->entityManager = $em;
$this->rollingDateConverter = $rollingDateConverter;
}
public function buildForm(FormBuilderInterface $builder)
{
// TODO: Implement buildForm() method.
$builder
->add('calc_date', PickRollingDateType::class, [
'data' => new RollingDate(RollingDate::T_TODAY),
'label' => self::TR_PREFIX . 'Date of calculation of household members',
'required' => false,
]);
}
public function getAllowedFormattersTypes(): array
@@ -45,7 +59,7 @@ class CountHousehold implements ExportInterface, GroupedExportInterface
public function getDescription(): string
{
return 'Count household by various parameters.';
return self::TR_PREFIX . 'Count household with accompanying course by various parameters.';
}
public function getGroup(): string
@@ -55,31 +69,41 @@ class CountHousehold implements ExportInterface, GroupedExportInterface
public function getLabels($key, array $values, $data)
{
if ('export_result' !== $key) {
throw new LogicException("the key {$key} is not used by this export");
}
return static function ($value) use ($key) {
if ('_header' === $value) {
switch ($key) {
case 'household_export_result':
return self::TR_PREFIX . 'Count households';
$labels = array_combine($values, $values);
$labels['_header'] = $this->getTitle();
case 'acp_export_result':
return self::TR_PREFIX . 'Count accompanying periods';
return static function ($value) use ($labels) {
return $labels[$value];
default:
throw new LogicException('Key not supported: ' . $key);
}
}
if (null === $value) {
return '';
}
return $value;
};
}
public function getQueryKeys($data): array
{
return ['export_result'];
return ['household_export_result', 'acp_export_result'];
}
public function getResult($qb, $data)
public function getResult($query, $data)
{
return $qb->getQuery()->getResult(Query::HYDRATE_SCALAR);
return $query->getQuery()->getResult(Query::HYDRATE_SCALAR);
}
public function getTitle(): string
{
return 'Count households';
return self::TR_PREFIX . 'Count households with accompanying course';
}
public function getType(): string
@@ -89,28 +113,48 @@ class CountHousehold implements ExportInterface, GroupedExportInterface
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{
$qb = $this->repository->createQueryBuilder('acp')
->join('acp.participations', 'acppart')
->join('acppart.person', 'person')
->join('person.householdParticipations', 'householdmember')
->join('householdmember.household', 'household');
$centers = array_map(static function ($el) {
return $el['center'];
}, $acl);
$qb->select('COUNT(DISTINCT householdmember.household) AS export_result');
$qb = $this->entityManager->createQueryBuilder();
$qb
->from(Household::class, 'household')
->join('household.members', 'hmember')
->join('hmember.person', 'person')
->join('person.accompanyingPeriodParticipations', 'acppart')
->join('acppart.accompanyingPeriod', 'acp')
->andWhere('acppart.startDate != acppart.endDate OR acppart.endDate IS NULL')
->andWhere(
$qb->expr()->exists(
'SELECT 1 FROM ' . PersonCenterHistory::class . ' acl_count_person_history WHERE acl_count_person_history.person = person
AND acl_count_person_history.center IN (:authorized_centers)
'
)
)
->andWhere('hmember.startDate <= :count_household_at_date AND (hmember.endDate IS NULL OR hmember.endDate > :count_household_at_date)')
->setParameter('authorized_centers', $centers)
->setParameter('count_household_at_date', $this->rollingDateConverter->convert($data['calc_date']));
$qb
->select('COUNT(DISTINCT household.id) AS household_export_result')
->addSelect('COUNT(DISTINCT acp.id) AS acp_export_result');
return $qb;
}
public function requiredRole()
public function requiredRole(): string
{
// TODO HouseholdVoter::STATS !??
return new Role(AccompanyingPeriodVoter::STATS);
return HouseholdVoter::STATS;
}
public function supportsModifiers(): array
{
return [
Declarations::HOUSEHOLD_TYPE,
//Declarations::ACP_TYPE
Declarations::ACP_TYPE,
Declarations::PERSON_TYPE,
];
}
}

View File

@@ -14,6 +14,7 @@ namespace Chill\PersonBundle\Export\Export;
use Chill\MainBundle\Export\ExportInterface;
use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\GroupedExportInterface;
use Chill\PersonBundle\Entity\Person\PersonCenterHistory;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Repository\PersonRepository;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
@@ -21,7 +22,6 @@ use Doctrine\ORM\Query;
use Doctrine\ORM\QueryBuilder;
use LogicException;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Security\Core\Role\Role;
class CountPerson implements ExportInterface, GroupedExportInterface
{
@@ -72,9 +72,9 @@ class CountPerson implements ExportInterface, GroupedExportInterface
return ['export_result'];
}
public function getResult($qb, $data)
public function getResult($query, $data)
{
return $qb->getQuery()->getResult(Query::HYDRATE_SCALAR);
return $query->getQuery()->getResult(Query::HYDRATE_SCALAR);
}
public function getTitle()
@@ -100,17 +100,20 @@ class CountPerson implements ExportInterface, GroupedExportInterface
$qb = $this->personRepository->createQueryBuilder('person');
$qb->select('COUNT(person.id) AS export_result')
->join('person.center', 'center')
->andWhere('center IN (:authorized_centers)')
$qb->select('COUNT(DISTINCT person.id) AS export_result')
->andWhere(
$qb->expr()->exists(
'SELECT 1 FROM ' . PersonCenterHistory::class . ' pch WHERE pch.person = person.id AND pch.center IN (:authorized_centers)'
)
)
->setParameter('authorized_centers', $centers);
return $qb;
}
public function requiredRole()
public function requiredRole(): string
{
return new Role(PersonVoter::STATS);
return PersonVoter::STATS;
}
public function supportsModifiers()

View File

@@ -15,13 +15,15 @@ use Chill\MainBundle\Export\ExportInterface;
use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\GroupedExportInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person\PersonCenterHistory;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query;
use LogicException;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Security\Core\Role\Role;
use function in_array;
class CountPersonWithAccompanyingCourse implements ExportInterface, GroupedExportInterface
{
@@ -72,9 +74,9 @@ class CountPersonWithAccompanyingCourse implements ExportInterface, GroupedExpor
return ['export_result'];
}
public function getResult($qb, $data)
public function getResult($query, $data)
{
return $qb->getQuery()->getResult(Query::HYDRATE_SCALAR);
return $query->getQuery()->getResult(Query::HYDRATE_SCALAR);
}
public function getTitle(): string
@@ -89,25 +91,41 @@ class CountPersonWithAccompanyingCourse implements ExportInterface, GroupedExpor
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{
$qb = $this->repository->createQueryBuilder('acp')
->join('acp.participations', 'acppart')
->join('acppart.person', 'person');
$centers = array_map(static function ($el) {
return $el['center'];
}, $acl);
$qb = $this->repository->createQueryBuilder('acp');
if (!in_array('acppart', $qb->getAllAliases(), true)) {
$qb->join('acp.participations', 'acppart');
}
if (!in_array('partperson', $qb->getAllAliases(), true)) {
$qb->join('acppart.person', 'person');
}
$qb->andWhere(
$qb->expr()->exists(
'SELECT 1 FROM ' . PersonCenterHistory::class . ' pch WHERE pch.person = person.id AND pch.center IN (:authorized_centers)'
)
)->setParameter('authorized_centers', $centers);
$qb->select('COUNT(DISTINCT person.id) AS export_result');
return $qb;
}
public function requiredRole()
public function requiredRole(): string
{
return new Role(AccompanyingPeriodVoter::STATS);
return AccompanyingPeriodVoter::STATS;
}
public function supportsModifiers(): array
{
return [
Declarations::ACP_TYPE,
Declarations::PERSON_TYPE,
Declarations::ACP_TYPE,
];
}
}

View File

@@ -15,24 +15,23 @@ use Chill\MainBundle\Export\ExportInterface;
use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\GroupedExportInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person\PersonCenterHistory;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query;
use Doctrine\ORM\QueryBuilder;
use LogicException;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Security\Core\Role\Role;
class CountSocialWorkActions implements ExportInterface, GroupedExportInterface
{
protected EntityRepository $repository;
protected EntityManagerInterface $em;
public function __construct(
EntityManagerInterface $em
) {
$this->repository = $em->getRepository(AccompanyingPeriod::class);
$this->em = $em;
}
public function buildForm(FormBuilderInterface $builder): void
@@ -74,9 +73,9 @@ class CountSocialWorkActions implements ExportInterface, GroupedExportInterface
return ['export_result'];
}
public function getResult($qb, $data)
public function getResult($query, $data)
{
return $qb->getQuery()->getResult(Query::HYDRATE_SCALAR);
return $query->getQuery()->getResult(Query::HYDRATE_SCALAR);
}
public function getTitle(): string
@@ -91,24 +90,43 @@ class CountSocialWorkActions implements ExportInterface, GroupedExportInterface
public function initiateQuery(array $requiredModifiers, array $acl, array $data = []): QueryBuilder
{
$qb = $this->repository->createQueryBuilder('acp')
->join('acp.works', 'acpw');
$centers = array_map(static function ($el) {
return $el['center'];
}, $acl);
$qb->select('COUNT(acpw.id) as export_result');
$qb = $this->em->createQueryBuilder();
$qb
->from(AccompanyingPeriod\AccompanyingPeriodWork::class, 'acpw')
->join('acpw.accompanyingPeriod', 'acp')
->join('acp.participations', 'acppart')
->join('acppart.person', 'person')
->andWhere('acppart.startDate != acppart.endDate OR acppart.endDate IS NULL')
->andWhere(
$qb->expr()->exists(
'SELECT 1 FROM ' . PersonCenterHistory::class . ' acl_count_person_history WHERE acl_count_person_history.person = person
AND acl_count_person_history.center IN (:authorized_centers)
'
)
)
->setParameter('authorized_centers', $centers);
$qb->select('COUNT(DISTINCT acpw.id) as export_result');
return $qb;
}
public function requiredRole(): Role
public function requiredRole(): string
{
//TODO change to string, but changes needed also in ExportManager and possibly other locations.
return new Role(AccompanyingPeriodVoter::STATS);
return AccompanyingPeriodVoter::STATS;
}
public function supportsModifiers(): array
{
return [
Declarations::SOCIAL_WORK_ACTION_TYPE,
Declarations::ACP_TYPE,
Declarations::PERSON_TYPE,
];
}
}

View File

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

View File

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

View File

@@ -24,7 +24,6 @@ use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Role\Role;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
@@ -121,10 +120,7 @@ class ListPersonDuplicate implements DirectExportInterface, ExportElementValidat
return $response;
}
/**
* @return string
*/
public function getDescription()
public function getDescription(): string
{
return 'Create a list of duplicate people';
}
@@ -142,9 +138,9 @@ class ListPersonDuplicate implements DirectExportInterface, ExportElementValidat
return 'List duplicates';
}
public function requiredRole(): Role
public function requiredRole(): string
{
return new Role(PersonVoter::DUPLICATE);
return PersonVoter::DUPLICATE;
}
public function validateForm($data, ExecutionContextInterface $context)

View File

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

View File

@@ -16,6 +16,7 @@ use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\GroupedExportInterface;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person\PersonCenterHistory;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
use DateTime;
@@ -23,8 +24,8 @@ use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query;
use Doctrine\ORM\QueryBuilder;
use LogicException;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Security\Core\Role\Role;
class StatAccompanyingCourseDuration implements ExportInterface, GroupedExportInterface
{
@@ -51,7 +52,7 @@ class StatAccompanyingCourseDuration implements ExportInterface, GroupedExportIn
public function getDescription(): string
{
return 'Create an average of accompanying courses duration according to various filters';
return 'Create an average of accompanying courses duration of each person participation to accompanying course, according to filters on persons, accompanying course';
}
public function getGroup(): string
@@ -61,31 +62,47 @@ class StatAccompanyingCourseDuration implements ExportInterface, GroupedExportIn
public function getLabels($key, array $values, $data)
{
if ('export_result' !== $key) {
throw new LogicException("the key {$key} is not used by this export");
}
return static function ($value) use ($key) {
if ('_header' === $value) {
switch ($key) {
case 'avg_export_result':
return 'export.export.acp_stats.avg_duration';
$labels = array_combine($values, $values);
$labels['_header'] = $this->getTitle();
case 'count_acppart_export_result':
return 'export.export.acp_stats.count_participations';
return static function ($value) use ($labels) {
return $labels[$value];
case 'count_acp_export_result':
return 'export.export.acp_stats.count_acps';
case 'count_pers_export_result':
return 'export.export.acp_stats.count_persons';
default:
throw new LogicException('key not supported: ' . $key);
}
}
if (null === $value) {
return '';
}
return $value;
};
}
public function getQueryKeys($data): array
{
return ['export_result'];
return ['avg_export_result', 'count_acp_export_result', 'count_acppart_export_result', 'count_pers_export_result'];
}
public function getResult($qb, $data)
public function getResult($query, $data)
{
return $qb->getQuery()->getResult(Query::HYDRATE_SCALAR);
return $query->getQuery()->getResult(Query::HYDRATE_SCALAR);
}
public function getTitle(): string
{
return 'Accompanying courses duration';
return 'Accompanying courses participation duration and number of participations';
}
public function getType(): string
@@ -95,30 +112,48 @@ class StatAccompanyingCourseDuration implements ExportInterface, GroupedExportIn
public function initiateQuery(array $requiredModifiers, array $acl, array $data = []): QueryBuilder
{
$centers = array_map(static function ($el) {
return $el['center'];
}, $acl);
$qb = $this->repository->createQueryBuilder('acp');
$qb
->select('AVG(
( CASE
WHEN acp.closingDate IS NOT NULL
THEN acp.closingDate
ELSE :force_closingDate
END ) - acp.openingDate
) AS export_result')
->setParameter('force_closingDate', $data['closingdate']);
LEAST(acppart.endDate, COALESCE(acp.closingDate, :force_closingDate))
- GREATEST(acppart.startDate, COALESCE(acp.openingDate, :force_closingDate))
) AS avg_export_result')
->addSelect('COUNT(DISTINCT acppart.id) AS count_acppart_export_result')
->addSelect('COUNT(DISTINCT person.id) AS count_pers_export_result')
->addSelect('COUNT(DISTINCT acp.id) AS count_acp_export_result')
->setParameter('force_closingDate', $data['closingdate'])
->leftJoin('acp.participations', 'acppart')
->leftJoin('acppart.person', 'person')
->andWhere('acp.step != :count_acp_step')
->andWhere('acppart.startDate != acppart.endDate OR acppart.endDate IS NULL')
->andWhere(
$qb->expr()->exists(
'SELECT 1 FROM ' . PersonCenterHistory::class . ' acl_count_person_history WHERE acl_count_person_history.person = person
AND acl_count_person_history.center IN (:authorized_centers)
'
)
)
->setParameter('count_acp_step', AccompanyingPeriod::STEP_DRAFT)
->setParameter('authorized_centers', $centers);
return $qb;
}
public function requiredRole(): Role
public function requiredRole(): string
{
return new Role(AccompanyingPeriodVoter::STATS);
return AccompanyingPeriodVoter::STATS;
}
public function supportsModifiers(): array
{
return [
Declarations::ACP_TYPE,
Declarations::PERSON_TYPE,
];
}
}

View File

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

View File

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

View File

@@ -1,99 +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\ActivityBundle\Entity\ActivityType;
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\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface;
/**
* TODO merge with ActivityTypeFilter in ChillActivity (!?).
*/
class ActivityTypeFilter implements FilterInterface
{
private TranslatableStringHelper $translatableStringHelper;
public function __construct(TranslatableStringHelper $translatableStringHelper)
{
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole()
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
// One2many between activity and accompanyingperiod is not reversed !
// we replace indicator 'from' clause by 'act', and put 'acp' in a join
$qb->resetDQLPart('from');
$qb->from('ChillActivityBundle:Activity', 'act');
$qb
->join('act.accompanyingPeriod', 'acp')
->join('act.activityType', 'aty');
$where = $qb->getDQLPart('where');
$clause = $qb->expr()->in('aty.id', ':activitytypes');
if ($where instanceof Andx) {
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->setParameter('activitytypes', $data['accepted_activitytypes']);
}
public function applyOn()
{
return Declarations::ACP_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('accepted_activitytypes', EntityType::class, [
'class' => ActivityType::class,
'choice_label' => function (ActivityType $aty) {
return $this->translatableStringHelper->localize($aty->getName());
},
'multiple' => true,
'expanded' => true,
]);
}
public function describeAction($data, $format = 'string'): array
{
$types = [];
foreach ($data['accepted_activitytypes'] as $aty) {
$types[] = $this->translatableStringHelper->localize($aty->getName());
}
return ['Filtered by activity types: only %activitytypes%', [
'%activitytypes%' => implode(', ou ', $types),
]];
}
public function getTitle(): string
{
return 'Filter accompanying course by activity type';
}
}

View File

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

View File

@@ -30,7 +30,7 @@ class ClosingMotiveFilter implements FilterInterface
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole()
public function addRole(): ?string
{
return null;
}
@@ -77,7 +77,7 @@ class ClosingMotiveFilter implements FilterInterface
return [
'Filtered by closingmotive: only %closingmotives%', [
'%closingmotives%' => implode(', ou ', $motives),
'%closingmotives%' => implode(', ', $motives),
], ];
}

View File

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

View File

@@ -0,0 +1,66 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\PickUserDynamicType;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use function in_array;
class CreatorFilter implements FilterInterface
{
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('acp_creator', $qb->getAllAliases(), true)) {
$qb->join('acp.createdBy', 'acp_creator');
}
$qb
->andWhere($qb->expr()->in('acp_creator', ':creators'))
->setParameter('creators', $data['accepted_creators']);
}
public function applyOn(): string
{
return Declarations::ACP_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder
->add('accepted_creators', PickUserDynamicType::class, [
'multiple' => true,
'label' => false,
]);
}
public function describeAction($data, $format = 'string'): array
{
return [
'Filtered by creator: only %creators%', [
'%creators%' => implode(', ', array_map(static fn (User $u) => $u->getLabel(), $data['accepted_creators'])),
], ];
}
public function getTitle(): string
{
return 'Filter by creator';
}
}

View File

@@ -0,0 +1,94 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters;
use Chill\MainBundle\Entity\UserJob;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Repository\UserJobRepositoryInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface;
use function in_array;
class CreatorJobFilter implements FilterInterface
{
private TranslatableStringHelper $translatableStringHelper;
private UserJobRepositoryInterface $userJobRepository;
public function __construct(
TranslatableStringHelper $translatableStringHelper,
UserJobRepositoryInterface $userJobRepository
) {
$this->translatableStringHelper = $translatableStringHelper;
$this->userJobRepository = $userJobRepository;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('acp_creator', $qb->getAllAliases(), true)) {
$qb->join('acp.createdBy', 'acp_creator');
}
$qb
->andWhere($qb->expr()->in('acp_creator.userJob', ':creator_job'))
->setParameter('creator_job', $data['creator_job']);
}
public function applyOn(): string
{
return Declarations::ACP_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('creator_job', EntityType::class, [
'class' => UserJob::class,
'choices' => $this->userJobRepository->findAllActive(),
'choice_label' => function (UserJob $j) {
return $this->translatableStringHelper->localize(
$j->getLabel()
);
},
'multiple' => true,
'expanded' => true,
'label' => 'Job',
]);
}
public function describeAction($data, $format = 'string'): array
{
$creatorJobs = [];
foreach ($data['creator_job'] as $j) {
$creatorJobs[] = $this->translatableStringHelper->localize(
$j->getLabel()
);
}
return ['export.filter.course.creator_job.Filtered by creator job: only %jobs%', [
'%jobs%' => implode(', ', $creatorJobs),
]];
}
public function getTitle(): string
{
return 'Filter by creator job';
}
}

View File

@@ -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()
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$where = $qb->getDQLPart('where');
$clause = $qb->expr()->eq('acp.job', ':userjob');
if ($where instanceof Andx) {
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->setParameter('userjob', $this->getUserJob());
}
public function applyOn()
{
return Declarations::ACP_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
{
}
public function describeAction($data, $format = 'string')
{
return [
'Filtered by user job: only %job%', [
'%job%' => $this->translatableStringHelper->localize(
$this->getUserJob()->getLabel()
),
],
];
}
public function getTitle()
{
return 'Filter by user job';
}
private function getUserJob(): UserJob
{
/** @var User $user */
$user = $this->security->getUser();
return $user->getUserJob();
}
}

View File

@@ -1,92 +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;
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()
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$qb->join('acp.scopes', 's');
$where = $qb->getDQLPart('where');
$clause = $qb->expr()->eq('s.id', ':userscope');
if ($where instanceof Andx) {
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->setParameter('userscope', $this->getUserMainScope());
}
public function applyOn()
{
return Declarations::ACP_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
{
}
public function describeAction($data, $format = 'string')
{
return [
'Filtered by user main scope: only %scope%', [
'%scope%' => $this->translatableStringHelper->localize(
$this->getUserMainScope()->getName()
),
],
];
}
public function getTitle()
{
return 'Filter by user scope';
}
private function getUserMainScope(): Scope
{
/** @var User $user */
$user = $this->security->getUser();
return $user->getMainScope();
}
}

View File

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

View File

@@ -15,43 +15,47 @@ 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;
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;
}
public function addRole()
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$qb
->join('acp.works', 'acpw')
->join('acpw.accompanyingPeriodWorkEvaluations', 'we')
->join('we.evaluation', 'ev');
$where = $qb->getDQLPart('where');
$clause = $qb->expr()->in('ev.id', ':evaluations');
if ($where instanceof Andx) {
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
if (!in_array('acpw', $qb->getAllAliases(), true)) {
$qb->join('acp.works', 'acpw');
}
$qb->add('where', $where);
if (!in_array('workeval', $qb->getAllAliases(), true)) {
$qb->join('acpw.accompanyingPeriodWorkEvaluations', 'workeval');
}
if (!in_array('eval', $qb->getAllAliases(), true)) {
$qb->join('workeval.evaluation', 'eval');
}
$clause = $qb->expr()->in('eval.id', ':evaluations');
$qb->andWhere($clause);
$qb->setParameter('evaluations', $data['accepted_evaluations']);
}
@@ -64,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'],
]);
}
@@ -81,7 +87,7 @@ class EvaluationFilter implements FilterInterface
}
return ['Filtered by evaluations: only %evals%', [
'%evals%' => implode(', ou ', $evaluations),
'%evals%' => implode(', ', $evaluations),
]];
}

View File

@@ -11,48 +11,82 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters;
use Chill\MainBundle\Entity\GeographicalUnit;
use Chill\MainBundle\Entity\Address;
use Chill\MainBundle\Entity\GeographicalUnit\SimpleGeographicalUnitDTO;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Repository\GeographicalUnitLayerRepositoryInterface;
use Chill\MainBundle\Repository\GeographicalUnitRepositoryInterface;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Household\PersonHouseholdAddress;
use Chill\PersonBundle\Export\Declarations;
use DateTime;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
/**
* e) par zone géographique.
*
* Paramètre:
* * Date
* * Choix unique entre: territoire, epci, canton, commune, secteur d'intervention
* * une fois le premier choix effectué, l'utilisateur choisi parmi les zones (choix multiple)
*
* Le filtre retiendra les parcours localisé dans un des territoires cochés, à la date indiquée en paramètre.
* Filter accompanying period by geographical zone.
*/
class GeographicalUnitStatFilter implements FilterInterface
{
public function addRole()
private GeographicalUnitLayerRepositoryInterface $geographicalUnitLayerRepository;
private GeographicalUnitRepositoryInterface $geographicalUnitRepository;
private RollingDateConverterInterface $rollingDateConverter;
private TranslatableStringHelperInterface $translatableStringHelper;
public function __construct(
GeographicalUnitRepositoryInterface $geographicalUnitRepository,
GeographicalUnitLayerRepositoryInterface $geographicalUnitLayerRepository,
TranslatableStringHelperInterface $translatableStringHelper,
RollingDateConverterInterface $rollingDateConverter
) {
$this->geographicalUnitRepository = $geographicalUnitRepository;
$this->geographicalUnitLayerRepository = $geographicalUnitLayerRepository;
$this->translatableStringHelper = $translatableStringHelper;
$this->rollingDateConverter = $rollingDateConverter;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$where = $qb->getDQLPart('where');
$clause = $qb->expr()->eq(1, 1);
$subQueryDql =
'SELECT
1
FROM ' . AccompanyingPeriod\AccompanyingPeriodLocationHistory::class . ' acp_geog_filter_location_history
LEFT JOIN ' . PersonHouseholdAddress::class . ' acp_geog_filter_address_person_location
WITH IDENTITY(acp_geog_filter_location_history.personLocation) = IDENTITY(acp_geog_filter_address_person_location.person)
LEFT JOIN ' . Address::class . ' acp_geog_filter_address
WITH COALESCE(IDENTITY(acp_geog_filter_address_person_location.address), IDENTITY(acp_geog_filter_location_history.addressLocation)) = acp_geog_filter_address.id
LEFT JOIN acp_geog_filter_address.geographicalUnits acp_geog_filter_units
WHERE
(acp_geog_filter_location_history.startDate <= :acp_geog_filter_date AND (
acp_geog_filter_location_history.endDate IS NULL OR acp_geog_filter_location_history.endDate < :acp_geog_filter_date
))
AND
(acp_geog_filter_address_person_location.validFrom < :acp_geog_filter_date AND (
acp_geog_filter_address_person_location.validTo IS NULL OR acp_geog_filter_address_person_location.validTo < :acp_geog_filter_date
))
AND acp_geog_filter_units IN (:acp_geog_filter_units)
AND acp_geog_filter_location_history.period = acp.id
';
if ($where instanceof Andx) {
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->setParameter('date', $data['date'], Types::DATE_MUTABLE);
$qb->setParameter('loctype', $data['accepted_loctype']);
$qb
->andWhere($qb->expr()->exists($subQueryDql))
->setParameter(
'acp_geog_filter_date',
$this->rollingDateConverter->convert($data['date_calc'])
)
->setParameter('acp_geog_filter_units', array_map(static fn (SimpleGeographicalUnitDTO $unitDTO) => $unitDTO->id, $data['units']));
}
public function applyOn(): string
@@ -63,23 +97,39 @@ class GeographicalUnitStatFilter implements FilterInterface
public function buildForm(FormBuilderInterface $builder)
{
$builder
->add('date', ChillDateType::class, [
'data' => new DateTime(),
->add('date_calc', PickRollingDateType::class, [
'label' => 'Compute geographical location at date',
'required' => true,
'data' => new RollingDate(RollingDate::T_TODAY),
])
->add('accepted_loctype', EntityType::class, [
'class' => GeographicalUnit::class,
'choice_label' => static function (GeographicalUnit $u) {
return $u->getUnitName();
->add('units', ChoiceType::class, [
'label' => 'Geographical unit',
'placeholder' => 'Select a geographical unit',
'choices' => $this->geographicalUnitRepository->findAll(),
'choice_value' => static fn (SimpleGeographicalUnitDTO $item) => $item->id,
'choice_label' => function (SimpleGeographicalUnitDTO $item) {
return $this->translatableStringHelper->localize($this->geographicalUnitLayerRepository->find($item->layerId)->getName()) . ' > ' . $item->unitName;
},
'attr' => [
'class' => 'select2',
],
'multiple' => true,
'expanded' => true,
]);
}
public function describeAction($data, $format = 'string'): array
{
return ['Filtered by geographic unit: only %date%', [
'%date%' => $data['date']->format('d-m-Y'),
return ['Filtered by geographic unit: computed at %date%, only in %units%', [
'%date%' => $this->rollingDateConverter->convert($data['date_calc'])->format('d-m-Y'),
'%units' => implode(
', ',
array_map(
function (SimpleGeographicalUnitDTO $item) {
return $this->translatableStringHelper->localize($this->geographicalUnitLayerRepository->find($item->layerId)->getName()) . ' > ' . $item->unitName;
},
$data['units']
)
),
]];
}

View File

@@ -0,0 +1,51 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters;
use Chill\MainBundle\Export\FilterInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
class HasNoActionFilter implements FilterInterface
{
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data): void
{
$qb->andWhere('NOT EXISTS (SELECT 1 FROM ' . AccompanyingPeriodWork::class . ' work WHERE work.accompanyingPeriod = acp)');
}
public function applyOn(): string
{
return Declarations::ACP_TYPE;
}
public function buildForm(FormBuilderInterface $builder): void
{
// no form
}
public function describeAction($data, $format = 'string'): array
{
return ['Filtered acp which has no actions'];
}
public function getTitle(): string
{
return 'Filter by which has no action';
}
}

View File

@@ -0,0 +1,83 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod\UserHistory;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
class HasNoReferrerFilter implements FilterInterface
{
private RollingDateConverterInterface $rollingDateConverter;
public function __construct(
RollingDateConverterInterface $rollingDateConverter
) {
$this->rollingDateConverter = $rollingDateConverter;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$qb
->andWhere('
NOT EXISTS (
SELECT 1 FROM ' . UserHistory::class . ' uh
WHERE uh.startDate <= :has_no_referrer_filter_date
AND (
uh.endDate IS NULL
or uh.endDate > :has_no_referrer_filter_date
)
AND uh.accompanyingPeriod = acp
)
')
->setParameter(
'has_no_referrer_filter_date',
$this->rollingDateConverter->convert($data['calc_date'])
);
}
public function applyOn(): string
{
return Declarations::ACP_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder
->add('calc_date', PickRollingDateType::class, [
'label' => 'Has no referrer on this date',
'data' => new RollingDate(RollingDate::T_TODAY),
]);
}
public function describeAction($data, $format = 'string'): array
{
return ['Filtered acp which has no referrer on date: %date%', [
'%date%' => $this->rollingDateConverter->convert($data['calc_date'])->format('d-m-Y'),
]];
}
public function getTitle(): string
{
return 'Filter by which has no referrer';
}
}

View File

@@ -0,0 +1,116 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\QueryBuilder;
use LogicException;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
class HasTemporaryLocationFilter implements FilterInterface
{
private RollingDateConverterInterface $rollingDateConverter;
public function __construct(
RollingDateConverterInterface $rollingDateConverter
) {
$this->rollingDateConverter = $rollingDateConverter;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$qb
->join('acp.locationHistories', 'acp_having_temporarily_location')
->andWhere('acp_having_temporarily_location.startDate <= :acp_having_temporarily_location_date
AND (acp_having_temporarily_location.endDate IS NULL OR acp_having_temporarily_location.endDate > :acp_having_temporarily_location_date)')
->setParameter(
'acp_having_temporarily_location_date',
$this->rollingDateConverter->convert($data['calc_date'])
);
switch ($data['having_temporarily']) {
case true:
$qb->andWhere('acp_having_temporarily_location.addressLocation IS NOT NULL');
break;
case false:
$qb->andWhere('acp_having_temporarily_location.personLocation IS NOT NULL');
break;
default:
throw new LogicException('value not supported');
}
}
public function applyOn(): string
{
return Declarations::ACP_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder
->add('having_temporarily', ChoiceType::class, [
'choices' => [
'export.filter.course.having_temporarily.Having a temporarily location' => true,
'export.filter.course.having_temporarily.Having a person\'s location' => false,
],
'choice_label' => static function ($choice) {
switch ($choice) {
case true:
return 'export.filter.course.having_temporarily.Having a temporarily location';
case false:
return 'export.filter.course.having_temporarily.Having a person\'s location';
default:
throw new LogicException('this choice is not supported');
}
},
])
->add('calc_date', PickRollingDateType::class, [
'label' => 'export.filter.course.having_temporarily.Calculation date',
'data' => new RollingDate(RollingDate::T_TODAY),
]);
}
public function describeAction($data, $format = 'string'): array
{
switch ($data['having_temporarily']) {
case true:
return ['export.filter.course.having_temporarily.Having a temporarily location', []];
case false:
return ['export.filter.course.having_temporarily.Having a person\'s location', []];
default:
throw new LogicException('value not supported');
}
}
public function getTitle(): string
{
return 'Filter by temporary location';
}
}

View File

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

View File

@@ -12,39 +12,38 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\PersonBundle\Export\Declarations;
use DateTime;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
class OpenBetweenDatesFilter implements FilterInterface
{
public function addRole()
private RollingDateConverterInterface $rollingDateConverter;
public function __construct(RollingDateConverterInterface $rollingDateConverter)
{
$this->rollingDateConverter = $rollingDateConverter;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$where = $qb->getDQLPart('where');
$clause = $qb->expr()->andX(
$qb->expr()->lt('acp.openingDate', ':datefrom'),
$qb->expr()->gt('acp.closingDate', ':dateto')
$qb->expr()->gte('acp.openingDate', ':datefrom'),
$qb->expr()->lte('acp.openingDate', ':dateto')
);
if ($where instanceof Andx) {
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->setParameter('datefrom', $data['date_from'], Types::DATE_MUTABLE);
$qb->setParameter('dateto', $data['date_to'], Types::DATE_MUTABLE);
$qb->andWhere($clause);
$qb->setParameter('datefrom', $this->rollingDateConverter->convert($data['date_from']), Types::DATE_IMMUTABLE);
$qb->setParameter('dateto', $this->rollingDateConverter->convert($data['date_to']), Types::DATE_IMMUTABLE);
}
public function applyOn(): string
@@ -55,19 +54,19 @@ class OpenBetweenDatesFilter implements FilterInterface
public function buildForm(FormBuilderInterface $builder)
{
$builder
->add('date_from', ChillDateType::class, [
'data' => new DateTime(),
->add('date_from', PickRollingDateType::class, [
'data' => new RollingDate(RollingDate::T_MONTH_PREVIOUS_START),
])
->add('date_to', ChillDateType::class, [
'data' => new DateTime(),
->add('date_to', PickRollingDateType::class, [
'data' => new RollingDate(RollingDate::T_TODAY),
]);
}
public function describeAction($data, $format = 'string'): array
{
return ['Filtered by opening dates: between %datefrom% and %dateto%', [
'%datefrom%' => $data['date_from']->format('d-m-Y'),
'%dateto%' => $data['date_to']->format('d-m-Y'),
'%datefrom%' => $this->rollingDateConverter->convert($data['date_from'])->format('d-m-Y'),
'%dateto%' => $this->rollingDateConverter->convert($data['date_to'])->format('d-m-Y'),
]];
}

View File

@@ -30,7 +30,7 @@ class OriginFilter implements FilterInterface
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole()
public function addRole(): ?string
{
return null;
}
@@ -76,7 +76,7 @@ class OriginFilter implements FilterInterface
}
return ['Filtered by origins: only %origins%', [
'%origins%' => implode(', ou ', $origins),
'%origins%' => implode(', ', $origins),
]];
}

View File

@@ -11,42 +11,62 @@ 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\PickRollingDateType;
use Chill\MainBundle\Form\Type\PickUserDynamicType;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\MainBundle\Templating\Entity\UserRender;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\Query\Expr\Andx;
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 RollingDateConverterInterface $rollingDateConverter;
private UserRender $userRender;
public function __construct(UserRender $userRender)
{
public function __construct(
UserRender $userRender,
RollingDateConverterInterface $rollingDateConverter
) {
$this->userRender = $userRender;
$this->rollingDateConverter = $rollingDateConverter;
}
public function addRole()
public function addRole(): ?string
{
return null;
}
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,
$this->rollingDateConverter->convert($data['date_calc'])
);
}
public function applyOn(): string
@@ -56,14 +76,15 @@ 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', PickRollingDateType::class, [
'data' => new RollingDate(RollingDate::T_TODAY),
'label' => 'export.filter.course.by_referrer.Computation date for referrer',
'required' => true,
]);
}
public function describeAction($data, $format = 'string'): array
@@ -76,7 +97,7 @@ class ReferrerFilter implements FilterInterface
return [
'Filtered by referrer: only %referrers%', [
'%referrers' => implode(', ou ', $users),
'%referrers' => implode(', ', $users),
], ];
}

View File

@@ -20,8 +20,9 @@ use Exception;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use function in_array;
class RequestorFilter implements FilterInterface
final class RequestorFilter implements FilterInterface
{
private const DEFAULT_CHOICE = 'participation';
@@ -32,9 +33,9 @@ class RequestorFilter implements FilterInterface
'no requestor' => 'no_requestor',
];
protected EntityManagerInterface $em;
private EntityManagerInterface $em;
protected TranslatorInterface $translator;
private TranslatorInterface $translator;
public function __construct(
TranslatorInterface $translator,
@@ -44,7 +45,7 @@ class RequestorFilter implements FilterInterface
$this->em = $em;
}
public function addRole()
public function addRole(): ?string
{
return null;
}
@@ -55,11 +56,13 @@ class RequestorFilter implements FilterInterface
switch ($data['accepted_choices']) {
case 'participation':
$qb->join('acp.participations', 'part');
if (!in_array('acppart', $qb->getAllAliases(), true)) {
$qb->join('acp.participations', 'acppart');
}
$clause = $qb->expr()->andX(
$qb->expr()->isNotNull('acp.requestorPerson'),
$qb->expr()->eq('acp.requestorPerson', 'part.person')
$qb->expr()->eq('acp.requestorPerson', 'acppart.person')
);
break;
@@ -67,8 +70,6 @@ class RequestorFilter implements FilterInterface
case 'other_person':
$expr = $this->em->getExpressionBuilder();
$qb->join('acp.participations', 'part');
$clause = $expr->andX(
$expr->isNotNull('acp.requestorPerson'),
$expr->notIn(
@@ -76,10 +77,11 @@ class RequestorFilter implements FilterInterface
// subquery
$this->em->createQueryBuilder()
->select('identity(acp2.requestorPerson)')
->from('ChillPersonBundle:AccompanyingPeriod', 'acp2')
->join('acp2.participations', 'part2')
->where($expr->eq('acp2.requestorPerson', 'part2.person'))
->join('acp2.participations', 'acppart2')
->select('identity(acp2.requestorPerson)')
->where($expr->eq('acp2.requestorPerson', 'acppart2.person'))
->andWhere('acp2.id = acp.id')
->getDQL()
)
);

View File

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

View File

@@ -12,15 +12,14 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\PersonBundle\Entity\SocialWork\SocialIssue;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Form\Type\PickSocialIssueType;
use Chill\PersonBundle\Templating\Entity\SocialIssueRender;
use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use function in_array;
class SocialIssueFilter implements FilterInterface
{
@@ -31,41 +30,32 @@ class SocialIssueFilter implements FilterInterface
private SocialIssueRender $socialIssueRender;
private TranslatableStringHelper $translatableStringHelper;
public function __construct(
TranslatorInterface $translator,
TranslatableStringHelper $translatableStringHelper,
SocialIssueRender $socialIssueRender
) {
$this->translator = $translator;
$this->translatableStringHelper = $translatableStringHelper;
$this->socialIssueRender = $socialIssueRender;
}
public function addRole()
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$qb->join('acp.socialIssues', 'si');
$where = $qb->getDQLPart('where');
$clause = $qb->expr()->in('si.id', ':socialissues');
if ($where instanceof Andx) {
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
if (!in_array('acpsocialissue', $qb->getAllAliases(), true)) {
$qb->join('acp.socialIssues', 'acpsocialissue');
}
$qb->add('where', $where);
$qb->setParameter(
'socialissues',
$this->addParentIssues($data['accepted_socialissues'])
);
$clause = $qb->expr()->in('acpsocialissue.id', ':socialissues');
$qb->andWhere($clause)
->setParameter(
'socialissues',
SocialIssue::getDescendantsWithThisForIssues($data['accepted_socialissues']->toArray())
);
}
public function applyOn()
@@ -75,74 +65,31 @@ class SocialIssueFilter implements FilterInterface
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('accepted_socialissues', EntityType::class, [
'class' => SocialIssue::class,
'choice_label' => function ($socialIssue) {
return $this->socialIssueRender->renderString($socialIssue, []);
},
$builder->add('accepted_socialissues', PickSocialIssueType::class, [
'multiple' => true,
'expanded' => true,
]);
}
public function describeAction($data, $format = 'string')
public function describeAction($data, $format = 'string'): array
{
$issues = [];
$socialissues = $this->addParentIssues($data['accepted_socialissues']);
$socialissues = $data['accepted_socialissues'];
foreach ($socialissues as $i) {
if ('null' === $i) {
$issues[] = $this->translator->trans('Not given');
} else {
$issues[] = $this->socialIssueRender->renderString($i, []);
}
foreach ($socialissues as $issue) {
$issues[] = $this->socialIssueRender->renderString($issue, [
'show_and_children' => true,
]);
}
return [
'Filtered by socialissues: only %socialissues%', [
'%socialissues%' => implode(', ou ', $issues),
'%socialissues%' => implode(', ', $issues),
], ];
}
public function getTitle()
public function getTitle(): string
{
return 'Filter by social issue';
}
/**
* "Le filtre retiendra les parcours qui comportent cette problématique,
* ou une problématique parente à celles choisies.".
*
* Add parent of each socialissue selected, and remove duplicates
*
* @param $accepted_issues
*/
private function addParentIssues($accepted_issues): array
{
$array = [];
foreach ($accepted_issues as $i) {
/** @var SocialIssue $i */
if ($i->hasParent()) {
$array[] = $i->getParent();
}
$array[] = $i;
}
return $this->removeDuplicate($array);
}
private function removeDuplicate(array $array): array
{
$ids = array_map(static function ($item) {
return $item->getId();
}, $array);
$unique_ids = array_unique($ids);
return array_values(
array_intersect_key($array, $unique_ids)
);
}
}

View File

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

View File

@@ -0,0 +1,137 @@
<?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\PickRollingDateType;
use Chill\MainBundle\Repository\UserJobRepositoryInterface;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\PersonBundle\Export\Declarations;
use 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 RollingDateConverterInterface $rollingDateConverter;
private Security $security;
private TranslatableStringHelper $translatableStringHelper;
private UserJobRepositoryInterface $userJobRepository;
public function __construct(
Security $security,
TranslatableStringHelper $translatableStringHelper,
UserJobRepositoryInterface $userJobRepository,
RollingDateConverterInterface $rollingDateConverter
) {
$this->security = $security;
$this->translatableStringHelper = $translatableStringHelper;
$this->userJobRepository = $userJobRepository;
$this->rollingDateConverter = $rollingDateConverter;
}
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,
$this->rollingDateConverter->convert($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', PickRollingDateType::class, [
'data' => new RollingDate(RollingDate::T_TODAY),
'label' => 'export.filter.course.by_user_scope.Computation date for referrer',
'required' => true,
]);
}
public function describeAction($data, $format = 'string')
{
return [
'Filtered by user job: only %job%', [
'%job%' => implode(
', ',
array_map(
fn (UserJob $job) => $this->translatableStringHelper->localize($job->getLabel()),
$data['jobs']->toArray()
)
),
],
];
}
public function getTitle()
{
return 'Filter by user job';
}
private function getUserJob(): UserJob
{
/** @var User $user */
$user = $this->security->getUser();
return $user->getUserJob();
}
}

View File

@@ -0,0 +1,136 @@
<?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\PickRollingDateType;
use Chill\MainBundle\Repository\ScopeRepositoryInterface;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\PersonBundle\Export\Declarations;
use 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 RollingDateConverterInterface $rollingDateConverter;
private ScopeRepositoryInterface $scopeRepository;
private Security $security;
private TranslatableStringHelper $translatableStringHelper;
public function __construct(
ScopeRepositoryInterface $scopeRepository,
Security $security,
TranslatableStringHelper $translatableStringHelper,
RollingDateConverterInterface $rollingDateConverter
) {
$this->scopeRepository = $scopeRepository;
$this->security = $security;
$this->translatableStringHelper = $translatableStringHelper;
$this->rollingDateConverter = $rollingDateConverter;
}
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,
$this->rollingDateConverter->convert($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', PickRollingDateType::class, [
'data' => new RollingDate(RollingDate::T_TODAY),
'label' => 'export.filter.course.by_user_scope.Computation date for referrer',
'required' => true,
]);
}
public function describeAction($data, $format = 'string')
{
return [
'Filtered by user main scope: only %scope%', [
'%scope%' => implode(
', ',
array_map(
fn (Scope $s) => $this->translatableStringHelper->localize($s->getName()),
$data['scopes']->toArray()
)
),
],
];
}
public function getTitle()
{
return 'Filter by user scope';
}
private function getUserMainScope(): Scope
{
/** @var User $user */
$user = $this->security->getUser();
return $user->getMainScope();
}
}

View File

@@ -0,0 +1,80 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Export\Filter\EvaluationFilters;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
class ByEndDateFilter implements FilterInterface
{
private RollingDateConverterInterface $rollingDateConverter;
public function __construct(RollingDateConverterInterface $rollingDateConverter)
{
$this->rollingDateConverter = $rollingDateConverter;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data): void
{
$qb
->andWhere('workeval.endDate BETWEEN :work_eval_by_end_date_start_date and :work_eval_by_end_date_end_date')
->setParameter(
'work_eval_by_end_date_start_date',
$this->rollingDateConverter->convert($data['start_date'])
)
->setParameter(
'work_eval_by_end_date_end_date',
$this->rollingDateConverter->convert($data['end_date'])
);
}
public function applyOn(): string
{
return Declarations::EVAL_TYPE;
}
public function buildForm(FormBuilderInterface $builder): void
{
$builder
->add('start_date', PickRollingDateType::class, [
'label' => 'start period date',
'data' => new RollingDate(RollingDate::T_YEAR_PREVIOUS_START),
])
->add('end_date', PickRollingDateType::class, [
'label' => 'end period date',
'data' => new RollingDate(RollingDate::T_TODAY),
]);
}
public function describeAction($data, $format = 'string'): array
{
return ['Filtered by end date: between %start_date% and %end_date%', [
'%start_date%' => $this->rollingDateConverter->convert($data['start_date'])->format('d-m-Y'),
'%end_date%' => $this->rollingDateConverter->convert($data['end_date'])->format('d-m-Y'),
]];
}
public function getTitle(): string
{
return 'Filter by end date evaluations';
}
}

View File

@@ -0,0 +1,80 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Export\Filter\EvaluationFilters;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
class ByStartDateFilter implements FilterInterface
{
private RollingDateConverterInterface $rollingDateConverter;
public function __construct(RollingDateConverterInterface $rollingDateConverter)
{
$this->rollingDateConverter = $rollingDateConverter;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data): void
{
$qb
->andWhere('workeval.startDate BETWEEN :work_eval_by_start_date_start_date and :work_eval_by_start_date_end_date')
->setParameter(
'work_eval_by_start_date_start_date',
$this->rollingDateConverter->convert($data['start_date'])
)
->setParameter(
'work_eval_by_start_date_end_date',
$this->rollingDateConverter->convert($data['end_date'])
);
}
public function applyOn(): string
{
return Declarations::EVAL_TYPE;
}
public function buildForm(FormBuilderInterface $builder): void
{
$builder
->add('start_date', PickRollingDateType::class, [
'label' => 'start period date',
'data' => new RollingDate(RollingDate::T_YEAR_PREVIOUS_START),
])
->add('end_date', PickRollingDateType::class, [
'label' => 'end period date',
'data' => new RollingDate(RollingDate::T_TODAY),
]);
}
public function describeAction($data, $format = 'string'): array
{
return ['Filtered by start date: between %start_date% and %end_date%', [
'%start_date%' => $this->rollingDateConverter->convert($data['start_date'])->format('d-m-Y'),
'%end_date%' => $this->rollingDateConverter->convert($data['end_date'])->format('d-m-Y'),
]];
}
public function getTitle(): string
{
return 'Filter by start date evaluations';
}
}

View File

@@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Export\Filter\EvaluationFilters;
use Chill\MainBundle\Export\FilterInterface;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
class CurrentEvaluationsFilter implements FilterInterface
{
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data): void
{
$qb->andWhere('workeval.endDate IS NULL');
}
public function applyOn(): string
{
return Declarations::EVAL_TYPE;
}
public function buildForm(FormBuilderInterface $builder): void
{
//no form needed
}
public function describeAction($data, $format = 'string'): array
{
return ['Filtered by current evaluations'];
}
public function getTitle(): string
{
return 'Filter by current evaluations';
}
}

View File

@@ -30,7 +30,7 @@ final class EvaluationTypeFilter implements FilterInterface
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole()
public function addRole(): ?string
{
return null;
}
@@ -76,7 +76,7 @@ final class EvaluationTypeFilter implements FilterInterface
}
return ['Filtered by evaluation type: only %evals%', [
'%evals%' => implode(', ou ', $evals),
'%evals%' => implode(', ', $evals),
]];
}

View File

@@ -13,7 +13,6 @@ namespace Chill\PersonBundle\Export\Filter\EvaluationFilters;
use Chill\MainBundle\Export\FilterInterface;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
@@ -22,8 +21,8 @@ use Symfony\Contracts\Translation\TranslatorInterface;
class MaxDateFilter implements FilterInterface
{
private const MAXDATE_CHOICES = [
'is specified' => true,
'is not specified' => false,
'maxdate is specified' => true,
'maxdate is not specified' => false,
];
private TranslatorInterface $translator;
@@ -33,28 +32,20 @@ class MaxDateFilter implements FilterInterface
$this->translator = $translator;
}
public function addRole()
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$where = $qb->getDQLPart('where');
if (true === $data['maxdate']) {
$clause = $qb->expr()->isNotNull('eval.maxDate');
$clause = $qb->expr()->isNotNull('workeval.maxDate');
} else {
$clause = $qb->expr()->isNull('eval.maxDate');
$clause = $qb->expr()->isNull('workeval.maxDate');
}
if ($where instanceof Andx) {
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->andWhere($clause);
}
public function applyOn(): string
@@ -73,19 +64,15 @@ class MaxDateFilter implements FilterInterface
public function describeAction($data, $format = 'string'): array
{
foreach (self::MAXDATE_CHOICES as $k => $v) {
if ($v === $data['maxdate']) {
$choice = $k;
}
}
return ['Filtered by maxdate: only %choice%', [
'%choice%' => $this->translator->trans($choice),
'%choice%' => $this->translator->trans(
$data['maxdate'] ? 'maxdate is specified' : 'maxdate is not specified'
),
]];
}
public function getTitle(): string
{
return 'Filter by maxdate';
return 'Filter evaluations by maxdate mention';
}
}

View File

@@ -12,13 +12,13 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Export\Filter\HouseholdFilters;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\PersonBundle\Entity\Household\HouseholdCompositionType;
use Chill\PersonBundle\Export\Declarations;
use DateTime;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\Query\Expr;
use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface;
@@ -26,47 +26,46 @@ use function in_array;
class CompositionFilter implements FilterInterface
{
private RollingDateConverterInterface $rollingDateConverter;
private TranslatableStringHelper $translatableStringHelper;
public function __construct(
TranslatableStringHelper $translatableStringHelper
TranslatableStringHelper $translatableStringHelper,
RollingDateConverterInterface $rollingDateConverter
) {
$this->translatableStringHelper = $translatableStringHelper;
$this->rollingDateConverter = $rollingDateConverter;
}
public function addRole()
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('composition', $qb->getAllAliases(), true)) {
$qb->join('household.compositions', 'composition');
if (!in_array('composition_type_filter', $qb->getAllAliases(), true)) {
$clause =
$qb->expr()->andX(
$qb->expr()->lte('composition_type_filter.startDate', ':ondate_composition_type_filter'),
$qb->expr()->orX(
$qb->expr()->gt('composition_type_filter.endDate', ':ondate_composition_type_filter'),
$qb->expr()->isNull('composition_type_filter.endDate')
)
);
$qb->join('household.compositions', 'composition', Expr\Join::WITH, $clause);
}
$where = $qb->getDQLPart('where');
$whereClause = $qb->expr()->in('composition_type_filter.householdCompositionType', ':compositions');
$clause = $qb->expr()->andX(
$qb->expr()->in('composition.householdCompositionType', ':compositions'),
$qb->expr()->andX(
$qb->expr()->lte('composition.startDate', ':ondate'),
$qb->expr()->orX(
$qb->expr()->gt('composition.endDate', ':ondate'),
$qb->expr()->isNull('composition.endDate')
)
)
);
if ($where instanceof Andx) {
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->andWhere($whereClause);
$qb->setParameter('compositions', $data['accepted_composition']);
$qb->setParameter('ondate', $data['on_date'], Types::DATE_MUTABLE);
$qb->setParameter(
'ondate_composition_type_filter',
$this->rollingDateConverter->convert($data['on_date'])
);
}
public function applyOn(): string
@@ -87,8 +86,8 @@ class CompositionFilter implements FilterInterface
'multiple' => true,
'expanded' => true,
])
->add('on_date', ChillDateType::class, [
'data' => new DateTime('now'),
->add('on_date', PickRollingDateType::class, [
'data' => new RollingDate(RollingDate::T_TODAY),
]);
}
@@ -103,8 +102,8 @@ class CompositionFilter implements FilterInterface
}
return ['Filtered by composition: only %compositions% on %ondate%', [
'%compositions%' => implode(', ou ', $compositions),
'%ondate%' => $data['on_date']->format('d-m-Y'),
'%compositions%' => implode(', ', $compositions),
'%ondate%' => $this->rollingDateConverter->convert($data['on_date'])->format('d-m-Y'),
]];
}

View File

@@ -1,78 +0,0 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Export\Filter\PersonFilters;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\PersonBundle\Export\AbstractAccompanyingPeriodExportElement;
use Chill\PersonBundle\Export\Declarations;
use DateTime;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
class AccompanyingPeriodClosingFilter extends AbstractAccompanyingPeriodExportElement implements FilterInterface
{
public function addRole()
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$this->addJoinAccompanyingPeriod($qb);
$clause = $qb->expr()->andX(
$qb->expr()->lte('accompanying_period.closingDate', ':date_to'),
$qb->expr()->gte('accompanying_period.closingDate', ':date_from')
);
$qb->andWhere($clause);
$qb->setParameter('date_from', $data['date_from'], Types::DATE_MUTABLE);
$qb->setParameter('date_to', $data['date_to'], Types::DATE_MUTABLE);
}
public function applyOn(): string
{
return Declarations::PERSON_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('date_from', ChillDateType::class, [
'label' => 'Having an accompanying period closed after this date',
'data' => new DateTime('-1 month'),
]);
$builder->add('date_to', ChillDateType::class, [
'label' => 'Having an accompanying period closed before this date',
'data' => new DateTime(),
]);
}
public function describeAction($data, $format = 'string')
{
return [
'Filtered by accompanying period: persons having an accompanying period'
. ' closed between the %date_from% and %date_to%',
[
'%date_from%' => $data['date_from']->format('d-m-Y'),
'%date_to%' => $data['date_to']->format('d-m-Y'),
],
];
}
public function getTitle(): string
{
return 'Filter by accompanying period: closed between two dates';
}
}

Some files were not shown because too many files have changed in this diff Show More