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

This commit is contained in:
2022-03-01 14:52:56 +01:00
233 changed files with 4800 additions and 1961 deletions

View File

@@ -21,6 +21,7 @@ use Chill\MainBundle\Entity\Location;
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\AccompanyingPeriodWork;
use Chill\PersonBundle\Entity\AccompanyingPeriod\ClosingMotive;
use Chill\PersonBundle\Entity\AccompanyingPeriod\Comment;
@@ -37,17 +38,19 @@ use DateTimeImmutable;
use DateTimeInterface;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\Criteria;
use Doctrine\ORM\Mapping as ORM;
use Iterator;
use LogicException;
use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Symfony\Component\Validator\GroupSequenceProviderInterface;
use UnexpectedValueException;
use function in_array;
use const SORT_REGULAR;
/**
@@ -207,6 +210,12 @@ class AccompanyingPeriod implements
*/
private ?UserJob $job = null;
/**
* @ORM\OneToMany(targetEntity=AccompanyingPeriodLocationHistory::class,
* mappedBy="period", cascade={"persist", "remove"}, orphanRemoval=true)
*/
private Collection $locationHistories;
/**
* @var DateTime
*
@@ -379,6 +388,7 @@ class AccompanyingPeriod implements
$this->works = new ArrayCollection();
$this->resources = new ArrayCollection();
$this->userHistories = new ArrayCollection();
$this->locationHistories = new ArrayCollection();
}
/**
@@ -429,6 +439,39 @@ class AccompanyingPeriod implements
return $this;
}
public function addLocationHistory(AccompanyingPeriodLocationHistory $history): self
{
if ($this->getStep() === self::STEP_DRAFT) {
return $this;
}
if (!$this->locationHistories->contains($history)) {
$this->locationHistories[] = $history;
$history->setPeriod($this);
}
// ensure continuity of histories
$criteria = new Criteria();
$criteria->orderBy(['startDate' => Criteria::ASC, 'id' => Criteria::ASC]);
/** @var Iterator $locations */
$locations = $this->getLocationHistories()->matching($criteria)->getIterator();
$locations->rewind();
do {
/** @var AccompanyingPeriodLocationHistory $current */
$current = $locations->current();
$locations->next();
if ($locations->valid()) {
$next = $locations->current();
$current->setEndDate($next->getStartDate());
}
} while ($locations->valid());
return $this;
}
public function addPerson(?Person $person = null): self
{
if (null !== $person) {
@@ -661,6 +704,17 @@ class AccompanyingPeriod implements
return $this->job;
}
public function getLastLocationHistory(): ?AccompanyingPeriodLocationHistory
{
foreach ($this->getLocationHistories() as $locationHistory) {
if (null === $locationHistory->getEndDate()) {
return $locationHistory;
}
}
return null;
}
/**
* Get the location, taking precedence into account.
*
@@ -675,6 +729,14 @@ class AccompanyingPeriod implements
return $this->getAddressLocation();
}
/**
* @return Collection|AccompanyingPeriodLocationHistory[]
*/
public function getLocationHistories(): Collection
{
return $this->locationHistories;
}
/**
* Get where the location is.
*
@@ -977,6 +1039,15 @@ class AccompanyingPeriod implements
$this->comments->removeElement($comment);
}
public function removeLocationHistory(AccompanyingPeriodLocationHistory $history): self
{
if ($this->locationHistories->removeElement($history)) {
$history->setPeriod(null);
}
return $this;
}
/**
* Remove Participation.
*/
@@ -1031,7 +1102,18 @@ class AccompanyingPeriod implements
*/
public function setAddressLocation(?Address $addressLocation = null): self
{
$this->addressLocation = $addressLocation;
if ($this->addressLocation !== $addressLocation) {
$this->addressLocation = $addressLocation;
if (null !== $addressLocation) {
$locationHistory = new AccompanyingPeriodLocationHistory();
$locationHistory
->setStartDate(new DateTimeImmutable('now'))
->setAddressLocation($addressLocation);
$this->addLocationHistory($locationHistory);
}
}
return $this;
}
@@ -1134,7 +1216,18 @@ class AccompanyingPeriod implements
*/
public function setPersonLocation(?Person $person = null): self
{
$this->personLocation = $person;
if ($this->personLocation !== $person) {
$this->personLocation = $person;
if (null !== $person) {
$locationHistory = new AccompanyingPeriodLocationHistory();
$locationHistory
->setStartDate(new DateTimeImmutable('now'))
->setPersonLocation($person);
$this->addLocationHistory($locationHistory);
}
}
return $this;
}
@@ -1201,8 +1294,14 @@ class AccompanyingPeriod implements
public function setStep(string $step): self
{
$previous = $this->step;
$this->step = $step;
if (self::STEP_DRAFT === $previous && self::STEP_DRAFT !== $step) {
$this->bootPeriod();
}
return $this;
}
@@ -1241,6 +1340,17 @@ class AccompanyingPeriod implements
return $this;
}
private function bootPeriod(): void
{
// first location history
$locationHistory = new AccompanyingPeriodLocationHistory();
$locationHistory
->setStartDate(new DateTimeImmutable('now'))
->setPersonLocation($this->getPersonLocation())
->setAddressLocation($this->getAddressLocation());
$this->addLocationHistory($locationHistory);
}
private function setRequestorPerson(?Person $requestorPerson = null): self
{
$this->requestorPerson = $requestorPerson;

View File

@@ -0,0 +1,129 @@
<?php
/**
* 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.
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\MainBundle\Doctrine\Model\TrackCreationInterface;
use Chill\MainBundle\Doctrine\Model\TrackCreationTrait;
use Chill\MainBundle\Entity\Address;
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_location_history")
*/
class AccompanyingPeriodLocationHistory implements TrackCreationInterface
{
use TrackCreationTrait;
/**
* @ORM\ManyToOne(targetEntity=Address::class, cascade={"persist"})
*/
private ?Address $addressLocation = 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=AccompanyingPeriod::class)
*/
private AccompanyingPeriod $period;
/**
* @ORM\ManyToOne(targetEntity=Person::class)
*/
private ?Person $personLocation = null;
/**
* @ORM\Column(type="date_immutable")
*/
private ?DateTimeImmutable $startDate = null;
public function getAddressLocation(): ?Address
{
return $this->addressLocation;
}
public function getEndDate(): ?DateTimeImmutable
{
return $this->endDate;
}
public function getId(): ?int
{
return $this->id;
}
public function getPeriod(): AccompanyingPeriod
{
return $this->period;
}
public function getPersonLocation(): ?Person
{
return $this->personLocation;
}
public function getStartDate(): ?DateTimeImmutable
{
return $this->startDate;
}
public function setAddressLocation(?Address $addressLocation): AccompanyingPeriodLocationHistory
{
$this->addressLocation = $addressLocation;
return $this;
}
public function setEndDate(?DateTimeImmutable $endDate): AccompanyingPeriodLocationHistory
{
$this->endDate = $endDate;
return $this;
}
/**
* @internal use AccompanyingPeriod::addLocationHistory
*/
public function setPeriod(AccompanyingPeriod $period): AccompanyingPeriodLocationHistory
{
$this->period = $period;
return $this;
}
public function setPersonLocation(?Person $personLocation): AccompanyingPeriodLocationHistory
{
$this->personLocation = $personLocation;
return $this;
}
public function setStartDate(?DateTimeImmutable $startDate): AccompanyingPeriodLocationHistory
{
$this->startDate = $startDate;
return $this;
}
}

View File

@@ -250,10 +250,7 @@ class AccompanyingPeriodWork implements AccompanyingPeriodLinkedWithSocialIssues
return $this->accompanyingPeriod;
}
/**
* @return Collection
*/
public function getAccompanyingPeriodWorkEvaluations()
public function getAccompanyingPeriodWorkEvaluations(): Collection
{
return $this->accompanyingPeriodWorkEvaluations;
}

View File

@@ -67,10 +67,15 @@ class AccompanyingPeriodWorkEvaluation implements TrackCreationInterface, TrackU
private ?User $createdBy = null;
/**
* **Note on deserialization/denormalization**: denormalization of documents is handled by.
*
* @see{Chill\PersonBundle\Serializer\Normalizer\AccompanyingPeriodWorkEvaluationDenormalizer}
*
* @ORM\OneToMany(
* targetEntity=AccompanyingPeriodWorkEvaluationDocument::class,
* mappedBy="accompanyingPeriodWorkEvaluation",
* cascade={"remove"}
* cascade={"remove", "persist"},
* orphanRemoval=true
* )
* @Serializer\Groups({"read"})
*/
@@ -261,6 +266,7 @@ class AccompanyingPeriodWorkEvaluation implements TrackCreationInterface, TrackU
public function removeDocument(AccompanyingPeriodWorkEvaluationDocument $document): self
{
$this->documents->removeElement($document);
$document->setAccompanyingPeriodWorkEvaluation(null);
return $this;
}

View File

@@ -41,6 +41,7 @@ class AccompanyingPeriodWorkEvaluationDocument implements \Chill\MainBundle\Doct
/**
* @ORM\Column(type="date_immutable", nullable=true, options={"default": null})
* @Serializer\Groups({"read"})
* @Serializer\Groups({"accompanying_period_work_evaluation:create"})
*/
private ?\DateTimeImmutable $createdAt = null;
@@ -49,6 +50,7 @@ class AccompanyingPeriodWorkEvaluationDocument implements \Chill\MainBundle\Doct
* targetEntity=User::class
* )
* @Serializer\Groups({"read"})
* @Serializer\Groups({"accompanying_period_work_evaluation:create"})
*/
private ?User $createdBy = null;
@@ -60,15 +62,32 @@ class AccompanyingPeriodWorkEvaluationDocument implements \Chill\MainBundle\Doct
* @internal the default name exceeds 64 characters, we must set manually:
* @ORM\SequenceGenerator(sequenceName="chill_person_social_work_eval_doc_id_seq", allocationSize=1, initialValue=1000)
* @Serializer\Groups({"read"})
* @Serializer\Groups({"accompanying_period_work_evaluation:create"})
*/
private ?int $id;
private ?int $id = null;
/**
* This is a workaround for client, to allow them to assign arbitrary data
* dedicated to their job.
*
* This data is not persisted into database, but will appears on the data
* normalized during the same request (like PUT/PATCH request)
*
* @Serializer\Groups({"read"})
* @Serializer\Groups({"write"})
* @Serializer\Groups({"accompanying_period_work_evaluation:create"})
*
* @var mixed
*/
private $key;
/**
* @ORM\ManyToOne(
* targetEntity=StoredObject::class,
* cascade={"remove"},
* )
* @Serializer\Groups({"read"})
* @Serializer\Groups({"write"})
* @Serializer\Groups({"accompanying_period_work_evaluation:create"})
*/
private ?StoredObject $storedObject = null;
@@ -77,12 +96,22 @@ class AccompanyingPeriodWorkEvaluationDocument implements \Chill\MainBundle\Doct
* targetEntity=DocGeneratorTemplate::class
* )
* @Serializer\Groups({"read"})
* @Serializer\Groups({"accompanying_period_work_evaluation:create"})
*/
private ?DocGeneratorTemplate $template = null;
/**
* @ORM\Column(type="text", nullable=false, options={"default": ""})
* @Serializer\Groups({"read"})
* @Serializer\Groups({"write"})
* @Serializer\Groups({"accompanying_period_work_evaluation:create"})
*/
private ?string $title = '';
/**
* @ORM\Column(type="date_immutable", nullable=true, options={"default": null})
* @Serializer\Groups({"read"})
* @Serializer\Groups({"accompanying_period_work_evaluation:create"})
*/
private ?\DateTimeImmutable $updatedAt = null;
@@ -91,6 +120,7 @@ class AccompanyingPeriodWorkEvaluationDocument implements \Chill\MainBundle\Doct
* targetEntity=User::class
* )
* @Serializer\Groups({"read"})
* @Serializer\Groups({"accompanying_period_work_evaluation:create"})
*/
private ?User $updatedBy = null;
@@ -117,6 +147,14 @@ class AccompanyingPeriodWorkEvaluationDocument implements \Chill\MainBundle\Doct
return $this->id;
}
/**
* @return mixed
*/
public function getKey()
{
return $this->key;
}
public function getStoredObject(): ?StoredObject
{
return $this->storedObject;
@@ -127,6 +165,11 @@ class AccompanyingPeriodWorkEvaluationDocument implements \Chill\MainBundle\Doct
return $this->template;
}
public function getTitle(): ?string
{
return $this->title;
}
/**
* @return DateTimeImmutable|null
*/
@@ -171,6 +214,18 @@ class AccompanyingPeriodWorkEvaluationDocument implements \Chill\MainBundle\Doct
return $this;
}
/**
* @param mixed $key
*
* @return AccompanyingPeriodWorkEvaluationDocument
*/
public function setKey($key)
{
$this->key = $key;
return $this;
}
public function setStoredObject(?StoredObject $storedObject): AccompanyingPeriodWorkEvaluationDocument
{
$this->storedObject = $storedObject;
@@ -185,6 +240,13 @@ class AccompanyingPeriodWorkEvaluationDocument implements \Chill\MainBundle\Doct
return $this;
}
public function setTitle(?string $title): AccompanyingPeriodWorkEvaluationDocument
{
$this->title = $title;
return $this;
}
public function setUpdatedAt(DateTimeInterface $datetime): TrackUpdateInterface
{
$this->updatedAt = $datetime;

View File

@@ -66,6 +66,7 @@ class AccompanyingPeriodParticipation
$this->startDate = new DateTime('now');
$this->accompanyingPeriod = $accompanyingPeriod;
$this->person = $person;
$person->getAccompanyingPeriodParticipations()->add($this);
}
public function getAccompanyingPeriod(): ?AccompanyingPeriod

View File

@@ -106,19 +106,13 @@ class Household
$this->compositions = new ArrayCollection();
}
/**
* @return $this
*/
public function addAddress(Address $address)
public function addAddress(Address $address): self
{
foreach ($this->getAddresses() as $a) {
if ($a->getValidFrom() <= $address->getValidFrom() && $a->getValidTo() === null) {
$a->setValidTo($address->getValidFrom());
}
if (!$this->addresses->contains($address)) {
$this->addresses[] = $address;
$this->makeAddressConsistent();
}
$this->addresses[] = $address;
return $this;
}
@@ -157,6 +151,31 @@ class Household
return $this->addresses;
}
public function getAddressesOrdered(): array
{
$addresses = $this->getAddresses()->toArray();
usort($addresses, static function (Address $a, Address $b) {
$validFromA = $a->getValidFrom()->format('Y-m-d');
$validFromB = $b->getValidFrom()->format('Y-m-d');
if ($a === $b) {
if (null === $a->getId()) {
return 1;
}
if (null === $b->getId()) {
return -1;
}
return $a->getId() <=> $b->getId();
}
return $validFromA <=> $validFromB;
});
return $addresses;
}
public function getCommentMembers(): CommentEmbeddable
{
return $this->commentMembers;
@@ -339,7 +358,7 @@ class Household
$membership->getEndDate()
)->filter(
static function (HouseholdMember $m) use ($membership) {
return $m !== $membership;
return $m->getPerson() !== $membership->getPerson();
}
);
}
@@ -358,24 +377,25 @@ class Household
public function getMembersOnRange(DateTimeImmutable $from, ?DateTimeImmutable $to): Collection
{
$criteria = new Criteria();
$expr = Criteria::expr();
return $this->getMembers()->filter(static function (HouseholdMember $m) use ($from, $to) {
if (null === $m->getEndDate() && null !== $to) {
return $m->getStartDate() <= $to;
}
$criteria->where(
$expr->gte('startDate', $from)
);
if (null === $to) {
return $m->getStartDate() >= $from || null === $m->getEndDate();
}
if (null !== $to) {
$criteria->andWhere(
$expr->orX(
$expr->lte('endDate', $to),
$expr->eq('endDate', null)
),
);
}
if (null !== $m->getEndDate() && $m->getEndDate() < $from) {
return false;
}
return $this->getMembers()
->matching($criteria);
if ($m->getStartDate() <= $to) {
return true;
}
return false;
});
}
public function getNonCurrentMembers(?DateTimeImmutable $now = null): Collection
@@ -418,6 +438,25 @@ class Household
return $this->getNonCurrentMembers($now)->matching($criteria);
}
public function getPreviousAddressOf(Address $address): ?Address
{
$iterator = new ArrayIterator($this->getAddressesOrdered());
$iterator->rewind();
while ($iterator->valid()) {
$current = $iterator->current();
$iterator->next();
if ($iterator->valid()) {
if ($iterator->current() === $address) {
return $current;
}
}
}
return null;
}
public function getWaitingForBirth(): bool
{
return $this->waitingForBirth;
@@ -462,6 +501,23 @@ class Household
} while ($iterator->valid());
}
public function makeAddressConsistent(): void
{
$iterator = new ArrayIterator($this->getAddressesOrdered());
$iterator->rewind();
while ($iterator->valid()) {
$current = $iterator->current();
$iterator->next();
if ($iterator->valid()) {
$current->setValidTo($iterator->current()->getValidFrom());
}
}
}
public function removeAddress(Address $address)
{
$this->addresses->removeElement($address);

View File

@@ -930,6 +930,8 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
/**
* Get current accompanyingPeriods array.
*
* @return AccompanyingPeriod[]|array
*/
public function getCurrentAccompanyingPeriods(): array
{