feat: dedicated room pricing calculation for step 1 of create flow

This commit is contained in:
Björn Fromme
2026-03-19 16:40:59 +01:00
parent df8566e1b5
commit 301b14f518
5 changed files with 79 additions and 20 deletions
@@ -11,6 +11,7 @@ use App\Form\Model\BookingDto;
use App\Htmx\HxTrait; use App\Htmx\HxTrait;
use App\Service\BookingService; use App\Service\BookingService;
use App\Service\BookingSummaryDataService; use App\Service\BookingSummaryDataService;
use App\Service\RoomPricingCalculator;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
@@ -78,7 +79,7 @@ class Step1Controller extends AbstractController
} }
// Get complete summary data (pricing, rooms, CMS data) // Get complete summary data (pricing, rooms, CMS data)
$summaryData = $this->summaryDataService->getSummaryData($bookingCreateDto); $summaryData = $this->summaryDataService->getSummaryData($bookingCreateDto, RoomPricingCalculator::PRICING_MODE_SELECTION);
$availableRooms = $bookingCreateDto->travel->getAvailableRooms(); $availableRooms = $bookingCreateDto->travel->getAvailableRooms();
$groupedRooms = $this->bookingService->groupRoomsBySelectionType($availableRooms); $groupedRooms = $this->bookingService->groupRoomsBySelectionType($availableRooms);
@@ -111,7 +112,7 @@ class Step1Controller extends AbstractController
$form->handleRequest($request); $form->handleRequest($request);
// Get complete summary data (pricing, rooms, CMS data) // Get complete summary data (pricing, rooms, CMS data)
$summaryData = $this->summaryDataService->getSummaryData($bookingCreateDto); $summaryData = $this->summaryDataService->getSummaryData($bookingCreateDto, RoomPricingCalculator::PRICING_MODE_SELECTION);
$availableRooms = $bookingCreateDto->travel->getAvailableRooms(); $availableRooms = $bookingCreateDto->travel->getAvailableRooms();
$groupedRooms = $this->bookingService->groupRoomsBySelectionType($availableRooms); $groupedRooms = $this->bookingService->groupRoomsBySelectionType($availableRooms);
+21 -9
View File
@@ -29,11 +29,14 @@ class BookingPriceCalculatorService
* *
* @return array{rooms: array, services: array, grandTotal: float} Complete pricing breakdown * @return array{rooms: array, services: array, grandTotal: float} Complete pricing breakdown
*/ */
public function getPricingBreakdown(BookingDto $bookingDto): array public function getPricingBreakdown(
BookingDto $bookingDto,
string $roomPricingMode = RoomPricingCalculator::PRICING_MODE_ASSIGNMENT,
): array
{ {
$roomPricing = $this->roomPricingCalculator->calculateRoomPricing($bookingDto); $roomPricing = $this->roomPricingCalculator->calculateRoomPricing($bookingDto, $roomPricingMode);
$servicePricing = $this->servicePricingCalculator->calculateServicePricing($bookingDto); $servicePricing = $this->servicePricingCalculator->calculateServicePricing($bookingDto);
$grandTotal = $this->calculateGrandTotal($bookingDto); $grandTotal = $this->calculateGrandTotal($bookingDto, $roomPricingMode);
$result = [ $result = [
'rooms' => $roomPricing, 'rooms' => $roomPricing,
@@ -64,9 +67,12 @@ class BookingPriceCalculatorService
* *
* @return array Array of room pricing data with labels, quantities, and totals * @return array Array of room pricing data with labels, quantities, and totals
*/ */
public function calculateRoomPricing(BookingDto $bookingDto): array public function calculateRoomPricing(
BookingDto $bookingDto,
string $roomPricingMode = RoomPricingCalculator::PRICING_MODE_ASSIGNMENT,
): array
{ {
return $this->roomPricingCalculator->calculateRoomPricing($bookingDto); return $this->roomPricingCalculator->calculateRoomPricing($bookingDto, $roomPricingMode);
} }
/** /**
@@ -90,9 +96,12 @@ class BookingPriceCalculatorService
* *
* @return float The grand total price * @return float The grand total price
*/ */
public function calculateGrandTotal(BookingDto $bookingDto): float public function calculateGrandTotal(
BookingDto $bookingDto,
string $roomPricingMode = RoomPricingCalculator::PRICING_MODE_ASSIGNMENT,
): float
{ {
$roomTotal = $this->roomPricingCalculator->calculateRoomTotal($bookingDto); $roomTotal = $this->roomPricingCalculator->calculateRoomTotal($bookingDto, $roomPricingMode);
$serviceTotal = $this->servicePricingCalculator->calculateServiceTotal($bookingDto); $serviceTotal = $this->servicePricingCalculator->calculateServiceTotal($bookingDto);
return $roomTotal + $serviceTotal; return $roomTotal + $serviceTotal;
@@ -101,9 +110,12 @@ class BookingPriceCalculatorService
/** /**
* Calculates total price for all rooms. * Calculates total price for all rooms.
*/ */
public function calculateRoomTotal(BookingDto $bookingDto): float public function calculateRoomTotal(
BookingDto $bookingDto,
string $roomPricingMode = RoomPricingCalculator::PRICING_MODE_ASSIGNMENT,
): float
{ {
return $this->roomPricingCalculator->calculateRoomTotal($bookingDto); return $this->roomPricingCalculator->calculateRoomTotal($bookingDto, $roomPricingMode);
} }
/** /**
+5 -2
View File
@@ -34,7 +34,10 @@ class BookingSummaryDataService
/** /**
* Get complete summary data for booking sidebar. * Get complete summary data for booking sidebar.
*/ */
public function getSummaryData(BookingDto $bookingDto): BookingSummaryDto public function getSummaryData(
BookingDto $bookingDto,
string $roomPricingMode = RoomPricingCalculator::PRICING_MODE_ASSIGNMENT,
): BookingSummaryDto
{ {
// Get selected rooms (for Step1 controller compatibility) // Get selected rooms (for Step1 controller compatibility)
$selectedRooms = $bookingDto->getSelectedRooms(); $selectedRooms = $bookingDto->getSelectedRooms();
@@ -66,7 +69,7 @@ class BookingSummaryDataService
} }
// Get detailed pricing breakdown // Get detailed pricing breakdown
$pricingData = $this->priceCalculator->getPricingBreakdown($bookingDto); $pricingData = $this->priceCalculator->getPricingBreakdown($bookingDto, $roomPricingMode);
// Fetch CMS data (images, etc.) // Fetch CMS data (images, etc.)
$cmsData = $this->getCmsData($bookingDto); $cmsData = $this->getCmsData($bookingDto);
+21 -7
View File
@@ -15,6 +15,9 @@ use App\Form\Model\BookingDto;
*/ */
class RoomPricingCalculator class RoomPricingCalculator
{ {
public const PRICING_MODE_ASSIGNMENT = 'assignment';
public const PRICING_MODE_SELECTION = 'selection';
/** /**
* Calculates pricing for all selected rooms. * Calculates pricing for all selected rooms.
* *
@@ -22,7 +25,10 @@ class RoomPricingCalculator
* *
* @return array Array of room pricing data with labels, quantities, and totals * @return array Array of room pricing data with labels, quantities, and totals
*/ */
public function calculateRoomPricing(BookingDto $bookingDto): array public function calculateRoomPricing(
BookingDto $bookingDto,
string $pricingMode = self::PRICING_MODE_ASSIGNMENT,
): array
{ {
// In edit mode, use room data from the booking entity // In edit mode, use room data from the booking entity
if (BookingDto::MODE_EDIT === $bookingDto->getMode() && null !== $bookingDto->booking) { if (BookingDto::MODE_EDIT === $bookingDto->getMode() && null !== $bookingDto->booking) {
@@ -30,15 +36,18 @@ class RoomPricingCalculator
} }
// In create mode, use room selections from the form // In create mode, use room selections from the form
return $this->calculateRoomPricingFromSelections($bookingDto); return $this->calculateRoomPricingFromSelections($bookingDto, $pricingMode);
} }
/** /**
* Calculates total price for all rooms. * Calculates total price for all rooms.
*/ */
public function calculateRoomTotal(BookingDto $bookingDto): float public function calculateRoomTotal(
BookingDto $bookingDto,
string $pricingMode = self::PRICING_MODE_ASSIGNMENT,
): float
{ {
$roomPricing = $this->calculateRoomPricing($bookingDto); $roomPricing = $this->calculateRoomPricing($bookingDto, $pricingMode);
return array_sum(array_column($roomPricing, 'totalPrice')); return array_sum(array_column($roomPricing, 'totalPrice'));
} }
@@ -68,7 +77,7 @@ class RoomPricingCalculator
* *
* @return array Array of room pricing data * @return array Array of room pricing data
*/ */
private function calculateRoomPricingFromSelections(BookingDto $bookingDto): array private function calculateRoomPricingFromSelections(BookingDto $bookingDto, string $pricingMode): array
{ {
$roomPricing = []; $roomPricing = [];
$selectedRooms = $bookingDto->getSelectedRooms(); $selectedRooms = $bookingDto->getSelectedRooms();
@@ -85,8 +94,13 @@ class RoomPricingCalculator
continue; continue;
} }
// Pricing in create flow is based on actual participant assignments per room. // Step 1 displays intended room pricing based on selected quantity and
$participantCount = $assignmentCounts[$room->id] ?? 0; // minimum room occupancy (minPax). Subsequent steps calculate based on
// actual participant assignments.
$participantCount = self::PRICING_MODE_SELECTION === $pricingMode
? (($room->minPax ?? 1) * $roomSelection->quantity)
: ($assignmentCounts[$room->id] ?? 0)
;
$totalPrice = $participantCount * $room->price; $totalPrice = $participantCount * $room->price;
$roomPricing[] = [ $roomPricing[] = [
@@ -259,6 +259,35 @@ class BookingPriceCalculatorServiceTest extends TestCase
$this->assertEquals(0.0, $result[0]['totalPrice']); $this->assertEquals(0.0, $result[0]['totalPrice']);
} }
public function testCalculateRoomPricingUsesSelectionModeForStepOneSummary(): void
{
$room = new Room();
$room->id = 3;
$room->price = 229.0;
$room->minPax = 2;
$room->label = 'Doppelzimmer Dusche/WC';
$travel = new Travel();
$travel->rooms = [$room];
$roomSelection = new RoomSelectionDto();
$roomSelection->id = 3;
$roomSelection->quantity = 1;
$bookingDto = new BookingDto($travel, 1);
$bookingDto->roomSelections = [$roomSelection];
$bookingDto->participants = [
$this->createParticipantWithAssignedRoom(3),
$this->createParticipantWithAssignedRoom(3),
];
$result = $this->service->calculateRoomPricing($bookingDto, RoomPricingCalculator::PRICING_MODE_SELECTION);
$this->assertCount(1, $result);
$this->assertEquals(2, $result[0]['participantCount']);
$this->assertEquals(458.0, $result[0]['totalPrice']);
}
public function testCalculateRoomPricingInEditModeUsesStoredIndividualPrices(): void public function testCalculateRoomPricingInEditModeUsesStoredIndividualPrices(): void
{ {
$travel = new Travel(); $travel = new Travel();