src/Controller/App/PageController.php line 93

Open in your IDE?
  1. <?php
  2. /**
  3.  * This file is part of the CosaVostra, TrackPay, Symfony package.
  4.  * (c) Mohamed Radhi GUENNICHI <rg@mate.tn> <+216 50 711 816>
  5.  * For the full copyright and license information, please view the LICENSE
  6.  * file that was distributed with this source code.
  7.  */
  8. declare(strict_types=1);
  9. namespace App\Controller\App;
  10. use App\Connector\Driver\Sellsy\Driver;
  11. use App\DTO\PayProcedureDto;
  12. use App\Entity\GoCardlessAccount;
  13. use App\Entity\GoCardlessMandate;
  14. use App\Entity\Invoice;
  15. use App\Entity\Procedure;
  16. use App\Entity\ProcedureStep;
  17. use App\Event\GoCardlessMandateEvent;
  18. use App\Event\InvoicePaymentEvent;
  19. use App\Event\ProcedureClaimEvent;
  20. use App\Event\ProcedureClosedEvent;
  21. use App\Event\ProcedureInvoicePaidEvent;
  22. use App\Event\ProcedureStepNotificationMethodEvent;
  23. use App\Events;
  24. use App\Factory\ProcedureClaimFactory;
  25. use App\Form\App\ConfirmActionType;
  26. use App\Form\App\InvoiceCustomerContactType;
  27. use App\Form\App\PayProcedureType;
  28. use App\Form\App\ProcedureClaimType;
  29. use App\Job\Message\ProceduresDispatch;
  30. use App\Manager\GoCardlessAccountManager;
  31. use App\Manager\ProcedureManager;
  32. use App\Payment\Stripe\Client;
  33. use App\Repository\ConnectorRepository;
  34. use App\Repository\GoCardlessAccountRepository;
  35. use App\Repository\GoCardlessMandateRepository;
  36. use App\Repository\InvoiceRepository;
  37. use App\Repository\ProcedureClaimRepository;
  38. use App\Service\GoCardlessService;
  39. use DateTime;
  40. use Doctrine\ORM\EntityManagerInterface;
  41. use Exception;
  42. use GoCardlessPro\Core\Exception\InvalidStateException;
  43. use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
  44. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  45. use Symfony\Component\EventDispatcher\EventDispatcher;
  46. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  47. use Symfony\Component\HttpFoundation\Request;
  48. use Symfony\Component\HttpFoundation\Response;
  49. use Symfony\Component\HttpKernel\Exception\ConflictHttpException;
  50. use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
  51. use Symfony\Component\Routing\Annotation\Route;
  52. use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
  53. use Symfony\Component\Translation\Translator;
  54. use Symfony\Contracts\Translation\TranslatorInterface;
  55. use Vich\UploaderBundle\Templating\Helper\UploaderHelper;
  56. /**
  57.  * Class PageController
  58.  * @package App\Controller\App
  59.  * @Route(name="app_page_")
  60.  */
  61. class PageController extends AbstractController
  62. {
  63.     /**
  64.      * @var EventDispatcherInterface|EventDispatcher
  65.      */
  66.     protected $eventDispatcher;
  67.     /**
  68.      * @var TranslatorInterface|Translator
  69.      */
  70.     protected $translator;
  71.     /**
  72.      * PageController constructor.
  73.      *
  74.      * @param EventDispatcherInterface $eventDispatcher
  75.      * @param TranslatorInterface      $translator
  76.      */
  77.     public function __construct(EventDispatcherInterface $eventDispatcherTranslatorInterface $translator)
  78.     {
  79.         $this->eventDispatcher $eventDispatcher;
  80.         $this->translator $translator;
  81.     }
  82.     /**
  83.      * @return Response
  84.      * @Route("", name="index")
  85.      */
  86.     public function index(): Response
  87.     {
  88.         if (null !== $this->getUser()) {
  89.             return $this->redirectToRoute('app_invoice_index');
  90.         }
  91.         return $this->redirectToRoute('app_security_signin');
  92.     }
  93.     /**
  94.      * @param Procedure        $procedure
  95.      * @param ProcedureManager $manager
  96.      * @param Request          $request
  97.      *
  98.      * @return Response
  99.      * @Route("/action/procedures/{closeToken}/close", name="procedure_close")
  100.      */
  101.     public function closeProcedure(Procedure $procedureProcedureManager $managerRequest $request): Response
  102.     {
  103.         if (!$procedure->isPending()) {
  104.             throw new NotFoundHttpException();
  105.         }
  106.         $invoice $procedure->getInvoice();
  107.         $this->updateLocale(
  108.                 $request,
  109.                 $invoice->getAccountLocale()
  110.         );
  111.         if (!$request->query->has('reason')) {
  112.             return $this->render('app/close/close.html.twig', [
  113.                     'invoice' => $invoice,
  114.                     'token' => $procedure->getCloseToken()
  115.             ]);
  116.         }
  117.         $event = new ProcedureClosedEvent($procedure);
  118.         $manager->close(
  119.                 $procedure,
  120.                 $request->query->get('reason'Procedure::CLOSE_REASON_CANCELLED)
  121.         );
  122.         $this->eventDispatcher->dispatch($eventEvents::PROCEDURE_CLOSED);
  123.         return $this->render('app/close/closed.html.twig', [
  124.                 'invoice' => $invoice
  125.         ]);
  126.     }
  127.     /**
  128.      * @param Request          $request
  129.      * @param Procedure        $procedure
  130.      * @param ProcedureManager $manager
  131.      *
  132.      * @return Response
  133.      * @Route("/action/procedures/{snoozeToken}/snooze", name="procedure_snooze")
  134.      */
  135.     public function snoozeProcedure(Request $requestProcedure $procedureProcedureManager $manager): Response
  136.     {
  137.         if (!$procedure->isPending()) {
  138.             throw new ConflictHttpException();
  139.         }
  140.         $invoice $procedure->getInvoice();
  141.         $this->updateLocale(
  142.                 $request,
  143.                 $invoice->getAccountLocale()
  144.         );
  145.         $form $this->createForm(ConfirmActionType::class, null, [
  146.                 'action_label' => 'action.confirm.checkbox.procedure.snooze'
  147.         ]);
  148.         $form->handleRequest($request);
  149.         if ($form->isSubmitted() && $form->isValid()) {
  150.             // Pause the procedure for 7 days.
  151.             $manager->pause($procedure7);
  152.             return $this->render('app/action/confirmed.html.twig', [
  153.                     'title' => 'action.confirm.title.snooze',
  154.                     'invoice' => $invoice,
  155.                     'result_title' => 'action.result.snooze.title',
  156.                     'result_text' => 'action.result.snooze.text'
  157.             ]);
  158.         }
  159.         return $this->render('app/action/confirm.html.twig', [
  160.                 'title' => 'action.confirm.title.snooze',
  161.                 'invoice' => $invoice,
  162.                 'submit_trans' => 'action.confirm.submit.snooze',
  163.                 'want_confirm_level1_trans' => 'action.snooze.want.confirm.level1',
  164.                 'want_confirm_level2_trans' => 'action.snooze.want.confirm.level2',
  165.                 'form' => $form->createView()
  166.         ]);
  167.     }
  168.     /**
  169.      * @param Request          $request
  170.      * @param Procedure        $procedure
  171.      * @param ProcedureManager $manager
  172.      *
  173.      * @return Response
  174.      * @Route("/action/procedures/{resumeToken}/resume", name="procedure_resume")
  175.      */
  176.     public function resumeProcedure(Request $requestProcedure $procedureProcedureManager $manager): Response
  177.     {
  178.         if (!$procedure->isPaused()) {
  179.             throw new ConflictHttpException();
  180.         }
  181.         $invoice $procedure->getInvoice();
  182.         $this->updateLocale(
  183.                 $request,
  184.                 $invoice->getAccountLocale()
  185.         );
  186.         $form $this->createForm(ConfirmActionType::class, null, [
  187.                 'action_label' => 'action.confirm.checkbox.procedure.resume'
  188.         ]);
  189.         $form->handleRequest($request);
  190.         if ($form->isSubmitted() && $form->isValid()) {
  191.             $manager->resume($procedure);
  192.             return $this->render('app/action/confirmed.html.twig', [
  193.                     'title' => 'action.confirm.title.resume',
  194.                     'invoice' => $invoice,
  195.                     'result_title' => 'action.result.resume.title',
  196.                     'result_text' => 'action.result.resume.text'
  197.             ]);
  198.         }
  199.         return $this->render('app/action/confirm.html.twig', [
  200.                 'title' => 'action.confirm.title.resume',
  201.                 'invoice' => $invoice,
  202.                 'submit_trans' => 'action.confirm.submit.resume',
  203.                 'want_confirm_level1_trans' => 'action.resume.want.confirm.level1',
  204.                 'want_confirm_level2_trans' => 'action.resume.want.confirm.level2',
  205.                 'form' => $form->createView()
  206.         ]);
  207.     }
  208.     /**
  209.      * @param Request                $request
  210.      * @param Procedure              $procedure
  211.      * @param EntityManagerInterface $entityManager
  212.      *
  213.      * @return Response
  214.      * @Route("/action/procedures/{forceDispatchToken}/dispatch", name="procedure_force_dispatch")
  215.      */
  216.     public function forceDispatchProcedure(Request $requestProcedure $procedureEntityManagerInterface $entityManager): Response
  217.     {
  218.         if (!$procedure->isPending()) {
  219.             throw new ConflictHttpException();
  220.         }
  221.         if (null === $pendingStep $procedure->getPendingStep()) {
  222.             throw new NotFoundHttpException();
  223.         }
  224.         $invoice $procedure->getInvoice();
  225.         $this->updateLocale(
  226.                 $request,
  227.                 $invoice->getAccountLocale()
  228.         );
  229.         $form $this->createForm(ConfirmActionType::class, null, [
  230.                 'action_label' => 'action.confirm.checkbox.procedure.dispatch'
  231.         ]);
  232.         $form->handleRequest($request);
  233.         if ($form->isSubmitted() && $form->isValid()) {
  234.             $procedure->refreshForceDispatchToken();
  235.             $pendingStep->setScheduledAt(new DateTime());
  236.             $entityManager->flush();
  237.             $this->dispatchMessage(new ProceduresDispatch([$procedure->getId()]));
  238.             return $this->render('app/action/confirmed.html.twig', [
  239.                     'title' => 'action.confirm.title.dispatch',
  240.                     'invoice' => $invoice,
  241.                     'result_title' => 'action.result.dispatch.title',
  242.                     'result_text' => 'action.result.dispatch.text'
  243.             ]);
  244.         }
  245.         return $this->render('app/action/confirm.html.twig', [
  246.                 'title' => 'action.confirm.title.dispatch',
  247.                 'invoice' => $invoice,
  248.                 'submit_trans' => 'action.confirm.submit.dispatch',
  249.                 'want_confirm_level1_trans' => 'action.dispatch.want.confirm.level1',
  250.                 'want_confirm_level2_trans' => 'action.dispatch.want.confirm.level2',
  251.                 'form' => $form->createView()
  252.         ]);
  253.     }
  254.     /**
  255.      * @param Request                $request
  256.      * @param ProcedureStep          $procedureStep
  257.      * @param string                 $notificationMethod
  258.      * @param EntityManagerInterface $entityManager
  259.      *
  260.      * @return Response
  261.      * @Route("/action/procedure_steps/{notifyToken}/{notificationMethod}", name="procedure_step_notify", requirements={
  262.      *      "notificationMethod"="mail|registered_post|lawyer"
  263.      *     })
  264.      * @ParamConverter("procedureStep", options={"exclude": {"notificationMethod"}})
  265.      */
  266.     public function notifyVia(Request $requestProcedureStep $procedureStepstring $notificationMethodEntityManagerInterface $entityManager): Response
  267.     {
  268.         $procedure $procedureStep->getProcedure();
  269.         $invoice $procedure->getInvoice();
  270.         $this->updateLocale(
  271.                 $request,
  272.                 $invoice->getAccountLocale()
  273.         );
  274.         // Firstly, check if the procedureStep is related to a step that
  275.         // has the property manualNotificationMethod with "TRUE" value.
  276.         // If not, throw conflict exception.
  277.         $step $procedureStep->getStep();
  278.         if (!$step->isManualNotificationMethod()) {
  279.             throw new ConflictHttpException('The given step is not configured for manual notification.');
  280.         }
  281.         if (!$procedure->isPending()) {
  282.             throw new ConflictHttpException('The given procedure is not running.');
  283.         }
  284.         if (null === $nextProcedureStep $procedure->getPendingStep()) {
  285.             throw new ConflictHttpException('Cannot choose a notification method for a NULL next step.');
  286.         }
  287.         $form $this->createForm(ConfirmActionType::class, null, [
  288.                 'action_label' => "action.confirm.checkbox.procedure.notify_$notificationMethod"
  289.         ]);
  290.         $form->handleRequest($request);
  291.         if ($form->isSubmitted() && $form->isValid()) {
  292.             $nextProcedureStep->setNotificationMethod($notificationMethod);
  293.             // Update the notifyToken to disallow multiple calls
  294.             $procedureStep->hashNotifyToken();
  295.             $entityManager->flush();
  296.             $this->eventDispatcher->dispatch(
  297.                     new ProcedureStepNotificationMethodEvent($procedureStep$notificationMethod)
  298.             );
  299.             return $this->render('app/action/confirmed.html.twig', [
  300.                     'title' => "action.confirm.title.notify_$notificationMethod",
  301.                     'invoice' => $invoice,
  302.                     'result_title' => "action.result.notify_$notificationMethod.title",
  303.                     'result_text' => "action.result.notify_$notificationMethod.text"
  304.             ]);
  305.         }
  306.         return $this->render('app/action/confirm.html.twig', [
  307.                 'title' => "action.confirm.title.notify_$notificationMethod",
  308.                 'invoice' => $invoice,
  309.                 'submit_trans' => "action.confirm.submit.notify_$notificationMethod",
  310.                 'want_confirm_level1_trans' => "action.notify_$notificationMethod.want.confirm.level1",
  311.                 'want_confirm_level2_trans' => "action.notify_$notificationMethod.want.confirm.level2",
  312.                 'form' => $form->createView()
  313.         ]);
  314.     }
  315.     /**
  316.      * @param Request                  $request
  317.      * @param Procedure                $procedure
  318.      * @param ProcedureClaimFactory    $claimFactory
  319.      * @param ProcedureClaimRepository $claimRepository
  320.      *
  321.      * @return Response
  322.      * @Route("/action/procedures/{claimToken}/claim", name="procedure_claim")
  323.      */
  324.     public function claimProcedure(
  325.             Request $request,
  326.             Procedure $procedure,
  327.             ProcedureClaimFactory $claimFactory,
  328.             ProcedureClaimRepository $claimRepository): Response
  329.     {
  330.         if (!$procedure->isPending()) {
  331.             throw new NotFoundHttpException();
  332.         }
  333.         $invoice $procedure->getInvoice();
  334.         $this->updateLocale(
  335.                 $request,
  336.                 $invoice->getCustomerLocale()
  337.         );
  338.         $form $this->createForm(ProcedureClaimType::class, $claim $claimFactory->create($procedure));
  339.         $form->handleRequest($request);
  340.         $event = new ProcedureClaimEvent($claim);
  341.         if ($form->isSubmitted() && $form->isValid()) {
  342.             $claimRepository->add($claim);
  343.             $this->eventDispatcher->dispatch($eventEvents::PROCEDURE_CLAIM_AFTER_CREATE);
  344.             return $this->render('app/claim/success.html.twig', [
  345.                     'procedure' => $procedure,
  346.                     'invoice' => $invoice
  347.             ]);
  348.         }
  349.         return $this->render('app/claim/form.html.twig', [
  350.                 'form' => $form->createView(),
  351.                 'invoice' => $invoice
  352.         ]);
  353.     }
  354.     /**
  355.      * @param Procedure                $procedure
  356.      * @param Request                  $request
  357.      * @param EntityManagerInterface   $entityManager
  358.      * @param Client                   $stripeAccount
  359.      * @param GoCardlessAccountManager $goCardlessAccountManager
  360.      *
  361.      * @return Response
  362.      * @Route("/action/procedures/{payToken}/pay", name="procedure_pay")
  363.      */
  364.     public function payProcedure(Procedure $procedureRequest $requestEntityManagerInterface $entityManagerClient $stripeAccountGoCardlessAccountManager $goCardlessAccountManager): Response
  365.     {
  366.         $invoice $procedure->getInvoice();
  367.         if ($invoice->isPaid()) {
  368.             throw new NotFoundHttpException();
  369.         }
  370.         $this->updateLocale($request$invoice->getCustomerLocale());
  371.         $form $this->createForm(PayProcedureType::class, new PayProcedureDto(), [
  372.                 'invoice' => $invoice
  373.         ]);
  374.         $form->handleRequest($request);
  375.         $event = new ProcedureInvoicePaidEvent($procedure);
  376.         if ($form->isSubmitted() && $form->isValid()) {
  377.             // Send notification email
  378.             $this->eventDispatcher->dispatch($eventEvents::PROCEDURE_INVOICE_PAID);
  379.             // Refresh payToken to disallow customer to use the same URL
  380.             $procedure->refreshPayToken();
  381.             $entityManager->flush();
  382.             $this->eventDispatcher->dispatch(
  383.                     new InvoicePaymentEvent($procedureInvoicePaymentEvent::PAYMENT_METHOD_TRANSFER)
  384.             );
  385.             return $this->render('app/pay/paid.html.twig', [
  386.                     'invoice' => $invoice,
  387.                     'has_stripe_account' => $stripeAccount->hasAssociatedAccount($invoice)
  388.             ]);
  389.         }
  390.         return $this->render('app/pay/pay.html.twig', [
  391.                 'invoice' => $invoice,
  392.                 'form' => $form->createView(),
  393.                 'has_stripe_account' => $stripeAccount->hasAssociatedAccount($invoice),
  394.                 'has_gocardless_account' => $goCardlessAccountManager->hasAssociatedAccount($invoice)
  395.         ]);
  396.     }
  397.     /**
  398.      * @param Request                     $request
  399.      * @param Invoice                     $invoice
  400.      * @param GoCardlessMandateRepository $mandateRepository
  401.      * @param GoCardlessAccountRepository $accountRepository
  402.      * @param GoCardlessService           $goCardlessService
  403.      *
  404.      * @return Response
  405.      * @Route("/action/gocardless/{invoice}/mandate", name="gocardless_mandate")
  406.      */
  407.     public function gocardlessMandate(
  408.             Request $request,
  409.             Invoice $invoice,
  410.             GoCardlessMandateRepository $mandateRepository,
  411.             GoCardlessAccountRepository $accountRepository,
  412.             GoCardlessService $goCardlessService): Response
  413.     {
  414.         if (!$invoice->getProcedure()->isPending()) {
  415.             throw new NotFoundHttpException();
  416.         }
  417.         // Fetch the gocardless account related to user's invoice
  418.         if (null === $account $accountRepository->findOneByAccount($invoice->getAccount())) {
  419.             throw new NotFoundHttpException();
  420.         }
  421.         // The invoice should not have an already submitted mandate otherwise
  422.         // Take the existed one and reset it.
  423.         if (null === $mandate $mandateRepository->findOneByInvoice($invoice)) {
  424.             $mandate = (new GoCardlessMandate())
  425.                     ->setInvoice($invoice);
  426.         } else {
  427.             $mandate->reset();
  428.         }
  429.         $mandate->setChargeDate(
  430.                 $request->query->getBoolean('at_due') ? $invoice->getDueDate() : new DateTime()
  431.         );
  432.         // Connect using gocardless account and create a new redirectFlow object
  433.         $redirectFlow $goCardlessService->connect($account->getCredential(GoCardlessAccount::CREDENTIAL_ACCESS_TOKEN))
  434.                 ->redirectFlows()
  435.                 ->create([
  436.                         'params' => [
  437.                                 'description' => $invoice->getName(),
  438.                                 'session_token' => $request->getSession()->getId(),
  439.                                 'success_redirect_url' => $this->generateUrl('app_page_gocardless_mandate_callback', ['invoice' => $invoice->getId()], UrlGeneratorInterface::ABSOLUTE_URL)
  440.                         ]
  441.                 ]);
  442.         // We need to save that, we need it later in the callback
  443.         $mandate->setRedirectFlowId($redirectFlow->id);
  444.         $mandateRepository->save($mandate);
  445.         return $this->redirect($redirectFlow->redirect_url);
  446.     }
  447.     /**
  448.      * @param Request                     $request
  449.      * @param Invoice                     $invoice
  450.      * @param GoCardlessMandateRepository $mandateRepository
  451.      * @param GoCardlessAccountRepository $accountRepository
  452.      * @param GoCardlessService           $goCardlessService
  453.      * @param EntityManagerInterface      $entityManager
  454.      *
  455.      * @return Response
  456.      * @Route("/action/gocardless/{invoice}/mandate/callback", name="gocardless_mandate_callback")
  457.      */
  458.     public function gocardlessMandateCallback(
  459.             Request $request,
  460.             Invoice $invoice,
  461.             GoCardlessMandateRepository $mandateRepository,
  462.             GoCardlessAccountRepository $accountRepository,
  463.             GoCardlessService $goCardlessService,
  464.             EntityManagerInterface $entityManager): Response
  465.     {
  466.         if (!$invoice->getProcedure()->isPending()) {
  467.             throw new NotFoundHttpException();
  468.         }
  469.         if (null === $mandate $mandateRepository->findOneByInvoice($invoice)) {
  470.             throw new NotFoundHttpException();
  471.         }
  472.         // Fetch the gocardless account related to user's invoice
  473.         if (null === $account $accountRepository->findOneByAccount($invoice->getAccount())) {
  474.             throw new NotFoundHttpException();
  475.         }
  476.         $entityManager->beginTransaction();
  477.         try {
  478.             $redirectFlow $goCardlessService->connect($account->getCredential(GoCardlessAccount::CREDENTIAL_ACCESS_TOKEN))
  479.                     ->redirectFlows()
  480.                     ->complete($mandate->getRedirectFlowId(), [
  481.                             'params' => ['session_token' => $request->getSession()->getId()]
  482.                     ]);
  483.             $mandate
  484.                     ->setMandateId($redirectFlow->links->mandate)
  485.                     ->setCustomerId($redirectFlow->links->customer);
  486.             $mandateRepository->save($mandate);
  487.             // Pause the procedure related to the invoice.
  488.             $this->eventDispatcher->dispatch(new GoCardlessMandateEvent($mandate));
  489.             $entityManager->getConnection()->commit();
  490.             return $this->render('app/pay/gocardless_mandate_success.html.twig', [
  491.                     'invoice' => $invoice
  492.             ]);
  493.         } catch (Exception $exception) {
  494.             $entityManager->getConnection()->rollBack();
  495.             throw new ConflictHttpException('Error while trying to process callback'$exception);
  496.         }
  497.     }
  498.     /**
  499.      * @param Invoice $invoice
  500.      * @param Client  $stripeClient
  501.      *
  502.      * @return Response
  503.      * @Route("/action/invoice/{invoice}/paid", name="invoice_paid")
  504.      */
  505.     public function paidStripe(Invoice $invoiceClient $stripeClient): Response
  506.     {
  507.         return $this->render('app/pay/paid.html.twig', [
  508.                 'credit_card' => true,
  509.                 'invoice' => $invoice,
  510.                 'has_stripe_account' => $stripeClient->hasAssociatedAccount($invoice)
  511.         ]);
  512.     }
  513.     /**
  514.      * @param Procedure         $procedure
  515.      * @param Request           $request
  516.      * @param InvoiceRepository $invoiceRepository
  517.      *
  518.      * @return Response
  519.      * @Route("/action/procedures/{customerContactToken}/contacts", name="procedure_customer_contacts")
  520.      */
  521.     public function addCustomerContactProcedure(Procedure $procedureRequest $requestInvoiceRepository $invoiceRepository): Response
  522.     {
  523.         $invoice $procedure->getInvoice();
  524.         $this->updateLocale($request$invoice->getCustomerLocale());
  525.         $form $this->createForm(InvoiceCustomerContactType::class, $invoice);
  526.         $form->handleRequest($request);
  527.         if ($form->isSubmitted() && $form->isValid()) {
  528.             $invoiceRepository->save($invoice);
  529.             return $this->render('app/customer_contact/add_contact_success.html.twig', [
  530.                     'invoice' => $invoice
  531.             ]);
  532.         }
  533.         return $this->render('app/customer_contact/add_contact.html.twig', [
  534.                 'invoice' => $invoice,
  535.                 'form' => $form->createView()
  536.         ]);
  537.     }
  538.     /**
  539.      * @param Request   $request
  540.      * @param Procedure $procedure
  541.      *
  542.      * @return Response
  543.      * @Route("/action/procedures/{procedure}/documents", name="procedure_documents")
  544.      */
  545.     public function documents(Request $requestProcedure $procedure): Response
  546.     {
  547.         $invoice $procedure->getInvoice();
  548.         $this->updateLocale($request$invoice->getCustomerLocale());
  549.         return $this->render('app/action/documents.html.twig', [
  550.                 'title' => 'action.documents.title',
  551.                 'invoice' => $invoice
  552.         ]);
  553.     }
  554.     /**
  555.      * @param Procedure      $procedure
  556.      * @param UploaderHelper $uploaderHelper
  557.      *
  558.      * @return Response
  559.      * @Route("/action/procedures/{procedure}/invoice", name="procedure_invoice")
  560.      */
  561.     public function invoice(Procedure $procedureUploaderHelper $uploaderHelper): Response
  562.     {
  563.         if (null === $invoice $procedure->getInvoice()) {
  564.             throw new NotFoundHttpException();
  565.         }
  566.         if (null !== $url $invoice->getFullFileUrl()) {
  567.             return $this->redirect($url);
  568.         }
  569.         return $this->redirect($uploaderHelper->asset($invoice->getInvoiceFile()));
  570.     }
  571.     /**
  572.      * @return Response
  573.      * @Route("/quickbooksintegrationdisconnected")
  574.      */
  575.     public function quickbooksIntegrationDisconnected(): Response
  576.     {
  577.         return $this->render('app/quickbooks_integration_disconnected.html.twig');
  578.     }
  579.     protected function updateLocale(Request $request, ?string $locale): void
  580.     {
  581.         $locale $locale ?? 'fr';
  582.         $request->getSession()->set('_locale'$locale);
  583.         $this->translator->setLocale($locale);
  584.     }
  585.     /**
  586.      * @param InvoiceRepository   $invoiceRepository
  587.      * @param Driver              $driver
  588.      * @param ConnectorRepository $connectorRepository
  589.      *
  590.      * @return Response
  591.      * @Route("/drivers")
  592.      */
  593.     public function drivers(InvoiceRepository $invoiceRepositoryDriver $driverConnectorRepository $connectorRepository): Response
  594.     {
  595.         $driver->connect(
  596.                 $connectorRepository->find(1)
  597.         )->createPayment(
  598.                 $invoiceRepository->find(314)
  599.         );
  600.         return $this->json(
  601.                 ['done']
  602.         );
  603.     }
  604. }