diff --git a/src/Bundle/ChillDocStoreBundle/Service/Signature/Driver/BaseSigner/PdfSignedMessageHandler.php b/src/Bundle/ChillDocStoreBundle/Service/Signature/Driver/BaseSigner/PdfSignedMessageHandler.php index 4002b107b..81fb97dbf 100644 --- a/src/Bundle/ChillDocStoreBundle/Service/Signature/Driver/BaseSigner/PdfSignedMessageHandler.php +++ b/src/Bundle/ChillDocStoreBundle/Service/Signature/Driver/BaseSigner/PdfSignedMessageHandler.php @@ -11,6 +11,9 @@ declare(strict_types=1); namespace Chill\DocStoreBundle\Service\Signature\Driver\BaseSigner; +use Chill\DocStoreBundle\Service\StoredObjectManagerInterface; +use Chill\MainBundle\Repository\Workflow\EntityWorkflowStepSignatureRepository; +use Chill\MainBundle\Workflow\EntityWorkflowManager; use Psr\Log\LoggerInterface; use Symfony\Component\Messenger\Handler\MessageHandlerInterface; @@ -23,10 +26,27 @@ final readonly class PdfSignedMessageHandler implements MessageHandlerInterface public function __construct( private LoggerInterface $logger, + private EntityWorkflowManager $entityWorkflowManager, + private StoredObjectManagerInterface $storedObjectManager, + private EntityWorkflowStepSignatureRepository $entityWorkflowStepSignatureRepository, ) {} public function __invoke(PdfSignedMessage $message): void { $this->logger->info(self::P.'a message is received', ['signaturedId' => $message->signatureId]); + + $signature = $this->entityWorkflowStepSignatureRepository->find($message->signatureId); + + if (null === $signature) { + throw new \RuntimeException('no signature found'); + } + + $storedObject = $this->entityWorkflowManager->getAssociatedStoredObject($signature->getStep()->getEntityWorkflow()); + + if (null === $storedObject) { + throw new \RuntimeException('no stored object found'); + } + + $this->storedObjectManager->write($storedObject, $message->content); } } diff --git a/src/Bundle/ChillDocStoreBundle/Tests/Service/Signature/Driver/BaseSigner/PdfSignedMessageHandlerTest.php b/src/Bundle/ChillDocStoreBundle/Tests/Service/Signature/Driver/BaseSigner/PdfSignedMessageHandlerTest.php new file mode 100644 index 000000000..62d50c03f --- /dev/null +++ b/src/Bundle/ChillDocStoreBundle/Tests/Service/Signature/Driver/BaseSigner/PdfSignedMessageHandlerTest.php @@ -0,0 +1,84 @@ +futurePersonSignatures[] = new Person(); + $entityWorkflow->setStep('new_step', $dto, 'new_transition', new \DateTimeImmutable(), new User()); + $step = $entityWorkflow->getCurrentStep(); + $signature = $step->getSignatures()->first(); + + $handler = new PdfSignedMessageHandler( + new NullLogger(), + $this->buildEntityWorkflowManager($storedObject), + $this->buildStoredObjectManager($storedObject, $expectedContent = '1234'), + $this->buildSignatureRepository($signature) + ); + + // we simply call the handler. The mocked StoredObjectManager will check that the "write" method is invoked once + // with the content "1234" + $handler(new PdfSignedMessage(10, $expectedContent)); + } + + private function buildSignatureRepository(EntityWorkflowStepSignature $signature): EntityWorkflowStepSignatureRepository + { + $entityWorkflowStepSignatureRepository = $this->createMock(EntityWorkflowStepSignatureRepository::class); + $entityWorkflowStepSignatureRepository->method('find')->with($this->isType('int'))->willReturn($signature); + + return $entityWorkflowStepSignatureRepository; + } + + private function buildEntityWorkflowManager(?StoredObject $associatedStoredObject): EntityWorkflowManager + { + $entityWorkflowManager = $this->createMock(EntityWorkflowManager::class); + $entityWorkflowManager->method('getAssociatedStoredObject')->willReturn($associatedStoredObject); + + return $entityWorkflowManager; + } + + private function buildStoredObjectManager(StoredObject $expectedStoredObject, string $expectedContent): StoredObjectManagerInterface + { + $storedObjectManager = $this->createMock(StoredObjectManagerInterface::class); + $storedObjectManager->expects($this->once()) + ->method('write') + ->with($this->identicalTo($expectedStoredObject), $expectedContent); + + return $storedObjectManager; + } +} diff --git a/src/Bundle/ChillDocStoreBundle/Workflow/AccompanyingCourseDocumentWorkflowHandler.php b/src/Bundle/ChillDocStoreBundle/Workflow/AccompanyingCourseDocumentWorkflowHandler.php index 59d2718c8..dc5394c9c 100644 --- a/src/Bundle/ChillDocStoreBundle/Workflow/AccompanyingCourseDocumentWorkflowHandler.php +++ b/src/Bundle/ChillDocStoreBundle/Workflow/AccompanyingCourseDocumentWorkflowHandler.php @@ -13,14 +13,18 @@ namespace Chill\DocStoreBundle\Workflow; use Chill\DocStoreBundle\Entity\AccompanyingCourseDocument; use Chill\DocStoreBundle\Repository\AccompanyingCourseDocumentRepository; +use Chill\DocStoreBundle\Entity\StoredObject; use Chill\DocStoreBundle\Security\Authorization\AccompanyingCourseDocumentVoter; use Chill\MainBundle\Entity\Workflow\EntityWorkflow; use Chill\MainBundle\Repository\Workflow\EntityWorkflowRepository; -use Chill\MainBundle\Workflow\EntityWorkflowHandlerInterface; +use Chill\MainBundle\Workflow\EntityWorkflowWithStoredObjectHandlerInterface; use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation; use Symfony\Contracts\Translation\TranslatorInterface; -readonly class AccompanyingCourseDocumentWorkflowHandler implements EntityWorkflowHandlerInterface +/** + * @implements EntityWorkflowWithStoredObjectHandlerInterface + */ +readonly class AccompanyingCourseDocumentWorkflowHandler implements EntityWorkflowWithStoredObjectHandlerInterface { public function __construct( private TranslatorInterface $translator, @@ -67,8 +71,6 @@ readonly class AccompanyingCourseDocumentWorkflowHandler implements EntityWorkfl } /** - * @param AccompanyingCourseDocument $object - * * @return array[] */ public function getRelatedObjects(object $object): array @@ -116,6 +118,11 @@ readonly class AccompanyingCourseDocumentWorkflowHandler implements EntityWorkfl return AccompanyingCourseDocument::class === $entityWorkflow->getRelatedEntityClass(); } + public function getAssociatedStoredObject(EntityWorkflow $entityWorkflow): ?StoredObject + { + return $this->getRelatedEntity($entityWorkflow)?->getObject(); + } + public function supportsFreeze(EntityWorkflow $entityWorkflow, array $options = []): bool { return false; diff --git a/src/Bundle/ChillMainBundle/Controller/WorkflowController.php b/src/Bundle/ChillMainBundle/Controller/WorkflowController.php index 3a8626f26..6898d87e4 100644 --- a/src/Bundle/ChillMainBundle/Controller/WorkflowController.php +++ b/src/Bundle/ChillMainBundle/Controller/WorkflowController.php @@ -25,6 +25,7 @@ use Chill\MainBundle\Workflow\EntityWorkflowManager; use Chill\MainBundle\Workflow\WorkflowTransitionContextDTO; use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\Clock\ClockInterface; use Symfony\Component\Form\Extension\Core\Type\FormType; use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\HttpFoundation\Request; @@ -39,7 +40,18 @@ use Symfony\Contracts\Translation\TranslatorInterface; class WorkflowController extends AbstractController { - public function __construct(private readonly EntityWorkflowManager $entityWorkflowManager, private readonly EntityWorkflowRepository $entityWorkflowRepository, private readonly ValidatorInterface $validator, private readonly PaginatorFactory $paginatorFactory, private readonly Registry $registry, private readonly EntityManagerInterface $entityManager, private readonly TranslatorInterface $translator, private readonly ChillSecurity $security, private readonly \Doctrine\Persistence\ManagerRegistry $managerRegistry) {} + public function __construct( + private readonly EntityWorkflowManager $entityWorkflowManager, + private readonly EntityWorkflowRepository $entityWorkflowRepository, + private readonly ValidatorInterface $validator, + private readonly PaginatorFactory $paginatorFactory, + private readonly Registry $registry, + private readonly EntityManagerInterface $entityManager, + private readonly TranslatorInterface $translator, + private readonly ChillSecurity $security, + private readonly \Doctrine\Persistence\ManagerRegistry $managerRegistry, + private readonly ClockInterface $clock, + ) {} #[Route(path: '/{_locale}/main/workflow/create', name: 'chill_main_workflow_create')] public function create(Request $request): Response @@ -310,7 +322,14 @@ class WorkflowController extends AbstractController throw $this->createAccessDeniedException(sprintf("not allowed to apply transition {$transition}: %s", implode(', ', $msgs))); } - $workflow->apply($entityWorkflow, $transition, ['context' => $stepDTO]); + $byUser = $this->security->getUser(); + + $workflow->apply($entityWorkflow, $transition, [ + 'context' => $stepDTO, + 'byUser' => $byUser, + 'transition' => $transition, + 'transitionAt' => $this->clock->now(), + ]); $this->entityManager->flush(); diff --git a/src/Bundle/ChillMainBundle/Entity/Workflow/EntityWorkflow.php b/src/Bundle/ChillMainBundle/Entity/Workflow/EntityWorkflow.php index 9c314115e..eb187cd39 100644 --- a/src/Bundle/ChillMainBundle/Entity/Workflow/EntityWorkflow.php +++ b/src/Bundle/ChillMainBundle/Entity/Workflow/EntityWorkflow.php @@ -413,8 +413,20 @@ class EntityWorkflow implements TrackCreationInterface, TrackUpdateInterface * * @return $this */ - public function setStep(string $step, WorkflowTransitionContextDTO $transitionContextDTO): self - { + public function setStep( + string $step, + WorkflowTransitionContextDTO $transitionContextDTO, + string $transition, + \DateTimeImmutable $transitionAt, + ?User $byUser = null + ): self { + $previousStep = $this->getCurrentStep(); + + $previousStep + ->setTransitionAfter($transition) + ->setTransitionAt($transitionAt) + ->setTransitionBy($byUser); + $newStep = new EntityWorkflowStep(); $newStep->setCurrentStep($step); @@ -430,6 +442,14 @@ class EntityWorkflow implements TrackCreationInterface, TrackUpdateInterface $newStep->addDestEmail($email); } + if (null !== $transitionContextDTO->futureUserSignature) { + new EntityWorkflowStepSignature($newStep, $transitionContextDTO->futureUserSignature); + } else { + foreach ($transitionContextDTO->futurePersonSignatures as $personSignature) { + new EntityWorkflowStepSignature($newStep, $personSignature); + } + } + // copy the freeze if ($this->isFreeze()) { $newStep->setFreezeAfter(true); diff --git a/src/Bundle/ChillMainBundle/Security/Authorization/WorkflowEntityDeletionVoter.php b/src/Bundle/ChillMainBundle/Security/Authorization/WorkflowEntityDeletionVoter.php index 9718cf013..3444a57a6 100644 --- a/src/Bundle/ChillMainBundle/Security/Authorization/WorkflowEntityDeletionVoter.php +++ b/src/Bundle/ChillMainBundle/Security/Authorization/WorkflowEntityDeletionVoter.php @@ -12,13 +12,14 @@ declare(strict_types=1); namespace Chill\MainBundle\Security\Authorization; use Chill\MainBundle\Repository\Workflow\EntityWorkflowRepository; +use Chill\MainBundle\Workflow\EntityWorkflowHandlerInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authorization\Voter\Voter; class WorkflowEntityDeletionVoter extends Voter { /** - * @param \Chill\MainBundle\Workflow\EntityWorkflowHandlerInterface[] $handlers + * @param EntityWorkflowHandlerInterface[] $handlers */ public function __construct(private $handlers, private readonly EntityWorkflowRepository $entityWorkflowRepository) {} @@ -30,7 +31,7 @@ class WorkflowEntityDeletionVoter extends Voter foreach ($this->handlers as $handler) { if ($handler->isObjectSupported($subject) - && \in_array($attribute, $handler->getDeletionRoles($subject), true)) { + && \in_array($attribute, $handler->getDeletionRoles(), true)) { return true; } } diff --git a/src/Bundle/ChillMainBundle/Tests/Entity/Workflow/EntityWorkflowTest.php b/src/Bundle/ChillMainBundle/Tests/Entity/Workflow/EntityWorkflowTest.php index ec5fa6ae2..4eb56f995 100644 --- a/src/Bundle/ChillMainBundle/Tests/Entity/Workflow/EntityWorkflowTest.php +++ b/src/Bundle/ChillMainBundle/Tests/Entity/Workflow/EntityWorkflowTest.php @@ -11,8 +11,11 @@ declare(strict_types=1); namespace Chill\MainBundle\Tests\Entity\Workflow; +use Chill\MainBundle\Entity\User; use Chill\MainBundle\Entity\Workflow\EntityWorkflow; +use Chill\MainBundle\Entity\Workflow\EntityWorkflowStepSignature; use Chill\MainBundle\Workflow\WorkflowTransitionContextDTO; +use Chill\PersonBundle\Entity\Person; use PHPUnit\Framework\TestCase; /** @@ -26,7 +29,7 @@ final class EntityWorkflowTest extends TestCase { $entityWorkflow = new EntityWorkflow(); - $entityWorkflow->setStep('final', new WorkflowTransitionContextDTO($entityWorkflow)); + $entityWorkflow->setStep('final', new WorkflowTransitionContextDTO($entityWorkflow), 'finalize', new \DateTimeImmutable()); $entityWorkflow->getCurrentStep()->setIsFinal(true); $this->assertTrue($entityWorkflow->isFinal()); @@ -38,16 +41,16 @@ final class EntityWorkflowTest extends TestCase $this->assertFalse($entityWorkflow->isFinal()); - $entityWorkflow->setStep('two', new WorkflowTransitionContextDTO($entityWorkflow)); + $entityWorkflow->setStep('two', new WorkflowTransitionContextDTO($entityWorkflow), 'two', new \DateTimeImmutable()); $this->assertFalse($entityWorkflow->isFinal()); - $entityWorkflow->setStep('previous_final', new WorkflowTransitionContextDTO($entityWorkflow)); + $entityWorkflow->setStep('previous_final', new WorkflowTransitionContextDTO($entityWorkflow), 'three', new \DateTimeImmutable()); $this->assertFalse($entityWorkflow->isFinal()); $entityWorkflow->getCurrentStep()->setIsFinal(true); - $entityWorkflow->setStep('final', new WorkflowTransitionContextDTO($entityWorkflow)); + $entityWorkflow->setStep('final', new WorkflowTransitionContextDTO($entityWorkflow), 'four', new \DateTimeImmutable()); $this->assertTrue($entityWorkflow->isFinal()); } @@ -58,23 +61,81 @@ final class EntityWorkflowTest extends TestCase $this->assertFalse($entityWorkflow->isFreeze()); - $entityWorkflow->setStep('step_one', new WorkflowTransitionContextDTO($entityWorkflow)); + $entityWorkflow->setStep('step_one', new WorkflowTransitionContextDTO($entityWorkflow), 'to_step_one', new \DateTimeImmutable()); $this->assertFalse($entityWorkflow->isFreeze()); - $entityWorkflow->setStep('step_three', new WorkflowTransitionContextDTO($entityWorkflow)); + $entityWorkflow->setStep('step_three', new WorkflowTransitionContextDTO($entityWorkflow), 'to_step_three', new \DateTimeImmutable()); $this->assertFalse($entityWorkflow->isFreeze()); - $entityWorkflow->setStep('freezed', new WorkflowTransitionContextDTO($entityWorkflow)); + $entityWorkflow->setStep('freezed', new WorkflowTransitionContextDTO($entityWorkflow), 'to_freezed', new \DateTimeImmutable()); $entityWorkflow->getCurrentStep()->setFreezeAfter(true); $this->assertTrue($entityWorkflow->isFreeze()); - $entityWorkflow->setStep('after_freeze', new WorkflowTransitionContextDTO($entityWorkflow)); + $entityWorkflow->setStep('after_freeze', new WorkflowTransitionContextDTO($entityWorkflow), 'to_after_freeze', new \DateTimeImmutable()); $this->assertTrue($entityWorkflow->isFreeze()); $this->assertTrue($entityWorkflow->getCurrentStep()->isFreezeAfter()); } + + public function testPreviousStepMetadataAreFilled() + { + $entityWorkflow = new EntityWorkflow(); + $initialStep = $entityWorkflow->getCurrentStep(); + + $entityWorkflow->setStep('step_one', new WorkflowTransitionContextDTO($entityWorkflow), 'to_step_one', new \DateTimeImmutable('2024-01-01'), $user1 = new User()); + + $previous = $entityWorkflow->getCurrentStep(); + + $entityWorkflow->setStep('step_one', new WorkflowTransitionContextDTO($entityWorkflow), 'to_step_two', new \DateTimeImmutable('2024-01-02'), $user2 = new User()); + + $final = $entityWorkflow->getCurrentStep(); + + $stepsChained = $entityWorkflow->getStepsChained(); + + self::assertCount(3, $stepsChained); + self::assertSame($initialStep, $stepsChained[0]); + self::assertSame($previous, $stepsChained[1]); + self::assertSame($final, $stepsChained[2]); + self::assertEquals($user1, $initialStep->getTransitionBy()); + self::assertEquals('2024-01-01', $initialStep->getTransitionAt()?->format('Y-m-d')); + self::assertEquals('to_step_one', $initialStep->getTransitionAfter()); + self::assertEquals($user2, $previous->getTransitionBy()); + self::assertEquals('2024-01-02', $previous->getTransitionAt()?->format('Y-m-d')); + self::assertEquals('to_step_two', $previous->getTransitionAfter()); + } + + public function testSetStepSignatureForUserIsCreated() + { + $entityWorkflow = new EntityWorkflow(); + $dto = new WorkflowTransitionContextDTO($entityWorkflow); + $dto->futureUserSignature = $user = new User(); + + $entityWorkflow->setStep('new', $dto, 'to_new', new \DateTimeImmutable()); + + $actual = $entityWorkflow->getCurrentStep(); + + self::assertCount(1, $actual->getSignatures()); + self::assertSame($user, $actual->getSignatures()->first()->getSigner()); + } + + public function testSetStepSignatureForPersonIsCreated() + { + $entityWorkflow = new EntityWorkflow(); + $dto = new WorkflowTransitionContextDTO($entityWorkflow); + $dto->futurePersonSignatures[] = $person1 = new Person(); + $dto->futurePersonSignatures[] = $person2 = new Person(); + + $entityWorkflow->setStep('new', $dto, 'to_new', new \DateTimeImmutable()); + + $actual = $entityWorkflow->getCurrentStep(); + $persons = $actual->getSignatures()->map(fn (EntityWorkflowStepSignature $signature) => $signature->getSigner()); + + self::assertCount(2, $actual->getSignatures()); + self::assertContains($person1, $persons); + self::assertContains($person2, $persons); + } } diff --git a/src/Bundle/ChillMainBundle/Tests/Workflow/EntityWorkflowMarkingStoreTest.php b/src/Bundle/ChillMainBundle/Tests/Workflow/EntityWorkflowMarkingStoreTest.php index a32cefb09..4922cab17 100644 --- a/src/Bundle/ChillMainBundle/Tests/Workflow/EntityWorkflowMarkingStoreTest.php +++ b/src/Bundle/ChillMainBundle/Tests/Workflow/EntityWorkflowMarkingStoreTest.php @@ -39,19 +39,29 @@ class EntityWorkflowMarkingStoreTest extends TestCase { $markingStore = $this->buildMarkingStore(); $workflow = new EntityWorkflow(); + $previousStep = $workflow->getCurrentStep(); $dto = new WorkflowTransitionContextDTO($workflow); $dto->futureCcUsers[] = $user1 = new User(); $dto->futureDestUsers[] = $user2 = new User(); $dto->futureDestEmails[] = $email = 'test@example.com'; - $markingStore->setMarking($workflow, new Marking(['foo' => 1]), ['context' => $dto]); + $markingStore->setMarking($workflow, new Marking(['foo' => 1]), [ + 'context' => $dto, + 'transition' => 'bar_transition', + 'byUser' => $user3 = new User(), + 'transitionAt' => $at = new \DateTimeImmutable(), + ]); $currentStep = $workflow->getCurrentStep(); self::assertEquals('foo', $currentStep->getCurrentStep()); self::assertContains($email, $currentStep->getDestEmail()); self::assertContains($user1, $currentStep->getCcUser()); self::assertContains($user2, $currentStep->getDestUser()); + + self::assertSame($user3, $previousStep->getTransitionBy()); + self::assertSame($at, $previousStep->getTransitionAt()); + self::assertEquals('bar_transition', $previousStep->getTransitionAfter()); } private function buildMarkingStore(): EntityWorkflowMarkingStore diff --git a/src/Bundle/ChillMainBundle/Workflow/EntityWorkflowHandlerInterface.php b/src/Bundle/ChillMainBundle/Workflow/EntityWorkflowHandlerInterface.php index ebe412587..c0d6469f6 100644 --- a/src/Bundle/ChillMainBundle/Workflow/EntityWorkflowHandlerInterface.php +++ b/src/Bundle/ChillMainBundle/Workflow/EntityWorkflowHandlerInterface.php @@ -14,6 +14,9 @@ namespace Chill\MainBundle\Workflow; use Chill\MainBundle\Entity\User; use Chill\MainBundle\Entity\Workflow\EntityWorkflow; +/** + * @template T of object + */ interface EntityWorkflowHandlerInterface { /** @@ -25,6 +28,9 @@ interface EntityWorkflowHandlerInterface public function getEntityTitle(EntityWorkflow $entityWorkflow, array $options = []): string; + /** + * @return T|null + */ public function getRelatedEntity(EntityWorkflow $entityWorkflow): ?object; public function getRelatedObjects(object $object): array; diff --git a/src/Bundle/ChillMainBundle/Workflow/EntityWorkflowManager.php b/src/Bundle/ChillMainBundle/Workflow/EntityWorkflowManager.php index a5b9b6a23..9f7b54ccd 100644 --- a/src/Bundle/ChillMainBundle/Workflow/EntityWorkflowManager.php +++ b/src/Bundle/ChillMainBundle/Workflow/EntityWorkflowManager.php @@ -11,6 +11,7 @@ declare(strict_types=1); namespace Chill\MainBundle\Workflow; +use Chill\DocStoreBundle\Entity\StoredObject; use Chill\MainBundle\Entity\Workflow\EntityWorkflow; use Chill\MainBundle\Workflow\Exception\HandlerNotFoundException; use Symfony\Component\Workflow\Registry; @@ -38,6 +39,17 @@ class EntityWorkflowManager return $this->registry->all($entityWorkflow); } + public function getAssociatedStoredObject(EntityWorkflow $entityWorkflow): ?StoredObject + { + foreach ($this->handlers as $handler) { + if ($handler instanceof EntityWorkflowWithStoredObjectHandlerInterface && $handler->supports($entityWorkflow)) { + return $handler->getAssociatedStoredObject($entityWorkflow); + } + } + + return null; + } + /** * @return list */ diff --git a/src/Bundle/ChillMainBundle/Workflow/EntityWorkflowMarkingStore.php b/src/Bundle/ChillMainBundle/Workflow/EntityWorkflowMarkingStore.php index dca929e86..ee07d7a62 100644 --- a/src/Bundle/ChillMainBundle/Workflow/EntityWorkflowMarkingStore.php +++ b/src/Bundle/ChillMainBundle/Workflow/EntityWorkflowMarkingStore.php @@ -40,10 +40,14 @@ final readonly class EntityWorkflowMarkingStore implements MarkingStoreInterface $next = array_keys($places)[0]; $transitionDTO = $context['context'] ?? null; + $transition = $context['transition']; + $byUser = $context['byUser'] ?? null; + $at = $context['transitionAt']; + if (!$transitionDTO instanceof WorkflowTransitionContextDTO) { throw new \UnexpectedValueException(sprintf('Expected instance of %s', WorkflowTransitionContextDTO::class)); } - $subject->setStep($next, $transitionDTO); + $subject->setStep($next, $transitionDTO, $transition, $at, $byUser); } } diff --git a/src/Bundle/ChillMainBundle/Workflow/EntityWorkflowWithStoredObjectHandlerInterface.php b/src/Bundle/ChillMainBundle/Workflow/EntityWorkflowWithStoredObjectHandlerInterface.php new file mode 100644 index 000000000..a1c7561cd --- /dev/null +++ b/src/Bundle/ChillMainBundle/Workflow/EntityWorkflowWithStoredObjectHandlerInterface.php @@ -0,0 +1,27 @@ + + */ +interface EntityWorkflowWithStoredObjectHandlerInterface extends EntityWorkflowHandlerInterface +{ + public function getAssociatedStoredObject(EntityWorkflow $entityWorkflow): ?StoredObject; +} diff --git a/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/EntityWorkflowTransitionEventSubscriber.php b/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/EntityWorkflowTransitionEventSubscriber.php index ce33ea6d0..9c74c861c 100644 --- a/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/EntityWorkflowTransitionEventSubscriber.php +++ b/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/EntityWorkflowTransitionEventSubscriber.php @@ -108,12 +108,6 @@ final readonly class EntityWorkflowTransitionEventSubscriber implements EventSub /** @var EntityWorkflow $entityWorkflow */ $entityWorkflow = $event->getSubject(); - $step = $entityWorkflow->getCurrentStep(); - - $step - ->setTransitionAfter($event->getTransition()->getName()) - ->setTransitionAt(new \DateTimeImmutable('now')) - ->setTransitionBy($this->security->getUser()); $this->chillLogger->info('[workflow] apply transition on entityWorkflow', [ 'relatedEntityClass' => $entityWorkflow->getRelatedEntityClass(), diff --git a/src/Bundle/ChillMainBundle/Workflow/WorkflowTransitionContextDTO.php b/src/Bundle/ChillMainBundle/Workflow/WorkflowTransitionContextDTO.php index 2a8253523..88bb0fa76 100644 --- a/src/Bundle/ChillMainBundle/Workflow/WorkflowTransitionContextDTO.php +++ b/src/Bundle/ChillMainBundle/Workflow/WorkflowTransitionContextDTO.php @@ -13,6 +13,7 @@ namespace Chill\MainBundle\Workflow; use Chill\MainBundle\Entity\User; use Chill\MainBundle\Entity\Workflow\EntityWorkflow; +use Chill\PersonBundle\Entity\Person; use Symfony\Component\Validator\Constraints as Assert; use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Component\Workflow\Transition; @@ -51,6 +52,18 @@ class WorkflowTransitionContextDTO */ public array $futureDestEmails = []; + /** + * A list of future @see{Person} with will sign the next step. + * + * @var list + */ + public array $futurePersonSignatures = []; + + /** + * An eventual user which is requested to apply a signature. + */ + public ?User $futureUserSignature = null; + public ?Transition $transition = null; public string $comment = ''; diff --git a/src/Bundle/ChillPersonBundle/Workflow/AccompanyingPeriodWorkEvaluationDocumentWorkflowHandler.php b/src/Bundle/ChillPersonBundle/Workflow/AccompanyingPeriodWorkEvaluationDocumentWorkflowHandler.php index cc59a509b..c987ea17e 100644 --- a/src/Bundle/ChillPersonBundle/Workflow/AccompanyingPeriodWorkEvaluationDocumentWorkflowHandler.php +++ b/src/Bundle/ChillPersonBundle/Workflow/AccompanyingPeriodWorkEvaluationDocumentWorkflowHandler.php @@ -11,16 +11,20 @@ declare(strict_types=1); namespace Chill\PersonBundle\Workflow; +use Chill\DocStoreBundle\Entity\StoredObject; use Chill\MainBundle\Entity\Workflow\EntityWorkflow; use Chill\MainBundle\Repository\Workflow\EntityWorkflowRepository; use Chill\MainBundle\Templating\TranslatableStringHelperInterface; -use Chill\MainBundle\Workflow\EntityWorkflowHandlerInterface; +use Chill\MainBundle\Workflow\EntityWorkflowWithStoredObjectHandlerInterface; use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument; use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocumentRepository; use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodWorkEvaluationDocumentVoter; use Symfony\Contracts\Translation\TranslatorInterface; -class AccompanyingPeriodWorkEvaluationDocumentWorkflowHandler implements EntityWorkflowHandlerInterface +/** + * @implements EntityWorkflowWithStoredObjectHandlerInterface + */ +class AccompanyingPeriodWorkEvaluationDocumentWorkflowHandler implements EntityWorkflowWithStoredObjectHandlerInterface { public function __construct( private readonly AccompanyingPeriodWorkEvaluationDocumentRepository $repository, @@ -73,8 +77,6 @@ class AccompanyingPeriodWorkEvaluationDocumentWorkflowHandler implements EntityW } /** - * @param AccompanyingPeriodWorkEvaluationDocument $object - * * @return array[] */ public function getRelatedObjects(object $object): array @@ -130,6 +132,11 @@ class AccompanyingPeriodWorkEvaluationDocumentWorkflowHandler implements EntityW return AccompanyingPeriodWorkEvaluationDocument::class === $entityWorkflow->getRelatedEntityClass(); } + public function getAssociatedStoredObject(EntityWorkflow $entityWorkflow): ?StoredObject + { + return $this->getRelatedEntity($entityWorkflow)?->getStoredObject(); + } + public function supportsFreeze(EntityWorkflow $entityWorkflow, array $options = []): bool { return false; diff --git a/src/Bundle/ChillPersonBundle/Workflow/AccompanyingPeriodWorkEvaluationWorkflowHandler.php b/src/Bundle/ChillPersonBundle/Workflow/AccompanyingPeriodWorkEvaluationWorkflowHandler.php index a9897f9bd..11b47a684 100644 --- a/src/Bundle/ChillPersonBundle/Workflow/AccompanyingPeriodWorkEvaluationWorkflowHandler.php +++ b/src/Bundle/ChillPersonBundle/Workflow/AccompanyingPeriodWorkEvaluationWorkflowHandler.php @@ -21,6 +21,9 @@ use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkEvalu use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodWorkEvaluationVoter; use Symfony\Contracts\Translation\TranslatorInterface; +/** + * @implements EntityWorkflowHandlerInterface + */ readonly class AccompanyingPeriodWorkEvaluationWorkflowHandler implements EntityWorkflowHandlerInterface { public function __construct( @@ -59,9 +62,6 @@ readonly class AccompanyingPeriodWorkEvaluationWorkflowHandler implements Entity return $this->repository->find($entityWorkflow->getRelatedEntityId()); } - /** - * @param AccompanyingPeriodWorkEvaluation $object - */ public function getRelatedObjects(object $object): array { $relateds = []; diff --git a/src/Bundle/ChillPersonBundle/Workflow/AccompanyingPeriodWorkWorkflowHandler.php b/src/Bundle/ChillPersonBundle/Workflow/AccompanyingPeriodWorkWorkflowHandler.php index cb9462b9e..6bed52cfb 100644 --- a/src/Bundle/ChillPersonBundle/Workflow/AccompanyingPeriodWorkWorkflowHandler.php +++ b/src/Bundle/ChillPersonBundle/Workflow/AccompanyingPeriodWorkWorkflowHandler.php @@ -22,13 +22,16 @@ use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkRepos use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodWorkVoter; use Symfony\Contracts\Translation\TranslatorInterface; -class AccompanyingPeriodWorkWorkflowHandler implements EntityWorkflowHandlerInterface +/** + * @implements EntityWorkflowHandlerInterface + */ +readonly class AccompanyingPeriodWorkWorkflowHandler implements EntityWorkflowHandlerInterface { public function __construct( - private readonly AccompanyingPeriodWorkRepository $repository, - private readonly EntityWorkflowRepository $workflowRepository, - private readonly TranslatableStringHelperInterface $translatableStringHelper, - private readonly TranslatorInterface $translator + private AccompanyingPeriodWorkRepository $repository, + private EntityWorkflowRepository $workflowRepository, + private TranslatableStringHelperInterface $translatableStringHelper, + private TranslatorInterface $translator ) {} public function getDeletionRoles(): array @@ -61,9 +64,6 @@ class AccompanyingPeriodWorkWorkflowHandler implements EntityWorkflowHandlerInte return $this->repository->find($entityWorkflow->getRelatedEntityId()); } - /** - * @param AccompanyingPeriodWork $object - */ public function getRelatedObjects(object $object): array { $relateds = [];