From bdcd54687dd208104ebf72583620bca9a1684395 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Fromme?= Date: Sat, 10 Jan 2026 12:12:54 +0100 Subject: [PATCH] feat: streamlined booking number property --- migrations/Version20260110105253.php | 26 +++++++++++++++++++ src/BusProNet/Model/BookingResponse.php | 12 ++++----- .../XmlParser/BookingResponseParser.php | 4 +-- ... => DraftBackfillBookingNumberCommand.php} | 25 +++++++++--------- .../Booking/Create/Step4Controller.php | 4 +-- src/Entity/BookingEditDraft.php | 21 +++++++++++++++ .../XmlParser/BookingResponseParserTest.php | 2 +- 7 files changed, 71 insertions(+), 23 deletions(-) create mode 100644 migrations/Version20260110105253.php rename src/Command/{DraftBackfillTravelDateCommand.php => DraftBackfillBookingNumberCommand.php} (76%) diff --git a/migrations/Version20260110105253.php b/migrations/Version20260110105253.php new file mode 100644 index 0000000..eb3270d --- /dev/null +++ b/migrations/Version20260110105253.php @@ -0,0 +1,26 @@ +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'); + } +} diff --git a/src/BusProNet/Model/BookingResponse.php b/src/BusProNet/Model/BookingResponse.php index 2868453..2fb56ce 100644 --- a/src/BusProNet/Model/BookingResponse.php +++ b/src/BusProNet/Model/BookingResponse.php @@ -16,15 +16,15 @@ namespace App\BusProNet\Model; class BookingResponse { /** - * @param string $status Booking status (möglich|erfolgt) - * @param string|null $transactionNumber Transaction number (vorgang) - * @param array $priceItems Individual price items from response - * @param float|null $totalPrice Total price (gesamtpreis) - * @param PaymentTerms|null $paymentTerms Payment terms (anzahlung/restzahlung) + * @param string $status Booking status (möglich|erfolgt) + * @param int|null $bookingNumber Booking number (BPN XML: vorgang) + * @param array $priceItems Individual price items from response + * @param float|null $totalPrice Total price (gesamtpreis) + * @param PaymentTerms|null $paymentTerms Payment terms (anzahlung/restzahlung) */ public function __construct( public readonly string $status, - public readonly ?string $transactionNumber = null, + public readonly ?int $bookingNumber = null, public readonly array $priceItems = [], public readonly ?float $totalPrice = null, public readonly ?PaymentTerms $paymentTerms = null, diff --git a/src/BusProNet/XmlParser/BookingResponseParser.php b/src/BusProNet/XmlParser/BookingResponseParser.php index 1df83ed..d2ce927 100644 --- a/src/BusProNet/XmlParser/BookingResponseParser.php +++ b/src/BusProNet/XmlParser/BookingResponseParser.php @@ -33,7 +33,7 @@ class BookingResponseParser extends AbstractParser public function parse(Crawler $node): BookingResponse { $status = $node->filterXPath('//buchung')->text(); - $transactionNumber = $this->getStringOrNullValue($node->filterXPath('//vorgang')); + $bookingNumber = $this->getIntOrNullValue($node->filterXPath('//vorgang')); $totalPrice = $this->getFloatOrNullValue($node->filterXPath('//gesamtpreis')); $message = $this->getStringOrNullValue($node->filterXPath('//hinweis')); @@ -42,7 +42,7 @@ class BookingResponseParser extends AbstractParser return new BookingResponse( status: $status, - transactionNumber: $transactionNumber, + bookingNumber: $bookingNumber, priceItems: $priceItems, totalPrice: $totalPrice, paymentTerms: $paymentTerms, diff --git a/src/Command/DraftBackfillTravelDateCommand.php b/src/Command/DraftBackfillBookingNumberCommand.php similarity index 76% rename from src/Command/DraftBackfillTravelDateCommand.php rename to src/Command/DraftBackfillBookingNumberCommand.php index 8bd6d0d..69292f6 100644 --- a/src/Command/DraftBackfillTravelDateCommand.php +++ b/src/Command/DraftBackfillBookingNumberCommand.php @@ -16,10 +16,10 @@ use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; #[AsCommand( - name: 'app:draft:backfill-travel-date', - description: 'Backfill travel dates for existing drafts from API', + name: 'app:draft:backfill-booking-number', + description: 'Backfill booking numbers (vorgang) for existing drafts from API', )] -class DraftBackfillTravelDateCommand extends Command +class DraftBackfillBookingNumberCommand extends Command { public function __construct( private readonly BookingEditDraftRepository $draftRepository, @@ -43,10 +43,10 @@ class DraftBackfillTravelDateCommand extends Command $io = new SymfonyStyle($input, $output); $dryRun = $input->getOption('dry-run'); - $drafts = $this->draftRepository->findAll(); + $drafts = $this->draftRepository->findBy(['bookingNumber' => null]); if (0 === \count($drafts)) { - $io->success('No drafts found.'); + $io->success('No drafts without booking number found.'); return Command::SUCCESS; } @@ -70,10 +70,11 @@ class DraftBackfillTravelDateCommand extends Command continue; } - $travelDate = $bookingData->travelDate; + // bookingNumber maps to BPN XML vorgang + $bookingNumber = $bookingData->bookingNumber; - if (null === $travelDate) { - $io->warning(sprintf('No travel date found for booking %d', $bookingId)); + if (null === $bookingNumber) { + $io->warning(sprintf('No booking number (vorgang) found for booking %d', $bookingId)); ++$skipped; continue; @@ -81,13 +82,13 @@ class DraftBackfillTravelDateCommand extends Command if ($dryRun) { $io->text(sprintf( - '[DRY RUN] Would set travel date %s for draft %d (booking %d)', - $travelDate->format('Y-m-d'), + '[DRY RUN] Would set booking number %d for draft %d (bookingId %d)', + $bookingNumber, $draft->getId(), $bookingId )); } else { - $draft->setTravelDate($travelDate); + $draft->setBookingNumber($bookingNumber); } ++$updated; @@ -103,7 +104,7 @@ class DraftBackfillTravelDateCommand extends Command [ ['Updated', $updated], ['Failed (API error)', $failed], - ['Skipped (no date)', $skipped], + ['Skipped (no booking number)', $skipped], ] ); diff --git a/src/Controller/Booking/Create/Step4Controller.php b/src/Controller/Booking/Create/Step4Controller.php index 8bb2e1e..ff42a04 100644 --- a/src/Controller/Booking/Create/Step4Controller.php +++ b/src/Controller/Booking/Create/Step4Controller.php @@ -93,14 +93,14 @@ class Step4Controller extends AbstractController } // 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->bookingService->clearBookingDto($request, BookingDto::MODE_CREATE); $this->logger->info('Booking successfully created.', [ 'date_id' => $bookingCreateDto->travel->id, 'hotel_id' => $bookingCreateDto->hotelId, - 'booking_number' => $bookingResponse->transactionNumber, + 'booking_number' => $bookingResponse->bookingNumber, ]); return $this->redirectToRoute('app_booking_create_success'); diff --git a/src/Entity/BookingEditDraft.php b/src/Entity/BookingEditDraft.php index 651521c..ff48d1f 100644 --- a/src/Entity/BookingEditDraft.php +++ b/src/Entity/BookingEditDraft.php @@ -22,9 +22,18 @@ class BookingEditDraft #[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')] private User $user; + /** + * Internal database ID (BPN XML: idbuchung). + */ #[ORM\Column(type: 'integer')] 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')] private \DateTimeImmutable $travelDate; @@ -62,6 +71,18 @@ class BookingEditDraft 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 { return $this->travelDate; diff --git a/tests/BusProNet/XmlParser/BookingResponseParserTest.php b/tests/BusProNet/XmlParser/BookingResponseParserTest.php index 54b6d19..e2c9e74 100644 --- a/tests/BusProNet/XmlParser/BookingResponseParserTest.php +++ b/tests/BusProNet/XmlParser/BookingResponseParserTest.php @@ -38,7 +38,7 @@ class BookingResponseParserTest extends TestCase $response = $this->parser->parse($crawler->filter('ergebnis')); $this->assertTrue($response->isInquiryValid()); - $this->assertEquals('321530', $response->transactionNumber); + $this->assertEquals(321530, $response->bookingNumber); $this->assertEquals(755.0, $response->totalPrice); // Verify purchase voucher is parsed