feat: finalize voucher handling and discount display in summary and overview

This commit is contained in:
Björn Fromme
2025-11-26 17:29:15 +01:00
parent 32bef9a4e9
commit f4691ec67e
15 changed files with 762 additions and 46 deletions
@@ -902,8 +902,9 @@ class BookingDataProcessor
}
// Add goodwill voucher as participant-level einloesecode
// Goodwill vouchers are excluded from inquiry bookings and only allowed in final booking requests
if (false === $isInquiryBooking && Constants::BOOKING_TYPE_BOOKING === $bookingType) {
// Goodwill vouchers are excluded from inquiry bookings (status='A') but included in final bookings (status='F')
// They can be sent in both validation requests (buchungsart=Anfrage) and commit requests (buchungsart=Buchung)
if (false === $isInquiryBooking) {
$goodwillCode = $this->getGoodwillVoucherCodeForParticipant($participant);
if (null !== $goodwillCode) {
$participantData['einloesecode'] = $goodwillCode;
@@ -1205,8 +1206,8 @@ class BookingDataProcessor
foreach ($bookingDto->participants as $participant) {
if (null !== $participant->purchaseVoucherCode && '' !== trim($participant->purchaseVoucherCode)) {
// Exclude goodwill vouchers (flagged by field handler)
if (false === $participant->hasGoodwillVoucher) {
// Exclude goodwill vouchers
if (false === $participant->hasGoodwillVoucher()) {
$vouchers[] = trim($participant->purchaseVoucherCode);
}
}
@@ -1242,7 +1243,7 @@ class BookingDataProcessor
*/
private function getGoodwillVoucherCodeForParticipant(ParticipantDto $participant): ?string
{
if (true === $participant->hasGoodwillVoucher && null !== $participant->purchaseVoucherCode && '' !== trim($participant->purchaseVoucherCode)) {
if ($participant->hasGoodwillVoucher() && null !== $participant->purchaseVoucherCode && '' !== trim($participant->purchaseVoucherCode)) {
return trim($participant->purchaseVoucherCode);
}
+55
View File
@@ -47,4 +47,59 @@ class BookingResponse
{
return 'erfolgt' === $this->status;
}
/**
* Gets voucher discounts from price items (AKTION and KULANZGUTSCHEIN types).
*
* Promotional vouchers appear as negative price items with art="AKTION".
* Goodwill vouchers appear as negative price items with art="KULANZGUTSCHEIN".
*
* @return array<PriceItem> Price items representing voucher discounts
*/
public function getVoucherDiscountsFromPrices(): array
{
return array_filter(
$this->priceItems,
fn (PriceItem $item) => \in_array($item->type, ['AKTION', 'KULANZGUTSCHEIN'], true)
);
}
/**
* Gets total discount from price-based vouchers (promotional and goodwill).
*
* Returns the absolute sum of negative price items with AKTION or KULANZGUTSCHEIN types.
*/
public function getVoucherDiscountFromPrices(): float
{
$discount = 0.0;
foreach ($this->getVoucherDiscountsFromPrices() as $item) {
// Price items for vouchers are negative, so we take absolute value
$discount += abs($item->totalPrice);
}
return round($discount, 2);
}
/**
* Gets total discount from purchase vouchers in payment terms.
*/
public function getPurchaseVoucherDiscount(): float
{
return $this->paymentTerms?->getPurchaseVoucherDiscount() ?? 0.0;
}
/**
* Gets total voucher discount from all sources.
*
* Combines discounts from:
* - Price items (art="AKTION" for promotional, art="KULANZGUTSCHEIN" for goodwill)
* - Payment terms (kaufgutschein elements for purchase vouchers)
*/
public function getTotalVoucherDiscount(): float
{
return round(
$this->getVoucherDiscountFromPrices() + $this->getPurchaseVoucherDiscount(),
2
);
}
}
+19
View File
@@ -6,14 +6,33 @@ namespace App\BusProNet\Model;
/**
* Represents payment terms from the booking response.
*
* Contains deposit/final payment details and any applied purchase vouchers.
*/
class PaymentTerms
{
/**
* @param array<array{nummer: string, betrag: float}> $appliedPurchaseVouchers
*/
public function __construct(
public readonly ?float $depositAmount = null,
public readonly ?string $depositDate = null,
public readonly ?float $finalPaymentAmount = null,
public readonly ?string $finalPaymentDate = null,
public readonly array $appliedPurchaseVouchers = [],
) {
}
/**
* Gets the total discount from purchase vouchers.
*/
public function getPurchaseVoucherDiscount(): float
{
$total = 0.0;
foreach ($this->appliedPurchaseVouchers as $voucher) {
$total += $voucher['betrag'];
}
return round($total, 2);
}
}
@@ -106,11 +106,22 @@ class BookingResponseParser extends AbstractParser
$finalPaymentDate = $finalPaymentNode->attr('termin');
}
// Parse purchase vouchers (kaufgutschein elements)
$appliedPurchaseVouchers = [];
$voucherNodes = $paymentNode->filterXPath('//kaufgutschein');
$voucherNodes->each(function (Crawler $voucherNode) use (&$appliedPurchaseVouchers): void {
$appliedPurchaseVouchers[] = [
'nummer' => $voucherNode->attr('nummer') ?? '',
'betrag' => $this->stringToFloat($voucherNode->attr('betrag')),
];
});
return new PaymentTerms(
depositAmount: $depositAmount,
depositDate: $depositDate,
finalPaymentAmount: $finalPaymentAmount,
finalPaymentDate: $finalPaymentDate
finalPaymentDate: $finalPaymentDate,
appliedPurchaseVouchers: $appliedPurchaseVouchers,
);
}
}