Merge branch 'master' into issue428_person_resource_ameliorations

This commit is contained in:
Julien Fastré 2022-02-01 18:08:48 +01:00
commit bc4b2c4e86
53 changed files with 650 additions and 243 deletions

View File

@ -29,11 +29,16 @@ and this project adheres to
* [parcours]: component added to change the opening date of a parcours (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/411) * [parcours]: component added to change the opening date of a parcours (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/411)
* [search]: listing of parcours display changed (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/410) * [search]: listing of parcours display changed (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/410)
* [user]: page with accompanying periods to which is user is referent (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/408) * [user]: page with accompanying periods to which is user is referent (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/408)
* [person]: Comment on marital status is possible even if marital status is not defined (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/421)
* [parcours]: In the list of person results the requestor is not displayed if defined as anonymous (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/424)
* [bugfix]: modal closes and newly created person/thirdparty is selected when multiple persons/thirdparties are created through the modal (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/429)
* [person] age added to renderstring + renderbox/ vue component created to display person text (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/389) * [person] age added to renderstring + renderbox/ vue component created to display person text (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/389)
* [household member editor] allow to push to existing household * [household member editor] allow to push to existing household
* [person_resource]: Onthefly button added to view person/thirdparty and badge differentiation for a contact-thirdparty (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/428) * [person_resource]: Onthefly button added to view person/thirdparty and badge differentiation for a contact-thirdparty (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/428)
* [workflow][notification] improve how notifications and workflows are 'attached' to entities: contextual list, counter, buttons and vue modal * [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 ### test release 2021-01-28

View File

@ -34,6 +34,7 @@ use Psr\Log\LoggerInterface;
use RuntimeException; use RuntimeException;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormInterface; use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
@ -200,12 +201,36 @@ final class ActivityController extends AbstractController
'role' => new Role('CHILL_ACTIVITY_UPDATE'), 'role' => new Role('CHILL_ACTIVITY_UPDATE'),
'activityType' => $entity->getActivityType(), 'activityType' => $entity->getActivityType(),
'accompanyingPeriod' => $accompanyingPeriod, 'accompanyingPeriod' => $accompanyingPeriod,
])->handleRequest($request); ]);
if ($form->has('documents')) {
$form->add('gendocTemplateId', HiddenType::class, [
'mapped' => false,
'data' => null,
'attr' => [
// required for js
'data-template-id' => 'data-template-id',
],
]);
}
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) { if ($form->isSubmitted() && $form->isValid()) {
$this->entityManager->persist($entity); $this->entityManager->persist($entity);
$this->entityManager->flush(); $this->entityManager->flush();
if ($form->has('gendocTemplateId') && '' !== $form['gendocTemplateId']) {
return $this->redirectToRoute(
'chill_docgenerator_generate_from_template',
[
'template' => $form->get('gendocTemplateId')->getData(),
'entityClassName' => Activity::class,
'entityId' => $entity->getId(),
]
);
}
$this->addFlash('success', $this->get('translator')->trans('Success : activity updated!')); $this->addFlash('success', $this->get('translator')->trans('Success : activity updated!'));
$params = $this->buildParamsToUrl($person, $accompanyingPeriod); $params = $this->buildParamsToUrl($person, $accompanyingPeriod);
@ -393,12 +418,36 @@ final class ActivityController extends AbstractController
'role' => new Role('CHILL_ACTIVITY_CREATE'), 'role' => new Role('CHILL_ACTIVITY_CREATE'),
'activityType' => $entity->getActivityType(), 'activityType' => $entity->getActivityType(),
'accompanyingPeriod' => $accompanyingPeriod, 'accompanyingPeriod' => $accompanyingPeriod,
])->handleRequest($request); ]);
if ($form->has('documents')) {
$form->add('gendocTemplateId', HiddenType::class, [
'mapped' => false,
'data' => null,
'attr' => [
// required for js
'data-template-id' => 'data-template-id',
],
]);
}
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) { if ($form->isSubmitted() && $form->isValid()) {
$this->entityManager->persist($entity); $this->entityManager->persist($entity);
$this->entityManager->flush(); $this->entityManager->flush();
if ($form->has('gendocTemplateId') && '' !== $form['gendocTemplateId']) {
return $this->redirectToRoute(
'chill_docgenerator_generate_from_template',
[
'template' => $form->get('gendocTemplateId')->getData(),
'entityClassName' => Activity::class,
'entityId' => $entity->getId(),
]
);
}
$this->addFlash('success', $this->get('translator')->trans('Success : activity created!')); $this->addFlash('success', $this->get('translator')->trans('Success : activity created!'));
$params = $this->buildParamsToUrl($person, $accompanyingPeriod); $params = $this->buildParamsToUrl($person, $accompanyingPeriod);

View File

@ -2,11 +2,15 @@ import { createApp } from 'vue';
import { _createI18n } from 'ChillMainAssets/vuejs/_js/i18n' import { _createI18n } from 'ChillMainAssets/vuejs/_js/i18n'
import { activityMessages } from './i18n' import { activityMessages } from './i18n'
import store from './store' import store from './store'
import PickTemplate from 'ChillDocGeneratorAssets/vuejs/_components/PickTemplatePreventMoving.vue';
import {fetchTemplates} from 'ChillDocGeneratorAssets/api/pickTemplate.js';
import App from './App.vue'; import App from './App.vue';
const i18n = _createI18n(activityMessages); const i18n = _createI18n(activityMessages);
// app for activity
const hasSocialIssues = document.querySelector('#social-issues-acc') !== null; const hasSocialIssues = document.querySelector('#social-issues-acc') !== null;
const hasLocation = document.querySelector('#location') !== null; const hasLocation = document.querySelector('#location') !== null;
const hasPerson = document.querySelector('#add-persons') !== null; const hasPerson = document.querySelector('#add-persons') !== null;
@ -29,3 +33,54 @@ const app = createApp({
.use(i18n) .use(i18n)
.component('app', App) .component('app', App)
.mount('#activity'); .mount('#activity');
// app for picking template
const i18nGendoc = _createI18n({});
document.querySelectorAll('div[data-docgen-template-picker]').forEach(el => {
fetchTemplates(el.dataset.entityClass).then(templates => {
const picker = {
template:
'<pick-template :templates="this.templates" :preventDefaultMoveToGenerate="true" ' +
':entityClass="faked" @go-to-generate-document="generateDoc"></pick-template>',
components: {
PickTemplate,
},
data() {
return {
templates: templates,
entityId: el.dataset.entityId,
}
},
methods: {
generateDoc({event, link, template}) {
console.log('generateDoc');
console.log('link', link);
console.log('template', template);
let hiddenInput = document.querySelector("input[data-template-id]");
if (hiddenInput === null) {
console.error('hidden input not found');
return;
}
hiddenInput.value = template;
let form = document.querySelector('form[name="chill_activitybundle_activity"');
if (form === null) {
console.error('form not found');
return;
}
form.submit();
}
}
};
createApp(picker).use(i18nGendoc).mount(el);
})
});

View File

@ -89,9 +89,9 @@
{%- if edit_form.documents is defined -%} {%- if edit_form.documents is defined -%}
{{ form_row(edit_form.documents) }} {{ form_row(edit_form.documents) }}
<div data-docgen-template-picker="data-docgen-template-picker" data-entity-class="Chill\ActivityBundle\Entity\Activity" data-entity-id="{{ entity.id }}"></div>
{% endif %} {% endif %}
<div data-docgen-template-picker="data-docgen-template-picker" data-entity-class="Chill\ActivityBundle\Entity\Activity" data-entity-id="{{ entity.id }}"></div>
{% set person_id = null %} {% set person_id = null %}
{% if entity.person %} {% if entity.person %}

View File

@ -24,12 +24,10 @@
window.activity = {{ activity_json|json_encode|raw }}; window.activity = {{ activity_json|json_encode|raw }};
</script> </script>
{{ encore_entry_script_tags('vue_activity') }} {{ encore_entry_script_tags('vue_activity') }}
{{ encore_entry_script_tags('mod_docgen_picktemplate') }}
{% endblock %} {% endblock %}
{% block css %} {% block css %}
{{ parent() }} {{ parent() }}
{{ encore_entry_link_tags('mod_async_upload') }} {{ encore_entry_link_tags('mod_async_upload') }}
{{ encore_entry_link_tags('vue_activity') }} {{ encore_entry_link_tags('vue_activity') }}
{{ encore_entry_link_tags('mod_docgen_picktemplate') }}
{% endblock %} {% endblock %}

View File

@ -39,11 +39,9 @@
window.activity = {{ activity_json|json_encode|raw }}; window.activity = {{ activity_json|json_encode|raw }};
</script> </script>
{{ encore_entry_script_tags('vue_activity') }} {{ encore_entry_script_tags('vue_activity') }}
{{ encore_entry_script_tags('mod_docgen_picktemplate') }}
{% endblock %} {% endblock %}
{% block css %} {% block css %}
{{ encore_entry_link_tags('mod_async_upload') }} {{ encore_entry_link_tags('mod_async_upload') }}
{{ encore_entry_link_tags('vue_activity') }} {{ encore_entry_link_tags('vue_activity') }}
{{ encore_entry_link_tags('mod_docgen_picktemplate') }}
{% endblock %} {% endblock %}

View File

@ -87,6 +87,7 @@
{%- if form.documents is defined -%} {%- if form.documents is defined -%}
{{ form_row(form.documents) }} {{ form_row(form.documents) }}
<div data-docgen-template-picker="data-docgen-template-picker" data-entity-class="Chill\ActivityBundle\Entity\Activity" data-entity-id="{{ entity.id }}"></div>
{% endif %} {% endif %}
{%- if form.attendee is defined -%} {%- if form.attendee is defined -%}

View File

@ -17,10 +17,6 @@
{{ parent() }} {{ parent() }}
{{ encore_entry_script_tags('mod_async_upload') }} {{ encore_entry_script_tags('mod_async_upload') }}
<script type="text/javascript"> <script type="text/javascript">
window.addEventListener('DOMContentLoaded', function (e) {
chill.displayAlertWhenLeavingUnsubmittedForm('form[name="{{ form.vars.form.vars.name }}"]',
'{{ "You are going to leave a page with unsubmitted data. Are you sure you want to leave ?"|trans }}');
});
window.activity = {{ activity_json|json_encode|raw }}; window.activity = {{ activity_json|json_encode|raw }};
{% if default_location is not null %}window.default_location_id = {{ default_location.id }}{% endif %}; {% if default_location is not null %}window.default_location_id = {{ default_location.id }}{% endif %};
</script> </script>

View File

@ -17,7 +17,6 @@ use Chill\DocGeneratorBundle\Context\DocGeneratorContextWithPublicFormInterface;
use Chill\DocGeneratorBundle\Context\Exception\UnexpectedTypeException; use Chill\DocGeneratorBundle\Context\Exception\UnexpectedTypeException;
use Chill\DocGeneratorBundle\Entity\DocGeneratorTemplate; use Chill\DocGeneratorBundle\Entity\DocGeneratorTemplate;
use Chill\DocGeneratorBundle\Service\Context\BaseContextData; use Chill\DocGeneratorBundle\Service\Context\BaseContextData;
use Chill\DocStoreBundle\Entity\Document;
use Chill\DocStoreBundle\Entity\StoredObject; use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\DocStoreBundle\Repository\DocumentCategoryRepository; use Chill\DocStoreBundle\Repository\DocumentCategoryRepository;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface; use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
@ -210,11 +209,8 @@ class ActivityContext implements
*/ */
public function storeGenerated(DocGeneratorTemplate $template, StoredObject $storedObject, object $entity, array $contextGenerationData): void public function storeGenerated(DocGeneratorTemplate $template, StoredObject $storedObject, object $entity, array $contextGenerationData): void
{ {
$doc = new StoredObject(); $entity->addDocument($storedObject);
// TODO push document to remote
$this->em->persist($doc); $this->em->persist($storedObject);
$entity->addDocument($doc);
} }
} }

View File

@ -9,7 +9,7 @@
declare(strict_types=1); declare(strict_types=1);
namespace Chill\ActivityBundle\Tests\Aggregator; namespace Chill\ActivityBundle\Tests\Export\Aggregator;
use Chill\MainBundle\Test\Export\AbstractAggregatorTest; use Chill\MainBundle\Test\Export\AbstractAggregatorTest;

View File

@ -9,7 +9,7 @@
declare(strict_types=1); declare(strict_types=1);
namespace Chill\ActivityBundle\Tests\Aggregator; namespace Chill\ActivityBundle\Tests\Export\Aggregator;
use Chill\MainBundle\Test\Export\AbstractAggregatorTest; use Chill\MainBundle\Test\Export\AbstractAggregatorTest;

View File

@ -9,7 +9,7 @@
declare(strict_types=1); declare(strict_types=1);
namespace Chill\ActivityBundle\Tests\Aggregator; namespace Chill\ActivityBundle\Tests\Export\Aggregator;
use Chill\MainBundle\Test\Export\AbstractAggregatorTest; use Chill\MainBundle\Test\Export\AbstractAggregatorTest;

View File

@ -9,7 +9,7 @@
declare(strict_types=1); declare(strict_types=1);
namespace Chill\ActivityBundle\Tests\Filter; namespace Chill\ActivityBundle\Tests\Export\Filter;
use Chill\MainBundle\Test\Export\AbstractFilterTest; use Chill\MainBundle\Test\Export\AbstractFilterTest;
use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\ArrayCollection;

View File

@ -9,7 +9,7 @@
declare(strict_types=1); declare(strict_types=1);
namespace Chill\ActivityBundle\Tests\Filter; namespace Chill\ActivityBundle\Tests\Export\Filter;
use Chill\MainBundle\Test\Export\AbstractFilterTest; use Chill\MainBundle\Test\Export\AbstractFilterTest;
use DateTime; use DateTime;

View File

@ -9,7 +9,7 @@
declare(strict_types=1); declare(strict_types=1);
namespace Chill\CustomFieldsBundle\Tests; namespace Chill\CustomFieldsBundle\Tests\CustomFields;
use Chill\CustomFieldsBundle\CustomFields\CustomFieldChoice; use Chill\CustomFieldsBundle\CustomFields\CustomFieldChoice;
use Chill\CustomFieldsBundle\Entity\CustomField; use Chill\CustomFieldsBundle\Entity\CustomField;

View File

@ -9,7 +9,7 @@
declare(strict_types=1); declare(strict_types=1);
namespace Chill\CustomFieldsBundle\Tests; namespace Chill\CustomFieldsBundle\Tests\CustomFields;
use Chill\CustomFieldsBundle\CustomFields\CustomFieldNumber; use Chill\CustomFieldsBundle\CustomFields\CustomFieldNumber;
use Chill\CustomFieldsBundle\Entity\CustomField; use Chill\CustomFieldsBundle\Entity\CustomField;

View File

@ -9,10 +9,11 @@
declare(strict_types=1); declare(strict_types=1);
namespace Chill\CustomFieldsBundle\Tests; namespace Chill\CustomFieldsBundle\Tests\CustomFields;
use Chill\CustomFieldsBundle\CustomFields\CustomFieldText; use Chill\CustomFieldsBundle\CustomFields\CustomFieldText;
use Chill\CustomFieldsBundle\Entity\CustomField; use Chill\CustomFieldsBundle\Entity\CustomField;
use Chill\CustomFieldsBundle\Tests\CustomFieldTestHelper;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
/** /**

View File

@ -9,7 +9,7 @@
declare(strict_types=1); declare(strict_types=1);
namespace Chill\CustomFieldsBundle\Tests; namespace Chill\CustomFieldsBundle\Tests\Routing;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;

View File

@ -0,0 +1,13 @@
const buildLink = function(templateId, entityId, entityClass) {
const
entityIdEncoded = encodeURI(entityId),
returnPath = encodeURIComponent(window.location.pathname + window.location.search + window.location.hash),
entityClassEncoded = encodeURI(entityClass),
url = `/fr/doc/gen/generate/from/${templateId}/for/${entityClassEncoded}/${entityIdEncoded}?returnPath=${returnPath}`
;
console.log('computed Url');
return url;
};
export {buildLink};

View File

@ -20,8 +20,8 @@
<option v-bind:value="t.id">{{ t.name.fr || 'Aucun nom défini' }}</option> <option v-bind:value="t.id">{{ t.name.fr || 'Aucun nom défini' }}</option>
</template> </template>
</select> </select>
<button v-if="canGenerate" class="btn btn-update btn-sm change-icon" type="button" @click="generateDocument"><i class="fa fa-fw fa-cog"></i></button> <a v-if="canGenerate" class="btn btn-update btn-sm change-icon" :href="buildUrlGenerate" @click.prevent="clickGenerate($event, buildUrlGenerate)"><i class="fa fa-fw fa-cog"></i></a>
<button v-else class="btn btn-update btn-sm change-icon" type="button" disabled ><i class="fa fa-fw fa-cog"></i></button> <a v-else class="btn btn-update btn-sm change-icon" href="#" disabled ><i class="fa fa-fw fa-cog"></i></a>
</div> </div>
</div> </div>
</div> </div>
@ -39,24 +39,27 @@
<script> <script>
import {buildLink} from 'ChillDocGeneratorAssets/lib/document-generator';
export default { export default {
name: "PickTemplate", name: "PickTemplate",
props: { props: {
entityId: [String, Number], entityId: [String, Number],
entityClass: { entityClass: {
type: String, type: String,
required: true, required: false,
}, },
templates: { templates: {
type: Array, type: Array,
required: true, required: true,
}, },
// beforeMove execute "something" before preventDefaultMoveToGenerate: {
beforeMove: { type: Boolean,
type: Function,
required: false, required: false,
default: false,
} }
}, },
emits: ['goToGenerateDocument'],
data() { data() {
return { return {
template: null, template: null,
@ -74,66 +77,37 @@ export default {
return true; return true;
}, },
getDescription() { getDescription() {
if (null === this.template) {
return '';
}
let desc = this.templates.find(t => t.id === this.template); let desc = this.templates.find(t => t.id === this.template);
if (null === desc) { if (null === desc) {
return ''; return '';
} }
return desc.description || ''; return desc.description || '';
}, },
buildUrlGenerate() {
if (null === this.template) {
return '#';
}
return buildLink(this.template, this.entityId, this.entityClass);
}
}, },
methods: { methods: {
generateDocument() { clickGenerate(event, link) {
console.log('generateDocument'); if (!this.preventDefaultMoveToGenerate) {
console.log('beforeMove', this.beforeMove); window.location.assign(link);
if (this.beforeMove != null) {
console.log('execute before move');
let r = this.beforeMove();
if (r instanceof Promise) {
r.then((obj) => this.goToGenerate(obj));
} else {
this.goToGenerate();
}
} else {
this.goToGenerate();
}
},
goToGenerate(obj) {
console.log('goToGenerate');
console.log('obj', obj);
console.log('entityId', this.entityId);
let
realId = this.entityId,
realEntityClass = this.entityClass
;
if (obj !== undefined) {
if (obj.entityId !== undefined) {
realId = obj.entityId;
}
if (obj.entityClass !== undefined) {
realEntityClass = obj.entityClass;
}
} }
console.log('realId', realId); this.$emit('goToGenerateDocument', {event, link, template: this.template});
console.log('realEntityClass', realEntityClass);
const
entityId = encodeURI(realId),
returnPath = encodeURIComponent(window.location.pathname + window.location.search + window.location.hash),
fqdnEntityClass = encodeURI(realEntityClass),
url = `/fr/doc/gen/generate/from/${this.template}/for/${fqdnEntityClass}/${entityId}?returnPath=${returnPath}`
;
console.log('I will generate your doc at', url);
window.location.assign(url);
}, },
}, },
i18n: { i18n: {
messages: { messages: {
fr: { fr: {
generate_document: 'Générer un document', generate_document: 'Générer un document',
select_a_template: 'Choisir un gabarit', select_a_template: 'Choisir un modèle',
choose_a_template: 'Choisir', choose_a_template: 'Choisir',
} }
} }

View File

@ -19,6 +19,7 @@ use Chill\MainBundle\Pagination\PaginatorFactory;
use Chill\MainBundle\Repository\Workflow\EntityWorkflowRepository; use Chill\MainBundle\Repository\Workflow\EntityWorkflowRepository;
use Chill\MainBundle\Security\Authorization\EntityWorkflowVoter; use Chill\MainBundle\Security\Authorization\EntityWorkflowVoter;
use Chill\MainBundle\Workflow\EntityWorkflowManager; use Chill\MainBundle\Workflow\EntityWorkflowManager;
use Chill\MainBundle\Workflow\Validator\StepDestValid;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
@ -201,11 +202,20 @@ class WorkflowController extends AbstractController
$entityWorkflow->getCurrentStep()->addDestUser($user); $entityWorkflow->getCurrentStep()->addDestUser($user);
} }
$errors = $this->validator->validate(
$entityWorkflow->getCurrentStep(),
new StepDestValid()
);
if (count($errors) === 0) {
$this->entityManager->flush(); $this->entityManager->flush();
return $this->redirectToRoute('chill_main_workflow_show', ['id' => $entityWorkflow->getId()]); return $this->redirectToRoute('chill_main_workflow_show', ['id' => $entityWorkflow->getId()]);
} }
return new Response((string) $errors, Response::HTTP_UNPROCESSABLE_ENTITY);
}
if ($transitionForm->isSubmitted() && !$transitionForm->isValid()) { if ($transitionForm->isSubmitted() && !$transitionForm->isValid()) {
$this->addFlash('error', $this->translator->trans('This form contains errors')); $this->addFlash('error', $this->translator->trans('This form contains errors'));
} }
@ -235,6 +245,7 @@ class WorkflowController extends AbstractController
'handler_template_data' => $handler->getTemplateData($entityWorkflow), 'handler_template_data' => $handler->getTemplateData($entityWorkflow),
'transition_form' => isset($transitionForm) ? $transitionForm->createView() : null, 'transition_form' => isset($transitionForm) ? $transitionForm->createView() : null,
'entity_workflow' => $entityWorkflow, 'entity_workflow' => $entityWorkflow,
'transition_form_errors' => $errors ?? [],
//'comment_form' => $commentForm->createView(), //'comment_form' => $commentForm->createView(),
] ]
); );

View File

@ -174,6 +174,20 @@ class EntityWorkflow implements TrackCreationInterface, TrackUpdateInterface
return null; 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 public function getCurrentStepCreatedAt(): ?DateTimeInterface
{ {
if (null !== $previous = $this->getPreviousStepIfAny()) { if (null !== $previous = $this->getPreviousStepIfAny()) {

View File

@ -48,6 +48,8 @@ class WorkflowStepType extends AbstractType
$entityWorkflow = $options['entity_workflow']; $entityWorkflow = $options['entity_workflow'];
$handler = $this->entityWorkflowManager->getHandler($entityWorkflow); $handler = $this->entityWorkflowManager->getHandler($entityWorkflow);
$workflow = $this->registry->get($entityWorkflow, $entityWorkflow->getWorkflowName()); $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 (true === $options['transition']) {
if (null === $options['entity_workflow']) { if (null === $options['entity_workflow']) {
@ -68,9 +70,34 @@ class WorkflowStepType extends AbstractType
$transitions $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 $builder
->add('transition', ChoiceType::class, [ ->add('transition', ChoiceType::class, [
'label' => 'workflow.Transition to apply', 'label' => 'workflow.Next step',
'mapped' => false, 'mapped' => false,
'multiple' => false, 'multiple' => false,
'expanded' => true, 'expanded' => true,
@ -86,6 +113,17 @@ class WorkflowStepType extends AbstractType
}, },
'choice_attr' => static function (Transition $transition) use ($workflow) { 'choice_attr' => static function (Transition $transition) use ($workflow) {
$toFinal = true; $toFinal = true;
$isForward = 'neutral';
$metadata = $workflow->getMetadataStore()->getTransitionMetadata($transition);
if (array_key_exists('isForward', $metadata)) {
if ($metadata['isForward']) {
$isForward = 'forward';
} else {
$isForward = 'backward';
}
}
foreach ($transition->getTos() as $to) { foreach ($transition->getTos() as $to) {
$meta = $workflow->getMetadataStore()->getPlaceMetadata($to); $meta = $workflow->getMetadataStore()->getPlaceMetadata($to);
@ -100,6 +138,7 @@ class WorkflowStepType extends AbstractType
return [ return [
'data-is-transition' => 'data-is-transition', 'data-is-transition' => 'data-is-transition',
'data-to-final' => $toFinal ? '1' : '0', 'data-to-final' => $toFinal ? '1' : '0',
'data-is-forward' => $isForward,
]; ];
}, },
]) ])

View File

@ -26,7 +26,9 @@ var ShowHide = function(options) {
container_content = [], container_content = [],
debug = 'debug' in options ? options.debug : false, debug = 'debug' in options ? options.debug : false,
load_event = 'load_event' in options ? options.load_event : 'load', 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) { var bootstrap = function(event) {
if (debug) { if (debug) {
@ -39,7 +41,6 @@ var ShowHide = function(options) {
contents.push(el); contents.push(el);
} }
container_content.push(contents); container_content.push(contents);
// console.log('container content', container_content);
} }
// attach the listener on each input // attach the listener on each input
@ -96,11 +97,15 @@ var ShowHide = function(options) {
if (debug) { if (debug) {
console.log('force hide'); console.log('force hide');
} }
if (toggle_callback !== null) {
toggle_callback(container, 'hide');
} else {
for (let contents of container_content.values()) { for (let contents of container_content.values()) {
for (let el of contents.values()) { for (let el of contents.values()) {
el.remove(); el.remove();
} }
} }
}
is_shown = false; is_shown = false;
}; };
@ -108,12 +113,16 @@ var ShowHide = function(options) {
if (debug) { if (debug) {
console.log('show'); console.log('show');
} }
if (toggle_callback !== null) {
toggle_callback(container, 'show');
} else {
for (let i of container_content.keys()) { for (let i of container_content.keys()) {
var contents = container_content[i]; var contents = container_content[i];
for (let el of contents.values()) { for (let el of contents.values()) {
container[i].appendChild(el); container[i].appendChild(el);
} }
} }
}
is_shown = true; is_shown = true;
}; };

View File

@ -2,29 +2,89 @@ import {ShowHide} from 'ChillMainAssets/lib/show_hide/show_hide.js';
window.addEventListener('DOMContentLoaded', function() { window.addEventListener('DOMContentLoaded', function() {
let let
finalizeAfterContainer = document.querySelector('#finalizeAfter'), divTransitions = document.querySelector('#transitions'),
futureDestUsersContainer = document.querySelector('#futureDestUsers') futureDestUsersContainer = document.querySelector('#futureDestUsers')
; ;
if (null === finalizeAfterContainer) { if (null !== divTransitions) {
return; 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({ new ShowHide({
load_event: null, load_event: null,
froms: [finalizeAfterContainer], froms: [transitionFilterContainer],
container: [futureDestUsersContainer], container: row,
test: function(containers, arg2, arg3) { test: function (containers) {
for (let container of containers) { for (let container of containers) {
for (let input of container.querySelectorAll('input')) { for (let input of container.querySelectorAll('input')) {
if (!input.checked) { if (input.checked) {
return true; 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 { } else {
return false; 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.');
}
}
});
}); });

View File

@ -12,6 +12,7 @@
:relatedEntityClass="this.relatedEntityClass" :relatedEntityClass="this.relatedEntityClass"
:relatedEntityId="this.relatedEntityId" :relatedEntityId="this.relatedEntityId"
:workflowsAvailables="workflowsAvailables" :workflowsAvailables="workflowsAvailables"
@go-to-generate-workflow="goToGenerateWorkflow"
></pick-workflow> ></pick-workflow>
<teleport to="body"> <teleport to="body">
@ -34,6 +35,8 @@
:relatedEntityClass="this.relatedEntityClass" :relatedEntityClass="this.relatedEntityClass"
:relatedEntityId="this.relatedEntityId" :relatedEntityId="this.relatedEntityId"
:workflowsAvailables="workflowsAvailables" :workflowsAvailables="workflowsAvailables"
:preventDefaultMoveToGenerate="this.$props.preventDefaultMoveToGenerate"
@go-to-generate-workflow="this.goToGenerateWorkflow"
></pick-workflow> ></pick-workflow>
</template> </template>
@ -53,6 +56,7 @@ export default {
PickWorkflow, PickWorkflow,
ListWorkflowVue ListWorkflowVue
}, },
emits: ['goToGenerateWorkflow'],
props: { props: {
workflows: { workflows: {
type: Array, type: Array,
@ -73,7 +77,12 @@ export default {
workflowsAvailables: { workflowsAvailables: {
type: Array, type: Array,
required: true, required: true,
} },
preventDefaultMoveToGenerate: {
type: Boolean,
required: false,
default: false,
},
}, },
data() { data() {
return { return {
@ -95,6 +104,10 @@ export default {
openModal() { openModal() {
this.modal.showModal = true; this.modal.showModal = true;
}, },
goToGenerateWorkflow(data) {
console.log('go to generate workflow intercepted');
this.$emit('goToGenerateWorkflow', data);
}
}, },
i18n: { i18n: {
messages: { messages: {

View File

@ -6,7 +6,7 @@
</button> </button>
<ul class="dropdown-menu" aria-labelledby="createWorkflowButton"> <ul class="dropdown-menu" aria-labelledby="createWorkflowButton">
<li v-for="w in workflowsAvailables" :key="w.name"> <li v-for="w in workflowsAvailables" :key="w.name">
<a class="dropdown-item" :href="makeLink(w.name)" @click="goToGenerateWorkflow($event, w.name)">{{ w.text }}</a> <a class="dropdown-item" :href="makeLink(w.name)" @click.prevent="goToGenerateWorkflow($event, w.name)">{{ w.text }}</a>
</li> </li>
</ul> </ul>
</div> </div>
@ -31,7 +31,12 @@ export default {
workflowsAvailables: { workflowsAvailables: {
type: Array, type: Array,
required: true, required: true,
} },
preventDefaultMoveToGenerate: {
type: Boolean,
required: false,
default: false,
},
}, },
emits: ['goToGenerateWorkflow'], emits: ['goToGenerateWorkflow'],
methods: { methods: {
@ -39,6 +44,12 @@ export default {
return buildLinkCreate(workflowName, this.relatedEntityClass, this.relatedEntityId); return buildLinkCreate(workflowName, this.relatedEntityClass, this.relatedEntityId);
}, },
goToGenerateWorkflow(event, workflowName) { goToGenerateWorkflow(event, workflowName) {
console.log('goToGenerateWorkflow', event, workflowName);
if (!this.$props.preventDefaultMoveToGenerate) {
event.target.click();
}
this.$emit('goToGenerateWorkflow', {event, workflowName, link: this.makeLink(workflowName)}); this.$emit('goToGenerateWorkflow', {event, workflowName, link: this.makeLink(workflowName)});
} }
} }

View File

@ -3,7 +3,58 @@
{% if transition_form is not null %} {% if transition_form is not null %}
{{ form_start(transition_form) }} {{ form_start(transition_form) }}
{% 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 %}
<div class="card">
<div class="card-body">
<div class="row">
<div class="col-sm-12">
{{ 'workflow.Current step'|trans }}&nbsp;:
<span class="badge bg-primary">{{ label }}</span>
</div>
</div>
{% if step.previous is not null %}
{% if step.previous.comment is not empty %}
<div class="row">
<div class="col-sm-12">
<blockquote class="chill-user-quote">
{{ step.previous.comment|chill_markdown_to_html }}
</blockquote>
</div>
</div>
{% endif %}
<div class="row">
<div class="col-sm-12">
{{ 'By'|trans }}
{{ step.previous.transitionBy|chill_entity_render_box }},
{{ step.previous.transitionAt|format_datetime('short', 'short') }}
</div>
</div>
{% else %}
<div class="row">
<div class="col-sm-4">{{ 'workflow.Created by'|trans }}</div>
<div class="col-sm-8">{{ step.entityWorkflow.createdBy|chill_entity_render_box }}</div>
</div>
<div class="row">
<div class="col-sm-4">{{ 'Le'|trans }}</div>
<div class="col-sm-8">{{ step.entityWorkflow.createdAt|format_datetime('short', 'short') }}</div>
</div>
{% endif %}
</div>
</div>
<div id="transitionFilter">
{% if transition_form.transitionFilter is defined %}
{{ form_row(transition_form.transitionFilter) }}
{% endif %}
</div>
<div id="transitions">
{{ form_row(transition_form.transition) }} {{ form_row(transition_form.transition) }}
</div>
{% if transition_form.freezeAfter is defined %} {% if transition_form.freezeAfter is defined %}
{{ form_row(transition_form.freezeAfter) }} {{ form_row(transition_form.freezeAfter) }}

View File

@ -69,6 +69,18 @@
</blockquote> </blockquote>
</div> </div>
{% endif %} {% endif %}
{% if loop.last and step.destUser|length > 0 %}
<div class="item-row separator">
<div>
<p><b>{{ 'workflow.Users allowed to apply transition'|trans }}&nbsp;: </b></p>
<ul>
{% for u in step.destUser %}
<li>{{ u|chill_entity_render_box }}</li>
{% endfor %}
</ul>
</div>
</div>
{% endif %}
</div> </div>
{% endfor %} {% endfor %}

View File

@ -44,7 +44,6 @@ class NotificationNormalizer implements NormalizerAwareInterface, NormalizerInte
*/ */
public function normalize($object, ?string $format = null, array $context = []) public function normalize($object, ?string $format = null, array $context = [])
{ {
dump($object);
$entity = $this->entityManager $entity = $this->entityManager
->getRepository($object->getRelatedEntityClass()) ->getRepository($object->getRelatedEntityClass())
->find($object->getRelatedEntityId()); ->find($object->getRelatedEntityId());

View File

@ -9,7 +9,7 @@
declare(strict_types=1); declare(strict_types=1);
namespace Chill\MainBundle\Tests\Security\Authorization; namespace Chill\MainBundle\Tests\Authorization;
use Chill\MainBundle\Security\ParentRoleHelper; use Chill\MainBundle\Security\ParentRoleHelper;
use Chill\PersonBundle\Security\Authorization\PersonVoter; use Chill\PersonBundle\Security\Authorization\PersonVoter;

View File

@ -9,10 +9,11 @@
declare(strict_types=1); declare(strict_types=1);
namespace Chill\MainBundle\Form\Type; namespace Chill\MainBundle\Tests\Form\Type;
use Chill\MainBundle\Entity\GroupCenter; use Chill\MainBundle\Entity\GroupCenter;
use Chill\MainBundle\Entity\User; use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Form\CenterType;
use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\Extension\Core\Type\HiddenType; use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\Test\TypeTestCase; use Symfony\Component\Form\Test\TypeTestCase;

View File

@ -9,7 +9,7 @@
declare(strict_types=1); declare(strict_types=1);
namespace Chill\MainBundle\Test\Search; namespace Chill\MainBundle\Tests\Search;
use Chill\MainBundle\Search\ParsingException; use Chill\MainBundle\Search\ParsingException;
use Chill\MainBundle\Search\SearchInterface; use Chill\MainBundle\Search\SearchInterface;

View File

@ -9,7 +9,7 @@
declare(strict_types=1); declare(strict_types=1);
namespace Chill\MainBundle\Tests\PasswordRecover; namespace Chill\MainBundle\Tests\Security\PasswordRecover;
use Chill\MainBundle\Entity\User; use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Security\PasswordRecover\TokenManager; use Chill\MainBundle\Security\PasswordRecover\TokenManager;

View File

@ -0,0 +1,26 @@
<?php
/**
* 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.
*/
declare(strict_types=1);
namespace Chill\MainBundle\Workflow\Validator;
use Symfony\Component\Validator\Constraint;
class StepDestValid extends Constraint
{
public string $messageDestNotAllowed = 'workflow.As the step is final, no dest are allowed';
public string $messageRequireDest = 'workflow.As the step is not final, dest are required';
public function getTargets()
{
return [self::CLASS_CONSTRAINT];
}
}

View File

@ -0,0 +1,49 @@
<?php
/**
* 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.
*/
declare(strict_types=1);
namespace Chill\MainBundle\Workflow\Validator;
use Chill\MainBundle\Entity\Workflow\EntityWorkflowStep;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
use Symfony\Component\Validator\Exception\UnexpectedValueException;
use function count;
class StepDestValidValidator extends ConstraintValidator
{
/**
* @param EntityWorkflowStep $value
* @param Constraint|StepDestValid $constraint
*
* @return void
*/
public function validate($value, Constraint $constraint)
{
if (!$constraint instanceof StepDestValid) {
throw new UnexpectedTypeException($constraint, StepDestValid::class);
}
if (!$value instanceof EntityWorkflowStep) {
throw new UnexpectedValueException($value, EntityWorkflowStep::class);
}
if ($value->isFinal() && 0 < count($value->getDestUser())) {
$this->context->buildViolation($constraint->messageDestNotAllowed)
->addViolation();
}
if (!$value->isFinal() && 0 === count($value->getDestUser())) {
$this->context->buildViolation($constraint->messageRequireDest)
->addViolation();
}
}
}

View File

@ -370,7 +370,8 @@ Workflow history: Historique de la décision
workflow: workflow:
Created by: Créé par 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 dest for next steps: Utilisateurs qui valideront la prochaine étape
Freeze: Geler Freeze: Geler
Freezed: Gelé Freezed: Gelé
@ -392,6 +393,9 @@ workflow:
dest: Workflows en attente d'action dest: Workflows en attente d'action
you subscribed to all steps: Vous recevrez une notification à chaque étape you subscribed to all steps: Vous recevrez une notification à chaque étape
you subscribed to final step: Vous recevrez une notification à l'étape finale 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 final: Recevoir une notification à l'étape finale
Subscribe all steps: Recevoir une notification à chaque étape Subscribe all steps: Recevoir une notification à chaque étape

View File

@ -99,7 +99,7 @@ class AccompanyingPeriodWorkEvaluationApiController
if ($request->query->getBoolean('countOnly', false)) { if ($request->query->getBoolean('countOnly', false)) {
return new JsonResponse( return new JsonResponse(
$this->serializer->serialize(new Counter($total), 'json', ['groups' => 'read']), $this->serializer->serialize(new Counter($total), 'json', ['groups' => ['read']]),
JsonResponse::HTTP_OK, JsonResponse::HTTP_OK,
[], [],
true true
@ -117,7 +117,7 @@ class AccompanyingPeriodWorkEvaluationApiController
$collection = new Collection($works, $paginator); $collection = new Collection($works, $paginator);
return new JsonResponse( return new JsonResponse(
$this->serializer->serialize($collection, 'json', ['groups' => 'read']), $this->serializer->serialize($collection, 'json', ['groups' => ['read', 'read:evaluation:include-work']]),
JsonResponse::HTTP_OK, JsonResponse::HTTP_OK,
[], [],
true true

View File

@ -44,7 +44,8 @@ class AccompanyingPeriodWork implements AccompanyingPeriodLinkedWithSocialIssues
{ {
/** /**
* @ORM\ManyToOne(targetEntity=AccompanyingPeriod::class) * @ORM\ManyToOne(targetEntity=AccompanyingPeriod::class)
* @Serializer\Groups({"read"}) * @Serializer\Groups({"read", "read:accompanyingPeriodWork:light"})
* @Serializer\Context(normalizationContext={"groups": {"read"}}, groups={"read:accompanyingPeriodWork:light"})
*/ */
private ?AccompanyingPeriod $accompanyingPeriod = null; private ?AccompanyingPeriod $accompanyingPeriod = null;
@ -63,26 +64,26 @@ class AccompanyingPeriodWork implements AccompanyingPeriodLinkedWithSocialIssues
/** /**
* @ORM\Column(type="datetime_immutable") * @ORM\Column(type="datetime_immutable")
* @Serializer\Groups({"read", "docgen:read"}) * @Serializer\Groups({"read", "docgen:read", "read:accompanyingPeriodWork:light"})
*/ */
private ?DateTimeImmutable $createdAt = null; private ?DateTimeImmutable $createdAt = null;
/** /**
* @ORM\Column(type="boolean") * @ORM\Column(type="boolean")
* @Serializer\Groups({"read", "docgen:read"}) * @Serializer\Groups({"read", "docgen:read", "read:accompanyingPeriodWork:light"})
*/ */
private bool $createdAutomatically = false; private bool $createdAutomatically = false;
/** /**
* @ORM\Column(type="text") * @ORM\Column(type="text")
* @Serializer\Groups({"read", "docgen:read"}) * @Serializer\Groups({"read", "docgen:read", "read:accompanyingPeriodWork:light"})
*/ */
private string $createdAutomaticallyReason = ''; private string $createdAutomaticallyReason = '';
/** /**
* @ORM\ManyToOne(targetEntity=User::class) * @ORM\ManyToOne(targetEntity=User::class)
* @ORM\JoinColumn(nullable=false) * @ORM\JoinColumn(nullable=false)
* @Serializer\Groups({"read", "docgen:read"}) * @Serializer\Groups({"read", "docgen:read", "read:accompanyingPeriodWork:light"})
*/ */
private ?User $createdBy = null; private ?User $createdBy = null;
@ -90,7 +91,7 @@ class AccompanyingPeriodWork implements AccompanyingPeriodLinkedWithSocialIssues
* @ORM\Column(type="date_immutable", nullable=true, options={"default": null}) * @ORM\Column(type="date_immutable", nullable=true, options={"default": null})
* @Serializer\Groups({"accompanying_period_work:create"}) * @Serializer\Groups({"accompanying_period_work:create"})
* @Serializer\Groups({"accompanying_period_work:edit"}) * @Serializer\Groups({"accompanying_period_work:edit"})
* @Serializer\Groups({"read", "docgen:read"}) * @Serializer\Groups({"read", "docgen:read", "read:accompanyingPeriodWork:light"})
* @Assert\GreaterThan(propertyPath="startDate", * @Assert\GreaterThan(propertyPath="startDate",
* message="accompanying_course_work.The endDate should be greater than the start date" * message="accompanying_course_work.The endDate should be greater than the start date"
* ) * )
@ -122,7 +123,7 @@ class AccompanyingPeriodWork implements AccompanyingPeriodLinkedWithSocialIssues
* @ORM\Id * @ORM\Id
* @ORM\GeneratedValue * @ORM\GeneratedValue
* @ORM\Column(type="integer") * @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; private ?int $id = null;
@ -135,7 +136,7 @@ class AccompanyingPeriodWork implements AccompanyingPeriodLinkedWithSocialIssues
/** /**
* @ORM\ManyToMany(targetEntity=Person::class) * @ORM\ManyToMany(targetEntity=Person::class)
* @ORM\JoinTable(name="chill_person_accompanying_period_work_person") * @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:edit"})
* @Serializer\Groups({"accompanying_period_work:create"}) * @Serializer\Groups({"accompanying_period_work:create"})
*/ */
@ -151,8 +152,9 @@ class AccompanyingPeriodWork implements AccompanyingPeriodLinkedWithSocialIssues
/** /**
* @ORM\ManyToOne(targetEntity=SocialAction::class) * @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\Groups({"accompanying_period_work:create"})
* @Serializer\Context(normalizationContext={"groups": {"read"}}, groups={"read:accompanyingPeriodWork:light"})
*/ */
private ?SocialAction $socialAction = null; private ?SocialAction $socialAction = null;
@ -160,7 +162,7 @@ class AccompanyingPeriodWork implements AccompanyingPeriodLinkedWithSocialIssues
* @ORM\Column(type="date_immutable") * @ORM\Column(type="date_immutable")
* @Serializer\Groups({"accompanying_period_work:create"}) * @Serializer\Groups({"accompanying_period_work:create"})
* @Serializer\Groups({"accompanying_period_work:edit"}) * @Serializer\Groups({"accompanying_period_work:edit"})
* @Serializer\Groups({"read", "docgen:read"}) * @Serializer\Groups({"read", "docgen:read", "read:accompanyingPeriodWork:light"})
*/ */
private ?DateTimeImmutable $startDate = null; private ?DateTimeImmutable $startDate = null;

View File

@ -39,6 +39,8 @@ class AccompanyingPeriodWorkEvaluation implements TrackCreationInterface, TrackU
* targetEntity=AccompanyingPeriodWork::class, * targetEntity=AccompanyingPeriodWork::class,
* inversedBy="accompanyingPeriodWorkEvaluations" * inversedBy="accompanyingPeriodWorkEvaluations"
* ) * )
* @Serializer\Groups({"read:evaluation:include-work"})
* @Serializer\Context(normalizationContext={"groups": {"read:accompanyingPeriodWork:light"}}, groups={"read:evaluation:include-work"})
*/ */
private ?AccompanyingPeriodWork $accompanyingPeriodWork = null; private ?AccompanyingPeriodWork $accompanyingPeriodWork = null;

View File

@ -28,6 +28,11 @@ final class AccompanyingPeriodRepository implements ObjectRepository
$this->repository = $entityManager->getRepository(AccompanyingPeriod::class); $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 public function countByRecentUserHistory(User $user, DateTimeImmutable $since): int
{ {
$qb = $this->buildQueryByRecentUserHistory($user, $since); $qb = $this->buildQueryByRecentUserHistory($user, $since);
@ -35,11 +40,6 @@ final class AccompanyingPeriodRepository implements ObjectRepository
return $qb->select('count(a)')->getQuery()->getSingleScalarResult(); 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 public function createQueryBuilder(string $alias, ?string $indexBy = null): QueryBuilder
{ {
return $this->repository->createQueryBuilder($alias, $indexBy); return $this->repository->createQueryBuilder($alias, $indexBy);

View File

@ -251,6 +251,7 @@
relatedEntityClass="Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork" relatedEntityClass="Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork"
:relatedEntityId="this.work.id" :relatedEntityId="this.work.id"
:workflowsAvailables="this.work.workflows_availables" :workflowsAvailables="this.work.workflows_availables"
@go-to-generate-workflow="goToGenerateWorkflow"
></list-workflow-modal> ></list-workflow-modal>
</li> </li>
@ -284,6 +285,7 @@ import OnTheFly from 'ChillMainAssets/vuejs/OnTheFly/components/OnTheFly.vue';
import ListWorkflowModal from 'ChillMainAssets/vuejs/_components/EntityWorkflow/ListWorkflowModal.vue'; import ListWorkflowModal from 'ChillMainAssets/vuejs/_components/EntityWorkflow/ListWorkflowModal.vue';
import PickWorkflow from 'ChillMainAssets/vuejs/_components/EntityWorkflow/PickWorkflow.vue'; import PickWorkflow from 'ChillMainAssets/vuejs/_components/EntityWorkflow/PickWorkflow.vue';
import PersonText from 'ChillPersonAssets/vuejs/_components/Entity/PersonText.vue'; import PersonText from 'ChillPersonAssets/vuejs/_components/Entity/PersonText.vue';
import {buildLinkCreate} from 'ChillMainAssets/lib/entity-workflow/api.js';
const i18n = { const i18n = {
messages: { messages: {
@ -334,7 +336,6 @@ export default {
ListWorkflowModal, ListWorkflowModal,
OnTheFly, OnTheFly,
PickWorkflow, PickWorkflow,
OnTheFly,
PersonText, PersonText,
}, },
i18n, i18n,
@ -461,6 +462,15 @@ export default {
removeThirdParty(t) { removeThirdParty(t) {
this.$store.commit('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() { submit() {
this.$store.dispatch('submit'); this.$store.dispatch('submit');
}, },

View File

@ -14,10 +14,10 @@
<list-workflow-modal <list-workflow-modal
:workflows="evaluation.workflows" :workflows="evaluation.workflows"
:allowCreate="true" :allowCreate="true"
relatedEntityClass="faked" relatedEntityClass="Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation"
:relatedEntityId="evaluation.id" :relatedEntityId="evaluation.id"
:workflowsAvailables="evaluation.workflows_availables" :workflowsAvailables="evaluation.workflows_availables"
@goToGenerateWorkflow="goToGenerateWorkflow" @go-to-generate-workflow="goToGenerateWorkflow"
></list-workflow-modal> ></list-workflow-modal>
</li> </li>
@ -106,8 +106,7 @@ export default {
this.toggleEditEvaluation(); this.toggleEditEvaluation();
}, },
goToGenerateWorkflow({event, link, workflowName}) { goToGenerateWorkflow({event, link, workflowName}) {
event.preventDefault(); console.log('goToGenerate in evaluation', event, link, workflowName);
console.log(event, link, workflowName);
const callback = (data) => { const callback = (data) => {
let evaluationId = data.accompanyingPeriodWorkEvaluations.find(e => e.key === this.evaluation.key).id; let evaluationId = data.accompanyingPeriodWorkEvaluations.find(e => e.key === this.evaluation.key).id;

View File

@ -92,7 +92,8 @@
entityClass="Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation" entityClass="Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation"
:id="evaluation.id" :id="evaluation.id"
:templates="getTemplatesAvailables" :templates="getTemplatesAvailables"
:beforeMove="submitBeforeGenerate" :preventDefaultMoveToGenerate="true"
@go-to-generate-document="submitBeforeGenerate"
> >
<template v-slot:title> <template v-slot:title>
<label class="col-sm-4 col-form-label">{{ $t('evaluation_generate_a_document') }}</label> <label class="col-sm-4 col-form-label">{{ $t('evaluation_generate_a_document') }}</label>
@ -109,6 +110,7 @@ import CKEditor from '@ckeditor/ckeditor5-vue';
import ClassicEditor from 'ChillMainAssets/module/ckeditor5/index.js'; import ClassicEditor from 'ChillMainAssets/module/ckeditor5/index.js';
import { mapGetters, mapState } from 'vuex'; import { mapGetters, mapState } from 'vuex';
import PickTemplate from 'ChillDocGeneratorAssets/vuejs/_components/PickTemplate.vue'; import PickTemplate from 'ChillDocGeneratorAssets/vuejs/_components/PickTemplate.vue';
import {buildLink} from 'ChillDocGeneratorAssets/lib/document-generator';
const i18n = { const i18n = {
messages: { messages: {
@ -123,7 +125,7 @@ const i18n = {
evaluation_public_comment: "Note publique", evaluation_public_comment: "Note publique",
evaluation_comment_placeholder: "Commencez à écrire ...", evaluation_comment_placeholder: "Commencez à écrire ...",
evaluation_generate_a_document: "Générer un document", evaluation_generate_a_document: "Générer un document",
evaluation_choose_a_template: "Choisir un gabarit", evaluation_choose_a_template: "Choisir un modèle",
evaluation_add_a_document: "Ajouter un document", evaluation_add_a_document: "Ajouter un document",
evaluation_add: "Ajouter une évaluation", evaluation_add: "Ajouter une évaluation",
Documents: "Documents", Documents: "Documents",
@ -207,10 +209,11 @@ export default {
return `/wopi/edit/${storedObject.uuid}?returnPath=` + encodeURIComponent( return `/wopi/edit/${storedObject.uuid}?returnPath=` + encodeURIComponent(
window.location.pathname + window.location.search + window.location.hash); window.location.pathname + window.location.search + window.location.hash);
}, },
submitBeforeGenerate() { submitBeforeGenerate({template}) {
const callback = (data) => { const callback = (data) => {
let evaluationId = data.accompanyingPeriodWorkEvaluations.find(e => e.key === this.evaluation.key).id; let evaluationId = data.accompanyingPeriodWorkEvaluations.find(e => e.key === this.evaluation.key).id;
return Promise.resolve({entityId: evaluationId});
window.location.assign(buildLink(template, evaluationId, 'Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWorkEvaluation'));
}; };
return this.$store.dispatch('submit', callback).catch(e => { console.log(e); throw e; }); return this.$store.dispatch('submit', callback).catch(e => { console.log(e); throw e; });

View File

@ -210,6 +210,7 @@ const store = createStore({
editEvaluation: true, editEvaluation: true,
workflows_availables: state.work.workflows_availables_evaluation, workflows_availables: state.work.workflows_availables_evaluation,
documents: [], documents: [],
workflows: [],
}; };
state.evaluationsPicked.push(e); state.evaluationsPicked.push(e);
}, },

View File

@ -241,13 +241,12 @@ export default {
return item.result.type + item.result.id; return item.result.type + item.result.id;
}, },
addPriorSuggestion() { addPriorSuggestion() {
//console.log('addPriorSuggestion', this.hasPriorSuggestion); // console.log('prior suggestion', this.priorSuggestion);
if (this.hasPriorSuggestion) { if (this.hasPriorSuggestion) {
console.log('addPriorSuggestion',); // console.log('addPriorSuggestion',);
this.suggested.unshift(this.priorSuggestion); this.suggested.unshift(this.priorSuggestion);
this.selected.unshift(this.priorSuggestion); this.selected.unshift(this.priorSuggestion);
console.log('reset priorSuggestion');
this.newPriorSuggestion(null); this.newPriorSuggestion(null);
} }
}, },
@ -260,13 +259,14 @@ export default {
result: entity result: entity
} }
this.search.priorSuggestion = suggestion; this.search.priorSuggestion = suggestion;
console.log('search priorSuggestion', this.search.priorSuggestion); // console.log('search priorSuggestion', this.search.priorSuggestion);
this.addPriorSuggestion(suggestion);
} else { } else {
this.search.priorSuggestion = {}; this.search.priorSuggestion = {};
} }
}, },
saveFormOnTheFly({ type, data }) { saveFormOnTheFly({ type, data }) {
console.log('saveFormOnTheFly from addPersons, type', type, ', data', data); // console.log('saveFormOnTheFly from addPersons, type', type, ', data', data);
if (type === 'person') { if (type === 'person') {
makeFetch('POST', '/api/1.0/person/person.json', data) makeFetch('POST', '/api/1.0/person/person.json', data)
.then(response => { .then(response => {
@ -299,6 +299,7 @@ export default {
} }
}) })
} }
this.canCloseOnTheFlyModal = false;
} }
}, },
} }

View File

@ -85,9 +85,11 @@
</div> </div>
<div id="maritalStatusDate"> <div id="maritalStatusDate">
{{ form_row(form.maritalStatusDate, { 'label' : 'Date of last marital status change'} ) }} {{ form_row(form.maritalStatusDate, { 'label' : 'Date of last marital status change'} ) }}
{{ form_row(form.maritalStatusComment, { 'label' : 'Comment on the marital status'} ) }}
</div> </div>
{%- endif -%} {%- endif -%}
<div id="maritalStatusComment">
{{ form_row(form.maritalStatusComment, { 'label' : 'Comment on the marital status'} ) }}
</div>
</fieldset> </fieldset>
{%- endif -%} {%- endif -%}

View File

@ -99,11 +99,12 @@
{% endif %} {% endif %}
</div> </div>
<div class="ms-auto"> <div class="ms-auto">
{% if acp.requestorPerson == person %} {% if acp.requestoranonymous == false and acp.requestorPerson == person %}
<span class="as-requestor badge bg-info" title="{{ 'Requestor'|trans|e('html_attr') }}"> <span class="as-requestor badge bg-info" title="{{ 'Requestor'|trans|e('html_attr') }}">
{{ 'Requestor'|trans({'gender': person.gender}) }} {{ 'Requestor'|trans({'gender': person.gender}) }}
</span> </span>
{% endif %} {% endif %}
{% if acp.emergency %} {% if acp.emergency %}
<span class="badge rounded-pill bg-danger">{{- 'Emergency'|trans|upper -}}</span> <span class="badge rounded-pill bg-danger">{{- 'Emergency'|trans|upper -}}</span>
{% endif %} {% endif %}
@ -186,7 +187,7 @@
</a> </a>
</li> </li>
</ul> </ul>
{% if acp.requestoranonymous == false %}
{% if (acp.requestorPerson is not null and acp.requestorPerson.id != person.id) or acp.requestorThirdParty is not null %} {% if (acp.requestorPerson is not null and acp.requestorPerson.id != person.id) or acp.requestorThirdParty is not null %}
<div class="wl-row"> <div class="wl-row">
<div class="wl-col title"> <div class="wl-col title">
@ -218,7 +219,7 @@
</div> </div>
</div> </div>
{% endif %} {% endif %}
{% endif %}
</div> </div>
</div> </div>
{% endfor %} {% endfor %}

View File

@ -44,6 +44,7 @@ class AccompanyingPeriodWorkEvaluationNormalizer implements ContextAwareNormaliz
*/ */
public function normalize($object, ?string $format = null, array $context = []): array public function normalize($object, ?string $format = null, array $context = []): array
{ {
dump($context);
$initial = $this->normalizer->normalize($object, $format, array_merge( $initial = $this->normalizer->normalize($object, $format, array_merge(
$context, $context,
[self::IGNORE_EVALUATION => spl_object_hash($object)] [self::IGNORE_EVALUATION => spl_object_hash($object)]

View File

@ -109,7 +109,6 @@ final class AccompanyingPeriodConfidentialTest extends WebTestCase
$user = new stdClass(); $user = new stdClass();
$user->id = 0; $user->id = 0;
$user->type = 'user'; $user->type = 'user';
dump($user);
$this->client->request( $this->client->request(
Request::METHOD_PATCH, Request::METHOD_PATCH,

View File

@ -9,8 +9,9 @@
declare(strict_types=1); declare(strict_types=1);
namespace Chill\ReportBundle\Tests\Controller; namespace Chill\ReportBundle\Tests\DependencyInjection;
use Exception;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
/** /**

View File

@ -9,7 +9,7 @@
declare(strict_types=1); declare(strict_types=1);
namespace Chill\ThirdPartyBundle\Test\Serializer\Normalizer; namespace Chill\ThirdPartyBundle\Tests\Serializer\Normalizer;
use Chill\MainBundle\Entity\Civility; use Chill\MainBundle\Entity\Civility;
use Chill\ThirdPartyBundle\Entity\ThirdParty; use Chill\ThirdPartyBundle\Entity\ThirdParty;

View File

@ -9,7 +9,7 @@
declare(strict_types=1); declare(strict_types=1);
namespace Chill\ThirdPartyBundle\Test\Serializer\Normalizer; namespace Chill\ThirdPartyBundle\Tests\Serializer\Normalizer;
use Chill\ThirdPartyBundle\Entity\ThirdParty; use Chill\ThirdPartyBundle\Entity\ThirdParty;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;