diff --git a/composer.lock b/composer.lock index aff87b6..9417cc3 100644 --- a/composer.lock +++ b/composer.lock @@ -1358,16 +1358,16 @@ }, { "name": "knplabs/knp-menu", - "version": "v3.6.0", + "version": "v3.7.0", "source": { "type": "git", "url": "https://github.com/KnpLabs/KnpMenu.git", - "reference": "ac96b711cf7b4178747b0ecde79d7e5e05dbcd28" + "reference": "328bb430a0afad0b6eae2bd4bfea8e15db616fd6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/KnpLabs/KnpMenu/zipball/ac96b711cf7b4178747b0ecde79d7e5e05dbcd28", - "reference": "ac96b711cf7b4178747b0ecde79d7e5e05dbcd28", + "url": "https://api.github.com/repos/KnpLabs/KnpMenu/zipball/328bb430a0afad0b6eae2bd4bfea8e15db616fd6", + "reference": "328bb430a0afad0b6eae2bd4bfea8e15db616fd6", "shasum": "" }, "require": { @@ -1425,9 +1425,9 @@ ], "support": { "issues": "https://github.com/KnpLabs/KnpMenu/issues", - "source": "https://github.com/KnpLabs/KnpMenu/tree/v3.6.0" + "source": "https://github.com/KnpLabs/KnpMenu/tree/v3.7.0" }, - "time": "2024-12-20T10:10:51+00:00" + "time": "2025-02-28T08:21:46+00:00" }, { "name": "knplabs/knp-menu-bundle", @@ -3830,16 +3830,16 @@ }, { "name": "symfony/flex", - "version": "v2.4.7", + "version": "v2.5.0", "source": { "type": "git", "url": "https://github.com/symfony/flex.git", - "reference": "92f4fba342161ff36072bd3b8e0b3c6c23160402" + "reference": "8ce1acd9842abe0e9b4c4a0bd3f259859516c018" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/flex/zipball/92f4fba342161ff36072bd3b8e0b3c6c23160402", - "reference": "92f4fba342161ff36072bd3b8e0b3c6c23160402", + "url": "https://api.github.com/repos/symfony/flex/zipball/8ce1acd9842abe0e9b4c4a0bd3f259859516c018", + "reference": "8ce1acd9842abe0e9b4c4a0bd3f259859516c018", "shasum": "" }, "require": { @@ -3878,7 +3878,7 @@ "description": "Composer plugin for Symfony", "support": { "issues": "https://github.com/symfony/flex/issues", - "source": "https://github.com/symfony/flex/tree/v2.4.7" + "source": "https://github.com/symfony/flex/tree/v2.5.0" }, "funding": [ { @@ -3894,7 +3894,7 @@ "type": "tidelift" } ], - "time": "2024-10-07T08:51:54+00:00" + "time": "2025-03-03T07:50:46+00:00" }, { "name": "symfony/form", diff --git a/src/BusProNet/DataProcessor/BookingDataProcessor.php b/src/BusProNet/DataProcessor/BookingDataProcessor.php index 4652edf..def3354 100644 --- a/src/BusProNet/DataProcessor/BookingDataProcessor.php +++ b/src/BusProNet/DataProcessor/BookingDataProcessor.php @@ -12,7 +12,7 @@ class BookingDataProcessor $travelData = $formData->travel; // Reset mappings - foreach ([...$bookingData->additionalServices, ...$bookingData->transportationServices, ...$bookingData->pickups] as $service) { + foreach ([...$bookingData->additionalServices, ...$bookingData->transportationServices, ...$bookingData->pickupsTo, ...$bookingData->pickupsFro] as $service) { $service->mapping = []; } // Update mappings, add services and pickups @@ -40,11 +40,17 @@ class BookingDataProcessor } $bookingData->transportationServices[$service->id]->mapping[] = $participant->index; } - if ('BUS' === $participant->transportationServiceTo->subType && null !== $selectedPickup = $participant->pickup) { - if (false === isset($bookingData->pickups[$selectedPickup->id])) { - $bookingData->pickups[$selectedPickup->id] = $selectedPickup; + if ('BUS' === $participant->transportationServiceTo->subType && null !== $selectedPickup = $participant->pickupTo) { + if (false === isset($bookingData->pickupsTo[$selectedPickup->id])) { + $bookingData->pickupsTo[$selectedPickup->id] = $selectedPickup; } - $bookingData->pickups[$selectedPickup->id]->mapping[] = $participant->index; + $bookingData->pickupsTo[$selectedPickup->id]->mapping[] = $participant->index; + } + if ('BUS' === $participant->transportationServiceFro->subType && null !== $selectedPickup = $participant->pickupFro) { + if (false === isset($bookingData->pickupsFro[$selectedPickup->id])) { + $bookingData->pickupsFro[$selectedPickup->id] = $selectedPickup; + } + $bookingData->pickupsFro[$selectedPickup->id]->mapping[] = $participant->index; } } // Remove services/pickups with empty mappings @@ -58,9 +64,14 @@ class BookingDataProcessor unset($bookingData->transportationServices[$service->id]); } } - foreach ($bookingData->pickups as $pickup) { + foreach ($bookingData->pickupsTo as $pickup) { if (0 === count($pickup->mapping)) { - unset($bookingData->pickups[$pickup->id]); + unset($bookingData->pickupsTo[$pickup->id]); + } + } + foreach ($bookingData->pickupsFro as $pickup) { + if (0 === count($pickup->mapping)) { + unset($bookingData->pickupsFro[$pickup->id]); } } @@ -147,9 +158,9 @@ class BookingDataProcessor ]; } - if (0 < count($bookingData->pickups)) { + if (0 < count($bookingData->pickupsTo)) { $payload['zustiege']['zustieg'] = []; - foreach ($bookingData->pickups as $pickup) { + foreach ($bookingData->pickupsTo as $pickup) { $payload['zustiege']['zustieg'][] = [ '@idzustieg' => $pickup->id, '@anzahl' => count($pickup->mapping), @@ -158,6 +169,17 @@ class BookingDataProcessor } } + if (0 < count($bookingData->pickupsFro)) { + $payload['zustiege_rueck']['zustieg_rueck'] = []; + foreach ($bookingData->pickupsFro as $pickup) { + $payload['zustiege_rueck']['zustieg_rueck'][] = [ + '@idzustieg' => $pickup->id, + '@anzahl' => count($pickup->mapping), + '@zuordnung' => implode(',', $pickup->mapping), + ]; + } + } + return $payload; } } diff --git a/src/BusProNet/Model/Booking.php b/src/BusProNet/Model/Booking.php index 54d6829..9e0de2f 100644 --- a/src/BusProNet/Model/Booking.php +++ b/src/BusProNet/Model/Booking.php @@ -29,7 +29,8 @@ class Booking public array $transportationServices = []; public array $additionalServices = []; public array $rooms = []; - public array $pickups = []; + public array $pickupsTo = []; + public array $pickupsFro = []; public array $surcharges = []; public ?int $invoiceNumber = null; public ?float $totalPrice = null; @@ -75,9 +76,20 @@ class Booking return null; } - public function getPickupForParticipant(int $participantIndex): ?Pickup + public function getPickupToForParticipant(int $participantIndex): ?Pickup { - foreach ($this->pickups as $pickup) { + foreach ($this->pickupsTo as $pickup) { + if (in_array($participantIndex, $pickup->mapping)) { + return $pickup; + } + } + + return null; + } + + public function getPickupFroForParticipant(int $participantIndex): ?Pickup + { + foreach ($this->pickupsFro as $pickup) { if (in_array($participantIndex, $pickup->mapping)) { return $pickup; } @@ -132,4 +144,4 @@ class Booking { return $this->travelDate > new \DateTimeImmutable() && false === in_array($this->status, ['S', 'U']); } -} \ No newline at end of file +} diff --git a/src/BusProNet/Model/Travel.php b/src/BusProNet/Model/Travel.php index a9f632a..cdc80ba 100644 --- a/src/BusProNet/Model/Travel.php +++ b/src/BusProNet/Model/Travel.php @@ -53,7 +53,10 @@ class Travel public bool $transportationServicesMutable = true; #[Groups(['api:single'])] - public array $pickups = []; + public array $pickupsTo = []; + + #[Groups(['api:single'])] + public array $pickupsFro = []; #[Groups(['api:single'])] public bool $pickupsMutable = true; @@ -112,4 +115,4 @@ class Travel return $services; } -} \ No newline at end of file +} diff --git a/src/BusProNet/XmlLoader/PickupLoader.php b/src/BusProNet/XmlLoader/PickupLoader.php index 4224fbe..e022e35 100644 --- a/src/BusProNet/XmlLoader/PickupLoader.php +++ b/src/BusProNet/XmlLoader/PickupLoader.php @@ -54,7 +54,15 @@ class PickupLoader extends AbstractLoader public function patchPickupsDetails(Travel $travel): void { - foreach ($travel->pickups as $pickupId => $pickup) { + foreach ($travel->pickupsTo as $pickupId => $pickup) { + $pickupData = $this->loadById($pickupId); + + $pickup->code = $pickupData->code; + $pickup->postalCode = $pickupData->postalCode; + $pickup->city = $pickupData->city; + $pickup->street = $pickupData->street; + } + foreach ($travel->pickupsFro as $pickupId => $pickup) { $pickupData = $this->loadById($pickupId); $pickup->code = $pickupData->code; @@ -63,4 +71,4 @@ class PickupLoader extends AbstractLoader $pickup->street = $pickupData->street; } } -} \ No newline at end of file +} diff --git a/src/BusProNet/XmlLoader/TravelLoader.php b/src/BusProNet/XmlLoader/TravelLoader.php index 32f2517..756b0e0 100644 --- a/src/BusProNet/XmlLoader/TravelLoader.php +++ b/src/BusProNet/XmlLoader/TravelLoader.php @@ -140,7 +140,8 @@ class TravelLoader extends AbstractLoader $travel->transportationServices = $this ->getTransportationServices($node->filterXPath('//lei_befoerderung/leistung')); $travel->rooms = $this->getRooms($hotelNode); - $travel->pickups = $this->getPickups($node->filterXPath('//zustiege/zustieg')); + $travel->pickupsTo = $this->getPickups($node->filterXPath('//zustiege/zustieg')); + $travel->pickupsFro = $this->getPickups($node->filterXPath('//zustiege_rueck/zustieg_rueck')); return $travel; } @@ -347,4 +348,4 @@ class TravelLoader extends AbstractLoader $booking->travelData = $this->loadById($travelId); } } -} \ No newline at end of file +} diff --git a/src/BusProNet/XmlParser/BookingParser.php b/src/BusProNet/XmlParser/BookingParser.php index 67fffda..7272349 100644 --- a/src/BusProNet/XmlParser/BookingParser.php +++ b/src/BusProNet/XmlParser/BookingParser.php @@ -78,7 +78,11 @@ class BookingParser extends AbstractParser $pickupsData = $node->filterXPath('//zustiege/zustieg'); if (0 < $pickupsData->count()) { - $booking->pickups = $this->pickupsParser->parse($pickupsData); + $booking->pickupsTo = $this->pickupsParser->parse($pickupsData); + } + $pickupsData = $node->filterXPath('//zustiege_rueck/zustieg_rueck'); + if (0 < $pickupsData->count()) { + $booking->pickupsFro = $this->pickupsParser->parse($pickupsData); } $surchargesData = $node->filterXPath('//zuschlaege/zuschlag'); @@ -151,4 +155,4 @@ class BookingParser extends AbstractParser return $personalData; } -} \ No newline at end of file +} diff --git a/src/Form/BookingType.php b/src/Form/BookingType.php index f553bc1..8a35e1f 100644 --- a/src/Form/BookingType.php +++ b/src/Form/BookingType.php @@ -33,7 +33,8 @@ final class BookingType extends AbstractType ->getTransportationServicesByDirection('HIN'), 'selectable_transportation_services_fro' => $travelData ->getTransportationServicesByDirection('RUECK'), - 'selectable_pickups' => $travelData->pickups, + 'selectable_pickups_to' => $travelData->pickupsTo, + 'selectable_pickups_fro' => $travelData->pickupsFro, 'personal_data_mutable' => $travelData->participantDataMutable, 'additional_services_mutable' => $travelData->additionalServicesMutable, 'transportation_services_mutable' => $travelData->transportationServicesMutable, @@ -72,4 +73,4 @@ final class BookingType extends AbstractType 'anti_xss' => true, ]); } -} \ No newline at end of file +} diff --git a/src/Form/Model/BookingData.php b/src/Form/Model/BookingData.php index 23e2e7a..91bb809 100644 --- a/src/Form/Model/BookingData.php +++ b/src/Form/Model/BookingData.php @@ -42,11 +42,12 @@ class BookingData ->getTransportationServiceForParticipantAndDirection($index, 'H'); $participantData->transportationServiceFro = $booking ->getTransportationServiceForParticipantAndDirection($index, 'R'); - $participantData->pickup = $booking->getPickupForParticipant($index); + $participantData->pickupTo = $booking->getPickupToForParticipant($index); + $participantData->pickupFro = $booking->getPickupFroForParticipant($index); $instance->participants[$index] = $participantData; } return $instance; } -} \ No newline at end of file +} diff --git a/src/Form/Model/ParticipantData.php b/src/Form/Model/ParticipantData.php index 9ed5a8d..13a9413 100644 --- a/src/Form/Model/ParticipantData.php +++ b/src/Form/Model/ParticipantData.php @@ -43,7 +43,8 @@ class ParticipantData public array $rentals = []; public ?Service $transportationServiceTo = null; public ?Service $transportationServiceFro = null; - public ?Pickup $pickup = null; + public ?Pickup $pickupTo = null; + public ?Pickup $pickupFro = null; #[Assert\Callback] public function assertBodyMeasurementsValid(ExecutionContextInterface $context): void @@ -77,12 +78,18 @@ class ParticipantData #[Assert\Callback] public function assertPickupSelected(ExecutionContextInterface $context): void { - if (null !== $this->transportationServiceTo && null === $this->pickup) { + if (null !== $this->transportationServiceTo && null === $this->pickupTo) { $context->buildViolation('Bitte auswählen') ->atPath('pickupTo') ->addViolation() ; } + if (null !== $this->transportationServiceFro && null === $this->pickupFro) { + $context->buildViolation('Bitte auswählen') + ->atPath('pickupFro') + ->addViolation() + ; + } } public static function fromPersonalData(PersonalData $personalData): static diff --git a/src/Form/ParticipantType.php b/src/Form/ParticipantType.php index 84cee78..5303364 100644 --- a/src/Form/ParticipantType.php +++ b/src/Form/ParticipantType.php @@ -133,9 +133,6 @@ class ParticipantType extends AbstractType 'expanded' => true, 'choice_value' => 'id', 'choice_label' => function (?Service $service) use ($participantIndex) { - if (null === $service) { - return null; - } $price = $service->individualPrice[$participantIndex] ?? $service->price; if (null === $price || 0.0 === $price) { return $service->label; @@ -175,7 +172,10 @@ class ParticipantType extends AbstractType return $attributes; }, - 'choice_filter' => function (Service $service) use ($participantData) { + 'choice_filter' => function (?Service $service) use ($participantData) { + if (null === $service) { + return false; + } // remove choices only available in booking data and // which are not mapped to the current participant if ( @@ -248,35 +248,53 @@ class ParticipantType extends AbstractType 'required' => true, 'multiple' => false, 'choices' => $options['selectable_transportation_services_fro'], + 'choice_attr' => function (?Service $service) { + return [ + 'data-booking-target' => 'field', + 'data-action' => 'select-toggle#toggle booking#toggle', + 'data-select-toggle-value' => $service->subType, + ]; + }, ]) ; // Pickup - $form - ->add('pickup', ChoiceType::class, [ - 'label' => 'Zustieg', + $form->add('pickupTo', ChoiceType::class, [ + 'label' => 'Zustieg', + 'multiple' => false, + 'expanded' => false, + 'choices' => $options['selectable_pickups_to'], + '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, ',', '.') + ); + }, + ]); + + if (0 < count($options['selectable_pickups_fro'])) { + $form->add('pickupFro', ChoiceType::class, [ + 'label' => 'Ausstieg', 'multiple' => false, 'expanded' => false, - 'choices' => $options['selectable_pickups'], + 'choices' => $options['selectable_pickups_fro'], '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, ',', '.') - ); + return $pickup?->city; }, - ]) - ; + ]); + } }) ->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) use ($options) { $data = $event->getData(); @@ -324,7 +342,8 @@ class ParticipantType extends AbstractType 'selectable_rentals' => [], 'selectable_transportation_services_to' => [], 'selectable_transportation_services_fro' => [], - 'selectable_pickups' => [], + 'selectable_pickups_to' => [], + 'selectable_pickups_fro' => [], 'personal_data_mutable' => true, 'additional_services_mutable' => true, 'transportation_services_mutable' => true, diff --git a/templates/_partials/_modal.html.twig b/templates/_partials/_modal.html.twig index 4e57cf9..5055d7f 100644 --- a/templates/_partials/_modal.html.twig +++ b/templates/_partials/_modal.html.twig @@ -12,7 +12,7 @@ {{ icon('close', 'w-6 h-6')}} -
+
{% block content %}{% endblock %}
diff --git a/templates/booking/edit.html.twig b/templates/booking/edit.html.twig index f7cf360..150e79b 100644 --- a/templates/booking/edit.html.twig +++ b/templates/booking/edit.html.twig @@ -188,14 +188,23 @@

Hin-/Rückreise

-
- {{ form_row(child.transportationServiceTo) }} - {{ form_row(child.transportationServiceFro) }} - {% if child.pickup is defined %} -
- {{ form_row(child.pickup) }} -
- {% endif %} +
+
+ {{ form_row(child.transportationServiceTo) }} + {% if child.pickupTo is defined %} +
+ {{ form_row(child.pickupTo) }} +
+ {% endif %} +
+
+ {{ form_row(child.transportationServiceFro) }} + {% if child.pickupFro is defined %} +
+ {{ form_row(child.pickupFro) }} +
+ {% endif %} +
{% if not travelData.transportationServicesMutable %}
diff --git a/tests/BusProNet/DataLoader/TravelDataLoaderTest.php b/tests/BusProNet/DataLoader/TravelDataLoaderTest.php index 8ffc2f6..1d020e5 100644 --- a/tests/BusProNet/DataLoader/TravelDataLoaderTest.php +++ b/tests/BusProNet/DataLoader/TravelDataLoaderTest.php @@ -30,7 +30,7 @@ class TravelDataLoaderTest extends TestCase $this->assertCount(8, $travel->selectionGroups); $this->assertCount(27, $travel->additionalServices); $this->assertCount(5, $travel->transportationServices); - $this->assertCount(7, $travel->pickups); + $this->assertCount(7, $travel->pickupsTo); $this->assertCount(12, $travel->rooms); } -} \ No newline at end of file +}