diff --git a/docs/source/installation/index.rst b/docs/source/installation/index.rst index 1312480e3..a962148f7 100644 --- a/docs/source/installation/index.rst +++ b/docs/source/installation/index.rst @@ -65,6 +65,17 @@ This script will : 4. build assets +.. note:: + + In some cases it can happen that an old image (chill_base_php or chill_php) stored in the docker cache will make the script fail. To solve this problem you have to delete the image and the container, before the make init : + + .. code-block:: bash + + docker-compose images php + docker rmi -f chill_php:prod + docker-compose rm php + + 4. Start the project ==================== diff --git a/src/Bundle/ChillActivityBundle/Service/DocGenerator/ActivityContext.php b/src/Bundle/ChillActivityBundle/Service/DocGenerator/ActivityContext.php index 55d64ef93..61bcaad3e 100644 --- a/src/Bundle/ChillActivityBundle/Service/DocGenerator/ActivityContext.php +++ b/src/Bundle/ChillActivityBundle/Service/DocGenerator/ActivityContext.php @@ -130,8 +130,10 @@ class ActivityContext implements return $this->personRender->renderString($p, []); }, 'multiple' => false, + 'required' => false, 'expanded' => true, 'label' => $options[$key . 'Label'], + 'placeholder' => $this->translator->trans('Any person selected'), ]); } } diff --git a/src/Bundle/ChillDocStoreBundle/Entity/AccompanyingCourseDocument.php b/src/Bundle/ChillDocStoreBundle/Entity/AccompanyingCourseDocument.php index b013f1a44..4f90b06fc 100644 --- a/src/Bundle/ChillDocStoreBundle/Entity/AccompanyingCourseDocument.php +++ b/src/Bundle/ChillDocStoreBundle/Entity/AccompanyingCourseDocument.php @@ -11,6 +11,7 @@ declare(strict_types=1); namespace Chill\DocStoreBundle\Entity; +use Chill\MainBundle\Entity\HasScopesInterface; use Chill\PersonBundle\Entity\AccompanyingPeriod; use Doctrine\ORM\Mapping as ORM; @@ -18,7 +19,7 @@ use Doctrine\ORM\Mapping as ORM; * @ORM\Entity * @ORM\Table("chill_doc.accompanyingcourse_document") */ -class AccompanyingCourseDocument extends Document +class AccompanyingCourseDocument extends Document implements HasScopesInterface { /** * @ORM\ManyToOne(targetEntity=AccompanyingPeriod::class) @@ -31,6 +32,15 @@ class AccompanyingCourseDocument extends Document return $this->course; } + public function getScopes(): iterable + { + if (null !== $this->course) { + return []; + } + + return $this->course->getScopes(); + } + public function setCourse(?AccompanyingPeriod $course): self { $this->course = $course; diff --git a/src/Bundle/ChillDocStoreBundle/Entity/Document.php b/src/Bundle/ChillDocStoreBundle/Entity/Document.php index e26469573..0fd329ff8 100644 --- a/src/Bundle/ChillDocStoreBundle/Entity/Document.php +++ b/src/Bundle/ChillDocStoreBundle/Entity/Document.php @@ -16,8 +16,6 @@ use Chill\MainBundle\Doctrine\Model\TrackCreationInterface; use Chill\MainBundle\Doctrine\Model\TrackCreationTrait; use Chill\MainBundle\Doctrine\Model\TrackUpdateInterface; use Chill\MainBundle\Doctrine\Model\TrackUpdateTrait; -use Chill\MainBundle\Entity\HasScopeInterface; -use Chill\MainBundle\Entity\Scope; use Chill\MainBundle\Entity\User; use DateTimeInterface; use Doctrine\ORM\Mapping as ORM; @@ -26,7 +24,7 @@ use Symfony\Component\Validator\Constraints as Assert; /** * @ORM\MappedSuperclass */ -class Document implements HasScopeInterface, TrackCreationInterface, TrackUpdateInterface +class Document implements TrackCreationInterface, TrackUpdateInterface { use TrackCreationTrait; @@ -70,13 +68,6 @@ class Document implements HasScopeInterface, TrackCreationInterface, TrackUpdate */ private $object; - /** - * @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\Scope") - * - * @var \Chill\MainBundle\Entity\Scope The document's center - */ - private $scope; - /** * @ORM\ManyToOne(targetEntity="Chill\DocGeneratorBundle\Entity\DocGeneratorTemplate") */ @@ -122,16 +113,6 @@ class Document implements HasScopeInterface, TrackCreationInterface, TrackUpdate return $this->object; } - /** - * Get scope. - * - * @return \Chill\MainBundle\Entity\Scope - */ - public function getScope(): ?Scope - { - return $this->scope; - } - public function getTemplate(): ?DocGeneratorTemplate { return $this->template; @@ -175,13 +156,6 @@ class Document implements HasScopeInterface, TrackCreationInterface, TrackUpdate return $this; } - public function setScope($scope): self - { - $this->scope = $scope; - - return $this; - } - public function setTemplate(?DocGeneratorTemplate $template): self { $this->template = $template; diff --git a/src/Bundle/ChillDocStoreBundle/Entity/PersonDocument.php b/src/Bundle/ChillDocStoreBundle/Entity/PersonDocument.php index fe888f587..b6cf5376f 100644 --- a/src/Bundle/ChillDocStoreBundle/Entity/PersonDocument.php +++ b/src/Bundle/ChillDocStoreBundle/Entity/PersonDocument.php @@ -13,6 +13,7 @@ namespace Chill\DocStoreBundle\Entity; use Chill\MainBundle\Entity\HasCenterInterface; use Chill\MainBundle\Entity\HasScopeInterface; +use Chill\MainBundle\Entity\Scope; use Chill\PersonBundle\Entity\Person; use Doctrine\ORM\Mapping as ORM; @@ -27,6 +28,13 @@ class PersonDocument extends Document implements HasCenterInterface, HasScopeInt */ private Person $person; + /** + * @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\Scope") + * + * @var \Chill\MainBundle\Entity\Scope The document's center + */ + private $scope; + public function getCenter() { return $this->getPerson()->getCenter(); @@ -37,10 +45,22 @@ class PersonDocument extends Document implements HasCenterInterface, HasScopeInt return $this->person; } + public function getScope(): ?Scope + { + return $this->scope; + } + public function setPerson($person): self { $this->person = $person; return $this; } + + public function setScope($scope): self + { + $this->scope = $scope; + + return $this; + } } diff --git a/src/Bundle/ChillDocStoreBundle/Form/AccompanyingCourseDocumentType.php b/src/Bundle/ChillDocStoreBundle/Form/AccompanyingCourseDocumentType.php index 64a81e9c7..c9f9365a4 100644 --- a/src/Bundle/ChillDocStoreBundle/Form/AccompanyingCourseDocumentType.php +++ b/src/Bundle/ChillDocStoreBundle/Form/AccompanyingCourseDocumentType.php @@ -88,10 +88,5 @@ class AccompanyingCourseDocumentType extends AbstractType $resolver->setDefaults([ 'data_class' => Document::class, ]); - - // $resolver->setRequired(['role', 'center']) - // ->setAllowedTypes('role', [ \Symfony\Component\Security\Core\Role\Role::class ]) - // ->setAllowedTypes('center', [ \Chill\MainBundle\Entity\Center::class ]) - // ; } } diff --git a/src/Bundle/ChillDocStoreBundle/Repository/PersonDocumentACLAwareRepository.php b/src/Bundle/ChillDocStoreBundle/Repository/PersonDocumentACLAwareRepository.php index 540390aef..2fee85a8f 100644 --- a/src/Bundle/ChillDocStoreBundle/Repository/PersonDocumentACLAwareRepository.php +++ b/src/Bundle/ChillDocStoreBundle/Repository/PersonDocumentACLAwareRepository.php @@ -65,7 +65,7 @@ class PersonDocumentACLAwareRepository implements PersonDocumentACLAwareReposito $this->addACL($qb, $person); foreach ($orderBy as $field => $order) { - $qb->addOrderBy($field, $order); + $qb->addOrderBy('d.' . $field, $order); } $qb->setFirstResult($offset)->setMaxResults($limit); diff --git a/src/Bundle/ChillDocStoreBundle/Security/Authorization/AccompanyingCourseDocumentVoter.php b/src/Bundle/ChillDocStoreBundle/Security/Authorization/AccompanyingCourseDocumentVoter.php index 35fde9b28..e37a8deab 100644 --- a/src/Bundle/ChillDocStoreBundle/Security/Authorization/AccompanyingCourseDocumentVoter.php +++ b/src/Bundle/ChillDocStoreBundle/Security/Authorization/AccompanyingCourseDocumentVoter.php @@ -106,6 +106,10 @@ class AccompanyingCourseDocumentVoter extends AbstractChillVoter implements Prov ) { return false; } + + if (self::CREATE === $attribute && null !== $subject->getCourse()) { + return $this->voterHelper->voteOnAttribute($attribute, $subject->getCourse(), $token); + } } return $this->voterHelper->voteOnAttribute($attribute, $subject, $token); diff --git a/src/Bundle/ChillEventBundle/Menu/AdminMenuBuilder.php b/src/Bundle/ChillEventBundle/Menu/AdminMenuBuilder.php index cede2a5b8..63307a99d 100644 --- a/src/Bundle/ChillEventBundle/Menu/AdminMenuBuilder.php +++ b/src/Bundle/ChillEventBundle/Menu/AdminMenuBuilder.php @@ -35,10 +35,10 @@ class AdminMenuBuilder implements LocalMenuBuilderInterface $menu->addChild('Events', [ 'route' => 'chill_event_admin_index', - ]) + ]) ->setAttribute('class', 'list-group-item-header') ->setExtras([ - 'order' => 6500 + 'order' => 6500, ]); $menu->addChild('Event type', [ diff --git a/src/Bundle/ChillMainBundle/Controller/UserApiController.php b/src/Bundle/ChillMainBundle/Controller/UserApiController.php index d1fd4d5bb..6acd0c4dd 100644 --- a/src/Bundle/ChillMainBundle/Controller/UserApiController.php +++ b/src/Bundle/ChillMainBundle/Controller/UserApiController.php @@ -12,7 +12,9 @@ declare(strict_types=1); namespace Chill\MainBundle\Controller; use Chill\MainBundle\CRUD\Controller\ApiController; +use Doctrine\ORM\QueryBuilder; use Symfony\Component\HttpFoundation\JsonResponse; +use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Annotation\Route; class UserApiController extends ApiController @@ -58,4 +60,14 @@ class UserApiController extends ApiController ['groups' => ['read']] ); } + + /** + * @param QueryBuilder $query + */ + protected function customizeQuery(string $action, Request $request, $query): void + { + if ('_index' === $action) { + $query->andWhere($query->expr()->eq('e.enabled', "'TRUE'")); + } + } } diff --git a/src/Bundle/ChillMainBundle/Controller/UserJobApiController.php b/src/Bundle/ChillMainBundle/Controller/UserJobApiController.php new file mode 100644 index 000000000..50b97809a --- /dev/null +++ b/src/Bundle/ChillMainBundle/Controller/UserJobApiController.php @@ -0,0 +1,25 @@ +andWhere($query->expr()->eq('e.active', "'TRUE'")); + } + } +} diff --git a/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php b/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php index 929bffe14..df0affa86 100644 --- a/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php +++ b/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php @@ -19,6 +19,7 @@ use Chill\MainBundle\Controller\LanguageController; use Chill\MainBundle\Controller\LocationController; use Chill\MainBundle\Controller\LocationTypeController; use Chill\MainBundle\Controller\UserController; +use Chill\MainBundle\Controller\UserJobApiController; use Chill\MainBundle\Controller\UserJobController; use Chill\MainBundle\DependencyInjection\Widget\Factory\WidgetFactoryInterface; use Chill\MainBundle\Doctrine\DQL\GetJsonFieldByKey; @@ -501,6 +502,7 @@ class ChillMainExtension extends Extension implements 'name' => 'user_job', 'base_path' => '/api/1.0/main/user-job', 'base_role' => 'ROLE_USER', + 'controller' => UserJobApiController::class, 'actions' => [ '_index' => [ 'methods' => [ diff --git a/src/Bundle/ChillMainBundle/Form/Type/Export/PickCenterType.php b/src/Bundle/ChillMainBundle/Form/Type/Export/PickCenterType.php index 9c36d452f..c73402dd3 100644 --- a/src/Bundle/ChillMainBundle/Form/Type/Export/PickCenterType.php +++ b/src/Bundle/ChillMainBundle/Form/Type/Export/PickCenterType.php @@ -75,8 +75,6 @@ class PickCenterType extends AbstractType public function buildForm(FormBuilderInterface $builder, array $options) { - - $export = $this->exportManager->getExport($options['export_alias']); $centers = $this->authorizationHelper->getReachableCenters( $this->user, diff --git a/src/Bundle/ChillMainBundle/Repository/PostalCodeRepository.php b/src/Bundle/ChillMainBundle/Repository/PostalCodeRepository.php index 02e63771b..1c4a69366 100644 --- a/src/Bundle/ChillMainBundle/Repository/PostalCodeRepository.php +++ b/src/Bundle/ChillMainBundle/Repository/PostalCodeRepository.php @@ -119,8 +119,8 @@ final class PostalCodeRepository implements ObjectRepository $pertinenceClause = ['STRICT_WORD_SIMILARITY(canonical, UNACCENT(?))']; $pertinenceArgs = [$pattern]; - $orWhere = ['canonical %>> UNACCENT(?)']; - $orWhereArgs = [$pattern]; + $andWhere = ['canonical %>> UNACCENT(?)']; + $andWhereArgs = [$pattern]; foreach (explode(' ', $pattern) as $part) { $part = trim($part); @@ -129,8 +129,8 @@ final class PostalCodeRepository implements ObjectRepository continue; } - $orWhere[] = "canonical LIKE '%' || UNACCENT(LOWER(?)) || '%'"; - $orWhereArgs[] = $part; + $andWhere[] = "canonical LIKE '%' || UNACCENT(LOWER(?)) || '%'"; + $andWhereArgs[] = $part; $pertinenceClause[] = "(EXISTS (SELECT 1 FROM unnest(string_to_array(canonical, ' ')) AS t WHERE starts_with(t, UNACCENT(LOWER(?)))))::int"; $pertinenceClause[] = @@ -139,7 +139,7 @@ final class PostalCodeRepository implements ObjectRepository } $query ->setSelectPertinence(implode(' + ', $pertinenceClause), $pertinenceArgs) - ->andWhereClause(implode(' OR ', $orWhere), $orWhereArgs); + ->andWhereClause(implode(' AND ', $andWhere), $andWhereArgs); return $query; } diff --git a/src/Bundle/ChillMainBundle/migrations/Version20210505153727.php b/src/Bundle/ChillMainBundle/migrations/Version20210505153727.php index 34c3c10b9..eca166ae9 100644 --- a/src/Bundle/ChillMainBundle/migrations/Version20210505153727.php +++ b/src/Bundle/ChillMainBundle/migrations/Version20210505153727.php @@ -47,12 +47,12 @@ final class Version20210505153727 extends AbstractMigration '); $this->addSql(' WITH hydrated_addresses AS ( - SELECT *, rank() OVER (PARTITION BY pa_a.person_id ORDER BY validfrom) + SELECT *, rank() OVER (PARTITION BY pa_a.person_id ORDER BY validfrom, id) FROM chill_main_address AS aa JOIN chill_person_persons_to_addresses AS pa_a ON aa.id = pa_a.address_id ) UPDATE chill_main_address AS b SET validto = ( - SELECT validfrom - INTERVAL \'1 DAY\' + SELECT validfrom FROM hydrated_addresses WHERE hydrated_addresses.id = ( SELECT a1.id diff --git a/src/Bundle/ChillMainBundle/migrations/Version20220711150006.php b/src/Bundle/ChillMainBundle/migrations/Version20220711150006.php new file mode 100644 index 000000000..0bac5ac96 --- /dev/null +++ b/src/Bundle/ChillMainBundle/migrations/Version20220711150006.php @@ -0,0 +1,39 @@ +addSql('ALTER TABLE chill_main_workflow_entity_step + DROP CONSTRAINT chill_custom_only_one_step_opened'); + } + + public function getDescription(): string + { + return 'Add a constraint to ensure that only one step is available at a time'; + } + + public function up(Schema $schema): void + { + $this->addSql('ALTER TABLE chill_main_workflow_entity_step + ADD CONSTRAINT chill_custom_only_one_step_opened + EXCLUDE ( + entityworkflow_id WITH = + ) WHERE (transitionafter IS NULL) + DEFERRABLE INITIALLY DEFERRED'); + } +} diff --git a/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseController.php b/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseController.php index 4d78bb42b..bf7f24f4b 100644 --- a/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseController.php +++ b/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseController.php @@ -177,7 +177,7 @@ class AccompanyingCourseController extends Controller */ public function editAction(AccompanyingPeriod $accompanyingCourse): Response { - $this->denyAccessUnlessGranted(AccompanyingPeriodVoter::SEE, $accompanyingCourse); + $this->denyAccessUnlessGranted(AccompanyingPeriodVoter::EDIT, $accompanyingCourse); return $this->render('@ChillPerson/AccompanyingCourse/edit.html.twig', [ 'accompanyingCourse' => $accompanyingCourse, @@ -215,7 +215,7 @@ class AccompanyingCourseController extends Controller // get persons without household $withoutHousehold = []; - foreach ($accompanyingCourse->getParticipations() as $p) { + foreach ($accompanyingCourse->getCurrentParticipations() as $p) { if (false === $p->getPerson()->isSharingHousehold()) { $withoutHousehold[] = $p->getPerson(); } diff --git a/src/Bundle/ChillPersonBundle/Entity/Household/Household.php b/src/Bundle/ChillPersonBundle/Entity/Household/Household.php index 19bda3785..91d9cea57 100644 --- a/src/Bundle/ChillPersonBundle/Entity/Household/Household.php +++ b/src/Bundle/ChillPersonBundle/Entity/Household/Household.php @@ -14,6 +14,7 @@ namespace Chill\PersonBundle\Entity\Household; use ArrayIterator; use Chill\MainBundle\Entity\Address; use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable; +use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Validator\Constraints\Household\MaxHolder; use DateTime; use DateTimeImmutable; @@ -354,7 +355,12 @@ class Household return $this->members; } - public function getMembersDuringMembership(HouseholdMember $membership) + /** + * get all the members during a given membership. + * + * @return Collection|HouseholdMember[] + */ + public function getMembersDuringMembership(HouseholdMember $membership): Collection { return $this->getMembersOnRange( $membership->getStartDate(), @@ -441,6 +447,28 @@ class Household return $this->getNonCurrentMembers($now)->matching($criteria); } + /** + * get all the unique persons during a given membership. + * + * same as @see(self::getMembersDuringMembership}, except that the collection is filtered to + * return unique members. + * + * @return Collection|Person[] + */ + public function getPersonsDuringMembership(HouseholdMember $member): Collection + { + // make list unique + $membersByHash = []; + + foreach ($this->getMembersDuringMembership($member) as $m) { + if (null !== $m && null !== $m->getPerson()) { + $membersByHash[spl_object_hash($m->getPerson())] = $m->getPerson(); + } + } + + return new ArrayCollection(array_values($membersByHash)); + } + public function getPreviousAddressOf(Address $address): ?Address { $iterator = new ArrayIterator($this->getAddressesOrdered()); diff --git a/src/Bundle/ChillPersonBundle/Entity/Person.php b/src/Bundle/ChillPersonBundle/Entity/Person.php index 7da08d740..3a197e414 100644 --- a/src/Bundle/ChillPersonBundle/Entity/Person.php +++ b/src/Bundle/ChillPersonBundle/Entity/Person.php @@ -1184,7 +1184,7 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI } /** - * @deprecated Use `getCurrentPersonAddress` instead + * @deprecated Use @see{Person::getCurrentPersonAddress} or @see{Person::getCurrentHouseholdAddress} instead * * @throws Exception * @@ -1192,7 +1192,9 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI */ public function getLastAddress(?DateTime $from = null) { - return $this->getCurrentPersonAddress(); + return $this->getCurrentHouseholdAddress( + null !== $from ? DateTimeImmutable::createFromMutable($from) : null + ); } public function getLastName(): string diff --git a/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/_join_household.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/_join_household.html.twig index 888ab8d43..2bdeb4cf4 100644 --- a/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/_join_household.html.twig +++ b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/_join_household.html.twig @@ -1,7 +1,7 @@
- + {% if is_granted('CHILL_PERSON_ACCOMPANYING_PERIOD_UPDATE', accompanyingCourse) %}
  • @@ -13,6 +13,8 @@
+ {% endif %} +

{{ 'Some peoples does not belong to any household currently. Add them to an household soon'|trans }}

diff --git a/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/_warning_address.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/_warning_address.html.twig index 1f9c09845..5ba0fe799 100644 --- a/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/_warning_address.html.twig +++ b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/_warning_address.html.twig @@ -3,6 +3,7 @@
+ {% if is_granted('CHILL_PERSON_ACCOMPANYING_PERIOD_UPDATE', accompanyingCourse) %}
  • @@ -14,6 +15,7 @@
+ {% endif %}

{{ 'This course is located at a temporarily address. You should locate this course to an user'|trans }}

{% if not hasPersonLocation %} diff --git a/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingPeriod/_list.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingPeriod/_list.html.twig index 307c462e2..986392056 100644 --- a/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingPeriod/_list.html.twig +++ b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingPeriod/_list.html.twig @@ -48,13 +48,57 @@ {% endmacro %} {% block content %} -
- {% for period in accompanying_periods %} + {%- set acps = [] %} + {%- set acpsClosed = [] %} + {% for acp in accompanying_periods %} + {% if acp.step == 'CLOSED' or (acp.requestorPerson is not same as(person) and acp.openParticipationContainsPerson(person) is null ) %} + {%- set acpsClosed = acpsClosed|merge([acp]) %} + {% else %} + {%- set acps = acps|merge([acp]) %} + {% endif %} + {% endfor %} +
+ {% for period in acps %} {% include 'ChillPersonBundle:AccompanyingPeriod:_list_item.html.twig' with { 'recordAction': _self.recordAction(period, contextEntity) } %} - + {% else %} +

{{ 'Any accompanying periods are open'|trans }}

{% endfor %}
+ + {% if acpsClosed|length > 0 %} +
+
+

+ +

+ +
+ +
+ {% for period in acpsClosed %} + {% include 'ChillPersonBundle:AccompanyingPeriod:_list_item.html.twig' with { + 'recordAction': _self.recordAction(period, contextEntity) + } %} + {% endfor %} +
+ +
+
+
+ {% endif %} {% endblock content %} diff --git a/src/Bundle/ChillPersonBundle/Resources/views/Person/household_history.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/Person/household_history.html.twig index 6f7ce0a06..6cacbdf96 100644 --- a/src/Bundle/ChillPersonBundle/Resources/views/Person/household_history.html.twig +++ b/src/Bundle/ChillPersonBundle/Resources/views/Person/household_history.html.twig @@ -65,18 +65,18 @@

{{ 'household.Members at same time'|trans }}

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

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

{% else %} - {% for m in simultaneous -%} + {% for person in simultaneous -%} {% include '@ChillMain/OnTheFly/_insert_vue_onthefly.html.twig' with { action: 'show', displayBadge: true, - targetEntity: { name: 'person', id: m.person.id }, - buttonText: m.person|chill_entity_render_string, - isDead: m.person.deathdate is not null + targetEntity: { name: 'person', id: person.id }, + buttonText: person|chill_entity_render_string, + isDead: person.deathdate is not null } %} {%- endfor -%} {% endif %} diff --git a/src/Bundle/ChillPersonBundle/Resources/views/Person/list_with_period.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/Person/list_with_period.html.twig index c407b6c57..8cc0128a5 100644 --- a/src/Bundle/ChillPersonBundle/Resources/views/Person/list_with_period.html.twig +++ b/src/Bundle/ChillPersonBundle/Resources/views/Person/list_with_period.html.twig @@ -1,13 +1,15 @@ {% macro button_person_after(person) %} {% set household = person.getCurrentHousehold %} - {% if household is not null %} + {% if household is not null and is_granted('CHILL_PERSON_HOUSEHOLD_SEE', household) %}
  • {% endif %} -
  • - -
  • + {% if is_granted('CHILL_PERSON_ACCOMPANYING_PERIOD_CREATE', person) %} +
  • + +
  • + {% endif %} {% endmacro %} {% macro accompanying_period(acp, person) %} @@ -233,7 +235,8 @@ {%- set acpsClosed = [] %} {%- for acp in person.accompanyingPeriodInvolved %} {%- if is_granted('CHILL_PERSON_ACCOMPANYING_PERIOD_SEE', acp) %} - {% if acp.step == 'CLOSED' %} + {# filter for "current" periods: either the person is a requestor, or is member of the period and not closed #} + {% if acp.step == 'CLOSED' or (acp.requestorPerson is not same as(person) and acp.openParticipationContainsPerson(person) is null ) %} {%- set acpsClosed = acpsClosed|merge([acp]) %} {% else %} {%- set acps = acps|merge([acp]) %} diff --git a/src/Bundle/ChillPersonBundle/Security/Authorization/AccompanyingPeriodVoter.php b/src/Bundle/ChillPersonBundle/Security/Authorization/AccompanyingPeriodVoter.php index 0859ba7bd..c6a06a663 100644 --- a/src/Bundle/ChillPersonBundle/Security/Authorization/AccompanyingPeriodVoter.php +++ b/src/Bundle/ChillPersonBundle/Security/Authorization/AccompanyingPeriodVoter.php @@ -113,7 +113,7 @@ class AccompanyingPeriodVoter extends AbstractChillVoter implements ProvideRoleH ->generate(self::class) ->addCheckFor(null, [self::CREATE, self::REASSIGN_BULK]) ->addCheckFor(AccompanyingPeriod::class, [self::TOGGLE_CONFIDENTIAL, ...self::ALL]) - ->addCheckFor(Person::class, [self::SEE]) + ->addCheckFor(Person::class, [self::SEE, self::CREATE]) ->build(); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Entity/Household/HouseholdTest.php b/src/Bundle/ChillPersonBundle/Tests/Entity/Household/HouseholdTest.php index 7cf8af3e5..b7b805be8 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Entity/Household/HouseholdTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Entity/Household/HouseholdTest.php @@ -135,4 +135,51 @@ final class HouseholdTest extends TestCase $this->assertEquals(new DateTimeImmutable('2021-12-31'), $second->getStartDate()); $this->assertEquals(new DateTimeImmutable('2021-12-31'), $inside->getEndDate()); } + + public function testHouseholdGetPersonsDuringMembership() + { + $household = new Household(); + $person1 = new Person(); + $person2 = new Person(); + $personOut = new Person(); + + $household->addMember( + $member1 = (new HouseholdMember()) + ->setStartDate(new DateTimeImmutable('2021-01-01')) + ->setEndDate(new DateTimeImmutable('2021-12-01')) + ->setPerson($person1) + ); + + $household->addMember( + $member2a = (new HouseholdMember()) + ->setStartDate(new DateTimeImmutable('2021-01-01')) + ->setEndDate(new DateTimeImmutable('2021-05-01')) + ->setPerson($person2) + ); + + $household->addMember( + $member2b = (new HouseholdMember()) + ->setStartDate(new DateTimeImmutable('2021-11-01')) + ->setEndDate(new DateTimeImmutable('2022-06-01')) + ->setPerson($person2) + ); + + $household->addMember( + $memberOut = (new HouseholdMember()) + ->setStartDate(new DateTimeImmutable('2019-01-01')) + ->setEndDate(new DateTimeImmutable('2019-12-01')) + ->setPerson($personOut) + ); + + $this->assertCount(0, $household->getPersonsDuringMembership($memberOut)); + + $this->assertCount(1, $household->getPersonsDuringMembership($member1)); + $this->assertContains($person2, $household->getPersonsDuringMembership($member1)); + + $this->assertCount(1, $household->getPersonsDuringMembership($member2a)); + $this->assertContains($person1, $household->getPersonsDuringMembership($member2a)); + + $this->assertCount(1, $household->getPersonsDuringMembership($member2b)); + $this->assertContains($person1, $household->getPersonsDuringMembership($member2b)); + } }