mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-08-20 14:43:49 +00:00
merge master into issue388
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
<?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\AccompanyingPeriod\Events;
|
||||
|
||||
use Chill\MainBundle\Entity\Address;
|
||||
use Chill\MainBundle\Entity\Notification;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||
use Chill\PersonBundle\Event\Person\PersonAddressMoveEvent;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
use Symfony\Component\Templating\EngineInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
class PersonAddressMoveEventSubscriber implements EventSubscriberInterface
|
||||
{
|
||||
private EngineInterface $engine;
|
||||
|
||||
private EntityManagerInterface $entityManager;
|
||||
|
||||
private Security $security;
|
||||
|
||||
private TranslatorInterface $translator;
|
||||
|
||||
public function __construct(
|
||||
EngineInterface $engine,
|
||||
EntityManagerInterface $entityManager,
|
||||
Security $security,
|
||||
TranslatorInterface $translator
|
||||
) {
|
||||
$this->engine = $engine;
|
||||
$this->entityManager = $entityManager;
|
||||
$this->security = $security;
|
||||
$this->translator = $translator;
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
return [
|
||||
PersonAddressMoveEvent::class => 'resetPeriodLocation',
|
||||
];
|
||||
}
|
||||
|
||||
public function resetPeriodLocation(PersonAddressMoveEvent $event)
|
||||
{
|
||||
if ($event->getPreviousAddress() !== $event->getNextAddress()
|
||||
&& null !== $event->getPreviousAddress()
|
||||
) {
|
||||
$person = $event->getPerson();
|
||||
|
||||
foreach ($person->getCurrentAccompanyingPeriods() as $period) {
|
||||
if ($period->getStep() === AccompanyingPeriod::STEP_DRAFT) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
$period->getPersonLocation() === $person
|
||||
&& (
|
||||
$event->getMoveDate() >= $period->getLastLocationHistory()->getStartDate()
|
||||
|| $event->willChangeBeActiveAt(new DateTimeImmutable('now'))
|
||||
)
|
||||
&& null !== $period->getUser()
|
||||
&& $period->getUser() !== $this->security->getUser()
|
||||
) {
|
||||
// reset the location, back to an address
|
||||
$period->setPersonLocation(null);
|
||||
$period->setAddressLocation(Address::createFromAddress($event->getPreviousAddress()));
|
||||
|
||||
$notification = new Notification();
|
||||
$notification
|
||||
->addAddressee($period->getUser())
|
||||
->setTitle($this->translator->trans('period_notification.Person locating period has moved'))
|
||||
->setRelatedEntityClass(AccompanyingPeriod::class)
|
||||
->setRelatedEntityId($period->getId())
|
||||
->setMessage($this->engine->render('@ChillPerson/AccompanyingPeriod/notification_location_user_on_period_has_moved.fr.txt.twig', [
|
||||
'oldPersonLocation' => $person,
|
||||
'period' => $period,
|
||||
]));
|
||||
|
||||
$this->entityManager->persist($notification);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -60,6 +60,7 @@ class UserRefEventSubscriber implements EventSubscriberInterface
|
||||
{
|
||||
if ($period->hasPreviousUser()
|
||||
&& $period->getUser() !== $this->security->getUser()
|
||||
&& null !== $period->getUser()
|
||||
&& $period->getStep() !== AccompanyingPeriod::STEP_DRAFT
|
||||
) {
|
||||
$this->generateNotificationToUser($period);
|
||||
|
@@ -16,6 +16,7 @@ use Chill\MainBundle\Entity\Scope;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Serializer\Model\Collection;
|
||||
use Chill\MainBundle\Serializer\Model\Counter;
|
||||
use Chill\MainBundle\Serializer\Normalizer\DiscriminatedObjectDenormalizer;
|
||||
use Chill\PersonBundle\AccompanyingPeriod\Suggestion\ReferralsSuggestionInterface;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
|
||||
@@ -234,17 +235,19 @@ final class AccompanyingCourseApiController extends ApiController
|
||||
$requestor = null;
|
||||
$exceptions = [];
|
||||
|
||||
foreach ([Person::class, ThirdParty::class] as $class) {
|
||||
try {
|
||||
$requestor = $this->getSerializer()
|
||||
->deserialize($request->getContent(), $class, $_format, []);
|
||||
} catch (RuntimeException $e) {
|
||||
$exceptions[] = $e;
|
||||
}
|
||||
try {
|
||||
$requestor = $this->getSerializer()->deserialize(
|
||||
$request->getContent(),
|
||||
'@multi',
|
||||
$_format,
|
||||
[DiscriminatedObjectDenormalizer::ALLOWED_TYPES => [Person::class, ThirdParty::class]]
|
||||
);
|
||||
} catch (RuntimeException $e) {
|
||||
$exceptions[] = $e;
|
||||
}
|
||||
|
||||
if (null === $requestor) {
|
||||
throw new BadRequestException('Could not find any person or requestor', 0, $exceptions[0]);
|
||||
throw new BadRequestException('Could not find any person or thirdparty', 0, null);
|
||||
}
|
||||
|
||||
$accompanyingPeriod->setRequestor($requestor);
|
||||
|
@@ -32,6 +32,7 @@ use Symfony\Component\Workflow\Registry;
|
||||
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
use function array_slice;
|
||||
use function is_array;
|
||||
|
||||
/**
|
||||
@@ -221,9 +222,11 @@ class AccompanyingCourseController extends Controller
|
||||
|
||||
$activities = $this->getDoctrine()->getManager()->getRepository(Activity::class)->findBy(
|
||||
['accompanyingPeriod' => $accompanyingCourse],
|
||||
['date' => 'DESC'],
|
||||
['date' => 'DESC', 'id' => 'DESC'],
|
||||
);
|
||||
|
||||
$activities = array_slice($activities, 0, 3);
|
||||
|
||||
$works = $this->workRepository->findByAccompanyingPeriod(
|
||||
$accompanyingCourse,
|
||||
['startDate' => 'DESC', 'endDate' => 'DESC'],
|
||||
|
@@ -16,28 +16,37 @@ use Chill\MainBundle\Entity\Address;
|
||||
use Chill\MainBundle\Entity\AddressReference;
|
||||
use Chill\MainBundle\Serializer\Model\Collection;
|
||||
use Chill\PersonBundle\Entity\Household\Household;
|
||||
use Chill\PersonBundle\Entity\Household\HouseholdMember;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\PersonBundle\Event\Person\PersonAddressMoveEvent;
|
||||
use Chill\PersonBundle\Repository\Household\HouseholdACLAwareRepositoryInterface;
|
||||
use Chill\PersonBundle\Repository\Household\HouseholdRepository;
|
||||
use Chill\PersonBundle\Security\Authorization\HouseholdVoter;
|
||||
use DateTimeImmutable;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
|
||||
|
||||
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
|
||||
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
|
||||
use function array_filter;
|
||||
use function array_values;
|
||||
|
||||
class HouseholdApiController extends ApiController
|
||||
{
|
||||
private EventDispatcherInterface $eventDispatcher;
|
||||
|
||||
private HouseholdACLAwareRepositoryInterface $householdACLAwareRepository;
|
||||
|
||||
private HouseholdRepository $householdRepository;
|
||||
|
||||
public function __construct(
|
||||
EventDispatcherInterface $eventDispatcher,
|
||||
HouseholdRepository $householdRepository,
|
||||
HouseholdACLAwareRepositoryInterface $householdACLAwareRepository
|
||||
) {
|
||||
$this->eventDispatcher = $eventDispatcher;
|
||||
$this->householdRepository = $householdRepository;
|
||||
$this->householdACLAwareRepository = $householdACLAwareRepository;
|
||||
}
|
||||
@@ -50,7 +59,6 @@ class HouseholdApiController extends ApiController
|
||||
*/
|
||||
public function getHouseholdByAddressReference(AddressReference $addressReference): Response
|
||||
{
|
||||
// TODO ACL
|
||||
$this->denyAccessUnlessGranted('ROLE_USER');
|
||||
|
||||
$total = $this->householdACLAwareRepository->countByAddressReference($addressReference);
|
||||
@@ -67,9 +75,51 @@ class HouseholdApiController extends ApiController
|
||||
]);
|
||||
}
|
||||
|
||||
public function householdAddressApi($id, Request $request, string $_format): Response
|
||||
/**
|
||||
* Add an address to a household.
|
||||
*
|
||||
* @Route("/api/1.0/person/household/{id}/address.{_format}", name="chill_api_single_household_address",
|
||||
* methods={"POST"}, requirements={"_format": "json"})
|
||||
*/
|
||||
public function householdAddressApi(Household $household, Request $request, string $_format): Response
|
||||
{
|
||||
return $this->addRemoveSomething('address', $id, $request, $_format, 'address', Address::class, ['groups' => ['read']]);
|
||||
$this->denyAccessUnlessGranted(HouseholdVoter::EDIT, $household);
|
||||
|
||||
/** @var Address $address */
|
||||
$address = $this->getSerializer()->deserialize($request->getContent(), Address::class, $_format, [
|
||||
AbstractNormalizer::GROUPS => ['write'],
|
||||
]);
|
||||
|
||||
$household->addAddress($address);
|
||||
|
||||
foreach ($household->getMembersOnRange(
|
||||
DateTimeImmutable::createFromMutable($address->getValidFrom()),
|
||||
null === $address->getValidTo() ? null :
|
||||
DateTimeImmutable::createFromMutable($address->getValidTo())
|
||||
) as $member) {
|
||||
/** @var HouseholdMember $member */
|
||||
$event = new PersonAddressMoveEvent($member->getPerson());
|
||||
$event
|
||||
->setPreviousAddress($household->getPreviousAddressOf($address))
|
||||
->setNextAddress($address);
|
||||
dump($event);
|
||||
$this->eventDispatcher->dispatch($event);
|
||||
}
|
||||
|
||||
$errors = $this->getValidator()->validate($household);
|
||||
|
||||
if ($errors->count() > 0) {
|
||||
return $this->json($errors, 422);
|
||||
}
|
||||
|
||||
$this->getDoctrine()->getManager()->flush();
|
||||
|
||||
return $this->json(
|
||||
$address,
|
||||
Response::HTTP_OK,
|
||||
[],
|
||||
[AbstractNormalizer::GROUPS => ['read']]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -24,7 +24,7 @@ use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
|
||||
use Symfony\Component\Serializer\SerializerInterface;
|
||||
use Symfony\Component\Translation\TranslatorInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
use function array_key_exists;
|
||||
use function count;
|
||||
|
@@ -26,7 +26,7 @@ use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
use Symfony\Component\Serializer\Exception;
|
||||
use Symfony\Component\Translation\TranslatorInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
use function count;
|
||||
|
||||
@@ -180,6 +180,7 @@ class HouseholdMemberController extends ApiController
|
||||
public function move(Request $request, $_format): Response
|
||||
{
|
||||
try {
|
||||
/** @var MembersEditor $editor */
|
||||
$editor = $this->getSerializer()
|
||||
->deserialize(
|
||||
$request->getContent(),
|
||||
@@ -199,6 +200,9 @@ class HouseholdMemberController extends ApiController
|
||||
return $this->json($errors, 422);
|
||||
}
|
||||
|
||||
// launch events on post move
|
||||
$editor->postMove();
|
||||
|
||||
$em = $this->getDoctrine()->getManager();
|
||||
|
||||
// if new household, persist it
|
||||
|
@@ -30,8 +30,8 @@ use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpFoundation\Session\SessionInterface;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
use Symfony\Component\Translation\TranslatorInterface;
|
||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
use function count;
|
||||
use function hash;
|
||||
@@ -224,6 +224,8 @@ final class PersonController extends AbstractController
|
||||
'label' => 'Add the person',
|
||||
])->add('createPeriod', SubmitType::class, [
|
||||
'label' => 'Add the person and create an accompanying period',
|
||||
])->add('createHousehold', SubmitType::class, [
|
||||
'label' => 'Add the person and create a household',
|
||||
]);
|
||||
|
||||
$form->handleRequest($request);
|
||||
@@ -252,6 +254,12 @@ final class PersonController extends AbstractController
|
||||
]);
|
||||
}
|
||||
|
||||
if ($form->get('createHousehold')->isClicked()) {
|
||||
return $this->redirectToRoute('chill_person_household_members_editor', [
|
||||
'persons' => [$person->getId()],
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->redirectToRoute(
|
||||
'chill_person_general_edit',
|
||||
['person_id' => $person->getId()]
|
||||
|
@@ -28,7 +28,7 @@ use http\Exception\InvalidArgumentException;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Translation\TranslatorInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
use function count;
|
||||
|
||||
|
@@ -30,14 +30,14 @@ class UserAccompanyingPeriodController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/{_locale}/accompanying-periods", name="chill_person_accompanying_period_user")
|
||||
* @Route("/{_locale}/person/accompanying-periods/my", name="chill_person_accompanying_period_user")
|
||||
*/
|
||||
public function listAction(Request $request)
|
||||
{
|
||||
$total = $this->accompanyingPeriodRepository->countBy(['user' => $this->getUser()]);
|
||||
$total = $this->accompanyingPeriodRepository->countBy(['user' => $this->getUser(), 'step' => ['CONFIRMED', 'CLOSED']]);
|
||||
$pagination = $this->paginatorFactory->create($total);
|
||||
$accompanyingPeriods = $this->accompanyingPeriodRepository->findBy(
|
||||
['user' => $this->getUser()],
|
||||
['user' => $this->getUser(), 'step' => ['CONFIRMED', 'CLOSED']],
|
||||
['openingDate' => 'DESC'],
|
||||
$pagination->getItemsPerPage(),
|
||||
$pagination->getCurrentPageFirstItemNumber()
|
||||
@@ -48,4 +48,24 @@ class UserAccompanyingPeriodController extends AbstractController
|
||||
'pagination' => $pagination,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/{_locale}/person/accompanying-periods/my/drafts", name="chill_person_accompanying_period_draft_user")
|
||||
*/
|
||||
public function listDraftsAction(Request $request)
|
||||
{
|
||||
$total = $this->accompanyingPeriodRepository->countBy(['user' => $this->getUser(), 'step' => 'DRAFT']);
|
||||
$pagination = $this->paginatorFactory->create($total);
|
||||
$accompanyingPeriods = $this->accompanyingPeriodRepository->findBy(
|
||||
['createdBy' => $this->getUser(), 'step' => 'DRAFT'],
|
||||
['id' => 'DESC'],
|
||||
$pagination->getItemsPerPage(),
|
||||
$pagination->getCurrentPageFirstItemNumber()
|
||||
);
|
||||
|
||||
return $this->render('@ChillPerson/AccompanyingPeriod/user_draft_periods_list.html.twig', [
|
||||
'accompanyingPeriods' => $accompanyingPeriods,
|
||||
'pagination' => $pagination,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@@ -527,15 +527,6 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac
|
||||
Request::METHOD_HEAD => true,
|
||||
],
|
||||
],
|
||||
'address' => [
|
||||
'methods' => [
|
||||
Request::METHOD_POST => true,
|
||||
Request::METHOD_DELETE => true,
|
||||
Request::METHOD_GET => false,
|
||||
Request::METHOD_HEAD => false,
|
||||
],
|
||||
'controller_action' => 'householdAddressApi',
|
||||
],
|
||||
'suggestHouseholdByAccompanyingPeriodParticipation' => [
|
||||
'path' => '/suggest/by-person/{person_id}/through-accompanying-period-participation.{_format}',
|
||||
'methods' => [
|
||||
|
@@ -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;
|
||||
|
||||
/**
|
||||
@@ -212,6 +215,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
|
||||
*
|
||||
@@ -384,6 +393,7 @@ class AccompanyingPeriod implements
|
||||
$this->works = new ArrayCollection();
|
||||
$this->resources = new ArrayCollection();
|
||||
$this->userHistories = new ArrayCollection();
|
||||
$this->locationHistories = new ArrayCollection();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -434,6 +444,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) {
|
||||
@@ -666,6 +709,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.
|
||||
*
|
||||
@@ -680,6 +734,14 @@ class AccompanyingPeriod implements
|
||||
return $this->getAddressLocation();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection|AccompanyingPeriodLocationHistory[]
|
||||
*/
|
||||
public function getLocationHistories(): Collection
|
||||
{
|
||||
return $this->locationHistories;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get where the location is.
|
||||
*
|
||||
@@ -982,6 +1044,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.
|
||||
*/
|
||||
@@ -1036,7 +1107,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;
|
||||
}
|
||||
@@ -1139,7 +1221,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;
|
||||
}
|
||||
@@ -1153,11 +1246,8 @@ class AccompanyingPeriod implements
|
||||
$this->removeComment($this->pinnedComment);
|
||||
}
|
||||
|
||||
if ($comment instanceof Comment) {
|
||||
if (null !== $this->pinnedComment) {
|
||||
$this->addComment($this->pinnedComment);
|
||||
}
|
||||
$this->addComment($comment);
|
||||
if (null !== $this->pinnedComment) {
|
||||
$this->addComment($this->pinnedComment);
|
||||
}
|
||||
|
||||
$this->pinnedComment = $comment;
|
||||
@@ -1209,8 +1299,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;
|
||||
}
|
||||
|
||||
@@ -1249,6 +1345,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;
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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
|
||||
|
@@ -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;
|
||||
@@ -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);
|
||||
|
@@ -930,6 +930,8 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
|
||||
|
||||
/**
|
||||
* Get current accompanyingPeriods array.
|
||||
*
|
||||
* @return AccompanyingPeriod[]|array
|
||||
*/
|
||||
public function getCurrentAccompanyingPeriods(): array
|
||||
{
|
||||
|
@@ -0,0 +1,209 @@
|
||||
<?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\Event\Person;
|
||||
|
||||
use Chill\MainBundle\Entity\Address;
|
||||
use Chill\PersonBundle\Entity\Household\Household;
|
||||
use Chill\PersonBundle\Entity\Household\HouseholdMember;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use DateTime;
|
||||
use DateTimeImmutable;
|
||||
use Symfony\Contracts\EventDispatcher\Event;
|
||||
|
||||
class PersonAddressMoveEvent extends Event
|
||||
{
|
||||
public const PERSON_MOVE_POST = 'chill_person.person_move_post';
|
||||
|
||||
private ?Address $nextAddress = null;
|
||||
|
||||
private ?HouseholdMember $nextMembership = null;
|
||||
|
||||
private Person $person;
|
||||
|
||||
private ?Address $previousAddress = null;
|
||||
|
||||
private ?HouseholdMember $previousMembership = null;
|
||||
|
||||
public function __construct(
|
||||
Person $person
|
||||
) {
|
||||
$this->person = $person;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the date of the move.
|
||||
*
|
||||
* It might be either:
|
||||
*
|
||||
* * the date of the new membership;
|
||||
* * or the date of the move
|
||||
* * or the date when the household leaving take place (the end date of the previous membership)
|
||||
*/
|
||||
public function getMoveDate(): DateTimeImmutable
|
||||
{
|
||||
if ($this->personLeaveWithoutHousehold()) {
|
||||
return $this->getPreviousMembership()->getEndDate();
|
||||
}
|
||||
|
||||
if ($this->personChangeHousehold()) {
|
||||
return $this->getNextMembership()->getStartDate();
|
||||
}
|
||||
|
||||
// person is changing address without household
|
||||
return DateTimeImmutable::createFromMutable($this->getNextAddress()->getValidFrom());
|
||||
}
|
||||
|
||||
public function getNextAddress(): ?Address
|
||||
{
|
||||
if (null !== $this->getNextMembership()) {
|
||||
return $this->getNextMembership()->getHousehold()
|
||||
->getCurrentAddress(
|
||||
$this->getMoveDate() === null ? null :
|
||||
DateTime::createFromImmutable($this->getMoveDate())
|
||||
);
|
||||
}
|
||||
|
||||
return $this->nextAddress;
|
||||
}
|
||||
|
||||
public function getNextHousehold(): ?Household
|
||||
{
|
||||
if (null !== $nextMembership = $this->getNextMembership()) {
|
||||
return $nextMembership->getHousehold();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getNextMembership(): ?HouseholdMember
|
||||
{
|
||||
return $this->nextMembership;
|
||||
}
|
||||
|
||||
public function getPerson(): Person
|
||||
{
|
||||
return $this->person;
|
||||
}
|
||||
|
||||
public function getPreviousAddress(): ?Address
|
||||
{
|
||||
if (null !== $this->getPreviousMembership()) {
|
||||
return $this->getPreviousMembership()->getHousehold()
|
||||
->getCurrentAddress(
|
||||
null === $this->getMoveDate() ? null :
|
||||
DateTime::createFromImmutable($this->getMoveDate())
|
||||
);
|
||||
}
|
||||
|
||||
return $this->previousAddress;
|
||||
}
|
||||
|
||||
public function getPreviousHousehold(): ?Household
|
||||
{
|
||||
if (null !== $previousMembership = $this->getPreviousMembership()) {
|
||||
return $previousMembership->getHousehold();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getPreviousMembership(): ?HouseholdMember
|
||||
{
|
||||
return $this->previousMembership;
|
||||
}
|
||||
|
||||
public function personChangeAddress(): bool
|
||||
{
|
||||
return $this->getPreviousAddress() !== $this->getNextAddress();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the user change household (this include the fact that a person
|
||||
* leave household without a new one).
|
||||
*/
|
||||
public function personChangeHousehold(): bool
|
||||
{
|
||||
return $this->getPreviousHousehold() !== $this->getNextHousehold();
|
||||
}
|
||||
|
||||
public function personLeaveWithoutHousehold(): bool
|
||||
{
|
||||
return null === $this->getNextMembership()
|
||||
&& null === $this->getNextAddress();
|
||||
}
|
||||
|
||||
public function setNextAddress(?Address $nextAddress): PersonAddressMoveEvent
|
||||
{
|
||||
$this->nextAddress = $nextAddress;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setNextMembership(?HouseholdMember $nextMembership): PersonAddressMoveEvent
|
||||
{
|
||||
$this->nextMembership = $nextMembership;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setPreviousAddress(?Address $previousAddress): PersonAddressMoveEvent
|
||||
{
|
||||
$this->previousAddress = $previousAddress;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setPreviousMembership(?HouseholdMember $previousMembership): PersonAddressMoveEvent
|
||||
{
|
||||
$this->previousMembership = $previousMembership;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will the change affect this date ?
|
||||
*/
|
||||
public function willChangeBeActiveAt(DateTimeImmutable $date): bool
|
||||
{
|
||||
if ($this->getMoveDate() < $date && $this->personLeaveWithoutHousehold()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->personChangeHousehold()) {
|
||||
if ($this->getMoveDate() > $date) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (null === $this->getNextMembership()->getEndDate()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->getNextMembership()->getEndDate() > $date) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if ($this->getNextAddress()->getValidFrom() > $date) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (null === $this->getNextAddress()->getValidTo()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->getNextAddress()->getValidTo() > $date) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@@ -20,8 +20,8 @@ use Doctrine\ORM\QueryBuilder;
|
||||
use LogicException;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Translation\TranslatorInterface;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
final class CountryOfBirthAggregator implements AggregatorInterface, ExportElementValidatedInterface
|
||||
{
|
||||
|
@@ -17,7 +17,7 @@ use Chill\PersonBundle\Export\Declarations;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use LogicException;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Translation\TranslatorInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
final class GenderAggregator implements AggregatorInterface
|
||||
{
|
||||
|
@@ -20,8 +20,8 @@ use Doctrine\ORM\QueryBuilder;
|
||||
use LogicException;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Translation\TranslatorInterface;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
final class NationalityAggregator implements AggregatorInterface, ExportElementValidatedInterface
|
||||
{
|
||||
|
@@ -29,9 +29,9 @@ 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\Translation\TranslatorInterface;
|
||||
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;
|
||||
|
@@ -24,8 +24,8 @@ 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\Translation\TranslatorInterface;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
/**
|
||||
* Render a list of duplicate peoples.
|
||||
@@ -67,7 +67,7 @@ class ListPersonDuplicate implements DirectExportInterface, ExportElementValidat
|
||||
$this->translator = $translator;
|
||||
$this->router = $router;
|
||||
$this->baseUrl = $routeParameters['scheme'] .
|
||||
'://' . $routeParameters['host'];
|
||||
'://' . $routeParameters['host'];
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
|
@@ -18,8 +18,8 @@ use Doctrine\ORM\Query\Expr;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Translation\TranslatorInterface;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
use function array_filter;
|
||||
use function count;
|
||||
|
@@ -25,6 +25,13 @@ class HouseholdMemberType extends AbstractType
|
||||
'label' => 'household.Start date',
|
||||
'input' => 'datetime_immutable',
|
||||
]);
|
||||
|
||||
if (!$options['data']->getPosition()->getShareHousehold()) {
|
||||
$builder->add('endDate', ChillDateType::class, [
|
||||
'label' => 'household.End date',
|
||||
'input' => 'datetime_immutable',
|
||||
]);
|
||||
}
|
||||
$builder
|
||||
->add('comment', ChillTextareaType::class, [
|
||||
'label' => 'household.Comment',
|
||||
|
@@ -12,11 +12,11 @@ declare(strict_types=1);
|
||||
namespace Chill\PersonBundle\Form;
|
||||
|
||||
use Chill\CustomFieldsBundle\Form\Type\CustomFieldType;
|
||||
use Chill\MainBundle\Entity\Civility;
|
||||
use Chill\MainBundle\Form\Type\ChillCollectionType;
|
||||
use Chill\MainBundle\Form\Type\ChillDateType;
|
||||
use Chill\MainBundle\Form\Type\ChillTextareaType;
|
||||
use Chill\MainBundle\Form\Type\CommentType;
|
||||
use Chill\MainBundle\Form\Type\PickCivilityType;
|
||||
use Chill\MainBundle\Form\Type\Select2CountryType;
|
||||
use Chill\MainBundle\Form\Type\Select2LanguageType;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||
@@ -27,9 +27,6 @@ use Chill\PersonBundle\Form\Type\GenderType;
|
||||
use Chill\PersonBundle\Form\Type\PersonAltNameType;
|
||||
use Chill\PersonBundle\Form\Type\PersonPhoneType;
|
||||
use Chill\PersonBundle\Form\Type\Select2MaritalStatusType;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\CallbackTransformer;
|
||||
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
||||
@@ -190,19 +187,10 @@ class PersonType extends AbstractType
|
||||
|
||||
if ('visible' === $this->config['civility']) {
|
||||
$builder
|
||||
->add('civility', EntityType::class, [
|
||||
'label' => 'Civility',
|
||||
'class' => Civility::class,
|
||||
'choice_label' => function (Civility $civility): string {
|
||||
return $this->translatableStringHelper->localize($civility->getName());
|
||||
},
|
||||
'query_builder' => static function (EntityRepository $er): QueryBuilder {
|
||||
return $er->createQueryBuilder('c')
|
||||
->where('c.active = true')
|
||||
->orderBy('c.order');
|
||||
},
|
||||
'placeholder' => 'choose civility',
|
||||
->add('civility', PickCivilityType::class, [
|
||||
'required' => false,
|
||||
'label' => 'Civility',
|
||||
'placeholder' => 'choose civility',
|
||||
]);
|
||||
}
|
||||
|
||||
|
@@ -27,7 +27,7 @@ use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
||||
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
|
||||
use Symfony\Component\Security\Core\Role\Role;
|
||||
use Symfony\Component\Translation\TranslatorInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
use function in_array;
|
||||
use function is_array;
|
||||
|
@@ -15,13 +15,14 @@ use Chill\PersonBundle\Entity\Household\Household;
|
||||
use Chill\PersonBundle\Entity\Household\HouseholdMember;
|
||||
use Chill\PersonBundle\Entity\Household\Position;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\PersonBundle\Event\Person\PersonAddressMoveEvent;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\Common\Collections\Criteria;
|
||||
use LogicException;
|
||||
use Symfony\Component\Validator\ConstraintViolationList;
|
||||
use Symfony\Component\Validator\ConstraintViolationListInterface;
|
||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
|
||||
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
|
||||
use function in_array;
|
||||
use function spl_object_hash;
|
||||
|
||||
@@ -33,6 +34,10 @@ class MembersEditor
|
||||
|
||||
public const VALIDATION_GROUP_CREATED = 'household_memberships_created';
|
||||
|
||||
private EventDispatcherInterface $eventDispatcher;
|
||||
|
||||
private array $events = [];
|
||||
|
||||
private ?Household $household = null;
|
||||
|
||||
private array $membershipsAffected = [];
|
||||
@@ -43,10 +48,11 @@ class MembersEditor
|
||||
|
||||
private ValidatorInterface $validator;
|
||||
|
||||
public function __construct(ValidatorInterface $validator, ?Household $household)
|
||||
public function __construct(ValidatorInterface $validator, ?Household $household, EventDispatcherInterface $eventDispatcher)
|
||||
{
|
||||
$this->validator = $validator;
|
||||
$this->household = $household;
|
||||
$this->eventDispatcher = $eventDispatcher;
|
||||
}
|
||||
|
||||
public function addMovement(DateTimeImmutable $date, Person $person, Position $position, ?bool $holder = false, ?string $comment = null): self
|
||||
@@ -55,6 +61,8 @@ class MembersEditor
|
||||
throw new LogicException('You must define a household first');
|
||||
}
|
||||
|
||||
$event = new PersonAddressMoveEvent($person);
|
||||
|
||||
$membership = (new HouseholdMember())
|
||||
->setStartDate($date)
|
||||
->setPerson($person)
|
||||
@@ -62,6 +70,7 @@ class MembersEditor
|
||||
->setHolder($holder)
|
||||
->setComment($comment);
|
||||
$this->household->addMember($membership);
|
||||
$event->setNextMembership($membership);
|
||||
|
||||
if ($position->getShareHousehold()) {
|
||||
foreach ($person->getHouseholdParticipationsShareHousehold() as $participation) {
|
||||
@@ -74,6 +83,7 @@ class MembersEditor
|
||||
}
|
||||
|
||||
if ($participation->getEndDate() === null || $participation->getEndDate() > $date) {
|
||||
$event->setPreviousMembership($participation);
|
||||
$participation->setEndDate($date);
|
||||
$this->membershipsAffected[] = $participation;
|
||||
$this->oldMembershipsHashes[] = spl_object_hash($participation);
|
||||
@@ -92,6 +102,7 @@ class MembersEditor
|
||||
|
||||
$this->membershipsAffected[] = $membership;
|
||||
$this->persistables[] = $membership;
|
||||
$this->events[] = $event;
|
||||
|
||||
return $this;
|
||||
}
|
||||
@@ -129,6 +140,8 @@ class MembersEditor
|
||||
->matching($criteria);
|
||||
|
||||
foreach ($participations as $participation) {
|
||||
$this->events[] = $event = new PersonAddressMoveEvent($person);
|
||||
$event->setPreviousMembership($participation);
|
||||
$participation->setEndDate($date);
|
||||
$this->membershipsAffected[] = $participation;
|
||||
}
|
||||
@@ -136,6 +149,13 @@ class MembersEditor
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function postMove(): void
|
||||
{
|
||||
foreach ($this->events as $event) {
|
||||
$this->eventDispatcher->dispatch($event);
|
||||
}
|
||||
}
|
||||
|
||||
public function validate(): ConstraintViolationListInterface
|
||||
{
|
||||
if ($this->hasHousehold()) {
|
||||
|
@@ -13,16 +13,24 @@ namespace Chill\PersonBundle\Household;
|
||||
|
||||
use Chill\PersonBundle\Entity\Household\Household;
|
||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
|
||||
|
||||
class MembersEditorFactory
|
||||
{
|
||||
public function __construct(ValidatorInterface $validator)
|
||||
{
|
||||
private EventDispatcherInterface $eventDispatcher;
|
||||
|
||||
private ValidatorInterface $validator;
|
||||
|
||||
public function __construct(
|
||||
EventDispatcherInterface $eventDispatcher,
|
||||
ValidatorInterface $validator
|
||||
) {
|
||||
$this->validator = $validator;
|
||||
$this->eventDispatcher = $eventDispatcher;
|
||||
}
|
||||
|
||||
public function createEditor(?Household $household = null): MembersEditor
|
||||
{
|
||||
return new MembersEditor($this->validator, $household);
|
||||
return new MembersEditor($this->validator, $household, $this->eventDispatcher);
|
||||
}
|
||||
}
|
||||
|
@@ -72,6 +72,16 @@ class PersonMenuBuilder implements LocalMenuBuilderInterface
|
||||
'order' => 60,
|
||||
]);
|
||||
|
||||
$menu->addChild($this->translator->trans('person_resources_menu'), [
|
||||
'route' => 'chill_person_resource_list',
|
||||
'routeParameters' => [
|
||||
'person_id' => $parameters['person']->getId(),
|
||||
],
|
||||
])
|
||||
->setExtras([
|
||||
'order' => 70,
|
||||
]);
|
||||
|
||||
$menu->addChild($this->translator->trans('household.person history'), [
|
||||
'route' => 'chill_person_household_person_history',
|
||||
'routeParameters' => [
|
||||
@@ -106,16 +116,6 @@ class PersonMenuBuilder implements LocalMenuBuilderInterface
|
||||
'order' => 100,
|
||||
]);
|
||||
}
|
||||
|
||||
$menu->addChild($this->translator->trans('person_resources_menu'), [
|
||||
'route' => 'chill_person_resource_list',
|
||||
'routeParameters' => [
|
||||
'person_id' => $parameters['person']->getId(),
|
||||
],
|
||||
])
|
||||
->setExtras([
|
||||
'order' => 99999,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function getMenuIds(): array
|
||||
|
@@ -15,7 +15,7 @@ use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
|
||||
use Chill\PersonBundle\Security\Authorization\PersonVoter;
|
||||
use Knp\Menu\MenuItem;
|
||||
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
|
||||
use Symfony\Component\Translation\TranslatorInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
/**
|
||||
* Class SectionMenuBuilder.
|
||||
|
@@ -44,6 +44,13 @@ class UserMenuBuilder implements LocalMenuBuilderInterface
|
||||
'order' => 20,
|
||||
'icon' => 'tasks',
|
||||
]);
|
||||
$menu->addChild('My accompanying periods in draft', [
|
||||
'route' => 'chill_person_accompanying_period_draft_user',
|
||||
])
|
||||
->setExtras([
|
||||
'order' => 30,
|
||||
'icon' => 'tasks',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -71,7 +71,7 @@ final class AccompanyingPeriodRepository implements ObjectRepository
|
||||
$qb = $this->buildQueryByRecentUserHistory($user, $since);
|
||||
|
||||
return $qb->select('a')
|
||||
->distinct(true)
|
||||
->addOrderBy('userHistory.startDate', 'DESC')
|
||||
->getQuery()
|
||||
->setMaxResults($limit)
|
||||
->setFirstResult($offset)
|
||||
@@ -95,6 +95,7 @@ final class AccompanyingPeriodRepository implements ObjectRepository
|
||||
$qb
|
||||
->join('a.userHistories', 'userHistory')
|
||||
->where($qb->expr()->eq('a.user', ':user'))
|
||||
->andWhere($qb->expr()->neq('a.step', "'" . AccompanyingPeriod::STEP_DRAFT . "'"))
|
||||
->andWhere($qb->expr()->gte('userHistory.startDate', ':since'))
|
||||
->andWhere($qb->expr()->isNull('userHistory.endDate'))
|
||||
->setParameter('user', $user)
|
||||
|
@@ -14,7 +14,7 @@ namespace Chill\PersonBundle\Repository\Household;
|
||||
use Chill\MainBundle\Entity\AddressReference;
|
||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
||||
use Chill\PersonBundle\Entity\Household\Household;
|
||||
use Chill\PersonBundle\Security\Authorization\HouseholdVoter;
|
||||
use Chill\PersonBundle\Security\Authorization\PersonVoter;
|
||||
use DateTime;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
@@ -39,7 +39,7 @@ final class HouseholdACLAwareRepository implements HouseholdACLAwareRepositoryIn
|
||||
{
|
||||
$centers = $this->authorizationHelper->getReachableCenters(
|
||||
$this->security->getUser(),
|
||||
HouseholdVoter::SEE
|
||||
PersonVoter::SEE // the authorization to see a household is the same as seeing a person
|
||||
);
|
||||
|
||||
if ([] === $centers) {
|
||||
|
@@ -20,7 +20,6 @@
|
||||
<div class="form-check" v-for="p in participationWithoutHousehold" :key="p.id">
|
||||
<input type="checkbox"
|
||||
class="form-check-input"
|
||||
v-model="hasNoHousehold"
|
||||
name="persons[]"
|
||||
checked="checked"
|
||||
:id="p.person.id"
|
||||
@@ -56,14 +55,18 @@
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<add-persons
|
||||
buttonTitle="persons_associated.add_persons"
|
||||
modalTitle="add_persons.title"
|
||||
v-bind:key="addPersons.key"
|
||||
v-bind:options="addPersons.options"
|
||||
@addNewPersons="addNewPersons"
|
||||
ref="addPersons"> <!-- to cast child method -->
|
||||
</add-persons>
|
||||
<ul class="record_actions">
|
||||
<li class="add-persons">
|
||||
<add-persons
|
||||
buttonTitle="persons_associated.add_persons"
|
||||
modalTitle="add_persons.title"
|
||||
v-bind:key="addPersons.key"
|
||||
v-bind:options="addPersons.options"
|
||||
@addNewPersons="addNewPersons"
|
||||
ref="addPersons"> <!-- to cast child method -->
|
||||
</add-persons>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div v-if="!isParticipationValid" class="alert alert-warning to-confirm">
|
||||
|
@@ -10,6 +10,7 @@
|
||||
addAge : true,
|
||||
hLevel : 3,
|
||||
isConfidential : false,
|
||||
isMultiline: true,
|
||||
}"
|
||||
:person="participation.person"
|
||||
:returnPath="getAccompanyingCourseReturnPath">
|
||||
@@ -28,7 +29,7 @@
|
||||
</a>
|
||||
</li>
|
||||
<li><on-the-fly :type="participation.person.type" :id="participation.person.id" action="show"></on-the-fly></li>
|
||||
<li><on-the-fly :type="participation.person.type" :id="participation.person.id" action="edit" @saveFormOnTheFly="saveFormOnTheFly" :canCloseModal="canCloseOnTheFlyModal"></on-the-fly></li>
|
||||
<li><on-the-fly :type="participation.person.type" :id="participation.person.id" action="edit" @saveFormOnTheFly="saveFormOnTheFly" ref="onTheFly"></on-the-fly></li>
|
||||
<li>
|
||||
<button v-if="!participation.endDate"
|
||||
class="btn btn-sm btn-remove"
|
||||
@@ -90,7 +91,6 @@ export default {
|
||||
hLevel: 1
|
||||
}
|
||||
},
|
||||
canCloseOnTheFlyModal: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -120,12 +120,14 @@ export default {
|
||||
if (payload.data.birthdate !== null) { body.birthdate = payload.data.birthdate; }
|
||||
body.phonenumber = payload.data.phonenumber;
|
||||
body.mobilenumber = payload.data.mobilenumber;
|
||||
body.email = payload.data.email;
|
||||
body.altNames = payload.data.altNames;
|
||||
body.gender = payload.data.gender;
|
||||
|
||||
makeFetch('PATCH', `/api/1.0/person/person/${payload.data.id}.json`, body)
|
||||
.then(response => {
|
||||
this.$store.dispatch('addPerson', { target: payload.target, body: response })
|
||||
this.canCloseOnTheFlyModal = true;
|
||||
this.$store.dispatch('addPerson', { target: payload.target, body: response });
|
||||
this.$refs.onTheFly.closeModal();
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error.name === 'ValidationException') {
|
||||
@@ -143,10 +145,10 @@ export default {
|
||||
body.telephone = payload.data.phonenumber;
|
||||
body.address = { id: payload.data.address.address_id };
|
||||
|
||||
makeFetch('PATCH', `/api/1.0/third-party/third-party/${payload.data.id}.json`, body)
|
||||
makeFetch('PATCH', `/api/1.0/thirdparty/thirdparty/${payload.data.id}.json`, body)
|
||||
.then(response => {
|
||||
this.$store.dispatch('addThirdparty', { target: payload.target, body: response })
|
||||
this.canCloseOnTheFlyModal = true;
|
||||
this.$refs.onTheFly.closeModal();
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error.name === 'ValidationException') {
|
||||
|
@@ -97,16 +97,10 @@ export default {
|
||||
...mapGetters(['isJobValid', 'usersSuggestedFilteredByJob']),
|
||||
users: function () {
|
||||
let users = this.$store.getters.usersFilteredByJob;
|
||||
|
||||
console.log('users filtered by job', users);
|
||||
|
||||
// ensure that the selected user is in the list. add it if necessary
|
||||
if (this.$store.state.accompanyingCourse.user !== null && users.find(u => this.$store.state.accompanyingCourse.user.id === u.id) === undefined) {
|
||||
console.log('add user to users');
|
||||
users.push(this.$store.state.accompanyingCourse.user);
|
||||
}
|
||||
|
||||
console.log('users to return', users);
|
||||
return users;
|
||||
},
|
||||
valueJob: {
|
||||
|
@@ -6,7 +6,7 @@
|
||||
<div v-if="accompanyingCourse.requestor && isAnonymous" class="flex-table">
|
||||
|
||||
<label>
|
||||
<input type="checkbox" v-model="isAnonymous" class="me-2" />
|
||||
<input type="checkbox" v-model="requestorIsAnonymous" class="me-2" />
|
||||
{{ $t('requestor.is_anonymous') }}
|
||||
</label>
|
||||
<confidential v-if="accompanyingCourse.requestor.type === 'thirdparty'">
|
||||
@@ -26,7 +26,7 @@
|
||||
<template v-slot:record-actions>
|
||||
<ul class="record_actions">
|
||||
<li><on-the-fly :type="accompanyingCourse.requestor.type" :id="accompanyingCourse.requestor.id" action="show"></on-the-fly></li>
|
||||
<li><on-the-fly :type="accompanyingCourse.requestor.type" :id="accompanyingCourse.requestor.id" action="edit" @saveFormOnTheFly="saveFormOnTheFly"></on-the-fly></li>
|
||||
<li><on-the-fly :type="accompanyingCourse.requestor.type" :id="accompanyingCourse.requestor.id" action="edit" @saveFormOnTheFly="saveFormOnTheFly" ref="onTheFly"></on-the-fly></li>
|
||||
</ul>
|
||||
</template>
|
||||
</third-party-render-box>
|
||||
@@ -52,7 +52,7 @@
|
||||
<template v-slot:record-actions>
|
||||
<ul class="record_actions">
|
||||
<li><on-the-fly :type="accompanyingCourse.requestor.type" :id="accompanyingCourse.requestor.id" action="show"></on-the-fly></li>
|
||||
<li><on-the-fly :type="accompanyingCourse.requestor.type" :id="accompanyingCourse.requestor.id" action="edit" @saveFormOnTheFly="saveFormOnTheFly"></on-the-fly></li>
|
||||
<li><on-the-fly :type="accompanyingCourse.requestor.type" :id="accompanyingCourse.requestor.id" action="edit" @saveFormOnTheFly="saveFormOnTheFly" ref="onTheFly"></on-the-fly></li>
|
||||
</ul>
|
||||
</template>
|
||||
</person-render-box>
|
||||
@@ -72,7 +72,7 @@
|
||||
|
||||
<div v-else-if="accompanyingCourse.requestor && !isAnonymous" class="flex-table">
|
||||
<label>
|
||||
<input type="checkbox" v-model="isAnonymous" class="me-2" />
|
||||
<input type="checkbox" v-model="requestorIsAnonymous" class="me-2" />
|
||||
{{ $t('requestor.is_anonymous') }}
|
||||
</label>
|
||||
|
||||
@@ -92,7 +92,7 @@
|
||||
<template v-slot:record-actions>
|
||||
<ul class="record_actions">
|
||||
<li><on-the-fly :type="accompanyingCourse.requestor.type" :id="accompanyingCourse.requestor.id" action="show"></on-the-fly></li>
|
||||
<li><on-the-fly :type="accompanyingCourse.requestor.type" :id="accompanyingCourse.requestor.id" action="edit" @saveFormOnTheFly="saveFormOnTheFly"></on-the-fly></li>
|
||||
<li><on-the-fly :type="accompanyingCourse.requestor.type" :id="accompanyingCourse.requestor.id" action="edit" @saveFormOnTheFly="saveFormOnTheFly" ref="onTheFly"></on-the-fly></li>
|
||||
</ul>
|
||||
</template>
|
||||
</third-party-render-box>
|
||||
@@ -114,7 +114,7 @@
|
||||
<template v-slot:record-actions>
|
||||
<ul class="record_actions">
|
||||
<li><on-the-fly :type="accompanyingCourse.requestor.type" :id="accompanyingCourse.requestor.id" action="show"></on-the-fly></li>
|
||||
<li><on-the-fly :type="accompanyingCourse.requestor.type" :id="accompanyingCourse.requestor.id" action="edit" @saveFormOnTheFly="saveFormOnTheFly" :canCloseModal="canCloseOnTheFlyModal"></on-the-fly></li>
|
||||
<li><on-the-fly :type="accompanyingCourse.requestor.type" :id="accompanyingCourse.requestor.id" action="edit" @saveFormOnTheFly="saveFormOnTheFly" ref="onTheFly"></on-the-fly></li>
|
||||
</ul>
|
||||
</template>
|
||||
</person-render-box>
|
||||
@@ -144,14 +144,18 @@
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<add-persons v-if="accompanyingCourse.requestor === null"
|
||||
buttonTitle="requestor.add_requestor"
|
||||
modalTitle="requestor.add_requestor"
|
||||
v-bind:key="addPersons.key"
|
||||
v-bind:options="addPersons.options"
|
||||
@addNewPersons="addNewPersons"
|
||||
ref="addPersons"> <!-- to cast child method -->
|
||||
</add-persons>
|
||||
<ul class="record_actions">
|
||||
<li class="add-persons">
|
||||
<add-persons v-if="accompanyingCourse.requestor === null"
|
||||
buttonTitle="requestor.add_requestor"
|
||||
modalTitle="requestor.add_requestor"
|
||||
v-bind:key="addPersons.key"
|
||||
v-bind:options="addPersons.options"
|
||||
@addNewPersons="addNewPersons"
|
||||
ref="addPersons"> <!-- to cast child method -->
|
||||
</add-persons>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -189,14 +193,13 @@ export default {
|
||||
uniq: true,
|
||||
}
|
||||
},
|
||||
canCloseOnTheFlyModal: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
suggestedEntities: state => {
|
||||
return [
|
||||
...state.accompanyingCourse.participations.map(p => p.person),
|
||||
...state.accompanyingCourse.participations.filter((p) => p.endDate === null).map((p) => p.person),
|
||||
...state.accompanyingCourse.resources.map(r => r.resource)
|
||||
]
|
||||
.filter((e) => e !== null)
|
||||
@@ -215,7 +218,7 @@ export default {
|
||||
accompanyingCourse() {
|
||||
return this.$store.state.accompanyingCourse
|
||||
},
|
||||
isAnonymous: {
|
||||
requestorIsAnonymous: {
|
||||
set(value) {
|
||||
this.$store.dispatch('requestorIsAnonymous', value);
|
||||
},
|
||||
@@ -261,12 +264,14 @@ export default {
|
||||
if (payload.data.birthdate !== null) { body.birthdate = payload.data.birthdate; }
|
||||
body.phonenumber = payload.data.phonenumber;
|
||||
body.mobilenumber = payload.data.mobilenumber;
|
||||
body.email = payload.data.email;
|
||||
body.altNames = payload.data.altNames;
|
||||
body.gender = payload.data.gender;
|
||||
|
||||
makeFetch('PATCH', `/api/1.0/person/person/${payload.data.id}.json`, body)
|
||||
.then(response => {
|
||||
this.$store.dispatch('addPerson', { target: payload.target, body: response })
|
||||
this.canCloseOnTheFlyModal = true;
|
||||
this.$refs.onTheFly.closeModal();
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error.name === 'ValidationException') {
|
||||
@@ -282,12 +287,14 @@ export default {
|
||||
body.name = payload.data.text;
|
||||
body.email = payload.data.email;
|
||||
body.telephone = payload.data.phonenumber;
|
||||
body.address = { id: payload.data.address.address_id };
|
||||
if (payload.data.address) {
|
||||
body.address = { id: payload.data.address.address_id };
|
||||
}
|
||||
|
||||
makeFetch('PATCH', `/api/1.0/third-party/third-party/${payload.data.id}.json`, body)
|
||||
makeFetch('PATCH', `/api/1.0/thirdparty/thirdparty/${payload.data.id}.json`, body)
|
||||
.then(response => {
|
||||
this.$store.dispatch('addThirdparty', { target: payload.target, body: response })
|
||||
this.canCloseOnTheFlyModal = true;
|
||||
this.$refs.onTheFly.closeModal();
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error.name === 'ValidationException') {
|
||||
|
@@ -29,14 +29,18 @@
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<add-persons
|
||||
buttonTitle="resources.add_resources"
|
||||
modalTitle="resources.add_resources"
|
||||
v-bind:key="addPersons.key"
|
||||
v-bind:options="addPersons.options"
|
||||
@addNewPersons="addNewPersons"
|
||||
ref="addPersons"> <!-- to cast child method -->
|
||||
</add-persons>
|
||||
<ul class="record_actions">
|
||||
<li class="add-persons">
|
||||
<add-persons
|
||||
buttonTitle="resources.add_resources"
|
||||
modalTitle="resources.add_resources"
|
||||
v-bind:key="addPersons.key"
|
||||
v-bind:options="addPersons.options"
|
||||
@addNewPersons="addNewPersons"
|
||||
ref="addPersons"> <!-- to cast child method -->
|
||||
</add-persons>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -73,7 +77,7 @@ export default {
|
||||
counter: state => state.accompanyingCourse.resources.length,
|
||||
suggestedEntities: state => [
|
||||
state.accompanyingCourse.requestor,
|
||||
...state.accompanyingCourse.participations.map(p => p.person),
|
||||
...state.accompanyingCourse.participations.filter((p) => p.endDate === null).map((p) => p.person),
|
||||
]
|
||||
.filter((e) => e !== null)
|
||||
.filter(
|
||||
|
@@ -35,7 +35,7 @@
|
||||
:id="resource.resource.id"
|
||||
action="edit"
|
||||
@saveFormOnTheFly="saveFormOnTheFly"
|
||||
:canCloseModal="canCloseOnTheFlyModal">
|
||||
ref="onTheFly">
|
||||
</on-the-fly>
|
||||
</li>
|
||||
<li>
|
||||
@@ -82,7 +82,7 @@
|
||||
:id="resource.resource.id"
|
||||
action="edit"
|
||||
@saveFormOnTheFly="saveFormOnTheFly"
|
||||
:canCloseModal="canCloseOnTheFlyModal">
|
||||
ref="onTheFly">
|
||||
</on-the-fly>
|
||||
</li>
|
||||
<li>
|
||||
@@ -116,11 +116,6 @@ export default {
|
||||
},
|
||||
props: ['resource'],
|
||||
emits: ['remove'],
|
||||
data() {
|
||||
return {
|
||||
canCloseOnTheFlyModal: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
parent() {
|
||||
return {
|
||||
@@ -152,12 +147,14 @@ export default {
|
||||
if (payload.data.birthdate !== null) { body.birthdate = payload.data.birthdate; }
|
||||
body.phonenumber = payload.data.phonenumber;
|
||||
body.mobilenumber = payload.data.mobilenumber;
|
||||
body.email = payload.data.email;
|
||||
body.altNames = payload.data.altNames;
|
||||
body.gender = payload.data.gender;
|
||||
|
||||
makeFetch('PATCH', `/api/1.0/person/person/${payload.data.id}.json`, body)
|
||||
.then(response => {
|
||||
this.$store.dispatch('addPerson', { target: payload.target, body: response })
|
||||
this.canCloseOnTheFlyModal = true;
|
||||
this.$refs.onTheFly.closeModal();
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error.name === 'ValidationException') {
|
||||
@@ -170,15 +167,18 @@ export default {
|
||||
})
|
||||
}
|
||||
else if (payload.type === 'thirdparty') {
|
||||
console.log('data', payload.data)
|
||||
body.name = payload.data.text;
|
||||
body.email = payload.data.email;
|
||||
body.telephone = payload.data.phonenumber;
|
||||
body.address = { id: payload.data.address.address_id };
|
||||
body.address = payload.data.address ? { id: payload.data.address.address_id } : null;
|
||||
|
||||
makeFetch('PATCH', `/api/1.0/third-party/third-party/${payload.data.id}.json`, body)
|
||||
console.log('body', body)
|
||||
|
||||
makeFetch('PATCH', `/api/1.0/thirdparty/thirdparty/${payload.data.id}.json`, body)
|
||||
.then(response => {
|
||||
this.$store.dispatch('addThirdparty', { target: payload.target, body: response })
|
||||
this.canCloseOnTheFlyModal = true;
|
||||
this.$refs.onTheFly.closeModal();
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error.name === 'ValidationException') {
|
||||
|
@@ -428,42 +428,6 @@ let initPromise = (root) => Promise.all([getScopesPromise(root), accompanyingCou
|
||||
throw error;
|
||||
})
|
||||
},
|
||||
/**
|
||||
* On The Fly
|
||||
*/
|
||||
patchOnTheFly({ commit }, payload) {
|
||||
// TODO should be into the dedicated component, no ? JF
|
||||
console.log('## action: patch OnTheFly', payload);
|
||||
let body = { type: payload.type };
|
||||
if (payload.type === 'person') {
|
||||
body.firstName = payload.data.firstName;
|
||||
body.lastName = payload.data.lastName;
|
||||
if (payload.data.birthdate !== null) { body.birthdate = payload.data.birthdate; }
|
||||
body.phonenumber = payload.data.phonenumber;
|
||||
body.mobilenumber = payload.data.mobilenumber;
|
||||
body.gender = payload.data.gender;
|
||||
console.log('id', payload.data.id, 'and body', body);
|
||||
patchPerson(payload.data.id, body)
|
||||
.then(person => new Promise((resolve, reject) => {
|
||||
console.log('patch person', person);
|
||||
commit('updatePerson', { target: payload.target, person: person });
|
||||
resolve();
|
||||
}));
|
||||
}
|
||||
else if (payload.type === 'thirdparty') {
|
||||
body.name = payload.data.text;
|
||||
body.email = payload.data.email;
|
||||
body.telephone = payload.data.phonenumber;
|
||||
body.address = { id: payload.data.address.address_id };
|
||||
console.log('id', payload.data.id, 'and body', body);
|
||||
patchThirdparty(payload.data.id, body)
|
||||
.then(thirdparty => new Promise((resolve, reject) => {
|
||||
console.log('patch thirdparty', thirdparty);
|
||||
commit('updateThirdparty', { target: payload.target, thirdparty: thirdparty });
|
||||
resolve();
|
||||
}));
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Update accompanying course intensity/emergency/confidentiality
|
||||
*/
|
||||
|
@@ -160,7 +160,7 @@
|
||||
</p>
|
||||
|
||||
<ul class="record_actions">
|
||||
<li>
|
||||
<li class="add-persons">
|
||||
<add-persons
|
||||
ref="handlingThirdPartyPicker"
|
||||
v-bind:key="handlingThirdPartyPicker.key"
|
||||
@@ -211,7 +211,7 @@
|
||||
<template v-slot:record-actions>
|
||||
<ul class="record_actions">
|
||||
<li><on-the-fly :type="thirdparty.type" :id="thirdparty.id" action="show"></on-the-fly></li>
|
||||
<li><on-the-fly :type="thirdparty.type" :id="thirdparty.id" action="edit" @saveFormOnTheFly="saveFormOnTheFly"></on-the-fly></li>
|
||||
<li><on-the-fly :type="thirdparty.type" :id="thirdparty.id" action="edit" @saveFormOnTheFly="saveFormOnTheFly" ref="onTheFly"></on-the-fly></li>
|
||||
<li>
|
||||
<button :title="$t('remove_thirdparty')" class="btn btn-sm btn-remove" @click="removeThirdParty(thirdparty)" />
|
||||
</li>
|
||||
@@ -222,7 +222,7 @@
|
||||
</div>
|
||||
|
||||
<ul class="record_actions">
|
||||
<li>
|
||||
<li class="add-persons">
|
||||
<add-persons
|
||||
ref="thirdPartyPicker"
|
||||
v-bind:key="thirdPartyPicker.key"
|
||||
@@ -286,6 +286,8 @@ import ListWorkflowModal from 'ChillMainAssets/vuejs/_components/EntityWorkflow/
|
||||
import PickWorkflow from 'ChillMainAssets/vuejs/_components/EntityWorkflow/PickWorkflow.vue';
|
||||
import PersonText from 'ChillPersonAssets/vuejs/_components/Entity/PersonText.vue';
|
||||
import {buildLinkCreate} from 'ChillMainAssets/lib/entity-workflow/api.js';
|
||||
import { makeFetch } from 'ChillMainAssets/lib/api/apiMethods';
|
||||
|
||||
|
||||
const i18n = {
|
||||
messages: {
|
||||
@@ -476,15 +478,27 @@ export default {
|
||||
},
|
||||
saveFormOnTheFly(payload) {
|
||||
console.log('saveFormOnTheFly: type', payload.type, ', data', payload.data);
|
||||
payload.target = 'resource';
|
||||
this.$store.dispatch('patchOnTheFly', payload)
|
||||
.catch(({name, violations}) => {
|
||||
if (name === 'ValidationException' || name === 'AccessException') {
|
||||
violations.forEach((violation) => this.$toast.open({message: violation}));
|
||||
|
||||
let body = { type: payload.type };
|
||||
body.name = payload.data.text;
|
||||
body.email = payload.data.email;
|
||||
body.telephone = payload.data.phonenumber;
|
||||
body.address = { id: payload.data.address.address_id };
|
||||
|
||||
makeFetch('PATCH', `/api/1.0/thirdparty/thirdparty/${payload.data.id}.json`, body)
|
||||
.then(response => {
|
||||
this.$store.dispatch('updateThirdParty', response)
|
||||
this.$refs.onTheFly.closeModal();
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error.name === 'ValidationException') {
|
||||
for (let v of error.violations) {
|
||||
this.$toast.open({message: v });
|
||||
}
|
||||
} else {
|
||||
this.$toast.open({message: 'An error occurred'})
|
||||
this.$toast.open({message: 'An error occurred'});
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@@ -1,7 +1,9 @@
|
||||
import { createApp } from 'vue';
|
||||
import { _createI18n } from 'ChillMainAssets/vuejs/_js/i18n';
|
||||
import { store } from './store';
|
||||
import { personMessages } from 'ChillPersonAssets/vuejs/_js/i18n'
|
||||
import { personMessages } from 'ChillPersonAssets/vuejs/_js/i18n';
|
||||
import VueToast from 'vue-toast-notification';
|
||||
import 'vue-toast-notification/dist/theme-sugar.css';
|
||||
import App from './App.vue';
|
||||
|
||||
const i18n = _createI18n(personMessages);
|
||||
@@ -10,6 +12,12 @@ const app = createApp({
|
||||
template: `<app></app>`,
|
||||
})
|
||||
.use(store)
|
||||
.use(VueToast, {
|
||||
position: "bottom-right",
|
||||
type: "error",
|
||||
duration: 5000,
|
||||
dismissible: true
|
||||
})
|
||||
.use(i18n)
|
||||
.component('app', App)
|
||||
.mount('#accompanying_course_work_edit');
|
||||
|
@@ -266,6 +266,14 @@ const store = createStore({
|
||||
state.thirdParties.push(unexistings[i]);
|
||||
}
|
||||
},
|
||||
updateThirdParty(state, thirdParty) {
|
||||
for (let t of state.thirdParties) {
|
||||
if (t.id === thirdParty.id){
|
||||
state.thirdParties = state.thirdParties.filter(t => t.id !== thirdParty.id);
|
||||
state.thirdParties.push(thirdParty);
|
||||
}
|
||||
}
|
||||
},
|
||||
removeThirdParty(state, thirdParty) {
|
||||
state.thirdParties = state.thirdParties
|
||||
.filter(t => t.id !== thirdParty.id);
|
||||
@@ -278,6 +286,10 @@ const store = createStore({
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
updateThirdParty({ commit }, payload) {
|
||||
console.log(payload);
|
||||
commit('updateThirdParty', payload);
|
||||
},
|
||||
getReachablesGoalsForAction({ getters, commit, dispatch }) {
|
||||
let
|
||||
socialActionId = getters.socialAction.id,
|
||||
@@ -385,36 +397,6 @@ const store = createStore({
|
||||
commit('setErrors', error.violations);
|
||||
});
|
||||
},
|
||||
patchOnTheFly({ commit }, payload) {
|
||||
let body = { type: payload.type };
|
||||
const id = payload.data.id;
|
||||
let url = `/api/1.0/person/person/${id}.json`;
|
||||
let mutation = "updatePerson";
|
||||
|
||||
if (payload.type === 'person') {
|
||||
body.firstName = payload.data.firstName;
|
||||
body.lastName = payload.data.lastName;
|
||||
if (payload.data.birthdate !== null) { body.birthdate = payload.data.birthdate; }
|
||||
body.phonenumber = payload.data.phonenumber;
|
||||
body.mobilenumber = payload.data.mobilenumber;
|
||||
body.gender = payload.data.gender;
|
||||
} else if (payload.type === 'thirdparty') {
|
||||
body.name = payload.data.text;
|
||||
body.email = payload.data.email;
|
||||
body.telephone = payload.data.phonenumber;
|
||||
body.address = { id: payload.data.address.address_id };
|
||||
|
||||
url = `/api/1.0/thirdparty/thirdparty/${id}.json`;
|
||||
mutation = 'updateThirdparty'
|
||||
}
|
||||
makeFetch('PATCH', url, body)
|
||||
.then((response) => {
|
||||
commit(mutation, {target: payload.target, thirdparty: response});
|
||||
})
|
||||
.catch((error) => {
|
||||
throw error;
|
||||
})
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
|
@@ -29,7 +29,7 @@
|
||||
</div>
|
||||
|
||||
<ul class="record_actions">
|
||||
<li>
|
||||
<li class="add-persons">
|
||||
<add-persons
|
||||
buttonTitle="household_members_editor.concerned.add_persons"
|
||||
modalTitle="household_members_editor.concerned.search"
|
||||
|
@@ -61,7 +61,7 @@
|
||||
<li v-if="hasHousehold">
|
||||
<button @click="resetMode" class="btn btn-sm btn-misc">{{ $t('household_members_editor.household.reset_mode')}}</button>
|
||||
</li>
|
||||
<li v-if="!hasHousehold">
|
||||
<li v-if="!hasHousehold" class="add-persons">
|
||||
<add-persons
|
||||
modalTitle="Chercher un ménage existant"
|
||||
buttonTitle="Chercher un ménage existant"
|
||||
|
@@ -1,10 +1,7 @@
|
||||
<template>
|
||||
<ul class="record_actions">
|
||||
<li class="add-persons">
|
||||
<a class="btn" :class="getClassButton" :title="$t(buttonTitle)"
|
||||
@click="openModal"><span v-if="displayTextButton">{{ $t(buttonTitle) }}</span></a>
|
||||
</li>
|
||||
</ul>
|
||||
<a class="btn" :class="getClassButton" :title="$t(buttonTitle)" @click="openModal">
|
||||
<span v-if="displayTextButton">{{ $t(buttonTitle) }}</span>
|
||||
</a>
|
||||
|
||||
<teleport to="body">
|
||||
<modal v-if="modal.showModal"
|
||||
@@ -65,11 +62,13 @@
|
||||
|
||||
<div class="create-button">
|
||||
<on-the-fly
|
||||
v-if="query.length >= 3"
|
||||
v-if="queryLength >= 3 && (options.type.includes('person') || options.type.includes('thirdparty'))"
|
||||
:buttonText="$t('onthefly.create.button', {q: query})"
|
||||
:allowedTypes="options.type"
|
||||
:query="query"
|
||||
action="create"
|
||||
@saveFormOnTheFly="saveFormOnTheFly"
|
||||
:canCloseModal="canCloseOnTheFlyModal">
|
||||
ref="onTheFly">
|
||||
</on-the-fly>
|
||||
</div>
|
||||
|
||||
@@ -121,7 +120,6 @@ export default {
|
||||
selected: [],
|
||||
priorSuggestion: {}
|
||||
},
|
||||
canCloseOnTheFlyModal: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -271,7 +269,7 @@ export default {
|
||||
makeFetch('POST', '/api/1.0/person/person.json', data)
|
||||
.then(response => {
|
||||
this.newPriorSuggestion(response);
|
||||
this.canCloseOnTheFlyModal = true;
|
||||
this.$refs.onTheFly.closeModal();
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error.name === 'ValidationException') {
|
||||
@@ -287,7 +285,7 @@ export default {
|
||||
makeFetch('POST', '/api/1.0/thirdparty/thirdparty.json', data)
|
||||
.then(response => {
|
||||
this.newPriorSuggestion(response);
|
||||
this.canCloseOnTheFlyModal = true;
|
||||
this.$refs.onTheFly.closeModal();
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error.name === 'ValidationException') {
|
||||
@@ -299,7 +297,6 @@ export default {
|
||||
}
|
||||
})
|
||||
}
|
||||
this.canCloseOnTheFlyModal = false;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
@@ -32,6 +32,14 @@
|
||||
<label for="lastname">{{ $t('person.lastname') }}</label>
|
||||
</div>
|
||||
|
||||
<div v-if="queryItems">
|
||||
<ul class="list-suggest add-items inline">
|
||||
<li v-for="(qi, i) in queryItems" :key="i" @click="addQueryItem('lastName', qi)">
|
||||
<span class="person-text">{{ qi }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="form-floating mb-3">
|
||||
<input
|
||||
class="form-control form-control-lg"
|
||||
@@ -43,10 +51,19 @@
|
||||
<label for="firstname">{{ $t('person.firstname') }}</label>
|
||||
</div>
|
||||
|
||||
<div v-for="(a) in config.altNames" :key="a.key" class="form-floating mb-3">
|
||||
<div v-if="queryItems">
|
||||
<ul class="list-suggest add-items inline">
|
||||
<li v-for="(qi, i) in queryItems" :key="i" @click="addQueryItem('firstName', qi)">
|
||||
<span class="person-text">{{ qi }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div v-for="(a, i) in config.altNames" :key="a.key" class="form-floating mb-3">
|
||||
<input
|
||||
class="form-control form-control-lg"
|
||||
:id="a.key"
|
||||
:value="personAltNamesLabels[i]"
|
||||
@input="onAltNameInput"
|
||||
/>
|
||||
<label :for="a.key">{{ a.labels.fr }}</label>
|
||||
@@ -121,7 +138,7 @@ import PersonRenderBox from '../Entity/PersonRenderBox.vue';
|
||||
|
||||
export default {
|
||||
name: "OnTheFlyPerson",
|
||||
props: ['id', 'type', 'action'],
|
||||
props: ['id', 'type', 'action', 'query'],
|
||||
//emits: ['createAction'],
|
||||
components: {
|
||||
PersonRenderBox
|
||||
@@ -199,6 +216,12 @@ export default {
|
||||
},
|
||||
feminized() {
|
||||
return (this.person.gender === 'woman')? 'e' : '';
|
||||
},
|
||||
personAltNamesLabels() {
|
||||
return this.person.altNames.map(a => a ? a.label : '');
|
||||
},
|
||||
queryItems() {
|
||||
return this.query ? this.query.split(' ') : null;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
@@ -240,6 +263,16 @@ export default {
|
||||
)
|
||||
this.person.altNames = updateAltNames;
|
||||
},
|
||||
addQueryItem(field, queryItem) {
|
||||
switch (field) {
|
||||
case 'lastName':
|
||||
this.person.lastName = queryItem;
|
||||
break;
|
||||
case 'firstName':
|
||||
this.person.firstName = queryItem;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@@ -26,9 +26,9 @@
|
||||
</div>
|
||||
<div class="wh-row">
|
||||
<div class="wh-col">
|
||||
{% if period.closingDate == null %}
|
||||
{% if period.closingDate == null and period.step != 'DRAFT' %}
|
||||
{{ 'accompanying_period.dates_from_%opening_date%'|trans({ '%opening_date%': period.openingDate|format_date('long') } ) }}
|
||||
{% else %}
|
||||
{% elseif period.closingDate != null %}
|
||||
{{ 'accompanying_period.dates_from_%opening_date%_to_%closing_date%'|trans({
|
||||
'%opening_date%': period.openingDate|format_date('long'),
|
||||
'%closing_date%': period.closingDate|format_date('long')}
|
||||
@@ -55,65 +55,67 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-row separator">
|
||||
<div class="wrap-list">
|
||||
{% if period.requestorPerson is not null or period.requestorThirdParty is not null %}
|
||||
<div class="wl-row">
|
||||
<div class="wl-col title"><h3>{{ 'Requestor'|trans({'gender': null }) }}</h3></div>
|
||||
<div class="wl-col list">
|
||||
{% if period.requestorPerson is not null %}
|
||||
<span class="wl-item">
|
||||
{% include '@ChillMain/OnTheFly/_insert_vue_onthefly.html.twig' with {
|
||||
action: 'show', displayBadge: true,
|
||||
targetEntity: { name: 'person', id: period.requestorPerson.id },
|
||||
buttonText: period.requestorPerson|chill_entity_render_string,
|
||||
isDead: period.requestorPerson.deathdate is not null
|
||||
} %}
|
||||
</span>
|
||||
{% endif %}
|
||||
{% if period.requestorThirdParty is not null %}
|
||||
<span class="wl-item">
|
||||
{% include '@ChillMain/OnTheFly/_insert_vue_onthefly.html.twig' with {
|
||||
action: 'show', displayBadge: true,
|
||||
targetEntity: { name: 'thirdparty', id: period.requestorThirdParty.id },
|
||||
buttonText: period.requestorThirdParty|chill_entity_render_string
|
||||
} %}
|
||||
</span>
|
||||
{% endif %}
|
||||
{% if period.requestor is not null or period.participations.count > 0 or period.socialIssues.count > 0%}
|
||||
<div class="item-row separator">
|
||||
<div class="wrap-list">
|
||||
{% if period.requestorPerson is not null or period.requestorThirdParty is not null %}
|
||||
<div class="wl-row">
|
||||
<div class="wl-col title"><h3>{{ 'Requestor'|trans({'gender': null }) }}</h3></div>
|
||||
<div class="wl-col list">
|
||||
{% if period.requestorPerson is not null %}
|
||||
<span class="wl-item">
|
||||
{% include '@ChillMain/OnTheFly/_insert_vue_onthefly.html.twig' with {
|
||||
action: 'show', displayBadge: true,
|
||||
targetEntity: { name: 'person', id: period.requestorPerson.id },
|
||||
buttonText: period.requestorPerson|chill_entity_render_string,
|
||||
isDead: period.requestorPerson.deathdate is not null
|
||||
} %}
|
||||
</span>
|
||||
{% endif %}
|
||||
{% if period.requestorThirdParty is not null %}
|
||||
<span class="wl-item">
|
||||
{% include '@ChillMain/OnTheFly/_insert_vue_onthefly.html.twig' with {
|
||||
action: 'show', displayBadge: true,
|
||||
targetEntity: { name: 'thirdparty', id: period.requestorThirdParty.id },
|
||||
buttonText: period.requestorThirdParty|chill_entity_render_string
|
||||
} %}
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if period.participations.count > 0 %}
|
||||
<div class="wl-row">
|
||||
<div class="wl-col title"><h3>{{ 'Participants'|trans }}</h3></div>
|
||||
<div class="wl-col list">
|
||||
{% for p in period.getCurrentParticipations %}
|
||||
<span class="wl-item">
|
||||
{% include '@ChillMain/OnTheFly/_insert_vue_onthefly.html.twig' with {
|
||||
action: 'show', displayBadge: true,
|
||||
targetEntity: { name: 'person', id: p.person.id },
|
||||
buttonText: p.person|chill_entity_render_string,
|
||||
isDead: p.person.deathdate is not null
|
||||
} %}
|
||||
</span>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if period.participations.count > 0 %}
|
||||
<div class="wl-row">
|
||||
<div class="wl-col title"><h3>{{ 'Participants'|trans }}</h3></div>
|
||||
<div class="wl-col list">
|
||||
{% for p in period.getCurrentParticipations %}
|
||||
<span class="wl-item">
|
||||
{% include '@ChillMain/OnTheFly/_insert_vue_onthefly.html.twig' with {
|
||||
action: 'show', displayBadge: true,
|
||||
targetEntity: { name: 'person', id: p.person.id },
|
||||
buttonText: p.person|chill_entity_render_string,
|
||||
isDead: p.person.deathdate is not null
|
||||
} %}
|
||||
</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if period.socialIssues.count > 0 %}
|
||||
<div class="wl-row">
|
||||
<div class="wl-col title"><h3>{{ 'Social issues'|trans }}</h3></div>
|
||||
<div class="wl-col list">
|
||||
{% for si in period.socialIssues %}
|
||||
<p class="wl-item">
|
||||
{{ si|chill_entity_render_box }}
|
||||
</p>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if period.socialIssues.count > 0 %}
|
||||
<div class="wl-row">
|
||||
<div class="wl-col title"><h3>{{ 'Social issues'|trans }}</h3></div>
|
||||
<div class="wl-col list">
|
||||
{% for si in period.socialIssues %}
|
||||
<p class="wl-item">
|
||||
{{ si|chill_entity_render_box }}
|
||||
</p>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="item-row separator">
|
||||
<div class="item-col item-meta">
|
||||
{% set notif_counter = chill_count_notifications('Chill\\PersonBundle\\Entity\\AccompanyingPeriod', period.id) %}
|
||||
|
@@ -0,0 +1,22 @@
|
||||
{#
|
||||
content of the notification if the person move and the person "localize" the period
|
||||
#}{{ period.user.label }},
|
||||
|
||||
L'usager {{ oldPersonLocation|chill_entity_render_string }} a déménagé.
|
||||
|
||||
Son adresse était utilisée pour localiser le parcours n°{{ period.id }}, dont vous êtes
|
||||
le référent.
|
||||
|
||||
En conséquence de ce déménage, le parcours est toujours localisé à cette adresse, mais à l'aide d'une
|
||||
adresse temporaire.
|
||||
|
||||
Si vous continuez à suivre le parcours, vous pouvez le localiser à nouveau auprès de l'adresse de
|
||||
l'usager {{ oldPersonLocation|chill_entity_render_string }}.
|
||||
|
||||
Sinon, veillez à vous assurer de la continuité du suivi par vos collègues.
|
||||
|
||||
Pour visualiser le parcours, cliquez ici:
|
||||
|
||||
{{ absolute_url(path('chill_person_accompanying_course_index', {'accompanying_period_id': period.id})) }}
|
||||
|
||||
Cordialement,
|
@@ -0,0 +1,32 @@
|
||||
{% extends "@ChillMain/layout.html.twig" %}
|
||||
|
||||
{% set activeRouteKey = 'chill_person_accompanying_period_user_list' %}
|
||||
|
||||
{% block title %}{{ 'My accompanying periods in draft'|trans }}{% endblock title %}
|
||||
|
||||
{% macro recordAction(period) %}
|
||||
<li>
|
||||
<a href="{{ path('chill_person_accompanying_course_index', { 'accompanying_period_id': period.id }) }}"
|
||||
class="btn btn-show" title="{{ 'See accompanying period'|trans }}"></a>
|
||||
</li>
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="col-md-10">
|
||||
<h1>{{ 'My accompanying periods in draft'|trans }}</h1>
|
||||
|
||||
<div class="flex-table accompanyingcourse-list">
|
||||
{% for period in accompanyingPeriods %}
|
||||
{% include '@ChillPerson/AccompanyingPeriod/_list_item.html.twig' with {'period': period, 'recordAction': _self.recordAction(period)} %}
|
||||
{% else %}
|
||||
<p class="chill-no-data-statement">{{ 'Any accompanying period'|trans }}</p>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
{{ chill_pagination(pagination) }}
|
||||
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
@@ -20,6 +20,8 @@
|
||||
<div class="flex-table accompanyingcourse-list">
|
||||
{% for period in accompanyingPeriods %}
|
||||
{% include '@ChillPerson/AccompanyingPeriod/_list_item.html.twig' with {'period': period, 'recordAction': _self.recordAction(period)} %}
|
||||
{% else %}
|
||||
<p class="chill-no-data-statement">{{ 'Any accompanying period'|trans }}</p>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
|
@@ -172,7 +172,7 @@
|
||||
|
||||
{%- if options['customButtons']['replace'] is defined -%}
|
||||
{{ options['customButtons']['replace'] }}
|
||||
{%- elseif is_granted('CHILL_PERSON_SEE', person) -%}
|
||||
{%- elseif is_granted('CHILL_PERSON_SEE', person) and options['addLink'] -%}
|
||||
<li>
|
||||
<a class="btn btn-sm btn-show" title="{{ 'Show person'|trans }}"
|
||||
href="{{ path('chill_person_view', { person_id: person.id }) }}"></a>
|
||||
|
@@ -115,6 +115,9 @@
|
||||
<li>
|
||||
{{ form_widget(form.editPerson, { 'attr': { 'class': 'dropdown-item' }}) }}
|
||||
</li>
|
||||
<li>
|
||||
{{ form_widget(form.createHousehold, { 'attr': { 'class': 'dropdown-item' }}) }}
|
||||
</li>
|
||||
<li>
|
||||
{{ form_widget(form.createPeriod, { 'attr': { 'class': 'dropdown-item' }}) }}
|
||||
</li>
|
||||
|
@@ -32,14 +32,28 @@
|
||||
</div>
|
||||
<div class="item-row separator">
|
||||
<div class="wrap-list">
|
||||
{% if p.household is same as p.person.getCurrentHousehold %}
|
||||
<div class="wl-row">
|
||||
<div class="wl-col title">
|
||||
<h3>{{ 'Address'|trans }}</h3>
|
||||
</div>
|
||||
<div class="wl-col list">
|
||||
<p class="item">{{ p.household.getCurrentAddress|chill_entity_render_box }}</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="wl-row">
|
||||
<div class="wl-col title">
|
||||
<h3>En tant que</h3>
|
||||
<h3>{{ 'household.As member'|trans }}</h3>
|
||||
</div>
|
||||
<div class="wl-col list">
|
||||
<p class="item">{{ p.position.label|localize_translatable_string }}
|
||||
<p class="item">
|
||||
{{ p.position.label|localize_translatable_string }}
|
||||
{% if p.holder %}
|
||||
<span class="holder">{{ 'household.holder'|trans }}</span>
|
||||
<span class="fa-stack fa-holder" title="{{ 'houshold.holder'|trans|e('html_attr') }}">
|
||||
<i class="fa fa-circle fa-stack-1x text-success"></i>
|
||||
<i class="fa fa-stack-1x">T</i>
|
||||
</span>
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
|
@@ -56,6 +56,11 @@
|
||||
|
||||
{% if action is defined %}
|
||||
<ul class="record_actions sticky-form-buttons">
|
||||
<li class="cancel">
|
||||
<a href="{{ path('chill_person_resource_list', { 'person_id': resource.personOwner.id } ) }}" class="btn btn-cancel">
|
||||
{{ 'Cancel'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
<li class="edit">
|
||||
<button class="btn btn-edit"
|
||||
type="submit" id="newPersonResource">
|
||||
|
@@ -22,41 +22,33 @@
|
||||
<div class="flex-table">
|
||||
{% for resource in personResources %}
|
||||
<div class="item-bloc">
|
||||
<div class="item-row">
|
||||
<div class="item-col" style="width: 50%">
|
||||
{% if resource.kind is not null %}
|
||||
<div class="item-row">
|
||||
<section>
|
||||
<p>{{ resource.kind.title.fr|capitalize }}</p>
|
||||
</section>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="{% if resource.kind is not null %} separator {% endif %}">
|
||||
{% if resource.person is not null %}
|
||||
<div class="denomination h3">
|
||||
<span>
|
||||
{% include '@ChillMain/OnTheFly/_insert_vue_onthefly.html.twig' with {
|
||||
action: 'show', displayBadge: true,
|
||||
targetEntity: { name: 'person', id: resource.person.id },
|
||||
buttonText: resource.person|chill_entity_render_string,
|
||||
isDead: resource.person.deathdate is not null
|
||||
} %}
|
||||
</span>
|
||||
</div>
|
||||
{{ resource.person|chill_entity_render_box({
|
||||
'render': 'bloc',
|
||||
'addLink': true,
|
||||
'addInfo': true,
|
||||
'addAge': true
|
||||
}) }}
|
||||
{% elseif resource.thirdparty is not null %}
|
||||
<div class="denomination h3">
|
||||
<span>
|
||||
{% include '@ChillMain/OnTheFly/_insert_vue_onthefly.html.twig' with {
|
||||
action: 'show', displayBadge: true,
|
||||
targetEntity: { name: 'thirdparty', id: resource.thirdparty.id },
|
||||
buttonText: resource.thirdParty|chill_entity_render_string,
|
||||
parent: resource.thirdparty.parent
|
||||
} %}
|
||||
</span>
|
||||
</div>
|
||||
{{ resource.thirdparty|chill_entity_render_box({
|
||||
'render': 'bloc',
|
||||
'showContacts': false,
|
||||
'addLink': true,
|
||||
'isConfidential': (resource.thirdparty.contactDataAnonymous ? true : false)
|
||||
}) }}
|
||||
{% else %}
|
||||
<div class="denomination h3">
|
||||
<span>{{ resource.freetext }}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="item-col" style="justify-content: flex-end; ">
|
||||
{% if resource.kind %}
|
||||
<span>{{ resource.kind.title.fr|capitalize }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% if resource.comment.comment is not empty %}
|
||||
<div class="item-row separator">
|
||||
|
@@ -29,10 +29,10 @@
|
||||
|
||||
<div class="item-col" style="width: 33%;">
|
||||
<ul class="list-unstyled h3">
|
||||
{% if a.endDate is not null %}
|
||||
<li><span class="item-key">{{'Since'|trans}} : </span>{{ a.startDate|format_date('long') }}</li>
|
||||
{% if a.endDate is not null %}
|
||||
<li><span class="item-key">{{'Until'|trans}} : </span><b>{{ a.endDate|format_date('long') }}</b></li>
|
||||
{% endif %}
|
||||
<li><span class="item-key">{{'Until'|trans}} : </span><b>{{ a.endDate|format_date('long') }}</b></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="item-col flex-column justify-content-start">
|
||||
|
@@ -58,6 +58,10 @@ class AccompanyingPeriodWorkVoter extends Voter
|
||||
case self::SEE:
|
||||
return $this->security->isGranted(AccompanyingPeriodVoter::SEE_DETAILS, $subject->getAccompanyingPeriod());
|
||||
|
||||
case self::CREATE:
|
||||
case self::UPDATE:
|
||||
return $this->security->isGranted(AccompanyingPeriodVoter::EDIT, $subject->getAccompanyingPeriod());
|
||||
|
||||
default:
|
||||
throw new UnexpectedValueException("attribute {$attribute} is not supported");
|
||||
}
|
||||
@@ -66,6 +70,9 @@ class AccompanyingPeriodWorkVoter extends Voter
|
||||
case self::SEE:
|
||||
return $this->security->isGranted(AccompanyingPeriodVoter::SEE_DETAILS, $subject);
|
||||
|
||||
case self::CREATE:
|
||||
return $this->security->isGranted(AccompanyingPeriodVoter::CREATE, $subject);
|
||||
|
||||
default:
|
||||
throw new UnexpectedValueException(sprintf(
|
||||
"attribute {$attribute} is not supported on instance %s",
|
||||
@@ -79,6 +86,6 @@ class AccompanyingPeriodWorkVoter extends Voter
|
||||
|
||||
private function getRoles(): array
|
||||
{
|
||||
return [self::SEE];
|
||||
return [self::SEE, self::CREATE, self::UPDATE];
|
||||
}
|
||||
}
|
||||
|
@@ -91,6 +91,7 @@ class PersonJsonNormalizer implements
|
||||
'deathdate',
|
||||
'center',
|
||||
'altNames',
|
||||
'email',
|
||||
];
|
||||
|
||||
$fields = array_filter(
|
||||
@@ -161,6 +162,11 @@ class PersonJsonNormalizer implements
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'email':
|
||||
$person->setEmail($data[$item]);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -189,6 +195,7 @@ class PersonJsonNormalizer implements
|
||||
'centers' => $this->normalizer->normalize($this->centerResolverManager->resolveCenters($person), $format, $context),
|
||||
'phonenumber' => $person->getPhonenumber(),
|
||||
'mobilenumber' => $person->getMobilenumber(),
|
||||
'email' => $person->getEmail(),
|
||||
'altNames' => $this->normalizeAltNames($person->getAltNames()),
|
||||
'gender' => $person->getGender(),
|
||||
'current_household_address' => $this->normalizer->normalize($person->getCurrentHouseholdAddress(), $format, $context),
|
||||
|
@@ -161,11 +161,13 @@ class AccompanyingPeriodContext implements
|
||||
'class' => Person::class,
|
||||
'choices' => $persons,
|
||||
'choice_label' => function (Person $p) {
|
||||
return $this->personRender->renderString($p, []);
|
||||
return $this->personRender->renderString($p, ['addAge' => true]);
|
||||
},
|
||||
'multiple' => false,
|
||||
'expanded' => true,
|
||||
'required' => false,
|
||||
'label' => $options[$key . 'Label'],
|
||||
'placeholder' => $this->translator->trans('Any person selected'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -241,7 +243,8 @@ class AccompanyingPeriodContext implements
|
||||
public function storeGenerated(DocGeneratorTemplate $template, StoredObject $storedObject, object $entity, array $contextGenerationData): void
|
||||
{
|
||||
$doc = new AccompanyingCourseDocument();
|
||||
$doc->setTitle($this->translatableStringHelper->localize($template->getName()))
|
||||
$doc->setTemplate($template)
|
||||
->setTitle($this->translatableStringHelper->localize($template->getName()))
|
||||
->setDate(new DateTime())
|
||||
->setDescription($this->translatableStringHelper->localize($template->getName()))
|
||||
->setCourse($entity)
|
||||
|
@@ -83,6 +83,12 @@ class PersonRender extends AbstractChillEntityRender
|
||||
. $this->addAltNames($person, false) . ' (' . $this->translator->trans('years_old', ['age' => $person->getAge()]) . ')';
|
||||
}
|
||||
|
||||
if (null !== $person->getDeathDate() && $options['addAge']) {
|
||||
return $person->getFirstName() . ' ' . $person->getLastName()
|
||||
. ' (‡)'
|
||||
. $this->addAltNames($person, false);
|
||||
}
|
||||
|
||||
return $person->getFirstName() . ' ' . $person->getLastName()
|
||||
. $this->addAltNames($person, false);
|
||||
}
|
||||
|
@@ -0,0 +1,290 @@
|
||||
<?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 AccompanyingPeriod\Events;
|
||||
|
||||
use Chill\MainBundle\Entity\Address;
|
||||
use Chill\MainBundle\Entity\Notification;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\PersonBundle\AccompanyingPeriod\Events\PersonAddressMoveEventSubscriber;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||
use Chill\PersonBundle\Entity\Household\Household;
|
||||
use Chill\PersonBundle\Entity\Household\HouseholdMember;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\PersonBundle\Event\Person\PersonAddressMoveEvent;
|
||||
use DateTime;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Prophecy\Argument;
|
||||
use Prophecy\PhpUnit\ProphecyTrait;
|
||||
use ReflectionClass;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
use Symfony\Component\Templating\EngineInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @coversNothing
|
||||
*/
|
||||
final class PersonMoveEventSubscriberTest extends KernelTestCase
|
||||
{
|
||||
use ProphecyTrait;
|
||||
|
||||
public function testEventChangeHouseholdNoNotificationForPeriodWithoutUser()
|
||||
{
|
||||
$person = new Person();
|
||||
$period = new AccompanyingPeriod();
|
||||
$period
|
||||
->setStep(AccompanyingPeriod::STEP_CONFIRMED)
|
||||
->setPersonLocation($person)
|
||||
->addPerson($person);
|
||||
$this->forceIdToPeriod($period);
|
||||
|
||||
$previousHousehold = (new Household())->addAddress(
|
||||
(new Address())->setValidFrom(new DateTime('1 year ago'))
|
||||
);
|
||||
$previousMembership = new HouseholdMember();
|
||||
$previousMembership
|
||||
->setPerson($person)
|
||||
->setHousehold($previousHousehold)
|
||||
->setStartDate(new DateTimeImmutable('1 year ago'))
|
||||
->setEndDate(new DateTimeImmutable('tomorrow'));
|
||||
|
||||
$nextHousehold = (new Household())->addAddress(
|
||||
(new Address())->setValidFrom(new DateTime('tomorrow'))
|
||||
);
|
||||
$nextMembership = new HouseholdMember();
|
||||
$nextMembership
|
||||
->setPerson($person)
|
||||
->setHousehold($nextHousehold)
|
||||
->setStartDate(new DateTimeImmutable('tomorrow'));
|
||||
|
||||
$event = new PersonAddressMoveEvent($person);
|
||||
$event
|
||||
->setPreviousMembership($previousMembership)
|
||||
->setNextMembership($nextMembership);
|
||||
|
||||
$em = $this->prophesize(EntityManagerInterface::class);
|
||||
$em->persist(Argument::type(Notification::class))->shouldNotBeCalled();
|
||||
$eventSubscriber = $this->buildSubscriber(null, $em->reveal(), null, null);
|
||||
|
||||
$eventSubscriber->resetPeriodLocation($event);
|
||||
}
|
||||
|
||||
public function testEventChangeHouseholdNotification()
|
||||
{
|
||||
$person = new Person();
|
||||
$period = new AccompanyingPeriod();
|
||||
$period
|
||||
->setStep(AccompanyingPeriod::STEP_CONFIRMED)
|
||||
->setPersonLocation($person)
|
||||
->addPerson($person)
|
||||
->setUser(new User());
|
||||
$this->forceIdToPeriod($period);
|
||||
|
||||
$previousHousehold = (new Household())->addAddress(
|
||||
($previousAddress = new Address())->setValidFrom(new DateTime('1 year ago'))
|
||||
);
|
||||
$previousMembership = new HouseholdMember();
|
||||
$previousMembership
|
||||
->setPerson($person)
|
||||
->setHousehold($previousHousehold)
|
||||
->setStartDate(new DateTimeImmutable('1 year ago'))
|
||||
->setEndDate(new DateTimeImmutable('tomorrow'));
|
||||
|
||||
$nextHousehold = (new Household())->addAddress(
|
||||
(new Address())->setValidFrom(new DateTime('tomorrow'))
|
||||
);
|
||||
$nextMembership = new HouseholdMember();
|
||||
$nextMembership
|
||||
->setPerson($person)
|
||||
->setHousehold($nextHousehold)
|
||||
->setStartDate(new DateTimeImmutable('tomorrow'));
|
||||
|
||||
$event = new PersonAddressMoveEvent($person);
|
||||
$event
|
||||
->setPreviousMembership($previousMembership)
|
||||
->setNextMembership($nextMembership);
|
||||
|
||||
$em = $this->prophesize(EntityManagerInterface::class);
|
||||
$em->persist(Argument::type(Notification::class))->shouldBeCalledTimes(1);
|
||||
$eventSubscriber = $this->buildSubscriber(null, $em->reveal(), null, null);
|
||||
|
||||
$eventSubscriber->resetPeriodLocation($event);
|
||||
|
||||
$this->assertNotNull($period->getAddressLocation());
|
||||
$this->assertNull($period->getPersonLocation());
|
||||
}
|
||||
|
||||
public function testEventChangeHouseholdNotificationForPeriodChangeLocationOfPersonAnteriorToCurrentLocationHistory()
|
||||
{
|
||||
$person = new Person();
|
||||
$period = new AccompanyingPeriod();
|
||||
$period
|
||||
->setStep(AccompanyingPeriod::STEP_CONFIRMED)
|
||||
->setPersonLocation($person)
|
||||
->setUser(new User())
|
||||
->addPerson($person);
|
||||
$this->forceIdToPeriod($period);
|
||||
|
||||
$previousHousehold = (new Household())->addAddress(
|
||||
($previousAddress = new Address())->setValidFrom(new DateTime('1 year ago'))
|
||||
);
|
||||
$previousMembership = new HouseholdMember();
|
||||
$previousMembership
|
||||
->setPerson($person)
|
||||
->setHousehold($previousHousehold)
|
||||
->setStartDate(new DateTimeImmutable('1 year ago'))
|
||||
->setEndDate(new DateTimeImmutable('tomorrow'));
|
||||
|
||||
$nextHousehold = (new Household())->addAddress(
|
||||
(new Address())->setValidFrom(new DateTime('1 month ago'))
|
||||
);
|
||||
$nextMembership = new HouseholdMember();
|
||||
$nextMembership
|
||||
->setPerson($person)
|
||||
->setHousehold($nextHousehold)
|
||||
->setStartDate(new DateTimeImmutable('1 month ago'));
|
||||
|
||||
$event = new PersonAddressMoveEvent($person);
|
||||
$event
|
||||
->setPreviousMembership($previousMembership)
|
||||
->setNextMembership($nextMembership);
|
||||
|
||||
$em = $this->prophesize(EntityManagerInterface::class);
|
||||
$em->persist(Argument::type(Notification::class))->shouldBeCalled(1);
|
||||
$eventSubscriber = $this->buildSubscriber(null, $em->reveal(), null, null);
|
||||
|
||||
$eventSubscriber->resetPeriodLocation($event);
|
||||
|
||||
$this->assertNotNull($period->getAddressLocation());
|
||||
$this->assertNull($period->getPersonLocation());
|
||||
}
|
||||
|
||||
public function testEventLeaveNotification()
|
||||
{
|
||||
$person = new Person();
|
||||
$period = new AccompanyingPeriod();
|
||||
$period
|
||||
->setStep(AccompanyingPeriod::STEP_CONFIRMED)
|
||||
->setPersonLocation($person)
|
||||
->addPerson($person)
|
||||
->setUser(new User());
|
||||
$this->forceIdToPeriod($period);
|
||||
|
||||
$previousHousehold = (new Household())->addAddress(
|
||||
($previousAddress = new Address())->setValidFrom(new DateTime('1 year ago'))
|
||||
);
|
||||
$previousMembership = new HouseholdMember();
|
||||
$previousMembership
|
||||
->setPerson($person)
|
||||
->setHousehold($previousHousehold)
|
||||
->setStartDate(new DateTimeImmutable('1 year ago'))
|
||||
->setEndDate(new DateTimeImmutable('tomorrow'));
|
||||
|
||||
$event = new PersonAddressMoveEvent($person);
|
||||
$event
|
||||
->setPreviousMembership($previousMembership);
|
||||
|
||||
$em = $this->prophesize(EntityManagerInterface::class);
|
||||
$em->persist(Argument::type(Notification::class))->shouldBeCalledTimes(1);
|
||||
$eventSubscriber = $this->buildSubscriber(null, $em->reveal(), null, null);
|
||||
|
||||
$eventSubscriber->resetPeriodLocation($event);
|
||||
|
||||
$this->assertNotNull($period->getAddressLocation());
|
||||
$this->assertNull($period->getPersonLocation());
|
||||
}
|
||||
|
||||
public function testEventPersonChangeAddressInThePast()
|
||||
{
|
||||
$person = new Person();
|
||||
$period = new AccompanyingPeriod();
|
||||
$period
|
||||
->setStep(AccompanyingPeriod::STEP_CONFIRMED)
|
||||
->setPersonLocation($person)
|
||||
->addPerson($person)
|
||||
->setUser(new User());
|
||||
$this->forceIdToPeriod($period);
|
||||
|
||||
$membership = new HouseholdMember();
|
||||
$membership
|
||||
->setPerson($person)
|
||||
->setHousehold($household = new Household())
|
||||
->setStartDate(new DateTimeImmutable('1 year ago'));
|
||||
|
||||
$previousAddress = new Address();
|
||||
$previousAddress->setValidFrom(new DateTime('6 months ago'));
|
||||
$household->addAddress($previousAddress);
|
||||
|
||||
$newAddress = new Address();
|
||||
$newAddress->setValidFrom(new DateTime('tomorrow'));
|
||||
$household->addAddress($newAddress);
|
||||
|
||||
$event = new PersonAddressMoveEvent($person);
|
||||
$event
|
||||
->setPreviousAddress($household->getPreviousAddressOf($newAddress))
|
||||
->setNextAddress($newAddress);
|
||||
|
||||
$em = $this->prophesize(EntityManagerInterface::class);
|
||||
$em->persist(Argument::type(Notification::class))->shouldBeCalledTimes(1);
|
||||
$eventSubscriber = $this->buildSubscriber(null, $em->reveal(), null, null);
|
||||
|
||||
$eventSubscriber->resetPeriodLocation($event);
|
||||
|
||||
$this->assertNotNull($period->getAddressLocation());
|
||||
$this->assertNull($period->getPersonLocation());
|
||||
}
|
||||
|
||||
private function buildSubscriber(
|
||||
?EngineInterface $engine = null,
|
||||
?EntityManagerInterface $entityManager = null,
|
||||
?Security $security = null,
|
||||
?TranslatorInterface $translator = null
|
||||
): PersonAddressMoveEventSubscriber {
|
||||
if (null === $translator) {
|
||||
$double = $this->prophesize(TranslatorInterface::class);
|
||||
$translator = $double->reveal();
|
||||
}
|
||||
|
||||
if (null === $security) {
|
||||
$double = $this->prophesize(Security::class);
|
||||
$double->getUser()->willReturn(new User());
|
||||
$security = $double->reveal();
|
||||
}
|
||||
|
||||
if (null === $engine) {
|
||||
$double = $this->prophesize(EngineInterface::class);
|
||||
$engine = $double->reveal();
|
||||
}
|
||||
|
||||
if (null === $entityManager) {
|
||||
$double = $this->prophesize(EntityManagerInterface::class);
|
||||
$entityManager = $double->reveal();
|
||||
}
|
||||
|
||||
return new PersonAddressMoveEventSubscriber(
|
||||
$engine,
|
||||
$entityManager,
|
||||
$security,
|
||||
$translator
|
||||
);
|
||||
}
|
||||
|
||||
private function forceIdToPeriod(AccompanyingPeriod $period): void
|
||||
{
|
||||
$reflectionClass = new ReflectionClass($period);
|
||||
$property = $reflectionClass->getProperty('id');
|
||||
$property->setAccessible(true);
|
||||
$property->setValue($period, 0);
|
||||
}
|
||||
}
|
@@ -21,6 +21,7 @@ use Chill\PersonBundle\Entity\Household\HouseholdMember;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use DateTime;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use RuntimeException;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use function array_map;
|
||||
@@ -58,10 +59,16 @@ final class HouseholdApiControllerTest extends WebTestCase
|
||||
$centerA = $em->getRepository(Center::class)->findOneBy(['name' => 'Center A']);
|
||||
$nbReference = $em->createQueryBuilder()->select('count(ar)')->from(AddressReference::class, 'ar')
|
||||
->getQuery()->getSingleScalarResult();
|
||||
|
||||
if (0 === $nbReference) {
|
||||
throw new RuntimeException('any reference found. Add a reference in database to perform this test');
|
||||
}
|
||||
|
||||
$reference = $em->createQueryBuilder()->select('ar')->from(AddressReference::class, 'ar')
|
||||
->setFirstResult(random_int(0, $nbReference))
|
||||
->setMaxResults(1)
|
||||
->getQuery()->getSingleResult();
|
||||
|
||||
$p = new Person();
|
||||
$p->setFirstname('test')->setLastName('test lastname')
|
||||
->setGender(Person::BOTH_GENDER)
|
||||
@@ -79,6 +86,7 @@ final class HouseholdApiControllerTest extends WebTestCase
|
||||
[HouseholdMember::class, $m->getId()],
|
||||
[User::class, $p->getId()],
|
||||
[Household::class, $h->getId()],
|
||||
[Person::class, $p->getId()],
|
||||
];
|
||||
|
||||
yield [$reference->getId(), $h->getId()];
|
||||
|
@@ -11,6 +11,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\PersonBundle\Tests\Entity;
|
||||
|
||||
use ArrayIterator;
|
||||
use Chill\MainBundle\Entity\Address;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\Comment;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation;
|
||||
@@ -60,6 +62,62 @@ final class AccompanyingPeriodTest extends \PHPUnit\Framework\TestCase
|
||||
$this->assertFalse($period->isClosingAfterOpening());
|
||||
}
|
||||
|
||||
public function testHistoryLocation()
|
||||
{
|
||||
$period = new AccompanyingPeriod();
|
||||
$person = new Person();
|
||||
$address = new Address();
|
||||
|
||||
$period->setPersonLocation($person);
|
||||
|
||||
$this->assertCount(0, $period->getLocationHistories());
|
||||
|
||||
$period->setAddressLocation($address);
|
||||
$period->setPersonLocation(null);
|
||||
|
||||
$this->assertCount(0, $period->getLocationHistories());
|
||||
|
||||
$period->setStep(AccompanyingPeriod::STEP_CONFIRMED);
|
||||
|
||||
$this->assertCount(1, $period->getLocationHistories());
|
||||
|
||||
$this->assertSame($address, $period->getLocationHistories()->first()->getAddressLocation());
|
||||
|
||||
$period->setPersonLocation($person);
|
||||
$period->setAddressLocation(null);
|
||||
|
||||
$this->assertCount(2, $period->getLocationHistories());
|
||||
$this->assertSame($person, $period->getLocationHistories()->last()->getPersonLocation());
|
||||
|
||||
$period->setAddressLocation($address);
|
||||
$period->setPersonLocation(null);
|
||||
|
||||
$this->assertCount(3, $period->getLocationHistories());
|
||||
|
||||
$locations = $period->getLocationHistories()->toArray();
|
||||
|
||||
usort($locations, static function (AccompanyingPeriod\AccompanyingPeriodLocationHistory $a, AccompanyingPeriod\AccompanyingPeriodLocationHistory $b) {
|
||||
return $a->getStartDate() <=> $b->getStartDate();
|
||||
});
|
||||
|
||||
$iterator = new ArrayIterator($locations);
|
||||
$iterator->rewind();
|
||||
|
||||
do {
|
||||
$current = $iterator->current();
|
||||
|
||||
$iterator->next();
|
||||
|
||||
if ($iterator->valid()) {
|
||||
$next = $iterator->current();
|
||||
$this->assertNotNull($current->getEndDate());
|
||||
$this->assertEquals($current->getEndDate(), $next->getStartDate());
|
||||
} else {
|
||||
$this->assertNull($current->getEndDate());
|
||||
}
|
||||
} while ($iterator->valid());
|
||||
}
|
||||
|
||||
public function testIsClosed()
|
||||
{
|
||||
$period = new AccompanyingPeriod(new DateTime());
|
||||
@@ -129,20 +187,27 @@ final class AccompanyingPeriodTest extends \PHPUnit\Framework\TestCase
|
||||
$this->assertNull($period->getPinnedComment());
|
||||
|
||||
$period->setPinnedComment($comment);
|
||||
|
||||
$this->assertSame($period->getPinnedComment(), $comment);
|
||||
$this->assertSame($period, $comment->getAccompanyingPeriod());
|
||||
$this->assertEquals(0, count($period->getComments()), 'The initial comment should not appears in the list of comments');
|
||||
$this->assertNull($comment->getAccompanyingPeriod());
|
||||
$this->assertEquals(0, count($period->getComments()));
|
||||
|
||||
$period->setPinnedComment($replacingComment);
|
||||
|
||||
$this->assertSame($period->getPinnedComment(), $replacingComment);
|
||||
$this->assertSame($period, $replacingComment->getAccompanyingPeriod());
|
||||
$this->assertEquals(0, count($period->getComments()), 'The initial comment should not appears in the list of comments');
|
||||
$this->assertNull($comment->getAccompanyingPeriod());
|
||||
$this->assertNull($replacingComment->getAccompanyingPeriod());
|
||||
$this->assertSame($period, $comment->getAccompanyingPeriod());
|
||||
$this->assertEquals(1, count($period->getComments()));
|
||||
$this->assertContains($comment, $period->getComments());
|
||||
|
||||
$period->setPinnedComment(null);
|
||||
|
||||
$this->assertNull($period->getPinnedComment());
|
||||
$this->assertNull($replacingComment->getAccompanyingPeriod());
|
||||
$this->assertEquals(0, count($period->getComments()), 'The initial comment should not appears in the list of comments');
|
||||
$this->assertSame($period, $comment->getAccompanyingPeriod());
|
||||
$this->assertSame($period, $replacingComment->getAccompanyingPeriod());
|
||||
$this->assertEquals(2, count($period->getComments()));
|
||||
$this->assertContains($comment, $period->getComments());
|
||||
$this->assertContains($replacingComment, $period->getComments());
|
||||
}
|
||||
|
||||
public function testRequestor()
|
||||
|
@@ -11,8 +11,12 @@ declare(strict_types=1);
|
||||
|
||||
namespace Entity\Household;
|
||||
|
||||
use Chill\MainBundle\Entity\Address;
|
||||
use Chill\PersonBundle\Entity\Household\Household;
|
||||
use Chill\PersonBundle\Entity\Household\HouseholdComposition;
|
||||
use Chill\PersonBundle\Entity\Household\HouseholdMember;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use DateTime;
|
||||
use DateTimeImmutable;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
@@ -22,6 +26,90 @@ use PHPUnit\Framework\TestCase;
|
||||
*/
|
||||
final class HouseholdTest extends TestCase
|
||||
{
|
||||
public function testGetMembersOnRange()
|
||||
{
|
||||
$household = new Household();
|
||||
|
||||
$household->addMember($householdMemberA = (new HouseholdMember())
|
||||
->setStartDate(new DateTimeImmutable('2020-01-01'))
|
||||
->setEndDate(new DateTimeImmutable('2020-12-31'))
|
||||
->setPerson(new Person()));
|
||||
$household->addMember($householdMemberB = (new HouseholdMember())
|
||||
->setStartDate(new DateTimeImmutable('2020-06-01'))
|
||||
->setEndDate(new DateTimeImmutable('2021-06-31'))
|
||||
->setPerson(new Person()));
|
||||
$household->addMember($householdMemberC = (new HouseholdMember())
|
||||
->setStartDate(new DateTimeImmutable('2021-01-01'))
|
||||
->setEndDate(null)
|
||||
->setPerson(new Person()));
|
||||
|
||||
$members = $household->getMembersOnRange(new DateTimeImmutable('2019-01-01'), null);
|
||||
|
||||
$this->assertCount(3, $members);
|
||||
$this->assertContains($householdMemberA, $members);
|
||||
$this->assertContains($householdMemberB, $members);
|
||||
$this->assertContains($householdMemberC, $members);
|
||||
|
||||
$members = $household->getMembersOnRange(new DateTimeImmutable('2020-01-01'), new DateTimeImmutable('2020-07-01'));
|
||||
$this->assertCount(2, $members);
|
||||
$this->assertContains($householdMemberA, $members);
|
||||
$this->assertContains($householdMemberB, $members);
|
||||
$this->assertNotContains($householdMemberC, $members);
|
||||
|
||||
$members = $household->getMembersOnRange(new DateTimeImmutable('2020-01-01'), new DateTimeImmutable('2022-12-31'));
|
||||
$this->assertCount(3, $members);
|
||||
$this->assertContains($householdMemberA, $members);
|
||||
$this->assertContains($householdMemberB, $members);
|
||||
$this->assertContains($householdMemberC, $members);
|
||||
|
||||
$members = $household->getMembersOnRange(new DateTimeImmutable('2021-01-01'), new DateTimeImmutable('2022-12-31'));
|
||||
$this->assertCount(2, $members);
|
||||
$this->assertNotContains($householdMemberA, $members);
|
||||
$this->assertContains($householdMemberB, $members);
|
||||
$this->assertContains($householdMemberC, $members);
|
||||
}
|
||||
|
||||
public function testHouseholdAddressConsistent()
|
||||
{
|
||||
$household = new Household();
|
||||
|
||||
$lastAddress = new Address();
|
||||
$lastAddress->setValidFrom($yesterday = new DateTime('yesterday'));
|
||||
$household->addAddress($lastAddress);
|
||||
|
||||
$this->assertNull($lastAddress->getValidTo());
|
||||
$this->assertEquals($yesterday, $lastAddress->getValidFrom());
|
||||
|
||||
$previousAddress = new Address();
|
||||
$previousAddress->setValidFrom($oneMonthAgo = new DateTime('1 month ago'));
|
||||
$household->addAddress($previousAddress);
|
||||
|
||||
$addresses = $household->getAddressesOrdered();
|
||||
$this->assertSame($previousAddress, $addresses[0]);
|
||||
$this->assertSame($lastAddress, $addresses[1]);
|
||||
|
||||
$this->assertEquals($oneMonthAgo, $previousAddress->getValidFrom());
|
||||
$this->assertEquals($yesterday, $previousAddress->getValidTo());
|
||||
$this->assertEquals($yesterday, $lastAddress->getValidFrom());
|
||||
$this->assertNull($lastAddress->getValidTo());
|
||||
|
||||
$futureAddress = new Address();
|
||||
$futureAddress->setValidFrom($tomorrow = new DateTime('tomorrow'));
|
||||
$household->addAddress($futureAddress);
|
||||
|
||||
$addresses = $household->getAddressesOrdered();
|
||||
$this->assertSame($previousAddress, $addresses[0]);
|
||||
$this->assertSame($lastAddress, $addresses[1]);
|
||||
$this->assertSame($futureAddress, $addresses[2]);
|
||||
|
||||
$this->assertEquals($oneMonthAgo, $previousAddress->getValidFrom());
|
||||
$this->assertEquals($yesterday, $previousAddress->getValidTo());
|
||||
$this->assertEquals($yesterday, $lastAddress->getValidFrom());
|
||||
$this->assertEquals($tomorrow, $lastAddress->getValidTo());
|
||||
$this->assertEquals($tomorrow, $futureAddress->getValidFrom());
|
||||
$this->assertNull($futureAddress->getValidTo());
|
||||
}
|
||||
|
||||
public function testHouseholdComposition()
|
||||
{
|
||||
$household = new Household();
|
||||
|
@@ -0,0 +1,118 @@
|
||||
<?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 Event\Person;
|
||||
|
||||
use Chill\MainBundle\Entity\Address;
|
||||
use Chill\PersonBundle\Entity\Household\Household;
|
||||
use Chill\PersonBundle\Entity\Household\HouseholdMember;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\PersonBundle\Event\Person\PersonAddressMoveEvent;
|
||||
use DateTime;
|
||||
use DateTimeImmutable;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @coversNothing
|
||||
*/
|
||||
final class PersonAddressMoveEventTest extends TestCase
|
||||
{
|
||||
public function testPersonChangeAddress()
|
||||
{
|
||||
$person = new Person();
|
||||
|
||||
$household = (new Household())->addAddress(
|
||||
($previousAddress = new Address())->setValidFrom(new DateTime('1 year ago'))
|
||||
);
|
||||
$household->addAddress(
|
||||
($nextAddress = new Address())->setValidFrom(new DateTime('1 month ago'))
|
||||
);
|
||||
$member = new HouseholdMember();
|
||||
$member
|
||||
->setPerson($person)
|
||||
->setHousehold($household)
|
||||
->setStartDate(new DateTimeImmutable('1 year ago'))
|
||||
->setEndDate(new DateTimeImmutable('tomorrow'));
|
||||
|
||||
$event = new PersonAddressMoveEvent($person);
|
||||
$event
|
||||
->setPreviousAddress($previousAddress)
|
||||
->setNextAddress($nextAddress);
|
||||
|
||||
$this->assertSame($previousAddress, $event->getPreviousAddress());
|
||||
$this->assertSame($nextAddress, $event->getNextAddress());
|
||||
$this->assertEquals((new DateTime('1 month ago'))->format('Y-m-d'), $nextAddress->getValidFrom()->format('Y-m-d'));
|
||||
$this->assertEquals((new DateTime('1 month ago'))->format('Y-m-d'), $event->getMoveDate()->format('Y-m-d'));
|
||||
}
|
||||
|
||||
public function testPersonChangeHousehold()
|
||||
{
|
||||
$person = new Person();
|
||||
|
||||
$previousHousehold = (new Household())->addAddress(
|
||||
($previousAddress = new Address())->setValidFrom(new DateTime('1 year ago'))
|
||||
);
|
||||
$previousMembership = new HouseholdMember();
|
||||
$previousMembership
|
||||
->setPerson($person)
|
||||
->setHousehold($previousHousehold)
|
||||
->setStartDate(new DateTimeImmutable('1 year ago'))
|
||||
->setEndDate(new DateTimeImmutable('tomorrow'));
|
||||
|
||||
$nextHousehold = (new Household())->addAddress(
|
||||
($nextAddress = new Address())->setValidFrom(new DateTime('tomorrow'))
|
||||
);
|
||||
$nextMembership = new HouseholdMember();
|
||||
$nextMembership
|
||||
->setPerson($person)
|
||||
->setHousehold($nextHousehold)
|
||||
->setStartDate(new DateTimeImmutable('tomorrow'));
|
||||
|
||||
$event = new PersonAddressMoveEvent($person);
|
||||
$event
|
||||
->setPreviousMembership($previousMembership)
|
||||
->setNextMembership($nextMembership);
|
||||
|
||||
$this->assertTrue($event->personChangeHousehold());
|
||||
$this->assertSame($previousAddress, $event->getPreviousAddress());
|
||||
$this->assertSame($nextAddress, $event->getNextAddress());
|
||||
$this->assertTrue($event->personChangeAddress());
|
||||
$this->assertFalse($event->personLeaveWithoutHousehold());
|
||||
$this->assertEquals(new DateTimeImmutable('tomorrow'), $event->getMoveDate());
|
||||
}
|
||||
|
||||
public function testPersonLeaveHousehold()
|
||||
{
|
||||
$person = new Person();
|
||||
|
||||
$previousHousehold = (new Household())->addAddress(
|
||||
($previousAddress = new Address())->setValidFrom(new DateTime('1 year ago'))
|
||||
);
|
||||
$previousMembership = new HouseholdMember();
|
||||
$previousMembership
|
||||
->setPerson($person)
|
||||
->setHousehold($previousHousehold)
|
||||
->setStartDate(new DateTimeImmutable('1 year ago'))
|
||||
->setEndDate(new DateTimeImmutable('tomorrow'));
|
||||
|
||||
$event = new PersonAddressMoveEvent($person);
|
||||
$event
|
||||
->setPreviousMembership($previousMembership);
|
||||
|
||||
$this->assertTrue($event->personChangeHousehold());
|
||||
$this->assertSame($previousAddress, $event->getPreviousAddress());
|
||||
$this->assertNull($event->getNextAddress());
|
||||
$this->assertTrue($event->personChangeAddress());
|
||||
$this->assertTrue($event->personLeaveWithoutHousehold());
|
||||
$this->assertEquals(new DateTimeImmutable('tomorrow'), $event->getMoveDate());
|
||||
}
|
||||
}
|
@@ -14,10 +14,14 @@ namespace Chill\PersonBundle\Tests\Household;
|
||||
use Chill\PersonBundle\Entity\Household\Household;
|
||||
use Chill\PersonBundle\Entity\Household\Position;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\PersonBundle\Event\Person\PersonAddressMoveEvent;
|
||||
use Chill\PersonBundle\Household\MembersEditorFactory;
|
||||
use DateTimeImmutable;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Prophecy\Argument;
|
||||
use Prophecy\PhpUnit\ProphecyTrait;
|
||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
|
||||
use function count;
|
||||
|
||||
/**
|
||||
@@ -26,13 +30,13 @@ use function count;
|
||||
*/
|
||||
final class MembersEditorTest extends TestCase
|
||||
{
|
||||
use ProphecyTrait;
|
||||
|
||||
private MembersEditorFactory $factory;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$validator = $this->createMock(ValidatorInterface::class);
|
||||
|
||||
$this->factory = new MembersEditorFactory($validator);
|
||||
$this->factory = $this->buildMembersEditorFactory();
|
||||
}
|
||||
|
||||
public function testMovePersonWithoutSharedHousehold()
|
||||
@@ -121,4 +125,49 @@ final class MembersEditorTest extends TestCase
|
||||
);
|
||||
$this->assertEquals($date, $membership1->getEndDate());
|
||||
}
|
||||
|
||||
public function testPostMove()
|
||||
{
|
||||
$person = new Person();
|
||||
$position = (new Position())
|
||||
->setShareHousehold(false);
|
||||
$household1 = new Household();
|
||||
$household2 = new Household();
|
||||
$eventDispatcher = $this->prophesize(EventDispatcherInterface::class);
|
||||
$eventDispatcher
|
||||
->dispatch(Argument::type(PersonAddressMoveEvent::class))
|
||||
->shouldBeCalled();
|
||||
$factory = $this->buildMembersEditorFactory(
|
||||
$eventDispatcher->reveal(),
|
||||
null
|
||||
);
|
||||
$editor = $factory->createEditor($household1);
|
||||
|
||||
$editor->addMovement(new DateTimeImmutable('now'), $person, $position);
|
||||
|
||||
$editor->postMove();
|
||||
}
|
||||
|
||||
private function buildMembersEditorFactory(
|
||||
?EventDispatcherInterface $eventDispatcher = null,
|
||||
?ValidatorInterface $validator = null
|
||||
) {
|
||||
if (null === $eventDispatcher) {
|
||||
$double = $this->getProphet()->prophesize();
|
||||
$double->willImplement(EventDispatcherInterface::class);
|
||||
$double->dispatch(Argument::type(PersonAddressMoveEvent::class));
|
||||
$eventDispatcher = $double->reveal();
|
||||
}
|
||||
|
||||
if (null === $validator) {
|
||||
$double = $this->getProphet()->prophesize();
|
||||
$double->willImplement(ValidatorInterface::class);
|
||||
$validator = $double->reveal();
|
||||
}
|
||||
|
||||
return new MembersEditorFactory(
|
||||
$eventDispatcher,
|
||||
$validator
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -16,14 +16,32 @@ use Chill\MainBundle\Workflow\EntityWorkflowHandlerInterface;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation;
|
||||
use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationRepository;
|
||||
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodWorkEvaluationVoter;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
class AccompanyingPeriodWorkEvaluationWorkflowHandler implements EntityWorkflowHandlerInterface
|
||||
{
|
||||
private AccompanyingPeriodWorkEvaluationRepository $repository;
|
||||
|
||||
public function __construct(AccompanyingPeriodWorkEvaluationRepository $repository)
|
||||
private TranslatorInterface $translator;
|
||||
|
||||
public function __construct(AccompanyingPeriodWorkEvaluationRepository $repository, TranslatorInterface $translator)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
$this->translator = $translator;
|
||||
}
|
||||
|
||||
public function getEntityData(EntityWorkflow $entityWorkflow, array $options = []): array
|
||||
{
|
||||
$evaluation = $this->getRelatedEntity($entityWorkflow);
|
||||
|
||||
return [
|
||||
'persons' => $evaluation->getAccompanyingPeriodWork()->getPersons(),
|
||||
];
|
||||
}
|
||||
|
||||
public function getEntityTitle(EntityWorkflow $entityWorkflow, array $options = []): string
|
||||
{
|
||||
return $this->translator->trans('workflow.Evaluation (n°%eval%)', ['%eval%' => $entityWorkflow->getRelatedEntityId()]);
|
||||
}
|
||||
|
||||
public function getRelatedEntity(EntityWorkflow $entityWorkflow): ?AccompanyingPeriodWorkEvaluation
|
||||
|
@@ -15,17 +15,34 @@ use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
|
||||
use Chill\MainBundle\Workflow\EntityWorkflowHandlerInterface;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
|
||||
use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkRepository;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
class AccompanyingPeriodWorkWorkflowHandler implements EntityWorkflowHandlerInterface
|
||||
{
|
||||
private AccompanyingPeriodWorkRepository $repository;
|
||||
|
||||
public function __construct(AccompanyingPeriodWorkRepository $repository)
|
||||
private TranslatorInterface $translator;
|
||||
|
||||
public function __construct(AccompanyingPeriodWorkRepository $repository, TranslatorInterface $translator)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
$this->translator = $translator;
|
||||
}
|
||||
|
||||
public function getRelatedEntity(EntityWorkflow $entityWorkflow): ?object
|
||||
public function getEntityData(EntityWorkflow $entityWorkflow, array $options = []): array
|
||||
{
|
||||
return [
|
||||
'persons' => $this->getRelatedEntity($entityWorkflow)
|
||||
->getPersons(),
|
||||
];
|
||||
}
|
||||
|
||||
public function getEntityTitle(EntityWorkflow $entityWorkflow, array $options = []): string
|
||||
{
|
||||
return $this->translator->trans('workflow.Work (n°%w%)', ['%w%' => $entityWorkflow->getRelatedEntityId()]);
|
||||
}
|
||||
|
||||
public function getRelatedEntity(EntityWorkflow $entityWorkflow): ?AccompanyingPeriodWork
|
||||
{
|
||||
return $this->repository->find($entityWorkflow->getRelatedEntityId());
|
||||
}
|
||||
|
@@ -25,7 +25,7 @@ services:
|
||||
Chill\PersonBundle\Controller\PersonDuplicateController:
|
||||
arguments:
|
||||
$similarPersonMatcher: '@Chill\PersonBundle\Search\SimilarPersonMatcher'
|
||||
$translator: '@Symfony\Component\Translation\TranslatorInterface'
|
||||
$translator: '@Symfony\Contracts\Translation\TranslatorInterface'
|
||||
$personRepository: '@Chill\PersonBundle\Repository\PersonRepository'
|
||||
$personMove: '@Chill\PersonBundle\Actions\Remove\PersonMove'
|
||||
$eventDispatcher: '@Symfony\Component\EventDispatcher\EventDispatcherInterface'
|
||||
|
@@ -8,5 +8,5 @@ services:
|
||||
Chill\PersonBundle\DataFixtures\ORM\LoadCustomFields:
|
||||
arguments:
|
||||
$translatableStringHelper: '@Chill\MainBundle\Templating\TranslatableStringHelper'
|
||||
$translator: '@Symfony\Component\Translation\TranslatorInterface'
|
||||
$translator: '@Symfony\Contracts\Translation\TranslatorInterface'
|
||||
tags: [ 'doctrine.fixture.orm' ]
|
||||
|
@@ -0,0 +1,49 @@
|
||||
<?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\Migrations\Person;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
final class Version20220214200327 extends AbstractMigration
|
||||
{
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql('DROP SEQUENCE chill_person_accompanying_period_location_history_id_seq CASCADE');
|
||||
$this->addSql('DROP TABLE chill_person_accompanying_period_location_history');
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Add location history to period';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->addSql('CREATE SEQUENCE chill_person_accompanying_period_location_history_id_seq INCREMENT BY 1 MINVALUE 1 START 1');
|
||||
$this->addSql('CREATE TABLE chill_person_accompanying_period_location_history (id INT NOT NULL, period_id INT DEFAULT NULL, startDate DATE NOT NULL, endDate DATE DEFAULT NULL, createdAt TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, personLocation_id INT DEFAULT NULL, addressLocation_id INT DEFAULT NULL, createdBy_id INT DEFAULT NULL, PRIMARY KEY(id))');
|
||||
$this->addSql('CREATE INDEX IDX_61E4E688EC8B7ADE ON chill_person_accompanying_period_location_history (period_id)');
|
||||
$this->addSql('CREATE INDEX IDX_61E4E688D5213D34 ON chill_person_accompanying_period_location_history (personLocation_id)');
|
||||
$this->addSql('CREATE INDEX IDX_61E4E6889B07D6BF ON chill_person_accompanying_period_location_history (addressLocation_id)');
|
||||
$this->addSql('CREATE INDEX IDX_61E4E6883174800F ON chill_person_accompanying_period_location_history (createdBy_id)');
|
||||
$this->addSql('COMMENT ON COLUMN chill_person_accompanying_period_location_history.startDate IS \'(DC2Type:date_immutable)\'');
|
||||
$this->addSql('COMMENT ON COLUMN chill_person_accompanying_period_location_history.endDate IS \'(DC2Type:date_immutable)\'');
|
||||
$this->addSql('COMMENT ON COLUMN chill_person_accompanying_period_location_history.createdAt IS \'(DC2Type:datetime_immutable)\'');
|
||||
$this->addSql('ALTER TABLE chill_person_accompanying_period_location_history ADD CONSTRAINT FK_61E4E688EC8B7ADE FOREIGN KEY (period_id) REFERENCES chill_person_accompanying_period (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE chill_person_accompanying_period_location_history ADD CONSTRAINT FK_61E4E688D5213D34 FOREIGN KEY (personLocation_id) REFERENCES chill_person_person (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE chill_person_accompanying_period_location_history ADD CONSTRAINT FK_61E4E6889B07D6BF FOREIGN KEY (addressLocation_id) REFERENCES chill_main_address (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE chill_person_accompanying_period_location_history ADD CONSTRAINT FK_61E4E6883174800F FOREIGN KEY (createdBy_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('INSERT INTO chill_person_accompanying_period_location_history (id, period_id, startDate, createdAt, personLocation_id, addresslocation_id)
|
||||
SELECT nextval(\'chill_person_accompanying_period_location_history_id_seq\'), id, NOW(), NOW(), personlocation_id, addresslocation_id FROM chill_person_accompanying_period WHERE step != \'DRAFT\'
|
||||
');
|
||||
}
|
||||
}
|
@@ -98,6 +98,7 @@ household:
|
||||
from: Depuis
|
||||
to: Jusqu'au
|
||||
person history: Ménages
|
||||
As member: En tant que
|
||||
|
||||
household_composition:
|
||||
Since: >-
|
||||
|
@@ -85,6 +85,7 @@ Course number: Parcours n°
|
||||
Civility: Civilité
|
||||
choose civility: --
|
||||
All genders: tous les genres
|
||||
Any person selected: Aucune personne sélectionnée
|
||||
|
||||
# dédoublonnage
|
||||
Old person: Doublon
|
||||
@@ -124,7 +125,7 @@ address_country_code: Code pays
|
||||
'Alreay existing person': 'Dossiers déjà encodés'
|
||||
'Add the person': 'Ajouter la personne'
|
||||
'Add the person and create an accompanying period': "Créer la personne & créer une période d'accompagnement"
|
||||
'Add the person and create an household': "Créer la personne & créer un ménage"
|
||||
'Add the person and create a household': "Créer la personne & créer un ménage"
|
||||
Show person: Voir le dossier de la personne
|
||||
'Confirm the creation': 'Confirmer la création'
|
||||
'You will create this person': 'Vous allez créer le dossier suivant'
|
||||
@@ -179,6 +180,7 @@ An accompanying period is open: Une période d'accompagnement est ouverte
|
||||
Accompanying period list: Périodes d'accompagnement
|
||||
Accompanying period list for person: Périodes d'accompagnement de la personne
|
||||
Accompanying period: Parcours d'accompagnement
|
||||
Any accompanying period: Aucun parcours d'accompagnement
|
||||
period: Parcours
|
||||
New accompanying course: Nouveau parcours d'accompagnement
|
||||
Choose a motive: Motif de fermeture
|
||||
@@ -224,7 +226,7 @@ Concerned scopes: Services concernés
|
||||
|
||||
# person resource
|
||||
|
||||
person_resources_menu: "Ressources"
|
||||
person_resources_menu: "Personnes ressources"
|
||||
Person resources: "Ressources de la personne"
|
||||
Add a person resource: "Ajouter une ressource"
|
||||
edit resource: "Modifier ressource"
|
||||
@@ -238,6 +240,7 @@ Select a type: "Choisissez un type"
|
||||
Select a person: "Choisissez un usager"
|
||||
Select a thirdparty: "Choisissez un tiers"
|
||||
Contact person: "Personne de contact"
|
||||
Kind: "Type"
|
||||
|
||||
|
||||
# pickAPersonType
|
||||
@@ -545,6 +548,7 @@ period_notification:
|
||||
Persons are: Les usagers concernés sont les suivants
|
||||
Social issues are: Les problématiques sociales renseignées sont les suivantes
|
||||
See it online: Visualisez le parcours en ligne
|
||||
Person locating period has moved: L'usager qui localise un parcours a déménagé
|
||||
|
||||
You are getting a notification for a period which does not exists any more: Cette notification ne correspond pas à une période d'accompagnement valide.
|
||||
You are getting a notification for a period you are not allowed to see: La notification fait référence à une période d'accompagnement à laquelle vous n'avez pas accès.
|
||||
@@ -566,3 +570,4 @@ Linked evaluations: Évaluations associées
|
||||
|
||||
# Accompanying period per user
|
||||
My accompanying periods: Mes parcours
|
||||
My accompanying periods in draft: Mes parcours brouillons
|
||||
|
Reference in New Issue
Block a user