feat: create new bookings or inquiries on behalf of customer
This commit is contained in:
@@ -0,0 +1,29 @@
|
|||||||
|
<?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 Version20260804152215 extends AbstractMigration
|
||||||
|
{
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return 'Make accommodation booking contact fields nullable (filled on the edit page after creation)';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function up(Schema $schema): void
|
||||||
|
{
|
||||||
|
$this->addSql('ALTER TABLE accommodation_booking CHANGE first_name first_name VARCHAR(100) DEFAULT NULL, CHANGE last_name last_name VARCHAR(100) DEFAULT NULL, CHANGE email email VARCHAR(255) DEFAULT NULL, CHANGE group_name group_name VARCHAR(255) DEFAULT NULL');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(Schema $schema): void
|
||||||
|
{
|
||||||
|
$this->addSql('ALTER TABLE accommodation_booking CHANGE group_name group_name VARCHAR(255) NOT NULL, CHANGE first_name first_name VARCHAR(100) NOT NULL, CHANGE last_name last_name VARCHAR(100) NOT NULL, CHANGE email email VARCHAR(255) NOT NULL');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,7 +5,8 @@ declare(strict_types=1);
|
|||||||
namespace App\Controller\Admin\AccommodationBooking;
|
namespace App\Controller\Admin\AccommodationBooking;
|
||||||
|
|
||||||
use App\Entity\Groups\AccommodationBooking;
|
use App\Entity\Groups\AccommodationBooking;
|
||||||
use App\Form\Admin\Groups\AccommodationBookingType;
|
use App\Form\Admin\Groups\AccommodationBookingCreateType;
|
||||||
|
use App\Htmx\HxRedirectResponse;
|
||||||
use App\Service\AccommodationBookingService;
|
use App\Service\AccommodationBookingService;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
@@ -30,8 +31,8 @@ class CreateController extends AbstractController
|
|||||||
{
|
{
|
||||||
$booking = new AccommodationBooking();
|
$booking = new AccommodationBooking();
|
||||||
|
|
||||||
$form = $this->createForm(AccommodationBookingType::class, $booking, [
|
$form = $this->createForm(AccommodationBookingCreateType::class, $booking, [
|
||||||
'with_accommodation' => true,
|
'hx_post' => $request->getRequestUri(),
|
||||||
]);
|
]);
|
||||||
$form->handleRequest($request);
|
$form->handleRequest($request);
|
||||||
|
|
||||||
@@ -40,20 +41,18 @@ class CreateController extends AbstractController
|
|||||||
$this->entityManager->persist($booking);
|
$this->entityManager->persist($booking);
|
||||||
$this->entityManager->flush();
|
$this->entityManager->flush();
|
||||||
|
|
||||||
$this->bookingService->issueAccessLinkForDirectBooking($booking);
|
|
||||||
$this->bookingService->sendCustomerConfirmationEmail($booking);
|
|
||||||
|
|
||||||
$this->addFlash('success', 'Die Buchung wurde erstellt');
|
$this->addFlash('success', 'Die Buchung wurde erstellt');
|
||||||
|
|
||||||
$this->logger->info('Created accommodation booking', [
|
$this->logger->info('Created accommodation booking', [
|
||||||
'id' => $booking->getId(),
|
'id' => $booking->getId(),
|
||||||
'groupName' => $booking->getGroupName(),
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return $this->redirectToRoute('app_admin_accommodationbooking_edit', ['id' => $booking->getId()]);
|
return new HxRedirectResponse(
|
||||||
|
$this->generateUrl('app_admin_accommodationbooking_edit', ['id' => $booking->getId()])
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->render('admin/accommodation_booking/create.html.twig', [
|
return $this->render('admin/accommodation_booking/modal_create.html.twig', [
|
||||||
'form' => $form,
|
'form' => $form,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,19 +84,24 @@ class AccommodationBooking implements BlameableEntityInterface, TimestampableEnt
|
|||||||
#[ORM\Column(type: Types::DATETIME_IMMUTABLE, nullable: true)]
|
#[ORM\Column(type: Types::DATETIME_IMMUTABLE, nullable: true)]
|
||||||
private ?\DateTimeImmutable $acceptedAt = null;
|
private ?\DateTimeImmutable $acceptedAt = null;
|
||||||
|
|
||||||
#[ORM\Column(length: 255)]
|
#[ORM\Column(length: 255, nullable: true)]
|
||||||
|
#[Assert\NotBlank(groups: ['edit'])]
|
||||||
private ?string $groupName = null;
|
private ?string $groupName = null;
|
||||||
|
|
||||||
#[ORM\Column(length: 10, nullable: true)]
|
#[ORM\Column(length: 10, nullable: true)]
|
||||||
private ?string $salutation = null;
|
private ?string $salutation = null;
|
||||||
|
|
||||||
#[ORM\Column(length: 100)]
|
#[ORM\Column(length: 100, nullable: true)]
|
||||||
|
#[Assert\NotBlank(groups: ['edit'])]
|
||||||
private ?string $firstName = null;
|
private ?string $firstName = null;
|
||||||
|
|
||||||
#[ORM\Column(length: 100)]
|
#[ORM\Column(length: 100, nullable: true)]
|
||||||
|
#[Assert\NotBlank(groups: ['edit'])]
|
||||||
private ?string $lastName = null;
|
private ?string $lastName = null;
|
||||||
|
|
||||||
#[ORM\Column(length: 255)]
|
#[ORM\Column(length: 255, nullable: true)]
|
||||||
|
#[Assert\NotBlank(groups: ['edit'])]
|
||||||
|
#[Assert\Email(groups: ['edit'])]
|
||||||
private ?string $email = null;
|
private ?string $email = null;
|
||||||
|
|
||||||
#[ORM\Column(length: 50, nullable: true)]
|
#[ORM\Column(length: 50, nullable: true)]
|
||||||
@@ -371,7 +376,7 @@ class AccommodationBooking implements BlameableEntityInterface, TimestampableEnt
|
|||||||
return $this->groupName;
|
return $this->groupName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setGroupName(string $groupName): self
|
public function setGroupName(?string $groupName): self
|
||||||
{
|
{
|
||||||
$this->groupName = $groupName;
|
$this->groupName = $groupName;
|
||||||
|
|
||||||
@@ -395,7 +400,7 @@ class AccommodationBooking implements BlameableEntityInterface, TimestampableEnt
|
|||||||
return $this->firstName;
|
return $this->firstName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setFirstName(string $firstName): self
|
public function setFirstName(?string $firstName): self
|
||||||
{
|
{
|
||||||
$this->firstName = $firstName;
|
$this->firstName = $firstName;
|
||||||
|
|
||||||
@@ -407,7 +412,7 @@ class AccommodationBooking implements BlameableEntityInterface, TimestampableEnt
|
|||||||
return $this->lastName;
|
return $this->lastName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setLastName(string $lastName): self
|
public function setLastName(?string $lastName): self
|
||||||
{
|
{
|
||||||
$this->lastName = $lastName;
|
$this->lastName = $lastName;
|
||||||
|
|
||||||
@@ -419,7 +424,7 @@ class AccommodationBooking implements BlameableEntityInterface, TimestampableEnt
|
|||||||
return $this->email;
|
return $this->email;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setEmail(string $email): self
|
public function setEmail(?string $email): self
|
||||||
{
|
{
|
||||||
$this->email = $email;
|
$this->email = $email;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Form\Admin\Groups;
|
||||||
|
|
||||||
|
use App\Entity\Groups\Accommodation;
|
||||||
|
use App\Entity\Groups\AccommodationBooking;
|
||||||
|
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||||
|
use Symfony\Component\Form\AbstractType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\DateType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lean form for creating a booking. The available board and additional services can only be
|
||||||
|
* resolved once accommodation and date range are known, so they are offered on the edit page.
|
||||||
|
*
|
||||||
|
* @extends AbstractType<AccommodationBooking>
|
||||||
|
*/
|
||||||
|
class AccommodationBookingCreateType extends AbstractType
|
||||||
|
{
|
||||||
|
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||||
|
{
|
||||||
|
$builder
|
||||||
|
->add('accommodation', EntityType::class, [
|
||||||
|
'class' => Accommodation::class,
|
||||||
|
'choice_label' => 'name',
|
||||||
|
'label' => 'Gruppenhaus',
|
||||||
|
])
|
||||||
|
->add('dateFrom', DateType::class, [
|
||||||
|
'label' => 'Anreise',
|
||||||
|
'widget' => 'single_text',
|
||||||
|
])
|
||||||
|
->add('dateTo', DateType::class, [
|
||||||
|
'label' => 'Abreise',
|
||||||
|
'widget' => 'single_text',
|
||||||
|
])
|
||||||
|
->add('paxCount', IntegerType::class, [
|
||||||
|
'label' => 'Anzahl Personen',
|
||||||
|
])
|
||||||
|
->add('minorsCount', IntegerType::class, [
|
||||||
|
'label' => 'davon Kinder (0–3 Jahre)',
|
||||||
|
])
|
||||||
|
->add('childrenCount', IntegerType::class, [
|
||||||
|
'label' => 'davon Kinder',
|
||||||
|
])
|
||||||
|
->add('isInquiry', CheckboxType::class, [
|
||||||
|
'label' => 'Anfrage (nicht bindend)',
|
||||||
|
'required' => false,
|
||||||
|
])
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function configureOptions(OptionsResolver $resolver): void
|
||||||
|
{
|
||||||
|
$resolver->setDefaults([
|
||||||
|
'data_class' => AccommodationBooking::class,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,6 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace App\Form\Admin\Groups;
|
namespace App\Form\Admin\Groups;
|
||||||
|
|
||||||
use App\Entity\Groups\Accommodation;
|
|
||||||
use App\Entity\Groups\AccommodationBooking;
|
use App\Entity\Groups\AccommodationBooking;
|
||||||
use App\Entity\Groups\AdditionalService;
|
use App\Entity\Groups\AdditionalService;
|
||||||
use App\Entity\Groups\BoardService;
|
use App\Entity\Groups\BoardService;
|
||||||
@@ -26,14 +25,6 @@ class AccommodationBookingType extends AbstractType
|
|||||||
{
|
{
|
||||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||||
{
|
{
|
||||||
if ($options['with_accommodation']) {
|
|
||||||
$builder->add('accommodation', EntityType::class, [
|
|
||||||
'class' => Accommodation::class,
|
|
||||||
'choice_label' => 'name',
|
|
||||||
'label' => 'Gruppenhaus',
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
$builder
|
$builder
|
||||||
->add('groupName', TextType::class, [
|
->add('groupName', TextType::class, [
|
||||||
'label' => 'Gruppenname',
|
'label' => 'Gruppenname',
|
||||||
@@ -158,14 +149,13 @@ class AccommodationBookingType extends AbstractType
|
|||||||
{
|
{
|
||||||
$resolver->setDefaults([
|
$resolver->setDefaults([
|
||||||
'data_class' => AccommodationBooking::class,
|
'data_class' => AccommodationBooking::class,
|
||||||
'with_accommodation' => false,
|
'validation_groups' => ['Default', 'edit'],
|
||||||
'max_adolescent_age' => 0,
|
'max_adolescent_age' => 0,
|
||||||
'board_services' => [],
|
'board_services' => [],
|
||||||
'additional_services' => [],
|
'additional_services' => [],
|
||||||
'current_board_service' => null,
|
'current_board_service' => null,
|
||||||
'current_additional_services' => [],
|
'current_additional_services' => [],
|
||||||
]);
|
]);
|
||||||
$resolver->setAllowedTypes('with_accommodation', 'bool');
|
|
||||||
$resolver->setAllowedTypes('max_adolescent_age', 'int');
|
$resolver->setAllowedTypes('max_adolescent_age', 'int');
|
||||||
$resolver->setAllowedTypes('board_services', 'array');
|
$resolver->setAllowedTypes('board_services', 'array');
|
||||||
$resolver->setAllowedTypes('additional_services', 'array');
|
$resolver->setAllowedTypes('additional_services', 'array');
|
||||||
|
|||||||
@@ -1,53 +0,0 @@
|
|||||||
{% extends 'layout_admin.html.twig' %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<twig:page:heading>Neue Buchung</twig:page:heading>
|
|
||||||
|
|
||||||
{% form_theme form 'forms_admin.html.twig' %}
|
|
||||||
|
|
||||||
{{ form_start(form) }}
|
|
||||||
<div class="grid lg:grid-cols-2 gap-y-4 lg:gap-x-8">
|
|
||||||
<div class="lg:col-span-2">
|
|
||||||
{{ form_row(form.accommodation) }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h2 class="lg:col-span-2 text-base font-bold pt-2">Kontaktdaten</h2>
|
|
||||||
{{ form_row(form.groupName) }}
|
|
||||||
{{ form_row(form.salutation) }}
|
|
||||||
{{ form_row(form.firstName) }}
|
|
||||||
{{ form_row(form.lastName) }}
|
|
||||||
{{ form_row(form.email) }}
|
|
||||||
{{ form_row(form.phone) }}
|
|
||||||
{{ form_row(form.street) }}
|
|
||||||
{{ form_row(form.zip) }}
|
|
||||||
{{ form_row(form.city) }}
|
|
||||||
|
|
||||||
<h2 class="lg:col-span-2 text-base font-bold pt-2">Reisedaten</h2>
|
|
||||||
{{ form_row(form.dateFrom) }}
|
|
||||||
{{ form_row(form.dateTo) }}
|
|
||||||
{{ form_row(form.paxCount) }}
|
|
||||||
{{ form_row(form.minorsCount) }}
|
|
||||||
{{ form_row(form.childrenCount) }}
|
|
||||||
|
|
||||||
<h2 class="lg:col-span-2 text-base font-bold pt-2">Status & Rabatt</h2>
|
|
||||||
<div class="lg:col-span-2">
|
|
||||||
{{ form_row(form.isInquiry) }}
|
|
||||||
</div>
|
|
||||||
{{ form_row(form.accommodationDiscount) }}
|
|
||||||
{{ form_row(form.boardServiceDiscount) }}
|
|
||||||
{{ form_row(form.additionalServicesDiscount) }}
|
|
||||||
<div class="lg:col-span-2">
|
|
||||||
{{ form_row(form.remarks) }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex justify-between pt-8">
|
|
||||||
<a href="{{ path('app_admin_accommodationbooking') }}" class="button button--secondary button--small">
|
|
||||||
zurück
|
|
||||||
</a>
|
|
||||||
<button type="submit" class="button button--primary button--small">
|
|
||||||
speichern
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
{{ form_rest(form) }}
|
|
||||||
{{ form_end(form) }}
|
|
||||||
{% endblock %}
|
|
||||||
@@ -93,8 +93,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-end">
|
<div class="flex justify-end">
|
||||||
<a href="{{ path('app_admin_accommodationbooking_create') }}" class="button button--primary button--small">
|
<button type="button"
|
||||||
|
hx-get="{{ path('app_admin_accommodationbooking_create') }}"
|
||||||
|
hx-target="body"
|
||||||
|
hx-swap="beforeend"
|
||||||
|
class="button button--primary button--small">
|
||||||
neu
|
neu
|
||||||
</a>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
{% extends 'htmx_modal_admin.html.twig' %}
|
||||||
|
{% form_theme form 'forms_admin.html.twig' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="text-xl font-bold pb-4">
|
||||||
|
Neue Buchung
|
||||||
|
</div>
|
||||||
|
{{ form_start(form) }}
|
||||||
|
<div class="grid lg:grid-cols-2 gap-y-4 lg:gap-x-8">
|
||||||
|
<div class="lg:col-span-2">
|
||||||
|
{{ form_row(form.accommodation) }}
|
||||||
|
</div>
|
||||||
|
{{ form_row(form.dateFrom) }}
|
||||||
|
{{ form_row(form.dateTo) }}
|
||||||
|
{{ form_row(form.paxCount) }}
|
||||||
|
{{ form_row(form.minorsCount) }}
|
||||||
|
{{ form_row(form.childrenCount) }}
|
||||||
|
<div class="lg:col-span-2">
|
||||||
|
{{ form_row(form.isInquiry) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-end pt-8">
|
||||||
|
<button type="submit" class="button button--primary button--small">
|
||||||
|
speichern
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{{ form_rest(form) }}
|
||||||
|
{{ form_end(form) }}
|
||||||
|
{% endblock %}
|
||||||
Reference in New Issue
Block a user