fix: account for discounts applied by bpn api in validation request
This commit is contained in:
@@ -102,4 +102,45 @@ class BookingResponse
|
||||
2
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets API-applied discounts that cannot be predicted by the local price calculator.
|
||||
*
|
||||
* The BusProNet API may apply automatic discounts based on business rules that
|
||||
* are not known to the local application, such as:
|
||||
* - ERM/GRU: Gruppenrabatt (group discount for large bookings)
|
||||
* - Other ERM subtypes: Various automatic discounts
|
||||
*
|
||||
* These discounts appear as negative price items and must be accounted for when
|
||||
* comparing the API total price against the locally calculated price.
|
||||
*
|
||||
* Note: Voucher discounts (AKTION, KULANZGUTSCHEIN) are handled separately via
|
||||
* getVoucherDiscountFromPrices() as they are user-initiated, not automatic.
|
||||
*
|
||||
* @return array<PriceItem> Price items representing API-applied automatic discounts
|
||||
*/
|
||||
public function getApiAppliedDiscounts(): array
|
||||
{
|
||||
return array_filter(
|
||||
$this->priceItems,
|
||||
fn (PriceItem $item) => $item->totalPrice < 0
|
||||
&& false === \in_array($item->type, ['AKTION', 'KULANZGUTSCHEIN'], true)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets total discount from API-applied automatic discounts.
|
||||
*
|
||||
* Returns the absolute sum of negative price items that represent automatic
|
||||
* discounts applied by the API (excluding voucher discounts which are handled separately).
|
||||
*/
|
||||
public function getApiAppliedDiscountTotal(): float
|
||||
{
|
||||
$discount = 0.0;
|
||||
foreach ($this->getApiAppliedDiscounts() as $item) {
|
||||
$discount += abs($item->totalPrice);
|
||||
}
|
||||
|
||||
return round($discount, 2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,12 +115,15 @@ class Step3Controller extends AbstractController
|
||||
}
|
||||
|
||||
// Validate price match (rounded to cent precision to avoid floating-point errors)
|
||||
// API gesamtpreis includes promotional/goodwill voucher discounts (negative price items),
|
||||
// but NOT purchase vouchers (those reduce restzahlung, not gesamtpreis)
|
||||
// API gesamtpreis includes:
|
||||
// - Promotional/goodwill voucher discounts (negative price items with art=AKTION/KULANZGUTSCHEIN)
|
||||
// - API-applied automatic discounts (e.g., Gruppenrabatt with art=ERM)
|
||||
// - NOT purchase vouchers (those reduce restzahlung, not gesamtpreis)
|
||||
$apiTotal = round($inquiryResponse->totalPrice ?? 0.0, 2);
|
||||
$calculatedSubtotal = round($this->priceCalculator->calculateGrandTotal($bookingCreateDto), 2);
|
||||
$promoGoodwillDiscount = round($inquiryResponse->getVoucherDiscountFromPrices(), 2);
|
||||
$expectedTotal = round($calculatedSubtotal - $promoGoodwillDiscount, 2);
|
||||
$apiAppliedDiscount = round($inquiryResponse->getApiAppliedDiscountTotal(), 2);
|
||||
$expectedTotal = round($calculatedSubtotal - $promoGoodwillDiscount - $apiAppliedDiscount, 2);
|
||||
|
||||
if ((int) round($apiTotal * 100) !== (int) round($expectedTotal * 100)) {
|
||||
return $this->handleApiError(
|
||||
@@ -129,6 +132,7 @@ class Step3Controller extends AbstractController
|
||||
'apiTotal' => $apiTotal,
|
||||
'calculatedSubtotal' => $calculatedSubtotal,
|
||||
'promoGoodwillDiscount' => $promoGoodwillDiscount,
|
||||
'apiAppliedDiscount' => $apiAppliedDiscount,
|
||||
'expectedTotal' => $expectedTotal,
|
||||
'difference' => abs($apiTotal - $expectedTotal),
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user