feat: refactor staffing status assignment and filtering
This commit is contained in:
@@ -0,0 +1,61 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace DoctrineMigrations;
|
||||||
|
|
||||||
|
use App\Entity\Assignment;
|
||||||
|
use App\Repository\AssignmentRepository;
|
||||||
|
use Doctrine\DBAL\Schema\Schema;
|
||||||
|
use Doctrine\Migrations\AbstractMigration;
|
||||||
|
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
|
||||||
|
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
|
||||||
|
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto-generated Migration: Please modify to your needs!
|
||||||
|
*/
|
||||||
|
final class Version20240716063200 extends AbstractMigration implements ContainerAwareInterface
|
||||||
|
{
|
||||||
|
private ContainerInterface $container;
|
||||||
|
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setContainer(?ContainerInterface $container): void
|
||||||
|
{
|
||||||
|
$this->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');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,6 +26,9 @@ class DeleteController extends AbstractController
|
|||||||
$this->entityManager->remove($application);
|
$this->entityManager->remove($application);
|
||||||
$this->entityManager->flush();
|
$this->entityManager->flush();
|
||||||
|
|
||||||
|
$application->getAssignment()->updateStaffingStatus();
|
||||||
|
$this->entityManager->flush();
|
||||||
|
|
||||||
$this->addFlash('success', 'Die Bewerbung wurde gelöscht');
|
$this->addFlash('success', 'Die Bewerbung wurde gelöscht');
|
||||||
$this->logger->info('Delete application', [
|
$this->logger->info('Delete application', [
|
||||||
'destination' => (string) $application->getAssignment()->getDestination(),
|
'destination' => (string) $application->getAssignment()->getDestination(),
|
||||||
|
|||||||
@@ -39,6 +39,10 @@ class DisposeController extends AbstractController
|
|||||||
$this->entityManager->remove($application);
|
$this->entityManager->remove($application);
|
||||||
$this->entityManager->flush();
|
$this->entityManager->flush();
|
||||||
|
|
||||||
|
// Update staffing status of related assignment
|
||||||
|
$disposition->getAssignment()->updateStaffingStatus();
|
||||||
|
$this->entityManager->flush();
|
||||||
|
|
||||||
$this->eventDispatcher->dispatch(new DispositionCreatedEvent($disposition), DispositionCreatedEvent::NAME);
|
$this->eventDispatcher->dispatch(new DispositionCreatedEvent($disposition), DispositionCreatedEvent::NAME);
|
||||||
|
|
||||||
$this->addFlash('success', 'Teamer:in wurde eingeteilt');
|
$this->addFlash('success', 'Teamer:in wurde eingeteilt');
|
||||||
|
|||||||
@@ -54,6 +54,10 @@ class DeleteController extends AbstractController
|
|||||||
$this->entityManager->remove($disposition);
|
$this->entityManager->remove($disposition);
|
||||||
$this->entityManager->flush();
|
$this->entityManager->flush();
|
||||||
|
|
||||||
|
// Update staffing status of related assignment
|
||||||
|
$disposition->getAssignment()->updateStaffingStatus();
|
||||||
|
$this->entityManager->flush();
|
||||||
|
|
||||||
return new HxRedirectResponse($this->generateUrl('app_administrative_assignment_detail', [
|
return new HxRedirectResponse($this->generateUrl('app_administrative_assignment_detail', [
|
||||||
'uuid' => $disposition->getAssignment()->getUuid(),
|
'uuid' => $disposition->getAssignment()->getUuid(),
|
||||||
]));
|
]));
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ class IndexController extends AbstractController
|
|||||||
$request->query->getInt('page', 1),
|
$request->query->getInt('page', 1),
|
||||||
10,
|
10,
|
||||||
[
|
[
|
||||||
'wrap-queries' => true, // Required for query with 'having' clause
|
|
||||||
'defaultSortFieldName' => 'destination.dateFrom',
|
'defaultSortFieldName' => 'destination.dateFrom',
|
||||||
'defaultSortDirection' => 'desc',
|
'defaultSortDirection' => 'desc',
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -46,6 +46,9 @@ class CheckController extends AbstractController
|
|||||||
switch ($formData->getStatus()) {
|
switch ($formData->getStatus()) {
|
||||||
case Upload::STATUS_CHECKED:
|
case Upload::STATUS_CHECKED:
|
||||||
$this->confirmDocument($document, 'confirm_contract', Upload::STATUS_CHECKED, $formData);
|
$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;
|
break;
|
||||||
case Upload::STATUS_PAID:
|
case Upload::STATUS_PAID:
|
||||||
$this->confirmDocument($document, 'confirm_invoice', Upload::STATUS_PAID, $formData);
|
$this->confirmDocument($document, 'confirm_invoice', Upload::STATUS_PAID, $formData);
|
||||||
|
|||||||
@@ -39,6 +39,10 @@ class CreateController extends AbstractController
|
|||||||
$this->entityManager->persist($application);
|
$this->entityManager->persist($application);
|
||||||
$this->entityManager->flush();
|
$this->entityManager->flush();
|
||||||
|
|
||||||
|
// Update staffing status of related assignment
|
||||||
|
$assignment->updateStaffingStatus();
|
||||||
|
$this->entityManager->flush();
|
||||||
|
|
||||||
$this->addFlash('success', 'Deine Bewerbung wurde entgegengenommen');
|
$this->addFlash('success', 'Deine Bewerbung wurde entgegengenommen');
|
||||||
$this->logger->info('Create application', [
|
$this->logger->info('Create application', [
|
||||||
'teamer_id' => $teamer->getId(),
|
'teamer_id' => $teamer->getId(),
|
||||||
|
|||||||
@@ -37,6 +37,10 @@ class WithdrawController extends AbstractController
|
|||||||
$this->entityManager->remove($application);
|
$this->entityManager->remove($application);
|
||||||
$this->entityManager->flush();
|
$this->entityManager->flush();
|
||||||
|
|
||||||
|
// Update staffing status of related assignment
|
||||||
|
$assignment->updateStaffingStatus();
|
||||||
|
$this->entityManager->flush();
|
||||||
|
|
||||||
$this->logger->info('Withdraw application', [
|
$this->logger->info('Withdraw application', [
|
||||||
'teamer_id' => $teamer->getId(),
|
'teamer_id' => $teamer->getId(),
|
||||||
'teamer_name' => (string) $teamer,
|
'teamer_name' => (string) $teamer,
|
||||||
|
|||||||
+30
-19
@@ -95,10 +95,14 @@ class Assignment implements BlameableEntityInterface, TimestampableEntityInterfa
|
|||||||
#[ORM\Column(length: 64)]
|
#[ORM\Column(length: 64)]
|
||||||
private ?string $status;
|
private ?string $status;
|
||||||
|
|
||||||
|
#[ORM\Column(length: 64)]
|
||||||
|
private ?string $staffingStatus;
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->uuid = Uuid::v4();
|
$this->uuid = Uuid::v4();
|
||||||
$this->status = static::STATUS_DRAFT;
|
$this->status = static::STATUS_DRAFT;
|
||||||
|
$this->staffingStatus = static::STATUS_UNSTAFFED;
|
||||||
$this->fees = new ArrayCollection();
|
$this->fees = new ArrayCollection();
|
||||||
$this->applications = new ArrayCollection();
|
$this->applications = new ArrayCollection();
|
||||||
$this->dispositions = new ArrayCollection();
|
$this->dispositions = new ArrayCollection();
|
||||||
@@ -129,6 +133,27 @@ class Assignment implements BlameableEntityInterface, TimestampableEntityInterfa
|
|||||||
return $instance;
|
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
|
public function getId(): ?int
|
||||||
{
|
{
|
||||||
return $this->id;
|
return $this->id;
|
||||||
@@ -488,29 +513,15 @@ class Assignment implements BlameableEntityInterface, TimestampableEntityInterfa
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isStaffed(): bool
|
public function getStaffingStatus(): ?string
|
||||||
{
|
{
|
||||||
$confirmedDispositions = $this->getConfirmedDispositions();
|
return $this->staffingStatus;
|
||||||
|
|
||||||
return $this->availableDispositions === $confirmedDispositions->count();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isUnstaffed(): bool
|
public function setStaffingStatus(?string $staffingStatus): static
|
||||||
{
|
{
|
||||||
return 0 === $this->dispositions->count();
|
$this->staffingStatus = $staffingStatus;
|
||||||
}
|
|
||||||
|
|
||||||
public function isPartlyStaffed(): bool
|
return $this;
|
||||||
{
|
|
||||||
$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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ trait SoftDeletableEntity
|
|||||||
#[ORM\Column(nullable: true)]
|
#[ORM\Column(nullable: true)]
|
||||||
private ?\DateTimeImmutable $deletedAt = null;
|
private ?\DateTimeImmutable $deletedAt = null;
|
||||||
|
|
||||||
public function getDeletedAt(): \DateTimeImmutable
|
public function getDeletedAt(): ?\DateTimeImmutable
|
||||||
{
|
{
|
||||||
return $this->deletedAt;
|
return $this->deletedAt;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,10 +65,10 @@ class AssignmentFilterType extends AbstractType
|
|||||||
;
|
;
|
||||||
|
|
||||||
if ($this->security->isGranted('ROLE_ADMINISTRATIVE')) {
|
if ($this->security->isGranted('ROLE_ADMINISTRATIVE')) {
|
||||||
$builder->add('status', ChoiceType::class, [
|
$builder->add('status', MultiselectType::class, [
|
||||||
'label' => 'Status',
|
'label' => 'Status',
|
||||||
'required' => false,
|
'required' => false,
|
||||||
'placeholder' => 'nicht filtern',
|
'empty_label' => 'nicht filtern',
|
||||||
'choices' => [
|
'choices' => [
|
||||||
'voll besetzt' => Assignment::STATUS_STAFFED,
|
'voll besetzt' => Assignment::STATUS_STAFFED,
|
||||||
'teilweise besetzt' => Assignment::STATUS_PARTLY_STAFFED,
|
'teilweise besetzt' => Assignment::STATUS_PARTLY_STAFFED,
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ class AssignmentFilterDto extends AbstractFilterDto
|
|||||||
protected ?JobProfile $jobProfile = null;
|
protected ?JobProfile $jobProfile = null;
|
||||||
protected ?string $hotel = null;
|
protected ?string $hotel = null;
|
||||||
protected ?int $duration = null;
|
protected ?int $duration = null;
|
||||||
protected ?string $status = null;
|
protected array $status = [];
|
||||||
protected bool $includePast = false;
|
protected bool $includePast = false;
|
||||||
|
|
||||||
public function getDateFrom(): ?\DateTimeImmutable
|
public function getDateFrom(): ?\DateTimeImmutable
|
||||||
@@ -80,12 +80,12 @@ class AssignmentFilterDto extends AbstractFilterDto
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getStatus(): ?string
|
public function getStatus(): array
|
||||||
{
|
{
|
||||||
return $this->status;
|
return $this->status;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setStatus(?string $status): static
|
public function setStatus(array $status): static
|
||||||
{
|
{
|
||||||
$this->status = $status;
|
$this->status = $status;
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ class AssignmentRepository extends ServiceEntityRepository
|
|||||||
->leftJoin('assignment.applications', 'application')
|
->leftJoin('assignment.applications', 'application')
|
||||||
->leftJoin('assignment.dispositions', 'disposition')
|
->leftJoin('assignment.dispositions', 'disposition')
|
||||||
->leftJoin('disposition.teamer', 'teamer')
|
->leftJoin('disposition.teamer', 'teamer')
|
||||||
->groupBy('assignment')
|
|
||||||
;
|
;
|
||||||
|
|
||||||
$this->applyFilterSettings($filterDto, $qb);
|
$this->applyFilterSettings($filterDto, $qb);
|
||||||
@@ -117,27 +116,11 @@ class AssignmentRepository extends ServiceEntityRepository
|
|||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (null !== $status = $filterDto->getStatus()) {
|
if (0 < count($filterDto->getStatus())) {
|
||||||
switch ($status) {
|
$qb
|
||||||
case Assignment::STATUS_STAFFED:
|
->andWhere($qb->expr()->in('assignment.staffingStatus', ':status'))
|
||||||
$qb->having('assignment.availableDispositions = COUNT(disposition.id)');
|
->setParameter('status', $filterDto->getStatus())
|
||||||
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 (null !== $dateFrom = $filterDto->getDateFrom()) {
|
if (null !== $dateFrom = $filterDto->getDateFrom()) {
|
||||||
|
|||||||
@@ -68,15 +68,15 @@
|
|||||||
{% if assignment.status == 'draft' %}
|
{% if assignment.status == 'draft' %}
|
||||||
{{ icon('edit', 'w-4 h-4') }}
|
{{ icon('edit', 'w-4 h-4') }}
|
||||||
{% else %}
|
{% else %}
|
||||||
{% if assignment.staffed %}
|
{% if assignment.staffingStatus == constant('App\\Entity\\Assignment::STATUS_STAFFED') %}
|
||||||
<svg viewBox="0 0 5 5" class="h-4 w-4 fill-current text-green-500">
|
<svg viewBox="0 0 5 5" class="h-4 w-4 fill-current text-green-500">
|
||||||
<circle cx="3" cy="3" r="2" />
|
<circle cx="3" cy="3" r="2" />
|
||||||
</svg>
|
</svg>
|
||||||
{% elseif assignment.partlyStaffed %}
|
{% elseif assignment.staffingStatus == constant('App\\Entity\\Assignment::STATUS_PARTLY_STAFFED') %}
|
||||||
<svg viewBox="0 0 5 5" class="h-4 w-4 fill-current text-yellow-500">
|
<svg viewBox="0 0 5 5" class="h-4 w-4 fill-current text-yellow-500">
|
||||||
<circle cx="3" cy="3" r="2" />
|
<circle cx="3" cy="3" r="2" />
|
||||||
</svg>
|
</svg>
|
||||||
{% elseif assignment.staffing %}
|
{% elseif assignment.staffingStatus == constant('App\\Entity\\Assignment::STATUS_STAFFING') %}
|
||||||
<svg viewBox="0 0 5 5" class="h-4 w-4 fill-current text-blue-500">
|
<svg viewBox="0 0 5 5" class="h-4 w-4 fill-current text-blue-500">
|
||||||
<circle cx="3" cy="3" r="2" />
|
<circle cx="3" cy="3" r="2" />
|
||||||
</svg>
|
</svg>
|
||||||
@@ -128,7 +128,7 @@
|
|||||||
{% if is_granted('ROLE_ADMIN') %}
|
{% if is_granted('ROLE_ADMIN') %}
|
||||||
<td>
|
<td>
|
||||||
<a href="{{ path('app_administrative_assignment_detail', { 'uuid': assignment.uuid, 'r': return_url() }) }}" class="flex items-center space-x-2">
|
<a href="{{ path('app_administrative_assignment_detail', { 'uuid': assignment.uuid, 'r': return_url() }) }}" class="flex items-center space-x-2">
|
||||||
<span>{{ assignment.validApplications|length }}</span>
|
<span>{{ assignment.applications|length }}</span>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
Reference in New Issue
Block a user