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
-
-
-
-
-
- |
- {{ knp_pagination_sortable(pagination, 'Dateiname', 'upload.originalFilename') }}
- |
-
- Dateigröße
- |
-
- Upload am
- |
-
- Upload von
- |
- |
-
-
-
- {% for document in pagination %}
+
+
+
+
- |
- {{ document.displayName ? document.displayName : document.originalFilename }}
- |
-
- {{ document.size|file_size }}
- |
-
- {{ document.createdAt|date('d.m.Y') }}
- |
-
- {{ document.createdBy }}
- |
-
-
- |
+
+ {{ 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') }}
+ |
+ |
- {% else %}
-
- |
- Keine Daten...
- |
-
- {% endfor %}
-
-
+
+
+ {% 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
+
+
+
+
+
+
+ |
+ {{ knp_pagination_sortable(pagination, 'Dateiname', 'upload.originalFilename') }}
+ |
+
+ Dateigröße
+ |
+
+ Upload am
+ |
+
+ Upload von
+ |
+ |
+
+
+
+ {% for document in pagination %}
+
+ |
+ {{ document.displayName ? document.displayName : document.originalFilename }}
+ |
+
+ {{ document.size|file_size }}
+ |
+
+ {{ document.createdAt|date('d.m.Y') }}
+ |
+
+ {{ document.createdBy }}
+ |
+
+
+ |
+
+ {% else %}
+
+ |
+ Keine Daten...
+ |
+
+ {% endfor %}
+
+
+
+
+
+{% 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
-
-
-
- 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
+
+
+
+ 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
+
+
+
+ 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 %}