feat: extended price mismatch diagnostics in logs
This commit is contained in:
@@ -14,6 +14,7 @@ use App\Form\BookingCreateStep3Type;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Htmx\HxTrait;
|
||||
use App\Service\BookingPriceCalculatorService;
|
||||
use App\Service\BookingPriceMismatchDiagnosticsService;
|
||||
use App\Service\BookingService;
|
||||
use App\Service\BookingSummaryDataService;
|
||||
use Psr\Log\LoggerInterface;
|
||||
@@ -36,6 +37,7 @@ class Step3Controller extends AbstractController
|
||||
private readonly BookingService $bookingService,
|
||||
private readonly BookingSummaryDataService $summaryDataService,
|
||||
private readonly BookingPriceCalculatorService $priceCalculator,
|
||||
private readonly BookingPriceMismatchDiagnosticsService $priceMismatchDiagnostics,
|
||||
private readonly ApiClient $apiClient,
|
||||
private readonly LoggerInterface $logger,
|
||||
) {
|
||||
@@ -126,6 +128,9 @@ class Step3Controller extends AbstractController
|
||||
$expectedTotal = round($calculatedSubtotal - $promoGoodwillDiscount - $apiAppliedDiscount, 2);
|
||||
|
||||
if ((int) round($apiTotal * 100) !== (int) round($expectedTotal * 100)) {
|
||||
$diagnostics = $this->priceMismatchDiagnostics->buildDiagnostics($bookingCreateDto, $inquiryResponse);
|
||||
$deltaBreakdown = $diagnostics['deltaBreakdown'] ?? [];
|
||||
|
||||
return $this->handleApiError(
|
||||
'Price mismatch detected - payload incomplete',
|
||||
[
|
||||
@@ -135,6 +140,11 @@ class Step3Controller extends AbstractController
|
||||
'apiAppliedDiscount' => $apiAppliedDiscount,
|
||||
'expectedTotal' => $expectedTotal,
|
||||
'difference' => abs($apiTotal - $expectedTotal),
|
||||
'deltaTotal' => $deltaBreakdown['deltaTotal'] ?? null,
|
||||
'deltaRoom' => $deltaBreakdown['deltaRoom'] ?? null,
|
||||
'deltaService' => $deltaBreakdown['deltaService'] ?? null,
|
||||
'deltaInsurance' => $deltaBreakdown['deltaInsurance'] ?? null,
|
||||
'diagnostics' => $diagnostics,
|
||||
],
|
||||
'Preisabweichung festgestellt. Bitte wende dich an den Kundenservice.',
|
||||
$bookingCreateDto,
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
use App\BusProNet\Constants;
|
||||
use App\BusProNet\Model\BookingResponse;
|
||||
use App\BusProNet\Model\PriceItem;
|
||||
use App\Form\Model\BookingDto;
|
||||
|
||||
/**
|
||||
* Builds diagnostic payloads for booking price mismatches.
|
||||
*/
|
||||
class BookingPriceMismatchDiagnosticsService
|
||||
{
|
||||
public function __construct(
|
||||
private readonly BookingPriceCalculatorService $priceCalculator,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function buildDiagnostics(BookingDto $bookingCreateDto, BookingResponse $response): array
|
||||
{
|
||||
$pricingBreakdown = $this->priceCalculator->getPricingBreakdown($bookingCreateDto);
|
||||
|
||||
$roomLines = $pricingBreakdown['rooms'] ?? [];
|
||||
$serviceGroups = $pricingBreakdown['services'] ?? [];
|
||||
|
||||
$roomTotal = round(array_sum(array_column($roomLines, 'totalPrice')), 2);
|
||||
$serviceTotal = round(array_sum(array_column($serviceGroups, 'groupTotal')), 2);
|
||||
$insuranceTotal = round($this->extractInsuranceTotal($serviceGroups), 2);
|
||||
|
||||
$selectedRooms = [];
|
||||
foreach ($bookingCreateDto->getSelectedRooms() as $roomSelection) {
|
||||
$selectedRooms[] = [
|
||||
'roomId' => $roomSelection->id,
|
||||
'quantity' => $roomSelection->quantity,
|
||||
'unitPrice' => $roomSelection->price,
|
||||
'capacity' => $roomSelection->capacity,
|
||||
];
|
||||
}
|
||||
|
||||
$participantInsurance = [];
|
||||
foreach ($bookingCreateDto->getParticipants() as $index => $participant) {
|
||||
$participantInsurance[] = [
|
||||
'participantIndex' => $index,
|
||||
'bulkInsuranceBooking' => $participant->bulkInsuranceBooking,
|
||||
'insuranceId' => $participant->insurance?->id,
|
||||
'insurancePrice' => $participant->insurance?->price,
|
||||
'assignedRoomId' => $participant->assignedRoomId,
|
||||
];
|
||||
}
|
||||
|
||||
$apiPriceItems = array_map(static function (PriceItem $item): array {
|
||||
return [
|
||||
'position' => $item->position,
|
||||
'type' => $item->type,
|
||||
'subType' => $item->subType,
|
||||
'id' => $item->id,
|
||||
'quantity' => $item->quantity,
|
||||
'totalPrice' => round($item->totalPrice, 2),
|
||||
];
|
||||
}, $response->priceItems);
|
||||
|
||||
$apiRoomTotal = round($this->sumApiRoomTotal($response->priceItems), 2);
|
||||
$apiInsuranceTotal = round($this->sumApiInsuranceTotal($response->priceItems), 2);
|
||||
$apiTotal = round($response->totalPrice ?? 0.0, 2);
|
||||
$apiServiceTotal = round($apiTotal - $apiRoomTotal, 2);
|
||||
|
||||
$deltaTotal = round(($pricingBreakdown['grandTotal'] ?? 0.0) - $apiTotal, 2);
|
||||
$deltaRoom = round($roomTotal - $apiRoomTotal, 2);
|
||||
$deltaService = round($serviceTotal - $apiServiceTotal, 2);
|
||||
$deltaInsurance = round($insuranceTotal - $apiInsuranceTotal, 2);
|
||||
|
||||
return [
|
||||
'localBreakdown' => [
|
||||
'grandTotal' => round($pricingBreakdown['grandTotal'] ?? 0.0, 2),
|
||||
'roomTotal' => $roomTotal,
|
||||
'serviceTotal' => $serviceTotal,
|
||||
'insuranceTotal' => $insuranceTotal,
|
||||
],
|
||||
'apiBreakdown' => [
|
||||
'total' => $apiTotal,
|
||||
'roomTotal' => $apiRoomTotal,
|
||||
'serviceTotal' => $apiServiceTotal,
|
||||
'insuranceTotal' => $apiInsuranceTotal,
|
||||
],
|
||||
'deltaBreakdown' => [
|
||||
'deltaTotal' => $deltaTotal,
|
||||
'deltaRoom' => $deltaRoom,
|
||||
'deltaService' => $deltaService,
|
||||
'deltaInsurance' => $deltaInsurance,
|
||||
],
|
||||
'roomLines' => $roomLines,
|
||||
'serviceGroups' => $serviceGroups,
|
||||
'selectedRooms' => $selectedRooms,
|
||||
'roomAssignmentCounts' => $bookingCreateDto->getRoomAssignmentCounts(),
|
||||
'participantInsurance' => $participantInsurance,
|
||||
'apiPriceItems' => $apiPriceItems,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, array<string, mixed>> $serviceGroups
|
||||
*/
|
||||
private function extractInsuranceTotal(array $serviceGroups): float
|
||||
{
|
||||
$total = 0.0;
|
||||
|
||||
foreach ($serviceGroups as $group) {
|
||||
foreach (($group['services'] ?? []) as $service) {
|
||||
$subType = $service['subType'] ?? null;
|
||||
if (null === $subType || false === in_array($subType, Constants::TOKEN_INSURANCES, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$total += (float) ($service['totalPrice'] ?? 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
return $total;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, PriceItem> $priceItems
|
||||
*/
|
||||
private function sumApiRoomTotal(array $priceItems): float
|
||||
{
|
||||
$total = 0.0;
|
||||
foreach ($priceItems as $item) {
|
||||
if ('UNT' === $item->type) {
|
||||
$total += $item->totalPrice;
|
||||
}
|
||||
}
|
||||
|
||||
return $total;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, PriceItem> $priceItems
|
||||
*/
|
||||
private function sumApiInsuranceTotal(array $priceItems): float
|
||||
{
|
||||
$total = 0.0;
|
||||
foreach ($priceItems as $item) {
|
||||
if (null !== $item->subType && true === in_array($item->subType, Constants::TOKEN_INSURANCES, true)) {
|
||||
$total += $item->totalPrice;
|
||||
}
|
||||
}
|
||||
|
||||
return $total;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user