feat: finalize voucher handling and discount display in summary and overview
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user