feat: assignment call off
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller\Administrative\Assignment;
|
||||
|
||||
use App\Entity\Assignment;
|
||||
use App\Event\AssignmentCalledOffEvent;
|
||||
use App\Htmx\HxRedirectResponse;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
|
||||
|
||||
class CallOffController extends AbstractController
|
||||
{
|
||||
public function __construct(
|
||||
private readonly EntityManagerInterface $entityManager,
|
||||
private readonly EventDispatcherInterface $eventDispatcher,
|
||||
private readonly LoggerInterface $logger
|
||||
) {
|
||||
}
|
||||
|
||||
#[Route('/administrative/assignment/call-off/{uuid}', name: 'app_administrative_assignment_call_off')]
|
||||
#[IsGranted('ROLE_ADMINISTRATIVE')]
|
||||
public function index(Assignment $assignment, Request $request): Response
|
||||
{
|
||||
if (true === $request->isMethod('POST')) {
|
||||
$assignment->setStatus(Assignment::STATUS_CALLED_OFF);
|
||||
|
||||
$this->entityManager->flush();
|
||||
|
||||
$this
|
||||
->eventDispatcher
|
||||
->dispatch(new AssignmentCalledOffEvent($assignment), AssignmentCalledOffEvent::NAME)
|
||||
;
|
||||
|
||||
$this->logger->info('Call off assignment', [
|
||||
'assignment_id' => $assignment->getId(),
|
||||
'destination' => (string) $assignment->getDestination(),
|
||||
'job_profile' => $assignment->getJobProfile()->getName(),
|
||||
]);
|
||||
|
||||
$this->addFlash('success', 'Der Einsatz/die Reise wurde abgesagt');
|
||||
|
||||
return new HxRedirectResponse($this->generateUrl('app_administrative_assignment_index'));
|
||||
}
|
||||
|
||||
return $this->render('administrative/assignment/modal_call_off.html.twig', [
|
||||
'assignment' => $assignment,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,7 @@ class Assignment implements BlameableEntityInterface, TimestampableEntityInterfa
|
||||
public const STATUS_STAFFING = 'staffing';
|
||||
public const STATUS_UNSTAFFED = 'unstaffed';
|
||||
public const STATUS_DELETED = 'deleted';
|
||||
public const STATUS_CALLED_OFF = 'called_off';
|
||||
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Event;
|
||||
|
||||
use App\Entity\Assignment;
|
||||
use Symfony\Contracts\EventDispatcher\Event;
|
||||
|
||||
class AssignmentCalledOffEvent extends Event
|
||||
{
|
||||
public const NAME = 'assignment.called_off';
|
||||
|
||||
public function __construct(private readonly Assignment $assignment)
|
||||
{}
|
||||
|
||||
public function getAssignment(): Assignment
|
||||
{
|
||||
return $this->assignment;
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ use App\Email\Mailer;
|
||||
use App\Entity\Application;
|
||||
use App\Entity\Upload;
|
||||
use App\Event\ApplicationStatusEvent;
|
||||
use App\Event\AssignmentCalledOffEvent;
|
||||
use App\Event\DispositionCreatedEvent;
|
||||
use App\Event\DocumentRejectedEvent;
|
||||
use App\Event\DocumentUploadedEvent;
|
||||
@@ -30,6 +31,7 @@ class EmailNotificationSubscriber implements EventSubscriberInterface
|
||||
DocumentUploadedEvent::NAME => 'onDocumentUploaded',
|
||||
DocumentRejectedEvent::NAME => 'onDocumentRejected',
|
||||
ApplicationStatusEvent::NAME => 'onApplicationStatus',
|
||||
AssignmentCalledOffEvent::NAME => 'onAssignmentCalledOff',
|
||||
];
|
||||
}
|
||||
|
||||
@@ -154,4 +156,20 @@ class EmailNotificationSubscriber implements EventSubscriberInterface
|
||||
'template' => 'email/application_rejected.html.twig',
|
||||
]);
|
||||
}
|
||||
|
||||
public function onAssignmentCalledOff(AssignmentCalledOffEvent $event): void
|
||||
{
|
||||
$assignment = $event->getAssignment();
|
||||
foreach ($assignment->getDispositions() as $disposition) {
|
||||
$teamer = $disposition->getTeamer();
|
||||
|
||||
$this->mailer->createAndSendEmail([
|
||||
'assignment' => $assignment,
|
||||
], [
|
||||
'to' => $teamer->getCommunication()->getEmail(),
|
||||
'subject' => 'Die Reise zu deinem Einsatz wurde abgesagt',
|
||||
'template' => 'email/assignment_called_off.html.twig',
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -37,6 +37,7 @@ class AssignmentType extends AbstractType
|
||||
'choices' => [
|
||||
'Entwurf' => Assignment::STATUS_DRAFT,
|
||||
'veröffentlicht' => Assignment::STATUS_PUBLISHED,
|
||||
'abgesagt' => Assignment::STATUS_CALLED_OFF,
|
||||
],
|
||||
])
|
||||
->add('availableDispositions', IntegerType::class, [
|
||||
|
||||
@@ -67,12 +67,14 @@ class AssignmentRepository extends ServiceEntityRepository
|
||||
)
|
||||
),
|
||||
$qb->expr()->isNull('assignment.deletedAt'),
|
||||
$qb->expr()->notIn('assignment.status', ':status'),
|
||||
$qb->expr()->in('assignment.id', $availableAssignmentsIds)
|
||||
))
|
||||
->leftJoin('assignment.applications', 'application', Join::WITH, $qb->expr()->eq('application.teamer', ':teamer'))
|
||||
->leftJoin('assignment.dispositions', 'disposition', Join::WITH, $qb->expr()->eq('disposition.teamer', ':teamer'))
|
||||
->groupBy('assignment')
|
||||
->setParameter('teamer', $teamer)
|
||||
->setParameter('status', [Assignment::STATUS_DRAFT, Assignment::STATUS_CALLED_OFF])
|
||||
->setParameter('now', new \DateTimeImmutable())
|
||||
;
|
||||
|
||||
@@ -273,12 +275,12 @@ class AssignmentRepository extends ServiceEntityRepository
|
||||
$qb->expr()->gt('assignment.dateFrom', ':now')
|
||||
)
|
||||
),
|
||||
$qb->expr()->neq('assignment.status', ':status'),
|
||||
$qb->expr()->notIn('assignment.status', ':status'),
|
||||
$qb->expr()->isNull('assignment.deletedAt')
|
||||
))
|
||||
->groupBy('assignment')
|
||||
->having($qb->expr()->gt('assignment.availableDispositions', $qb->expr()->count('disposition.id')))
|
||||
->setParameter('status', Assignment::STATUS_DRAFT)
|
||||
->setParameter('status', [Assignment::STATUS_DRAFT, Assignment::STATUS_CALLED_OFF])
|
||||
->setParameter('now', new \DateTimeImmutable())
|
||||
->getQuery()
|
||||
->getArrayResult()
|
||||
|
||||
@@ -37,6 +37,7 @@ class DispositionRepository extends ServiceEntityRepository
|
||||
->innerJoin('assignment.destination', 'destination')
|
||||
->where($qb->expr()->andX(
|
||||
$qb->expr()->eq('disposition.teamer', ':teamer'),
|
||||
$qb->expr()->neq('assignment.status', ':status'),
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->isNull('assignment.dateFrom'),
|
||||
@@ -49,6 +50,7 @@ class DispositionRepository extends ServiceEntityRepository
|
||||
)
|
||||
))
|
||||
->setParameter('teamer', $teamer)
|
||||
->setParameter('status', Assignment::STATUS_CALLED_OFF)
|
||||
->setParameter('date', new \DateTimeImmutable())
|
||||
->getQuery()
|
||||
;
|
||||
|
||||
@@ -17,6 +17,7 @@ class AssignmentVoter extends Voter
|
||||
public const EDIT = 'EDIT';
|
||||
public const APPLY = 'APPLY';
|
||||
public const PUBLISH = 'PUBLISH';
|
||||
public const CALL_OFF = 'CALL_OFF';
|
||||
|
||||
public function __construct(
|
||||
private readonly Security $security,
|
||||
@@ -30,7 +31,7 @@ class AssignmentVoter extends Voter
|
||||
return false;
|
||||
}
|
||||
|
||||
return in_array($attribute, [static::VIEW, static::EDIT, static::APPLY, static::PUBLISH]);
|
||||
return in_array($attribute, [static::VIEW, static::EDIT, static::APPLY, static::PUBLISH, static::CALL_OFF]);
|
||||
}
|
||||
|
||||
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
|
||||
@@ -54,8 +55,19 @@ class AssignmentVoter extends Voter
|
||||
&& $this->security->isGranted('ROLE_ADMINISTRATIVE');
|
||||
}
|
||||
|
||||
// Only allow applications to published assignments
|
||||
if (Assignment::STATUS_DRAFT === $assignment->getStatus()) {
|
||||
// Only users with administrative role may call off assignments
|
||||
if (static::CALL_OFF === $attribute) {
|
||||
return Assignment::STATUS_CALLED_OFF !== $assignment->getStatus()
|
||||
&& $this->security->isGranted('ROLE_ADMINISTRATIVE');
|
||||
}
|
||||
|
||||
// Only allow applications to published, not deleted and not called off assignments
|
||||
if (
|
||||
true === in_array($assignment->getStatus(), [
|
||||
Assignment::STATUS_DRAFT,
|
||||
Assignment::STATUS_DELETED,
|
||||
Assignment::STATUS_CALLED_OFF,
|
||||
])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user