Merge branch 'issue464_document_on_person' into 'master'

Person Document generation

See merge request Chill-Projet/chill-bundles!360
This commit is contained in:
Julien Fastré 2022-03-03 14:27:53 +00:00
commit db4b0b104b
11 changed files with 253 additions and 12 deletions

View File

@ -11,8 +11,8 @@ and this project adheres to
## Unreleased ## Unreleased
<!-- write down unreleased development here --> <!-- write down unreleased development here -->
* [person] Add document generation in admin and in person/{id}/document (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/464)
* [activity] do not override location if already exist (when validating new activity) (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/470) * [activity] do not override location if already exist (when validating new activity) (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/470)
* [parcours] Toggle emergency/intensity only by referrer (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/442) * [parcours] Toggle emergency/intensity only by referrer (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/442)
* [docstore] Add an API entrypoint for StoredObject (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/466) * [docstore] Add an API entrypoint for StoredObject (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/466)
* [person] Add the possibility of uploading existing documents to AccPeriodWorkEvaluationDocument (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/466) * [person] Add the possibility of uploading existing documents to AccPeriodWorkEvaluationDocument (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/466)
@ -21,7 +21,7 @@ and this project adheres to
* [Person/Household list] when listing other simultaneous members of an household, exclude the members on person, not on members (avoid to show two membersship with the same person) * [Person/Household list] when listing other simultaneous members of an household, exclude the members on person, not on members (avoid to show two membersship with the same person)
* [draft periods] add a delete button (if acl granted) on each draft period listed on draft period page (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/463) * [draft periods] add a delete button (if acl granted) on each draft period listed on draft period page (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/463)
* [Person] Display suffixText in RenderPerson, PersonText.vue, RenderPersonBox.vue (was made for displaying "enfant confie") (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/441) * [Person] Display suffixText in RenderPerson, PersonText.vue, RenderPersonBox.vue (was made for displaying "enfant confie") (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/441)
* [person] residential address: show residential address or info in PersonRenderBox, refactor Residential Address (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/439) * [person] residential address: show residential address or info in PersonRenderBox, refactor Residential Address (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/439)
* [thirdparty] Add a contact to a thirdparty from within onTheFly (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/345) * [thirdparty] Add a contact to a thirdparty from within onTheFly (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/345)
* [documents] Improve flex-table item-col placement when long buttons and long metadata * [documents] Improve flex-table item-col placement when long buttons and long metadata
* [thirdparty] Fix display of multiple contact badges so they wrap onto next line (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/482) * [thirdparty] Fix display of multiple contact badges so they wrap onto next line (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/482)

View File

@ -207,21 +207,21 @@ final class DocGeneratorTemplateController extends AbstractController
$context instanceof DocGeneratorContextWithPublicFormInterface $context instanceof DocGeneratorContextWithPublicFormInterface
&& $context->hasPublicForm($template, $entity) || $isTest && $context->hasPublicForm($template, $entity) || $isTest
) { ) {
if ($context instanceof DocGeneratorContextWithPublicFormInterface) { if ($context instanceof DocGeneratorContextWithPublicFormInterface && $context->hasPublicForm($template, $entity)) {
$builder = $this->createFormBuilder( $builder = $this->createFormBuilder(
array_merge( array_merge(
$context->getFormData($template, $entity), $context->getFormData($template, $entity),
$isTest ? ['test_file' => null] : [] $isTest ? ['test_file' => null] : []
) )
); );
$context->buildPublicForm($builder, $template, $entity);
} else { } else {
$builder = $this->createFormBuilder( $builder = $this->createFormBuilder(
['test_file' => null] ['test_file' => null]
); );
} }
$context->buildPublicForm($builder, $template, $entity);
if ($isTest) { if ($isTest) {
$builder->add('test_file', FileType::class, [ $builder->add('test_file', FileType::class, [
'label' => 'Template file', 'label' => 'Template file',

View File

@ -26,7 +26,17 @@
{% endblock %} {% endblock %}
{% block js %} {% block js %}
{{ parent() }}
{{ encore_entry_script_tags('mod_async_upload') }} {{ encore_entry_script_tags('mod_async_upload') }}
{{ encore_entry_script_tags('mod_docgen_picktemplate') }}
{{ encore_entry_script_tags('mod_entity_workflow_pick') }}
{% endblock %}
{% block css %}
{{ parent() }}
{{ encore_entry_link_tags('mod_async_upload') }}
{{ encore_entry_link_tags('mod_docgen_picktemplate') }}
{{ encore_entry_link_tags('mod_entity_workflow_pick') }}
{% endblock %} {% endblock %}
{% block personcontent %} {% block personcontent %}
@ -46,6 +56,8 @@
{{ chill_pagination(pagination) }} {{ chill_pagination(pagination) }}
<div data-docgen-template-picker="data-docgen-template-picker" data-entity-class="Chill\PersonBundle\Entity\Person" data-entity-id="{{ person.id }}"></div>
{% if is_granted('CHILL_PERSON_DOCUMENT_CREATE', person) %} {% if is_granted('CHILL_PERSON_DOCUMENT_CREATE', person) %}
<ul class="record_actions sticky-form-buttons"> <ul class="record_actions sticky-form-buttons">
<li class="create"> <li class="create">

View File

@ -13,7 +13,7 @@ namespace Chill\PersonBundle\Controller;
use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Entity\Person\ResidentialAddress; use Chill\PersonBundle\Entity\Person\ResidentialAddress;
use Chill\PersonBundle\Form\Type\ResidentialAddressType; use Chill\PersonBundle\Form\ResidentialAddressType;
use Chill\PersonBundle\Repository\ResidentialAddressRepository; use Chill\PersonBundle\Repository\ResidentialAddressRepository;
use Chill\PersonBundle\Security\Authorization\PersonVoter; use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;

View File

@ -9,11 +9,12 @@
declare(strict_types=1); declare(strict_types=1);
namespace Chill\PersonBundle\Form\Type; namespace Chill\PersonBundle\Form;
use Chill\MainBundle\Form\Type\CommentType; use Chill\MainBundle\Form\Type\CommentType;
use Chill\MainBundle\Form\Type\PickAddressType; use Chill\MainBundle\Form\Type\PickAddressType;
use Chill\PersonBundle\Entity\Person\ResidentialAddress; use Chill\PersonBundle\Entity\Person\ResidentialAddress;
use Chill\PersonBundle\Form\Type\PickPersonDynamicType;
use Chill\ThirdPartyBundle\Form\Type\PickThirdpartyDynamicType; use Chill\ThirdPartyBundle\Form\Type\PickThirdpartyDynamicType;
use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\DateType; use Symfony\Component\Form\Extension\Core\Type\DateType;

View File

@ -74,6 +74,7 @@ class PersonDocGenNormalizer implements
$data = [ $data = [
'type' => 'person', 'type' => 'person',
'id' => $person->getId(),
'isNull' => false, 'isNull' => false,
'civility' => $this->normalizer->normalize($person->getCivility(), $format, array_merge($context, ['docgen:expects' => Civility::class])), 'civility' => $this->normalizer->normalize($person->getCivility(), $format, array_merge($context, ['docgen:expects' => Civility::class])),
'firstName' => $person->getFirstName(), 'firstName' => $person->getFirstName(),
@ -151,7 +152,7 @@ class PersonDocGenNormalizer implements
$normalizer = new NormalizeNullValueHelper($this->normalizer, 'type', 'person'); $normalizer = new NormalizeNullValueHelper($this->normalizer, 'type', 'person');
$attributes = [ $attributes = [
'firstName', 'lastName', 'age', 'altNames', 'text', 'id', 'firstName', 'lastName', 'age', 'altNames', 'text',
'civility' => Civility::class, 'civility' => Civility::class,
'birthdate' => DateTimeInterface::class, 'birthdate' => DateTimeInterface::class,
'deathdate' => DateTimeInterface::class, 'deathdate' => DateTimeInterface::class,

View File

@ -33,6 +33,7 @@ use Symfony\Component\Serializer\Normalizer\ObjectToPopulateTrait;
use function array_key_exists; use function array_key_exists;
use function count; use function count;
use function in_array; use function in_array;
use function is_string;
/** /**
* Serialize a Person entity. * Serialize a Person entity.
@ -56,11 +57,11 @@ class PersonJsonNormalizer implements DenormalizerAwareInterface, NormalizerAwar
private ResidentialAddressRepository $residentialAddressRepository; private ResidentialAddressRepository $residentialAddressRepository;
public function __construct( public function __construct(
ChillEntityRenderExtension $render, ChillEntityRenderExtension $render, /* TODO: replace by PersonRenderInterface, as sthis is the only one required */
PersonRepository $repository, PersonRepository $repository,
CenterResolverManagerInterface $centerResolverManager, CenterResolverManagerInterface $centerResolverManager,
ResidentialAddressRepository $residentialAddressRepository, ResidentialAddressRepository $residentialAddressRepository,
PhoneNumberHelperInterface $phoneNumberHelper PhoneNumberHelperInterface $phoneNumberHelper /* TODO maybe not necessayr any more */
) { ) {
$this->render = $render; $this->render = $render;
$this->repository = $repository; $this->repository = $repository;
@ -189,6 +190,7 @@ class PersonJsonNormalizer implements DenormalizerAwareInterface, NormalizerAwar
public function normalize($person, $format = null, array $context = []) public function normalize($person, $format = null, array $context = [])
{ {
$groups = $context[AbstractNormalizer::GROUPS] ?? []; $groups = $context[AbstractNormalizer::GROUPS] ?? [];
if (is_string($groups)) { if (is_string($groups)) {
$groups = [$groups]; $groups = [$groups];
} }

View File

@ -0,0 +1,179 @@
<?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\PersonBundle\Service\DocGenerator;
use Chill\DocGeneratorBundle\Context\DocGeneratorContextWithAdminFormInterface;
use Chill\DocGeneratorBundle\Context\Exception\UnexpectedTypeException;
use Chill\DocGeneratorBundle\Entity\DocGeneratorTemplate;
use Chill\DocGeneratorBundle\Service\Context\BaseContextData;
use Chill\DocStoreBundle\Entity\PersonDocument;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\DocStoreBundle\Repository\DocumentCategoryRepository;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Entity\Person;
use DateTime;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use function array_key_exists;
class PersonContext implements DocGeneratorContextWithAdminFormInterface
{
private BaseContextData $baseContextData;
private DocumentCategoryRepository $documentCategoryRepository;
private EntityManagerInterface $em;
private NormalizerInterface $normalizer;
private TranslatableStringHelperInterface $translatableStringHelper;
private TranslatorInterface $translator;
public function __construct(
DocumentCategoryRepository $documentCategoryRepository,
NormalizerInterface $normalizer,
TranslatableStringHelperInterface $translatableStringHelper,
EntityManagerInterface $em,
TranslatorInterface $translator,
BaseContextData $baseContextData
) {
$this->documentCategoryRepository = $documentCategoryRepository;
$this->normalizer = $normalizer;
$this->translatableStringHelper = $translatableStringHelper;
$this->em = $em;
$this->baseContextData = $baseContextData;
$this->translator = $translator;
}
public function adminFormReverseTransform(array $data): array
{
if (array_key_exists('category', $data)) {
$data['category'] = [
'idInsideBundle' => $data['category']->getIdInsideBundle(),
'bundleId' => $data['category']->getBundleId(),
];
}
return $data;
}
public function adminFormTransform(array $data): array
{
$r = [
'mainPerson' => $data['mainPerson'] ?? false,
'mainPersonLabel' => $data['mainPersonLabel'] ?? $this->translator->trans('docgen.Main person'),
];
if (array_key_exists('category', $data)) {
$r['category'] = array_key_exists('category', $data) ?
$this->documentCategoryRepository->find($data['category']) : null;
}
return $r;
}
public function buildAdminForm(FormBuilderInterface $builder): void
{
$builder
->add('category', EntityType::class, [
'placeholder' => 'Choose a document category',
'class' => 'ChillDocStoreBundle:DocumentCategory',
'query_builder' => static function (EntityRepository $er) {
return $er->createQueryBuilder('c')
->where('c.documentClass = :docClass')
->setParameter('docClass', PersonDocument::class);
},
'choice_label' => function ($entity = null) {
return $entity ? $this->translatableStringHelper->localize($entity->getName()) : '';
},
]);
}
public function getData(DocGeneratorTemplate $template, $entity, array $contextGenerationData = []): array
{
if (!$entity instanceof Person) {
throw new UnexpectedTypeException($entity, Person::class);
}
$data = [];
$data = array_merge($data, $this->baseContextData->getData());
$data['person'] = $this->normalizer->normalize($entity, 'docgen', [
'docgen:expects' => Person::class,
'groups' => ['docgen:read', 'docgen:person:with-household', 'docgen:person:with-relations'],
]);
return $data;
}
public function getDescription(): string
{
return 'docgen.A basic context for person';
}
public function getEntityClass(): string
{
return Person::class;
}
public function getFormData(DocGeneratorTemplate $template, $entity): array
{
return [
'person' => $entity,
];
}
public static function getKey(): string
{
return self::class;
}
public function getName(): string
{
return 'docgen.Person basic';
}
public function hasAdminForm(): bool
{
return true;
}
/**
* @param Person $entity
*/
public function storeGenerated(DocGeneratorTemplate $template, StoredObject $storedObject, object $entity, array $contextGenerationData): void
{
$doc = new PersonDocument();
$doc->setTemplate($template)
->setTitle($this->translatableStringHelper->localize($template->getName()))
->setDate(new DateTime())
->setDescription($this->translatableStringHelper->localize($template->getName()))
->setPerson($entity)
->setObject($storedObject);
if (array_key_exists('category', $template->getOptions())) {
$doc
->setCategory(
$this->documentCategoryRepository->find(
$template->getOptions()['category']
)
);
}
$this->em->persist($doc);
}
}

View File

@ -38,6 +38,7 @@ final class PersonDocGenNormalizerTest extends KernelTestCase
use ProphecyTrait; use ProphecyTrait;
private const BLANK = [ private const BLANK = [
'id' => '',
'firstName' => '', 'firstName' => '',
'lastName' => '', 'lastName' => '',
'altNames' => '', 'altNames' => '',

View File

@ -11,7 +11,15 @@ declare(strict_types=1);
namespace Serializer\Normalizer; namespace Serializer\Normalizer;
use Chill\MainBundle\Phonenumber\PhoneNumberHelperInterface;
use Chill\MainBundle\Security\Resolver\CenterResolverManagerInterface;
use Chill\MainBundle\Templating\Entity\ChillEntityRenderExtension;
use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Repository\PersonRepository;
use Chill\PersonBundle\Repository\ResidentialAddressRepository;
use Chill\PersonBundle\Serializer\Normalizer\PersonJsonNormalizer;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer; use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface; use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
@ -22,12 +30,27 @@ use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
*/ */
final class PersonJsonNormalizerTest extends KernelTestCase final class PersonJsonNormalizerTest extends KernelTestCase
{ {
private NormalizerInterface $normalizer; use ProphecyTrait;
private PersonJsonNormalizer $normalizer;
protected function setUp(): void protected function setUp(): void
{ {
self::bootKernel(); self::bootKernel();
$this->normalizer = self::$container->get(NormalizerInterface::class);
$residentialAddressRepository = $this->prophesize(ResidentialAddressRepository::class);
$residentialAddressRepository
->findCurrentResidentialAddressByPerson(Argument::type(Person::class), Argument::any())
->willReturn([]);
$this->normalizer = $this->buildPersonJsonNormalizer(
self::$container->get(ChillEntityRenderExtension::class),
self::$container->get(PersonRepository::class),
self::$container->get(CenterResolverManagerInterface::class),
$residentialAddressRepository->reveal(),
self::$container->get(PhoneNumberHelperInterface::class),
self::$container->get(NormalizerInterface::class)
);
} }
public function testNormalization() public function testNormalization()
@ -37,4 +60,24 @@ final class PersonJsonNormalizerTest extends KernelTestCase
$this->assertIsArray($result); $this->assertIsArray($result);
} }
private function buildPersonJsonNormalizer(
ChillEntityRenderExtension $render,
PersonRepository $repository,
CenterResolverManagerInterface $centerResolverManager,
ResidentialAddressRepository $residentialAddressRepository,
PhoneNumberHelperInterface $phoneNumberHelper,
NormalizerInterface $normalizer
): PersonJsonNormalizer {
$personJsonNormalizer = new PersonJsonNormalizer(
$render,
$repository,
$centerResolverManager,
$residentialAddressRepository,
$phoneNumberHelper
);
$personJsonNormalizer->setNormalizer($normalizer);
return $personJsonNormalizer;
}
} }

View File

@ -541,6 +541,8 @@ docgen:
A basic context for accompanying period: Contexte pour les parcours A basic context for accompanying period: Contexte pour les parcours
A context for accompanying period work: Contexte pour les actions d'accompagnement A context for accompanying period work: Contexte pour les actions d'accompagnement
A context for accompanying period work evaluation: Contexte pour les évaluations dans les actions d'accompagnement A context for accompanying period work evaluation: Contexte pour les évaluations dans les actions d'accompagnement
Person basic: Personne (basique)
A basic context for person: Contexte pour les personnes
period_notification: period_notification:
period_designated_subject: Vous êtes référent d'un parcours d'accompagnement period_designated_subject: Vous êtes référent d'un parcours d'accompagnement