diff --git a/.changes/unreleased/Fixed-20260113-162130.yaml b/.changes/unreleased/Fixed-20260113-162130.yaml new file mode 100644 index 000000000..d0919a1af --- /dev/null +++ b/.changes/unreleased/Fixed-20260113-162130.yaml @@ -0,0 +1,7 @@ +kind: Fixed +body: | + Prevent sending a notification when the user sign the document himself +time: 2026-01-13T16:21:30.279454299+01:00 +custom: + Issue: "490" + SchemaChange: No schema change diff --git a/src/Bundle/ChillMainBundle/Tests/Workflow/EventSubscriber/NotificationOnTransitionTest.php b/src/Bundle/ChillMainBundle/Tests/Workflow/EventSubscriber/NotificationOnTransitionTest.php index 9f90c5b12..52f3e4eba 100644 --- a/src/Bundle/ChillMainBundle/Tests/Workflow/EventSubscriber/NotificationOnTransitionTest.php +++ b/src/Bundle/ChillMainBundle/Tests/Workflow/EventSubscriber/NotificationOnTransitionTest.php @@ -15,7 +15,9 @@ use Chill\MainBundle\Entity\Notification; use Chill\MainBundle\Entity\User; use Chill\MainBundle\Entity\UserGroup; use Chill\MainBundle\Entity\Workflow\EntityWorkflow; +use Chill\MainBundle\Entity\Workflow\EntityWorkflowSignatureStateEnum; use Chill\MainBundle\Entity\Workflow\EntityWorkflowStep; +use Chill\MainBundle\Entity\Workflow\EntityWorkflowStepSignature; use Chill\MainBundle\Workflow\EntityWorkflowHandlerInterface; use Chill\MainBundle\Workflow\EntityWorkflowManager; use Chill\MainBundle\Workflow\EventSubscriber\NotificationOnTransition; @@ -87,7 +89,7 @@ final class NotificationOnTransitionTest extends TestCase ->willReturn([]); $registry = $this->prophesize(Registry::class); - $registry->get(Argument::type(EntityWorkflow::class), Argument::type('string')) + $registry->get(Argument::type(EntityWorkflow::class), Argument::any()) ->willReturn($workflow); $security = $this->prophesize(Security::class); @@ -111,4 +113,74 @@ final class NotificationOnTransitionTest extends TestCase $notificationOnTransition->onCompletedSendNotification($event); } + + public function testOnCompleteDoNotSendNotificationIfStepCreatedByPreviousSignature(): void + { + $dest = new User(); + $currentUser = new User(); + $workflowProphecy = $this->prophesize(WorkflowInterface::class); + $workflow = $workflowProphecy->reveal(); + $entityWorkflow = new EntityWorkflow(); + $entityWorkflow + ->setWorkflowName('workflow_name') + ->setRelatedEntityClass(\stdClass::class) + ->setRelatedEntityId(1); + // force an id to entityWorkflow: + $reflection = new \ReflectionClass($entityWorkflow); + $id = $reflection->getProperty('id'); + $id->setValue($entityWorkflow, 1); + + $previousStep = new EntityWorkflowStep(); + $previousStep->addSignature($signature = new EntityWorkflowStepSignature($previousStep, $dest)); + $signature->setState(EntityWorkflowSignatureStateEnum::SIGNED); + + $currentStep = new EntityWorkflowStep(); + $currentStep->addDestUser($dest); + $currentStep->setCurrentStep('to_state'); + + $entityWorkflow->addStep($previousStep); + $entityWorkflow->addStep($currentStep); + + $em = $this->prophesize(EntityManagerInterface::class); + + // we check that NO notification has been persisted for $dest + $em->persist(Argument::that( + fn ($notificationCandidate) => $notificationCandidate instanceof Notification && $notificationCandidate->getAddressees()->contains($dest) + ))->shouldNotBeCalled(); + + $engine = $this->prophesize(\Twig\Environment::class); + $engine->render(Argument::type('string'), Argument::type('array')) + ->willReturn('dummy text'); + + $extractor = $this->prophesize(MetadataExtractor::class); + $extractor->buildArrayPresentationForPlace(Argument::type(EntityWorkflow::class), Argument::any()) + ->willReturn([]); + $extractor->buildArrayPresentationForWorkflow(Argument::any()) + ->willReturn([]); + + $registry = $this->prophesize(Registry::class); + $registry->get(Argument::type(EntityWorkflow::class), Argument::any()) + ->willReturn($workflow); + + $security = $this->prophesize(Security::class); + $security->getUser()->willReturn(null); + + $entityWorkflowHandler = $this->prophesize(EntityWorkflowHandlerInterface::class); + $entityWorkflowHandler->getEntityTitle($entityWorkflow)->willReturn('workflow title'); + $entityWorkflowManager = $this->prophesize(EntityWorkflowManager::class); + $entityWorkflowManager->getHandler($entityWorkflow)->willReturn($entityWorkflowHandler->reveal()); + + $notificationOnTransition = new NotificationOnTransition( + $em->reveal(), + $engine->reveal(), + $extractor->reveal(), + $security->reveal(), + $registry->reveal(), + $entityWorkflowManager->reveal(), + ); + + $event = new Event($entityWorkflow, new Marking(), new Transition('dummy_transition', ['from_state'], ['to_state']), $workflow); + + $notificationOnTransition->onCompletedSendNotification($event); + } } diff --git a/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/NotificationOnTransition.php b/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/NotificationOnTransition.php index 3775869e7..a48107156 100644 --- a/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/NotificationOnTransition.php +++ b/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/NotificationOnTransition.php @@ -103,7 +103,10 @@ class NotificationOnTransition implements EventSubscriberInterface foreach ($dests as $subscriber) { if ( + // prevent to send a notification to the one who created the step $this->security->getUser() === $subscriber + // prevent to send a notification if the user applyied a signature on the previous step + || $this->isStepCreatedByPreviousSignature($entityWorkflow, $subscriber) ) { continue; } @@ -131,4 +134,31 @@ class NotificationOnTransition implements EventSubscriberInterface $this->entityManager->persist($notification); } } + + /** + * Checks if the current step in the workflow was created by a previous signature of the specified user. + * + * This method retrieves the current step of the workflow and its preceding step. It iterates through + * the signatures of the preceding step to verify if the provided user is the signer of any of those + * signatures. Returns true if the user matches any signer; otherwise, returns false. + * + * @param EntityWorkflow $entityWorkflow the workflow entity containing the current step and its details + * @param User $user the user to check against the signatures of the previous step in the workflow + * + * @return bool true if the specified user created the step via a previous signature, false otherwise + */ + private function isStepCreatedByPreviousSignature(EntityWorkflow $entityWorkflow, User $user): bool + { + $step = $entityWorkflow->getCurrentStepChained(); + $previous = $step->getPrevious(); + + + foreach ($previous->getSignatures() as $signature) { + if ($signature->getSigner() === $user) { + return true; + } + } + + return false; + } }