mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-08-20 14:43:49 +00:00
Merge branch 'master' into features/sql-vue-from-household-address-to-person
This commit is contained in:
@@ -8,6 +8,7 @@ use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
|
||||
use Chill\PersonBundle\Entity\Household\Household;
|
||||
use Chill\PersonBundle\Entity\Household\Position;
|
||||
|
||||
/**
|
||||
* @Route("/{_locale}/person/household")
|
||||
@@ -25,9 +26,20 @@ class HouseholdController extends AbstractController
|
||||
public function summary(Request $request, Household $household)
|
||||
{
|
||||
// TODO ACL
|
||||
|
||||
$positions = $this->getDoctrine()->getManager()
|
||||
->getRepository(Position::class)
|
||||
->findAll()
|
||||
;
|
||||
|
||||
// little performance improvement:
|
||||
// initialize members collection, which will avoid
|
||||
// some queries
|
||||
$household->getMembers()->initialize();
|
||||
return $this->render('@ChillPerson/Household/summary.html.twig',
|
||||
[
|
||||
'household' => $household
|
||||
'household' => $household,
|
||||
'positions' => $positions
|
||||
]
|
||||
);
|
||||
}
|
||||
@@ -43,9 +55,20 @@ class HouseholdController extends AbstractController
|
||||
public function members(Request $request, Household $household)
|
||||
{
|
||||
// TODO ACL
|
||||
$positions = $this->getDoctrine()->getManager()
|
||||
->getRepository(Position::class)
|
||||
->findAll()
|
||||
;
|
||||
|
||||
// little performance improvement:
|
||||
// initialize members collection, which will avoid
|
||||
// some queries
|
||||
$household->getMembers()->initialize();
|
||||
|
||||
return $this->render('@ChillPerson/Household/members.html.twig',
|
||||
[
|
||||
'household' => $household
|
||||
'household' => $household,
|
||||
'positions' => $positions
|
||||
]
|
||||
);
|
||||
}
|
||||
|
@@ -2,21 +2,40 @@
|
||||
|
||||
namespace Chill\PersonBundle\Controller;
|
||||
|
||||
use Chill\PersonBundle\Entity\Household\Position;
|
||||
use Chill\PersonBundle\Security\Authorization\PersonVoter;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\PersonBundle\Entity\Household\Household;
|
||||
use Chill\PersonBundle\Form\HouseholdMemberType;
|
||||
use Chill\PersonBundle\Entity\Household\HouseholdMember;
|
||||
use Symfony\Component\HttpFoundation\Exception\BadRequestException;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
use Symfony\Component\Serializer\Exception;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Chill\MainBundle\CRUD\Controller\ApiController;
|
||||
use Symfony\Component\Translation\TranslatorInterface;
|
||||
use Chill\PersonBundle\Household\MembersEditor;
|
||||
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
|
||||
|
||||
class HouseholdMemberController extends ApiController
|
||||
{
|
||||
private UrlGeneratorInterface $generator;
|
||||
|
||||
private TranslatorInterface $translator;
|
||||
|
||||
|
||||
public function __construct(UrlGeneratorInterface $generator, TranslatorInterface $translator)
|
||||
{
|
||||
$this->generator = $generator;
|
||||
$this->translator = $translator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route(
|
||||
* "/api/1.0/person/household/members/move.{_format}",
|
||||
* name="chill_person_household_members_move"
|
||||
* name="chill_api_person_household_members_move"
|
||||
* )
|
||||
*/
|
||||
public function move(Request $request, $_format): Response
|
||||
@@ -35,13 +54,129 @@ class HouseholdMemberController extends ApiController
|
||||
//
|
||||
$em = $this->getDoctrine()->getManager();
|
||||
|
||||
// if new household, persist it
|
||||
if (
|
||||
$editor->hasHousehold()
|
||||
&&
|
||||
FALSE === $em->contains($editor->getHousehold())
|
||||
) {
|
||||
$em->persist($editor->getHousehold());
|
||||
}
|
||||
|
||||
foreach ($editor->getPersistable() as $el) {
|
||||
$em->persist($el);
|
||||
}
|
||||
$em->flush();
|
||||
|
||||
return $this->json($editor->getHousehold(), Response::HTTP_OK, [], [
|
||||
"groups" => ["read"],
|
||||
|
||||
return $this->json($editor->getHousehold(), Response::HTTP_OK, [], ["groups" => ["read"]]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Route for showing an editor to leave a household.
|
||||
*
|
||||
* Possibles arguments are:
|
||||
*
|
||||
* * persons[]: an id of the person to add to the form
|
||||
* * household: the id of the destination household
|
||||
* * allow_leave_without_household: if present, the editor will allow
|
||||
* to leave household without joining another
|
||||
*
|
||||
* @Route(
|
||||
* "/{_locale}/person/household/members/editor",
|
||||
* name="chill_person_household_members_editor"
|
||||
* )
|
||||
*/
|
||||
public function editor(Request $request)
|
||||
{
|
||||
$em = $this->getDoctrine()->getManager();
|
||||
|
||||
if ($request->query->has('persons')) {
|
||||
$ids = $request->query->get('persons', []);
|
||||
|
||||
if (0 === count($ids)) {
|
||||
throw new BadRequestExceptions("parameters persons in query ".
|
||||
"is not an array or empty");
|
||||
}
|
||||
|
||||
$persons = $em->getRepository(Person::class)
|
||||
->findById($ids)
|
||||
;
|
||||
|
||||
foreach ($persons as $person) {
|
||||
$this->denyAccessUnlessGranted(PersonVoter::SEE, $person,
|
||||
"You are not allowed to see person with id {$person->getId()}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ($householdId = $request->query->get('household', false)) {
|
||||
$household = $em->getRepository(Household::class)
|
||||
->find($householdId)
|
||||
;
|
||||
$allowHouseholdCreate = false;
|
||||
$allowHouseholdSearch = false;
|
||||
$allowLeaveWithoutHousehold = false;
|
||||
|
||||
if (NULL === $household) {
|
||||
throw $this->createNotFoundException('household not found');
|
||||
}
|
||||
// TODO ACL on household
|
||||
}
|
||||
|
||||
$positions = $this->getDoctrine()->getManager()
|
||||
->getRepository(Position::class)
|
||||
->findAll()
|
||||
;
|
||||
|
||||
$data = [
|
||||
'persons' => $persons ?? false ?
|
||||
$this->getSerializer()->normalize($persons, 'json', [ 'groups' => [ 'read' ]]) : [],
|
||||
'household' => $household ?? false ?
|
||||
$this->getSerializer()->normalize($household, 'json', [ 'groups' => [ 'read' ]]) : null,
|
||||
'positions' =>
|
||||
$this->getSerializer()->normalize($positions, 'json', [ 'groups' => [ 'read' ]]),
|
||||
'allowHouseholdCreate' => $allowHouseholdCreate ?? true,
|
||||
'allowHouseholdSearch' => $allowHouseholdSearch ?? true,
|
||||
'allowLeaveWithoutHousehold' => $allowLeaveWithoutHousehold ?? $request->query->has('allow_leave_without_household'),
|
||||
];
|
||||
|
||||
return $this->render('@ChillPerson/Household/members_editor.html.twig', [
|
||||
'data' => $data
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route(
|
||||
* "/{_locale}/person/household/member/{id}/edit",
|
||||
* name="chill_person_household_member_edit"
|
||||
* )
|
||||
*/
|
||||
public function editMembership(Request $request, HouseholdMember $member): Response
|
||||
{
|
||||
// TODO ACL
|
||||
|
||||
$form = $this->createForm(HouseholdMemberType::class, $member);
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
$this->getDoctrine()->getManager()->flush();
|
||||
|
||||
$this->addFlash('success', $this->translator
|
||||
->trans('household.successfully saved member'))
|
||||
;
|
||||
|
||||
return $this->redirect(
|
||||
$request->get('returnPath', null) ??
|
||||
$this->generator->generate('chill_person_household_members', [ 'household_id' =>
|
||||
$member->getHousehold()->getId() ])
|
||||
);
|
||||
}
|
||||
|
||||
return $this->render('@ChillPerson/Household/Member/edit.html.twig', [
|
||||
'household' => $member->getHousehold(),
|
||||
'member' => $member,
|
||||
'form' => $form->createView()
|
||||
]);
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -72,7 +72,6 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac
|
||||
$loader->load('services/command.yaml');
|
||||
$loader->load('services/actions.yaml');
|
||||
$loader->load('services/form.yaml');
|
||||
$loader->load('services/templating.yaml');
|
||||
$loader->load('services/alt_names.yaml');
|
||||
$loader->load('services/household.yaml');
|
||||
// We can get rid of this file when the service 'chill.person.repository.person' is no more used.
|
||||
@@ -555,6 +554,25 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac
|
||||
],
|
||||
]
|
||||
],
|
||||
[
|
||||
'class' => \Chill\PersonBundle\Entity\Household\Household::class,
|
||||
'name' => 'household',
|
||||
'base_path' => '/api/1.0/person/household',
|
||||
// TODO: acl
|
||||
'base_role' => 'ROLE_USER',
|
||||
'actions' => [
|
||||
'_entity' => [
|
||||
'methods' => [
|
||||
Request::METHOD_GET => true,
|
||||
Request::METHOD_HEAD => true,
|
||||
],
|
||||
'roles' => [
|
||||
Request::METHOD_GET => 'ROLE_USER',
|
||||
Request::METHOD_HEAD => 'ROLE_USER',
|
||||
]
|
||||
],
|
||||
]
|
||||
],
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
@@ -53,36 +53,36 @@ class AccompanyingPeriod implements TrackCreationInterface, TrackUpdateInterface
|
||||
{
|
||||
/**
|
||||
* Mark an accompanying period as "occasional"
|
||||
*
|
||||
*
|
||||
* used in INTENSITY
|
||||
*/
|
||||
public const INTENSITY_OCCASIONAL = 'occasional';
|
||||
|
||||
|
||||
/**
|
||||
* Mark an accompanying period as "regular"
|
||||
*
|
||||
*
|
||||
* used in INTENSITY
|
||||
*/
|
||||
public const INTENSITY_REGULAR = 'regular';
|
||||
|
||||
|
||||
public const INTENSITIES = [self::INTENSITY_OCCASIONAL, self::INTENSITY_REGULAR];
|
||||
|
||||
|
||||
/**
|
||||
* Mark an accompanying period as "draft".
|
||||
*
|
||||
* This means that the accompanying period is not yet
|
||||
*
|
||||
* This means that the accompanying period is not yet
|
||||
* confirmed by the creator
|
||||
*/
|
||||
public const STEP_DRAFT = 'DRAFT';
|
||||
|
||||
|
||||
/**
|
||||
* Mark an accompanying period as "confirmed".
|
||||
*
|
||||
* This means that the accompanying period **is**
|
||||
*
|
||||
* This means that the accompanying period **is**
|
||||
* confirmed by the creator
|
||||
*/
|
||||
public const STEP_CONFIRMED = 'CONFIRMED';
|
||||
|
||||
|
||||
/**
|
||||
* @var integer
|
||||
*
|
||||
@@ -176,7 +176,7 @@ class AccompanyingPeriod implements TrackCreationInterface, TrackUpdateInterface
|
||||
* @Groups({"read"})
|
||||
*/
|
||||
private $step = self::STEP_DRAFT;
|
||||
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity=Origin::class)
|
||||
* @ORM\JoinColumn(nullable=true)
|
||||
@@ -274,7 +274,7 @@ class AccompanyingPeriod implements TrackCreationInterface, TrackUpdateInterface
|
||||
* )
|
||||
*/
|
||||
private User $updatedBy;
|
||||
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="datetime", nullable=true, options={"default": NULL})
|
||||
*/
|
||||
@@ -412,7 +412,7 @@ class AccompanyingPeriod implements TrackCreationInterface, TrackUpdateInterface
|
||||
{
|
||||
if (NULL !== $this->initialComment) {
|
||||
$this->removeComment($this->initialComment);
|
||||
}
|
||||
}
|
||||
if ($comment instanceof Comment) {
|
||||
$this->addComment($comment);
|
||||
}
|
||||
@@ -471,7 +471,7 @@ class AccompanyingPeriod implements TrackCreationInterface, TrackUpdateInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the accompanying period contains a person.
|
||||
* Return true if the accompanying period contains a person.
|
||||
*
|
||||
* **Note**: this participation can be opened or not.
|
||||
*/
|
||||
@@ -518,7 +518,7 @@ class AccompanyingPeriod implements TrackCreationInterface, TrackUpdateInterface
|
||||
|
||||
return $participation;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Remove Person
|
||||
@@ -822,6 +822,44 @@ class AccompanyingPeriod implements TrackCreationInterface, TrackUpdateInterface
|
||||
$this->socialIssues->removeElement($socialIssue);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection|SocialIssues[] All social issues and their descendants
|
||||
*/
|
||||
public function getRecursiveSocialIssues(): Collection
|
||||
{
|
||||
$recursiveSocialIssues = new ArrayCollection();
|
||||
|
||||
foreach( $this->socialIssues as $socialIssue) {
|
||||
foreach ($socialIssue->getDescendantsWithThis() as $descendant) {
|
||||
if(! $recursiveSocialIssues->contains($descendant)) {
|
||||
$recursiveSocialIssues->add($descendant);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $recursiveSocialIssues;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Collection|SocialAction[] All the descendant social actions of all
|
||||
* the descendants of the entity
|
||||
*/
|
||||
public function getRecursiveSocialActions(): Collection
|
||||
{
|
||||
$recursiveSocialActions = new ArrayCollection();
|
||||
|
||||
foreach( $this->socialIssues as $socialIssue) {
|
||||
foreach ($socialIssue->getRecursiveSocialActions() as $descendant) {
|
||||
if(! $recursiveSocialActions->contains($descendant)) {
|
||||
$recursiveSocialActions->add($descendant);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $recursiveSocialActions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all persons which are participating to this course
|
||||
*
|
||||
|
@@ -5,6 +5,7 @@ namespace Chill\PersonBundle\Entity\Household;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\Common\Collections\Criteria;
|
||||
use Symfony\Component\Serializer\Annotation as Serializer;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
@@ -107,6 +108,78 @@ class Household
|
||||
return $this->members;
|
||||
}
|
||||
|
||||
public function getCurrentMembers(?\DateTimeImmutable $now = null): Collection
|
||||
{
|
||||
$criteria = new Criteria();
|
||||
$expr = Criteria::expr();
|
||||
$date = $now === null ? (new \DateTimeImmutable('today')) : $now;
|
||||
|
||||
$criteria
|
||||
->where($expr->orX(
|
||||
$expr->isNull('startDate'),
|
||||
$expr->lte('startDate', $date)
|
||||
))
|
||||
->andWhere($expr->orX(
|
||||
$expr->isNull('endDate'),
|
||||
$expr->gte('endDate', $date)
|
||||
));
|
||||
|
||||
return $this->getMembers()->matching($criteria);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the persons currently associated to the household.
|
||||
*
|
||||
* Return a list of Person, instead of a list of HouseholdMembers
|
||||
*
|
||||
* @return Person[]
|
||||
*/
|
||||
public function getCurrentPersons(?\DateTimeImmutable $now = null): Collection
|
||||
{
|
||||
return $this->getCurrentMembers($now)
|
||||
->map(function(HouseholdMember $m) { return $m->getPerson(); });
|
||||
}
|
||||
|
||||
public function getNonCurrentMembers(\DateTimeImmutable $now = null): Collection
|
||||
{
|
||||
$criteria = new Criteria();
|
||||
$expr = Criteria::expr();
|
||||
$date = $now === null ? (new \DateTimeImmutable('today')) : $now;
|
||||
|
||||
$criteria
|
||||
->where(
|
||||
$expr->gt('startDate', $date)
|
||||
)
|
||||
->orWhere(
|
||||
$expr->andX(
|
||||
$expr->lt('endDate', $date),
|
||||
$expr->neq('endDate', null)
|
||||
)
|
||||
);
|
||||
|
||||
return $this->getMembers()->matching($criteria);
|
||||
}
|
||||
|
||||
public function getCurrentMembersByPosition(Position $position, \DateTimeInterface $now = null)
|
||||
{
|
||||
$criteria = new Criteria();
|
||||
$expr = Criteria::expr();
|
||||
|
||||
$criteria->where($expr->eq('position', $position));
|
||||
|
||||
return $this->getCurrentMembers($now)->matching($criteria);
|
||||
}
|
||||
|
||||
public function getNonCurrentMembersByPosition(Position $position, \DateTimeInterface $now = null)
|
||||
{
|
||||
$criteria = new Criteria();
|
||||
$expr = Criteria::expr();
|
||||
|
||||
$criteria->where($expr->eq('position', $position));
|
||||
|
||||
return $this->getNonCurrentMembers($now)->matching($criteria);
|
||||
}
|
||||
|
||||
public function addMember(HouseholdMember $member): self
|
||||
{
|
||||
if (!$this->members->contains($member)) {
|
||||
|
@@ -50,9 +50,9 @@ class HouseholdMember
|
||||
private ?string $comment = NULL;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="boolean")
|
||||
* @ORM\Column(type="boolean", name="sharedhousehold")
|
||||
*/
|
||||
private bool $sharedHousehold = false;
|
||||
private bool $shareHousehold = false;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="boolean", options={"default": false})
|
||||
@@ -98,7 +98,7 @@ class HouseholdMember
|
||||
}
|
||||
|
||||
$this->position = $position;
|
||||
$this->sharedHousehold = $position->getShareHousehold();
|
||||
$this->shareHousehold = $position->getShareHousehold();
|
||||
|
||||
return $this;
|
||||
}
|
||||
@@ -120,7 +120,7 @@ class HouseholdMember
|
||||
return $this->endDate;
|
||||
}
|
||||
|
||||
public function setEndDate(\DateTimeImmutable $endDate): self
|
||||
public function setEndDate(?\DateTimeImmutable $endDate = null): self
|
||||
{
|
||||
$this->endDate = $endDate;
|
||||
|
||||
@@ -144,7 +144,7 @@ class HouseholdMember
|
||||
*/
|
||||
public function getShareHousehold(): ?bool
|
||||
{
|
||||
return $this->sharedHousehold;
|
||||
return $this->shareHousehold;
|
||||
}
|
||||
|
||||
|
||||
|
@@ -7,7 +7,7 @@ use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Serializer\Annotation as Serializer;
|
||||
|
||||
/**
|
||||
* @ORM\Entity(repositoryClass=PositionRepository::class)
|
||||
* @ORM\Entity
|
||||
* @ORM\Table(name="chill_person_household_position")
|
||||
* @Serializer\DiscriminatorMap(typeProperty="type", mapping={
|
||||
* "household_position"=Position::class
|
||||
@@ -21,34 +21,38 @@ class Position
|
||||
* @ORM\Column(type="integer")
|
||||
* @Serializer\Groups({ "read" })
|
||||
*/
|
||||
private $id;
|
||||
private ?int $id;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="json")
|
||||
* @Serializer\Groups({ "read" })
|
||||
*/
|
||||
private $label = [];
|
||||
private array $label = [];
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="boolean")
|
||||
* @Serializer\Groups({ "read" })
|
||||
*/
|
||||
private $shareHouseHold;
|
||||
private bool $shareHouseHold = true;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="boolean")
|
||||
* @Serializer\Groups({ "read" })
|
||||
*/
|
||||
private $allowHolder;
|
||||
private bool $allowHolder = false;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="float")
|
||||
* @Serializer\Groups({ "read" })
|
||||
*/
|
||||
private $ordering;
|
||||
private float $ordering = 0.00;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getLabel(): ?array
|
||||
public function getLabel(): array
|
||||
{
|
||||
return $this->label;
|
||||
}
|
||||
@@ -60,7 +64,7 @@ class Position
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getShareHousehold(): ?bool
|
||||
public function getShareHousehold(): bool
|
||||
{
|
||||
return $this->shareHouseHold;
|
||||
}
|
||||
@@ -72,11 +76,16 @@ class Position
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAllowHolder(): ?bool
|
||||
public function getAllowHolder(): bool
|
||||
{
|
||||
return $this->allowHolder;
|
||||
}
|
||||
|
||||
public function isAllowHolder(): bool
|
||||
{
|
||||
return $this->getAllowHolder();
|
||||
}
|
||||
|
||||
public function setAllowHolder(bool $allowHolder): self
|
||||
{
|
||||
$this->allowHolder = $allowHolder;
|
||||
@@ -84,7 +93,7 @@ class Position
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getOrdering(): ?float
|
||||
public function getOrdering(): float
|
||||
{
|
||||
return $this->ordering;
|
||||
}
|
||||
|
@@ -25,6 +25,7 @@ namespace Chill\PersonBundle\Entity;
|
||||
use ArrayIterator;
|
||||
use Chill\MainBundle\Entity\Center;
|
||||
use Chill\MainBundle\Entity\Country;
|
||||
use Chill\PersonBundle\Entity\Household\Household;
|
||||
use Chill\PersonBundle\Entity\MaritalStatus;
|
||||
use Chill\PersonBundle\Entity\Household\HouseholdMember;
|
||||
use Chill\MainBundle\Entity\HasCenterInterface;
|
||||
@@ -281,6 +282,11 @@ class Person implements HasCenterInterface
|
||||
*/
|
||||
private Collection $householdParticipations;
|
||||
|
||||
/**
|
||||
* Cache the computation of household
|
||||
*/
|
||||
private array $currentHouseholdAt = [];
|
||||
|
||||
/**
|
||||
* Person constructor.
|
||||
*
|
||||
@@ -1202,4 +1208,43 @@ class Person implements HasCenterInterface
|
||||
{
|
||||
return $this->householdParticipations;
|
||||
}
|
||||
|
||||
public function getCurrentHousehold(?\DateTimeImmutable $at = null): ?Household
|
||||
{
|
||||
$criteria = new Criteria();
|
||||
$expr = Criteria::expr();
|
||||
$date = NULL === $at ? new \DateTimeImmutable('now') : $at;
|
||||
$datef = $date->format('Y-m-d');
|
||||
|
||||
if (
|
||||
NULL !== ($this->currentHouseholdAt[$datef] ?? NULL)) {
|
||||
return $this->currentHouseholdAt[$datef];
|
||||
}
|
||||
|
||||
$criteria
|
||||
->where(
|
||||
$expr->andX(
|
||||
$expr->lte('startDate', $date),
|
||||
$expr->orX(
|
||||
$expr->isNull('endDate'),
|
||||
$expr->gte('endDate', $date)
|
||||
),
|
||||
$expr->eq('shareHousehold', true)
|
||||
)
|
||||
);
|
||||
|
||||
$participations = $this->getHouseholdParticipations()
|
||||
->matching($criteria)
|
||||
;
|
||||
|
||||
return $participations->count() > 0 ?
|
||||
$this->currentHouseholdAt[$datef] = $participations->first()
|
||||
->getHousehold()
|
||||
: null;
|
||||
}
|
||||
|
||||
public function isSharingHousehold(?\DateTimeImmutable $at = null): bool
|
||||
{
|
||||
return NULL !== $this->getCurrentHousehold($at);
|
||||
}
|
||||
}
|
||||
|
@@ -102,6 +102,11 @@ class SocialAction
|
||||
return $this->parent;
|
||||
}
|
||||
|
||||
public function hasParent(): bool
|
||||
{
|
||||
return $this->getParent() instanceof self;
|
||||
}
|
||||
|
||||
public function setParent(?self $parent): self
|
||||
{
|
||||
$this->parent = $parent;
|
||||
@@ -139,6 +144,41 @@ class SocialAction
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection|self[] All the descendants (children, children of children, ...)
|
||||
*/
|
||||
public function getDescendants(): Collection
|
||||
{
|
||||
$descendants = new ArrayCollection();
|
||||
|
||||
foreach ($this->getChildren() as $child) {
|
||||
if(! $descendants->contains($child)) {
|
||||
$descendants->add($child);
|
||||
foreach($child->getDescendants() as $descendantsOfChild) {
|
||||
if(! $descendants->contains($descendantsOfChild)) {
|
||||
$descendants->add($descendantsOfChild);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $descendants;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection|self[] All the descendants with the current entity (this)
|
||||
*/
|
||||
public function getDescendantsWithThis(): Collection
|
||||
{
|
||||
$descendants = $this->getDescendants();
|
||||
|
||||
if(! $descendants->contains($this)) {
|
||||
$descendants->add($this);
|
||||
}
|
||||
|
||||
return $descendants;
|
||||
}
|
||||
|
||||
public function getDefaultNotificationDelay(): ?\DateInterval
|
||||
{
|
||||
return $this->defaultNotificationDelay;
|
||||
|
@@ -107,6 +107,42 @@ class SocialIssue
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection|self[] All the descendants (children, children of children, ...)
|
||||
*/
|
||||
public function getDescendants(): Collection
|
||||
{
|
||||
$descendants = new ArrayCollection();
|
||||
|
||||
foreach ($this->getChildren() as $child) {
|
||||
if(! $descendants->contains($child)) {
|
||||
$descendants->add($child);
|
||||
foreach($child->getDescendants() as $descendantsOfChild) {
|
||||
if(! $descendants->contains($descendantsOfChild)) {
|
||||
$descendants->add($descendantsOfChild);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $descendants;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection|self[] All the descendants with the current entity (this)
|
||||
*/
|
||||
public function getDescendantsWithThis(): Collection
|
||||
{
|
||||
$descendants = $this->getDescendants();
|
||||
|
||||
if(! $descendants->contains($this)) {
|
||||
$descendants->add($this);
|
||||
}
|
||||
|
||||
return $descendants;
|
||||
}
|
||||
|
||||
|
||||
public function getDesactivationDate(): ?\DateTimeInterface
|
||||
{
|
||||
return $this->desactivationDate;
|
||||
@@ -160,4 +196,41 @@ class SocialIssue
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection|SocialAction[] All the descendant social actions of the entity
|
||||
*/
|
||||
public function getDescendantsSocialActions(): Collection
|
||||
{
|
||||
$descendantsSocialActions = new ArrayCollection();
|
||||
|
||||
foreach ($this->getSocialActions() as $socialAction) {
|
||||
foreach ($socialAction->getDescendantsWithThis() as $descendant) {
|
||||
if(! $descendantsSocialActions->contains($descendant)) {
|
||||
$descendantsSocialActions->add($descendant);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $descendantsSocialActions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection|SocialAction[] All the descendant social actions of all
|
||||
* the descendants of the entity
|
||||
*/
|
||||
public function getRecursiveSocialActions(): Collection
|
||||
{
|
||||
$recursiveSocialActions = new ArrayCollection();
|
||||
|
||||
foreach ($this->getDescendantsWithThis() as $socialIssue) {
|
||||
foreach ($socialIssue->getDescendantsSocialActions() as $descendant) {
|
||||
if(! $recursiveSocialActions->contains($descendant)) {
|
||||
$recursiveSocialActions->add($descendant);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $recursiveSocialActions;
|
||||
}
|
||||
}
|
||||
|
@@ -24,28 +24,17 @@ use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Form\Extension\Core\Type\DateType;
|
||||
use Chill\MainBundle\Export\ExportElementValidatedInterface;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
use Symfony\Component\Translation\TranslatorInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
*/
|
||||
class AgeAggregator implements AggregatorInterface,
|
||||
ExportElementValidatedInterface
|
||||
final class AgeAggregator implements AggregatorInterface, ExportElementValidatedInterface
|
||||
{
|
||||
/**
|
||||
*
|
||||
* @var
|
||||
*/
|
||||
protected $translator;
|
||||
|
||||
public function __construct($translator)
|
||||
private TranslatorInterface $translator;
|
||||
|
||||
public function __construct(TranslatorInterface $translator)
|
||||
{
|
||||
$this->translator = $translator;
|
||||
}
|
||||
|
||||
|
||||
public function addRole()
|
||||
{
|
||||
return null;
|
||||
|
@@ -29,40 +29,23 @@ use Chill\MainBundle\Util\CountriesInfo;
|
||||
use Symfony\Component\Security\Core\Role\Role;
|
||||
use Chill\PersonBundle\Security\Authorization\PersonVoter;
|
||||
use Chill\MainBundle\Export\ExportElementValidatedInterface;
|
||||
use Chill\MainBundle\Repository\CountryRepository;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
*/
|
||||
class CountryOfBirthAggregator implements AggregatorInterface,
|
||||
ExportElementValidatedInterface
|
||||
final class CountryOfBirthAggregator implements AggregatorInterface, ExportElementValidatedInterface
|
||||
{
|
||||
/**
|
||||
*
|
||||
* @var EntityRepository
|
||||
*/
|
||||
protected $countriesRepository;
|
||||
private CountryRepository $countriesRepository;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var TranslatableStringHelper
|
||||
*/
|
||||
protected $translatableStringHelper;
|
||||
private TranslatableStringHelper $translatableStringHelper;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var TranslatorInterface
|
||||
*/
|
||||
protected $translator;
|
||||
private TranslatorInterface $translator;
|
||||
|
||||
public function __construct(EntityRepository $countriesRepository,
|
||||
TranslatableStringHelper $translatableStringHelper,
|
||||
TranslatorInterface $translator)
|
||||
{
|
||||
public function __construct(
|
||||
CountryRepository $countriesRepository,
|
||||
TranslatableStringHelper $translatableStringHelper,
|
||||
TranslatorInterface $translator
|
||||
) {
|
||||
$this->countriesRepository = $countriesRepository;
|
||||
$this->translatableStringHelper = $translatableStringHelper;
|
||||
$this->translator = $translator;
|
||||
@@ -73,7 +56,6 @@ class CountryOfBirthAggregator implements AggregatorInterface,
|
||||
return 'person';
|
||||
}
|
||||
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
$builder->add('group_by_level', ChoiceType::class, array(
|
||||
|
@@ -26,31 +26,20 @@ use Symfony\Component\Translation\TranslatorInterface;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\PersonBundle\Export\Declarations;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
*/
|
||||
class GenderAggregator implements AggregatorInterface
|
||||
final class GenderAggregator implements AggregatorInterface
|
||||
{
|
||||
|
||||
/**
|
||||
*
|
||||
* @var TranslatorInterface
|
||||
*/
|
||||
protected $translator;
|
||||
|
||||
private TranslatorInterface $translator;
|
||||
|
||||
public function __construct(TranslatorInterface $translator)
|
||||
{
|
||||
$this->translator = $translator;
|
||||
}
|
||||
|
||||
|
||||
public function applyOn()
|
||||
{
|
||||
return Declarations::PERSON_TYPE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
|
||||
|
@@ -29,39 +29,23 @@ use Chill\MainBundle\Util\CountriesInfo;
|
||||
use Symfony\Component\Security\Core\Role\Role;
|
||||
use Chill\PersonBundle\Security\Authorization\PersonVoter;
|
||||
use Chill\MainBundle\Export\ExportElementValidatedInterface;
|
||||
use Chill\MainBundle\Repository\CountryRepository;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
*/
|
||||
class NationalityAggregator implements AggregatorInterface,
|
||||
ExportElementValidatedInterface
|
||||
final class NationalityAggregator implements AggregatorInterface, ExportElementValidatedInterface
|
||||
{
|
||||
/**
|
||||
*
|
||||
* @var EntityRepository
|
||||
*/
|
||||
protected $countriesRepository;
|
||||
private CountryRepository $countriesRepository;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var TranslatableStringHelper
|
||||
*/
|
||||
protected $translatableStringHelper;
|
||||
private TranslatableStringHelper $translatableStringHelper;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var TranslatorInterface
|
||||
*/
|
||||
protected $translator;
|
||||
private TranslatorInterface $translator;
|
||||
|
||||
public function __construct(EntityRepository $countriesRepository,
|
||||
TranslatableStringHelper $translatableStringHelper,
|
||||
TranslatorInterface $translator)
|
||||
{
|
||||
public function __construct(
|
||||
CountryRepository $countriesRepository,
|
||||
TranslatableStringHelper $translatableStringHelper,
|
||||
TranslatorInterface $translator
|
||||
) {
|
||||
$this->countriesRepository = $countriesRepository;
|
||||
$this->translatableStringHelper = $translatableStringHelper;
|
||||
$this->translator = $translator;
|
||||
|
42
src/Bundle/ChillPersonBundle/Form/HouseholdMemberType.php
Normal file
42
src/Bundle/ChillPersonBundle/Form/HouseholdMemberType.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace Chill\PersonBundle\Form;
|
||||
|
||||
use Chill\MainBundle\Form\Type\ChillTextareaType;
|
||||
use Chill\MainBundle\Form\Type\ChillDateType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
class HouseholdMemberType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$builder
|
||||
->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, [
|
||||
'label' => 'household.holder',
|
||||
'choices' => [
|
||||
'household.is holder' => true,
|
||||
'household.is not holder' => false
|
||||
]
|
||||
]);
|
||||
}
|
||||
$builder
|
||||
->add('comment', ChillTextareaType::class, [
|
||||
'label' => 'household.Comment'
|
||||
])
|
||||
;
|
||||
}
|
||||
}
|
@@ -33,7 +33,7 @@ use Chill\PersonBundle\Form\Type\PersonPhoneType;
|
||||
use Chill\PersonBundle\Entity\PersonPhone;
|
||||
use Chill\PersonBundle\Form\Type\Select2MaritalStatusType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\DateType;
|
||||
use Chill\MainBundle\Form\Type\ChillDateType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\EmailType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TelType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
@@ -79,7 +79,9 @@ class PersonType extends AbstractType
|
||||
$builder
|
||||
->add('firstName')
|
||||
->add('lastName')
|
||||
->add('birthdate', DateType::class, array('required' => false, 'widget' => 'single_text', 'format' => 'dd-MM-yyyy'))
|
||||
->add('birthdate', ChillDateType::class, [
|
||||
'required' => false,
|
||||
])
|
||||
->add('gender', GenderType::class, array(
|
||||
'required' => true
|
||||
));
|
||||
|
@@ -3,6 +3,7 @@
|
||||
namespace Chill\PersonBundle\Household;
|
||||
|
||||
use Symfony\Component\Validator\ConstraintViolationListInterface;
|
||||
use Doctrine\Common\Collections\Criteria;
|
||||
use Chill\PersonBundle\Entity\Household\HouseholdMember;
|
||||
use Chill\PersonBundle\Entity\Household\Position;
|
||||
use Chill\PersonBundle\Entity\Household\Household;
|
||||
@@ -13,12 +14,12 @@ use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
class MembersEditor
|
||||
{
|
||||
private ValidatorInterface $validator;
|
||||
private Household $household;
|
||||
private ?Household $household = null;
|
||||
|
||||
private array $persistables = [];
|
||||
private array $membershipsAffected = [];
|
||||
|
||||
public function __construct(ValidatorInterface $validator, Household $household)
|
||||
public function __construct(ValidatorInterface $validator, ?Household $household)
|
||||
{
|
||||
$this->validation = $validator;
|
||||
$this->household = $household;
|
||||
@@ -35,9 +36,9 @@ class MembersEditor
|
||||
->setPerson($person)
|
||||
->setPosition($position)
|
||||
->setHolder($holder)
|
||||
->setHousehold($this->household)
|
||||
->setComment($comment)
|
||||
;
|
||||
$this->household->addMember($membership);
|
||||
|
||||
if ($position->getShareHousehold()) {
|
||||
foreach ($person->getHouseholdParticipations() as $participation) {
|
||||
@@ -62,6 +63,33 @@ class MembersEditor
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function leaveMovement(
|
||||
\DateTimeImmutable $date,
|
||||
Person $person
|
||||
): self {
|
||||
$criteria = new Criteria();
|
||||
$expr = Criteria::expr();
|
||||
|
||||
$criteria->where(
|
||||
$expr->andX(
|
||||
$expr->lt('startDate', $date),
|
||||
$expr->isNull('endDate', $date)
|
||||
)
|
||||
);
|
||||
|
||||
$participations = $person->getHouseholdParticipations()
|
||||
->matching($criteria)
|
||||
;
|
||||
|
||||
foreach ($participations as $participation) {
|
||||
$participation->setEndDate($date);
|
||||
$this->membershipsAffected[] = $participation;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
public function validate(): ConstraintViolationListInterface
|
||||
{
|
||||
|
||||
@@ -72,8 +100,13 @@ class MembersEditor
|
||||
return $this->persistables;
|
||||
}
|
||||
|
||||
public function getHousehold(): Household
|
||||
public function getHousehold(): ?Household
|
||||
{
|
||||
return $this->household;
|
||||
}
|
||||
|
||||
public function hasHousehold(): bool
|
||||
{
|
||||
return $this->household !== null;
|
||||
}
|
||||
}
|
||||
|
@@ -14,7 +14,7 @@ class MembersEditorFactory
|
||||
$this->validator = $validator;
|
||||
}
|
||||
|
||||
public function createEditor(Household $household): MembersEditor
|
||||
public function createEditor(?Household $household = null): MembersEditor
|
||||
{
|
||||
return new MembersEditor($this->validator, $household);
|
||||
}
|
||||
|
@@ -29,21 +29,21 @@ class HouseholdMenuBuilder implements LocalMenuBuilderInterface
|
||||
{
|
||||
$household = $parameters['household'];
|
||||
|
||||
$menu->addChild($this->translator->trans('Summary'), [
|
||||
$menu->addChild($this->translator->trans('household.Household summary'), [
|
||||
'route' => 'chill_person_household_summary',
|
||||
'routeParameters' => [
|
||||
'household_id' => $household->getId()
|
||||
]])
|
||||
->setExtras(['order' => 10]);
|
||||
|
||||
$menu->addChild($this->translator->trans('Members'), [
|
||||
$menu->addChild($this->translator->trans('household.Household members'), [
|
||||
'route' => 'chill_person_household_members',
|
||||
'routeParameters' => [
|
||||
'household_id' => $household->getId()
|
||||
]])
|
||||
->setExtras(['order' => 20]);
|
||||
|
||||
$menu->addChild($this->translator->trans('Addresses'), [
|
||||
$menu->addChild($this->translator->trans('household.Addresses'), [
|
||||
'route' => 'chill_person_household_addresses',
|
||||
'routeParameters' => [
|
||||
'household_id' => $household->getId()
|
||||
|
@@ -3,8 +3,10 @@
|
||||
namespace Chill\PersonBundle\Repository\Household;
|
||||
|
||||
use Chill\PersonBundle\Entity\Household\Position;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
//use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
//use Doctrine\Persistence\ManagerRegistry;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
|
||||
/**
|
||||
* @method Position|null find($id, $lockMode = null, $lockVersion = null)
|
||||
@@ -12,11 +14,20 @@ use Doctrine\Persistence\ManagerRegistry;
|
||||
* @method Position[] findAll()
|
||||
* @method Position[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||
*/
|
||||
class PositionRepository extends ServiceEntityRepository
|
||||
final class PositionRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
private EntityRepository $repository;
|
||||
|
||||
public function __construct(EntityManagerInterface $entityManager)
|
||||
{
|
||||
parent::__construct($registry, Position::class);
|
||||
$this->repository = $entityManager->getRepository(Position::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Position[]
|
||||
*/
|
||||
public function findAll(): array
|
||||
{
|
||||
return $this->repository->findAll();
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,21 @@
|
||||
/// complete and overwrite flex-table in chillmain.scss
|
||||
div.list-with-period {
|
||||
div.list-with-period,
|
||||
div.list-household-members,
|
||||
div.list-household-members--summary {
|
||||
|
||||
.chill-entity__person {
|
||||
.chill-entity__person__first-name,
|
||||
.chill-entity__person__last-name {
|
||||
font-size: 1.3em;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
|
||||
.chill_denomination {
|
||||
font-size: 1.3em;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
div.person {
|
||||
ul.record_actions {
|
||||
li {
|
||||
@@ -7,6 +23,9 @@ div.list-with-period {
|
||||
}
|
||||
}
|
||||
}
|
||||
div.comment {
|
||||
// for the comment for household-members
|
||||
}
|
||||
div.periods {
|
||||
div.header,
|
||||
div.list-content {
|
||||
@@ -40,11 +59,3 @@ div.list-with-period {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chill-entity__person {
|
||||
.chill-entity__person__first-name,
|
||||
.chill-entity__person__last-name {
|
||||
font-size: 1.3em;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
|
@@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<div>
|
||||
<a @click="toggleIntensity" class="flag-toggle">
|
||||
{{ $t('course.occasional') }}
|
||||
<span :class="{ 'on': !isRegular }">{{ $t('course.occasional') }}</span>
|
||||
<i class="fa" :class="{ 'fa-toggle-on': isRegular, 'fa-toggle-on fa-flip-horizontal': !isRegular }"></i>
|
||||
{{ $t('course.regular') }}
|
||||
<span :class="{ 'on': isRegular }">{{ $t('course.regular') }}</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -76,6 +76,9 @@ export default {
|
||||
i {
|
||||
margin: auto 0.4em;
|
||||
}
|
||||
span.on {
|
||||
font-weight: bolder;
|
||||
}
|
||||
}
|
||||
button.badge {
|
||||
margin-left: 0.8em;
|
||||
|
@@ -9,8 +9,7 @@
|
||||
<table class="rounded" v-if="participations.length > 0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="chill-orange">{{ $t('persons_associated.firstname') }}</th>
|
||||
<th class="chill-orange">{{ $t('persons_associated.lastname') }}</th>
|
||||
<th class="chill-orange">{{ $t('persons_associated.name') }}</th>
|
||||
<th class="chill-orange">{{ $t('persons_associated.startdate') }}</th>
|
||||
<th class="chill-orange">{{ $t('persons_associated.enddate') }}</th>
|
||||
<th class="chill-orange">{{ $t('action.actions') }}</th>
|
||||
|
@@ -1,7 +1,9 @@
|
||||
<template>
|
||||
<tr>
|
||||
<td>{{ participation.person.firstName }}</td>
|
||||
<td>{{ participation.person.lastName }}</td>
|
||||
<td>
|
||||
{{ participation.person.firstName }}
|
||||
{{ participation.person.lastName }}
|
||||
</td>
|
||||
<td><span v-if="participation.startDate">
|
||||
{{ $d(participation.startDate.datetime, 'short') }}</span>
|
||||
</td>
|
||||
@@ -11,16 +13,18 @@
|
||||
<td>
|
||||
<ul class="record_actions">
|
||||
<li>
|
||||
<a class="sc-button bt-show" target="_blank"
|
||||
:href="url.show"
|
||||
:title="$t('action.show')">
|
||||
</a>
|
||||
<on-the-fly
|
||||
v-bind:type="participation.person.type"
|
||||
v-bind:id="participation.person.id"
|
||||
action="show">
|
||||
</on-the-fly>
|
||||
</li>
|
||||
<li>
|
||||
<a class="sc-button bt-update" target="_blank"
|
||||
:href="url.edit"
|
||||
:title="$t('action.edit')">
|
||||
</a>
|
||||
<on-the-fly
|
||||
v-bind:type="participation.person.type"
|
||||
v-bind:id="participation.person.id"
|
||||
action="edit">
|
||||
</on-the-fly>
|
||||
</li>
|
||||
<!--li>
|
||||
<button class="sc-button bt-delete"
|
||||
@@ -31,7 +35,7 @@
|
||||
<li>
|
||||
<button v-if="!participation.endDate"
|
||||
class="sc-button bt-remove"
|
||||
:title="$t('action.remove')"
|
||||
v-bind:title="$t('action.remove')"
|
||||
@click.prevent="$emit('close', participation)">
|
||||
</button>
|
||||
<button v-else class="sc-button bt-remove disabled"></button>
|
||||
@@ -42,17 +46,14 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import OnTheFly from 'ChillMainAssets/vuejs/_components/OnTheFly.vue';
|
||||
|
||||
export default {
|
||||
name: 'PersonItem',
|
||||
props: ['participation'],
|
||||
data() {
|
||||
return {
|
||||
url: {
|
||||
show: '/fr/person/' + this.participation.person.id + '/general',
|
||||
edit: '/fr/person/' + this.participation.person.id + '/general/edit'
|
||||
}
|
||||
}
|
||||
components: {
|
||||
OnTheFly
|
||||
},
|
||||
emits: ['remove', 'close']
|
||||
props: ['participation'],
|
||||
emits: ['remove', 'close'],
|
||||
}
|
||||
</script>
|
||||
|
@@ -41,11 +41,21 @@
|
||||
|
||||
<ul class="record_actions">
|
||||
<li>
|
||||
<a class="sc-button bt-show" :title="$t('action.show')" target="_blank" :href="url.show"></a>
|
||||
<on-the-fly
|
||||
v-bind:type="accompanyingCourse.requestor.type"
|
||||
v-bind:id="accompanyingCourse.requestor.id"
|
||||
action="show">
|
||||
</on-the-fly>
|
||||
</li>
|
||||
<li>
|
||||
<on-the-fly
|
||||
v-bind:type="accompanyingCourse.requestor.type"
|
||||
v-bind:id="accompanyingCourse.requestor.id"
|
||||
action="edit">
|
||||
</on-the-fly>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<ul class="record_actions">
|
||||
<li>
|
||||
<button class="sc-button bt-remove"
|
||||
@@ -55,7 +65,6 @@
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
<div v-else>
|
||||
<label>{{ $t('requestor.counter') }}</label>
|
||||
@@ -76,12 +85,14 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AddPersons from 'ChillPersonAssets/vuejs/_components/AddPersons.vue'
|
||||
import AddPersons from 'ChillPersonAssets/vuejs/_components/AddPersons.vue';
|
||||
import OnTheFly from 'ChillMainAssets/vuejs/_components/OnTheFly.vue';
|
||||
|
||||
export default {
|
||||
name: 'Requestor',
|
||||
components: {
|
||||
AddPersons,
|
||||
OnTheFly
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -107,13 +118,6 @@ export default {
|
||||
get() {
|
||||
return this.$store.state.accompanyingCourse.requestorAnonymous;
|
||||
}
|
||||
},
|
||||
url() {
|
||||
return (this.accompanyingCourse.requestor.type === 'person') ? {
|
||||
show: `/fr/person/${this.accompanyingCourse.requestor.id}/general`,
|
||||
} : {
|
||||
show: `/fr/thirdparty/thirdparty/${this.accompanyingCourse.requestor.id}/show`,
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -141,7 +145,6 @@ div.flex-table {
|
||||
}
|
||||
div.item-bloc {
|
||||
background-color: white !important;
|
||||
border: 1px solid #000;
|
||||
padding: 1em;
|
||||
margin-top: 1em;
|
||||
.content-bloc {
|
||||
|
@@ -21,24 +21,25 @@
|
||||
<td>
|
||||
<ul class="record_actions">
|
||||
<li>
|
||||
<a class="sc-button bt-show" target="_blank"
|
||||
:href="url.show"
|
||||
:title="$t('action.show')">
|
||||
</a>
|
||||
<on-the-fly
|
||||
v-bind:type="resource.resource.type"
|
||||
v-bind:id="resource.resource.id"
|
||||
action="show">
|
||||
</on-the-fly>
|
||||
</li>
|
||||
<li>
|
||||
<a class="sc-button bt-update" target="_blank"
|
||||
:href="url.edit"
|
||||
:title="$t('action.edit')">
|
||||
</a>
|
||||
<on-the-fly
|
||||
v-bind:type="resource.resource.type"
|
||||
v-bind:id="resource.resource.id"
|
||||
action="edit">
|
||||
</on-the-fly>
|
||||
</li>
|
||||
<li>
|
||||
<button
|
||||
class="sc-button bt-remove"
|
||||
:title="$t('action.remove')"
|
||||
v-bind:title="$t('action.remove')"
|
||||
@click.prevent="$emit('remove', resource)">
|
||||
</button>
|
||||
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
@@ -46,23 +47,14 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import OnTheFly from 'ChillMainAssets/vuejs/_components/OnTheFly.vue';
|
||||
|
||||
export default {
|
||||
name: 'ResourceItem',
|
||||
components: {
|
||||
OnTheFly
|
||||
},
|
||||
props: ['resource'],
|
||||
emits: ['remove'],
|
||||
computed: {
|
||||
type() {
|
||||
return this.resource.resource.type;
|
||||
},
|
||||
url() {
|
||||
return (this.type === 'person') ? {
|
||||
show: `/fr/person/${this.resource.resource.id}/general`,
|
||||
edit: `/fr/person/${this.resource.resource.id}/general/edit`
|
||||
} : {
|
||||
show: `/fr/thirdparty/thirdparty/${this.resource.resource.id}/show`,
|
||||
edit: `/fr/thirdparty/thirdparty/${this.resource.resource.id}/update`
|
||||
}
|
||||
}
|
||||
}
|
||||
emits: ['remove']
|
||||
}
|
||||
</script>
|
||||
|
@@ -13,7 +13,7 @@
|
||||
track-by="id"
|
||||
label="text"
|
||||
:multiple="true"
|
||||
:searchable="false"
|
||||
:searchable="true"
|
||||
:placeholder="$t('social_issue.label')"
|
||||
@update:model-value="updateSocialIssues"
|
||||
:model-value="value"
|
||||
@@ -75,6 +75,6 @@ export default {
|
||||
<style src="vue-multiselect/dist/vue-multiselect.css"></style>
|
||||
<style lang="scss">
|
||||
span.multiselect__tag {
|
||||
background: #e2793d;
|
||||
background: var(--chill-orange);
|
||||
}
|
||||
</style>
|
||||
|
@@ -31,6 +31,7 @@ const appMessages = {
|
||||
counter: "Il n'y a pas encore d'usager | 1 usager | {count} usagers",
|
||||
firstname: "Prénom",
|
||||
lastname: "Nom",
|
||||
name: "Nom",
|
||||
startdate: "Date d'entrée",
|
||||
enddate: "Date de sortie",
|
||||
add_persons: "Ajouter des usagers",
|
||||
@@ -73,7 +74,7 @@ const appMessages = {
|
||||
comment: {
|
||||
title: "Observations",
|
||||
label: "Ajout d'une note",
|
||||
content: "Rédigez une première note...",
|
||||
content: "Rédigez une première note…",
|
||||
created_by: "créé par {0}, le {1}"
|
||||
},
|
||||
confirm: {
|
||||
|
@@ -0,0 +1,33 @@
|
||||
<template>
|
||||
<household></household>
|
||||
<concerned></concerned>
|
||||
<dates></dates>
|
||||
<confirmation></confirmation>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import { mapState } from 'vuex';
|
||||
import Concerned from './components/Concerned.vue';
|
||||
import Household from './components/Household.vue';
|
||||
import Dates from './components/Dates.vue';
|
||||
import Confirmation from './components/Confirmation.vue';
|
||||
|
||||
export default {
|
||||
name: 'App',
|
||||
components: {
|
||||
Concerned,
|
||||
Household,
|
||||
Dates,
|
||||
Confirmation,
|
||||
},
|
||||
computed: {
|
||||
// for debugging purpose
|
||||
// (not working)
|
||||
//...mapState({
|
||||
// 'concerned', 'household', 'positions'
|
||||
// })
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
*/
|
||||
const householdMove = (payload) => {
|
||||
const url = `/api/1.0/person/household/members/move.json`;
|
||||
console.log(payload);
|
||||
console.log(JSON.stringify(payload));
|
||||
|
||||
return fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(payload),
|
||||
})
|
||||
.then(response => {
|
||||
if (response.ok) {
|
||||
return response.json();
|
||||
}
|
||||
throw Error('Error with testing move');
|
||||
});
|
||||
};
|
||||
|
||||
const householdMoveTest = (payload) => {
|
||||
const url = `/api/1.0/person/household/members/move/test.json`;
|
||||
return fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(payload),
|
||||
})
|
||||
.then(response => {
|
||||
if (response.status === 422) {
|
||||
return response.json();
|
||||
}
|
||||
if (response.ok) {
|
||||
// return an empty array if ok
|
||||
return new Promise((resolve, reject) => resolve({ violations: [] }) );
|
||||
}
|
||||
throw Error('Error with testing move');
|
||||
});
|
||||
};
|
||||
|
||||
export {
|
||||
householdMove,
|
||||
householdMoveTest
|
||||
};
|
@@ -0,0 +1,215 @@
|
||||
<template>
|
||||
<h2>{{ $t('household_members_editor.concerned.title') }}</h2>
|
||||
|
||||
<h3 v-if="needsPositionning">
|
||||
{{ $t('household_members_editor.concerned.persons_to_positionnate') }}
|
||||
</h3>
|
||||
<h3 v-else>
|
||||
{{ $t('household_members_editor.concerned.persons_leaving') }}
|
||||
</h3>
|
||||
|
||||
<div v-if="noPerson">
|
||||
<div class="alert alert-info">
|
||||
{{ $t('household_members_editor.add_at_least_onePerson') }}
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="allPersonsPositionnated">
|
||||
<span class="chill-no-data-statement">{{ $t('household_members_editor.all_positionnated') }}</span>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div class="flex-table list-household-members">
|
||||
<div v-for="conc in concUnpositionned"
|
||||
class="item-bloc"
|
||||
v-bind:key="conc.person.id"
|
||||
draggable="true"
|
||||
@dragstart="onStartDragConcern($event, conc.person.id)"
|
||||
>
|
||||
<div class="item-row person">
|
||||
<div class="item-col box-person">
|
||||
<div>
|
||||
<img src="~ChillMainAssets/img/draggable.svg" class="drag-icon" />
|
||||
<person :person="conc.person"></person>
|
||||
</div>
|
||||
<div>
|
||||
{{ $t('person.born', {'gender': conc.person.gender} ) }}
|
||||
{{ $d(conc.person.birthdate.datetime, 'short') }}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="item-col box-where">
|
||||
<ul class="list-content fa-ul">
|
||||
<li>
|
||||
<i class="fa fa-li fa-map-marker"></i>
|
||||
<span class="chill-no-data-statement">Sans adresse</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="needsPositionning" class="item-row move_to">
|
||||
<div class="item-col">
|
||||
|
||||
<p class="move_hint">{{ $t('household_members_editor.concerned.move_to') }}:</p>
|
||||
|
||||
<template
|
||||
v-for="position in positions"
|
||||
>
|
||||
|
||||
<button
|
||||
class="btn btn-outline-primary"
|
||||
@click="moveToPosition(conc.person.id, position.id)"
|
||||
>
|
||||
{{ position.label.fr }}
|
||||
</button>
|
||||
|
||||
</template>
|
||||
|
||||
<button v-if="conc.allowRemove" @click="removeConcerned(conc)" class="btn bt-primary">
|
||||
{{ $t('household_members_editor.remove_concerned') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<add-persons
|
||||
buttonTitle="household_members_editor.concerned.add_persons"
|
||||
modalTitle="household_members_editor.concerned.search"
|
||||
v-bind:key="addPersons.key"
|
||||
v-bind:options="addPersons.options"
|
||||
@addNewPersons="addNewPersons"
|
||||
ref="addPersons"> <!-- to cast child method -->
|
||||
</add-persons>
|
||||
</div>
|
||||
|
||||
|
||||
<div v-if="needsPositionning" class="positions">
|
||||
<div
|
||||
v-for="position in positions"
|
||||
>
|
||||
<h3>{{ position.label.fr }}</h3>
|
||||
<div class="flex-table list-household-members">
|
||||
<member-details
|
||||
v-for="conc in concByPosition(position.id)"
|
||||
v-bind:key="conc.person.id"
|
||||
v-bind:conc="conc"
|
||||
>
|
||||
</member-details>
|
||||
<div
|
||||
class="droppable_zone"
|
||||
@drop="onDropConcern($event, position.id)"
|
||||
@dragover.prevent
|
||||
@dragenter.prevent
|
||||
>
|
||||
{{ $t('household_members_editor.drop_persons_here', {'position': position.label.fr }) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
div.person {
|
||||
cursor: move;
|
||||
|
||||
* {
|
||||
cursor: move
|
||||
}
|
||||
}
|
||||
|
||||
.drag-icon {
|
||||
height: 1.1em;
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
|
||||
.droppable_zone {
|
||||
background-color: var(--chill-llight-gray);
|
||||
color: white;
|
||||
font-size: large;
|
||||
text-align: center;
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
padding: 1em;
|
||||
background: linear-gradient(to top, var(--chill-light-gray), 30%, var(--chill-llight-gray));
|
||||
}
|
||||
|
||||
.move_to {
|
||||
.move_hint {
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
padding: 0.400rem 0.5rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import AddPersons from 'ChillPersonAssets/vuejs/_components/AddPersons.vue';
|
||||
import Person from 'ChillPersonAssets/vuejs/_components/Person/Person.vue';
|
||||
import MemberDetails from './MemberDetails.vue';
|
||||
import { ISOToDatetime } from 'ChillMainAssets/js/date.js';
|
||||
|
||||
export default {
|
||||
name: 'Concerned',
|
||||
components: {
|
||||
AddPersons,
|
||||
MemberDetails,
|
||||
Person,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'concUnpositionned',
|
||||
'positions',
|
||||
'concByPosition',
|
||||
'needsPositionning'
|
||||
]),
|
||||
noPerson () {
|
||||
return this.$store.getters.persons.length === 0;
|
||||
},
|
||||
allPersonsPositionnated () {
|
||||
return this.$store.getters.persons.length > 0
|
||||
&& this.$store.getters.concUnpositionned.length === 0;
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
addPersons: {
|
||||
key: 'household_members_editor_concerned',
|
||||
options: {
|
||||
type: ['person'],
|
||||
priority: null,
|
||||
uniq: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
addNewPersons({ selected, modal }) {
|
||||
selected.forEach(function(item) {
|
||||
this.$store.dispatch('addConcerned', item.result);
|
||||
}, this);
|
||||
this.$refs.addPersons.resetSearch(); // to cast child method
|
||||
modal.showModal = false;
|
||||
},
|
||||
onStartDragConcern(evt, person_id) {
|
||||
evt.dataTransfer.dropEffect = 'move'
|
||||
evt.dataTransfer.effectAllowed = 'move'
|
||||
evt.dataTransfer.setData('application/x.person', person_id)
|
||||
},
|
||||
onDropConcern(evt, position_id) {
|
||||
const person_id = Number(evt.dataTransfer.getData('application/x.person'));
|
||||
this.moveToPosition(person_id, position_id);
|
||||
},
|
||||
moveToPosition(person_id, position_id) {
|
||||
this.$store.dispatch('markPosition', { person_id, position_id });
|
||||
|
||||
},
|
||||
removeConcerned(conc) {
|
||||
this.$store.dispatch('removeConcerned', conc);
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
@@ -0,0 +1,48 @@
|
||||
<template>
|
||||
|
||||
<div v-if="hasWarnings" class="alert alert-warning">
|
||||
{{ $t('household_members_editor.confirmation.there_are_warnings') }}
|
||||
</div>
|
||||
|
||||
<p v-if="hasWarnings">
|
||||
{{ $t('household_members_editor.confirmation.check_those_items') }}
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li v-for="(msg, index) in warnings">
|
||||
{{ $t(msg.m, msg.a) }}
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul class="record_actions sticky-form-buttons">
|
||||
<li>
|
||||
<button class="sc-button bt-save" :disabled="hasWarnings" @click="confirm">
|
||||
{{ $t('household_members_editor.confirmation.save') }}
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex';
|
||||
|
||||
export default {
|
||||
name: 'Confirmation',
|
||||
computed: {
|
||||
...mapState({
|
||||
warnings: (state) => state.warnings,
|
||||
hasNoWarnings: (state) => state.warnings.length === 0,
|
||||
hasWarnings: (state) => state.warnings.length > 0,
|
||||
}),
|
||||
},
|
||||
methods: {
|
||||
confirm() {
|
||||
this.$store.dispatch('confirm');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
@@ -0,0 +1,36 @@
|
||||
<template>
|
||||
<h2>{{ $t('household_members_editor.dates_title') }}</h2>
|
||||
|
||||
<p>
|
||||
<label for="start_date">
|
||||
{{ $t('household_members_editor.dates.start_date') }}
|
||||
</label>
|
||||
<input type="date" v-model="startDate" />
|
||||
</p>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'Dates',
|
||||
computed: {
|
||||
startDate: {
|
||||
get() {
|
||||
return [
|
||||
this.$store.state.startDate.getFullYear(),
|
||||
(this.$store.state.startDate.getMonth() + 1).toString().padStart(2, '0'),
|
||||
this.$store.state.startDate.getDate().toString().padStart(2, '0')
|
||||
].join('-');
|
||||
},
|
||||
set(value) {
|
||||
let
|
||||
[year, month, day] = value.split('-'),
|
||||
dValue = new Date(year, month-1, day);
|
||||
|
||||
this.$store.dispatch('setStartDate', dValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
@@ -0,0 +1,80 @@
|
||||
<template>
|
||||
<h2>{{ $t('household_members_editor.household_part') }}</h2>
|
||||
|
||||
<div v-if="hasHousehold">
|
||||
<span v-if="isHouseholdNew">
|
||||
{{ $t('household_members_editor.household.new_household') }}
|
||||
</span>
|
||||
<div v-else>
|
||||
Ménage existant
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="isForceLeaveWithoutHousehold">
|
||||
{{ $t('household_members_editor.household.will_leave_any_household') }}
|
||||
</div>
|
||||
<div v-else>
|
||||
<div class="alert alert-info">{{ $t('household_members_editor.household.no_household_choose_one') }}</div>
|
||||
</div>
|
||||
|
||||
<ul v-if="allowChangeHousehold" class="record_actions">
|
||||
<li v-if="allowHouseholdCreate">
|
||||
<button class="sc-button bt-create" @click="createHousehold">
|
||||
{{ $t('household_members_editor.household.create_household') }}
|
||||
</button>
|
||||
</li>
|
||||
<li v-if="allowHouseholdSearch">
|
||||
<button class="sc-button">
|
||||
<i class="fa fa-search"></i>{{ $t('household_members_editor.household.search_household') }}
|
||||
</button>
|
||||
</li>
|
||||
<li v-if="allowLeaveWithoutHousehold" >
|
||||
<button @click="forceLeaveWithoutHousehold" class="sc-button bt-orange">
|
||||
<i class="fa fa-sign-out"></i>{{ $t('household_members_editor.household.leave_without_household') }}
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import { mapGetters } from 'vuex';
|
||||
|
||||
export default {
|
||||
name: 'Household',
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'hasHousehold',
|
||||
'isHouseholdNew',
|
||||
]),
|
||||
household() {
|
||||
return this.$store.household;
|
||||
},
|
||||
allowHouseholdCreate() {
|
||||
return this.$store.state.allowHouseholdCreate;
|
||||
},
|
||||
allowHouseholdSearch() {
|
||||
return this.$store.state.allowHouseholdSearch;
|
||||
},
|
||||
allowLeaveWithoutHousehold() {
|
||||
return this.$store.state.allowLeaveWithoutHousehold;
|
||||
},
|
||||
allowChangeHousehold() {
|
||||
return this.allowHouseholdCreate || this.allowHouseholdSearch ||
|
||||
this.allowLeaveWithoutHousehold;
|
||||
},
|
||||
isForceLeaveWithoutHousehold() {
|
||||
return this.$store.state.forceLeaveWithoutHousehold;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
createHousehold() {
|
||||
this.$store.dispatch('createHousehold');
|
||||
},
|
||||
forceLeaveWithoutHousehold() {
|
||||
this.$store.dispatch('forceLeaveWithoutHousehold');
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
</script>
|
@@ -0,0 +1,126 @@
|
||||
<template>
|
||||
<div class="item-bloc">
|
||||
<div class="item-row person">
|
||||
<div class="item-col box-person">
|
||||
<div>
|
||||
<person :person="conc.person"></person>
|
||||
<span v-if="isHolder" class="badge badge-primary holder">
|
||||
{{ $t('household_members_editor.holder') }}
|
||||
</span>
|
||||
</div>
|
||||
<div>{{ $t('person.born', {'gender': conc.person.gender} ) }}</div>
|
||||
</div>
|
||||
<div class="item-col box-where">
|
||||
<ul class="list-content fa-ul">
|
||||
<li>
|
||||
<i class="fa fa-li fa-map-marker"></i>
|
||||
<span class="chill-no-data-statement">Sans adresse</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="item-row comment">
|
||||
<ckeditor :editor="editor" v-model="comment" tag-name="textarea"></ckeditor>
|
||||
</div>
|
||||
|
||||
<div class="item-row participation-details">
|
||||
<div v-if="conc.position.allowHolder" class="action">
|
||||
<button class="btn" :class="{ 'btn-primary': isHolder, 'btn-secondary': !isHolder}" @click="toggleHolder">
|
||||
{{ $t(isHolder ? 'household_members_editor.is_holder' : 'household_members_editor.is_not_holder') }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button @click="removePosition" class="btn btn-outline-primary">
|
||||
{{ $t('household_members_editor.remove_position', {position: conc.position.label.fr}) }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button v-if="conc.allowRemove" @click="removeConcerned" class="btn btn-primary">
|
||||
{{ $t('household_members_editor.remove_concerned') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
.drag-icon {
|
||||
height: 1.1em;
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
div.participation-details {
|
||||
display: flex;
|
||||
flex-direction: row !important;
|
||||
justify-content: flex-end;
|
||||
|
||||
.action {
|
||||
align-self: flex-start;
|
||||
margin-right: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.holder {
|
||||
display: inline;
|
||||
vertical-align: super;
|
||||
font-size: 0.6em;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import Person from 'ChillPersonAssets/vuejs/_components/Person/Person.vue';
|
||||
import CKEditor from '@ckeditor/ckeditor5-vue';
|
||||
import ClassicEditor from 'ChillMainAssets/modules/ckeditor5/index.js';
|
||||
|
||||
export default {
|
||||
name: 'MemberDetails',
|
||||
components: {
|
||||
Person,
|
||||
ckeditor: CKEditor.component,
|
||||
},
|
||||
props: [
|
||||
'conc'
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
editor: ClassicEditor,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters( [
|
||||
'concByPersonId'
|
||||
]),
|
||||
isHolder() {
|
||||
return this.conc.holder;
|
||||
},
|
||||
comment: {
|
||||
get() {
|
||||
return this.conc.comment;
|
||||
},
|
||||
set(text) {
|
||||
console.log('set comment');
|
||||
console.log('comment', text);
|
||||
|
||||
this.$store.dispatch('setComment', { conc: this.conc, comment: text });
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
toggleHolder() {
|
||||
this.$store.dispatch('toggleHolder', this.conc);
|
||||
},
|
||||
removePosition() {
|
||||
this.$store.dispatch('removePosition', this.conc);
|
||||
},
|
||||
removeConcerned() {
|
||||
this.$store.dispatch('removeConcerned', this.conc);
|
||||
},
|
||||
}
|
||||
|
||||
};
|
||||
</script>
|
@@ -0,0 +1,16 @@
|
||||
import { createApp } from 'vue';
|
||||
import { _createI18n } from 'ChillMainAssets/vuejs/_js/i18n';
|
||||
import { appMessages } from './js/i18n';
|
||||
import { store } from './store';
|
||||
|
||||
import App from './App.vue';
|
||||
|
||||
const i18n = _createI18n(appMessages);
|
||||
|
||||
const app = createApp({
|
||||
template: `<app></app>`,
|
||||
})
|
||||
.use(store)
|
||||
.use(i18n)
|
||||
.component('app', App)
|
||||
.mount('#household_members_editor');
|
@@ -0,0 +1,52 @@
|
||||
|
||||
import { personMessages } from 'ChillPersonAssets/vuejs/_js/i18n'
|
||||
|
||||
const appMessages = {
|
||||
fr: {
|
||||
household_members_editor: {
|
||||
household: {
|
||||
no_household_choose_one: "Aucun ménage de destination. Choisissez un ménage.",
|
||||
new_household: "Nouveau ménage",
|
||||
create_household: "Créer un ménage",
|
||||
search_household: "Chercher un ménage",
|
||||
will_leave_any_household: "Ne rejoignent pas de ménage",
|
||||
leave_without_household: "Sans nouveau ménage"
|
||||
},
|
||||
concerned: {
|
||||
title: "Usagers concernés",
|
||||
add_persons: "Ajouter d'autres usagers",
|
||||
search: "Rechercher des usagers",
|
||||
move_to: "Déplacer vers",
|
||||
persons_to_positionnate: 'Usagers à positionner',
|
||||
persons_leaving: "Usagers quittant leurs ménages",
|
||||
},
|
||||
drop_persons_here: "Glissez-déposez ici les usagers pour la position \"{position}\"",
|
||||
all_positionnated: "Tous les usagers sont positionnés",
|
||||
holder: "Titulaire",
|
||||
is_holder: "Sera titulaire",
|
||||
is_not_holder: "Ne sera pas titulaire",
|
||||
remove_position: "Retirer des {position}",
|
||||
remove_concerned: "Ne plus transférer",
|
||||
household_part: "Ménage de destination",
|
||||
dates_title: "Période de validité",
|
||||
dates: {
|
||||
start_date: "Début de validité",
|
||||
end_date: "Fin de validité",
|
||||
},
|
||||
confirmation: {
|
||||
save: "Enregistrer",
|
||||
there_are_warnings: "Impossible de valider actuellement",
|
||||
check_those_items: "Veuillez corriger les éléments suivants",
|
||||
},
|
||||
give_a_position_to_every_person: "Indiquez une position pour chaque usager concerné",
|
||||
add_destination: "Indiquez un ménage de destination",
|
||||
add_at_least_onePerson: "Indiquez au moins un usager à transférer",
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Object.assign(appMessages.fr, personMessages.fr);
|
||||
|
||||
export {
|
||||
appMessages
|
||||
};
|
@@ -0,0 +1,239 @@
|
||||
import { createStore } from 'vuex';
|
||||
import { householdMove, householdMoveTest } from './../api.js';
|
||||
import { datetimeToISO } from 'ChillMainAssets/js/date.js';
|
||||
|
||||
const debug = process.env.NODE_ENV !== 'production';
|
||||
|
||||
const concerned = window.household_members_editor_data.persons.map(p => {
|
||||
return {
|
||||
person: p,
|
||||
position: null,
|
||||
allowRemove: false,
|
||||
holder: false,
|
||||
comment: "",
|
||||
};
|
||||
});
|
||||
|
||||
const store = createStore({
|
||||
strict: debug,
|
||||
state: {
|
||||
concerned,
|
||||
household: window.household_members_editor_data.household,
|
||||
positions: window.household_members_editor_data.positions,
|
||||
startDate: new Date(),
|
||||
allowHouseholdCreate: window.household_members_editor_data.allowHouseholdCreate,
|
||||
allowHouseholdSearch: window.household_members_editor_data.allowHouseholdSearch,
|
||||
allowLeaveWithoutHousehold: window.household_members_editor_data.allowLeaveWithoutHousehold,
|
||||
forceLeaveWithoutHousehold: false,
|
||||
warnings: [],
|
||||
},
|
||||
getters: {
|
||||
isHouseholdNew(state) {
|
||||
return !Number.isInteger(state.household.id);
|
||||
},
|
||||
hasHousehold(state) {
|
||||
return state.household !== null;
|
||||
},
|
||||
persons(state) {
|
||||
return state.concerned.map(conc => conc.person);
|
||||
},
|
||||
concUnpositionned(state) {
|
||||
return state.concerned
|
||||
.filter(conc => conc.position === null)
|
||||
;
|
||||
},
|
||||
positions(state) {
|
||||
return state.positions;
|
||||
},
|
||||
personByPosition: (state) => (position_id) => {
|
||||
return state.concerned
|
||||
.filter(conc =>
|
||||
conc.position !== null ? conc.position.id === position_id : false
|
||||
)
|
||||
.map(conc => conc.person)
|
||||
;
|
||||
},
|
||||
concByPosition: (state) => (position_id) => {
|
||||
return state.concerned
|
||||
.filter(conc =>
|
||||
conc.position !== null ? conc.position.id === position_id : false
|
||||
)
|
||||
;
|
||||
},
|
||||
concByPersonId: (state) => (person_id) => {
|
||||
return state.concerned
|
||||
.find(conc => conc.person.id === person_id)
|
||||
;
|
||||
},
|
||||
needsPositionning(state) {
|
||||
return state.forceLeaveWithoutHousehold === false;
|
||||
},
|
||||
buildPayload: (state) => {
|
||||
let
|
||||
conc,
|
||||
payload_conc,
|
||||
payload = {
|
||||
concerned: [],
|
||||
destination: null
|
||||
}
|
||||
;
|
||||
|
||||
if (state.forceLeaveWithoutHousehold === false) {
|
||||
payload.destination = {
|
||||
id: state.household.id,
|
||||
type: state.household.type
|
||||
};
|
||||
}
|
||||
|
||||
for (let i in state.concerned) {
|
||||
conc = state.concerned[i];
|
||||
payload_conc = {
|
||||
person: {
|
||||
id: conc.person.id,
|
||||
type: conc.person.type
|
||||
},
|
||||
start_date: {
|
||||
datetime: datetimeToISO(state.startDate)
|
||||
}
|
||||
};
|
||||
|
||||
if (state.forceLeaveWithoutHousehold === false) {
|
||||
payload_conc.position = {
|
||||
id: conc.position.id,
|
||||
type: conc.position.type
|
||||
};
|
||||
payload_conc.holder = conc.holder;
|
||||
payload_conc.comment = conc.comment;
|
||||
}
|
||||
|
||||
payload.concerned.push(payload_conc);
|
||||
}
|
||||
|
||||
return payload;
|
||||
},
|
||||
},
|
||||
mutations: {
|
||||
addConcerned(state, person) {
|
||||
let persons = state.concerned.map(conc => conc.person.id);
|
||||
if (!persons.includes(person.id)) {
|
||||
state.concerned.push({
|
||||
person,
|
||||
position: null,
|
||||
allowRemove: true,
|
||||
holder: false,
|
||||
comment: "",
|
||||
});
|
||||
} else {
|
||||
console.err("person already included");
|
||||
}
|
||||
},
|
||||
markPosition(state, { person_id, position_id}) {
|
||||
let
|
||||
position = state.positions.find(pos => pos.id === position_id),
|
||||
conc = state.concerned.find(c => c.person.id === person_id);
|
||||
conc.position = position;
|
||||
},
|
||||
setComment(state, {conc, comment}) {
|
||||
conc.comment = comment;
|
||||
},
|
||||
toggleHolder(state, conc) {
|
||||
conc.holder = !conc.holder;
|
||||
},
|
||||
removePosition(state, conc) {
|
||||
conc.holder = false;
|
||||
conc.position = null;
|
||||
},
|
||||
removeConcerned(state, conc) {
|
||||
state.concerned = state.concerned.filter(c =>
|
||||
c.person.id !== conc.person.id
|
||||
)
|
||||
},
|
||||
createHousehold(state) {
|
||||
state.household = { type: 'household', members: [], address: null }
|
||||
state.forceLeaveWithoutHousehold = false;
|
||||
},
|
||||
forceLeaveWithoutHousehold(state) {
|
||||
state.household = null;
|
||||
state.forceLeaveWithoutHousehold = true;
|
||||
},
|
||||
setStartDate(state, dateI) {
|
||||
state.startDate = dateI;
|
||||
},
|
||||
setWarnings(state, warnings) {
|
||||
state.warnings = warnings;
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
addConcerned({ commit, dispatch }, person) {
|
||||
commit('addConcerned', person);
|
||||
dispatch('computeWarnings');
|
||||
},
|
||||
markPosition({ commit, state, dispatch }, { person_id, position_id }) {
|
||||
commit('markPosition', { person_id, position_id });
|
||||
dispatch('computeWarnings');
|
||||
},
|
||||
toggleHolder({ commit }, conc) {
|
||||
commit('toggleHolder', conc);
|
||||
},
|
||||
removePosition({ commit, dispatch }, conc) {
|
||||
commit('removePosition', conc);
|
||||
dispatch('computeWarnings');
|
||||
},
|
||||
removeConcerned({ commit, dispatch }, conc) {
|
||||
commit('removeConcerned', conc);
|
||||
dispatch('computeWarnings');
|
||||
},
|
||||
createHousehold({ commit, dispatch }) {
|
||||
commit('createHousehold');
|
||||
dispatch('computeWarnings');
|
||||
},
|
||||
forceLeaveWithoutHousehold({ commit, dispatch }) {
|
||||
commit('forceLeaveWithoutHousehold');
|
||||
dispatch('computeWarnings');
|
||||
},
|
||||
setStartDate({ commit }, date) {
|
||||
commit('setStartDate', date);
|
||||
},
|
||||
setComment({ commit }, payload) {
|
||||
commit('setComment', payload);
|
||||
},
|
||||
computeWarnings({ commit, state, getters }) {
|
||||
let warnings = [],
|
||||
payload;
|
||||
|
||||
if (!getters.hasHousehold && !state.forceLeaveWithoutHousehold) {
|
||||
warnings.push({ m: 'household_members_editor.add_destination', a: {} });
|
||||
}
|
||||
|
||||
if (state.concerned.length === 0) {
|
||||
warnings.push({ m: 'household_members_editor.add_at_least_onePerson', a: {} });
|
||||
}
|
||||
|
||||
if (getters.concUnpositionned.length > 0
|
||||
&& !state.forceLeaveWithoutHousehold) {
|
||||
warnings.push({ m: 'household_members_editor.give_a_position_to_every_person', a: {} })
|
||||
}
|
||||
|
||||
commit('setWarnings', warnings);
|
||||
},
|
||||
confirm({ getters, state }) {
|
||||
let payload = getters.buildPayload,
|
||||
person_id,
|
||||
household_id;
|
||||
householdMove(payload).then(household => {
|
||||
if (household === null) {
|
||||
person_id = getters.persons[0].id;
|
||||
window.location.replace(`/fr/person/${person_id}/general`);
|
||||
} else {
|
||||
household_id = household.id;
|
||||
// nothing to do anymore here, bye-bye !
|
||||
window.location.replace(`/fr/person/household/${household_id}/members`);
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
store.dispatch('computeWarnings');
|
||||
|
||||
export { store };
|
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* GET a person by id
|
||||
*/
|
||||
const getPerson = (id) => {
|
||||
const url = `/api/1.0/person/person/${id}.json`;
|
||||
return fetch(url)
|
||||
.then(response => {
|
||||
if (response.ok) { return response.json(); }
|
||||
throw Error('Error with request resource response');
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
* POST a new person
|
||||
*/
|
||||
const postPerson = (body) => {
|
||||
const url = `/api/1.0/person/person.json`;
|
||||
return fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json;charset=utf-8'
|
||||
},
|
||||
body: JSON.stringify(body)
|
||||
})
|
||||
.then(response => {
|
||||
if (response.ok) { return response.json(); }
|
||||
throw Error('Error with request resource response');
|
||||
});
|
||||
};
|
||||
|
||||
export {
|
||||
getPerson,
|
||||
postPerson
|
||||
};
|
@@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<ul class="record_actions">
|
||||
<li>
|
||||
<button class="sc-button bt-create" @click="openModal">
|
||||
<li class="add-persons">
|
||||
<a class="sc-button bt-create" @click="openModal">
|
||||
{{ $t(buttonTitle) }}
|
||||
</button>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -65,9 +65,14 @@
|
||||
@updateSelected="updateSelected">
|
||||
</person-suggestion>
|
||||
|
||||
<button v-if="query.length >= 3" class="sc-button bt-create ml-5 mt-2" name="createPerson">
|
||||
{{ $t('action.create') }} "{{ query }}"
|
||||
</button>
|
||||
<div class="create-button">
|
||||
<on-the-fly
|
||||
v-if="query.length >= 3"
|
||||
v-bind:buttonText="$t('onthefly.create.button', {q: query})"
|
||||
action="create"><!-- TODO first close this modal -->
|
||||
</on-the-fly>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -84,6 +89,7 @@
|
||||
|
||||
<script>
|
||||
import Modal from 'ChillMainAssets/vuejs/_components/Modal';
|
||||
import OnTheFly from 'ChillMainAssets/vuejs/_components/OnTheFly.vue';
|
||||
import PersonSuggestion from './AddPersons/PersonSuggestion';
|
||||
import { searchPersons, searchPersons_2 } from 'ChillPersonAssets/vuejs/_api/AddPersons';
|
||||
|
||||
@@ -92,6 +98,7 @@ export default {
|
||||
components: {
|
||||
Modal,
|
||||
PersonSuggestion,
|
||||
OnTheFly
|
||||
},
|
||||
props: [
|
||||
'buttonTitle',
|
||||
@@ -170,7 +177,7 @@ export default {
|
||||
if (query.length >= 3) {
|
||||
searchPersons_2({ query, options: this.options })
|
||||
.then(suggested => new Promise((resolve, reject) => {
|
||||
console.log('suggested', suggested);
|
||||
//console.log('suggested', suggested);
|
||||
this.loadSuggestions(suggested.results);
|
||||
resolve();
|
||||
}));
|
||||
@@ -179,14 +186,14 @@ export default {
|
||||
}
|
||||
},
|
||||
loadSuggestions(suggested) {
|
||||
console.log('suggested', suggested);
|
||||
//console.log('suggested', suggested);
|
||||
this.search.suggested = suggested;
|
||||
this.search.suggested.forEach(function(item) {
|
||||
item.key = this.itemKey(item);
|
||||
}, this);
|
||||
},
|
||||
updateSelected(value) {
|
||||
console.log('value', value);
|
||||
//console.log('value', value);
|
||||
this.search.selected = value;
|
||||
},
|
||||
resetSearch() {
|
||||
@@ -213,6 +220,11 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
li.add-persons {
|
||||
a {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
div.body-head {
|
||||
overflow-y: unset;
|
||||
div.modal-body:first-child {
|
||||
@@ -251,4 +263,8 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
.create-button > a {
|
||||
margin-top: 0.5em;
|
||||
margin-left: 2.6em;
|
||||
}
|
||||
</style>
|
||||
|
@@ -42,7 +42,7 @@ export default {
|
||||
computed: {
|
||||
selected: {
|
||||
set(value) {
|
||||
console.log('value', value);
|
||||
//console.log('value', value);
|
||||
this.$emit('updateSelected', value);
|
||||
},
|
||||
get() {
|
||||
|
@@ -14,22 +14,23 @@
|
||||
<span class="badge badge-pill badge-secondary" :title="item.key">
|
||||
{{ $t('item.type_person') }}
|
||||
</span>
|
||||
<a class="sc-button bt-show" target="_blank" :title="item.key" :href="url.show"></a>
|
||||
<on-the-fly
|
||||
type="person"
|
||||
v-bind:id="item.result.id"
|
||||
action="show">
|
||||
</on-the-fly>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import OnTheFly from 'ChillMainAssets/vuejs/_components/OnTheFly.vue';
|
||||
|
||||
export default {
|
||||
name: 'SuggestionPerson',
|
||||
props: ['item'],
|
||||
data() {
|
||||
return {
|
||||
url: {
|
||||
show: '/fr/person/' + this.item.result.person_id + '/general',
|
||||
edit: '/fr/person/' + this.item.result.person_id + '/general/edit'
|
||||
},
|
||||
}
|
||||
components: {
|
||||
OnTheFly
|
||||
},
|
||||
props: ['item']
|
||||
}
|
||||
</script>
|
||||
|
@@ -15,22 +15,23 @@
|
||||
<span class="badge badge-pill badge-secondary" :title="item.key">
|
||||
{{ $t('item.type_thirdparty') }}
|
||||
</span>
|
||||
<a class="sc-button bt-show" target="_blank" :title="item.key" :href="url.show"></a>
|
||||
<on-the-fly
|
||||
type="thirdparty"
|
||||
v-bind:id="item.result.id"
|
||||
action="show">
|
||||
</on-the-fly>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import OnTheFly from 'ChillMainAssets/vuejs/_components/OnTheFly.vue';
|
||||
|
||||
export default {
|
||||
name: 'SuggestionThirdParty',
|
||||
props: ['item'],
|
||||
data() {
|
||||
return {
|
||||
url: {
|
||||
show: '/fr/thirdparty/thirdparty/' + this.item.result.thirdparty_id + '/show',
|
||||
edit: '/fr/thirdparty/thirdparty/' + this.item.result.thirdparty_id + '/edit'
|
||||
},
|
||||
}
|
||||
components: {
|
||||
OnTheFly
|
||||
},
|
||||
props: ['item']
|
||||
}
|
||||
</script>
|
||||
|
@@ -0,0 +1,200 @@
|
||||
<template>
|
||||
<div v-if="action === 'show'">
|
||||
|
||||
<div class="flex-table">
|
||||
<div class="item-bloc">
|
||||
<div class="item-row">
|
||||
<div class="item-col">
|
||||
<h3 :title="person.id">{{ person.text }}</h3>
|
||||
<p>
|
||||
<i class="fa fa-fw"
|
||||
:class="genderClass">
|
||||
<!--
|
||||
:title="$t(genderTranslation)"
|
||||
-->
|
||||
</i>
|
||||
<span v-if="person.birthdate">
|
||||
{{ $t('person.born', { e: feminized }) }}
|
||||
{{ $d(person.birthdate.datetime, 'short') }}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="item-col">
|
||||
<dl class="list-content">
|
||||
<dt>{{ $t('person.firstname') }}</dt>
|
||||
<dd>{{ person.firstName }}</dd>
|
||||
|
||||
<dt>{{ $t('person.lastname') }}</dt>
|
||||
<dd>{{ person.lastName }}</dd>
|
||||
|
||||
<dt>{{ $t('person.altnames') }}</dt>
|
||||
<dd>{{ person.altNames }}</dd>
|
||||
|
||||
<span v-if="person.center">
|
||||
<dt>{{ $t('person.center_name') }}</dt>
|
||||
<dd :title="person.center.id">{{ person.center.name }}</dd>
|
||||
</span>
|
||||
|
||||
<dt>{{ $t('person.phonenumber') }}</dt>
|
||||
<dd>{{ person.phonenumber }}</dd>
|
||||
|
||||
<dt>{{ $t('person.mobilenumber') }}</dt>
|
||||
<dd>{{ person.mobilenumber }}</dd>
|
||||
|
||||
<dt>{{ $t('person.gender.title') }}</dt>
|
||||
<!--
|
||||
<dd>{{ $t(genderTranslation) }}</dd>
|
||||
-->
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div v-else-if="action === 'edit' || action === 'create'">
|
||||
|
||||
<input v-model="firstName" :placeholder="$t('person.firstname')" />
|
||||
<input v-model="lastName" :placeholder="$t('person.lastname')" />
|
||||
|
||||
<!-- TODO fix placeholder if undefined
|
||||
TODO dynamically get gender options
|
||||
-->
|
||||
<select v-model="gender">
|
||||
<option disabled value="">{{ $t('person.gender.placeholder') }}</option>
|
||||
<option value="woman">{{ $t('person.gender.woman') }}</option>
|
||||
<option value="man">{{ $t('person.gender.man') }}</option>
|
||||
<option value="neuter">{{ $t('person.gender.neuter') }}</option>
|
||||
</select>
|
||||
|
||||
<i class="fa fa-birthday-cake"></i>
|
||||
<input type="date"
|
||||
id="chill_personbundle_person_birthdate"
|
||||
name="chill_personbundle_person[birthdate]"
|
||||
v-model="birthDate"
|
||||
/>
|
||||
|
||||
<i class="fa fa-phone">
|
||||
</i><input v-model="phonenumber" :placeholder="$t('person.phonenumber')" />
|
||||
<i class="fa fa-mobile">
|
||||
</i><input v-model="mobilenumber" :placeholder="$t('person.mobilenumber')" />
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getPerson, postPerson } from '../../_api/OnTheFly';
|
||||
|
||||
export default {
|
||||
name: "OnTheFlyPerson",
|
||||
props: ['id', 'type', 'action'],
|
||||
data() {
|
||||
return {
|
||||
person: {
|
||||
type: 'person'
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
firstName: {
|
||||
set(value) { this.person.firstName = value; },
|
||||
get() { return this.person.firstName; }
|
||||
},
|
||||
lastName: {
|
||||
set(value) { this.person.lastName = value; },
|
||||
get() { return this.person.lastName; }
|
||||
},
|
||||
gender: {
|
||||
set(value) { this.person.gender = value; },
|
||||
get() { return this.person.gender; }
|
||||
},
|
||||
birthDate: {
|
||||
set(value) {
|
||||
if (this.person.birthdate) {
|
||||
this.person.birthdate.datetime = value + "T00:00:00+0100";
|
||||
} else {
|
||||
this.person.birthdate = { datetime: value + "T00:00:00+0100"};
|
||||
}
|
||||
},
|
||||
get() {
|
||||
return (this.person.birthdate) ? this.person.birthdate.datetime.split('T')[0] : '';
|
||||
}
|
||||
},
|
||||
phonenumber: {
|
||||
set(value) { this.person.phonenumber = value; },
|
||||
get() { return this.person.phonenumber; }
|
||||
},
|
||||
mobilenumber: {
|
||||
set(value) { this.person.mobilenumber = value; },
|
||||
get() { return this.person.mobilenumber; }
|
||||
},
|
||||
genderClass() {
|
||||
switch (this.person.gender) {
|
||||
case 'woman':
|
||||
return 'fa-venus';
|
||||
case 'man':
|
||||
return 'fa-mars';
|
||||
case 'neuter':
|
||||
return 'fa-neuter';
|
||||
}
|
||||
},
|
||||
genderTranslation() {
|
||||
switch (this.person.gender) {
|
||||
case 'woman':
|
||||
return 'person.gender.woman';
|
||||
case 'man':
|
||||
return 'person.gender.man';
|
||||
case 'neuter':
|
||||
return 'person.gender.neuter';
|
||||
}
|
||||
},
|
||||
feminized() {
|
||||
return (this.person.gender === 'woman')? 'e' : '';
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (this.action !== 'create') {
|
||||
this.loadData();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
loadData() {
|
||||
getPerson(this.id)
|
||||
.then(person => new Promise((resolve, reject) => {
|
||||
this.person = person;
|
||||
console.log('get person', this.person);
|
||||
resolve();
|
||||
}));
|
||||
},
|
||||
postData() {
|
||||
postPerson(this.person)
|
||||
.then(person => new Promise((resolve, reject) => {
|
||||
this.person = person;
|
||||
console.log('post person', person);
|
||||
resolve();
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
ul {
|
||||
li::marker {
|
||||
}
|
||||
}
|
||||
div.flex-table {
|
||||
div.item-bloc {
|
||||
div.item-row {
|
||||
div.item-col:last-child {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dl {
|
||||
dd {
|
||||
margin-left: 1em;
|
||||
}
|
||||
}
|
||||
</style>
|
@@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<span class="chill-entity chill-entity__person">
|
||||
<span class="chill-entity__person__text">
|
||||
{{ person.text }}
|
||||
</span>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'Person',
|
||||
props: ['person']
|
||||
}
|
||||
|
||||
</script>
|
@@ -15,7 +15,29 @@ const personMessages = {
|
||||
person: {
|
||||
firstname: "Prénom",
|
||||
lastname: "Nom",
|
||||
born: "né le ",
|
||||
born: (ctx) => {
|
||||
if (ctx.gender === 'man') {
|
||||
return 'Né le';
|
||||
} else if (ctx.gender === 'woman') {
|
||||
return 'Née le';
|
||||
} else {
|
||||
return 'Né·e le';
|
||||
}
|
||||
},
|
||||
center_id: "Identifiant du centre",
|
||||
center_type: "Type de centre",
|
||||
center_name: "Territoire", // vendée
|
||||
phonenumber: "Téléphone",
|
||||
mobilenumber: "Mobile",
|
||||
altnames: "Autres noms",
|
||||
gender: {
|
||||
title: "Genre",
|
||||
placeholder: "Choisissez le genre de l'usager",
|
||||
woman: "Femme",
|
||||
man: "Homme",
|
||||
neuter: "Neutre",
|
||||
}
|
||||
|
||||
},
|
||||
error_only_one_person: "Une seule personne peut être sélectionnée !"
|
||||
}
|
||||
|
@@ -15,5 +15,5 @@
|
||||
window.accompanyingCourseId = {{ accompanyingCourse.id|e('js') }};
|
||||
window.vueRootComponent = 'app';
|
||||
</script>
|
||||
{{ encore_entry_script_tags('accompanying_course') }}
|
||||
{{ encore_entry_script_tags('vue_accourse') }}
|
||||
{% endblock %}
|
||||
|
@@ -31,7 +31,7 @@
|
||||
{% set gender = (p.person.gender == 'woman') ? 'fa-venus' :
|
||||
(p.person.gender == 'man') ? 'fa-mars' : 'fa-neuter' %}
|
||||
{% set genderTitle = (p.person.gender == 'woman') ? 'femme' :
|
||||
(p.person.gender == 'homme') ? 'fa-mars' : 'neutre' %}
|
||||
(p.person.gender == 'man') ? 'homme' : 'neutre' %}
|
||||
<i class="fa fa-fw {{ gender }}" title="{{ genderTitle }}"></i>{{ born ~ ' le ' ~ p.person.birthdate|format_date('short') }}
|
||||
</p>
|
||||
</div>
|
||||
@@ -62,6 +62,19 @@
|
||||
<li>
|
||||
<a href="{{ path('chill_person_view', { person_id: p.person.id }) }}" class="sc-button bt-show" target="_blank" title="Voir"></a>
|
||||
</li>
|
||||
{% if p.person.isSharingHousehold %}
|
||||
<li>
|
||||
<a
|
||||
href="{{ chill_path_add_return_path(
|
||||
'chill_person_household_summary',
|
||||
{ 'household_id': p.person.getCurrentHousehold.id }
|
||||
) }}"
|
||||
class="sc-button">
|
||||
<i class="fa fa-home"></i>
|
||||
{{ 'household.Household file'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -16,7 +16,7 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block css %}
|
||||
{{ encore_entry_link_tags('accompanying_course') }}
|
||||
{{ encore_entry_link_tags('vue_accourse') }}
|
||||
{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
@@ -24,5 +24,5 @@
|
||||
window.accompanyingCourseId = {{ accompanyingCourse.id|e('js') }};
|
||||
window.vueRootComponent = 'banner';
|
||||
</script>
|
||||
{{ encore_entry_script_tags('accompanying_course') }}
|
||||
{{ encore_entry_script_tags('vue_accourse') }}
|
||||
{% endblock %}
|
||||
|
@@ -1,15 +1,17 @@
|
||||
<span class="chill-entity chill-entity__person">
|
||||
{%- if addLink and is_granted('CHILL_PERSON_SEE', person) -%}
|
||||
{%- set showLink = true -%}<a href="{{ chill_path_add_return_path('chill_person_view', { 'person_id': person.id }) }}">{%- endif -%}
|
||||
<span class="chill-entity__person__first-name">{{ person.firstName }}</span>
|
||||
<span class="chill-entity__person__last-name">{{ person.lastName }}</span>
|
||||
<span class="chill_denomination">{{ person.firstName }}</span>
|
||||
<span class="chill_denomination">{{ person.lastName }}</span>
|
||||
{%- if addAltNames -%}
|
||||
{%- for n in person.altNames -%}
|
||||
{%- if loop.first -%}({% else %} {%- endif -%}
|
||||
<span class="chill-entity__person__alt-name chill-entity__person__altname--{{ n.key }}">
|
||||
{{ n.label }}
|
||||
{{- n.label -}}
|
||||
</span>
|
||||
{%- if loop.last %}) {% endif -%}
|
||||
{%- if loop.last -%}) {%- endif -%}
|
||||
{%- endfor -%}
|
||||
{%- endif -%}
|
||||
{%- if showLink is defined -%}</a>{%- endif -%}</span>
|
||||
{%- if showLink is defined -%}</a>{%- endif -%}
|
||||
{#- tricks to remove easily whitespace after template -#}
|
||||
{%- if true -%}</span>{%- endif -%}
|
||||
|
@@ -0,0 +1,13 @@
|
||||
{% set reversed_parents = parents|reverse %}
|
||||
<span class="chill-entity chill-entity__social-action">
|
||||
<span class="badge badge-primary">
|
||||
{%- for p in reversed_parents %}
|
||||
<span class="chill-entity__social-action__parent--{{ loop.revindex0 }}">
|
||||
{{ p.title|localize_translatable_string }}{{ options['default.separator'] }}
|
||||
</span>
|
||||
{%- endfor -%}
|
||||
<span class="chill-entity__social-action__child">
|
||||
{{ socialAction.title|localize_translatable_string }}
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
@@ -0,0 +1,28 @@
|
||||
{% extends '@ChillPerson/Household/layout.html.twig' %}
|
||||
|
||||
{% block title 'household.Edit member household'|trans %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h1>{{ block('title') }}</h1>
|
||||
|
||||
{{ form_start(form) }}
|
||||
{{ form_widget(form) }}
|
||||
|
||||
<ul class="record_actions sticky-form-buttons">
|
||||
<li class="cancel">
|
||||
<a
|
||||
href="{{ chill_return_path_or('chill_person_household_members', { 'household_id': household.id}) }}"
|
||||
class="sc-button bt-cancel"
|
||||
>
|
||||
{{ 'Cancel'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<button type="submit" class="sc-button bt-save">{{ 'Save'|trans }}</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
{{ form_end(form) }}
|
||||
|
||||
{% endblock %}
|
@@ -1,11 +1,11 @@
|
||||
<div class="subheader">
|
||||
<div class="subheader banner-household">
|
||||
<div class="grid-12 parent" id="header-accompanying_course-name" >
|
||||
<div class="grid-10 push-1 grid-mobile-12 grid-tablet-12 push-mobile-0 push-tablet-0 parent">
|
||||
|
||||
<div class="grid-6">{% set title = title %}
|
||||
<h1>
|
||||
<i class="fa fa-child"></i>
|
||||
{{ 'Household'|trans }}
|
||||
<i class="fa fa-home"></i>
|
||||
{{ 'household.Household'|trans }}
|
||||
<span style="font-weight: lighter; font-size: 50%;">(n°{{ household.id }})</span>
|
||||
</h1>
|
||||
</div>
|
||||
@@ -18,9 +18,23 @@
|
||||
</div>
|
||||
<div class="grid-12 parent" id="header-accompanying_course-details" >
|
||||
<div class="grid-10 push-1 grid-mobile-12 grid-tablet-12 push-mobile-0 push-tablet-0 parent">
|
||||
|
||||
<div id="banner-misc"></div>
|
||||
|
||||
<div id="banner-misc">
|
||||
{%- set persons = household.getCurrentPersons() -%}
|
||||
{%- if persons|length > 0 -%}
|
||||
<span class="current-members-explain">
|
||||
{{- 'household.Current household members'|trans }}:
|
||||
</span>
|
||||
{%- for p in persons|slice(0, 5) -%}
|
||||
{{- p|chill_entity_render_box({'addLink': false}) -}}
|
||||
{%- if false == loop.last -%}, {% endif -%}
|
||||
{%- endfor -%}
|
||||
{% if persons|length > 5 %}
|
||||
<span class="current-members-more">
|
||||
{{ 'household.and x other persons'|trans({'x': persons|length-5}) }}
|
||||
</span>
|
||||
{% endif %}
|
||||
{%- endif -%}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -1,10 +1,151 @@
|
||||
{% extends '@ChillPerson/Household/layout.html.twig' %}
|
||||
|
||||
{% block title 'Household members'|trans %}
|
||||
{% block title 'household.Household members'|trans %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{{ block('title') }}</h1>
|
||||
|
||||
<p>Household with id {{ household.id }}</p>
|
||||
{% for p in positions %}
|
||||
<h3>{{ p.label|localize_translatable_string }}</h3>
|
||||
|
||||
{% if false == p.shareHousehold %}
|
||||
<p>{{ 'household.Those members does not share address'|trans }}</p>
|
||||
{% endif %}
|
||||
|
||||
{%- set members = household.currentMembersByPosition(p) %}
|
||||
{% if members|length > 0 %}
|
||||
<div class="flex-table list-household-members">
|
||||
{% for m in members %}
|
||||
<div class="item-bloc">
|
||||
<div class="item-row person">
|
||||
<div class="item-col box-person">
|
||||
<div>
|
||||
{{ m.person|chill_entity_render_box({'addLink': true}) }}
|
||||
{% if m.holder %}
|
||||
<span class="badge badge-primary">{{ 'household.holder'|trans }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div>
|
||||
{{ 'Born the date'|trans({ 'gender': m.person.gender, 'birthdate': m.person.birthdate|format_date('long') }) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-col box-where">
|
||||
<ul class="list-content fa-ul">
|
||||
{% if m.startDate is not empty %}
|
||||
<li>{{ 'Since %date%'|trans({'%date%': m.startDate|format_date('long') }) }}</li>
|
||||
{% endif %}
|
||||
{% if m.endDate is not empty %}
|
||||
<li>{{ 'Until %date%'|trans({'%date%': m.endDate|format_date('long') }) }}</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
<ul class="record_actions">
|
||||
<li>
|
||||
<a
|
||||
href="{{ chill_path_add_return_path('chill_person_household_member_edit', { 'id': m.id }) }}"
|
||||
class="sc-button bt-edit"
|
||||
/>
|
||||
{{ 'household.Update membership'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="{{ chill_path_add_return_path(
|
||||
'chill_person_household_members_editor',
|
||||
{
|
||||
'persons': [ m.person.id ],
|
||||
'allow_leave_without_household': true
|
||||
} ) }}"
|
||||
class="sc-button"
|
||||
/>
|
||||
<i class="fa fa-sign-out"></i>
|
||||
{{ 'household.Leave'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% if m.comment is not empty %}
|
||||
<div class="item-row comment">
|
||||
<blockquote class="chill-user-quote">
|
||||
{{ m.comment|chill_markdown_to_html }}
|
||||
</blockquote>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% 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 %}
|
||||
<p><!-- force a space after table --></p>
|
||||
<button class="sc-button bt-green" type="button" data-toggle="collapse" data-target="#nonCurrent_{{ p.id }}" aria-expanded="false" aria-controls="collapse non current members">
|
||||
{{ 'household.Show future or past memberships'|trans({'length': members|length}) }}
|
||||
</button>
|
||||
|
||||
<div id="nonCurrent_{{ p.id }}" class="collapse">
|
||||
<div class="flex-table list-household-members">
|
||||
{% for m in members %}
|
||||
<div class="item-bloc">
|
||||
<div class="item-row person">
|
||||
<div class="item-col box-person">
|
||||
<div>
|
||||
{{ m.person|chill_entity_render_box({'addLink': true}) }}
|
||||
{% if m.holder %}
|
||||
<span class="badge badge-primary">{{ 'household.holder'|trans }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div>
|
||||
{{ 'Born the date'|trans({ 'gender': m.person.gender, 'birthdate': m.person.birthdate|format_date('long') }) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-col box-where">
|
||||
<ul class="list-content fa-ul">
|
||||
{% if m.startDate is not empty %}
|
||||
<li>{{ 'Since %date%'|trans({'%date%': m.startDate|format_date('long') }) }}</li>
|
||||
{% endif %}
|
||||
{% if m.endDate is not empty %}
|
||||
<li>{{ 'Until %date%'|trans({'%date%': m.endDate|format_date('long') }) }}</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
<ul class="record_actions">
|
||||
<li>
|
||||
<a
|
||||
href="{{ chill_path_add_return_path('chill_person_household_member_edit', { 'id': m.id }) }}"
|
||||
class="sc-button bt-edit"
|
||||
/>
|
||||
{{ 'household.Update membership'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% if m.comment is not empty %}
|
||||
<div class="item-row comment">
|
||||
<blockquote class="chill-user-quote">
|
||||
{{ m.comment|chill_markdown_to_html }}
|
||||
</blockquote>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endif %}
|
||||
|
||||
{% endfor %}
|
||||
|
||||
<ul class="record_actions sticky-form-buttons">
|
||||
<li>
|
||||
<a
|
||||
href="{{ chill_path_add_return_path('chill_person_household_members_editor', {'household': household.id }) }}"
|
||||
class="sc-button bt-create">
|
||||
{{ 'household.Add a member'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
{% endblock %}
|
||||
|
@@ -0,0 +1,23 @@
|
||||
{% extends '@ChillMain/layout.html.twig' %}
|
||||
|
||||
{% block title 'household.Edit household members'|trans %}
|
||||
|
||||
{% block content %}
|
||||
<div class="grid-12 parent">
|
||||
<div class="grid-10 push-1 parent">
|
||||
<h1>{{ block('title') }}</h1>
|
||||
<div id="household_members_editor"></div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
<script type="text/javascript">
|
||||
window.household_members_editor_data = {{ data|json_encode|raw }};
|
||||
</script>
|
||||
{{ encore_entry_script_tags('household_members_editor') }}
|
||||
{% endblock %}
|
||||
|
||||
{% block css %}
|
||||
{{ encore_entry_link_tags('household_members_editor') }}
|
||||
{% endblock %}
|
@@ -1,14 +1,53 @@
|
||||
{% extends '@ChillPerson/Household/layout.html.twig' %}
|
||||
|
||||
{% block title 'Household summary'|trans %}
|
||||
{% block title 'household.Household summary'|trans %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{{ block('title') }}</h1>
|
||||
|
||||
<p>Household with id {{ household.id }}</p>
|
||||
<h2>{{ 'household.Current household members'|trans }}</h2>
|
||||
|
||||
<h2>{{ 'Actual household members'|trans }}</h2>
|
||||
{% for p in positions %}
|
||||
{%- set members = household.currentMembersByPosition(p) %}
|
||||
{% if members|length > 0 %}
|
||||
<h3>{{ p.label|localize_translatable_string }}</h3>
|
||||
|
||||
<p>TODO</p>
|
||||
{% if false == p.shareHousehold %}
|
||||
<p>{{ 'household.Those members does not share address'|trans }}</p>
|
||||
{% endif %}
|
||||
|
||||
<div class="flex-table list-household-members--summary">
|
||||
{% for m in members %}
|
||||
<div class="item-bloc">
|
||||
<div class="item-row person">
|
||||
<div class="item-col box-person">
|
||||
<div>
|
||||
{{ m.person|chill_entity_render_box({'addLink': true}) }}
|
||||
{% if m.holder %}
|
||||
<span class="badge badge-primary">{{ 'household.holder'|trans }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div>
|
||||
{{ 'Born the date'|trans({ 'gender': m.person.gender, 'birthdate': m.person.birthdate|format_date('long') }) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-col box-where">
|
||||
<ul class="list-content fa-ul">
|
||||
{% if m.startDate is not empty %}
|
||||
<li>{{ 'Since %date%'|trans({'%date%': m.startDate|format_date('long') }) }}</li>
|
||||
{% endif %}
|
||||
{% if m.endDate is not empty %}
|
||||
<li>{{ 'Until %date%'|trans({'%date%': m.endDate|format_date('long') }) }}</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
{% endif %}
|
||||
|
||||
{% endfor %}
|
||||
|
||||
{% endblock %}
|
||||
|
@@ -24,6 +24,58 @@ class MembersEditorNormalizer implements DenormalizerInterface, DenormalizerAwar
|
||||
}
|
||||
|
||||
public function denormalize($data, string $type, string $format = null, array $context = [])
|
||||
{
|
||||
// some test about schema first...
|
||||
$this->performChecks($data);
|
||||
|
||||
// route to "leave movement" (all concerned leave household)
|
||||
// or "move to another household" (all concerned go to another
|
||||
// household)
|
||||
if (NULL === $data['destination']) {
|
||||
return $this->denormalizeLeave($data, $type, $format, $context);
|
||||
} else {
|
||||
return $this->denormalizeMove($data, $type, $format, $context);
|
||||
}
|
||||
}
|
||||
|
||||
private function performChecks($data): void
|
||||
{
|
||||
if (NULL == $data['concerned'] ?? NULL
|
||||
&& FALSE === ·\is_array('concerned')) {
|
||||
throw new Exception\UnexpectedValueException("The schema does not have any key 'concerned'");
|
||||
}
|
||||
|
||||
if (FALSE === \array_key_exists('destination', $data)) {
|
||||
throw new Exception\UnexpectedValueException("The schema does not have any key 'destination'");
|
||||
}
|
||||
}
|
||||
|
||||
protected function denormalizeLeave($data, string $type, string $format, array $context = [])
|
||||
{
|
||||
$editor = $this->factory->createEditor(null);
|
||||
|
||||
foreach ($data['concerned'] as $key => $concerned) {
|
||||
$person = $this->denormalizer->denormalize($concerned['person'] ?? null, Person::class,
|
||||
$format, $context);
|
||||
$startDate = $this->denormalizer->denormalize($concerned['start_date'] ?? null, \DateTimeImmutable::class,
|
||||
$format, $context);
|
||||
|
||||
if (
|
||||
NULL === $person
|
||||
&& NULL === $startDate
|
||||
) {
|
||||
throw new Exception\InvalidArgumentException("position with ".
|
||||
"key $key could not be denormalized: missing ".
|
||||
"person or start_date.");
|
||||
}
|
||||
|
||||
$editor->leaveMovement($startDate, $person);
|
||||
}
|
||||
|
||||
return $editor;
|
||||
}
|
||||
|
||||
protected function denormalizeMove($data, string $type, string $format, array $context = [])
|
||||
{
|
||||
$household = $this->denormalizer->denormalize($data['destination'], Household::class,
|
||||
$format, $context);
|
||||
@@ -34,11 +86,6 @@ class MembersEditorNormalizer implements DenormalizerInterface, DenormalizerAwar
|
||||
|
||||
$editor = $this->factory->createEditor($household);
|
||||
|
||||
if (NULL == $data['concerned'] ?? []
|
||||
&& FALSE === ·\is_array('concerned')) {
|
||||
throw new Exception\UnexpectedValueException("The schema does not have any key 'concerned'");
|
||||
}
|
||||
|
||||
foreach ($data['concerned'] as $key => $concerned) {
|
||||
$person = $this->denormalizer->denormalize($concerned['person'] ?? null, Person::class,
|
||||
$format, $context);
|
||||
@@ -62,9 +109,9 @@ class MembersEditorNormalizer implements DenormalizerInterface, DenormalizerAwar
|
||||
|
||||
$editor->addMovement($startDate, $person, $position, $holder,
|
||||
$comment);
|
||||
|
||||
return $editor;
|
||||
}
|
||||
|
||||
return $editor;
|
||||
}
|
||||
|
||||
public function supportsDenormalization($data, string $type, string $format = null)
|
||||
|
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace Chill\PersonBundle\Templating\Entity;
|
||||
|
||||
use Chill\MainBundle\Templating\Entity\ChillEntityRenderInterface;
|
||||
use Chill\PersonBundle\Entity\SocialWork\SocialAction;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||
use Symfony\Component\Templating\EngineInterface;
|
||||
|
||||
class SocialActionRender implements ChillEntityRenderInterface
|
||||
{
|
||||
private TranslatableStringHelper $translatableStringHelper;
|
||||
private EngineInterface $engine;
|
||||
|
||||
public const SEPARATOR_KEY = 'default.separator';
|
||||
|
||||
public const DEFAULT_ARGS = [
|
||||
self::SEPARATOR_KEY => ' > ',
|
||||
];
|
||||
|
||||
public function __construct(TranslatableStringHelper $translatableStringHelper, EngineInterface $engine)
|
||||
{
|
||||
$this->translatableStringHelper = $translatableStringHelper;
|
||||
$this->engine = $engine;
|
||||
}
|
||||
|
||||
public function supports($entity, array $options): bool
|
||||
{
|
||||
return $entity instanceof SocialAction;
|
||||
}
|
||||
|
||||
public function renderString($socialAction, array $options): string
|
||||
{
|
||||
/** @var $socialAction SocialAction */
|
||||
$options = \array_merge(self::DEFAULT_ARGS, $options);
|
||||
|
||||
$str = $this->translatableStringHelper->localize($socialAction->getTitle());
|
||||
|
||||
while ($socialAction->hasParent()) {
|
||||
$socialAction = $socialAction->getParent();
|
||||
$str .= $options[self::SEPARATOR_KEY].$this->translatableStringHelper->localize(
|
||||
$socialAction->getTitle()
|
||||
);
|
||||
}
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
protected function buildParents($socialAction): array
|
||||
{
|
||||
$parents = [];
|
||||
while ($socialAction->hasParent()) {
|
||||
$socialAction = $parents[] = $socialAction->getParent();
|
||||
}
|
||||
|
||||
return $parents;
|
||||
}
|
||||
|
||||
public function renderBox($socialAction, array $options): string
|
||||
{
|
||||
$options = \array_merge(self::DEFAULT_ARGS, $options);
|
||||
// give some help to twig: an array of parents
|
||||
$parents = $this->buildParents($socialAction);
|
||||
|
||||
return $this->engine->render('@ChillPerson/Entity/social_action.html.twig', [
|
||||
'socialAction' => $socialAction,
|
||||
'parents' => $parents,
|
||||
'options' => $options
|
||||
]);
|
||||
}
|
||||
}
|
@@ -5,9 +5,10 @@ namespace Chill\PersonBundle\Templating\Entity;
|
||||
use Chill\MainBundle\Templating\Entity\ChillEntityRenderInterface;
|
||||
use Chill\PersonBundle\Entity\SocialWork\SocialIssue;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||
use RuntimeException;
|
||||
use Symfony\Component\Templating\EngineInterface;
|
||||
|
||||
class SocialIssueRender implements ChillEntityRenderInterface
|
||||
final class SocialIssueRender implements ChillEntityRenderInterface
|
||||
{
|
||||
private TranslatableStringHelper $translatableStringHelper;
|
||||
private EngineInterface $engine;
|
||||
@@ -28,11 +29,14 @@ class SocialIssueRender implements ChillEntityRenderInterface
|
||||
{
|
||||
return $entity instanceof SocialIssue;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param SocialIssue $socialIssue
|
||||
*/
|
||||
public function renderString($socialIssue, array $options): string
|
||||
{
|
||||
/** @var $socialIssue SocialIssue */
|
||||
$options = \array_merge(self::DEFAULT_ARGS, $options);
|
||||
$options = array_merge(self::DEFAULT_ARGS, $options);
|
||||
|
||||
$str = $this->translatableStringHelper->localize($socialIssue->getTitle());
|
||||
|
||||
@@ -46,26 +50,36 @@ class SocialIssueRender implements ChillEntityRenderInterface
|
||||
return $str;
|
||||
}
|
||||
|
||||
protected function buildParents($socialIssue): array
|
||||
protected function buildParents(SocialIssue $socialIssue): array
|
||||
{
|
||||
$parents = [];
|
||||
|
||||
while ($socialIssue->hasParent()) {
|
||||
$socialIssue = $parents[] = $socialIssue->getParent();
|
||||
}
|
||||
|
||||
return $parents;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param SocialIssue $socialIssue
|
||||
*/
|
||||
public function renderBox($socialIssue, array $options): string
|
||||
{
|
||||
$options = \array_merge(self::DEFAULT_ARGS, $options);
|
||||
$options = array_merge(self::DEFAULT_ARGS, $options);
|
||||
// give some help to twig: an array of parents
|
||||
$parents = $this->buildParents($socialIssue);
|
||||
|
||||
return $this->engine->render('@ChillPerson/Entity/social_issue.html.twig', [
|
||||
'socialIssue' => $socialIssue,
|
||||
'parents' => $parents,
|
||||
'options' => $options
|
||||
]);
|
||||
return $this
|
||||
->engine
|
||||
->render(
|
||||
'@ChillPerson/Entity/social_issue.html.twig',
|
||||
[
|
||||
'socialIssue' => $socialIssue,
|
||||
'parents' => $parents,
|
||||
'options' => $options
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -8,6 +8,7 @@ use Chill\MainBundle\Test\PrepareClientTrait;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\PersonBundle\Entity\Household\Household;
|
||||
use Chill\PersonBundle\Entity\Household\HouseholdMember;
|
||||
use Chill\PersonBundle\Entity\Household\Position;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
@@ -17,7 +18,7 @@ class HouseholdMemberControllerTest extends WebTestCase
|
||||
use PrepareClientTrait;
|
||||
|
||||
/**
|
||||
* @dataProvider provideValidData
|
||||
* @dataProvider provideValidDataMove
|
||||
*/
|
||||
public function testMoveMember($personId, $householdId, $positionId, \DateTimeInterface $date)
|
||||
{
|
||||
@@ -66,13 +67,153 @@ class HouseholdMemberControllerTest extends WebTestCase
|
||||
);
|
||||
}
|
||||
|
||||
public function provideValidData(): \Iterator
|
||||
/**
|
||||
* @dataProvider provideValidDataMove
|
||||
*/
|
||||
public function testMoveMemberToNewHousehold($personId, $householdId, $positionId, \DateTimeInterface $date)
|
||||
{
|
||||
$client = $this->getClientAuthenticated();
|
||||
|
||||
$client->request(
|
||||
Request::METHOD_POST,
|
||||
'/api/1.0/person/household/members/move.json',
|
||||
[], // parameters
|
||||
[], // files
|
||||
[], // server
|
||||
\json_encode(
|
||||
[
|
||||
'concerned' =>
|
||||
[
|
||||
[
|
||||
'person' =>
|
||||
[
|
||||
'type' => 'person',
|
||||
'id' => $personId
|
||||
],
|
||||
'start_date' =>
|
||||
[
|
||||
'datetime' => $date->format(\DateTimeInterface::RFC3339)
|
||||
],
|
||||
'position' =>
|
||||
[
|
||||
'type' => 'household_position',
|
||||
'id' => $positionId
|
||||
],
|
||||
'holder' => false,
|
||||
'comment' => "Introduced by automated test",
|
||||
],
|
||||
],
|
||||
'destination' =>
|
||||
[
|
||||
'type' => 'household',
|
||||
]
|
||||
],
|
||||
true)
|
||||
);
|
||||
|
||||
$this->assertEquals(Response::HTTP_OK,
|
||||
$client->getResponse()->getStatusCode()
|
||||
);
|
||||
|
||||
$data = \json_decode($client->getResponse()->getContent(), true);
|
||||
|
||||
$this->assertIsArray($data);
|
||||
$this->assertArrayHasKey('members', $data);
|
||||
$this->assertIsArray($data['members']);
|
||||
$this->assertEquals(1, count($data['members']),
|
||||
"assert new household count one member");
|
||||
$this->assertArrayHasKey('person', $data['members'][0]);
|
||||
$this->assertArrayHasKey('id', $data['members'][0]['person']);
|
||||
$this->assertEquals($personId, $data['members'][0]['person']['id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideValidDataMove
|
||||
*/
|
||||
public function testLeaveWithoutHousehold($personId, $householdId, $positionId, \DateTimeInterface $date)
|
||||
{
|
||||
$client = $this->getClientAuthenticated();
|
||||
|
||||
$client->request(
|
||||
Request::METHOD_POST,
|
||||
'/api/1.0/person/household/members/move.json',
|
||||
[], // parameters
|
||||
[], // files
|
||||
[], // server
|
||||
\json_encode(
|
||||
[
|
||||
'concerned' =>
|
||||
[
|
||||
[
|
||||
'person' =>
|
||||
[
|
||||
'type' => 'person',
|
||||
'id' => $personId
|
||||
],
|
||||
'start_date' =>
|
||||
[
|
||||
'datetime' => $date->format(\DateTimeInterface::RFC3339)
|
||||
],
|
||||
'position' =>
|
||||
[
|
||||
'type' => 'household_position',
|
||||
'id' => $positionId
|
||||
],
|
||||
'holder' => false,
|
||||
'comment' => "Introduced by automated test",
|
||||
],
|
||||
],
|
||||
'destination' => null
|
||||
],
|
||||
true)
|
||||
);
|
||||
|
||||
$this->assertEquals(Response::HTTP_OK,
|
||||
$client->getResponse()->getStatusCode()
|
||||
);
|
||||
|
||||
$data = \json_decode($client->getResponse()->getContent(), true);
|
||||
|
||||
$this->assertEquals(null, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideValidDataEditMember
|
||||
*/
|
||||
public function testEditMember($memberId)
|
||||
{
|
||||
$client = $this->getClientAuthenticated();
|
||||
|
||||
$crawler = $client->request(
|
||||
Request::METHOD_GET,
|
||||
"/fr/person/household/member/{$memberId}/edit"
|
||||
);
|
||||
|
||||
$this->assertResponseIsSuccessful();
|
||||
|
||||
$form = $crawler->selectButton('Enregistrer')
|
||||
->form();
|
||||
$form['household_member[endDate]'] = (new \DateTime('tomorrow'))
|
||||
->format('Y-m-d');
|
||||
|
||||
$crawler = $client->submit($form);
|
||||
|
||||
$this->assertEquals(302, $client->getResponse()->getStatusCode());
|
||||
}
|
||||
|
||||
public function provideValidDataMove(): \Iterator
|
||||
{
|
||||
self::bootKernel();
|
||||
$em = self::$container->get(EntityManagerInterface::class);
|
||||
|
||||
$personIds = $em->createQuery("SELECT p.id FROM ".Person::class." p ".
|
||||
"JOIN p.center c WHERE c.name = :center")
|
||||
"JOIN p.center c ".
|
||||
"JOIN p.householdParticipations hp ".
|
||||
"WHERE ".
|
||||
"c.name = :center ".
|
||||
"AND hp.startDate < CURRENT_DATE() ".
|
||||
"AND hp.endDate IS NULL "
|
||||
)
|
||||
->setParameter('center', "Center A")
|
||||
->setMaxResults(100)
|
||||
->getScalarResult()
|
||||
@@ -95,4 +236,22 @@ class HouseholdMemberControllerTest extends WebTestCase
|
||||
new \DateTimeImmutable('today')
|
||||
];
|
||||
}
|
||||
|
||||
public function provideValidDataEditMember(): \Iterator
|
||||
{
|
||||
self::bootKernel();
|
||||
$em = self::$container->get(EntityManagerInterface::class);
|
||||
|
||||
$membershipIds = $em->createQuery("SELECT m.id FROM ".HouseholdMember::class." m ".
|
||||
"JOIN m.person p ".
|
||||
"JOIN p.center c ".
|
||||
"WHERE c.name = :center AND m.endDate IS NULL")
|
||||
->setParameter('center', 'Center A')
|
||||
->getScalarResult()
|
||||
;
|
||||
|
||||
\shuffle($membershipIds);
|
||||
|
||||
yield [ \array_pop($membershipIds)['id'] ];
|
||||
}
|
||||
}
|
||||
|
@@ -3,17 +3,17 @@
|
||||
/*
|
||||
* Chill is a suite of a modules, Chill is a software for social workers
|
||||
* Copyright (C) 2014, Champs Libres Cooperative SCRLFS, <http://www.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/>.
|
||||
*/
|
||||
@@ -27,7 +27,7 @@ use Chill\MainBundle\Test\PrepareClientTrait;
|
||||
|
||||
/**
|
||||
* Test the edition of persons
|
||||
*
|
||||
*
|
||||
* As I am logged in as "center a_social"
|
||||
*
|
||||
*/
|
||||
@@ -37,66 +37,66 @@ class PersonControllerUpdateTest extends WebTestCase
|
||||
|
||||
/** @var \Doctrine\ORM\EntityManagerInterface The entity manager */
|
||||
private $em;
|
||||
|
||||
|
||||
/** @var Person The person on which the test is executed */
|
||||
private $person;
|
||||
|
||||
|
||||
/** @var string The url using for editing the person's information */
|
||||
private $editUrl;
|
||||
|
||||
/** @var string The url using for seeing the person's information */
|
||||
private $viewUrl;
|
||||
|
||||
|
||||
/**
|
||||
* Prepare client and create a random person
|
||||
*/
|
||||
public function setUp()
|
||||
{
|
||||
static::bootKernel();
|
||||
|
||||
|
||||
$this->em = static::$kernel->getContainer()
|
||||
->get('doctrine.orm.entity_manager');
|
||||
|
||||
|
||||
$center = $this->em->getRepository('ChillMainBundle:Center')
|
||||
->findOneBy(array('name' => 'Center A'));
|
||||
|
||||
|
||||
$this->person = (new Person())
|
||||
->setLastName("My Beloved")
|
||||
->setFirstName("Jesus")
|
||||
->setCenter($center)
|
||||
->setGender(Person::MALE_GENDER);
|
||||
|
||||
|
||||
$this->em->persist($this->person);
|
||||
$this->em->flush();
|
||||
|
||||
|
||||
$this->editUrl = '/fr/person/'.$this->person->getId().'/general/edit';
|
||||
$this->viewUrl = '/fr/person/'.$this->person->getId().'/general';
|
||||
|
||||
|
||||
$this->client = $this->getClientAuthenticated();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reload the person from the db
|
||||
*/
|
||||
protected function refreshPerson()
|
||||
protected function refreshPerson()
|
||||
{
|
||||
$this->person = $this->em->getRepository('ChillPersonBundle:Person')
|
||||
->find($this->person->getId());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test the edit page are accessible
|
||||
*/
|
||||
public function testEditPageIsSuccessful()
|
||||
{
|
||||
$this->client->request('GET', $this->editUrl);
|
||||
$this->assertTrue($this->client->getResponse()->isSuccessful(),
|
||||
$this->assertTrue($this->client->getResponse()->isSuccessful(),
|
||||
"The person edit form is accessible");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test the configurable fields are present
|
||||
*
|
||||
*
|
||||
* @group configurable_fields
|
||||
*/
|
||||
public function testHiddenFielsArePresent()
|
||||
@@ -106,12 +106,12 @@ class PersonControllerUpdateTest extends WebTestCase
|
||||
$configurables = array('placeOfBirth', 'phonenumber', 'email',
|
||||
'countryOfBirth', 'nationality', 'spokenLanguages', 'maritalStatus');
|
||||
$form = $crawler->selectButton('Enregistrer')->form(); //;
|
||||
|
||||
|
||||
foreach($configurables as $key) {
|
||||
$this->assertTrue($form->has('chill_personbundle_person['.$key.']'));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test if the edit page of a given person is not accessible for a user
|
||||
* of another center of the person
|
||||
@@ -122,12 +122,12 @@ class PersonControllerUpdateTest extends WebTestCase
|
||||
'PHP_AUTH_USER' => 'center b_social',
|
||||
'PHP_AUTH_PW' => 'password',
|
||||
));
|
||||
|
||||
|
||||
$client->request('GET', $this->editUrl);
|
||||
$this->assertEquals(403, $client->getResponse()->getStatusCode(),
|
||||
"The edit page of a person of a center A must not be accessible for user of center B");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test the edit page of a given person are not accessible for an
|
||||
* administrative user
|
||||
@@ -138,19 +138,19 @@ class PersonControllerUpdateTest extends WebTestCase
|
||||
'PHP_AUTH_USER' => 'center a_administrative',
|
||||
'PHP_AUTH_PW' => 'password',
|
||||
));
|
||||
|
||||
|
||||
$client->request('GET', $this->editUrl);
|
||||
$this->assertEquals(403, $client->getResponse()->getStatusCode());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test the edition of a field
|
||||
*
|
||||
*
|
||||
* Given I fill the field with $value
|
||||
* And I submit the form
|
||||
* Then I am redirected to the 'general' page
|
||||
* And the person is updated in the db
|
||||
*
|
||||
*
|
||||
* @dataProvider validTextFieldsProvider
|
||||
* @param string $field
|
||||
* @param string $value
|
||||
@@ -159,7 +159,7 @@ class PersonControllerUpdateTest extends WebTestCase
|
||||
public function testEditTextField($field, $value, \Closure $callback)
|
||||
{
|
||||
$crawler = $this->client->request('GET', $this->editUrl);
|
||||
|
||||
|
||||
$form = $crawler->selectButton('Enregistrer')
|
||||
->form();
|
||||
//transform countries into value if needed
|
||||
@@ -177,13 +177,13 @@ class PersonControllerUpdateTest extends WebTestCase
|
||||
default:
|
||||
$transformedValue = $value;
|
||||
}
|
||||
|
||||
|
||||
$form->get('chill_personbundle_person['.$field. ']')
|
||||
->setValue($transformedValue);
|
||||
|
||||
|
||||
$this->client->submit($form);
|
||||
$this->refreshPerson();
|
||||
|
||||
|
||||
$this->assertTrue($this->client->getResponse()->isRedirect($this->viewUrl),
|
||||
'the page is redirected to general view');
|
||||
$this->assertEquals($value, $callback($this->person),
|
||||
@@ -200,20 +200,20 @@ class PersonControllerUpdateTest extends WebTestCase
|
||||
$this->assertGreaterThan(0, $crawler->filter('html:contains("'.$value.'")')->count());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function testEditLanguages()
|
||||
{
|
||||
$crawler = $this->client->request('GET', $this->editUrl);
|
||||
$selectedLanguages = array('en', 'an', 'bbj');
|
||||
|
||||
|
||||
$form = $crawler->selectButton('Enregistrer')
|
||||
->form();
|
||||
$form->get('chill_personbundle_person[spokenLanguages]')
|
||||
->setValue($selectedLanguages);
|
||||
|
||||
|
||||
$this->client->submit($form);
|
||||
$this->refreshPerson();
|
||||
|
||||
|
||||
$this->assertTrue($this->client->getResponse()->isRedirect($this->viewUrl),
|
||||
'the page is redirected to /general view');
|
||||
//retrieve languages codes present in person
|
||||
@@ -223,10 +223,10 @@ class PersonControllerUpdateTest extends WebTestCase
|
||||
$this->assertEquals(asort($selectedLanguages), asort($languagesCodesPresents),
|
||||
'the person speaks the expected languages');
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Test tbe detection of invalid data during the update procedure
|
||||
* Test tbe detection of invalid data during the update procedure
|
||||
*
|
||||
* @dataProvider providesInvalidFieldsValues
|
||||
* @param string $field
|
||||
@@ -235,33 +235,33 @@ class PersonControllerUpdateTest extends WebTestCase
|
||||
public function testInvalidFields($field, $value)
|
||||
{
|
||||
$crawler = $this->client->request('GET', $this->editUrl);
|
||||
|
||||
|
||||
$form = $crawler->selectButton('Enregistrer')
|
||||
->form();
|
||||
$form->get('chill_personbundle_person['.$field.']')
|
||||
->setValue($value);
|
||||
|
||||
|
||||
$crawler = $this->client->submit($form);
|
||||
|
||||
|
||||
$this->assertFalse($this->client->getResponse()->isRedirect(),
|
||||
'the page is not redirected to /general');
|
||||
$this->assertGreaterThan(0, $crawler->filter('.error')->count(),
|
||||
'a element .error is shown');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* provide valid values to test, with field name and
|
||||
* provide valid values to test, with field name and
|
||||
* a function to find the value back from person entity
|
||||
*
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
public function validTextFieldsProvider()
|
||||
{
|
||||
{
|
||||
return array(
|
||||
['firstName', 'random Value', function(Person $person) { return $person->getFirstName(); } ],
|
||||
['lastName' , 'random Value', function(Person $person) { return $person->getLastName(); } ],
|
||||
['placeOfBirth', 'none place', function(Person $person) { return $person->getPlaceOfBirth(); }],
|
||||
['birthdate', '15-12-1980', function(Person $person) { return $person->getBirthdate()->format('d-m-Y'); }],
|
||||
['birthdate', '1980-12-15', function(Person $person) { return $person->getBirthdate()->format('Y-m-d'); }],
|
||||
['phonenumber', '+32123456789', function(Person $person) { return $person->getPhonenumber(); }],
|
||||
['memo', 'jfkdlmq jkfldmsq jkmfdsq', function(Person $person) { return $person->getMemo(); }],
|
||||
['countryOfBirth', 'BE', function(Person $person) { return $person->getCountryOfBirth()->getCountryCode(); }],
|
||||
@@ -275,7 +275,7 @@ class PersonControllerUpdateTest extends WebTestCase
|
||||
['gender', Person::FEMALE_GENDER, function(Person $person) { return $person->getGender(); }],
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public function providesInvalidFieldsValues()
|
||||
{
|
||||
return array(
|
||||
@@ -286,18 +286,18 @@ class PersonControllerUpdateTest extends WebTestCase
|
||||
['birthdate', 'false date']
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public function tearDown()
|
||||
{
|
||||
$this->refreshPerson();
|
||||
$this->em->remove($this->person);
|
||||
$this->em->flush();
|
||||
}
|
||||
|
||||
|
||||
private function getVeryLongText()
|
||||
{
|
||||
return <<<EOT
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse molestie at enim id auctor. Vivamus malesuada elit ipsum, ac mollis ex facilisis sit amet. Phasellus accumsan, quam ut aliquet accumsan, augue ligula consequat erat, condimentum iaculis orci magna egestas eros. In vel blandit sapien. Duis ut dui vitae tortor iaculis malesuada vitae vitae lorem. Morbi efficitur dolor orci, a rhoncus urna blandit quis. Aenean at placerat dui, ut tincidunt nulla. In ultricies tempus ligula ac rutrum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Fusce urna nibh, placerat vel auctor sed, maximus quis magna. Vivamus quam ante, consectetur vel feugiat quis, aliquet id ante. Integer gravida erat dignissim ante commodo mollis. Donec imperdiet mauris elit, nec blandit dolor feugiat ut. Proin iaculis enim ut tortor pretium commodo. Etiam aliquet hendrerit dolor sed fringilla. Vestibulum facilisis nibh tincidunt dui egestas, vitae congue mi imperdiet. Duis vulputate ultricies lectus id cursus. Fusce bibendum sem dignissim, bibendum purus quis, mollis ex. Cras ac est justo. Duis congue mattis ipsum, vitae sagittis justo dictum sit amet. Duis aliquam pharetra sem, non laoreet ante laoreet ac. Mauris ornare mi tempus rutrum consequat.
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse molestie at enim id auctor. Vivamus malesuada elit ipsum, ac mollis ex facilisis sit amet. Phasellus accumsan, quam ut aliquet accumsan, augue ligula consequat erat, condimentum iaculis orci magna egestas eros. In vel blandit sapien. Duis ut dui vitae tortor iaculis malesuada vitae vitae lorem. Morbi efficitur dolor orci, a rhoncus urna blandit quis. Aenean at placerat dui, ut tincidunt nulla. In ultricies tempus ligula ac rutrum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Fusce urna nibh, placerat vel auctor sed, maximus quis magna. Vivamus quam ante, consectetur vel feugiat quis, aliquet id ante. Integer gravida erat dignissim ante commodo mollis. Donec imperdiet mauris elit, nec blandit dolor feugiat ut. Proin iaculis enim ut tortor pretium commodo. Etiam aliquet hendrerit dolor sed fringilla. Vestibulum facilisis nibh tincidunt dui egestas, vitae congue mi imperdiet. Duis vulputate ultricies lectus id cursus. Fusce bibendum sem dignissim, bibendum purus quis, mollis ex. Cras ac est justo. Duis congue mattis ipsum, vitae sagittis justo dictum sit amet. Duis aliquam pharetra sem, non laoreet ante laoreet ac. Mauris ornare mi tempus rutrum consequat.
|
||||
EOT;
|
||||
}
|
||||
}
|
||||
|
@@ -22,6 +22,9 @@
|
||||
|
||||
namespace Chill\PersonBundle\Tests\Entity;
|
||||
|
||||
use Chill\PersonBundle\Entity\Household\HouseholdMember;
|
||||
use Chill\PersonBundle\Entity\Household\Position;
|
||||
use Chill\PersonBundle\Entity\Household\Household;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||
use Chill\MainBundle\Entity\Address;
|
||||
@@ -205,4 +208,39 @@ class PersonTest extends \PHPUnit\Framework\TestCase
|
||||
$this::assertEquals($address3, $p->getLastAddress($addressDate3));
|
||||
}
|
||||
|
||||
public function testIsSharingHousehold()
|
||||
{
|
||||
$person = new Person();
|
||||
$household = new Household();
|
||||
$positionShare = (new Position())
|
||||
->setShareHousehold(true);
|
||||
$positionNotShare = (new Position())
|
||||
->setShareHousehold(false);
|
||||
|
||||
$membership1 = (new HouseholdMember())
|
||||
->setStartDate(new \DateTimeImmutable('10 years ago'))
|
||||
->setEndDate(new \DateTimeImmutable('5 years ago'))
|
||||
->setPerson($person)
|
||||
->setPosition($positionShare)
|
||||
;
|
||||
$household->addMember($membership1);
|
||||
|
||||
$membership2 = (new HouseholdMember())
|
||||
->setStartDate(new \DateTimeImmutable('4 years ago'))
|
||||
->setEndDate(new \DateTimeImmutable('2 years ago'))
|
||||
->setPerson($person)
|
||||
->setPosition($positionNotShare)
|
||||
;
|
||||
$household->addMember($membership2);
|
||||
|
||||
$this->assertEquals(2, $person->getHouseholdParticipations()
|
||||
->count());
|
||||
|
||||
$this->assertFalse($person->isSharingHousehold());
|
||||
$this->assertTrue($person->isSharingHousehold(
|
||||
new \DateTimeImmutable('6 years ago')));
|
||||
$this->assertFalse($person->isSharingHousehold(
|
||||
new \DateTimeImmutable('3 years ago')));
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace Chill\PersonBundle\Validator\Constraints\Household;
|
||||
|
||||
class MaxHolderValidator
|
||||
{
|
||||
|
||||
}
|
@@ -875,18 +875,61 @@ paths:
|
||||
type: object
|
||||
properties:
|
||||
person:
|
||||
$ref: '#/components/schemas/PersonById'
|
||||
$ref: '#/components/schemas/PersonById'
|
||||
start_date:
|
||||
$ref: '#/components/schemas/Date'
|
||||
$ref: '#/components/schemas/Date'
|
||||
position:
|
||||
$ref: '#/components/schemas/HouseholdPosition'
|
||||
$ref: '#/components/schemas/HouseholdPosition'
|
||||
holder:
|
||||
type: boolean
|
||||
comment:
|
||||
type: string
|
||||
destination:
|
||||
oneOf:
|
||||
- $ref: '#/components/schemas/Household'
|
||||
$ref: '#/components/schemas/Household'
|
||||
examples:
|
||||
Moving person to a new household:
|
||||
value:
|
||||
concerned:
|
||||
-
|
||||
person:
|
||||
id: 0
|
||||
type: person
|
||||
position:
|
||||
type: position
|
||||
id: 1
|
||||
start_date:
|
||||
datetime: 2021-06-01T00:00:00+02:00
|
||||
comment: "This is my comment for moving"
|
||||
holder: false
|
||||
destination:
|
||||
type: household
|
||||
Moving person to an existing household:
|
||||
value:
|
||||
concerned:
|
||||
-
|
||||
person:
|
||||
id: 0
|
||||
type: person
|
||||
position:
|
||||
type: position
|
||||
id: 1
|
||||
start_date:
|
||||
datetime: 2021-06-01T00:00:00+02:00
|
||||
comment: "This is my comment for moving"
|
||||
holder: false
|
||||
destination:
|
||||
type: household
|
||||
id: 54
|
||||
Removing a person from any household:
|
||||
value:
|
||||
concerned:
|
||||
-
|
||||
person:
|
||||
id: 0
|
||||
type: person
|
||||
start_date:
|
||||
datetime: 2021-06-01T00:00:00+02:00
|
||||
destination: null
|
||||
responses:
|
||||
401:
|
||||
description: "Unauthorized"
|
||||
|
@@ -10,4 +10,6 @@ module.exports = function(encore, entries)
|
||||
|
||||
encore.addEntry('accompanying_course', __dirname + '/Resources/public/vuejs/AccompanyingCourse/index.js');
|
||||
encore.addEntry('household_address', __dirname + '/Resources/public/vuejs/HouseholdAddress/index.js');
|
||||
encore.addEntry('household_members_editor', __dirname + '/Resources/public/vuejs/HouseholdMembersEditor/index.js');
|
||||
encore.addEntry('vue_accourse', __dirname + '/Resources/public/vuejs/AccompanyingCourse/index.js');
|
||||
};
|
||||
|
@@ -1,7 +1,7 @@
|
||||
parameters:
|
||||
# cl_chill_person.example.class: Chill\PersonBundle\Example
|
||||
|
||||
services:
|
||||
services:
|
||||
_defaults:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
@@ -62,3 +62,20 @@ services:
|
||||
autoconfigure: true
|
||||
resource: '../Repository/'
|
||||
tags: ['doctrine.repository_service']
|
||||
|
||||
Chill\PersonBundle\Controller\:
|
||||
autowire: true
|
||||
resource: '../Controller/'
|
||||
tags: ['controller.service_arguments']
|
||||
|
||||
Chill\PersonBundle\Export\:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
resource: '../Export/'
|
||||
|
||||
Chill\PersonBundle\Templating\Entity\:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
resource: '../Templating/Entity'
|
||||
tags:
|
||||
- 'chill.render_entity'
|
||||
|
@@ -36,43 +36,39 @@ services:
|
||||
chill.person.export.filter_birthdate:
|
||||
class: Chill\PersonBundle\Export\Filter\BirthdateFilter
|
||||
tags:
|
||||
- { name: chill.export_filter, alias: person_birthdate_filter }
|
||||
|
||||
- { name: chill.export_filter, alias: person_birthdate_filter }
|
||||
|
||||
chill.person.export.filter_nationality:
|
||||
class: Chill\PersonBundle\Export\Filter\NationalityFilter
|
||||
arguments:
|
||||
- "@chill.main.helper.translatable_string"
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
tags:
|
||||
- { name: chill.export_filter, alias: person_nationality_filter }
|
||||
|
||||
chill.person.export.aggregator_nationality:
|
||||
class: Chill\PersonBundle\Export\Aggregator\NationalityAggregator
|
||||
arguments:
|
||||
- "@chill.main.countries_repository"
|
||||
- "@chill.main.helper.translatable_string"
|
||||
- "@translator"
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
tags:
|
||||
- { name: chill.export_aggregator, alias: person_nationality_aggregator }
|
||||
|
||||
chill.person.export.aggregator_country_of_birth:
|
||||
class: Chill\PersonBundle\Export\Aggregator\CountryOfBirthAggregator
|
||||
arguments:
|
||||
- "@chill.main.countries_repository"
|
||||
- "@chill.main.helper.translatable_string"
|
||||
- "@translator"
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
tags:
|
||||
- { name: chill.export_aggregator, alias: person_country_of_birth_aggregator }
|
||||
|
||||
chill.person.export.aggregator_gender:
|
||||
class: Chill\PersonBundle\Export\Aggregator\GenderAggregator
|
||||
arguments:
|
||||
- "@translator"
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
tags:
|
||||
- { name: chill.export_aggregator, alias: person_gender_aggregator }
|
||||
|
||||
chill.person.export.aggregator_age:
|
||||
class: Chill\PersonBundle\Export\Aggregator\AgeAggregator
|
||||
arguments:
|
||||
- "@translator"
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
tags:
|
||||
- { name: chill.export_aggregator, alias: person_age_aggregator }
|
||||
|
@@ -1,25 +0,0 @@
|
||||
services:
|
||||
Chill\PersonBundle\Templating\Entity\:
|
||||
resource: '../../Templating/Entity'
|
||||
tags:
|
||||
- 'chill.render_entity'
|
||||
|
||||
Chill\PersonBundle\Templating\Entity\PersonRender:
|
||||
arguments:
|
||||
$configAltNamesHelper: '@Chill\PersonBundle\Config\ConfigPersonAltNamesHelper'
|
||||
$engine: '@Symfony\Component\Templating\EngineInterface'
|
||||
tags:
|
||||
- 'chill.render_entity'
|
||||
|
||||
Chill\PersonBundle\Templating\Entity\ClosingMotiveRender:
|
||||
arguments:
|
||||
$translatableStringHelper: '@Chill\MainBundle\Templating\TranslatableStringHelper'
|
||||
tags:
|
||||
- 'chill.render_entity'
|
||||
|
||||
Chill\PersonBundle\Templating\Entity\SocialIssueRender:
|
||||
arguments:
|
||||
$translatableStringHelper: '@Chill\MainBundle\Templating\TranslatableStringHelper'
|
||||
$engine: '@Symfony\Component\Templating\EngineInterface'
|
||||
tags:
|
||||
- 'chill.render_entity'
|
@@ -0,0 +1,40 @@
|
||||
Born the date: >-
|
||||
{gender, select,
|
||||
man {Né le {birthdate}}
|
||||
woman {Née le {birthdate}}
|
||||
other {Né·e le {birthdate}}
|
||||
}
|
||||
|
||||
household:
|
||||
Household: Ménage
|
||||
Household members: Membres du ménage
|
||||
Show future or past memberships: >-
|
||||
{length, plural,
|
||||
one {Montrer une ancienne appartenance}
|
||||
many {Montrer # anciennes ou futures appartenances}
|
||||
other {Montrer # anciennes ou futures appartenances}
|
||||
}
|
||||
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.
|
||||
Leave: Quitter le ménage
|
||||
Household file: Dossier ménage
|
||||
Add a member: Ajouter un membre
|
||||
Update membership: Modifier
|
||||
successfully saved member: Membre enregistré avec succès
|
||||
Start date: Date de début de l'appartenance au ménage
|
||||
End date: Date de fin de l'appartenance au ménage
|
||||
Comment: Commentaire
|
||||
is holder: Est titulaire
|
||||
is not holder: N'est pas titulaire
|
||||
holder: Titulaire
|
||||
Edit member household: Modifier l'appartenance au ménage
|
||||
Current household members: Membres actuels du ménage
|
||||
Household summary: Résumé
|
||||
Addresses: Adresses
|
||||
Edit household members: Modifier l'appartenance au ménage
|
||||
and x other persons: >-
|
||||
{x, plural,
|
||||
one {et une autre personne}
|
||||
many {et # autres personnes}
|
||||
other {et # autres personnes}
|
||||
}
|
@@ -46,7 +46,6 @@ Add new phone: Ajouter un numéro de téléphone
|
||||
Remove phone: Supprimer
|
||||
'Notes on contact information': 'Remarques sur les informations de contact'
|
||||
'Remarks': 'Remarques'
|
||||
'Born the %date%': '{0} Né le %date% | {1} Née le %date%'
|
||||
'Spoken languages': 'Langues parlées'
|
||||
'Unknown spoken languages': 'Langues parlées inconnues'
|
||||
Male: Homme
|
||||
@@ -173,6 +172,7 @@ This accompanying course is still a draft: Ce parcours est à l'état brouillon
|
||||
Associated peoples: Usagers concernés
|
||||
Resources: Interlocuteurs privilégiés
|
||||
Social actions: Actions d'accompagnement
|
||||
Social issues: Problématiques sociales
|
||||
Last events on accompanying course: Dernières actions de suivi
|
||||
Edit & activate accompanying course: Modifier et valider
|
||||
See accompanying periods: Voir les périodes d'accompagnement
|
||||
|
Reference in New Issue
Block a user