diff --git a/src/BusProNet/ApiClient.php b/src/BusProNet/ApiClient.php
index e94ad39..4ca50d2 100644
--- a/src/BusProNet/ApiClient.php
+++ b/src/BusProNet/ApiClient.php
@@ -6,6 +6,7 @@ use App\BusProNet\ApiResponseParser\ResponseParser;
use App\BusProNet\Exception\ApiClientException;
use App\BusProNet\Model\BaseData;
use App\BusProNet\Model\Booking;
+use App\BusProNet\Model\BookingUpdate;
use App\BusProNet\Model\CrmAttributes;
use App\BusProNet\Model\File;
use App\BusProNet\Model\Notification;
@@ -13,6 +14,7 @@ use App\BusProNet\Model\PersonalData;
use App\Form\Model\BookingData;
use Psr\Log\LoggerInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
+use Symfony\Component\Serializer\Encoder\XmlEncoder;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Component\Uid\Uuid;
use Symfony\Contracts\HttpClient\HttpClientInterface;
@@ -44,14 +46,12 @@ class ApiClient
public function getPersonalData(string $email, string $password): Notification|PersonalData
{
$data = [
- 'anfrage' => [
- 'user' => $this->config['bpn_username'],
- 'key' => $this->createKey($this->config['bpn_username'], $this->config['bpn_password'], static::TYPE_CUSTOMER_DATA),
- 'satz' => ['@typ' => static::TYPE_CUSTOMER_DATA],
- 'art' => 'Adressdaten',
- 'email' => $email,
- 'passwort' => $password,
- ],
+ 'user' => $this->config['bpn_username'],
+ 'key' => $this->createKey($this->config['bpn_username'], $this->config['bpn_password'], static::TYPE_CUSTOMER_DATA),
+ 'satz' => ['@typ' => static::TYPE_CUSTOMER_DATA],
+ 'art' => 'Adressdaten',
+ 'email' => $email,
+ 'passwort' => $password,
];
return $this->sendRequest(static::TYPE_CUSTOMER_DATA, $data);
@@ -63,16 +63,14 @@ class ApiClient
public function updatePersonalData(string $email, string $password, PersonalData $personalData): Notification|PersonalData
{
$data = [
- 'anfrage' => [
- 'user' => $this->config['bpn_username'],
- 'key' => $this->createKey($this->config['bpn_username'], $this->config['bpn_password'], static::TYPE_CUSTOMER_DATA),
- 'satz' => ['@typ' => static::TYPE_CUSTOMER_DATA],
- 'art' => 'Adressdaten_Ändern',
- 'email' => $email,
- 'passwort' => $password,
- 'idadresse' => $personalData->addressId,
- 'adressdaten' => $personalData->toPayload(),
- ],
+ 'user' => $this->config['bpn_username'],
+ 'key' => $this->createKey($this->config['bpn_username'], $this->config['bpn_password'], static::TYPE_CUSTOMER_DATA),
+ 'satz' => ['@typ' => static::TYPE_CUSTOMER_DATA],
+ 'art' => 'Adressdaten_Ändern',
+ 'email' => $email,
+ 'passwort' => $password,
+ 'idadresse' => $personalData->addressId,
+ 'adressdaten' => $personalData->toPayload(),
];
return $this->sendRequest(static::TYPE_CUSTOMER_DATA, $data);
@@ -84,14 +82,12 @@ class ApiClient
public function getBookings(string $email, string $password): Notification|BaseData
{
$data = [
- 'anfrage' => [
- 'user' => $this->config['bpn_username'],
- 'key' => $this->createKey($this->config['bpn_username'], $this->config['bpn_password'], static::TYPE_CUSTOMER_DATA),
- 'satz' => ['@typ' => static::TYPE_CUSTOMER_DATA],
- 'art' => 'Vorgänge',
- 'email' => $email,
- 'passwort' => $password,
- ],
+ 'user' => $this->config['bpn_username'],
+ 'key' => $this->createKey($this->config['bpn_username'], $this->config['bpn_password'], static::TYPE_CUSTOMER_DATA),
+ 'satz' => ['@typ' => static::TYPE_CUSTOMER_DATA],
+ 'art' => 'Vorgänge',
+ 'email' => $email,
+ 'passwort' => $password,
];
return $this->sendRequest(static::TYPE_CUSTOMER_DATA, $data);
@@ -103,33 +99,29 @@ class ApiClient
public function getBooking(string $email, string $password, int $id): Notification|Booking
{
$data = [
- 'anfrage' => [
- 'user' => $this->config['bpn_username'],
- 'key' => $this->createKey($this->config['bpn_username'], $this->config['bpn_password'], static::TYPE_CUSTOMER_DATA),
- 'satz' => ['@typ' => static::TYPE_CUSTOMER_DATA],
- 'art' => 'Vorgang_Details',
- 'email' => $email,
- 'passwort' => $password,
- 'idbuchung' => $id,
- ],
+ 'user' => $this->config['bpn_username'],
+ 'key' => $this->createKey($this->config['bpn_username'], $this->config['bpn_password'], static::TYPE_CUSTOMER_DATA),
+ 'satz' => ['@typ' => static::TYPE_CUSTOMER_DATA],
+ 'art' => 'Vorgang_Details',
+ 'email' => $email,
+ 'passwort' => $password,
+ 'idbuchung' => $id,
];
return $this->sendRequest(static::TYPE_CUSTOMER_DATA, $data);
}
- public function updateBooking(string $email, string $password, BookingData $formData): Notification
+ public function updateBooking(BookingData $formData, bool $dryRun = true): Notification|BookingUpdate
{
$booking = $formData->booking;
+ $mode = $dryRun ? 'Anfrage' : 'Buchung';
$data = [
- 'anfrage' => [
- 'user' => $this->config['bpn_username'],
- 'key' => $this->createKey($this->config['bpn_username'], $this->config['bpn_password'], static::TYPE_BOOKING_UPDATE),
- 'satz' => ['@typ' => static::TYPE_BOOKING_UPDATE],
- 'buchungsart' => 'Buchung',
- 'idbuchung' => $booking->id,
- ...$booking->toPayload($formData),
- ],
+ 'user' => $this->config['bpn_username'],
+ 'key' => $this->createKey($this->config['bpn_username'], $this->config['bpn_password'], static::TYPE_BOOKING_UPDATE),
+ 'satz' => ['@typ' => static::TYPE_BOOKING_UPDATE],
+ 'buchungsart' => $mode,
+ ...$booking->toPayload($formData),
];
return $this->sendRequest(static::TYPE_BOOKING_UPDATE, $data);
@@ -141,13 +133,11 @@ class ApiClient
public function getMutableFields(int $id): Notification|BaseData
{
$data = [
- 'anfrage' => [
- 'user' => $this->config['bpn_username'],
- 'key' => $this->createKey($this->config['bpn_username'], $this->config['bpn_password'], static::TYPE_MUTABLE_FIELDS),
- 'satz' => ['@typ' => static::TYPE_MUTABLE_FIELDS],
- 'art' => 'Vorgang_Details',
- 'idreise' => $id,
- ],
+ 'user' => $this->config['bpn_username'],
+ 'key' => $this->createKey($this->config['bpn_username'], $this->config['bpn_password'], static::TYPE_MUTABLE_FIELDS),
+ 'satz' => ['@typ' => static::TYPE_MUTABLE_FIELDS],
+ 'art' => 'Vorgang_Details',
+ 'idreise' => $id,
];
return $this->sendRequest(static::TYPE_MUTABLE_FIELDS, $data);
@@ -159,12 +149,10 @@ class ApiClient
public function getAvailabilities(int $id): Notification|BaseData
{
$data = [
- 'anfrage' => [
- 'user' => $this->config['bpn_username'],
- 'key' => $this->createKey($this->config['bpn_username'], $this->config['bpn_password'], static::TYPE_AVAILABILITY),
- 'satz' => ['@typ' => static::TYPE_AVAILABILITY],
- 'idreise' => $id,
- ],
+ 'user' => $this->config['bpn_username'],
+ 'key' => $this->createKey($this->config['bpn_username'], $this->config['bpn_password'], static::TYPE_AVAILABILITY),
+ 'satz' => ['@typ' => static::TYPE_AVAILABILITY],
+ 'idreise' => $id,
];
return $this->sendRequest(static::TYPE_AVAILABILITY, $data);
@@ -176,13 +164,11 @@ class ApiClient
public function resetPassword(string $email): Notification
{
$data = [
- 'anfrage' => [
- 'user' => $this->config['bpn_username'],
- 'key' => $this->createKey($this->config['bpn_username'], $this->config['bpn_password'], static::TYPE_CUSTOMER_DATA),
- 'satz' => ['@typ' => static::TYPE_CUSTOMER_DATA],
- 'art' => 'Passwort_Anfrage',
- 'email' => $email,
- ],
+ 'user' => $this->config['bpn_username'],
+ 'key' => $this->createKey($this->config['bpn_username'], $this->config['bpn_password'], static::TYPE_CUSTOMER_DATA),
+ 'satz' => ['@typ' => static::TYPE_CUSTOMER_DATA],
+ 'art' => 'Passwort_Anfrage',
+ 'email' => $email,
];
return $this->sendRequest(static::TYPE_CUSTOMER_DATA, $data);
@@ -194,14 +180,12 @@ class ApiClient
public function getCrmAttributes(string $email, string $password): Notification|CrmAttributes
{
$data = [
- 'anfrage' => [
- 'user' => $this->config['bpn_username'],
- 'key' => $this->createKey($this->config['bpn_username'], $this->config['bpn_password'], static::TYPE_CUSTOMER_DATA),
- 'satz' => ['@typ' => static::TYPE_CUSTOMER_DATA],
- 'art' => 'SelektionCRM',
- 'email' => $email,
- 'passwort' => $password,
- ],
+ 'user' => $this->config['bpn_username'],
+ 'key' => $this->createKey($this->config['bpn_username'], $this->config['bpn_password'], static::TYPE_CUSTOMER_DATA),
+ 'satz' => ['@typ' => static::TYPE_CUSTOMER_DATA],
+ 'art' => 'SelektionCRM',
+ 'email' => $email,
+ 'passwort' => $password,
];
return $this->sendRequest(static::TYPE_CUSTOMER_DATA, $data);
@@ -213,11 +197,9 @@ class ApiClient
public function getBaseData(string $type): Notification|BaseData
{
$data = [
- 'anfrage' => [
- 'user' => $this->config['bpn_username'],
- 'key' => $this->createKey($this->config['bpn_username'], $this->config['bpn_password'], $type),
- 'satz' => ['@typ' => $type],
- ],
+ 'user' => $this->config['bpn_username'],
+ 'key' => $this->createKey($this->config['bpn_username'], $this->config['bpn_password'], $type),
+ 'satz' => ['@typ' => $type],
];
return $this->sendRequest($type, $data);
@@ -230,15 +212,13 @@ class ApiClient
public function getDocuments(string $email, string $password, int $id, string $type): mixed
{
$data = [
- 'anfrage' => [
- 'user' => $this->config['bpn_username'],
- 'key' => $this->createKey($this->config['bpn_username'], $this->config['bpn_password'], static::TYPE_CUSTOMER_DATA),
- 'satz' => ['@typ' => static::TYPE_CUSTOMER_DATA],
- 'art' => $type,
- 'email' => $email,
- 'passwort' => $password,
- 'idbuchung' => $id,
- ],
+ 'user' => $this->config['bpn_username'],
+ 'key' => $this->createKey($this->config['bpn_username'], $this->config['bpn_password'], static::TYPE_CUSTOMER_DATA),
+ 'satz' => ['@typ' => static::TYPE_CUSTOMER_DATA],
+ 'art' => $type,
+ 'email' => $email,
+ 'passwort' => $password,
+ 'idbuchung' => $id,
];
return $this->sendRequest(static::TYPE_CUSTOMER_DATA, $data);
@@ -249,18 +229,18 @@ class ApiClient
*/
private function sendRequest(string $type, array $data): mixed
{
- $requestId = (string) Uuid::v7();
+ $requestId = Uuid::v7();
$body = $this
->serializer
- ->serialize($data, 'xml')
+ ->serialize($data, 'xml', [
+ XmlEncoder::ROOT_NODE_NAME => 'anfrage',
+ XmlEncoder::ENCODING => 'UTF-8',
+ ])
;
if (true === $this->config['debug']) {
- $this->logger->info('Request sent', [
- 'id' => $requestId,
- 'request' => $body,
- ]);
+ $this->dumpXmlToFile('request', $requestId, $body);
}
try {
@@ -275,10 +255,7 @@ class ApiClient
$xml = $response->getContent();
if (true === $this->config['debug']) {
- $this->logger->info('Response received', [
- 'id' => $requestId,
- 'response' => $xml,
- ]);
+ $this->dumpXmlToFile('response', $requestId, $xml);
}
return $this->responseParser->parseXmlString($type, $xml);
@@ -289,6 +266,15 @@ class ApiClient
throw new ApiClientException($e->getMessage());
}
+ private function dumpXmlToFile(string $type, string $requestId, string $body): void
+ {
+ if (false === file_exists($this->config['target_folder_dumps'])) {
+ mkdir($this->config['target_folder_dumps']);
+ }
+
+ file_put_contents($this->config['target_folder_dumps'].'/'.$requestId.'_'.$type.'.xml', $body);
+ }
+
private function createKey(string $username, string $password, string $type): string
{
$date = (new \DateTimeImmutable())->format('Ymd');
@@ -302,6 +288,7 @@ class ApiClient
$optionsResolver->setRequired(['bpn_url', 'bpn_username', 'bpn_password']);
$optionsResolver->setDefaults([
'debug' => false,
+ 'target_folder_dumps' => '/var/www/html/var/bpn',
]);
return $optionsResolver->resolve($options);
diff --git a/src/BusProNet/ApiResponseParser/BookingResponseParser.php b/src/BusProNet/ApiResponseParser/BookingResponseParser.php
index 667245b..aa22bfe 100644
--- a/src/BusProNet/ApiResponseParser/BookingResponseParser.php
+++ b/src/BusProNet/ApiResponseParser/BookingResponseParser.php
@@ -20,6 +20,7 @@ class BookingResponseParser
$booking = new Booking();
$booking->id = (int) $xml->idbuchung;
+ $booking->agencyId = (int) $xml->idagentur;
$booking->bookingNumber = (int) $xml->vorgang;
$booking->invoiceNumber = (int) $xml->zahlungsdaten->rechnung;
$booking->totalPrice = $this->stringToFloat((string) $xml->zahlungsdaten->gesamtbetrag);
@@ -35,9 +36,16 @@ class BookingResponseParser
$booking->applicant = $this->parsePersonalData($xml->anmelder);
- $booking->participantsStatus = $this->stringToArray((string) $xml->status_teilnehmer, '/');
+ $participantsStatus = $this->stringToArray((string) $xml->status_teilnehmer, '/');
+ $booking->participantsStatus = $this->arrayToOneBased($participantsStatus);
+
$booking->participants = $this->parseParticipants($xml);
+ $paymentData = $xml->zahlung;
+ $booking->paymentId = (int) $paymentData->attributes()['idzahlungsart'];
+ $booking->paymentLabel = (string) $paymentData->attributes()['bezeichnung'];
+ $booking->paymentType = (string) $paymentData->attributes()['art'];
+
if ($xml->beförderungen) {
$booking->transportationServices = $this
->parseServices($xml->beförderungen->beförderung, Service::TYPE_TRANSPORTATION);
@@ -149,8 +157,10 @@ class BookingResponseParser
$room = new Room();
$room->id = $id;
$room->label = (string) $attributes['zimmer'];
- $room->dateFrom = $attributes['termin'] ? $this->stringToDate((string) $attributes['anreise']) : null;
- $room->dateTo = $attributes['terminbis'] ? $this->stringToDate((string) $attributes['abreise']) : null;
+ $room->category = (string) $attributes['kategorie'];
+ $room->boardId = (int) $attributes['idverpflegung'];
+ $room->dateFrom = $attributes['anreise'] ? $this->stringToDate((string) $attributes['anreise']) : null;
+ $room->dateTo = $attributes['abreise'] ? $this->stringToDate((string) $attributes['abreise']) : null;
$room->totalCount = (int) $attributes['anzahl'];
$room->minPax = (int) $attributes['minpax'];
$room->maxPax = (int) $attributes['maxpax'];
diff --git a/src/BusProNet/ApiResponseParser/BookingUpdateResponseParser.php b/src/BusProNet/ApiResponseParser/BookingUpdateResponseParser.php
new file mode 100644
index 0000000..929e5d3
--- /dev/null
+++ b/src/BusProNet/ApiResponseParser/BookingUpdateResponseParser.php
@@ -0,0 +1,19 @@
+valid = 'möglich' === (string) $xml->aenderung;
+ $bookingUpdate->totalPrice = $this->stringToFloat((string) $xml->gesamtpreis);
+
+ return $bookingUpdate;
+ }
+}
\ No newline at end of file
diff --git a/src/BusProNet/ApiResponseParser/ResponseParser.php b/src/BusProNet/ApiResponseParser/ResponseParser.php
index c67210c..4a0935a 100644
--- a/src/BusProNet/ApiResponseParser/ResponseParser.php
+++ b/src/BusProNet/ApiResponseParser/ResponseParser.php
@@ -49,6 +49,8 @@ class ResponseParser
return (new MutableFieldsResponseParser())->parse($xml);
case ApiClient::TYPE_AVAILABILITY:
return (new AvailabilitiesResponseParser())->parse($xml);
+ case ApiClient::TYPE_BOOKING_UPDATE:
+ return (new BookingUpdateResponseParser())->parse($xml);
}
throw new ResponseParserException('Unable to parse XML response');
diff --git a/src/BusProNet/DataLoader/TravelDataLoader.php b/src/BusProNet/DataLoader/TravelDataLoader.php
index 7bb72fd..decb44e 100644
--- a/src/BusProNet/DataLoader/TravelDataLoader.php
+++ b/src/BusProNet/DataLoader/TravelDataLoader.php
@@ -166,14 +166,16 @@ class TravelDataLoader extends AbstractDataLoader
$room = new Room();
$room->id = $roomId;
- $room->code = (string) $item->attributes()['zimmercode'];
- $room->label = (string) $item->attributes()['zimmertext'];
- $room->minPax = (int) $item->attributes()['MinPax'];
- $room->maxPax = (int) $item->attributes()['MaxPax'];
- $room->nights = (int) $item->attributes()['naechte'];
+ $room->code = (string) $attributes['zimmercode'];
+ $room->category = (string) $attributes['kat'];
+ $room->boardId = (int) $attributes['idbuspro_vp'];
+ $room->label = (string) $attributes['zimmertext'];
+ $room->minPax = (int) $attributes['MinPax'];
+ $room->maxPax = (int) $attributes['MaxPax'];
+ $room->nights = (int) $attributes['naechte'];
$room->price = $attributes['preis'] ? $this->stringToFloat((string) $attributes['preis']) : null;
$room->status = (string) $item->status;
- $room->available = (int) $item->attributes()['verfuegbar'];
+ $room->available = (int) $attributes['verfuegbar'];
$rooms[$roomId] = $room;
}
diff --git a/src/BusProNet/Model/Booking.php b/src/BusProNet/Model/Booking.php
index 5258314..bbd999a 100644
--- a/src/BusProNet/Model/Booking.php
+++ b/src/BusProNet/Model/Booking.php
@@ -7,6 +7,7 @@ use App\Form\Model\BookingData;
class Booking
{
public ?int $id = null;
+ public ?int $agencyId = null;
public ?int $bookingNumber = null;
public ?Travel $travelData = null;
public ?string $status = null;
@@ -22,11 +23,15 @@ class Booking
public ?string $hotelName = null;
public ?bool $document = null;
public ?float $payment = null;
+ public ?string $paymentId = null;
+ public ?string $paymentType = null;
+ public ?string $paymentLabel = null;
public array $participantsStatus = [];
public array $participants = [];
public array $transportationServices = [];
public array $additionalServices = [];
public array $rooms = [];
+ public array $pickups = [];
public ?int $invoiceNumber = null;
public ?float $totalPrice = null;
@@ -111,11 +116,11 @@ class Booking
public function toPayload(?BookingData $formData): array
{
- // Reset services to participants mappings
- foreach ([...$this->additionalServices, ...$this->transportationServices] as $service) {
+ // Reset mappings
+ foreach ([...$this->additionalServices, ...$this->transportationServices, ...$this->pickups] as $service) {
$service->mapping = [];
}
- // Update mappings and add previously unselected services
+ // Update mappings, add services and pickups
foreach ($formData->participants as $participant) {
$servicesToMap = [
...$participant->courses,
@@ -140,6 +145,12 @@ class Booking
}
$this->transportationServices[$service->id]->mapping[] = $participant->index;
}
+ if ('BUS' === $participant->transportationServiceTo->subType && null !== $selectedPickup = $participant->pickup) {
+ if (false === isset($this->pickups[$selectedPickup->id])) {
+ $this->pickups[$selectedPickup->id] = $selectedPickup;
+ }
+ $this->pickups[$selectedPickup->id]->mapping[] = $participant->index;
+ }
}
// Remove services with empty mappings
foreach ($this->additionalServices as $service) {
@@ -166,15 +177,79 @@ class Booking
$this->participants[$participant->index]->communication->mobile = $participant->mobile;
}
- return [
+ $payload = [
+ 'idbuchung' => $this->id,
'status' => $this->status,
+ 'idagentur' => $this->agencyId,
'idreise' => $this->travelId,
+ 'idpartner' => $this->hotelId,
'anmelder' => $this->applicant->toPayload(),
- 'teilnehmerliste' => [],
- 'beförderungen' => [],
- 'unterbringungen' => [],
- 'zusatzleistungen' => [],
- 'zustiege' => [],
+ 'zahlung' => [
+ '@idzahlungsart' => $this->paymentId,
+ '@bezeichnung' => $this->paymentLabel,
+ '@art' => $this->paymentType,
+ ],
+ 'teilnehmerliste' => [
+ 'teilnehmer' => [],
+ ],
+ 'zusatzleistungen' => [
+ 'zusatzleistung' => [],
+ ],
+ 'beförderungen' => [
+ 'beförderung' => [],
+ ],
+ 'ferienzielunterbringungen' => [
+ 'ferienzielunterbringung' => [],
+ ],
];
+
+ foreach ($this->participants as $index => $participant) {
+ $payload['teilnehmerliste']['teilnehmer'][] = [
+ '@id' => $index,
+ 'status' => $this->participantsStatus[$index],
+ ...$participant->toPayload(),
+ ];
+ }
+
+ foreach ($this->additionalServices as $service) {
+ $payload['zusatzleistungen']['zusatzleistung'][] = [
+ '@idleistung' => $service->id,
+ '@anzahl' => count($service->mapping),
+ '@zuordnung' => implode(',', $service->mapping),
+ ];
+ }
+
+ foreach ($this->transportationServices as $service) {
+ $payload['beförderungen']['beförderung'][] = [
+ '@idleistung' => $service->id,
+ '@anzahl' => count($service->mapping),
+ '@zuordnung' => implode(',', $service->mapping),
+ ];
+ }
+
+ foreach ($this->rooms as $room) {
+ $payload['ferienzielunterbringungen']['ferienzielunterbringung'][] = [
+ '@idzimmer' => $room->id,
+ '@kategorie' => $room->category,
+ '@idverpflegung' => $room->boardId,
+ '@anreise' => $room->dateFrom ? $room->dateFrom->format('d.m.Y') : null,
+ '@abreise' => $room->dateTo ? $room->dateTo->format('d.m.Y') : null,
+ '@anzahl' => count($room->mapping),
+ '@zuordnung' => implode(',', $room->mapping),
+ ];
+ }
+
+ if (0 < count($this->pickups)) {
+ $payload['zustiege']['zustieg'] = [];
+ foreach ($this->pickups as $pickup) {
+ $payload['zustiege']['zustieg'][] = [
+ '@idzustieg' => $pickup->id,
+ '@anzahl' => count($pickup->mapping),
+ '@zuordnung' => implode(',', $pickup->mapping),
+ ];
+ }
+ }
+
+ return $payload;
}
}
\ No newline at end of file
diff --git a/src/BusProNet/Model/BookingUpdate.php b/src/BusProNet/Model/BookingUpdate.php
new file mode 100644
index 0000000..6225f37
--- /dev/null
+++ b/src/BusProNet/Model/BookingUpdate.php
@@ -0,0 +1,11 @@
+ $this->name,
'anschrift' => $this->address->toPayload(),
'kommunikation' => $this->communication->toPayload(),
+ 'sonstiges1' => $this->height,
+ 'sonstiges2' => $this->weight,
+ 'sonstiges3' => $this->shoeSize,
];
}
}
\ No newline at end of file
diff --git a/src/BusProNet/Model/Pickup.php b/src/BusProNet/Model/Pickup.php
index 28c141d..0eda5c2 100644
--- a/src/BusProNet/Model/Pickup.php
+++ b/src/BusProNet/Model/Pickup.php
@@ -11,4 +11,5 @@ class Pickup
public ?string $street = null;
public ?\DateTimeImmutable $time = null;
public ?float $price = null;
+ public array $mapping = [];
}
\ No newline at end of file
diff --git a/src/BusProNet/Model/Room.php b/src/BusProNet/Model/Room.php
index 654abdd..ae88418 100644
--- a/src/BusProNet/Model/Room.php
+++ b/src/BusProNet/Model/Room.php
@@ -5,6 +5,8 @@ namespace App\BusProNet\Model;
class Room
{
public ?int $id = null;
+ public ?string $category = null;
+ public ?int $boardId = null;
public ?string $code = null;
public ?string $label = null;
public ?\DateTimeImmutable $dateFrom = null;
diff --git a/src/Controller/Booking/DownloadController.php b/src/Controller/Booking/DownloadController.php
new file mode 100644
index 0000000..3097c0b
--- /dev/null
+++ b/src/Controller/Booking/DownloadController.php
@@ -0,0 +1,76 @@
+ '\d+'],
+ defaults: ['fileType' => 'documents']
+ )]
+ #[Route(
+ path: '/bookings/{id}/confirmation',
+ name: 'app_booking_confirmation',
+ requirements: ['id' => '\d+'],
+ defaults: ['fileType' => 'confirmation']
+ )]
+ #[IsGranted("ROLE_USER")]
+ public function documents(int $id, string $fileType, Request $request): Response
+ {
+ $bpnUser = $request->getSession()->get('bpn_user');
+
+ if (null === $bpnUser) {
+ return $this->security->logout();
+ }
+
+ $type = match ($fileType) {
+ 'documents' => 'Dokumentendruck',
+ 'confirmation' => 'Vorgangdruck',
+ };
+
+ $file = $this
+ ->apiClient
+ ->getDocuments($bpnUser->getEmail(), $bpnUser->getPassword(), $id, $type)
+ ;
+
+ if (null === $file || $file instanceof Notification) {
+ $this->addFlash('error', 'Keine Dokumente vorhanden');
+
+ return $this->redirectToRoute('app_bookings');
+ }
+
+ $filename = u($file->filename)->ascii();
+
+ $response = new Response($file->content);
+
+ $disposition = $response->headers->makeDisposition(
+ ResponseHeaderBag::DISPOSITION_ATTACHMENT,
+ $filename,
+ md5($filename)
+ );
+
+ $response->headers->set('Content-Disposition', $disposition);
+ $response->headers->set('Content-Type', $file->mimeType);
+
+ return $response;
+ }
+}
\ No newline at end of file
diff --git a/src/Controller/Booking/EditController.php b/src/Controller/Booking/EditController.php
new file mode 100644
index 0000000..8c3823f
--- /dev/null
+++ b/src/Controller/Booking/EditController.php
@@ -0,0 +1,86 @@
+ '\d+'])]
+ #[IsGranted("ROLE_USER")]
+ public function edit(int $id, Request $request): Response
+ {
+ $bpnUser = $request->getSession()->get('bpn_user');
+
+ if (null === $bpnUser) {
+ return $this->security->logout();
+ }
+
+ $booking = $this->apiClient->getBooking($bpnUser->getEmail(), $bpnUser->getPassword(), $id);
+
+ //$this->denyAccessUnlessGranted('VIEW', $booking);
+
+ $travelData = $this->travelDataLoader->loadById($booking->travelId);
+
+ if (null === $travelData) {
+ $this->addFlash('error', 'Reisedaten nicht (mehr) verfügbar');
+
+ return $this->redirectToRoute('app_bookings');
+ }
+
+ $booking->travelData = $travelData;
+ $mutableFields = $this->apiClient->getMutableFields($booking->travelId);
+ $availabilities = $this->apiClient->getAvailabilities($booking->travelId);
+
+ $this->travelDataLoader->enrichServicesData($travelData, $availabilities);
+ $this->pickupDataLoader->enrichPickupsData($travelData);
+
+ $formData = BookingData::fromBooking($booking);
+
+ $form = $this->createForm(BookingType::class, $formData, [
+ 'hx_post' => $this->generateUrl('app_booking_edit', ['id' => $id]),
+ 'hx_target' => '#app',
+ 'hx_swap' => 'innerHTML show:top',
+ 'attr' => ['novalidate' => 'novalidate'],
+ 'travel' => $travelData,
+ 'mutable_fields' => $mutableFields,
+ ]);
+
+ $form->handleRequest($request);
+
+ if ($form->isSubmitted() && $form->isValid()) {
+ $this->apiClient->updateBooking($formData, false);
+
+ $this->addFlash('success', 'Buchung erfolgreich aktualisiert');
+
+ return new HxRedirectResponse($this->generateUrl('app_booking_edit', ['id' => $id]));
+ }
+
+ return $this->render('booking/edit.html.twig', [
+ 'booking' => $booking,
+ 'travelData' => $travelData,
+ 'mutableFields' => $mutableFields,
+ 'availabilities' => $availabilities,
+ 'form' => $form->createView(),
+ ]);
+ }
+}
\ No newline at end of file
diff --git a/src/Controller/Booking/IndexController.php b/src/Controller/Booking/IndexController.php
new file mode 100644
index 0000000..9f68711
--- /dev/null
+++ b/src/Controller/Booking/IndexController.php
@@ -0,0 +1,37 @@
+getSession()->get('bpn_user');
+
+ if (null === $bpnUser) {
+ return $this->security->logout();
+ }
+
+ $bookings = $this->apiClient->getBookings($bpnUser->getEmail(), $bpnUser->getPassword());
+
+ return $this->render('booking/index.html.twig', [
+ 'bookings' => $bookings->getItems(),
+ ]);
+ }
+}
\ No newline at end of file
diff --git a/src/Controller/BookingController.php b/src/Controller/BookingController.php
deleted file mode 100644
index d03121c..0000000
--- a/src/Controller/BookingController.php
+++ /dev/null
@@ -1,160 +0,0 @@
-getSession()->get('bpn_user');
-
- if (null === $bpnUser) {
- return $this->security->logout();
- }
-
- $bookings = $this->apiClient->getBookings($bpnUser->getEmail(), $bpnUser->getPassword());
-
- return $this->render('booking/index.html.twig', [
- 'bookings' => $bookings->getItems(),
- ]);
- }
-
- #[Route('/bookings/{id}/edit', name: 'app_booking_edit', requirements: ['id' => '\d+'])]
- #[IsGranted("ROLE_USER")]
- public function edit(int $id, Request $request): Response
- {
- $bpnUser = $request->getSession()->get('bpn_user');
-
- if (null === $bpnUser) {
- return $this->security->logout();
- }
-
- $booking = $this->apiClient->getBooking($bpnUser->getEmail(), $bpnUser->getPassword(), $id);
-
- //$this->denyAccessUnlessGranted('VIEW', $booking);
-
- $travelData = $this->travelDataLoader->loadById($booking->travelId);
-
- if (null === $travelData) {
- $this->addFlash('error', 'Reisedaten nicht (mehr) verfügbar');
-
- return $this->redirectToRoute('app_bookings');
- }
-
- $mutableFields = $this->apiClient->getMutableFields($booking->travelId);
- $availabilities = $this->apiClient->getAvailabilities($booking->travelId);
-
- $this->travelDataLoader->enrichServicesData($travelData, $availabilities);
- $this->pickupDataLoader->enrichPickupsData($travelData);
-
- $formData = BookingData::fromBooking($booking);
-
- $form = $this->createForm(BookingType::class, $formData, [
- 'hx_post' => $this->generateUrl('app_booking_edit', ['id' => $id]),
- 'hx_target' => '#app',
- 'hx_swap' => 'innerHTML show:top',
- 'attr' => ['novalidate' => 'novalidate'],
- 'travel' => $travelData,
- 'mutable_fields' => $mutableFields,
- ]);
- $form->handleRequest($request);
-
- if ($form->isSubmitted() && $form->isValid()) {
- $this->apiClient->updateBooking($bpnUser->getEmail(), $bpnUser->getPassword(), $formData);
- }
-
- return $this->render('booking/edit.html.twig', [
- 'booking' => $booking,
- 'travelData' => $travelData,
- 'mutableFields' => $mutableFields,
- 'availabilities' => $availabilities,
- 'form' => $form->createView(),
- ]);
- }
-
- #[Route('/bookings/{id}/documents', name: 'app_booking_documents', requirements: ['id' => '\d+'])]
- #[IsGranted("ROLE_USER")]
- public function documents(int $id, Request $request): Response
- {
- $bpnUser = $request->getSession()->get('bpn_user');
-
- if (null === $bpnUser) {
- return $this->security->logout();
- }
-
- $file = $this
- ->apiClient
- ->getDocuments($bpnUser->getEmail(), $bpnUser->getPassword(), $id, 'Dokumentdruck')
- ;
-
- if (null === $file || $file instanceof Notification) {
- $this->addFlash('error', 'Keine Dokumente vorhanden');
-
- return $this->redirectToRoute('app_bookings');
- }
-
- return $this->createDownloadResponse($file);
- }
-
- #[Route('/bookings/{id}/confirmation', name: 'app_booking_confirmation', requirements: ['id' => '\d+'])]
- #[IsGranted("ROLE_USER")]
- public function confirmation(int $id, Request $request): Response
- {
- $bpnUser = $request->getSession()->get('bpn_user');
-
- if (null === $bpnUser) {
- return $this->security->logout();
- }
-
- $file = $this
- ->apiClient
- ->getDocuments($bpnUser->getEmail(), $bpnUser->getPassword(), $id, 'Vorgangdruck')
- ;
-
- return $this->createDownloadResponse($file);
- }
-
- private function createDownloadResponse(File $file): Response
- {
- $filename = u($file->filename)->ascii();
-
- $response = new Response($file->content);
-
- $disposition = $response->headers->makeDisposition(
- ResponseHeaderBag::DISPOSITION_ATTACHMENT,
- $filename,
- md5($filename)
- );
-
- $response->headers->set('Content-Disposition', $disposition);
- $response->headers->set('Content-Type', $file->mimeType);
-
- return $response;
- }
-}
\ No newline at end of file
diff --git a/src/Form/BookingType.php b/src/Form/BookingType.php
index 6e54807..84f3924 100644
--- a/src/Form/BookingType.php
+++ b/src/Form/BookingType.php
@@ -26,6 +26,7 @@ final class BookingType extends AbstractType
->getTransportationServicesByDirection('HIN'),
'selectable_transportation_services_fro' => $options['travel']
->getTransportationServicesByDirection('RUECK'),
+ 'selectable_pickups' => $options['travel']->pickups,
],
'allow_add' => false,
'allow_delete' => false,
diff --git a/src/Form/Model/BookingData.php b/src/Form/Model/BookingData.php
index f256d04..9226bab 100644
--- a/src/Form/Model/BookingData.php
+++ b/src/Form/Model/BookingData.php
@@ -5,8 +5,10 @@ namespace App\Form\Model;
use App\BusProNet\Model\Booking;
use App\BusProNet\Model\PersonalData;
use App\BusProNet\Model\Service;
+use App\Validator\Constraints as AppAssert;
use Symfony\Component\Validator\Constraints as Assert;
+#[AppAssert\Booking]
class BookingData
{
public ?Booking $booking = null;
diff --git a/src/Form/Model/ParticipantData.php b/src/Form/Model/ParticipantData.php
index 3726d1d..b7f43f7 100644
--- a/src/Form/Model/ParticipantData.php
+++ b/src/Form/Model/ParticipantData.php
@@ -3,6 +3,7 @@
namespace App\Form\Model;
use App\BusProNet\Model\PersonalData;
+use App\BusProNet\Model\Pickup;
use App\BusProNet\Model\Service;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
@@ -47,9 +48,10 @@ class ParticipantData
public array $rentals = [];
public ?Service $transportationServiceTo = null;
public ?Service $transportationServiceFro = null;
+ public ?Pickup $pickup = null;
#[Assert\Callback]
- public function assertBodyMeasurements(ExecutionContextInterface $context): void
+ public function assertBodyMeasurementsValid(ExecutionContextInterface $context): void
{
if (0 === count($this->rentals)) {
return;
@@ -63,20 +65,31 @@ class ParticipantData
}
if (empty($this->shoeSize)) {
- $context->buildViolation('Bitte angeben Leihmaterial')
+ $context->buildViolation('Bitte angeben wegen Leihmaterial')
->atPath('shoeSize')
->addViolation()
;
}
if (empty($this->weight)) {
- $context->buildViolation('Bitte angeben Leihmaterial')
+ $context->buildViolation('Bitte angeben wegen Leihmaterial')
->atPath('weight')
->addViolation()
;
}
}
+ #[Assert\Callback]
+ public function assertPickupSelected(ExecutionContextInterface $context): void
+ {
+ if (null !== $this->transportationServiceTo && null === $this->pickup) {
+ $context->buildViolation('Bitte auswählen')
+ ->atPath('pickupTo')
+ ->addViolation()
+ ;
+ }
+ }
+
public static function fromPersonalData(PersonalData $personalData): static
{
$instance = new static();
diff --git a/src/Form/ParticipantType.php b/src/Form/ParticipantType.php
index 9807b2d..a51975f 100644
--- a/src/Form/ParticipantType.php
+++ b/src/Form/ParticipantType.php
@@ -3,6 +3,7 @@
namespace App\Form;
use App\BusProNet\Form\CountryType;
+use App\BusProNet\Model\Pickup;
use App\BusProNet\Model\Service;
use App\Form\Model\ParticipantData;
use Symfony\Component\Form\AbstractType;
@@ -35,6 +36,8 @@ class ParticipantType extends AbstractType
])
->add('gender', ChoiceType::class, [
'label' => 'Geschlecht',
+ 'required' => false,
+ 'placeholder' => 'keine Angabe',
'choices' => [
'männlich' => 'M',
'weiblich' => 'W',
@@ -65,9 +68,9 @@ class ParticipantType extends AbstractType
'required' => false,
])
->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) use ($options) {
- /** @var ParticipantData $participant */
- $participant = $event->getData();
- $participantIndex = $participant->index;
+ /** @var ParticipantData $participantData */
+ $participantData = $event->getData();
+ $participantIndex = $participantData->index;
$form = $event->getForm();
@@ -139,8 +142,43 @@ class ParticipantType extends AbstractType
'multiple' => false,
'choices' => $options['selectable_transportation_services_fro'],
])
+ ->add('pickup', ChoiceType::class, [
+ 'label' => 'Zustieg (bei Busanreise)',
+ 'multiple' => false,
+ 'expanded' => false,
+ 'choices' => $options['selectable_pickups'],
+ 'choice_value' => 'id',
+ 'choice_label' => function (?Pickup $pickup) {
+ if (null === $pickup) {
+ return null;
+ }
+
+ $price = $pickup->price;
+
+ if (null === $price || 0.0 === $price) {
+ return $pickup->city;
+ }
+
+ return sprintf('%s (%s€)',
+ $pickup->city,
+ number_format($price, 2, ',', '.')
+ );
+ },
+ ])
;
})
+ ->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) use ($options) {
+ $data = $event->getData();
+ $form = $event->getForm();
+
+ $transportationId = $data['transportationServiceTo'];
+ $transportation = $options['travel']->pickups[$transportationId] ?? null;
+
+ if (null !== $transportation && 'PKW' === $transportation->subType) {
+ $form->remove('pickup');
+ unset($data['pickup']);
+ }
+ })
;
}
@@ -155,6 +193,7 @@ class ParticipantType extends AbstractType
'selectable_rentals' => [],
'selectable_transportation_services_to' => [],
'selectable_transportation_services_fro' => [],
+ 'selectable_pickups' => [],
]);
}
}
\ No newline at end of file
diff --git a/src/Validator/Constraints/Booking.php b/src/Validator/Constraints/Booking.php
new file mode 100644
index 0000000..51a2253
--- /dev/null
+++ b/src/Validator/Constraints/Booking.php
@@ -0,0 +1,16 @@
+apiClient->updateBooking($bookingData);
+
+ if ($result instanceof Notification) {
+ $this->context
+ ->buildViolation($result->message)
+ ->addViolation()
+ ;
+ }
+
+ if ($result instanceof BookingUpdate && false === $result->valid) {
+ $this->context
+ ->buildViolation($constraint->message)
+ ->addViolation()
+ ;
+ }
+ }
+}
diff --git a/templates/booking/edit.html.twig b/templates/booking/edit.html.twig
index 5d8ca2c..97a399d 100644
--- a/templates/booking/edit.html.twig
+++ b/templates/booking/edit.html.twig
@@ -2,6 +2,14 @@
{% block content %}
{{ form_start(form) }}
+ {% if not form.vars.valid %}
+
| - Buchungsdatum - | -
- Reisedatum
+ Buchungsdatum/ Reisedatum |
Reise @@ -17,25 +15,19 @@ Vorgangsnr. | - Preis - | -
- offen
+ Preis/ offen |
Status | |
|---|---|---|---|---|---|---|
| - {{ booking.bookingDate | date('d.m.Y') }} - | -
- {{ booking.travelDate | date('d.m.Y') }}
+ {{ booking.bookingDate | date('d.m.Y') }}/ {{ booking.travelDate | date('d.m.Y') }} |
{{ booking.travel }} @@ -44,10 +36,7 @@ {{ booking.bookingNumber }} | - {{ booking.price|format_currency('EUR') }} - | -
- {{ booking.balance ? booking.balance|format_currency('EUR') : '-' }}
+ {{ booking.price|format_currency('EUR') }}/ {{ booking.balance ? booking.balance|format_currency('EUR') : '-' }} |
{{ booking.status }} @@ -76,6 +65,7 @@ |