validation on simultaneous household membership for a person

This commit is contained in:
Julien Fastré 2021-06-14 12:56:01 +02:00
parent 51399b21b9
commit 17c3ecbabe
9 changed files with 112 additions and 3 deletions

View File

@ -159,7 +159,9 @@ class HouseholdMemberController extends ApiController
{
// TODO ACL
$form = $this->createForm(HouseholdMemberType::class, $member);
$form = $this->createForm(HouseholdMemberType::class, $member, [
'validation_groups' => [ 'household_memberships' ]
]);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {

View File

@ -19,7 +19,7 @@ use Chill\PersonBundle\Validator\Constraints\Household\MaxHolder;
* @Serializer\DiscriminatorMap(typeProperty="type", mapping={
* "household"=Household::class
* })
* @MaxHolder(groups={"memberships"})
* @MaxHolder(groups={"household_memberships"})
*/
class Household
{

View File

@ -7,6 +7,7 @@ use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Entity\Household\Household;
use Chill\PersonBundle\Entity\Household\Position;
use Symfony\Component\Serializer\Annotation as Serializer;
use Symfony\Component\Validator\Constraints as Assert;
/**
@ -28,18 +29,25 @@ class HouseholdMember
/**
* @ORM\ManyToOne(targetEntity=Position::class)
* @Serializer\Groups({"read"})
* @Assert\NotNull(groups={"household_memberships"})
*/
private ?Position $position = null;
/**
* @ORM\Column(type="date_immutable", nullable=true, options={"default": null})
* @Serializer\Groups({"read"})
* @Assert\NotNull(groups={"household_memberships"})
*/
private ?\DateTimeImmutable $startDate = null;
/**
* @ORM\Column(type="date_immutable", nullable= true, options={"default": null})
* @Serializer\Groups({"read"})
* @Assert\GreaterThan(
* propertyPath="startDate",
* message="household_membership.The end date must be after start date",
* groups={"household_memberships"}
* )
*/
private ?\DateTimeImmutable $endDate = null;
@ -67,6 +75,8 @@ class HouseholdMember
* targetEntity="\Chill\PersonBundle\Entity\Person"
* )
* @Serializer\Groups({"read"})
* @Assert\Valid(groups={"household_memberships"})
* @Assert\NotNull(groups={"household_memberships"})
*/
private ?Person $person = null;
@ -76,6 +86,8 @@ class HouseholdMember
* @ORM\ManyToOne(
* targetEntity="\Chill\PersonBundle\Entity\Household\Household"
* )
* @Assert\Valid(groups={"household_memberships"})
* @Assert\NotNull(groups={"household_memberships"})
*/
private ?Household $household = null;

View File

@ -35,7 +35,8 @@ class HouseholdMemberType extends AbstractType
}
$builder
->add('comment', ChillTextareaType::class, [
'label' => 'household.Comment'
'label' => 'household.Comment',
'required' => false
])
;
}

View File

@ -0,0 +1,18 @@
<?php
namespace Chill\PersonBundle\Validator\Constraints\Household;
use Symfony\Component\Validator\Constraint;
/**
* @Annotation
*/
class HouseholdMembershipSequential extends Constraint
{
public $message = 'household_membership.Person with membership covering';
public function getTargets()
{
return [ self::CLASS_CONSTRAINT ];
}
}

View File

@ -0,0 +1,65 @@
<?php
namespace Chill\PersonBundle\Validator\Constraints\Household;
use Chill\PersonBundle\Entity\Person;
use Chill\MainBundle\Util\DateRangeCovering;
use Chill\PersonBundle\Templating\Entity\PersonRender;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
use Symfony\Component\Validator\Exception\UnexpectedValueException;
/**
* Validate that a person does not belong to two household at
* the same time
*/
class HouseholdMembershipSequentialValidator extends ConstraintValidator
{
private PersonRender $render;
public function __construct(PersonRender $render)
{
$this->render = $render;
}
public function validate($person, Constraint $constraint)
{
if (!$person instanceof Person) {
throw new UnexpectedTypeException($constraint, Person::class);
}
$participations = $person->getHouseholdParticipations();
if ($participations->count() === 0) {
return;
}
$covers = new DateRangeCovering(1, $participations[0]
->getStartDate()->getTimezone());
foreach ($participations as $k => $p) {
$covers->add($p->getStartDate(), $p->getEndDate(), $k);
}
$covers->compute();
if ($covers->hasIntersections()) {
foreach ($covers->getIntersections() as list($start, $end, $metadata)) {
$participation = $participations[$metadata[0]];
$this->context
->buildViolation("household_membership.Person with membership covering")
->setParameters([
'%person_name%' => $this->render->renderString(
$participation->getPerson(), []
),
// TODO when date is correctly i18n, fix this
'%from%' => $start->format('d-m-Y')
])
->addViolation()
;
}
}
}
}

View File

@ -57,6 +57,11 @@ services:
tags:
- { name: validator.constraint_validator, alias: birthdate_not_before }
Chill\PersonBundle\Validator\Constraints\Household\HouseholdMembershipSequentialValidator:
autowire: true
tags:
- { name: validator.constraint_validator }
Chill\PersonBundle\Repository\:
autowire: true
autoconfigure: true

View File

@ -61,6 +61,9 @@ Chill\PersonBundle\Entity\Person:
- Callback:
callback: isAddressesValid
groups: [addresses_consistent]
- Chill\PersonBundle\Validator\Constraints\Household\HouseholdMembershipSequential:
groups: [ 'household_memberships' ]
Chill\PersonBundle\Entity\AccompanyingPeriod:
properties:

View File

@ -38,3 +38,6 @@ The date should not be empty: La date ne doit pas être vide
household:
max_holder_overflowed_infinity: Il ne peut pas y avoir plus de deux titulaires simultanément. Or, avec cette modification, ce nombre sera dépassé à partir du {{ start }}.
max_holder_overflowed: Il ne peut y avoir plus de deux titulaires simultanément. Or, avec cette modification, ce nombre sera dépassé entre le {{ start }} et le {{ end }}.
household_membership:
The end date must be after start date: La date de la fin de l'appartenance doit être postérieure à la date de début.
Person with membership covering: Une personne ne peut pas appartenir à deux ménages simultanément. Or, avec cette modification, %person_name% appartiendrait à deux ménages à partir du %from%.