From 6df725edc40211a421cd9f90efa4ecdb267141a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Fromme?= Date: Thu, 12 Oct 2023 19:50:08 +0200 Subject: [PATCH] WIP: Implement teamer document upload handling --- composer.json | 1 + composer.lock | 87 +++++++++- config/packages/oneup_uploader.yaml | 6 + config/packages/workflow.yaml | 33 ++++ migrations/Version20231012164550.php | 45 +++++ .../Admin/Document/IndexController.php | 7 +- .../Document/DeleteController.php | 6 +- .../Admin/System/Document/IndexController.php | 41 +++++ .../Document/RenameController.php | 10 +- .../Document/ReplaceController.php | 10 +- .../Document/UploadController.php | 10 +- ...ntroller.php => ContractPdfController.php} | 4 +- .../Teamer/Disposition/DetailController.php | 65 +++++-- src/Entity/Disposition.php | 54 ++++-- src/Entity/Upload.php | 19 ++- src/Menu/AdminMenuBuilder.php | 4 + src/Repository/UploadRepository.php | 15 ++ symfony.lock | 12 ++ templates/admin/document/index.html.twig | 158 ++++++++---------- .../admin/system/document/index.html.twig | 99 +++++++++++ .../{ => system}/document/rename.html.twig | 0 .../{ => system}/document/replace.html.twig | 0 .../{ => system}/document/upload.html.twig | 0 templates/teamer/disposition/detail.html.twig | 88 +++++++--- translations/messages.de.yaml | 2 +- 25 files changed, 611 insertions(+), 165 deletions(-) create mode 100644 config/packages/workflow.yaml create mode 100644 migrations/Version20231012164550.php rename src/Controller/Admin/{ => System}/Document/DeleteController.php (81%) create mode 100644 src/Controller/Admin/System/Document/IndexController.php rename src/Controller/Admin/{ => System}/Document/RenameController.php (80%) rename src/Controller/Admin/{ => System}/Document/ReplaceController.php (84%) rename src/Controller/Admin/{ => System}/Document/UploadController.php (86%) rename src/Controller/Teamer/Disposition/{ContractController.php => ContractPdfController.php} (93%) create mode 100644 templates/admin/system/document/index.html.twig rename templates/admin/{ => system}/document/rename.html.twig (100%) rename templates/admin/{ => system}/document/replace.html.twig (100%) rename templates/admin/{ => system}/document/upload.html.twig (100%) diff --git a/composer.json b/composer.json index b1dc0fd..3b9414d 100644 --- a/composer.json +++ b/composer.json @@ -55,6 +55,7 @@ "symfony/validator": "6.3.*", "symfony/web-link": "6.3.*", "symfony/webpack-encore-bundle": "^2.0", + "symfony/workflow": "6.3.*", "symfony/yaml": "6.3.*", "twig/extra-bundle": "^2.12|^3.0", "twig/html-extra": "^3.7", diff --git a/composer.lock b/composer.lock index 797fe09..0c34105 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "d714db6b49b6684caaf4544a90193da3", + "content-hash": "51c0d10a5e4bd074ddab5fe5717af560", "packages": [ { "name": "doctrine/cache", @@ -8116,6 +8116,91 @@ ], "time": "2023-09-26T14:41:29+00:00" }, + { + "name": "symfony/workflow", + "version": "v6.3.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/workflow.git", + "reference": "a2a506b81556dea75a18f0d4a34462ff633e3522" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/workflow/zipball/a2a506b81556dea75a18f0d4a34462ff633e3522", + "reference": "a2a506b81556dea75a18f0d4a34462ff633e3522", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/event-dispatcher": "<5.4" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/event-dispatcher": "^5.4|^6.0", + "symfony/expression-language": "^5.4|^6.0", + "symfony/security-core": "^5.4|^6.0", + "symfony/validator": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Workflow\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Grégoire Pineau", + "email": "lyrixx@lyrixx.info" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools for managing a workflow or finite state machine", + "homepage": "https://symfony.com", + "keywords": [ + "petrinet", + "place", + "state", + "statemachine", + "transition", + "workflow" + ], + "support": { + "source": "https://github.com/symfony/workflow/tree/v6.3.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-08-01T15:55:06+00:00" + }, { "name": "symfony/yaml", "version": "v6.3.3", diff --git a/config/packages/oneup_uploader.yaml b/config/packages/oneup_uploader.yaml index d3ad07b..d6ab107 100644 --- a/config/packages/oneup_uploader.yaml +++ b/config/packages/oneup_uploader.yaml @@ -18,6 +18,12 @@ oneup_uploader: namer: app.upload_namer storage: directory: '%kernel.project_dir%/uploads/contract' + invoice: + frontend: dropzone + use_orphanage: true + namer: app.upload_namer + storage: + directory: '%kernel.project_dir%/uploads/invoice' document: frontend: dropzone use_orphanage: true diff --git a/config/packages/workflow.yaml b/config/packages/workflow.yaml new file mode 100644 index 0000000..01eaa51 --- /dev/null +++ b/config/packages/workflow.yaml @@ -0,0 +1,33 @@ +framework: + workflows: + disposition: + type: state_machine + marking_store: + type: method + property: status + supports: + - App\Entity\Disposition + initial_marking: new + places: + - new + - checking_contract + - confirmed + - checking_invoice + - paid + - completed + transitions: + upload_contract: + from: new + to: checking_contract + confirm_contract: + from: checking_contract + to: confirmed + upload_invoice: + from: confirmed + to: checking_invoice + confirm_invoice: + from: checking_invoice + to: paid + complete: + from: confirmed + to: completed diff --git a/migrations/Version20231012164550.php b/migrations/Version20231012164550.php new file mode 100644 index 0000000..bcd8708 --- /dev/null +++ b/migrations/Version20231012164550.php @@ -0,0 +1,45 @@ +addSql('ALTER TABLE disposition DROP FOREIGN KEY FK_4C58BF60EA295441'); + $this->addSql('ALTER TABLE disposition DROP FOREIGN KEY FK_4C58BF606D5F2C5F'); + $this->addSql('DROP INDEX UNIQ_4C58BF606D5F2C5F ON disposition'); + $this->addSql('DROP INDEX UNIQ_4C58BF60EA295441 ON disposition'); + $this->addSql('ALTER TABLE disposition DROP contract_pdf_id, DROP invoice_pdf_id'); + $this->addSql('ALTER TABLE upload ADD disposition_id INT DEFAULT NULL'); + $this->addSql('ALTER TABLE upload ADD CONSTRAINT FK_17BDE61F287B65ED FOREIGN KEY (disposition_id) REFERENCES disposition (id)'); + $this->addSql('CREATE INDEX IDX_17BDE61F287B65ED ON upload (disposition_id)'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE disposition ADD contract_pdf_id INT DEFAULT NULL, ADD invoice_pdf_id INT DEFAULT NULL'); + $this->addSql('ALTER TABLE disposition ADD CONSTRAINT FK_4C58BF60EA295441 FOREIGN KEY (invoice_pdf_id) REFERENCES upload (id)'); + $this->addSql('ALTER TABLE disposition ADD CONSTRAINT FK_4C58BF606D5F2C5F FOREIGN KEY (contract_pdf_id) REFERENCES upload (id)'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_4C58BF606D5F2C5F ON disposition (contract_pdf_id)'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_4C58BF60EA295441 ON disposition (invoice_pdf_id)'); + $this->addSql('ALTER TABLE upload DROP FOREIGN KEY FK_17BDE61F287B65ED'); + $this->addSql('DROP INDEX IDX_17BDE61F287B65ED ON upload'); + $this->addSql('ALTER TABLE upload DROP disposition_id'); + } +} diff --git a/src/Controller/Admin/Document/IndexController.php b/src/Controller/Admin/Document/IndexController.php index 64a586e..a7f8048 100644 --- a/src/Controller/Admin/Document/IndexController.php +++ b/src/Controller/Admin/Document/IndexController.php @@ -22,14 +22,17 @@ class IndexController extends AbstractController #[IsGranted('ROLE_ADMINISTRATIVE')] public function index(Request $request): Response { - $query = $this->uploadRepository->getDocumentListQuery(); + $query = $this + ->uploadRepository + ->getUploadListQuery() + ; $pagination = $this->paginator->paginate( $query, $request->query->getInt('page', 1), 10, [ - 'defaultSortFieldName' => 'upload.originalFilename', + 'defaultSortFieldName' => 'upload.createdAt', 'defaultSortDirection' => 'asc', ] ); diff --git a/src/Controller/Admin/Document/DeleteController.php b/src/Controller/Admin/System/Document/DeleteController.php similarity index 81% rename from src/Controller/Admin/Document/DeleteController.php rename to src/Controller/Admin/System/Document/DeleteController.php index 545185a..a98eb41 100644 --- a/src/Controller/Admin/Document/DeleteController.php +++ b/src/Controller/Admin/System/Document/DeleteController.php @@ -1,6 +1,6 @@ $document->getOriginalFilename(), ]); - return $this->redirectToRoute('app_admin_document_index'); + return $this->redirectToRoute('app_admin_system_document_index'); } } \ No newline at end of file diff --git a/src/Controller/Admin/System/Document/IndexController.php b/src/Controller/Admin/System/Document/IndexController.php new file mode 100644 index 0000000..c48fea0 --- /dev/null +++ b/src/Controller/Admin/System/Document/IndexController.php @@ -0,0 +1,41 @@ +uploadRepository->getDocumentListQuery(); + + $pagination = $this->paginator->paginate( + $query, + $request->query->getInt('page', 1), + 10, + [ + 'defaultSortFieldName' => 'upload.originalFilename', + 'defaultSortDirection' => 'asc', + ] + ); + + return $this->render('admin/system/document/index.html.twig', [ + 'pagination' => $pagination, + ]); + } +} \ No newline at end of file diff --git a/src/Controller/Admin/Document/RenameController.php b/src/Controller/Admin/System/Document/RenameController.php similarity index 80% rename from src/Controller/Admin/Document/RenameController.php rename to src/Controller/Admin/System/Document/RenameController.php index 149b953..13b0b90 100644 --- a/src/Controller/Admin/Document/RenameController.php +++ b/src/Controller/Admin/System/Document/RenameController.php @@ -1,6 +1,6 @@ generateUrl('app_admin_document_rename', ['uuid' => $upload->getUuid()]); + $formAction = $this->generateUrl('app_admin_system_document_rename', ['uuid' => $upload->getUuid()]); $form = $this ->createFormBuilder($upload, ['action' => $formAction, 'ajax_submit' => true]) @@ -42,9 +42,9 @@ class RenameController extends AbstractController $this->addFlash('success', 'Das Dokument wurden umbenannt'); $this->logger->info('Rename document'); - $response->setCloseAndRedirect($this->generateUrl('app_admin_document_index')); + $response->setCloseAndRedirect($this->generateUrl('app_admin_system_document_index')); } else { - $response->setContent($this->renderView('admin/document/rename.html.twig', [ + $response->setContent($this->renderView('admin/system/document/rename.html.twig', [ 'form' => $form, ])); } diff --git a/src/Controller/Admin/Document/ReplaceController.php b/src/Controller/Admin/System/Document/ReplaceController.php similarity index 84% rename from src/Controller/Admin/Document/ReplaceController.php rename to src/Controller/Admin/System/Document/ReplaceController.php index a39dd87..2ec8116 100644 --- a/src/Controller/Admin/Document/ReplaceController.php +++ b/src/Controller/Admin/System/Document/ReplaceController.php @@ -1,6 +1,6 @@ generateUrl('app_admin_document_replace', ['uuid' => $upload->getUuid()]); + $formAction = $this->generateUrl('app_admin_system_document_replace', ['uuid' => $upload->getUuid()]); $form = $this ->createFormBuilder(null, ['action' => $formAction, 'ajax_submit' => true]) @@ -51,9 +51,9 @@ class ReplaceController extends AbstractController $this->addFlash('success', 'Das Dokument wurden ersetzt'); $this->logger->info('Replace document'); - $response->setCloseAndRedirect($this->generateUrl('app_admin_document_index')); + $response->setCloseAndRedirect($this->generateUrl('app_admin_system_document_index')); } else { - $response->setContent($this->renderView('admin/document/replace.html.twig', [ + $response->setContent($this->renderView('admin/system/document/replace.html.twig', [ 'form' => $form, ])); } diff --git a/src/Controller/Admin/Document/UploadController.php b/src/Controller/Admin/System/Document/UploadController.php similarity index 86% rename from src/Controller/Admin/Document/UploadController.php rename to src/Controller/Admin/System/Document/UploadController.php index 0fee9e7..4904ea2 100644 --- a/src/Controller/Admin/Document/UploadController.php +++ b/src/Controller/Admin/System/Document/UploadController.php @@ -1,6 +1,6 @@ generateUrl('app_admin_document_upload'); + $formAction = $this->generateUrl('app_admin_system_document_upload'); $form = $this ->createFormBuilder(null, ['action' => $formAction, 'ajax_submit' => true]) @@ -54,9 +54,9 @@ class UploadController extends AbstractController $this->addFlash('success', 'Das/die Dokument/e wurden hochgeladen'); $this->logger->info('Upload document'); - $response->setCloseAndRedirect($this->generateUrl('app_admin_document_index')); + $response->setCloseAndRedirect($this->generateUrl('app_admin_system_document_index')); } else { - $response->setContent($this->renderView('admin/document/upload.html.twig', [ + $response->setContent($this->renderView('admin/system/document/upload.html.twig', [ 'form' => $form, ])); } diff --git a/src/Controller/Teamer/Disposition/ContractController.php b/src/Controller/Teamer/Disposition/ContractPdfController.php similarity index 93% rename from src/Controller/Teamer/Disposition/ContractController.php rename to src/Controller/Teamer/Disposition/ContractPdfController.php index 4ba1db2..db3c84c 100644 --- a/src/Controller/Teamer/Disposition/ContractController.php +++ b/src/Controller/Teamer/Disposition/ContractPdfController.php @@ -10,12 +10,12 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Security\Http\Attribute\IsGranted; -class ContractController extends AbstractController +class ContractPdfController extends AbstractController { public function __construct(private readonly ContractRenderer $renderer) {} - #[Route('/teamer/disposition/contract/{uuid}', name: 'app_teamer_disposition_contract')] + #[Route('/teamer/disposition/contract/{uuid}', name: 'app_teamer_disposition_contractpdf')] #[IsGranted('CONTRACT', subject: 'disposition')] public function index(Disposition $disposition): Response { diff --git a/src/Controller/Teamer/Disposition/DetailController.php b/src/Controller/Teamer/Disposition/DetailController.php index c0fad1e..40df4ca 100644 --- a/src/Controller/Teamer/Disposition/DetailController.php +++ b/src/Controller/Teamer/Disposition/DetailController.php @@ -15,6 +15,7 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Security\Http\Attribute\IsGranted; +use Symfony\Component\Workflow\WorkflowInterface; class DetailController extends AbstractController { @@ -23,6 +24,7 @@ class DetailController extends AbstractController public function __construct( private readonly UploadHandler $uploadHandler, private readonly EntityManagerInterface $entityManager, + private readonly WorkflowInterface $dispositionStateMachine, private readonly LoggerInterface $logger ) { } @@ -31,16 +33,22 @@ class DetailController extends AbstractController #[IsGranted('VIEW', subject: 'disposition')] public function index(Disposition $disposition, Request $request): Response { - // Handle upload independently from form submission to avoid issues with failing validation - $uploadSession = $this->uploadHandler->getUploadSession(); - if (true === $request->isMethod('POST') && 0 < $uploadSession->getCount()) { - $this->updateContract($disposition, $uploadSession); - } - + // Create dummy form without fields to generate POST requests $form = $this->createFormBuilder()->getForm(); $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { + $uploadSession = $this->uploadHandler->getUploadSession(); + + if (0 < $uploadSession->getCount()) { + if (true === $this->dispositionStateMachine->can($disposition, 'upload_contract')) { + $this->uploadContract($disposition, $uploadSession); + } + if (true === $this->dispositionStateMachine->can($disposition, 'upload_invoice')) { + $this->uploadInvoice($disposition, $uploadSession); + } + } + return $this->redirectToRoute('app_teamer_disposition_detail', [ 'uuid' => $disposition->getUuid(), ]); @@ -48,28 +56,61 @@ class DetailController extends AbstractController return $this->render('teamer/disposition/detail.html.twig', [ 'disposition' => $disposition, - 'returnUrl' => $this->getReturnUrl($request, 'app_teamer_index'), 'form' => $form, + 'returnUrl' => $this->getReturnUrl($request, 'app_teamer_index'), ]); } - - private function updateContract(Disposition $disposition, UploadSessionDto $uploadSession): void + private function uploadContract(Disposition $disposition, UploadSessionDto $uploadSession): void { - if (null !== $existingUpload = $disposition->getContractPdf()) { + if (null !== $existingUpload = $disposition->getDocumentByType(Upload::TYPE_CONTRACT)) { $this->entityManager->remove($existingUpload); } /** @var User $user */ $user = $this->getUser(); + $upload = Upload::fromUploadDto($uploadSession->getUploads()->first(), $user, Upload::TYPE_CONTRACT); - $disposition->setContractPdf($upload); + $upload->setStatus(Upload::STATUS_NEW); + + $disposition->addDocument($upload); + $this->dispositionStateMachine->apply($disposition, 'upload_contract'); + $this->entityManager->flush(); + $this->uploadHandler->moveUploadSessionFilesFromOrphanage(Upload::TYPE_CONTRACT, $uploadSession); $this->uploadHandler->destroyUploadSession(); $this->addFlash('success', 'Der Honorarvertrag wurde hochgeladen'); - $this->logger->info('Update contract', [ + + $this->logger->info('Upload contract', [ + 'user' => $user->getUserIdentifier(), + ]); + } + + private function uploadInvoice(Disposition $disposition, UploadSessionDto $uploadSession): void + { + if (null !== $existingUpload = $disposition->getDocumentByType(Upload::TYPE_INVOICE)) { + $this->entityManager->remove($existingUpload); + } + + /** @var User $user */ + $user = $this->getUser(); + + $upload = Upload::fromUploadDto($uploadSession->getUploads()->first(), $user, Upload::TYPE_INVOICE); + $upload->setStatus(Upload::STATUS_NEW); + + $disposition->addDocument($upload); + $this->dispositionStateMachine->apply($disposition, 'upload_invoice'); + + $this->entityManager->flush(); + + $this->uploadHandler->moveUploadSessionFilesFromOrphanage(Upload::TYPE_INVOICE, $uploadSession); + $this->uploadHandler->destroyUploadSession(); + + $this->addFlash('success', 'Die Honornote wurde hochgeladen'); + + $this->logger->info('Upload invoice', [ 'user' => $user->getUserIdentifier(), ]); } diff --git a/src/Entity/Disposition.php b/src/Entity/Disposition.php index e1079f1..de56ed0 100644 --- a/src/Entity/Disposition.php +++ b/src/Entity/Disposition.php @@ -5,6 +5,8 @@ namespace App\Entity; use App\Entity\Traits\BlameableEntity; use App\Entity\Traits\TimestampableEntity; use App\Repository\DispositionRepository; +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Collections\Collection; use Doctrine\DBAL\Types\Types; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Uid\Uuid; @@ -16,8 +18,11 @@ class Disposition implements BlameableEntityInterface, TimestampableEntityInterf use TimestampableEntity; public const STATUS_NEW = 'new'; - public const STATUS_PENDING = 'pending'; + public const STATUS_CHECKING_CONTRACT = 'checking_contract'; public const STATUS_CONFIRMED = 'confirmed'; + public const STATUS_CHECKING_INVOICE = 'checking_invoice'; + public const STATUS_PAID = 'paid'; + public const STATUS_COMPLETED = 'completed'; #[ORM\Id] #[ORM\GeneratedValue] @@ -39,11 +44,8 @@ class Disposition implements BlameableEntityInterface, TimestampableEntityInterf #[ORM\Column(type: Types::TEXT, nullable: true)] private ?string $remarks; - #[ORM\OneToOne(cascade: ['persist', 'remove'])] - private ?Upload $contractPdf = null; - - #[ORM\OneToOne(cascade: ['persist', 'remove'])] - private ?Upload $invoicePdf = null; + #[ORM\OneToMany(mappedBy: 'disposition', targetEntity: Upload::class, cascade: ['persist', 'remove'])] + private Collection $documents; public function __construct(Application $application) { @@ -52,6 +54,7 @@ class Disposition implements BlameableEntityInterface, TimestampableEntityInterf $this->assignment = $application->getAssignment(); $this->teamer = $application->getTeamer(); $this->remarks = $application->getRemarks(); + $this->documents = new ArrayCollection(); } public function getId(): ?int @@ -112,26 +115,43 @@ class Disposition implements BlameableEntityInterface, TimestampableEntityInterf return $this; } - public function getContractPdf(): ?Upload + /** + * @return Collection + */ + public function getDocuments(): Collection { - return $this->contractPdf; + return $this->documents; } - public function setContractPdf(?Upload $contractPdf): static + public function getDocumentByType(string $type): ?Upload { - $this->contractPdf = $contractPdf; + foreach ($this->getDocuments() as $upload) { + if ($type === $upload->getType()) { + return $upload; + } + } + + return null; + } + + public function addDocument(Upload $document): static + { + if (!$this->documents->contains($document)) { + $this->documents->add($document); + $document->setDisposition($this); + } return $this; } - public function getInvoicePdf(): ?Upload + public function removeDocument(Upload $document): static { - return $this->invoicePdf; - } - - public function setInvoicePdf(?Upload $invoicePdf): static - { - $this->invoicePdf = $invoicePdf; + if ($this->documents->removeElement($document)) { + // set the owning side to null (unless already changed) + if ($document->getDisposition() === $this) { + $document->setDisposition(null); + } + } return $this; } diff --git a/src/Entity/Upload.php b/src/Entity/Upload.php index 42e5dac..c5814b7 100644 --- a/src/Entity/Upload.php +++ b/src/Entity/Upload.php @@ -22,9 +22,9 @@ class Upload implements BlameableEntityInterface, TimestampableEntityInterface public const TYPE_DOCUMENT = 'document'; public const STATUS_NEW = 'new'; - public const STATUS_IN_PROCESS = 'in_process'; + public const CHECKING = 'checking'; public const STATUS_CHECKED = 'checked'; - public const STATUS_PAYED = 'payed'; + public const STATUS_PAID = 'paid'; public const STATUS_ERROR = 'error'; #[ORM\Id] @@ -59,6 +59,9 @@ class Upload implements BlameableEntityInterface, TimestampableEntityInterface #[ORM\ManyToOne] private ?User $owner = null; + #[ORM\ManyToOne(inversedBy: 'documents')] + private ?Disposition $disposition = null; + public function __construct() { $this->uuid = Uuid::v4(); @@ -184,4 +187,16 @@ class Upload implements BlameableEntityInterface, TimestampableEntityInterface return $this; } + + public function getDisposition(): ?Disposition + { + return $this->disposition; + } + + public function setDisposition(?Disposition $disposition): static + { + $this->disposition = $disposition; + + return $this; + } } diff --git a/src/Menu/AdminMenuBuilder.php b/src/Menu/AdminMenuBuilder.php index 2365f02..5f37c0a 100644 --- a/src/Menu/AdminMenuBuilder.php +++ b/src/Menu/AdminMenuBuilder.php @@ -83,6 +83,10 @@ class AdminMenuBuilder extends AbstractMenuBuilder ], ], ], + [ + 'route' => 'app_admin_system_document_index', + 'title' => 'Dokumente', + ], ], ], ]; diff --git a/src/Repository/UploadRepository.php b/src/Repository/UploadRepository.php index 7842726..eafba3c 100644 --- a/src/Repository/UploadRepository.php +++ b/src/Repository/UploadRepository.php @@ -68,4 +68,19 @@ class UploadRepository extends ServiceEntityRepository return $data; } + + public function getUploadListQuery(): Query + { + $qb = $this->createQueryBuilder('upload'); + + return $qb + ->select('upload', 'disposition', 'assignment', 'job_profile', 'destination', 'teamer') + ->innerJoin('upload.disposition', 'disposition') + ->innerJoin('disposition.assignment', 'assignment') + ->innerJoin('assignment.jobProfile', 'job_profile') + ->innerJoin('assignment.destination', 'destination') + ->innerJoin('disposition.teamer', 'teamer') + ->getQuery() + ; + } } diff --git a/symfony.lock b/symfony.lock index 7009ce6..d4e29f3 100644 --- a/symfony.lock +++ b/symfony.lock @@ -341,6 +341,18 @@ "webpack.config.js" ] }, + "symfony/workflow": { + "version": "6.3", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "3.3", + "ref": "3b2f8ca32a07fcb00f899649053943fa3d8bbfb6" + }, + "files": [ + "config/packages/workflow.yaml" + ] + }, "twig/extra-bundle": { "version": "v3.6.1" } diff --git a/templates/admin/document/index.html.twig b/templates/admin/document/index.html.twig index dbdab1a..137ace0 100644 --- a/templates/admin/document/index.html.twig +++ b/templates/admin/document/index.html.twig @@ -6,94 +6,78 @@

Dokumentenübersicht

-
-
- - - - - - - - - - - - {% for document in pagination %} +
+
+
- {{ knp_pagination_sortable(pagination, 'Dateiname', 'upload.originalFilename') }} - - Dateigröße - - Upload am - - Upload von -
+ - - - - - + + + + + + + - {% else %} - - - - {% endfor %} - -
- {{ document.displayName ? document.displayName : document.originalFilename }} - - {{ document.size|file_size }} - - {{ document.createdAt|date('d.m.Y') }} - - {{ document.createdBy }} - -
- - - - - {{ icon('download') }} - -
-
+ {{ knp_pagination_sortable(pagination, 'Einsatzzeitraum', 'destination.dateFrom') }} + + {{ knp_pagination_sortable(pagination, 'Jobprofil', 'job_profile.name') }} + + {{ knp_pagination_sortable(pagination, 'Destination', 'destination.product') }} + + {{ knp_pagination_sortable(pagination, 'Dokument', 'upload.type') }} + + {{ knp_pagination_sortable(pagination, 'Teamer', 'teamer.lastName') }} + + {{ knp_pagination_sortable(pagination, 'Status', 'disposition.status') }} +
- Keine Daten... -
+ + + {% for upload in pagination %} + {% set disposition = upload.disposition %} + {% set assignment = disposition.assignment %} + + + {{ assignment.destination.dateFrom|date('d.m.Y') }} + - + {{ assignment.destination.dateTo|date('d.m.Y') }} + + + {{ assignment.jobProfile.name }} + + + {{ assignment.destination.product }} +
+ {{ assignment.destination.hotel }} + + + {{ ('label.document.type.' ~ upload.type)|trans }} + + + {{ disposition.teamer }} + + + {{ ('label.document.status.' ~ upload.status)|trans }} + + + + + + {% else %} + + + Keine Daten... + + + {% endfor %} + + +
- - {% endblock %} \ No newline at end of file diff --git a/templates/admin/system/document/index.html.twig b/templates/admin/system/document/index.html.twig new file mode 100644 index 0000000..c8e47f1 --- /dev/null +++ b/templates/admin/system/document/index.html.twig @@ -0,0 +1,99 @@ +{% extends 'admin/layout.html.twig' %} + +{% block title %}Dokumentenübersicht{% endblock %} + +{% block content %} +

+ Dokumentenübersicht +

+
+
+ + + + + + + + + + + + {% for document in pagination %} + + + + + + + + {% else %} + + + + {% endfor %} + +
+ {{ knp_pagination_sortable(pagination, 'Dateiname', 'upload.originalFilename') }} + + Dateigröße + + Upload am + + Upload von +
+ {{ document.displayName ? document.displayName : document.originalFilename }} + + {{ document.size|file_size }} + + {{ document.createdAt|date('d.m.Y') }} + + {{ document.createdBy }} + +
+ + + + + {{ icon('download') }} + +
+
+ Keine Daten... +
+
+
+ +{% endblock %} \ No newline at end of file diff --git a/templates/admin/document/rename.html.twig b/templates/admin/system/document/rename.html.twig similarity index 100% rename from templates/admin/document/rename.html.twig rename to templates/admin/system/document/rename.html.twig diff --git a/templates/admin/document/replace.html.twig b/templates/admin/system/document/replace.html.twig similarity index 100% rename from templates/admin/document/replace.html.twig rename to templates/admin/system/document/replace.html.twig diff --git a/templates/admin/document/upload.html.twig b/templates/admin/system/document/upload.html.twig similarity index 100% rename from templates/admin/document/upload.html.twig rename to templates/admin/system/document/upload.html.twig diff --git a/templates/teamer/disposition/detail.html.twig b/templates/teamer/disposition/detail.html.twig index 9481eb1..3d2946b 100644 --- a/templates/teamer/disposition/detail.html.twig +++ b/templates/teamer/disposition/detail.html.twig @@ -20,29 +20,71 @@
{{ assignment.contact }}, {{ assignment.contact.email }}

-

- Hier findest du deinen Honorarvertrag -

-
- - Honorarvertrag - -
-

- Bitte lade ihn unterschrieben hier wieder hoch: -

- {{ form_start(form) }} -
- {% include '_partials/_upload_collection_form.html.twig' with { - 'endpoint_upload': path('_uploader_upload_contract'), - 'accepted_files': 'image/jpg,image/jpeg,application/pdf', - } %} -
- - {{ form_rest(form) }} - {{ form_end(form) }} + {% if workflow_can(disposition, 'upload_contract') %} +

+ Hier findest du deinen Honorarvertrag +

+
+ + Honorarvertrag + +
+

+ Bitte lade ihn unterschrieben hier wieder hoch: +

+ {{ form_start(form) }} +
+ {% include '_partials/_upload_collection_form.html.twig' with { + 'endpoint_upload': path('_uploader_upload_contract'), + 'accepted_files': 'image/jpg,image/jpeg,application/pdf', + } %} +
+ + {{ form_rest(form) }} + {{ form_end(form) }} + {% elseif workflow_can(disposition, 'upload_invoice') %} +

+ Hier findest du deine Honorarnote +

+
+ + Honorarvertrag + +
+

+ Bitte lade sie ausgefüllt und unterschrieben hier wieder hoch: +

+ {{ form_start(form) }} +
+ {% include '_partials/_upload_collection_form.html.twig' with { + 'endpoint_upload': path('_uploader_upload_invoice'), + 'accepted_files': 'image/jpg,image/jpeg,application/pdf', + } %} +
+ + {{ form_rest(form) }} + {{ form_end(form) }} + {% elseif workflow_has_marked_place(disposition, 'checking_contract') %} +

+ Danke für den Upload des Honorarvertrags +

+ {% elseif workflow_has_marked_place(disposition, 'checking_invoice') %} +

+ Danke für den Upload der Honorarnote +

+ {% elseif workflow_has_marked_place(disposition, 'paid') %} +

+ Dein Honorar wurde überwiesen +

+ {% else %} +

+ Dein Einsatz ist abgeschlossen +

+ {% endif %}