$this->security->isGranted('ROLE_ADMINISTRATIVE') || $this->assertTeamerAccess($token, $disposition), // The documents themselves do not exist on a skip-formalities assignment, so neither // the blank PDFs nor the upload screens may be reachable for it. static::CONTRACT, static::INVOICE => false === $disposition->isSkipFormalities() && ($this->security->isGranted('ROLE_ADMINISTRATIVE') || $this->assertTeamerAccess($token, $disposition)), static::DELETE => $this->security->isGranted('ROLE_ADMIN'), static::FEEDBACK => false === $disposition->isSkipFormalities() && ($this->security->isGranted('ROLE_ADMINISTRATIVE') || $this->assertHouseManagerAccess($token, $disposition)), static::CONTRACT_SUPPLEMENTARY => $this->assertContractUploadAllowed($disposition), static::ADMIN_DOCUMENT_UPLOAD => $this->assertAdminDocumentUploadAllowed($disposition), static::CALL_OFF => $this->security->isGranted('ROLE_ADMINISTRATIVE') && Disposition::STATUS_CALLED_OFF !== $disposition->getStatus(), static::MANAGE_RECEIPTS => $this->assertManageReceiptsAllowed($token, $disposition), default => false, }; } /** * Whether Belege backing the Honorarnote may be added, listed or deleted. * * This is deliberately a permission and not a workflow transition. The disposition state * machine offers upload_invoice only from "ended", so a teamer whose invoice is being checked * has no transition available at all - which is precisely the phase in which receipts must * stay manageable. Receipts also have to survive the ended -> checking_invoice -> ended * bouncing that upload_invoice and reject_invoice cause, so the window is expressed as the two * places themselves rather than as anything workflow_can() could answer. Do not "tidy" this * into a workflow check. * * Once the invoice is accepted the disposition leaves for "completed" and the receipts are * deleted (see Administrative\Document\CheckController), so the window closes on its own. */ private function assertManageReceiptsAllowed(TokenInterface $token, Disposition $disposition): bool { if (true === $disposition->isSkipFormalities()) { return false; } if (false === $this->security->isGranted('ROLE_ADMINISTRATIVE') && false === $this->assertTeamerAccess($token, $disposition)) { return false; } return in_array($disposition->getStatus(), [ Disposition::STATUS_ENDED, Disposition::STATUS_CHECKING_INVOICE, ], true); } private function assertHouseManagerAccess(TokenInterface $token, Disposition $disposition): bool { if (false === $this->security->isGranted('ROLE_HOUSE_MANAGER')) { return false; } if (Disposition::STATUS_CALLED_OFF === $disposition->getStatus()) { return false; } /** @var User $user */ $user = $token->getUser(); $destination = $disposition ->getAssignment() ->getDestination() ; $isMatchingHotel = $user->hasHotelCodeMatch($destination->getHotelCode()); $isPast = $destination->getDateTo() < new \DateTimeImmutable(); return $isMatchingHotel && $isPast; } private function assertTeamerAccess(TokenInterface $token, Disposition $disposition): bool { if (false === $this->security->isGranted('ROLE_TEAMER')) { return false; } /** @var User $user */ $user = $token->getUser(); return $user->getTeamer() === $disposition->getTeamer(); } private function assertContractUploadAllowed(Disposition $disposition): bool { if (false === $this->security->isGranted('ROLE_ADMINISTRATIVE')) { return false; } if (true === $disposition->isSkipFormalities()) { return false; } if (Disposition::STATUS_CALLED_OFF === $disposition->getStatus()) { return false; } $existingContract = $disposition->getDocumentByType(Upload::TYPE_CONTRACT); return null === $existingContract || Upload::STATUS_REJECTED === $existingContract->getStatus(); } private function assertAdminDocumentUploadAllowed(Disposition $disposition): bool { if (false === $this->security->isGranted('ROLE_ADMINISTRATIVE') && false === $this->security->isGranted('ROLE_ADMIN')) { return false; } if (true === $disposition->isSkipFormalities()) { return false; } return Disposition::STATUS_CALLED_OFF !== $disposition->getStatus(); } }