Add signature functionality to workflow entities

Created new files to add signature functionality to the workflow entities, including signature state enums and signature metadata. Added these changes to the migration script as well. Updated EntityWorkflowStep to include a collection for signatures.
This commit is contained in:
2024-06-28 11:58:39 +02:00
parent c9d54a5fea
commit 3db4fff80d
7 changed files with 684 additions and 2 deletions

View File

@@ -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';
}

View File

@@ -42,19 +42,25 @@ class EntityWorkflowStep
private array $destEmail = [];
/**
* @var Collection<User>
* @var Collection<int, User>
*/
#[ORM\ManyToMany(targetEntity: User::class)]
#[ORM\JoinTable(name: 'chill_main_workflow_entity_step_user')]
private Collection $destUser;
/**
* @var Collection<User>
* @var Collection<int, User>
*/
#[ORM\ManyToMany(targetEntity: User::class)]
#[ORM\JoinTable(name: 'chill_main_workflow_entity_step_user_by_accesskey')]
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')]
private ?EntityWorkflow $entityWorkflow = null;
@@ -97,6 +103,7 @@ class EntityWorkflowStep
$this->ccUser = new ArrayCollection();
$this->destUser = new ArrayCollection();
$this->destUserByAccessKey = new ArrayCollection();
$this->signatures = new ArrayCollection();
$this->accessKey = bin2hex(openssl_random_pseudo_bytes(32));
}
@@ -136,6 +143,29 @@ class EntityWorkflowStep
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
{
return $this->accessKey;
@@ -198,6 +228,14 @@ class EntityWorkflowStep
return $this->entityWorkflow;
}
/**
* @return Collection<int, EntityWorkflowStepSignature>
*/
public function getSignatures(): Collection
{
return $this->signatures;
}
public function getId(): ?int
{
return $this->id;

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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());
}
}

View File

@@ -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');
}
}