<?php namespace App\Controller; use App\Entity\Adherent; use App\Entity\CotisationAdherent; use App\Entity\CotisationPrestataire; use App\Entity\GlobalParameter; use App\Entity\Payment; use App\Entity\Prestataire; use App\Entity\TransactionPrestataireAdherent; use App\Entity\User; use App\Entity\TransactionAdherentPrestataire; use App\Enum\MoyenEnum; use App\Form\Type\CotiserFormType; use App\Form\Type\DonAdherentFormType; use App\Form\Type\DonPrestataireFormType; use App\Form\Type\UserInfosFormType; use App\Form\Type\EncaissementFormType; use App\Form\Type\EncaissementValidationFormType; use App\Utils\CotisationUtils; use App\Utils\OperationUtils; use Doctrine\ORM\EntityManagerInterface; use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Session\SessionInterface; use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Security\Core\Security; use Symfony\Component\Translation\TranslatorInterface; use Twig\Environment; class UserController extends AbstractController { private $em; private $translator; private $security; private $operationUtils; private $cotisationUtils; private $session; private $mailer; private $templating; public function __construct( EntityManagerInterface $em, TranslatorInterface $translator, Security $security, OperationUtils $operationUtils, CotisationUtils $cotisationUtils, SessionInterface $session, \Swift_Mailer $mailer, Environment $templating ) { $this->em = $em; $this->translator = $translator; $this->security = $security; $this->operationUtils = $operationUtils; $this->cotisationUtils = $cotisationUtils; $this->session = $session; $this->mailer = $mailer; $this->templating = $templating; } /** * @Route("/cotiser", name="cotiser", defaults={"type": "null"}) * @Route("/cotiser/next", name="cotisernextyear", defaults={"type": "nextyear"}) * @IsGranted("ROLE_USER") */ public function cotiserAction($type, Request $request) { $options = []; $payment_type = ''; if ($this->security->isGranted('ROLE_ADHERENT')) { $options['data_class'] = CotisationAdherent::class; $options['don_class'] = DonAdherentFormType::class; $payment_type = Payment::TYPE_COTISATION_ADHERENT; } elseif ($this->security->isGranted('ROLE_PRESTATAIRE')) { $options['data_class'] = CotisationPrestataire::class; $options['don_class'] = DonPrestataireFormType::class; $payment_type = Payment::TYPE_COTISATION_PRESTA; } $form = $this->createForm(CotiserFormType::class, null, $options); $form->handleRequest($request); if ('nextyear' === $type) { $nextYear = new \DateTime('+1 year'); $startDate = $this->cotisationUtils->isCotisationValid($this->getUser()); $endDate = new \DateTime(date('Y-m-d H:i:s', strtotime('+1 year', strtotime($startDate->format('Y-m-d H:i:s'))))); $kohYear = (string) $endDate->format('Y'); } else { $now = new \DateTime(); $kohYear = (string) $now->format('Y'); } if ($form->isSubmitted()) { $cotisation = $form->getData(); if ($form->isValid()) { //Manage don if null => delete it ! if ($cotisation->getDon() && 0 == $cotisation->getDon()->getMontant()) { $cotisation->setDon(null); } if ('nextyear' === $type) { // Cotiser pour l'année suivante $startDate = $this->cotisationUtils->isCotisationValid($this->getUser()); $cotisation->getCotisationInfos()->setDebut($startDate); $endDate = new \DateTime(date('Y-m-d H:i:s', strtotime('+1 year', strtotime($startDate->format('Y-m-d H:i:s'))))); $cotisation->getCotisationInfos()->setFin($endDate); $cotisation->getCotisationInfos()->setAnnee((string) $endDate->format('Y')); } if (MoyenEnum::MOYEN_EMLC == $cotisation->getMoyen()) { try { $cotisation->setRecu(true); $this->operationUtils->executeOperations($cotisation); $this->addFlash( 'success', // "Cotisation pour l'année {$cotisation->getCotisationInfos()->getAnnee()} bien reçue. Merci !" $this->translator->trans('Cotisation bien reçue. Merci !') ); } catch (\Exception $e) { $this->addFlash( 'error', $this->translator->trans('Problème avec la cotisation !') . ' ' . $e->getMessage() ); } return $this->redirectToRoute('index'); } elseif (MoyenEnum::MOYEN_HELLOASSO == $cotisation->getMoyen()) { if ($this->security->isGranted('ROLE_ADHERENT')) { $url = $this->em->getRepository(GlobalParameter::class)->val(GlobalParameter::HELLOASSO_URL_COTISATION_ADHERENT); } else { $url = $this->em->getRepository(GlobalParameter::class)->val(GlobalParameter::HELLOASSO_URL_COTISATION_PRESTATAIRE); } return $this->redirect($url); } elseif (MoyenEnum::MOYEN_CB == $cotisation->getMoyen()) { // Redirect to payment page return $this->forward('App\Controller\PaymentController::preparePaymentAction', [ 'form' => $form, 'type' => $payment_type, ]); } } else { $this->addFlash( 'error', $this->translator->trans('Problème avec la cotisation !') . ' ' . $form->getErrors() ); } } return $this->render('@kohinos/cotiser.html.twig', [ 'form' => $form->createView(), 'koh_year' => $kohYear, ]); } /** * @Route("/userinfos", name="user_infos") * @IsGranted("ROLE_USER") */ public function userInfosAction(Request $request) { $form = $this->createForm(UserInfosFormType::class, $this->getUser()); $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { $this->em->persist($form->getData()); $this->em->flush(); $this->addFlash( 'success', $this->translator->trans("Infos de l'utilisateur modifiée !") ); $referer = $request->headers->get('referer'); if ($referer && !$request->isXmlHttpRequest()) { return $this->redirect($referer); } elseif (!$request->isXmlHttpRequest()) { return new Response('', Response::HTTP_BAD_REQUEST); } } return $this->redirectToRoute('index'); // return $this->render('@kohinos/presta/infos.html.twig', array( // 'form' => $form->createView() // )); } /** * @Route("/account/user", name="myaccount") * @IsGranted("ROLE_USER") */ public function myaccountAction(Request $request) { return $this->render('@kohinos/common/myaccount.html.twig', [ ]); } /** * @Route("/search/adherent", methods="GET", name="searchadherent") * @IsGranted("ROLE_USER") */ public function searchAdherentAction(Request $request) { $term = $request->get('term'); $result = $this->em->getRepository(Adherent::class)->findsByData(['email' => $term, 'name' => $term]); $arrayData = []; foreach ($result as $adherent) { $arrayData[] = ['id' => $adherent->getId(), 'text' => $adherent->__toString()]; } return new JsonResponse(['results' => $arrayData]); } /** * @Route("/user/achatMonnaieAConfirmer", name="achatMonnaieAConfirmer") * @IsGranted({"ROLE_ADHERENT", "ROLE_PRESTATAIRE"}) */ public function achatMonnaieAConfirmerAction(Request $request) { $obj = $this->operationUtils->getCurrentAccountable($this->security->getUser()); if ($obj instanceof Prestataire) { return $this->redirectToRoute('achatMonnaieAConfirmerPrestataire'); } elseif ($obj instanceof Adherent) { return $this->redirectToRoute('achatMonnaieAConfirmerAdherent'); } return $this->redirectToRoute('index'); } /** * @Route("/user/listachatMonnaieAConfirmer", name="listachatMonnaieAConfirmer") * @IsGranted({"ROLE_ADHERENT", "ROLE_PRESTATAIRE"}) */ public function listachatMonnaieAConfirmerAction(Request $request) { $obj = $this->operationUtils->getCurrentAccountable($this->security->getUser()); if ($obj instanceof Prestataire) { return $this->redirectToRoute('listachatMonnaieAConfirmerPrestataire'); } elseif ($obj instanceof Adherent) { return $this->redirectToRoute('listachatMonnaieAConfirmerAdherent'); } return $this->redirectToRoute('index'); } /** * @param Request $request * @param TransactionAdherentPrestataire $transactionAdherentPrestataire * @return void * @IsGranted({"ROLE_CAISSIER", "ROLE_PRESTATAIRE"}) * @Route("/cancel-transaction-adherent-prestataire/{id}", name="cancel_transaction_adherent_prestataire") */ public function cancelTransactionAdherentPrestataireAction(Request $request, TransactionAdherentPrestataire $transactionAdherentPrestataire) { $adherent = $transactionAdherentPrestataire->getExpediteur(); $presta = $transactionAdherentPrestataire->getDestinataire(); //Make sure current user is destinataire of transaction if(!in_array($this->getUser(),$presta->getUsers()->toArray())) { $this->addFlash( 'error', "Vous n'êtes pas autorisé à annuler cette transaction." ); return $this->redirectToRoute('index'); } //Make sure flux has not been cancelled already if($transactionAdherentPrestataire->getCancellerFlux()) { $this->addFlash( 'error', "Cette transaction a déjà été annulée." ); return $this->redirectToRoute('index'); } // Check prestataire has enough funds $balance = $presta->getEmlcAccount()->getBalance(); //I think this is unlikely in real situation but we need to make sure prestataire has enough founds $montant = $transactionAdherentPrestataire->getMontant(); if ($balance < $montant) { $this->addFlash( 'error', 'Fonds insuffisants pour annuler cette transaction.' ); return $this->redirectToRoute('index'); } //Create new transaction in opposite direction $flux = new TransactionPrestataireAdherent(); $flux->setExpediteur($presta); $flux->setDestinataire($adherent); $flux->setOperateur($this->security->getUser()); $flux->setMontant($montant); $flux->setMoyen(MoyenEnum::MOYEN_EMLC); // The flux to cancel a ccas flux is also marked as ccas if ($this->getParameter('ccas_mode')) { $flux->setIsCcas($transactionAdherentPrestataire->getIsCcas()); } $now = (new \Datetime('now'))->format('d/m/Y H:i:s'); $flux->setReference( 'Remboursement en Monnaie Solidaire du ' . $now . ' annule ' . $transactionAdherentPrestataire->getReference() ); $this->em->persist($flux); $this->operationUtils->executeOperations($flux); //Mark original transaction as cancelled $transactionAdherentPrestataire->setCancellerFlux($flux); $this->em->flush(); $this->addFlash( 'success', 'La transaction a bien été annulée.' ); return $this->redirectToRoute('index'); } /** * Payment terminal page. * * @Route("/encaissement", name="encaissement") * @IsGranted({"ROLE_CAISSIER", "ROLE_PRESTATAIRE"}) */ public function encaissementAction(Request $request) { // If conventionnement process is activated, prevent access to payment terminal if prestataire conventionnement is not set $conventionnementMode = $this->getParameter('presta_self_init_and_eval'); if ($conventionnementMode == true) { $presta = $this->session->get('_prestagere'); $presta = $this->em->getRepository(Prestataire::class)->findOneById($presta->getId()); $conventionnement = $presta->getConventionnement(); if ($conventionnement == null || $conventionnement == 0) { $this->addFlash( 'error', $this->translator->trans("Impossible d'accéder au terminal de paiement tant que votre conventionnement n'a pas été établi par un·e gestionnaire.") ); return $this->redirectToRoute('index'); } } $form = $this->createForm(EncaissementFormType::class, null, []); $form->handleRequest($request); $validation = false; // validation = user enters its code $insufficientBalance = null; // if not null, will indicate for the template an insufficient founds situation if ($form->isSubmitted()) { $data = $form->getData(); if ($form->isValid()) { $adherent = $data["adherent"]; $adherent_code = $adherent->getPaymentCode(); $input_code = $data["payment_code"]; // Perform first step checks if (empty($input_code)) { // if adherent account isn't enabled if (!$adherent->getUser()->isEnabled()) { $this->addFlash( 'error', $this->translator->trans('Le compte de l\'habitant·e est désactivé.') ); goto end; } // if adherent doesn't have a validation code if (is_null($adherent_code)) { $this->addFlash( 'error', $this->translator->trans('L\'habitant·e n\'a pas encore défini un code de validation de paiement sur son espace personnel, il·elle ne peut pas payer en Monnaie Solidaire pour l\'instant.') ); goto end; } // If entered amount is above maximum amount, depending on prestataire conventionnement if ($conventionnementMode == true) { $transaction_amount = floatval($data["montant"]); $montantPanier = floatval($form['montantPanier']->getData()); $maxAmount = $montantPanier * floatval($conventionnement); if ($transaction_amount > $maxAmount) { $this->addFlash( 'error', $this->translator->trans('Montant maximal en MonA dépassé. Veuillez modifier le montant en MonA.') ); goto end; } } // When the form is submitted & valid, and the user account checks passed, we enter the validation process $validation = true; // First step validated (set user & amount) -> go to validation goto end; } else { // We're in the validation process $validation = true; } // Check validation code // NOTE as we use password salt, the user must change his payment code if his password changes $encoded_input = crypt($input_code, $adherent->getUser()->getSalt()); if (!hash_equals($adherent_code, $encoded_input)) { $this->addFlash( 'error', $this->translator->trans('Code incorrect') ); goto end; } // Check adherent has enough funds $balance = $adherent->getEmlcAccount()->getBalance(); $transaction_amount = floatval($data["montant"]); if ($balance < $transaction_amount) { // Send mail for insufficient funds $subject = $this->translator->trans('[MONNAIE ALIMENTAIRE COMMUNE] – Solde insuffisant'); $mail = (new \Swift_Message($subject)) ->setFrom($this->em->getRepository(GlobalParameter::class)->val(GlobalParameter::MLC_NOTIF_EMAIL)) ->setTo($adherent->getUser()->getEmail()) ->setBody( $this->templating->render( '@kohinos/email/tav/insufficient_funds.html.twig', [ 'subject' => $subject ] ), 'text/html' ); $this->mailer->send($mail); $this->addFlash( 'error', $this->translator->trans('Solde de l\'habitant·e insuffisant') ); $insufficientBalance = $balance; goto end; } // No error at this point: save transaction $flux = new TransactionAdherentPrestataire(); $flux->setExpediteur($adherent); if (!isset($presta)) { $presta = $this->session->get('_prestagere'); $presta = $this->em->getRepository(Prestataire::class)->findOneById($presta->getId()); } $flux->setDestinataire($presta); $flux->setOperateur($this->security->getUser()); $flux->setMontant($transaction_amount); $flux->setMoyen(MoyenEnum::MOYEN_EMLC); $now = (new \Datetime('now'))->format('d/m/Y H:i:s'); $flux->setReference('Achat en Monnaie Solidaire du ' . $now); // Mark transaction as CCAS if at the time of the transation, both adherent & presta are CCAS complient if ( $this->getParameter('ccas_mode') && $adherent->getCcasEligible() && $adherent->getCcasAccepted() && $presta->getCcasOk() ) { $flux->setIsCcas(true); } $this->em->persist($flux); $this->operationUtils->executeOperations($flux); $this->em->flush(); $redirectParams = []; if ($form->has('montantPanier')) { $transaction_amount = floatval($data["montant"]); $montantPanier = floatval($form['montantPanier']->getData()); $redirectParams['remainingAmount'] = $montantPanier - $transaction_amount; } return $this->redirectToRoute('encaissementSuccess', $redirectParams); } else { $this->addFlash( 'error', $this->translator->trans('Problème avec l\'encaissement !') . ' ' . $form->getErrors() ); } } end: $templateData = [ 'form' => $form->createView(), 'validation' => $validation, 'insufficientBalance' => $insufficientBalance ]; if ($conventionnementMode == true) { $templateData['conventionnement'] = $conventionnement; } return $this->render('@kohinos/tav/encaissement_page.html.twig', $templateData); } /** * @Route("/encaissement/succes", name="encaissementSuccess") * @IsGranted({"ROLE_CAISSIER", "ROLE_PRESTATAIRE"}) */ public function encaissementSuccessAction(Request $request) { $templateData = []; $remainingAmount = $request->query->get('remainingAmount'); if ($remainingAmount != '') { $templateData['remainingAmount'] = $remainingAmount; } return $this->render('@kohinos/tav/payment_done_page.html.twig', $templateData); } }