bookingDataProcessor->createBookingDtoFromBooking($freshBooking, $workingDto->travel, $workingDto->isInternalAgencyBooking()); // Booking-level D-4 cutoff used as submit-time safety net for rentals. $serviceCutoffReachedCondition = new TravelStartCutoffReachedCondition( TravelStartCutoffReachedCondition::DEFAULT_DAYS_BEFORE_START ); // TravelStartCutoffReachedCondition is booking-level (travel date only). // The interface requires a participant index, but this condition does not use it, // so we pass 0 as a neutral placeholder. $rentalsLockedByCutoff = $serviceCutoffReachedCondition->evaluate($workingDto, 0, []); $changed = false; foreach ($workingDto->participants as $index => $participant) { $baseline = $baselineDto->participants[$index] ?? null; if (null === $baseline) { continue; } if (false === $workingDto->travel->additionalServicesMutable) { $changed = $this->reconcileAdditionalServices($participant, $baseline) || $changed; } // Reconcile rentals either when BPN additional services are immutable // or when our D-4 rule has been reached. if (false === $workingDto->travel->additionalServicesMutable || $rentalsLockedByCutoff) { $changed = $this->reconcileRentals($participant, $baseline) || $changed; } if (false === $workingDto->travel->transportationServicesMutable) { $changed = $this->reconcileTransportationServices($participant, $baseline) || $changed; } if (false === $workingDto->travel->pickupsMutable) { $changed = $this->reconcilePickups($participant, $baseline) || $changed; } } return $changed; } private function reconcileAdditionalServices(ParticipantDto $participant, ParticipantDto $baseline): bool { $changed = false; if (false === $this->areServiceListsEqual($participant->courses, $baseline->courses)) { $participant->courses = $baseline->courses; $changed = true; } if (false === $this->areServiceListsEqual($participant->additionalServices, $baseline->additionalServices)) { $participant->additionalServices = $baseline->additionalServices; $changed = true; } if (false === $this->areServiceListsEqual($participant->board, $baseline->board)) { $participant->board = $baseline->board; $changed = true; } if (false === $this->isSameService($participant->veg, $baseline->veg)) { $participant->veg = $baseline->veg; $changed = true; } if (false === $this->isSameService($participant->skiPass, $baseline->skiPass)) { $participant->skiPass = $baseline->skiPass; $changed = true; } if (false === $this->isSameService($participant->rentalInsurance, $baseline->rentalInsurance)) { $participant->rentalInsurance = $baseline->rentalInsurance; $changed = true; } if ($participant->rentalInsuranceSelected !== $baseline->rentalInsuranceSelected) { $participant->rentalInsuranceSelected = $baseline->rentalInsuranceSelected; $changed = true; } return $changed; } private function reconcileRentals(ParticipantDto $participant, ParticipantDto $baseline): bool { if (false === $this->areServiceListsEqual($participant->rentals, $baseline->rentals)) { $participant->rentals = $baseline->rentals; return true; } return false; } private function reconcileTransportationServices(ParticipantDto $participant, ParticipantDto $baseline): bool { $changed = false; if (false === $this->isSameService($participant->transportationOutbound, $baseline->transportationOutbound)) { $participant->transportationOutbound = $baseline->transportationOutbound; $changed = true; } if (false === $this->isSameService($participant->transportationInbound, $baseline->transportationInbound)) { $participant->transportationInbound = $baseline->transportationInbound; $changed = true; } if ($participant->parking !== $baseline->parking) { $participant->parking = $baseline->parking; $changed = true; } if (false === $this->isSameService($participant->parkingService, $baseline->parkingService)) { $participant->parkingService = $baseline->parkingService; $changed = true; } return $changed; } private function reconcilePickups(ParticipantDto $participant, ParticipantDto $baseline): bool { $changed = false; if (false === $this->isSamePickup($participant->pickup, $baseline->pickup)) { $participant->pickup = $baseline->pickup; $changed = true; } if (false === $this->isSamePickup($participant->dropOff, $baseline->dropOff)) { $participant->dropOff = $baseline->dropOff; $changed = true; } if ($participant->differentDropOff !== $baseline->differentDropOff) { $participant->differentDropOff = $baseline->differentDropOff; $changed = true; } return $changed; } /** * @param list $left * @param list $right */ private function areServiceListsEqual(array $left, array $right): bool { return $this->normalizeServiceIds($left) === $this->normalizeServiceIds($right); } /** * @param Service[] $services * * @return int[] */ private function normalizeServiceIds(array $services): array { $ids = []; foreach ($services as $service) { $ids[] = $service->id; } $ids = array_values(array_unique($ids)); sort($ids); return $ids; } private function isSameService(?Service $left, ?Service $right): bool { return $left?->id === $right?->id; } private function isSamePickup(?Pickup $left, ?Pickup $right): bool { return $left?->id === $right?->id; } }