feat: let the teamer fill in the questionnaire after a bus escort

This commit is contained in:
fromme committed 2026-10-27 16:53:23 +01:00
1 parent df6734c939
commit 2a041d25a0
3 files changed
+166 -4

No files matched your search

+46 -2
View File
@@ -22,9 +22,21 @@ class DispositionVoter extends Voter
public const FEEDBACK = 'FEEDBACK';
public const CALL_OFF = 'CALL_OFF';
public const MANAGE_RECEIPTS = 'MANAGE_RECEIPTS';
public const QUESTIONNAIRE = 'QUESTIONNAIRE';
public function __construct(private readonly Security $security)
{
private readonly ?\DateTimeImmutable $questionnaireGoLive;
/**
* @param string|null $questionnaireGoLive Y-m-d, the services.yaml parameter questionnaire_go_live
*/
public function __construct(
private readonly Security $security,
?string $questionnaireGoLive,
) {
$this->questionnaireGoLive = null === $questionnaireGoLive
? null
: (\DateTimeImmutable::createFromFormat('!Y-m-d', $questionnaireGoLive)
?: throw new \InvalidArgumentException(sprintf('questionnaire_go_live "%s" is not a Y-m-d date', $questionnaireGoLive)));
}
protected function supports(string $attribute, mixed $subject): bool
@@ -44,6 +56,7 @@ class DispositionVoter extends Voter
static::FEEDBACK,
static::CALL_OFF,
static::MANAGE_RECEIPTS,
static::QUESTIONNAIRE,
]);
}
@@ -69,6 +82,7 @@ class DispositionVoter extends Voter
static::CALL_OFF => $this->security->isGranted('ROLE_ADMINISTRATIVE')
&& Disposition::STATUS_CALLED_OFF !== $disposition->getStatus(),
static::MANAGE_RECEIPTS => $this->assertManageReceiptsAllowed($token, $disposition),
static::QUESTIONNAIRE => $this->assertQuestionnaireAllowed($token, $disposition),
default => false,
};
}
@@ -104,6 +118,36 @@ class DispositionVoter extends Voter
], true);
}
/**
* Whether the teamer may fill in, and later read, the questionnaire after this placement
* (config/questionnaires/teamer_escort.yaml). Only the teamer themself: the office reads the
* submitted responses through ROLE_ADMINISTRATIVE, not through this attribute.
*
* "Over" is the destination's end date, not Disposition::STATUS_ENDED: the cron sets that a
* run late, and it moves on to checking_invoice and completed while the date stays true. An
* assignment ending today counts as over, as in DispositionRepository's feedback queries.
*
* Skip-formalities placements are left out, as they are from contract, invoice and feedback.
*/
private function assertQuestionnaireAllowed(TokenInterface $token, Disposition $disposition): bool
{
if (null === $this->questionnaireGoLive || false === $this->assertTeamerAccess($token, $disposition)) {
return false;
}
if (true === $disposition->isSkipFormalities()
|| Disposition::STATUS_CALLED_OFF === $disposition->getStatus()
|| true !== $disposition->getAssignment()?->hasBusEscort()) {
return false;
}
$dateTo = $disposition->getAssignment()->getDestination()?->getDateTo();
return null !== $dateTo
&& $dateTo >= $this->questionnaireGoLive
&& $dateTo <= new \DateTimeImmutable('today');
}
private function assertHouseManagerAccess(TokenInterface $token, Disposition $disposition): bool
{
if (false === $this->security->isGranted('ROLE_HOUSE_MANAGER')) {