fix: exclude cancelled participants' services from price breakdown
This commit is contained in:
1 parent
553ec901f3
commit
849a19b67a
4 files changed
+74
-4
No files matched your search
@@ -288,7 +288,8 @@ class BookingConfigurator
|
||||
int $participantIndex,
|
||||
ParticipantDto $participant,
|
||||
): bool {
|
||||
if (null === $participant->dateOfBirth) {
|
||||
// A cancelled participant's services are no longer billed, so nothing may be added for them
|
||||
if (true === $participant->isCanceled() || null === $participant->dateOfBirth) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -63,7 +63,8 @@ class BookingPricingAssembler
|
||||
/**
|
||||
* Calculates pricing for all selected services across all participants, grouped by subtype.
|
||||
*
|
||||
* Only includes services from eligible participants (those with available skipasses for their age).
|
||||
* Only includes services from billed participants: not cancelled, and eligible (those with available
|
||||
* skipasses for their age).
|
||||
*
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
@@ -78,7 +79,7 @@ class BookingPricingAssembler
|
||||
$serviceAggregation = [];
|
||||
|
||||
foreach ($participants as $participantIndex => $participant) {
|
||||
if (false === $this->participantEligibilityChecker->isParticipantEligible($bookingDto, $participantIndex)) {
|
||||
if (false === $this->isParticipantBilled($bookingDto, $participantIndex, $participant)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -142,7 +143,7 @@ class BookingPricingAssembler
|
||||
$parkingParticipants = 0;
|
||||
|
||||
foreach ($participants as $participantIndex => $participant) {
|
||||
if (false === $this->participantEligibilityChecker->isParticipantEligible($bookingDto, $participantIndex)) {
|
||||
if (false === $this->isParticipantBilled($bookingDto, $participantIndex, $participant)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -306,6 +307,18 @@ class BookingPricingAssembler
|
||||
return $groupedServices;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the participant's services count towards the breakdown.
|
||||
*
|
||||
* A cancelled participant is billed only through BusPro's Stornokosten surcharge. Their services
|
||||
* are never sent back to BusPro, so counting them would put the breakdown above BusPro's total.
|
||||
*/
|
||||
private function isParticipantBilled(BookingDto $bookingDto, int $participantIndex, ParticipantDto $participant): bool
|
||||
{
|
||||
return false === $participant->isCanceled()
|
||||
&& true === $this->participantEligibilityChecker->isParticipantEligible($bookingDto, $participantIndex);
|
||||
}
|
||||
|
||||
private function getGroupNameForSubtype(string $subType): string
|
||||
{
|
||||
if (true === in_array($subType, Constants::TOKEN_RENTALS, true)) {
|
||||
|
||||
@@ -100,6 +100,27 @@ class BookingConfiguratorBabyTest extends TestCase
|
||||
$this->assertSame('Mandatory Service', $participant->additionalServices[0]->label);
|
||||
}
|
||||
|
||||
public function testPreselectMandatoryServicesSkipsCanceledParticipant(): void
|
||||
{
|
||||
$travel = $this->createTravelWithMandatoryServices();
|
||||
$bookingDto = new BookingDto($travel, 1);
|
||||
|
||||
$participant = new ParticipantDto();
|
||||
$participant->index = 0;
|
||||
$participant->status = 'S';
|
||||
$participant->dateOfBirth = $travel->dateFrom->modify('-25 years');
|
||||
$participant->additionalServices = [];
|
||||
$bookingDto->participants[0] = $participant;
|
||||
|
||||
$this->participantEligibilityService
|
||||
->method('isParticipantEligible')
|
||||
->willReturn(true);
|
||||
|
||||
$this->bookingService()->preselectDefaultServices($bookingDto);
|
||||
|
||||
$this->assertSame([], $participant->additionalServices, 'Mandatory services should not be preselected for cancelled participants');
|
||||
}
|
||||
|
||||
public function testPreselectMandatoryServicesIncludesAdultParticipant(): void
|
||||
{
|
||||
$travel = $this->createTravelWithMandatoryServices();
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\Service;
|
||||
|
||||
use App\BusProNet\Constants;
|
||||
use App\BusProNet\Model\Booking;
|
||||
use App\BusProNet\Model\Insurance;
|
||||
use App\BusProNet\Model\Pickup;
|
||||
@@ -358,6 +359,40 @@ class BookingPricingAssemblerTest extends TestCase
|
||||
$this->assertEqualsWithDelta($group['groupTotal'], $participantTotal, 0.001, 'Summary and per-participant totals must agree');
|
||||
}
|
||||
|
||||
public function testServicePricingExcludesCanceledParticipants(): void
|
||||
{
|
||||
$travel = new Travel();
|
||||
$travel->insurances = [];
|
||||
|
||||
$bookingDto = new BookingDto($travel, 2);
|
||||
$bookingDto->participants = [];
|
||||
|
||||
foreach (['F', 'S'] as $index => $status) {
|
||||
$mandatoryService = new Service();
|
||||
$mandatoryService->id = 1;
|
||||
$mandatoryService->label = 'Kurtaxe';
|
||||
$mandatoryService->subType = Constants::TOKEN_ADDITIONAL;
|
||||
$mandatoryService->mandatory = true;
|
||||
$mandatoryService->price = 12.5;
|
||||
|
||||
$participant = new ParticipantDto();
|
||||
$participant->index = $index;
|
||||
$participant->status = $status;
|
||||
$participant->additionalServices = [$mandatoryService];
|
||||
$participant->transportationOutbound = $this->createTransportationService(203222, 'Bus-Hinfahrt', 'BUS');
|
||||
$participant->pickup = $this->createBoardingPoint(18, 17.9);
|
||||
$bookingDto->participants[$index] = $participant;
|
||||
}
|
||||
|
||||
$servicePricing = $this->assembler->calculateServicePricing($bookingDto);
|
||||
|
||||
$this->assertCount(2, $servicePricing);
|
||||
foreach ($servicePricing as $group) {
|
||||
$this->assertSame(1, $group['services'][0]['participantCount'], $group['groupName'].' counts only the active participant');
|
||||
}
|
||||
$this->assertEqualsWithDelta(12.5 + 17.9, array_sum(array_column($servicePricing, 'groupTotal')), 0.001);
|
||||
}
|
||||
|
||||
public function testSurchargePricingOmitsZeroAmountSurcharges(): void
|
||||
{
|
||||
$booking = new Booking();
|
||||
|
||||
Reference in new issue
Block a user