wip: service descriptions and car license plate field

This commit is contained in:
Björn Fromme
2025-09-10 12:53:33 +02:00
parent 195246af89
commit a3ea02a136
12 changed files with 623 additions and 5 deletions
@@ -0,0 +1,187 @@
<?php
declare(strict_types=1);
namespace App\Tests\BusProNet\XmlParser;
use App\BusProNet\XmlParser\TravelParser;
use PHPUnit\Framework\TestCase;
use Symfony\Component\DomCrawler\Crawler;
class TravelParserTest extends TestCase
{
private TravelParser $parser;
protected function setUp(): void
{
$this->parser = new TravelParser();
}
public function testParseServiceDescriptions(): void
{
$xmlContent = '<?xml version="1.0" encoding="utf-8"?>
<reisen>
<reise id="1" idbuspro="2187" code="SSTMR" erstellt_am="27.08.2025 09:09:40">
<termin id="1" idbuspro="11946" idprodukt="2187" termin="01.01.2030" bis="06.01.2030" code="SSTMR010130" reiseart="F">
<text>Test Travel</text>
<abpreis>659,00</abpreis>
<lei_sonstiges>
<leistung id="1" idbuspro="183658" unterart="SON" termin="01.01.2030" bis="06.01.2030" automatisch_buchen="False" pflicht="False">
<text>Begleitperson (gemäß Gruppenregelung)</text>
<hinweis>Gilt für Betreuende, Lehrkräfte, Gruppenleitungen oder andere berechtigte Begleitpersonen laut Buchungsregelung. Bei Unklarheiten bitte zunächst ohne Angabe buchen eine spätere Ergänzung ist möglich.</hinweis>
<preis>0,00</preis>
<status>Frei</status>
</leistung>
<leistung id="2" idbuspro="183659" unterart="SON" termin="01.01.2030" bis="06.01.2030" automatisch_buchen="False" pflicht="False">
<text>Bettwäsche-Set inkl. Handtuch</text>
<hinweis>Das Bettwäsche-Set umfasst Spannbettlaken, Kissen- und Deckenbezug. Beim Handtuch handelt es sich um ein Duschtuch (ca. 70x140cm).</hinweis>
<preis>13,90</preis>
<status>Frei</status>
</leistung>
<leistung id="3" idbuspro="183656" unterart="SPA" termin="01.01.2030" bis="06.01.2030" automatisch_buchen="False" pflicht="False">
<text>Skipass 6 Tage (Erwachsene) (Jahrgang 1926-2006) inkl. Anreisetag</text>
<hinweis_stamm>JG:1926-2006</hinweis_stamm>
<preis>59,00</preis>
<status>Frei</status>
</leistung>
<leistung id="4" idbuspro="183660" unterart="VPF" termin="01.01.2030" bis="01.01.2030" automatisch_buchen="False" pflicht="False">
<text>Frühstück am Anreisetag</text>
<preis>8,90</preis>
<status>Frei</status>
</leistung>
</lei_sonstiges>
<lei_befoerderung>
<leistung id="5" idbuspro="183649" unterart="BUS" termin="30.12.2029" bis="01.01.2030" automatisch_buchen="False" pflicht="True">
<text>Bus-Hinfahrt</text>
<hinweis>Bus fährt nur bei ausreichender Teilnehmerzahl</hinweis>
<preis>0,00</preis>
<status>Frei</status>
<richtung>HIN</richtung>
</leistung>
</lei_befoerderung>
<hotel id="1" code="SSTGR" idbuspro="157047" ankunft="01.01.2030" abreise="06.01.2030">
<zimmer>
<preis zimmercode="4erDW" idbuspro_zimmer="95" zimmertext="4er Zimmer Dusche/WC" MinPax="4" MaxPax="4" naechte="5" kat="GR" vpcode="Lt. Auss." idbuspro_vp="2" vptext="Laut Ausschreibung" preis="689,00" status="Frei" verfuegbar="8" />
</zimmer>
</hotel>
</termin>
</reise>
</reisen>';
$crawler = new Crawler($xmlContent);
$travelNode = $crawler->filterXPath('//reise/termin')->first();
$travel = $this->parser->parse($travelNode);
// Test additional services descriptions
$additionalServices = $travel->additionalServices;
// Service with description (ID 183658 - Begleitperson)
$this->assertArrayHasKey(183658, $additionalServices);
$begleitpersonService = $additionalServices[183658];
$this->assertSame('Begleitperson (gemäß Gruppenregelung)', $begleitpersonService->label);
$this->assertSame(
'Gilt für Betreuende, Lehrkräfte, Gruppenleitungen oder andere berechtigte Begleitpersonen laut Buchungsregelung. Bei Unklarheiten bitte zunächst ohne Angabe buchen eine spätere Ergänzung ist möglich.',
$begleitpersonService->description
);
// Service with description (ID 183659 - Bettwäsche)
$this->assertArrayHasKey(183659, $additionalServices);
$bettwascheService = $additionalServices[183659];
$this->assertSame('Bettwäsche-Set inkl. Handtuch', $bettwascheService->label);
$this->assertSame(
'Das Bettwäsche-Set umfasst Spannbettlaken, Kissen- und Deckenbezug. Beim Handtuch handelt es sich um ein Duschtuch (ca. 70x140cm).',
$bettwascheService->description
);
// Service without description (ID 183656 - Skipass, has hinweis_stamm but no hinweis)
$this->assertArrayHasKey(183656, $additionalServices);
$skipassService = $additionalServices[183656];
$this->assertSame('Skipass 6 Tage (Erwachsene) (Jahrgang 1926-2006) inkl. Anreisetag', $skipassService->label);
$this->assertNull($skipassService->description); // No hinweis node for this service
$this->assertSame('JG:1926-2006', $skipassService->rawAgeConstraintData); // But has age constraints
// Service without description (ID 183660 - Frühstück)
$this->assertArrayHasKey(183660, $additionalServices);
$fruehstueckService = $additionalServices[183660];
$this->assertSame('Frühstück am Anreisetag', $fruehstueckService->label);
$this->assertNull($fruehstueckService->description); // No hinweis node
// Test transportation services descriptions
$transportationServices = $travel->transportationServices;
$this->assertArrayHasKey(183649, $transportationServices);
$busService = $transportationServices[183649];
$this->assertSame('Bus-Hinfahrt', $busService->label);
$this->assertSame('Bus fährt nur bei ausreichender Teilnehmerzahl', $busService->description);
}
public function testParseServiceWithoutDescriptions(): void
{
$xmlContent = '<?xml version="1.0" encoding="utf-8"?>
<reisen>
<reise id="1" idbuspro="2187" code="SSTMR">
<termin id="1" idbuspro="11946" termin="01.01.2030" bis="06.01.2030" reiseart="F">
<text>Test Travel</text>
<abpreis>659,00</abpreis>
<lei_sonstiges>
<leistung id="1" idbuspro="183660" unterart="VPF">
<text>Service without description</text>
<preis>8,90</preis>
<status>Frei</status>
</leistung>
</lei_sonstiges>
<hotel id="1" idbuspro="157047">
<zimmer>
<preis zimmercode="4erDW" idbuspro_zimmer="95" zimmertext="4er Zimmer" MinPax="4" MaxPax="4" naechte="5" preis="689,00" status="Frei" verfuegbar="8" />
</zimmer>
</hotel>
</termin>
</reise>
</reisen>';
$crawler = new Crawler($xmlContent);
$travelNode = $crawler->filterXPath('//reise/termin')->first();
$travel = $this->parser->parse($travelNode);
$additionalServices = $travel->additionalServices;
$this->assertArrayHasKey(183660, $additionalServices);
$service = $additionalServices[183660];
$this->assertSame('Service without description', $service->label);
$this->assertNull($service->description); // No hinweis node should result in null description
}
public function testParseServiceWithEmptyDescription(): void
{
$xmlContent = '<?xml version="1.0" encoding="utf-8"?>
<reisen>
<reise id="1" idbuspro="2187" code="SSTMR">
<termin id="1" idbuspro="11946" termin="01.01.2030" bis="06.01.2030" reiseart="F">
<text>Test Travel</text>
<abpreis>659,00</abpreis>
<lei_sonstiges>
<leistung id="1" idbuspro="183660" unterart="VPF">
<text>Service with empty description</text>
<hinweis></hinweis>
<preis>8,90</preis>
<status>Frei</status>
</leistung>
</lei_sonstiges>
<hotel id="1" idbuspro="157047">
<zimmer>
<preis zimmercode="4erDW" idbuspro_zimmer="95" zimmertext="4er Zimmer" MinPax="4" MaxPax="4" naechte="5" preis="689,00" status="Frei" verfuegbar="8" />
</zimmer>
</hotel>
</termin>
</reise>
</reisen>';
$crawler = new Crawler($xmlContent);
$travelNode = $crawler->filterXPath('//reise/termin')->first();
$travel = $this->parser->parse($travelNode);
$additionalServices = $travel->additionalServices;
$this->assertArrayHasKey(183660, $additionalServices);
$service = $additionalServices[183660];
$this->assertSame('Service with empty description', $service->label);
$this->assertNull($service->description); // Empty hinweis should result in null description
}
}
@@ -0,0 +1,203 @@
<?php
declare(strict_types=1);
namespace App\Tests\Form\Service;
use App\BusProNet\Model\Travel;
use App\Form\Model\BookingCreateDto;
use App\Form\Model\ParticipantDto;
use App\Form\Service\ParticipantLicensePlateFieldHandler;
use PHPUnit\Framework\TestCase;
class ParticipantLicensePlateFieldHandlerTest extends TestCase
{
private ParticipantLicensePlateFieldHandler $handler;
protected function setUp(): void
{
$this->handler = new ParticipantLicensePlateFieldHandler();
}
public function testGetFieldName(): void
{
$this->assertSame('licensePlate', $this->handler->getFieldName());
}
public function testGetDependencies(): void
{
$this->assertSame(['parking'], $this->handler->getDependencies());
}
public function testShouldProcessAlwaysReturnsTrue(): void
{
$this->assertTrue($this->handler->shouldProcess([], 0));
$this->assertTrue($this->handler->shouldProcess(['some' => 'data'], 5));
}
public function testProcessFieldWithoutParticipant(): void
{
$travel = new Travel();
$bookingDto = new BookingCreateDto($travel, 1);
$submittedData = ['licensePlate' => 'AB-CD 123'];
$this->handler->processField($submittedData, $bookingDto, 0);
// Should handle gracefully when participant doesn't exist
$this->expectNotToPerformAssertions();
}
public function testProcessFieldClearsLicensePlateWhenParkingNotSelected(): void
{
$participant = new ParticipantDto();
$participant->parking = false; // Parking not selected
$participant->licensePlate = 'AB-CD 123'; // Should be cleared
$travel = new Travel();
$bookingDto = new BookingCreateDto($travel, 1);
$bookingDto->participants = [$participant];
$submittedData = ['licensePlate' => 'XY-ZZ 999'];
$this->handler->processField($submittedData, $bookingDto, 0);
$this->assertNull($participant->licensePlate);
}
public function testProcessFieldSetsLicensePlateWhenParkingSelected(): void
{
$participant = new ParticipantDto();
$participant->parking = true; // Parking selected
$travel = new Travel();
$bookingDto = new BookingCreateDto($travel, 1);
$bookingDto->participants = [$participant];
$submittedData = ['licensePlate' => 'AB-CD 123'];
$this->handler->processField($submittedData, $bookingDto, 0);
$this->assertSame('AB-CD 123', $participant->licensePlate);
}
public function testProcessFieldHandlesEmptyLicensePlate(): void
{
$participant = new ParticipantDto();
$participant->parking = true; // Parking selected
$travel = new Travel();
$bookingDto = new BookingCreateDto($travel, 1);
$bookingDto->participants = [$participant];
$submittedData = ['licensePlate' => ''];
$this->handler->processField($submittedData, $bookingDto, 0);
$this->assertNull($participant->licensePlate);
}
public function testProcessFieldHandlesNullLicensePlate(): void
{
$participant = new ParticipantDto();
$participant->parking = true; // Parking selected
$travel = new Travel();
$bookingDto = new BookingCreateDto($travel, 1);
$bookingDto->participants = [$participant];
$submittedData = ['licensePlate' => null];
$this->handler->processField($submittedData, $bookingDto, 0);
$this->assertNull($participant->licensePlate);
}
public function testProcessFieldTrimsWhitespace(): void
{
$participant = new ParticipantDto();
$participant->parking = true; // Parking selected
$travel = new Travel();
$bookingDto = new BookingCreateDto($travel, 1);
$bookingDto->participants = [$participant];
$submittedData = ['licensePlate' => ' AB-CD 123 '];
$this->handler->processField($submittedData, $bookingDto, 0);
$this->assertSame('AB-CD 123', $participant->licensePlate);
}
public function testProcessFieldHandlesWhitespaceOnlyAsEmpty(): void
{
$participant = new ParticipantDto();
$participant->parking = true; // Parking selected
$travel = new Travel();
$bookingDto = new BookingCreateDto($travel, 1);
$bookingDto->participants = [$participant];
$submittedData = ['licensePlate' => ' '];
$this->handler->processField($submittedData, $bookingDto, 0);
$this->assertNull($participant->licensePlate);
}
public function testProcessFieldHandlesNonStringValues(): void
{
$participant = new ParticipantDto();
$participant->parking = true; // Parking selected
$travel = new Travel();
$bookingDto = new BookingCreateDto($travel, 1);
$bookingDto->participants = [$participant];
// Test with integer value
$submittedData = ['licensePlate' => 123];
$this->handler->processField($submittedData, $bookingDto, 0);
$this->assertNull($participant->licensePlate);
// Test with array value
$submittedData = ['licensePlate' => ['invalid']];
$this->handler->processField($submittedData, $bookingDto, 0);
$this->assertNull($participant->licensePlate);
}
public function testProcessFieldHandlesMissingLicensePlateField(): void
{
$participant = new ParticipantDto();
$participant->parking = true; // Parking selected
$travel = new Travel();
$bookingDto = new BookingCreateDto($travel, 1);
$bookingDto->participants = [$participant];
$submittedData = []; // No licensePlate field
$this->handler->processField($submittedData, $bookingDto, 0);
$this->assertNull($participant->licensePlate);
}
public function testProcessFieldWithDifferentParticipantIndex(): void
{
$participant1 = new ParticipantDto();
$participant1->parking = false;
$participant2 = new ParticipantDto();
$participant2->parking = true;
$travel = new Travel();
$bookingDto = new BookingCreateDto($travel, 1);
$bookingDto->participants = [$participant1, $participant2];
$submittedData = ['licensePlate' => 'AB-CD 123'];
// Process for participant at index 1
$this->handler->processField($submittedData, $bookingDto, 1);
$this->assertNull($participant1->licensePlate); // Should not be affected
$this->assertSame('AB-CD 123', $participant2->licensePlate); // Should be set
}
}