mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 18:44:08 +00:00
Implement signature cancellation feature
Added functionality to cancel signatures in workflow, including controller, view, and tests. Updated translations and adjusted templates to support and display cancellation actions.
This commit is contained in:
parent
5a467ae38d
commit
83121c2a83
@ -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\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
|
||||
{
|
||||
if (!$this->security->isGranted(EntityWorkflowStepSignatureVoter::CANCEL, $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()) {
|
||||
$this->signatureStepStateChanger->markSignatureAsCanceled($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(
|
||||
'@ChillMain/WorkflowSignature/cancel.html.twig',
|
||||
['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,27 @@
|
||||
} %}
|
||||
{% 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_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.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 %}
|
@ -20,9 +20,12 @@ final class EntityWorkflowStepSignatureVoter extends Voter
|
||||
{
|
||||
public const SIGN = 'CHILL_MAIN_ENTITY_WORKFLOW_SIGNATURE_SIGN';
|
||||
|
||||
public const CANCEL = 'CHILL_MAIN_ENTITY_WORKFLOW_SIGNATURE_CANCEL';
|
||||
|
||||
protected function supports(string $attribute, $subject)
|
||||
{
|
||||
return $subject instanceof EntityWorkflowStepSignature && self::SIGN === $attribute;
|
||||
return $subject instanceof EntityWorkflowStepSignature
|
||||
&& in_array($attribute, [self::SIGN, self::CANCEL], true);
|
||||
}
|
||||
|
||||
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token)
|
||||
|
@ -89,6 +89,13 @@ class SignatureStepStateChanger
|
||||
$this->logger->info(self::LOG_PREFIX.'Transition automatically applied', ['signatureId' => $signature->getId()]);
|
||||
}
|
||||
|
||||
public function markSignatureAsCanceled(EntityWorkflowStepSignature $signature): void
|
||||
{
|
||||
$signature
|
||||
->setState(EntityWorkflowSignatureStateEnum::CANCELED)
|
||||
->setStateDate($this->clock->now());
|
||||
}
|
||||
|
||||
private function getPreviousSender(EntityWorkflowStep $entityWorkflowStep): ?User
|
||||
{
|
||||
$stepsChained = $entityWorkflowStep->getEntityWorkflow()->getStepsChained();
|
||||
|
@ -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,7 @@ workflow:
|
||||
signature_zone:
|
||||
title: Signatures électroniques
|
||||
button_sign: Signer
|
||||
button_cancel: Annuler
|
||||
metadata:
|
||||
sign_by: 'Signature pour %name%'
|
||||
docType: Type de document
|
||||
@ -550,6 +551,10 @@ workflow:
|
||||
user: Utilisateur
|
||||
already_signed_alert: La signature a déjà été appliquée
|
||||
|
||||
signature:
|
||||
cancel_signature_of: Annulation de la signature de %signer%
|
||||
are_you_sure: Êtes-vous sûr de vouloir annuler 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