feat: accommodation booking type and status as dedicated properties

This commit is contained in:
Björn Fromme
2026-08-06 08:52:09 +02:00
parent 2cfa87f44b
commit aa8fa5c1b2
22 changed files with 218 additions and 151 deletions
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

+46
View File
@@ -0,0 +1,46 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20260806120000 extends AbstractMigration
{
public function getDescription(): string
{
return 'Split the accommodation booking status into a status and a type';
}
public function up(Schema $schema): void
{
$this->addSql('ALTER TABLE accommodation_booking ADD type VARCHAR(20) DEFAULT NULL');
// An old 'booking' row with an accepted_at came from an accepted offer, so it is an
// Anfrage; without one it was placed directly. Rows in 'draft'/'discarded' carry no
// trace of their kind any more — they are mapped to 'inquiry', which is where the
// office path produces them.
$this->addSql("UPDATE accommodation_booking SET type = CASE
WHEN status = 'booking' AND accepted_at IS NULL THEN 'booking'
ELSE 'inquiry' END");
$this->addSql("UPDATE accommodation_booking SET status = CASE
WHEN status = 'inquiry' THEN 'open'
WHEN status = 'booking' THEN 'accepted'
ELSE status END");
$this->addSql('ALTER TABLE accommodation_booking CHANGE type type VARCHAR(20) NOT NULL');
}
public function down(Schema $schema): void
{
$this->addSql("UPDATE accommodation_booking SET status = CASE
WHEN status IN ('draft', 'discarded') THEN status
WHEN type = 'inquiry' AND status = 'open' THEN 'inquiry'
ELSE 'booking' END");
$this->addSql('ALTER TABLE accommodation_booking DROP type');
}
}
@@ -127,9 +127,9 @@ class EditController extends AbstractController
$this->entityManager->persist($booking); $this->entityManager->persist($booking);
$this->entityManager->flush(); $this->entityManager->flush();
// Entering Anfrage or Buchung is what the customer needs to hear about: the offer // Entering Offen or Bestätigt is what the customer needs to hear about: the offer
// becomes viewable, or the booking is confirmed. Entwurf and Absage stay silent. // becomes viewable, or the booking is confirmed. Entwurf and Absage stay silent.
$notifiableStatuses = [AccommodationBookingStatus::Inquiry, AccommodationBookingStatus::Booking]; $notifiableStatuses = [AccommodationBookingStatus::Open, AccommodationBookingStatus::Accepted];
if ($previousStatus !== $booking->getStatus() && in_array($booking->getStatus(), $notifiableStatuses, true)) { if ($previousStatus !== $booking->getStatus() && in_array($booking->getStatus(), $notifiableStatuses, true)) {
$this->bookingService->issueAccessLink($booking); $this->bookingService->issueAccessLink($booking);
$this->bookingService->sendCustomerConfirmationEmail($booking); $this->bookingService->sendCustomerConfirmationEmail($booking);
+1 -1
View File
@@ -93,7 +93,7 @@ class OfferController extends AbstractController
return $this->redirectToOfferPage($request, $uuid); return $this->redirectToOfferPage($request, $uuid);
} }
if (!$booking->isInquiry()) { if (!$booking->isInquiry() || !$booking->isOpen()) {
return $this->redirectToOfferPage($request, $uuid); return $this->redirectToOfferPage($request, $uuid);
} }
+1 -1
View File
@@ -153,7 +153,7 @@ class Step4Controller extends AbstractAccommodationController
{ {
$prices = $this->bookingService->loadPrices($dto, $accommodation); $prices = $this->bookingService->loadPrices($dto, $accommodation);
$booking = $this->bookingService->finalizeBooking($dto, $accommodation, $prices, $services); $booking = $this->bookingService->finalizeBooking($dto, $accommodation, $prices, $services);
$this->addFlash('groups_booking_result', $booking->getStatus()->value); $this->addFlash('groups_booking_result', $booking->getType()->value);
$this->sessionManager->clear($request); $this->sessionManager->clear($request);
} }
} }
+35 -4
View File
@@ -10,6 +10,7 @@ use App\Entity\TimestampableEntity;
use App\Entity\TimestampableEntityInterface; use App\Entity\TimestampableEntityInterface;
use App\Entity\User; use App\Entity\User;
use App\Enum\Groups\AccommodationBookingStatus; use App\Enum\Groups\AccommodationBookingStatus;
use App\Enum\Groups\AccommodationBookingType;
use App\Enum\Groups\AdditionalServiceType; use App\Enum\Groups\AdditionalServiceType;
use App\Repository\Groups\AccommodationBookingRepository; use App\Repository\Groups\AccommodationBookingRepository;
use Doctrine\DBAL\Types\Types; use Doctrine\DBAL\Types\Types;
@@ -79,6 +80,14 @@ class AccommodationBooking implements BlameableEntityInterface, TimestampableEnt
#[ORM\Column(length: 20, enumType: AccommodationBookingStatus::class)] #[ORM\Column(length: 20, enumType: AccommodationBookingStatus::class)]
private AccommodationBookingStatus $status = AccommodationBookingStatus::Draft; private AccommodationBookingStatus $status = AccommodationBookingStatus::Draft;
/**
* What kind of record this is — an offer the customer still has to accept, or a
* directly placed, binding booking. Independent of the status: an accepted offer
* keeps its Inquiry type, which is how offer-originated bookings stay recognisable.
*/
#[ORM\Column(length: 20, enumType: AccommodationBookingType::class)]
private AccommodationBookingType $type = AccommodationBookingType::Inquiry;
#[ORM\Column(type: Types::DATETIME_IMMUTABLE, nullable: true)] #[ORM\Column(type: Types::DATETIME_IMMUTABLE, nullable: true)]
private ?\DateTimeImmutable $accessLinkIssuedAt = null; private ?\DateTimeImmutable $accessLinkIssuedAt = null;
@@ -353,14 +362,14 @@ class AccommodationBooking implements BlameableEntityInterface, TimestampableEnt
return AccommodationBookingStatus::Draft === $this->status; return AccommodationBookingStatus::Draft === $this->status;
} }
public function isInquiry(): bool public function isOpen(): bool
{ {
return AccommodationBookingStatus::Inquiry === $this->status; return AccommodationBookingStatus::Open === $this->status;
} }
public function isBooking(): bool public function isAccepted(): bool
{ {
return AccommodationBookingStatus::Booking === $this->status; return AccommodationBookingStatus::Accepted === $this->status;
} }
public function isDiscarded(): bool public function isDiscarded(): bool
@@ -368,6 +377,28 @@ class AccommodationBooking implements BlameableEntityInterface, TimestampableEnt
return AccommodationBookingStatus::Discarded === $this->status; return AccommodationBookingStatus::Discarded === $this->status;
} }
public function getType(): AccommodationBookingType
{
return $this->type;
}
public function setType(AccommodationBookingType $type): self
{
$this->type = $type;
return $this;
}
public function isInquiry(): bool
{
return AccommodationBookingType::Inquiry === $this->type;
}
public function isBooking(): bool
{
return AccommodationBookingType::Booking === $this->type;
}
public function getAccessLinkIssuedAt(): ?\DateTimeImmutable public function getAccessLinkIssuedAt(): ?\DateTimeImmutable
{ {
return $this->accessLinkIssuedAt; return $this->accessLinkIssuedAt;
@@ -5,16 +5,16 @@ namespace App\Enum\Groups;
enum AccommodationBookingStatus: string enum AccommodationBookingStatus: string
{ {
case Draft = 'draft'; case Draft = 'draft';
case Inquiry = 'inquiry'; case Open = 'open';
case Booking = 'booking'; case Accepted = 'accepted';
case Discarded = 'discarded'; case Discarded = 'discarded';
public function label(): string public function label(): string
{ {
return match ($this) { return match ($this) {
self::Draft => 'Entwurf', self::Draft => 'Entwurf',
self::Inquiry => 'Anfrage', self::Open => 'Offen',
self::Booking => 'Buchung', self::Accepted => 'Bestätigt',
self::Discarded => 'Absage', self::Discarded => 'Absage',
}; };
} }
@@ -0,0 +1,17 @@
<?php
namespace App\Enum\Groups;
enum AccommodationBookingType: string
{
case Inquiry = 'inquiry';
case Booking = 'booking';
public function label(): string
{
return match ($this) {
self::Inquiry => 'Anfrage',
self::Booking => 'Buchung',
};
}
}
@@ -53,8 +53,8 @@ class AccommodationBookingCreateType extends AbstractType
'class' => AccommodationBookingStatus::class, 'class' => AccommodationBookingStatus::class,
'choices' => [ 'choices' => [
AccommodationBookingStatus::Draft, AccommodationBookingStatus::Draft,
AccommodationBookingStatus::Inquiry, AccommodationBookingStatus::Open,
AccommodationBookingStatus::Booking, AccommodationBookingStatus::Accepted,
], ],
'choice_label' => fn (AccommodationBookingStatus $status) => $status->label(), 'choice_label' => fn (AccommodationBookingStatus $status) => $status->label(),
]) ])
@@ -17,6 +17,9 @@ final readonly class AccommodationBookingApiResponse
#[Groups(['api:single'])] #[Groups(['api:single'])]
public string $status; public string $status;
#[Groups(['api:single'])]
public string $type;
#[Groups(['api:single'])] #[Groups(['api:single'])]
#[Context([DateTimeNormalizer::FORMAT_KEY => 'Y-m-d'])] #[Context([DateTimeNormalizer::FORMAT_KEY => 'Y-m-d'])]
public ?\DateTimeImmutable $dateFrom; public ?\DateTimeImmutable $dateFrom;
@@ -89,6 +92,7 @@ final readonly class AccommodationBookingApiResponse
{ {
$this->uuid = $booking->getUuid(); $this->uuid = $booking->getUuid();
$this->status = $booking->getStatus()->value; $this->status = $booking->getStatus()->value;
$this->type = $booking->getType()->value;
$this->dateFrom = $booking->getDateFrom(); $this->dateFrom = $booking->getDateFrom();
$this->dateTo = $booking->getDateTo(); $this->dateTo = $booking->getDateTo();
$this->nights = $booking->getNights(); $this->nights = $booking->getNights();
+15 -12
View File
@@ -11,6 +11,7 @@ use App\Entity\Groups\AccommodationPrice;
use App\Entity\Groups\AdditionalService; use App\Entity\Groups\AdditionalService;
use App\Entity\Groups\BoardService; use App\Entity\Groups\BoardService;
use App\Enum\Groups\AccommodationBookingStatus; use App\Enum\Groups\AccommodationBookingStatus;
use App\Enum\Groups\AccommodationBookingType;
use App\Form\Model\AccommodationBookingDto; use App\Form\Model\AccommodationBookingDto;
use App\Model\AccommodationBookingQueryParams; use App\Model\AccommodationBookingQueryParams;
use App\Model\CmsHotelData; use App\Model\CmsHotelData;
@@ -265,11 +266,11 @@ class AccommodationBookingService
$booking->setPaxCount($dto->paxCount); $booking->setPaxCount($dto->paxCount);
$booking->setMinorsCount($dto->minorsCount); $booking->setMinorsCount($dto->minorsCount);
$booking->setChildrenCount($dto->childrenCount); $booking->setChildrenCount($dto->childrenCount);
$booking->setStatus( // An inquiry arrives live — the office has nothing to prepare first, so it goes
$dto->forceInquiry || $this->computeInquiryStatus($dto, $prices)->isInquiry // straight to Offen. A direct booking is binding on arrival, hence Bestätigt.
? AccommodationBookingStatus::Inquiry $isInquiry = $dto->forceInquiry || $this->computeInquiryStatus($dto, $prices)->isInquiry;
: AccommodationBookingStatus::Booking $booking->setType($isInquiry ? AccommodationBookingType::Inquiry : AccommodationBookingType::Booking);
); $booking->setStatus($isInquiry ? AccommodationBookingStatus::Open : AccommodationBookingStatus::Accepted);
// Freeze board service as scalar fields (no FK) // Freeze board service as scalar fields (no FK)
if (null !== $dto->selectedBoardServiceId) { if (null !== $dto->selectedBoardServiceId) {
@@ -432,9 +433,9 @@ class AccommodationBookingService
} }
/** /**
* Issues the access link only for a confirmed booking. Inquiries from the public flow * Issues the access link only for a direct booking. Inquiries from the public flow
* deliberately get no link — the office prepares the offer first and issues the link * deliberately get no link — the office prepares the offer first and issues the link
* when it switches the booking to Anfrage. * when it publishes the offer.
*/ */
public function issueAccessLinkForDirectBooking(AccommodationBooking $booking): void public function issueAccessLinkForDirectBooking(AccommodationBooking $booking): void
{ {
@@ -452,7 +453,7 @@ class AccommodationBookingService
*/ */
public function sendCustomerConfirmationEmail(AccommodationBooking $booking): void public function sendCustomerConfirmationEmail(AccommodationBooking $booking): void
{ {
if (!$booking->isInquiry()) { if ($booking->isAccepted()) {
$subject = 'Deine Buchung ist bestätigt'; $subject = 'Deine Buchung ist bestätigt';
} elseif (null !== $booking->getAccessLinkIssuedAt()) { } elseif (null !== $booking->getAccessLinkIssuedAt()) {
$subject = 'Dein Angebot ist bereit'; $subject = 'Dein Angebot ist bereit';
@@ -481,16 +482,18 @@ class AccommodationBookingService
} }
/** /**
* Transitions a booking from inquiry to booking and notifies office and customer. * Accepts an open offer and notifies office and customer. The type stays Anfrage —
* Idempotent — a no-op (including no emails) if the booking is already accepted. * only the status moves to Bestätigt, so an offer-originated booking remains
* distinguishable from a direct one.
* Idempotent — a no-op (including no emails) if the offer is not open any more.
*/ */
public function acceptBooking(AccommodationBooking $booking): void public function acceptBooking(AccommodationBooking $booking): void
{ {
if (!$booking->isInquiry()) { if (!$booking->isInquiry() || !$booking->isOpen()) {
return; return;
} }
$booking->setStatus(AccommodationBookingStatus::Booking); $booking->setStatus(AccommodationBookingStatus::Accepted);
$booking->setAcceptedAt(new \DateTimeImmutable()); $booking->setAcceptedAt(new \DateTimeImmutable());
$this->entityManager->flush(); $this->entityManager->flush();
@@ -60,7 +60,7 @@
{% endif %} {% endif %}
</td> </td>
<td> <td>
{{ booking.status.label }} {{ booking.type.label }} · {{ booking.status.label }}
</td> </td>
<td> <td>
{% set discounts = [] %} {% set discounts = [] %}
@@ -2,7 +2,7 @@
{% block content %} {% block content %}
<twig:page:heading> <twig:page:heading>
{{ booking.status.label }} {{ booking.type.label }} · {{ booking.status.label }}
{{ booking.groupName }} {{ booking.groupName }}
</twig:page:heading> </twig:page:heading>
@@ -53,6 +53,8 @@
<dt class="font-medium">davon Kinder (4{{ booking.accommodation.maxAdolescentAge }} Jahre)</dt> <dt class="font-medium">davon Kinder (4{{ booking.accommodation.maxAdolescentAge }} Jahre)</dt>
<dd>{{ booking.childrenCount }}</dd> <dd>{{ booking.childrenCount }}</dd>
{% endif %} {% endif %}
<dt class="font-medium">Art</dt>
<dd>{{ booking.type.label }}</dd>
<dt class="font-medium">Status</dt> <dt class="font-medium">Status</dt>
<dd>{{ booking.status.label }}</dd> <dd>{{ booking.status.label }}</dd>
{% if booking.acceptedAt is not null %} {% if booking.acceptedAt is not null %}
@@ -2,11 +2,11 @@
{% block body %} {% block body %}
{% if accessLink %} {% if accessLink %}
<h1>{{ booking.inquiry ? 'Dein Angebot ist bereit' : 'Deine Buchung ist bestätigt' }}</h1> <h1>{{ booking.accepted ? 'Deine Buchung ist bestätigt' : 'Dein Angebot ist bereit' }}</h1>
<p> <p>
Hallo {{ booking.firstName }}, Hallo {{ booking.firstName }},
{% if booking.inquiry %} {% if not booking.accepted %}
vielen Dank für deine Anfrage! Wir haben sie geprüft — dein Angebot wartet auf dich. vielen Dank für deine Anfrage! Wir haben sie geprüft — dein Angebot wartet auf dich.
{% else %} {% else %}
vielen Dank! Deine Buchung ist bei uns eingegangen und bestätigt. vielen Dank! Deine Buchung ist bei uns eingegangen und bestätigt.
@@ -15,7 +15,7 @@
<p> <p>
<a href="{{ accessLink }}" class="button"> <a href="{{ accessLink }}" class="button">
{{ booking.inquiry ? 'Angebot ansehen & bestätigen' : 'Buchung ansehen' }} {{ booking.accepted ? 'Buchung ansehen' : 'Angebot ansehen & bestätigen' }}
</a> </a>
</p> </p>
{% else %} {% else %}
+4 -4
View File
@@ -24,7 +24,7 @@
{% import _self as macros %} {% import _self as macros %}
{% block title %}{{ booking.inquiry ? 'Dein Angebot' : 'Deine Buchung' }}{% endblock %} {% block title %}{{ booking.accepted ? 'Deine Buchung' : 'Dein Angebot' }}{% endblock %}
{% block background %}bg-outer bg-outer--summer{% endblock %} {% block background %}bg-outer bg-outer--summer{% endblock %}
@@ -52,7 +52,7 @@
<hgroup> <hgroup>
<h1 class="text-2xl lg:text-4xl font-bold mb-2"> <h1 class="text-2xl lg:text-4xl font-bold mb-2">
{{ booking.inquiry ? 'Dein Angebot' : 'Deine Buchung' }} {{ booking.accepted ? 'Deine Buchung' : 'Dein Angebot' }}
</h1> </h1>
<p class="text-2xl"> <p class="text-2xl">
{{ ctx.accommodation.name }} {{ ctx.accommodation.name }}
@@ -63,7 +63,7 @@
{% if booking.boardServiceLabel or booking.additionalServices | length > 0 %} {% if booking.boardServiceLabel or booking.additionalServices | length > 0 %}
<div> <div>
<h2 class="text-xl font-bold mb-2"> <h2 class="text-xl font-bold mb-2">
{{ booking.inquiry ? 'Angefragte' : 'Gebuchte' }} Leistungen {{ booking.accepted ? 'Gebuchte' : 'Angefragte' }} Leistungen
</h2> </h2>
<ul class="list-disc pl-4"> <ul class="list-disc pl-4">
{% if booking.boardServiceLabel %} {% if booking.boardServiceLabel %}
@@ -76,7 +76,7 @@
</div> </div>
{% endif %} {% endif %}
{% if booking.inquiry %} {% if booking.inquiry and booking.open %}
<div> <div>
<button type="button" <button type="button"
class="button button--primary w-full" class="button button--primary w-full"
@@ -25,32 +25,32 @@ use Symfony\Component\HttpFoundation\Response;
*/ */
class EditControllerTest extends TestCase class EditControllerTest extends TestCase
{ {
public function testTransitionToInquiryIssuesAccessLinkAndSendsCustomerEmail(): void public function testTransitionToOpenIssuesAccessLinkAndSendsCustomerEmail(): void
{ {
$bookingService = $this->assertNotified(AccommodationBookingStatus::Inquiry); $bookingService = $this->assertNotified(AccommodationBookingStatus::Open);
$this->submitStatusChange(AccommodationBookingStatus::Draft, AccommodationBookingStatus::Inquiry, $bookingService); $this->submitStatusChange(AccommodationBookingStatus::Draft, AccommodationBookingStatus::Open, $bookingService);
} }
public function testTransitionToBookingIssuesAccessLinkAndSendsCustomerEmail(): void public function testTransitionToAcceptedIssuesAccessLinkAndSendsCustomerEmail(): void
{ {
$bookingService = $this->assertNotified(AccommodationBookingStatus::Booking); $bookingService = $this->assertNotified(AccommodationBookingStatus::Accepted);
$this->submitStatusChange(AccommodationBookingStatus::Draft, AccommodationBookingStatus::Booking, $bookingService); $this->submitStatusChange(AccommodationBookingStatus::Draft, AccommodationBookingStatus::Accepted, $bookingService);
} }
public function testTransitionToDiscardedSendsNothing(): void public function testTransitionToDiscardedSendsNothing(): void
{ {
$bookingService = $this->assertNotNotified(); $bookingService = $this->assertNotNotified();
$this->submitStatusChange(AccommodationBookingStatus::Inquiry, AccommodationBookingStatus::Discarded, $bookingService); $this->submitStatusChange(AccommodationBookingStatus::Open, AccommodationBookingStatus::Discarded, $bookingService);
} }
public function testSavingWithoutStatusChangeSendsNothing(): void public function testSavingWithoutStatusChangeSendsNothing(): void
{ {
$bookingService = $this->assertNotNotified(); $bookingService = $this->assertNotNotified();
$this->submitStatusChange(AccommodationBookingStatus::Booking, AccommodationBookingStatus::Booking, $bookingService); $this->submitStatusChange(AccommodationBookingStatus::Accepted, AccommodationBookingStatus::Accepted, $bookingService);
} }
private function assertNotified(AccommodationBookingStatus $expected): AccommodationBookingService private function assertNotified(AccommodationBookingStatus $expected): AccommodationBookingService
@@ -8,6 +8,7 @@ use App\Controller\Api\AccommodationBookingController;
use App\Entity\Groups\Accommodation; use App\Entity\Groups\Accommodation;
use App\Entity\Groups\AccommodationBooking; use App\Entity\Groups\AccommodationBooking;
use App\Enum\Groups\AccommodationBookingStatus; use App\Enum\Groups\AccommodationBookingStatus;
use App\Enum\Groups\AccommodationBookingType;
use App\Repository\Groups\AccommodationBookingRepository; use App\Repository\Groups\AccommodationBookingRepository;
use App\Service\AccommodationBookingBreakdownCalculator; use App\Service\AccommodationBookingBreakdownCalculator;
use App\Service\AccommodationBookingService; use App\Service\AccommodationBookingService;
@@ -55,7 +56,8 @@ class AccommodationBookingControllerTest extends TestCase
$booking->setAccommodationDiscount(10); $booking->setAccommodationDiscount(10);
$booking->setBoardServiceDiscount(20); $booking->setBoardServiceDiscount(20);
$booking->setAdditionalServicesDiscount(30); $booking->setAdditionalServicesDiscount(30);
$booking->setStatus(AccommodationBookingStatus::Booking); $booking->setStatus(AccommodationBookingStatus::Accepted);
$booking->setType(AccommodationBookingType::Booking);
$booking->setAcceptedAt(new \DateTimeImmutable('2026-07-15T10:00:00+00:00')); $booking->setAcceptedAt(new \DateTimeImmutable('2026-07-15T10:00:00+00:00'));
$bookingRepository = $this->createMock(AccommodationBookingRepository::class); $bookingRepository = $this->createMock(AccommodationBookingRepository::class);
@@ -85,7 +87,8 @@ class AccommodationBookingControllerTest extends TestCase
self::assertSame(Response::HTTP_OK, $response->getStatusCode()); self::assertSame(Response::HTTP_OK, $response->getStatusCode());
self::assertSame([ self::assertSame([
'uuid' => $booking->getUuid(), 'uuid' => $booking->getUuid(),
'status' => 'booking', 'status' => 'accepted',
'type' => 'booking',
'dateFrom' => '2026-07-20', 'dateFrom' => '2026-07-20',
'dateTo' => '2026-07-25', 'dateTo' => '2026-07-25',
'nights' => 5, 'nights' => 5,
@@ -135,7 +138,7 @@ class AccommodationBookingControllerTest extends TestCase
self::assertArrayNotHasKey('currency', $payload['accommodation']); self::assertArrayNotHasKey('currency', $payload['accommodation']);
} }
public function testSingleReturnsInquiryStatusWhenBookingIsInquiry(): void public function testSingleReturnsOpenStatusAndInquiryTypeForAnOffer(): void
{ {
$booking = new AccommodationBooking(); $booking = new AccommodationBooking();
$booking->setDateFrom(new \DateTimeImmutable('2026-08-01')); $booking->setDateFrom(new \DateTimeImmutable('2026-08-01'));
@@ -144,7 +147,8 @@ class AccommodationBookingControllerTest extends TestCase
$booking->setFirstName('Tom'); $booking->setFirstName('Tom');
$booking->setLastName('Beispiel'); $booking->setLastName('Beispiel');
$booking->setEmail('[email protected]'); $booking->setEmail('[email protected]');
$booking->setStatus(AccommodationBookingStatus::Inquiry); $booking->setStatus(AccommodationBookingStatus::Open);
$booking->setType(AccommodationBookingType::Inquiry);
$bookingRepository = $this->createMock(AccommodationBookingRepository::class); $bookingRepository = $this->createMock(AccommodationBookingRepository::class);
$bookingRepository $bookingRepository
@@ -160,7 +164,8 @@ class AccommodationBookingControllerTest extends TestCase
$response = $controller->single($booking->getUuid()); $response = $controller->single($booking->getUuid());
$payload = json_decode((string) $response->getContent(), true, 512, JSON_THROW_ON_ERROR); $payload = json_decode((string) $response->getContent(), true, 512, JSON_THROW_ON_ERROR);
self::assertSame('inquiry', $payload['status']); self::assertSame('open', $payload['status']);
self::assertSame('inquiry', $payload['type']);
self::assertNull($payload['priceBreakdown']); self::assertNull($payload['priceBreakdown']);
self::assertNull($payload['acceptedAt']); self::assertNull($payload['acceptedAt']);
} }
@@ -186,7 +191,7 @@ class AccommodationBookingControllerTest extends TestCase
self::assertSame(['message' => 'Not found'], $payload); self::assertSame(['message' => 'Not found'], $payload);
} }
public function testAcceptTransitionsInquiryToBookingAndSetsAcceptedAt(): void public function testAcceptAcceptsOpenOfferAndSetsAcceptedAt(): void
{ {
$booking = new AccommodationBooking(); $booking = new AccommodationBooking();
$booking->setDateFrom(new \DateTimeImmutable('2026-08-01')); $booking->setDateFrom(new \DateTimeImmutable('2026-08-01'));
@@ -195,7 +200,7 @@ class AccommodationBookingControllerTest extends TestCase
$booking->setFirstName('Tom'); $booking->setFirstName('Tom');
$booking->setLastName('Beispiel'); $booking->setLastName('Beispiel');
$booking->setEmail('[email protected]'); $booking->setEmail('[email protected]');
$booking->setStatus(AccommodationBookingStatus::Inquiry); $booking->setStatus(AccommodationBookingStatus::Open);
$bookingRepository = $this->createMock(AccommodationBookingRepository::class); $bookingRepository = $this->createMock(AccommodationBookingRepository::class);
$bookingRepository $bookingRepository
@@ -213,7 +218,7 @@ class AccommodationBookingControllerTest extends TestCase
->method('acceptBooking') ->method('acceptBooking')
->with($booking) ->with($booking)
->willReturnCallback(static function (AccommodationBooking $b): void { ->willReturnCallback(static function (AccommodationBooking $b): void {
$b->setStatus(AccommodationBookingStatus::Booking); $b->setStatus(AccommodationBookingStatus::Accepted);
$b->setAcceptedAt(new \DateTimeImmutable()); $b->setAcceptedAt(new \DateTimeImmutable());
}); });
@@ -223,8 +228,9 @@ class AccommodationBookingControllerTest extends TestCase
$payload = json_decode((string) $response->getContent(), true, 512, JSON_THROW_ON_ERROR); $payload = json_decode((string) $response->getContent(), true, 512, JSON_THROW_ON_ERROR);
self::assertSame(Response::HTTP_OK, $response->getStatusCode()); self::assertSame(Response::HTTP_OK, $response->getStatusCode());
self::assertSame('booking', $payload['status']); self::assertSame('accepted', $payload['status']);
self::assertSame(AccommodationBookingStatus::Booking, $booking->getStatus()); self::assertSame('inquiry', $payload['type']);
self::assertSame(AccommodationBookingStatus::Accepted, $booking->getStatus());
self::assertNotNull($booking->getAcceptedAt()); self::assertNotNull($booking->getAcceptedAt());
self::assertSame($booking->getAcceptedAt()->format(\DATE_ATOM), $payload['acceptedAt']); self::assertSame($booking->getAcceptedAt()->format(\DATE_ATOM), $payload['acceptedAt']);
} }
@@ -240,7 +246,7 @@ class AccommodationBookingControllerTest extends TestCase
$booking->setFirstName('Tom'); $booking->setFirstName('Tom');
$booking->setLastName('Beispiel'); $booking->setLastName('Beispiel');
$booking->setEmail('[email protected]'); $booking->setEmail('[email protected]');
$booking->setStatus(AccommodationBookingStatus::Booking); $booking->setStatus(AccommodationBookingStatus::Accepted);
$booking->setAcceptedAt($acceptedAt); $booking->setAcceptedAt($acceptedAt);
$bookingRepository = $this->createMock(AccommodationBookingRepository::class); $bookingRepository = $this->createMock(AccommodationBookingRepository::class);
@@ -261,7 +267,7 @@ class AccommodationBookingControllerTest extends TestCase
$payload = json_decode((string) $response->getContent(), true, 512, JSON_THROW_ON_ERROR); $payload = json_decode((string) $response->getContent(), true, 512, JSON_THROW_ON_ERROR);
self::assertSame(Response::HTTP_OK, $response->getStatusCode()); self::assertSame(Response::HTTP_OK, $response->getStatusCode());
self::assertSame('booking', $payload['status']); self::assertSame('accepted', $payload['status']);
self::assertSame($acceptedAt, $booking->getAcceptedAt()); self::assertSame($acceptedAt, $booking->getAcceptedAt());
self::assertSame($acceptedAt->format(\DATE_ATOM), $payload['acceptedAt']); self::assertSame($acceptedAt->format(\DATE_ATOM), $payload['acceptedAt']);
} }
@@ -26,7 +26,7 @@ class OfferControllerTest extends TestCase
public function testAccessAuthorizesSessionAndRedirectsToView(): void public function testAccessAuthorizesSessionAndRedirectsToView(): void
{ {
$booking = new AccommodationBooking(); $booking = new AccommodationBooking();
$booking->setStatus(AccommodationBookingStatus::Inquiry); $booking->setStatus(AccommodationBookingStatus::Open);
$bookingRepository = $this->createMock(AccommodationBookingRepository::class); $bookingRepository = $this->createMock(AccommodationBookingRepository::class);
$bookingRepository->method('findOneBy')->with(['uuid' => $booking->getUuid()])->willReturn($booking); $bookingRepository->method('findOneBy')->with(['uuid' => $booking->getUuid()])->willReturn($booking);
@@ -151,7 +151,7 @@ class OfferControllerTest extends TestCase
public function testViewRendersOfferWhenSessionAuthorized(): void public function testViewRendersOfferWhenSessionAuthorized(): void
{ {
$booking = new AccommodationBooking(); $booking = new AccommodationBooking();
$booking->setStatus(AccommodationBookingStatus::Inquiry); $booking->setStatus(AccommodationBookingStatus::Open);
$booking->setAccommodation(new Accommodation()); $booking->setAccommodation(new Accommodation());
$bookingRepository = $this->createMock(AccommodationBookingRepository::class); $bookingRepository = $this->createMock(AccommodationBookingRepository::class);
@@ -207,7 +207,7 @@ class OfferControllerTest extends TestCase
public function testConfirmGetRendersModalWithFreshForm(): void public function testConfirmGetRendersModalWithFreshForm(): void
{ {
$booking = new AccommodationBooking(); $booking = new AccommodationBooking();
$booking->setStatus(AccommodationBookingStatus::Inquiry); $booking->setStatus(AccommodationBookingStatus::Open);
$bookingRepository = $this->createMock(AccommodationBookingRepository::class); $bookingRepository = $this->createMock(AccommodationBookingRepository::class);
$bookingRepository->method('findOneBy')->with(['uuid' => $booking->getUuid()])->willReturn($booking); $bookingRepository->method('findOneBy')->with(['uuid' => $booking->getUuid()])->willReturn($booking);
@@ -238,7 +238,7 @@ class OfferControllerTest extends TestCase
public function testConfirmPostValidAcceptsBookingAndRedirects(): void public function testConfirmPostValidAcceptsBookingAndRedirects(): void
{ {
$booking = new AccommodationBooking(); $booking = new AccommodationBooking();
$booking->setStatus(AccommodationBookingStatus::Inquiry); $booking->setStatus(AccommodationBookingStatus::Open);
$bookingRepository = $this->createMock(AccommodationBookingRepository::class); $bookingRepository = $this->createMock(AccommodationBookingRepository::class);
$bookingRepository->method('findOneBy')->willReturn($booking); $bookingRepository->method('findOneBy')->willReturn($booking);
@@ -273,7 +273,7 @@ class OfferControllerTest extends TestCase
public function testConfirmPostValidViaHtmxReturnsHxRedirect(): void public function testConfirmPostValidViaHtmxReturnsHxRedirect(): void
{ {
$booking = new AccommodationBooking(); $booking = new AccommodationBooking();
$booking->setStatus(AccommodationBookingStatus::Inquiry); $booking->setStatus(AccommodationBookingStatus::Open);
$bookingRepository = $this->createMock(AccommodationBookingRepository::class); $bookingRepository = $this->createMock(AccommodationBookingRepository::class);
$bookingRepository->method('findOneBy')->willReturn($booking); $bookingRepository->method('findOneBy')->willReturn($booking);
@@ -308,7 +308,7 @@ class OfferControllerTest extends TestCase
public function testConfirmPostInvalidReRendersModalWithoutAccepting(): void public function testConfirmPostInvalidReRendersModalWithoutAccepting(): void
{ {
$booking = new AccommodationBooking(); $booking = new AccommodationBooking();
$booking->setStatus(AccommodationBookingStatus::Inquiry); $booking->setStatus(AccommodationBookingStatus::Open);
$bookingRepository = $this->createMock(AccommodationBookingRepository::class); $bookingRepository = $this->createMock(AccommodationBookingRepository::class);
$bookingRepository->method('findOneBy')->willReturn($booking); $bookingRepository->method('findOneBy')->willReturn($booking);
@@ -342,7 +342,7 @@ class OfferControllerTest extends TestCase
public function testConfirmRedirectsWhenAlreadyAccepted(): void public function testConfirmRedirectsWhenAlreadyAccepted(): void
{ {
$booking = new AccommodationBooking(); $booking = new AccommodationBooking();
$booking->setStatus(AccommodationBookingStatus::Booking); $booking->setStatus(AccommodationBookingStatus::Accepted);
$bookingRepository = $this->createMock(AccommodationBookingRepository::class); $bookingRepository = $this->createMock(AccommodationBookingRepository::class);
$bookingRepository->method('findOneBy')->willReturn($booking); $bookingRepository->method('findOneBy')->willReturn($booking);
@@ -462,7 +462,7 @@ final class TestableOfferController extends OfferController
$this->renderedParameters = $parameters; $this->renderedParameters = $parameters;
if ('groups/booking/offer.html.twig' === $view) { if ('groups/booking/offer.html.twig' === $view) {
$content = $parameters['booking']->isInquiry() ? '<html>offer</html>' : '<html>confirmed</html>'; $content = $parameters['booking']->isAccepted() ? '<html>confirmed</html>' : '<html>offer</html>';
} else { } else {
$content = '<html>unavailable</html>'; $content = '<html>unavailable</html>';
} }
@@ -7,7 +7,7 @@ namespace App\Tests\Controller\Groups;
use App\Controller\Groups\Step4Controller; use App\Controller\Groups\Step4Controller;
use App\Entity\Groups\Accommodation; use App\Entity\Groups\Accommodation;
use App\Entity\Groups\AccommodationBooking; use App\Entity\Groups\AccommodationBooking;
use App\Enum\Groups\AccommodationBookingStatus; use App\Enum\Groups\AccommodationBookingType;
use App\Form\Model\AccommodationBookingDto; use App\Form\Model\AccommodationBookingDto;
use App\Service\AccommodationBookingService; use App\Service\AccommodationBookingService;
use App\Service\AccommodationSessionManager; use App\Service\AccommodationSessionManager;
@@ -102,7 +102,7 @@ class Step4ControllerTest extends TestCase
->setCalendarCode('HOTEL') ->setCalendarCode('HOTEL')
->setMaxAdolescentAge(17); ->setMaxAdolescentAge(17);
$booking = (new AccommodationBooking())->setStatus(AccommodationBookingStatus::Inquiry); $booking = (new AccommodationBooking())->setType(AccommodationBookingType::Inquiry);
$bookingService = $this->createMock(AccommodationBookingService::class); $bookingService = $this->createMock(AccommodationBookingService::class);
$bookingService->method('loadAccommodation')->with(1)->willReturn($accommodation); $bookingService->method('loadAccommodation')->with(1)->willReturn($accommodation);
@@ -275,7 +275,7 @@ class Step4ControllerTest extends TestCase
->setCalendarCode('HOTEL') ->setCalendarCode('HOTEL')
->setMaxAdolescentAge(17); ->setMaxAdolescentAge(17);
$booking = (new AccommodationBooking())->setStatus(AccommodationBookingStatus::Booking); $booking = (new AccommodationBooking())->setType(AccommodationBookingType::Booking);
$bookingService = $this->createMock(AccommodationBookingService::class); $bookingService = $this->createMock(AccommodationBookingService::class);
$bookingService->method('loadAccommodation')->with(1)->willReturn($accommodation); $bookingService->method('loadAccommodation')->with(1)->willReturn($accommodation);
@@ -47,8 +47,8 @@ class AccommodationBookingTypeTest extends TestCase
*/ */
public static function customerFacingStatuses(): iterable public static function customerFacingStatuses(): iterable
{ {
yield 'inquiry' => [AccommodationBookingStatus::Inquiry]; yield 'open' => [AccommodationBookingStatus::Open];
yield 'booking' => [AccommodationBookingStatus::Booking]; yield 'accepted' => [AccommodationBookingStatus::Accepted];
} }
/** /**
@@ -67,7 +67,7 @@ class AccommodationBookingTypeTest extends TestCase
public function testPromotedBookingRequiresContactData(): void public function testPromotedBookingRequiresContactData(): void
{ {
$booking = new AccommodationBooking(); $booking = new AccommodationBooking();
$booking->setStatus(AccommodationBookingStatus::Inquiry); $booking->setStatus(AccommodationBookingStatus::Open);
$violations = $this->validate($booking, ['Default', 'edit']); $violations = $this->validate($booking, ['Default', 'edit']);
@@ -9,6 +9,7 @@ use App\Entity\Groups\Accommodation;
use App\Entity\Groups\AccommodationBooking; use App\Entity\Groups\AccommodationBooking;
use App\Entity\Groups\AccommodationPrice; use App\Entity\Groups\AccommodationPrice;
use App\Enum\Groups\AccommodationBookingStatus; use App\Enum\Groups\AccommodationBookingStatus;
use App\Enum\Groups\AccommodationBookingType;
use App\Model\AccommodationBookingQueryParams; use App\Model\AccommodationBookingQueryParams;
use App\Repository\Groups\AccommodationPriceRepository; use App\Repository\Groups\AccommodationPriceRepository;
use App\Repository\Groups\AccommodationRepository; use App\Repository\Groups\AccommodationRepository;
@@ -66,7 +67,7 @@ class AccommodationBookingServiceTest extends TestCase
$service = $this->createServiceWithAccommodation(entityManager: $entityManager); $service = $this->createServiceWithAccommodation(entityManager: $entityManager);
$booking = new AccommodationBooking(); $booking = new AccommodationBooking();
$booking->setStatus(AccommodationBookingStatus::Booking); $booking->setType(AccommodationBookingType::Booking);
$service->issueAccessLinkForDirectBooking($booking); $service->issueAccessLinkForDirectBooking($booking);
@@ -81,7 +82,7 @@ class AccommodationBookingServiceTest extends TestCase
$service = $this->createServiceWithAccommodation(entityManager: $entityManager); $service = $this->createServiceWithAccommodation(entityManager: $entityManager);
$booking = new AccommodationBooking(); $booking = new AccommodationBooking();
$booking->setStatus(AccommodationBookingStatus::Inquiry); $booking->setType(AccommodationBookingType::Inquiry);
$service->issueAccessLinkForDirectBooking($booking); $service->issueAccessLinkForDirectBooking($booking);
@@ -96,7 +97,7 @@ class AccommodationBookingServiceTest extends TestCase
$service = $this->createServiceWithAccommodation(entityManager: $entityManager); $service = $this->createServiceWithAccommodation(entityManager: $entityManager);
$booking = new AccommodationBooking(); $booking = new AccommodationBooking();
$booking->setStatus(AccommodationBookingStatus::Booking); $booking->setType(AccommodationBookingType::Booking);
$issuedAt = new \DateTimeImmutable('2026-01-01'); $issuedAt = new \DateTimeImmutable('2026-01-01');
$booking->setAccessLinkIssuedAt($issuedAt); $booking->setAccessLinkIssuedAt($issuedAt);
@@ -113,7 +114,7 @@ class AccommodationBookingServiceTest extends TestCase
$service = $this->createServiceWithAccommodation(entityManager: $entityManager); $service = $this->createServiceWithAccommodation(entityManager: $entityManager);
$booking = new AccommodationBooking(); $booking = new AccommodationBooking();
$booking->setStatus(AccommodationBookingStatus::Inquiry); $booking->setStatus(AccommodationBookingStatus::Open);
$service->issueAccessLink($booking); $service->issueAccessLink($booking);
@@ -128,7 +129,7 @@ class AccommodationBookingServiceTest extends TestCase
$service = $this->createServiceWithAccommodation(entityManager: $entityManager); $service = $this->createServiceWithAccommodation(entityManager: $entityManager);
$booking = new AccommodationBooking(); $booking = new AccommodationBooking();
$booking->setStatus(AccommodationBookingStatus::Inquiry); $booking->setStatus(AccommodationBookingStatus::Open);
$issuedAt = new \DateTimeImmutable('2026-01-01'); $issuedAt = new \DateTimeImmutable('2026-01-01');
$booking->setAccessLinkIssuedAt($issuedAt); $booking->setAccessLinkIssuedAt($issuedAt);
@@ -141,7 +142,7 @@ class AccommodationBookingServiceTest extends TestCase
{ {
$booking = new AccommodationBooking(); $booking = new AccommodationBooking();
$booking->setEmail('[email protected]'); $booking->setEmail('[email protected]');
$booking->setStatus(AccommodationBookingStatus::Inquiry); $booking->setStatus(AccommodationBookingStatus::Open);
$booking->setAccessLinkIssuedAt(new \DateTimeImmutable()); $booking->setAccessLinkIssuedAt(new \DateTimeImmutable());
$linkSigner = $this->createMock(AccommodationBookingLinkSigner::class); $linkSigner = $this->createMock(AccommodationBookingLinkSigner::class);
@@ -168,7 +169,7 @@ class AccommodationBookingServiceTest extends TestCase
{ {
$booking = new AccommodationBooking(); $booking = new AccommodationBooking();
$booking->setEmail('[email protected]'); $booking->setEmail('[email protected]');
$booking->setStatus(AccommodationBookingStatus::Booking); $booking->setStatus(AccommodationBookingStatus::Accepted);
$booking->setAccessLinkIssuedAt(new \DateTimeImmutable()); $booking->setAccessLinkIssuedAt(new \DateTimeImmutable());
$linkSigner = $this->createMock(AccommodationBookingLinkSigner::class); $linkSigner = $this->createMock(AccommodationBookingLinkSigner::class);
@@ -197,7 +198,7 @@ class AccommodationBookingServiceTest extends TestCase
{ {
$booking = new AccommodationBooking(); $booking = new AccommodationBooking();
$booking->setEmail('[email protected]'); $booking->setEmail('[email protected]');
$booking->setStatus(AccommodationBookingStatus::Inquiry); $booking->setStatus(AccommodationBookingStatus::Open);
$breakdownCalculator = $this->createMock(AccommodationBookingBreakdownCalculator::class); $breakdownCalculator = $this->createMock(AccommodationBookingBreakdownCalculator::class);
$breakdownCalculator->method('compute')->willReturn(null); $breakdownCalculator->method('compute')->willReturn(null);
@@ -220,7 +221,7 @@ class AccommodationBookingServiceTest extends TestCase
{ {
$booking = new AccommodationBooking(); $booking = new AccommodationBooking();
$booking->setEmail('[email protected]'); $booking->setEmail('[email protected]');
$booking->setStatus(AccommodationBookingStatus::Inquiry); $booking->setStatus(AccommodationBookingStatus::Open);
$breakdownCalculator = $this->createMock(AccommodationBookingBreakdownCalculator::class); $breakdownCalculator = $this->createMock(AccommodationBookingBreakdownCalculator::class);
$breakdownCalculator->method('compute')->willReturn(null); $breakdownCalculator->method('compute')->willReturn(null);
@@ -240,7 +241,7 @@ class AccommodationBookingServiceTest extends TestCase
{ {
$booking = new AccommodationBooking(); $booking = new AccommodationBooking();
$booking->setEmail('[email protected]'); $booking->setEmail('[email protected]');
$booking->setStatus(AccommodationBookingStatus::Inquiry); $booking->setStatus(AccommodationBookingStatus::Open);
$previousIssuedAt = new \DateTimeImmutable('2026-01-01'); $previousIssuedAt = new \DateTimeImmutable('2026-01-01');
$booking->setAccessLinkIssuedAt($previousIssuedAt); $booking->setAccessLinkIssuedAt($previousIssuedAt);
@@ -264,13 +265,13 @@ class AccommodationBookingServiceTest extends TestCase
self::assertNotSame($previousIssuedAt, $booking->getAccessLinkIssuedAt()); self::assertNotSame($previousIssuedAt, $booking->getAccessLinkIssuedAt());
} }
public function testAcceptBookingTransitionsInquiryToBookingAndSendsNotifications(): void public function testAcceptBookingAcceptsOpenInquiryAndSendsNotifications(): void
{ {
$entityManager = $this->createMock(EntityManagerInterface::class); $entityManager = $this->createMock(EntityManagerInterface::class);
$entityManager->expects(self::once())->method('flush'); $entityManager->expects(self::once())->method('flush');
$booking = new AccommodationBooking(); $booking = new AccommodationBooking();
$booking->setStatus(AccommodationBookingStatus::Inquiry); $booking->setStatus(AccommodationBookingStatus::Open);
$booking->setEmail('[email protected]'); $booking->setEmail('[email protected]');
$mailer = $this->createMock(Mailer::class); $mailer = $this->createMock(Mailer::class);
@@ -288,7 +289,8 @@ class AccommodationBookingServiceTest extends TestCase
$service->acceptBooking($booking); $service->acceptBooking($booking);
self::assertSame(AccommodationBookingStatus::Booking, $booking->getStatus()); self::assertSame(AccommodationBookingStatus::Accepted, $booking->getStatus());
self::assertSame(AccommodationBookingType::Inquiry, $booking->getType(), 'an accepted offer stays an Anfrage');
self::assertNotNull($booking->getAcceptedAt()); self::assertNotNull($booking->getAcceptedAt());
} }
@@ -303,17 +305,37 @@ class AccommodationBookingServiceTest extends TestCase
$service = $this->createServiceWithAccommodation(entityManager: $entityManager, mailer: $mailer); $service = $this->createServiceWithAccommodation(entityManager: $entityManager, mailer: $mailer);
$booking = new AccommodationBooking(); $booking = new AccommodationBooking();
$booking->setStatus(AccommodationBookingStatus::Booking); $booking->setStatus(AccommodationBookingStatus::Accepted);
$service->acceptBooking($booking); $service->acceptBooking($booking);
self::assertNull($booking->getAcceptedAt()); self::assertNull($booking->getAcceptedAt());
} }
public function testAcceptBookingNoOpsForOfferThatIsNotOpenYet(): void
{
$entityManager = $this->createMock(EntityManagerInterface::class);
$entityManager->expects(self::never())->method('flush');
$mailer = $this->createMock(Mailer::class);
$mailer->expects(self::never())->method('createAndSendEmail');
$service = $this->createServiceWithAccommodation(entityManager: $entityManager, mailer: $mailer);
$booking = new AccommodationBooking();
$booking->setType(AccommodationBookingType::Inquiry);
$booking->setStatus(AccommodationBookingStatus::Draft);
$service->acceptBooking($booking);
self::assertSame(AccommodationBookingStatus::Draft, $booking->getStatus());
self::assertNull($booking->getAcceptedAt());
}
public function testSendOfferAcceptedNotificationEmailLogsAndSwallowsMailerFailures(): void public function testSendOfferAcceptedNotificationEmailLogsAndSwallowsMailerFailures(): void
{ {
$booking = new AccommodationBooking(); $booking = new AccommodationBooking();
$booking->setStatus(AccommodationBookingStatus::Booking); $booking->setStatus(AccommodationBookingStatus::Accepted);
$mailer = $this->createMock(Mailer::class); $mailer = $this->createMock(Mailer::class);
$mailer->method('createAndSendEmail')->willThrowException(new \RuntimeException('SMTP down')); $mailer->method('createAndSendEmail')->willThrowException(new \RuntimeException('SMTP down'));
@@ -330,7 +352,7 @@ class AccommodationBookingServiceTest extends TestCase
{ {
$booking = new AccommodationBooking(); $booking = new AccommodationBooking();
$booking->setEmail('[email protected]'); $booking->setEmail('[email protected]');
$booking->setStatus(AccommodationBookingStatus::Booking); $booking->setStatus(AccommodationBookingStatus::Accepted);
$mailer = $this->createMock(Mailer::class); $mailer = $this->createMock(Mailer::class);
$mailer->method('createAndSendEmail')->willThrowException(new \RuntimeException('SMTP down')); $mailer->method('createAndSendEmail')->willThrowException(new \RuntimeException('SMTP down'));