WIP: Implement disposition process

This commit is contained in:
Björn Fromme
2023-10-12 15:34:29 +02:00
parent 4b7afb109d
commit 191619c451
8 changed files with 260 additions and 93 deletions
+36
View File
@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20231012132143 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}
public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE disposition ADD status VARCHAR(64) NOT NULL');
}
public function postUp(Schema $schema): void
{
$this->connection->executeQuery('UPDATE disposition SET status=\'new\'');
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE disposition DROP status');
}
}
@@ -5,6 +5,7 @@ namespace App\Controller\Admin\Assignment;
use App\Controller\Traits\ReturnUrlTrait; use App\Controller\Traits\ReturnUrlTrait;
use App\Entity\Assignment; use App\Entity\Assignment;
use App\Repository\ApplicationRepository; use App\Repository\ApplicationRepository;
use App\Repository\DispositionRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
@@ -15,8 +16,11 @@ class DetailController extends AbstractController
{ {
use ReturnUrlTrait; use ReturnUrlTrait;
public function __construct(private readonly ApplicationRepository $applicationRepository) public function __construct(
{} private readonly ApplicationRepository $applicationRepository,
private readonly DispositionRepository $dispositionRepository
) {
}
#[Route('/admin/assignment/detail/{uuid}', name: 'app_admin_assignment_detail')] #[Route('/admin/assignment/detail/{uuid}', name: 'app_admin_assignment_detail')]
#[IsGranted('ROLE_ADMINISTRATIVE')] #[IsGranted('ROLE_ADMINISTRATIVE')]
@@ -26,9 +30,14 @@ class DetailController extends AbstractController
'assignment' => $assignment, 'assignment' => $assignment,
]); ]);
$dispositions = $this->dispositionRepository->findBy([
'assignment' => $assignment,
]);
return $this->render('admin/assignment/detail.html.twig', [ return $this->render('admin/assignment/detail.html.twig', [
'assignment' => $assignment, 'assignment' => $assignment,
'applications' => $applications, 'applications' => $applications,
'dispositions' => $dispositions,
'returnUrl' => $this->getReturnUrl($request, 'app_admin_assignment_index'), 'returnUrl' => $this->getReturnUrl($request, 'app_admin_assignment_index'),
]); ]);
} }
+20
View File
@@ -15,6 +15,10 @@ class Disposition implements BlameableEntityInterface, TimestampableEntityInterf
use BlameableEntity; use BlameableEntity;
use TimestampableEntity; use TimestampableEntity;
public const STATUS_NEW = 'new';
public const STATUS_PENDING = 'pending';
public const STATUS_CONFIRMED = 'confirmed';
#[ORM\Id] #[ORM\Id]
#[ORM\GeneratedValue] #[ORM\GeneratedValue]
#[ORM\Column] #[ORM\Column]
@@ -23,6 +27,9 @@ class Disposition implements BlameableEntityInterface, TimestampableEntityInterf
#[ORM\Column(type: 'string', length: 36, unique: true)] #[ORM\Column(type: 'string', length: 36, unique: true)]
private string $uuid; private string $uuid;
#[ORM\Column(length: 64)]
private ?string $status;
#[ORM\ManyToOne(inversedBy: 'dispositions')] #[ORM\ManyToOne(inversedBy: 'dispositions')]
private ?Assignment $assignment; private ?Assignment $assignment;
@@ -41,6 +48,7 @@ class Disposition implements BlameableEntityInterface, TimestampableEntityInterf
public function __construct(Application $application) public function __construct(Application $application)
{ {
$this->uuid = Uuid::v4(); $this->uuid = Uuid::v4();
$this->status = static::STATUS_NEW;
$this->assignment = $application->getAssignment(); $this->assignment = $application->getAssignment();
$this->teamer = $application->getTeamer(); $this->teamer = $application->getTeamer();
$this->remarks = $application->getRemarks(); $this->remarks = $application->getRemarks();
@@ -56,6 +64,18 @@ class Disposition implements BlameableEntityInterface, TimestampableEntityInterf
return $this->uuid; return $this->uuid;
} }
public function getStatus(): ?string
{
return $this->status;
}
public function setStatus(string $status): static
{
$this->status = $status;
return $this;
}
public function getAssignment(): ?Assignment public function getAssignment(): ?Assignment
{ {
return $this->assignment; return $this->assignment;
+1 -1
View File
@@ -31,7 +31,7 @@ class TeamerJobProfileType extends AbstractType
$requirements[] = $training->getName(); $requirements[] = $training->getName();
} }
foreach ($choice->getRequiredLicenses() as $license) { foreach ($choice->getRequiredLicenses() as $license) {
$requirements[] = sprintf('Offizielle %slehrer*innenlizenz', ucfirst(strtolower($license))); $requirements[] = sprintf('Offizielle %slehrer:innenlizenz', ucfirst(strtolower($license)));
} }
if ($requirements) { if ($requirements) {
+1 -1
View File
@@ -33,7 +33,7 @@ class AppRuntime implements RuntimeExtensionInterface
public function licenseLabel(string $type): string public function licenseLabel(string $type): string
{ {
return sprintf('Offizielle %slehrer*innenlizenz', ucfirst(strtolower($type))); return sprintf('Offizielle %slehrer:innenlizenz', ucfirst(strtolower($type)));
} }
public function bpnCountryLabel(?string $token, string $property = 'name'): string public function bpnCountryLabel(?string $token, string $property = 'name'): string
@@ -0,0 +1,49 @@
<div class="grid lg:grid-cols-2 gap-x-8 gap-y-4">
<div class="lg:col-span-2 font-bold text-xl">
Jobprofil {{ assignment.jobProfile.name }}
</div>
<div class="grid grid-cols-2 gap-x-4 gap-y-3">
<div class="font-bold">
Destination
</div>
<div>
{{ assignment.destination.product }}
<br>
{{ assignment.destination.hotel }}
</div>
<div class="font-bold">
Einsatztermin
</div>
<div>
{{ assignment.totalPeriod.start|date('d.m.Y') }} - {{ assignment.totalPeriod.end|date('d.m.Y') }}
</div>
</div>
<div class="grid grid-cols-2 gap-x-4 gap-y-3">
{% if assignment.pickup and assignment.pickupDate %}
<div class="font-bold">
Busabfahrt
</div>
<div>
{{ assignment.pickupDate|date('d.m.Y') }}
</div>
<div class="font-bold">
Busbegleitung
</div>
<div>
ab {{ assignment.pickup|bpn_pickup_label }}
</div>
{% endif %}
<div class="font-bold">
Honorar
</div>
<div>
{% if assignment.fees|length %}
{% for fee in assignment.fees %}
{{ fee.name }} {{ fee.value|format_money }}
{% endfor %}
{% else %}
-
{% endif %}
</div>
</div>
</div>
+137 -89
View File
@@ -6,98 +6,146 @@
<h1 class="text-2xl font-bold pb-8"> <h1 class="text-2xl font-bold pb-8">
Einsatzübersicht Einsatzübersicht
</h1> </h1>
<div class="grid grid-cols-3 gap-8 items-start"> <div class="bg-gray-100 rounded-md p-4 mb-8">
<div class="bg-gray-100 rounded-md p-4"> {% include '_partials/_assignment_info_compact.html.twig' %}
{% include '_partials/_assignment_info.html.twig' %} </div>
</div> <div class="col-span-2">
<div class="col-span-2"> <h2 class="font-bold text-lg pb-4">
<h2 class="font-bold text-lg pb-4"> Bewerbungen
Bewerbungen </h2>
</h2> <div class="data-table-wrapper w-full">
<div class="data-table-wrapper w-full"> <div class="data-table-wrapper__inner">
<div class="data-table-wrapper__inner"> <table class="data-table">
<table class="data-table"> <thead>
<thead> <tr>
<th class="w-1/3">
Name
</th>
<th class="w-1/3">
Anmerkungen
</th>
<th class="w-1/6">
Status
</th>
<th class="w-1/6"></th>
</tr>
</thead>
<tbody>
{% for application in applications %}
<tr> <tr>
<th> <td>
Name <a href="{{ path('app_admin_teamer_profile', { 'uuid': application.teamer.uuid, 'r': return_url() }) }}">
</th> {{ application.teamer }}
<th> </a>
Anmerkungen </td>
</th> <td>
<th> {{ application.requests|nl2br|default('-') }}
Status </td>
</th> <td>
<th></th> {{ ('label.application.status.' ~ application.status)|trans }}
</td>
<td>
<div class="flex items-center justify-end space-x-2">
{% if is_granted('DISPOSE', application) %}
<button type="button"
class="btn"
{{ stimulus_controller('modal-button', [], [], {'confirmation-modal': '#confirmation-modal'}) }}
{{ stimulus_action('modal-button', 'confirmation', null, {
'title': 'Bist du sicher?',
'content': 'Möchtest du den Teamer ' ~ application.teamer ~ ' wirklich einteilen?',
'target-url': path('app_admin_application_dispose', { 'uuid': application.uuid })
}) }}>
einteilen
</button>
{% endif %}
{% if is_granted('REJECT', application) %}
<button type="button" class="btn btn--secondary"
{{ stimulus_controller('modal-button', [], [], {'confirmation-modal': '#confirmation-modal'}) }}
{{ stimulus_action('modal-button', 'confirmation', null, {
'title': 'Bist du sicher?',
'content': 'Möchtest du die Bewerbung von ' ~ application.teamer ~ ' wirklich ablehnen?',
'target-url': path('app_admin_application_reject', { 'uuid': application.uuid })
}) }}>
ablehnen
</button>
{% endif %}
{% if is_granted('DELETE', application) %}
<button type="button" class="btn btn--secondary"
{{ stimulus_controller('modal-button', [], [], {'confirmation-modal': '#confirmation-modal'}) }}
{{ stimulus_action('modal-button', 'confirmation', null, {
'title': 'Bist du sicher?',
'content': 'Möchtest du die Bewerbung von ' ~ application.teamer ~ ' wirklich löschen?',
'target-url': path('app_admin_application_delete', { 'uuid': application.uuid })
}) }}>
löschen
</button>
{% endif %}
</div>
</td>
</tr> </tr>
</thead> {% else %}
<tbody> <tr>
{% for application in applications %} <td colspan="3">
<tr> Es liegen keine Bewerbungen vor...
<td> </td>
<a href="{{ path('app_admin_teamer_profile', { 'uuid': application.teamer.uuid, 'r': return_url() }) }}"> </tr>
{{ application.teamer }} {% endfor %}
</a> </tbody>
</td> </table>
<td>
{{ application.requests|nl2br|default('-') }}
</td>
<td>
{{ ('label.application.status.' ~ application.status)|trans }}
</td>
<td>
<div class="flex items-center justify-end space-x-2">
{% if is_granted('DISPOSE', application) %}
<button type="button"
class="btn"
{{ stimulus_controller('modal-button', [], [], {'confirmation-modal': '#confirmation-modal'}) }}
{{ stimulus_action('modal-button', 'confirmation', null, {
'title': 'Bist du sicher?',
'content': 'Möchtest du den Teamer ' ~ application.teamer ~ ' wirklich einteilen?',
'target-url': path('app_admin_application_dispose', { 'uuid': application.uuid })
}) }}>
einteilen
</button>
{% endif %}
{% if is_granted('REJECT', application) %}
<button type="button" class="btn btn--secondary"
{{ stimulus_controller('modal-button', [], [], {'confirmation-modal': '#confirmation-modal'}) }}
{{ stimulus_action('modal-button', 'confirmation', null, {
'title': 'Bist du sicher?',
'content': 'Möchtest du die Bewerbung von ' ~ application.teamer ~ ' wirklich ablehnen?',
'target-url': path('app_admin_application_reject', { 'uuid': application.uuid })
}) }}>
ablehnen
</button>
{% endif %}
{% if is_granted('DELETE', application) %}
<button type="button" class="btn btn--secondary"
{{ stimulus_controller('modal-button', [], [], {'confirmation-modal': '#confirmation-modal'}) }}
{{ stimulus_action('modal-button', 'confirmation', null, {
'title': 'Bist du sicher?',
'content': 'Möchtest du die Bewerbung von ' ~ application.teamer ~ ' wirklich löschen?',
'target-url': path('app_admin_application_delete', { 'uuid': application.uuid })
}) }}>
löschen
</button>
{% endif %}
</div>
</td>
</tr>
{% else %}
<tr>
<td colspan="3">
Bisher liegen keine Bewerbungen vor...
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div> </div>
<a href="{{ returnUrl }}" class="underline">
zurück
</a>
</div> </div>
<h2 class="font-bold text-lg pb-4">
Einteilungen
</h2>
<div class="data-table-wrapper w-full">
<div class="data-table-wrapper__inner">
<table class="data-table">
<thead>
<tr>
<th class="w-1/3">
Name
</th>
<th class="w-1/3">
Anmerkungen
</th>
<th class="w-1/6">
Status
</th>
<th class="w-1/6"></th>
</tr>
</thead>
<tbody>
{% for disposition in dispositions %}
<tr>
<td>
<a href="{{ path('app_admin_teamer_profile', { 'uuid': disposition.teamer.uuid, 'r': return_url() }) }}">
{{ disposition.teamer }}
</a>
</td>
<td>
{{ disposition.remarks|nl2br|default('-') }}
</td>
<td>
{{ ('label.disposition.status.' ~ disposition.status)|trans }}
</td>
<td>
<div class="flex items-center justify-end space-x-2">
</div>
</td>
</tr>
{% else %}
<tr>
<td colspan="3">
Es gibt noch keine Einteilungen...
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<a href="{{ returnUrl }}" class="underline">
zurück
</a>
</div> </div>
{% endblock %} {% endblock %}
+5
View File
@@ -4,6 +4,11 @@ label:
new: neu new: neu
pending: in Bearbeitung pending: in Bearbeitung
rejected: abgelehnt rejected: abgelehnt
disposition:
status:
new: neu
pending: in Prüfung
confirmed: bestätigt
document: document:
type: type:
invoice: Honorarnote invoice: Honorarnote