fix: resolve stale dependent selections after booking family insurance
This commit is contained in:
1 parent
35826088cb
commit
c9c28792e5
11 files changed
+490
-173
No files matched your search
@@ -8,6 +8,8 @@ use App\BusProNet\Model\Insurance;
|
||||
use App\BusProNet\Model\Travel;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Model\ParticipantDto;
|
||||
use App\Service\ApplicantInsuranceCascade;
|
||||
use App\Service\BookingPriceCalculator;
|
||||
use App\Service\BookingPricingAssembler;
|
||||
use App\Service\InsuranceManager;
|
||||
use App\Service\ParticipantEligibilityChecker;
|
||||
@@ -83,6 +85,10 @@ class BookingPricingAssemblerTest extends TestCase
|
||||
$bookingDto = new BookingDto($travel, 3);
|
||||
$bookingDto->participants = [$participant1, $participant2, $participant3];
|
||||
|
||||
// Bulk assignment is materialised into the DTO by the cascade; the assembler then
|
||||
// simply reports what each participant holds.
|
||||
$this->createCascade()->apply($bookingDto);
|
||||
|
||||
$result = $this->assembler->calculateServicePricing($bookingDto);
|
||||
|
||||
// 3× same insurance should aggregate into one line item
|
||||
@@ -207,6 +213,8 @@ class BookingPricingAssemblerTest extends TestCase
|
||||
$bookingDto = new BookingDto($travel, 3);
|
||||
$bookingDto->participants = [$participant1, $participant2, $participant3];
|
||||
|
||||
$this->createCascade()->apply($bookingDto);
|
||||
|
||||
$result = $this->assembler->calculateServicePricing($bookingDto);
|
||||
|
||||
$insuranceGroup = $this->findServiceGroup($result, 'Reiseversicherungen');
|
||||
@@ -216,8 +224,93 @@ class BookingPricingAssemblerTest extends TestCase
|
||||
$this->assertEquals(3, $totalParticipants, 'Should count all 3 participants across all tiers');
|
||||
}
|
||||
|
||||
/**
|
||||
* Regression for the production report: an adult and two children each picked their own
|
||||
* insurance, then the applicant switched to the family insurance that had become
|
||||
* available. The sidebar kept listing the two children's superseded policies, and the
|
||||
* grand total stayed inflated by their premiums.
|
||||
*
|
||||
* The sidebar reports whatever the DTO holds, so this asserts the end-to-end contract:
|
||||
* once ApplicantInsuranceCascade has run, only the family policy remains to aggregate.
|
||||
*/
|
||||
public function testFamilyInsuranceLeavesOnlyOneInsuranceLineItem(): void
|
||||
{
|
||||
$travel = $this->createFamilyTravel();
|
||||
|
||||
$familyInsurance = $this->createInsurance(1, 'Familienversicherung', 120.0);
|
||||
$familyInsurance->familyInsurance = true;
|
||||
|
||||
$applicant = $this->createParticipantWithAge(0, '1985-06-15');
|
||||
$applicant->insurance = $familyInsurance;
|
||||
$applicant->bulkInsuranceBooking = false;
|
||||
|
||||
$childOne = $this->createParticipantWithAge(1, '2016-01-01');
|
||||
$childOne->insurance = $this->createInsurance(2, 'Insurance A', 50.0);
|
||||
|
||||
$childTwo = $this->createParticipantWithAge(2, '2018-01-01');
|
||||
$childTwo->insurance = $this->createInsurance(3, 'Insurance B', 75.0);
|
||||
|
||||
$travel->insurances = [$familyInsurance];
|
||||
|
||||
$bookingDto = new BookingDto($travel, 3);
|
||||
$bookingDto->participants = [$applicant, $childOne, $childTwo];
|
||||
|
||||
$this->createCascade()->apply($bookingDto);
|
||||
|
||||
$result = $this->assembler->calculateServicePricing($bookingDto);
|
||||
|
||||
$insuranceGroup = $this->findServiceGroup($result, 'Reiseversicherungen');
|
||||
$this->assertNotNull($insuranceGroup, 'Insurance group should exist');
|
||||
$this->assertCount(
|
||||
1,
|
||||
$insuranceGroup['services'],
|
||||
'Only the family policy may be listed - the children\'s superseded selections must be gone'
|
||||
);
|
||||
|
||||
$lineItem = array_values($insuranceGroup['services'])[0];
|
||||
$this->assertSame('Familienversicherung', $lineItem['label']);
|
||||
$this->assertEquals(1, $lineItem['participantCount'], 'Family policy is held once, by the applicant');
|
||||
$this->assertEqualsWithDelta(120.0, $insuranceGroup['groupTotal'], 0.001, 'Total must not include the children\'s former premiums');
|
||||
}
|
||||
|
||||
// Helpers
|
||||
|
||||
private function createCascade(): ApplicantInsuranceCascade
|
||||
{
|
||||
$roomPricingCalculator = new RoomPricingCalculator();
|
||||
$participantPricingCalculator = new ParticipantPricingCalculator($roomPricingCalculator);
|
||||
$insuranceManager = new InsuranceManager();
|
||||
|
||||
return new ApplicantInsuranceCascade(
|
||||
$insuranceManager,
|
||||
new BookingPriceCalculator(
|
||||
$roomPricingCalculator,
|
||||
$this->createAssembler($this->eligibilityChecker),
|
||||
$participantPricingCalculator
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private function createFamilyTravel(): Travel
|
||||
{
|
||||
$travel = new Travel();
|
||||
$travel->id = 1;
|
||||
$travel->dateFrom = new \DateTimeImmutable('2026-02-01');
|
||||
$travel->dateTo = new \DateTimeImmutable('2026-02-08');
|
||||
$travel->insurances = [];
|
||||
|
||||
return $travel;
|
||||
}
|
||||
|
||||
private function createParticipantWithAge(int $index, string $dateOfBirth): ParticipantDto
|
||||
{
|
||||
$participant = new ParticipantDto();
|
||||
$participant->index = $index;
|
||||
$participant->dateOfBirth = new \DateTimeImmutable($dateOfBirth);
|
||||
|
||||
return $participant;
|
||||
}
|
||||
|
||||
private function createAssembler(ParticipantEligibilityChecker $eligibilityChecker): BookingPricingAssembler
|
||||
{
|
||||
$roomPricingCalculator = new RoomPricingCalculator();
|
||||
@@ -225,8 +318,6 @@ class BookingPricingAssemblerTest extends TestCase
|
||||
return new BookingPricingAssembler(
|
||||
$roomPricingCalculator,
|
||||
$eligibilityChecker,
|
||||
new InsuranceManager(),
|
||||
new ParticipantPricingCalculator($roomPricingCalculator),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user