<?php
namespace App\Controller;
use App\Entity\Person;
use App\Entity\Category;
use App\Entity\Course;
use App\Entity\Order;
use App\Entity\CourseData;
use App\Entity\Invoice;
use App\Entity\Textblocks;
use App\Form\CourseType;
use App\Repository\CourseFieldRepository;
use App\Repository\CourseDataRepository;
use App\Service\PdfService;
use App\Form\CourseImagesType;
use App\Service\MailerService;
use App\Service\InvoiceService;
use App\Service\SepaXmlService;
use App\Service\PaymentService;
use App\Entity\InvoicePayment;
use App\Entity\CourseOccurrence;
use App\Repository\TagsRepository;
use App\Repository\TagsPersonRepository;
use App\Repository\PersonRepository;
use App\Entity\OrderItem;
use App\Repository\ClientConfigRepository;
use App\Repository\OrderRepository;
use App\Repository\CourseRepository;
use App\Service\EmailHistoryService;
use App\Service\ConfigurationService;
use App\Repository\CartItemRepository;
use App\Entity\CustomerHistoryEntry;
use App\Service\CustomerHistoryService;
use Doctrine\ORM\EntityManagerInterface;
use App\Repository\TextblocksRepository;
use App\Repository\WaitItemRepository;
use App\Repository\OrderItemRepository;
use App\Service\Exception\ServiceException;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use App\Repository\CourseOccurrenceRepository;
use App\Repository\InvoiceItemRepository;
use App\Repository\InvoiceRepository;
use App\Service\OrderService;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\Routing\Annotation\Route;
use Doctrine\Common\Collections\ArrayCollection;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
use Menke\UserBundle\Controller\AbstractClientableController;
use Menke\UserBundle\Repository\ClientRepository;
/**
* @Route("/course")
* @IsGranted("ROLE_SPEAKER")
*/
class CourseController extends AbstractClientableController
{
const LISTING_LIMIT = 25;
/**
*
*/
private function getListingLimit(): int
{
return !empty($_ENV['COURSES_LISTINGLIMIT']) ? (int) $_ENV['COURSES_LISTINGLIMIT'] : 20;
}
/**
* @Route("/", name="course_index", methods="GET")
*/
public function index(
Request $request,
\App\Service\UiService $uiService,
CourseRepository $courseRepository,
EntityManagerInterface $manager,
): Response {
$order = $uiService->getSortOrder('course-index-listing');
$archive = !empty($request->get('archive'));
$courses = $courseRepository->getCoursesByClientPaged(
$this->getCurrentClient(),
$this->getListingLimit(),
$order['orderDirection'] ?? 'ASC',
$order['orderBy'] ?? 'title',
1,
$archive,
);
// die Anzahl der Kurse mit den gebuchten überprüfen und die bookedSlots bei den CourseOccurreces aktualisieren
$this->manager = $manager;
foreach ($courses as $course)
{
foreach ($course->getOccurrences() as $occurrence)
{
$occurrence->setBookedSlots($occurrence->getBookedSlots());
$this->manager->persist($occurrence);
}
}
$this->manager->flush();
///////////////////////////////////////////
return $this->render('course/index.html.twig', [
'uiService' => $uiService,
'courses' => $courses,
'total' => $courses->count(),
'pages' => ceil($courses->count() / $this->getListingLimit()),
'page' => 1,
'archive' => $archive,
'env' => $_ENV,
]);
}
/**
* @Route("/{page}/{orderby}/{order}", name="course_index_listing", methods="GET", requirements={"page"="\d+","order"="asc|desc"})
*/
public function indexListing(
Request $request,
CourseRepository $courseRepository,
\App\Service\UiService $uiService,
$page,
$orderby,
$order
): Response {
$uiService->storeSortOrder('course-index-listing', $orderby, $order);
$archive = !empty($request->get('archive'));
$courses = $courseRepository->getCoursesByClientPaged($this->getCurrentClient(), $this->getListingLimit(), $order, $orderby, $page, $archive);
return $this->render('course/_index_listing.html.twig', [
'courses' => $courses,
'total' => $courses->count(),
'pages' => ceil($courses->count() / $this->getListingLimit()),
'page' => $page,
'archive' => $archive,
'env' => $_ENV,
]);
}
/**
* @Route("/new", name="course_new", methods="GET|POST")
*/
public function new(Request $request, ConfigurationService $configService): Response
{
$course = new Course();
if (!empty($courseNature = $request->get('courseNature'))) {
$course->setCourseNature($courseNature);
}
$form = $this->createForm(CourseType::class, $course, [
'client' => $this->getCurrentClient(),
'taxes' => $configService->getTaxConfigbyClient($this->getCurrentClient()),
]);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$course->setClient($this->getCurrentClient());
$course->setNumber($configService->getNewCourseNumberByClient($this->getCurrentClient()));
foreach ($course->getTexts() as $key => $text) {
$text->setCreated(new \DateTime());
if (empty($text->getOrderId())) {
$text->setOrderId($key + 1000);
}
}
$em = $this->getDoctrine()->getManager();
$course->setCreated(new \DateTime());
$em->persist($course);
$em->flush();
$this->addFlash('success', 'Kurs angelegt');
return $this->redirectToRoute('course-occurrence_new', ['courseId' => $course->getId()]);
}
return $this->render('course/new.html.twig', [
'course' => $course,
'fields' => null,
'form' => $form->createView(),
]);
}
/**
* @Route("/{id}/edit", name="course_edit", methods="GET|POST", requirements={"id"="\d+"})
*/
public function edit(
Request $request,
Course $course,
ConfigurationService $configService,
CourseFieldRepository $courseFieldRepository,
CourseDataRepository $courseDataRepository
): Response {
$this->denyAccessUnlessGranted('ROLE_ADMIN', $course);
$courseTexts = new ArrayCollection();
foreach ($course->getTexts() as $text) {
$courseTexts->add($text);
}
$form = $this->createForm(CourseType::class, $course, [
'client' => $this->getCurrentClient(),
'taxes' => $configService->getTaxConfigbyClient($this->getCurrentClient()),
]);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$manager = $this->getDoctrine()->getManager();
foreach ($courseTexts as $text) {
if (false === $course->getTexts()->contains($text)) {
$text->setCourse(null);
$manager->remove($text);
}
}
foreach ($course->getTexts() as $key => $text) {
if (empty($text->getOrderId())) {
$text->setCreated(new \DateTime());
$text->setOrderId($key + 1000);
}
$text->setModified(new \DateTime());
}
$fields = $request->request->get('fields');
if (!is_null($fields)) {
foreach ($fields as $fieldId => $value) {
$field = $courseFieldRepository->find($fieldId);
$data = $courseDataRepository->findBy([
'course' => $course,
'field' => $field,
]);
if (count($data) == 0) {
$data = new CourseData();
$data->setClient($this->getCurrentClient());
$data->setCourse($course);
$data->setField($field);
$data->setCreated(new \datetime());
$manager->persist($data);
} else {
$data = $data[0];
}
$data->setValueText($value);
$data->setModified(new \datetime());
}
} else {
$fields = [];
}
$course->setModified(new \datetime());
$manager->flush();
$this->addFlash('notice', 'Kurs gespeichert');
return $this->redirectToRoute('course_edit', ['id' => $course->getId()]);
}
// Fetch course fields
$sql = 'SELECT
f.*,
d.value_text,
d.value_integer,
d.value_date
FROM
course_field f
LEFT JOIN
course_data d
ON
d.field_id = f.id AND
d.course_id = ' . $course->getId();
$em = $this->getDoctrine()->getManager();
$stmt = $em->getConnection()->prepare($sql);
$stmt->execute();
$result = $stmt->fetchAll();
$fields = [];
$isset = false;
foreach ($result as $field) {
$isset = false;
if (!empty($field['category'])) {
if (!$course->getCategory()) {
continue;
}
if (!in_array($course->getCategory()->getId(), json_decode($field['category'], true))) {
continue;
} else {
$field = $this->createDescription($field, 'course');
$isset = true;
}
}
if (!empty($field['course_type'])) {
if (!$course->getType()) {
continue;
}
if (!in_array($course->getType()->getId(), json_decode($field['course_type'], true))) {
continue;
} else {
if (!$isset) {
$field = $this->createDescription($field, 'course');
$isset = true;
}
}
}
if (empty($field['category']) && empty($field['ourse_type']) && !empty($field['certificate'])) {
if (!$isset) $field = $this->createDescription($field, 'certificate');
}
if (
!empty($field['category']) ||
!empty($field['course_type']) ||
$field['certificate']
) {
$fields[] = $field;
}
}
return $this->render('course/edit.html.twig', [
'course' => $course,
'form' => $form->createView(),
'fields' => $fields,
'env' => $_ENV,
]);
}
/**
* @Route("/{id}", name="course_delete", methods="DELETE", requirements={"id"="\d+"})
*/
public function delete(Request $request, Course $course): Response
{
$this->denyAccessUnlessGranted('ROLE_ADMIN', $course);
if ($this->isCsrfTokenValid('delete' . $course->getId(), $request->request->get('_token'))) {
$em = $this->getDoctrine()->getManager();
$em->remove($course);
try {
$em->flush();
}
catch (\Exception $e)
{
$errorMessage = $e->getMessage();
if (str_contains($errorMessage, 'Integrity constraint violation'))
{
$this->addFlash('error', 'Der Kurs kann nicht gelöscht werden, weil er an anderer Stelle gebraucht wird.');
// $this->addFlash('error', $errorMessage);
}
else
{
$this->addFlash('error', 'Der Kurs kann nicht gelöscht werden, weil er an anderer Stelle gebraucht wird.');
}
return $this->redirectToRoute('course_index');
}
$this->addFlash('notice', 'Kurs gelöscht');
}
return $this->redirectToRoute('course_index');
}
/**
* @Route("/multiple", name="course_delete-multiple", methods="DELETE")
*/
public function deleteMultiple(
Request $request,
CourseRepository $courseRepo,
CartItemRepository $cartItemRepo,
WaitItemRepository $waitItemRepo,
OrderItemRepository $orderItemRepo
): Response {
if ($this->isCsrfTokenValid('delete_courses', $request->request->get('_token'))) {
$em = $this->getDoctrine()->getManager();
$deleteIds = $request->request->get('delete');
foreach ($deleteIds as $id => $value) {
if ($value) {
$course = $courseRepo->find($id);
$this->denyAccessUnlessGranted('ROLE_ADMIN', $course);
$waitItems = $waitItemRepo->findBy(['course' => $course]);
foreach ($waitItems as $waitItem) {
$em->remove($waitItem);
}
$cartItems = $cartItemRepo->findBy(['course' => $course]);
foreach ($cartItems as $cartItem) {
$em->remove($cartItem);
}
$orderItems = $orderItemRepo->findBy(['course' => $course]);
foreach ($orderItems as $orderItem) {
$orderItem->setCourseOccurrence(null);
}
$em->remove($course);
}
}
try {
$em->flush();
} catch (\Exception $e) {
$errorMessage = $e->getMessage();
if (str_contains($errorMessage, 'Integrity constraint violation')){
$this->addFlash('error', 'Der Kurs kann nicht gelöscht werden, weil er an anderer Stelle gebraucht wird.');
// $this->addFlash('error', $errorMessage);
} else {
$this->addFlash('error', 'Der Kurs kann nicht gelöscht werden, weil er an anderer Stelle gebraucht wird.');
}
return $this->redirectToRoute('course_index');
}
$this->addFlash('notice', count($deleteIds) > 1 ? 'Kurse gelöscht' : 'Kurs gelöscht');
}
return $this->redirectToRoute('course_index');
}
/**
* @Route("/{id}/occurrences", name="course_occurrences", methods="GET", requirements={"id"="\d+"})
*/
public function courseOccurrences(
Request $request,
Course $course,
CourseOccurrenceRepository $repo,
\App\Service\UiService $uiService
): Response {
$this->denyAccessUnlessGranted('ROLE_ADMIN', $course);
$order = $uiService->getSortOrder('course-occurrences-listing');
$archive = !empty($request->get('archive'));
$occurrences = $repo->findByCoursePaged(
$course,
self::LISTING_LIMIT,
$order['orderDirection'] ?? 'ASC',
$order['orderBy'] ?? 'title'
);
return $this->render('course/occurrences.html.twig', [
'uiService' => $uiService,
'course' => $course,
'occurrences' => $occurrences->getIterator(),
'total' => $occurrences->count(),
'pages' => ceil($occurrences->count() / self::LISTING_LIMIT),
'page' => 1,
'env' => $_ENV,
'archive' => $archive,
]);
}
/**
* @Route("/{id}/occurrences/{page}/{orderby}/{order}/{search}", name="course_occurrences_listing", methods="GET", defaults={"search"="", "order"="desc", "orderby"="start"}, requirements={"id"="\d+"})
*/
public function courseOccurrencesListing(
Request $request,
Course $course,
$page,
$orderby,
$order,
$search,
CourseOccurrenceRepository $repo,
\App\Service\UiService $uiService
): Response {
$this->denyAccessUnlessGranted('ROLE_ADMIN', $course);
$uiService->storeSortOrder('course-occurrences-listing', $orderby, $order);
$occurrences = $repo->findByCoursePaged($course, self::LISTING_LIMIT, $order, $orderby, $page, $search);
return $this->render('course/tabs/_occurrences_listing.html.twig', [
'course' => $course,
'occurrences' => $occurrences->getIterator(),
'total' => $occurrences->count(),
'pages' => ceil($occurrences->count() / self::LISTING_LIMIT),
'page' => $page,
'env' => $_ENV,
]);
}
/**
* @Route("/{id}/images", name="course_images", methods="GET|POST", requirements={"id"="\d+"})
*/
public function courseImages(Request $request, Course $course)
{
$this->denyAccessUnlessGranted('ROLE_ADMIN', $course);
$courseImages = new ArrayCollection();
foreach ($course->getImages() as $image) {
$courseImages->add($image);
}
$form = $this->createForm(CourseImagesType::class, $course);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$manager = $this->getDoctrine()->getManager();
foreach ($courseImages as $image) {
if (false === $course->getImages()->contains($image)) {
$image->setCourse(null);
$manager->remove($image);
}
}
foreach ($course->getImages() as $key => $image) {
if (empty($image->getOrderId())) {
$image->setOrderId($key + 1000);
}
}
$image->setCreated(new \Datetime());
$manager->flush();
$this->addFlash('notice', 'Kursbilder gespeichert');
return $this->redirectToRoute('course_images', ['id' => $course->getId()]);
}
return $this->render('course/images.html.twig', [
'course' => $course,
'form' => $form->createView(),
'env' => $_ENV,
]);
}
/**
* @Route("/{id}/invoices", name="course_invoices", methods="GET", requirements={"id"="\d+"})
*/
public function courseInvoices(
Request $request,
Course $course,
OrderItemRepository $repo,
OrderService $orderService,
TagsPersonRepository $tagsPersonRepository,
) {
$this->denyAccessUnlessGranted('ROLE_ADMIN', $course);
$orderItems = $repo->findByCoursePaged($course);
/**
* The display logic of subscription courses is different, as there only one order exists per
* customer/participant, but they should appear in every following course occurrence until they cancel.
*/
// if ($course->getCourseNature() === 'CourseSubscription') {
// return $this->render('course/invoices-subscription.html.twig', [
// 'course' => $course,
// 'orderItems' => $orderItems->getIterator(),
// ]);
// } else {
$archive = !empty($request->get('archive'));
if ($course->getCourseNature() === 'CourseSubscription') {
foreach ($orderItems as $orderItem) {
$orderItem->isAfterCancelDate = $orderService->isOrderItemOccurrenceAfterCancelDateByParticipant($this->getCurrentClient(), $orderItem);
}
}
return $this->render('course/invoices.html.twig', [
'tagsPerson' => $tagsPersonRepository->findAll(),
'course' => $course,
'orderItems' => $orderItems->getIterator(),
'archive' => $archive,
]);
// }
}
/**
* @Route("/{id}/invoices/create", name="course_create_invoices", methods="POST", requirements={"id"="\d+"})
*/
public function courseCreateInvoices(
Request $request,
Course $course,
OrderItemRepository $itemRepo,
InvoiceService $invoiceService
) {
$this->denyAccessUnlessGranted('ROLE_ADMIN', $course);
if ($this->isCsrfTokenValid('create_invoices', $request->request->get('_token'))) {
$em = $this->getDoctrine()->getManager();
$createIds = $request->request->get('create');
$count = 0;
if (!empty($createIds)) {
foreach ($createIds as $id => $value) {
if ($value) {
$orderItem = $itemRepo->find($id);
$results = $invoiceService->createInvoiceFromOrderItem($orderItem);
foreach ($results['attendees'] as $attendee) {
$em->persist($attendee);
}
$em->persist($results['invoice']);
$em->flush();
$count++;
}
}
$em->flush();
}
$this->addFlash('notice', $count . ($count === 1 ? ' Rechnung' : ' Rechnungen') . ' erstellt');
}
return $this->redirectToRoute('course_invoices', ['id' => $course->getId()]);
}
/**
* @Route("/{id}/invoices/merge-pdf", name="course_invoices_merge-pdf", methods="POST", requirements={"id"="\d+"})
*/
public function courseMergePdf(
Request $request,
Course $course,
OrderItemRepository $repo,
PdfService $pdfService
) {
$this->denyAccessUnlessGranted('ROLE_ADMIN', $course);
if ($this->isCsrfTokenValid('create_invoices', $request->request->get('_token'))) {
$em = $this->getDoctrine()->getManager();
$mergeIds = $request->request->get('close');
if (!empty($mergeIds)) {
$mergeInvoices = new ArrayCollection();
foreach ($mergeIds as $id => $value) {
if ($value) {
$orderItem = $repo->find($id);
$order = $orderItem->getOrder();
foreach ($order->getInvoices() as $invoice) {
if (!$mergeInvoices->contains($invoice)) {
$mergeInvoices->add($invoice);
}
}
}
}
$pdf = $pdfService->getMergedInvoicePdf($this->getCurrentClient(), $mergeInvoices->toArray());
$pdf->Output('D', 'Rechnungen_' . date('Y-m-d_H-i') . '.pdf');
die;
} else {
$this->addFlash('notice', 'Keine Rechnungen ausgewählt.');
}
}
return $this->redirectToRoute('course_invoices', ['id' => $course->getId()]);
}
/**
* @Route("/{id}/invoices/close", name="course_close_invoices", methods="POST", requirements={"id"="\d+"})
*/
public function courseCloseInvoices(
Request $request,
Course $course,
InvoiceItemRepository $repo,
ConfigurationService $configService,
MailerService $mailer,
PdfService $pdfService,
EmailHistoryService $emailHistoryService
) {
$this->denyAccessUnlessGranted('ROLE_ADMIN', $course);
if ($this->isCsrfTokenValid('create_invoices', $request->request->get('_token'))) {
$em = $this->getDoctrine()->getManager();
$closeIds = $request->request->get('close');
$count = 0;
if (!empty($closeIds)) {
foreach ($closeIds as $id => $value) {
if ($value) {
$invoiceItem = $repo->findOneBy(['orderItem' => $id]);
$invoice = $invoiceItem->getInvoice();
if ($invoice->getStatus() == Invoice::STATUS_DRAFT) {
$pdf = $pdfService->getInvoicePdf($this->getCurrentClient(), $invoice);
$sentMessage = $mailer->sendInvoiceEmail(
$invoice,
'Rechnung-' . $invoice->getNumber() . '.pdf',
$pdf->Output('S', 'Rechnung-' . $invoice->getNumber() . '.pdf')
);
$outputfile = $this->generateUniqueFileName() . '.pdf';
$outputpath = $this->getParameter('attachment_directory') . '/' . $outputfile;
$pdf->Output('F', $outputpath);
$emailHistoryService->saveProtocolEntryFromInvoiceMessage(
$invoice,
$sentMessage['sender'],
$sentMessage['subject'],
$sentMessage['message'],
$outputfile,
'Rechnung-' . $invoice->getNumber() . '.pdf'
);
if ($invoice->getStatus() != Invoice::STATUS_CLOSED) {
if ($invoice->isPaymentDebit()) {
$invoice->setStatus(Invoice::STATUS_DEBIT_PENDING);
} else {
$invoice->setStatus(Invoice::STATUS_CLOSED);
}
}
$count++;
} else {
// Send invoice again
$pdf = $pdfService->getInvoicePdf($this->getCurrentClient(), $invoice);
$sentMessage = $mailer->sendInvoiceEmail(
$invoice,
'Rechnung-' . $invoice->getNumber() . '.pdf',
$pdf->Output('S', 'Rechnung-' . $invoice->getNumber() . '.pdf')
);
$count++;
}
//Update the order status
$newOrderState = $invoice->getOrder()->setStatus(Order::STATUS_DONE);
$em->persist($newOrderState);
$em->flush();
}
}
}
$this->addFlash('notice', $count . ($count === 1 ? ' Rechnung' : ' Rechnungen') . ' versendet');
}
return $this->redirectToRoute('course_invoices', ['id' => $course->getId()]);
}
/**
* @Route("/{id}/invoices/close-sepa/{all}", name="course_close_sepa-invoices", defaults={"all"="false"},methods="POST", requirements={"id"="\d+"})
*/
public function courseCloseSepaInvoices(
Request $request,
Course $course,
$all = false,
OrderRepository $repo,
OrderItemRepository $itemRepo,
ConfigurationService $configService,
SepaXmlService $sepaXmlService
) {
$this->denyAccessUnlessGranted('ROLE_ADMIN', $course);
if ($this->isCsrfTokenValid('create_invoices', $request->request->get('_token'))) {
$em = $this->getDoctrine()->getManager();
$closeIds = $request->request->get('close');
$invoicesToExport = new ArrayCollection();
if ($all) {
$orderItems = $itemRepo->findByCoursePaged($course);
foreach ($orderItems as $orderItem) {
$order = $orderItem->getOrder();
foreach ($order->getInvoices() as $invoice) {
if (
$invoice->containsCourse($course) &&
!$invoicesToExport->contains($invoice) &&
$invoice->isPaymentDebit()
) {
$invoicesToExport->add($invoice);
$invoice->setStatus(Invoice::STATUS_CLOSED);
if (!$order->getCustomer()->getDebitActive()) {
$order->getCustomer()->setDebitActive(true);
$invoice->setIsNewSepaMandate(true);
}
}
if (!empty($_ENV['SEPAEXPORT_PAYED'])) {
$restsumme = $invoice->getMissingSum();
if ($restsumme != 0) {
$invoicePayment = new InvoicePayment();
$invoicePayment->setInvoice($invoice);
$invoicePayment->setPayedDate(new \DateTime());
$invoicePayment->setSum($invoice->getMissingSum());
$invoice->setPaymentStatus(Invoice::FULLY_PAID);
$invoice->setExportStatus(Invoice::EXPORTED);
$em = $this->getDoctrine()->getManager();
$em->persist($invoicePayment);
}
$invoice->setPaymentStatus(Invoice::FULLY_PAID);
$invoice->setExportStatus(Invoice::EXPORTED);
$em->persist($invoice);
$em->flush();
}
}
}
} elseif (!empty($closeIds)) {
foreach ($closeIds as $id => $value) {
if ($value) {
$orderItem = $itemRepo->find($id);
$order = $orderItem->getOrder();
foreach ($order->getInvoices() as $invoice) {
if (
$invoice->containsCourse($course) &&
!$invoicesToExport->contains($invoice) &&
$invoice->isPaymentDebit()
) {
$invoicesToExport->add($invoice);
$invoice->setStatus(Invoice::STATUS_CLOSED);
if (!$order->getCustomer()->getDebitActive()) {
$order->getCustomer()->setDebitActive(true);
$invoice->setIsNewSepaMandate(true);
}
}
}
if (!empty($_ENV['SEPAEXPORT_PAYED'])) {
$restsumme = $invoice->getMissingSum();
if ($restsumme != 0) {
$invoicePayment = new InvoicePayment();
$invoicePayment->setInvoice($invoice);
$invoicePayment->setPayedDate(new \DateTime());
$invoicePayment->setSum($invoice->getMissingSum());
$invoice->setExportStatus(Invoice::EXPORTED);
$em = $this->getDoctrine()->getManager();
$em->persist($invoicePayment);
}
$invoice->setPaymentStatus(Invoice::FULLY_PAID);
$invoice->setExportStatus(Invoice::EXPORTED);
$em->persist($invoice);
$em->flush();
}
}
}
} else {
$this->addFlash('warning', 'Es wurden keine Rechnungen zum Export ausgewählt.');
return $this->redirectToRoute('course_invoices', ['id' => $course->getId()]);
}
// Check invoices for past due dates
foreach ($invoicesToExport as $invoice) {
if (new \DateTime() > $invoice->getDueDate()) {
$this->addFlash('warning', 'Mindestens eine Rechnung enthält ein Zahlungsziel in der Vergangenheit.');
// return $this->redirectToRoute('course_invoices', ['id' => $course->getId()]);
}
}
if (count($invoicesToExport) > 0) {
$config = $configService->getSepaXmlConfigByClient($this->getCurrentClient());
try {
$xml = $sepaXmlService->getSepaXmlMultiple($this->getCurrentClient(), $config, $invoicesToExport);
} catch (ServiceException $e) {
$this->addFlash('error', $e->getMessage());
return $this->redirectToRoute('invoice_index');
}
$em->flush();
$response = new Response($xml);
$response->headers->set('Content-Type', 'text/xml');
$response->headers->set('Content-disposition', 'attachment; filename="SEPA-' . date('Ymd-His') . '.xml"');
return $response;
}
$this->addFlash('error', 'Mindestens eine Rechnung enthält Fehler.');
return $this->redirectToRoute('course_invoices', ['id' => $course->getId()]);
}
$this->addFlash('error', 'Der Sicherheits-Token ist ungültig. Bitte versuchen Sie es noch einmal.');
return $this->redirectToRoute('course_invoices', ['id' => $course->getId()]);
}
/**
* @Route("/{id}/invoices/close-sepa/", name="course_close_sepa-invoice_selected", methods="POST", requirements={"id"="\d+"})
*/
public function courseCloseSepaInvoiceSelected(
Request $request,
Course $course,
OrderItemRepository $itemRepo,
ConfigurationService $configService,
SepaXmlService $sepaXmlService
) {
$this->denyAccessUnlessGranted('ROLE_ADMIN', $course);
if ($this->isCsrfTokenValid('create_invoices', $request->request->get('_token'))) {
$em = $this->getDoctrine()->getManager();
$closeIds = $request->request->get('close');
$invoicesToExport = new ArrayCollection();
if (!empty($closeIds)) {
foreach ($closeIds as $id => $value) {
if ($value) {
$orderItem = $itemRepo->find($id);
$order = $orderItem->getOrder();
foreach ($order->getInvoices() as $invoice) {
if (
$invoice->containsCourse($course) &&
!$invoicesToExport->contains($invoice) &&
$invoice->isPaymentDebit()
) {
$invoicesToExport->add($invoice);
$invoice->setStatus(Invoice::STATUS_CLOSED);
if (!$order->getCustomer()->getDebitActive()) {
$order->getCustomer()->setDebitActive(true);
$invoice->setIsNewSepaMandate(true);
}
}
}
if (!empty($_ENV['SEPAEXPORT_PAYED'])) {
$restsumme = $invoice->getMissingSum();
if ($restsumme != 0) {
$invoicePayment = new InvoicePayment();
$invoicePayment->setInvoice($invoice);
$invoicePayment->setPayedDate(new \DateTime());
$invoicePayment->setSum($invoice->getMissingSum());
$invoice->setExportStatus(Invoice::EXPORTED);
$em = $this->getDoctrine()->getManager();
$em->persist($invoicePayment);
}
$invoice->setPaymentStatus(Invoice::FULLY_PAID);
$invoice->setExportStatus(Invoice::EXPORTED);
$em->persist($invoice);
$em->flush();
}
}
}
} else {
$this->addFlash('warning', 'Es wurden keine Rechnungen zum Export ausgewählt.');
return $this->redirectToRoute('course_invoices', ['id' => $course->getId()]);
}
// Check invoices for past due dates
foreach ($invoicesToExport as $invoice) {
if (new \DateTime() > $invoice->getDueDate()) {
$this->addFlash('warning', 'Mindestens eine Rechnung enthält ein Zahlungsziel in der Vergangenheit.');
// return $this->redirectToRoute('course_invoices', ['id' => $course->getId()]);
}
}
if (count($invoicesToExport) > 0) {
$config = $configService->getSepaXmlConfigByClient($this->getCurrentClient());
try {
$xml = $sepaXmlService->getSepaXmlMultiple($this->getCurrentClient(), $config, $invoicesToExport);
} catch (ServiceException $e) {
$this->addFlash('error', $e->getMessage());
return $this->redirectToRoute('invoice_index');
}
$em->flush();
$response = new Response($xml);
$response->headers->set('Content-Type', 'text/xml');
$response->headers->set('Content-disposition', 'attachment; filename="SEPA-' . date('Ymd-His') . '.xml"');
return $response;
}
$this->addFlash('error', 'Mindestens eine Rechnung enthält Fehler.');
return $this->redirectToRoute('course_invoices', ['id' => $course->getId()]);
}
$this->addFlash('error', 'Der Sicherheits-Token ist ungültig. Bitte versuchen Sie es noch einmal.');
return $this->redirectToRoute('course_invoices', ['id' => $course->getId()]);
}
/**
* @Route("/{id}/participants", name="course_participants", methods="GET", requirements={"id"="\d+"})
*/
public function courseParticipants(
Request $request,
Course $course,
OrderItemRepository $repo,
OrderService $orderService,
TagsPersonRepository $tagsPersonRepository,
) {
$this->denyAccessUnlessGranted('ROLE_ADMIN', $course);
$orderItems = $repo->findByCoursePaged($course);
/**
* The display logic of subscription courses is different, as there only one order exists per
* customer/participant, but they should appear in every following course occurrence until they cancel.
*/
// if ($course->getCourseNature() === 'CourseSubscription') {
// return $this->render('course/participants-subscription.html.twig', [
// 'course' => $course,
// 'orderItems' => $orderItems->getIterator(),
// ]);
// } else {
$archive = !empty($request->get('archive'));
if ($course->getCourseNature() === 'CourseSubscription') {
foreach ($orderItems as $orderItem) {
foreach ($orderItem->getParticipants() as $participant) {
$participant->isAfterCancelDate = $orderService->isOrderItemOccurrenceAfterCancelDateByParticipant($this->getCurrentClient(), $orderItem, $participant->getId());
$participant->cancelDate = $orderService->getCancelDateForParticipantInCourse($this->getCurrentClient(), $participant);
}
}
}
return $this->render('course/participants.html.twig', [
'tagsPerson' => $tagsPersonRepository->findAll(),
'env' => $_ENV,
'course' => $course,
'orderItems' => $orderItems->getIterator(),
'showCertificatesLink' => !empty($_ENV['CERTIFICATES_ENABLED']),
'archive' => $archive,
]);
// }
}
/**
* @Route("/{id}/participants-pdf/{page}/{orderby}/{order}", name="course_participants_pdf", methods="GET", requirements={"id"="\d+"})
* @IsGranted("ROLE_SPEAKER")
*/
public function courseParticipantsPdf(
Request $request,
CourseOccurrence $courseOccurrence,
OrderItemRepository $repo,
PdfService $pdfService,
OrderService $orderService,
$page = 1,
$orderby = 'customerLastname',
$order = 'asc'
) {
// $this->denyAccessUnlessGranted('client_allowed', $courseOccurrence);
$this->denyAccessUnlessGranted('ROLE_SPEAKER', $courseOccurrence);
$orderItems = $repo->findByCourseOccurrence($courseOccurrence, $orderby, $order);
if ($courseOccurrence->getCourse()->getCourseNature() === 'CourseSubscription') {
foreach ($orderItems as $orderItem) {
foreach ($orderItem->getParticipants() as $participant) {
$participant->isAfterCancelDate = $orderService->isOrderItemOccurrenceAfterCancelDateByParticipant($this->getCurrentClient(), $orderItem, $participant->getId());
}
}
}
$pdf = $pdfService->getParticipantsPdf($this->getCurrentClient(), $courseOccurrence, $orderItems);
$pdf->Output('D', 'Teilnehmerliste-' . $courseOccurrence->getStart()->format('Y-m-d') . '.pdf');
exit();
}
/**
* @Route("/participant/{id}/certificateemail", name="course_participants_certificate_email", methods="GET", requirements={"id"="\d+","downlaod"="\d+"})
*/
public function courseParticipantsCertificateEmail(
Request $request,
ConfigurationService $configService,
\App\Entity\OrderItemPerson $orderItemPerson,
TextblocksRepository $textblocks,
ParameterBagInterface $params,
CourseDataRepository $courseDataRepository,
MailerService $mailer,
\Doctrine\DBAL\Driver\Connection $connection,
EmailHistoryService $emailHistoryService
): Response {
$download = $_GET['download'];
$viewTemplate = $_ENV['CERTIFICATES_TEMPLATE'] ?? 'Default';
$viewFolder = '/data/static/certificates/';
$viewFile = $viewTemplate . '/Certificate.html.twig';
if (file_exists($params->get('kernel.project_dir') . $viewFolder . dirname($viewFile) . '/data.xml')) {
$xml = simplexml_load_file($params->get('kernel.project_dir') . $viewFolder . dirname($viewFile) . '/data.xml', 'SimpleXMLElement', LIBXML_NOCDATA);
$json = json_encode($xml);
$data = json_decode($json, true);
}
try {
$course = $orderItemPerson->getOrderItem()->getCourseOccurrence()->getCourse();
$person = $orderItemPerson->getPerson();
$customer = $orderItemPerson->getOrderItem()->getOrder()->getCustomer();
$courseFields = $courseDataRepository->findBy([
'course' => $course,
'client' => $this->getCurrentClient()
]);
//proof if an certificate isset
$certificateIsset = false;
foreach ($courseFields as $field) {
if (
$field->getCourseField()->getCertificate() &&
!empty($field->getValueText())
) {
$certificateIsset = true;
break;
}
}
$response = $this->render('course/certificates/' . $viewTemplate . '/Certificate.html.twig', [
'data' => $data ?? [],
'folder' => '..' . $viewFolder . dirname($viewFile) . '/',
'person' => $person,
'course' => $course,
'courseFields' => $courseFields,
'certificateIsset' => $certificateIsset,
'occurence' => $orderItemPerson->getOrderItem()->getCourseOccurrence(),
]);
} catch (\Twig\Error\LoaderError $exception) {
$this->addFlash('error', 'Das Zertifikat konnte nicht erstellt werden (Ordner: ' . $viewFolder . ').');
return $this->redirectToRoute('course_participants', ['id' => $orderItemPerson->getOrderItem()->getCourseOccurrence()->getCourse()->getId()]);
} catch (\InvalidArgumentException $exception) {
$this->addFlash('error', 'Das Zertifikat konnte nicht erstellt werden (Template: ' . $viewFile . ').');
return $this->redirectToRoute('course_participants', ['id' => $orderItemPerson->getOrderItem()->getCourseOccurrence()->getCourse()->getId()]);
}
$source = $response->getContent();
// Generate filename
$filename = $course->getNumber() . '-' . $person->getLastname() . '-' . $person->getFirstname();
$filename = mb_strtolower($filename);
$tr = [
'ä' => 'ae',
'ü' => 'ue',
'ö' => 'oe',
'ß' => 'ss',
];
$filename = strtr($filename, $tr);
$filename = preg_replace('#\s#', '-', $filename);
$filename = preg_replace('#[^a-z0-9\-]#', '', $filename);
$filename = preg_replace('#[\-]{2,}#i', '-', $filename);
$filename .= '.pdf';
// Generate pdf
$html2pdf = new \Spipu\Html2Pdf\Html2Pdf('P', 'A4', 'de', true, 'UTF-8', [25, 10, 0, 0]);
$html2pdf->setDefaultFont('DejaVuSans');
$html2pdf->writeHTML($source);
if ($download == 'ja') {
$html2pdf->output($filename, 'D');
}
$outputpath = $this->getParameter('attachment_directory') . '/' . $filename;
if ($download == 'nein') {
$html2pdf->output($outputpath, 'F');
}
$client = $this->getCurrentClient();
$sender = $client->getEmail();
$recipient = $person->getContactEmail() ? $person->getContactEmail() : $orderItemPerson->getEmail();
// Fetch subject und message from textblocks
$sql = 'SELECT * FROM textblocks WHERE position = "certificate" ';
$result = $connection->fetchAll($sql);
$subject = 'Ihr Zertifikat wurde erstellt';
$message = 'Lieber Teilnehmer<p>Für Ihren Kurs wurde das Zertifikat erstellt.</p><p>Ihr Team';
$historyTitle = 'Zertifikat erstellt';
if ($download == 'nein') {
foreach ($result as $value) {
if ($value['position'] = 'cetrificate') {
$subject = $value['subject'];
$message = $value['message'];
}
}
$mailer->sendCertificateMessage(
$client,
$sender,
$subject . ' ' . $course->getTitle(),
$message . '<p>Anhang: ' . $filename,
$recipient,
$html2pdf->output($filename, 'S'),
$filename
);
}
//$client, $recipient, $sender, $subject, $message, $filename
$emailHistoryService->saveProtocolEntriesFromEmailCertificate(
$this->getCurrentClient(),
$recipient,
$sender,
$subject . ' ' . $course->getTitle(),
$message,
$filename,
$historyTitle,
$customer,
$download
);
if ($download == 'nein') {
$this->addFlash('notice', 'Das Zertifikat wurde an ' . $recipient . ' versendet. ');
}
if ($download == 'ja') {
$this->addFlash('notice', 'Das Zertifikat wurde erstellt. ');
}
return $this->redirectToRoute('course_participants', ['id' => $course->getId()]);
}
/**
* @Route("/{id}/reservations", name="course_reservations", methods="GET", requirements={"id"="\d+"})
*/
public function courseReservations(
Request $request,
Course $course,
WaitItemRepository $repo,
TagsPersonRepository $tagsPersonRepository,
) {
$this->denyAccessUnlessGranted('ROLE_ADMIN', $course);
$waitItems = $repo->findByCoursePaged($course);
return $this->render('course/reservations.html.twig', [
'course' => $course,
'waitItems' => $waitItems->getIterator(),
'tagsPerson' => $tagsPersonRepository->findAll(),
]);
}
/**
* @Route("/{id}/reservations/move", name="course_reservations_move", methods="POST", requirements={"id"="\d+"})
*/
public function moveCourseReservations(
Request $request,
Course $course,
WaitItemRepository $repo
): Response {
$this->denyAccessUnlessGranted('ROLE_ADMIN', $course);
$em = $this->getDoctrine()->getManager();
$moveIds = $request->request->get('item');
foreach ($moveIds as $id => $value) {
if ($value) {
$waitItem = $repo->find($value);
$orderItem = OrderItem::createFromWaitItem($waitItem);
$participants = $waitItem->getParticipants();
foreach ($participants as $participant) {
if ($participant->getPerson()->getId() === $id) {
$participant->setWaitItem(null);
$participant->setOrderItem($orderItem);
$orderItem->setQuantity($orderItem->getQuantity() + 1);
$waitItem->setQuantity($waitItem->getQuantity() - 1);
break;
}
}
$waitItem->getCourseOccurrence()->bookSlots($orderItem->getQuantity());
$order = $waitItem->getOrder();
$order->addOrderItem($orderItem);
if ($waitItem->getQuantity() === 0) {
$order->removeWaitItem($waitItem);
}
$em->persist($order);
}
}
$this->addFlash('notice', count($moveIds) . (count($moveIds) > 1 ? ' Wartelistenplätze verschoben' : ' Wartelistenplatz verschoben'));
$em->flush();
return $this->redirectToRoute('course_reservations', ['id' => $course->getId()]);
}
private function generateUniqueFileName()
{
return md5(uniqid());
}
private function createDescription($field, $option)
{
switch ($option) {
case 'course':
if (!empty($field['certificate'])) {
$field['name'] = $this->generateHTMLForDescription(
$field['name'],
'für den Kurs und das Zertifikat'
);
} else {
$field['name'] = $this->generateHTMLForDescription(
$field['name'],
'für den Kurs'
);
}
break;
case 'certificate':
$field['name'] = $this->generateHTMLForDescription(
$field['name'],
'für das Zertifikat'
);
break;
default:
break;
}
return $field;
}
private function generateHTMLForDescription($name, $text)
{
return '<strong>' . $name . '</strong>' . '<span style="font-size: 0.7rem"> (' . $text . ')</span>';
}
}