src/Controller/CourseController.php line 685

Open in your IDE?
  1. <?php
  2. namespace App\Controller;
  3. use App\Entity\Person;
  4. use App\Entity\Category;
  5. use App\Entity\Course;
  6. use App\Entity\Order;
  7. use App\Entity\CourseData;
  8. use App\Entity\Invoice;
  9. use App\Entity\Textblocks;
  10. use App\Form\CourseType;
  11. use App\Repository\CourseFieldRepository;
  12. use App\Repository\CourseDataRepository;
  13. use App\Service\PdfService;
  14. use App\Form\CourseImagesType;
  15. use App\Service\MailerService;
  16. use App\Service\InvoiceService;
  17. use App\Service\SepaXmlService;
  18. use App\Service\PaymentService;
  19. use App\Entity\InvoicePayment;
  20. use App\Entity\CourseOccurrence;
  21. use App\Entity\OrderItem;
  22. use App\Repository\ClientConfigRepository;
  23. use App\Repository\OrderRepository;
  24. use App\Repository\CourseRepository;
  25. use App\Service\EmailHistoryService;
  26. use App\Service\ConfigurationService;
  27. use App\Repository\CartItemRepository;
  28. use App\Entity\CustomerHistoryEntry;
  29. use App\Service\CustomerHistoryService;
  30. use App\Repository\TextblocksRepository;
  31. use App\Repository\WaitItemRepository;
  32. use App\Repository\OrderItemRepository;
  33. use App\Service\Exception\ServiceException;
  34. use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
  35. use App\Repository\CourseOccurrenceRepository;
  36. use App\Repository\InvoiceItemRepository;
  37. use App\Repository\InvoiceRepository;
  38. use App\Service\OrderService;
  39. use Symfony\Component\HttpFoundation\Response;
  40. use Symfony\Component\HttpFoundation\Request;
  41. use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
  42. use Symfony\Component\Routing\Annotation\Route;
  43. use Doctrine\Common\Collections\ArrayCollection;
  44. use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
  45. use Menke\UserBundle\Controller\AbstractClientableController;
  46. use Menke\UserBundle\Repository\ClientRepository;
  47. /**
  48.  * @Route("/course")
  49.  * @IsGranted("ROLE_SPEAKER")
  50.  */
  51. class CourseController extends AbstractClientableController
  52. {
  53.     const LISTING_LIMIT 20;
  54.     /**
  55.      *
  56.      */
  57.     private function getListingLimit(): int
  58.     {
  59.         return !empty($_ENV['COURSES_LISTINGLIMIT']) ? (int) $_ENV['COURSES_LISTINGLIMIT'] : 20;
  60.     }
  61.     /**
  62.      * @Route("/", name="course_index", methods="GET")
  63.      */
  64.     public function index(
  65.         Request $request,
  66.         \App\Service\UiService $uiService,
  67.         CourseRepository $courseRepository
  68.     ): Response {
  69.         $order $uiService->getSortOrder('course-index-listing');
  70.         $archive = !empty($request->get('archive'));
  71.         $courses $courseRepository->getCoursesByClientPaged(
  72.             $this->getCurrentClient(),
  73.             $this->getListingLimit(),
  74.             $order['orderDirection'] ?? 'ASC',
  75.             $order['orderBy'] ?? 'title',
  76.             1,
  77.             $archive
  78.         );
  79.         return $this->render('course/index.html.twig', [
  80.             'uiService' => $uiService,
  81.             'courses' =>  $courses,
  82.             'total' => $courses->count(),
  83.             'pages' => ceil($courses->count() / $this->getListingLimit()),
  84.             'page' => 1,
  85.             'archive' => $archive,
  86.             'env' => $_ENV,
  87.         ]);
  88.     }
  89.     /**
  90.      * @Route("/{page}/{orderby}/{order}", name="course_index_listing", methods="GET", requirements={"page"="\d+","order"="asc|desc"})
  91.      */
  92.     public function indexListing(
  93.         Request $request,
  94.         CourseRepository $courseRepository,
  95.         \App\Service\UiService $uiService,
  96.         $page,
  97.         $orderby,
  98.         $order
  99.     ): Response {
  100.         $uiService->storeSortOrder('course-index-listing'$orderby$order);
  101.         $archive = !empty($request->get('archive'));
  102.         $courses $courseRepository->getCoursesByClientPaged($this->getCurrentClient(), $this->getListingLimit(), $order$orderby$page$archive);
  103.         return $this->render('course/_index_listing.html.twig', [
  104.             'courses' => $courses,
  105.             'total' => $courses->count(),
  106.             'pages' => ceil($courses->count() / $this->getListingLimit()),
  107.             'page' => $page,
  108.             'archive' => $archive,
  109.             'env' => $_ENV,
  110.         ]);
  111.     }
  112.     /**
  113.      * @Route("/new", name="course_new", methods="GET|POST")
  114.      */
  115.     public function new(Request $requestConfigurationService $configService): Response
  116.     {
  117.         $course = new Course();
  118.         if (!empty($courseNature $request->get('courseNature'))) {
  119.             $course->setCourseNature($courseNature);
  120.         }
  121.         $form $this->createForm(CourseType::class, $course, [
  122.             'client' => $this->getCurrentClient(),
  123.             'taxes' => $configService->getTaxConfigbyClient($this->getCurrentClient()),
  124.         ]);
  125.         $form->handleRequest($request);
  126.         if ($form->isSubmitted() && $form->isValid()) {
  127.             $course->setClient($this->getCurrentClient());
  128.             $course->setNumber($configService->getNewCourseNumberByClient($this->getCurrentClient()));
  129.             foreach ($course->getTexts() as $key => $text) {
  130.                 if (empty($text->getOrderId())) {
  131.                     $text->setOrderId($key 1000);
  132.                 }
  133.             }
  134.             $em $this->getDoctrine()->getManager();
  135.             $em->persist($course);
  136.             $em->flush();
  137.             $this->addFlash('success''Kurs angelegt');
  138.             return $this->redirectToRoute('course-occurrence_new', ['courseId' => $course->getId()]);
  139.         }
  140.         return $this->render('course/new.html.twig', [
  141.             'course' => $course,
  142.             'fields' => null,
  143.             'form' => $form->createView(),
  144.             
  145.         ]);
  146.     }
  147.     /**
  148.      * @Route("/{id}/edit", name="course_edit", methods="GET|POST", requirements={"id"="\d+"})
  149.      */
  150.     public function edit(
  151.         Request $request,
  152.         Course $course,
  153.         ConfigurationService $configService,
  154.         CourseFieldRepository $courseFieldRepository,
  155.         CourseDataRepository $courseDataRepository
  156.     ): Response {
  157.         $this->denyAccessUnlessGranted('ROLE_ADMIN'$course);
  158.         $courseTexts = new ArrayCollection();
  159.         foreach ($course->getTexts() as $text) {
  160.             $courseTexts->add($text);
  161.         }
  162.         $form $this->createForm(CourseType::class, $course, [
  163.             'client' => $this->getCurrentClient(),
  164.             'taxes' => $configService->getTaxConfigbyClient($this->getCurrentClient()),
  165.         ]);
  166.         $form->handleRequest($request);
  167.         if ($form->isSubmitted() && $form->isValid()) {
  168.             $manager $this->getDoctrine()->getManager();
  169.             foreach ($courseTexts as $text) {
  170.                 if (false === $course->getTexts()->contains($text)) {
  171.                     $text->setCourse(null);
  172.                     $manager->remove($text);
  173.                 }
  174.             }
  175.             foreach ($course->getTexts() as $key => $text) {
  176.                 if (empty($text->getOrderId())) {
  177.                     $text->setOrderId($key 1000);
  178.                 }
  179.             }
  180.             $fields $request->request->get('fields');
  181.             if (!is_null($fields)) {
  182.                 foreach ($fields as $fieldId => $value) {
  183.                     $field $courseFieldRepository->find($fieldId);
  184.                     $data $courseDataRepository->findBy([
  185.                         'course' => $course,
  186.                         'field' => $field,
  187.                     ]);
  188.                     if (count($data) == 0) {
  189.                         $data = new CourseData();
  190.                         $data->setClient($this->getCurrentClient());
  191.                         $data->setCourse($course);
  192.                         $data->setField($field);
  193.                         $manager->persist($data);
  194.                     } else {
  195.                         $data $data[0];
  196.                     }
  197.                     $data->setValueText($value);
  198.                 }
  199.             } else {
  200.                 $fields = [];
  201.             }
  202.             $manager->flush();
  203.             $this->addFlash('notice''Kurs gespeichert');
  204.             return $this->redirectToRoute('course_edit', ['id' => $course->getId()]);
  205.         }
  206.         // Fetch course fields
  207.         $sql 'SELECT
  208.             f.*,
  209.             d.value_text,
  210.             d.value_integer,
  211.             d.value_date
  212.         FROM
  213.             course_field f
  214.         LEFT JOIN
  215.             course_data d
  216.         ON 
  217.             d.field_id = f.id AND
  218.             d.course_id = ' $course->getId();
  219.         $em $this->getDoctrine()->getManager();
  220.         $stmt $em->getConnection()->prepare($sql);
  221.         $stmt->execute();
  222.         $result $stmt->fetchAll();
  223.         $fields = [];
  224.         $isset false;
  225.         foreach ($result as $field) {
  226.             $isset false;
  227.             if (!empty($field['category'])) {
  228.                 if (!$course->getCategory()) {
  229.                     continue;
  230.                 }
  231.                 if (!in_array($course->getCategory()->getId(), json_decode($field['category'], true))) {
  232.                     continue;
  233.                 } else {
  234.                     $field $this->createDescription($field'course');
  235.                     $isset true;
  236.                 }
  237.             }
  238.             if (!empty($field['course_type'])) {
  239.                 if (!$course->getType()) {
  240.                     continue;
  241.                 }
  242.                 if (!in_array($course->getType()->getId(), json_decode($field['course_type'], true))) {
  243.                     continue;
  244.                 } else {
  245.                     if (!$isset) {
  246.                         $field $this->createDescription($field'course');
  247.                         $isset true;
  248.                     }
  249.                 }
  250.             }
  251.             if (empty($field['category']) && empty($field['ourse_type']) && !empty($field['certificate'])) {
  252.                 if (!$isset$field $this->createDescription($field'certificate');
  253.             }
  254.             if (
  255.                 !empty($field['category']) ||
  256.                 !empty($field['course_type']) ||
  257.                 $field['certificate']
  258.             ) {
  259.                 $fields[] = $field;
  260.             }
  261.         }
  262.         return $this->render('course/edit.html.twig', [
  263.             'course' => $course,
  264.             'form' => $form->createView(),
  265.             'fields' => $fields,
  266.             'env' => $_ENV,
  267.            
  268.         ]);
  269.     }
  270.     /**
  271.      * @Route("/{id}", name="course_delete", methods="DELETE", requirements={"id"="\d+"})
  272.      */
  273.     public function delete(Request $requestCourse $course): Response
  274.     {
  275.         $this->denyAccessUnlessGranted('ROLE_ADMIN'$course);
  276.         if ($this->isCsrfTokenValid('delete' $course->getId(), $request->request->get('_token'))) {
  277.             $em $this->getDoctrine()->getManager();
  278.             $em->remove($course);
  279.             $em->flush();
  280.             $this->addFlash('notice''Kurs gelöscht');
  281.         }
  282.         return $this->redirectToRoute('course_index');
  283.     }
  284.     /**
  285.      * @Route("/multiple", name="course_delete-multiple", methods="DELETE")
  286.      */
  287.     public function deleteMultiple(
  288.         Request $request,
  289.         CourseRepository $courseRepo,
  290.         CartItemRepository $cartItemRepo,
  291.         WaitItemRepository $waitItemRepo,
  292.         OrderItemRepository $orderItemRepo
  293.     ): Response {
  294.         if ($this->isCsrfTokenValid('delete_courses'$request->request->get('_token'))) {
  295.             $em $this->getDoctrine()->getManager();
  296.             $deleteIds $request->request->get('delete');
  297.             foreach ($deleteIds as $id => $value) {
  298.                 if ($value) {
  299.                     $course $courseRepo->find($id);
  300.                     $this->denyAccessUnlessGranted('ROLE_ADMIN'$course);
  301.                     $waitItems $waitItemRepo->findBy(['course' => $course]);
  302.                     foreach ($waitItems as $waitItem) {
  303.                         $em->remove($waitItem);
  304.                     }
  305.                     $cartItems $cartItemRepo->findBy(['course' => $course]);
  306.                     foreach ($cartItems as $cartItem) {
  307.                         $em->remove($cartItem);
  308.                     }
  309.                     $orderItems $orderItemRepo->findBy(['course' => $course]);
  310.                     foreach ($orderItems as $orderItem) {
  311.                         $orderItem->setCourseOccurrence(null);
  312.                     }
  313.                     $em->remove($course);
  314.                 }
  315.             }
  316.             $em->flush();
  317.             $this->addFlash('notice'count($deleteIds) > 'Kurse gelöscht' 'Kurs gelöscht');
  318.         }
  319.         return $this->redirectToRoute('course_index');
  320.     }
  321.     /**
  322.      * @Route("/{id}/occurrences", name="course_occurrences", methods="GET", requirements={"id"="\d+"})
  323.      */
  324.     public function courseOccurrences(
  325.         Request $request,
  326.         Course $course,
  327.         CourseOccurrenceRepository $repo,
  328.         \App\Service\UiService $uiService
  329.     ): Response {
  330.         $this->denyAccessUnlessGranted('ROLE_ADMIN'$course);
  331.         $order $uiService->getSortOrder('course-occurrences-listing');
  332.         
  333.         $archive = !empty($request->get('archive'));
  334.         $occurrences $repo->findByCoursePaged(
  335.             $course,
  336.             self::LISTING_LIMIT,
  337.             $order['orderDirection'] ?? 'ASC',
  338.             $order['orderBy'] ?? 'title'
  339.         );
  340.         return $this->render('course/occurrences.html.twig', [
  341.             'uiService' => $uiService,
  342.             'course' => $course,
  343.             'occurrences' => $occurrences->getIterator(),
  344.             'total' => $occurrences->count(),
  345.             'pages' => ceil($occurrences->count() / self::LISTING_LIMIT),
  346.             'page' => 1,
  347.             'env' => $_ENV,
  348.             'archive' => $archive,
  349.            
  350.         ]);
  351.     }
  352.     /**
  353.      * @Route("/{id}/occurrences/{page}/{orderby}/{order}/{search}", name="course_occurrences_listing", methods="GET", defaults={"search"=""}, requirements={"id"="\d+"})
  354.      */
  355.     public function courseOccurrencesListing(
  356.         Request $request,
  357.         Course $course,
  358.         $page,
  359.         $orderby,
  360.         $order,
  361.         $search,
  362.         CourseOccurrenceRepository $repo,
  363.         \App\Service\UiService $uiService
  364.     ): Response {
  365.         $this->denyAccessUnlessGranted('ROLE_ADMIN'$course);
  366.         $uiService->storeSortOrder('course-occurrences-listing'$orderby$order);
  367.         $occurrences $repo->findByCoursePaged($courseself::LISTING_LIMIT$order$orderby$page$search);
  368.         return $this->render('course/tabs/_occurrences_listing.html.twig', [
  369.             'course' => $course,
  370.             'occurrences' => $occurrences->getIterator(),
  371.             'total' => $occurrences->count(),
  372.             'pages' => ceil($occurrences->count() / self::LISTING_LIMIT),
  373.             'page' => $page,
  374.             'env' => $_ENV,
  375.         ]);
  376.     }
  377.     /**
  378.      * @Route("/{id}/images", name="course_images", methods="GET|POST", requirements={"id"="\d+"})
  379.      */
  380.     public function courseImages(Request $requestCourse $course)
  381.     {
  382.         $this->denyAccessUnlessGranted('ROLE_ADMIN'$course);
  383.         $courseImages = new ArrayCollection();
  384.         foreach ($course->getImages() as $image) {
  385.             $courseImages->add($image);
  386.         }
  387.         $form $this->createForm(CourseImagesType::class, $course);
  388.         $form->handleRequest($request);
  389.         if ($form->isSubmitted() && $form->isValid()) {
  390.             $manager $this->getDoctrine()->getManager();
  391.             foreach ($courseImages as $image) {
  392.                 if (false === $course->getImages()->contains($image)) {
  393.                     $image->setCourse(null);
  394.                     $manager->remove($image);
  395.                 }
  396.             }
  397.             foreach ($course->getImages() as $key => $image) {
  398.                 if (empty($image->getOrderId())) {
  399.                     $image->setOrderId($key 1000);
  400.                 }
  401.             }
  402.             $manager->flush();
  403.             $this->addFlash('notice''Kursbilder gespeichert');
  404.             return $this->redirectToRoute('course_images', ['id' => $course->getId()]);
  405.         }
  406.         return $this->render('course/images.html.twig', [
  407.             'course' => $course,
  408.             'form' => $form->createView(),
  409.             'env' => $_ENV,
  410.         ]);
  411.     }
  412.     /**
  413.      * @Route("/{id}/invoices", name="course_invoices", methods="GET", requirements={"id"="\d+"})
  414.      */
  415.     public function courseInvoices(
  416.         Request $request
  417.         Course $course
  418.          OrderItemRepository $repo
  419.         OrderService $orderService
  420.          )
  421.     {
  422.         $this->denyAccessUnlessGranted('ROLE_ADMIN'$course);
  423.         $orderItems $repo->findByCoursePaged($course);
  424.         /**
  425.          * The display logic of subscription courses is different, as there only one order exists per
  426.          * customer/participant, but they should appear in every following course occurrence until they cancel.
  427.          */
  428.         // if ($course->getCourseNature() === 'CourseSubscription') {
  429.         //     return $this->render('course/invoices-subscription.html.twig', [
  430.         //         'course' => $course,
  431.         //         'orderItems' => $orderItems->getIterator(),
  432.         //     ]);
  433.         // } else {
  434.          $archive = !empty($request->get('archive'));
  435.         
  436.          if ($course->getCourseNature() === 'CourseSubscription') {
  437.             foreach ($orderItems as $orderItem) {
  438.                 $orderItem->isAfterCancelDate $orderService->isOrderItemOccurrenceAfterCancelDateByParticipant($this->getCurrentClient(), $orderItem);
  439.             }
  440.         }
  441.         return $this->render('course/invoices.html.twig', [
  442.           
  443.             'course' => $course,
  444.             'orderItems' => $orderItems->getIterator(),
  445.             'archive' => $archive,
  446.         ]);
  447.         // }
  448.     }
  449.     /**
  450.      * @Route("/{id}/invoices/create", name="course_create_invoices", methods="POST", requirements={"id"="\d+"})
  451.      */
  452.     public function courseCreateInvoices(
  453.         Request $request,
  454.         Course $course,
  455.         OrderItemRepository $itemRepo,
  456.         InvoiceService $invoiceService
  457.     ) {
  458.         $this->denyAccessUnlessGranted('ROLE_ADMIN'$course);
  459.         if ($this->isCsrfTokenValid('create_invoices'$request->request->get('_token'))) {
  460.             $em $this->getDoctrine()->getManager();
  461.             $createIds $request->request->get('create');
  462.             $count 0;
  463.             if (!empty($createIds)) {
  464.                 foreach ($createIds as $id => $value) {
  465.                     if ($value) {
  466.                         $orderItem $itemRepo->find($id);
  467.                         $results $invoiceService->createInvoiceFromOrderItem($orderItem);
  468.                         foreach ($results['attendees'] as $attendee) {
  469.                             $em->persist($attendee);
  470.                         }
  471.                         $em->persist($results['invoice']);
  472.                         $em->flush();
  473.                         $count++;
  474.                     }
  475.                 }
  476.                 $em->flush();
  477.             }
  478.             $this->addFlash('notice'$count . ($count === ' Rechnung' ' Rechnungen') . ' erstellt');
  479.         }
  480.         return $this->redirectToRoute('course_invoices', ['id' => $course->getId()]);
  481.     }
  482.     /**
  483.      * @Route("/{id}/invoices/merge-pdf", name="course_invoices_merge-pdf", methods="POST", requirements={"id"="\d+"})
  484.      */
  485.     public function courseMergePdf(
  486.         Request $request,
  487.         Course $course,
  488.         OrderItemRepository $repo,
  489.         PdfService $pdfService
  490.     ) {
  491.         $this->denyAccessUnlessGranted('ROLE_ADMIN'$course);
  492.         if ($this->isCsrfTokenValid('create_invoices'$request->request->get('_token'))) {
  493.             $em $this->getDoctrine()->getManager();
  494.             $mergeIds $request->request->get('close');
  495.             if (!empty($mergeIds)) {
  496.                 $mergeInvoices = new ArrayCollection();
  497.                 foreach ($mergeIds as $id => $value) {
  498.                     if ($value) {
  499.                         $orderItem $repo->find($id);
  500.                         $order $orderItem->getOrder();
  501.                         foreach ($order->getInvoices() as $invoice) {
  502.                             if (!$mergeInvoices->contains($invoice)) {
  503.                                 $mergeInvoices->add($invoice);
  504.                             }
  505.                         }
  506.                     }
  507.                 }
  508.                 $pdf $pdfService->getMergedInvoicePdf($this->getCurrentClient(), $mergeInvoices->toArray());
  509.                 $pdf->Output('D''Rechnungen_' date('Y-m-d_H-i') . '.pdf');
  510.                 die;
  511.             } else {
  512.                 $this->addFlash('notice''Keine Rechnungen ausgewählt.');
  513.             }
  514.         }
  515.         return $this->redirectToRoute('course_invoices', ['id' => $course->getId()]);
  516.     }
  517.     /**
  518.      * @Route("/{id}/invoices/close", name="course_close_invoices", methods="POST", requirements={"id"="\d+"})
  519.      */
  520.     public function courseCloseInvoices(
  521.         Request $request,
  522.         Course $course,
  523.         InvoiceItemRepository $repo,
  524.         ConfigurationService $configService,
  525.         MailerService $mailer,
  526.         PdfService $pdfService,
  527.         EmailHistoryService $emailHistoryService
  528.     ) {
  529.         $this->denyAccessUnlessGranted('ROLE_ADMIN'$course);
  530.         if ($this->isCsrfTokenValid('create_invoices'$request->request->get('_token'))) {
  531.             $em $this->getDoctrine()->getManager();
  532.             $closeIds $request->request->get('close');
  533.             $count 0;
  534.             if (!empty($closeIds)) {
  535.                 foreach ($closeIds as $id => $value) {
  536.                     if ($value) {
  537.                         $invoiceItem $repo->findOneBy(['orderItem' => $id]);
  538.                         $invoice $invoiceItem->getInvoice();
  539.                             if ($invoice->getStatus() == Invoice::STATUS_DRAFT) {
  540.                                 $pdf $pdfService->getInvoicePdf($this->getCurrentClient(), $invoice);
  541.                                 $sentMessage $mailer->sendInvoiceEmail(
  542.                                     $invoice,
  543.                                     'Rechnung-' $invoice->getNumber() . '.pdf',
  544.                                     $pdf->Output('S''Rechnung-' $invoice->getNumber() . '.pdf')
  545.                                 );
  546.                                 $outputfile $this->generateUniqueFileName() . '.pdf';
  547.                                 $outputpath $this->getParameter('attachment_directory') . '/' $outputfile;
  548.                                 $pdf->Output('F'$outputpath);
  549.                                 $emailHistoryService->saveProtocolEntryFromInvoiceMessage(
  550.                                     $invoice,
  551.                                     $sentMessage['sender'],
  552.                                     $sentMessage['subject'],
  553.                                     $sentMessage['message'],
  554.                                     $outputfile,
  555.                                     'Rechnung-' $invoice->getNumber() . '.pdf'
  556.                                 );
  557.                                 if ($invoice->getStatus() != Invoice::STATUS_CLOSED) {
  558.                                     if ($invoice->isPaymentDebit()) {
  559.                                         $invoice->setStatus(Invoice::STATUS_DEBIT_PENDING);
  560.                                     } else {
  561.                                         $invoice->setStatus(Invoice::STATUS_CLOSED);
  562.                                     }
  563.                                 }
  564.                              
  565.                                 $count++;
  566.                             } else {
  567.                                 // Send invoice again
  568.                                 $pdf $pdfService->getInvoicePdf($this->getCurrentClient(), $invoice);
  569.                                 $sentMessage $mailer->sendInvoiceEmail(
  570.                                     $invoice,
  571.                                     'Rechnung-' $invoice->getNumber() . '.pdf',
  572.                                     $pdf->Output('S''Rechnung-' $invoice->getNumber() . '.pdf')
  573.                                 );
  574.                                 $count++;
  575.                             }
  576.                                  //Update the order status
  577.                                 $newOrderState $invoice->getOrder()->setStatus(Order::STATUS_DONE);
  578.                                 $em->persist($newOrderState);
  579.                                 $em->flush();
  580.                     }
  581.                 }
  582.             }
  583.             $this->addFlash('notice'$count . ($count === ' Rechnung' ' Rechnungen') . ' versendet');
  584.         }
  585.         return $this->redirectToRoute('course_invoices', ['id' => $course->getId()]);
  586.     }
  587.     /**
  588.      * @Route("/{id}/invoices/close-sepa/{all}", name="course_close_sepa-invoices", methods="POST", requirements={"id"="\d+"})
  589.      */
  590.     public function courseCloseSepaInvoices(
  591.         Request $request,
  592.         Course $course,
  593.         $all false,
  594.         OrderRepository $repo,
  595.         OrderItemRepository $itemRepo,
  596.         ConfigurationService $configService,
  597.         SepaXmlService $sepaXmlService
  598.     ) {
  599.         $this->denyAccessUnlessGranted('ROLE_ADMIN'$course);
  600.         if ($this->isCsrfTokenValid('create_invoices'$request->request->get('_token'))) {
  601.             $em $this->getDoctrine()->getManager();
  602.             $closeIds $request->request->get('close');
  603.             $invoicesToExport = new ArrayCollection();
  604.             if ($all) {
  605.                 $orderItems $itemRepo->findByCoursePaged($course);
  606.                 foreach ($orderItems as $orderItem) {
  607.                     $order $orderItem->getOrder();
  608.                     foreach ($order->getInvoices() as $invoice) {
  609.                         if (
  610.                             $invoice->containsCourse($course) &&
  611.                             !$invoicesToExport->contains($invoice) &&
  612.                             $invoice->isPaymentDebit()
  613.                         ) {
  614.                             $invoicesToExport->add($invoice);
  615.                             $invoice->setStatus(Invoice::STATUS_CLOSED);
  616.                             if (!$order->getCustomer()->getDebitActive()) {
  617.                                 $order->getCustomer()->setDebitActive(true);
  618.                                 $invoice->setIsNewSepaMandate(true);
  619.                             }
  620.                         }
  621.                         $restsumme $invoice->getMissingSum();
  622.                         if ($restsumme != 0){
  623.                             $invoicePayment = new InvoicePayment();
  624.                             $invoicePayment->setInvoice($invoice);
  625.                             $invoicePayment->setPayedDate(new \DateTime());
  626.                             $invoicePayment->setSum($invoice->getMissingSum());
  627.                             $invoice->setPaymentStatus(Invoice::FULLY_PAID);
  628.                             $invoice->setExportStatus(Invoice::EXPORTED);
  629.                             $em $this->getDoctrine()->getManager();
  630.                             $em->persist($invoicePayment);
  631.                             }
  632.                             $invoice->setPaymentStatus(Invoice::FULLY_PAID);
  633.                             $invoice->setExportStatus(Invoice::EXPORTED);
  634.                             $em->persist($invoice);
  635.                             $em->flush();
  636.                     }
  637.                 }
  638.             } elseif (!empty($closeIds)) {
  639.                 foreach ($closeIds as $id => $value) {
  640.                     if ($value) {
  641.                         $orderItem $itemRepo->find($id);
  642.                         $order $orderItem->getOrder();
  643.                         foreach ($order->getInvoices() as $invoice) {
  644.                             if (
  645.                                 $invoice->containsCourse($course) &&
  646.                                 !$invoicesToExport->contains($invoice) &&
  647.                                 $invoice->isPaymentDebit()
  648.                             ) {
  649.                                 $invoicesToExport->add($invoice);
  650.                                 $invoice->setStatus(Invoice::STATUS_CLOSED);
  651.                                 if (!$order->getCustomer()->getDebitActive()) {
  652.                                     $order->getCustomer()->setDebitActive(true);
  653.                                     $invoice->setIsNewSepaMandate(true);
  654.                                 }
  655.                             }
  656.                         }
  657.                         $restsumme $invoice->getMissingSum();
  658.                         if ($restsumme != 0){
  659.                         $invoicePayment = new InvoicePayment();
  660.                         $invoicePayment->setInvoice($invoice);
  661.                         $invoicePayment->setPayedDate(new \DateTime());
  662.                         $invoicePayment->setSum($invoice->getMissingSum());
  663.                         $invoice->setPaymentStatus(Invoice::FULLY_PAID);
  664.                         $invoice->setExportStatus(Invoice::EXPORTED);
  665.                         $em $this->getDoctrine()->getManager();
  666.                         $em->persist($invoicePayment);
  667.                         }
  668.                         $invoice->setPaymentStatus(Invoice::FULLY_PAID);
  669.                         $invoice->setExportStatus(Invoice::EXPORTED);
  670.                         $em->persist($invoice);
  671.                         $em->flush();
  672.                     }
  673.                 }
  674.             } else {
  675.                 $this->addFlash('warning''Es wurden keine Rechnungen zum Export ausgewählt.');
  676.                 return $this->redirectToRoute('course_invoices', ['id' => $course->getId()]);
  677.             }
  678.             // Check invoices for past due dates
  679.             foreach ($invoicesToExport as $invoice) {
  680.                 if (new \DateTime() > $invoice->getDueDate()) {
  681.                     $this->addFlash('warning''Mindestens eine Rechnung enthält ein Zahlungsziel in der Vergangenheit.');
  682.                     // return $this->redirectToRoute('course_invoices', ['id' => $course->getId()]);
  683.                 }
  684.             }
  685.             if (count($invoicesToExport) > 0) {
  686.                 $config $configService->getSepaXmlConfigByClient($this->getCurrentClient());
  687.                 try {
  688.                     $xml $sepaXmlService->getSepaXmlMultiple($this->getCurrentClient(), $config$invoicesToExport);
  689.                 } catch (ServiceException $e) {
  690.                     $this->addFlash('error'$e->getMessage());
  691.                     return $this->redirectToRoute('invoice_index');
  692.                 }
  693.                 $em->flush();
  694.                 $response = new Response($xml);
  695.                 $response->headers->set('Content-Type''text/xml');
  696.                 $response->headers->set('Content-disposition''attachment; filename="SEPA-' date('Ymd-His') . '.xml"');
  697.                 return $response;
  698.             }
  699.             $this->addFlash('error''Mindestens eine Rechnung enthält Fehler.');
  700.             return $this->redirectToRoute('course_invoices', ['id' => $course->getId()]);
  701.         }
  702.         $this->addFlash('error''Der Sicherheits-Token ist ungültig. Bitte versuchen Sie es noch einmal.');
  703.         return $this->redirectToRoute('course_invoices', ['id' => $course->getId()]);
  704.     }
  705.     /**
  706.      * @Route("/{id}/participants", name="course_participants", methods="GET", requirements={"id"="\d+"})
  707.      */
  708.     public function courseParticipants(Request $requestCourse $courseOrderItemRepository $repoOrderService $orderService)
  709.     {
  710.         $this->denyAccessUnlessGranted('ROLE_ADMIN'$course);
  711.         $orderItems $repo->findByCoursePaged($course);
  712.         /**
  713.          * The display logic of subscription courses is different, as there only one order exists per
  714.          * customer/participant, but they should appear in every following course occurrence until they cancel.
  715.          */
  716.         // if ($course->getCourseNature() === 'CourseSubscription') {
  717.         //     return $this->render('course/participants-subscription.html.twig', [
  718.         //         'course' => $course,
  719.         //         'orderItems' => $orderItems->getIterator(),
  720.         //     ]);
  721.         // } else {
  722.             $archive = !empty($request->get('archive'));
  723.         if ($course->getCourseNature() === 'CourseSubscription') {
  724.             foreach ($orderItems as $orderItem) {
  725.                 foreach ($orderItem->getParticipants() as $participant) {
  726.                     $participant->isAfterCancelDate $orderService->isOrderItemOccurrenceAfterCancelDateByParticipant($this->getCurrentClient(), $orderItem$participant->getId());
  727.                     $participant->cancelDate $orderService->getCancelDateForParticipantInCourse($this->getCurrentClient(), $participant);
  728.                 }
  729.             }
  730.         }
  731.         return $this->render('course/participants.html.twig', [
  732.             'env' => $_ENV,
  733.             'course' => $course,
  734.             'orderItems' => $orderItems->getIterator(),
  735.             'showCertificatesLink' => !empty($_ENV['CERTIFICATES_ENABLED']),
  736.             'archive' => $archive,
  737.         ]);
  738.         // }
  739.     }
  740.     /**
  741.      * @Route("/{id}/participants-pdf/{page}/{orderby}/{order}", name="course_participants_pdf", methods="GET", requirements={"id"="\d+"})
  742.      * @IsGranted("ROLE_SPEAKER")
  743.      */
  744.     public function courseParticipantsPdf(
  745.         Request $request,
  746.         CourseOccurrence $courseOccurrence,
  747.         OrderItemRepository $repo,
  748.         PdfService $pdfService,
  749.         OrderService $orderService,
  750.         $page 1,
  751.         $orderby 'customerLastname',
  752.         $order 'asc'
  753.     ) {
  754.     //    $this->denyAccessUnlessGranted('client_allowed', $courseOccurrence);
  755.   $this->denyAccessUnlessGranted('ROLE_SPEAKER'$courseOccurrence);
  756.         $orderItems $repo->findByCourseOccurrence($courseOccurrence$orderby$order);
  757.         if ($courseOccurrence->getCourse()->getCourseNature() === 'CourseSubscription') {
  758.             foreach ($orderItems as $orderItem) {
  759.                 foreach ($orderItem->getParticipants() as $participant) {
  760.                     $participant->isAfterCancelDate $orderService->isOrderItemOccurrenceAfterCancelDateByParticipant($this->getCurrentClient(), $orderItem$participant->getId());
  761.                 }
  762.             }
  763.         }
  764.         $pdf $pdfService->getParticipantsPdf($this->getCurrentClient(), $courseOccurrence$orderItems);
  765.         $pdf->Output('D''Teilnehmerliste-' $courseOccurrence->getStart()->format('Y-m-d') . '.pdf');
  766.         exit();
  767.     }
  768.      /**
  769.      * @Route("/participant/{id}/certificateemail", name="course_participants_certificate_email", methods="GET", requirements={"id"="\d+","downlaod"="\d+"})
  770.      */
  771.     public function courseParticipantsCertificateEmail(
  772.         Request $request
  773.         ConfigurationService $configService,
  774.         \App\Entity\OrderItemPerson $orderItemPerson,
  775.         TextblocksRepository $textblocks,
  776.         ParameterBagInterface $params,
  777.         CourseDataRepository $courseDataRepository,
  778.         MailerService $mailer,
  779.         \Doctrine\DBAL\Driver\Connection $connection,
  780.         EmailHistoryService $emailHistoryService
  781.         ): Response {
  782.             $download $_GET['download'];
  783.         $viewTemplate $_ENV['CERTIFICATES_TEMPLATE'] ?? 'Default';
  784.         $viewFolder '/data/static/certificates/';
  785.         $viewFile $viewTemplate '/Certificate.html.twig';
  786.         if (file_exists($params->get('kernel.project_dir') . $viewFolder dirname($viewFile) . '/data.xml')) {
  787.             $xml simplexml_load_file($params->get('kernel.project_dir') . $viewFolder dirname($viewFile) . '/data.xml''SimpleXMLElement'LIBXML_NOCDATA);
  788.             $json json_encode($xml);
  789.             $data json_decode($jsontrue);
  790.         }
  791.         try {
  792.             $course $orderItemPerson->getOrderItem()->getCourseOccurrence()->getCourse();
  793.             $person $orderItemPerson->getPerson();
  794.             $customer $orderItemPerson->getOrderItem()->getOrder()->getCustomer();
  795.             $courseFields $courseDataRepository->findBy([
  796.                 'course' => $course,
  797.                 'client' => $this->getCurrentClient()
  798.             ]);
  799.             //proof if an certificate isset
  800.             $certificateIsset false;
  801.             foreach ($courseFields as $field) {
  802.                 if (
  803.                     $field->getCourseField()->getCertificate() &&
  804.                     !empty($field->getValueText())
  805.                 ) {
  806.                     $certificateIsset true;
  807.                     break;
  808.                 }
  809.             }
  810.             $response $this->render('course/certificates/' $viewTemplate '/Certificate.html.twig', [
  811.                 'data' => $data ?? [],
  812.                 'folder' => '..' $viewFolder dirname($viewFile) . '/',
  813.                 'person' => $person,
  814.                 'course' => $course,
  815.                 'courseFields' => $courseFields,
  816.                 'certificateIsset' => $certificateIsset,
  817.                 'occurence' => $orderItemPerson->getOrderItem()->getCourseOccurrence(),
  818.             ]);
  819.         } catch (\Twig\Error\LoaderError $exception) {
  820.             $this->addFlash('error''Das Zertifikat konnte nicht erstellt werden (Ordner: ' $viewFolder ').');
  821.             return $this->redirectToRoute('course_participants', ['id' => $orderItemPerson->getOrderItem()->getCourseOccurrence()->getCourse()->getId()]);
  822.         } catch (\InvalidArgumentException $exception) {
  823.             $this->addFlash('error''Das Zertifikat konnte nicht erstellt werden (Template: ' $viewFile ').');
  824.             return $this->redirectToRoute('course_participants', ['id' => $orderItemPerson->getOrderItem()->getCourseOccurrence()->getCourse()->getId()]);
  825.         }
  826.         $source $response->getContent();
  827.         // Generate filename
  828.         
  829.         $filename $course->getNumber() . '-' $person->getLastname() . '-' $person->getFirstname();
  830.         $filename mb_strtolower($filename);
  831.         $tr = [
  832.             'ä' => 'ae',
  833.             'ü' => 'ue',
  834.             'ö' => 'oe',
  835.             'ß' => 'ss',
  836.         ];
  837.         $filename strtr($filename$tr);
  838.         $filename preg_replace('#\s#''-'$filename);
  839.         $filename preg_replace('#[^a-z0-9\-]#'''$filename);
  840.         $filename preg_replace('#[\-]{2,}#i''-'$filename);
  841.         $filename .= '.pdf';
  842.        
  843.         // Generate pdf
  844.         $html2pdf = new \Spipu\Html2Pdf\Html2Pdf('P''A4''de'true'UTF-8', [251000]);
  845.         $html2pdf->setDefaultFont('Arial');
  846.         $html2pdf->writeHTML($source);
  847.             if ( $download == 'ja'){
  848.                 $html2pdf->output($filename'D');
  849.             }
  850.         $outputpath $this->getParameter('attachment_directory') . '/' $filename;
  851.         if ( $download == 'nein'){
  852.         $html2pdf->output($outputpath'F');
  853.         }
  854.         $client $this->getCurrentClient();
  855.         $sender $client->getEmail();
  856.         $recipient $person->getContactEmail() ? $person->getContactEmail() :  $orderItemPerson->getEmail();
  857. // Fetch subject und message from textblocks
  858.         $sql 'SELECT * FROM textblocks WHERE position = "certificate" ';
  859.         $result $connection->fetchAll($sql);
  860.         $subject 'Ihr Zertifikat wurde erstellt';
  861.         $message 'Lieber Teilnehmer<p>Für Ihren Kurs wurde das Zertifikat erstellt.</p><p>Ihr Team';
  862.         $historyTitle 'Zertifikat erstellt';
  863.     if ( $download == 'nein'){
  864.         foreach ($result as $value) {
  865.             if ($value['position'] = 'cetrificate'){
  866.             $subject $value['subject'];
  867.             $message $value['message'];
  868.             } 
  869.         }   
  870.     $mailer->sendCertificateMessage(
  871.                 $client,
  872.                 $sender,
  873.                 $subject .' '$course->getTitle(),
  874.             $message '<p>Anhang: ' $filename,
  875.                 $recipient,
  876.                 $html2pdf->output($filename'S'),
  877.                 $filename
  878.             );
  879.     }
  880.             //$client, $recipient, $sender, $subject, $message, $filename
  881.             $emailHistoryService->saveProtocolEntriesFromEmailCertificate(
  882.                 $this->getCurrentClient(),
  883.                 $recipient,
  884.                 $sender,
  885.                 $subject  .' '$course->getTitle(),
  886.                 $message,
  887.                 $filename,
  888.                 $historyTitle,
  889.                 $customer,
  890.                 $download
  891.             );
  892.             if ( $download == 'nein' ){
  893.                 $this->addFlash('notice''Das Zertifikat wurde an '$recipient .' versendet. ');   
  894.             }
  895.             if ( $download == 'ja'){
  896.                 $this->addFlash('notice''Das Zertifikat wurde erstellt. ');  
  897.             }
  898.         return $this->redirectToRoute('course_participants', ['id' => $course->getId()]);
  899.     }
  900.     /**
  901.      * @Route("/{id}/reservations", name="course_reservations", methods="GET", requirements={"id"="\d+"})
  902.      */
  903.     public function courseReservations(Request $requestCourse $courseWaitItemRepository $repo)
  904.     {
  905.         $this->denyAccessUnlessGranted('ROLE_ADMIN'$course);
  906.         $waitItems $repo->findByCoursePaged($course);
  907.         return $this->render('course/reservations.html.twig', [
  908.             'course' => $course,
  909.             'waitItems' => $waitItems->getIterator(),
  910.         ]);
  911.     }
  912.     /**
  913.      * @Route("/{id}/reservations/move", name="course_reservations_move", methods="POST", requirements={"id"="\d+"})
  914.      */
  915.     public function moveCourseReservations(
  916.         Request $request,
  917.         Course $course,
  918.         WaitItemRepository $repo
  919.     ): Response {
  920.         $this->denyAccessUnlessGranted('ROLE_ADMIN'$course);
  921.         $em $this->getDoctrine()->getManager();
  922.         $moveIds $request->request->get('item');
  923.         foreach ($moveIds as $id => $value) {
  924.             if ($value) {
  925.                 $waitItem $repo->find($value);
  926.                 $orderItem OrderItem::createFromWaitItem($waitItem);
  927.                 $participants $waitItem->getParticipants();
  928.                 foreach ($participants as $participant) {
  929.                     if ($participant->getPerson()->getId() === $id) {
  930.                         $participant->setWaitItem(null);
  931.                         $participant->setOrderItem($orderItem);
  932.                         $orderItem->setQuantity($orderItem->getQuantity() + 1);
  933.                         $waitItem->setQuantity($waitItem->getQuantity() - 1);
  934.                         break;
  935.                     }
  936.                 }
  937.                 $waitItem->getCourseOccurrence()->bookSlots($orderItem->getQuantity());
  938.                 $order $waitItem->getOrder();
  939.                 $order->addOrderItem($orderItem);
  940.                 if ($waitItem->getQuantity() === 0) {
  941.                     $order->removeWaitItem($waitItem);
  942.                 }
  943.                 $em->persist($order);
  944.             }
  945.         }
  946.         $this->addFlash('notice'count($moveIds) . (count($moveIds) > ' Wartelistenplätze verschoben' ' Wartelistenplatz verschoben'));
  947.         $em->flush();
  948.         return $this->redirectToRoute('course_reservations',  ['id' => $course->getId()]);
  949.     }
  950.     private function generateUniqueFileName()
  951.     {
  952.         return md5(uniqid());
  953.     }
  954.     private function createDescription($field$option)
  955.     {
  956.         switch ($option) {
  957.             case 'course':
  958.                 if (!empty($field['certificate'])) {
  959.                     $field['name'] = $this->generateHTMLForDescription(
  960.                         $field['name'],
  961.                         'für den Kurs und das Zertifikat'
  962.                     );
  963.                 } else {
  964.                     $field['name'] = $this->generateHTMLForDescription(
  965.                         $field['name'],
  966.                         'für den Kurs'
  967.                     );
  968.                 }
  969.                 break;
  970.             case 'certificate':
  971.                 $field['name'] = $this->generateHTMLForDescription(
  972.                     $field['name'],
  973.                     'für das Zertifikat'
  974.                 );
  975.                 break;
  976.             default:
  977.                 break;
  978.         }
  979.         return $field;
  980.     }
  981.     private function generateHTMLForDescription($name$text)
  982.     {
  983.         return '<strong>' $name '</strong>' .  '<span style="font-size: 0.7rem"> (' $text ')</span>';
  984.     }
  985. }