Files
myep/src/Service/AccommodationBookingBreakdownCalculator.php
T

114 lines
4.1 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
namespace App\Service;
use App\Entity\Groups\AccommodationBooking;
use App\Repository\Groups\AccommodationPriceRepository;
class AccommodationBookingBreakdownCalculator
{
public function __construct(
private readonly GroupsPriceCalculator $priceCalculator,
private readonly AccommodationPriceRepository $priceRepo,
) {
}
/**
* Returns the authoritative stored snapshot, falling back to current prices only for
* bookings created before snapshot persistence was introduced, enriched with the
* discount rows and the discounted total for rendering.
*
* @return array<string, mixed>|null null when no accommodation or dates are set
*/
public function compute(AccommodationBooking $booking): ?array
{
$breakdown = $booking->getPriceBreakdown() ?? $this->computeCurrent($booking);
return null !== $breakdown ? $this->withDiscounts($booking, $breakdown) : null;
}
/**
* Adds the discount rows and the price the customer actually pays. Derived on every read
* and never persisted — refreshPriceSnapshot() stores the raw breakdown from
* computeCurrent() — so the stored total and this one come out of the same code and
* cannot drift apart.
*
* @param array<string, mixed> $breakdown
*
* @return array<string, mixed> the breakdown plus `discounts` and `discountedTotal`
*/
public function withDiscounts(AccommodationBooking $booking, array $breakdown): array
{
$buckets = [
['Unterkunft', $booking->getAccommodationDiscount(), (int) ($breakdown['basePrice'] ?? 0) + (int) ($breakdown['additionalPersonsPrice'] ?? 0)],
['Verpflegung', $booking->getBoardServiceDiscount(), (int) ($breakdown['boardPrice'] ?? 0)],
['Zusatzleistungen', $booking->getAdditionalServicesDiscount(), (int) ($breakdown['servicesPrice'] ?? 0)],
];
$discounts = [];
$discountSum = 0;
foreach ($buckets as [$label, $percent, $base]) {
$amount = self::discountAmount($base, $percent);
// Nothing to show for a discount of 0 %, or one on a bucket this booking has no
// price in — a "– 0,00 €" row only raises questions.
if (0 === $amount) {
continue;
}
$discountSum += $amount;
$discounts[] = ['label' => $label, 'percent' => $percent, 'amount' => $amount];
}
$breakdown['discounts'] = $discounts;
// Deliberately not $booking->getTotalPrice(): refreshPriceSnapshot() calls this while
// computing the new total, where the stored one is still the outdated value.
$breakdown['discountedTotal'] = (int) ($breakdown['total'] ?? 0) - $discountSum;
return $breakdown;
}
private static function discountAmount(int $base, ?int $percent): int
{
return null !== $percent ? (int) round($base * $percent / 100) : 0;
}
/**
* Calculates against the current catalog. Use only while creating or explicitly editing
* a booking, or as a compatibility fallback for records created before price snapshots.
*
* @return array<string, mixed>|null
*/
public function computeCurrent(AccommodationBooking $booking): ?array
{
$accommodation = $booking->getAccommodation();
$dateFrom = $booking->getDateFrom();
$dateTo = $booking->getDateTo();
if (null === $accommodation || null === $dateFrom || null === $dateTo) {
return null;
}
$prices = $this->priceRepo->findByHotelCodeAndDateRange(
$accommodation->getCalendarCode() ?? '',
$dateFrom,
$dateTo,
);
return $this->priceCalculator->calculateFromSnapshots(
$booking->getPaxCount(),
$booking->getMinorsCount(),
$booking->getNights(),
$dateFrom,
$dateTo,
$prices,
$booking->getBoardServicePrice(),
$booking->getAdditionalServices(),
$accommodation->getCurrency(),
);
}
}