mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-13 13:54:23 +00:00
Merge branch 'signature-app/signature-doctrine-model' into 'signature-app-master'
Create entity workflow signature See merge request Chill-Projet/chill-bundles!705
This commit is contained in:
commit
ba95687f46
@ -121,9 +121,4 @@ class AccompanyingCourseDocumentWorkflowHandler implements EntityWorkflowHandler
|
|||||||
{
|
{
|
||||||
return AccompanyingCourseDocument::class === $entityWorkflow->getRelatedEntityClass();
|
return AccompanyingCourseDocument::class === $entityWorkflow->getRelatedEntityClass();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function supportsFreeze(EntityWorkflow $entityWorkflow, array $options = []): bool
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ use Chill\MainBundle\Repository\Workflow\EntityWorkflowRepository;
|
|||||||
use Chill\MainBundle\Security\Authorization\EntityWorkflowVoter;
|
use Chill\MainBundle\Security\Authorization\EntityWorkflowVoter;
|
||||||
use Chill\MainBundle\Security\ChillSecurity;
|
use Chill\MainBundle\Security\ChillSecurity;
|
||||||
use Chill\MainBundle\Workflow\EntityWorkflowManager;
|
use Chill\MainBundle\Workflow\EntityWorkflowManager;
|
||||||
|
use Chill\MainBundle\Workflow\WorkflowTransitionContextDTO;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\FormType;
|
use Symfony\Component\Form\Extension\Core\Type\FormType;
|
||||||
@ -279,7 +280,7 @@ class WorkflowController extends AbstractController
|
|||||||
|
|
||||||
if (\count($workflow->getEnabledTransitions($entityWorkflow)) > 0) {
|
if (\count($workflow->getEnabledTransitions($entityWorkflow)) > 0) {
|
||||||
// possible transition
|
// possible transition
|
||||||
|
$stepDTO = new WorkflowTransitionContextDTO($entityWorkflow);
|
||||||
$usersInvolved = $entityWorkflow->getUsersInvolved();
|
$usersInvolved = $entityWorkflow->getUsersInvolved();
|
||||||
$currentUserFound = array_search($this->security->getUser(), $usersInvolved, true);
|
$currentUserFound = array_search($this->security->getUser(), $usersInvolved, true);
|
||||||
|
|
||||||
@ -289,9 +290,8 @@ class WorkflowController extends AbstractController
|
|||||||
|
|
||||||
$transitionForm = $this->createForm(
|
$transitionForm = $this->createForm(
|
||||||
WorkflowStepType::class,
|
WorkflowStepType::class,
|
||||||
$entityWorkflow->getCurrentStep(),
|
$stepDTO,
|
||||||
[
|
[
|
||||||
'transition' => true,
|
|
||||||
'entity_workflow' => $entityWorkflow,
|
'entity_workflow' => $entityWorkflow,
|
||||||
'suggested_users' => $usersInvolved,
|
'suggested_users' => $usersInvolved,
|
||||||
]
|
]
|
||||||
@ -310,12 +310,7 @@ class WorkflowController extends AbstractController
|
|||||||
throw $this->createAccessDeniedException(sprintf("not allowed to apply transition {$transition}: %s", implode(', ', $msgs)));
|
throw $this->createAccessDeniedException(sprintf("not allowed to apply transition {$transition}: %s", implode(', ', $msgs)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO symfony 5: add those "future" on context ($workflow->apply($entityWorkflow, $transition, $context)
|
$workflow->apply($entityWorkflow, $transition, ['context' => $stepDTO]);
|
||||||
$entityWorkflow->futureCcUsers = $transitionForm['future_cc_users']->getData() ?? [];
|
|
||||||
$entityWorkflow->futureDestUsers = $transitionForm['future_dest_users']->getData() ?? [];
|
|
||||||
$entityWorkflow->futureDestEmails = $transitionForm['future_dest_emails']->getData() ?? [];
|
|
||||||
|
|
||||||
$workflow->apply($entityWorkflow, $transition);
|
|
||||||
|
|
||||||
$this->entityManager->flush();
|
$this->entityManager->flush();
|
||||||
|
|
||||||
|
@ -17,9 +17,9 @@ use Chill\MainBundle\Doctrine\Model\TrackUpdateInterface;
|
|||||||
use Chill\MainBundle\Doctrine\Model\TrackUpdateTrait;
|
use Chill\MainBundle\Doctrine\Model\TrackUpdateTrait;
|
||||||
use Chill\MainBundle\Entity\User;
|
use Chill\MainBundle\Entity\User;
|
||||||
use Chill\MainBundle\Workflow\Validator\EntityWorkflowCreation;
|
use Chill\MainBundle\Workflow\Validator\EntityWorkflowCreation;
|
||||||
|
use Chill\MainBundle\Workflow\WorkflowTransitionContextDTO;
|
||||||
use Doctrine\Common\Collections\ArrayCollection;
|
use Doctrine\Common\Collections\ArrayCollection;
|
||||||
use Doctrine\Common\Collections\Collection;
|
use Doctrine\Common\Collections\Collection;
|
||||||
use Doctrine\Common\Collections\Order;
|
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
use Symfony\Component\Serializer\Annotation as Serializer;
|
use Symfony\Component\Serializer\Annotation as Serializer;
|
||||||
use Symfony\Component\Validator\Constraints as Assert;
|
use Symfony\Component\Validator\Constraints as Assert;
|
||||||
@ -34,35 +34,6 @@ class EntityWorkflow implements TrackCreationInterface, TrackUpdateInterface
|
|||||||
|
|
||||||
use TrackUpdateTrait;
|
use TrackUpdateTrait;
|
||||||
|
|
||||||
/**
|
|
||||||
* a list of future cc users for the next steps.
|
|
||||||
*
|
|
||||||
* @var array|User[]
|
|
||||||
*/
|
|
||||||
public array $futureCcUsers = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* a list of future dest emails for the next steps.
|
|
||||||
*
|
|
||||||
* This is in used in order to let controller inform who will be the future emails which will validate
|
|
||||||
* the next step. This is necessary to perform some computation about the next emails, before they are
|
|
||||||
* associated to the entity EntityWorkflowStep.
|
|
||||||
*
|
|
||||||
* @var array|string[]
|
|
||||||
*/
|
|
||||||
public array $futureDestEmails = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* a list of future dest users for the next steps.
|
|
||||||
*
|
|
||||||
* This is in used in order to let controller inform who will be the future users which will validate
|
|
||||||
* the next step. This is necessary to perform some computation about the next users, before they are
|
|
||||||
* associated to the entity EntityWorkflowStep.
|
|
||||||
*
|
|
||||||
* @var array|User[]
|
|
||||||
*/
|
|
||||||
public array $futureDestUsers = [];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Collection<EntityWorkflowComment>
|
* @var Collection<EntityWorkflowComment>
|
||||||
*/
|
*/
|
||||||
@ -442,11 +413,23 @@ class EntityWorkflow implements TrackCreationInterface, TrackUpdateInterface
|
|||||||
*
|
*
|
||||||
* @return $this
|
* @return $this
|
||||||
*/
|
*/
|
||||||
public function setStep(string $step): self
|
public function setStep(string $step, WorkflowTransitionContextDTO $transitionContextDTO): self
|
||||||
{
|
{
|
||||||
$newStep = new EntityWorkflowStep();
|
$newStep = new EntityWorkflowStep();
|
||||||
$newStep->setCurrentStep($step);
|
$newStep->setCurrentStep($step);
|
||||||
|
|
||||||
|
foreach ($transitionContextDTO->futureCcUsers as $user) {
|
||||||
|
$newStep->addCcUser($user);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($transitionContextDTO->futureDestUsers as $user) {
|
||||||
|
$newStep->addDestUser($user);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($transitionContextDTO->futureDestEmails as $email) {
|
||||||
|
$newStep->addDestEmail($email);
|
||||||
|
}
|
||||||
|
|
||||||
// copy the freeze
|
// copy the freeze
|
||||||
if ($this->isFreeze()) {
|
if ($this->isFreeze()) {
|
||||||
$newStep->setFreezeAfter(true);
|
$newStep->setFreezeAfter(true);
|
||||||
|
@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\MainBundle\Entity\Workflow;
|
||||||
|
|
||||||
|
enum EntityWorkflowSignatureStateEnum: string
|
||||||
|
{
|
||||||
|
case PENDING = 'pending';
|
||||||
|
case SIGNED = 'signed';
|
||||||
|
case REJECTED = 'rejected';
|
||||||
|
case CANCELED = 'canceled';
|
||||||
|
}
|
@ -42,19 +42,25 @@ class EntityWorkflowStep
|
|||||||
private array $destEmail = [];
|
private array $destEmail = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Collection<User>
|
* @var Collection<int, User>
|
||||||
*/
|
*/
|
||||||
#[ORM\ManyToMany(targetEntity: User::class)]
|
#[ORM\ManyToMany(targetEntity: User::class)]
|
||||||
#[ORM\JoinTable(name: 'chill_main_workflow_entity_step_user')]
|
#[ORM\JoinTable(name: 'chill_main_workflow_entity_step_user')]
|
||||||
private Collection $destUser;
|
private Collection $destUser;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Collection<User>
|
* @var Collection<int, User>
|
||||||
*/
|
*/
|
||||||
#[ORM\ManyToMany(targetEntity: User::class)]
|
#[ORM\ManyToMany(targetEntity: User::class)]
|
||||||
#[ORM\JoinTable(name: 'chill_main_workflow_entity_step_user_by_accesskey')]
|
#[ORM\JoinTable(name: 'chill_main_workflow_entity_step_user_by_accesskey')]
|
||||||
private Collection $destUserByAccessKey;
|
private Collection $destUserByAccessKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Collection <int, EntityWorkflowStepSignature>
|
||||||
|
*/
|
||||||
|
#[ORM\OneToMany(mappedBy: 'step', targetEntity: EntityWorkflowStepSignature::class, cascade: ['persist'], orphanRemoval: true)]
|
||||||
|
private Collection $signatures;
|
||||||
|
|
||||||
#[ORM\ManyToOne(targetEntity: EntityWorkflow::class, inversedBy: 'steps')]
|
#[ORM\ManyToOne(targetEntity: EntityWorkflow::class, inversedBy: 'steps')]
|
||||||
private ?EntityWorkflow $entityWorkflow = null;
|
private ?EntityWorkflow $entityWorkflow = null;
|
||||||
|
|
||||||
@ -97,6 +103,7 @@ class EntityWorkflowStep
|
|||||||
$this->ccUser = new ArrayCollection();
|
$this->ccUser = new ArrayCollection();
|
||||||
$this->destUser = new ArrayCollection();
|
$this->destUser = new ArrayCollection();
|
||||||
$this->destUserByAccessKey = new ArrayCollection();
|
$this->destUserByAccessKey = new ArrayCollection();
|
||||||
|
$this->signatures = new ArrayCollection();
|
||||||
$this->accessKey = bin2hex(openssl_random_pseudo_bytes(32));
|
$this->accessKey = bin2hex(openssl_random_pseudo_bytes(32));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,6 +143,29 @@ class EntityWorkflowStep
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal use @see{EntityWorkflowStepSignature}'s constructor instead
|
||||||
|
*/
|
||||||
|
public function addSignature(EntityWorkflowStepSignature $signature): self
|
||||||
|
{
|
||||||
|
if (!$this->signatures->contains($signature)) {
|
||||||
|
$this->signatures[] = $signature;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function removeSignature(EntityWorkflowStepSignature $signature): self
|
||||||
|
{
|
||||||
|
if ($this->signatures->contains($signature)) {
|
||||||
|
$this->signatures->removeElement($signature);
|
||||||
|
}
|
||||||
|
|
||||||
|
$signature->detachEntityWorkflowStep();
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function getAccessKey(): string
|
public function getAccessKey(): string
|
||||||
{
|
{
|
||||||
return $this->accessKey;
|
return $this->accessKey;
|
||||||
@ -198,6 +228,14 @@ class EntityWorkflowStep
|
|||||||
return $this->entityWorkflow;
|
return $this->entityWorkflow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Collection<int, EntityWorkflowStepSignature>
|
||||||
|
*/
|
||||||
|
public function getSignatures(): Collection
|
||||||
|
{
|
||||||
|
return $this->signatures;
|
||||||
|
}
|
||||||
|
|
||||||
public function getId(): ?int
|
public function getId(): ?int
|
||||||
{
|
{
|
||||||
return $this->id;
|
return $this->id;
|
||||||
|
@ -0,0 +1,156 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\MainBundle\Entity\Workflow;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Doctrine\Model\TrackCreationInterface;
|
||||||
|
use Chill\MainBundle\Doctrine\Model\TrackCreationTrait;
|
||||||
|
use Chill\MainBundle\Doctrine\Model\TrackUpdateInterface;
|
||||||
|
use Chill\MainBundle\Doctrine\Model\TrackUpdateTrait;
|
||||||
|
use Chill\MainBundle\Entity\User;
|
||||||
|
use Chill\PersonBundle\Entity\Person;
|
||||||
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
|
||||||
|
#[ORM\Entity]
|
||||||
|
#[ORM\Table(name: 'chill_main_workflow_entity_step_signature')]
|
||||||
|
class EntityWorkflowStepSignature implements TrackCreationInterface, TrackUpdateInterface
|
||||||
|
{
|
||||||
|
use TrackCreationTrait;
|
||||||
|
use TrackUpdateTrait;
|
||||||
|
|
||||||
|
#[ORM\Id]
|
||||||
|
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::INTEGER, unique: true)]
|
||||||
|
#[ORM\GeneratedValue(strategy: 'AUTO')]
|
||||||
|
private ?int $id = null;
|
||||||
|
|
||||||
|
#[ORM\ManyToOne(targetEntity: User::class)]
|
||||||
|
#[ORM\JoinColumn(nullable: true)]
|
||||||
|
private ?User $userSigner = null;
|
||||||
|
|
||||||
|
#[ORM\ManyToOne(targetEntity: Person::class)]
|
||||||
|
#[ORM\JoinColumn(nullable: true)]
|
||||||
|
private ?Person $personSigner = null;
|
||||||
|
|
||||||
|
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::STRING, length: 50, nullable: false, enumType: EntityWorkflowSignatureStateEnum::class)]
|
||||||
|
private EntityWorkflowSignatureStateEnum $state = EntityWorkflowSignatureStateEnum::PENDING;
|
||||||
|
|
||||||
|
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::DATETIMETZ_IMMUTABLE, nullable: true, options: ['default' => null])]
|
||||||
|
private ?\DateTimeImmutable $stateDate = null;
|
||||||
|
|
||||||
|
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::JSON, nullable: false, options: ['default' => '[]'])]
|
||||||
|
private array $signatureMetadata = [];
|
||||||
|
|
||||||
|
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::INTEGER, nullable: true, options: ['default' => null])]
|
||||||
|
private ?int $zoneSignatureIndex = null;
|
||||||
|
|
||||||
|
#[ORM\ManyToOne(targetEntity: EntityWorkflowStep::class, inversedBy: 'signatures')]
|
||||||
|
#[ORM\JoinColumn(nullable: false)]
|
||||||
|
private ?EntityWorkflowStep $step = null;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
EntityWorkflowStep $step,
|
||||||
|
User|Person $signer,
|
||||||
|
) {
|
||||||
|
$this->step = $step;
|
||||||
|
$step->addSignature($this);
|
||||||
|
$this->setSigner($signer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function setSigner(User|Person $signer): void
|
||||||
|
{
|
||||||
|
if ($signer instanceof User) {
|
||||||
|
$this->userSigner = $signer;
|
||||||
|
} else {
|
||||||
|
$this->personSigner = $signer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getId(): ?int
|
||||||
|
{
|
||||||
|
return $this->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getStep(): EntityWorkflowStep
|
||||||
|
{
|
||||||
|
return $this->step;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSigner(): User|Person
|
||||||
|
{
|
||||||
|
if (null !== $this->userSigner) {
|
||||||
|
return $this->userSigner;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->personSigner;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSignatureMetadata(): array
|
||||||
|
{
|
||||||
|
return $this->signatureMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setSignatureMetadata(array $signatureMetadata): EntityWorkflowStepSignature
|
||||||
|
{
|
||||||
|
$this->signatureMetadata = $signatureMetadata;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getState(): EntityWorkflowSignatureStateEnum
|
||||||
|
{
|
||||||
|
return $this->state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setState(EntityWorkflowSignatureStateEnum $state): EntityWorkflowStepSignature
|
||||||
|
{
|
||||||
|
$this->state = $state;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getStateDate(): ?\DateTimeImmutable
|
||||||
|
{
|
||||||
|
return $this->stateDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setStateDate(?\DateTimeImmutable $stateDate): EntityWorkflowStepSignature
|
||||||
|
{
|
||||||
|
$this->stateDate = $stateDate;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getZoneSignatureIndex(): ?int
|
||||||
|
{
|
||||||
|
return $this->zoneSignatureIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setZoneSignatureIndex(?int $zoneSignatureIndex): EntityWorkflowStepSignature
|
||||||
|
{
|
||||||
|
$this->zoneSignatureIndex = $zoneSignatureIndex;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detach from the @see{EntityWorkflowStep}.
|
||||||
|
*
|
||||||
|
* @internal used internally to remove the current signature
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function detachEntityWorkflowStep(): self
|
||||||
|
{
|
||||||
|
$this->step = null;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
@ -12,14 +12,12 @@ declare(strict_types=1);
|
|||||||
namespace Chill\MainBundle\Form;
|
namespace Chill\MainBundle\Form;
|
||||||
|
|
||||||
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
|
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
|
||||||
use Chill\MainBundle\Entity\Workflow\EntityWorkflowStep;
|
|
||||||
use Chill\MainBundle\Form\Type\ChillCollectionType;
|
use Chill\MainBundle\Form\Type\ChillCollectionType;
|
||||||
use Chill\MainBundle\Form\Type\ChillTextareaType;
|
use Chill\MainBundle\Form\Type\ChillTextareaType;
|
||||||
use Chill\MainBundle\Form\Type\PickUserDynamicType;
|
use Chill\MainBundle\Form\Type\PickUserDynamicType;
|
||||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||||
use Chill\MainBundle\Workflow\EntityWorkflowManager;
|
use Chill\MainBundle\Workflow\WorkflowTransitionContextDTO;
|
||||||
use Symfony\Component\Form\AbstractType;
|
use Symfony\Component\Form\AbstractType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
|
||||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\EmailType;
|
use Symfony\Component\Form\Extension\Core\Type\EmailType;
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
@ -34,169 +32,151 @@ use Symfony\Component\Workflow\Transition;
|
|||||||
|
|
||||||
class WorkflowStepType extends AbstractType
|
class WorkflowStepType extends AbstractType
|
||||||
{
|
{
|
||||||
public function __construct(private readonly EntityWorkflowManager $entityWorkflowManager, private readonly Registry $registry, private readonly TranslatableStringHelperInterface $translatableStringHelper) {}
|
public function __construct(
|
||||||
|
private readonly Registry $registry,
|
||||||
|
private readonly TranslatableStringHelperInterface $translatableStringHelper
|
||||||
|
) {}
|
||||||
|
|
||||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||||
{
|
{
|
||||||
/** @var EntityWorkflow $entityWorkflow */
|
/** @var EntityWorkflow $entityWorkflow */
|
||||||
$entityWorkflow = $options['entity_workflow'];
|
$entityWorkflow = $options['entity_workflow'];
|
||||||
$handler = $this->entityWorkflowManager->getHandler($entityWorkflow);
|
|
||||||
$workflow = $this->registry->get($entityWorkflow, $entityWorkflow->getWorkflowName());
|
$workflow = $this->registry->get($entityWorkflow, $entityWorkflow->getWorkflowName());
|
||||||
$place = $workflow->getMarking($entityWorkflow);
|
$place = $workflow->getMarking($entityWorkflow);
|
||||||
$placeMetadata = $workflow->getMetadataStore()->getPlaceMetadata(array_keys($place->getPlaces())[0]);
|
$placeMetadata = $workflow->getMetadataStore()->getPlaceMetadata(array_keys($place->getPlaces())[0]);
|
||||||
|
|
||||||
if (true === $options['transition']) {
|
if (null === $options['entity_workflow']) {
|
||||||
if (null === $options['entity_workflow']) {
|
throw new \LogicException('if transition is true, entity_workflow should be defined');
|
||||||
throw new \LogicException('if transition is true, entity_workflow should be defined');
|
}
|
||||||
}
|
|
||||||
|
|
||||||
$transitions = $this->registry
|
$transitions = $this->registry
|
||||||
->get($options['entity_workflow'], $entityWorkflow->getWorkflowName())
|
->get($options['entity_workflow'], $entityWorkflow->getWorkflowName())
|
||||||
->getEnabledTransitions($entityWorkflow);
|
->getEnabledTransitions($entityWorkflow);
|
||||||
|
|
||||||
$choices = array_combine(
|
$choices = array_combine(
|
||||||
array_map(
|
array_map(
|
||||||
static fn (Transition $transition) => $transition->getName(),
|
static fn (Transition $transition) => $transition->getName(),
|
||||||
$transitions
|
|
||||||
),
|
|
||||||
$transitions
|
$transitions
|
||||||
);
|
),
|
||||||
|
$transitions
|
||||||
|
);
|
||||||
|
|
||||||
if (\array_key_exists('validationFilterInputLabels', $placeMetadata)) {
|
if (\array_key_exists('validationFilterInputLabels', $placeMetadata)) {
|
||||||
$inputLabels = $placeMetadata['validationFilterInputLabels'];
|
$inputLabels = $placeMetadata['validationFilterInputLabels'];
|
||||||
|
|
||||||
$builder->add('transitionFilter', ChoiceType::class, [
|
$builder->add('transitionFilter', ChoiceType::class, [
|
||||||
'multiple' => false,
|
'multiple' => false,
|
||||||
'label' => 'workflow.My decision',
|
'label' => 'workflow.My decision',
|
||||||
'choices' => [
|
'choices' => [
|
||||||
'forward' => 'forward',
|
'forward' => 'forward',
|
||||||
'backward' => 'backward',
|
'backward' => 'backward',
|
||||||
'neutral' => 'neutral',
|
'neutral' => 'neutral',
|
||||||
],
|
],
|
||||||
'choice_label' => fn (string $key) => $this->translatableStringHelper->localize($inputLabels[$key]),
|
'choice_label' => fn (string $key) => $this->translatableStringHelper->localize($inputLabels[$key]),
|
||||||
'choice_attr' => static fn (string $key) => [
|
'choice_attr' => static fn (string $key) => [
|
||||||
$key => $key,
|
$key => $key,
|
||||||
],
|
],
|
||||||
'mapped' => false,
|
'mapped' => false,
|
||||||
'expanded' => true,
|
'expanded' => true,
|
||||||
'data' => 'forward',
|
'data' => 'forward',
|
||||||
]);
|
]);
|
||||||
}
|
|
||||||
|
|
||||||
$builder
|
|
||||||
->add('transition', ChoiceType::class, [
|
|
||||||
'label' => 'workflow.Next step',
|
|
||||||
'mapped' => false,
|
|
||||||
'multiple' => false,
|
|
||||||
'expanded' => true,
|
|
||||||
'choices' => $choices,
|
|
||||||
'constraints' => [new NotNull()],
|
|
||||||
'choice_label' => function (Transition $transition) use ($workflow) {
|
|
||||||
$meta = $workflow->getMetadataStore()->getTransitionMetadata($transition);
|
|
||||||
|
|
||||||
if (\array_key_exists('label', $meta)) {
|
|
||||||
return $this->translatableStringHelper->localize($meta['label']);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $transition->getName();
|
|
||||||
},
|
|
||||||
'choice_attr' => static function (Transition $transition) use ($workflow) {
|
|
||||||
$toFinal = true;
|
|
||||||
$isForward = 'neutral';
|
|
||||||
|
|
||||||
$metadata = $workflow->getMetadataStore()->getTransitionMetadata($transition);
|
|
||||||
|
|
||||||
if (\array_key_exists('isForward', $metadata)) {
|
|
||||||
if ($metadata['isForward']) {
|
|
||||||
$isForward = 'forward';
|
|
||||||
} else {
|
|
||||||
$isForward = 'backward';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($transition->getTos() as $to) {
|
|
||||||
$meta = $workflow->getMetadataStore()->getPlaceMetadata($to);
|
|
||||||
|
|
||||||
if (
|
|
||||||
!\array_key_exists('isFinal', $meta) || false === $meta['isFinal']
|
|
||||||
) {
|
|
||||||
$toFinal = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
'data-is-transition' => 'data-is-transition',
|
|
||||||
'data-to-final' => $toFinal ? '1' : '0',
|
|
||||||
'data-is-forward' => $isForward,
|
|
||||||
];
|
|
||||||
},
|
|
||||||
])
|
|
||||||
->add('future_dest_users', PickUserDynamicType::class, [
|
|
||||||
'label' => 'workflow.dest for next steps',
|
|
||||||
'multiple' => true,
|
|
||||||
'mapped' => false,
|
|
||||||
'suggested' => $options['suggested_users'],
|
|
||||||
])
|
|
||||||
->add('future_cc_users', PickUserDynamicType::class, [
|
|
||||||
'label' => 'workflow.cc for next steps',
|
|
||||||
'multiple' => true,
|
|
||||||
'mapped' => false,
|
|
||||||
'required' => false,
|
|
||||||
'suggested' => $options['suggested_users'],
|
|
||||||
])
|
|
||||||
->add('future_dest_emails', ChillCollectionType::class, [
|
|
||||||
'label' => 'workflow.dest by email',
|
|
||||||
'help' => 'workflow.dest by email help',
|
|
||||||
'mapped' => false,
|
|
||||||
'allow_add' => true,
|
|
||||||
'entry_type' => EmailType::class,
|
|
||||||
'button_add_label' => 'workflow.Add an email',
|
|
||||||
'button_remove_label' => 'workflow.Remove an email',
|
|
||||||
'empty_collection_explain' => 'workflow.Any email',
|
|
||||||
'entry_options' => [
|
|
||||||
'constraints' => [
|
|
||||||
new NotNull(), new NotBlank(), new Email(),
|
|
||||||
],
|
|
||||||
'label' => 'Email',
|
|
||||||
],
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
$builder
|
||||||
$handler->supportsFreeze($entityWorkflow)
|
->add('transition', ChoiceType::class, [
|
||||||
&& !$entityWorkflow->isFreeze()
|
'label' => 'workflow.Next step',
|
||||||
) {
|
'mapped' => false,
|
||||||
$builder
|
'multiple' => false,
|
||||||
->add('freezeAfter', CheckboxType::class, [
|
'expanded' => true,
|
||||||
'required' => false,
|
'choices' => $choices,
|
||||||
'label' => 'workflow.Freeze',
|
'constraints' => [new NotNull()],
|
||||||
'help' => 'workflow.The associated element will be freezed',
|
'choice_label' => function (Transition $transition) use ($workflow) {
|
||||||
]);
|
$meta = $workflow->getMetadataStore()->getTransitionMetadata($transition);
|
||||||
}
|
|
||||||
|
if (\array_key_exists('label', $meta)) {
|
||||||
|
return $this->translatableStringHelper->localize($meta['label']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $transition->getName();
|
||||||
|
},
|
||||||
|
'choice_attr' => static function (Transition $transition) use ($workflow) {
|
||||||
|
$toFinal = true;
|
||||||
|
$isForward = 'neutral';
|
||||||
|
|
||||||
|
$metadata = $workflow->getMetadataStore()->getTransitionMetadata($transition);
|
||||||
|
|
||||||
|
if (\array_key_exists('isForward', $metadata)) {
|
||||||
|
if ($metadata['isForward']) {
|
||||||
|
$isForward = 'forward';
|
||||||
|
} else {
|
||||||
|
$isForward = 'backward';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($transition->getTos() as $to) {
|
||||||
|
$meta = $workflow->getMetadataStore()->getPlaceMetadata($to);
|
||||||
|
|
||||||
|
if (
|
||||||
|
!\array_key_exists('isFinal', $meta) || false === $meta['isFinal']
|
||||||
|
) {
|
||||||
|
$toFinal = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'data-is-transition' => 'data-is-transition',
|
||||||
|
'data-to-final' => $toFinal ? '1' : '0',
|
||||||
|
'data-is-forward' => $isForward,
|
||||||
|
];
|
||||||
|
},
|
||||||
|
])
|
||||||
|
->add('futureDestUsers', PickUserDynamicType::class, [
|
||||||
|
'label' => 'workflow.dest for next steps',
|
||||||
|
'multiple' => true,
|
||||||
|
'suggested' => $options['suggested_users'],
|
||||||
|
])
|
||||||
|
->add('futureCcUsers', PickUserDynamicType::class, [
|
||||||
|
'label' => 'workflow.cc for next steps',
|
||||||
|
'multiple' => true,
|
||||||
|
'required' => false,
|
||||||
|
'suggested' => $options['suggested_users'],
|
||||||
|
])
|
||||||
|
->add('futureDestEmails', ChillCollectionType::class, [
|
||||||
|
'label' => 'workflow.dest by email',
|
||||||
|
'help' => 'workflow.dest by email help',
|
||||||
|
'allow_add' => true,
|
||||||
|
'entry_type' => EmailType::class,
|
||||||
|
'button_add_label' => 'workflow.Add an email',
|
||||||
|
'button_remove_label' => 'workflow.Remove an email',
|
||||||
|
'empty_collection_explain' => 'workflow.Any email',
|
||||||
|
'entry_options' => [
|
||||||
|
'constraints' => [
|
||||||
|
new NotNull(), new NotBlank(), new Email(),
|
||||||
|
],
|
||||||
|
'label' => 'Email',
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
$builder
|
$builder
|
||||||
->add('comment', ChillTextareaType::class, [
|
->add('comment', ChillTextareaType::class, [
|
||||||
'required' => false,
|
'required' => false,
|
||||||
'label' => 'Comment',
|
'label' => 'Comment',
|
||||||
|
'empty_data' => '',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function configureOptions(OptionsResolver $resolver)
|
public function configureOptions(OptionsResolver $resolver)
|
||||||
{
|
{
|
||||||
$resolver
|
$resolver
|
||||||
->setDefined('class')
|
->setDefault('data_class', WorkflowTransitionContextDTO::class)
|
||||||
->setRequired('transition')
|
|
||||||
->setAllowedTypes('transition', 'bool')
|
|
||||||
->setRequired('entity_workflow')
|
->setRequired('entity_workflow')
|
||||||
->setAllowedTypes('entity_workflow', EntityWorkflow::class)
|
->setAllowedTypes('entity_workflow', EntityWorkflow::class)
|
||||||
->setDefault('suggested_users', [])
|
->setDefault('suggested_users', [])
|
||||||
->setDefault('constraints', [
|
->setDefault('constraints', [
|
||||||
new Callback(
|
new Callback(
|
||||||
function ($step, ExecutionContextInterface $context, $payload) {
|
function (WorkflowTransitionContextDTO $step, ExecutionContextInterface $context, $payload) {
|
||||||
/** @var EntityWorkflowStep $step */
|
$workflow = $this->registry->get($step->entityWorkflow, $step->entityWorkflow->getWorkflowName());
|
||||||
$form = $context->getObject();
|
$transition = $step->transition;
|
||||||
$workflow = $this->registry->get($step->getEntityWorkflow(), $step->getEntityWorkflow()->getWorkflowName());
|
|
||||||
$transition = $form['transition']->getData();
|
|
||||||
$toFinal = true;
|
$toFinal = true;
|
||||||
|
|
||||||
if (null === $transition) {
|
if (null === $transition) {
|
||||||
@ -212,8 +192,8 @@ class WorkflowStepType extends AbstractType
|
|||||||
$toFinal = false;
|
$toFinal = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$destUsers = $form['future_dest_users']->getData();
|
$destUsers = $step->futureDestUsers;
|
||||||
$destEmails = $form['future_dest_emails']->getData();
|
$destEmails = $step->futureDestEmails;
|
||||||
|
|
||||||
if (!$toFinal && [] === $destUsers && [] === $destEmails) {
|
if (!$toFinal && [] === $destUsers && [] === $destEmails) {
|
||||||
$context
|
$context
|
||||||
@ -224,20 +204,6 @@ class WorkflowStepType extends AbstractType
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
new Callback(
|
|
||||||
function ($step, ExecutionContextInterface $context, $payload) {
|
|
||||||
$form = $context->getObject();
|
|
||||||
|
|
||||||
foreach ($form->get('future_dest_users')->getData() as $u) {
|
|
||||||
if (in_array($u, $form->get('future_cc_users')->getData(), true)) {
|
|
||||||
$context
|
|
||||||
->buildViolation('workflow.The user in cc cannot be a dest user in the same workflow step')
|
|
||||||
->atPath('ccUsers')
|
|
||||||
->addViolation();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
),
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\MainBundle\Repository\Workflow;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Entity\Workflow\EntityWorkflowStepSignature;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Doctrine\Persistence\ObjectRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template-implements ObjectRepository<EntityWorkflowStepSignature>
|
||||||
|
*/
|
||||||
|
class EntityWorkflowStepSignatureRepository implements ObjectRepository
|
||||||
|
{
|
||||||
|
private \Doctrine\ORM\EntityRepository $repository;
|
||||||
|
|
||||||
|
public function __construct(EntityManagerInterface $entityManager)
|
||||||
|
{
|
||||||
|
$this->repository = $entityManager->getRepository(EntityWorkflowStepSignature::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function find($id): ?EntityWorkflowStepSignature
|
||||||
|
{
|
||||||
|
return $this->repository->find($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findAll(): array
|
||||||
|
{
|
||||||
|
return $this->repository->findAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array
|
||||||
|
{
|
||||||
|
return $this->findBy($criteria, $orderBy, $limit, $offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findOneBy(array $criteria): ?EntityWorkflowStepSignature
|
||||||
|
{
|
||||||
|
return $this->findOneBy($criteria);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getClassName(): string
|
||||||
|
{
|
||||||
|
return EntityWorkflowStepSignature::class;
|
||||||
|
}
|
||||||
|
}
|
@ -58,17 +58,15 @@
|
|||||||
{{ form_row(transition_form.transition) }}
|
{{ form_row(transition_form.transition) }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if transition_form.freezeAfter is defined %}
|
|
||||||
{{ form_row(transition_form.freezeAfter) }}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<div id="futureDests">
|
<div id="futureDests">
|
||||||
{{ form_row(transition_form.future_dest_users) }}
|
{{ form_row(transition_form.futureDestUsers) }}
|
||||||
|
{{ form_errors(transition_form.futureDestUsers) }}
|
||||||
|
|
||||||
{{ form_row(transition_form.future_cc_users) }}
|
{{ form_row(transition_form.futureCcUsers) }}
|
||||||
|
{{ form_errors(transition_form.futureCcUsers) }}
|
||||||
|
|
||||||
{{ form_row(transition_form.future_dest_emails) }}
|
{{ form_row(transition_form.futureDestEmails) }}
|
||||||
{{ form_errors(transition_form.future_dest_users) }}
|
{{ form_errors(transition_form.futureDestEmails) }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p>{{ form_label(transition_form.comment) }}</p>
|
<p>{{ form_label(transition_form.comment) }}</p>
|
||||||
|
@ -0,0 +1,69 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\MainBundle\Tests\Entity\Workflow;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
|
||||||
|
use Chill\MainBundle\Entity\Workflow\EntityWorkflowStepSignature;
|
||||||
|
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument;
|
||||||
|
use Chill\PersonBundle\Entity\Person;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* @coversNothing
|
||||||
|
*/
|
||||||
|
class EntityWorkflowStepSignatureTest extends KernelTestCase
|
||||||
|
{
|
||||||
|
private EntityManagerInterface $entityManager;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
self::bootKernel();
|
||||||
|
$this->entityManager = self::getContainer()->get(EntityManagerInterface::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testConstruct()
|
||||||
|
{
|
||||||
|
$workflow = new EntityWorkflow();
|
||||||
|
$workflow->setWorkflowName('vendee_internal')
|
||||||
|
->setRelatedEntityId(0)
|
||||||
|
->setRelatedEntityClass(AccompanyingPeriodWorkEvaluationDocument::class);
|
||||||
|
|
||||||
|
$step = $workflow->getCurrentStep();
|
||||||
|
|
||||||
|
$person = $this->entityManager->createQuery('SELECT p FROM '.Person::class.' p')
|
||||||
|
->setMaxResults(1)
|
||||||
|
->getSingleResult();
|
||||||
|
|
||||||
|
$signature = new EntityWorkflowStepSignature($step, $person);
|
||||||
|
|
||||||
|
self::assertCount(1, $step->getSignatures());
|
||||||
|
self::assertSame($signature, $step->getSignatures()->first());
|
||||||
|
|
||||||
|
$this->entityManager->getConnection()->beginTransaction();
|
||||||
|
$this->entityManager->persist($workflow);
|
||||||
|
$this->entityManager->persist($step);
|
||||||
|
$this->entityManager->persist($signature);
|
||||||
|
|
||||||
|
$this->entityManager->flush();
|
||||||
|
$this->entityManager->getConnection()->commit();
|
||||||
|
|
||||||
|
$this->entityManager->clear();
|
||||||
|
|
||||||
|
$signatureBis = $this->entityManager->find(EntityWorkflowStepSignature::class, $signature->getId());
|
||||||
|
|
||||||
|
self::assertEquals($signature->getId(), $signatureBis->getId());
|
||||||
|
self::assertEquals($step->getId(), $signatureBis->getStep()->getId());
|
||||||
|
}
|
||||||
|
}
|
@ -12,6 +12,7 @@ declare(strict_types=1);
|
|||||||
namespace Chill\MainBundle\Tests\Entity\Workflow;
|
namespace Chill\MainBundle\Tests\Entity\Workflow;
|
||||||
|
|
||||||
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
|
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
|
||||||
|
use Chill\MainBundle\Workflow\WorkflowTransitionContextDTO;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -25,7 +26,7 @@ final class EntityWorkflowTest extends TestCase
|
|||||||
{
|
{
|
||||||
$entityWorkflow = new EntityWorkflow();
|
$entityWorkflow = new EntityWorkflow();
|
||||||
|
|
||||||
$entityWorkflow->setStep('final');
|
$entityWorkflow->setStep('final', new WorkflowTransitionContextDTO($entityWorkflow));
|
||||||
$entityWorkflow->getCurrentStep()->setIsFinal(true);
|
$entityWorkflow->getCurrentStep()->setIsFinal(true);
|
||||||
|
|
||||||
$this->assertTrue($entityWorkflow->isFinal());
|
$this->assertTrue($entityWorkflow->isFinal());
|
||||||
@ -37,16 +38,16 @@ final class EntityWorkflowTest extends TestCase
|
|||||||
|
|
||||||
$this->assertFalse($entityWorkflow->isFinal());
|
$this->assertFalse($entityWorkflow->isFinal());
|
||||||
|
|
||||||
$entityWorkflow->setStep('two');
|
$entityWorkflow->setStep('two', new WorkflowTransitionContextDTO($entityWorkflow));
|
||||||
|
|
||||||
$this->assertFalse($entityWorkflow->isFinal());
|
$this->assertFalse($entityWorkflow->isFinal());
|
||||||
|
|
||||||
$entityWorkflow->setStep('previous_final');
|
$entityWorkflow->setStep('previous_final', new WorkflowTransitionContextDTO($entityWorkflow));
|
||||||
|
|
||||||
$this->assertFalse($entityWorkflow->isFinal());
|
$this->assertFalse($entityWorkflow->isFinal());
|
||||||
|
|
||||||
$entityWorkflow->getCurrentStep()->setIsFinal(true);
|
$entityWorkflow->getCurrentStep()->setIsFinal(true);
|
||||||
$entityWorkflow->setStep('final');
|
$entityWorkflow->setStep('final', new WorkflowTransitionContextDTO($entityWorkflow));
|
||||||
|
|
||||||
$this->assertTrue($entityWorkflow->isFinal());
|
$this->assertTrue($entityWorkflow->isFinal());
|
||||||
}
|
}
|
||||||
@ -57,20 +58,20 @@ final class EntityWorkflowTest extends TestCase
|
|||||||
|
|
||||||
$this->assertFalse($entityWorkflow->isFreeze());
|
$this->assertFalse($entityWorkflow->isFreeze());
|
||||||
|
|
||||||
$entityWorkflow->setStep('step_one');
|
$entityWorkflow->setStep('step_one', new WorkflowTransitionContextDTO($entityWorkflow));
|
||||||
|
|
||||||
$this->assertFalse($entityWorkflow->isFreeze());
|
$this->assertFalse($entityWorkflow->isFreeze());
|
||||||
|
|
||||||
$entityWorkflow->setStep('step_three');
|
$entityWorkflow->setStep('step_three', new WorkflowTransitionContextDTO($entityWorkflow));
|
||||||
|
|
||||||
$this->assertFalse($entityWorkflow->isFreeze());
|
$this->assertFalse($entityWorkflow->isFreeze());
|
||||||
|
|
||||||
$entityWorkflow->setStep('freezed');
|
$entityWorkflow->setStep('freezed', new WorkflowTransitionContextDTO($entityWorkflow));
|
||||||
$entityWorkflow->getCurrentStep()->setFreezeAfter(true);
|
$entityWorkflow->getCurrentStep()->setFreezeAfter(true);
|
||||||
|
|
||||||
$this->assertTrue($entityWorkflow->isFreeze());
|
$this->assertTrue($entityWorkflow->isFreeze());
|
||||||
|
|
||||||
$entityWorkflow->setStep('after_freeze');
|
$entityWorkflow->setStep('after_freeze', new WorkflowTransitionContextDTO($entityWorkflow));
|
||||||
|
|
||||||
$this->assertTrue($entityWorkflow->isFreeze());
|
$this->assertTrue($entityWorkflow->isFreeze());
|
||||||
|
|
||||||
|
@ -0,0 +1,61 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\MainBundle\Tests\Workflow;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Entity\User;
|
||||||
|
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
|
||||||
|
use Chill\MainBundle\Workflow\EntityWorkflowMarkingStore;
|
||||||
|
use Chill\MainBundle\Workflow\WorkflowTransitionContextDTO;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Symfony\Component\Workflow\Marking;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* @coversNothing
|
||||||
|
*/
|
||||||
|
class EntityWorkflowMarkingStoreTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testGetMarking(): void
|
||||||
|
{
|
||||||
|
$markingStore = $this->buildMarkingStore();
|
||||||
|
$workflow = new EntityWorkflow();
|
||||||
|
|
||||||
|
$marking = $markingStore->getMarking($workflow);
|
||||||
|
|
||||||
|
self::assertEquals(['initial' => 1], $marking->getPlaces());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSetMarking(): void
|
||||||
|
{
|
||||||
|
$markingStore = $this->buildMarkingStore();
|
||||||
|
$workflow = new EntityWorkflow();
|
||||||
|
|
||||||
|
$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]);
|
||||||
|
|
||||||
|
$currentStep = $workflow->getCurrentStep();
|
||||||
|
self::assertEquals('foo', $currentStep->getCurrentStep());
|
||||||
|
self::assertContains($email, $currentStep->getDestEmail());
|
||||||
|
self::assertContains($user1, $currentStep->getCcUser());
|
||||||
|
self::assertContains($user2, $currentStep->getDestUser());
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildMarkingStore(): EntityWorkflowMarkingStore
|
||||||
|
{
|
||||||
|
return new EntityWorkflowMarkingStore();
|
||||||
|
}
|
||||||
|
}
|
@ -49,6 +49,4 @@ interface EntityWorkflowHandlerInterface
|
|||||||
public function isObjectSupported(object $object): bool;
|
public function isObjectSupported(object $object): bool;
|
||||||
|
|
||||||
public function supports(EntityWorkflow $entityWorkflow, array $options = []): bool;
|
public function supports(EntityWorkflow $entityWorkflow, array $options = []): bool;
|
||||||
|
|
||||||
public function supportsFreeze(EntityWorkflow $entityWorkflow, array $options = []): bool;
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\MainBundle\Workflow;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
|
||||||
|
use Symfony\Component\Workflow\Marking;
|
||||||
|
use Symfony\Component\Workflow\MarkingStore\MarkingStoreInterface;
|
||||||
|
|
||||||
|
final readonly class EntityWorkflowMarkingStore implements MarkingStoreInterface
|
||||||
|
{
|
||||||
|
public function getMarking(object $subject): Marking
|
||||||
|
{
|
||||||
|
if (!$subject instanceof EntityWorkflow) {
|
||||||
|
throw new \UnexpectedValueException('Expected instance of EntityWorkflow');
|
||||||
|
}
|
||||||
|
$step = $subject->getCurrentStep();
|
||||||
|
|
||||||
|
return new Marking([$step->getCurrentStep() => 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setMarking(object $subject, Marking $marking, array $context = []): void
|
||||||
|
{
|
||||||
|
if (!$subject instanceof EntityWorkflow) {
|
||||||
|
throw new \UnexpectedValueException('Expected instance of EntityWorkflow');
|
||||||
|
}
|
||||||
|
|
||||||
|
$places = $marking->getPlaces();
|
||||||
|
if (1 < count($places)) {
|
||||||
|
throw new \LogicException('Expected maximum one place');
|
||||||
|
}
|
||||||
|
$next = array_keys($places)[0];
|
||||||
|
|
||||||
|
$transitionDTO = $context['context'] ?? null;
|
||||||
|
if (!$transitionDTO instanceof WorkflowTransitionContextDTO) {
|
||||||
|
throw new \UnexpectedValueException(sprintf('Expected instance of %s', WorkflowTransitionContextDTO::class));
|
||||||
|
}
|
||||||
|
|
||||||
|
$subject->setStep($next, $transitionDTO);
|
||||||
|
}
|
||||||
|
}
|
@ -21,31 +21,13 @@ use Symfony\Component\Workflow\Event\Event;
|
|||||||
use Symfony\Component\Workflow\Event\GuardEvent;
|
use Symfony\Component\Workflow\Event\GuardEvent;
|
||||||
use Symfony\Component\Workflow\TransitionBlocker;
|
use Symfony\Component\Workflow\TransitionBlocker;
|
||||||
|
|
||||||
class EntityWorkflowTransitionEventSubscriber implements EventSubscriberInterface
|
final readonly class EntityWorkflowTransitionEventSubscriber implements EventSubscriberInterface
|
||||||
{
|
{
|
||||||
public function __construct(private readonly LoggerInterface $chillLogger, private readonly Security $security, private readonly UserRender $userRender) {}
|
public function __construct(
|
||||||
|
private LoggerInterface $chillLogger,
|
||||||
public function addDests(Event $event): void
|
private Security $security,
|
||||||
{
|
private UserRender $userRender
|
||||||
if (!$event->getSubject() instanceof EntityWorkflow) {
|
) {}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @var EntityWorkflow $entityWorkflow */
|
|
||||||
$entityWorkflow = $event->getSubject();
|
|
||||||
|
|
||||||
foreach ($entityWorkflow->futureCcUsers as $user) {
|
|
||||||
$entityWorkflow->getCurrentStep()->addCcUser($user);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($entityWorkflow->futureDestUsers as $user) {
|
|
||||||
$entityWorkflow->getCurrentStep()->addDestUser($user);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($entityWorkflow->futureDestEmails as $email) {
|
|
||||||
$entityWorkflow->getCurrentStep()->addDestEmail($email);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getSubscribedEvents(): array
|
public static function getSubscribedEvents(): array
|
||||||
{
|
{
|
||||||
@ -53,7 +35,6 @@ class EntityWorkflowTransitionEventSubscriber implements EventSubscriberInterfac
|
|||||||
'workflow.transition' => 'onTransition',
|
'workflow.transition' => 'onTransition',
|
||||||
'workflow.completed' => [
|
'workflow.completed' => [
|
||||||
['markAsFinal', 2048],
|
['markAsFinal', 2048],
|
||||||
['addDests', 2048],
|
|
||||||
],
|
],
|
||||||
'workflow.guard' => [
|
'workflow.guard' => [
|
||||||
['guardEntityWorkflow', 0],
|
['guardEntityWorkflow', 0],
|
||||||
@ -99,6 +80,10 @@ class EntityWorkflowTransitionEventSubscriber implements EventSubscriberInterfac
|
|||||||
|
|
||||||
public function markAsFinal(Event $event): void
|
public function markAsFinal(Event $event): void
|
||||||
{
|
{
|
||||||
|
// NOTE: it is not possible to move this method to the marking store, because
|
||||||
|
// there is dependency between the Workflow definition and the MarkingStoreInterface (the workflow
|
||||||
|
// constructor need a MarkingStoreInterface)
|
||||||
|
|
||||||
if (!$event->getSubject() instanceof EntityWorkflow) {
|
if (!$event->getSubject() instanceof EntityWorkflow) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,13 @@ use Symfony\Component\Workflow\Registry;
|
|||||||
|
|
||||||
class NotificationOnTransition implements EventSubscriberInterface
|
class NotificationOnTransition implements EventSubscriberInterface
|
||||||
{
|
{
|
||||||
public function __construct(private readonly EntityManagerInterface $entityManager, private readonly \Twig\Environment $engine, private readonly MetadataExtractor $metadataExtractor, private readonly Security $security, private readonly Registry $registry) {}
|
public function __construct(
|
||||||
|
private readonly EntityManagerInterface $entityManager,
|
||||||
|
private readonly \Twig\Environment $engine,
|
||||||
|
private readonly MetadataExtractor $metadataExtractor,
|
||||||
|
private readonly Security $security,
|
||||||
|
private readonly Registry $registry
|
||||||
|
) {}
|
||||||
|
|
||||||
public static function getSubscribedEvents(): array
|
public static function getSubscribedEvents(): array
|
||||||
{
|
{
|
||||||
@ -85,7 +91,10 @@ class NotificationOnTransition implements EventSubscriberInterface
|
|||||||
'dest' => $subscriber,
|
'dest' => $subscriber,
|
||||||
'place' => $place,
|
'place' => $place,
|
||||||
'workflow' => $workflow,
|
'workflow' => $workflow,
|
||||||
'is_dest' => \in_array($subscriber->getId(), array_map(static fn (User $u) => $u->getId(), $entityWorkflow->futureDestUsers), true),
|
'is_dest' => \in_array($subscriber->getId(), array_map(
|
||||||
|
static fn (User $u) => $u->getId(),
|
||||||
|
$entityWorkflow->getCurrentStep()->getDestUser()->toArray()
|
||||||
|
), true),
|
||||||
];
|
];
|
||||||
|
|
||||||
$notification = new Notification();
|
$notification = new Notification();
|
||||||
|
@ -20,7 +20,13 @@ use Symfony\Component\Workflow\Registry;
|
|||||||
|
|
||||||
class SendAccessKeyEventSubscriber
|
class SendAccessKeyEventSubscriber
|
||||||
{
|
{
|
||||||
public function __construct(private readonly \Twig\Environment $engine, private readonly MetadataExtractor $metadataExtractor, private readonly Registry $registry, private readonly EntityWorkflowManager $entityWorkflowManager, private readonly MailerInterface $mailer) {}
|
public function __construct(
|
||||||
|
private readonly \Twig\Environment $engine,
|
||||||
|
private readonly MetadataExtractor $metadataExtractor,
|
||||||
|
private readonly Registry $registry,
|
||||||
|
private readonly EntityWorkflowManager $entityWorkflowManager,
|
||||||
|
private readonly MailerInterface $mailer
|
||||||
|
) {}
|
||||||
|
|
||||||
public function postPersist(EntityWorkflowStep $step): void
|
public function postPersist(EntityWorkflowStep $step): void
|
||||||
{
|
{
|
||||||
@ -32,7 +38,7 @@ class SendAccessKeyEventSubscriber
|
|||||||
);
|
);
|
||||||
$handler = $this->entityWorkflowManager->getHandler($entityWorkflow);
|
$handler = $this->entityWorkflowManager->getHandler($entityWorkflow);
|
||||||
|
|
||||||
foreach ($entityWorkflow->futureDestEmails as $emailAddress) {
|
foreach ($step->getDestEmail() as $emailAddress) {
|
||||||
$context = [
|
$context = [
|
||||||
'entity_workflow' => $entityWorkflow,
|
'entity_workflow' => $entityWorkflow,
|
||||||
'dest' => $emailAddress,
|
'dest' => $emailAddress,
|
||||||
|
@ -0,0 +1,74 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\MainBundle\Workflow;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Entity\User;
|
||||||
|
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
|
||||||
|
use Symfony\Component\Validator\Constraints as Assert;
|
||||||
|
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||||
|
use Symfony\Component\Workflow\Transition;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Context for a transition on an workflow entity.
|
||||||
|
*/
|
||||||
|
class WorkflowTransitionContextDTO
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* a list of future dest users for the next steps.
|
||||||
|
*
|
||||||
|
* This is in used in order to let controller inform who will be the future users which will validate
|
||||||
|
* the next step. This is necessary to perform some computation about the next users, before they are
|
||||||
|
* associated to the entity EntityWorkflowStep.
|
||||||
|
*
|
||||||
|
* @var array|User[]
|
||||||
|
*/
|
||||||
|
public array $futureDestUsers = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* a list of future cc users for the next steps.
|
||||||
|
*
|
||||||
|
* @var array|User[]
|
||||||
|
*/
|
||||||
|
public array $futureCcUsers = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* a list of future dest emails for the next steps.
|
||||||
|
*
|
||||||
|
* This is in used in order to let controller inform who will be the future emails which will validate
|
||||||
|
* the next step. This is necessary to perform some computation about the next emails, before they are
|
||||||
|
* associated to the entity EntityWorkflowStep.
|
||||||
|
*
|
||||||
|
* @var array|string[]
|
||||||
|
*/
|
||||||
|
public array $futureDestEmails = [];
|
||||||
|
|
||||||
|
public ?Transition $transition = null;
|
||||||
|
|
||||||
|
public string $comment = '';
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
public EntityWorkflow $entityWorkflow
|
||||||
|
) {}
|
||||||
|
|
||||||
|
#[Assert\Callback()]
|
||||||
|
public function validateCCUserIsNotInDest(ExecutionContextInterface $context, $payload): void
|
||||||
|
{
|
||||||
|
foreach ($this->futureDestUsers as $u) {
|
||||||
|
if (in_array($u, $this->futureCcUsers, true)) {
|
||||||
|
$context
|
||||||
|
->buildViolation('workflow.The user in cc cannot be a dest user in the same workflow step')
|
||||||
|
->atPath('ccUsers')
|
||||||
|
->addViolation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\Migrations\Main;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Schema\Schema;
|
||||||
|
use Doctrine\Migrations\AbstractMigration;
|
||||||
|
|
||||||
|
final class Version20240628095159 extends AbstractMigration
|
||||||
|
{
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return 'Add signatures to workflow';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function up(Schema $schema): void
|
||||||
|
{
|
||||||
|
$this->addSql('CREATE SEQUENCE chill_main_workflow_entity_step_signature_id_seq INCREMENT BY 1 MINVALUE 1 START 1');
|
||||||
|
$this->addSql('CREATE TABLE chill_main_workflow_entity_step_signature (id INT NOT NULL, step_id INT NOT NULL, '.
|
||||||
|
'state VARCHAR(50) NOT NULL, stateDate TIMESTAMP(0) WITH TIME ZONE DEFAULT NULL, signatureMetadata JSON DEFAULT \'[]\' NOT NULL,'.
|
||||||
|
' zoneSignatureIndex INT DEFAULT NULL, createdAt TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, updatedAt TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL,'.
|
||||||
|
' userSigner_id INT DEFAULT NULL, personSigner_id INT DEFAULT NULL, createdBy_id INT DEFAULT NULL, updatedBy_id INT DEFAULT NULL, PRIMARY KEY(id))');
|
||||||
|
$this->addSql('CREATE INDEX IDX_C47D4BA3D934E3A4 ON chill_main_workflow_entity_step_signature (userSigner_id)');
|
||||||
|
$this->addSql('CREATE INDEX IDX_C47D4BA3ADFFA293 ON chill_main_workflow_entity_step_signature (personSigner_id)');
|
||||||
|
$this->addSql('CREATE INDEX IDX_C47D4BA373B21E9C ON chill_main_workflow_entity_step_signature (step_id)');
|
||||||
|
$this->addSql('CREATE INDEX IDX_C47D4BA33174800F ON chill_main_workflow_entity_step_signature (createdBy_id)');
|
||||||
|
$this->addSql('CREATE INDEX IDX_C47D4BA365FF1AEC ON chill_main_workflow_entity_step_signature (updatedBy_id)');
|
||||||
|
$this->addSql('COMMENT ON COLUMN chill_main_workflow_entity_step_signature.stateDate IS \'(DC2Type:datetimetz_immutable)\'');
|
||||||
|
$this->addSql('COMMENT ON COLUMN chill_main_workflow_entity_step_signature.createdAt IS \'(DC2Type:datetime_immutable)\'');
|
||||||
|
$this->addSql('COMMENT ON COLUMN chill_main_workflow_entity_step_signature.updatedAt IS \'(DC2Type:datetime_immutable)\'');
|
||||||
|
$this->addSql('ALTER TABLE chill_main_workflow_entity_step_signature ADD CONSTRAINT FK_C47D4BA3D934E3A4 FOREIGN KEY (userSigner_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||||
|
$this->addSql('ALTER TABLE chill_main_workflow_entity_step_signature ADD CONSTRAINT FK_C47D4BA3ADFFA293 FOREIGN KEY (personSigner_id) REFERENCES chill_person_person (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||||
|
$this->addSql('ALTER TABLE chill_main_workflow_entity_step_signature ADD CONSTRAINT FK_C47D4BA373B21E9C FOREIGN KEY (step_id) REFERENCES chill_main_workflow_entity_step (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||||
|
$this->addSql('ALTER TABLE chill_main_workflow_entity_step_signature ADD CONSTRAINT FK_C47D4BA33174800F FOREIGN KEY (createdBy_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||||
|
$this->addSql('ALTER TABLE chill_main_workflow_entity_step_signature ADD CONSTRAINT FK_C47D4BA365FF1AEC FOREIGN KEY (updatedBy_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(Schema $schema): void
|
||||||
|
{
|
||||||
|
$this->addSql('DROP SEQUENCE chill_main_workflow_entity_step_signature_id_seq CASCADE');
|
||||||
|
$this->addSql('DROP TABLE chill_main_workflow_entity_step_signature');
|
||||||
|
}
|
||||||
|
}
|
@ -123,9 +123,4 @@ class AccompanyingPeriodWorkEvaluationDocumentWorkflowHandler implements EntityW
|
|||||||
{
|
{
|
||||||
return AccompanyingPeriodWorkEvaluationDocument::class === $entityWorkflow->getRelatedEntityClass();
|
return AccompanyingPeriodWorkEvaluationDocument::class === $entityWorkflow->getRelatedEntityClass();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function supportsFreeze(EntityWorkflow $entityWorkflow, array $options = []): bool
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -109,9 +109,4 @@ class AccompanyingPeriodWorkEvaluationWorkflowHandler implements EntityWorkflowH
|
|||||||
{
|
{
|
||||||
return AccompanyingPeriodWorkEvaluation::class === $entityWorkflow->getRelatedEntityClass();
|
return AccompanyingPeriodWorkEvaluation::class === $entityWorkflow->getRelatedEntityClass();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function supportsFreeze(EntityWorkflow $entityWorkflow, array $options = []): bool
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -116,9 +116,4 @@ class AccompanyingPeriodWorkWorkflowHandler implements EntityWorkflowHandlerInte
|
|||||||
{
|
{
|
||||||
return AccompanyingPeriodWork::class === $entityWorkflow->getRelatedEntityClass();
|
return AccompanyingPeriodWork::class === $entityWorkflow->getRelatedEntityClass();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function supportsFreeze(EntityWorkflow $entityWorkflow, array $options = []): bool
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
294
tests/app/config/packages/workflow_chill.yaml
Normal file
294
tests/app/config/packages/workflow_chill.yaml
Normal file
@ -0,0 +1,294 @@
|
|||||||
|
framework:
|
||||||
|
workflows:
|
||||||
|
vendee_internal:
|
||||||
|
type: state_machine
|
||||||
|
metadata:
|
||||||
|
related_entity:
|
||||||
|
- Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument
|
||||||
|
- Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork
|
||||||
|
- Chill\DocStoreBundle\Entity\AccompanyingCourseDocument
|
||||||
|
label:
|
||||||
|
fr: 'Suivi'
|
||||||
|
support_strategy: Chill\MainBundle\Workflow\RelatedEntityWorkflowSupportsStrategy
|
||||||
|
initial_marking: 'initial'
|
||||||
|
marking_store:
|
||||||
|
property: step
|
||||||
|
type: method
|
||||||
|
places:
|
||||||
|
initial:
|
||||||
|
metadata:
|
||||||
|
label:
|
||||||
|
fr: Étape initiale
|
||||||
|
attenteModification:
|
||||||
|
metadata:
|
||||||
|
label:
|
||||||
|
fr: En attente de modification du document
|
||||||
|
validationFilterInputLabels:
|
||||||
|
forward: {fr: Modification effectuée}
|
||||||
|
backward: {fr: Pas de modification effectuée}
|
||||||
|
neutral: {fr: Autre}
|
||||||
|
attenteMiseEnForme:
|
||||||
|
metadata:
|
||||||
|
label:
|
||||||
|
fr: En attente de mise en forme
|
||||||
|
validationFilterInputLabels:
|
||||||
|
forward: {fr: Mise en forme terminée}
|
||||||
|
backward: {fr: Pas de mise en forme effectuée}
|
||||||
|
neutral: {fr: Autre}
|
||||||
|
attenteVisa:
|
||||||
|
metadata:
|
||||||
|
label:
|
||||||
|
fr: En attente de visa
|
||||||
|
validationFilterInputLabels:
|
||||||
|
forward: {fr: Visa accordé}
|
||||||
|
backward: {fr: Visa refusé}
|
||||||
|
neutral: {fr: Autre}
|
||||||
|
attenteSignature:
|
||||||
|
metadata:
|
||||||
|
label:
|
||||||
|
fr: En attente de signature
|
||||||
|
validationFilterInputLabels:
|
||||||
|
forward: {fr: Signature accordée}
|
||||||
|
backward: {fr: Signature refusée}
|
||||||
|
neutral: {fr: Autre}
|
||||||
|
attenteTraitement:
|
||||||
|
metadata:
|
||||||
|
label:
|
||||||
|
fr: En attente de traitement
|
||||||
|
validationFilterInputLabels:
|
||||||
|
forward: {fr: Traitement terminé favorablement}
|
||||||
|
backward: {fr: Traitement terminé défavorablement}
|
||||||
|
neutral: {fr: Autre}
|
||||||
|
attenteEnvoi:
|
||||||
|
metadata:
|
||||||
|
label:
|
||||||
|
fr: En attente d'envoi
|
||||||
|
validationFilterInputLabels:
|
||||||
|
forward: {fr: Document envoyé}
|
||||||
|
backward: {fr: Document non envoyé}
|
||||||
|
neutral: {fr: Autre}
|
||||||
|
attenteValidationMiseEnForme:
|
||||||
|
metadata:
|
||||||
|
label:
|
||||||
|
fr: En attente de validation de la mise en forme
|
||||||
|
validationFilterInputLabels:
|
||||||
|
forward: {fr: Validation de la mise en forme}
|
||||||
|
backward: {fr: Refus de validation de la mise en forme}
|
||||||
|
neutral: {fr: Autre}
|
||||||
|
annule:
|
||||||
|
metadata:
|
||||||
|
isFinal: true
|
||||||
|
isFinalPositive: false
|
||||||
|
label:
|
||||||
|
fr: Annulé
|
||||||
|
final:
|
||||||
|
metadata:
|
||||||
|
isFinal: true
|
||||||
|
isFinalPositive: true
|
||||||
|
label:
|
||||||
|
fr: Finalisé
|
||||||
|
transitions:
|
||||||
|
# transition qui avancent
|
||||||
|
demandeModificationDocument:
|
||||||
|
from:
|
||||||
|
- initial
|
||||||
|
to: attenteModification
|
||||||
|
metadata:
|
||||||
|
label:
|
||||||
|
fr: Demande de modification du document
|
||||||
|
isForward: true
|
||||||
|
demandeMiseEnForme:
|
||||||
|
from:
|
||||||
|
- initial
|
||||||
|
- attenteModification
|
||||||
|
to: attenteMiseEnForme
|
||||||
|
metadata:
|
||||||
|
label:
|
||||||
|
fr: Demande de mise en forme
|
||||||
|
isForward: true
|
||||||
|
demandeValidationMiseEnForme:
|
||||||
|
from:
|
||||||
|
- attenteMiseEnForme
|
||||||
|
to: attenteValidationMiseEnForme
|
||||||
|
metadata:
|
||||||
|
label:
|
||||||
|
fr: Demande de validation de la mise en forme
|
||||||
|
isForward: true
|
||||||
|
demandeVisa:
|
||||||
|
from:
|
||||||
|
- initial
|
||||||
|
- attenteModification
|
||||||
|
- attenteMiseEnForme
|
||||||
|
- attenteValidationMiseEnForme
|
||||||
|
to: attenteVisa
|
||||||
|
metadata:
|
||||||
|
label:
|
||||||
|
fr: Demande de visa
|
||||||
|
isForward: true
|
||||||
|
demandeSignature:
|
||||||
|
from:
|
||||||
|
- initial
|
||||||
|
- attenteModification
|
||||||
|
- attenteMiseEnForme
|
||||||
|
- attenteValidationMiseEnForme
|
||||||
|
- attenteVisa
|
||||||
|
to: attenteSignature
|
||||||
|
metadata:
|
||||||
|
label: {fr: Demande de signature}
|
||||||
|
isForward: true
|
||||||
|
demandeTraitement:
|
||||||
|
from:
|
||||||
|
- initial
|
||||||
|
- attenteModification
|
||||||
|
- attenteMiseEnForme
|
||||||
|
- attenteValidationMiseEnForme
|
||||||
|
- attenteVisa
|
||||||
|
- attenteSignature
|
||||||
|
to: attenteTraitement
|
||||||
|
metadata:
|
||||||
|
label: {fr: Demande de traitement}
|
||||||
|
isForward: true
|
||||||
|
demandeEnvoi:
|
||||||
|
from:
|
||||||
|
- initial
|
||||||
|
- attenteModification
|
||||||
|
- attenteMiseEnForme
|
||||||
|
- attenteValidationMiseEnForme
|
||||||
|
- attenteVisa
|
||||||
|
- attenteSignature
|
||||||
|
- attenteTraitement
|
||||||
|
to: attenteEnvoi
|
||||||
|
metadata:
|
||||||
|
label: {fr: Demande d'envoi}
|
||||||
|
isForward: true
|
||||||
|
annulation:
|
||||||
|
from:
|
||||||
|
- initial
|
||||||
|
- attenteModification
|
||||||
|
- attenteMiseEnForme
|
||||||
|
- attenteValidationMiseEnForme
|
||||||
|
- attenteVisa
|
||||||
|
- attenteSignature
|
||||||
|
- attenteTraitement
|
||||||
|
- attenteEnvoi
|
||||||
|
to: annule
|
||||||
|
metadata:
|
||||||
|
label: {fr: Annulation}
|
||||||
|
isForward: false
|
||||||
|
# transitions qui répètent l'étape
|
||||||
|
demandeMiseEnFormeSupplementaire:
|
||||||
|
from:
|
||||||
|
- attenteMiseEnForme
|
||||||
|
- attenteValidationMiseEnForme
|
||||||
|
to: attenteMiseEnForme
|
||||||
|
metadata:
|
||||||
|
label: {fr: Demande de mise en forme supplémentaire}
|
||||||
|
demandeVisaSupplementaire:
|
||||||
|
from:
|
||||||
|
- attenteVisa
|
||||||
|
to: attenteVisa
|
||||||
|
metadata:
|
||||||
|
label: {fr: Demande de visa supplémentaire}
|
||||||
|
isForward: true
|
||||||
|
demandeSignatureSupplementaire:
|
||||||
|
from:
|
||||||
|
- attenteSignature
|
||||||
|
to: attenteSignature
|
||||||
|
metadata:
|
||||||
|
label: {fr: Demande de signature supplémentaire}
|
||||||
|
demandeTraitementSupplementaire:
|
||||||
|
from:
|
||||||
|
- attenteTraitement
|
||||||
|
to: attenteTraitement
|
||||||
|
metadata:
|
||||||
|
label: {fr: Demande de traitement supplémentaire}
|
||||||
|
# transitions qui renvoient vers une étape précédente
|
||||||
|
refusEtModificationDocument:
|
||||||
|
from:
|
||||||
|
- attenteVisa
|
||||||
|
- attenteSignature
|
||||||
|
- attenteTraitement
|
||||||
|
- attenteEnvoi
|
||||||
|
to: attenteModification
|
||||||
|
metadata:
|
||||||
|
label:
|
||||||
|
fr: Refus et demande de modification du document
|
||||||
|
isForward: false
|
||||||
|
refusEtDemandeMiseEnForme:
|
||||||
|
from:
|
||||||
|
- attenteVisa
|
||||||
|
- attenteSignature
|
||||||
|
- attenteTraitement
|
||||||
|
- attenteEnvoi
|
||||||
|
to: attenteMiseEnForme
|
||||||
|
metadata:
|
||||||
|
label: {fr: Refus et demande de mise en forme}
|
||||||
|
isForward: false
|
||||||
|
refusEtDemandeVisa:
|
||||||
|
from:
|
||||||
|
- attenteSignature
|
||||||
|
- attenteTraitement
|
||||||
|
- attenteEnvoi
|
||||||
|
to: attenteVisa
|
||||||
|
metadata:
|
||||||
|
label: {fr: Refus et demande de visa}
|
||||||
|
isForward: false
|
||||||
|
refusEtDemandeSignature:
|
||||||
|
from:
|
||||||
|
- attenteTraitement
|
||||||
|
- attenteEnvoi
|
||||||
|
to: attenteSignature
|
||||||
|
metadata:
|
||||||
|
label: {fr: Refus et demande de signature}
|
||||||
|
isForward: false
|
||||||
|
refusEtDemandeTraitement:
|
||||||
|
from:
|
||||||
|
- attenteEnvoi
|
||||||
|
to: attenteTraitement
|
||||||
|
metadata:
|
||||||
|
label: {fr: Refus et demande de traitement}
|
||||||
|
isForward: false
|
||||||
|
# transition vers final
|
||||||
|
initialToFinal:
|
||||||
|
from:
|
||||||
|
- initial
|
||||||
|
to: final
|
||||||
|
metadata:
|
||||||
|
label: {fr: Clotûre immédiate et cloture positive}
|
||||||
|
isForward: true
|
||||||
|
attenteMiseEnFormeToFinal:
|
||||||
|
from:
|
||||||
|
- attenteMiseEnForme
|
||||||
|
- attenteValidationMiseEnForme
|
||||||
|
to: final
|
||||||
|
metadata:
|
||||||
|
label: {fr: Mise en forme terminée et cloture positive}
|
||||||
|
isForward: true
|
||||||
|
attenteVisaToFinal:
|
||||||
|
from:
|
||||||
|
- attenteVisa
|
||||||
|
to: final
|
||||||
|
metadata:
|
||||||
|
label: {fr: Accorde le visa et cloture positive}
|
||||||
|
isForward: true
|
||||||
|
attenteSignatureToFinal:
|
||||||
|
from:
|
||||||
|
- attenteSignature
|
||||||
|
to: final
|
||||||
|
metadata:
|
||||||
|
label: {fr: Accorde la signature et cloture positive}
|
||||||
|
isForward: true
|
||||||
|
attenteTraitementToFinal:
|
||||||
|
from:
|
||||||
|
- attenteTraitement
|
||||||
|
to: final
|
||||||
|
metadata:
|
||||||
|
label: {fr: Traitement terminé et cloture postive}
|
||||||
|
isForward: true
|
||||||
|
attenteEnvoiToFinal:
|
||||||
|
from:
|
||||||
|
- attenteEnvoi
|
||||||
|
to: final
|
||||||
|
metadata:
|
||||||
|
label: {fr: Envoyé et cloture postive}
|
||||||
|
isForward: true
|
Loading…
x
Reference in New Issue
Block a user