diff --git a/CHANGELOG.md b/CHANGELOG.md index 7185ca588..57372ed35 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,6 +47,7 @@ and this project adheres to * [contact] add contact button color changed plus the pipe at the side removed (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/506) * [household] create-edit household composition placed in separate page to avoid confusion (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/505) * [blur] Improved positioning of toggle icon (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/486) +* [parcours] List of parcours for a specific user so they can be reassigned in case of absence (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/509) * [thirdparty] Thirdparty view page, english text translated (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/534) * [social_action] Translation changed in evaluation section (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/512) diff --git a/src/Bundle/ChillPersonBundle/Controller/ReassignAccompanyingPeriodController.php b/src/Bundle/ChillPersonBundle/Controller/ReassignAccompanyingPeriodController.php new file mode 100644 index 000000000..ac5b6dfab --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Controller/ReassignAccompanyingPeriodController.php @@ -0,0 +1,115 @@ +accompanyingPeriodACLAwareRepository = $accompanyingPeriodACLAwareRepository; + $this->engine = $engine; + $this->formFactory = $formFactory; + $this->paginatorFactory = $paginatorFactory; + $this->security = $security; + $this->userRepository = $userRepository; + $this->userRender = $userRender; + } + + /** + * @Route("/{_locale}/person/accompanying-periods/reassign", name="chill_course_list_reassign") + */ + public function listAction(Request $request): Response + { + if (!$this->security->isGranted('ROLE_USER') || !$this->security->getUser() instanceof User) { + throw new AccessDeniedException(); + } + + $form = $this->buildFilterForm(); + + $form->handleRequest($request); + + $total = $this->accompanyingPeriodACLAwareRepository->countByUserOpenedAccompanyingPeriod( + $form['user']->getData() + ); + $paginator = $this->paginatorFactory->create($total); + $periods = $this->accompanyingPeriodACLAwareRepository + ->findByUserOpenedAccompanyingPeriod( + $form['user']->getData(), + ['openingDate' => 'ASC'], + $paginator->getItemsPerPage(), + $paginator->getCurrentPageFirstItemNumber() + ); + + return new Response( + $this->engine->render('@ChillPerson/AccompanyingPeriod/reassign_list.html.twig', [ + 'paginator' => $paginator, + 'periods' => $periods, + 'form' => $form->createView(), + ]) + ); + } + + private function buildFilterForm(): FormInterface + { + $data = [ + 'user' => null, + ]; + $builder = $this->formFactory->createBuilder(FormType::class, $data, [ + 'method' => 'get', 'csrf_protection' => false, ]); + + $builder + ->add('user', EntityType::class, [ + 'class' => User::class, + 'choices' => $this->userRepository->findByActive(['username' => 'ASC']), + 'choice_label' => function (User $u) { + return $this->userRender->renderString($u, []); + }, + 'multiple' => false, + 'label' => 'User', + 'required' => false, + ]); + + return $builder->getForm(); + } +} diff --git a/src/Bundle/ChillPersonBundle/Menu/SectionMenuBuilder.php b/src/Bundle/ChillPersonBundle/Menu/SectionMenuBuilder.php index 04f989d84..4635eb4fc 100644 --- a/src/Bundle/ChillPersonBundle/Menu/SectionMenuBuilder.php +++ b/src/Bundle/ChillPersonBundle/Menu/SectionMenuBuilder.php @@ -22,15 +22,9 @@ use Symfony\Contracts\Translation\TranslatorInterface; */ class SectionMenuBuilder implements LocalMenuBuilderInterface { - /** - * @var AuthorizationCheckerInterface - */ - protected $authorizationChecker; + protected AuthorizationCheckerInterface $authorizationChecker; - /** - * @var TranslatorInterface - */ - protected $translator; + protected TranslatorInterface $translator; /** * SectionMenuBuilder constructor. @@ -63,6 +57,14 @@ class SectionMenuBuilder implements LocalMenuBuilderInterface 'order' => 11, 'icons' => ['plus'], ]); + + $menu->addChild($this->translator->trans('Accompanying courses of users'), [ + 'route' => 'chill_course_list_reassign', + ]) + ->setExtras([ + 'order' => 12, + 'icons' => ['task'], + ]); } public static function getMenuIds(): array diff --git a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriodACLAwareRepository.php b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriodACLAwareRepository.php index 6e6c3ca44..f4c9a52a3 100644 --- a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriodACLAwareRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriodACLAwareRepository.php @@ -11,6 +11,7 @@ declare(strict_types=1); namespace Chill\PersonBundle\Repository; +use Chill\MainBundle\Entity\User; use Chill\MainBundle\Security\Authorization\AuthorizationHelper; use Chill\MainBundle\Security\Resolver\CenterResolverDispatcherInterface; use Chill\PersonBundle\Entity\AccompanyingPeriod; @@ -41,6 +42,37 @@ final class AccompanyingPeriodACLAwareRepository implements AccompanyingPeriodAC $this->centerResolverDispatcher = $centerResolverDispatcher; } + public function buildQueryOpenedAccompanyingCourseByUser(?User $user) + { + $qb = $this->accompanyingPeriodRepository->createQueryBuilder('ap'); + + $qb->where($qb->expr()->eq('ap.user', ':user')) + ->andWhere( + $qb->expr()->neq('ap.step', ':draft'), + $qb->expr()->orX( + $qb->expr()->isNull('ap.closingDate'), + $qb->expr()->gt('ap.closingDate', ':now') + ) + ) + ->setParameter('user', $user) + ->setParameter('now', new \DateTime('now')) + ->setParameter('draft', AccompanyingPeriod::STEP_DRAFT); + + return $qb; + } + + public function countByUserOpenedAccompanyingPeriod(?User $user): int + { + if (null === $user) { + return 0; + } + + return $this->buildQueryOpenedAccompanyingCourseByUser($user) + ->select('COUNT(ap)') + ->getQuery() + ->getSingleScalarResult(); + } + public function findByPerson( Person $person, string $role, @@ -92,4 +124,25 @@ final class AccompanyingPeriodACLAwareRepository implements AccompanyingPeriodAC return $qb->getQuery()->getResult(); } + + /** + * @return array|AccompanyingPeriod[] + */ + public function findByUserOpenedAccompanyingPeriod(?User $user, array $orderBy = [], int $limit = 0, int $offset = 50): array + { + if (null === $user) { + return []; + } + + $qb = $this->buildQueryOpenedAccompanyingCourseByUser($user); + + $qb->setFirstResult($offset) + ->setMaxResults($limit); + + foreach ($orderBy as $field => $direction) { + $qb->addOrderBy('ap.'.$field, $direction); + } + + return $qb->getQuery()->getResult(); + } } diff --git a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriodACLAwareRepositoryInterface.php b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriodACLAwareRepositoryInterface.php index 778e8d8fe..0c5af217f 100644 --- a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriodACLAwareRepositoryInterface.php +++ b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriodACLAwareRepositoryInterface.php @@ -11,6 +11,7 @@ declare(strict_types=1); namespace Chill\PersonBundle\Repository; +use Chill\MainBundle\Entity\User; use Chill\PersonBundle\Entity\Person; interface AccompanyingPeriodACLAwareRepositoryInterface @@ -22,4 +23,8 @@ interface AccompanyingPeriodACLAwareRepositoryInterface ?int $limit = null, ?int $offset = null ): array; + + public function findByUserOpenedAccompanyingPeriod(?User $user, array $orderBy = [], int $limit = 0, int $offset = 50): array; + + public function countByUserOpenedAccompanyingPeriod(?User $user): int; } diff --git a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriodRepository.php b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriodRepository.php index 122b658bd..ab7006fd8 100644 --- a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriodRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriodRepository.php @@ -78,6 +78,16 @@ final class AccompanyingPeriodRepository implements ObjectRepository ->getResult(); } + public function findConfirmedByUser(User $user) + { + $qb = $this->createQueryBuilder('ap'); + $qb->where($qb->expr()->eq('ap.user', ':user')) + ->andWhere('ap.step', 'CONFIRMED') + ->setParameter('user', $user); + + return $qb; + } + public function findOneBy(array $criteria): ?AccompanyingPeriod { return $this->findOneBy($criteria); diff --git a/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingPeriod/reassign_list.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingPeriod/reassign_list.html.twig new file mode 100644 index 000000000..87166d990 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingPeriod/reassign_list.html.twig @@ -0,0 +1,82 @@ +{% extends 'ChillMainBundle::layout.html.twig' %} + +{% block title 'period_by_user_list.Period by user'|trans %} + +{% block js %} + {{ encore_entry_script_tags('mod_set_referrer') }} +{% endblock %} + +{% block css %} + {{ encore_entry_link_tags('mod_set_referrer') }} +{% endblock %} + +{% macro period_meta(period) %} + {% if is_granted('CHILL_PERSON_ACCOMPANYING_PERIOD_UPDATE', period) %} +
+ {% endif %} +{% endmacro %} + + +{% macro period_actions(period) %} + {% if is_granted('CHILL_PERSON_ACCOMPANYING_PERIOD_SEE', period) %} +{{ 'period_by_user_list.Pick a user'|trans }}
+ {% elseif periods|length == 0 and form.user.vars.value is not empty %} +{{ 'period_by_user_list.Any course or no authorization to see them'|trans }}
+ + {% else %} +{{ paginator.totalItems }} parcours à réassigner (calculé ce jour à {{ null|format_time('medium') }})
+ +