From 7cd638c5fcde9343a3b221da2492c15af7dc9e6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Fri, 4 Oct 2024 10:25:18 +0200 Subject: [PATCH] Add TransitionHasDestineeIfIsSentExternal validator This commit introduces a new validator to ensure that transitions marked as 'sent' have a designated external recipient. It includes related tests for scenarios with and without recipients and covers integration with the workflow context. --- .../ChillMainBundle/Form/WorkflowStepType.php | 1 - ...sDestineeIfIsSentExternalValidatorTest.php | 181 ++++++++++++++++++ .../TransitionHasDestineeIfIsSentExternal.php | 31 +++ ...onHasDestineeIfIsSentExternalValidator.php | 70 +++++++ .../Workflow/WorkflowTransitionContextDTO.php | 2 + .../translations/validators.fr.yml | 2 + 6 files changed, 286 insertions(+), 1 deletion(-) create mode 100644 src/Bundle/ChillMainBundle/Tests/Workflow/Validator/TransitionHasDestineeIfIsSentExternalValidatorTest.php create mode 100644 src/Bundle/ChillMainBundle/Workflow/Validator/TransitionHasDestineeIfIsSentExternal.php create mode 100644 src/Bundle/ChillMainBundle/Workflow/Validator/TransitionHasDestineeIfIsSentExternalValidator.php diff --git a/src/Bundle/ChillMainBundle/Form/WorkflowStepType.php b/src/Bundle/ChillMainBundle/Form/WorkflowStepType.php index 42613ca54..0b63adf01 100644 --- a/src/Bundle/ChillMainBundle/Form/WorkflowStepType.php +++ b/src/Bundle/ChillMainBundle/Form/WorkflowStepType.php @@ -86,7 +86,6 @@ class WorkflowStepType extends AbstractType $builder ->add('transition', ChoiceType::class, [ 'label' => 'workflow.Next step', - 'mapped' => false, 'multiple' => false, 'expanded' => true, 'choices' => $choices, diff --git a/src/Bundle/ChillMainBundle/Tests/Workflow/Validator/TransitionHasDestineeIfIsSentExternalValidatorTest.php b/src/Bundle/ChillMainBundle/Tests/Workflow/Validator/TransitionHasDestineeIfIsSentExternalValidatorTest.php new file mode 100644 index 000000000..1f5fe4444 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Tests/Workflow/Validator/TransitionHasDestineeIfIsSentExternalValidatorTest.php @@ -0,0 +1,181 @@ +transitionToSent = new Transition('send', 'initial', 'sent'), + $this->transitionToNotSent = new Transition('notSend', 'initial', 'notSent'), + ] + ); + $builder + ->setInitialPlaces('initial') + ->setMetadataStore(new InMemoryMetadataStore( + placesMetadata: [ + 'sent' => ['isSentExternal' => true], + ] + )) + ; + + $workflow = new Workflow($builder->build(), name: 'dummy'); + $registry = new Registry(); + $registry->addWorkflow($workflow, new class () implements WorkflowSupportStrategyInterface { + public function supports(WorkflowInterface $workflow, object $subject): bool + { + return true; + } + }); + + return $registry; + } + + public function testToSentPlaceWithoutDestineeAddViolation(): void + { + $entityWorkflow = new EntityWorkflow(); + $entityWorkflow->setWorkflowName('dummy'); + + $dto = new WorkflowTransitionContextDTO($entityWorkflow); + $dto->transition = $this->transitionToSent; + + $constraint = new TransitionHasDestineeIfIsSentExternal(); + $constraint->messageDestineeRequired = 'validation_message'; + + $this->validator->validate($dto, $constraint); + + self::buildViolation('validation_message') + ->setCode('d78ea142-819d-11ef-a459-b7009a3e4caf') + ->atPath('property.path.futureDestineeThirdParties') + ->assertRaised(); + } + + public function testToSentPlaceWithDestineeThirdPartyDoesNotAddViolation(): void + { + $entityWorkflow = new EntityWorkflow(); + $entityWorkflow->setWorkflowName('dummy'); + + $dto = new WorkflowTransitionContextDTO($entityWorkflow); + $dto->transition = $this->transitionToSent; + $dto->futureDestineeThirdParties = [new ThirdParty()]; + + $constraint = new TransitionHasDestineeIfIsSentExternal(); + $constraint->messageDestineeRequired = 'validation_message'; + + $this->validator->validate($dto, $constraint); + + self::assertNoViolation(); + } + + public function testToSentPlaceWithDestineeEmailDoesNotAddViolation(): void + { + $entityWorkflow = new EntityWorkflow(); + $entityWorkflow->setWorkflowName('dummy'); + + $dto = new WorkflowTransitionContextDTO($entityWorkflow); + $dto->transition = $this->transitionToSent; + $dto->futureDestineeEmails = ['test@example.com']; + + $constraint = new TransitionHasDestineeIfIsSentExternal(); + $constraint->messageDestineeRequired = 'validation_message'; + + $this->validator->validate($dto, $constraint); + + self::assertNoViolation(); + } + + public function testToNoSentPlaceWithNoDestineesDoesNotAddViolation(): void + { + $entityWorkflow = new EntityWorkflow(); + $entityWorkflow->setWorkflowName('dummy'); + + $dto = new WorkflowTransitionContextDTO($entityWorkflow); + $dto->transition = $this->transitionToNotSent; + + $constraint = new TransitionHasDestineeIfIsSentExternal(); + $constraint->messageDestineeRequired = 'validation_message'; + + $this->validator->validate($dto, $constraint); + + self::assertNoViolation(); + } + + public function testToNoSentPlaceWithDestineeThirdPartyAddViolation(): void + { + $entityWorkflow = new EntityWorkflow(); + $entityWorkflow->setWorkflowName('dummy'); + + $dto = new WorkflowTransitionContextDTO($entityWorkflow); + $dto->transition = $this->transitionToNotSent; + $dto->futureDestineeThirdParties = [new ThirdParty()]; + + $constraint = new TransitionHasDestineeIfIsSentExternal(); + $constraint->messageDestineeRequired = 'validation_message'; + + $this->validator->validate($dto, $constraint); + + self::buildViolation('validation_message') + ->atPath('property.path.futureDestineeThirdParties') + ->setCode('eb8051fc-8227-11ef-8c3b-7f2de85bdc5b') + ->assertRaised(); + } + + public function testToNoSentPlaceWithDestineeEmailAddViolation(): void + { + $entityWorkflow = new EntityWorkflow(); + $entityWorkflow->setWorkflowName('dummy'); + + $dto = new WorkflowTransitionContextDTO($entityWorkflow); + $dto->transition = $this->transitionToNotSent; + $dto->futureDestineeEmails = ['test@example.com']; + + $constraint = new TransitionHasDestineeIfIsSentExternal(); + $constraint->messageDestineeRequired = 'validation_message'; + + $this->validator->validate($dto, $constraint); + + self::buildViolation('validation_message') + ->atPath('property.path.futureDestineeEmails') + ->setCode('eb8051fc-8227-11ef-8c3b-7f2de85bdc5b') + ->assertRaised(); + } + + protected function createValidator(): TransitionHasDestineeIfIsSentExternalValidator + { + return new TransitionHasDestineeIfIsSentExternalValidator($this->buildRegistry()); + } +} diff --git a/src/Bundle/ChillMainBundle/Workflow/Validator/TransitionHasDestineeIfIsSentExternal.php b/src/Bundle/ChillMainBundle/Workflow/Validator/TransitionHasDestineeIfIsSentExternal.php new file mode 100644 index 000000000..6178bdce3 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Workflow/Validator/TransitionHasDestineeIfIsSentExternal.php @@ -0,0 +1,31 @@ +transition) { + return; + } + + $workflow = $this->registry->get($value->entityWorkflow, $value->entityWorkflow->getWorkflowName()); + $isSentExternal = false; + foreach ($value->transition->getTos() as $to) { + $metadata = $workflow->getMetadataStore()->getPlaceMetadata($to); + + $isSentExternal = $isSentExternal ? true : $metadata['isSentExternal'] ?? false; + } + + if (!$isSentExternal) { + if (0 !== count($value->futureDestineeThirdParties)) { + $this->context->buildViolation($constraint->messageDestineeRequired) + ->atPath('futureDestineeThirdParties') + ->setCode($constraint->codeDestineeUnauthorized) + ->addViolation(); + } + if (0 !== count($value->futureDestineeEmails)) { + $this->context->buildViolation($constraint->messageDestineeRequired) + ->atPath('futureDestineeEmails') + ->setCode($constraint->codeDestineeUnauthorized) + ->addViolation(); + } + + return; + } + + if (0 === count($value->futureDestineeEmails) && 0 === count($value->futureDestineeThirdParties)) { + $this->context->buildViolation($constraint->messageDestineeRequired) + ->atPath('futureDestineeThirdParties') + ->setCode($constraint->codeNoNecessaryDestinee) + ->addViolation(); + } + } +} diff --git a/src/Bundle/ChillMainBundle/Workflow/WorkflowTransitionContextDTO.php b/src/Bundle/ChillMainBundle/Workflow/WorkflowTransitionContextDTO.php index f00761ee4..0a8e1ac36 100644 --- a/src/Bundle/ChillMainBundle/Workflow/WorkflowTransitionContextDTO.php +++ b/src/Bundle/ChillMainBundle/Workflow/WorkflowTransitionContextDTO.php @@ -14,6 +14,7 @@ namespace Chill\MainBundle\Workflow; use Chill\MainBundle\Entity\User; use Chill\MainBundle\Entity\UserGroup; use Chill\MainBundle\Entity\Workflow\EntityWorkflow; +use Chill\MainBundle\Workflow\Validator\TransitionHasDestineeIfIsSentExternal; use Chill\PersonBundle\Entity\Person; use Chill\ThirdPartyBundle\Entity\ThirdParty; use Chill\ThirdPartyBundle\Validator\ThirdPartyHasEmail; @@ -24,6 +25,7 @@ use Symfony\Component\Workflow\Transition; /** * Context for a transition on an workflow entity. */ +#[TransitionHasDestineeIfIsSentExternal] class WorkflowTransitionContextDTO { /** diff --git a/src/Bundle/ChillMainBundle/translations/validators.fr.yml b/src/Bundle/ChillMainBundle/translations/validators.fr.yml index 4ab9bde34..7e852177a 100644 --- a/src/Bundle/ChillMainBundle/translations/validators.fr.yml +++ b/src/Bundle/ChillMainBundle/translations/validators.fr.yml @@ -34,6 +34,8 @@ notification: workflow: You must add at least one dest user or email: Indiquez au moins un destinataire ou une adresse email The user in cc cannot be a dest user in the same workflow step: L'utilisateur en copie ne peut pas être présent dans les utilisateurs qui valideront la prochaine étape + transition_has_destinee_if_sent_external: Indiquez un destinataire de l'envoi externe + transition_destinee_not_necessary: Pour cette transition, vous ne pouvez pas indiquer de destinataires externes rolling_date: When fixed date is selected, you must provide a date: Indiquez la date fixe choisie