From c7e0ad67e68aea90a9dd8f15ee9e11ffeb4d8472 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Fromme?= Date: Fri, 16 Oct 2026 09:55:04 +0200 Subject: [PATCH] fix: put the guiding fee on one guide place only --- src/Service/Assignment/AssignmentPlanner.php | 32 +++++++++++-------- .../Command/AssignmentGenerateCommandTest.php | 15 ++++++--- .../StaffingRuleControllerTest.php | 3 +- .../Assignment/AssignmentPlannerTest.php | 24 ++++++++++++-- 4 files changed, 53 insertions(+), 21 deletions(-) diff --git a/src/Service/Assignment/AssignmentPlanner.php b/src/Service/Assignment/AssignmentPlanner.php index 5394ad9..828ad07 100644 --- a/src/Service/Assignment/AssignmentPlanner.php +++ b/src/Service/Assignment/AssignmentPlanner.php @@ -17,11 +17,13 @@ use App\Service\Common\PickupOptions; * - without split, each ski level is a Kursleitung of its own, one place; * - with split, a Reiseleitung takes over each level through the combined profile, one place * per level, and the plain Reiseleitung keeps the remaining places; - * - Guiding is the guiding fee on every guide-type assignment, not an assignment of its own; * - a date with bus travel (boarding points) gets its first boarding point pre-selected as * Busbegleitung on exactly one assignment, the first guide-type one - the plain Reiseleitung * where there is one. Taken from the date, not the matrix: bus travel varies between the - * dates of one product. + * dates of one product; + * - Guiding is the guiding fee on exactly one guide place, not an assignment of its own: the + * bus escort's. Where that assignment has more places, one place is split off to carry + * fee and pickup, so the others are neither guiding nor escorting the bus. */ final class AssignmentPlanner { @@ -64,6 +66,7 @@ final class AssignmentPlanner } $pickup = $this->firstPickup($destination); + $guiding = $rule->isGuiding(); $planned = []; foreach ($roles as [$role, $places]) { @@ -71,26 +74,29 @@ final class AssignmentPlanner continue; } - $fees = []; + $fees = true === $role->isCourse() ? [StaffingFee::COURSE] : []; - if (true === $role->isGuide() && true === $rule->isGuiding()) { - $fees[] = StaffingFee::GUIDING; + if (false === $role->isGuide()) { + $planned[] = new PlannedAssignment($role, $places, null, $fees); + + continue; } - if (true === $role->isCourse()) { - $fees[] = StaffingFee::COURSE; - } + $first = true === $guiding ? 1 : $places; $planned[] = new PlannedAssignment( $role, - $places, - true === $role->isGuide() ? $pickup : null, - $fees, + $first, + $pickup, + true === $guiding ? [StaffingFee::GUIDING, ...$fees] : $fees, ); - if (true === $role->isGuide()) { - $pickup = null; + if ($first < $places) { + $planned[] = new PlannedAssignment($role, $places - $first, null, $fees); } + + $guiding = false; + $pickup = null; } return $planned; diff --git a/tests/Command/AssignmentGenerateCommandTest.php b/tests/Command/AssignmentGenerateCommandTest.php index 090b52a..632304b 100644 --- a/tests/Command/AssignmentGenerateCommandTest.php +++ b/tests/Command/AssignmentGenerateCommandTest.php @@ -47,18 +47,23 @@ class AssignmentGenerateCommandTest extends KernelTestCase $assignments = $this->assignments(); - $this->assertCount(2, $assignments); + $this->assertCount(3, $assignments); - [$guide, $course] = $assignments; + [$guide, $secondGuide, $course] = $assignments; $this->assertSame($destination->getId(), $guide->getDestination()->getId()); $this->assertSame('Reiseleitung', $guide->getJobProfile()->getName()); - $this->assertSame(2, $guide->getAvailableDispositions()); + $this->assertSame(1, $guide->getAvailableDispositions()); $this->assertSame(5, $guide->getPickup()); $this->assertSame(['Skigebietsguiding (pro Einheit)'], $guide->getFees()->map(static fn ($fee) => $fee->getName())->toArray()); $this->assertSame(Assignment::STATUS_DRAFT, $guide->getStatus()); $this->assertSame('system', $guide->getCreatedBy()); + $this->assertSame('Reiseleitung', $secondGuide->getJobProfile()->getName()); + $this->assertSame(1, $secondGuide->getAvailableDispositions()); + $this->assertSame(0, $secondGuide->getPickup()); + $this->assertCount(0, $secondGuide->getFees()); + $this->assertSame('Kursleitung Ski Anfänger:innen', $course->getJobProfile()->getName()); $this->assertSame(0, $course->getPickup()); } @@ -79,7 +84,7 @@ class AssignmentGenerateCommandTest extends KernelTestCase $this->tester->execute([]); $this->assertStringContainsString('davon 1 schon mit Einsätzen', $this->tester->getDisplay()); - $this->assertCount(2, $this->assignments()); + $this->assertCount(3, $this->assignments()); } public function testADryRunWritesNothing(): void @@ -90,7 +95,7 @@ class AssignmentGenerateCommandTest extends KernelTestCase $this->assertSame(Command::SUCCESS, $this->tester->execute(['--dry-run' => true])); - $this->assertStringContainsString('2× Reiseleitung (Busbegleitung)', $this->tester->getDisplay()); + $this->assertStringContainsString('1× Reiseleitung (Busbegleitung)', $this->tester->getDisplay()); $this->assertCount(0, $this->assignments()); } diff --git a/tests/Controller/Admin/System/StaffingRule/StaffingRuleControllerTest.php b/tests/Controller/Admin/System/StaffingRule/StaffingRuleControllerTest.php index db88d63..2f146dc 100644 --- a/tests/Controller/Admin/System/StaffingRule/StaffingRuleControllerTest.php +++ b/tests/Controller/Admin/System/StaffingRule/StaffingRuleControllerTest.php @@ -93,7 +93,8 @@ class StaffingRuleControllerTest extends WebTestCase $this->assertSame(Response::HTTP_OK, $this->client->getResponse()->getStatusCode()); $this->assertStringContainsString('DKIMP110127', $content); $this->assertStringContainsString('Zeile inaktiv', $content); - $this->assertStringContainsString('2× Reiseleitung', $content); + $this->assertSame(2, substr_count($content, '1× Reiseleitung')); + $this->assertSame(1, substr_count($content, 'Skigebietsguiding (pro Einheit)')); $this->assertStringContainsString('Busbegleitung ab Göttingen, 22:00 Uhr', $content); $this->assertStringContainsString('1× Kursleitung Ski Anfänger:innen', $content); $this->assertStringContainsString('Ski-/Snowboardkurs (pro Stunde)', $content); diff --git a/tests/Service/Assignment/AssignmentPlannerTest.php b/tests/Service/Assignment/AssignmentPlannerTest.php index cceb804..e380968 100644 --- a/tests/Service/Assignment/AssignmentPlannerTest.php +++ b/tests/Service/Assignment/AssignmentPlannerTest.php @@ -56,14 +56,34 @@ class AssignmentPlannerTest extends TestCase ], self::summarize($planned)); } - public function testGuidingIsAFeeOnEveryGuideTypeAssignment(): void + public function testGuidingIsAFeeOnTheFirstGuideTypeAssignmentOnly(): void { $planned = $this->plan(self::rule(guides: 3, beginner: true, advanced: true, split: true, guiding: true)); $this->assertSame([ [StaffingRole::GUIDE, 1, null, [StaffingFee::GUIDING]], + [StaffingRole::GUIDE_SKI_BEGINNER, 1, null, [StaffingFee::COURSE]], + [StaffingRole::GUIDE_SKI_ADVANCED, 1, null, [StaffingFee::COURSE]], + ], self::summarize($planned)); + } + + public function testGuidingSplitsOffOnePlaceWhichAlsoEscortsTheBus(): void + { + $planned = $this->plan(self::rule(guides: 3, guiding: true), self::busTravel()); + + $this->assertSame([ + [StaffingRole::GUIDE, 1, 5, [StaffingFee::GUIDING]], + [StaffingRole::GUIDE, 2, null, []], + ], self::summarize($planned)); + } + + public function testWithoutAPlainReiseleitungTheFirstCombinedOneGuides(): void + { + $planned = $this->plan(self::rule(guides: 2, beginner: true, advanced: true, split: true, guiding: true)); + + $this->assertSame([ [StaffingRole::GUIDE_SKI_BEGINNER, 1, null, [StaffingFee::GUIDING, StaffingFee::COURSE]], - [StaffingRole::GUIDE_SKI_ADVANCED, 1, null, [StaffingFee::GUIDING, StaffingFee::COURSE]], + [StaffingRole::GUIDE_SKI_ADVANCED, 1, null, [StaffingFee::COURSE]], ], self::summarize($planned)); }