wip: bulk insurance booking fix
This commit is contained in:
@@ -11,6 +11,7 @@ use App\Form\Model\BookingDto;
|
||||
use App\Form\Model\ParticipantDto;
|
||||
use App\Form\Model\RoomSelectionDto;
|
||||
use App\Service\BookingPriceCalculatorService;
|
||||
use App\Service\ParticipantEligibilityService;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class BookingPriceCalculatorServiceTest extends TestCase
|
||||
@@ -19,7 +20,17 @@ class BookingPriceCalculatorServiceTest extends TestCase
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->service = new BookingPriceCalculatorService();
|
||||
$participantEligibilityService = $this->createMock(ParticipantEligibilityService::class);
|
||||
$participantEligibilityService->method('isParticipantEligible')->willReturn(true);
|
||||
|
||||
$insuranceTypeFilterService = new \App\Service\InsuranceTypeFilterService();
|
||||
$insuranceEligibilityService = new \App\Service\InsuranceEligibilityService();
|
||||
|
||||
$this->service = new BookingPriceCalculatorService(
|
||||
$participantEligibilityService,
|
||||
$insuranceTypeFilterService,
|
||||
$insuranceEligibilityService
|
||||
);
|
||||
}
|
||||
|
||||
public function testCalculateRoomPricingWithParticipantBasedCalculation(): void
|
||||
@@ -315,4 +326,266 @@ class BookingPriceCalculatorServiceTest extends TestCase
|
||||
|
||||
$this->assertEmpty($result);
|
||||
}
|
||||
|
||||
public function testServicePricingWithoutBulkInsurance(): void
|
||||
{
|
||||
// Test that aggregation works normally when bulk insurance is not enabled
|
||||
$travel = new Travel();
|
||||
$travel->insurances = [];
|
||||
|
||||
// Create participants with their own insurances
|
||||
$insurance1 = $this->createInsurance(1, 'Insurance A', 50.0);
|
||||
$insurance2 = $this->createInsurance(2, 'Insurance B', 75.0);
|
||||
|
||||
$participant1 = new ParticipantDto();
|
||||
$participant1->index = 0;
|
||||
$participant1->insurance = $insurance1;
|
||||
$participant1->bulkInsuranceBooking = false;
|
||||
|
||||
$participant2 = new ParticipantDto();
|
||||
$participant2->index = 1;
|
||||
$participant2->insurance = $insurance2;
|
||||
|
||||
$bookingDto = new BookingDto($travel, 2);
|
||||
$bookingDto->participants = [$participant1, $participant2];
|
||||
|
||||
$result = $this->service->calculateServicePricing($bookingDto);
|
||||
|
||||
// Expected: Both insurances should be counted separately
|
||||
$this->assertNotEmpty($result);
|
||||
$insuranceGroup = $this->findServiceGroup($result, 'Reiseversicherungen');
|
||||
$this->assertNotNull($insuranceGroup, 'Insurance group should exist');
|
||||
$this->assertCount(2, $insuranceGroup['services'], 'Should have 2 different insurance line items');
|
||||
}
|
||||
|
||||
public function testServicePricingWithBulkInsuranceSamePriceTier(): void
|
||||
{
|
||||
// Test that bulk insurance counts all participants when enabled with same price tier
|
||||
$insurance = $this->createInsurance(1, 'Reise-Rücktritt', 50.0, 'RRV');
|
||||
$insurance->travelPriceFrom = 0.0; // Accepts all prices
|
||||
$insurance->travelPriceTo = 10000.0;
|
||||
|
||||
$travel = new Travel();
|
||||
$travel->insurances = [$insurance];
|
||||
$travel->dateFrom = new \DateTimeImmutable('2025-06-01');
|
||||
$travel->dateTo = new \DateTimeImmutable('2025-06-08');
|
||||
|
||||
// Applicant with bulk insurance enabled
|
||||
$participant1 = new ParticipantDto();
|
||||
$participant1->index = 0;
|
||||
$participant1->insurance = $insurance;
|
||||
$participant1->bulkInsuranceBooking = true;
|
||||
|
||||
// Dependent with no insurance (will get price-tier-adjusted version)
|
||||
$participant2 = new ParticipantDto();
|
||||
$participant2->index = 1;
|
||||
$participant2->insurance = null;
|
||||
|
||||
$participant3 = new ParticipantDto();
|
||||
$participant3->index = 2;
|
||||
$participant3->insurance = null;
|
||||
|
||||
$bookingDto = new BookingDto($travel, 3);
|
||||
$bookingDto->participants = [$participant1, $participant2, $participant3];
|
||||
|
||||
$result = $this->service->calculateServicePricing($bookingDto);
|
||||
|
||||
// Expected: 3x same insurance should be aggregated into one line item
|
||||
$insuranceGroup = $this->findServiceGroup($result, 'Reiseversicherungen');
|
||||
$this->assertNotNull($insuranceGroup, 'Insurance group should exist');
|
||||
$this->assertCount(1, $insuranceGroup['services'], 'Should have 1 insurance line item');
|
||||
$this->assertEquals(3, $insuranceGroup['services'][0]['participantCount'], 'Should count all 3 participants');
|
||||
$this->assertEquals(150.0, $insuranceGroup['services'][0]['totalPrice'], 'Total should be 3 × €50');
|
||||
}
|
||||
|
||||
public function testServicePricingWithBulkInsuranceShowsPriceTierAdjustment(): void
|
||||
{
|
||||
// Test that bulk insurance shows correct price tiers based on individual travel prices
|
||||
// Price tiers: Tier 1 (€0-€500): €30, Tier 2 (€501-€1000): €50
|
||||
$insuranceTier1 = $this->createInsurance(1, 'Reise-Rücktritt', 30.0, 'RRV');
|
||||
$insuranceTier1->travelPriceFrom = 0.0;
|
||||
$insuranceTier1->travelPriceTo = 500.0;
|
||||
|
||||
$insuranceTier2 = $this->createInsurance(2, 'Reise-Rücktritt', 50.0, 'RRV');
|
||||
$insuranceTier2->travelPriceFrom = 501.0;
|
||||
$insuranceTier2->travelPriceTo = 1000.0;
|
||||
|
||||
$travel = new Travel();
|
||||
$travel->insurances = [$insuranceTier1, $insuranceTier2];
|
||||
$travel->dateFrom = new \DateTimeImmutable('2025-06-01');
|
||||
$travel->dateTo = new \DateTimeImmutable('2025-06-08');
|
||||
|
||||
// Applicant in Tier 2 (travel price €600)
|
||||
$participant1 = new ParticipantDto();
|
||||
$participant1->index = 0;
|
||||
$participant1->insurance = $insuranceTier2;
|
||||
$participant1->bulkInsuranceBooking = true;
|
||||
|
||||
// This test will aggregate based on what insurance is resolved
|
||||
// Without room/service assignments, we can't test real price calculation
|
||||
// So this test verifies the logic structure is correct
|
||||
$bookingDto = new BookingDto($travel, 1);
|
||||
$bookingDto->participants = [$participant1];
|
||||
|
||||
$result = $this->service->calculateServicePricing($bookingDto);
|
||||
|
||||
// Expected: Applicant's insurance is counted
|
||||
$insuranceGroup = $this->findServiceGroup($result, 'Reiseversicherungen');
|
||||
$this->assertNotNull($insuranceGroup, 'Insurance group should exist');
|
||||
$this->assertEquals(1, $insuranceGroup['services'][0]['participantCount'], 'Should count applicant');
|
||||
}
|
||||
|
||||
public function testServicePricingWithBulkInsuranceWhenNoBulkEnabled(): void
|
||||
{
|
||||
// Test that dependents are not counted when bulk insurance checkbox is not enabled
|
||||
$insurance = $this->createInsurance(1, 'Reise-Rücktritt', 50.0, 'RRV');
|
||||
|
||||
$travel = new Travel();
|
||||
$travel->insurances = [$insurance];
|
||||
|
||||
// Applicant WITHOUT bulk insurance enabled
|
||||
$participant1 = new ParticipantDto();
|
||||
$participant1->index = 0;
|
||||
$participant1->insurance = $insurance;
|
||||
$participant1->bulkInsuranceBooking = false; // NOT enabled
|
||||
|
||||
// Dependent with no insurance
|
||||
$participant2 = new ParticipantDto();
|
||||
$participant2->index = 1;
|
||||
$participant2->insurance = null; // No insurance assigned
|
||||
|
||||
$bookingDto = new BookingDto($travel, 2);
|
||||
$bookingDto->participants = [$participant1, $participant2];
|
||||
|
||||
$result = $this->service->calculateServicePricing($bookingDto);
|
||||
|
||||
// Expected: Only applicant's insurance counted
|
||||
$insuranceGroup = $this->findServiceGroup($result, 'Reiseversicherungen');
|
||||
$this->assertNotNull($insuranceGroup, 'Insurance group should exist');
|
||||
$this->assertCount(1, $insuranceGroup['services'], 'Should have 1 insurance line item');
|
||||
$this->assertEquals(1, $insuranceGroup['services'][0]['participantCount'], 'Should count only applicant');
|
||||
$this->assertEquals(50.0, $insuranceGroup['services'][0]['totalPrice'], 'Total should be 1 × €50');
|
||||
}
|
||||
|
||||
public function testServicePricingWithIneligibleParticipant(): void
|
||||
{
|
||||
// Test that ineligible participants are skipped (not counted at all)
|
||||
$insurance = $this->createInsurance(1, 'Reise-Rücktritt', 50.0, 'RRV');
|
||||
|
||||
$travel = new Travel();
|
||||
$travel->insurances = [$insurance];
|
||||
|
||||
// Applicant with insurance
|
||||
$participant1 = new ParticipantDto();
|
||||
$participant1->index = 0;
|
||||
$participant1->insurance = $insurance;
|
||||
$participant1->bulkInsuranceBooking = false;
|
||||
|
||||
// Dependent (will be marked as ineligible by the eligibility service)
|
||||
$participant2 = new ParticipantDto();
|
||||
$participant2->index = 1;
|
||||
$participant2->insurance = null;
|
||||
|
||||
$bookingDto = new BookingDto($travel, 2);
|
||||
$bookingDto->participants = [$participant1, $participant2];
|
||||
|
||||
// Mock participant eligibility to mark second participant as ineligible
|
||||
$participantEligibilityService = $this->createMock(ParticipantEligibilityService::class);
|
||||
$participantEligibilityService->method('isParticipantEligible')
|
||||
->willReturnCallback(fn ($booking, $index) => 0 === $index); // Only first participant eligible
|
||||
|
||||
$insuranceTypeFilterService = new \App\Service\InsuranceTypeFilterService();
|
||||
$insuranceEligibilityService = new \App\Service\InsuranceEligibilityService();
|
||||
|
||||
$this->service = new BookingPriceCalculatorService(
|
||||
$participantEligibilityService,
|
||||
$insuranceTypeFilterService,
|
||||
$insuranceEligibilityService
|
||||
);
|
||||
|
||||
$result = $this->service->calculateServicePricing($bookingDto);
|
||||
|
||||
// Expected: Only applicant's insurance counted (dependent is ineligible)
|
||||
$insuranceGroup = $this->findServiceGroup($result, 'Reiseversicherungen');
|
||||
$this->assertNotNull($insuranceGroup, 'Insurance group should exist');
|
||||
$this->assertCount(1, $insuranceGroup['services'], 'Should have 1 insurance line item');
|
||||
$this->assertEquals(1, $insuranceGroup['services'][0]['participantCount'], 'Should count only applicant');
|
||||
}
|
||||
|
||||
public function testServicePricingWithBulkInsuranceCountsAllParticipants(): void
|
||||
{
|
||||
// Test that bulk insurance counts all participants (price tier adjusted per participant)
|
||||
$insuranceA = $this->createInsurance(1, 'Reise-Rücktritt', 50.0, 'RRV');
|
||||
|
||||
$travel = new Travel();
|
||||
$travel->insurances = [$insuranceA];
|
||||
$travel->dateFrom = new \DateTimeImmutable('2025-06-01');
|
||||
$travel->dateTo = new \DateTimeImmutable('2025-06-08');
|
||||
|
||||
// Applicant with bulk insurance enabled
|
||||
$participant1 = new ParticipantDto();
|
||||
$participant1->index = 0;
|
||||
$participant1->insurance = $insuranceA;
|
||||
$participant1->bulkInsuranceBooking = true;
|
||||
|
||||
// Dependent 1 - no insurance (will get price-tier-adjusted version of A)
|
||||
$participant2 = new ParticipantDto();
|
||||
$participant2->index = 1;
|
||||
$participant2->insurance = null;
|
||||
|
||||
// Dependent 2 - no insurance (will get price-tier-adjusted version of A)
|
||||
$participant3 = new ParticipantDto();
|
||||
$participant3->index = 2;
|
||||
$participant3->insurance = null;
|
||||
|
||||
$bookingDto = new BookingDto($travel, 3);
|
||||
$bookingDto->participants = [$participant1, $participant2, $participant3];
|
||||
|
||||
$result = $this->service->calculateServicePricing($bookingDto);
|
||||
|
||||
// Expected: All 3 participants counted (price tier may vary per participant based on travel price)
|
||||
$insuranceGroup = $this->findServiceGroup($result, 'Reiseversicherungen');
|
||||
$this->assertNotNull($insuranceGroup, 'Insurance group should exist');
|
||||
|
||||
// Count total participants across all insurance line items
|
||||
$totalParticipants = array_sum(array_column($insuranceGroup['services'], 'participantCount'));
|
||||
$this->assertEquals(3, $totalParticipants, 'Should count all 3 participants across all tiers');
|
||||
}
|
||||
|
||||
// Helper methods
|
||||
|
||||
private function createInsurance(int $id, string $label, float $price, string $subType = 'RRV', bool $package = false, bool $complementary = false): \App\BusProNet\Model\Insurance
|
||||
{
|
||||
$insurance = new \App\BusProNet\Model\Insurance();
|
||||
$insurance->id = (string) $id;
|
||||
$insurance->label = $label;
|
||||
$insurance->price = $price;
|
||||
$insurance->subType = $subType;
|
||||
$insurance->package = $package;
|
||||
$insurance->complementary = $complementary;
|
||||
|
||||
return $insurance;
|
||||
}
|
||||
|
||||
private function findServiceGroup(array $groups, string $groupName): ?array
|
||||
{
|
||||
foreach ($groups as $group) {
|
||||
if ($group['groupName'] === $groupName) {
|
||||
return $group;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function findServiceItem(array $items, string $label): ?array
|
||||
{
|
||||
foreach ($items as $item) {
|
||||
if ($item['label'] === $label) {
|
||||
return $item;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user