mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 18:44:08 +00:00
Merge branch 'signature-app/OP722-cancel-refuse-signature' into 'signature-app-master'
Adjust logic for removing the hold on a workflow only by user who owns the... Closes #307 See merge request Chill-Projet/chill-bundles!738
This commit is contained in:
commit
42438d5bb5
@ -0,0 +1,98 @@
|
||||
<?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\Controller;
|
||||
|
||||
use Chill\MainBundle\Entity\Workflow\EntityWorkflowStepSignature;
|
||||
use Chill\MainBundle\Routing\ChillUrlGeneratorInterface;
|
||||
use Chill\MainBundle\Security\Authorization\EntityWorkflowStepSignatureVoter;
|
||||
use Chill\MainBundle\Workflow\SignatureStepStateChanger;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
||||
use Symfony\Component\Form\FormFactoryInterface;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
use Twig\Environment;
|
||||
|
||||
final readonly class WorkflowSignatureCancelController
|
||||
{
|
||||
public function __construct(
|
||||
private EntityManagerInterface $entityManager,
|
||||
private Security $security,
|
||||
private FormFactoryInterface $formFactory,
|
||||
private Environment $twig,
|
||||
private SignatureStepStateChanger $signatureStepStateChanger,
|
||||
private ChillUrlGeneratorInterface $chillUrlGenerator,
|
||||
) {}
|
||||
|
||||
#[Route('/{_locale}/main/workflow/signature/{id}/cancel', name: 'chill_main_workflow_signature_cancel')]
|
||||
public function cancelSignature(EntityWorkflowStepSignature $signature, Request $request): Response
|
||||
{
|
||||
return $this->markSignatureAction(
|
||||
$signature,
|
||||
$request,
|
||||
EntityWorkflowStepSignatureVoter::CANCEL,
|
||||
function (EntityWorkflowStepSignature $signature) {$this->signatureStepStateChanger->markSignatureAsCanceled($signature); },
|
||||
'@ChillMain/WorkflowSignature/cancel.html.twig',
|
||||
);
|
||||
}
|
||||
|
||||
#[Route('/{_locale}/main/workflow/signature/{id}/reject', name: 'chill_main_workflow_signature_reject')]
|
||||
public function rejectSignature(EntityWorkflowStepSignature $signature, Request $request): Response
|
||||
{
|
||||
return $this->markSignatureAction(
|
||||
$signature,
|
||||
$request,
|
||||
EntityWorkflowStepSignatureVoter::REJECT,
|
||||
function (EntityWorkflowStepSignature $signature) {$this->signatureStepStateChanger->markSignatureAsRejected($signature); },
|
||||
'@ChillMain/WorkflowSignature/reject.html.twig',
|
||||
);
|
||||
}
|
||||
|
||||
private function markSignatureAction(
|
||||
EntityWorkflowStepSignature $signature,
|
||||
Request $request,
|
||||
string $permissionAttribute,
|
||||
callable $markSignature,
|
||||
string $template,
|
||||
): Response {
|
||||
|
||||
if (!$this->security->isGranted($permissionAttribute, $signature)) {
|
||||
throw new AccessDeniedHttpException('not allowed to cancel this signature');
|
||||
}
|
||||
|
||||
$form = $this->formFactory->create();
|
||||
$form->add('confirm', SubmitType::class, ['label' => 'Confirm']);
|
||||
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
$markSignature($signature);
|
||||
$this->entityManager->flush();
|
||||
|
||||
return new RedirectResponse(
|
||||
$this->chillUrlGenerator->returnPathOr('chill_main_workflow_show', ['id' => $signature->getStep()->getEntityWorkflow()->getId()])
|
||||
);
|
||||
}
|
||||
|
||||
return
|
||||
new Response(
|
||||
$this->twig->render(
|
||||
$template,
|
||||
['form' => $form->createView(), 'signature' => $signature]
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
@ -161,6 +161,16 @@ class EntityWorkflowStepSignature implements TrackCreationInterface, TrackUpdate
|
||||
return EntityWorkflowSignatureStateEnum::PENDING == $this->getState();
|
||||
}
|
||||
|
||||
public function isCanceled(): bool
|
||||
{
|
||||
return EntityWorkflowSignatureStateEnum::CANCELED === $this->getState();
|
||||
}
|
||||
|
||||
public function isRejected(): bool
|
||||
{
|
||||
return EntityWorkflowSignatureStateEnum::REJECTED === $this->getState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether all signatures associated with a given workflow step are not pending.
|
||||
*
|
||||
|
@ -3,7 +3,7 @@
|
||||
<div class="container">
|
||||
{% for s in signatures %}
|
||||
<div class="row row-hover align-items-center">
|
||||
<div class="col-sm-12 col-md-8">
|
||||
<div class="col-sm-12 col-md-5">
|
||||
{% if s.signerKind == 'person' %}
|
||||
{% include '@ChillMain/OnTheFly/_insert_vue_onthefly.html.twig' with {
|
||||
action: 'show', displayBadge: true,
|
||||
@ -19,21 +19,32 @@
|
||||
} %}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-4">
|
||||
<div class="col-sm-12 col-md-7 text-end">
|
||||
{% if s.isSigned %}
|
||||
<span class="text-end">{{ 'workflow.signature_zone.has_signed_statement'|trans({ 'datetime' : s.stateDate }) }}</span>
|
||||
<span class="text-end">{{ 'workflow.signature.signed_statement'|trans({ 'datetime' : s.stateDate }) }}</span>
|
||||
{% elseif s.isCanceled %}
|
||||
<span class="text-end">{{ 'workflow.signature.canceled_statement'|trans({ 'datetime' : s.stateDate }) }}</span>
|
||||
{% elseif s.isRejected%}
|
||||
<span class="text-end">{{ 'workflow.signature.rejected_statement'|trans({ 'datetime' : s.stateDate }) }}</span>
|
||||
{% else %}
|
||||
{% if is_granted('CHILL_MAIN_ENTITY_WORKFLOW_SIGNATURE_SIGN', s) %}
|
||||
{% if (is_granted('CHILL_MAIN_ENTITY_WORKFLOW_SIGNATURE_CANCEL', s) or is_granted('CHILL_MAIN_ENTITY_WORKFLOW_SIGNATURE_SIGN', s)) %}
|
||||
<ul class="record_actions slim">
|
||||
<li>
|
||||
<a class="btn btn-misc" href="{{ chill_path_add_return_path('chill_main_workflow_signature_metadata', { 'signature_id': s.id}) }}"><i class="fa fa-pencil-square-o"></i> {{ 'workflow.signature_zone.button_sign'|trans }}</a>
|
||||
{% if s.state is same as('signed') %}
|
||||
<p class="updatedBy">{{ s.stateDate }}</p>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% if is_granted('CHILL_MAIN_ENTITY_WORKFLOW_SIGNATURE_REJECT', s) %}
|
||||
<li>
|
||||
<a class="btn btn-remove" href="{{ chill_path_add_return_path('chill_main_workflow_signature_reject', { 'id': s.id}) }}">{{ 'workflow.signature_zone.button_reject'|trans }}</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if is_granted('CHILL_MAIN_ENTITY_WORKFLOW_SIGNATURE_CANCEL', s) %}
|
||||
<li>
|
||||
<a class="btn btn-misc" href="{{ chill_path_add_return_path('chill_main_workflow_signature_cancel', { 'id': s.id}) }}">{{ 'workflow.signature_zone.button_cancel'|trans }}</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if is_granted('CHILL_MAIN_ENTITY_WORKFLOW_SIGNATURE_SIGN', s) %}
|
||||
<li>
|
||||
<a class="btn btn-misc" href="{{ chill_path_add_return_path('chill_main_workflow_signature_metadata', { 'signature_id': s.id}) }}"><i class="fa fa-pencil-square-o"></i> {{ 'workflow.signature_zone.button_sign'|trans }}</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<span class="text-end">{{ 'workflow.waiting_for_signature'|trans }}</span>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
@ -0,0 +1,20 @@
|
||||
{% extends '@ChillMain/layout.html.twig' %}
|
||||
|
||||
{% block title %}{{ 'workflow.signature.cancel_signature_of'|trans({ '%signer%': signature.signer|chill_entity_render_string }) }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{{ block('title') }}</h1>
|
||||
|
||||
<p>{{ 'workflow.signature.cancel_are_you_sure'|trans({'%signer%': signature.signer|chill_entity_render_string}) }}</p>
|
||||
|
||||
{{ form_start(form) }}
|
||||
<ul class="record_actions sticky-form-buttons">
|
||||
<li class="cancel">
|
||||
<a class="btn btn-cancel" href="{{ chill_return_path_or('chill_main_workflow_show', {'id': signature.step.entityWorkflow.id}) }}">{{ 'Cancel'|trans }}</a>
|
||||
</li>
|
||||
<li>
|
||||
{{ form_widget(form.confirm, {'attr': {'class': 'btn btn-misc'}}) }}
|
||||
</li>
|
||||
</ul>
|
||||
{{ form_end(form) }}
|
||||
{% endblock %}
|
@ -0,0 +1,20 @@
|
||||
{% extends '@ChillMain/layout.html.twig' %}
|
||||
|
||||
{% block title %}{{ 'workflow.signature.reject_signature_of'|trans({ '%signer%': signature.signer|chill_entity_render_string }) }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{{ block('title') }}</h1>
|
||||
|
||||
<p>{{ 'workflow.signature.reject_are_you_sure'|trans({'%signer%': signature.signer|chill_entity_render_string}) }}</p>
|
||||
|
||||
{{ form_start(form) }}
|
||||
<ul class="record_actions sticky-form-buttons">
|
||||
<li class="cancel">
|
||||
<a class="btn btn-cancel" href="{{ chill_return_path_or('chill_main_workflow_show', {'id': signature.step.entityWorkflow.id}) }}">{{ 'Cancel'|trans }}</a>
|
||||
</li>
|
||||
<li>
|
||||
{{ form_widget(form.confirm, {'attr': {'class': 'btn btn-misc'}}) }}
|
||||
</li>
|
||||
</ul>
|
||||
{{ form_end(form) }}
|
||||
{% endblock %}
|
43
src/Bundle/ChillMainBundle/Routing/ChillUrlGenerator.php
Normal file
43
src/Bundle/ChillMainBundle/Routing/ChillUrlGenerator.php
Normal file
@ -0,0 +1,43 @@
|
||||
<?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\Routing;
|
||||
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
|
||||
final readonly class ChillUrlGenerator implements ChillUrlGeneratorInterface
|
||||
{
|
||||
public function __construct(private UrlGeneratorInterface $urlGenerator, private RequestStack $requestStack) {}
|
||||
|
||||
public function generate(string $name, array $parameters = [], int $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH): string
|
||||
{
|
||||
return $this->urlGenerator->generate($name, $parameters, $referenceType);
|
||||
}
|
||||
|
||||
public function generateWithReturnPath(string $name, array $parameters = [], int $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH): string
|
||||
{
|
||||
$uri = $this->requestStack->getCurrentRequest()->getRequestUri();
|
||||
|
||||
return $this->urlGenerator->generate($name, [$parameters, 'returnPath' => $uri], $referenceType);
|
||||
}
|
||||
|
||||
public function returnPathOr(string $name, array $parameters = [], int $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH): string
|
||||
{
|
||||
$request = $this->requestStack->getCurrentRequest();
|
||||
|
||||
if ($request->query->has('returnPath')) {
|
||||
return $request->query->get('returnPath');
|
||||
}
|
||||
|
||||
return $this->urlGenerator->generate($name, $parameters, $referenceType);
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
<?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\Routing;
|
||||
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
|
||||
/**
|
||||
* Interface for generating URLs with returnPath information.
|
||||
*/
|
||||
interface ChillUrlGeneratorInterface
|
||||
{
|
||||
/**
|
||||
* Generate an URL without any return path. This is the same as using @see{UrlGeneratorInterface::generate}.
|
||||
*/
|
||||
public function generate(string $name, array $parameters = [], int $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH): string;
|
||||
|
||||
/**
|
||||
* Generate an url, append a return path in it.
|
||||
*/
|
||||
public function generateWithReturnPath(string $name, array $parameters = [], int $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH): string;
|
||||
|
||||
/**
|
||||
* Get the return path or, if any, generate an url.
|
||||
*/
|
||||
public function returnPathOr(string $name, array $parameters = [], int $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH): string;
|
||||
}
|
@ -20,9 +20,14 @@ final class EntityWorkflowStepSignatureVoter extends Voter
|
||||
{
|
||||
public const SIGN = 'CHILL_MAIN_ENTITY_WORKFLOW_SIGNATURE_SIGN';
|
||||
|
||||
public const CANCEL = 'CHILL_MAIN_ENTITY_WORKFLOW_SIGNATURE_CANCEL';
|
||||
|
||||
public const REJECT = 'CHILL_MAIN_ENTITY_WORKFLOW_SIGNATURE_REJECT';
|
||||
|
||||
protected function supports(string $attribute, $subject)
|
||||
{
|
||||
return $subject instanceof EntityWorkflowStepSignature && self::SIGN === $attribute;
|
||||
return $subject instanceof EntityWorkflowStepSignature
|
||||
&& in_array($attribute, [self::SIGN, self::CANCEL, self::REJECT], true);
|
||||
}
|
||||
|
||||
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token)
|
||||
|
@ -21,6 +21,7 @@ use Chill\PersonBundle\Entity\Person;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Log\NullLogger;
|
||||
use Symfony\Component\Clock\MockClock;
|
||||
use Symfony\Component\Messenger\MessageBus;
|
||||
use Symfony\Component\Workflow\DefinitionBuilder;
|
||||
use Symfony\Component\Workflow\Metadata\InMemoryMetadataStore;
|
||||
use Symfony\Component\Workflow\Registry;
|
||||
@ -44,7 +45,9 @@ class SignatureStepStateChangerTest extends TestCase
|
||||
$workflow = $registry->get($entityWorkflow, 'dummy');
|
||||
$clock = new MockClock();
|
||||
$user = new User();
|
||||
$changer = new SignatureStepStateChanger($registry, $clock, new NullLogger());
|
||||
|
||||
$messengerBus = new MessageBus([]);
|
||||
$changer = new SignatureStepStateChanger($registry, $clock, new NullLogger(), $messengerBus);
|
||||
|
||||
// move it to signature
|
||||
$dto = new WorkflowTransitionContextDTO($entityWorkflow);
|
||||
@ -61,6 +64,8 @@ class SignatureStepStateChangerTest extends TestCase
|
||||
|
||||
// we mark the first signature as signed
|
||||
$changer->markSignatureAsSigned($signatures[0], 1);
|
||||
// the next step should be done by handling an async message
|
||||
$changer->onPostMark($signatures[0]);
|
||||
|
||||
self::assertEquals('signature', $entityWorkflow->getStep(), 'there should have any change in the entity workflow step');
|
||||
self::assertEquals(EntityWorkflowSignatureStateEnum::SIGNED, $signatures[0]->getState());
|
||||
@ -70,6 +75,8 @@ class SignatureStepStateChangerTest extends TestCase
|
||||
|
||||
// we mark the second signature as signed
|
||||
$changer->markSignatureAsSigned($signatures[1], 2);
|
||||
// the next step should be done by handling an async message
|
||||
$changer->onPostMark($signatures[1]);
|
||||
self::assertEquals(EntityWorkflowSignatureStateEnum::SIGNED, $signatures[1]->getState());
|
||||
self::assertEquals('post-signature', $entityWorkflow->getStep(), 'the entity workflow step should be post-signature');
|
||||
self::assertContains($user, $entityWorkflow->getCurrentStep()->getAllDestUser());
|
||||
@ -85,7 +92,7 @@ class SignatureStepStateChangerTest extends TestCase
|
||||
$workflow = $registry->get($entityWorkflow, 'dummy');
|
||||
$clock = new MockClock();
|
||||
$user = new User();
|
||||
$changer = new SignatureStepStateChanger($registry, $clock, new NullLogger());
|
||||
$changer = new SignatureStepStateChanger($registry, $clock, new NullLogger(), new MessageBus([]));
|
||||
|
||||
// move it to signature
|
||||
$dto = new WorkflowTransitionContextDTO($entityWorkflow);
|
||||
@ -102,6 +109,8 @@ class SignatureStepStateChangerTest extends TestCase
|
||||
|
||||
// we mark the first signature as signed
|
||||
$changer->markSignatureAsSigned($signatures[0], 1);
|
||||
// the next step should be done by handling an async message
|
||||
$changer->onPostMark($signatures[0]);
|
||||
|
||||
self::assertEquals('signature-without-metadata', $entityWorkflow->getStep(), 'there should have any change in the entity workflow step');
|
||||
self::assertEquals(EntityWorkflowSignatureStateEnum::SIGNED, $signatures[0]->getState());
|
||||
|
@ -0,0 +1,41 @@
|
||||
<?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\Messenger;
|
||||
|
||||
use Chill\MainBundle\Repository\Workflow\EntityWorkflowStepSignatureRepository;
|
||||
use Chill\MainBundle\Workflow\SignatureStepStateChanger;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\Messenger\Exception\UnrecoverableMessageHandlingException;
|
||||
use Symfony\Component\Messenger\Handler\MessageHandlerInterface;
|
||||
|
||||
final readonly class PostSignatureStateChangeHandler implements MessageHandlerInterface
|
||||
{
|
||||
public function __construct(
|
||||
private EntityWorkflowStepSignatureRepository $entityWorkflowStepSignatureRepository,
|
||||
private SignatureStepStateChanger $signatureStepStateChanger,
|
||||
private EntityManagerInterface $entityManager,
|
||||
) {}
|
||||
|
||||
public function __invoke(PostSignatureStateChangeMessage $message): void
|
||||
{
|
||||
$signature = $this->entityWorkflowStepSignatureRepository->find($message->signatureId);
|
||||
|
||||
if (null === $signature) {
|
||||
throw new UnrecoverableMessageHandlingException('signature not found');
|
||||
}
|
||||
|
||||
$this->signatureStepStateChanger->onPostMark($signature);
|
||||
|
||||
$this->entityManager->flush();
|
||||
$this->entityManager->clear();
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
<?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\Messenger;
|
||||
|
||||
/**
|
||||
* Message which is dispatched after a notification has a step changed.
|
||||
*/
|
||||
class PostSignatureStateChangeMessage
|
||||
{
|
||||
public function __construct(
|
||||
public int $signatureId,
|
||||
) {}
|
||||
}
|
@ -15,8 +15,10 @@ use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Entity\Workflow\EntityWorkflowSignatureStateEnum;
|
||||
use Chill\MainBundle\Entity\Workflow\EntityWorkflowStep;
|
||||
use Chill\MainBundle\Entity\Workflow\EntityWorkflowStepSignature;
|
||||
use Chill\MainBundle\Workflow\Messenger\PostSignatureStateChangeMessage;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\Clock\ClockInterface;
|
||||
use Symfony\Component\Messenger\MessageBusInterface;
|
||||
use Symfony\Component\Workflow\Registry;
|
||||
|
||||
class SignatureStepStateChanger
|
||||
@ -27,6 +29,7 @@ class SignatureStepStateChanger
|
||||
private readonly Registry $registry,
|
||||
private readonly ClockInterface $clock,
|
||||
private readonly LoggerInterface $logger,
|
||||
private readonly MessageBusInterface $messageBus,
|
||||
) {}
|
||||
|
||||
public function markSignatureAsSigned(EntityWorkflowStepSignature $signature, ?int $atIndex): void
|
||||
@ -34,11 +37,36 @@ class SignatureStepStateChanger
|
||||
$signature
|
||||
->setState(EntityWorkflowSignatureStateEnum::SIGNED)
|
||||
->setZoneSignatureIndex($atIndex)
|
||||
->setStateDate($this->clock->now())
|
||||
;
|
||||
|
||||
->setStateDate($this->clock->now());
|
||||
$this->logger->info(self::LOG_PREFIX.'Mark signature entity as signed', ['signatureId' => $signature->getId(), 'index' => (string) $atIndex]);
|
||||
$this->messageBus->dispatch(new PostSignatureStateChangeMessage((int) $signature->getId()));
|
||||
}
|
||||
|
||||
public function markSignatureAsCanceled(EntityWorkflowStepSignature $signature): void
|
||||
{
|
||||
$signature
|
||||
->setState(EntityWorkflowSignatureStateEnum::CANCELED)
|
||||
->setStateDate($this->clock->now());
|
||||
$this->logger->info(self::LOG_PREFIX.'Mark signature entity as canceled', ['signatureId' => $signature->getId()]);
|
||||
$this->messageBus->dispatch(new PostSignatureStateChangeMessage((int) $signature->getId()));
|
||||
}
|
||||
|
||||
public function markSignatureAsRejected(EntityWorkflowStepSignature $signature): void
|
||||
{
|
||||
$signature
|
||||
->setState(EntityWorkflowSignatureStateEnum::REJECTED)
|
||||
->setStateDate($this->clock->now());
|
||||
$this->logger->info(self::LOG_PREFIX.'Mark signature entity as rejected', ['signatureId' => $signature->getId()]);
|
||||
$this->messageBus->dispatch(new PostSignatureStateChangeMessage((int) $signature->getId()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Executed after a signature has a new state.
|
||||
*
|
||||
* This should be executed only by a system user (without any user registered)
|
||||
*/
|
||||
public function onPostMark(EntityWorkflowStepSignature $signature): void
|
||||
{
|
||||
if (!EntityWorkflowStepSignature::isAllSignatureNotPendingForStep($signature->getStep())) {
|
||||
$this->logger->info(self::LOG_PREFIX.'This is not the last signature, skipping transition to another place', ['signatureId' => $signature->getId()]);
|
||||
|
||||
|
@ -22,3 +22,8 @@ services:
|
||||
class: Chill\MainBundle\Routing\MenuTwig
|
||||
tags:
|
||||
- { name: twig.extension }
|
||||
|
||||
Chill\MainBundle\Routing\ChillUrlGenerator: ~
|
||||
|
||||
Chill\MainBundle\Routing\ChillUrlGeneratorInterface:
|
||||
alias: 'Chill\MainBundle\Routing\ChillUrlGenerator'
|
||||
|
@ -45,8 +45,10 @@ workflow:
|
||||
few {# workflows}
|
||||
other {# workflows}
|
||||
}
|
||||
signature_zone:
|
||||
has_signed_statement: 'A signé le {datetime, date, short} à {datetime, time, short}'
|
||||
signature:
|
||||
signed_statement: 'Signature appliquée le {datetime, date, short} à {datetime, time, short}'
|
||||
rejected_statement: 'Signature rejectée le {datetime, date, short} à {datetime, time, short}'
|
||||
canceled_statement: 'Signature annulée le {datetime, date, short} à {datetime, time, short}'
|
||||
|
||||
|
||||
duration:
|
||||
|
@ -538,6 +538,8 @@ workflow:
|
||||
signature_zone:
|
||||
title: Signatures électroniques
|
||||
button_sign: Signer
|
||||
button_cancel: Annuler
|
||||
button_reject: Rejeter
|
||||
metadata:
|
||||
sign_by: 'Signature pour %name%'
|
||||
docType: Type de document
|
||||
@ -550,6 +552,11 @@ workflow:
|
||||
user: Utilisateur
|
||||
already_signed_alert: La signature a déjà été appliquée
|
||||
|
||||
signature:
|
||||
cancel_signature_of: Annulation de la signature de %signer%
|
||||
cancel_are_you_sure: Êtes-vous sûr de vouloir annuler la signature de %signer%
|
||||
reject_signature_of: Rejet de la signature de %signer%
|
||||
reject_are_you_sure: Êtes-vous sûr de vouloir rejeter la signature de %signer%
|
||||
|
||||
Subscribe final: Recevoir une notification à l'étape finale
|
||||
Subscribe all steps: Recevoir une notification à chaque étape
|
||||
|
@ -0,0 +1,86 @@
|
||||
<?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\PersonBundle\Tests\Controller;
|
||||
|
||||
use Chill\MainBundle\Controller\WorkflowSignatureCancelController;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
|
||||
use Chill\MainBundle\Routing\ChillUrlGeneratorInterface;
|
||||
use Chill\MainBundle\Security\Authorization\EntityWorkflowStepSignatureVoter;
|
||||
use Chill\MainBundle\Workflow\SignatureStepStateChanger;
|
||||
use Chill\MainBundle\Workflow\WorkflowTransitionContextDTO;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||
use Symfony\Component\Form\FormFactoryInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\Routing\RequestContext;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
use Twig\Environment;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @coversNothing
|
||||
*/
|
||||
class WorkflowSignatureCancelControllerStepTest extends WebTestCase
|
||||
{
|
||||
private FormFactoryInterface $formFactory;
|
||||
private SignatureStepStateChanger $signatureStepStateChanger;
|
||||
private ChillUrlGeneratorInterface $chillUrlGenerator;
|
||||
private RequestStack $requestStack;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
self::bootKernel();
|
||||
|
||||
$this->formFactory = self::getContainer()->get('form.factory');
|
||||
$this->signatureStepStateChanger = self::getContainer()->get(SignatureStepStateChanger::class);
|
||||
$this->chillUrlGenerator = self::getContainer()->get(ChillUrlGeneratorInterface::class);
|
||||
|
||||
$requestContext = self::getContainer()->get(RequestContext::class);
|
||||
$requestContext->setParameter('_locale', 'fr');
|
||||
|
||||
$this->requestStack = self::getContainer()->get(RequestStack::class);
|
||||
|
||||
}
|
||||
|
||||
public function testCancelSignatureGet(): void
|
||||
{
|
||||
$entityWorkflow = new EntityWorkflow();
|
||||
$dto = new WorkflowTransitionContextDTO($entityWorkflow);
|
||||
$dto->futureUserSignature = new User();
|
||||
$entityWorkflow->setStep('signature', $dto, 'to_signature', new \DateTimeImmutable(), new User());
|
||||
$signature = $entityWorkflow->getCurrentStep()->getSignatures()->first();
|
||||
|
||||
$security = $this->createMock(Security::class);
|
||||
$security->expects($this->once())->method('isGranted')
|
||||
->with(EntityWorkflowStepSignatureVoter::CANCEL, $signature)->willReturn(true);
|
||||
|
||||
$entityManager = $this->createMock(EntityManager::class);
|
||||
|
||||
$twig = $this->createMock(Environment::class);
|
||||
$twig->expects($this->once())->method('render')->withAnyParameters()
|
||||
->willReturn('template');
|
||||
|
||||
$controller = new WorkflowSignatureCancelController($entityManager, $security, $this->formFactory, $twig, $this->signatureStepStateChanger, $this->chillUrlGenerator);
|
||||
|
||||
$request = new Request();
|
||||
$request->setMethod('GET');
|
||||
|
||||
$this->requestStack->push($request);
|
||||
|
||||
$response = $controller->cancelSignature($signature, $request);
|
||||
|
||||
self::assertEquals(200, $response->getStatusCode());
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user