feat: split booking between participants and applicant

addresses #869f2q1vu
This commit is contained in:
fromme committed 2026-10-19 09:18:22 +02:00
1 parent 2d49212b08
commit 1ff293f41f
44 files changed
+2024 -316

No files matched your search

@@ -155,6 +155,79 @@ class BookingDataProcessorTest extends TestCase
$this->assertSame(194672, $dto->participants[0]->veg?->id);
}
public function testApplicantAddressIsCopiedOntoFirstParticipantOnAnOrdinaryBooking(): void
{
$booking = $this->createMockBooking();
$travel = $this->createMockTravel();
$booking->applicant->personId = 4711;
$booking->participants[0]->personId = 4711;
$booking->applicant->address = $this->createFilledAddress('Elternweg 7');
$booking->participants[0]->address = new Address();
$booking->participants[0]->height = null;
$dto = $this->processor->createBookingDtoFromBooking($booking, $travel);
$this->assertSame(
'Elternweg 7',
$dto->participants[0]->address?->street,
'BPN returns the full record only in <anmelder>, so participant[0] is filled from it'
);
$this->assertSame('175', $dto->participants[0]->height);
}
public function testApplicantAddressIsNotCopiedOntoFirstParticipantOnASplitBooking(): void
{
$booking = $this->createMockBooking();
$travel = $this->createMockTravel();
// The applicant is a different person: a parent who booked for their child.
$booking->applicant->personId = 4711;
$booking->participants[0]->personId = 4712;
$booking->applicant->address = $this->createFilledAddress('Elternweg 7');
$booking->participants[0]->address = new Address();
$booking->participants[0]->height = null;
$dto = $this->processor->createBookingDtoFromBooking($booking, $travel);
$this->assertNull(
$dto->participants[0]->address?->street,
"The parent's address must not be written onto the child at the next edit submit"
);
$this->assertNull(
$dto->participants[0]->height,
"The parent's body dimensions must not be written onto the child either"
);
}
public function testApplicantAddressIsCopiedWhenPersonIdsAreUnknown(): void
{
$booking = $this->createMockBooking();
$travel = $this->createMockTravel();
$booking->applicant->address = $this->createFilledAddress('Elternweg 7');
$booking->participants[0]->address = new Address();
$dto = $this->processor->createBookingDtoFromBooking($booking, $travel);
$this->assertSame(
'Elternweg 7',
$dto->participants[0]->address?->street,
'Incomplete BPN data falls back to the established, stricter behaviour'
);
}
private function createFilledAddress(string $street): Address
{
$address = new Address();
$address->street = $street;
$address->postCode = '99999';
$address->city = 'MusterOrt';
$address->country = 'D';
return $address;
}
public function testTransportationServicesProcessing(): void
{
$formData = $this->createFormDataWithTransportation();
@@ -0,0 +1,152 @@
<?php
declare(strict_types=1);
namespace App\Tests\BusProNet\DataProcessor;
use App\BusProNet\DataProcessor\BookingPayloadBuilder;
use App\BusProNet\DataProcessor\ServiceMappingCollector;
use App\BusProNet\Model\Address;
use App\BusProNet\Model\Travel;
use App\Form\Model\BookingDto;
use App\Form\Model\ParticipantDto;
use PHPUnit\Framework\TestCase;
/**
* Covers who the <anmelder> block is built from, and the participant email fallback.
*/
class BookingPayloadBuilderTest extends TestCase
{
private BookingPayloadBuilder $builder;
protected function setUp(): void
{
$mappingCollector = $this->createStub(ServiceMappingCollector::class);
$mappingCollector->method('getPromoVoucherCodeForParticipant')->willReturn(null);
$this->builder = new BookingPayloadBuilder($mappingCollector);
}
public function testAnmelderComesFromFirstParticipantWhenTheApplicantTravels(): void
{
$bookingDto = $this->createBookingDto();
$payload = $this->builder->buildCreatePayload($bookingDto, 'BUCHUNG');
$this->assertSame('Kind', $payload['anmelder']['vorname']);
$this->assertSame('Kind', $payload['teilnehmerliste']['teilnehmer'][0]['vorname']);
}
public function testAnmelderComesFromTheApplicantRecordWhenTheyDoNotTravel(): void
{
$bookingDto = $this->createBookingDto();
$bookingDto->applicantTravels = false;
$bookingDto->applicant = $this->createPerson('Elternteil', '[email protected]');
$payload = $this->builder->buildCreatePayload($bookingDto, 'BUCHUNG');
$this->assertSame('Elternteil', $payload['anmelder']['vorname']);
$this->assertSame('[email protected]', $payload['anmelder']['kommunikation']['email']);
$this->assertSame(
'Kind',
$payload['teilnehmerliste']['teilnehmer'][0]['vorname'],
'participant[0] stays an ordinary traveller'
);
}
public function testAnmelderStillCarriesNoIdsOnCreate(): void
{
$bookingDto = $this->createBookingDto();
$bookingDto->applicantTravels = false;
$applicant = $this->createPerson('Elternteil', '[email protected]');
$applicant->personId = 4711;
$applicant->addressId = 1234;
$bookingDto->applicant = $applicant;
$payload = $this->builder->buildCreatePayload($bookingDto, 'BUCHUNG');
$this->assertArrayNotHasKey('idadresseperson', $payload['anmelder']);
$this->assertArrayNotHasKey('idadresse', $payload['anmelder']);
}
public function testParticipantWithoutEmailInheritsTheApplicants(): void
{
$bookingDto = $this->createBookingDto();
$bookingDto->applicantTravels = false;
$bookingDto->applicant = $this->createPerson('Elternteil', '[email protected]');
$bookingDto->participants[0]->email = null;
$payload = $this->builder->buildCreatePayload($bookingDto, 'BUCHUNG');
$this->assertSame(
'[email protected]',
$payload['teilnehmerliste']['teilnehmer'][0]['kommunikation']['email'],
'BusPro rejects a participant without a Mail-Adresse with 650, but accepts a shared one'
);
}
public function testParticipantKeepsTheirOwnEmail(): void
{
$bookingDto = $this->createBookingDto();
$bookingDto->applicantTravels = false;
$bookingDto->applicant = $this->createPerson('Elternteil', '[email protected]');
$payload = $this->builder->buildCreatePayload($bookingDto, 'BUCHUNG');
$this->assertSame(
'[email protected]',
$payload['teilnehmerliste']['teilnehmer'][0]['kommunikation']['email']
);
}
public function testOrdinaryBookingPayloadIsUnchangedByTheApplicantSwitch(): void
{
$withApplicantTravelling = $this->builder->buildCreatePayload($this->createBookingDto(), 'BUCHUNG');
$bookingDto = $this->createBookingDto();
$bookingDto->applicant = $this->createPerson('Elternteil', '[email protected]');
$this->assertSame(
$withApplicantTravelling,
$this->builder->buildCreatePayload($bookingDto, 'BUCHUNG'),
'A stale applicant record must not leak into a booking where the applicant travels'
);
}
private function createBookingDto(): BookingDto
{
$travel = new Travel();
$travel->id = 12500;
$travel->hotelId = 223055;
$bookingDto = new BookingDto($travel, 223055);
$bookingDto->agencyId = 77883;
$participant = $this->createPerson('Kind', '[email protected]');
$participant->index = 0;
$bookingDto->participants = [$participant];
return $bookingDto;
}
private function createPerson(string $firstName, string $email): ParticipantDto
{
$person = new ParticipantDto();
$person->firstName = $firstName;
$person->lastName = 'Probe';
$person->gender = 'W';
$person->nationality = 'D';
$person->dateOfBirth = new \DateTimeImmutable('2014-09-16');
$person->email = $email;
$person->mobile = '0171/222222';
$address = new Address();
$address->street = 'Probeweg 7';
$address->postCode = '99999';
$address->city = 'MusterOrt';
$address->country = 'D';
$person->address = $address;
return $person;
}
}