getParticipant($bookingDto, $participantIndex); if (!$participant instanceof ParticipantDto) { return; } $this->removeRequestedCode($submittedData, $participant); $this->recheckAppliedPromoCode($bookingDto, $participant, $participantIndex); $this->applyTypedCode($submittedData, $bookingDto, $participant, $participantIndex); } /** * @param array $submittedData */ private function removeRequestedCode(array $submittedData, ParticipantDto $participant): void { $participant->removeVoucherCode = null; $code = $this->getFieldValue($submittedData, 'removeVoucherCode'); if (false === is_string($code) || '' === trim($code)) { return; } $code = $this->voucherCodeClassifier->normalize($code); if ($code === $participant->promoVoucherCode) { $participant->promoVoucherCode = null; $participant->validatedPromoVoucher = null; $participant->promoVoucherError = null; } if ($code === $participant->purchaseVoucherCode) { $participant->purchaseVoucherCode = null; $participant->validatedPurchaseVoucher = null; } } private function recheckAppliedPromoCode(BookingDto $bookingDto, ParticipantDto $participant, int $participantIndex): void { if (null === $participant->promoVoucherCode) { return; } $error = $this->validatePromoCode($participant->promoVoucherCode, $bookingDto, $participant, $participantIndex); $participant->promoVoucherError = $error; } /** * @param array $submittedData */ private function applyTypedCode(array $submittedData, BookingDto $bookingDto, ParticipantDto $participant, int $participantIndex): void { $participant->voucherCodeError = null; // Only a submitted field carries a typed code; when the field is hidden nothing may linger $typedCode = $this->getFieldValue($submittedData, $this->getFieldName()); if (false === is_string($typedCode) || '' === trim($typedCode)) { $participant->voucherCode = null; return; } $participant->voucherCode = trim($typedCode); $code = $this->voucherCodeClassifier->normalize($typedCode); $error = match (true) { $code === $participant->promoVoucherCode, $code === $participant->purchaseVoucherCode => 'Dieser Code ist bereits eingelöst.', default => match ($this->voucherCodeClassifier->classify($code)) { VoucherCodeKind::Malformed => 'Dieser Code ist ungültig. Bitte prüfe deine Eingabe.', VoucherCodeKind::Promo => $this->applyPromoCode($code, $bookingDto, $participant, $participantIndex), VoucherCodeKind::Purchase => $this->applyPurchaseCode($code, $participant), }, }; if (null === $error) { $participant->voucherCode = null; } $participant->voucherCodeError = $error; } /** * @return string|null Why the code was not applied, or null when it was */ private function applyPromoCode(string $code, BookingDto $bookingDto, ParticipantDto $participant, int $participantIndex): ?string { if (null !== $participant->promoVoucherCode) { return sprintf('Es kann nur ein Aktionscode eingelöst werden. Bitte entferne zuerst %s.', $participant->promoVoucherCode); } $error = $this->validatePromoCode($code, $bookingDto, $participant, $participantIndex); if (null !== $error) { return $error; } $participant->promoVoucherCode = $code; $participant->promoVoucherError = null; return null; } /** * @return string|null Why the code was not applied, or null when it was */ private function applyPurchaseCode(string $code, ParticipantDto $participant): ?string { if (null !== $participant->purchaseVoucherCode) { return sprintf('Es kann nur ein Gutschein eingelöst werden. Bitte entferne zuerst %s.', $participant->purchaseVoucherCode); } $result = $this->voucherValidationService->validatePurchaseVoucher($code); if (false === $result->isValid) { return sprintf('Gutschein ungültig: %s', $result->errorMessage ?? 'Unbekannter Fehler'); } $participant->purchaseVoucherCode = $code; $participant->validatedPurchaseVoucher = $result->voucher; return null; } /** * Checks a promo code against the participant's current price and stores the validated voucher. * * @return string|null Why BusPro rejected the code, or null when it is valid */ private function validatePromoCode(string $code, BookingDto $bookingDto, ParticipantDto $participant, int $participantIndex): ?string { $travelId = $bookingDto->travel->id; if (null === $travelId) { $participant->validatedPromoVoucher = null; return 'Aktionscode konnte nicht validiert werden: Reise-ID fehlt.'; } $participantPrice = $this->priceCalculatorService->calculateIndividualParticipantPrice($bookingDto, $participantIndex); $result = $this->voucherValidationService->validatePromoVoucher($code, $travelId, $participantPrice); if (false === $result->isValid) { // Only an applied code is replaced: a rejected new code must not discard the applied one's discount if ($code === $participant->promoVoucherCode) { $participant->validatedPromoVoucher = null; } return sprintf('Aktionscode ungültig: %s', $result->errorMessage ?? 'Unbekannter Fehler'); } $participant->validatedPromoVoucher = $result->voucher; return null; } }