src/Controller/OrderController.php line 568

Open in your IDE?
  1. <?php
  2. namespace App\Controller;
  3. use App\Entity\CourseSubscriptionBooking;
  4. use App\Entity\Invoice;
  5. use App\Entity\Order;
  6. use App\Entity\OrderItem;
  7. use App\Entity\OrderItemPerson;
  8. use App\Entity\WaitItem;
  9. use App\Form\OrderItemPersonCancelDate;
  10. use App\Form\OrderItemPersonCopy;
  11. use App\Form\OrderStatusType;
  12. use App\Form\OrderType;
  13. use App\Repository\CourseOccurrenceRepository;
  14. use App\Repository\CourseOccurrenceTimeRepository;
  15. use App\Repository\InvoiceRepository;
  16. use App\Repository\OrderItemPersonRepository;
  17. use App\Repository\OrderRepository;
  18. use App\Repository\PersonRepository;
  19. use App\Service\ConfigurationService;
  20. use App\Service\EmailHistoryService;
  21. use App\Service\IbanService;
  22. use App\Service\InvoiceService;
  23. use App\Service\MailerService;
  24. use App\Service\OrderService;
  25. use App\Service\UiService;
  26. use App\Service\ZoomService;
  27. use Doctrine\Common\Collections\Collection;
  28. use Doctrine\DBAL\Connection;
  29. use Doctrine\Persistence\ManagerRegistry;
  30. use App\User\Controller\AbstractClientableController;
  31. use Psr\Log\LoggerInterface;
  32. use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
  33. use Symfony\Component\HttpFoundation\Request;
  34. use Symfony\Component\HttpFoundation\RequestStack;
  35. use Symfony\Component\HttpFoundation\Response;
  36. use Symfony\Component\Routing\Annotation\Route;
  37. /**
  38.  * @Route("/order")
  39.  *
  40.  * @IsGranted("ROLE_MANAGER")
  41.  */
  42. class OrderController extends AbstractClientableController
  43. {
  44.     public const LISTING_LIMIT 25;
  45.     /**
  46.      * @Route("/", name="order_index", methods="GET")
  47.      */
  48.     public function index(
  49.         UiService $uiService,
  50.         OrderRepository $orderRepo,
  51.         OrderItemPersonRepository $orderItemPersonRepository,
  52.         Request $request,
  53.         RequestStack $requestStack,
  54.     ): Response {
  55.         $order $uiService->getSortOrder('order-index-listing');
  56.         $em $this->getDoctrine()->getManager();
  57.         $filterOrders $request->get('orderAction');
  58.         if ($filterOrders) {
  59.             $requestStack->getSession()->set('orderFilter'$filterOrders);
  60.         } else {
  61.             $requestStack->getSession()->set('orderFilter''pending');
  62.         }
  63.         $dateareas $request->get('datearea');
  64.         if ($dateareas) {
  65.             $requestStack->getSession()->set('datearea'$dateareas);
  66.         }
  67.         /*  Alle Member in den Orders und order item persons abgleichen und aktualisieren
  68.                 $orderstest = $orderRepo->findAll();
  69.                 foreach ($orderstest as $ordertest) {
  70.                     if($ordertest->getCustomer() && $ordertest->getCustomer()->getMember()){
  71.                     $ordertest->setCustomerMember($ordertest->getCustomer()->getMember());
  72.                     $em->persist($ordertest);}
  73.                 }
  74.                 $entries = $orderItemPersonRepository->findAll();
  75.                 foreach ($entries as $entry) {
  76.                     // Hier wird der aktualisierte Member-Wert gesetzt
  77.                     if($entry->getPerson() && $entry->getPerson()->getMember()){
  78.                     $entry->setMember($entry->getPerson()->getMember());
  79.                     $em->persist($entry);}
  80.                 }
  81.           */
  82.         $orders $orderRepo->getByClientPaged(
  83.             $this->getCurrentClient(),
  84.             self::LISTING_LIMIT,
  85.             $order['orderDirection'] ?? 'desc',
  86.             $order['orderBy'] ?? 'id',
  87.             1,
  88.             ($filterOrders) ? $filterOrders 'pending',
  89.             ($dateareas) ? $dateareas null,
  90.         );
  91.         return $this->render('order/index.html.twig', [
  92.             'uiService' => $uiService,
  93.             'orders' => $orders->getIterator(),
  94.             'total' => $orders->count(),
  95.             'pages' => ceil($orders->count() / self::LISTING_LIMIT),
  96.             'page' => 1,
  97.             'orderAction' => $request->get('orderAction'),
  98.             'datearea' => $request->get('datearea'),
  99.         ]);
  100.     }
  101.     /**
  102.      * @Route("/fast", name="order_index_fast", methods="GET")
  103.      */
  104.     public function indexfast(
  105.         UiService $uiService,
  106.         OrderRepository $orderRepo,
  107.         Request $request,
  108.         RequestStack $requestStack,
  109.     ): Response {
  110.         $order $uiService->getSortOrder('order-index-listing');
  111.         $filterOrders $request->get('orderAction');
  112.         if ($filterOrders) {
  113.             $requestStack->getSession()->set('orderFilter'$filterOrders);
  114.         } else {
  115.             $requestStack->getSession()->set('orderFilter''pending');
  116.         }
  117.         $orders $orderRepo->getByClientPaged(
  118.             $this->getCurrentClient(),
  119.             self::LISTING_LIMIT,
  120.             $order['orderDirection'] ?? 'desc',
  121.             $order['orderBy'] ?? 'date',
  122.             1,
  123.             ($filterOrders) ? $filterOrders ''
  124.         );
  125.         return $this->render('order/fastindex.html.twig', [
  126.             'uiService' => $uiService,
  127.             'orders' => $orders->getIterator(),
  128.             'total' => $orders->count(),
  129.             'pages' => ceil($orders->count() / self::LISTING_LIMIT),
  130.             'page' => 1,
  131.             'orderAction' => $request->get('orderAction'),
  132.         ]);
  133.     }
  134.     /**
  135.      * @Route("/{page}/{orderby}/{order}", name="order_index_listing", methods="GET", requirements={"page"="\d+","order"="asc|desc"})
  136.      */
  137.     public function indexListing(
  138.         OrderRepository $orderRepo,
  139.         UiService $uiService,
  140.         $page,
  141.         $orderby,
  142.         $order,
  143.         Request $request,
  144.         RequestStack $requestStack): Response
  145.     {
  146.         $uiService->storeSortOrder('order-index-listing'$orderby$order);
  147.         $orders $orderRepo->getByClientPaged(
  148.             $this->getCurrentClient(),
  149.             self::LISTING_LIMIT,
  150.             $order,
  151.             $orderby,
  152.             $page,
  153.             $requestStack->getSession()->get('orderFilter')
  154.         );
  155.         return $this->render('order/_index_listing.html.twig', [
  156.             'uiService' => $uiService,
  157.             'orders' => $orders->getIterator(),
  158.             'total' => $orders->count(),
  159.             'pages' => ceil($orders->count() / self::LISTING_LIMIT),
  160.             'page' => $page,
  161.         ]);
  162.     }
  163.     /**
  164.      * @Route("/new/{return}", name="order_new", methods="GET|POST")
  165.      */
  166.     public function new(
  167.         Request $request,
  168.         $return null,
  169.         PersonRepository $personRepo,
  170.         ConfigurationService $configService,
  171.         OrderService $orderService,
  172.         ZoomService $zoomService,
  173.         IbanService $ibanService): Response
  174.     {
  175.         $customer null;
  176.         $invoiceRecipient null;
  177.         $isEmptyParticipants false;
  178.         if ($return) {
  179.             $customer $personRepo->find($return);
  180.             $invoiceRecipient $personRepo->getInvoiceReciepientMembers($return);
  181.             $invoiceRecipient $this->createInvoiceRecipientValues($invoiceRecipient$customer);
  182.             $customerChildrens $personRepo->getMembersByClient($customer);
  183.             $isEmptyParticipants = (empty($customerChildrens)) ? true false;
  184.         }
  185.         $em $this->getDoctrine()->getManager();
  186.         $order = new Order();
  187.         $order->setDate(new \DateTime());
  188.         $order->setStatus(Order::STATUS_PENDING);
  189.         $order->setNumber($configService->getNewOrderNumberByClient($this->getCurrentClient()));
  190.         $order->setClient($this->getCurrentClient());
  191.         $order->setCustomer($customer);
  192.         $order->setCustomerData($customer);
  193.         $order->setCreated(new \DateTime());
  194.         $order->setPerson($customer);
  195.         $form $this->createForm(OrderType::class, $order, [
  196.             'client' => $this->getCurrentClient(),
  197.             'taxes' => $configService->getTaxConfigbyClient($this->getCurrentClient()),
  198.             'customer' => $customer,
  199.             'invoiceRecipient' => $invoiceRecipient,
  200.             'form_type' => OrderType::TYPE_CREATE_FOR_CUSTOMER,
  201.         ]);
  202.         $form->handleRequest($request);
  203.         //   $em->persist($order);
  204.         // Check if creation is possible
  205.         if ($form->isSubmitted() && $form->isValid()) {
  206.             if (!$order->checkCustomerData($customer)) {
  207.                 $this->addFlash('error''Die Kundendaten sind unvollständig. Es kann keine neue Bestellung angelegt werden.');
  208.                 if ($return) {
  209.                     return $this->redirectToRoute('customer_orders', ['id' => $return]);
  210.                 } else {
  211.                     return $this->redirectToRoute('order_index');
  212.                 }
  213.             }
  214.             foreach ($order->getOrderItems() as $orderItem) {
  215.                 if ($orderItem->getCourseOccurrence()) {
  216.                     $orderItem->setCourseOccurrence($orderItem->getCourseOccurrence());
  217.                     if ($orderItem->getCourseOccurrence()->getCourse()->getMaterialCost() > || $orderItem->getCourseOccurrence()->getMaterialCost() > 0) {
  218.                         $item = new OrderItem();
  219.                         $item->setCourseOccurrence($orderItem->getCourseOccurrence());
  220.                         $item->setPrice($orderItem->getCourseOccurrence()->getMaterialCost() ?? $orderItem->getCourseOccurrence()->getCourse()->getMaterialCost());
  221.                         $item->setTaxRate($orderItem->getCourseOccurrence()->getTaxRate() ?? $orderItem->getCourseOccurrence()->getCourse()->getTaxRate());
  222.                         $item->setName('Materialkosten');
  223.                         $item->setQuantity($orderItem->getQuantity());
  224.                         // $item->setCourseItem($orderItem->getCourseOccurrence()->getCourse());
  225.                         $item->setCourseItem($orderItem);
  226.                         $item->setIsFree(false);
  227.                         $item->setOrder($order);
  228.                         $item->setCreated(new \DateTime());
  229.                         // $item->setCourseItem($orderItem);
  230.                         $em->persist($item);
  231.                     }
  232.                     $em->persist($orderItem);
  233.                 }
  234.                 // Skip participants check for free items
  235.                 if (null === $orderItem->getCourseOccurrence()) {
  236.                     continue;
  237.                 }
  238.                 // Check if order item has at least one participant
  239.                 if (== count($orderItem->getParticipants())) {
  240.                     $this->addFlash('error''Der Kurs enthält keine Teilnehmer.');
  241.                     return $this->redirectToRoute('order_new', ['return' => $return]);
  242.                 }
  243.             }
  244.             $allItemsBookable true;
  245.             // $em->flush();
  246.             foreach ($order->getOrderItems() as $orderItem) {
  247.                 $orderItem->setCourseOccurrence($orderItem->getCourseOccurrence());
  248.                 $orderItem->setCreated(new \DateTime());
  249.                 if ($orderItem->getCourse()) {
  250.                     $orderItem->setName($orderItem->getCourse()->getTitle());
  251.                     $orderItem->setDescription($orderItem->getCourse()->getSubtitle());
  252.                 } else {
  253.                     $orderItem->setName($orderItem->getName());
  254.                     $orderItem->setDescription($orderItem->getDescription());
  255.                 }
  256.                 //    $em->persist($orderItem);
  257.                 //    $em->flush($orderItem);
  258.                 if ($orderItem->getCourseOccurrence()) {
  259.                     $occurrence $orderItem->getCourseOccurrence();
  260.                     // /// testen und abgleichen was in der Datenbank steht und wie viele Buchungen es wirklich gibt ////
  261.                     //  $occurrence->setBookedSlots($occurrence->getBookedSlots());
  262.                     //    $em->persist($occurrence);
  263.                     //    $em->flush($occurrence);
  264.                     if (($occurrence->getSlots() < ($orderItem->getQuantity() + $occurrence->getBookedSlots())) && $occurrence->getReservationAllowed()) {
  265.                         $waitItem WaitItem::fromOrderItem($orderItem);
  266.                         $waitItem->setCreated(new \DateTime());
  267.                         $em->persist($waitItem);
  268.                         foreach ($orderItem->getParticipants() as $participant) {
  269.                             $participant->setOrderItem(null);
  270.                             $participant->setTitle($participant->getPerson()->getTitle());
  271.                             $participant->setSalutation($participant->getPerson()->getSalutation());
  272.                             $participant->setMember($participant->getPerson()->getMember());
  273.                             $participant->setFirstname($participant->getPerson()->getFirstname());
  274.                             $participant->setLastname($participant->getPerson()->getLastname());
  275.                             $participant->setDateOfBirth($participant->getPerson()->getDateOfBirth());
  276.                             $participant->setComment($participant->getPerson()->getComment());
  277.                             $participant->setOrderItem($orderItem);
  278.                             $participant->setCreated(new \DateTime());
  279.                             // $orderItem->removeParticipant($participant);
  280.                             // $orderItem->setQuantity($orderItem->getQuantity() - 1);
  281.                             $em->persist($participant);
  282.                         }
  283.                         $order->addWaitItem($waitItem);
  284.                         $this->addFlash('error''Im Kurs "'.$occurrence->getTitle().'" sind nicht mehr genug Plätze verfügbar. Es fehlen "'.($orderItem->getQuantity() + $occurrence->getBookedSlots()) - $occurrence->getSlots().'" Plätze. Die komplette Buchung wurde stattdessen zur Warteliste hinzugefügt.');
  285.                         $em->flush();
  286.                         // $order->removeOrderItem($orderItem);
  287.                         $orderItem->setOrder(null);
  288.                         $orderItem->setStatus('wait_item');
  289.                         // $orderItem->setCancelledQuantity($orderItem->getQuantity());
  290.                         //   $orderItem->setQuantity($orderItem->getQuantity());
  291.                         $em->persist($orderItem);
  292.                         $em->persist($order);
  293.                         // $em->remove($orderItem);
  294.                         $em->flush();
  295.                         //  var_dump($orderItem->getOrder());
  296.                         return $this->redirectToRoute('customer_orders', ['id' => $return]);
  297.                     } elseif (($occurrence->getSlots() < ($orderItem->getQuantity() + $occurrence->getBookedSlots())) && !$occurrence->getReservationAllowed()) {
  298.                         //  $occurrence->setBookedSlots($occurrence->getBookedSlots());
  299.                         $em->persist($occurrence);
  300.                         $em->remove($order);
  301.                         $em->flush();
  302.                         $allItemsBookable false;
  303.                         $this->addFlash('error''Im Kurs "'.$occurrence->getTitle().'" sind nicht mehr genug Plätze verfügbar. Es fehlen "'.($orderItem->getQuantity() + $occurrence->getBookedSlots()) - $occurrence->getSlots().'" Plätze.');
  304.                         return $this->redirectToRoute('customer_orders', ['id' => $return]);
  305.                     } else {
  306.                         if ('CourseSubscription' == $orderItem->getCourseOccurrence()->getCourse()->getCourseNature()) {
  307.                             $this->addFlash('warning''courseSubsciption');
  308.                             //    $courseSubscriptionBooking = new CourseSubscriptionBooking($orderItem, $orderItem->getCourse());
  309.                             //    $courseSubscriptionBooking->setOrderItem($orderItem);
  310.                             //    $courseSubscriptionBooking->setCourse($orderItem->getCourseOccurrence()->getCourse());
  311.                             //    $courseSubscriptionBooking->setCourseSubscription($orderItem->getCourseOccurrence()->getCourse()->getSubscription());
  312.                             //    $orderItem->setCourseSubscriptionBooking($courseSubscriptionBooking);
  313.                             //    $em->persist($courseSubscriptionBooking);
  314.                         }
  315.                         /*
  316.                         foreach ($orderItem->getParticipants() as $participant)
  317.                         {
  318.                             $occurrence->setBookedSlots($occurrence->getBookedSlots() + 1);
  319.                         }
  320.                         $em->persist($occurrence);
  321.                         */
  322.                         // $em->persist($occurrence);
  323.                         //            $occurrence = $orderItem->getCourseOccurrence();
  324.                         //  $occurrence->setBookedSlots($occurrence->getBookedSlotsDirectly() + $orderItem->getQuantity());
  325.                         //  $occurrence->setBookedSlots($occurrence->getBookedSlots() + $orderItem->getQuantity());
  326.                         //            $em->persist($occurrence);
  327.                         //     $em->flush();
  328.                         //   $occurrences[] = $occurrence;
  329.                     }
  330.                 }
  331.                 //  $em->flush();
  332.             }
  333.             $this->updateParticipantsOfOrder($order);
  334.             if ($orderItem->getCourse()) {
  335.                 $occurrence->setBookedSlots($occurrence->getBookedSlots() + $orderItem->getQuantity());
  336.             }
  337.             //  $em->flush();
  338.             if ($allItemsBookable) {
  339.                 if (count($order->getOrderItems()) > || count($order->getWaitItems()) > 0) {
  340.                     $order->setClient($this->getCurrentClient());
  341.                     $order->setCustomer($customer);
  342.                     $order->setCustomerData($customer);
  343.                     if ($customer->getIban()) {
  344.                         $order->setPaymentType(Order::PAYMENT_DEBIT);
  345.                         $order->setIban(
  346.                             strtoupper(
  347.                                 preg_replace('/\s+/'''$customer->getIban())
  348.                             )
  349.                         );
  350.                         $order->setBic($customer->getBic());
  351.                         $order->setBank($customer->getBank());
  352.                     }
  353.                     $orderItem->setOrder($order);
  354.                     $orderItem->getCourseOccurrence();
  355.                     // Ggf. nachfolgende Bestellungen fuer Abokurse generieren und speichern
  356.                     $flashs = [];
  357.                     $followingOrders $orderService->generateOrdersForSubscriptionCoursesAllFollowingOccurrences($this->getCurrentClient(), $order$flashs);
  358.                     foreach ($followingOrders as $orderToSave) {
  359.                         $em->persist($orderToSave);
  360.                     }
  361.                     foreach ($flashs as $flashToShow) {
  362.                         foreach ($flashToShow as $key => $value) {
  363.                             $this->addFlash($key$value);
  364.                         }
  365.                     }
  366.                 }
  367.                 /*
  368.                 foreach ($order->getOrderItems() as $orderItem) {
  369.                     if ($orderItem->getMaterialCosts()) {
  370.                                    $item = new OrderItem();
  371.                                    $item->setCourseOccurrence($orderItem->getCourseOccurrence());
  372.                                    $item->setPrice($orderItem->getCourseOccurrence()->getCourse()->getMaterialCost());
  373.                                    $item->setTaxRate($orderItem->getCourseOccurrence()->getCourse()->getTaxRate());
  374.                                    $item->setQuantity(1);
  375.                                    $item->setIsFree(false);
  376.                                    $item->setOrder($order);
  377.                                    $item->setCourseItem($orderItem);
  378.                                    $item->setCreated(new \Datetime());
  379.                        }
  380.                        $em->persist($item);
  381.                    }
  382.                    */
  383.                 // ##################################### ZOOM MEETING ########################################
  384.                 if (isset($_ENV['ZOOM_WEBINAR']) && === $_ENV['ZOOM_WEBINAR']) {
  385.                     foreach ($order->getOrderItems() as $orderItem) {
  386.                         foreach ($orderItem->getParticipants() as $participant) {
  387.                             $registrant $zoomService->addRegistrantToWebinar(
  388.                                 $this->getCurrentClient(),  // Client-Kontext für mandantenspezifische Zoom-Config
  389.                                 $orderItem->getCourseOccurrence()->getCode(),
  390.                                 $participant->getPerson()->getContactEmail(),
  391.                                 $participant->getPerson()->getFirstname(),
  392.                                 $participant->getPerson()->getLastname()
  393.                             );
  394.                         }
  395.                     }
  396.                 }
  397.                 // ##################################### ZOOM MEETING ########################################
  398.                 $em->persist($order);
  399.                 $em->flush();
  400.                 if ($return) {
  401.                     if ($orderItem->getCourse()) {
  402.                         $this->addFlash('warning''Der Kurs "'.$occurrence->getTitle().'" wurde mit '.$orderItem->getQuantity().' Plätzen bebucht');
  403.                     }
  404.                     return $this->redirectToRoute('customer_orders', ['id' => $return]);
  405.                 } else {
  406.                     return $this->redirectToRoute('order_index');
  407.                 }
  408.             }
  409.             $em->remove($orderItem);
  410.             $em->remove($order);
  411.             $em->flush();
  412.             $this->addFlash('warning''LETZTE MÖGLICHKEIT "'.$occurrence->getTitle().'" '.$occurrence->getSlots().' | '.$occurrence->getBookedSlots().'|'.($occurrence->getSlots() - $occurrence->getBookedSlots()).' | '.$orderItem->getQuantity());
  413.         }
  414.         return $this->render('order/new.html.twig', [
  415.             'order' => $order,
  416.             'form' => $form->createView(),
  417.             'customer' => $customer,
  418.             'invoiceRecipient' => $invoiceRecipient,
  419.             'invalidCustomerData' => !$order->checkCustomerData($customer),
  420.             'isEmptyParticipants' => $isEmptyParticipants,
  421.             'isInvoiceClosed' => false,
  422.         ]);
  423.     }
  424.     /**
  425.      * @Route("/{id}", name="order_show", methods="GET|POST", requirements={"id"="\d+"})
  426.      */
  427.     public function show(
  428.         Request $request,
  429.         Order $order,
  430.         InvoiceRepository $invoiceRepo,
  431.     ): Response {
  432.         // Security: Check client ownership via Voter
  433.         $this->denyAccessUnlessGranted('view'$order);
  434.         $form $this->createForm(OrderStatusType::class, $order);
  435.         $form->handleRequest($request);
  436.         if ($form->isSubmitted() && $form->isValid()) {
  437.             $this->getDoctrine()->getManager()->flush();
  438.             return $this->redirectToRoute('order_show', ['id' => $order->getId()]);
  439.         }
  440.         $orderedInvoices $invoiceRepo->getOrderedInvoices($order->getId());
  441.         $isInvoiceWithCancillation $this->isInvoiceWithCancillation($order->getInvoices());
  442.         return $this->render('order/show.html.twig', [
  443.             'order' => $order,
  444.             'form' => $form->createView(),
  445.             'isInvoiceWithCancillation' => $isInvoiceWithCancillation,
  446.             'orderedInvoices' => $orderedInvoices,
  447.         ]);
  448.     }
  449.     /**
  450.      * @Route("/search/{occurrenceId}/{timeId}", name="order_search", methods="GET|POST", requirements={"id"="\d+"})
  451.      */
  452.     public function search(
  453.         Request $request,
  454.         Connection $connection,
  455.         CourseOccurrenceTimeRepository $timeRepository,
  456.     ): Response {
  457.         $time $timeRepository->find($request->get('timeId'));
  458.         $sql 'SELECT
  459.             oi.*
  460.         FROM
  461.             customer_order_item oi,
  462.             course_occurrence o
  463.         WHERE
  464.             o.id = oi.course_occurrence_id AND
  465.             o.start = "'.$time->getStart()->format('Y-m-d H:i:s').'"
  466.         GROUP BY
  467.             oi._order_id';
  468.         $result $connection->fetchAssoc($sql);
  469.         if (empty($result)) {
  470.             $sql 'SELECT
  471.                 oi.*
  472.             FROM
  473.                 wait_item oi,
  474.                 course_occurrence o
  475.             WHERE
  476.                 o.id = oi.course_occurrence_id AND
  477.                 o.start = "'.$time->getStart()->format('Y-m-d H:i:s').'"
  478.             GROUP BY
  479.                 oi._order_id';
  480.             $result $connection->fetchAssoc($sql);
  481.         }
  482.         return $this->redirectToRoute('order_show', ['id' => $result['_order_id']]);
  483.     }
  484.     /**
  485.      * @Route("/{id}/edit/{return}", name="order_edit", methods="GET|POST", requirements={"id"="\d+","return"="\d+"})
  486.      */
  487.     public function edit(
  488.         Request $request,
  489.         Order $order,
  490.         $return '',
  491.         ConfigurationService $configService,
  492.         PersonRepository $personRepo,
  493.     ): Response {
  494.         $isEmptyParticipants false;
  495.         $invoiceRecipient null;
  496.         $isInvoiceClosed false;
  497.         // Security: Check client ownership via Voter
  498.         $this->denyAccessUnlessGranted('view'$order);
  499.         $order->setModified(new \DateTime());
  500.         $customer null;
  501.         if ($order->getCustomer()) {
  502.             $customer $order->getCustomer();
  503.         }
  504.         if (
  505.             $order->getInvoices()->first()
  506.             && Invoice::STATUS_CLOSED == $order->getInvoices()->first()->getStatus()
  507.         ) {
  508.             $isInvoiceClosed true;
  509.         }
  510.         if ($return) {
  511.             $customer $personRepo->find($return);
  512.             $invoiceRecipient $personRepo->getInvoiceReciepientMembers($return);
  513.             $invoiceRecipient $this->createInvoiceRecipientValues($invoiceRecipient$customer);
  514.             $customerChildrens $personRepo->getMembersByClient($customer);
  515.             $isEmptyParticipants = (empty($customerChildrens)) ? true false;
  516.         }
  517.         if (Order::STATUS_PENDING != $order->getStatus()) {
  518.             $this->addFlash('notice''Die Bestellung kann nur bearbeitet werden wenn Sie in Wartestellung ist.');
  519.             if ($return) {
  520.                 return $this->redirectToRoute('customer_orders', ['id' => $return]);
  521.             } else {
  522.                 return $this->redirectToRoute('order_index');
  523.             }
  524.         }
  525.         $form $this->createForm(OrderType::class, $order, [
  526.             'client' => $this->getCurrentClient(),
  527.             'taxes' => $configService->getTaxConfigbyClient($this->getCurrentClient()),
  528.             'invoiceRecipient' => $invoiceRecipient,
  529.             'customer' => $customer,
  530.             'form_type' => OrderType::TYPE_CREATE_FOR_CUSTOMER,
  531.         ]);
  532.         $form->handleRequest($request);
  533.         if ($form->isSubmitted() && $form->isValid()) {
  534.             // $this->updateParticipantsOfOrder($order);
  535.             $em $this->getDoctrine()->getManager();
  536.             foreach ($order->getOrderItems() as $item) {
  537.                 $item->setModified(new \DateTime());
  538.                 if (!$item->isFree() && $item->getCourseOccurrence()) {
  539.                     $item->setName($item->getCourseOccurrence()->getTitle());
  540.                     $em->persist($item);
  541.                 } else {
  542.                     // $item->setCourseOccurrence(null);
  543.                     $em->persist($item);
  544.                 }
  545.             }
  546.             $em->flush();
  547.             $this->addFlash('notice''Die Bestellung wurde bearbeitet.');
  548.             return $this->redirectToRoute('order_index');
  549.             // }
  550.         }
  551.         return $this->render('order/edit.html.twig', [
  552.             'order' => $order,
  553.             'form' => $form->createView(),
  554.             'customer' => $customer,
  555.             'isEmptyParticipants' => $isEmptyParticipants,
  556.             'invoiceRecipient' => $invoiceRecipient,
  557.             'isInvoiceClosed' => $isInvoiceClosed,
  558.         ]);
  559.     }
  560.     /**
  561.      * @Route("/{id}/cancel", name="order_cancel", methods="POST")
  562.      */
  563.     public function cancel(
  564.         Request $request,
  565.         Order $order,
  566.         ConfigurationService $configService,
  567.         OrderService $orderService,
  568.     ): Response {
  569.         // Security: Check client ownership via Voter
  570.         $this->denyAccessUnlessGranted('view'$order);
  571.         if ($this->isCsrfTokenValid('cancel'.$order->getId(), $request->request->get('_token'))) {
  572.             $em $this->getDoctrine()->getManager();
  573.             foreach ($order->getOrderItems() as $orderItem) {
  574.                 // If order item is connected to time slot mark it bookable again
  575.                 if ($time $orderItem->getCourseOccurrenceTime()) {
  576.                     $time->setAvailability('Bookable');
  577.                     $time->setOrderItem(null);
  578.                 }
  579.                 if ($orderItem) {
  580.                     $orderItem->setStatus('cancelled');
  581.                     foreach ($orderItem->getParticipants() as $person) {
  582.                         $person->setStatus('cancelled');
  583.                         $person->setCancelled(new \DateTime());
  584.                         $person->setModified(new \DateTime());
  585.                         $em->persist($person);
  586.                     }
  587.                     $orderItem->setCancelledQuantity($orderItem->getCancelledQuantity() + 1);
  588.                     $orderItem->setQuantity($orderItem->getQuantity() - 1);
  589.                     $orderItem->setModified(new \DateTime());
  590.                     // $orderItem->setQuantity('0');
  591.                 }
  592.             }
  593.             foreach ($order->getWaitItems() as $waitItem) {
  594.                 if ($time $waitItem->getCourseOccurrenceTime()) {
  595.                     $time->setAvailability('Requestable');
  596.                     $time->setWaitItem(null);
  597.                 }
  598.             }
  599.             foreach ($order->getInvoices() as $invoice) {
  600.                 if (!$invoice->isCancelled() && !$invoice->isCancellation()) {
  601.                     $cancellation $orderService->createCancellation($invoice);
  602.                     $cancellation->setSignedBy($this->getCurrentUser());
  603.                     $invoice->setCancelled(true);
  604.                     $em->persist($cancellation);
  605.                 }
  606.             }
  607.             $order->setStatus(Order::STATUS_CANCELLED);
  608.             $order->setModified(new \DateTime());
  609.             $orderService->calculateCancelOrderItems($order);
  610.             $em->flush();
  611.             $this->addFlash('notice''Bestellung storniert');
  612.         }
  613.         return $this->redirectToRoute('order_show', ['id' => $order->getId()]);
  614.     }
  615.     /**
  616.      * @Route("/set-cancel-date/{id}", name="participant_set-cancel-date")
  617.      */
  618.     public function setCancelDateForParticicpant(
  619.         Request $request,
  620.         OrderItemPerson $participant,
  621.         OrderService $orderService,
  622.         RequestStack $requestStack,
  623.         ManagerRegistry $managerRegistry,
  624.         //  LoggerInterface $logger
  625.     ): Response {
  626.         // Security: Check client ownership via Voter
  627.         $this->denyAccessUnlessGranted('view'$participant->getOrderItem()->getOrder());
  628.         $form $this->createForm(OrderItemPersonCancelDate::class, null, [
  629.             'action' => $this->generateUrl('participant_set-cancel-date', ['id' => $participant->getId()]),
  630.         ]);
  631.         $form->handleRequest($request);
  632.         $participant->setCancelled($orderService->getCancelDateForParticipantInCourse($this->getCurrentClient(), $participant));
  633.         if ($form->isSubmitted() && $form->isValid()) {
  634.             /**
  635.              * @var \DateTimeInterface $cancelDate
  636.              */
  637.             $cancelDate $form->getData()['cancelDate'];
  638.             // Kuendigungsdatum in entsprechenden Teilnehmereintrag im Kurs schreiben
  639.             $participant->setCancelled($cancelDate);
  640.             $em $managerRegistry->getManager();
  641.             $booking $participant->getOrderItem()->getCourseSubscriptionBooking();
  642.             if (!empty($booking)) {
  643.                 $terminationPeriod $booking->getCourseSubscription()->getTerminationPeriod();
  644.                 $cancelDate->modify('+'.$terminationPeriod.' months');
  645.             }
  646.             $result $orderService->setCancelDateForParticipantInCourse(
  647.                 $this->getCurrentClient(),
  648.                 $participant,
  649.                 $cancelDate
  650.             );
  651.             $em->flush();
  652.             // Aktive Eintraege/Bestellpositionen/Bestellungen fuer Kurstermine nach dem Kuendigungsdatum erhalten
  653.             $oipsToCancel $orderService->getAllActiveParticipationsAfterCancelDateByParticipant(
  654.                 $this->getCurrentClient(),
  655.                 $participant
  656.             );
  657.             // Diese Eintraege einzeln durchlaufen und stornieren
  658.             foreach ($oipsToCancel as $oipToCancel) {
  659.                 // Bei schon vorhandenen Rechnungen ggf. Storno-Rechnungen erstellen
  660.                 foreach ($oipToCancel->getOrderItem()->getOrder()->getInvoices() as $invoice) {
  661.                     if (!$invoice->isCancelled() && !$invoice->isCancellation() && $invoice->containsOrderItem($oipToCancel->getOrderItem())) {
  662.                         $cancellation $orderService->createCancellation($invoice$oipToCancel->getOrderItem(), $oipToCancel->getId());
  663.                         $cancellation->setSignedBy($this->getCurrentUser());
  664.                         $em->persist($cancellation);
  665.                     }
  666.                 }
  667.                 // Eintrag stornieren
  668.                 $orderService->cancelOrderItemParticipant($oipToCancel->getOrderItem(), $oipToCancel->getId());
  669.                 // Ggf. Bestellposition stornieren
  670.                 if (!$oipToCancel->getOrderItem()->hasUncancelledParticipants()) {
  671.                     $oipToCancel->getOrderItem()->setStatus(OrderItem::STATUS_CANCELLED);
  672.                     foreach ($oipToCancel->getOrderItem()->getMaterialCosts() as $materialCost) {
  673.                         $materialCost->setStatus(OrderItem::STATUS_CANCELLED);
  674.                     }
  675.                 }
  676.                 // Ggf. Bestellung stornieren
  677.                 if (!$oipToCancel->getOrderItem()->getOrder()->hasUncancelledItems()) {
  678.                     $oipToCancel->getOrderItem()->getOrder()->setStatus(Order::STATUS_CANCELLED);
  679.                 }
  680.                 $orderService->calculateCancelOrderItem($oipToCancel->getOrderItem());
  681.             }
  682.             // Aenderungen in Datenbank speichern
  683.             $em->flush();
  684.             $flashExists false;
  685.             foreach ($requestStack->getSession()->all() as $flashType => $flashTitle) {
  686.                 if ('notice' == $flashType && 'Kündigungsdatum eingetragen' == $flashTitle) {
  687.                     $flashExists true;
  688.                 }
  689.             }
  690.             if (!$flashExists) {
  691.                 $this->addFlash('notice''Kündigungsdatum eingetragen');
  692.             }
  693.             $route $request->headers->get('referer');
  694.             return $this->redirect($route);
  695.         }
  696.         return $this->render('course/_set-cancel-date.html.twig', [
  697.             'person' => $participant->getPerson(),
  698.             'form' => $form->createView(),
  699.             'cancelDate' => $participant->getCancelled(),
  700.             'today' => new \DateTime(),
  701.         ]);
  702.     }
  703.     /**
  704.      * @Route("/{id}/cancel-item/{participantId}/{return}", name="order-item_cancel", methods="GET")
  705.      */
  706.     public function cancelItem(
  707.         Request $request,
  708.         OrderItem $orderItem,
  709.         int $participantId 0,
  710.         string $return '',
  711.         ConfigurationService $configService,
  712.         OrderService $orderService,
  713.     ): Response {
  714.         $order $orderItem->getOrder();
  715.         // Security: Check client ownership via Voter
  716.         $this->denyAccessUnlessGranted('view'$order);
  717.         $em $this->getDoctrine()->getManager();
  718.         foreach ($order->getInvoices() as $invoice) {
  719.             if (!$invoice->isCancelled() && !$invoice->isCancellation() && $invoice->containsOrderItem($orderItem)) {
  720.                 $cancellation $orderService->createCancellation($invoice$orderItem$participantId);
  721.                 $cancellation->setSignedBy($this->getCurrentUser());
  722.                 $em->persist($cancellation);
  723.             }
  724.         }
  725.         if ($participantId 0) {
  726.             $orderService->cancelOrderItemParticipant($orderItem$participantId);
  727.         } else {
  728.             $orderItem->cancelAllParticipants();
  729.         }
  730.         if (!$orderItem->hasUncancelledParticipants()) {
  731.             $orderItem->setStatus(OrderItem::STATUS_CANCELLED);
  732.             foreach ($orderItem->getMaterialCosts() as $materialCost) {
  733.                 $materialCost->setStatus(OrderItem::STATUS_CANCELLED);
  734.             }
  735.         }
  736.         if (!$order->hasUncancelledItems()) {
  737.             $order->setStatus(Order::STATUS_CANCELLED);
  738.         }
  739.         $orderService->calculateCancelOrderItem($orderItem);
  740.         $orderItem->setModified(new \DateTime());
  741.         $order->setModified(new \DateTime());
  742.         $em->flush();
  743.         $this->addFlash('notice''Kurs storniert');
  744.         // return $this->redirect(urldecode($return));
  745.         return $this->redirectToRoute('course_invoices', ['id' => $request->get('course_id')]);
  746.     }
  747.     /**
  748.      * @Route("/copy-participant/{id}", name="participant_copy-to-other-occurrence")
  749.      */
  750.     public function copyParticipantToOtherOccurrence(
  751.         Request $request,
  752.         OrderItemPerson $participant,
  753.         OrderService $orderService,
  754.         CourseOccurrenceRepository $coRepo,
  755.         RequestStack $requestStack,
  756.     ): Response {
  757.         // Security: Check client ownership via Voter
  758.         $this->denyAccessUnlessGranted('view'$participant->getOrderItem()->getOrder());
  759.         $selectableOccurrences $orderService->getAllOccurrencesOfCourseAParticipantIsNotInBeforeCancelDate($this->getCurrentClient(), $participant);
  760.         $selectableOccurrencesArray = [];
  761.         foreach ($selectableOccurrences as $selectableOccurrence) {
  762.             $selectableOccurrencesArray[$selectableOccurrence->getStart()->format('d.m.Y').' - '.$selectableOccurrence->getEnd()->format('d.m.Y')] = $selectableOccurrence->getId();
  763.         }
  764.         $form $this->createForm(OrderItemPersonCopy::class, null, [
  765.             'occurrences' => $selectableOccurrencesArray,
  766.             'action' => $this->generateUrl('participant_copy-to-other-occurrence', ['id' => $participant->getId()]),
  767.         ]);
  768.         $form->handleRequest($request);
  769.         if ($form->isSubmitted() && $form->isValid()) {
  770.             $em $this->getDoctrine()->getManager();
  771.             $flashs = [];
  772.             $orders = [];
  773.             foreach ($form->getData()['occurrences'] as $occurrenceId) {
  774.                 $flash = [];
  775.                 $occurrence $coRepo->find($occurrenceId);
  776.                 $newOrder $orderService->generateOrderForCourseOccurrenceFromOrderItemPerson(
  777.                     $this->getCurrentClient(),
  778.                     $participant,
  779.                     $occurrence,
  780.                     $flash,
  781.                     false
  782.                 );
  783.                 if (null !== $newOrder) {
  784.                     $orders[] = $newOrder;
  785.                 }
  786.                 if (count($flash) > 0) {
  787.                     $flashs[] = $flash;
  788.                 }
  789.             }
  790.             foreach ($orders as $orderToSave) {
  791.                 $em->persist($orderToSave);
  792.             }
  793.             foreach ($flashs as $flashToShow) {
  794.                 foreach ($flashToShow as $key => $value) {
  795.                     $this->addFlash($key$value);
  796.                 }
  797.             }
  798.             // Aenderungen in Datenbank speichern
  799.             $em->flush();
  800.             $flashExists false;
  801.             foreach ($requestStack->getSession()->all() as $flashType => $flashTitle) {
  802.                 if ('notice' == $flashType && $flashTitle == 'Teilnehmer in '.count($orders).' weitere Termine übernommen') {
  803.                     $flashExists true;
  804.                 }
  805.             }
  806.             if (!$flashExists) {
  807.                 $this->addFlash('notice''Teilnehmer in '.count($orders).' weitere Termine übernommen');
  808.             }
  809.             return $this->redirectToRoute('course_participants', ['id' => $participant->getOrderItem()->getCourseOccurrence()->getCourse()->getId()]);
  810.         }
  811.         return $this->render('course/_copy-to-occurrence.html.twig', [
  812.             'person' => $participant->getPerson(),
  813.             'form' => $form->createView(),
  814.         ]);
  815.     }
  816.     /**
  817.      * @Route("/{id}/create-invoice", name="order_invoice_create", methods="GET")
  818.      */
  819.     public function createInvoice(
  820.         Request $request,
  821.         Order $order,
  822.         InvoiceService $invoiceService,
  823.     ): Response {
  824.         $results $invoiceService->createInvoiceFromOrder($order);
  825.         $em $this->getDoctrine()->getManager();
  826.         // Update the order status
  827.         $newOrderState $order->setStatus(Order::STATUS_PROCESSING);
  828.         $em->persist($newOrderState);
  829.         foreach ($results['attendees'] as $attendee) {
  830.             $em->persist($attendee);
  831.         }
  832.         $em->persist($results['invoice']);
  833.         $em->flush();
  834.         $this->addFlash('notice'' Rechnung erstellt');
  835.         return $this->redirectToRoute('order_show', ['id' => $order->getId()]);
  836.     }
  837.     /**
  838.      * @Route("/{id}", name="order_delete", methods="DELETE")
  839.      */
  840.     public function delete(Request $requestOrder $order): Response
  841.     {
  842.         if ($this->isCsrfTokenValid('delete'.$order->getId(), $request->request->get('_token'))) {
  843.             $em $this->getDoctrine()->getManager();
  844.             $em->remove($order);
  845.             $em->flush();
  846.         }
  847.         return $this->redirectToRoute('order_index');
  848.     }
  849.     protected function updateParticipantsOfOrder(Order $order)
  850.     {
  851.         if (count($order->getOrderItems()) > 0) {
  852.             foreach ($order->getOrderItems() as $orderItem) {
  853.                 if (count($orderItem->getParticipants()) > 0) {
  854.                     foreach ($orderItem->getParticipants() as $participant) {
  855.                         $participant->updateFieldsFromPerson();
  856.                     }
  857.                 }
  858.             }
  859.         }
  860.     }
  861.     private function createInvoiceRecipientValues($persons$customer)
  862.     {
  863.         $res[] = $customer;
  864.         foreach ($persons as $person) {
  865.             $res[] = $person;
  866.         }
  867.         return $res;
  868.     }
  869.     /**
  870.      * isInvoiceWithCancillation.
  871.      *
  872.      * Checks whether one of the invoices is a cancellation invoice
  873.      *
  874.      * @return bool
  875.      */
  876.     private function isInvoiceWithCancillation(Collection $invoices)
  877.     {
  878.         $cancelation false;
  879.         if (empty($invoices)) {
  880.             return $cancelation;
  881.         }
  882.         $counter count($invoices);
  883.         if ($counter || !$invoices[$counter 1]->isCancellation()) {
  884.             return $cancelation;
  885.         }
  886.         foreach ($invoices as $invoice) {
  887.             if ($invoice->isCancellation()) {
  888.                 $cancelation true;
  889.                 break;
  890.             }
  891.         }
  892.         return $cancelation;
  893.     }
  894.     /**
  895.      * @Route("/changestatus/{id}/{status}", name="change_status")
  896.      */
  897.     public function changeStatus(Order $order$status): Response
  898.     {
  899.         $em $this->getDoctrine()->getManager();
  900.         $newstatus $order->setStatus($status);
  901.         $em->persist($newstatus);
  902.         $em->flush();
  903.         return $this->json([
  904.             'success' => 'Der Status wurde geändert.',
  905.         ]);
  906.     }
  907.     /**
  908.      * @Route("/sendordermail/{id}", name="send_order_mail")
  909.      */
  910.     public function sendordermail(Order $orderPersonRepository $personRepoEmailHistoryService $emailHistoryServiceMailerService $mailerServiceConnection $connection): Response
  911.     {
  912.         $orderItems $order->getOrderItems() ? $order->getOrderItems()->toArray() : [];
  913.         $waitItems $order->getWaitItems() ? $order->getWaitItems()->toArray() : [];
  914.         $allItems array_merge($orderItems$waitItems);
  915.         foreach ($allItems as $item) {
  916.             // ///////////////////////////////// FIELDS Start //////////////////////////////////////
  917.             // Fetch course fields
  918.             $sql 'SELECT
  919.             f.*,
  920.             d.value_text,
  921.             d.value_integer
  922.         FROM
  923.             course_field f
  924.         LEFT JOIN
  925.             course_data d
  926.             ON 
  927.             d.field_id = f.id AND
  928.             d.course_id = :courseId
  929.         WHERE f.certificate = 0';
  930.             $stmt $connection->prepare($sql);
  931.             $stmt->bindValue(
  932.                 'courseId',
  933.                 $item->getCourseOccurrence()->getCourse()->getId()
  934.             );
  935.             $stmt->executeQuery();
  936.             $result $stmt->fetchAll();
  937.             $fields = [];
  938.             foreach ($result as $index => $field) {
  939.                 if (!empty($field['category'])) {
  940.                     if (!$item->getCourseOccurrence()->getCourse()->getCategory()) {
  941.                         continue;
  942.                     }
  943.                     if (!in_array($item->getCourseOccurrence()->getCourse()->getCategory()->getId(), json_decode($field['category'], true))) {
  944.                         continue;
  945.                     }
  946.                 }
  947.                 if (!empty($field['course_type'])) {
  948.                     if (!$item->getCourseOccurrence()->getCourse()->getType()) {
  949.                         continue;
  950.                     }
  951.                     if (!in_array($item->getCourseOccurrence()->getCourse()->getType()->getId(), json_decode($field['course_type'], true))) {
  952.                         continue;
  953.                     }
  954.                 }
  955.                 $fields[] = [
  956.                     'id' => $field['id'],
  957.                     'name' => $field['name'],
  958.                     'value' => !empty($field['value_integer']) ? $field['value_integer'] : $field['value_text'],
  959.                 ];
  960.             }
  961.             $order->setFields($fields);
  962.             $sentMessage $mailerService->sendCheckoutConfirmMessage($order);
  963.             $customer $order->getCustomer();
  964.             $emailHistoryService->saveProtocolEntryFromOrder(
  965.                 $order,
  966.                 $this->getCurrentClient(),
  967.                 $customer,
  968.                 'versandt',
  969.                 $sentMessage['subject'],
  970.                 $sentMessage['message'],
  971.                 'Bestellbestätigung gesendet',
  972.                 $sentMessage['email']
  973.             );
  974.             $message 'Bestellbestätigung ist versendet';
  975.             $this->addFlash('notice'$message);
  976.             return $this->redirectToRoute('order_index');
  977.         }
  978.         // Ensure a return value if there are no order items
  979.         $this->addFlash('error''Keine Bestellpositionen gefunden.');
  980.         return $this->redirectToRoute('order_index');
  981.     }
  982. }