feat: streamlined booking number property
This commit is contained in:
@@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace DoctrineMigrations;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Schema\Schema;
|
||||||
|
use Doctrine\Migrations\AbstractMigration;
|
||||||
|
|
||||||
|
final class Version20260110105253 extends AbstractMigration
|
||||||
|
{
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return 'Add booking_number column to booking_edit_draft table';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function up(Schema $schema): void
|
||||||
|
{
|
||||||
|
$this->addSql('ALTER TABLE booking_edit_draft ADD booking_number INT DEFAULT NULL');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(Schema $schema): void
|
||||||
|
{
|
||||||
|
$this->addSql('ALTER TABLE booking_edit_draft DROP booking_number');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,14 +17,14 @@ class BookingResponse
|
|||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @param string $status Booking status (möglich|erfolgt)
|
* @param string $status Booking status (möglich|erfolgt)
|
||||||
* @param string|null $transactionNumber Transaction number (vorgang)
|
* @param int|null $bookingNumber Booking number (BPN XML: vorgang)
|
||||||
* @param array<int, PriceItem> $priceItems Individual price items from response
|
* @param array<int, PriceItem> $priceItems Individual price items from response
|
||||||
* @param float|null $totalPrice Total price (gesamtpreis)
|
* @param float|null $totalPrice Total price (gesamtpreis)
|
||||||
* @param PaymentTerms|null $paymentTerms Payment terms (anzahlung/restzahlung)
|
* @param PaymentTerms|null $paymentTerms Payment terms (anzahlung/restzahlung)
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
public readonly string $status,
|
public readonly string $status,
|
||||||
public readonly ?string $transactionNumber = null,
|
public readonly ?int $bookingNumber = null,
|
||||||
public readonly array $priceItems = [],
|
public readonly array $priceItems = [],
|
||||||
public readonly ?float $totalPrice = null,
|
public readonly ?float $totalPrice = null,
|
||||||
public readonly ?PaymentTerms $paymentTerms = null,
|
public readonly ?PaymentTerms $paymentTerms = null,
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ class BookingResponseParser extends AbstractParser
|
|||||||
public function parse(Crawler $node): BookingResponse
|
public function parse(Crawler $node): BookingResponse
|
||||||
{
|
{
|
||||||
$status = $node->filterXPath('//buchung')->text();
|
$status = $node->filterXPath('//buchung')->text();
|
||||||
$transactionNumber = $this->getStringOrNullValue($node->filterXPath('//vorgang'));
|
$bookingNumber = $this->getIntOrNullValue($node->filterXPath('//vorgang'));
|
||||||
$totalPrice = $this->getFloatOrNullValue($node->filterXPath('//gesamtpreis'));
|
$totalPrice = $this->getFloatOrNullValue($node->filterXPath('//gesamtpreis'));
|
||||||
$message = $this->getStringOrNullValue($node->filterXPath('//hinweis'));
|
$message = $this->getStringOrNullValue($node->filterXPath('//hinweis'));
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@ class BookingResponseParser extends AbstractParser
|
|||||||
|
|
||||||
return new BookingResponse(
|
return new BookingResponse(
|
||||||
status: $status,
|
status: $status,
|
||||||
transactionNumber: $transactionNumber,
|
bookingNumber: $bookingNumber,
|
||||||
priceItems: $priceItems,
|
priceItems: $priceItems,
|
||||||
totalPrice: $totalPrice,
|
totalPrice: $totalPrice,
|
||||||
paymentTerms: $paymentTerms,
|
paymentTerms: $paymentTerms,
|
||||||
|
|||||||
+13
-12
@@ -16,10 +16,10 @@ use Symfony\Component\Console\Output\OutputInterface;
|
|||||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||||
|
|
||||||
#[AsCommand(
|
#[AsCommand(
|
||||||
name: 'app:draft:backfill-travel-date',
|
name: 'app:draft:backfill-booking-number',
|
||||||
description: 'Backfill travel dates for existing drafts from API',
|
description: 'Backfill booking numbers (vorgang) for existing drafts from API',
|
||||||
)]
|
)]
|
||||||
class DraftBackfillTravelDateCommand extends Command
|
class DraftBackfillBookingNumberCommand extends Command
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly BookingEditDraftRepository $draftRepository,
|
private readonly BookingEditDraftRepository $draftRepository,
|
||||||
@@ -43,10 +43,10 @@ class DraftBackfillTravelDateCommand extends Command
|
|||||||
$io = new SymfonyStyle($input, $output);
|
$io = new SymfonyStyle($input, $output);
|
||||||
$dryRun = $input->getOption('dry-run');
|
$dryRun = $input->getOption('dry-run');
|
||||||
|
|
||||||
$drafts = $this->draftRepository->findAll();
|
$drafts = $this->draftRepository->findBy(['bookingNumber' => null]);
|
||||||
|
|
||||||
if (0 === \count($drafts)) {
|
if (0 === \count($drafts)) {
|
||||||
$io->success('No drafts found.');
|
$io->success('No drafts without booking number found.');
|
||||||
|
|
||||||
return Command::SUCCESS;
|
return Command::SUCCESS;
|
||||||
}
|
}
|
||||||
@@ -70,10 +70,11 @@ class DraftBackfillTravelDateCommand extends Command
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$travelDate = $bookingData->travelDate;
|
// bookingNumber maps to BPN XML vorgang
|
||||||
|
$bookingNumber = $bookingData->bookingNumber;
|
||||||
|
|
||||||
if (null === $travelDate) {
|
if (null === $bookingNumber) {
|
||||||
$io->warning(sprintf('No travel date found for booking %d', $bookingId));
|
$io->warning(sprintf('No booking number (vorgang) found for booking %d', $bookingId));
|
||||||
++$skipped;
|
++$skipped;
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
@@ -81,13 +82,13 @@ class DraftBackfillTravelDateCommand extends Command
|
|||||||
|
|
||||||
if ($dryRun) {
|
if ($dryRun) {
|
||||||
$io->text(sprintf(
|
$io->text(sprintf(
|
||||||
'[DRY RUN] Would set travel date %s for draft %d (booking %d)',
|
'[DRY RUN] Would set booking number %d for draft %d (bookingId %d)',
|
||||||
$travelDate->format('Y-m-d'),
|
$bookingNumber,
|
||||||
$draft->getId(),
|
$draft->getId(),
|
||||||
$bookingId
|
$bookingId
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
$draft->setTravelDate($travelDate);
|
$draft->setBookingNumber($bookingNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
++$updated;
|
++$updated;
|
||||||
@@ -103,7 +104,7 @@ class DraftBackfillTravelDateCommand extends Command
|
|||||||
[
|
[
|
||||||
['Updated', $updated],
|
['Updated', $updated],
|
||||||
['Failed (API error)', $failed],
|
['Failed (API error)', $failed],
|
||||||
['Skipped (no date)', $skipped],
|
['Skipped (no booking number)', $skipped],
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -93,14 +93,14 @@ class Step4Controller extends AbstractController
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Success: Store booking number in flash and clear session
|
// Success: Store booking number in flash and clear session
|
||||||
$this->addFlash('booking_number', $bookingResponse->transactionNumber);
|
$this->addFlash('booking_number', $bookingResponse->bookingNumber);
|
||||||
$this->clearTravelDataCache($bookingCreateDto);
|
$this->clearTravelDataCache($bookingCreateDto);
|
||||||
$this->bookingService->clearBookingDto($request, BookingDto::MODE_CREATE);
|
$this->bookingService->clearBookingDto($request, BookingDto::MODE_CREATE);
|
||||||
|
|
||||||
$this->logger->info('Booking successfully created.', [
|
$this->logger->info('Booking successfully created.', [
|
||||||
'date_id' => $bookingCreateDto->travel->id,
|
'date_id' => $bookingCreateDto->travel->id,
|
||||||
'hotel_id' => $bookingCreateDto->hotelId,
|
'hotel_id' => $bookingCreateDto->hotelId,
|
||||||
'booking_number' => $bookingResponse->transactionNumber,
|
'booking_number' => $bookingResponse->bookingNumber,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return $this->redirectToRoute('app_booking_create_success');
|
return $this->redirectToRoute('app_booking_create_success');
|
||||||
|
|||||||
@@ -22,9 +22,18 @@ class BookingEditDraft
|
|||||||
#[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')]
|
#[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')]
|
||||||
private User $user;
|
private User $user;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal database ID (BPN XML: idbuchung).
|
||||||
|
*/
|
||||||
#[ORM\Column(type: 'integer')]
|
#[ORM\Column(type: 'integer')]
|
||||||
private int $bookingId;
|
private int $bookingId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User-facing transaction number (BPN XML: vorgang).
|
||||||
|
*/
|
||||||
|
#[ORM\Column(type: 'integer', nullable: true)]
|
||||||
|
private ?int $bookingNumber = null;
|
||||||
|
|
||||||
#[ORM\Column(type: 'date_immutable')]
|
#[ORM\Column(type: 'date_immutable')]
|
||||||
private \DateTimeImmutable $travelDate;
|
private \DateTimeImmutable $travelDate;
|
||||||
|
|
||||||
@@ -62,6 +71,18 @@ class BookingEditDraft
|
|||||||
return $this->bookingId;
|
return $this->bookingId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getBookingNumber(): ?int
|
||||||
|
{
|
||||||
|
return $this->bookingNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setBookingNumber(?int $bookingNumber): static
|
||||||
|
{
|
||||||
|
$this->bookingNumber = $bookingNumber;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function getTravelDate(): \DateTimeImmutable
|
public function getTravelDate(): \DateTimeImmutable
|
||||||
{
|
{
|
||||||
return $this->travelDate;
|
return $this->travelDate;
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ class BookingResponseParserTest extends TestCase
|
|||||||
$response = $this->parser->parse($crawler->filter('ergebnis'));
|
$response = $this->parser->parse($crawler->filter('ergebnis'));
|
||||||
|
|
||||||
$this->assertTrue($response->isInquiryValid());
|
$this->assertTrue($response->isInquiryValid());
|
||||||
$this->assertEquals('321530', $response->transactionNumber);
|
$this->assertEquals(321530, $response->bookingNumber);
|
||||||
$this->assertEquals(755.0, $response->totalPrice);
|
$this->assertEquals(755.0, $response->totalPrice);
|
||||||
|
|
||||||
// Verify purchase voucher is parsed
|
// Verify purchase voucher is parsed
|
||||||
|
|||||||
Reference in New Issue
Block a user