From 7e182e3d514fa3b7cc97b4f33a01faf28127301a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Fromme?= Date: Tue, 13 Oct 2026 13:53:25 +0200 Subject: [PATCH] feat: per-disposition feedback waiver The overdue-feedback lists are a live query without stored state, so a feedback nobody will provide any more could not be written off. The new disposition.feedback_waived flag takes a placement out of the admin and manager dashboards, the house-manager list and the reminder mail. Statistics still count a waived placement as a missing feedback. The flag has no UI yet and is set by SQL. Co-Authored-By: Claude Opus 5.5 --- migrations/Version20261013120000.php | 36 +++++++++++++++++++ src/Entity/Disposition.php | 20 +++++++++++ src/Repository/DispositionRepository.php | 3 ++ .../Repository/DispositionRepositoryTest.php | 15 ++++++++ 4 files changed, 74 insertions(+) create mode 100644 migrations/Version20261013120000.php diff --git a/migrations/Version20261013120000.php b/migrations/Version20261013120000.php new file mode 100644 index 0000000..b111fdc --- /dev/null +++ b/migrations/Version20261013120000.php @@ -0,0 +1,36 @@ +addSql('ALTER TABLE disposition ADD feedback_waived TINYINT DEFAULT 0 NOT NULL'); + } + + public function down(Schema $schema): void + { + $this->addSql('ALTER TABLE disposition DROP feedback_waived'); + } +} diff --git a/src/Entity/Disposition.php b/src/Entity/Disposition.php index 43f94ac..7b6aceb 100644 --- a/src/Entity/Disposition.php +++ b/src/Entity/Disposition.php @@ -72,6 +72,14 @@ class Disposition implements BlameableEntityInterface, TimestampableEntityInterf #[ORM\Column(type: Types::BOOLEAN)] private bool $syncedWithBusProNet = false; + /** + * The office no longer expects a feedback for this placement, so nothing chases one: not the + * dashboards' overdue lists, not the house-manager list, not the reminder mail. It still + * counts as missing in the statistics. + */ + #[ORM\Column(type: Types::BOOLEAN, options: ['default' => false])] + private bool $feedbackWaived = false; + public function __construct(Application $application) { $this->uuid = Uuid::v4(); @@ -353,4 +361,16 @@ class Disposition implements BlameableEntityInterface, TimestampableEntityInterf return $this; } + + public function isFeedbackWaived(): bool + { + return $this->feedbackWaived; + } + + public function setFeedbackWaived(bool $feedbackWaived): static + { + $this->feedbackWaived = $feedbackWaived; + + return $this; + } } diff --git a/src/Repository/DispositionRepository.php b/src/Repository/DispositionRepository.php index af80504..54cc87c 100644 --- a/src/Repository/DispositionRepository.php +++ b/src/Repository/DispositionRepository.php @@ -225,9 +225,12 @@ class DispositionRepository extends ServiceEntityRepository $qb->expr()->notIn('assignment.status', ':status'), $qb->expr()->lte('destination.dateTo', ':dateTo'), $qb->expr()->isNull('feedback'), + // waived placements are written off: nobody is asked for their feedback any more + $qb->expr()->eq('disposition.feedbackWaived', ':feedbackWaived'), $this->excludesSkipFormalities($qb) )) ->setParameter('status', [Assignment::STATUS_CALLED_OFF, Assignment::STATUS_DELETED]) + ->setParameter('feedbackWaived', false) ; $dateTo = new \DateTimeImmutable(); diff --git a/tests/Repository/DispositionRepositoryTest.php b/tests/Repository/DispositionRepositoryTest.php index bf99024..22cd5bb 100644 --- a/tests/Repository/DispositionRepositoryTest.php +++ b/tests/Repository/DispositionRepositoryTest.php @@ -212,6 +212,21 @@ class DispositionRepositoryTest extends KernelTestCase $this->assertFalse($query->getParameter('skipFormalities')->getValue()); } + /** + * A waived placement is written off by the office. The same query backs every surface that + * chases a feedback, so this is the one place the waiver has to be honoured. + */ + public function testPendingFeedbackSkipsWaivedDispositions(): void + { + $query = $this->repository()->getDispositionsWithPendingFeedbackQuery(); + + $this->assertStringContainsString( + 'disposition.feedbackWaived = :feedbackWaived', + $query->getDQL() + ); + $this->assertFalse($query->getParameter('feedbackWaived')->getValue()); + } + /** * Otherwise placements nobody is expected to rate would be counted as missingFeedbacks and * drag every hotel's percentage down.