Merge branch 'master' into feature/multiple-hotelcodes

This commit is contained in:
Björn Fromme
2025-01-08 13:12:34 +01:00
4 changed files with 158 additions and 1 deletions
@@ -0,0 +1,117 @@
<?php
namespace App\Controller\Administrative\Disposition;
use App\Entity\Disposition;
use App\Entity\Upload;
use App\Event\DocumentUploadedEvent;
use App\Htmx\HxRedirectResponse;
use App\Service\Upload\UploadHandler;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\DependencyInjection\Attribute\Target;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
use Symfony\Component\Workflow\WorkflowInterface;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
class DocumentUploadController extends AbstractController
{
public function __construct(
private readonly UploadHandler $uploadHandler,
private readonly EntityManagerInterface $entityManager,
#[Target('disposition')]
private readonly WorkflowInterface $workflow,
private readonly EventDispatcherInterface $eventDispatcher,
private readonly LoggerInterface $logger,
) {
}
#[Route(
path: '/administrative/disposition/document-upload/{uuid}',
name: 'app_administrative_disposition_document_upload'
)]
#[IsGranted('ROLE_ADMINISTRATIVE')]
public function index(Disposition $disposition, Request $request): Response
{
if (true === $this->workflow->can($disposition, 'upload_contract')) {
$uploadType = Upload::TYPE_CONTRACT;
$uploadTypeLabel = 'Honorarvertrag';
$uploaderRoute = '_uploader_upload_contract';
} elseif (true === $this->workflow->can($disposition, 'upload_invoice')) {
$uploadType = Upload::TYPE_INVOICE;
$uploadTypeLabel = 'Honoranote';
$uploaderRoute = '_uploader_upload_invoice';
} else {
$this->addFlash('error', 'Kein Upload möglich beim aktuellen Status der Einteilung');
return $this->getRedirectResponse($disposition);
}
$teamer = $disposition->getTeamer();
$modalTitle = sprintf('%s für %s hochladen', $uploadTypeLabel, $teamer);
$form = $this->createFormBuilder()->getForm();
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$this->processUpload($disposition, $uploadType);
$this->addFlash('success', sprintf('%s wurde hochgeladen', $uploadTypeLabel));
return $this->getRedirectResponse($disposition);
}
return $this->render('administrative/disposition/modal_document_upload.html.twig', [
'disposition' => $disposition,
'modalTitle' => $modalTitle,
'uploaderRoute' => $uploaderRoute,
'form' => $form->createView(),
]);
}
private function processUpload(Disposition $disposition, string $uploadType): void
{
if (null !== $existingUpload = $disposition->getDocumentByType($uploadType)) {
$this->entityManager->remove($existingUpload);
}
$teamer = $disposition->getTeamer();
$user = $teamer->getUser();
$uploadSession = $this->uploadHandler->getUploadSession();
$upload = Upload::fromUploadDto($uploadSession->getUploads()->first(), $user, $uploadType);
$upload->setStatus(Upload::STATUS_NEW);
$disposition->addDocument($upload);
$transitionName = sprintf('upload_%s', $uploadType);
$this->workflow->apply($disposition, $transitionName);
$this->entityManager->flush();
$this->uploadHandler->moveUploadSessionFilesFromOrphanage($uploadType, $uploadSession);
$this->uploadHandler->destroyUploadSession();
$this->eventDispatcher->dispatch(new DocumentUploadedEvent($upload), DocumentUploadedEvent::NAME);
$this->logger->info('Document upload for teamer', [
'teamer_id' => $teamer->getId(),
'teamer_name' => $teamer->getFullName(),
'disposition_id' => $disposition->getId(),
'document_id' => $upload->getId(),
'upload_type' => $uploadType,
]);
}
private function getRedirectResponse(Disposition $disposition): HxRedirectResponse
{
return new HxRedirectResponse($this->generateUrl('app_administrative_assignment_detail', [
'uuid' => $disposition->getAssignment()->getUuid(),
]));
}
}
@@ -4,13 +4,14 @@ namespace App\EventListener;
use App\Entity\Disposition;
use App\Entity\Upload;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Workflow\Event\GuardEvent;
use Symfony\Contracts\Translation\TranslatorInterface;
class DispositionWorkflowGuardSubscriber implements EventSubscriberInterface
{
public function __construct(private readonly TranslatorInterface $translator)
public function __construct(private readonly TranslatorInterface $translator, private readonly Security $security)
{}
public static function getSubscribedEvents(): array
@@ -40,6 +41,11 @@ class DispositionWorkflowGuardSubscriber implements EventSubscriberInterface
public function guardUploadContract(GuardEvent $event): void
{
// no checks for administrative users
if ($this->security->isGranted('ROLE_ADMINISTRATIVE')) {
return;
}
/** @var Disposition $disposition */
$disposition = $event->getSubject();
$assignment = $disposition->getAssignment();
@@ -79,6 +85,11 @@ class DispositionWorkflowGuardSubscriber implements EventSubscriberInterface
$event->setBlocked(true, $message);
}
// no further checks for administrative users
if ($this->security->isGranted('ROLE_ADMINISTRATIVE')) {
return;
}
$earliestDate = $destination->getDateTo();
if ($earliestDate > $today) {
@@ -179,6 +179,15 @@
</td>
<td>
<div class="flex items-center justify-end space-x-2">
{% if workflow_can(disposition, 'upload_contract') or workflow_can(disposition, 'upload_invoice') %}
<button type="button"
title="Dokumentenupload"
hx-get="{{ path('app_administrative_disposition_document_upload', { 'uuid': disposition.uuid }) }}"
hx-target="body"
hx-swap="beforeend">
{{ icon('upload') }}
</button>
{% endif %}
{% if is_granted('DELETE', disposition) %}
<button type="button"
class="text-red-500"
@@ -0,0 +1,20 @@
{% extends 'htmx_modal.html.twig' %}
{% block title %}{{ modalTitle }}{% endblock %}
{% block content %}
{{ form_start(form, { 'attr': { 'hx-post': app.request.uri, 'hx-target': '#htmx-modal', 'hx-swap': 'outerHTML' }}) }}
<div class="pb-4">
{% include '_partials/_upload_collection_form.html.twig' with {
'endpoint_upload': path(uploaderRoute),
'max_filesize': 5,
'max_files': 1,
'accepted_files': 'image/jpg,image/jpeg,application/pdf',
} %}
</div>
<button type="submit" class="btn">
Upload speichern
</button>
{{ form_rest(form) }}
{{ form_end(form) }}
{% endblock %}