feat: automatic unselection of discounted transportation
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Form\Service;
|
||||
|
||||
use App\BusProNet\Utility\DirectionMapper;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Service\Abstract\AbstractParticipantFieldHandler;
|
||||
use App\Service\ServiceAvailabilityCalculator;
|
||||
|
||||
class ParticipantTransportationDiscountReplacementFieldHandler extends AbstractParticipantFieldHandler
|
||||
{
|
||||
public function __construct(
|
||||
private readonly ServiceAvailabilityCalculator $serviceAvailabilityCalculator,
|
||||
) {
|
||||
}
|
||||
|
||||
public function getFieldName(): string
|
||||
{
|
||||
return 'transportationDiscountReplacement'; // Virtual field name
|
||||
}
|
||||
|
||||
public function getDependencies(): array
|
||||
{
|
||||
return ['transportationOutbound', 'transportationInbound'];
|
||||
}
|
||||
|
||||
public function shouldProcess(array $submittedData, string $mode, int $participantIndex): bool
|
||||
{
|
||||
return true; // Always process in all modes
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes transportation discount replacement logic.
|
||||
*
|
||||
* Detects incompatible transportation combinations and automatically replaces
|
||||
* discounted PKW with regular variant when inbound bus is selected. Also restores
|
||||
* discounted PKW when inbound changes back to PKW and discount is still available.
|
||||
*
|
||||
* Business Rules:
|
||||
* - Inbound BUS + Outbound discounted PKW → Replace with regular PKW (notification: warning)
|
||||
* - Inbound PKW + Outbound regular PKW → Restore discounted PKW if available (notification: success)
|
||||
* - Only processes PKW variants, never touches BUS or other services
|
||||
*/
|
||||
public function processField(array $submittedData, BookingDto $bookingDto, int $participantIndex): void
|
||||
{
|
||||
// Step 1: Get participant and validate prerequisites
|
||||
$participant = $bookingDto->getParticipant($participantIndex);
|
||||
if (null === $participant) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Need both transportation fields to process
|
||||
if (null === $participant->transportationOutbound) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 2: Detect inbound transportation type
|
||||
$inboundIsBus = null !== $participant->transportationInbound
|
||||
&& DirectionMapper::SUBTYPE_BUS_API === $participant->transportationInbound->subType;
|
||||
|
||||
// Step 3: Get all outbound PKW services
|
||||
// Get ALL outbound services (not filtered by availability or smart filtering)
|
||||
$allOutboundServices = $bookingDto->travel->getTransportationServicesByDirection(
|
||||
DirectionMapper::OUTBOUND_TRAVEL,
|
||||
false // Do NOT filter by per-booking availability
|
||||
);
|
||||
|
||||
// Find discounted and regular PKW services
|
||||
$discountedPkw = null;
|
||||
$regularPkw = null;
|
||||
|
||||
foreach ($allOutboundServices as $service) {
|
||||
if (false === DirectionMapper::isCar($service->subType)) {
|
||||
continue; // Skip non-PKW services
|
||||
}
|
||||
|
||||
if (null !== $service->price && $service->price < 0) {
|
||||
$discountedPkw = $service;
|
||||
} elseif (null === $service->price || $service->price >= 0) {
|
||||
$regularPkw = $service;
|
||||
}
|
||||
}
|
||||
|
||||
// Edge case: no regular PKW found (impossible in production, but handle gracefully)
|
||||
if (null === $regularPkw) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 4a: Handle inbound BUS scenario (force regular PKW)
|
||||
if (true === $inboundIsBus) {
|
||||
// Check if outbound is currently DISCOUNTED PKW
|
||||
$outboundIsDiscountedPkw = DirectionMapper::isCar($participant->transportationOutbound->subType)
|
||||
&& null !== $participant->transportationOutbound->price
|
||||
&& $participant->transportationOutbound->price < 0;
|
||||
|
||||
if ($outboundIsDiscountedPkw) {
|
||||
// Store original for notification
|
||||
$originalLabel = $participant->transportationOutbound->label;
|
||||
|
||||
// Replace with regular PKW
|
||||
$participant->transportationOutbound = $regularPkw;
|
||||
|
||||
// Notify user about replacement
|
||||
$participant->addNotification(
|
||||
'warning',
|
||||
sprintf(
|
||||
'Hinfahrt automatisch angepasst: Rabattierte Anreise nicht möglich bei Busrückfahrt. Geändert von "%s" zu "%s".',
|
||||
$originalLabel,
|
||||
$regularPkw->label
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return; // Done processing for BUS inbound scenario
|
||||
}
|
||||
|
||||
// Step 4b: Handle non-BUS inbound scenario (restore discount if available)
|
||||
// Inbound is NOT BUS - try to restore discounted PKW if available
|
||||
|
||||
// Check if outbound is currently REGULAR PKW
|
||||
$outboundIsRegularPkw = DirectionMapper::isCar($participant->transportationOutbound->subType)
|
||||
&& (null === $participant->transportationOutbound->price
|
||||
|| $participant->transportationOutbound->price >= 0);
|
||||
|
||||
if (false === $outboundIsRegularPkw || null === $discountedPkw) {
|
||||
return; // Not eligible for discount restoration
|
||||
}
|
||||
|
||||
// Check per-booking availability of discounted PKW
|
||||
$discountedIsAvailable = false === $this->serviceAvailabilityCalculator->isServiceUnavailable(
|
||||
$discountedPkw->id,
|
||||
$bookingDto,
|
||||
$participantIndex
|
||||
);
|
||||
|
||||
if ($discountedIsAvailable) {
|
||||
// Store original for notification
|
||||
$originalLabel = $participant->transportationOutbound->label;
|
||||
|
||||
// Restore discounted PKW
|
||||
$participant->transportationOutbound = $discountedPkw;
|
||||
|
||||
// Notify user about restoration
|
||||
$participant->addNotification(
|
||||
'success',
|
||||
sprintf(
|
||||
'Hinfahrt automatisch angepasst: Rabattierte Anreise wieder verfügbar. Geändert von "%s" zu "%s".',
|
||||
$originalLabel,
|
||||
$discountedPkw->label
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user