diff --git a/app/Resources/views/admin/booking/_partial/bucket_modal.html.twig b/app/Resources/views/admin/booking/_partial/bucket_modal.html.twig index d04690ae7..4972675f8 100644 --- a/app/Resources/views/admin/booking/_partial/bucket_modal.html.twig +++ b/app/Resources/views/admin/booking/_partial/bucket_modal.html.twig @@ -97,39 +97,25 @@ It use the materialize modal class https://materializecss.com/modals.html
-
- + {{ form_start(shift_book_forms[shift.id]) }}
- - + {{ form_label(shift_book_forms[shift.id].shifter) }} + {{ form_widget(shift_book_forms[shift.id].shifter) }}
{% if use_fly_and_fixed %}
-
- -
-
- -
-
-
- -
- {% else %} -
- - + {{ form_widget(shift_book_forms[shift.id].fixe) }}
{% endif %} +
+ {% if not use_fly_and_fixed %} + {{ form_widget(shift_book_forms[shift.id].fixe) }} + {% endif %} + +
-
+ {{ form_end(shift_book_forms[shift.id]) }}
{% endif %} diff --git a/app/Resources/views/admin/booking/index.html.twig b/app/Resources/views/admin/booking/index.html.twig index 32f8cdb75..8cf17aed2 100644 --- a/app/Resources/views/admin/booking/index.html.twig +++ b/app/Resources/views/admin/booking/index.html.twig @@ -83,7 +83,7 @@ {% include "admin/booking/_partial/bucket_calendar.html.twig" with { bucketsByDay: bucketsByDay } %}
@@ -30,26 +29,3 @@ onclick="javascript:$('#contactform{{ shift.id }}').submit();return false;">sendEnvoyer
- diff --git a/app/Resources/views/form/fields.html.twig b/app/Resources/views/form/fields.html.twig index 353dc999b..412ffe117 100644 --- a/app/Resources/views/form/fields.html.twig +++ b/app/Resources/views/form/fields.html.twig @@ -11,39 +11,87 @@ {% block autocomplete_beneficiary_widget %} {% spaceless %} - {% set attr = attr|merge({class: (attr.class|default('') ~ 'email_membership_autocomplete')|trim}) %} - {{ form_label(form) }} - + {% endspaceless %} +{% endblock %} + +{% block autocomplete_beneficiary_collection_widget %} + {% spaceless %} +
+ {% for child in form %} + {{ form_widget(child) }} + {% endfor %} +
+ {% endspaceless %} +
+
+ {% endblock %} +{% block radio_choice_widget %} + {% spaceless %} +
+ {% for child in form %} +
+ +
+ {% endfor %} + {% for child in form %} + {{ form_widget(child) }} + {% endfor %} +
+ {% endspaceless %} +{% endblock %} {% block simplemde_editor_row %}
diff --git a/app/Resources/views/user/_partial/user_quick_form.html.twig b/app/Resources/views/user/_partial/user_quick_form.html.twig index 0560e927e..9b3bab510 100644 --- a/app/Resources/views/user/_partial/user_quick_form.html.twig +++ b/app/Resources/views/user/_partial/user_quick_form.html.twig @@ -106,14 +106,13 @@

Ajout à une adhésion existante

-
+
{{ form_errors(form.join_to) }}
-
- person_add - {{ form_widget(form.join_to) }} -
+ person_add + {{ form_widget(form.join_to) }} + {{ form_label(form.join_to) }}
diff --git a/app/config/config.yml b/app/config/config.yml index 3868b78dc..70351882e 100644 --- a/app/config/config.yml +++ b/app/config/config.yml @@ -53,6 +53,7 @@ twig: registration_duration: '%registration_duration%' support_email: '%transactional_mailer_user%' images_tmp_dir: '%images_tmp_dir%' + beneficiary_service: "@beneficiary_service" shift_service: "@shift_service" maximum_nb_of_beneficiaries_in_membership: '%maximum_nb_of_beneficiaries_in_membership%' allow_extra_shifts: '%allow_extra_shifts%' diff --git a/app/config/services.yml b/app/config/services.yml index 1e928ae62..0ab31a0ae 100644 --- a/app/config/services.yml +++ b/app/config/services.yml @@ -37,6 +37,9 @@ services: AppBundle\Controller\AmbassadorController: arguments: - "%time_after_which_members_are_late_with_shifts%" + AppBundle\Controller\BookingController: + arguments: + - "%use_fly_and_fixed%" AppBundle\Helper\: resource: '../../src/AppBundle/Helper' arguments: ['@service_container'] @@ -167,6 +170,12 @@ services: - "%max_time_in_advance_to_book_extra_shifts%" - "%forbid_shift_overlap_time%" + beneficiary_service: + class: AppBundle\Service\BeneficiaryService + public: true + arguments: + - "@doctrine.orm.entity_manager" + logger.user_processor: class: AppBundle\Monolog\MonologUserProcessor arguments: diff --git a/src/AppBundle/Command/SendMassMailCommand.php b/src/AppBundle/Command/SendMassMailCommand.php index 82e731194..061e710eb 100644 --- a/src/AppBundle/Command/SendMassMailCommand.php +++ b/src/AppBundle/Command/SendMassMailCommand.php @@ -100,7 +100,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $to[] = $beneficiary->getEmail(); } if (!$exclude_non_member){ - $non_members = $em->getRepository("AppBundle:User")->findNonMember(); + $non_members = $em->getRepository("AppBundle:User")->findActiveNonMembers(); foreach ($non_members as $user){ $to[] = $user->getEmail(); } @@ -125,4 +125,4 @@ protected function execute(InputInterface $input, OutputInterface $output) $output->writeln('>>> message envoyé à '.count($to).' beneficiaires ('.count($memberships).' comptes membre) '); } -} \ No newline at end of file +} diff --git a/src/AppBundle/Controller/BeneficiaryController.php b/src/AppBundle/Controller/BeneficiaryController.php index 74e7e7a5e..5a02608c7 100644 --- a/src/AppBundle/Controller/BeneficiaryController.php +++ b/src/AppBundle/Controller/BeneficiaryController.php @@ -270,53 +270,6 @@ public function confirmAction(Beneficiary $beneficiary, Request $request) return $this->render('beneficiary/confirm.html.twig', array('beneficiary' => $beneficiary)); } - /** - * @Route("/list", name="beneficiary_list") - * @Method({"POST"}) - * @Security("has_role('ROLE_USER')") - * @param Request $request - * @return \Symfony\Component\HttpFoundation\Response - */ - public function listAction(Request $request) - { - $granted = false; - if ($this->get('security.authorization_checker')->isGranted('ROLE_USER_MANAGER')) - $granted = true; - if ($this->getUser()->getBeneficiary() && count($this->getUser()->getBeneficiary()->getOwnedCommissions())) - $granted = true; - if ($granted && $request->isXmlHttpRequest()){ - $em = $this->getDoctrine()->getManager(); - $userRepo = $em->getRepository(Beneficiary::class); - - $string = $request->get('string'); - - $rsm = new ResultSetMappingBuilder($em); - $rsm->addRootEntityFromClassMetadata('AppBundle:Beneficiary', 'b'); - - $query = $em->createNativeQuery('SELECT b.* FROM beneficiary AS b LEFT JOIN fos_user as u ON u.id = b.user_id WHERE LOWER(CONCAT_WS(u.username,u.email,b.lastname,b.firstname)) LIKE :key', $rsm); - - $beneficiaries = $query->setParameter('key', '%' . $string . '%') - ->getResult(); - - $returnArray = array(); - foreach ($beneficiaries as $beneficiary){ - $dead = false; - if ($beneficiary->getMembership()->isWithdrawn()){ - $dead = true; - } - if (!$this->get('membership_service')->isUptodate($beneficiary->getMembership())){ - $dead = true; - } - if (!$beneficiary->getMembership()){ - $dead = true; - } - $returnArray[] = array('name' => $beneficiary->getAutocompleteLabelFull() ,'icon' => (!$dead) ? $request->getUriForPath('/bundles/app/img/cancel.svg') : ''); - } - return new JsonResponse($returnArray); - } - return new Response("Ajax only",400); - } - private function redirectToShow(Membership $member) { $user = $member->getMainBeneficiary()->getUser(); // FIXME diff --git a/src/AppBundle/Controller/BookingController.php b/src/AppBundle/Controller/BookingController.php index 222a45a75..9026c4acb 100644 --- a/src/AppBundle/Controller/BookingController.php +++ b/src/AppBundle/Controller/BookingController.php @@ -16,6 +16,8 @@ use AppBundle\Security\ShiftVoter; use DateTime; use AppBundle\Entity\ShiftBucket; +use AppBundle\Form\AutocompleteBeneficiaryType; +use AppBundle\Form\RadioChoiceType; use AppBundle\Form\ShiftType; use Exception; use Symfony\Bridge\Doctrine\Form\Type\EntityType; @@ -41,6 +43,16 @@ */ class BookingController extends Controller { + /** + * @var boolean + */ + private $useFlyAndFixed; + + public function __construct(bool $useFlyAndFixed) + { + $this->useFlyAndFixed = $useFlyAndFixed; + } + /** * @return Response */ @@ -407,6 +419,10 @@ public function showBucketAction(Request $request, Shift $bucket) $em = $this->getDoctrine()->getManager(); $shifts = $em->getRepository('AppBundle:Shift')->findBucket($bucket); + $shift_book_forms = []; + foreach ($shifts as $shift) { + $shift_book_forms[$shift->getId()] = $this->createBookForm($shift)->createView(); + } $shift_add_form = $this->createForm( ShiftType::class, $bucket, @@ -419,7 +435,8 @@ public function showBucketAction(Request $request, Shift $bucket) return $this->render('admin/booking/_partial/bucket_modal.html.twig', [ 'shifts' => $shifts, - 'shift_add_form' => $shift_add_form + 'shift_add_form' => $shift_add_form, + 'shift_book_forms' => $shift_book_forms ]); } @@ -731,32 +748,18 @@ public function bookShiftAdminAction(Request $request, Shift $shift) { $session = new Session(); - $form = $this->createFormBuilder() - ->add('shifter', TextType::class) - ->add('fixe', RadioType::class, array('required' => false)) // TODO Symfony 4.1 : 'false_values' => ['false', '0'] - ->getForm(); - + $form = $this->createBookForm($shift); $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { if ($shift->getShifter() && !$shift->getIsDismissed()) { $session->getFlashBag()->add("error", "Désolé, ce créneau est déjà réservé"); - return new Response($this->generateUrl("booking_admin"), 205); - } - - // $fixe = $form->get("fixe")->getData(); // Symfony 3.4 : always returns true, even if "0" is passed from the form data - $fixe = $request->request->get("form")["fixe"] ?? false; // TODO Symfony 4.1 : re-use the previous line - $str = $form->get("shifter")->getData(); - $em = $this->getDoctrine()->getManager(); - // $membership = $em->getRepository('AppBundle:Membership')->findOneFromAutoComplete($str); - // $beneficiary = $membership->getBeneficiaries()->findOneFromAutoComplete($str); - $beneficiary = $em->getRepository('AppBundle:Beneficiary')->findOneFromAutoComplete($str); - - if (!$beneficiary) { - $session->getFlashBag()->add("error", "Impossible de trouve ce béneficiaire 😕"); return $this->redirectToRoute('booking_admin'); } + $fixe = $form->get("fixe")->getData(); + $beneficiary = $form->get("shifter")->getData(); + if ($shift->getFormation() && !$beneficiary->getFormations()->contains($shift->getFormation())) { $session->getFlashBag()->add("error", "Désolé, ce bénévole n'a pas la qualification necessaire (" . $shift->getFormation()->getName() . ")"); return $this->redirectToRoute('booking_admin'); @@ -774,6 +777,7 @@ public function bookShiftAdminAction(Request $request, Shift $shift) $shift->setLastShifter(null); $shift->setFixe($fixe); + $em = $this->getDoctrine()->getManager(); $em->persist($shift); $member = $beneficiary->getMembership(); @@ -949,4 +953,35 @@ private function createDeleteForm(Shift $shift) ->getForm() ; } + + /** + * Creates a form to book a shift entity. + * + * @param Shift $shift The shift entity + * + * @return \Symfony\Component\Form\Form The form + */ + private function createBookForm(Shift $shift) + { + $form = $this->get('form.factory')->createNamedBuilder('shift_book_forms_' . $shift->getId()) + ->setAction($this->generateUrl('admin_shift_book', array('id' => $shift->getId()))) + ->add('shifter', AutocompleteBeneficiaryType::class, array('label'=>'Numéro d\'adhérent ou nom du membre', 'required'=>true)); + + if ($this->useFlyAndFixed) { + $form = $form->add('fixe', RadioChoiceType::class, [ + 'choices' => [ + 'Volant' => 0, + 'Fixe' => 1, + ], + 'data' => 0 + ]); + } else { + $form = $form->add('fixe', HiddenType::class, [ + 'data' => 0 + ]); + } + + return $form->getForm(); + } + } diff --git a/src/AppBundle/Controller/DefaultController.php b/src/AppBundle/Controller/DefaultController.php index ecf99fad0..80d77166a 100644 --- a/src/AppBundle/Controller/DefaultController.php +++ b/src/AppBundle/Controller/DefaultController.php @@ -14,6 +14,7 @@ use AppBundle\Event\HelloassoEvent; use AppBundle\Event\SwipeCardEvent; use AppBundle\Event\ShiftValidatedEvent; +use AppBundle\Form\AutocompleteBeneficiaryCollectionType; use AppBundle\Service\MembershipService; use AppBundle\Twig\Extension\AppExtension; use Psr\Log\LoggerInterface; @@ -351,9 +352,14 @@ public function helloassoNotify(Request $request) public function shiftContactFormAction(Shift $shift, Request $request, \Swift_Mailer $mailer) { + $em = $this->getDoctrine()->getManager(); + $coShifters = $em->getRepository('AppBundle:Beneficiary')->findCoShifters($shift); $formBuilder = $this->createFormBuilder(); $formBuilder->add('from', HiddenType::class, array('data' => $shift->getShifter()->getId())); - $formBuilder->add('to', HiddenType::class, array('label' => 'A')); + $formBuilder->add('to', AutocompleteBeneficiaryCollectionType::class, [ + 'label' => 'A', + 'data' => $coShifters, + ]); $formBuilder->add('message', TextareaType::class, [ 'attr' => ['class' => 'materialize-textarea'], 'label' => 'Message', @@ -364,11 +370,8 @@ public function shiftContactFormAction(Shift $shift, Request $request, \Swift_Ma $form = $formBuilder->getForm(); if ($form->handleRequest($request)->isValid()) { - $to = $form->get('to')->getData(); - $to = json_decode($to); + $beneficiaries = $form->get('to')->getData(); $from = $form->get('from')->getData(); - $em = $this->getDoctrine()->getManager(); - $beneficiaries = $em->getRepository('AppBundle:Beneficiary')->findBy(array('id' => $to)); $from = $em->getRepository('AppBundle:Beneficiary')->findOneBy(array('id' => $from)); $emails = array(); $firstnames = array(); @@ -403,21 +406,11 @@ public function shiftContactFormAction(Shift $shift, Request $request, \Swift_Ma $session->getFlashBag()->add('success', 'Ton message a été transmis à ' . $firstnames); return $this->redirectToRoute('homepage'); - } else { - $em = $this->getDoctrine()->getManager(); - $shifts = $em->getRepository('AppBundle:Shift')->findBy(array('start' => $shift->getStart(), 'end' => $shift->getEnd(), 'job' => $shift->getJob())); - $coShifts = array(); - foreach ($shifts as $s) { - if ($s->getBooker() != null && $s->getId() != $shift->getId()) { - $coShifts[] = $s; - } - } - return $this->render('booking/_partial/home_shift_contactform.html.twig', array( - 'shift' => $shift, - 'coShifts' => $coShifts, - 'form' => $form->createView() - )); } + return $this->render('booking/_partial/home_shift_contactform.html.twig', array( + 'shift' => $shift, + 'form' => $form->createView() + )); } /** diff --git a/src/AppBundle/Controller/MailController.php b/src/AppBundle/Controller/MailController.php index b12b95960..d3910f463 100644 --- a/src/AppBundle/Controller/MailController.php +++ b/src/AppBundle/Controller/MailController.php @@ -5,6 +5,7 @@ use AppBundle\Entity\Beneficiary; use AppBundle\Entity\Shift; use AppBundle\Entity\User; +use AppBundle\Form\AutocompleteBeneficiaryCollectionType; use AppBundle\Form\MarkdownEditorType; use AppBundle\Service\SearchUserFormHelper; use Michelf\Markdown; @@ -27,39 +28,6 @@ */ class MailController extends Controller { - /** - * Get beneficiaries autocomplete labels - * - * @Route("/beneficiaries", name="mail_get_beneficiaries") - * @Method({"GET"}) - */ - public function allBeneficiariesAction() - { - $em = $this->getDoctrine()->getManager(); - $beneficiaries = $em->getRepository('AppBundle:Beneficiary')->findAll(); - $r = array(); - foreach ($beneficiaries as $beneficiary) { - $r[] = $beneficiary->getAutocompleteLabel(); - } - return $this->json($r); - } - - /** - * Get non members autocomplete labels - * - * @Route("/non_members", name="mail_get_non_members") - * @Method({"GET"}) - */ - public function nonMembersListAction() - { - $em = $this->getDoctrine()->getManager(); - $users = $em->getRepository("AppBundle:User")->findNonMember(); - $r = array(); - foreach ($users as $user) { - $r[] = $user->getUsername() . ' [' . $user->getEmail() . ']'; - } - return $this->json($r); - } /** * Edit a message @@ -69,10 +37,11 @@ public function nonMembersListAction() */ public function editActionOneBeneficiary(Request $request, Beneficiary $beneficiary) { - $mailform = $this->getMailForm(); - return $this->render('admin/mail/edit.html.twig', array( + $mailform = $this->getMailForm(array($beneficiary)); + $non_members = $this->getNonMemberEmails(); + return $this->render('admin/mail/send.html.twig', array( 'form' => $mailform->createView(), - 'to' => array($beneficiary), + 'non_members' => $non_members )); } @@ -82,19 +51,20 @@ public function editActionOneBeneficiary(Request $request, Beneficiary $benefici */ public function mailBucketShift(Request $request, Shift $shift) { - $mailform = $this->getMailForm(); if ($shift) { $em = $this->getDoctrine()->getManager(); $shifts = $em->getRepository(Shift::class)->findBy(array('job' => $shift->getJob(), 'start' => $shift->getStart(), 'end' => $shift->getEnd())); - $beneficiary = array(); + $beneficiaries = array(); foreach ($shifts as $shift) { if ($shift->getShifter()) { - $beneficiary[] = $shift->getShifter(); + $beneficiaries[] = $shift->getShifter(); } } - return $this->render('admin/mail/edit.html.twig', array( + $mailform = $this->getMailForm($beneficiaries); + $non_members = $this->getNonMemberEmails(); + return $this->render('admin/mail/send.html.twig', array( 'form' => $mailform->createView(), - 'to' => $beneficiary + 'non_members' => $non_members )); } } @@ -121,22 +91,12 @@ public function editAction(Request $request, SearchUserFormHelper $formHelper) } } } - $non_members_users = array(); - $non_members = $this->getDoctrine()->getManager()->getRepository("AppBundle:User")->findNonMember(); - foreach ($non_members as $user) { - $non_members_emails[] = $user; - } - - $params = array(); - foreach ($request->request as $k => $param) { - $params[$k] = $param; - } + $non_members = $this->getNonMemberEmails(); - $mailform = $this->getMailForm(); - return $this->render('admin/mail/edit.html.twig', array( + $mailform = $this->getMailForm($to); + return $this->render('admin/mail/send.html.twig', array( 'form' => $mailform->createView(), - 'to' => $to, - 'non_member' => $non_members_users + 'non_members' => $non_members )); } @@ -155,20 +115,10 @@ public function sendAction(Request $request, \Swift_Mailer $mailer) if ($mailform->isSubmitted() && $mailform->isValid()) { $em = $this->getDoctrine()->getManager(); //beneficiaries - $to = $mailform->get('to')->getData(); - $to = json_decode($to); - $beneficiaries = $em->getRepository('AppBundle:Beneficiary')->findFromAutoComplete($to); + $beneficiaries = $mailform->get('to')->getData(); //non-member $cci = $mailform->get('cci')->getData(); - $chips = json_decode($cci); - $nonMembers = array(); - $re = '/\[(?.*?)\]/'; - foreach ($chips as $chip) { - $matches = array(); - preg_match($re, $chip, $matches); - if (isset($matches['email'])) - $nonMembers[] = $matches['email']; - } + $nonMembers = json_decode($cci); foreach ($nonMembers as $nonMember) { /** @var User $user */ $user = $em->getRepository(User::class)->findOneBy(array('email' => $nonMember)); @@ -234,7 +184,7 @@ public function sendAction(Request $request, \Swift_Mailer $mailer) return $this->redirectToRoute('mail_edit'); } - private function getMailForm() { + private function getMailForm($to = []) { $mailerService = $this->get('mailer_service'); $mailform = $this->createFormBuilder() ->setAction($this->generateUrl('mail_send')) @@ -244,7 +194,10 @@ private function getMailForm() { 'required' => false, 'choices' => $mailerService->getAllowedEmails() )) - ->add('to', HiddenType::class, array('label' => 'Destinataires', 'required' => true)) + ->add('to', AutocompleteBeneficiaryCollectionType::class, [ + 'data' => $to, + 'label' => "Destinataire(s)", + ]) ->add('cci', HiddenType::class, array('label' => 'Non-membres', 'required' => false)) ->add('template', EntityType::class, array( 'class' => 'AppBundle:EmailTemplate', @@ -259,4 +212,14 @@ private function getMailForm() { ->getForm(); return $mailform; } + + private function getNonMemberEmails() { + $em = $this->getDoctrine()->getManager(); + $non_members = $em->getRepository("AppBundle:User")->findActiveNonMembers(); + $list = []; + foreach ($non_members as $non_member){ + $list[$non_member->getEmail()] = ''; + } + return $list; + } } diff --git a/src/AppBundle/Controller/MembershipController.php b/src/AppBundle/Controller/MembershipController.php index d050ef58f..29d493765 100644 --- a/src/AppBundle/Controller/MembershipController.php +++ b/src/AppBundle/Controller/MembershipController.php @@ -16,6 +16,7 @@ use AppBundle\Event\BeneficiaryAddEvent; use AppBundle\Event\MemberCreatedEvent; use AppBundle\EventListener\SetFirstPasswordListener; +use AppBundle\Form\AutocompleteBeneficiaryType; use AppBundle\Form\BeneficiaryType; use AppBundle\Form\MembershipType; use AppBundle\Form\NoteType; @@ -907,8 +908,8 @@ public function addBeneficiaryAction(Request $request) public function joinAction(Request $request) { $form = $this->createFormBuilder() - ->add('from_text', TextType::class, array('label' => 'Adhérent a joindre', 'attr' => array('class' => 'autocomplete'))) - ->add('dest_text', TextType::class, array('label' => 'au compte de l\'adhérent', 'attr' => array('class' => 'autocomplete'))) + ->add('from_text', AutocompleteBeneficiaryType::class, array('label' => 'Adhérent a joindre')) + ->add('dest_text', AutocompleteBeneficiaryType::class, array('label' => 'au compte de l\'adhérent')) ->add('join', SubmitType::class, array('label' => 'Joindre les deux comptes', 'attr' => array('class' => 'btn'))) ->getForm(); $form->handleRequest($request); @@ -917,47 +918,35 @@ public function joinAction(Request $request) $session = new Session(); if ($form->isSubmitted() && $form->isValid()) { - $fromMemberStr = $form->get('from_text')->getData(); - $fromMember = $em->getRepository('AppBundle:Membership')->findOneFromAutoComplete($fromMemberStr); - if (!$fromMember) { - $session->getFlashBag()->add('error', 'Impossible de trouver le compte à lier.'); + $fromMember = $form->get('from_text')->getData()->getMembership(); + $destMember = $form->get('dest_text')->getData()->getMembership(); + if ($fromMember == $destMember) { + $session->getFlashBag()->add('error', 'Impossible de joindre deux comptes identiques.'); + } else if ($fromMember->getBeneficiaries()->count() >= $this->getParameter('maximum_nb_of_beneficiaries_in_membership')) { + $session->getFlashBag()->add('error', 'Le compte à lier a déjà le nombre maximum de bénéficiaires.'); + }else if ($destMember->getBeneficiaries()->count() >= $this->getParameter('maximum_nb_of_beneficiaries_in_membership')) { + $session->getFlashBag()->add('error', 'Le compte de destination a déjà le nombre maximum de bénéficiaires.'); + } else if ($fromMember->getBeneficiaries()->count() + $destMember->getBeneficiaries()->count() > $this->getParameter('maximum_nb_of_beneficiaries_in_membership')) { + $session->getFlashBag()->add('error', 'La somme des bénéficiaires du compte à lier (' . $destMember->getBeneficiaries()->count() . ') et du compte de destination (' . $fromMember->getBeneficiaries()->count() . ') dépasse le nombre maximum de bénéficiaires.'); } else { - $destMemberStr = $form->get('dest_text')->getData(); - $destMember = $em->getRepository('AppBundle:Membership')->findOneFromAutoComplete($destMemberStr); - if (!$destMember) { - $session->getFlashBag()->add('error', 'Impossible de trouver le compte de destination.'); - } else { - if ($fromMember == $destMember) { - $session->getFlashBag()->add('error', 'Impossible de joindre deux comptes identiques.'); - } else if ($fromMember->getBeneficiaries()->count() >= $this->getParameter('maximum_nb_of_beneficiaries_in_membership')) { - $session->getFlashBag()->add('error', 'Le compte à lier a déjà le nombre maximum de bénéficiaires.'); - }else if ($destMember->getBeneficiaries()->count() >= $this->getParameter('maximum_nb_of_beneficiaries_in_membership')) { - $session->getFlashBag()->add('error', 'Le compte de destination a déjà le nombre maximum de bénéficiaires.'); - } else if ($fromMember->getBeneficiaries()->count() + $destMember->getBeneficiaries()->count() > $this->getParameter('maximum_nb_of_beneficiaries_in_membership')) { - $session->getFlashBag()->add('error', 'La somme des bénéficiaires du compte à lier (' . $destMember->getBeneficiaries()->count() . ') et du compte de destination (' . $fromMember->getBeneficiaries()->count() . ') dépasse le nombre maximum de bénéficiaires.'); - } else { - foreach ($fromMember->getBeneficiaries() as $beneficiary) { - $destMember->addBeneficiary($beneficiary); //in - $fromMember->removeBeneficiary($beneficiary); //out - $beneficiary->setMembership($destMember); - $em->persist($beneficiary); - } - $em->persist($destMember); - $em->flush(); - $fromMember->setMainBeneficiary(null); - $em->remove($fromMember); - $em->flush(); - - $session->getFlashBag()->add('success', 'Les deux comptes adhérents ont bien été fusionnés !'); - - return $this->redirectToShow($destMember); - } + foreach ($fromMember->getBeneficiaries() as $beneficiary) { + $destMember->addBeneficiary($beneficiary); //in + $fromMember->removeBeneficiary($beneficiary); //out + $beneficiary->setMembership($destMember); + $em->persist($beneficiary); } + $em->persist($destMember); + $em->flush(); + $fromMember->setMainBeneficiary(null); + $em->remove($fromMember); + $em->flush(); + + $session->getFlashBag()->add('success', 'Les deux comptes adhérents ont bien été fusionnés !'); + + return $this->redirectToShow($destMember); } } - - $members = $em->getRepository('AppBundle:Membership')->findAll(); //todo exclude closed - return $this->render('admin/member/join.html.twig', array('form' => $form->createView(), 'members' => $members)); + return $this->render('admin/member/join.html.twig', array('form' => $form->createView())); } /** diff --git a/src/AppBundle/Controller/PeriodController.php b/src/AppBundle/Controller/PeriodController.php index e3d858c16..da61a526b 100644 --- a/src/AppBundle/Controller/PeriodController.php +++ b/src/AppBundle/Controller/PeriodController.php @@ -7,6 +7,7 @@ use AppBundle\Entity\Job; use AppBundle\Entity\Period; use AppBundle\Entity\PeriodPosition; +use AppBundle\Form\AutocompleteBeneficiaryType; use AppBundle\Form\PeriodPositionType; use AppBundle\Form\PeriodType; use AppBundle\Repository\JobRepository; @@ -226,6 +227,12 @@ public function editAction(Request $request,Period $period) 'add_position_to_period', array('id' => $period->getId())))) ; + $pp_book_forms = []; + foreach ($period->getPositions() as $position) { + if (!$position->getShifter()) { + $pp_book_forms[$position->getId()] = $this->createBookForm($position)->createView(); + } + } return $this->render('admin/period/edit.html.twig', array( "form" => $form->createView(), @@ -233,7 +240,8 @@ public function editAction(Request $request,Period $period) "beneficiaries" => $beneficiaries, "position_form" => $positionForm->createView(), "delete_form" => $deleteForm->createView(), - "positions_delete_form" => $positionsDeleteForm + "positions_delete_form" => $positionsDeleteForm, + "pp_book_forms" => $pp_book_forms )); } @@ -310,41 +318,38 @@ public function bookPositionToPeriodAction(Request $request, PeriodPosition $pos $session = new Session(); $period = $position->getPeriod(); - if ($position->getShifter()) { - $session->getFlashBag()->add("error", "Désolé, ce créneau est déjà réservé"); - return new Response($this->generateUrl('period_edit',array('id'=>$period->getId())), 205); - } - - $content = json_decode($request->getContent()); - $str = $content->beneficiary; + $form = $this->createBookForm($position); + $form->handleRequest($request); - $em = $this->getDoctrine()->getManager(); - $beneficiary = $em->getRepository('AppBundle:Beneficiary')->findOneFromAutoComplete($str); + if ($form->isSubmitted() && $form->isValid()) { - if (!$beneficiary) { - $session->getFlashBag()->add("error", "Impossible de trouve ce béneficiaire 😕"); - return new Response($this->generateUrl('period_edit',array('id'=>$period->getId())), 205); - } + if ($position->getShifter()) { + $session->getFlashBag()->add("error", "Désolé, ce créneau est déjà réservé"); + return new Response($this->generateUrl('period_edit',array('id'=>$period->getId())), 205); + } - if ($position->getFormation() && !$beneficiary->getFormations()->contains($position->getFormation())) { - $session - ->getFlashBag() - ->add("error", "Désolé, ce bénévole n'a pas la qualification nécessaire (" . $position->getFormation()->getName() . ")"); - return new Response($this->generateUrl('period_edit',array('id'=>$period->getId())), 205); - } + $beneficiary = $form->get("shifter")->getData(); + if ($position->getFormation() && !$beneficiary->getFormations()->contains($position->getFormation())) { + $session + ->getFlashBag() + ->add("error", "Désolé, ce bénévole n'a pas la qualification nécessaire (" . $position->getFormation()->getName() . ")"); + return new Response($this->generateUrl('period_edit',array('id'=>$period->getId())), 205); + } - if (!$position->getBooker()) { - $current_user = $this->get('security.token_storage')->getToken()->getUser(); - $position->setBooker($current_user); - $position->setBookedTime(new \DateTime('now')); - } - $position->setShifter($beneficiary); - $em->persist($position); - $em->flush(); + if (!$position->getBooker()) { + $current_user = $this->get('security.token_storage')->getToken()->getUser(); + $position->setBooker($current_user); + $position->setBookedTime(new \DateTime('now')); + } - $session->getFlashBag()->add("success", "Créneau fixe réservé avec succès pour " . $position->getShifter()); - return new Response($this->generateUrl('period_edit',array('id'=>$period->getId())), 200); + $em = $this->getDoctrine()->getManager(); + $position->setShifter($beneficiary); + $em->persist($position); + $em->flush(); + $session->getFlashBag()->add("success", "Créneau fixe réservé avec succès pour " . $position->getShifter()); + } + return $this->redirectToRoute('period_edit',array('id'=>$period->getId())); } /** @@ -490,5 +495,19 @@ public function generateShiftsForDateAction(Request $request, KernelInterface $k "form" => $form->createView() )); } - + + /** + * Creates a form to book a period position entity. + * + * @param PeriodPosition $pp The period position entity + * + * @return \Symfony\Component\Form\Form The form + */ + private function createBookForm(PeriodPosition $pp) + { + return $this->get('form.factory')->createNamedBuilder('pp_book_forms_' . $pp->getId()) + ->setAction($this->generateUrl('book_position_from_period', array('id' => $pp->getId()))) + ->add('shifter', AutocompleteBeneficiaryType::class, array('label'=>'Numéro d\'adhérent ou nom du membre', 'required'=>true)) + ->getForm(); + } } diff --git a/src/AppBundle/Entity/Beneficiary.php b/src/AppBundle/Entity/Beneficiary.php index 23aa043a6..10a1d1acd 100644 --- a/src/AppBundle/Entity/Beneficiary.php +++ b/src/AppBundle/Entity/Beneficiary.php @@ -224,9 +224,17 @@ public function getDisplayName(): string return $this->getFirstname() . ' ' . $this->getLastname(); } + /** + * /!\ DO NOT MODIFY /!\ + * + * Such a method is also used for autocomplete. If you want to + * change it, you HAVE to adapt the methods used in data + * transformer: BeneficiaryToStringTransformer. Otherwise, + * autocomplete will be broken. + */ public function getDisplayNameWithMemberNumber(): string { - return '#' . $this->getMemberNumber() . ' ' . $this->getDisplayName(); + return '#' . $this->getMemberNumber() . ' ' . $this->getFirstname() . ' ' . $this->getLastname(); } public function getDisplayNameWithMemberNumberAndStatusIcon(): string @@ -620,27 +628,6 @@ public function getStatusIcon(bool $includeLeadingSpace = false):string{ } - public function getAutocompleteLabel(): string - { - return '#' . $this->getMemberNumber() . ' ' . $this->getFirstname() . ' ' . $this->getLastname(); - } - - public function getAutocompleteLabelFull(): string - { - $label = '#' . $this->getMemberNumber(); - - if($this->getMembership()->getWithdrawn()){ - $label .= " [⚠]"; - }elseif ($this->getMembership()->isFrozen()){ - $label .= " [❄]"; - } - - $label .= ' ' . $this->getFirstname() . ' ' . $this->getLastname(); - $label .= ' ' . $this->getEmail() . ' (' . $this->getId() . ')'; - return $label; - - } - /** * Add reservedShift * diff --git a/src/AppBundle/Entity/User.php b/src/AppBundle/Entity/User.php index 9eb670155..0d57c3d23 100644 --- a/src/AppBundle/Entity/User.php +++ b/src/AppBundle/Entity/User.php @@ -311,13 +311,6 @@ public function getAnnotations() return $this->annotations; } - public function getAutocompleteLabel(){ - if ($this->getBeneficiary()) - return '#'.$this->getId().' '.$this->getFirstname().' '.$this->getLastname(); - else - return '#'.$this->getId().' '.$this->getUsername(); - } - /** * @return Beneficiary */ diff --git a/src/AppBundle/Form/AutocompleteBeneficiaryCollectionType.php b/src/AppBundle/Form/AutocompleteBeneficiaryCollectionType.php new file mode 100644 index 000000000..1b6494f1f --- /dev/null +++ b/src/AppBundle/Form/AutocompleteBeneficiaryCollectionType.php @@ -0,0 +1,31 @@ +setDefaults([ + 'entry_type' => AutocompleteBeneficiaryHiddenType::class, + 'allow_add' => true, + 'prototype' => false, + 'block_prefix' => 'autocomplete_beneficiary_collection' + ]); + } + + public function getParent() + { + return CollectionType::class; + } + +} diff --git a/src/AppBundle/Form/AutocompleteBeneficiaryHiddenType.php b/src/AppBundle/Form/AutocompleteBeneficiaryHiddenType.php new file mode 100644 index 000000000..95891cab9 --- /dev/null +++ b/src/AppBundle/Form/AutocompleteBeneficiaryHiddenType.php @@ -0,0 +1,33 @@ +transformer = $transformer; + } + + public function getParent() + { + return HiddenType::class; + } + + public function buildForm(FormBuilderInterface $builder, array $options) + { + $builder->addModelTransformer($this->transformer); + } + +} diff --git a/src/AppBundle/Form/AutocompleteBeneficiaryType.php b/src/AppBundle/Form/AutocompleteBeneficiaryType.php index c5938d419..47fe1a632 100644 --- a/src/AppBundle/Form/AutocompleteBeneficiaryType.php +++ b/src/AppBundle/Form/AutocompleteBeneficiaryType.php @@ -20,15 +20,11 @@ public function __construct(BeneficiaryToStringTransformer $transformer) $this->transformer = $transformer; } - public function getBlockPrefix() - { - return 'autocomplete_beneficiary'; - } - public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults([ - 'invalid_message' => 'Les données sélectionnées ne correspondent à aucun bénéficiaire', + 'block_prefix' => 'autocomplete_beneficiary', + 'attr' => ['class' => 'autocomplete'], ]); } diff --git a/src/AppBundle/Form/DataTransformer/BeneficiaryToStringTransformer.php b/src/AppBundle/Form/DataTransformer/BeneficiaryToStringTransformer.php index 6a784dba1..1aadf2b23 100644 --- a/src/AppBundle/Form/DataTransformer/BeneficiaryToStringTransformer.php +++ b/src/AppBundle/Form/DataTransformer/BeneficiaryToStringTransformer.php @@ -35,7 +35,7 @@ public function transform($beneficiary) return ''; } - return $beneficiary->getAutocompleteLabelFull(); + return $beneficiary->getDisplayNameWithMemberNumber(); } /** @@ -48,44 +48,20 @@ public function transform($beneficiary) public function reverseTransform($autocomplete) { if (!$autocomplete) { - return; + return null; } - $userRepo = $this->entityManager->getRepository(User::class); - $beneficiaryRepo = $this->entityManager->getRepository(Beneficiary::class); + $beneficiary = $this->entityManager + ->getRepository(Beneficiary::class) + ->findOneFromAutoComplete($autocomplete); - $re = '/([a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+).*\(([0-9]+)\)/'; - preg_match($re, $autocomplete, $matches, PREG_OFFSET_CAPTURE, 0); - - if (count($matches)<=1){ + if (null === $beneficiary){ throw new TransformationFailedException(sprintf( 'Aucun utilisateur trouvé avec ces données "%s".', $autocomplete )); } - $user = $userRepo->findOneBy(array('email'=>$matches[1][0])); - $beneficiary = $beneficiaryRepo->find($matches[2][0]); - - if (null === $user) { - throw new TransformationFailedException(sprintf( - 'Aucun utilisateur trouvé avec cet email "%s" !', - $matches[1][0] - )); - } - - if (null === $beneficiary) { - throw new TransformationFailedException(sprintf( - 'Aucun beneficiaire trouvé avec cet id "%s" !', - $matches[2][0] - )); - } - - if ($user != $beneficiary->getUser()) { - throw new TransformationFailedException('L\'email et l\'identifiant ne correspondent pas' - ); - } - return $beneficiary; } } diff --git a/src/AppBundle/Form/RadioChoiceType.php b/src/AppBundle/Form/RadioChoiceType.php new file mode 100644 index 000000000..86d394424 --- /dev/null +++ b/src/AppBundle/Form/RadioChoiceType.php @@ -0,0 +1,30 @@ +setDefaults([ + 'expanded' => true, + 'multiple' => false, + 'block_prefix' => 'radio_choice' + ]); + } + + public function getParent() + { + return ChoiceType::class; + } + + +} diff --git a/src/AppBundle/Repository/BeneficiaryRepository.php b/src/AppBundle/Repository/BeneficiaryRepository.php index b94af61ea..162bee4ee 100644 --- a/src/AppBundle/Repository/BeneficiaryRepository.php +++ b/src/AppBundle/Repository/BeneficiaryRepository.php @@ -24,7 +24,7 @@ public function findOneFromAutoComplete($beneficiary) ->where('CONCAT(\'#\', m.member_number, \' \', b.firstname, \' \', b.lastname) = :fullname') ->setParameter('fullname', $beneficiary); - return $qb->getQuery()->getSingleResult(); + return $qb->getQuery()->getOneOrNullResult(); } /** @@ -62,4 +62,24 @@ public function findAllActive() return $qb->getQuery()->getResult(); } + + public function findCoShifters($shift) + { + $qb = $this->createQueryBuilder('b') + ->leftJoin('b.shifts', 's') + ->where('s.start = :start') + ->andWhere('s.end = :end') + ->andWhere('s.job = :job') + ->andWhere('s.id != :id') + ->andWhere('s.shifter IS NOT NULL') + ->setParameter('start', $shift->getStart()) + ->setParameter('end', $shift->getEnd()) + ->setParameter('job', $shift->getJob()) + ->setParameter('id', $shift->getId()); + + return $qb + ->getQuery() + ->getResult(); + + } } diff --git a/src/AppBundle/Repository/MembershipRepository.php b/src/AppBundle/Repository/MembershipRepository.php index 32b502961..8fca24350 100644 --- a/src/AppBundle/Repository/MembershipRepository.php +++ b/src/AppBundle/Repository/MembershipRepository.php @@ -12,22 +12,6 @@ */ class MembershipRepository extends \Doctrine\ORM\EntityRepository { - /** - * findOneFromAutoComplete - * - * We consider that the $str will have the following format: - * " " - */ - public function findOneFromAutoComplete($str) - { - $re = '/^#([0-9]+).*/'; - preg_match_all($re, $str, $matches, PREG_SET_ORDER, 0); - if (count($matches) == 1) { - $membershipNumber = $matches[0][1]; - return $this->findOneBy(array("member_number" => $membershipNumber)); - } - return null; - } public function findWithNewCycleStarting($date = null) { diff --git a/src/AppBundle/Repository/UserRepository.php b/src/AppBundle/Repository/UserRepository.php index a36e3ea25..03951d6e7 100644 --- a/src/AppBundle/Repository/UserRepository.php +++ b/src/AppBundle/Repository/UserRepository.php @@ -27,13 +27,14 @@ public function findByRole($role) return $qb->getQuery()->getResult(); } - public function findNonMember() + public function findActiveNonMembers() { $qb = $this->_em->createQueryBuilder(); $qb->select('u') ->from($this->_entityName, 'u') ->leftJoin('u.beneficiary', 'b') - ->where('b.id is NULL'); + ->where('b.id is NULL') + ->andWhere("u.enabled = 1"); return $qb->getQuery()->getResult(); } diff --git a/src/AppBundle/Service/BeneficiaryService.php b/src/AppBundle/Service/BeneficiaryService.php new file mode 100644 index 000000000..8d755ddbc --- /dev/null +++ b/src/AppBundle/Service/BeneficiaryService.php @@ -0,0 +1,38 @@ +em = $em; + } + + /** + * Return autocomplete information + */ + public function getAutocompleteBeneficiaries() + { + $beneficiaries = $this->em->getRepository('AppBundle:Beneficiary')->findAllActive(); + + $returnArray = array(); + foreach ($beneficiaries as $beneficiary){ + $returnArray[$beneficiary->getDisplayNameWithMemberNumber()] = ''; + } + return $returnArray; + } + +}