diff --git a/src/Bundle/ChillPersonBundle/Controller/PersonController.php b/src/Bundle/ChillPersonBundle/Controller/PersonController.php index 02320aed3..747299e59 100644 --- a/src/Bundle/ChillPersonBundle/Controller/PersonController.php +++ b/src/Bundle/ChillPersonBundle/Controller/PersonController.php @@ -41,6 +41,8 @@ use Chill\PersonBundle\Config\ConfigPersonAltNamesHelper; use Chill\PersonBundle\Repository\PersonNotDuplicateRepository; use Symfony\Component\Validator\Validator\ValidatorInterface; use Doctrine\ORM\EntityManagerInterface; +use Symfony\Component\Routing\Annotation\Route; +use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; final class PersonController extends AbstractController { @@ -416,4 +418,29 @@ final class PersonController extends AbstractController return $person; } + + /** + * + * @Route( + * "/{_locale}/person/household/{person_id}/history", + * name="chill_person_household_person_history", + * methods={"GET", "POST"} + * ) + * @ParamConverter("person", options={"id" = "person_id"}) + */ + public function householdHistoryByPerson(Request $request, Person $person): Response + { + $this->denyAccessUnlessGranted('CHILL_PERSON_SEE', $person, + "You are not allowed to see this person."); + + $event = new PrivacyEvent($person); + $this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event); + + return $this->render( + '@ChillPerson/Person/household_history.html.twig', + [ + 'person' => $person + ] + ); + } } diff --git a/src/Bundle/ChillPersonBundle/Entity/Household/Household.php b/src/Bundle/ChillPersonBundle/Entity/Household/Household.php index f3214fc98..d76466eff 100644 --- a/src/Bundle/ChillPersonBundle/Entity/Household/Household.php +++ b/src/Bundle/ChillPersonBundle/Entity/Household/Household.php @@ -117,6 +117,41 @@ class Household return $this->members; } + public function getMembersOnRange(\DateTimeImmutable $from, ?\DateTimeImmutable $to): Collection + { + $criteria = new Criteria(); + $expr = Criteria::expr(); + + $criteria->where( + $expr->gte('startDate', $from) + ); + + if (NULL !== $to) { + $criteria->andWhere( + $expr->orX( + $expr->lte('endDate', $to), + $expr->eq('endDate', NULL) + ), + ); + } + + return $this->getMembers() + ->matching($criteria) + ; + } + + public function getMembersDuringMembership(HouseholdMember $membership) + { + return $this->getMembersOnRange( + $membership->getStartDate(), + $membership->getEndDate() + )->filter( + function(HouseholdMember $m) use ($membership) { + return $m !== $membership; + } + ); + } + public function getMembersHolder(): Collection { $criteria = new Criteria(); diff --git a/src/Bundle/ChillPersonBundle/Entity/Household/HouseholdMember.php b/src/Bundle/ChillPersonBundle/Entity/Household/HouseholdMember.php index 611a9e84f..985084623 100644 --- a/src/Bundle/ChillPersonBundle/Entity/Household/HouseholdMember.php +++ b/src/Bundle/ChillPersonBundle/Entity/Household/HouseholdMember.php @@ -206,4 +206,13 @@ class HouseholdMember { return $this->holder; } + + public function isCurrent(\DateTimeImmutable $at = null): bool + { + $at = NULL === $at ? new \DateTimeImmutable('now'): $at; + + return $this->getStartDate() < $at && ( + NULL === $this->getEndDate() || $at < $this->getEndDate() + ); + } } diff --git a/src/Bundle/ChillPersonBundle/Entity/Person.php b/src/Bundle/ChillPersonBundle/Entity/Person.php index 5373f0420..eff39eda4 100644 --- a/src/Bundle/ChillPersonBundle/Entity/Person.php +++ b/src/Bundle/ChillPersonBundle/Entity/Person.php @@ -1209,14 +1209,44 @@ class Person implements HasCenterInterface return $this->householdParticipations; } + /** + * Get participation where the person does share the household. + * + * Order by startDate, desc + */ public function getHouseholdParticipationsShareHousehold(): Collection { $criteria = new Criteria(); $expr = Criteria::expr(); - $criteria->where( - $expr->eq('shareHousehold', true) - ); + $criteria + ->where( + $expr->eq('shareHousehold', true) + ) + ->orderBy(['startDate' => Criteria::DESC]) + ; + + return $this->getHouseholdParticipations() + ->matching($criteria) + ; + } + + /** + * Get participation where the person does not share the household. + * + * Order by startDate, desc + */ + public function getHouseholdParticipationsNotShareHousehold(): Collection + { + $criteria = new Criteria(); + $expr = Criteria::expr(); + + $criteria + ->where( + $expr->eq('shareHousehold', false) + ) + ->orderBy(['startDate' => Criteria::DESC]) + ; return $this->getHouseholdParticipations() ->matching($criteria) diff --git a/src/Bundle/ChillPersonBundle/Menu/PersonMenuBuilder.php b/src/Bundle/ChillPersonBundle/Menu/PersonMenuBuilder.php index b39b14254..fdb8feb3e 100644 --- a/src/Bundle/ChillPersonBundle/Menu/PersonMenuBuilder.php +++ b/src/Bundle/ChillPersonBundle/Menu/PersonMenuBuilder.php @@ -64,6 +64,16 @@ class PersonMenuBuilder implements LocalMenuBuilderInterface 'order' => 50 ]); + $menu->addChild($this->translator->trans('household.person history'), [ + 'route' => 'chill_person_household_person_history', + 'routeParameters' => [ + 'person_id' => $parameters['person']->getId() + ] + ]) + ->setExtras([ + 'order' => 99999 + ]); + $menu->addChild($this->translator->trans('Person duplicate'), [ 'route' => 'chill_person_duplicate_view', 'routeParameters' => [ @@ -71,7 +81,7 @@ class PersonMenuBuilder implements LocalMenuBuilderInterface ] ]) ->setExtras([ - 'order' => 51 + 'order' => 99999 ]); if ($this->showAccompanyingPeriod === 'visible') { diff --git a/src/Bundle/ChillPersonBundle/Resources/public/modules/person_household_history/index.js b/src/Bundle/ChillPersonBundle/Resources/public/modules/person_household_history/index.js new file mode 100644 index 000000000..e69de29bb diff --git a/src/Bundle/ChillPersonBundle/Resources/public/sass/person.scss b/src/Bundle/ChillPersonBundle/Resources/public/sass/person.scss index 0bcea886a..7ea0d7dfb 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/sass/person.scss +++ b/src/Bundle/ChillPersonBundle/Resources/public/sass/person.scss @@ -86,3 +86,83 @@ div.person-view { } } + + +/* + * HOUSEHOLD + */ + + +div.household__address, div.person__address { + div.row { + height: 100px; + width: 100%; + position: relative; + & > div { + position: absolute; + display: table; + height: 100%; + border: 1px dotted #c3c3c3; + } + div.household__address--date, div.person__address--date { + width: 30%; + background-color: #c3c3c3; + height: 100%; + div.cell { + box-sizing: border-box; + position: relative; + height: 100%; + width: 100%; + margin-left: 50%; + div.pill { + position: absolute; + box-sizing: border-box; + width: 120px; + height: 40px; + bottom: -20px; + background-color: white; + padding: 10px; + border-radius: 30px; + left: -60px; + text-align: center; + z-index: 10; + } + } + } + div.household__address--content, div.person__address--content { + width: 70%; + left: 30%; + text-align: left; + background-color: #ececec; + border: 1px solid #888; + div.cell { + display: table-cell; + padding: 5px 30px; + vertical-align: middle; + & > div { + display: flex; + justify-content: space-between; + } + i.dot::before, i.dot::after { + position: absolute; + width: 20px; + height: 20px; + content: ''; + border: 0; + background-color: white; + border-radius: 50%; + border: 5px solid #c3c3c3; + z-index: 10; + left: -15px; + bottom: -15px; + } + } + } + } +} +div.household__address-move { + div.household__address-move__create { + display: flex; + flex-direction: column; + } +} diff --git a/src/Bundle/ChillPersonBundle/Resources/views/Person/household_history.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/Person/household_history.html.twig new file mode 100644 index 000000000..f43b495c0 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Resources/views/Person/household_history.html.twig @@ -0,0 +1,213 @@ +{% extends "@ChillPerson/layout.html.twig" %} + +{% set activeRouteKey = 'chill_person_view' %} + +{% block title 'household.Household history for %name%'|trans({'name': person|chill_entity_render_string}) %} + + +{% block personcontent %} +

{{ block('title') }}

+ +

{{ 'household.Household shared'|trans }}

+ +{% set memberships = person.getHouseholdParticipationsShareHousehold() %} + +{% if memberships|length == 0 %} +

{{ 'household.Never in any household'|trans }}

+ + + +{% else %} + +
+
+ + {% if not person.isSharingHousehold() %} + + {% endif %} + + {% for p in memberships %} +
+
+
+
+ {{ p.startDate|format_date('long') }} +
+
+
+
+
+ +
+
+

+ + + {{ 'household.Household number'|trans({'household_num': p.household.id }) }} + +

+

{{ p.position.label|localize_translatable_string }} {% if p.holder %}{{ 'household.holder'|trans }}{% endif %} +

+
+ {% set simultaneous = p.household.getMembersDuringMembership(p) %} + {% if simultaneous|length == 0 %} +

+ {{ 'household.Any simultaneous members'|trans }} +

+ {% else %} + {{ 'household.Members at same time'|trans }}: + {% for p in simultaneous -%} + {{- p.person|chill_entity_render_box({'addLink': true }) -}} + {%- if p.holder %} {{'household.holder'|trans }} {% endif %} + {%- if not loop.last %}, {% endif -%} + {%- endfor -%} + {% endif %} + +
+
+
+
+
+ {% endfor %} +
+ +
+{% endif %} + +

{{ 'household.Household not shared'|trans }}

+ +{% set memberships = person.getHouseholdParticipationsNotShareHousehold() %} + +{% if memberships|length == 0 %} +

{{ 'household.Never in any household'|trans }}

+{% else %} + + + + + + + + + + {% for p in memberships %} + + + + + + + {% endfor %} + +
{{ 'household.from'|trans }}{{ 'household.to'|trans }}{{ 'household.Household'|trans }}
{{ p.startDate|format_date('long') }} + {% if p.endDate is not empty %} + {{ p.endDate|format_date('long') }} + {% else %} + {{ 'household.Membership currently running'|trans }} + {% endif %} + +
+

+ + + {{ 'household.Household number'|trans({'household_num': p.household.id }) }} + +

+

{{ p.position.label|localize_translatable_string }} {% if p.holder %}{{ 'household.holder'|trans }}{% endif %} +

+
+ {% set simultaneous = p.household.getMembersDuringMembership(p) %} + {% if simultaneous|length == 0 %} +

+ {{ 'household.Any simultaneous members'|trans }} +

+ {% else %} + {{ 'household.Members at same time'|trans }}: + {% for p in simultaneous -%} + {{- p.person|chill_entity_render_box({'addLink': true }) -}} + {%- if p.holder %} {{'household.holder'|trans }} {% endif %} + {%- if not loop.last %}, {% endif -%} + {%- endfor -%} + {% endif %} +
+
+ +
+{% endif %} + +{% endblock %} diff --git a/src/Bundle/ChillPersonBundle/translations/messages+intl-icu.fr.yaml b/src/Bundle/ChillPersonBundle/translations/messages+intl-icu.fr.yaml index a9ff7faa0..dcca83c40 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages+intl-icu.fr.yaml +++ b/src/Bundle/ChillPersonBundle/translations/messages+intl-icu.fr.yaml @@ -7,8 +7,11 @@ Born the date: >- household: Household: Ménage + Household number: Ménage {household_num} Household members: Membres du ménage Household editor: Modifier l'appartenance + Members at same time: Membres simultanés + Any simultaneous members: Aucun membre simultanément Select people to move: Choisir les usagers Show future or past memberships: >- {length, plural, @@ -19,6 +22,7 @@ household: 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 + Join: Rejoindre un ménage Household file: Dossier ménage Add a member: Ajouter un membre Update membership: Modifier @@ -49,5 +53,12 @@ household: expecting_birth: Naissance attendue ? date_expecting_birth: Date de la naissance attendue data_saved: Données enregistrées - + Household history for %name%: Historique des ménages pour {name} + Household shared: Ménages domiciliés + Household not shared: Ménage non domiciliés + Never in any household: Membre d'aucun ménage + Membership currently running: En cours + from: Depuis + to: Jusqu'au + person history: Ménages