feat: merge draft data with api response for selected fields
This commit is contained in:
@@ -240,7 +240,7 @@ class BookingEditDraftService
|
||||
if (true === array_key_exists('gender', $data)) {
|
||||
$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'];
|
||||
}
|
||||
}
|
||||
@@ -308,66 +308,101 @@ class BookingEditDraftService
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
// Ski pass (single service)
|
||||
if (true === array_key_exists('skiPass', $data)) {
|
||||
$participant->skiPass = $this->resolveService($data['skiPass'], $travel->additionalServices);
|
||||
// Ski pass (single service) - merge strategy: only apply if resolves to valid service
|
||||
if (true === array_key_exists('skiPass', $data) && null !== $data['skiPass']) {
|
||||
$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'])) {
|
||||
$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'])) {
|
||||
$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'])) {
|
||||
$participant->rentals = $this->resolveServiceArray($data['rentals'], $travel->additionalServices);
|
||||
}
|
||||
|
||||
// Rental insurance (single)
|
||||
if (true === array_key_exists('rentalInsurance', $data)) {
|
||||
$participant->rentalInsurance = $this->resolveService($data['rentalInsurance'], $travel->additionalServices);
|
||||
$participant->rentalInsuranceSelected = null !== $participant->rentalInsurance;
|
||||
// Rental insurance (single) - merge strategy: only apply if resolves to valid service
|
||||
if (true === array_key_exists('rentalInsurance', $data) && null !== $data['rentalInsurance']) {
|
||||
$resolved = $this->resolveService($data['rentalInsurance'], $travel->additionalServices);
|
||||
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'])) {
|
||||
$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)
|
||||
if (true === array_key_exists('transportationOutbound', $data)) {
|
||||
$participant->transportationOutbound = $this->resolveService($data['transportationOutbound'], $travel->transportationServices);
|
||||
// Transportation outbound (single) - merge strategy: only apply if resolves to valid service
|
||||
if (true === array_key_exists('transportationOutbound', $data) && null !== $data['transportationOutbound']) {
|
||||
$resolved = $this->resolveService($data['transportationOutbound'], $travel->transportationServices);
|
||||
if (null !== $resolved) {
|
||||
$participant->transportationOutbound = $resolved;
|
||||
}
|
||||
}
|
||||
|
||||
// Transportation inbound (single)
|
||||
if (true === array_key_exists('transportationInbound', $data)) {
|
||||
$participant->transportationInbound = $this->resolveService($data['transportationInbound'], $travel->transportationServices);
|
||||
// Transportation inbound (single) - merge strategy: only apply if resolves to valid service
|
||||
if (true === array_key_exists('transportationInbound', $data) && null !== $data['transportationInbound']) {
|
||||
$resolved = $this->resolveService($data['transportationInbound'], $travel->transportationServices);
|
||||
if (null !== $resolved) {
|
||||
$participant->transportationInbound = $resolved;
|
||||
}
|
||||
}
|
||||
|
||||
// Pickup (single)
|
||||
if (true === array_key_exists('pickup', $data)) {
|
||||
$participant->pickup = $this->resolvePickup($data['pickup'], $travel);
|
||||
// Pickup (single) - merge strategy: only apply if resolves to valid pickup
|
||||
if (true === array_key_exists('pickup', $data) && null !== $data['pickup']) {
|
||||
$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)) {
|
||||
$participant->parking = (bool) $data['parking'];
|
||||
}
|
||||
|
||||
// Insurance (single)
|
||||
if (true === array_key_exists('insurance', $data)) {
|
||||
$participant->insurance = $this->resolveInsurance($data['insurance'], $travel);
|
||||
// Insurance (single) - merge strategy: only apply if resolves to valid insurance
|
||||
if (true === array_key_exists('insurance', $data) && null !== $data['insurance']) {
|
||||
$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)) {
|
||||
$participant->bulkInsuranceBooking = (bool) $data['bulkInsuranceBooking'];
|
||||
}
|
||||
@@ -447,4 +482,53 @@ class BookingEditDraftService
|
||||
|
||||
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