mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 18:44:08 +00:00
Add PDF signature zone availability checks
Introduce `PDFSignatureZoneAvailable` service to check available PDF signature zones. Updated `WorkflowAddSignatureController` to use the new service. Added unit tests to verify the correctness of the functionality.
This commit is contained in:
parent
c6a6d76790
commit
e17203ca3a
@ -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\DocStoreBundle\Service\Signature;
|
||||||
|
|
||||||
|
use Chill\DocStoreBundle\Service\StoredObjectManagerInterface;
|
||||||
|
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
|
||||||
|
use Chill\MainBundle\Entity\Workflow\EntityWorkflowSignatureStateEnum;
|
||||||
|
use Chill\MainBundle\Entity\Workflow\EntityWorkflowStep;
|
||||||
|
use Chill\MainBundle\Entity\Workflow\EntityWorkflowStepSignature;
|
||||||
|
use Chill\MainBundle\Workflow\EntityWorkflowManager;
|
||||||
|
|
||||||
|
class PDFSignatureZoneAvailable
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly EntityWorkflowManager $entityWorkflowManager,
|
||||||
|
private readonly PDFSignatureZoneParser $pdfSignatureZoneParser,
|
||||||
|
private readonly StoredObjectManagerInterface $storedObjectManager,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return list<PDFSignatureZone>
|
||||||
|
*/
|
||||||
|
public function getAvailableSignatureZones(EntityWorkflow $entityWorkflow): array
|
||||||
|
{
|
||||||
|
$storedObject = $this->entityWorkflowManager->getAssociatedStoredObject($entityWorkflow);
|
||||||
|
|
||||||
|
if (null === $storedObject) {
|
||||||
|
throw new \RuntimeException('No stored object found');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('application/pdf' !== $storedObject->getType()) {
|
||||||
|
throw new \RuntimeException('Only PDF documents are supported');
|
||||||
|
}
|
||||||
|
|
||||||
|
$zones = $this->pdfSignatureZoneParser->findSignatureZones($this->storedObjectManager->read($storedObject));
|
||||||
|
$signatureZonesIndexes = array_map(
|
||||||
|
fn (EntityWorkflowStepSignature $step) => $step->getZoneSignatureIndex(),
|
||||||
|
$this->collectSignaturesInUse($entityWorkflow)
|
||||||
|
);
|
||||||
|
|
||||||
|
return array_filter($zones, fn (PDFSignatureZone $zone) => !in_array($zone->index, $signatureZonesIndexes, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return list<EntityWorkflowStepSignature>
|
||||||
|
*/
|
||||||
|
private function collectSignaturesInUse(EntityWorkflow $entityWorkflow): array
|
||||||
|
{
|
||||||
|
return array_reduce($entityWorkflow->getSteps()->toArray(), function (array $result, EntityWorkflowStep $step) {
|
||||||
|
$current = [...$result];
|
||||||
|
foreach ($step->getSignatures() as $signature) {
|
||||||
|
if (EntityWorkflowSignatureStateEnum::SIGNED === $signature->getState()) {
|
||||||
|
$current[] = $signature;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $current;
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,76 @@
|
|||||||
|
<?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 Tests\Service\Signature;
|
||||||
|
|
||||||
|
use Chill\DocStoreBundle\Entity\StoredObject;
|
||||||
|
use Chill\DocStoreBundle\Service\Signature\PDFPage;
|
||||||
|
use Chill\DocStoreBundle\Service\Signature\PDFSignatureZone;
|
||||||
|
use Chill\DocStoreBundle\Service\Signature\PDFSignatureZoneAvailable;
|
||||||
|
use Chill\DocStoreBundle\Service\Signature\PDFSignatureZoneParser;
|
||||||
|
use Chill\DocStoreBundle\Service\StoredObjectManagerInterface;
|
||||||
|
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
|
||||||
|
use Chill\MainBundle\Entity\Workflow\EntityWorkflowSignatureStateEnum;
|
||||||
|
use Chill\MainBundle\Workflow\EntityWorkflowManager;
|
||||||
|
use Chill\MainBundle\Workflow\WorkflowTransitionContextDTO;
|
||||||
|
use Chill\PersonBundle\Entity\Person;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Prophecy\PhpUnit\ProphecyTrait;
|
||||||
|
use Symfony\Component\Clock\MockClock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* @coversNothing
|
||||||
|
*/
|
||||||
|
class PDFSignatureZoneAvailableTest extends TestCase
|
||||||
|
{
|
||||||
|
use ProphecyTrait;
|
||||||
|
|
||||||
|
public function testGetAvailableSignatureZones(): void
|
||||||
|
{
|
||||||
|
$clock = new MockClock();
|
||||||
|
|
||||||
|
$storedObject = new StoredObject();
|
||||||
|
$storedObject->registerVersion(type: 'application/pdf');
|
||||||
|
|
||||||
|
$entityWorkflow = new EntityWorkflow();
|
||||||
|
$dto1 = new WorkflowTransitionContextDTO($entityWorkflow);
|
||||||
|
$dto1->futurePersonSignatures[] = new Person();
|
||||||
|
$entityWorkflow->setStep('step1', $dto1, 'transition1', $clock->now());
|
||||||
|
$signature = $entityWorkflow->getCurrentStep()->getSignatures()->first();
|
||||||
|
$signature->setZoneSignatureIndex(1)->setState(EntityWorkflowSignatureStateEnum::SIGNED);
|
||||||
|
|
||||||
|
$entityWorkflowManager = $this->prophesize(EntityWorkflowManager::class);
|
||||||
|
$entityWorkflowManager->getAssociatedStoredObject($entityWorkflow)->willReturn($storedObject);
|
||||||
|
|
||||||
|
$storedObjectManager = $this->prophesize(StoredObjectManagerInterface::class);
|
||||||
|
$storedObjectManager->read($storedObject)->willReturn('fake-content');
|
||||||
|
|
||||||
|
$parser = $this->prophesize(PDFSignatureZoneParser::class);
|
||||||
|
$parser->findSignatureZones('fake-content')->willReturn([
|
||||||
|
$zone1 = new PDFSignatureZone(1, 0.0, 10.0, 20.0, 20.0, new PDFPage(1, 500, 500)),
|
||||||
|
$zone2 = new PDFSignatureZone(2, 0.0, 10.0, 20.0, 20.0, new PDFPage(1, 500, 500)),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$filter = new PDFSignatureZoneAvailable(
|
||||||
|
$entityWorkflowManager->reveal(),
|
||||||
|
$parser->reveal(),
|
||||||
|
$storedObjectManager->reveal(),
|
||||||
|
);
|
||||||
|
|
||||||
|
$actual = $filter->getAvailableSignatureZones($entityWorkflow);
|
||||||
|
|
||||||
|
self::assertNotContains($zone1, $actual);
|
||||||
|
self::assertContains($zone2, $actual);
|
||||||
|
self::assertCount(1, $actual, 'there should be only one remaining zone');
|
||||||
|
}
|
||||||
|
}
|
@ -11,11 +11,9 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Chill\MainBundle\Controller;
|
namespace Chill\MainBundle\Controller;
|
||||||
|
|
||||||
use Chill\DocStoreBundle\Service\Signature\PDFSignatureZoneParser;
|
use Chill\DocStoreBundle\Service\Signature\PDFSignatureZoneAvailable;
|
||||||
use Chill\DocStoreBundle\Service\StoredObjectManagerInterface;
|
|
||||||
use Chill\MainBundle\Entity\Workflow\EntityWorkflowStepSignature;
|
use Chill\MainBundle\Entity\Workflow\EntityWorkflowStepSignature;
|
||||||
use Chill\MainBundle\Workflow\EntityWorkflowManager;
|
use Chill\MainBundle\Workflow\EntityWorkflowManager;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||||
@ -23,21 +21,18 @@ use Symfony\Component\Routing\Annotation\Route;
|
|||||||
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
||||||
use Twig\Environment;
|
use Twig\Environment;
|
||||||
|
|
||||||
final class WorkflowAddSignatureController
|
final readonly class WorkflowAddSignatureController
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly EntityManagerInterface $entityManager,
|
private EntityWorkflowManager $entityWorkflowManager,
|
||||||
private readonly EntityWorkflowManager $entityWorkflowManager,
|
private PDFSignatureZoneAvailable $PDFSignatureZoneAvailable,
|
||||||
private readonly StoredObjectManagerInterface $storedObjectManager,
|
private NormalizerInterface $normalizer,
|
||||||
private readonly PDFSignatureZoneParser $PDFSignatureZoneParser,
|
private Environment $twig
|
||||||
private readonly NormalizerInterface $normalizer,
|
|
||||||
private readonly Environment $twig
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
#[Route(path: '/{_locale}/main/workflow/signature/{signature_id}/sign', name: 'chill_main_workflow_signature', methods: 'GET')]
|
#[Route(path: '/{_locale}/main/workflow/signature/{id}/sign', name: 'chill_main_workflow_signature', methods: 'GET')]
|
||||||
public function __invoke(int $signature_id, Request $request, WorkflowController $workflowController): Response
|
public function __invoke(EntityWorkflowStepSignature $signature, Request $request): Response
|
||||||
{
|
{
|
||||||
$signature = $this->entityManager->getRepository(EntityWorkflowStepSignature::class)->find($signature_id);
|
|
||||||
$entityWorkflow = $signature->getStep()->getEntityWorkflow();
|
$entityWorkflow = $signature->getStep()->getEntityWorkflow();
|
||||||
|
|
||||||
$storedObject = $this->entityWorkflowManager->getAssociatedStoredObject($entityWorkflow);
|
$storedObject = $this->entityWorkflowManager->getAssociatedStoredObject($entityWorkflow);
|
||||||
@ -45,20 +40,18 @@ final class WorkflowAddSignatureController
|
|||||||
throw new NotFoundHttpException('No stored object found');
|
throw new NotFoundHttpException('No stored object found');
|
||||||
}
|
}
|
||||||
|
|
||||||
$zones = [];
|
$zones = $this->PDFSignatureZoneAvailable->getAvailableSignatureZones($entityWorkflow);
|
||||||
$content = $this->storedObjectManager->read($storedObject);
|
|
||||||
if (null != $content) {
|
|
||||||
$zones = $this->PDFSignatureZoneParser->findSignatureZones($content);
|
|
||||||
}
|
|
||||||
|
|
||||||
$signatureClient = [];
|
$signatureClient = [];
|
||||||
$signatureClient['id'] = $signature->getId();
|
$signatureClient['id'] = $signature->getId();
|
||||||
$signatureClient['storedObject'] = $this->normalizer->normalize($storedObject, 'json');
|
$signatureClient['storedObject'] = $this->normalizer->normalize($storedObject, 'json');
|
||||||
$signatureClient['zones'] = $zones;
|
$signatureClient['zones'] = $zones;
|
||||||
|
|
||||||
return new Response($this->twig->render(
|
return new Response(
|
||||||
|
$this->twig->render(
|
||||||
'@ChillMain/Workflow/_signature_sign.html.twig',
|
'@ChillMain/Workflow/_signature_sign.html.twig',
|
||||||
['signature' => $signatureClient]
|
['signature' => $signatureClient]
|
||||||
));
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ use Chill\MainBundle\Workflow\Validator\EntityWorkflowCreation;
|
|||||||
use Chill\MainBundle\Workflow\WorkflowTransitionContextDTO;
|
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\Selectable;
|
||||||
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;
|
||||||
@ -52,12 +53,12 @@ class EntityWorkflow implements TrackCreationInterface, TrackUpdateInterface
|
|||||||
private int $relatedEntityId;
|
private int $relatedEntityId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Collection<int, EntityWorkflowStep>
|
* @var Collection<int, EntityWorkflowStep>&Selectable<int, EntityWorkflowStep>
|
||||||
*/
|
*/
|
||||||
#[Assert\Valid(traverse: true)]
|
#[Assert\Valid(traverse: true)]
|
||||||
#[ORM\OneToMany(mappedBy: 'entityWorkflow', targetEntity: EntityWorkflowStep::class, cascade: ['persist'], orphanRemoval: true)]
|
#[ORM\OneToMany(mappedBy: 'entityWorkflow', targetEntity: EntityWorkflowStep::class, cascade: ['persist'], orphanRemoval: true)]
|
||||||
#[ORM\OrderBy(['transitionAt' => \Doctrine\Common\Collections\Criteria::ASC, 'id' => 'ASC'])]
|
#[ORM\OrderBy(['transitionAt' => \Doctrine\Common\Collections\Criteria::ASC, 'id' => 'ASC'])]
|
||||||
private Collection $steps;
|
private Collection&Selectable $steps;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var array|EntityWorkflowStep[]|null
|
* @var array|EntityWorkflowStep[]|null
|
||||||
@ -242,7 +243,7 @@ class EntityWorkflow implements TrackCreationInterface, TrackUpdateInterface
|
|||||||
throw new \RuntimeException();
|
throw new \RuntimeException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getSteps(): ArrayCollection|Collection
|
public function getSteps(): Collection&Selectable
|
||||||
{
|
{
|
||||||
return $this->steps;
|
return $this->steps;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,72 @@
|
|||||||
|
<?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\Controller;
|
||||||
|
|
||||||
|
use Chill\DocStoreBundle\Entity\StoredObject;
|
||||||
|
use Chill\DocStoreBundle\Service\Signature\PDFPage;
|
||||||
|
use Chill\DocStoreBundle\Service\Signature\PDFSignatureZone;
|
||||||
|
use Chill\DocStoreBundle\Service\Signature\PDFSignatureZoneAvailable;
|
||||||
|
use Chill\MainBundle\Controller\WorkflowAddSignatureController;
|
||||||
|
use Chill\MainBundle\Entity\User;
|
||||||
|
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
|
||||||
|
use Chill\MainBundle\Workflow\EntityWorkflowManager;
|
||||||
|
use Chill\MainBundle\Workflow\WorkflowTransitionContextDTO;
|
||||||
|
use Chill\PersonBundle\Entity\Person;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
||||||
|
use Twig\Environment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* @coversNothing
|
||||||
|
*/
|
||||||
|
class WorkflowAddSignatureControllerTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testAddSignature(): void
|
||||||
|
{
|
||||||
|
$storedObject = new StoredObject();
|
||||||
|
$entityWorkflow = new EntityWorkflow();
|
||||||
|
$stepTransition = new WorkflowTransitionContextDTO($entityWorkflow);
|
||||||
|
$stepTransition->futurePersonSignatures[] = new Person();
|
||||||
|
$entityWorkflow->setStep('step_signature', $stepTransition, 'to_signature', new \DateTimeImmutable('now'), new User());
|
||||||
|
|
||||||
|
$signature = $entityWorkflow->getCurrentStep()->getSignatures()->first();
|
||||||
|
|
||||||
|
$entityWorkflowManager = $this->createMock(EntityWorkflowManager::class);
|
||||||
|
$entityWorkflowManager->method('getAssociatedStoredObject')
|
||||||
|
->with($entityWorkflow)
|
||||||
|
->willReturn($storedObject);
|
||||||
|
|
||||||
|
$pdfSignatureZoneAvailable = $this->createMock(PDFSignatureZoneAvailable::class);
|
||||||
|
$pdfSignatureZoneAvailable->method('getAvailableSignatureZones')->withAnyParameters()
|
||||||
|
->willReturn([
|
||||||
|
new PDFSignatureZone(1, 0.0, 0.0, 100, 100, new PDFPage(1, 500.0, 500.0)),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$normalizer = $this->createMock(NormalizerInterface::class);
|
||||||
|
$normalizer->method('normalize')->withAnyParameters()
|
||||||
|
->willReturn([]);
|
||||||
|
|
||||||
|
$twig = $this->createMock(Environment::class);
|
||||||
|
$twig->method('render')->with('@ChillMain/Workflow/_signature_sign.html.twig', $this->isType('array'))
|
||||||
|
->willReturn('ok');
|
||||||
|
|
||||||
|
$controller = new WorkflowAddSignatureController($entityWorkflowManager, $pdfSignatureZoneAvailable, $normalizer, $twig);
|
||||||
|
|
||||||
|
$actual = $controller($signature, new Request());
|
||||||
|
|
||||||
|
self::assertEquals(200, $actual->getStatusCode());
|
||||||
|
self::assertEquals('ok', $actual->getContent());
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user