diff --git a/CHANGELOG.md b/CHANGELOG.md index edd7fbc91..a198c0929 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,14 @@ and this project adheres to ## Unreleased +* renommer "dossier numéro" en "parcours numéro" dans les résultats de recherche +* renomme date de début en date d'ouverture dans le formulaire parcours + + +## Test releases + +### test release 2021-01-31 + [fast_actions] improve fast-actions buttons override mechanism, fix https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/413 [homepage widget] add vue homepage_widget with asynchone loading, give a global view resume of the user concerned actions, notifications, etc. * [person] accompanying course: optimisation: do not fetch some resources for the banner (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/409) @@ -26,8 +34,6 @@ and this project adheres to * [workflow][notification] improve how notifications and workflows are 'attached' to entities: contextual list, counter, buttons and vue modal -## Test releases - ### test release 2021-01-28 * [person] improve filiations vis graph: disable physics, use chill colors for persons-households-course, increase label of relations, remove labels on household arrows and other improvements (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/286, https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/362) diff --git a/src/Bundle/ChillActivityBundle/Entity/ActivityType.php b/src/Bundle/ChillActivityBundle/Entity/ActivityType.php index 94f732a89..bdf75ed05 100644 --- a/src/Bundle/ChillActivityBundle/Entity/ActivityType.php +++ b/src/Bundle/ChillActivityBundle/Entity/ActivityType.php @@ -271,7 +271,7 @@ class ActivityType public function checkSocialActionsVisibility(ExecutionContextInterface $context, $payload) { if ($this->socialIssuesVisible !== $this->socialActionsVisible) { - if (!($this->socialIssuesVisible === 2 && $this->socialActionsVisible === 1)) { + if (!(2 === $this->socialIssuesVisible && 1 === $this->socialActionsVisible)) { $context ->buildViolation('The socialActionsVisible value is not compatible with the socialIssuesVisible value') ->atPath('socialActionsVisible') diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ActivityReasonAggregatorTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ActivityReasonAggregatorTest.php index fb98f0a8e..436bfc697 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ActivityReasonAggregatorTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ActivityReasonAggregatorTest.php @@ -9,7 +9,7 @@ declare(strict_types=1); -namespace Chill\ActivityBundle\Tests\Aggregator; +namespace Chill\ActivityBundle\Tests\Export\Aggregator; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ActivityTypeAggregatorTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ActivityTypeAggregatorTest.php index 7959825a7..f6efe17a5 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ActivityTypeAggregatorTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ActivityTypeAggregatorTest.php @@ -9,7 +9,7 @@ declare(strict_types=1); -namespace Chill\ActivityBundle\Tests\Aggregator; +namespace Chill\ActivityBundle\Tests\Export\Aggregator; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ActivityUserAggregatorTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ActivityUserAggregatorTest.php index 056ba6049..1447f473b 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ActivityUserAggregatorTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ActivityUserAggregatorTest.php @@ -9,7 +9,7 @@ declare(strict_types=1); -namespace Chill\ActivityBundle\Tests\Aggregator; +namespace Chill\ActivityBundle\Tests\Export\Aggregator; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ActivityReasonFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ActivityReasonFilterTest.php index cbac6febd..5b8ae08c3 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ActivityReasonFilterTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ActivityReasonFilterTest.php @@ -9,7 +9,7 @@ declare(strict_types=1); -namespace Chill\ActivityBundle\Tests\Filter; +namespace Chill\ActivityBundle\Tests\Export\Filter; use Chill\MainBundle\Test\Export\AbstractFilterTest; use Doctrine\Common\Collections\ArrayCollection; diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/PersonHavingActivityBetweenDateFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/PersonHavingActivityBetweenDateFilterTest.php index f444c368b..94d99c2a7 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/PersonHavingActivityBetweenDateFilterTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/PersonHavingActivityBetweenDateFilterTest.php @@ -9,7 +9,7 @@ declare(strict_types=1); -namespace Chill\ActivityBundle\Tests\Filter; +namespace Chill\ActivityBundle\Tests\Export\Filter; use Chill\MainBundle\Test\Export\AbstractFilterTest; use DateTime; diff --git a/src/Bundle/ChillCustomFieldsBundle/Tests/CustomFields/CustomFieldsChoiceTest.php b/src/Bundle/ChillCustomFieldsBundle/Tests/CustomFields/CustomFieldsChoiceTest.php index db27ec921..d3355b9ce 100644 --- a/src/Bundle/ChillCustomFieldsBundle/Tests/CustomFields/CustomFieldsChoiceTest.php +++ b/src/Bundle/ChillCustomFieldsBundle/Tests/CustomFields/CustomFieldsChoiceTest.php @@ -9,7 +9,7 @@ declare(strict_types=1); -namespace Chill\CustomFieldsBundle\Tests; +namespace Chill\CustomFieldsBundle\Tests\CustomFields; use Chill\CustomFieldsBundle\CustomFields\CustomFieldChoice; use Chill\CustomFieldsBundle\Entity\CustomField; diff --git a/src/Bundle/ChillCustomFieldsBundle/Tests/CustomFields/CustomFieldsNumberTest.php b/src/Bundle/ChillCustomFieldsBundle/Tests/CustomFields/CustomFieldsNumberTest.php index 60381f567..fb0079f05 100644 --- a/src/Bundle/ChillCustomFieldsBundle/Tests/CustomFields/CustomFieldsNumberTest.php +++ b/src/Bundle/ChillCustomFieldsBundle/Tests/CustomFields/CustomFieldsNumberTest.php @@ -9,7 +9,7 @@ declare(strict_types=1); -namespace Chill\CustomFieldsBundle\Tests; +namespace Chill\CustomFieldsBundle\Tests\CustomFields; use Chill\CustomFieldsBundle\CustomFields\CustomFieldNumber; use Chill\CustomFieldsBundle\Entity\CustomField; diff --git a/src/Bundle/ChillCustomFieldsBundle/Tests/CustomFields/CustomFieldsTextTest.php b/src/Bundle/ChillCustomFieldsBundle/Tests/CustomFields/CustomFieldsTextTest.php index 50bf689d4..c1dca44c0 100644 --- a/src/Bundle/ChillCustomFieldsBundle/Tests/CustomFields/CustomFieldsTextTest.php +++ b/src/Bundle/ChillCustomFieldsBundle/Tests/CustomFields/CustomFieldsTextTest.php @@ -9,10 +9,11 @@ declare(strict_types=1); -namespace Chill\CustomFieldsBundle\Tests; +namespace Chill\CustomFieldsBundle\Tests\CustomFields; use Chill\CustomFieldsBundle\CustomFields\CustomFieldText; use Chill\CustomFieldsBundle\Entity\CustomField; +use Chill\CustomFieldsBundle\Tests\CustomFieldTestHelper; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; /** diff --git a/src/Bundle/ChillCustomFieldsBundle/Tests/Routing/RoutingLoaderTest.php b/src/Bundle/ChillCustomFieldsBundle/Tests/Routing/RoutingLoaderTest.php index 3cde3890a..32c6639bc 100644 --- a/src/Bundle/ChillCustomFieldsBundle/Tests/Routing/RoutingLoaderTest.php +++ b/src/Bundle/ChillCustomFieldsBundle/Tests/Routing/RoutingLoaderTest.php @@ -9,7 +9,7 @@ declare(strict_types=1); -namespace Chill\CustomFieldsBundle\Tests; +namespace Chill\CustomFieldsBundle\Tests\Routing; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; use Symfony\Component\HttpFoundation\Response; diff --git a/src/Bundle/ChillMainBundle/Controller/WorkflowController.php b/src/Bundle/ChillMainBundle/Controller/WorkflowController.php index 33f1bd774..247cdf552 100644 --- a/src/Bundle/ChillMainBundle/Controller/WorkflowController.php +++ b/src/Bundle/ChillMainBundle/Controller/WorkflowController.php @@ -19,6 +19,7 @@ use Chill\MainBundle\Pagination\PaginatorFactory; use Chill\MainBundle\Repository\Workflow\EntityWorkflowRepository; use Chill\MainBundle\Security\Authorization\EntityWorkflowVoter; use Chill\MainBundle\Workflow\EntityWorkflowManager; +use Chill\MainBundle\Workflow\Validator\StepDestValid; use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; @@ -201,9 +202,18 @@ class WorkflowController extends AbstractController $entityWorkflow->getCurrentStep()->addDestUser($user); } - $this->entityManager->flush(); + $errors = $this->validator->validate( + $entityWorkflow->getCurrentStep(), + new StepDestValid() + ); - return $this->redirectToRoute('chill_main_workflow_show', ['id' => $entityWorkflow->getId()]); + if (count($errors) === 0) { + $this->entityManager->flush(); + + return $this->redirectToRoute('chill_main_workflow_show', ['id' => $entityWorkflow->getId()]); + } + + return new Response((string) $errors, Response::HTTP_UNPROCESSABLE_ENTITY); } if ($transitionForm->isSubmitted() && !$transitionForm->isValid()) { @@ -235,6 +245,7 @@ class WorkflowController extends AbstractController 'handler_template_data' => $handler->getTemplateData($entityWorkflow), 'transition_form' => isset($transitionForm) ? $transitionForm->createView() : null, 'entity_workflow' => $entityWorkflow, + 'transition_form_errors' => $errors ?? [], //'comment_form' => $commentForm->createView(), ] ); diff --git a/src/Bundle/ChillMainBundle/Entity/Workflow/EntityWorkflow.php b/src/Bundle/ChillMainBundle/Entity/Workflow/EntityWorkflow.php index c7a322338..a132706d7 100644 --- a/src/Bundle/ChillMainBundle/Entity/Workflow/EntityWorkflow.php +++ b/src/Bundle/ChillMainBundle/Entity/Workflow/EntityWorkflow.php @@ -174,6 +174,20 @@ class EntityWorkflow implements TrackCreationInterface, TrackUpdateInterface return null; } + public function getCurrentStepChained(): ?EntityWorkflowStep + { + $steps = $this->getStepsChained(); + $currentStep = $this->getCurrentStep(); + + foreach ($steps as $step) { + if ($step === $currentStep) { + return $step; + } + } + + return null; + } + public function getCurrentStepCreatedAt(): ?DateTimeInterface { if (null !== $previous = $this->getPreviousStepIfAny()) { diff --git a/src/Bundle/ChillMainBundle/Form/WorkflowStepType.php b/src/Bundle/ChillMainBundle/Form/WorkflowStepType.php index 18ac10c47..eaca35812 100644 --- a/src/Bundle/ChillMainBundle/Form/WorkflowStepType.php +++ b/src/Bundle/ChillMainBundle/Form/WorkflowStepType.php @@ -48,6 +48,8 @@ class WorkflowStepType extends AbstractType $entityWorkflow = $options['entity_workflow']; $handler = $this->entityWorkflowManager->getHandler($entityWorkflow); $workflow = $this->registry->get($entityWorkflow, $entityWorkflow->getWorkflowName()); + $place = $workflow->getMarking($entityWorkflow); + $placeMetadata = $workflow->getMetadataStore()->getPlaceMetadata(array_keys($place->getPlaces())[0]); if (true === $options['transition']) { if (null === $options['entity_workflow']) { @@ -68,40 +70,77 @@ class WorkflowStepType extends AbstractType $transitions ); + if (array_key_exists('validationFilterInputLabels', $placeMetadata)) { + $inputLabels = $placeMetadata['validationFilterInputLabels']; + + $builder->add('transitionFilter', ChoiceType::class, [ + 'multiple' => false, + 'label' => 'workflow.My decision', + 'choices' => [ + 'forward' => 'forward', + 'backward' => 'backward', + 'neutral' => 'neutral', + ], + 'choice_label' => function (string $key) use ($inputLabels) { + return $this->translatableStringHelper->localize($inputLabels[$key]); + }, + 'choice_attr' => static function (string $key) { + return [ + $key => $key, + ]; + }, + 'mapped' => false, + 'expanded' => true, + 'data' => 'forward', + ]); + } + $builder ->add('transition', ChoiceType::class, [ - 'label' => 'workflow.Transition to apply', + 'label' => 'workflow.Next step', 'mapped' => false, 'multiple' => false, 'expanded' => true, 'choices' => $choices, 'choice_label' => function (Transition $transition) use ($workflow) { - $meta = $workflow->getMetadataStore()->getTransitionMetadata($transition); + $meta = $workflow->getMetadataStore()->getTransitionMetadata($transition); - if (array_key_exists('label', $meta)) { - return $this->translatableStringHelper->localize($meta['label']); - } + if (array_key_exists('label', $meta)) { + return $this->translatableStringHelper->localize($meta['label']); + } - return $transition->getName(); - }, + return $transition->getName(); + }, 'choice_attr' => static function (Transition $transition) use ($workflow) { - $toFinal = true; + $toFinal = true; + $isForward = 'neutral'; - foreach ($transition->getTos() as $to) { - $meta = $workflow->getMetadataStore()->getPlaceMetadata($to); + $metadata = $workflow->getMetadataStore()->getTransitionMetadata($transition); - if ( + 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; - } + $toFinal = false; } + } - return [ - 'data-is-transition' => 'data-is-transition', - 'data-to-final' => $toFinal ? '1' : '0', - ]; - }, + 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', diff --git a/src/Bundle/ChillMainBundle/Resources/public/lib/show_hide/show_hide.js b/src/Bundle/ChillMainBundle/Resources/public/lib/show_hide/show_hide.js index f2bcc9b45..25307a120 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/lib/show_hide/show_hide.js +++ b/src/Bundle/ChillMainBundle/Resources/public/lib/show_hide/show_hide.js @@ -1,19 +1,19 @@ /** * Create a control to show or hide values - * + * * Possible options are: - * + * * - froms: an Element, an Array of Element, or a NodeList. A * listener will be attached to **all** input of those elements * and will trigger the check on changes - * - test: a function which will test the element and will return true + * - test: a function which will test the element and will return true * if the content must be shown, false if it must be hidden. - * The function will receive the `froms` as first argument, and the + * The function will receive the `froms` as first argument, and the * event as second argument. - * - container: an Element, an Array of Element, or a Node List. The + * - container: an Element, an Array of Element, or a Node List. The * child nodes will be hidden / shown inside this container * - event_name: the name of the event to listen to. `'change'` by default. - * + * * @param object options */ var ShowHide = function(options) { @@ -26,8 +26,10 @@ var ShowHide = function(options) { container_content = [], debug = 'debug' in options ? options.debug : false, load_event = 'load_event' in options ? options.load_event : 'load', - id = 'uid' in options ? options.id : Math.random(); - + id = 'uid' in options ? options.id : Math.random(), + toggle_callback = 'toggle_callback' in options ? options.toggle_callback : null + ; + var bootstrap = function(event) { if (debug) { console.log('debug is activated on this show-hide', this); @@ -39,15 +41,14 @@ var ShowHide = function(options) { contents.push(el); } container_content.push(contents); - // console.log('container content', container_content); } // attach the listener on each input for (let f of froms.values()) { - let - inputs = f.querySelectorAll('input'), + let + inputs = f.querySelectorAll('input'), selects = f.querySelectorAll('select'); - + for (let input of inputs.values()) { if (debug) { console.log('attaching event to input', input); @@ -67,10 +68,10 @@ var ShowHide = function(options) { } // first launch of the show/hide - onChange(event); + onChange(event); }; - + var onChange = function (event) { var result = test(froms, event), me; @@ -89,45 +90,53 @@ var ShowHide = function(options) { } else { throw "the result of test is not a boolean"; } - + }; - + var forceHide = function() { if (debug) { console.log('force hide'); } - for (let contents of container_content.values()) { - for (let el of contents.values()) { - el.remove(); + if (toggle_callback !== null) { + toggle_callback(container, 'hide'); + } else { + for (let contents of container_content.values()) { + for (let el of contents.values()) { + el.remove(); + } } } is_shown = false; }; - + var forceShow = function() { if (debug) { console.log('show'); } - for (let i of container_content.keys()) { - var contents = container_content[i]; - for (let el of contents.values()) { - container[i].appendChild(el); + if (toggle_callback !== null) { + toggle_callback(container, 'show'); + } else { + for (let i of container_content.keys()) { + var contents = container_content[i]; + for (let el of contents.values()) { + container[i].appendChild(el); + } } } is_shown = true; }; - + var forceCompute = function(event) { onChange(event); }; - - + + if (load_event !== null) { window.addEventListener('load', bootstrap); } else { bootstrap(null); } - + return { forceHide: forceHide, forceShow: forceShow, diff --git a/src/Bundle/ChillMainBundle/Resources/public/page/workflow-show/index.js b/src/Bundle/ChillMainBundle/Resources/public/page/workflow-show/index.js index 2e2d4e89c..bd05a8aaa 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/page/workflow-show/index.js +++ b/src/Bundle/ChillMainBundle/Resources/public/page/workflow-show/index.js @@ -2,29 +2,89 @@ import {ShowHide} from 'ChillMainAssets/lib/show_hide/show_hide.js'; window.addEventListener('DOMContentLoaded', function() { let - finalizeAfterContainer = document.querySelector('#finalizeAfter'), + divTransitions = document.querySelector('#transitions'), futureDestUsersContainer = document.querySelector('#futureDestUsers') ; - if (null === finalizeAfterContainer) { - return; - } - - new ShowHide({ - load_event: null, - froms: [finalizeAfterContainer], - container: [futureDestUsersContainer], - test: function(containers, arg2, arg3) { - for (let container of containers) { - for (let input of container.querySelectorAll('input')) { - if (!input.checked) { - return true; - } else { - return false; + if (null !== divTransitions) { + new ShowHide({ + load_event: null, + froms: [divTransitions], + container: [futureDestUsersContainer], + test: function(divs, arg2, arg3) { + for (let div of divs) { + for (let input of div.querySelectorAll('input')) { + if (input.checked) { + if (input.dataset.toFinal === "1") { + return false; + } else { + return true; + } + } } } - } - }, - }) + return true; + }, + }); + } + + let + transitionFilterContainer = document.querySelector('#transitionFilter'), + transitions = document.querySelector('#transitions') + ; + + if (null !== transitionFilterContainer) { + transitions.querySelectorAll('.form-check').forEach(function(row) { + + const isForward = row.querySelector('input').dataset.isForward; + + new ShowHide({ + load_event: null, + froms: [transitionFilterContainer], + container: row, + test: function (containers) { + for (let container of containers) { + for (let input of container.querySelectorAll('input')) { + if (input.checked) { + return isForward === input.value; + } + } + } + }, + toggle_callback: function (c, dir) { + for (let div of c) { + let input = div.querySelector('input'); + if ('hide' === dir) { + input.checked = false; + input.disabled = true; + } else { + input.disabled = false; + } + } + }, + }); + }); + } + + // validate form + let form = document.querySelector('form[name="workflow_step"]'); + + if (form === null) { + console.error('form to validate not found'); + } + + form.addEventListener('submit', function (event) { + const datas = new FormData(event.target); + + if (datas.has('workflow_step[future_dest_users]')) { + const dests = JSON.parse(datas.get('workflow_step[future_dest_users]')); + if (dests === null || (dests instanceof Array && dests.length === 0)) { + event.preventDefault(); + console.log('no users!'); + window.alert('Indiquez un utilisateur pour traiter la prochaine étape.'); + } + } + }); + }); diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/EntityWorkflow/ListWorkflowModal.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/EntityWorkflow/ListWorkflowModal.vue index a188a3a02..2e4125c1a 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/EntityWorkflow/ListWorkflowModal.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/EntityWorkflow/ListWorkflowModal.vue @@ -1,5 +1,5 @@ @@ -53,6 +56,7 @@ export default { PickWorkflow, ListWorkflowVue }, + emits: ['goToGenerateWorkflow'], props: { workflows: { type: Array, @@ -73,9 +77,14 @@ export default { workflowsAvailables: { type: Array, required: true, - } + }, + preventDefaultMoveToGenerate: { + type: Boolean, + required: false, + default: false, + }, }, - data() { + data() { return { modal: { showModal: false, @@ -95,6 +104,10 @@ export default { openModal() { this.modal.showModal = true; }, + goToGenerateWorkflow(data) { + console.log('go to generate workflow intercepted'); + this.$emit('goToGenerateWorkflow', data); + } }, i18n: { messages: { @@ -108,4 +121,4 @@ export default { } - \ No newline at end of file + diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/EntityWorkflow/PickWorkflow.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/EntityWorkflow/PickWorkflow.vue index 3a39c0eff..e74cd7ef2 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/EntityWorkflow/PickWorkflow.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/EntityWorkflow/PickWorkflow.vue @@ -6,7 +6,7 @@ @@ -31,7 +31,12 @@ export default { workflowsAvailables: { type: Array, required: true, - } + }, + preventDefaultMoveToGenerate: { + type: Boolean, + required: false, + default: false, + }, }, emits: ['goToGenerateWorkflow'], methods: { @@ -39,6 +44,12 @@ export default { return buildLinkCreate(workflowName, this.relatedEntityClass, this.relatedEntityId); }, goToGenerateWorkflow(event, workflowName) { + console.log('goToGenerateWorkflow', event, workflowName); + + if (!this.$props.preventDefaultMoveToGenerate) { + event.target.click(); + } + this.$emit('goToGenerateWorkflow', {event, workflowName, link: this.makeLink(workflowName)}); } } diff --git a/src/Bundle/ChillMainBundle/Resources/views/Workflow/_decision.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Workflow/_decision.html.twig index 4b14b39bc..64482c067 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/Workflow/_decision.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/Workflow/_decision.html.twig @@ -3,7 +3,58 @@ {% if transition_form is not null %} {{ form_start(transition_form) }} - {{ form_row(transition_form.transition) }} + {% set step = entity_workflow.currentStepChained %} + {% set labels = workflow_metadata(entity_workflow, 'label', step.currentStep) %} + {% set label = labels is null ? step.currentStep : labels|localize_translatable_string %} + +
+
+
+
+ {{ 'workflow.Current step'|trans }} : + {{ label }} +
+
+ + {% if step.previous is not null %} + {% if step.previous.comment is not empty %} +
+
+
+ {{ step.previous.comment|chill_markdown_to_html }} +
+
+
+ {% endif %} +
+
+ {{ 'By'|trans }} + {{ step.previous.transitionBy|chill_entity_render_box }}, + {{ step.previous.transitionAt|format_datetime('short', 'short') }} +
+
+ {% else %} +
+
{{ 'workflow.Created by'|trans }}
+
{{ step.entityWorkflow.createdBy|chill_entity_render_box }}
+
+
+
{{ 'Le'|trans }}
+
{{ step.entityWorkflow.createdAt|format_datetime('short', 'short') }}
+
+ {% endif %} +
+
+ +
+ {% if transition_form.transitionFilter is defined %} + {{ form_row(transition_form.transitionFilter) }} + {% endif %} +
+ +
+ {{ form_row(transition_form.transition) }} +
{% if transition_form.freezeAfter is defined %} {{ form_row(transition_form.freezeAfter) }} diff --git a/src/Bundle/ChillMainBundle/Resources/views/Workflow/_history.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Workflow/_history.html.twig index cee9d219c..e40e82dab 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/Workflow/_history.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/Workflow/_history.html.twig @@ -69,6 +69,18 @@ {% endif %} + {% if loop.last and step.destUser|length > 0 %} +
+
+

{{ 'workflow.Users allowed to apply transition'|trans }} :

+ +
+
+ {% endif %} {% endfor %} diff --git a/src/Bundle/ChillMainBundle/Serializer/Normalizer/NotificationNormalizer.php b/src/Bundle/ChillMainBundle/Serializer/Normalizer/NotificationNormalizer.php index 08deac745..4c09aaa55 100644 --- a/src/Bundle/ChillMainBundle/Serializer/Normalizer/NotificationNormalizer.php +++ b/src/Bundle/ChillMainBundle/Serializer/Normalizer/NotificationNormalizer.php @@ -44,7 +44,6 @@ class NotificationNormalizer implements NormalizerAwareInterface, NormalizerInte */ public function normalize($object, ?string $format = null, array $context = []) { - dump($object); $entity = $this->entityManager ->getRepository($object->getRelatedEntityClass()) ->find($object->getRelatedEntityId()); diff --git a/src/Bundle/ChillMainBundle/Tests/Authorization/ParentRoleHelperTest.php b/src/Bundle/ChillMainBundle/Tests/Authorization/ParentRoleHelperTest.php index 69a3ac733..05b18407a 100644 --- a/src/Bundle/ChillMainBundle/Tests/Authorization/ParentRoleHelperTest.php +++ b/src/Bundle/ChillMainBundle/Tests/Authorization/ParentRoleHelperTest.php @@ -9,7 +9,7 @@ declare(strict_types=1); -namespace Chill\MainBundle\Tests\Security\Authorization; +namespace Chill\MainBundle\Tests\Authorization; use Chill\MainBundle\Security\ParentRoleHelper; use Chill\PersonBundle\Security\Authorization\PersonVoter; diff --git a/src/Bundle/ChillMainBundle/Tests/Form/Type/PickCenterTypeTest.php b/src/Bundle/ChillMainBundle/Tests/Form/Type/PickCenterTypeTest.php index 55f9b64c5..e7a6ee096 100644 --- a/src/Bundle/ChillMainBundle/Tests/Form/Type/PickCenterTypeTest.php +++ b/src/Bundle/ChillMainBundle/Tests/Form/Type/PickCenterTypeTest.php @@ -9,10 +9,11 @@ declare(strict_types=1); -namespace Chill\MainBundle\Form\Type; +namespace Chill\MainBundle\Tests\Form\Type; use Chill\MainBundle\Entity\GroupCenter; use Chill\MainBundle\Entity\User; +use Chill\MainBundle\Form\CenterType; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\Extension\Core\Type\HiddenType; use Symfony\Component\Form\Test\TypeTestCase; diff --git a/src/Bundle/ChillMainBundle/Tests/Search/SearchProviderTest.php b/src/Bundle/ChillMainBundle/Tests/Search/SearchProviderTest.php index 07d7c7ab7..e9cbd5e3c 100644 --- a/src/Bundle/ChillMainBundle/Tests/Search/SearchProviderTest.php +++ b/src/Bundle/ChillMainBundle/Tests/Search/SearchProviderTest.php @@ -9,7 +9,7 @@ declare(strict_types=1); -namespace Chill\MainBundle\Test\Search; +namespace Chill\MainBundle\Tests\Search; use Chill\MainBundle\Search\ParsingException; use Chill\MainBundle\Search\SearchInterface; diff --git a/src/Bundle/ChillMainBundle/Tests/Security/PasswordRecover/TokenManagerTest.php b/src/Bundle/ChillMainBundle/Tests/Security/PasswordRecover/TokenManagerTest.php index 7208c3ee9..8e29a190b 100644 --- a/src/Bundle/ChillMainBundle/Tests/Security/PasswordRecover/TokenManagerTest.php +++ b/src/Bundle/ChillMainBundle/Tests/Security/PasswordRecover/TokenManagerTest.php @@ -9,7 +9,7 @@ declare(strict_types=1); -namespace Chill\MainBundle\Tests\PasswordRecover; +namespace Chill\MainBundle\Tests\Security\PasswordRecover; use Chill\MainBundle\Entity\User; use Chill\MainBundle\Security\PasswordRecover\TokenManager; diff --git a/src/Bundle/ChillMainBundle/Workflow/Validator/StepDestValid.php b/src/Bundle/ChillMainBundle/Workflow/Validator/StepDestValid.php new file mode 100644 index 000000000..7c2d954b2 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Workflow/Validator/StepDestValid.php @@ -0,0 +1,26 @@ +isFinal() && 0 < count($value->getDestUser())) { + $this->context->buildViolation($constraint->messageDestNotAllowed) + ->addViolation(); + } + + if (!$value->isFinal() && 0 === count($value->getDestUser())) { + $this->context->buildViolation($constraint->messageRequireDest) + ->addViolation(); + } + } +} diff --git a/src/Bundle/ChillMainBundle/translations/messages.fr.yml b/src/Bundle/ChillMainBundle/translations/messages.fr.yml index 605cafe8e..d3173bd7c 100644 --- a/src/Bundle/ChillMainBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillMainBundle/translations/messages.fr.yml @@ -370,7 +370,8 @@ Workflow history: Historique de la décision workflow: Created by: Créé par - Transition to apply: Ma décision + My decision: Ma décision + Next step: Prochaine étape dest for next steps: Utilisateurs qui valideront la prochaine étape Freeze: Geler Freezed: Gelé @@ -392,6 +393,9 @@ workflow: dest: Workflows en attente d'action you subscribed to all steps: Vous recevrez une notification à chaque étape you subscribed to final step: Vous recevrez une notification à l'étape finale + Current step: Étape actuelle + Comment on last change: Commentaire à la transition précédente + Users allowed to apply transition: Utilisateurs pouvant valider cette étape Subscribe final: Recevoir une notification à l'étape finale Subscribe all steps: Recevoir une notification à chaque étape diff --git a/src/Bundle/ChillPersonBundle/Controller/AccompanyingPeriodWorkEvaluationApiController.php b/src/Bundle/ChillPersonBundle/Controller/AccompanyingPeriodWorkEvaluationApiController.php index 1bb4bc1fe..757e8df79 100644 --- a/src/Bundle/ChillPersonBundle/Controller/AccompanyingPeriodWorkEvaluationApiController.php +++ b/src/Bundle/ChillPersonBundle/Controller/AccompanyingPeriodWorkEvaluationApiController.php @@ -99,7 +99,7 @@ class AccompanyingPeriodWorkEvaluationApiController if ($request->query->getBoolean('countOnly', false)) { return new JsonResponse( - $this->serializer->serialize(new Counter($total), 'json', ['groups' => 'read']), + $this->serializer->serialize(new Counter($total), 'json', ['groups' => ['read']]), JsonResponse::HTTP_OK, [], true @@ -117,7 +117,7 @@ class AccompanyingPeriodWorkEvaluationApiController $collection = new Collection($works, $paginator); return new JsonResponse( - $this->serializer->serialize($collection, 'json', ['groups' => 'read']), + $this->serializer->serialize($collection, 'json', ['groups' => ['read', 'read:evaluation:include-work']]), JsonResponse::HTTP_OK, [], true diff --git a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php index d71ac2b9f..07fb859b7 100644 --- a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php +++ b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php @@ -213,7 +213,7 @@ class AccompanyingPeriod implements * * @ORM\Column(type="date") * @Groups({"read", "write", "docgen:read"}) - * @Assert\LessThan(value= "today", groups={AccompanyingPeriod::STEP_CONFIRMED}) + * @Assert\LessThan(value="today", groups={AccompanyingPeriod::STEP_CONFIRMED}) * @Assert\LessThan(propertyPath="closingDate", groups={AccompanyingPeriod::STEP_CONFIRMED}) */ private ?DateTime $openingDate = null; diff --git a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWork.php b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWork.php index 97424c2e3..9a80fde3d 100644 --- a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWork.php +++ b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWork.php @@ -44,7 +44,7 @@ class AccompanyingPeriodWork implements AccompanyingPeriodLinkedWithSocialIssues { /** * @ORM\ManyToOne(targetEntity=AccompanyingPeriod::class) - * @Serializer\Groups({"read"}) + * @Serializer\Groups({"read","read:accompanyingPeriodWork:light"}) */ private ?AccompanyingPeriod $accompanyingPeriod = null; @@ -63,26 +63,26 @@ class AccompanyingPeriodWork implements AccompanyingPeriodLinkedWithSocialIssues /** * @ORM\Column(type="datetime_immutable") - * @Serializer\Groups({"read", "docgen:read"}) + * @Serializer\Groups({"read", "docgen:read", "read:accompanyingPeriodWork:light"}) */ private ?DateTimeImmutable $createdAt = null; /** * @ORM\Column(type="boolean") - * @Serializer\Groups({"read", "docgen:read"}) + * @Serializer\Groups({"read", "docgen:read", "read:accompanyingPeriodWork:light"}) */ private bool $createdAutomatically = false; /** * @ORM\Column(type="text") - * @Serializer\Groups({"read", "docgen:read"}) + * @Serializer\Groups({"read", "docgen:read", "read:accompanyingPeriodWork:light"}) */ private string $createdAutomaticallyReason = ''; /** * @ORM\ManyToOne(targetEntity=User::class) * @ORM\JoinColumn(nullable=false) - * @Serializer\Groups({"read", "docgen:read"}) + * @Serializer\Groups({"read", "docgen:read", "read:accompanyingPeriodWork:light"}) */ private ?User $createdBy = null; @@ -90,7 +90,7 @@ class AccompanyingPeriodWork implements AccompanyingPeriodLinkedWithSocialIssues * @ORM\Column(type="date_immutable", nullable=true, options={"default": null}) * @Serializer\Groups({"accompanying_period_work:create"}) * @Serializer\Groups({"accompanying_period_work:edit"}) - * @Serializer\Groups({"read", "docgen:read"}) + * @Serializer\Groups({"read", "docgen:read", "read:accompanyingPeriodWork:light"}) * @Assert\GreaterThan(propertyPath="startDate", * message="accompanying_course_work.The endDate should be greater than the start date" * ) @@ -122,7 +122,7 @@ class AccompanyingPeriodWork implements AccompanyingPeriodLinkedWithSocialIssues * @ORM\Id * @ORM\GeneratedValue * @ORM\Column(type="integer") - * @Serializer\Groups({"read", "docgen:read"}) + * @Serializer\Groups({"read", "docgen:read", "read:accompanyingPeriodWork:light", "read:evaluation:include-work"}) */ private ?int $id = null; @@ -135,7 +135,7 @@ class AccompanyingPeriodWork implements AccompanyingPeriodLinkedWithSocialIssues /** * @ORM\ManyToMany(targetEntity=Person::class) * @ORM\JoinTable(name="chill_person_accompanying_period_work_person") - * @Serializer\Groups({"read", "docgen:read"}) + * @Serializer\Groups({"read", "docgen:read", "read:accompanyingPeriodWork:light"}) * @Serializer\Groups({"accompanying_period_work:edit"}) * @Serializer\Groups({"accompanying_period_work:create"}) */ @@ -151,8 +151,9 @@ class AccompanyingPeriodWork implements AccompanyingPeriodLinkedWithSocialIssues /** * @ORM\ManyToOne(targetEntity=SocialAction::class) - * @Serializer\Groups({"read", "docgen:read"}) + * @Serializer\Groups({"read", "docgen:read", "read:accompanyingPeriodWork:light"}) * @Serializer\Groups({"accompanying_period_work:create"}) + * @Serializer\Context(normalizationContext={"groups"={"read"}}, groups={"read:accompanyingPeriodWork:light"}) */ private ?SocialAction $socialAction = null; @@ -160,7 +161,7 @@ class AccompanyingPeriodWork implements AccompanyingPeriodLinkedWithSocialIssues * @ORM\Column(type="date_immutable") * @Serializer\Groups({"accompanying_period_work:create"}) * @Serializer\Groups({"accompanying_period_work:edit"}) - * @Serializer\Groups({"read", "docgen:read"}) + * @Serializer\Groups({"read", "docgen:read", "read:accompanyingPeriodWork:light"}) */ private ?DateTimeImmutable $startDate = null; diff --git a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWorkEvaluation.php b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWorkEvaluation.php index f9fbc95f9..483495ebd 100644 --- a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWorkEvaluation.php +++ b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWorkEvaluation.php @@ -39,6 +39,8 @@ class AccompanyingPeriodWorkEvaluation implements TrackCreationInterface, TrackU * targetEntity=AccompanyingPeriodWork::class, * inversedBy="accompanyingPeriodWorkEvaluations" * ) + * @Serializer\Groups({"read:evaluation:include-work"}) + * @Serializer\Context(normalizationContext={"groups"={"read:accompanyingPeriodWork:light"}}, groups={"read:evaluation:include-work"}) */ private ?AccompanyingPeriodWork $accompanyingPeriodWork = null; diff --git a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriodRepository.php b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriodRepository.php index 3f0c30e47..10b424cef 100644 --- a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriodRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriodRepository.php @@ -28,6 +28,11 @@ final class AccompanyingPeriodRepository implements ObjectRepository $this->repository = $entityManager->getRepository(AccompanyingPeriod::class); } + public function countBy(array $criteria): int + { + return $this->repository->count($criteria); + } + public function countByRecentUserHistory(User $user, DateTimeImmutable $since): int { $qb = $this->buildQueryByRecentUserHistory($user, $since); @@ -35,11 +40,6 @@ final class AccompanyingPeriodRepository implements ObjectRepository return $qb->select('count(a)')->getQuery()->getSingleScalarResult(); } - public function countBy(array $criteria): int - { - return $this->repository->count($criteria); - } - public function createQueryBuilder(string $alias, ?string $indexBy = null): QueryBuilder { return $this->repository->createQueryBuilder($alias, $indexBy); diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/js/i18n.js b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/js/i18n.js index 249cd7d41..87f6d8d09 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/js/i18n.js +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/js/i18n.js @@ -152,8 +152,8 @@ const appMessages = { not_valid: "Sélectionnez un métier du référent" }, startdate: { - change: "Modifier la date de début", - date: "Date de début", + change: "Date d'ouverture", + date: "Date d'ouverture", }, // catch errors 'Error while updating AccompanyingPeriod Course.': "Erreur du serveur lors de la mise à jour du parcours d'accompagnement.", diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/App.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/App.vue index c88de825b..f9d1a747c 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/App.vue +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/App.vue @@ -251,6 +251,7 @@ relatedEntityClass="Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork" :relatedEntityId="this.work.id" :workflowsAvailables="this.work.workflows_availables" + @go-to-generate-workflow="goToGenerateWorkflow" > @@ -284,6 +285,7 @@ import OnTheFly from 'ChillMainAssets/vuejs/OnTheFly/components/OnTheFly.vue'; import ListWorkflowModal from 'ChillMainAssets/vuejs/_components/EntityWorkflow/ListWorkflowModal.vue'; import PickWorkflow from 'ChillMainAssets/vuejs/_components/EntityWorkflow/PickWorkflow.vue'; import PersonText from 'ChillPersonAssets/vuejs/_components/Entity/PersonText.vue'; +import {buildLinkCreate} from 'ChillMainAssets/lib/entity-workflow/api.js'; const i18n = { messages: { @@ -334,7 +336,6 @@ export default { ListWorkflowModal, OnTheFly, PickWorkflow, - OnTheFly, PersonText, }, i18n, @@ -461,6 +462,15 @@ export default { removeThirdParty(t) { this.$store.commit('removeThirdParty', t); }, + goToGenerateWorkflow({link}) { + console.log('save before leave to generate workflow') + const callback = (data) => { + window.location.assign(link); + }; + + return this.$store.dispatch('submit', callback) + .catch(e => { console.log(e); throw e; }); + }, submit() { this.$store.dispatch('submit'); }, diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/components/AddEvaluation.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/components/AddEvaluation.vue index 08cb6b3e6..faf4b0ed0 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/components/AddEvaluation.vue +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/components/AddEvaluation.vue @@ -10,16 +10,16 @@