From 5dd2f79c2dfee01357c1895f614266320786935f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Fromme?= Date: Tue, 16 Jul 2024 17:34:50 +0200 Subject: [PATCH] feat: refactor staffing status assignment and filtering --- migrations/Version20240716063200.php | 61 +++++++++++++++++++ .../Admin/Application/DeleteController.php | 3 + .../Admin/Application/DisposeController.php | 4 ++ .../Admin/Disposition/DeleteController.php | 4 ++ .../Assignment/IndexController.php | 1 - .../Document/CheckController.php | 3 + .../Teamer/Application/CreateController.php | 4 ++ .../Teamer/Application/WithdrawController.php | 4 ++ src/Entity/Assignment.php | 49 +++++++++------ src/Entity/Traits/SoftDeletableEntity.php | 2 +- src/Form/AssignmentFilterType.php | 4 +- src/Model/AssignmentFilterDto.php | 6 +- src/Repository/AssignmentRepository.php | 27 ++------ .../administrative/assignment/index.html.twig | 8 +-- 14 files changed, 128 insertions(+), 52 deletions(-) create mode 100644 migrations/Version20240716063200.php diff --git a/migrations/Version20240716063200.php b/migrations/Version20240716063200.php new file mode 100644 index 0000000..24ea1a0 --- /dev/null +++ b/migrations/Version20240716063200.php @@ -0,0 +1,61 @@ +container = $container; + } + + public function up(Schema $schema): void + { + // this up() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE assignment ADD staffing_status VARCHAR(64) NOT NULL'); + } + + public function postUp(Schema $schema): void + { + $entityManager = $this->container->get('doctrine.orm.entity_manager'); + $assignmentRepository = $entityManager->getRepository(Assignment::class); + + $assignments = $assignmentRepository->findAll(); + + foreach ($assignments as $assignment) { + if (Assignment::STATUS_DRAFT === $assignment->getStatus() || null !== $assignment->getDeletedAt()) { + continue; + } + + $assignment->updateStaffingStatus(); + } + + $entityManager->flush(); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE assignment DROP staffing_status'); + } +} diff --git a/src/Controller/Admin/Application/DeleteController.php b/src/Controller/Admin/Application/DeleteController.php index 012eed3..18d8887 100644 --- a/src/Controller/Admin/Application/DeleteController.php +++ b/src/Controller/Admin/Application/DeleteController.php @@ -26,6 +26,9 @@ class DeleteController extends AbstractController $this->entityManager->remove($application); $this->entityManager->flush(); + $application->getAssignment()->updateStaffingStatus(); + $this->entityManager->flush(); + $this->addFlash('success', 'Die Bewerbung wurde gelöscht'); $this->logger->info('Delete application', [ 'destination' => (string) $application->getAssignment()->getDestination(), diff --git a/src/Controller/Admin/Application/DisposeController.php b/src/Controller/Admin/Application/DisposeController.php index f6849bd..2f5419c 100644 --- a/src/Controller/Admin/Application/DisposeController.php +++ b/src/Controller/Admin/Application/DisposeController.php @@ -39,6 +39,10 @@ class DisposeController extends AbstractController $this->entityManager->remove($application); $this->entityManager->flush(); + // Update staffing status of related assignment + $disposition->getAssignment()->updateStaffingStatus(); + $this->entityManager->flush(); + $this->eventDispatcher->dispatch(new DispositionCreatedEvent($disposition), DispositionCreatedEvent::NAME); $this->addFlash('success', 'Teamer:in wurde eingeteilt'); diff --git a/src/Controller/Admin/Disposition/DeleteController.php b/src/Controller/Admin/Disposition/DeleteController.php index fab2703..18d6396 100644 --- a/src/Controller/Admin/Disposition/DeleteController.php +++ b/src/Controller/Admin/Disposition/DeleteController.php @@ -54,6 +54,10 @@ class DeleteController extends AbstractController $this->entityManager->remove($disposition); $this->entityManager->flush(); + // Update staffing status of related assignment + $disposition->getAssignment()->updateStaffingStatus(); + $this->entityManager->flush(); + return new HxRedirectResponse($this->generateUrl('app_administrative_assignment_detail', [ 'uuid' => $disposition->getAssignment()->getUuid(), ])); diff --git a/src/Controller/Administrative/Assignment/IndexController.php b/src/Controller/Administrative/Assignment/IndexController.php index 4c76741..e976642 100644 --- a/src/Controller/Administrative/Assignment/IndexController.php +++ b/src/Controller/Administrative/Assignment/IndexController.php @@ -36,7 +36,6 @@ class IndexController extends AbstractController $request->query->getInt('page', 1), 10, [ - 'wrap-queries' => true, // Required for query with 'having' clause 'defaultSortFieldName' => 'destination.dateFrom', 'defaultSortDirection' => 'desc', ] diff --git a/src/Controller/Administrative/Document/CheckController.php b/src/Controller/Administrative/Document/CheckController.php index 0745725..cca08f2 100644 --- a/src/Controller/Administrative/Document/CheckController.php +++ b/src/Controller/Administrative/Document/CheckController.php @@ -46,6 +46,9 @@ class CheckController extends AbstractController switch ($formData->getStatus()) { case Upload::STATUS_CHECKED: $this->confirmDocument($document, 'confirm_contract', Upload::STATUS_CHECKED, $formData); + // Contract confirmed: Update staffing status of related assignment + $document->getDisposition()->getAssignment()->updateStaffingStatus(); + $this->entityManager->flush(); break; case Upload::STATUS_PAID: $this->confirmDocument($document, 'confirm_invoice', Upload::STATUS_PAID, $formData); diff --git a/src/Controller/Teamer/Application/CreateController.php b/src/Controller/Teamer/Application/CreateController.php index b4786aa..d62966c 100644 --- a/src/Controller/Teamer/Application/CreateController.php +++ b/src/Controller/Teamer/Application/CreateController.php @@ -39,6 +39,10 @@ class CreateController extends AbstractController $this->entityManager->persist($application); $this->entityManager->flush(); + // Update staffing status of related assignment + $assignment->updateStaffingStatus(); + $this->entityManager->flush(); + $this->addFlash('success', 'Deine Bewerbung wurde entgegengenommen'); $this->logger->info('Create application', [ 'teamer_id' => $teamer->getId(), diff --git a/src/Controller/Teamer/Application/WithdrawController.php b/src/Controller/Teamer/Application/WithdrawController.php index 89e3a62..8b6efe2 100644 --- a/src/Controller/Teamer/Application/WithdrawController.php +++ b/src/Controller/Teamer/Application/WithdrawController.php @@ -37,6 +37,10 @@ class WithdrawController extends AbstractController $this->entityManager->remove($application); $this->entityManager->flush(); + // Update staffing status of related assignment + $assignment->updateStaffingStatus(); + $this->entityManager->flush(); + $this->logger->info('Withdraw application', [ 'teamer_id' => $teamer->getId(), 'teamer_name' => (string) $teamer, diff --git a/src/Entity/Assignment.php b/src/Entity/Assignment.php index a132f81..92e3cfe 100644 --- a/src/Entity/Assignment.php +++ b/src/Entity/Assignment.php @@ -95,10 +95,14 @@ class Assignment implements BlameableEntityInterface, TimestampableEntityInterfa #[ORM\Column(length: 64)] private ?string $status; + #[ORM\Column(length: 64)] + private ?string $staffingStatus; + public function __construct() { $this->uuid = Uuid::v4(); $this->status = static::STATUS_DRAFT; + $this->staffingStatus = static::STATUS_UNSTAFFED; $this->fees = new ArrayCollection(); $this->applications = new ArrayCollection(); $this->dispositions = new ArrayCollection(); @@ -129,6 +133,27 @@ class Assignment implements BlameableEntityInterface, TimestampableEntityInterfa return $instance; } + public function updateStaffingStatus(): void + { + $confirmedDispositions = $this->getConfirmedDispositions(); + + if ($this->availableDispositions === $confirmedDispositions->count()) { + $this->setStaffingStatus(Assignment::STATUS_STAFFED); + } elseif ( + $this->availableDispositions > $confirmedDispositions->count() + && 0 < $confirmedDispositions->count() + ) { + $this->setStaffingStatus(Assignment::STATUS_PARTLY_STAFFED); + } elseif ( + $this->availableDispositions > $this->dispositions->count() + && 0 < $this->applications->count() + ) { + $this->setStaffingStatus(Assignment::STATUS_STAFFING); + } else { + $this->setStaffingStatus(Assignment::STATUS_UNSTAFFED); + } + } + public function getId(): ?int { return $this->id; @@ -488,29 +513,15 @@ class Assignment implements BlameableEntityInterface, TimestampableEntityInterfa return $this; } - public function isStaffed(): bool + public function getStaffingStatus(): ?string { - $confirmedDispositions = $this->getConfirmedDispositions(); - - return $this->availableDispositions === $confirmedDispositions->count(); + return $this->staffingStatus; } - public function isUnstaffed(): bool + public function setStaffingStatus(?string $staffingStatus): static { - return 0 === $this->dispositions->count(); - } + $this->staffingStatus = $staffingStatus; - public function isPartlyStaffed(): bool - { - $confirmedDispositions = $this->getConfirmedDispositions(); - - return $this->availableDispositions > $confirmedDispositions->count() - && 0 < $confirmedDispositions->count(); - } - - public function isStaffing(): bool - { - return $this->availableDispositions > $this->dispositions->count() - && 0 < $this->applications->count(); + return $this; } } diff --git a/src/Entity/Traits/SoftDeletableEntity.php b/src/Entity/Traits/SoftDeletableEntity.php index b544155..8cf5f4f 100644 --- a/src/Entity/Traits/SoftDeletableEntity.php +++ b/src/Entity/Traits/SoftDeletableEntity.php @@ -9,7 +9,7 @@ trait SoftDeletableEntity #[ORM\Column(nullable: true)] private ?\DateTimeImmutable $deletedAt = null; - public function getDeletedAt(): \DateTimeImmutable + public function getDeletedAt(): ?\DateTimeImmutable { return $this->deletedAt; } diff --git a/src/Form/AssignmentFilterType.php b/src/Form/AssignmentFilterType.php index b37c9ae..73ed0d7 100644 --- a/src/Form/AssignmentFilterType.php +++ b/src/Form/AssignmentFilterType.php @@ -65,10 +65,10 @@ class AssignmentFilterType extends AbstractType ; if ($this->security->isGranted('ROLE_ADMINISTRATIVE')) { - $builder->add('status', ChoiceType::class, [ + $builder->add('status', MultiselectType::class, [ 'label' => 'Status', 'required' => false, - 'placeholder' => 'nicht filtern', + 'empty_label' => 'nicht filtern', 'choices' => [ 'voll besetzt' => Assignment::STATUS_STAFFED, 'teilweise besetzt' => Assignment::STATUS_PARTLY_STAFFED, diff --git a/src/Model/AssignmentFilterDto.php b/src/Model/AssignmentFilterDto.php index 8627a58..e6d003b 100644 --- a/src/Model/AssignmentFilterDto.php +++ b/src/Model/AssignmentFilterDto.php @@ -17,7 +17,7 @@ class AssignmentFilterDto extends AbstractFilterDto protected ?JobProfile $jobProfile = null; protected ?string $hotel = null; protected ?int $duration = null; - protected ?string $status = null; + protected array $status = []; protected bool $includePast = false; public function getDateFrom(): ?\DateTimeImmutable @@ -80,12 +80,12 @@ class AssignmentFilterDto extends AbstractFilterDto return $this; } - public function getStatus(): ?string + public function getStatus(): array { return $this->status; } - public function setStatus(?string $status): static + public function setStatus(array $status): static { $this->status = $status; diff --git a/src/Repository/AssignmentRepository.php b/src/Repository/AssignmentRepository.php index 9b79818..18f7e3b 100644 --- a/src/Repository/AssignmentRepository.php +++ b/src/Repository/AssignmentRepository.php @@ -40,7 +40,6 @@ class AssignmentRepository extends ServiceEntityRepository ->leftJoin('assignment.applications', 'application') ->leftJoin('assignment.dispositions', 'disposition') ->leftJoin('disposition.teamer', 'teamer') - ->groupBy('assignment') ; $this->applyFilterSettings($filterDto, $qb); @@ -117,27 +116,11 @@ class AssignmentRepository extends ServiceEntityRepository ; } - if (null !== $status = $filterDto->getStatus()) { - switch ($status) { - case Assignment::STATUS_STAFFED: - $qb->having('assignment.availableDispositions = COUNT(disposition.id)'); - break; - case Assignment::STATUS_PARTLY_STAFFED: - $qb - ->having('COUNT(disposition.id) > 0') - ->andHaving('assignment.availableDispositions > COUNT(disposition.id)') - ; - break; - case Assignment::STATUS_STAFFING: - $qb - ->having('COUNT(disposition.id) = 0') - ->andHaving('COUNT(application.id) > 0') - ; - break; - case Assignment::STATUS_UNSTAFFED: - $qb->having('COUNT(disposition.id) = 0'); - break; - } + if (0 < count($filterDto->getStatus())) { + $qb + ->andWhere($qb->expr()->in('assignment.staffingStatus', ':status')) + ->setParameter('status', $filterDto->getStatus()) + ; } if (null !== $dateFrom = $filterDto->getDateFrom()) { diff --git a/templates/administrative/assignment/index.html.twig b/templates/administrative/assignment/index.html.twig index b8ae1c5..f38fc21 100644 --- a/templates/administrative/assignment/index.html.twig +++ b/templates/administrative/assignment/index.html.twig @@ -68,15 +68,15 @@ {% if assignment.status == 'draft' %} {{ icon('edit', 'w-4 h-4') }} {% else %} - {% if assignment.staffed %} + {% if assignment.staffingStatus == constant('App\\Entity\\Assignment::STATUS_STAFFED') %} - {% elseif assignment.partlyStaffed %} + {% elseif assignment.staffingStatus == constant('App\\Entity\\Assignment::STATUS_PARTLY_STAFFED') %} - {% elseif assignment.staffing %} + {% elseif assignment.staffingStatus == constant('App\\Entity\\Assignment::STATUS_STAFFING') %} @@ -128,7 +128,7 @@ {% if is_granted('ROLE_ADMIN') %} - {{ assignment.validApplications|length }} + {{ assignment.applications|length }} {% endif %}