WIP: Implement disposition process
This commit is contained in:
@@ -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'),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -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 %}
|
||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user