feat: merge draft data with api response for selected fields
This commit is contained in:
@@ -1080,9 +1080,19 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
|
|||||||
if (null !== $discountedPkw && null !== $regularPkw) {
|
if (null !== $discountedPkw && null !== $regularPkw) {
|
||||||
$participant = $bookingDto->getParticipant($participantIndex);
|
$participant = $bookingDto->getParticipant($participantIndex);
|
||||||
|
|
||||||
|
// In edit mode, always preserve the participant's current selection in the choices
|
||||||
|
// This ensures the form can render the selected value even if filtering would hide it
|
||||||
|
$currentOutboundId = $participant?->transportationOutbound?->id;
|
||||||
|
$preserveDiscounted = $currentOutboundId === $discountedPkw->id;
|
||||||
|
$preserveRegular = $currentOutboundId === $regularPkw->id;
|
||||||
|
|
||||||
// Baby participants always get regular PKW (no discounts)
|
// Baby participants always get regular PKW (no discounts)
|
||||||
$age = $participant?->getAge($bookingDto->travel->dateFrom);
|
$age = $participant?->getAge($bookingDto->travel->dateFrom);
|
||||||
if (null !== $age && $age <= Constants::BABY_MAX_AGE) {
|
if (null !== $age && $age <= Constants::BABY_MAX_AGE) {
|
||||||
|
if ($preserveDiscounted) {
|
||||||
|
return [...$otherServices, $discountedPkw, $regularPkw];
|
||||||
|
}
|
||||||
|
|
||||||
return [...$otherServices, $regularPkw];
|
return [...$otherServices, $regularPkw];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1092,6 +1102,10 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
|
|||||||
|
|
||||||
// If inbound is BUS, always show regular PKW (not discounted)
|
// If inbound is BUS, always show regular PKW (not discounted)
|
||||||
if ($inboundIsBus) {
|
if ($inboundIsBus) {
|
||||||
|
if ($preserveDiscounted) {
|
||||||
|
return [...$otherServices, $discountedPkw, $regularPkw];
|
||||||
|
}
|
||||||
|
|
||||||
return [...$otherServices, $regularPkw];
|
return [...$otherServices, $regularPkw];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1104,9 +1118,19 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
|
|||||||
|
|
||||||
if ($discountedIsUnavailable) {
|
if ($discountedIsUnavailable) {
|
||||||
// Discounted is sold out within this booking, show only regular
|
// Discounted is sold out within this booking, show only regular
|
||||||
|
// But preserve discounted if participant already has it selected
|
||||||
|
if ($preserveDiscounted) {
|
||||||
|
return [...$otherServices, $discountedPkw, $regularPkw];
|
||||||
|
}
|
||||||
|
|
||||||
return [...$otherServices, $regularPkw];
|
return [...$otherServices, $regularPkw];
|
||||||
} else {
|
} else {
|
||||||
// Discounted has availability, show only discounted (hide regular)
|
// Discounted has availability, show only discounted (hide regular)
|
||||||
|
// But preserve regular if participant already has it selected
|
||||||
|
if ($preserveRegular) {
|
||||||
|
return [...$otherServices, $discountedPkw, $regularPkw];
|
||||||
|
}
|
||||||
|
|
||||||
return [...$otherServices, $discountedPkw];
|
return [...$otherServices, $discountedPkw];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -240,7 +240,7 @@ class BookingEditDraftService
|
|||||||
if (true === array_key_exists('gender', $data)) {
|
if (true === array_key_exists('gender', $data)) {
|
||||||
$participant->gender = $data['gender'];
|
$participant->gender = $data['gender'];
|
||||||
}
|
}
|
||||||
if (true === array_key_exists('nationality', $data)) {
|
if (true === array_key_exists('nationality', $data) && '' !== $data['nationality'] && null !== $data['nationality']) {
|
||||||
$participant->nationality = $data['nationality'];
|
$participant->nationality = $data['nationality'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -308,66 +308,101 @@ class BookingEditDraftService
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Applies service selections to participant, resolving IDs against Travel data.
|
* Applies service selections to participant, resolving IDs against Travel data.
|
||||||
|
*
|
||||||
|
* Single-select fields (radio buttons) use a merge strategy: draft values are only applied
|
||||||
|
* if they resolve to a valid service. This preserves API data when:
|
||||||
|
* - The draft was saved before certain services were assigned
|
||||||
|
* - The draft contains service IDs that no longer exist in current travel data
|
||||||
|
*
|
||||||
|
* Multi-select fields (checkboxes) and booleans use overwrite strategy: draft values
|
||||||
|
* always replace API data, since users can intentionally clear these selections.
|
||||||
*/
|
*/
|
||||||
private function applyServiceSelections(ParticipantDto $participant, array $data, Travel $travel): void
|
private function applyServiceSelections(ParticipantDto $participant, array $data, Travel $travel): void
|
||||||
{
|
{
|
||||||
// Ski pass (single service)
|
// Ski pass (single service) - merge strategy: only apply if resolves to valid service
|
||||||
if (true === array_key_exists('skiPass', $data)) {
|
if (true === array_key_exists('skiPass', $data) && null !== $data['skiPass']) {
|
||||||
$participant->skiPass = $this->resolveService($data['skiPass'], $travel->additionalServices);
|
$draftSkiPassId = $data['skiPass'];
|
||||||
|
$resolved = $this->resolveService($draftSkiPassId, $travel->additionalServices);
|
||||||
|
|
||||||
|
if (null !== $resolved) {
|
||||||
|
$participant->skiPass = $resolved;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Courses (array)
|
// Courses (array) - overwrite strategy: user can deselect all
|
||||||
if (true === array_key_exists('courses', $data) && true === is_array($data['courses'])) {
|
if (true === array_key_exists('courses', $data) && true === is_array($data['courses'])) {
|
||||||
$participant->courses = $this->resolveServiceArray($data['courses'], $travel->additionalServices);
|
$participant->courses = $this->resolveServiceArray($data['courses'], $travel->additionalServices);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Board (array)
|
// Board (array) - overwrite strategy: user can deselect all
|
||||||
if (true === array_key_exists('board', $data) && true === is_array($data['board'])) {
|
if (true === array_key_exists('board', $data) && true === is_array($data['board'])) {
|
||||||
$participant->board = $this->resolveServiceArray($data['board'], $travel->additionalServices);
|
$participant->board = $this->resolveServiceArray($data['board'], $travel->additionalServices);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rentals (array)
|
// Rentals (array) - overwrite strategy: user can deselect all
|
||||||
if (true === array_key_exists('rentals', $data) && true === is_array($data['rentals'])) {
|
if (true === array_key_exists('rentals', $data) && true === is_array($data['rentals'])) {
|
||||||
$participant->rentals = $this->resolveServiceArray($data['rentals'], $travel->additionalServices);
|
$participant->rentals = $this->resolveServiceArray($data['rentals'], $travel->additionalServices);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rental insurance (single)
|
// Rental insurance (single) - merge strategy: only apply if resolves to valid service
|
||||||
if (true === array_key_exists('rentalInsurance', $data)) {
|
if (true === array_key_exists('rentalInsurance', $data) && null !== $data['rentalInsurance']) {
|
||||||
$participant->rentalInsurance = $this->resolveService($data['rentalInsurance'], $travel->additionalServices);
|
$resolved = $this->resolveService($data['rentalInsurance'], $travel->additionalServices);
|
||||||
$participant->rentalInsuranceSelected = null !== $participant->rentalInsurance;
|
if (null !== $resolved) {
|
||||||
|
$participant->rentalInsurance = $resolved;
|
||||||
|
$participant->rentalInsuranceSelected = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Additional services (array)
|
// Additional services (array) - overwrite strategy with mandatory service preservation
|
||||||
|
// Additional services (array) - overwrite strategy with mandatory service preservation
|
||||||
|
// User can deselect optional services, but mandatory services from API must be preserved
|
||||||
if (true === array_key_exists('additionalServices', $data) && true === is_array($data['additionalServices'])) {
|
if (true === array_key_exists('additionalServices', $data) && true === is_array($data['additionalServices'])) {
|
||||||
$participant->additionalServices = $this->resolveServiceArray($data['additionalServices'], $travel->additionalServices);
|
$resolvedFromDraft = $this->resolveServiceArray($data['additionalServices'], $travel->additionalServices);
|
||||||
|
$participant->additionalServices = $this->preserveMandatoryServices(
|
||||||
|
$resolvedFromDraft,
|
||||||
|
$participant->additionalServices,
|
||||||
|
$travel
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transportation outbound (single)
|
// Transportation outbound (single) - merge strategy: only apply if resolves to valid service
|
||||||
if (true === array_key_exists('transportationOutbound', $data)) {
|
if (true === array_key_exists('transportationOutbound', $data) && null !== $data['transportationOutbound']) {
|
||||||
$participant->transportationOutbound = $this->resolveService($data['transportationOutbound'], $travel->transportationServices);
|
$resolved = $this->resolveService($data['transportationOutbound'], $travel->transportationServices);
|
||||||
|
if (null !== $resolved) {
|
||||||
|
$participant->transportationOutbound = $resolved;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transportation inbound (single)
|
// Transportation inbound (single) - merge strategy: only apply if resolves to valid service
|
||||||
if (true === array_key_exists('transportationInbound', $data)) {
|
if (true === array_key_exists('transportationInbound', $data) && null !== $data['transportationInbound']) {
|
||||||
$participant->transportationInbound = $this->resolveService($data['transportationInbound'], $travel->transportationServices);
|
$resolved = $this->resolveService($data['transportationInbound'], $travel->transportationServices);
|
||||||
|
if (null !== $resolved) {
|
||||||
|
$participant->transportationInbound = $resolved;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pickup (single)
|
// Pickup (single) - merge strategy: only apply if resolves to valid pickup
|
||||||
if (true === array_key_exists('pickup', $data)) {
|
if (true === array_key_exists('pickup', $data) && null !== $data['pickup']) {
|
||||||
$participant->pickup = $this->resolvePickup($data['pickup'], $travel);
|
$resolved = $this->resolvePickup($data['pickup'], $travel);
|
||||||
|
if (null !== $resolved) {
|
||||||
|
$participant->pickup = $resolved;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parking (boolean)
|
// Parking (boolean) - overwrite strategy: user can uncheck
|
||||||
if (true === array_key_exists('parking', $data)) {
|
if (true === array_key_exists('parking', $data)) {
|
||||||
$participant->parking = (bool) $data['parking'];
|
$participant->parking = (bool) $data['parking'];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insurance (single)
|
// Insurance (single) - merge strategy: only apply if resolves to valid insurance
|
||||||
if (true === array_key_exists('insurance', $data)) {
|
if (true === array_key_exists('insurance', $data) && null !== $data['insurance']) {
|
||||||
$participant->insurance = $this->resolveInsurance($data['insurance'], $travel);
|
$resolved = $this->resolveInsurance($data['insurance'], $travel);
|
||||||
|
if (null !== $resolved) {
|
||||||
|
$participant->insurance = $resolved;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bulk insurance booking (boolean)
|
// Bulk insurance booking (boolean) - overwrite strategy: user can uncheck
|
||||||
if (true === array_key_exists('bulkInsuranceBooking', $data)) {
|
if (true === array_key_exists('bulkInsuranceBooking', $data)) {
|
||||||
$participant->bulkInsuranceBooking = (bool) $data['bulkInsuranceBooking'];
|
$participant->bulkInsuranceBooking = (bool) $data['bulkInsuranceBooking'];
|
||||||
}
|
}
|
||||||
@@ -447,4 +482,53 @@ class BookingEditDraftService
|
|||||||
|
|
||||||
return $travel->insurances[$insuranceId] ?? null;
|
return $travel->insurances[$insuranceId] ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Preserves mandatory services from API data when applying draft.
|
||||||
|
*
|
||||||
|
* Mandatory services (like Ortstaxe) cannot be deselected by users and must
|
||||||
|
* always be present in the booking. When a draft was created before the agency
|
||||||
|
* assigned mandatory services, this method ensures those services are preserved
|
||||||
|
* from the fresh API data rather than being overwritten with stale draft data.
|
||||||
|
*
|
||||||
|
* The mandatory flag must be looked up from travel data since booking data
|
||||||
|
* doesn't include the pflicht attribute.
|
||||||
|
*
|
||||||
|
* @param array $draftServices Services resolved from draft data
|
||||||
|
* @param array $originalServices Services from fresh API data (booking assignments)
|
||||||
|
* @param Travel $travel Travel data containing mandatory flag on services
|
||||||
|
*
|
||||||
|
* @return array Merged array with draft services plus any missing mandatory services
|
||||||
|
*/
|
||||||
|
private function preserveMandatoryServices(array $draftServices, array $originalServices, Travel $travel): array
|
||||||
|
{
|
||||||
|
// Build lookup of service IDs already in the draft
|
||||||
|
$draftServiceIds = [];
|
||||||
|
foreach ($draftServices as $service) {
|
||||||
|
if (null !== $service->id) {
|
||||||
|
$draftServiceIds[$service->id] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add mandatory services from original API data that aren't in draft
|
||||||
|
// Check mandatory status from travel data (pflicht attribute)
|
||||||
|
foreach ($originalServices as $service) {
|
||||||
|
if (false === isset($draftServiceIds[$service->id])) {
|
||||||
|
// Look up mandatory status from travel data
|
||||||
|
$travelService = $travel->additionalServices[$service->id] ?? null;
|
||||||
|
$isMandatory = null !== $travelService && true === $travelService->mandatory;
|
||||||
|
|
||||||
|
if ($isMandatory) {
|
||||||
|
$draftServices[] = $service;
|
||||||
|
|
||||||
|
$this->logger->debug('Preserved mandatory service from API during draft application', [
|
||||||
|
'service_id' => $service->id,
|
||||||
|
'service_label' => $service->label,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $draftServices;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user