src/Controller/InvoiceController.php line 271

Open in your IDE?
  1. <?php
  2. namespace App\Controller;
  3. use App\Entity\Dto\CsvImportDto;
  4. use App\Entity\Invoice;
  5. use App\Entity\InvoicePayment;
  6. use App\Entity\Order;
  7. use App\Entity\Person;
  8. use App\Entity\WaitItem;
  9. use App\Form\CsvImportType;
  10. use App\Form\InvoiceOnlyType;
  11. use App\Form\InvoiceType;
  12. use App\Repository\InvoiceItemAttendeesRepository;
  13. use App\Repository\InvoicePaymentRepository;
  14. use App\Repository\InvoiceReminderRepository;
  15. use App\Repository\InvoiceRepository;
  16. use App\Repository\PersonRepository;
  17. use App\Service\ConfigurationService;
  18. use App\Service\CsvImportService;
  19. use App\Service\EmailHistoryService;
  20. use App\Service\Exception\ServiceException;
  21. use App\Service\InvoiceService;
  22. use App\Service\MailerService;
  23. use App\Service\PaymentService;
  24. use App\Service\PdfService;
  25. use App\Service\SepaXmlService;
  26. use App\Service\UiService;
  27. use Doctrine\Common\Collections\ArrayCollection;
  28. use App\User\Controller\AbstractClientableController;
  29. use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
  30. use Symfony\Component\HttpFoundation\File\Exception\FileException;
  31. use Symfony\Component\HttpFoundation\RedirectResponse;
  32. use Symfony\Component\HttpFoundation\Request;
  33. use Symfony\Component\HttpFoundation\RequestStack;
  34. use Symfony\Component\HttpFoundation\Response;
  35. use Symfony\Component\Routing\Annotation\Route;
  36. /**
  37.  * @Route("/invoice")
  38.  *
  39.  * @IsGranted("ROLE_MANAGER")
  40.  */
  41. class InvoiceController extends AbstractClientableController
  42. {
  43.     public const LISTING_LIMIT 20;
  44.     private function generateUniqueFileName()
  45.     {
  46.         return md5(uniqid());
  47.     }
  48.     /**
  49.      * @Route("/", name="invoice_index", methods="GET|POST")
  50.      */
  51.     public function index(
  52.         Request $request,
  53.         InvoiceRepository $invoiceRepo,
  54.         CsvImportService $importService,
  55.         UiService $uiService,
  56.         RequestStack $requestStack,
  57.         PaymentService $paymentService,
  58.     ): Response {
  59.         $order $uiService->getSortOrder('invoice-index-listing');
  60.         // Check for unset invoice status
  61.         $this->checkForUnsetInvoiceStatus($invoiceRepo$paymentService);
  62.         $filterInvoices $request->get('action');
  63.         if ($filterInvoices) {
  64.             $requestStack->getSession()->set('filter'$filterInvoices);
  65.         } else {
  66.             $requestStack->getSession()->set('filter'null);
  67.         }
  68.         $invoice $invoiceRepo->getByClientPaged(
  69.             $this->getCurrentClient(),
  70.             self::LISTING_LIMIT,
  71.             $order['orderDirection'] ?? 'desc',
  72.             $order['orderBy'] ?? 'id',
  73.             1,
  74.             ($filterInvoices) ? $filterInvoices null
  75.         );
  76.         $csvImportDto = new CsvImportDto();
  77.         $form $this->createForm(CsvImportType::class, $csvImportDto);
  78.         $form->handleRequest($request);
  79.         if ($form->isSubmitted() && $form->isValid()) {
  80.             $fileName =
  81.                 md5(uniqid()).'.'.$csvImportDto->file->guessExtension();
  82.             try {
  83.                 $csvImportDto->file->move(
  84.                     $this->getParameter('import_directory'),
  85.                     $fileName
  86.                 );
  87.                 $fullFileName $this->getParameter('import_directory').'/'.$fileName;
  88.             } catch (FileException $e) {
  89.                 $this->addFlash(
  90.                     'error',
  91.                     'Beim Datei-Upload ist ein Fehler aufgetreten. Bitte versuchen Sie es später noch einmal.'
  92.                 );
  93.                 return $this->redirectToRoute('invoice_index');
  94.             }
  95.             $result $importService->updateInvoiceStatus(
  96.                 $fullFileName,
  97.                 $this->getCurrentClient()
  98.             );
  99.             if (is_array($result)) {
  100.                 $message 'Beim Rechnungsimport ';
  101.                 if (count($result) > 1) {
  102.                     $message .=
  103.                         'sind '.count($result).' Fehler aufgetreten.';
  104.                 } else {
  105.                     $message .= 'ist ein Fehler aufgetreten.';
  106.                 }
  107.                 $this->addFlash('error'$message);
  108.                 foreach ($result as $key => $error) {
  109.                     $this->addFlash('error'$key .'. '.$error);
  110.                 }
  111.             } elseif (false !== $result) {
  112.                 $this->addFlash('notice'$result.' Rechnungen importiert.');
  113.                 return $this->redirectToRoute('invoice_index');
  114.             }
  115.         }
  116.         return $this->render('invoice/index.html.twig', [
  117.             'uiService' => $uiService,
  118.             'invoices' => $invoice->getIterator(),
  119.             'total' => $invoice->count(),
  120.             'pages' => ceil($invoice->count() / self::LISTING_LIMIT),
  121.             'page' => 1,
  122.             'form' => $form->createView(),
  123.             'filterAction' => $requestStack->getSession()->get('filter'),
  124.             'env' => $_ENV,
  125.             'now' => new \DateTime(),
  126.         ]);
  127.     }
  128.     /**
  129.      * @Route("/{page}/{orderby}/{order}", name="invoice_index_listing", methods="GET", requirements={"page"="\d+","order"="asc|desc"})
  130.      */
  131.     public function indexListing(
  132.         InvoiceRepository $invoiceRepo,
  133.         UiService $uiService,
  134.         $page,
  135.         $orderby,
  136.         $order,
  137.         RequestStack $requestStack,
  138.     ): Response {
  139.         $uiService->storeSortOrder('invoice-index-listing'$orderby$order);
  140.         $invoice $invoiceRepo->getByClientPaged(
  141.             $this->getCurrentClient(),
  142.             self::LISTING_LIMIT,
  143.             $order,
  144.             $orderby,
  145.             $page,
  146.             $requestStack->getSession()->get('filter')
  147.         );
  148.         return $this->render('invoice/_index_listing.html.twig', [
  149.             'invoices' => $invoice->getIterator(),
  150.             'total' => $invoice->count(),
  151.             'pages' => ceil($invoice->count() / self::LISTING_LIMIT),
  152.             'page' => $page,
  153.             'filterAction' => $requestStack->getSession()->get('filter'),
  154.             'env' => $_ENV,
  155.             'now' => new \DateTime(),
  156.         ]);
  157.     }
  158.     /**
  159.      * @Route("/{id}/close", name="invoice_close", methods="GET", requirements={"id"="\d+"})
  160.      */
  161.     public function close(
  162.         Request $request,
  163.         Invoice $invoice,
  164.         PdfService $pdfService,
  165.         MailerService $mailer,
  166.         EmailHistoryService $emailHistoryService,
  167.     ): Response {
  168.         // Security: Check client ownership via Voter
  169.         $this->denyAccessUnlessGranted('view'$invoice);
  170.         $pdf $pdfService->getInvoicePdf($this->getCurrentClient(), $invoice);
  171.         $sentMessage $mailer->sendInvoiceEmail(
  172.             $invoice,
  173.             'Rechnung-'.$invoice->getNumber().'.pdf',
  174.             $pdf->Output('S''Rechnung-'.$invoice->getNumber().'.pdf')
  175.         );
  176.         $outputfile $this->generateUniqueFileName().'.pdf';
  177.         $outputpath $this->getParameter('attachment_directory').'/'.$outputfile;
  178.         $pdf->Output('F'$outputpath);
  179.         $emailHistoryService->saveProtocolEntryFromInvoiceMessage(
  180.             $invoice,
  181.             $sentMessage['sender'],
  182.             $sentMessage['subject'],
  183.             $sentMessage['message'],
  184.             $outputfile,
  185.             'Rechnung-'.$invoice->getNumber().'.pdf'
  186.         );
  187.         if (Invoice::STATUS_CLOSED != $invoice->getStatus()) {
  188.             if ($invoice->isPaymentDebit()) {
  189.                 $invoice->setStatus(Invoice::STATUS_DEBIT_PENDING);
  190.             } else {
  191.                 $invoice->setStatus(Invoice::STATUS_CLOSED);
  192.             }
  193.         }
  194.         $em $this->getDoctrine()->getManager();
  195.         // Update the order status
  196.         $newOrderState $invoice->getOrder()->setStatus(Order::STATUS_DONE);
  197.         $em->persist($newOrderState);
  198.         $em->flush();
  199.         $this->addFlash('notice''Rechnung versendet');
  200.         return $this->redirect($request->get('return'));
  201.     }
  202.     /**
  203.      * @Route("/{id}/lock", name="invoice_lock", methods="GET", requirements={"id"="\d+"})
  204.      */
  205.     public function lock(
  206.         Request $request,
  207.         Invoice $invoice,
  208.         PdfService $pdfService,
  209.     ): Response {
  210.         // Security: Check client ownership via Voter
  211.         $this->denyAccessUnlessGranted('view'$invoice);
  212.         $pdf $pdfService->getInvoicePdf($this->getCurrentClient(), $invoice);
  213.         $outputfile $this->generateUniqueFileName().'.pdf';
  214.         $outputpath $this->getParameter('attachment_directory').'/'.$outputfile;
  215.         $pdf->Output('F'$outputpath);
  216.         if (Invoice::STATUS_CLOSED != $invoice->getStatus()) {
  217.             if ($invoice->isPaymentDebit()) {
  218.                 $invoice->setStatus(Invoice::STATUS_DEBIT_PENDING);
  219.             } else {
  220.                 $invoice->setStatus(Invoice::STATUS_CLOSED);
  221.             }
  222.         }
  223.         $em $this->getDoctrine()->getManager();
  224.         // Update the order status
  225.         $newOrderState $invoice->getOrder()->setStatus(Order::STATUS_DONE);
  226.         $em->persist($newOrderState);
  227.         $em->flush();
  228.         $this->addFlash('notice''Rechnung abgeschlossen');
  229.         return $this->redirect($request->get('return'));
  230.     }
  231.     /**
  232.      * @Route("/new/{return}", name="invoice_new", methods="GET|POST")
  233.      */
  234.     public function new(
  235.         Request $request,
  236.         $return '',
  237.         PersonRepository $personRepo,
  238.         ConfigurationService $configService,
  239.     ): Response {
  240.         $customer null;
  241.         if ($return) {
  242.             $customer $personRepo->find($return);
  243.         }
  244.         $invoice = new Invoice();
  245.         $form $this->createForm(InvoiceType::class, $invoice, [
  246.             'client' => $this->getCurrentClient(),
  247.             'taxes' => $configService->getTaxConfigbyClient(
  248.                 $this->getCurrentClient()
  249.             ),
  250.             'customer' => $customer,
  251.         ]);
  252.         $form->handleRequest($request);
  253.         // check if creation is possible
  254.         if ($form->isSubmitted() && $form->isValid()) {
  255.             $em $this->getDoctrine()->getManager();
  256.             $allItemsBookable true;
  257.             $occurrences = [];
  258.             foreach ($invoice->getOrder()->getOrderItems() as $orderItem) {
  259.                 if ($orderItem->getCourseOccurrence()) {
  260.                     $occurrence $orderItem->getCourseOccurrence();
  261.                     if (
  262.                         !$occurrence->isBookable($orderItem->getQuantity())
  263.                         && $occurrence->getReservationAllowed()
  264.                     ) {
  265.                         $waitItem WaitItem::fromOrderItem($orderItem);
  266.                         foreach ($orderItem->getParticipants() as $participant) {
  267.                             $orderItem->removeParticipant($participant);
  268.                         }
  269.                         $invoice->getOrder()->addWaitItem($waitItem);
  270.                         $invoice->getOrder()->removeOrderItem($orderItem);
  271.                         $em->persist($waitItem);
  272.                         $this->addFlash(
  273.                             'error',
  274.                             'Im Kurs "'.
  275.                                 $occurrence->getTitle().
  276.                                 '" sind nicht mehr genug Plätz verfügbar. Die Buchung wurde stattdessen zur Warteliste hinzugefügt.'
  277.                         );
  278.                     } elseif (
  279.                         !$occurrence->isBookable($orderItem->getQuantity())
  280.                         && !$occurrence->getReservationAllowed()
  281.                     ) {
  282.                         $allItemsBookable false;
  283.                         $this->addFlash(
  284.                             'error',
  285.                             'Im Kurs "'.
  286.                                 $occurrence->getTitle().
  287.                                 '" sind nicht mehr genug Plätz verfügbar.'
  288.                         );
  289.                     } else {
  290.                         $occurrence->bookSlots($orderItem->getQuantity());
  291.                         $occurrences[] = $occurrence;
  292.                     }
  293.                 }
  294.             }
  295.             if ($allItemsBookable) {
  296.                 if (
  297.                     count($invoice->getOrderItems()) > 0
  298.                     || count($invoice->getOrder()->getWaitItems()) > 0
  299.                 ) {
  300.                     $invoice->setNumber(
  301.                         $configService->getNewInvoiceNumberByClient(
  302.                             $this->getCurrentClient()
  303.                         )
  304.                     );
  305.                     $invoice->setSignedBy($this->getCurrentUser());
  306.                     $order $invoice->getOrder();
  307.                     $order->setClient($this->getCurrentClient());
  308.                     $order->setCustomer($customer);
  309.                     $order->setCustomerData($customer);
  310.                     $order->setDate(new \DateTime());
  311.                     $order->setNumber(
  312.                         $configService->getNewOrderNumberByClient(
  313.                             $this->getCurrentClient()
  314.                         )
  315.                     );
  316.                     $em->persist($order);
  317.                     $em->persist($invoice);
  318.                     $em->flush();
  319.                     foreach ($occurrences as $occurrence) {
  320.                         $occurrence->flushBooking();
  321.                     }
  322.                 }
  323.                 $em->flush();
  324.                 if ($return) {
  325.                     return $this->redirectToRoute('customer_invoices', [
  326.                         'id' => $return,
  327.                     ]);
  328.                 } else {
  329.                     return $this->redirectToRoute('invoice_index');
  330.                 }
  331.             }
  332.         }
  333.         return $this->render('invoice/new.html.twig', [
  334.             'invoice' => $invoice,
  335.             'form' => $form->createView(),
  336.             'customer' => $customer,
  337.         ]);
  338.     }
  339.     /**
  340.      * @Route("/{id}", name="invoice_show", methods="GET|POST", requirements={"id"="\d+"})
  341.      */
  342.     public function show(
  343.         Request $request,
  344.         Invoice $invoice,
  345.         InvoicePaymentRepository $invoicePaymentRepo,
  346.         InvoiceRepository $repo,
  347.         ConfigurationService $configService,
  348.         InvoiceService $invoiceService,
  349.         PersonRepository $personRepo,
  350.     ): Response {
  351.         $invoiceRecipient null;
  352.         if ($request->query->has('invoice')) {
  353.             // Entries from the invoice table
  354.             $invoiceRecipient $repo->findOneBy([
  355.                 'invoiceAdressPerson' => $personRepo,
  356.                 'id' => $request->get('invoice'),
  357.             ]);
  358.         }
  359.         $payments $invoicePaymentRepo->getByInvoicePaged($invoice, static::LISTING_LIMIT);
  360.         $invoiceRecipientMembers null;
  361.         $adressChanged false;
  362.         // Need the original recipient ID before update
  363.         $lastInvoiceRecipient $invoice->getInvoiceAdressPerson();
  364.         // var_dump($invoice);
  365.         if (is_null($invoice->getInvoiceAdressPerson())) {
  366.             if (!is_null($invoice->getInvoiceAdressPerson())) {
  367.                 $currentRecipient $invoice->getInvoiceAdressPerson();
  368.                 $invoiceRecipientMembers $personRepo->getInvoiceReciepientMembers($currentRecipient);
  369.                 /*
  370.                  * If there are no Members, so we have to look, if the current Invoice Recipient
  371.                  * is an family member. Then we can get the original client and his recipient members.
  372.                  */
  373.                 if (empty($invoiceRecipientMembers)) {
  374.                     // Get FamilyMembers By Client
  375.                     $invoiceRecipientMembers $personRepo->getInvoiceReciepientsByParent(
  376.                         $currentRecipient->getFamilyMemberOf()
  377.                     );
  378.                     // Client of the Family Member
  379.                     $currentRecipient $currentRecipient->getFamilyMemberOf();
  380.                 }
  381.                 // Create values for the SelectBox (Frontend invoice/edit)
  382.                 $invoiceRecipient $this->createInvoiceRecipientValues(
  383.                     $invoiceRecipientMembers,
  384.                     $currentRecipient,
  385.                     $invoice->getInvoiceAdressPerson()
  386.                 );
  387.             } else {
  388.                 // If there is no InvoiceAdressPerson, we have to take the Person from the Order Table.
  389.                 $currentRecipient $invoice->getOrder()->getCustomer();
  390.                 $invoiceRecipientMembers $personRepo->getInvoiceReciepientMembers(
  391.                     $currentRecipient
  392.                 );
  393.                 // Create values for the SelectBox (Frontend invoice/edit)
  394.                 $invoiceRecipient $this->createInvoiceRecipientValues(
  395.                     $invoiceRecipientMembers,
  396.                     $currentRecipient,
  397.                     $invoice->getInvoiceAdressPerson(),
  398.                     true
  399.                 );
  400.             }
  401.             if (
  402.                 $invoice->getInvoiceAdressPerson()
  403.                 // && $invoice->getInvoiceAdressPerson()->getIsInvoiceRecipient()
  404.             ) {
  405.                 $adressChanged $invoiceService->checkIfAdressHasChanged($invoicetrue);
  406.             }
  407.             // else {
  408.             //     $adressChanged = $invoiceService->checkIfAdressHasChanged($invoice, true);
  409.             // }
  410.         }
  411.         if ($request->get('flashMessage')) {
  412.             $this->addFlash('notice''Rechnungsadresse wurde erfolgreich aktualisiert.');
  413.         }
  414.         $cancellations $repo->findCancellationByInvoice($invoice);
  415.         if ($invoice->isDraft()) {
  416.             $currentRecipient $invoice->getInvoiceAdressPerson();
  417.             $invoiceRecipientMembers $personRepo->getInvoiceReciepientMembers($currentRecipient);
  418.             $invoiceRecipient $this->createInvoiceRecipientValues(
  419.                 $invoiceRecipientMembers,
  420.                 $currentRecipient,
  421.                 $invoice->getInvoiceAdressPerson()
  422.             );
  423.             //  var_dump($invoiceRecipient);
  424.             $form $this->createForm(InvoiceOnlyType::class, $invoice, [
  425.                 'client' => $this->getCurrentClient(),
  426.                 'taxes' => $configService->getTaxConfigbyClient(
  427.                     $this->getCurrentClient()
  428.                 ),
  429.                 'invoiceRecipient' => $invoiceRecipient,
  430.             ]);
  431.             $form->handleRequest($request);
  432.             if ($form->isSubmitted() && $form->isValid()) {
  433.                 $em $this->getDoctrine()->getManager();
  434.                 $em->flush();
  435.                 $this->addFlash('notice''Rechnung gespeichert.');
  436.             }
  437.             return $this->render('invoice/edit.html.twig', [
  438.                 'invoice' => $invoice,
  439.                 'cancellations' => $cancellations,
  440.                 'payments' => $payments->getIterator(),
  441.                 'total' => $payments->count(),
  442.                 'pages' => ceil($payments->count() / self::LISTING_LIMIT),
  443.                 'page' => 1,
  444.                 'form' => $form->createView(),
  445.                 'adressChanged' => $adressChanged,
  446.             ]);
  447.         } else {
  448.             return $this->render('invoice/show.html.twig', [
  449.                 'invoice' => $invoice,
  450.                 'cancellations' => $cancellations,
  451.                 'payments' => $payments->getIterator(),
  452.                 'total' => $payments->count(),
  453.                 'pages' => ceil($payments->count() / self::LISTING_LIMIT),
  454.                 'page' => 1,
  455.             ]);
  456.         }
  457.     }
  458.     /**
  459.      * @Route("/{id}/updateAdress", name="invoice_update_adress", methods="GET|POST", requirements={"id"="\d+"})
  460.      */
  461.     public function updateAdress(
  462.         Request $request,
  463.         Invoice $invoice,
  464.         InvoiceService $invoiceService,
  465.         PersonRepository $personRepo,
  466.     ): RedirectResponse {
  467.         $em $this->getDoctrine()->getManager();
  468.         if ($request->get('person')) {
  469.             $person $personRepo->find($request->get('personId'));
  470.             $updatedInvoice $invoiceService->updateInvoiceAdress($invoice$person);
  471.             $em->persist($updatedInvoice);
  472.             $em->flush();
  473.         }
  474.         // else {
  475.         //     $updatedInvoice = $invoiceService->updateInvoiceAdress($invoice, $null);
  476.         // }
  477.         return $this->redirectToRoute('invoice_show', [
  478.             'id' => $invoice->getId(),
  479.             'flashMessage' => true,
  480.         ]);
  481.     }
  482.     /**
  483.      * @Route("/{id}", name="invoice_delete", methods="DELETE", requirements={"id"="\d+"})
  484.      */
  485.     public function delete(Request $requestInvoice $invoiceInvoiceItemAttendeesRepository $invoiceItemAttendeesRepo): Response
  486.     {
  487.         // Security: Check client ownership via Voter
  488.         $this->denyAccessUnlessGranted('view'$invoice);
  489.         if ($this->isCsrfTokenValid('delete'.$invoice->getId(), $request->request->get('_token')) && $invoice->isDraft()) {
  490.             $em $this->getDoctrine()->getManager();
  491.             // Prüfe, ob andere Rechnungen (z.B. Stornierungen) auf diese Rechnung verweisen
  492.             $childInvoices $em->getRepository(Invoice::class)->findBy(['parent' => $invoice]);
  493.             if (count($childInvoices) > 0) {
  494.                 $this->addFlash('error'sprintf(
  495.                     'Die Rechnung kann nicht gelöscht werden, weil %d Stornierungsrechnung(en) darauf verweisen. Löschen Sie zuerst die Stornierungen.',
  496.                     count($childInvoices)
  497.                 ));
  498.                 return $this->redirectToRoute('invoice_index');
  499.             }
  500.             // Erst alle InvoiceItemAttendees löschen, dann Items, dann Invoice
  501.             foreach ($invoice->getItems() as $item) {
  502.                 $attendees $invoiceItemAttendeesRepo->findBy(['invoice_item' => $item]);
  503.                 foreach ($attendees as $attendee) {
  504.                     $em->remove($attendee);
  505.                 }
  506.                 // Item wird explizit entfernt
  507.                 $em->remove($item);
  508.             }
  509.             // Jetzt kann die Invoice gelöscht werden
  510.             $em->remove($invoice);
  511.             $em->flush();
  512.             $this->addFlash('notice''Rechnung gelöscht');
  513.         }
  514.         return $this->redirectToRoute('invoice_index');
  515.     }
  516.     /**
  517.      * @Route("/{id}/cancel", name="invoice_cancel", methods="POST", requirements={"id"="\d+"})
  518.      */
  519.     public function cancel(
  520.         Request $request,
  521.         Invoice $invoice,
  522.         ConfigurationService $configService,
  523.         InvoiceItemAttendeesRepository $invoiceItemAttendeesRepo,
  524.     ): Response {
  525.         // Security: Check client ownership via Voter
  526.         $this->denyAccessUnlessGranted('view'$invoice);
  527.         if ($this->isCsrfTokenValid('cancel'.$invoice->getId(), $request->request->get('_token')) && !$invoice->isDraft()) {
  528.             $em $this->getDoctrine()->getManager();
  529.             $coppiedInvoice $invoice->cloneForCancellation();
  530.             $cancellation $coppiedInvoice['invoice'];
  531.             $cancellation->setInvoiceDate(new \DateTime());
  532.             $cancellation->setNumber(
  533.                 $configService->getNewInvoiceNumberByClient(
  534.                     $this->getCurrentClient()
  535.                 )
  536.             );
  537.             $cancellation->setCancellation(true);
  538.             $cancellation->setParent($invoice);
  539.             $cancellation->setSignedBy($this->getCurrentUser());
  540.             $cancellation->setStatus(Invoice::STATUS_CLOSED);
  541.             $invoice->setCancelled(true);
  542.             foreach ($coppiedInvoice['attendees'] as $attendee) {
  543.                 $em->persist($attendee);
  544.             }
  545.             $em->persist($cancellation);
  546.             $em->flush();
  547.             $this->addFlash('notice''Rechnung storniert');
  548.         }
  549.         if ($this->isCsrfTokenValid('cancel'.$invoice->getId(), $request->request->get('_token')) && $invoice->isDraft()) {
  550.             $em $this->getDoctrine()->getManager();
  551.             $attendees = [];
  552.             foreach ($invoice->getItems() as $item) {
  553.                 $attendees $invoiceItemAttendeesRepo->findBy(['invoice_item' => $item]);
  554.             }
  555.             if ($attendees) {
  556.                 foreach ($attendees as $attendee) {
  557.                     $em->remove($attendee);
  558.                 }
  559.             }
  560.             $em->remove($invoice);
  561.             $em->flush();
  562.             $this->addFlash('notice''Rechnung gelöscht');
  563.         }
  564.         return $this->redirectToRoute('invoice_show', [
  565.             'id' => $invoice->getId(),
  566.         ]);
  567.     }
  568.     /**
  569.      * @Route("/{id}/cancel_fromorder/{order}", name="invoice_cancel_fromorder", methods="POST", requirements={"id"="\d+"})
  570.      */
  571.     public function cancel_fromorder(
  572.         Request $request,
  573.         Invoice $invoice,
  574.         Order $order,
  575.         InvoiceItemAttendeesRepository $invoiceItemAttendeesRepo,
  576.         ConfigurationService $configService,
  577.     ): Response {
  578.         // Security: Check client ownership via Voter
  579.         $this->denyAccessUnlessGranted('view'$invoice);
  580.         if ($this->isCsrfTokenValid('cancel'.$invoice->getId(), $request->request->get('_token')) && !$invoice->isDraft()) {
  581.             $em $this->getDoctrine()->getManager();
  582.             $coppiedInvoice $invoice->cloneForCancellation();
  583.             $cancellation $coppiedInvoice['invoice'];
  584.             $cancellation->setInvoiceDate(new \DateTime());
  585.             $cancellation->setNumber(
  586.                 $configService->getNewInvoiceNumberByClient(
  587.                     $this->getCurrentClient()
  588.                 )
  589.             );
  590.             $cancellation->setCancellation(true);
  591.             $cancellation->setParent($invoice);
  592.             $cancellation->setSignedBy($this->getCurrentUser());
  593.             $cancellation->setStatus(Invoice::STATUS_CLOSED);
  594.             $invoice->setCancelled(true);
  595.             foreach ($coppiedInvoice['attendees'] as $attendee) {
  596.                 $em->persist($attendee);
  597.             }
  598.             $em->persist($cancellation);
  599.             $em->flush();
  600.             $this->addFlash('notice''Rechnung storniert');
  601.         }
  602.         if ($this->isCsrfTokenValid('cancel'.$invoice->getId(), $request->request->get('_token')) && $invoice->isDraft()) {
  603.             $em $this->getDoctrine()->getManager();
  604.             $attendees = [];
  605.             foreach ($invoice->getItems() as $item) {
  606.                 $attendees $invoiceItemAttendeesRepo->findBy(['invoice_item' => $item]);
  607.             }
  608.             if ($attendees) {
  609.                 foreach ($attendees as $attendee) {
  610.                     $em->remove($attendee);
  611.                 }
  612.             }
  613.             $em->remove($invoice);
  614.             $em->flush();
  615.             $this->addFlash('notice''Rechnung gelöscht');
  616.         }
  617.         return $this->redirectToRoute('order_show', [
  618.             'id' => $order->getId(),
  619.         ]);
  620.     }
  621.     /**
  622.      * @Route("/{id}/pdf", name="invoice_pdf", methods="GET", requirements={"id"="\d+"})
  623.      */
  624.     public function pdf(
  625.         Request $request,
  626.         Invoice $invoice,
  627.         ConfigurationService $configService,
  628.         PdfService $pdfService,
  629.         InvoiceItemAttendeesRepository $invoiceItemAttendeesRepository,
  630.     ) {
  631.         // dd($invoice->getItems()[0]->getId());
  632.         //        dd(count($invoice->getItems()));
  633.         // Security: Check client ownership via Voter
  634.         $this->denyAccessUnlessGranted('view'$invoice);
  635.         $pdf $pdfService->getInvoicePdf($this->getCurrentClient(), $invoice);
  636.         $pdf->Output('D''Rechnung-'.$invoice->getNumber().'.pdf');
  637.         exit;
  638.     }
  639.     /**
  640.      * @Route("/{id}/cancellation-pdf", name="invoice_cancellation-pdf", methods="GET", requirements={"id"="\d+"})
  641.      */
  642.     public function cancellationPdf(
  643.         Request $request,
  644.         Invoice $invoice,
  645.         ConfigurationService $configService,
  646.         PdfService $pdfService,
  647.     ) {
  648.         // Security: Check client ownership via Voter
  649.         $this->denyAccessUnlessGranted('view'$invoice);
  650.         $pdf $pdfService->getCancellationPdf(
  651.             $this->getCurrentClient(),
  652.             $invoice
  653.         );
  654.         $pdf->Output('D''Gutschrift-'.$invoice->getNumber().'.pdf');
  655.         exit;
  656.     }
  657.     /**
  658.      * @Route("/{id}/sepa-xml", name="invoice_sepa-xml", methods="GET", requirements={"id"="\d+"})
  659.      */
  660.     public function sepaXml(
  661.         Request $request,
  662.         Invoice $invoice,
  663.         ConfigurationService $configService,
  664.         SepaXmlService $sepaXmlService,
  665.     ) {
  666.         // Security: Check client ownership via Voter
  667.         $this->denyAccessUnlessGranted('view'$invoice);
  668.         $em $this->getDoctrine()->getManager();
  669.         $invoice->setStatus(Invoice::STATUS_CLOSED);
  670.         if (
  671.             !$invoice
  672.                 ->getOrder()
  673.                 ->getCustomer()
  674.                 ->getDebitActive()
  675.         ) {
  676.             $invoice
  677.                 ->getOrder()
  678.                 ->getCustomer()
  679.                 ->setDebitActive(true);
  680.             $invoice->setIsNewSepaMandate(true);
  681.         }
  682.         $config $configService->getSepaXmlConfigByClient(
  683.             $this->getCurrentClient()
  684.         );
  685.         try {
  686.             $xml $sepaXmlService->getSepaXmlSingle(
  687.                 $this->getCurrentClient(),
  688.                 $config,
  689.                 $invoice
  690.             );
  691.         } catch (ServiceException $e) {
  692.             $this->addFlash('error'$e->getMessage());
  693.             return $this->redirectToRoute('invoice_index');
  694.         }
  695.         $em->flush();
  696.         $response = new Response($xml);
  697.         $response->headers->set('Content-Type''text/xml');
  698.         $response->headers->set('Content-disposition''attachment; filename="SEPA-'.date('Ymd-His').'.xml"');
  699.         return $response;
  700.     }
  701.     /**
  702.      * @Route("/new-sepa-xml", name="invoice_new-sepa-xml", methods="GET")
  703.      */
  704.     public function newInvoicesSepaXml(
  705.         Request $request,
  706.         ConfigurationService $configService,
  707.         SepaXmlService $sepaXmlService,
  708.         InvoiceRepository $invoiceRepo,
  709.     ) {
  710.         $invoices $invoiceRepo->getByClientAndStatuses(
  711.             $this->getCurrentClient(),
  712.             [Invoice::STATUS_DRAFTInvoice::STATUS_DEBIT_PENDING],
  713.             Invoice::PAYMENT_DEBIT
  714.         );
  715.         $invoicesToExport = new ArrayCollection();
  716.         foreach ($invoices as $invoice) {
  717.             if (!$invoicesToExport->contains($invoice)) {
  718.                 $invoice->setStatus(Invoice::STATUS_CLOSED);
  719.                 if (!empty($_ENV['SEPAEXPORT_PAYED'])) {
  720.                     $invoice->setExportStatus(Invoice::EXPORTED);
  721.                 }
  722.                 $invoicesToExport->add($invoice);
  723.                 if (
  724.                     $invoice->getOrder()->getCustomer()
  725.                     && !$invoice
  726.                         ->getOrder()
  727.                         ->getCustomer()
  728.                         ->getDebitActive()
  729.                 ) {
  730.                     $invoice
  731.                         ->getOrder()
  732.                         ->getCustomer()
  733.                         ->setDebitActive(true);
  734.                     $invoice->setIsNewSepaMandate(true);
  735.                 }
  736.             }
  737.         }
  738.         try {
  739.             if (count($invoicesToExport) > 0) {
  740.                 $config $configService->getSepaXmlConfigByClient($this->getCurrentClient());
  741.                 $xml $sepaXmlService->getSepaXmlMultiple($this->getCurrentClient(), $config$invoicesToExport);
  742.                 $em $this->getDoctrine()->getManager();
  743.                 $em->flush();
  744.                 $response = new Response($xml);
  745.                 $response->headers->set('Content-Type''text/xml');
  746.                 $response->headers->set('Content-disposition''attachment; filename="SEPA-'.date('Ymd-His').'.xml"');
  747.                 return $response;
  748.             }
  749.         } catch (ServiceException $e) {
  750.             $this->addFlash('error'$e->getMessage());
  751.             return $this->redirectToRoute('invoice_index');
  752.         }
  753.         $this->addFlash('error''Es wurden keine exportierbaren Rechnungen gefunden.');
  754.         return $this->redirectToRoute('invoice_index');
  755.     }
  756.     /**
  757.      * @Route("/{id}/payments/{page}/{orderby}/{order}", name="invoice_payments_listing", methods="GET", requirements={"id"="\d+"})
  758.      */
  759.     public function paymentsListing(
  760.         Request $request,
  761.         Invoice $invoice,
  762.         $page 1,
  763.         $orderby 'payedDate',
  764.         $order 'ASC',
  765.         InvoicePaymentRepository $repo,
  766.     ) {
  767.         // Security: Check client ownership via Voter
  768.         $this->denyAccessUnlessGranted('view'$invoice);
  769.         $payments $repo->getByInvoicePaged(
  770.             $invoice,
  771.             self::LISTING_LIMIT,
  772.             $order,
  773.             $orderby,
  774.             $page
  775.         );
  776.         return $this->render('invoice/tabs/_payments_listing.html.twig', [
  777.             'payments' => $payments->getIterator(),
  778.             'total' => $payments->count(),
  779.             'pages' => ceil($payments->count() / self::LISTING_LIMIT),
  780.             'page' => $page,
  781.         ]);
  782.     }
  783.     /**
  784.      * @Route("/{id}/reminders/{page}/{orderby}/{order}", name="invoice_reminders_listing", methods="GET", requirements={"id"="\d+"})
  785.      */
  786.     public function remindersListing(
  787.         Request $request,
  788.         Invoice $invoice,
  789.         $page 1,
  790.         $orderby 'remindDate',
  791.         $order 'ASC',
  792.         InvoiceReminderRepository $repo,
  793.     ) {
  794.         // Security: Check client ownership via Voter
  795.         $this->denyAccessUnlessGranted('view'$invoice);
  796.         $reminders $repo->getByInvoicePaged(
  797.             $invoice,
  798.             self::LISTING_LIMIT,
  799.             $order,
  800.             $orderby,
  801.             $page
  802.         );
  803.         return $this->render('invoice/tabs/_reminders_listing.html.twig', [
  804.             'reminders' => $reminders->getIterator(),
  805.             'total' => $reminders->count(),
  806.             'pages' => ceil($reminders->count() / self::LISTING_LIMIT),
  807.             'page' => $page,
  808.         ]);
  809.     }
  810.     private function getInvoiceRecipientId(Person $person)
  811.     {
  812.         if ($person->getIsInvoiceRecipient()) {
  813.             return [
  814.                 'id' => $person->getId(),
  815.                 'type' => 'personRecipient',
  816.             ];
  817.         }
  818.         return [
  819.             'id' => $person->getId(),
  820.             'type' => 'clientRecipient',
  821.         ];
  822.     }
  823.     private function createInvoiceRecipientValues($invoiceRecipientMembers$currentRecipient$invoicePerson null$onlyClient false)
  824.     {
  825.         $res = [];
  826.         $invoicePersonId null;
  827.         if ($invoicePerson) {
  828.             $res[] = $invoicePerson;
  829.             $invoicePersonId $invoicePerson->getId();
  830.         }
  831.         if ($onlyClient) {
  832.             $res[] = $currentRecipient;
  833.         } else {
  834.             if (
  835.                 $currentRecipient
  836.                 && $currentRecipient->getId() != $invoicePersonId
  837.             ) {
  838.                 $res[] = $currentRecipient;
  839.             }
  840.         }
  841.         foreach ($invoiceRecipientMembers as $person) {
  842.             if ($person->getId() != $invoicePersonId) {
  843.                 $res[] = $person;
  844.             }
  845.         }
  846.         return $res;
  847.     }
  848.     /**
  849.      * @Route("/{id}/getPersonAdress", name="get_person_adress", methods="GET|POST")
  850.      */
  851.     public function getClientAdress(Request $requestPerson $personInvoiceRepository $invoiceRepo)
  852.     {
  853.         /**
  854.          * Ajax Call
  855.          * Returns the person informations back to the invoice/edit by onChange the persons.
  856.          */
  857.         $invoiceRecipient null;
  858.         if ($request->query->has('invoice')) {
  859.             // Entries from the invoice table
  860.             $invoiceRecipient $invoiceRepo->findOneBy([
  861.                 'invoiceAdressPerson' => $person,
  862.                 'id' => $request->get('invoice'),
  863.             ]);
  864.         }
  865.         if ($invoiceRecipient) {
  866.             return $this->json([
  867.                 'person' => [
  868.                     'id' => $person->getId(),
  869.                     'company' => $invoiceRecipient->getCompany(),
  870.                     'name' => $invoiceRecipient->getInvoiceFullname(),
  871.                     'street' => $invoiceRecipient->getInvoiceFullStreet(),
  872.                     'place' => $invoiceRecipient->getInvoicePostalcodeCity(),
  873.                     'isInvoiceRecipient' => $person->getIsInvoiceRecipient(),
  874.                 ],
  875.             ], Response::HTTP_OK);
  876.         }
  877.         // Entries from the person table
  878.         return $this->json([
  879.             'person' => [
  880.                 'id' => $person->getId(),
  881.                 'company' => $person->getCompany(),
  882.                 'name' => $person->getFullname(),
  883.                 'street' => $person->getFullStreet(),
  884.                 'place' => $person->getPostalcodeCity(),
  885.                 'isInvoiceRecipient' => $person->getIsInvoiceRecipient(),
  886.             ],
  887.         ], Response::HTTP_OK);
  888.     }
  889.     private function checkForUnsetInvoiceStatus(
  890.         InvoiceRepository $invoiceRepo,
  891.         PaymentService $paymentService,
  892.     ) {
  893.         $allInvoicesWithoutState $invoiceRepo->findBy(['paymentStatus' => null]);
  894.         if (!empty($allInvoicesWithoutState)) {
  895.             foreach ($allInvoicesWithoutState as $invoice) {
  896.                 $sumOfPayments $paymentService->getPayments($invoice);
  897.                 $state $paymentService->interpretPayments($sumOfPayments$invoice);
  898.                 $invoice->setPaymentStatus($state);
  899.                 $em $this->getDoctrine()->getManager();
  900.                 $em->persist($invoice);
  901.             }
  902.             $em->flush();
  903.         }
  904.     }
  905.     /**
  906.      * @Route("/{id}/payInvoice", name="pay_invoice", methods="GET|POST")
  907.      */
  908.     public function payTheBill(Invoice $invoicePaymentService $paymentService)
  909.     {
  910.         $openSum = (float) $paymentService->payAmount($invoice);
  911.         $invoicePayment = new InvoicePayment();
  912.         $invoicePayment->setInvoice($invoice);
  913.         $invoicePayment->setPayedDate(new \DateTime());
  914.         $invoicePayment->setSum($openSum);
  915.         $invoice->setPaymentStatus(Invoice::FULLY_PAID);
  916.         $em $this->getDoctrine()->getManager();
  917.         $em->persist($invoicePayment);
  918.         $em->persist($invoice);
  919.         $em->flush();
  920.         return $this->json([
  921.             'success' => 'Die Rechnung wurde als bezahlt markiert.',
  922.             'invoice' => true,
  923.         ]);
  924.     }
  925. }