feat: promotional and purchase vouchers
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Form\Service;
|
||||
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Service\Abstract\AbstractParticipantFieldHandler;
|
||||
use App\Service\BookingPriceCalculatorService;
|
||||
use App\Service\VoucherValidationService;
|
||||
|
||||
/**
|
||||
* Handles processing and validation of promotional voucher codes for booking participants.
|
||||
*
|
||||
* This handler validates promo vouchers via the BPN API and provides immediate feedback
|
||||
* about voucher validity, discount amounts, and applicability. Promo vouchers can be
|
||||
* applied per person or per booking, using absolute discount amounts (never percentages).
|
||||
*
|
||||
* Validation includes:
|
||||
* - Code validity check
|
||||
* - Travel applicability verification
|
||||
* - Minimum price threshold validation
|
||||
* - Real-time API validation with 15-minute caching
|
||||
*
|
||||
* Dependencies: ALL price-affecting fields (insurance, services, ski pass, rentals, etc.)
|
||||
* since promo validation requires accurate participant pricing.
|
||||
*/
|
||||
class ParticipantPromoVoucherFieldHandler extends AbstractParticipantFieldHandler
|
||||
{
|
||||
public function __construct(
|
||||
private readonly VoucherValidationService $voucherValidationService,
|
||||
private readonly BookingPriceCalculatorService $priceCalculatorService,
|
||||
) {
|
||||
}
|
||||
|
||||
public function getFieldName(): string
|
||||
{
|
||||
return 'promoVoucherCode';
|
||||
}
|
||||
|
||||
public function getDependencies(): array
|
||||
{
|
||||
// CRITICAL: Must run after ALL price-affecting handlers to ensure accurate pricing
|
||||
return [
|
||||
'dateOfBirth', // Required for age-based services
|
||||
'skiPass', // Affects travel price
|
||||
'rentals', // Affects travel price
|
||||
'courses', // Affects travel price
|
||||
'additionalServices', // Affects travel price
|
||||
'board', // Affects travel price
|
||||
'transportationOutbound', // Affects travel price
|
||||
'transportationInbound', // Affects travel price
|
||||
'pickup', // Affects travel price
|
||||
'parking', // Affects travel price
|
||||
'insurance', // Affects travel price (must be last)
|
||||
];
|
||||
}
|
||||
|
||||
public function shouldProcess(array $submittedData, string $mode, int $participantIndex): bool
|
||||
{
|
||||
$promoCode = $this->getFieldValue($submittedData, $this->getFieldName());
|
||||
|
||||
return null !== $promoCode && '' !== trim($promoCode);
|
||||
}
|
||||
|
||||
public function processField(array $submittedData, BookingDto $bookingDto, int $participantIndex): void
|
||||
{
|
||||
$participant = $this->getParticipant($bookingDto, $participantIndex);
|
||||
|
||||
if (null === $participant) {
|
||||
return;
|
||||
}
|
||||
|
||||
$promoCode = $this->getFieldValue($submittedData, $this->getFieldName());
|
||||
|
||||
if (null === $promoCode || '' === trim($promoCode)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate participant's current price including all services
|
||||
$participantPrice = $this->priceCalculatorService->calculateIndividualParticipantPrice(
|
||||
$bookingDto,
|
||||
$participantIndex
|
||||
);
|
||||
|
||||
// Get travel ID for validation
|
||||
$travelId = $bookingDto->travel->id;
|
||||
|
||||
if (null === $travelId) {
|
||||
$participant->addNotification(
|
||||
'error',
|
||||
'Aktionscode konnte nicht validiert werden: Reise-ID fehlt.'
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate promo voucher via API
|
||||
$result = $this->voucherValidationService->validatePromoVoucher(
|
||||
$promoCode,
|
||||
$travelId,
|
||||
$participantPrice
|
||||
);
|
||||
|
||||
if (true === $result->isValid && null !== $result->voucher) {
|
||||
$applicability = $result->voucher->isPerPerson() ? 'je Person' : 'pro Buchung';
|
||||
$participant->addNotification(
|
||||
'success',
|
||||
sprintf(
|
||||
'Aktionscode gültig: %s (Rabatt: %.2f € %s)',
|
||||
$result->voucher->description,
|
||||
$result->voucher->discountAmount,
|
||||
$applicability
|
||||
)
|
||||
);
|
||||
} else {
|
||||
$participant->addNotification(
|
||||
'error',
|
||||
sprintf('Aktionscode ungültig: %s', $result->errorMessage ?? 'Unbekannter Fehler')
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user