mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-08-20 14:43:49 +00:00
Merge branch 'master' into household_filiation
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace Chill\PersonBundle\AccompanyingPeriod\Suggestion;
|
||||
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||
|
||||
/**
|
||||
* Basic implementation: this suggestion does not return any suggestion
|
||||
*/
|
||||
final class ReferralsSuggestion implements ReferralsSuggestionInterface
|
||||
{
|
||||
public function countReferralSuggested(AccompanyingPeriod $period, ?array $options = []): int
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AccompanyingPeriod $period
|
||||
* @return array|User[]
|
||||
*/
|
||||
public function findReferralSuggested(AccompanyingPeriod $period, int $limit = 50, int $start = 0): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace Chill\PersonBundle\AccompanyingPeriod\Suggestion;
|
||||
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||
|
||||
/**
|
||||
* Process the suggestion of referral for a given accompanying period
|
||||
*/
|
||||
interface ReferralsSuggestionInterface
|
||||
{
|
||||
public function countReferralSuggested(AccompanyingPeriod $period, ?array $options = []): int;
|
||||
|
||||
/**
|
||||
* @return array|User[]
|
||||
*/
|
||||
public function findReferralSuggested(AccompanyingPeriod $period, int $limit = 50, int $start = 0): array;
|
||||
|
||||
}
|
@@ -1,20 +1,7 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (C) 2016-2019 Champs-Libres <info@champs-libres.coop>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\PersonBundle\CRUD\Controller;
|
||||
|
||||
use Chill\MainBundle\CRUD\Controller\CRUDController;
|
||||
@@ -23,11 +10,8 @@ use Chill\PersonBundle\Entity\Person;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use BadMethodCallException;
|
||||
|
||||
/**
|
||||
* Controller for entities attached as one-to-on to a person
|
||||
*
|
||||
*/
|
||||
class OneToOneEntityPersonCRUDController extends CRUDController
|
||||
{
|
||||
protected function getTemplateFor($action, $entity, Request $request)
|
||||
@@ -35,11 +19,11 @@ class OneToOneEntityPersonCRUDController extends CRUDController
|
||||
if (!empty($this->crudConfig[$action]['template'])) {
|
||||
return $this->crudConfig[$action]['template'];
|
||||
}
|
||||
|
||||
|
||||
switch ($action) {
|
||||
case 'new':
|
||||
return '@ChillPerson/CRUD/new.html.twig';
|
||||
case 'edit':
|
||||
case 'edit':
|
||||
return '@ChillPerson/CRUD/edit.html.twig';
|
||||
case 'index':
|
||||
return '@ChillPerson/CRUD/index.html.twig';
|
||||
@@ -49,41 +33,41 @@ class OneToOneEntityPersonCRUDController extends CRUDController
|
||||
. "action");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected function getEntity($action, $id, Request $request): ?object
|
||||
{
|
||||
$entity = parent::getEntity($action, $id, $request);
|
||||
|
||||
|
||||
if (NULL === $entity) {
|
||||
$entity = $this->createEntity($action, $request);
|
||||
$person = $this->getDoctrine()
|
||||
->getManager()
|
||||
->getRepository(Person::class)
|
||||
->find($id);
|
||||
|
||||
|
||||
$entity->setPerson($person);
|
||||
}
|
||||
|
||||
|
||||
return $entity;
|
||||
}
|
||||
|
||||
|
||||
protected function onPreFlush(string $action, $entity, FormInterface $form, Request $request)
|
||||
{
|
||||
$this->getDoctrine()->getManager()->persist($entity);
|
||||
}
|
||||
|
||||
|
||||
protected function onPostFetchEntity($action, Request $request, $entity): ?Response
|
||||
{
|
||||
if (FALSE === $this->getDoctrine()->getManager()->contains($entity)) {
|
||||
return new RedirectResponse($this->generateRedirectOnCreateRoute($action, $request, $entity));
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
protected function generateRedirectOnCreateRoute($action, Request $request, $entity)
|
||||
{
|
||||
throw new BadMethodCallException("not implemtented yet");
|
||||
throw new BadMethodCallException('Not implemented yet.');
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -959,6 +959,8 @@ EOF
|
||||
$table->setHeaders(array('#', 'label', 'value'));
|
||||
$i = 0;
|
||||
|
||||
$matchingTableRowAnswer = [];
|
||||
|
||||
foreach($answers as $key => $answer) {
|
||||
$table->addRow(array(
|
||||
$i, $answer, $key
|
||||
|
@@ -3,12 +3,18 @@
|
||||
namespace Chill\PersonBundle\Controller;
|
||||
|
||||
use Chill\MainBundle\CRUD\Controller\ApiController;
|
||||
use Chill\MainBundle\Serializer\Model\Collection;
|
||||
use Chill\PersonBundle\AccompanyingPeriod\Suggestion\ReferralsSuggestionInterface;
|
||||
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
|
||||
use Symfony\Component\HttpFoundation\Exception\BadRequestException;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
|
||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
use Chill\PersonBundle\Privacy\AccompanyingPeriodPrivacyEvent;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
@@ -19,7 +25,6 @@ use Chill\PersonBundle\Entity\AccompanyingPeriod\Comment;
|
||||
use Chill\PersonBundle\Entity\SocialWork\SocialIssue;
|
||||
use Chill\MainBundle\Entity\Scope;
|
||||
use Chill\PersonBundle\Repository\AccompanyingPeriodACLAwareRepository;
|
||||
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
|
||||
use Symfony\Component\Workflow\Registry;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
|
||||
|
||||
@@ -31,28 +36,36 @@ class AccompanyingCourseApiController extends ApiController
|
||||
|
||||
private Registry $registry;
|
||||
|
||||
private ReferralsSuggestionInterface $referralAvailable;
|
||||
|
||||
public function __construct(
|
||||
EventDispatcherInterface $eventDispatcher,
|
||||
ValidatorInterface $validator,
|
||||
Registry $registry,
|
||||
AccompanyingPeriodACLAwareRepository $accompanyingPeriodACLAwareRepository
|
||||
AccompanyingPeriodACLAwareRepository $accompanyingPeriodACLAwareRepository,
|
||||
ReferralsSuggestionInterface $referralAvailable
|
||||
) {
|
||||
$this->eventDispatcher = $eventDispatcher;
|
||||
$this->validator = $validator;
|
||||
$this->registry = $registry;
|
||||
$this->accompanyingPeriodACLAwareRepository = $accompanyingPeriodACLAwareRepository;
|
||||
$this->referralAvailable = $referralAvailable;
|
||||
}
|
||||
|
||||
public function confirmApi($id, Request $request, $_format): Response
|
||||
{
|
||||
/** @var AccompanyingPeriod $accompanyingPeriod */
|
||||
/** @var AccompanyingPeriod $accompanyingPeriod */
|
||||
$accompanyingPeriod = $this->getEntity('participation', $id, $request);
|
||||
|
||||
$this->checkACL('confirm', $request, $_format, $accompanyingPeriod);
|
||||
$workflow = $this->registry->get($accompanyingPeriod);
|
||||
$workflow = $this->registry->get($accompanyingPeriod);
|
||||
|
||||
if (FALSE === $workflow->can($accompanyingPeriod, 'confirm')) {
|
||||
throw new BadRequestException('It is not possible to confirm this period');
|
||||
// throw new BadRequestException('It is not possible to confirm this period');
|
||||
$errors = $this->validator->validate($accompanyingPeriod, null, [$accompanyingPeriod::STEP_CONFIRMED]);
|
||||
if( count($errors) > 0 ){
|
||||
return $this->json($errors, 422);
|
||||
}
|
||||
}
|
||||
|
||||
$workflow->apply($accompanyingPeriod, 'confirm');
|
||||
@@ -63,10 +76,10 @@ $workflow = $this->registry->get($accompanyingPeriod);
|
||||
'groups' => [ 'read' ]
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
public function participationApi($id, Request $request, $_format)
|
||||
{
|
||||
/** @var AccompanyingPeriod $accompanyingPeriod */
|
||||
/** @var AccompanyingPeriod $accompanyingPeriod */
|
||||
$accompanyingPeriod = $this->getEntity('participation', $id, $request);
|
||||
$person = $this->getSerializer()
|
||||
->deserialize($request->getContent(), Person::class, $_format, []);
|
||||
@@ -104,6 +117,13 @@ $workflow = $this->registry->get($accompanyingPeriod);
|
||||
|
||||
public function resourceApi($id, Request $request, string $_format): Response
|
||||
{
|
||||
$accompanyingPeriod = $this->getEntity('resource', $id, $request);
|
||||
$errors = $this->validator->validate($accompanyingPeriod);
|
||||
|
||||
if ($errors->count() > 0) {
|
||||
return $this->json($errors, 422);
|
||||
}
|
||||
|
||||
return $this->addRemoveSomething('resource', $id, $request, $_format, 'resource', Resource::class);
|
||||
}
|
||||
|
||||
@@ -157,7 +177,7 @@ $workflow = $this->registry->get($accompanyingPeriod);
|
||||
->deserialize($request->getContent(), $class, $_format, []);
|
||||
} catch (RuntimeException $e) {
|
||||
$exceptions[] = $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($requestor === null) {
|
||||
throw new BadRequestException('Could not find any person or requestor', 0, $exceptions[0]);
|
||||
@@ -205,4 +225,28 @@ $workflow = $this->registry->get($accompanyingPeriod);
|
||||
return $this->json(\array_values($accompanyingPeriodsChecked), Response::HTTP_OK, [], ['groups' => [ 'read']]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/api/1.0/person/accompanying-course/{id}/referrers-suggested.{_format}",
|
||||
* requirements={ "_format"="json"},
|
||||
* name="chill_api_person_accompanying_period_referrers_suggested")
|
||||
* @param AccompanyingPeriod $period
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function suggestReferrals(AccompanyingPeriod $period, string $_format = 'json'): JsonResponse
|
||||
{
|
||||
$this->denyAccessUnlessGranted(AccompanyingPeriodVoter::EDIT, $period);
|
||||
|
||||
$total = $this->referralAvailable->countReferralSuggested($period);
|
||||
$paginator = $this->getPaginatorFactory()->create($total);
|
||||
|
||||
if (0 < $total) {
|
||||
$users = $this->referralAvailable->findReferralSuggested($period, $paginator->getItemsPerPage(),
|
||||
$paginator->getCurrentPageFirstItemNumber());
|
||||
} else {
|
||||
$users = [];
|
||||
}
|
||||
|
||||
return $this->json(new Collection($users, $paginator), Response::HTTP_OK,
|
||||
[], [ AbstractNormalizer::GROUPS => [ 'read' ]]);
|
||||
}
|
||||
}
|
||||
|
@@ -11,7 +11,10 @@ use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Serializer\SerializerInterface;
|
||||
use Symfony\Component\Translation\TranslatorInterface;
|
||||
use Symfony\Component\Form\Form;
|
||||
use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkRepository;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
||||
|
||||
class AccompanyingCourseWorkController extends AbstractController
|
||||
{
|
||||
@@ -19,17 +22,20 @@ class AccompanyingCourseWorkController extends AbstractController
|
||||
private SerializerInterface $serializer;
|
||||
private AccompanyingPeriodWorkRepository $workRepository;
|
||||
private PaginatorFactory $paginator;
|
||||
protected LoggerInterface $logger;
|
||||
|
||||
public function __construct(
|
||||
TranslatorInterface $trans,
|
||||
SerializerInterface $serializer,
|
||||
AccompanyingPeriodWorkRepository $workRepository,
|
||||
PaginatorFactory $paginator
|
||||
PaginatorFactory $paginator,
|
||||
LoggerInterface $chillLogger
|
||||
) {
|
||||
$this->trans = $trans;
|
||||
$this->serializer = $serializer;
|
||||
$this->workRepository = $workRepository;
|
||||
$this->paginator = $paginator;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -106,4 +112,65 @@ class AccompanyingCourseWorkController extends AbstractController
|
||||
'paginator' => $paginator
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @Route(
|
||||
* "{_locale}/person/accompanying-period/work/{id}/delete",
|
||||
* name="chill_person_accompanying_period_work_delete",
|
||||
* methods={"GET", "POST", "DELETE"}
|
||||
* )
|
||||
*/
|
||||
public function deleteWork(AccompanyingPeriodWork $work, Request $request): Response
|
||||
{
|
||||
// TODO ACL
|
||||
$em = $this->getDoctrine()->getManager();
|
||||
|
||||
$form = $this->createDeleteForm($work->getId());
|
||||
|
||||
if ($request->getMethod() === Request::METHOD_DELETE) {
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isValid()) {
|
||||
|
||||
$this->logger->notice("An accompanying period work has been removed", [
|
||||
'by_user' => $this->getUser()->getUsername(),
|
||||
'work_id' => $work->getId(),
|
||||
'accompanying_period_id' => $work->getAccompanyingPeriod()->getId()
|
||||
]);
|
||||
|
||||
$em->remove($work);
|
||||
$em->flush();
|
||||
|
||||
$this->addFlash(
|
||||
'success',
|
||||
$this->trans->trans("The accompanying period work has been successfully removed.")
|
||||
);
|
||||
|
||||
return $this->redirectToRoute('chill_person_accompanying_period_work_list', [
|
||||
'id' => $work->getAccompanyingPeriod()->getId()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->render('@ChillPerson/AccompanyingCourseWork/delete.html.twig', [
|
||||
'accompanyingCourse' => $work->getAccompanyingPeriod(),
|
||||
'work' => $work,
|
||||
'delete_form' => $form->createView()
|
||||
]);
|
||||
}
|
||||
|
||||
private function createDeleteForm(int $id): Form
|
||||
{
|
||||
$params['id'] = $id;
|
||||
|
||||
return $this->createFormBuilder()
|
||||
->setAction($this->generateUrl('chill_person_accompanying_period_work_delete', $params))
|
||||
->setMethod('DELETE')
|
||||
->add('submit', SubmitType::class, ['label' => 'Delete'])
|
||||
->getForm()
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@@ -16,6 +16,8 @@ use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
|
||||
use Chill\PersonBundle\Entity\Household\Household;
|
||||
use Chill\PersonBundle\Entity\Household\Position;
|
||||
use Chill\PersonBundle\Repository\Household\PositionRepository;
|
||||
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
|
||||
/**
|
||||
* @Route("/{_locale}/person/household")
|
||||
@@ -28,14 +30,18 @@ class HouseholdController extends AbstractController
|
||||
|
||||
private SerializerInterface $serializer;
|
||||
|
||||
private Security $security;
|
||||
|
||||
public function __construct(
|
||||
TranslatorInterface $translator,
|
||||
PositionRepository $positionRepository,
|
||||
SerializerInterface $serializer
|
||||
SerializerInterface $serializer,
|
||||
Security $security
|
||||
) {
|
||||
$this->translator = $translator;
|
||||
$this->positionRepository = $positionRepository;
|
||||
$this->serializer = $serializer;
|
||||
$this->security = $security;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -84,17 +90,46 @@ class HouseholdController extends AbstractController
|
||||
*/
|
||||
public function accompanyingPeriod(Request $request, Household $household)
|
||||
{
|
||||
// TODO ACL
|
||||
|
||||
$members = $household->getMembers();
|
||||
foreach($members as $m) {
|
||||
$accompanyingPeriods = $m->getPerson()->getAccompanyingPeriods();
|
||||
$currentMembers = $household->getCurrentPersons();
|
||||
$accompanyingPeriods = [];
|
||||
|
||||
foreach ($currentMembers as $p) {
|
||||
$accompanyingPeriodsMember = $p->getCurrentAccompanyingPeriods();
|
||||
|
||||
foreach ($accompanyingPeriodsMember as $accompanyingPeriod) {
|
||||
if (!$this->security->isGranted(AccompanyingPeriodVoter::SEE, $accompanyingPeriod)) {
|
||||
continue;
|
||||
} else {
|
||||
$accompanyingPeriods[$accompanyingPeriod->getId()] = $accompanyingPeriod;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$oldMembers = $household->getNonCurrentMembers();
|
||||
$accompanyingPeriodsOld = [];
|
||||
|
||||
foreach ($oldMembers as $m) {
|
||||
$accompanyingPeriodsOldMember = $m->getPerson()->getAccompanyingPeriods();
|
||||
|
||||
foreach ($accompanyingPeriodsOldMember as $accompanyingPeriod) {
|
||||
if (!$this->security->isGranted(AccompanyingPeriodVoter::SEE, $accompanyingPeriod)) {
|
||||
continue;
|
||||
} else {
|
||||
$id = $accompanyingPeriod->getId();
|
||||
|
||||
if (!array_key_exists($id, $accompanyingPeriodsOld) && !array_key_exists($id, $accompanyingPeriods)) {
|
||||
$accompanyingPeriodsOld[$id] = $accompanyingPeriod;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->render('@ChillPerson/Household/accompanying_period.html.twig',
|
||||
[
|
||||
'household' => $household,
|
||||
'accompanying_periods' => $accompanyingPeriods
|
||||
'accompanying_periods' => $accompanyingPeriods,
|
||||
'accompanying_periods_old' => $accompanyingPeriodsOld
|
||||
]
|
||||
);
|
||||
}
|
||||
|
@@ -150,7 +150,7 @@ final class PersonController extends AbstractController
|
||||
));
|
||||
}
|
||||
|
||||
public function editAction($person_id)
|
||||
public function editAction($person_id, Request $request)
|
||||
{
|
||||
$person = $this->_getPerson($person_id);
|
||||
|
||||
@@ -163,57 +163,33 @@ final class PersonController extends AbstractController
|
||||
|
||||
$form = $this->createForm(PersonType::class, $person,
|
||||
array(
|
||||
"action" => $this->generateUrl('chill_person_general_update',
|
||||
array("person_id" => $person_id)),
|
||||
"cFGroup" => $this->getCFGroup()
|
||||
)
|
||||
);
|
||||
|
||||
return $this->render('ChillPersonBundle:Person:edit.html.twig',
|
||||
array('person' => $person, 'form' => $form->createView()));
|
||||
}
|
||||
|
||||
public function updateAction($person_id, Request $request)
|
||||
{
|
||||
$person = $this->_getPerson($person_id);
|
||||
|
||||
if ($person === null) {
|
||||
return $this->createNotFoundException();
|
||||
}
|
||||
|
||||
$this->denyAccessUnlessGranted('CHILL_PERSON_UPDATE', $person,
|
||||
'You are not allowed to edit this person');
|
||||
|
||||
$form = $this->createForm(PersonType::class, $person,
|
||||
array("cFGroup" => $this->getCFGroup()));
|
||||
|
||||
if ($request->getMethod() === 'POST') {
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ( ! $form->isValid() ) {
|
||||
$this->get('session')
|
||||
->getFlashBag()->add('error', $this->translator
|
||||
->trans('This form contains errors'));
|
||||
|
||||
return $this->render('ChillPersonBundle:Person:edit.html.twig',
|
||||
array('person' => $person,
|
||||
'form' => $form->createView()));
|
||||
}
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isSubmitted() && !$form->isValid()) {
|
||||
$this->get('session')
|
||||
->getFlashBag()->add('error', $this->translator
|
||||
->trans('This form contains errors'));
|
||||
} elseif ($form->isSubmitted() && $form->isValid()) {
|
||||
$this->get('session')->getFlashBag()
|
||||
->add('success',
|
||||
$this->get('translator')
|
||||
->trans('The person data has been updated')
|
||||
);
|
||||
->add('success',
|
||||
$this->get('translator')
|
||||
->trans('The person data has been updated')
|
||||
);
|
||||
|
||||
$this->em->flush();
|
||||
|
||||
$url = $this->generateUrl('chill_person_view', array(
|
||||
return $this->redirectToRoute('chill_person_view', [
|
||||
'person_id' => $person->getId()
|
||||
));
|
||||
|
||||
return $this->redirect($url);
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
return $this->render('ChillPersonBundle:Person:edit.html.twig',
|
||||
array('person' => $person, 'form' => $form->createView()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -40,7 +40,7 @@ class LoadAccompanyingPeriodOrigin extends AbstractFixture implements OrderedFix
|
||||
|
||||
public function getOrder()
|
||||
{
|
||||
return 10005;
|
||||
return 9000;
|
||||
}
|
||||
|
||||
private $phoneCall = ['en' => 'phone call', 'fr' => 'appel téléphonique'];
|
||||
|
@@ -161,8 +161,10 @@ class LoadHousehold extends Fixture implements DependentFixtureInterface
|
||||
\shuffle($this->personIds);
|
||||
}
|
||||
|
||||
private function getRandomPersons(int $min, int $max)
|
||||
private function getRandomPersons(int $min, int $max): array
|
||||
{
|
||||
$persons = [];
|
||||
|
||||
$nb = \random_int($min, $max);
|
||||
|
||||
for ($i=0; $i < $nb; $i++) {
|
||||
@@ -172,7 +174,7 @@ class LoadHousehold extends Fixture implements DependentFixtureInterface
|
||||
;
|
||||
}
|
||||
|
||||
return $persons ?? [];
|
||||
return $persons;
|
||||
}
|
||||
|
||||
public function getDependencies()
|
||||
|
@@ -249,7 +249,9 @@ class LoadPeople extends AbstractFixture implements OrderedFixtureInterface, Con
|
||||
if (\random_int(0, 10) > 3) {
|
||||
// always add social scope:
|
||||
$accompanyingPeriod->addScope($this->getReference('scope_social'));
|
||||
|
||||
$origin = $this->getReference(LoadAccompanyingPeriodOrigin::ACCOMPANYING_PERIOD_ORIGIN);
|
||||
$accompanyingPeriod->setOrigin($origin);
|
||||
$accompanyingPeriod->setIntensity('regular');
|
||||
$accompanyingPeriod->setAddressLocation($this->createAddress());
|
||||
$manager->persist($accompanyingPeriod->getAddressLocation());
|
||||
$workflow = $this->workflowRegistry->get($accompanyingPeriod);
|
||||
|
@@ -77,11 +77,13 @@ class Configuration implements ConfigurationInterface
|
||||
->append($this->addFieldNode('nationality'))
|
||||
->append($this->addFieldNode('country_of_birth'))
|
||||
->append($this->addFieldNode('marital_status'))
|
||||
->append($this->addFieldNode('civility'))
|
||||
->append($this->addFieldNode('spoken_languages'))
|
||||
->append($this->addFieldNode('address'))
|
||||
->append($this->addFieldNode('accompanying_period'))
|
||||
->append($this->addFieldNode('memo'))
|
||||
->append($this->addFieldNode('number_of_children'))
|
||||
->append($this->addFieldNode('acceptEmail'))
|
||||
->arrayNode('alt_names')
|
||||
->defaultValue([])
|
||||
->arrayPrototype()
|
||||
|
@@ -28,6 +28,7 @@ use Chill\MainBundle\Entity\HasCentersInterface;
|
||||
use Chill\MainBundle\Entity\HasScopesInterface;
|
||||
use Chill\MainBundle\Entity\Scope;
|
||||
use Chill\MainBundle\Entity\Address;
|
||||
use Chill\MainBundle\Entity\Center;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\ClosingMotive;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\Comment;
|
||||
@@ -44,6 +45,9 @@ use Chill\MainBundle\Entity\User;
|
||||
use Symfony\Component\Serializer\Annotation\Groups;
|
||||
use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
use Symfony\Component\Validator\GroupSequenceProviderInterface;
|
||||
use Chill\PersonBundle\Validator\Constraints\AccompanyingPeriod\ParticipationOverlap;
|
||||
use Chill\PersonBundle\Validator\Constraints\AccompanyingPeriod\ResourceDuplicateCheck;
|
||||
|
||||
/**
|
||||
* AccompanyingPeriod Class
|
||||
@@ -53,9 +57,10 @@ use Symfony\Component\Validator\Constraints as Assert;
|
||||
* @DiscriminatorMap(typeProperty="type", mapping={
|
||||
* "accompanying_period"=AccompanyingPeriod::class
|
||||
* })
|
||||
* @Assert\GroupSequenceProvider
|
||||
*/
|
||||
class AccompanyingPeriod implements TrackCreationInterface, TrackUpdateInterface,
|
||||
HasScopesInterface, HasCentersInterface
|
||||
HasScopesInterface, HasCentersInterface, GroupSequenceProviderInterface
|
||||
{
|
||||
/**
|
||||
* Mark an accompanying period as "occasional"
|
||||
@@ -131,6 +136,7 @@ class AccompanyingPeriod implements TrackCreationInterface, TrackUpdateInterface
|
||||
* cascade={"persist", "remove"},
|
||||
* orphanRemoval=true
|
||||
* )
|
||||
* @Assert\NotBlank(groups={AccompanyingPeriod::STEP_DRAFT})
|
||||
*/
|
||||
private $comments;
|
||||
|
||||
@@ -146,9 +152,10 @@ class AccompanyingPeriod implements TrackCreationInterface, TrackUpdateInterface
|
||||
* @var Collection
|
||||
*
|
||||
* @ORM\OneToMany(targetEntity=AccompanyingPeriodParticipation::class,
|
||||
* mappedBy="accompanyingPeriod",
|
||||
* mappedBy="accompanyingPeriod", orphanRemoval=true,
|
||||
* cascade={"persist", "refresh", "remove", "merge", "detach"})
|
||||
* @Groups({"read"})
|
||||
* @ParticipationOverlap(groups={AccompanyingPeriod::STEP_DRAFT, AccompanyingPeriod::STEP_CONFIRMED})
|
||||
*/
|
||||
private $participations;
|
||||
|
||||
@@ -187,6 +194,7 @@ class AccompanyingPeriod implements TrackCreationInterface, TrackUpdateInterface
|
||||
* @ORM\ManyToOne(targetEntity=Origin::class)
|
||||
* @ORM\JoinColumn(nullable=true)
|
||||
* @Groups({"read", "write"})
|
||||
* @Assert\NotBlank(groups={AccompanyingPeriod::STEP_CONFIRMED})
|
||||
*/
|
||||
private $origin;
|
||||
|
||||
@@ -194,8 +202,9 @@ class AccompanyingPeriod implements TrackCreationInterface, TrackUpdateInterface
|
||||
* @var string
|
||||
* @ORM\Column(type="string", nullable=true)
|
||||
* @Groups({"read", "write"})
|
||||
* @Assert\NotBlank(groups={AccompanyingPeriod::STEP_CONFIRMED})
|
||||
*/
|
||||
private $intensity;
|
||||
private $intensity = self::INTENSITY_OCCASIONAL;
|
||||
|
||||
/**
|
||||
* @var Collection
|
||||
@@ -209,6 +218,7 @@ class AccompanyingPeriod implements TrackCreationInterface, TrackUpdateInterface
|
||||
* inverseJoinColumns={@ORM\JoinColumn(name="scope_id", referencedColumnName="id")}
|
||||
* )
|
||||
* @Groups({"read"})
|
||||
* @Assert\Count(min=1, groups={AccompanyingPeriod::STEP_CONFIRMED})
|
||||
*/
|
||||
private $scopes;
|
||||
|
||||
@@ -255,6 +265,7 @@ class AccompanyingPeriod implements TrackCreationInterface, TrackUpdateInterface
|
||||
* orphanRemoval=true
|
||||
* )
|
||||
* @Groups({"read"})
|
||||
* @ResourceDuplicateCheck(groups={AccompanyingPeriod::STEP_DRAFT, AccompanyingPeriod::STEP_CONFIRMED, "Default", "default"})
|
||||
*/
|
||||
private $resources;
|
||||
|
||||
@@ -266,6 +277,7 @@ class AccompanyingPeriod implements TrackCreationInterface, TrackUpdateInterface
|
||||
* name="chill_person_accompanying_period_social_issues"
|
||||
* )
|
||||
* @Groups({"read"})
|
||||
* @Assert\Count(min=1, groups={AccompanyingPeriod::STEP_CONFIRMED})
|
||||
*/
|
||||
private Collection $socialIssues;
|
||||
|
||||
@@ -502,7 +514,7 @@ class AccompanyingPeriod implements TrackCreationInterface, TrackUpdateInterface
|
||||
return $collection->count() > 0 ? $collection->first() : NULL;
|
||||
}
|
||||
|
||||
public function getOPenParticipations(): Collection
|
||||
public function getOpenParticipations(): Collection
|
||||
{
|
||||
return $this
|
||||
->getParticipations()
|
||||
@@ -513,6 +525,11 @@ class AccompanyingPeriod implements TrackCreationInterface, TrackUpdateInterface
|
||||
);
|
||||
}
|
||||
|
||||
public function getCurrentParticipations(): Collection
|
||||
{
|
||||
return $this->getOpenParticipations();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array with open participations sorted by household
|
||||
* [
|
||||
@@ -600,6 +617,14 @@ class AccompanyingPeriod implements TrackCreationInterface, TrackUpdateInterface
|
||||
return $participation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove Participation
|
||||
*/
|
||||
|
||||
public function removeParticipation(AccompanyingPeriodParticipation $participation)
|
||||
{
|
||||
$participation->setAccompanyingPeriod(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove Person
|
||||
@@ -1015,6 +1040,15 @@ class AccompanyingPeriod implements TrackCreationInterface, TrackUpdateInterface
|
||||
return $this->addressLocation;
|
||||
}
|
||||
|
||||
public function getCenter(): ?Center
|
||||
{
|
||||
if (count($this->getPersons()) === 0){
|
||||
return null;
|
||||
} else {
|
||||
return $this->getPersons()->first()->getCenter();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Groups({"write"})
|
||||
*/
|
||||
@@ -1100,4 +1134,17 @@ class AccompanyingPeriod implements TrackCreationInterface, TrackUpdateInterface
|
||||
|
||||
return $centers ?? null;
|
||||
}
|
||||
|
||||
public function getGroupSequence()
|
||||
{
|
||||
if($this->getStep() == self::STEP_DRAFT)
|
||||
{
|
||||
return [[self::STEP_DRAFT]];
|
||||
}
|
||||
|
||||
if($this->getStep() == self::STEP_CONFIRMED)
|
||||
{
|
||||
return [[self::STEP_DRAFT, self::STEP_CONFIRMED]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -167,7 +167,7 @@ use Symfony\Component\Validator\Constraints as Assert;
|
||||
* @ORM\OneToMany(
|
||||
* targetEntity=AccompanyingPeriodWorkEvaluation::class,
|
||||
* mappedBy="accompanyingPeriodWork",
|
||||
* cascade={"persist"},
|
||||
* cascade={"remove", "persist"},
|
||||
* orphanRemoval=true
|
||||
* )
|
||||
* @Serializer\Groups({"read"})
|
||||
|
@@ -70,7 +70,8 @@ class AccompanyingPeriodWorkEvaluationDocument implements \Chill\MainBundle\Doct
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(
|
||||
* targetEntity=StoredObject::class
|
||||
* targetEntity=StoredObject::class,
|
||||
* cascade={"remove"},
|
||||
* )
|
||||
* @Serializer\Groups({"read"})
|
||||
*/
|
||||
|
@@ -33,7 +33,13 @@ use Symfony\Component\Serializer\Annotation\Groups;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
* @ORM\Table(name="chill_person_accompanying_period_resource")
|
||||
* @ORM\Table(
|
||||
* name="chill_person_accompanying_period_resource",
|
||||
* uniqueConstraints={
|
||||
* @ORM\UniqueConstraint(name="person_unique", columns={"person_id", "accompanyingperiod_id"}),
|
||||
* @ORM\UniqueConstraint(name="thirdparty_unique", columns={"thirdparty_id", "accompanyingperiod_id"})
|
||||
* }
|
||||
* )
|
||||
* @DiscriminatorMap(typeProperty="type", mapping={
|
||||
* "accompanying_period_resource"=Resource::class
|
||||
* })
|
||||
|
@@ -134,4 +134,11 @@ class AccompanyingPeriodParticipation
|
||||
{
|
||||
return $this->endDate === null;
|
||||
}
|
||||
|
||||
private function checkSameStartEnd()
|
||||
{
|
||||
if($this->endDate == $this->startDate) {
|
||||
$this->accompanyingPeriod->removeParticipation($this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -41,7 +41,7 @@ class Household
|
||||
* targetEntity="Chill\MainBundle\Entity\Address",
|
||||
* cascade={"persist", "remove", "merge", "detach"})
|
||||
* @ORM\JoinTable(name="chill_person_household_to_addresses")
|
||||
* @ORM\OrderBy({"validFrom" = "DESC"})
|
||||
* @ORM\OrderBy({"validFrom" = "DESC", "id" = "DESC"})
|
||||
* @Serializer\Groups({"write"})
|
||||
*/
|
||||
private Collection $addresses;
|
||||
@@ -245,6 +245,16 @@ class Household
|
||||
$members->getIterator()
|
||||
->uasort(
|
||||
function (HouseholdMember $a, HouseholdMember $b) {
|
||||
if ($a->getPosition() === NULL) {
|
||||
if ($b->getPosition() === NULL) {
|
||||
return 0;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
} elseif ($b->getPosition() === NULL) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ($a->getPosition()->getOrdering() < $b->getPosition()->getOrdering()) {
|
||||
return -1;
|
||||
}
|
||||
@@ -334,6 +344,26 @@ class Household
|
||||
return $this->getNonCurrentMembers($now)->matching($criteria);
|
||||
}
|
||||
|
||||
public function getCurrentMembersWithoutPosition(\DateTimeInterface $now = null)
|
||||
{
|
||||
$criteria = new Criteria();
|
||||
$expr = Criteria::expr();
|
||||
|
||||
$criteria->where($expr->isNull('position'));
|
||||
|
||||
return $this->getCurrentMembers($now)->matching($criteria);
|
||||
}
|
||||
|
||||
public function getNonCurrentMembersWithoutPosition(\DateTimeInterface $now = null)
|
||||
{
|
||||
$criteria = new Criteria();
|
||||
$expr = Criteria::expr();
|
||||
|
||||
$criteria->where($expr->isNull('position'));
|
||||
|
||||
return $this->getNonCurrentMembers($now)->matching($criteria);
|
||||
}
|
||||
|
||||
public function addMember(HouseholdMember $member): self
|
||||
{
|
||||
if (!$this->members->contains($member)) {
|
||||
|
@@ -29,7 +29,7 @@ class HouseholdMember
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity=Position::class)
|
||||
* @Serializer\Groups({"read"})
|
||||
* @Assert\NotNull(groups={"household_memberships"})
|
||||
* @Assert\NotNull(groups={"household_memberships_created"})
|
||||
*/
|
||||
private ?Position $position = null;
|
||||
|
||||
@@ -159,6 +159,12 @@ class HouseholdMember
|
||||
return $this->shareHousehold;
|
||||
}
|
||||
|
||||
public function setShareHousehold(bool $shareHousehold): self
|
||||
{
|
||||
$this->shareHousehold = $shareHousehold;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPerson(): ?Person
|
||||
{
|
||||
|
@@ -41,14 +41,14 @@ class MaritalStatus
|
||||
|
||||
/**
|
||||
* @var string array
|
||||
* @ORM\Column(type="json_array")
|
||||
* @ORM\Column(type="json")
|
||||
*/
|
||||
private $name;
|
||||
|
||||
/**
|
||||
* Get id
|
||||
*
|
||||
* @return string
|
||||
* @return string
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
@@ -57,7 +57,7 @@ class MaritalStatus
|
||||
|
||||
/**
|
||||
* Set id
|
||||
*
|
||||
*
|
||||
* @param string $id
|
||||
* @return MaritalStatus
|
||||
*/
|
||||
@@ -76,17 +76,17 @@ class MaritalStatus
|
||||
public function setName($name)
|
||||
{
|
||||
$this->name = $name;
|
||||
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get name
|
||||
*
|
||||
* @return string array
|
||||
* @return string array
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -34,6 +34,7 @@ use Chill\PersonBundle\Entity\MaritalStatus;
|
||||
use Chill\PersonBundle\Entity\Household\HouseholdMember;
|
||||
use Chill\MainBundle\Entity\HasCenterInterface;
|
||||
use Chill\MainBundle\Entity\Address;
|
||||
use Chill\MainBundle\Entity\Civility;
|
||||
use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable;
|
||||
use Chill\PersonBundle\Entity\Person\PersonCurrentAddress;
|
||||
use DateTime;
|
||||
@@ -212,6 +213,15 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
|
||||
*/
|
||||
private $maritalStatus;
|
||||
|
||||
/**
|
||||
* The marital status of the person
|
||||
* @var Civility
|
||||
*
|
||||
* @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\Civility")
|
||||
* @ORM\JoinColumn(nullable=true)
|
||||
*/
|
||||
private $civility;
|
||||
|
||||
/**
|
||||
* The date of the last marital status change of the person
|
||||
* @var \DateTime
|
||||
@@ -406,7 +416,7 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
|
||||
* Array where customfield's data are stored
|
||||
* @var array
|
||||
*
|
||||
* @ORM\Column(type="json_array")
|
||||
* @ORM\Column(type="json")
|
||||
*/
|
||||
private $cFData;
|
||||
|
||||
@@ -620,7 +630,7 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
|
||||
{
|
||||
$currentAccompanyingPeriods = [];
|
||||
$currentDate = new DateTime();
|
||||
|
||||
|
||||
foreach ($this->accompanyingPeriodParticipations as $participation)
|
||||
{
|
||||
$endDate = $participation->getEndDate();
|
||||
@@ -988,6 +998,28 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
|
||||
return $this->maritalStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set civility
|
||||
*
|
||||
* @param Civility $civility
|
||||
* @return Person
|
||||
*/
|
||||
public function setCivility(Civility $civility = null)
|
||||
{
|
||||
$this->civility = $civility;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get civility
|
||||
*
|
||||
* @return Civility
|
||||
*/
|
||||
public function getCivility()
|
||||
{
|
||||
return $this->civility;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set contactInfo
|
||||
*
|
||||
@@ -1309,11 +1341,14 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
|
||||
/**
|
||||
* get the address associated with the person at the given date
|
||||
*
|
||||
* If the `$at` parameter is now, use the method `getCurrentPersonAddress`, which is optimized
|
||||
* on database side.
|
||||
*
|
||||
* @param DateTime|null $at
|
||||
* @return Address|null
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getCurrentPersonAddress(?\DateTime $at = null): ?Address
|
||||
public function getAddressAt(?\DateTime $at = null): ?Address
|
||||
{
|
||||
$at ??= new DateTime('now');
|
||||
|
||||
@@ -1331,6 +1366,20 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
|
||||
current($addresses);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current person address
|
||||
*
|
||||
* @return Address|null
|
||||
*/
|
||||
public function getCurrentPersonAddress(): ?Address
|
||||
{
|
||||
if (null === $this->currentPersonAddress) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->currentPersonAddress->getAddress();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validation callback that checks if the accompanying periods are valid
|
||||
*
|
||||
|
@@ -143,6 +143,8 @@ final class CountryOfBirthAggregator implements AggregatorInterface, ExportEleme
|
||||
|
||||
public function getLabels($key, array $values, $data)
|
||||
{
|
||||
$labels = [];
|
||||
|
||||
if ($data['group_by_level'] === 'country') {
|
||||
$qb = $this->countriesRepository->createQueryBuilder('c');
|
||||
|
||||
@@ -153,15 +155,17 @@ final class CountryOfBirthAggregator implements AggregatorInterface, ExportEleme
|
||||
->getResult(\Doctrine\ORM\Query::HYDRATE_SCALAR);
|
||||
|
||||
// initialize array and add blank key for null values
|
||||
$labels[''] = $this->translator->trans('without data');
|
||||
$labels['_header'] = $this->translator->trans('Country of birth');
|
||||
$labels = [
|
||||
'' => $this->translator->trans('without data'),
|
||||
'_header' => $this->translator->trans('Country of birth'),
|
||||
];
|
||||
|
||||
foreach($countries as $row) {
|
||||
$labels[$row['c_countryCode']] = $this->translatableStringHelper->localize($row['c_name']);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} elseif ($data['group_by_level'] === 'continent') {
|
||||
|
||||
if ($data['group_by_level'] === 'continent') {
|
||||
$labels = array(
|
||||
'EU' => $this->translator->trans('Europe'),
|
||||
'AS' => $this->translator->trans('Asia'),
|
||||
@@ -170,13 +174,12 @@ final class CountryOfBirthAggregator implements AggregatorInterface, ExportEleme
|
||||
'SA' => $this->translator->trans('South America'),
|
||||
'NA' => $this->translator->trans('North America'),
|
||||
'OC' => $this->translator->trans('Oceania'),
|
||||
'' => $this->translator->trans('without data'),
|
||||
'' => $this->translator->trans('without data'),
|
||||
'_header' => $this->translator->trans('Continent of birth')
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
return function($value) use ($labels) {
|
||||
return function(string $value) use ($labels): string {
|
||||
return $labels[$value];
|
||||
};
|
||||
|
||||
|
@@ -144,6 +144,8 @@ final class NationalityAggregator implements AggregatorInterface, ExportElementV
|
||||
|
||||
public function getLabels($key, array $values, $data)
|
||||
{
|
||||
$labels = [];
|
||||
|
||||
if ($data['group_by_level'] === 'country') {
|
||||
$qb = $this->countriesRepository->createQueryBuilder('c');
|
||||
|
||||
@@ -154,15 +156,17 @@ final class NationalityAggregator implements AggregatorInterface, ExportElementV
|
||||
->getResult(\Doctrine\ORM\Query::HYDRATE_SCALAR);
|
||||
|
||||
// initialize array and add blank key for null values
|
||||
$labels[''] = $this->translator->trans('without data');
|
||||
$labels['_header'] = $this->translator->trans('Nationality');
|
||||
$labels = [
|
||||
'' => $this->translator->trans('without data'),
|
||||
'_header' => $this->translator->trans('Nationality'),
|
||||
];
|
||||
|
||||
foreach($countries as $row) {
|
||||
$labels[$row['c_countryCode']] = $this->translatableStringHelper->localize($row['c_name']);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} elseif ($data['group_by_level'] === 'continent') {
|
||||
|
||||
if ($data['group_by_level'] === 'continent') {
|
||||
$labels = array(
|
||||
'EU' => $this->translator->trans('Europe'),
|
||||
'AS' => $this->translator->trans('Asia'),
|
||||
@@ -176,8 +180,7 @@ final class NationalityAggregator implements AggregatorInterface, ExportElementV
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
return function($value) use ($labels) {
|
||||
return function(string $value) use ($labels): string {
|
||||
return $labels[$value];
|
||||
};
|
||||
|
||||
|
@@ -21,11 +21,11 @@ use Chill\MainBundle\Export\FilterInterface;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Chill\MainBundle\Form\Type\ChillDateType;
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Chill\PersonBundle\Export\AbstractAccompanyingPeriodExportElement;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
class AccompanyingPeriodClosingFilter extends AbstractAccompanyingPeriodExportElement implements FilterInterface
|
||||
@@ -38,14 +38,14 @@ class AccompanyingPeriodClosingFilter extends AbstractAccompanyingPeriodExportEl
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
$this->addJoinAccompanyingPeriod($qb);
|
||||
|
||||
|
||||
$clause = $qb->expr()->andX(
|
||||
$qb->expr()->lte('accompanying_period.closingDate', ':date_to'),
|
||||
$qb->expr()->gte('accompanying_period.closingDate', ':date_from'));
|
||||
|
||||
$qb->andWhere($clause);
|
||||
$qb->setParameter('date_from', $data['date_from'], Type::DATE);
|
||||
$qb->setParameter('date_to', $data['date_to'], Type::DATE);
|
||||
$qb->setParameter('date_from', $data['date_from'], Types::DATE_MUTABLE);
|
||||
$qb->setParameter('date_to', $data['date_to'], Types::DATE_MUTABLE);
|
||||
}
|
||||
|
||||
public function applyOn(): string
|
||||
@@ -59,7 +59,7 @@ class AccompanyingPeriodClosingFilter extends AbstractAccompanyingPeriodExportEl
|
||||
'label' => "Having an accompanying period closed after this date",
|
||||
'data' => new \DateTime("-1 month"),
|
||||
));
|
||||
|
||||
|
||||
$builder->add('date_to', ChillDateType::class, array(
|
||||
'label' => "Having an accompanying period closed before this date",
|
||||
'data' => new \DateTime(),
|
||||
|
@@ -21,11 +21,11 @@ use Chill\MainBundle\Export\FilterInterface;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Chill\MainBundle\Form\Type\ChillDateType;
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Chill\PersonBundle\Export\AbstractAccompanyingPeriodExportElement;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
class AccompanyingPeriodFilter extends AbstractAccompanyingPeriodExportElement implements FilterInterface
|
||||
@@ -38,9 +38,9 @@ class AccompanyingPeriodFilter extends AbstractAccompanyingPeriodExportElement i
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
$this->addJoinAccompanyingPeriod($qb);
|
||||
|
||||
|
||||
$clause = $qb->expr()->andX();
|
||||
|
||||
|
||||
$clause->add(
|
||||
$qb->expr()->lte('accompanying_period.openingDate', ':date_to')
|
||||
);
|
||||
@@ -52,8 +52,8 @@ class AccompanyingPeriodFilter extends AbstractAccompanyingPeriodExportElement i
|
||||
);
|
||||
|
||||
$qb->andWhere($clause);
|
||||
$qb->setParameter('date_from', $data['date_from'], Type::DATE);
|
||||
$qb->setParameter('date_to', $data['date_to'], Type::DATE);
|
||||
$qb->setParameter('date_from', $data['date_from'], Types::DATE_MUTABLE);
|
||||
$qb->setParameter('date_to', $data['date_to'], Types::DATE_MUTABLE);
|
||||
}
|
||||
|
||||
public function applyOn(): string
|
||||
@@ -67,7 +67,7 @@ class AccompanyingPeriodFilter extends AbstractAccompanyingPeriodExportElement i
|
||||
'label' => "Having an accompanying period opened after this date",
|
||||
'data' => new \DateTime("-1 month"),
|
||||
));
|
||||
|
||||
|
||||
$builder->add('date_to', ChillDateType::class, array(
|
||||
'label' => "Having an accompanying period ending before this date, or "
|
||||
. "still opened at this date",
|
||||
|
@@ -21,11 +21,11 @@ use Chill\MainBundle\Export\FilterInterface;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Chill\MainBundle\Form\Type\ChillDateType;
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Chill\PersonBundle\Export\AbstractAccompanyingPeriodExportElement;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
class AccompanyingPeriodOpeningFilter extends AbstractAccompanyingPeriodExportElement implements FilterInterface
|
||||
@@ -38,14 +38,14 @@ class AccompanyingPeriodOpeningFilter extends AbstractAccompanyingPeriodExportEl
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
$this->addJoinAccompanyingPeriod($qb);
|
||||
|
||||
|
||||
$clause = $qb->expr()->andX(
|
||||
$qb->expr()->lte('accompanying_period.openingDate', ':date_to'),
|
||||
$qb->expr()->gte('accompanying_period.openingDate', ':date_from'));
|
||||
|
||||
$qb->andWhere($clause);
|
||||
$qb->setParameter('date_from', $data['date_from'], Type::DATE);
|
||||
$qb->setParameter('date_to', $data['date_to'], Type::DATE);
|
||||
$qb->setParameter('date_from', $data['date_from'], Types::DATE_MUTABLE);
|
||||
$qb->setParameter('date_to', $data['date_to'], Types::DATE_MUTABLE);
|
||||
}
|
||||
|
||||
public function applyOn(): string
|
||||
@@ -59,7 +59,7 @@ class AccompanyingPeriodOpeningFilter extends AbstractAccompanyingPeriodExportEl
|
||||
'label' => "Having an accompanying period opened after this date",
|
||||
'data' => new \DateTime("-1 month"),
|
||||
));
|
||||
|
||||
|
||||
$builder->add('date_to', ChillDateType::class, array(
|
||||
'label' => "Having an accompanying period opened before this date",
|
||||
'data' => new \DateTime(),
|
||||
|
@@ -16,13 +16,7 @@ class HouseholdMemberType extends AbstractType
|
||||
->add('startDate', ChillDateType::class, [
|
||||
'label' => 'household.Start date',
|
||||
'input' => 'datetime_immutable',
|
||||
])
|
||||
->add('endDate', ChillDateType::class, [
|
||||
'label' => 'household.End date',
|
||||
'input' => 'datetime_immutable',
|
||||
'required' => false
|
||||
])
|
||||
;
|
||||
]);
|
||||
if ($options['data']->getPosition()->isAllowHolder()) {
|
||||
$builder
|
||||
->add('holder', ChoiceType::class, [
|
||||
|
@@ -22,6 +22,7 @@
|
||||
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\ChillTextareaType;
|
||||
use Chill\MainBundle\Form\Type\Select2CountryType;
|
||||
@@ -35,6 +36,11 @@ use Chill\PersonBundle\Form\Type\Select2MaritalStatusType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Chill\MainBundle\Form\Type\ChillDateType;
|
||||
use Chill\MainBundle\Form\Type\CommentType;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Component\Form\CallbackTransformer;
|
||||
use Symfony\Component\Form\Extension\Core\Type\EmailType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TelType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
@@ -62,16 +68,20 @@ class PersonType extends AbstractType
|
||||
*/
|
||||
protected $configAltNamesHelper;
|
||||
|
||||
protected TranslatableStringHelper $translatableStringHelper;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string[] $personFieldsConfiguration configuration of visibility of some fields
|
||||
*/
|
||||
public function __construct(
|
||||
array $personFieldsConfiguration,
|
||||
ConfigPersonAltNamesHelper $configAltNamesHelper
|
||||
ConfigPersonAltNamesHelper $configAltNamesHelper,
|
||||
TranslatableStringHelper $translatableStringHelper
|
||||
) {
|
||||
$this->config = $personFieldsConfiguration;
|
||||
$this->configAltNamesHelper = $configAltNamesHelper;
|
||||
$this->translatableStringHelper = $translatableStringHelper;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -114,7 +124,19 @@ class PersonType extends AbstractType
|
||||
}
|
||||
|
||||
if ($this->config['place_of_birth'] === 'visible') {
|
||||
$builder->add('placeOfBirth', TextType::class, array('required' => false));
|
||||
$builder->add('placeOfBirth', TextType::class, array(
|
||||
'required' => false,
|
||||
'attr' => ['style' => 'text-transform: uppercase;'],
|
||||
));
|
||||
|
||||
$builder->get('placeOfBirth')->addModelTransformer(new CallbackTransformer(
|
||||
function ($string) {
|
||||
return strtoupper($string);
|
||||
},
|
||||
function ($string) {
|
||||
return strtoupper($string);
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
if ($this->config['contact_info'] === 'visible') {
|
||||
@@ -150,7 +172,11 @@ class PersonType extends AbstractType
|
||||
|
||||
if ($this->config['email'] === 'visible') {
|
||||
$builder
|
||||
->add('email', EmailType::class, array('required' => false))
|
||||
->add('email', EmailType::class, array('required' => false));
|
||||
}
|
||||
|
||||
if ($this->config['acceptEmail'] === 'visible') {
|
||||
$builder
|
||||
->add('acceptEmail', CheckboxType::class, array('required' => false));
|
||||
}
|
||||
|
||||
@@ -173,6 +199,23 @@ class PersonType extends AbstractType
|
||||
));
|
||||
}
|
||||
|
||||
if ($this->config['civility'] === 'visible'){
|
||||
$builder
|
||||
->add('civility', EntityType::class, [
|
||||
'label' => 'Civility',
|
||||
'class' => Civility::class,
|
||||
'choice_label' => function (Civility $civility): string {
|
||||
return $this->translatableStringHelper->localize($civility->getName());
|
||||
},
|
||||
'query_builder' => function (EntityRepository $er): QueryBuilder {
|
||||
return $er->createQueryBuilder('c')
|
||||
->where('c.active = true');
|
||||
},
|
||||
'placeholder' => 'choose civility',
|
||||
'required' => false
|
||||
]);
|
||||
}
|
||||
|
||||
if ($this->config['marital_status'] === 'visible'){
|
||||
$builder
|
||||
->add('maritalStatus', Select2MaritalStatusType::class, array(
|
||||
@@ -192,6 +235,7 @@ class PersonType extends AbstractType
|
||||
array('attr' => array('class' => 'cf-fields'), 'group' => $options['cFGroup']))
|
||||
;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -19,15 +19,17 @@ class MembersEditor
|
||||
|
||||
private array $persistables = [];
|
||||
private array $membershipsAffected = [];
|
||||
private array $oldMembershipsHashes = [];
|
||||
|
||||
public const VALIDATION_GROUP = 'household_memberships';
|
||||
public const VALIDATION_GROUP_CREATED = 'household_memberships_created';
|
||||
public const VALIDATION_GROUP_AFFECTED = 'household_memberships';
|
||||
|
||||
public function __construct(ValidatorInterface $validator, ?Household $household)
|
||||
{
|
||||
$this->validator = $validator;
|
||||
$this->household = $household;
|
||||
}
|
||||
|
||||
|
||||
public function addMovement(\DateTimeImmutable $date, Person $person, Position $position, ?bool $holder = false, ?string $comment = null): self
|
||||
{
|
||||
if (NULL === $this->household) {
|
||||
@@ -56,6 +58,7 @@ class MembersEditor
|
||||
if ($participation->getEndDate() === NULL || $participation->getEndDate() > $date) {
|
||||
$participation->setEndDate($date);
|
||||
$this->membershipsAffected[] = $participation;
|
||||
$this->oldMembershipsHashes[] = \spl_object_hash($participation);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -97,13 +100,18 @@ class MembersEditor
|
||||
{
|
||||
if ($this->hasHousehold()) {
|
||||
$list = $this->validator
|
||||
->validate($this->getHousehold(), null, [ self::VALIDATION_GROUP ]);
|
||||
->validate($this->getHousehold(), null, [ self::VALIDATION_GROUP_AFFECTED ]);
|
||||
} else {
|
||||
$list = new ConstraintViolationList();
|
||||
}
|
||||
|
||||
foreach ($this->membershipsAffected as $m) {
|
||||
$list->addAll($this->validator->validate($m, null, [ self::VALIDATION_GROUP ]));
|
||||
if (\in_array(\spl_object_hash($m), $this->oldMembershipsHashes)) {
|
||||
$list->addAll($this->validator->validate($m, null, [ self::VALIDATION_GROUP_AFFECTED ]));
|
||||
} else {
|
||||
$list->addAll($this->validator->validate($m, null, [ self::VALIDATION_GROUP_CREATED,
|
||||
self::VALIDATION_GROUP_AFFECTED ]));
|
||||
}
|
||||
}
|
||||
|
||||
return $list;
|
||||
|
@@ -66,7 +66,7 @@ class AccompanyingCourseMenuBuilder implements LocalMenuBuilderInterface
|
||||
'routeParameters' => [
|
||||
'id' => $period->getId()
|
||||
]])
|
||||
->setExtras(['order' => 50]);
|
||||
->setExtras(['order' => 40]);
|
||||
}
|
||||
|
||||
|
||||
|
@@ -1,8 +1,10 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\PersonBundle\Repository\Household;
|
||||
|
||||
use Chill\PersonBundle\Entity\Household\HouseholdMembers;
|
||||
use Chill\PersonBundle\Entity\Household\HouseholdMember;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
|
||||
@@ -12,6 +14,6 @@ final class HouseholdMembersRepository
|
||||
|
||||
public function __construct(EntityManagerInterface $entityManager)
|
||||
{
|
||||
$this->repository = $entityManager->getRepository(HouseholdMembers::class);
|
||||
$this->repository = $entityManager->getRepository(HouseholdMember::class);
|
||||
}
|
||||
}
|
||||
|
@@ -37,6 +37,9 @@ div.accompanying_course_work-list {
|
||||
border-radius: 12px;
|
||||
border: 2px solid $chill-green;
|
||||
}
|
||||
&.no-label:before {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -7,7 +7,6 @@ const personAcceptEmail = document.getElementById("personAcceptEmail");
|
||||
const personPhoneNumber = document.getElementById("personPhoneNumber");
|
||||
const personAcceptSMS = document.getElementById("personAcceptSMS");
|
||||
|
||||
|
||||
new ShowHide({
|
||||
froms: [maritalStatus],
|
||||
container: [maritalStatusDate],
|
||||
@@ -24,21 +23,24 @@ new ShowHide({
|
||||
event_name: 'change'
|
||||
});
|
||||
|
||||
new ShowHide({
|
||||
froms: [personEmail],
|
||||
container: [personAcceptEmail],
|
||||
test: function(froms) {
|
||||
for (let f of froms.values()) {
|
||||
for (let input of f.querySelectorAll('input').values()) {
|
||||
if (input.value) {
|
||||
return true
|
||||
if (personAcceptEmail) {
|
||||
new ShowHide({
|
||||
froms: [personEmail],
|
||||
container: [personAcceptEmail],
|
||||
test: function(froms) {
|
||||
for (let f of froms.values()) {
|
||||
for (let input of f.querySelectorAll('input').values()) {
|
||||
if (input.value) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
event_name: 'input'
|
||||
});
|
||||
return false;
|
||||
},
|
||||
event_name: 'input'
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
new ShowHide({
|
||||
froms: [personPhoneNumber],
|
||||
|
@@ -8,7 +8,7 @@
|
||||
<persons-associated></persons-associated>
|
||||
<course-location></course-location>
|
||||
<origin-demand></origin-demand>
|
||||
<requestor></requestor>
|
||||
<requestor v-bind:isAnonymous="accompanyingCourse.requestorAnonymous"></requestor>
|
||||
<social-issue></social-issue>
|
||||
<scopes></scopes>
|
||||
<referrer></referrer>
|
||||
@@ -16,15 +16,15 @@
|
||||
<comment v-if="accompanyingCourse.step === 'DRAFT'"></comment>
|
||||
<confirm v-if="accompanyingCourse.step === 'DRAFT'"></confirm>
|
||||
|
||||
<div v-for="error in errorMsg" class="vue-component errors alert alert-danger">
|
||||
<!-- <div v-for="error in errorMsg" v-bind:key="error.id" class="vue-component errors alert alert-danger">
|
||||
<p>
|
||||
<span>{{ error.sta }} {{ error.txt }}</span><br>
|
||||
<span>{{ $t(error.msg) }}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div> -->
|
||||
</template>
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
import { mapGetters, mapState } from 'vuex'
|
||||
import Banner from './components/Banner.vue';
|
||||
import StickyNav from './components/StickyNav.vue';
|
||||
import OriginDemand from './components/OriginDemand.vue';
|
||||
@@ -54,11 +54,12 @@ export default {
|
||||
Comment,
|
||||
Confirm,
|
||||
},
|
||||
computed: mapState([
|
||||
'accompanyingCourse',
|
||||
'addressContext',
|
||||
'errorMsg'
|
||||
])
|
||||
computed: {
|
||||
...mapState([
|
||||
'accompanyingCourse',
|
||||
'addressContext'
|
||||
]),
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
|
@@ -1,3 +1,5 @@
|
||||
import { fetchResults } from 'ChillMainAssets/lib/api/download.js';
|
||||
|
||||
/*
|
||||
* Endpoint v.2 chill_api_single_accompanying_course__entity
|
||||
* method GET/HEAD, get AccompanyingCourse Instance
|
||||
@@ -84,7 +86,8 @@ const postParticipation = (id, payload, method) => {
|
||||
})
|
||||
.then(response => {
|
||||
if (response.ok) { return response.json(); }
|
||||
throw { msg: 'Error while sending AccompanyingPeriod Course participation.', sta: response.status, txt: response.statusText, err: new Error(), body: response.body };
|
||||
// TODO: adjust message according to status code? Or how to access the message from the violation array?
|
||||
throw { msg: 'Error while sending AccompanyingPeriod Course participation', sta: response.status, txt: response.statusText, err: new Error(), body: response.body };
|
||||
});
|
||||
};
|
||||
|
||||
@@ -168,11 +171,8 @@ const postSocialIssue = (id, body, method) => {
|
||||
|
||||
const getUsers = () => {
|
||||
const url = `/api/1.0/main/user.json`;
|
||||
return fetch(url)
|
||||
.then(response => {
|
||||
if (response.ok) { return response.json(); }
|
||||
throw { msg: 'Error while retriving users.', sta: response.status, txt: response.statusText, err: new Error(), body: response.body };
|
||||
});
|
||||
|
||||
return fetchResults(url);
|
||||
};
|
||||
|
||||
const whoami = () => {
|
||||
@@ -216,8 +216,6 @@ const addScope = (id, scope) => {
|
||||
|
||||
const removeScope = (id, scope) => {
|
||||
const url = `/api/1.0/person/accompanying-course/${id}/scope.json`;
|
||||
console.log(url);
|
||||
console.log(scope);
|
||||
|
||||
return fetch(url, {
|
||||
method: 'DELETE',
|
||||
@@ -235,6 +233,12 @@ const removeScope = (id, scope) => {
|
||||
});
|
||||
};
|
||||
|
||||
const getReferrersSuggested = (course) => {
|
||||
const url = `/api/1.0/person/accompanying-course/${course.id}/referrers-suggested.json`;
|
||||
|
||||
return fetchResults(url);
|
||||
}
|
||||
|
||||
export {
|
||||
getAccompanyingCourse,
|
||||
patchAccompanyingCourse,
|
||||
@@ -249,4 +253,5 @@ export {
|
||||
postSocialIssue,
|
||||
addScope,
|
||||
removeScope,
|
||||
getReferrersSuggested,
|
||||
};
|
||||
|
@@ -10,13 +10,13 @@
|
||||
<VueMultiselect
|
||||
name="selectOrigin"
|
||||
label="text"
|
||||
v-bind:custom-label="transText"
|
||||
:custom-label="transText"
|
||||
track-by="id"
|
||||
v-bind:multiple="false"
|
||||
v-bind:searchable="true"
|
||||
v-bind:placeholder="$t('origin.placeholder')"
|
||||
:multiple="false"
|
||||
:searchable="true"
|
||||
:placeholder="$t('origin.placeholder')"
|
||||
v-model="value"
|
||||
v-bind:options="options"
|
||||
:options="options"
|
||||
@select="updateOrigin">
|
||||
</VueMultiselect>
|
||||
|
||||
@@ -47,18 +47,18 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
getOptions() {
|
||||
//console.log('loading origins list');
|
||||
getListOrigins().then(response => new Promise((resolve, reject) => {
|
||||
this.options = response.results;
|
||||
resolve();
|
||||
}));
|
||||
},
|
||||
updateOrigin(value) {
|
||||
//console.log('value', value);
|
||||
console.log('value', value);
|
||||
this.$store.dispatch('updateOrigin', value);
|
||||
},
|
||||
transText ({ text }) {
|
||||
return text.fr //TODO multilang
|
||||
const parsedText = JSON.parse(text);
|
||||
return parsedText.fr;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@@ -9,6 +9,7 @@
|
||||
addAltNames: true,
|
||||
addAge : true,
|
||||
hLevel : 3,
|
||||
isConfidential : false,
|
||||
}"
|
||||
:person="participation.person"
|
||||
:returnPath="getAccompanyingCourseReturnPath">
|
||||
|
@@ -15,9 +15,20 @@
|
||||
v-bind:searchable="true"
|
||||
v-bind:placeholder="$t('referrer.placeholder')"
|
||||
v-model="value"
|
||||
v-bind:options="options"
|
||||
v-bind:options="users"
|
||||
@select="updateReferrer">
|
||||
</VueMultiselect>
|
||||
|
||||
|
||||
<template v-if="referrersSuggested.length > 0">
|
||||
<ul>
|
||||
<li v-for="u in referrersSuggested" @click="updateReferrer(u)">
|
||||
<user-render-box-badge :user="u"></user-render-box-badge>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@@ -41,30 +52,29 @@
|
||||
import VueMultiselect from 'vue-multiselect';
|
||||
import { getUsers, whoami } from '../api';
|
||||
import { mapState } from 'vuex';
|
||||
import UserRenderBoxBadge from "ChillMainAssets/vuejs/_components/Entity/UserRenderBoxBadge";
|
||||
|
||||
export default {
|
||||
name: "Referrer",
|
||||
components: { VueMultiselect },
|
||||
data() {
|
||||
return {
|
||||
options: []
|
||||
}
|
||||
components: {
|
||||
UserRenderBoxBadge,
|
||||
VueMultiselect,
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
value: state => state.accompanyingCourse.user,
|
||||
users: state => state.users,
|
||||
referrersSuggested: state => {
|
||||
return state.referrersSuggested.filter(u => {
|
||||
if (null === state.accompanyingCourse.user) {
|
||||
return true;
|
||||
}
|
||||
return state.accompanyingCourse.user.id !== u.id;
|
||||
})
|
||||
},
|
||||
}),
|
||||
},
|
||||
mounted() {
|
||||
this.getOptions();
|
||||
},
|
||||
methods: {
|
||||
getOptions() {
|
||||
getUsers().then(response => new Promise((resolve, reject) => {
|
||||
this.options = response.results;
|
||||
resolve();
|
||||
}));
|
||||
},
|
||||
updateReferrer(value) {
|
||||
//console.log('value', value);
|
||||
this.$store.dispatch('updateReferrer', value);
|
||||
|
@@ -3,51 +3,60 @@
|
||||
|
||||
<h2><a id="section-40"></a>{{ $t('requestor.title') }}</h2>
|
||||
|
||||
<div v-if="accompanyingCourse.requestor" class="flex-table">
|
||||
<div v-if="accompanyingCourse.requestor && isAnonymous" class="flex-table">
|
||||
|
||||
<label>
|
||||
<input type="checkbox" v-model="isAnonymous" class="me-2" />
|
||||
{{ $t('requestor.is_anonymous') }}
|
||||
</label>
|
||||
|
||||
<third-party-render-box v-if="accompanyingCourse.requestor.type === 'thirdparty'"
|
||||
:thirdparty="accompanyingCourse.requestor"
|
||||
:options="{
|
||||
addLink: false,
|
||||
addId: false,
|
||||
addEntity: true,
|
||||
addInfo: false,
|
||||
hLevel: 3,
|
||||
isMultiline: true
|
||||
}"
|
||||
>
|
||||
<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>
|
||||
</ul>
|
||||
</template>
|
||||
</third-party-render-box>
|
||||
|
||||
<person-render-box render="bloc" v-else-if="accompanyingCourse.requestor.type === 'person'"
|
||||
:person="accompanyingCourse.requestor"
|
||||
:options="{
|
||||
addLink: false,
|
||||
addId: false,
|
||||
addAltNames: false,
|
||||
addEntity: true,
|
||||
addInfo: true,
|
||||
hLevel: 3,
|
||||
isMultiline: true
|
||||
}"
|
||||
>
|
||||
<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>
|
||||
</ul>
|
||||
</template>
|
||||
</person-render-box>
|
||||
<confidential v-if="accompanyingCourse.requestor.type === 'thirdparty'">
|
||||
<template v-slot:confidential-content>
|
||||
<third-party-render-box
|
||||
:thirdparty="accompanyingCourse.requestor"
|
||||
:options="{
|
||||
addLink: false,
|
||||
addId: false,
|
||||
addEntity: true,
|
||||
addInfo: false,
|
||||
hLevel: 3,
|
||||
isMultiline: true,
|
||||
isConfidential: true
|
||||
}"
|
||||
>
|
||||
<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>
|
||||
</ul>
|
||||
</template>
|
||||
</third-party-render-box>
|
||||
</template>
|
||||
</confidential>
|
||||
|
||||
<confidential v-else-if="accompanyingCourse.requestor.type === 'person'">
|
||||
<template v-slot:confidential-content>
|
||||
<person-render-box render="bloc"
|
||||
:person="accompanyingCourse.requestor"
|
||||
:options="{
|
||||
addLink: false,
|
||||
addId: false,
|
||||
addAltNames: false,
|
||||
addEntity: true,
|
||||
addInfo: true,
|
||||
hLevel: 3,
|
||||
isMultiline: true,
|
||||
isConfidential: false
|
||||
}"
|
||||
>
|
||||
<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>
|
||||
</ul>
|
||||
</template>
|
||||
</person-render-box>
|
||||
</template>
|
||||
</confidential>
|
||||
|
||||
<ul class="record_actions">
|
||||
<li>
|
||||
@@ -59,6 +68,57 @@
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div v-else-if="accompanyingCourse.requestor && !isAnonymous" class="flex-table">
|
||||
<label>
|
||||
<input type="checkbox" v-model="isAnonymous" class="me-2" />
|
||||
{{ $t('requestor.is_anonymous') }}
|
||||
</label>
|
||||
|
||||
<third-party-render-box
|
||||
v-if="accompanyingCourse.requestor.type === 'thirdparty'"
|
||||
:thirdparty="accompanyingCourse.requestor"
|
||||
:options="{
|
||||
addLink: false,
|
||||
addId: false,
|
||||
addEntity: true,
|
||||
addInfo: false,
|
||||
hLevel: 3,
|
||||
isMultiline: true,
|
||||
isConfidential: true
|
||||
}"
|
||||
>
|
||||
<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>
|
||||
</ul>
|
||||
</template>
|
||||
</third-party-render-box>
|
||||
|
||||
<person-render-box render="bloc"
|
||||
v-if="accompanyingCourse.requestor.type === 'person'"
|
||||
:person="accompanyingCourse.requestor"
|
||||
:options="{
|
||||
addLink: false,
|
||||
addId: false,
|
||||
addAltNames: false,
|
||||
addEntity: true,
|
||||
addInfo: true,
|
||||
hLevel: 3,
|
||||
isMultiline: true,
|
||||
isConfidential: false
|
||||
}"
|
||||
>
|
||||
<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>
|
||||
</ul>
|
||||
</template>
|
||||
</person-render-box>
|
||||
</div>
|
||||
|
||||
<div v-else>
|
||||
<label class="chill-no-data-statement">{{ $t('requestor.counter') }}</label>
|
||||
</div>
|
||||
@@ -82,6 +142,7 @@ import AddPersons from 'ChillPersonAssets/vuejs/_components/AddPersons.vue';
|
||||
import OnTheFly from 'ChillMainAssets/vuejs/OnTheFly/components/OnTheFly.vue';
|
||||
import PersonRenderBox from '../../_components/Entity/PersonRenderBox.vue';
|
||||
import ThirdPartyRenderBox from 'ChillThirdPartyAssets/vuejs/_components/Entity/ThirdPartyRenderBox.vue';
|
||||
import Confidential from 'ChillMainAssets/vuejs/_components/Confidential.vue';
|
||||
|
||||
export default {
|
||||
name: 'Requestor',
|
||||
@@ -90,7 +151,9 @@ export default {
|
||||
OnTheFly,
|
||||
PersonRenderBox,
|
||||
ThirdPartyRenderBox,
|
||||
Confidential
|
||||
},
|
||||
props: ['isAnonymous'],
|
||||
data() {
|
||||
return {
|
||||
addPersons: {
|
||||
@@ -149,4 +212,8 @@ div.flex-table {
|
||||
margin-top: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.confidential {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
|
@@ -2,7 +2,7 @@
|
||||
<person-render-box render="bloc"
|
||||
v-if="resource.resource.type === 'person'"
|
||||
:person="resource.resource"
|
||||
:options="{ addInfo : true, addId : false, addEntity: true, addLink: false, addAltNames: true, addAge : false, hLevel : 3 }"
|
||||
:options="{ addInfo : true, addId : false, addEntity: true, addLink: false, addAltNames: true, addAge : false, hLevel : 3, isConfidential : true }"
|
||||
>
|
||||
<template v-slot:record-actions>
|
||||
<ul class="record_actions">
|
||||
|
@@ -2,6 +2,8 @@ import { createApp } from 'vue'
|
||||
import { _createI18n } from 'ChillMainAssets/vuejs/_js/i18n'
|
||||
import { appMessages } from './js/i18n'
|
||||
import { initPromise } from './store'
|
||||
import VueToast from 'vue-toast-notification';
|
||||
import 'vue-toast-notification/dist/theme-sugar.css';
|
||||
|
||||
import App from './App.vue';
|
||||
import Banner from './components/Banner.vue';
|
||||
@@ -21,6 +23,7 @@ if (root === 'app') {
|
||||
})
|
||||
.use(store)
|
||||
.use(i18n)
|
||||
.use(VueToast)
|
||||
.component('app', App)
|
||||
.mount('#accompanying-course');
|
||||
});
|
||||
|
@@ -10,6 +10,8 @@ import { getAccompanyingCourse,
|
||||
postSocialIssue,
|
||||
addScope,
|
||||
removeScope,
|
||||
getReferrersSuggested,
|
||||
getUsers,
|
||||
} from '../api';
|
||||
import { patchPerson } from "ChillPersonAssets/vuejs/_api/OnTheFly";
|
||||
import { patchThirdparty } from "ChillThirdPartyAssets/vuejs/_api/OnTheFly";
|
||||
@@ -38,6 +40,10 @@ let initPromise = Promise.all([scopesPromise, accompanyingCoursePromise])
|
||||
scopesAtStart: accompanyingCourse.scopes.map(scope => scope),
|
||||
// the scope states at server side
|
||||
scopesAtBackend: accompanyingCourse.scopes.map(scope => scope),
|
||||
// the users which are available for referrer
|
||||
referrersSuggested: [],
|
||||
// all the users available
|
||||
users: [],
|
||||
},
|
||||
getters: {
|
||||
isParticipationValid(state) {
|
||||
@@ -71,7 +77,7 @@ let initPromise = Promise.all([scopesPromise, accompanyingCoursePromise])
|
||||
},
|
||||
mutations: {
|
||||
catchError(state, error) {
|
||||
console.log('### mutation: a new error have been catched and pushed in store !', error);
|
||||
// console.log('### mutation: a new error have been catched and pushed in store !', error);
|
||||
state.errorMsg.push(error);
|
||||
},
|
||||
removeParticipation(state, participation) {
|
||||
@@ -170,6 +176,16 @@ let initPromise = Promise.all([scopesPromise, accompanyingCoursePromise])
|
||||
//console.log('value', value);
|
||||
state.accompanyingCourse.user = value;
|
||||
},
|
||||
setReferrersSuggested(state, users) {
|
||||
state.referrersSuggested = users.map(u => {
|
||||
if (state.accompanyingCourse.user !== null) {
|
||||
if (state.accompanyingCourse.user.id === u.id) {
|
||||
return state.accompanyingCourse.user;
|
||||
}
|
||||
}
|
||||
return u;
|
||||
});
|
||||
},
|
||||
confirmAccompanyingCourse(state, response) {
|
||||
//console.log('### mutation: confirmAccompanyingCourse: response', response);
|
||||
state.accompanyingCourse.step = response.step;
|
||||
@@ -178,6 +194,16 @@ let initPromise = Promise.all([scopesPromise, accompanyingCoursePromise])
|
||||
//console.log('define location context');
|
||||
state.addressContext = context;
|
||||
},
|
||||
setUsers(state, users) {
|
||||
state.users = users.map(u => {
|
||||
if (state.accompanyingCourse.user !== null) {
|
||||
if (state.accompanyingCourse.user.id === u.id) {
|
||||
return state.accompanyingCourse.user;
|
||||
}
|
||||
}
|
||||
return u;
|
||||
});
|
||||
},
|
||||
updateLocation(state, r) {
|
||||
//console.log('### mutation: set location attributes', r);
|
||||
state.accompanyingCourse.locationStatus = r.locationStatus;
|
||||
@@ -272,6 +298,7 @@ let initPromise = Promise.all([scopesPromise, accompanyingCoursePromise])
|
||||
})).catch((error) => { commit('catchError', error) });
|
||||
},
|
||||
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') {
|
||||
@@ -313,10 +340,12 @@ let initPromise = Promise.all([scopesPromise, accompanyingCoursePromise])
|
||||
},
|
||||
toggleEmergency({ commit }, payload) {
|
||||
patchAccompanyingCourse(id, { type: "accompanying_period", emergency: payload })
|
||||
.then(course => new Promise((resolve, reject) => {
|
||||
commit('toggleEmergency', course.emergency);
|
||||
resolve();
|
||||
})).catch((error) => { commit('catchError', error) });
|
||||
.then(course => new Promise((resolve, reject) => {
|
||||
commit('toggleEmergency', course.emergency);
|
||||
return dispatch('setRefererresAvailable');
|
||||
}))
|
||||
.then(() => Promise.resolve())
|
||||
.catch((error) => { commit('catchError', error) });
|
||||
},
|
||||
toggleConfidential({ commit }, payload) {
|
||||
patchAccompanyingCourse(id, { type: "accompanying_period", confidential: payload })
|
||||
@@ -387,11 +416,15 @@ let initPromise = Promise.all([scopesPromise, accompanyingCoursePromise])
|
||||
// check/uncheck in the UI. I do not know of to avoid it.
|
||||
commit('setScopes', scopes);
|
||||
return Promise.resolve();
|
||||
}).then(() => {
|
||||
return dispatch('fetchReferrersSuggested');
|
||||
});
|
||||
} else {
|
||||
return dispatch('setScopes', state.scopesAtStart).then(() => {
|
||||
commit('setScopes', scopes);
|
||||
return Promise.resolve();
|
||||
}).then(() => {
|
||||
return dispatch('fetchReferrersSuggested');
|
||||
});
|
||||
}
|
||||
},
|
||||
@@ -434,7 +467,7 @@ let initPromise = Promise.all([scopesPromise, accompanyingCoursePromise])
|
||||
resolve();
|
||||
})).catch((error) => { commit('catchError', error) });
|
||||
},
|
||||
updateSocialIssues({ state, commit }, { payload, body, method }) {
|
||||
updateSocialIssues({ state, commit, dispatch }, { payload, body, method }) {
|
||||
//console.log('## action: payload', { payload, body, method });
|
||||
postSocialIssue(id, body, method)
|
||||
.then(response => new Promise((resolve, reject) => {
|
||||
@@ -445,6 +478,7 @@ let initPromise = Promise.all([scopesPromise, accompanyingCoursePromise])
|
||||
return getAccompanyingCourse(state.accompanyingCourse.id);
|
||||
}).then(accompanying_course => {
|
||||
commit('refreshSocialIssues', accompanying_course.socialIssues);
|
||||
dispatch('fetchReferrersSuggested');
|
||||
})
|
||||
.catch((error) => { commit('catchError', error) });
|
||||
},
|
||||
@@ -462,7 +496,24 @@ let initPromise = Promise.all([scopesPromise, accompanyingCoursePromise])
|
||||
resolve();
|
||||
})).catch((error) => { commit('catchError', error) });
|
||||
},
|
||||
updateLocation({ commit }, payload) {
|
||||
async fetchReferrersSuggested({ state, commit}) {
|
||||
let users = await getReferrersSuggested(state.accompanyingCourse);
|
||||
commit('setReferrersSuggested', users);
|
||||
if (
|
||||
null === state.accompanyingCourse.user
|
||||
&& !state.accompanyingCourse.confidential
|
||||
&& !state.accompanyingCourse.step === 'DRAFT'
|
||||
&& users.length === 1
|
||||
) {
|
||||
// set the user if unique
|
||||
commit('updateReferrer', users[0]);
|
||||
}
|
||||
},
|
||||
async fetchUsers({commit}) {
|
||||
let users = await getUsers();
|
||||
commit('setUsers', users);
|
||||
},
|
||||
updateLocation({ commit, dispatch }, payload) {
|
||||
//console.log('## action: updateLocation', payload.locationStatusTo);
|
||||
let body = { 'type': payload.target, 'id': payload.targetId };
|
||||
let location = {};
|
||||
@@ -484,6 +535,7 @@ let initPromise = Promise.all([scopesPromise, accompanyingCoursePromise])
|
||||
personLocation: accompanyingCourse.personLocation
|
||||
});
|
||||
resolve();
|
||||
dispatch('fetchReferrersSuggested');
|
||||
})).catch((error) => { commit('catchError', error) });
|
||||
},
|
||||
confirmAccompanyingCourse({ commit }) {
|
||||
@@ -498,6 +550,9 @@ let initPromise = Promise.all([scopesPromise, accompanyingCoursePromise])
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
store.dispatch('fetchReferrersSuggested');
|
||||
store.dispatch('fetchUsers');
|
||||
resolve(store);
|
||||
}));
|
||||
|
||||
|
@@ -9,7 +9,7 @@
|
||||
<p>{{ $t('pick_social_issue_linked_with_action') }}</p>
|
||||
|
||||
<div v-for="si in socialIssues">
|
||||
<input type="radio" v-bind:value="si.id" name="socialIssue" v-model="socialIssuePicked"> {{ si.title.fr }}
|
||||
<input type="radio" v-bind:value="si.id" name="socialIssue" v-model="socialIssuePicked"><span class="badge bg-chill-l-gray text-dark">{{ si.text }}</span>
|
||||
</div>
|
||||
|
||||
<div v-if="hasSocialIssuePicked">
|
||||
@@ -26,7 +26,7 @@
|
||||
</div>
|
||||
|
||||
<div v-if="isLoadingSocialActions">
|
||||
<p>spinner</p>
|
||||
<i class="fa fa-circle-o-notch fa-spin fa-fw"></i>
|
||||
</div>
|
||||
|
||||
<div v-if="hasSocialActionPicked" id="persons">
|
||||
@@ -72,7 +72,7 @@
|
||||
{{ $t('action.save') }}
|
||||
</button>
|
||||
<button class="btn btn-save" v-show="isPostingWork" disabled>
|
||||
{{ $t('Save') }}
|
||||
{{ $t('action.save') }}
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -222,3 +222,16 @@ export default {
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import 'ChillMainAssets/module/bootstrap/shared';
|
||||
@import 'ChillPersonAssets/chill/scss/mixins';
|
||||
@import 'ChillMainAssets/chill/scss/chill_variables';
|
||||
span.badge {
|
||||
@include badge_social($social-issue-color);
|
||||
font-size: 95%;
|
||||
margin-bottom: 5px;
|
||||
margin-right: 1em;
|
||||
margin-left: 1em;
|
||||
}
|
||||
</style>
|
@@ -84,7 +84,7 @@
|
||||
|
||||
<div id="evaluations" class="action-row">
|
||||
<div aria="hidden" class="title">
|
||||
<div><h3>{{ $t('Evaluations') }}</h3></div>
|
||||
<div><h3>{{ $t('Evaluations') }} - {{ $t('Forms') }} - {{ $t('Post') }}</h3></div>
|
||||
</div>
|
||||
|
||||
<!-- list evaluations -->
|
||||
@@ -247,6 +247,8 @@ const i18n = {
|
||||
add_objectif: "Ajouter un motif - objectif - dispositif",
|
||||
add_an_objective: "Ajouter un objectif",
|
||||
Evaluations: "Évaluations",
|
||||
Forms: "Formulaires",
|
||||
Post: "Courriers",
|
||||
add_an_evaluation: "Ajouter une évaluation",
|
||||
persons_involved: "Usagers concernés",
|
||||
handling_thirdparty: "Tiers traitant",
|
||||
|
@@ -20,18 +20,25 @@
|
||||
v-bind:item="item">
|
||||
</suggestion-third-party>
|
||||
|
||||
<suggestion-user
|
||||
v-if="item.result.type === 'user'"
|
||||
v-bind:item="item">
|
||||
</suggestion-user>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import SuggestionPerson from './TypePerson';
|
||||
import SuggestionThirdParty from './TypeThirdParty';
|
||||
import SuggestionUser from './TypeUser';
|
||||
|
||||
export default {
|
||||
name: 'PersonSuggestion',
|
||||
components: {
|
||||
SuggestionPerson,
|
||||
SuggestionThirdParty,
|
||||
SuggestionUser,
|
||||
},
|
||||
props: [
|
||||
'item',
|
||||
|
@@ -0,0 +1,47 @@
|
||||
<template>
|
||||
<div class="container usercontainer">
|
||||
<div class="user-identification">
|
||||
<span class="name">
|
||||
{{ item.result.text }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right_actions">
|
||||
<span class="badge rounded-pill bg-secondary">
|
||||
{{ $t('user')}}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
const i18n = {
|
||||
messages: {
|
||||
fr: {
|
||||
user: 'Utilisateur' // TODO how to define other translations?
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default {
|
||||
name: 'SuggestionUser',
|
||||
props: ['item'],
|
||||
i18n,
|
||||
computed: {
|
||||
hasParent() {
|
||||
return this.$props.item.result.parent !== null;
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.usercontainer {
|
||||
.userparent {
|
||||
.name {
|
||||
font-weight: bold;
|
||||
font-variant: all-small-caps;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@@ -35,7 +35,7 @@
|
||||
</time>
|
||||
|
||||
<time v-else-if="person.birthdate && person.deathdate" :datetime="person.deathdate" :title="person.deathdate">
|
||||
{{ birthdate }} - {{ deathdate }}
|
||||
{{ $d(birthdate) }} - {{ $d(deathdate) }}
|
||||
</time>
|
||||
|
||||
<time v-else-if="person.deathdate" :datetime="person.deathdate" :title="person.deathdate">
|
||||
@@ -97,8 +97,13 @@
|
||||
</li>
|
||||
|
||||
<li v-if="person.center && options.addCenter">
|
||||
<i class="fa fa-li fa-long-arrow-right"></i>
|
||||
{{ person.center.name }}
|
||||
<i class="fa fa-li fa-long-arrow-right"></i>
|
||||
<template v-if="person.center.type !== undefined">
|
||||
{{ person.center.name }}
|
||||
</template>
|
||||
<template v-else>
|
||||
<template v-for="c in person.center">{{ c.name }}</template>
|
||||
</template>
|
||||
</li>
|
||||
<li v-else-if="options.addNoData">
|
||||
<i class="fa fa-li fa-long-arrow-right"></i>
|
||||
@@ -138,11 +143,13 @@
|
||||
<script>
|
||||
import {dateToISO} from 'ChillMainAssets/chill/js/date.js';
|
||||
import AddressRenderBox from 'ChillMainAssets/vuejs/_components/Entity/AddressRenderBox.vue';
|
||||
import Confidential from 'ChillMainAssets/vuejs/_components/Confidential.vue';
|
||||
|
||||
export default {
|
||||
name: "PersonRenderBox",
|
||||
components: {
|
||||
AddressRenderBox
|
||||
AddressRenderBox,
|
||||
Confidential
|
||||
},
|
||||
props: ['person', 'options', 'render', 'returnPath'],
|
||||
computed: {
|
||||
@@ -154,31 +161,24 @@ export default {
|
||||
}
|
||||
},
|
||||
getGenderIcon: function() {
|
||||
return this.person.gender === 'woman' ? 'fa-venus' : this.person.gender === 'man' ? 'fa-mars' : 'fa-neuter';
|
||||
return this.person.gender === 'woman' ? 'fa-venus' : this.person.gender === 'man' ? 'fa-mars' : this.person.gender === 'neuter' ? 'fa-neuter' : 'fa-genderless';
|
||||
},
|
||||
getGenderTranslation: function() {
|
||||
return this.person.gender === 'woman' ? 'renderbox.birthday.woman' : 'renderbox.birthday.man';
|
||||
},
|
||||
getGender() {
|
||||
return this.person.gender === 'woman' ? 'person.gender.woman' : this.person.gender === 'man' ? 'person.gender.man' : 'person.gender.neuter';
|
||||
return this.person.gender === 'woman' ? 'person.gender.woman' : this.person.gender === 'man' ? 'person.gender.man' : this.person.gender === 'neuter' ? 'person.gender.neuter' : 'person.gender.undefined';
|
||||
},
|
||||
birthdate: function(){
|
||||
if(this.person.birthdate !== null){
|
||||
const date = new Date(this.person.birthdate.datetime);
|
||||
return dateToISO(date)
|
||||
if(this.person.birthdate !== null || this.person.birthdate === "undefined"){
|
||||
return new Date(this.person.birthdate.datetime);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
},
|
||||
deathdate: function(){
|
||||
// TODO FIX edition conflict: if null or undefined ?
|
||||
// if (typeof this.person.deathdate !== 'undefined') {
|
||||
// var date = new Date(this.person.deathdate.datetime);
|
||||
// return dateToISO(date);
|
||||
//}
|
||||
if(this.person.deathdate !== null){
|
||||
const date = new Date(this.person.deathdate.datetime);
|
||||
return date.toLocaleDateString("fr-FR");
|
||||
if(this.person.deathdate !== null || this.person.birthdate === "undefined"){
|
||||
return new Date(this.person.deathdate.datetime);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
|
@@ -36,6 +36,7 @@ const personMessages = {
|
||||
woman: "Féminin",
|
||||
man: "Masculin",
|
||||
neuter: "Neutre, non binaire",
|
||||
undefined: "Non renseigné"
|
||||
}
|
||||
|
||||
},
|
||||
|
@@ -15,4 +15,5 @@ chill_person:
|
||||
phonenumber: hidden
|
||||
country_of_birth: hidden
|
||||
marital_status: hidden
|
||||
spoken_languages: hidden
|
||||
spoken_languages: hidden
|
||||
civility: hidden
|
@@ -18,6 +18,7 @@
|
||||
window.accompanyingCourse = {{ json|json_encode|raw }};
|
||||
</script>
|
||||
|
||||
{{ parent() }}
|
||||
{{ encore_entry_script_tags('vue_accourse_work_create') }}
|
||||
{% endblock %}
|
||||
|
||||
|
@@ -0,0 +1,34 @@
|
||||
{% extends "@ChillPerson/AccompanyingCourse/layout.html.twig" %}
|
||||
|
||||
{% set activeRouteKey = 'chill_person_accompanying_period_work_list' %}
|
||||
|
||||
{% block title 'accompanying_course_work.remove'|trans %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="accompanying_course_work-list">
|
||||
<h2 class="badge-title">
|
||||
<span class="title_label">{{ 'accompanying_course_work.action'|trans }}</span>
|
||||
<span class="title_action">{{ work.socialAction|chill_entity_render_string }}</span>
|
||||
</h2>
|
||||
|
||||
<div>
|
||||
<h3>{{ "Associated peoples"|trans }}</h3>
|
||||
<ul>
|
||||
{% for p in work.persons %}
|
||||
{{ p|chill_entity_render_box }}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{{ include('@ChillMain/Util/confirmation_template.html.twig',
|
||||
{
|
||||
'title' : 'accompanying_course_work.remove'|trans,
|
||||
'confirm_question' : 'Are you sure you want to remove this work of the accompanying period %name% ?'|trans({ '%name%' : accompanyingCourse.id } ),
|
||||
'cancel_route' : 'chill_person_accompanying_period_work_list',
|
||||
'cancel_parameters' : {'id' : accompanyingCourse.id},
|
||||
'form' : delete_form
|
||||
} ) }}
|
||||
{% endblock %}
|
@@ -20,14 +20,6 @@
|
||||
|
||||
<div class="timeline">
|
||||
<ul>
|
||||
<li class="completed">
|
||||
<div class="date">
|
||||
<span>{{ w.createdAt|format_date('long') }}</span>
|
||||
</div>
|
||||
<div class="label">
|
||||
<span>{{ 'accompanying_course_work.create_date'|trans }}</span>
|
||||
</div>
|
||||
</li>
|
||||
<li class="completed">
|
||||
<div class="date">
|
||||
<span>{{ w.startDate|format_date('long') }}</span>
|
||||
@@ -36,6 +28,11 @@
|
||||
<span>{{ 'accompanying_course_work.start_date'|trans }}</span>
|
||||
</div>
|
||||
</li>
|
||||
{% if w.endDate == null %}
|
||||
<li>
|
||||
<div class="label no-label"></div>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="{%if date(w.endDate) < date('now') %}completed{% endif %}">
|
||||
<div class="date">
|
||||
<span>{{ w.endDate|format_date('long') }}</span>
|
||||
@@ -44,6 +41,7 @@
|
||||
<span>{{ 'accompanying_course_work.end_date'|trans }}</span>
|
||||
</div>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -105,6 +103,11 @@
|
||||
href="{{ chill_path_add_return_path('chill_person_accompanying_period_work_edit', { 'id': w.id }) }}"
|
||||
>{% if buttonText is not defined or buttonText == true %}{{ 'Edit'|trans }}{% endif %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="btn btn-delete" title="{{ 'Delete'|trans }}"
|
||||
href="{{ path('chill_person_accompanying_period_work_delete', { 'id': w.id } ) }}"
|
||||
>{% if buttonText is not defined or buttonText == true %}{{ 'Delete'|trans }}{% endif %}</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
@@ -77,7 +77,7 @@
|
||||
<div class="wl-row">
|
||||
<div class="wl-col title"><h3>{{ 'Participants'|trans }}</h3></div>
|
||||
<div class="wl-col list">
|
||||
{% for p in accompanying_period.participations %}
|
||||
{% for p in accompanying_period.getCurrentParticipations %}
|
||||
<span class="wl-item badge-person">
|
||||
<a href="{{ path('chill_person_accompanying_period_list', { person_id: p.person.id }) }}">
|
||||
{{ p.person|chill_entity_render_string }}
|
||||
|
@@ -62,15 +62,15 @@
|
||||
{%- endif -%}
|
||||
{%- if options['addId'] -%}
|
||||
<span class="id-number" title="{{ 'Person'|trans ~ ' n° ' ~ person.id }}">
|
||||
{{ person.id|upper }}
|
||||
{{ person.id|upper -}}
|
||||
</span>
|
||||
{%- endif -%}
|
||||
</div>
|
||||
{%- if options['addInfo'] -%}
|
||||
{% set gender = (person.gender == 'woman') ? 'fa-venus' :
|
||||
(person.gender == 'man') ? 'fa-mars' : 'fa-neuter' %}
|
||||
(person.gender == 'man') ? 'fa-mars' : (person.gender == 'neuter') ? 'fa-neuter' : 'fa-genderless' %}
|
||||
{% set genderTitle = (person.gender == 'woman') ? 'woman' :
|
||||
(person.gender == 'man') ? 'man' : 'neuter' %}
|
||||
(person.gender == 'man') ? 'man' : (person.gender == 'neuter') ? 'neuter' : 'Not given'|trans %}
|
||||
<p class="moreinfo">
|
||||
<i class="fa fa-fw {{ gender }}" title="{{ genderTitle|trans }}"></i>
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
</time>
|
||||
{%- if options['addAge'] -%}
|
||||
<span class="age">
|
||||
({{ 'years_old'|trans({ 'age': person.age }) }})
|
||||
{{- 'years_old'|trans({ 'age': person.age }) -}}
|
||||
</span>
|
||||
{%- endif -%}
|
||||
{%- endif -%}
|
||||
@@ -123,13 +123,15 @@
|
||||
</div>
|
||||
<div class="item-col">
|
||||
<ul class="list-content fa-ul">
|
||||
{% set multiline = (options['address_multiline']) ? true : false %}
|
||||
{{ person.getLastAddress|chill_entity_render_box({
|
||||
'render': 'list',
|
||||
'with_picto': true,
|
||||
'multiline': multiline,
|
||||
'with_valid_from': false
|
||||
}) }}
|
||||
{% if person.getCurrentPersonAddress is not null %}
|
||||
{% set multiline = (options['address_multiline']) ? true : false %}
|
||||
{{ person.getCurrentPersonAddress|chill_entity_render_box({
|
||||
'render': 'list',
|
||||
'with_picto': true,
|
||||
'multiline': multiline,
|
||||
'with_valid_from': false
|
||||
}) }}
|
||||
{% endif %}
|
||||
<li>
|
||||
{% if person.mobilenumber %}
|
||||
<i class="fa fa-li fa-mobile"></i><a href="{{ 'tel:' ~ person.mobilenumber }}">
|
||||
|
@@ -9,6 +9,37 @@
|
||||
|
||||
{% include 'ChillPersonBundle:AccompanyingPeriod:_list.html.twig' %}
|
||||
|
||||
{% if accompanying_periods_old|length > 0 %}
|
||||
<style>
|
||||
button[aria-expanded="true"] > span.folded,
|
||||
button[aria-expanded="false"] > span.unfolded { display: none; }
|
||||
button[aria-expanded="false"] > span.folded,
|
||||
button[aria-expanded="true"] > span.unfolded { display: inline; }
|
||||
</style>
|
||||
<div class="accordion" id="nonCurrent">
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="heading_{{ household.id }}">
|
||||
<button
|
||||
class="accordion-button collapsed"
|
||||
type="button"
|
||||
data-bs-toggle="collapse"
|
||||
data-bs-target="#collapse_{{ household.id }}"
|
||||
aria-expanded="false"
|
||||
aria-controls="collapse_{{ household.id }}">
|
||||
<span class="folded">{{ 'household.Show accompanying periods of past or future memberships'|trans({'length': accompanying_periods_old|length}) }}</span>
|
||||
<span class="unfolded text-secondary">{{ 'household.Hide memberships'|trans }}</span>
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapse_{{ household.id }}"
|
||||
class="accordion-collapse collapse"
|
||||
aria-labelledby="heading_{{ household.id }}"
|
||||
data-bs-parent="#nonCurrent">
|
||||
{% include 'ChillPersonBundle:AccompanyingPeriod:_list.html.twig' with {'accompanying_periods' : accompanying_periods_old} %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<ul class="record_actions sticky-form-buttons">
|
||||
<li class="cancel">
|
||||
<a href="{{ path ('chill_person_household_summary', {'household_id' : household.id } ) }}" class="btn btn-cancel">
|
||||
|
@@ -22,8 +22,11 @@
|
||||
|
||||
{%- for m in members -%}
|
||||
<span
|
||||
class="badge-member{%- if m.holder %} holder{% endif -%}{%- if m.position.ordering >= 2 %} child{% endif -%}"
|
||||
title="{{ m.position.label.fr }}">
|
||||
class="badge-member{%- if m.holder %} holder{% endif -%}"
|
||||
{% if m.position is not null %}
|
||||
title="{{ m.position.label.fr }}"
|
||||
{% endif %}
|
||||
>
|
||||
<a href="{{ path('chill_person_view', { person_id: m.person.id}) }}">
|
||||
{%- if m.holder %}
|
||||
<span class="fa-stack fa-holder" title="{{ 'household.holder'|trans }}">
|
||||
|
@@ -106,81 +106,95 @@
|
||||
|
||||
<h2 class="my-5">{{ 'household.Household members'|trans }}</h2>
|
||||
|
||||
{% for p in positions %}
|
||||
<div class="mb-5">
|
||||
<h3>{{ p.label|localize_translatable_string }}
|
||||
{% if false == p.shareHousehold %}
|
||||
<i class="chill-help-tooltip" data-bs-toggle="tooltip" data-bs-placement="top" data-bs-html="true"
|
||||
title="{{ 'household.Those members does not share address'|trans }}"></i>
|
||||
{% endif %}
|
||||
</h3>
|
||||
{% for p in positions|merge([ '_none' ]) %}
|
||||
|
||||
{%- set members = household.currentMembersByPosition(p) %}
|
||||
{% if p == '_none' %}
|
||||
{% set members = household.currentMembersWithoutPosition %}
|
||||
{% set old_members = household.nonCurrentMembersWithoutPosition %}
|
||||
{% else %}
|
||||
{%- set members = household.currentMembersByPosition(p) %}
|
||||
{% set old_members = household.nonCurrentMembersByPosition(p) %}
|
||||
{% endif %}
|
||||
|
||||
{% macro customButtons(member, household) %}
|
||||
<li>
|
||||
<a href="{{ chill_path_add_return_path('chill_person_household_members_editor', {'persons': [ member.person.id ], 'allow_leave_without_household': true } ) }}"
|
||||
class="btn btn-sm btn-unlink" title="{{ 'household.person.leave'|trans }}"></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ chill_path_add_return_path('chill_person_household_members_editor', {'persons': [ member.person.id ], 'household': household.id} ) }}"
|
||||
class="btn btn-sm btn-misc" title="{{ 'household.Change position'|trans }}"><i class="fa fa-arrows-h"></i></a>
|
||||
</li>
|
||||
{% endmacro %}
|
||||
{% if not (p == '_none' and members|length == 0 and old_members|length == 0) %}
|
||||
<div class="mb-5">
|
||||
{% if p != '_none' %}
|
||||
<h3>{{ p.label|localize_translatable_string }}
|
||||
{% if false == p.shareHousehold %}
|
||||
<i class="chill-help-tooltip" data-bs-toggle="tooltip" data-bs-placement="top" data-bs-html="true"
|
||||
title="{{ 'household.Those members does not share address'|trans }}"></i>
|
||||
{% endif %}
|
||||
</h3>
|
||||
{% else %}
|
||||
<h3 class="chill-entity">{{ 'household.Members without position'|trans }}</h3>
|
||||
{% endif %}
|
||||
|
||||
{% if members|length > 0 %}
|
||||
<div class="flex-table list-household-members">
|
||||
{% for m in members %}
|
||||
{% include '@ChillPerson/Household/_render_member.html.twig' with {
|
||||
'member': m,
|
||||
'customButtons': { 'after': _self.customButtons(m, household) }
|
||||
} %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="chill-no-data-statement">{{ 'household.Any persons into this position'|trans }}</p>
|
||||
{% endif %}
|
||||
|
||||
{% set members = household.nonCurrentMembersByPosition(p) %}
|
||||
{% if members|length > 0 %}
|
||||
{% macro customButtons(member, household) %}
|
||||
<li>
|
||||
<a href="{{ chill_path_add_return_path('chill_person_household_members_editor', {'persons': [ member.person.id ], 'allow_leave_without_household': true } ) }}"
|
||||
class="btn btn-sm btn-unlink" title="{{ 'household.person.leave'|trans }}"></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ chill_path_add_return_path('chill_person_household_members_editor', {'persons': [ member.person.id ], 'household': household.id} ) }}"
|
||||
class="btn btn-sm btn-misc" title="{{ 'household.Change position'|trans }}"><i class="fa fa-arrows-h"></i></a>
|
||||
</li>
|
||||
{% endmacro %}
|
||||
|
||||
<style>
|
||||
button[aria-expanded="true"] > span.folded,
|
||||
button[aria-expanded="false"] > span.unfolded { display: none; }
|
||||
button[aria-expanded="false"] > span.folded,
|
||||
button[aria-expanded="true"] > span.unfolded { display: inline; }
|
||||
</style>
|
||||
<div class="accordion" id="nonCurrent">
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="heading_{{ p.id }}">
|
||||
<button
|
||||
class="accordion-button collapsed"
|
||||
type="button"
|
||||
data-bs-toggle="collapse"
|
||||
data-bs-target="#collapse_{{ p.id }}"
|
||||
aria-expanded="false"
|
||||
aria-controls="collapse_{{ p.id }}">
|
||||
<span class="folded">{{ 'household.Show future or past memberships'|trans({'length': members|length}) }}</span>
|
||||
<span class="unfolded text-secondary">{{ 'household.Hide memberships'|trans }}</span>
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapse_{{ p.id }}"
|
||||
class="accordion-collapse collapse"
|
||||
aria-labelledby="heading_{{ p.id }}"
|
||||
data-bs-parent="#nonCurrent">
|
||||
<div class="flex-table my-0 list-household-members">
|
||||
{% for m in members %}
|
||||
{% include '@ChillPerson/Household/_render_member.html.twig' with { 'member': m } %}
|
||||
{% endfor %}
|
||||
{% if members|length > 0 %}
|
||||
<div class="flex-table list-household-members">
|
||||
{% for m in members %}
|
||||
{% include '@ChillPerson/Household/_render_member.html.twig' with {
|
||||
'member': m,
|
||||
'customButtons': { 'after': _self.customButtons(m, household) }
|
||||
} %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="chill-no-data-statement">{{ 'household.Any persons into this position'|trans }}</p>
|
||||
{% endif %}
|
||||
|
||||
{% if old_members|length > 0 %}
|
||||
<style>
|
||||
button[aria-expanded="true"] > span.folded,
|
||||
button[aria-expanded="false"] > span.unfolded { display: none; }
|
||||
button[aria-expanded="false"] > span.folded,
|
||||
button[aria-expanded="true"] > span.unfolded { display: inline; }
|
||||
</style>
|
||||
<div class="accordion" id="nonCurrent">
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="heading_{{ p == '_none' ? '_none' : p.id }}">
|
||||
<button
|
||||
class="accordion-button collapsed"
|
||||
type="button"
|
||||
data-bs-toggle="collapse"
|
||||
data-bs-target="#collapse_{{ p == '_none' ? '_none' : p.id }}"
|
||||
aria-expanded="false"
|
||||
aria-controls="collapse_{{ p == '_none' ? '_none' : p.id }}">
|
||||
<span class="folded">{{ 'household.Show future or past memberships'|trans({'length': old_members|length}) }}</span>
|
||||
<span class="unfolded text-secondary">{{ 'household.Hide memberships'|trans }}</span>
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapse_{{ p == '_none' ? '_none' : p.id }}"
|
||||
class="accordion-collapse collapse"
|
||||
aria-labelledby="heading_{{ p == '_none' ? '_none' : p.id }}"
|
||||
data-bs-parent="#nonCurrent">
|
||||
<div class="flex-table my-0 list-household-members">
|
||||
{% for m in old_members %}
|
||||
{% include '@ChillPerson/Household/_render_member.html.twig' with { 'member': m } %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
|
||||
|
||||
<ul class="record_actions">
|
||||
<li>
|
||||
<a href="{{ chill_path_add_return_path('chill_person_household_members_editor', {'household': household.id }) }}"
|
||||
|
@@ -37,6 +37,9 @@
|
||||
|
||||
<fieldset>
|
||||
<legend><h2>{{ 'General information'|trans }}</h2></legend>
|
||||
{%- if form.civility is defined -%}
|
||||
{{ form_row(form.civility, {'label' : 'Civility'}) }}
|
||||
{% endif %}
|
||||
{{ form_row(form.firstName, {'label' : 'First name'}) }}
|
||||
{{ form_row(form.lastName, {'label' : 'Last name'}) }}
|
||||
{% if form.altNames is defined %}
|
||||
@@ -95,6 +98,8 @@
|
||||
<div id="personEmail">
|
||||
{{ form_row(form.email, {'label': 'Email'}) }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{%- if form.acceptEmail is defined -%}
|
||||
<div id="personAcceptEmail">
|
||||
{{ form_row(form.acceptEmail, {'label' : 'Accept emails ?'}) }}
|
||||
</div>
|
||||
@@ -135,12 +140,11 @@
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
{{ form_end(form) }}
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
{{ encore_entry_link_tags('page_person') }}
|
||||
{{ encore_entry_script_tags('page_person') }}
|
||||
{% endblock %}
|
||||
|
@@ -53,7 +53,7 @@
|
||||
'addLink': true,
|
||||
'addInfo': true,
|
||||
'addAge': true,
|
||||
'addAltNames': false,
|
||||
'addAltNames': true,
|
||||
'addCenter': true,
|
||||
'address_multiline': false,
|
||||
'customButtons': { 'after': _self.button_person(person) }
|
||||
@@ -120,39 +120,6 @@
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<ul class="record_actions">
|
||||
{% if is_granted('CHILL_PERSON_CREATE') %}
|
||||
<li>
|
||||
<a href="{{ path('chill_person_new') }}" class="btn btn-create">
|
||||
{{ 'Add a person'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if search_name != "person_similarity" %}
|
||||
<li>
|
||||
<a href="{{ path('chill_main_advanced_search', { "name": search_name, "q": pattern } ) }}" class="btn btn-action">
|
||||
<i class="fa fa-fw fa-search" aria-hidden="true"></i> {{ 'Advanced search'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if preview == true and persons|length < total %}
|
||||
<li>
|
||||
<a href="{{ path('chill_main_search', { "name": search_name|default('abcd'), "q" : pattern }) }}" class="btn btn-misc">
|
||||
{{ 'See all results'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
{% else %}
|
||||
|
||||
<ul class="record_actions">
|
||||
<li>
|
||||
<a href="{{ path('chill_main_advanced_search', { "name": search_name, "q": pattern } ) }}" class="btn btn-action">
|
||||
<i class="fa fa-fw fa-search" aria-hidden="true"></i> {{ 'Advanced search'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
{% if preview == false %}
|
||||
|
@@ -51,6 +51,15 @@ This view should receive those arguments:
|
||||
<figure class="person-details">
|
||||
<h2 class="chill-red">{{ 'General information'|trans }}</h2>
|
||||
<dl>
|
||||
{% if person.civility is not null %}
|
||||
<dt>{{ 'Civility'|trans }} :</dt>
|
||||
<dd>
|
||||
{% if person.civility.name|length > 0 %}
|
||||
{{ person.civility.name|first }}
|
||||
{% endif %}
|
||||
</dd>
|
||||
{% endif %}
|
||||
|
||||
<dt>{{ 'First name'|trans }} :</dt>
|
||||
<dd>{{ person.firstName }}</dd>
|
||||
|
||||
@@ -93,14 +102,14 @@ This view should receive those arguments:
|
||||
{%- endif -%}
|
||||
|
||||
{%- if chill_person.fields.country_of_birth == 'visible' -%}
|
||||
<dt>{{ 'Country of birth'|trans }} :</dt>
|
||||
<dd>{% apply spaceless %}
|
||||
{% if person.countryOfBirth is not null %}
|
||||
{{ person.countryOfBirth.name|localize_translatable_string }}
|
||||
{% else %}
|
||||
<span class="chill-no-data-statement">{{ 'Unknown country of birth'|trans }}</span>
|
||||
{% endif %}
|
||||
{% endapply %}</dd>
|
||||
<dt>{{ 'Country of birth'|trans }} :</dt>
|
||||
<dd>{% apply spaceless %}
|
||||
{% if person.countryOfBirth is not null %}
|
||||
{{ person.countryOfBirth.name|localize_translatable_string }}
|
||||
{% else %}
|
||||
<span class="chill-no-data-statement">{{ 'Unknown country of birth'|trans }}</span>
|
||||
{% endif %}
|
||||
{% endapply %}</dd>
|
||||
{%- endif -%}
|
||||
</dl>
|
||||
</figure>
|
||||
@@ -198,10 +207,10 @@ This view should receive those arguments:
|
||||
{%- endif -%}
|
||||
|
||||
{%- if chill_person.fields.phonenumber == 'visible' -%}
|
||||
<dl>
|
||||
<dt>{{ 'Phonenumber'|trans }} :</dt>
|
||||
<dd>{% if person.phonenumber is not empty %}<a href="tel:{{ person.phonenumber }}"><pre>{{ person.phonenumber|chill_format_phonenumber }}</pre></a>{% else %}<span class="chill-no-data-statement">{{ 'No data given'|trans }}{% endif %}</dd>
|
||||
</dl>
|
||||
<dl>
|
||||
<dt>{{ 'Phonenumber'|trans }} :</dt>
|
||||
<dd>{% if person.phonenumber is not empty %}<a href="tel:{{ person.phonenumber }}"><pre>{{ person.phonenumber|chill_format_phonenumber }}</pre></a>{% else %}<span class="chill-no-data-statement">{{ 'No data given'|trans }}{% endif %}</dd>
|
||||
</dl>
|
||||
{% endif %}
|
||||
|
||||
{%- if chill_person.fields.mobilenumber == 'visible' -%}
|
||||
@@ -261,4 +270,4 @@ This view should receive those arguments:
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
@@ -263,8 +263,9 @@ class PersonSearch extends AbstractSearch implements HasAdvancedSearchFormInterf
|
||||
|
||||
public function convertTermsToFormData(array $terms)
|
||||
{
|
||||
foreach(['firstname', 'lastname', 'gender', '_default']
|
||||
as $key) {
|
||||
$data = [];
|
||||
|
||||
foreach(['firstname', 'lastname', 'gender', '_default'] as $key) {
|
||||
$data[$key] = $terms[$key] ?? null;
|
||||
}
|
||||
|
||||
|
@@ -19,6 +19,7 @@
|
||||
namespace Chill\PersonBundle\Serializer\Normalizer;
|
||||
|
||||
use Chill\MainBundle\Entity\Center;
|
||||
use Chill\MainBundle\Security\Resolver\CenterResolverDispatcher;
|
||||
use Chill\PersonBundle\Entity\Household\Household;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface;
|
||||
@@ -48,16 +49,22 @@ class PersonNormalizer implements
|
||||
|
||||
private PersonRepository $repository;
|
||||
|
||||
private CenterResolverDispatcher $centerResolverDispatcher;
|
||||
|
||||
use NormalizerAwareTrait;
|
||||
|
||||
use ObjectToPopulateTrait;
|
||||
|
||||
use DenormalizerAwareTrait;
|
||||
|
||||
public function __construct(ChillEntityRenderExtension $render, PersonRepository $repository)
|
||||
{
|
||||
public function __construct(
|
||||
ChillEntityRenderExtension $render,
|
||||
PersonRepository $repository,
|
||||
CenterResolverDispatcher $centerResolverDispatcher
|
||||
) {
|
||||
$this->render = $render;
|
||||
$this->repository = $repository;
|
||||
$this->centerResolverDispatcher = $centerResolverDispatcher;
|
||||
}
|
||||
|
||||
public function normalize($person, string $format = null, array $context = array())
|
||||
@@ -74,7 +81,7 @@ class PersonNormalizer implements
|
||||
'lastName' => $person->getLastName(),
|
||||
'birthdate' => $this->normalizer->normalize($person->getBirthdate()),
|
||||
'deathdate' => $this->normalizer->normalize($person->getDeathdate()),
|
||||
'center' => $this->normalizer->normalize($person->getCenter()),
|
||||
'center' => $this->normalizer->normalize($this->centerResolverDispatcher->resolveCenter($person)),
|
||||
'phonenumber' => $person->getPhonenumber(),
|
||||
'mobilenumber' => $person->getMobilenumber(),
|
||||
'altNames' => $this->normalizeAltNames($person->getAltNames()),
|
||||
|
@@ -38,7 +38,7 @@ class SocialActionRender implements ChillEntityRenderInterface
|
||||
{
|
||||
/** @var $socialAction SocialAction */
|
||||
$options = \array_merge(self::DEFAULT_ARGS, $options);
|
||||
$titles[] = $this->translatableStringHelper->localize($socialAction->getTitle());
|
||||
$titles = [$this->translatableStringHelper->localize($socialAction->getTitle())];
|
||||
|
||||
while ($socialAction->hasParent()) {
|
||||
$socialAction = $socialAction->getParent();
|
||||
|
@@ -38,8 +38,7 @@ final class SocialIssueRender implements ChillEntityRenderInterface
|
||||
/** @var $socialIssue SocialIssue */
|
||||
$options = array_merge(self::DEFAULT_ARGS, $options);
|
||||
|
||||
$titles[] = $this->translatableStringHelper
|
||||
->localize($socialIssue->getTitle());
|
||||
$titles = [$this->translatableStringHelper->localize($socialIssue->getTitle())];
|
||||
|
||||
// loop to parent, until root
|
||||
while ($socialIssue->hasParent()) {
|
||||
|
@@ -325,6 +325,19 @@ class AccompanyingCourseApiControllerTest extends WebTestCase
|
||||
$this->period = $period;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider dataGenerateRandomAccompanyingCourse
|
||||
*/
|
||||
public function testReferralAvailable(int $personId, int $periodId)
|
||||
{
|
||||
$this->client->request(
|
||||
Request::METHOD_POST,
|
||||
sprintf('/api/1.0/person/accompanying-course/%d/referrers-suggested.json', $periodId)
|
||||
);
|
||||
|
||||
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @dataProvider dataGenerateRandomAccompanyingCourse
|
||||
|
@@ -193,8 +193,6 @@ class HouseholdMemberControllerTest extends WebTestCase
|
||||
|
||||
$form = $crawler->selectButton('Enregistrer')
|
||||
->form();
|
||||
$form['household_member[endDate]'] = (new \DateTime('tomorrow'))
|
||||
->format('Y-m-d');
|
||||
|
||||
$crawler = $client->submit($form);
|
||||
|
||||
|
@@ -2,8 +2,8 @@
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* Copyright (C) 2014-2015, Champs Libres Cooperative SCRLFS,
|
||||
*
|
||||
* Copyright (C) 2014-2015, Champs Libres Cooperative SCRLFS,
|
||||
* <http://www.champs-libres.coop>, <info@champs-libres.coop>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
@@ -38,57 +38,57 @@ use Generator;
|
||||
class PersonTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
/**
|
||||
* Test the creation of an accompanying, its closure and the access to
|
||||
* Test the creation of an accompanying, its closure and the access to
|
||||
* the current accompaniying period via the getCurrentAccompanyingPeriod
|
||||
* function.
|
||||
*/
|
||||
public function testGetCurrentAccompanyingPeriod()
|
||||
{
|
||||
$d = new \DateTime('yesterday');
|
||||
$d = new \DateTime('yesterday');
|
||||
$p = new Person();
|
||||
$p->addAccompanyingPeriod(new AccompanyingPeriod($d));
|
||||
|
||||
|
||||
$period = $p->getCurrentAccompanyingPeriod();
|
||||
|
||||
|
||||
$this->assertInstanceOf(AccompanyingPeriod::class, $period);
|
||||
$this->assertTrue($period->isOpen());
|
||||
$this->assertEquals($d, $period->getOpeningDate());
|
||||
|
||||
|
||||
//close and test
|
||||
$period->setClosingDate(new \DateTime('tomorrow'));
|
||||
|
||||
|
||||
$shouldBeNull = $p->getCurrentAccompanyingPeriod();
|
||||
$this->assertNull($shouldBeNull);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test if the getAccompanyingPeriodsOrdered function return a list of
|
||||
* periods ordered ascendency.
|
||||
*/
|
||||
public function testAccompanyingPeriodOrderWithUnorderedAccompanyingPeriod()
|
||||
{
|
||||
{
|
||||
$d = new \DateTime("2013/2/1");
|
||||
$p = new Person();
|
||||
$p->addAccompanyingPeriod(new AccompanyingPeriod($d));
|
||||
|
||||
|
||||
$e = new \DateTime("2013/3/1");
|
||||
$period = $p->getCurrentAccompanyingPeriod()->setClosingDate($e);
|
||||
$p->close($period);
|
||||
|
||||
|
||||
$f = new \DateTime("2013/1/1");
|
||||
$p->open(new AccompanyingPeriod($f));
|
||||
|
||||
$g = new \DateTime("2013/4/1");
|
||||
|
||||
$g = new \DateTime("2013/4/1");
|
||||
$period = $p->getCurrentAccompanyingPeriod()->setClosingDate($g);
|
||||
$p->close($period);
|
||||
|
||||
|
||||
$r = $p->getAccompanyingPeriodsOrdered();
|
||||
|
||||
|
||||
$date = $r[0]->getOpeningDate()->format('Y-m-d');
|
||||
|
||||
|
||||
$this->assertEquals($date, '2013-01-01');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test if the getAccompanyingPeriodsOrdered function, for periods
|
||||
* starting at the same time order regarding to the closing date.
|
||||
@@ -97,25 +97,25 @@ class PersonTest extends \PHPUnit\Framework\TestCase
|
||||
$d = new \DateTime("2013/2/1");
|
||||
$p = new Person();
|
||||
$p->addAccompanyingPeriod(new AccompanyingPeriod($d));
|
||||
|
||||
$g = new \DateTime("2013/4/1");
|
||||
|
||||
$g = new \DateTime("2013/4/1");
|
||||
$period = $p->getCurrentAccompanyingPeriod()->setClosingDate($g);
|
||||
$p->close($period);
|
||||
|
||||
|
||||
$f = new \DateTime("2013/2/1");
|
||||
$p->open(new AccompanyingPeriod($f));
|
||||
|
||||
|
||||
$e = new \DateTime("2013/3/1");
|
||||
$period = $p->getCurrentAccompanyingPeriod()->setClosingDate($e);
|
||||
$p->close($period);
|
||||
|
||||
$r = $p->getAccompanyingPeriodsOrdered();
|
||||
|
||||
|
||||
$date = $r[0]->getClosingDate()->format('Y-m-d');
|
||||
|
||||
|
||||
$this->assertEquals($date, '2013-03-01');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test if the function checkAccompanyingPeriodIsNotCovering returns
|
||||
* the good constant when two periods are collapsing : a period
|
||||
@@ -125,23 +125,23 @@ class PersonTest extends \PHPUnit\Framework\TestCase
|
||||
$d = new \DateTime("2013/2/1");
|
||||
$p = new Person();
|
||||
$p->addAccompanyingPeriod(new AccompanyingPeriod($d));
|
||||
|
||||
|
||||
$e = new \DateTime("2013/3/1");
|
||||
$period = $p->getCurrentAccompanyingPeriod()->setClosingDate($e);
|
||||
$p->close($period);
|
||||
|
||||
|
||||
$f = new \DateTime("2013/1/1");
|
||||
$p->open(new AccompanyingPeriod($f));
|
||||
|
||||
$g = new \DateTime("2013/4/1");
|
||||
|
||||
$g = new \DateTime("2013/4/1");
|
||||
$period = $p->getCurrentAccompanyingPeriod()->setClosingDate($g);
|
||||
$p->close($period);
|
||||
|
||||
|
||||
$r = $p->checkAccompanyingPeriodsAreNotCollapsing();
|
||||
|
||||
|
||||
$this->assertEquals($r['result'], Person::ERROR_PERIODS_ARE_COLLAPSING);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test if the function checkAccompanyingPeriodIsNotCovering returns
|
||||
* the good constant when two periods are collapsing : a period is open
|
||||
@@ -151,68 +151,19 @@ class PersonTest extends \PHPUnit\Framework\TestCase
|
||||
$d = new \DateTime("2013/2/1");
|
||||
$p = new Person();
|
||||
$p->addAccompanyingPeriod(new AccompanyingPeriod($d));
|
||||
|
||||
|
||||
$e = new \DateTime("2013/3/1");
|
||||
$period = $p->getCurrentAccompanyingPeriod()->setClosingDate($e);
|
||||
$p->close($period);
|
||||
|
||||
|
||||
$f = new \DateTime("2013/1/1");
|
||||
$p->open(new AccompanyingPeriod($f));
|
||||
|
||||
|
||||
$r = $p->checkAccompanyingPeriodsAreNotCollapsing();
|
||||
|
||||
|
||||
$this->assertEquals($r['result'], Person::ERROR_ADDIND_PERIOD_AFTER_AN_OPEN_PERIOD);
|
||||
}
|
||||
|
||||
public function dateProvider(): Generator
|
||||
{
|
||||
yield [(DateTime::createFromFormat('Y-m-d', '2021-01-05'))->settime(0, 0)];
|
||||
yield [(DateTime::createFromFormat('Y-m-d', '2021-02-05'))->settime(0, 0)];
|
||||
yield [(DateTime::createFromFormat('Y-m-d', '2021-03-05'))->settime(0, 0)];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider dateProvider
|
||||
*/
|
||||
public function testGetLastAddress(DateTime $date)
|
||||
{
|
||||
$p = new Person($date);
|
||||
|
||||
// Make sure that there is no last address.
|
||||
$this::assertNull($p->getLastAddress());
|
||||
|
||||
// Take an arbitrary date before the $date in parameter.
|
||||
$addressDate = clone $date;
|
||||
|
||||
// 1. Smoke test: Test that the first address added is the last one.
|
||||
$address1 = (new Address())->setValidFrom($addressDate->sub(new DateInterval('PT180M')));
|
||||
$p->addAddress($address1);
|
||||
|
||||
$this::assertCount(1, $p->getAddresses());
|
||||
$this::assertSame($address1, $p->getLastAddress());
|
||||
|
||||
// 2. Add an older address, which should not be the last address.
|
||||
$addressDate2 = clone $addressDate;
|
||||
$address2 = (new Address())->setValidFrom($addressDate2->sub(new DateInterval('PT30M')));
|
||||
$p->addAddress($address2);
|
||||
|
||||
$this::assertCount(2, $p->getAddresses());
|
||||
$this::assertSame($address1, $p->getLastAddress());
|
||||
|
||||
// 3. Add a newer address, which should be the last address.
|
||||
$addressDate3 = clone $addressDate;
|
||||
$address3 = (new Address())->setValidFrom($addressDate3->add(new DateInterval('PT30M')));
|
||||
$p->addAddress($address3);
|
||||
|
||||
$this::assertCount(3, $p->getAddresses());
|
||||
$this::assertSame($address3, $p->getLastAddress());
|
||||
|
||||
// 4. Get the last address from a specific date.
|
||||
$this::assertEquals($address1, $p->getLastAddress($addressDate));
|
||||
$this::assertEquals($address2, $p->getLastAddress($addressDate2));
|
||||
$this::assertEquals($address3, $p->getLastAddress($addressDate3));
|
||||
}
|
||||
|
||||
public function testIsSharingHousehold()
|
||||
{
|
||||
$person = new Person();
|
||||
|
@@ -11,7 +11,7 @@ class LocationValidity extends Constraint
|
||||
{
|
||||
public $messagePersonLocatedMustBeAssociated = "The person where the course is located must be associated to the course. Change course's location before removing the person.";
|
||||
|
||||
public $messagePeriodMustRemainsLocated = "The period must remains located";
|
||||
public $messagePeriodMustRemainsLocated = "The period must remain located";
|
||||
|
||||
public function getTargets()
|
||||
{
|
||||
|
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\PersonBundle\Validator\Constraints\AccompanyingPeriod;
|
||||
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
*/
|
||||
class ParticipationOverlap extends Constraint
|
||||
{
|
||||
public $message = 'This participation already exists.';
|
||||
}
|
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\PersonBundle\Validator\Constraints\AccompanyingPeriod;
|
||||
|
||||
use Chill\MainBundle\Util\DateRangeCovering;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation;
|
||||
use Chill\PersonBundle\Validator\Constraints\AccompanyingPeriod\ParticipationOverlap;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
||||
|
||||
class ParticipationOverlapValidator extends ConstraintValidator
|
||||
{
|
||||
private const MAX_PARTICIPATION = 1;
|
||||
|
||||
public function validate($participations, Constraint $constraint)
|
||||
{
|
||||
if (!$constraint instanceof ParticipationOverlap) {
|
||||
throw new UnexpectedTypeException($constraint, ParticipationOverlap::class);
|
||||
}
|
||||
|
||||
if (!$participations instanceof Collection) {
|
||||
throw new UnexpectedTypeException($participations, 'This should be a collection');
|
||||
}
|
||||
|
||||
if (count($participations) <= self::MAX_PARTICIPATION) {
|
||||
return;
|
||||
}
|
||||
|
||||
$overlaps = new DateRangeCovering(self::MAX_PARTICIPATION, $participations[0]->getStartDate()->getTimezone());
|
||||
|
||||
foreach ($participations as $participation) {
|
||||
|
||||
if (!$participation instanceof AccompanyingPeriodParticipation) {
|
||||
throw new UnexpectedTypeException($participation, AccompanyingPeriodParticipation::class);
|
||||
}
|
||||
|
||||
$personId = $participation->getPerson()->getId();
|
||||
|
||||
$particpationList[$personId][] = $participation;
|
||||
|
||||
}
|
||||
|
||||
foreach ($particpationList as $group) {
|
||||
if (count($group) > 1) {
|
||||
foreach ($group as $p) {
|
||||
$overlaps->add($p->getStartDate(), $p->getEndDate(), $p->getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$overlaps->compute();
|
||||
|
||||
if ($overlaps->hasIntersections()) {
|
||||
foreach ($overlaps->getIntersections() as list($start, $end, $ids)) {
|
||||
$msg = $end === null ? $constraint->message :
|
||||
$constraint->message;
|
||||
|
||||
$this->context->buildViolation($msg)
|
||||
->setParameters([
|
||||
'{{ start }}' => $start->format('d-m-Y'),
|
||||
'{{ end }}' => $end === null ? null : $end->format('d-m-Y'),
|
||||
'{{ ids }}' => $ids,
|
||||
])
|
||||
->addViolation();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\PersonBundle\Validator\Constraints\AccompanyingPeriod;
|
||||
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
*/
|
||||
class ResourceDuplicateCheck extends Constraint
|
||||
{
|
||||
public $message = '{{ name }} is already associated to this accompanying course.';
|
||||
}
|
||||
|
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\PersonBundle\Validator\Constraints\AccompanyingPeriod;
|
||||
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\PersonBundle\Templating\Entity\PersonRender;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
use Chill\PersonBundle\Validator\Constraints\AccompanyingPeriod\ResourceDuplicateCheck;
|
||||
use Chill\ThirdPartyBundle\Templating\Entity\ThirdPartyRender;
|
||||
|
||||
class ResourceDuplicateCheckValidator extends ConstraintValidator
|
||||
{
|
||||
|
||||
private PersonRender $personRender;
|
||||
private ThirdPartyRender $thirdpartyRender;
|
||||
|
||||
public function __construct(PersonRender $personRender, ThirdPartyRender $thirdPartyRender)
|
||||
{
|
||||
$this->personRender = $personRender;
|
||||
$this->thirdpartyRender = $thirdPartyRender;
|
||||
}
|
||||
|
||||
public function validate($resources, Constraint $constraint)
|
||||
{
|
||||
if (!$constraint instanceof ResourceDuplicateCheck) {
|
||||
throw new UnexpectedTypeException($constraint, ParticipationOverlap::class);
|
||||
}
|
||||
|
||||
if (!$resources instanceof Collection) {
|
||||
throw new UnexpectedTypeException($resources, Collection::class);
|
||||
}
|
||||
|
||||
$resourceList = [];
|
||||
|
||||
foreach ($resources as $resource) {
|
||||
$id = ($resource->getResource() instanceof Person ? 'p' :
|
||||
't').$resource->getResource()->getId();
|
||||
|
||||
if (\in_array($id, $resourceList, true)) {
|
||||
$this->context->buildViolation($constraint->message)
|
||||
->setParameter('{{ name }}', $resource->getResource() instanceof Person ? $this->personRender->renderString($resource->getResource(), []) :
|
||||
$this->thirdpartyRender->renderString($resource->getResource(), []))
|
||||
->addViolation();
|
||||
}
|
||||
|
||||
$resourceList[] = $id;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -22,7 +22,7 @@ namespace Chill\PersonBundle\Widget;
|
||||
use Chill\MainBundle\Templating\Widget\WidgetInterface;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Doctrine\ORM\Query\Expr;
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
|
||||
@@ -33,11 +33,11 @@ use Chill\CustomFieldsBundle\Entity\CustomField;
|
||||
use Twig\Environment;
|
||||
|
||||
/**
|
||||
* add a widget with person list.
|
||||
*
|
||||
* add a widget with person list.
|
||||
*
|
||||
* The configuration is defined by `PersonListWidgetFactory`
|
||||
*
|
||||
* If the options 'custom_fields' is used, the custom fields entity will be
|
||||
*
|
||||
* If the options 'custom_fields' is used, the custom fields entity will be
|
||||
* queried from the db and transmitted to the view under the `customFields` variable.
|
||||
*/
|
||||
class PersonListWidget implements WidgetInterface
|
||||
@@ -48,33 +48,33 @@ class PersonListWidget implements WidgetInterface
|
||||
* @var PersonRepository
|
||||
*/
|
||||
protected $personRepository;
|
||||
|
||||
|
||||
/**
|
||||
* The entity manager
|
||||
*
|
||||
* @var EntityManager
|
||||
*/
|
||||
protected $entityManager;
|
||||
|
||||
|
||||
/**
|
||||
* the authorization helper
|
||||
*
|
||||
*
|
||||
* @var AuthorizationHelper;
|
||||
*/
|
||||
protected $authorizationHelper;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @var TokenStorage
|
||||
*/
|
||||
protected $tokenStorage;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @var UserInterface
|
||||
*/
|
||||
protected $user;
|
||||
|
||||
|
||||
public function __construct(
|
||||
PersonRepository $personRepostory,
|
||||
EntityManager $em,
|
||||
@@ -88,26 +88,26 @@ class PersonListWidget implements WidgetInterface
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param type $place
|
||||
* @param array $context
|
||||
* @param array $config
|
||||
* @return string
|
||||
*/
|
||||
public function render(Environment $env, $place, array $context, array $config)
|
||||
{
|
||||
{
|
||||
$numberOfItems = $config['number_of_items'] ?? 20;
|
||||
|
||||
|
||||
$qb = $this->personRepository
|
||||
->createQueryBuilder('person');
|
||||
|
||||
|
||||
// show only the person from the authorized centers
|
||||
$and = $qb->expr()->andX();
|
||||
$centers = $this->authorizationHelper
|
||||
->getReachableCenters($this->getUser(), new Role(PersonVoter::SEE));
|
||||
$and->add($qb->expr()->in('person.center', ':centers'));
|
||||
$qb->setParameter('centers', $centers);
|
||||
|
||||
|
||||
|
||||
// add the "only active" query
|
||||
if (\array_key_exists('only_active', $config) && $config['only_active'] === true) {
|
||||
@@ -123,37 +123,37 @@ class PersonListWidget implements WidgetInterface
|
||||
(new Expr())->between(':now', 'ap.openingDate', 'ap.closingDate')
|
||||
);
|
||||
$and->add($or);
|
||||
$qb->setParameter('now', new \DateTime(), Type::DATE);
|
||||
$qb->setParameter('now', new \DateTime(), Types::DATE_MUTABLE);
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (\array_key_exists('filtering_class', $config) && $config['filtering_class'] !== NULL) {
|
||||
$filteringClass = new $config['filtering_class'];
|
||||
if ( ! $filteringClass instanceof PersonListWidget\PersonFilteringInterface) {
|
||||
throw new \UnexpectedValueException(sprintf("the class %s does not "
|
||||
. "implements %s", $config['filtering_class'],
|
||||
. "implements %s", $config['filtering_class'],
|
||||
PersonListWidget\PersonFilteringInterface::class));
|
||||
}
|
||||
$ids = $filteringClass->getPersonIds($this->entityManager,
|
||||
$ids = $filteringClass->getPersonIds($this->entityManager,
|
||||
$this->getUser());
|
||||
$in = (new Expr())->in('person.id', ':ids');
|
||||
$and->add($in);
|
||||
$qb->setParameter('ids', $ids);
|
||||
}
|
||||
|
||||
|
||||
// adding the where clause to the query
|
||||
$qb->where($and);
|
||||
|
||||
|
||||
// ordering the query by lastname, firstname
|
||||
$qb->addOrderBy('person.lastName', 'ASC')
|
||||
->addOrderBy('person.firstName', 'ASC');
|
||||
|
||||
|
||||
|
||||
|
||||
$qb->setFirstResult(0)->setMaxResults($numberOfItems);
|
||||
|
||||
|
||||
$persons = $qb->getQuery()->getResult();
|
||||
|
||||
// get some custom field when the view is overriden and we want to
|
||||
|
||||
// get some custom field when the view is overriden and we want to
|
||||
// show some custom field in the overriden view.
|
||||
$cfields = array();
|
||||
if (isset($config['custom_fields'])) {
|
||||
@@ -166,39 +166,39 @@ class PersonListWidget implements WidgetInterface
|
||||
$cfields[$cf->getSlug()] = $cf;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $env->render(
|
||||
'ChillPersonBundle:Widget:homepage_person_list.html.twig',
|
||||
array(
|
||||
'persons' => $persons,
|
||||
'customFields' => $cfields
|
||||
|
||||
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @return UserInterface
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
private function getUser()
|
||||
{
|
||||
$token = $this->tokenStorage->getToken();
|
||||
|
||||
|
||||
if ($token === null) {
|
||||
throw new \RuntimeException("the token should not be null");
|
||||
}
|
||||
|
||||
|
||||
$user = $token->getUser();
|
||||
|
||||
|
||||
if (!$user instanceof UserInterface || $user == null) {
|
||||
throw new \RuntimeException("the user should implement UserInterface. "
|
||||
. "Are you logged in ?");
|
||||
}
|
||||
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -27,7 +27,7 @@ use Chill\MainBundle\Entity\User;
|
||||
/**
|
||||
* Interface to implement on classes called in configuration for
|
||||
* PersonListWidget (`person_list`), under the key `filtering_class` :
|
||||
*
|
||||
*
|
||||
* ```
|
||||
* widgets:
|
||||
* homepage:
|
||||
@@ -35,41 +35,41 @@ use Chill\MainBundle\Entity\User;
|
||||
* # where \FQDN\To\Class implements PersonFiltering
|
||||
* class_filtering: \FQDN\To\Class
|
||||
* ```
|
||||
*
|
||||
*
|
||||
*/
|
||||
interface PersonFilteringInterface
|
||||
interface PersonFilteringInterface
|
||||
{
|
||||
/**
|
||||
* Return an array of persons id to show.
|
||||
*
|
||||
* Those ids are inserted into the query like this (where ids is the array
|
||||
*
|
||||
* Those ids are inserted into the query like this (where ids is the array
|
||||
* returned by this class) :
|
||||
*
|
||||
*
|
||||
* ```
|
||||
* SELECT p FROM ChillPersonBundle:Persons p
|
||||
* SELECT p FROM ChillPersonBundle:Persons p
|
||||
* WHERE p.id IN (:ids)
|
||||
* AND
|
||||
* AND
|
||||
* -- security/authorization statement: restraint person to authorized centers
|
||||
* p.center in :authorized_centers
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* Example of use : filtering based on custom field data :
|
||||
* ```
|
||||
|
||||
|
||||
class HomepagePersonFiltering implements PersonFilteringInterface {
|
||||
|
||||
public function getPersonIds(EntityManager $em, User $user)
|
||||
{
|
||||
$rsmBuilder = new ResultSetMappingBuilder($em);
|
||||
$rsmBuilder->addScalarResult('id', 'id', Type::BIGINT);
|
||||
|
||||
$rsmBuilder->addScalarResult('id', 'id', Types::BIGINT);
|
||||
|
||||
$personTable = $em->getClassMetadata('ChillPersonBundle:Person')
|
||||
->getTableName();
|
||||
$personIdColumn = $em->getClassMetadata('ChillPersonBundle:Person')
|
||||
->getColumnName('id');
|
||||
$personCfDataColumn = $em->getClassMetadata('ChillPersonBundle:Person')
|
||||
->getColumnName('cfData');
|
||||
|
||||
|
||||
return $em->createNativeQuery(sprintf("SELECT %s FROM %s WHERE "
|
||||
. "jsonb_exists(%s, 'school-2fb5440e-192c-11e6-b2fd-74d02b0c9b55')",
|
||||
$personIdColumn, $personTable, $personCfDataColumn), $rsmBuilder)
|
||||
@@ -77,7 +77,7 @@ interface PersonFilteringInterface
|
||||
}
|
||||
}
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* @param EntityManager $em
|
||||
* @return int[] an array of persons id to show
|
||||
*/
|
||||
|
@@ -480,7 +480,7 @@ paths:
|
||||
/1.0/person/accompanying-course/{id}.json:
|
||||
get:
|
||||
tags:
|
||||
- person
|
||||
- accompanying-course
|
||||
summary: "Return the description for an accompanying course (accompanying period)"
|
||||
parameters:
|
||||
- name: id
|
||||
@@ -567,7 +567,7 @@ paths:
|
||||
/1.0/person/accompanying-course/{id}/requestor.json:
|
||||
post:
|
||||
tags:
|
||||
- person
|
||||
- accompanying-course
|
||||
summary: "Add a requestor to the accompanying course"
|
||||
parameters:
|
||||
- name: id
|
||||
@@ -609,7 +609,7 @@ paths:
|
||||
description: "object with validation errors"
|
||||
delete:
|
||||
tags:
|
||||
- person
|
||||
- accompanying-course
|
||||
summary: "Remove the requestor for the accompanying course"
|
||||
parameters:
|
||||
- name: id
|
||||
@@ -633,7 +633,7 @@ paths:
|
||||
/1.0/person/accompanying-course/{id}/participation.json:
|
||||
post:
|
||||
tags:
|
||||
- person
|
||||
- accompanying-course
|
||||
summary: "Add a participant to the accompanying course"
|
||||
parameters:
|
||||
- name: id
|
||||
@@ -662,7 +662,7 @@ paths:
|
||||
description: "object with validation errors"
|
||||
delete:
|
||||
tags:
|
||||
- person
|
||||
- accompanying-course
|
||||
summary: "Remove the participant for the accompanying course"
|
||||
parameters:
|
||||
- name: id
|
||||
@@ -693,7 +693,7 @@ paths:
|
||||
/1.0/person/accompanying-course/{id}/resource.json:
|
||||
post:
|
||||
tags:
|
||||
- person
|
||||
- accompanying-course
|
||||
summary: "Add a resource to the accompanying course"
|
||||
parameters:
|
||||
- name: id
|
||||
@@ -738,7 +738,7 @@ paths:
|
||||
description: "object with validation errors"
|
||||
delete:
|
||||
tags:
|
||||
- person
|
||||
- accompanying-course
|
||||
summary: "Remove the resource"
|
||||
parameters:
|
||||
- name: id
|
||||
@@ -769,7 +769,7 @@ paths:
|
||||
/1.0/person/accompanying-course/{id}/comment.json:
|
||||
post:
|
||||
tags:
|
||||
- person
|
||||
- accompanying-course
|
||||
summary: "Add a comment to the accompanying course"
|
||||
parameters:
|
||||
- name: id
|
||||
@@ -807,7 +807,7 @@ paths:
|
||||
description: "object with validation errors"
|
||||
delete:
|
||||
tags:
|
||||
- person
|
||||
- accompanying-course
|
||||
summary: "Remove the comment"
|
||||
parameters:
|
||||
- name: id
|
||||
@@ -838,7 +838,7 @@ paths:
|
||||
/1.0/person/accompanying-course/{id}/scope.json:
|
||||
post:
|
||||
tags:
|
||||
- person
|
||||
- accompanying-course
|
||||
summary: "Add a scope to the accompanying course"
|
||||
parameters:
|
||||
- name: id
|
||||
@@ -872,7 +872,7 @@ paths:
|
||||
description: "object with validation errors"
|
||||
delete:
|
||||
tags:
|
||||
- person
|
||||
- accompanying-course
|
||||
summary: "Remove the scope"
|
||||
parameters:
|
||||
- name: id
|
||||
@@ -903,7 +903,7 @@ paths:
|
||||
/1.0/person/accompanying-course/{id}/socialissue.json:
|
||||
post:
|
||||
tags:
|
||||
- person
|
||||
- accompanying-course
|
||||
summary: "Add a social issue to the accompanying course"
|
||||
parameters:
|
||||
- name: id
|
||||
@@ -937,7 +937,7 @@ paths:
|
||||
description: "object with validation errors"
|
||||
delete:
|
||||
tags:
|
||||
- person
|
||||
- accompanying-course
|
||||
summary: "Remove the social issue"
|
||||
parameters:
|
||||
- name: id
|
||||
@@ -964,11 +964,31 @@ paths:
|
||||
description: "OK"
|
||||
422:
|
||||
description: "object with validation errors"
|
||||
/1.0/person/accompanying-course/{id}/referrers-suggested.json:
|
||||
get:
|
||||
tags:
|
||||
- accompanying-course
|
||||
summary: "get a list of available referral for a given accompanying cours"
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
description: The accompanying period's id
|
||||
schema:
|
||||
type: integer
|
||||
format: integer
|
||||
minimum: 1
|
||||
responses:
|
||||
401:
|
||||
description: "Unauthorized"
|
||||
404:
|
||||
description: "Not found"
|
||||
200:
|
||||
description: "OK"
|
||||
|
||||
/1.0/person/accompanying-course/{id}/work.json:
|
||||
post:
|
||||
tags:
|
||||
- person
|
||||
- accompanying-course-work
|
||||
summary: "Add a work (AccompanyingPeriodwork) to the accompanying course"
|
||||
parameters:
|
||||
|
@@ -13,3 +13,10 @@ services:
|
||||
entity: 'Chill\PersonBundle\Entity\AccompanyingPeriod'
|
||||
lazy: true
|
||||
method: preUpdateAccompanyingPeriod
|
||||
|
||||
Chill\PersonBundle\AccompanyingPeriod\Suggestion\ReferralsSuggestion:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
|
||||
Chill\PersonBundle\AccompanyingPeriod\Suggestion\ReferralsSuggestionInterface: '@Chill\PersonBundle\AccompanyingPeriod\Suggestion\ReferralsSuggestion'
|
||||
|
||||
|
@@ -41,6 +41,7 @@ services:
|
||||
tags: ['controller.service_arguments']
|
||||
|
||||
Chill\PersonBundle\Controller\AccompanyingCourseApiController:
|
||||
autoconfigure: true
|
||||
autowire: true
|
||||
tags: ['controller.service_arguments']
|
||||
|
||||
|
@@ -7,8 +7,9 @@ services:
|
||||
|
||||
Chill\PersonBundle\Form\PersonType:
|
||||
arguments:
|
||||
- '%chill_person.person_fields%'
|
||||
- '@Chill\PersonBundle\Config\ConfigPersonAltNamesHelper'
|
||||
$personFieldsConfiguration: '%chill_person.person_fields%'
|
||||
$configAltNamesHelper: '@Chill\PersonBundle\Config\ConfigPersonAltNamesHelper'
|
||||
$translatableStringHelper: '@Chill\MainBundle\Templating\TranslatableStringHelper'
|
||||
tags:
|
||||
- { name: form.type, alias: '@chill.person.form.person_creation' }
|
||||
|
||||
|
@@ -0,0 +1,6 @@
|
||||
services:
|
||||
Chill\PersonBundle\Validator\Constraints\AccompanyingPeriod:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
tags: ['validator.service_arguments']
|
||||
|
@@ -17,7 +17,6 @@ class Version20150607231010 extends AbstractMigration
|
||||
. 'recorded.';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param Schema $schema
|
||||
*/
|
||||
@@ -28,36 +27,26 @@ class Version20150607231010 extends AbstractMigration
|
||||
'Migration can only be executed safely on \'postgresql\'.'
|
||||
);
|
||||
|
||||
// retrieve center for setting a default center
|
||||
$centers = $this->connection->fetchAll('SELECT id FROM centers');
|
||||
|
||||
if (count($centers) > 0) {
|
||||
$defaultCenterId = $centers[0]['id'];
|
||||
} else { // if no center, performs other checks
|
||||
//check if there are data in person table
|
||||
$nbPeople = $this->connection->fetchColumn('SELECT count(*) FROM person');
|
||||
|
||||
if ($nbPeople > 0) {
|
||||
// we have data ! We have to create a center !
|
||||
$newCenterId = $this->connection->fetchColumn('SELECT nextval(\'centers_id_seq\');');
|
||||
$this->addSql(
|
||||
'INSERT INTO centers (id, name) VALUES (:id, :name)',
|
||||
['id' => $newCenterId, 'name' => 'Auto-created center']
|
||||
);
|
||||
$defaultCenterId = $newCenterId;
|
||||
}
|
||||
}
|
||||
|
||||
$this->addSql('ALTER TABLE person ADD center_id INT');
|
||||
|
||||
// retrieve center for setting a default center
|
||||
$stmt = $this->connection->executeQuery("SELECT id FROM centers ORDER BY id ASC LIMIT 1");
|
||||
$center = $stmt->fetchOne();
|
||||
|
||||
if ($center !== false) {
|
||||
$defaultCenterId = $center['id'];
|
||||
}
|
||||
|
||||
if (isset($defaultCenterId)) {
|
||||
$this->addSql('UPDATE person SET center_id = :id', array('id' => $defaultCenterId));
|
||||
}
|
||||
|
||||
// this will fail if a person is defined, which should not happen any more at this
|
||||
// time of writing (2021-11-09)
|
||||
$this->addSql('ALTER TABLE person ALTER center_id SET NOT NULL');
|
||||
$this->addSql('ALTER TABLE person '
|
||||
. 'ADD CONSTRAINT FK_person_center FOREIGN KEY (center_id) '
|
||||
. 'REFERENCES centers (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE person ALTER center_id SET NOT NULL');
|
||||
$this->addSql('CREATE INDEX IDX_person_center ON person (center_id)');
|
||||
}
|
||||
|
||||
|
@@ -15,7 +15,6 @@ final class Version20200310090632 extends AbstractMigration
|
||||
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'postgresql', 'Migration can only be executed safely on \'postgresql\'.');
|
||||
|
||||
$this->addSql('ALTER TABLE chill_person_closingmotive ADD parent_id INT DEFAULT NULL');
|
||||
$this->addSql('COMMENT ON COLUMN chill_person_closingmotive.name IS \'(DC2Type:json_array)\'');
|
||||
$this->addSql('ALTER TABLE chill_person_closingmotive ADD CONSTRAINT FK_92351ECE727ACA70 FOREIGN KEY (parent_id) REFERENCES chill_person_closingmotive (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('CREATE INDEX IDX_92351ECE727ACA70 ON chill_person_closingmotive (parent_id)');
|
||||
$this->addsql("ALTER TABLE chill_person_closingmotive ADD ordering DOUBLE PRECISION DEFAULT '0' NOT NULL;");
|
||||
|
@@ -24,6 +24,5 @@ final class Version20210419112619 extends AbstractMigration
|
||||
|
||||
public function down(Schema $schema) : void
|
||||
{
|
||||
$this->addSql('COMMENT ON COLUMN chill_person_accompanying_period_closingmotive.name IS \'(DC2Type:json_array)\'');
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\Migrations\Person;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Validation added to accompanying period resources and accompanying period.
|
||||
*/
|
||||
final class Version20211020131133 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Validation added to accompanying period resources and accompanying period.';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->addSql('CREATE UNIQUE INDEX person_unique ON chill_person_accompanying_period_resource (person_id, accompanyingperiod_id) WHERE person_id IS NOT NULL');
|
||||
$this->addSql('CREATE UNIQUE INDEX thirdparty_unique ON chill_person_accompanying_period_resource (thirdparty_id, accompanyingperiod_id) WHERE thirdparty_id IS NOT NULL');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql('DROP INDEX person_unique');
|
||||
$this->addSql('DROP INDEX thirdparty_unique');
|
||||
}
|
||||
}
|
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\Migrations\Person;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Custom constraint added to database to prevent identical participations.
|
||||
*/
|
||||
final class Version20211021125359 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Custom constraint added to database to prevent identical participations.';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
// creates a constraint 'participations may not overlap'
|
||||
$this->addSql('ALTER TABLE chill_person_accompanying_period_participation ADD CONSTRAINT '.
|
||||
"participations_no_overlap EXCLUDE USING GIST(
|
||||
-- extension btree_gist required to include comparaison with integer
|
||||
person_id WITH =, accompanyingperiod_id WITH =,
|
||||
daterange(startdate, enddate) WITH &&
|
||||
)
|
||||
INITIALLY DEFERRED");
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql('CREATE UNIQUE INDEX participation_unique ON chill_person_accompanying_period_participation (accompanyingperiod_id, person_id)');
|
||||
}
|
||||
}
|
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\Migrations\Person;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Add Civility to Person
|
||||
*/
|
||||
final class Version20211108100849 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Add Civility to Person';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE chill_person_person ADD civility_id INT DEFAULT NULL');
|
||||
$this->addSql('ALTER TABLE chill_person_person ADD CONSTRAINT FK_BF210A1423D6A298 FOREIGN KEY (civility_id) REFERENCES chill_main_civility (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('CREATE INDEX IDX_BF210A1423D6A298 ON chill_person_person (civility_id)');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE chill_person_person DROP CONSTRAINT FK_BF210A1423D6A298');
|
||||
$this->addSql('DROP INDEX IDX_BF210A1423D6A298');
|
||||
$this->addSql('ALTER TABLE chill_person_person DROP civility_id');
|
||||
}
|
||||
}
|
@@ -26,6 +26,12 @@ household:
|
||||
many {Montrer # anciennes ou futures appartenances}
|
||||
other {Montrer # anciennes ou futures appartenances}
|
||||
}
|
||||
Show accompanying periods of past or future memberships: >-
|
||||
{length, plural,
|
||||
one {Montrer les parcours d'une ancienne appartenance}
|
||||
many {Montrer # parcours des anciennes ou futures appartenances}
|
||||
other {Montrer # parcours des anciennes ou futures appartenances}
|
||||
}
|
||||
Hide memberships: Masquer
|
||||
Those members does not share address: Ces usagers ne partagent pas l'adresse du ménage.
|
||||
Any persons into this position: Aucune personne n'appartient au ménage à cette position.
|
||||
@@ -77,6 +83,7 @@ household:
|
||||
Household history for %name%: Historique des ménages pour {name}
|
||||
Household shared: Ménages domiciliés
|
||||
Household not shared: Ménage non domiciliés
|
||||
Members without position: Membres non positionnés
|
||||
Never in any household: Membre d'aucun ménage
|
||||
Membership currently running: En cours
|
||||
from: Depuis
|
||||
|
@@ -47,8 +47,8 @@ Phonenumber: 'Numéro de téléphone'
|
||||
phonenumber: numéro de téléphone
|
||||
Mobilenumber: 'Numéro de téléphone portable'
|
||||
mobilenumber: numéro de téléphone portable
|
||||
Accept short text message ?: Accepte les SMS?
|
||||
Accept short text message: Accepte les SMS
|
||||
Accept short text message ?: La personne a donné l'autorisation d'utiliser ce no de téléphone pour l'envoi de rappel par SMS
|
||||
Accept short text message: La personne a donné l'autorisation d'utiliser ce no de téléphone pour l'envoi de rappel par SMS
|
||||
Other phonenumber: Autre numéro de téléphone
|
||||
Description: description
|
||||
Add new phone: Ajouter un numéro de téléphone
|
||||
@@ -80,6 +80,8 @@ Married: Marié(e)
|
||||
'Contact information': 'Informations de contact'
|
||||
'Administrative information': Administratif
|
||||
File number: Dossier n°
|
||||
Civility: Civilité
|
||||
choose civility: --
|
||||
|
||||
# dédoublonnage
|
||||
Old person: Doublon
|
||||
@@ -403,6 +405,8 @@ Back to household: Revenir au ménage
|
||||
# accompanying course work
|
||||
Accompanying Course Actions: Actions d'accompagnements
|
||||
Accompanying Course Action: Action d'accompagnement
|
||||
Are you sure you want to remove this work of the accompanying period %name% ?: Êtes-vous sûr de vouloir supprimer cette action de la période d'accompagnement %name% ?
|
||||
The accompanying period work has been successfully removed.: L'action d'accompagnement a été supprimée.
|
||||
accompanying_course_work:
|
||||
create: Créer une action
|
||||
Create accompanying course work: Créer une action d'accompagnement
|
||||
@@ -417,6 +421,7 @@ accompanying_course_work:
|
||||
results: Résultats - orientations
|
||||
goal: Objectif - motif - dispositif
|
||||
Any work: Aucune action d'accompagnement
|
||||
remove: Supprimer une action d'accompagnement
|
||||
|
||||
#
|
||||
Person addresses: Adresses de résidence
|
||||
|
@@ -41,3 +41,6 @@ household:
|
||||
household_membership:
|
||||
The end date must be after start date: La date de la fin de l'appartenance doit être postérieure à la date de début.
|
||||
Person with membership covering: Une personne ne peut pas appartenir à deux ménages simultanément. Or, avec cette modification, %person_name% appartiendrait à %nbHousehold% ménages à partir du %from%.
|
||||
|
||||
# Accompanying period
|
||||
'{{ name }} is already associated to this accompanying course.': '{{ name }} est déjà associé avec ce parcours.'
|
Reference in New Issue
Block a user