fix: correctly calculate room pricing based on participant assignment

This commit is contained in:
Björn Fromme
2026-03-19 15:45:23 +01:00
parent 6a6bebb2d1
commit 45d05786da
2 changed files with 68 additions and 16 deletions
+5 -4
View File
@@ -77,16 +77,17 @@ class RoomPricingCalculator
return $roomPricing; return $roomPricing;
} }
$assignmentCounts = $bookingDto->getRoomAssignmentCounts();
foreach ($selectedRooms as $roomSelection) { foreach ($selectedRooms as $roomSelection) {
$room = $this->getRoomById($bookingDto, $roomSelection->id); $room = $this->getRoomById($bookingDto, $roomSelection->id);
if (null === $room || null === $room->price) { if (null === $room || null === $room->price) {
continue; continue;
} }
// Pricing in create flow is based on selected room quantity from step 1. // Pricing in create flow is based on actual participant assignments per room.
// Do not derive billable units from minPax, which represents capacity constraints. $participantCount = $assignmentCounts[$room->id] ?? 0;
$participantCount = $roomSelection->quantity; $totalPrice = $participantCount * $room->price;
$totalPrice = $roomSelection->quantity * $room->price;
$roomPricing[] = [ $roomPricing[] = [
'roomId' => $room->id, 'roomId' => $room->id,
@@ -68,18 +68,24 @@ class BookingPriceCalculatorServiceTest extends TestCase
$bookingDto = new BookingDto($travel, 1); $bookingDto = new BookingDto($travel, 1);
$bookingDto->roomSelections = [$roomSelection]; $bookingDto->roomSelections = [$roomSelection];
$bookingDto->participants = [
$this->createParticipantWithAssignedRoom(1),
$this->createParticipantWithAssignedRoom(1),
$this->createParticipantWithAssignedRoom(1),
$this->createParticipantWithAssignedRoom(1),
];
// Test the calculation // Test the calculation
$result = $this->service->calculateRoomPricing($bookingDto); $result = $this->service->calculateRoomPricing($bookingDto);
// Expected: 2 rooms × €100 = €200 total // Expected: 4 assigned participants × €100 = €400 total
$this->assertCount(1, $result); $this->assertCount(1, $result);
$this->assertEquals(1, $result[0]['roomId']); $this->assertEquals(1, $result[0]['roomId']);
$this->assertEquals('Double Room', $result[0]['label']); $this->assertEquals('Double Room', $result[0]['label']);
$this->assertEquals(2, $result[0]['quantity']); // 2 rooms $this->assertEquals(2, $result[0]['quantity']); // 2 rooms
$this->assertEquals(2, $result[0]['participantCount']); // billed units follow selected room count $this->assertEquals(4, $result[0]['participantCount']); // billed units follow assigned participants
$this->assertEquals(100.0, $result[0]['unitPrice']); // €100 per room $this->assertEquals(100.0, $result[0]['unitPrice']); // €100 per participant
$this->assertEquals(200.0, $result[0]['totalPrice']); // €200 total $this->assertEquals(400.0, $result[0]['totalPrice']); // €400 total
} }
public function testCalculateRoomPricingWithSingleRoomSelection(): void public function testCalculateRoomPricingWithSingleRoomSelection(): void
@@ -100,18 +106,22 @@ class BookingPriceCalculatorServiceTest extends TestCase
$bookingDto = new BookingDto($travel, 1); $bookingDto = new BookingDto($travel, 1);
$bookingDto->roomSelections = [$roomSelection]; $bookingDto->roomSelections = [$roomSelection];
$bookingDto->participants = [
$this->createParticipantWithAssignedRoom(2),
$this->createParticipantWithAssignedRoom(2),
];
// Test the calculation // Test the calculation
$result = $this->service->calculateRoomPricing($bookingDto); $result = $this->service->calculateRoomPricing($bookingDto);
// Expected: 1 room × €150 = €150 total // Expected: 2 assigned participants × €150 = €300 total
$this->assertCount(1, $result); $this->assertCount(1, $result);
$this->assertEquals(2, $result[0]['roomId']); $this->assertEquals(2, $result[0]['roomId']);
$this->assertEquals('Triple Room', $result[0]['label']); $this->assertEquals('Triple Room', $result[0]['label']);
$this->assertEquals(1, $result[0]['quantity']); // 1 room $this->assertEquals(1, $result[0]['quantity']); // 1 room
$this->assertEquals(1, $result[0]['participantCount']); // billed units follow selected room count $this->assertEquals(2, $result[0]['participantCount']); // billed units follow assigned participants
$this->assertEquals(150.0, $result[0]['unitPrice']); // €150 per room $this->assertEquals(150.0, $result[0]['unitPrice']); // €150 per participant
$this->assertEquals(150.0, $result[0]['totalPrice']); // €150 total $this->assertEquals(300.0, $result[0]['totalPrice']); // €300 total
} }
public function testCalculateRoomPricingWithMultipleRoomTypes(): void public function testCalculateRoomPricingWithMultipleRoomTypes(): void
@@ -142,13 +152,19 @@ class BookingPriceCalculatorServiceTest extends TestCase
$bookingDto = new BookingDto($travel, 1); $bookingDto = new BookingDto($travel, 1);
$bookingDto->roomSelections = [$singleRoomSelection, $doubleRoomSelection]; $bookingDto->roomSelections = [$singleRoomSelection, $doubleRoomSelection];
$bookingDto->participants = [
$this->createParticipantWithAssignedRoom(1),
$this->createParticipantWithAssignedRoom(2),
$this->createParticipantWithAssignedRoom(2),
$this->createParticipantWithAssignedRoom(2),
];
// Test the calculation // Test the calculation
$result = $this->service->calculateRoomPricing($bookingDto); $result = $this->service->calculateRoomPricing($bookingDto);
// Expected: // Expected:
// - Single: 1 room × €80 = €80 // - Single: 1 assigned participant × €80 = €80
// - Double: 2 rooms × €120 = €240 // - Double: 3 assigned participants × €120 = €360
$this->assertCount(2, $result); $this->assertCount(2, $result);
// Single room result // Single room result
@@ -163,9 +179,9 @@ class BookingPriceCalculatorServiceTest extends TestCase
$doubleResult = $result[1]; $doubleResult = $result[1];
$this->assertEquals(2, $doubleResult['roomId']); $this->assertEquals(2, $doubleResult['roomId']);
$this->assertEquals(2, $doubleResult['quantity']); $this->assertEquals(2, $doubleResult['quantity']);
$this->assertEquals(2, $doubleResult['participantCount']); $this->assertEquals(3, $doubleResult['participantCount']);
$this->assertEquals(120.0, $doubleResult['unitPrice']); $this->assertEquals(120.0, $doubleResult['unitPrice']);
$this->assertEquals(240.0, $doubleResult['totalPrice']); $this->assertEquals(360.0, $doubleResult['totalPrice']);
} }
public function testCalculateRoomPricingSkipsRoomsWithNullPrice(): void public function testCalculateRoomPricingSkipsRoomsWithNullPrice(): void
@@ -216,6 +232,33 @@ class BookingPriceCalculatorServiceTest extends TestCase
$this->assertEmpty($result); $this->assertEmpty($result);
} }
public function testCalculateRoomPricingShowsSelectedRoomWithZeroAssignments(): void
{
$room = new Room();
$room->id = 1;
$room->price = 100.0;
$room->label = 'Double Room';
$travel = new Travel();
$travel->rooms = [$room];
$roomSelection = new RoomSelectionDto();
$roomSelection->id = 1;
$roomSelection->quantity = 1;
$bookingDto = new BookingDto($travel, 1);
$bookingDto->roomSelections = [$roomSelection];
$bookingDto->participants = []; // nothing assigned yet
$result = $this->service->calculateRoomPricing($bookingDto);
$this->assertCount(1, $result);
$this->assertEquals(1, $result[0]['roomId']);
$this->assertEquals(1, $result[0]['quantity']);
$this->assertEquals(0, $result[0]['participantCount']);
$this->assertEquals(0.0, $result[0]['totalPrice']);
}
public function testCalculateRoomPricingInEditModeUsesStoredIndividualPrices(): void public function testCalculateRoomPricingInEditModeUsesStoredIndividualPrices(): void
{ {
$travel = new Travel(); $travel = new Travel();
@@ -645,4 +688,12 @@ class BookingPriceCalculatorServiceTest extends TestCase
return null; return null;
} }
private function createParticipantWithAssignedRoom(int $roomId): ParticipantDto
{
$participant = new ParticipantDto();
$participant->assignedRoomId = $roomId;
return $participant;
}
} }