Merge remote-tracking branch 'origin/master' into calendar/finalization

This commit is contained in:
2022-06-17 16:53:56 +02:00
35 changed files with 852 additions and 170 deletions

View File

@@ -0,0 +1,167 @@
<?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\Controller;
use Chill\MainBundle\Entity\Location;
use Chill\MainBundle\Entity\Scope;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Entity\UserJob;
use Chill\MainBundle\Pagination\PaginatorFactory;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Repository\AccompanyingPeriodACLAwareRepositoryInterface;
use Doctrine\ORM\EntityRepository;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Templating\EngineInterface;
class AccompanyingPeriodRegulationListController
{
private AccompanyingPeriodACLAwareRepositoryInterface $accompanyingPeriodACLAwareRepository;
private EngineInterface $engine;
private FormFactoryInterface $formFactory;
private PaginatorFactory $paginatorFactory;
private Security $security;
private TranslatableStringHelperInterface $translatableStringHelper;
public function __construct(AccompanyingPeriodACLAwareRepositoryInterface $accompanyingPeriodACLAwareRepository, EngineInterface $engine, FormFactoryInterface $formFactory, PaginatorFactory $paginatorFactory, Security $security, TranslatableStringHelperInterface $translatableStringHelper)
{
$this->accompanyingPeriodACLAwareRepository = $accompanyingPeriodACLAwareRepository;
$this->engine = $engine;
$this->formFactory = $formFactory;
$this->paginatorFactory = $paginatorFactory;
$this->security = $security;
$this->translatableStringHelper = $translatableStringHelper;
}
/**
* @Route("/{_locale}/person/periods/undispatched", name="chill_person_course_list_regulation")
*/
public function listRegul(Request $request): Response
{
if (!$this->security->isGranted('ROLE_USER') || !$this->security->getUser() instanceof User) {
throw new AccessDeniedHttpException();
}
$form = $this->buildFilterForm();
$form->handleRequest($request);
$total = $this->accompanyingPeriodACLAwareRepository->countByUnDispatched(
$form['jobs']->getData(),
$form['services']->getData(),
$form['locations']->getData(),
);
$paginator = $this->paginatorFactory->create($total);
$periods = $this->accompanyingPeriodACLAwareRepository
->findByUnDispatched(
$form['jobs']->getData(),
$form['services']->getData(),
$form['locations']->getData(),
$paginator->getItemsPerPage(),
$paginator->getCurrentPageFirstItemNumber()
);
return new Response(
$this->engine->render('@ChillPerson/AccompanyingCourse/dispatch_list.html.twig', [
'paginator' => $paginator,
'periods' => $periods,
'form' => $form->createView(),
])
);
}
private function buildFilterForm(): FormInterface
{
$data = [
'services' => [],
'jobs' => [],
'locations' => [],
];
$builder = $this->formFactory->createBuilder(FormType::class, $data, [
'method' => 'get', 'csrf_protection' => false, ]);
$builder
->add('services', EntityType::class, [
'class' => Scope::class,
'query_builder' => static function (EntityRepository $er) {
return $er->createQueryBuilder('s');
},
'choice_label' => function (Scope $s) {
return $this->translatableStringHelper->localize($s->getName());
},
'multiple' => true,
'label' => 'Service',
'required' => false,
])
->add('jobs', EntityType::class, [
'class' => UserJob::class,
'query_builder' => static function (EntityRepository $er) {
$qb = $er->createQueryBuilder('j');
$qb->andWhere($qb->expr()->eq('j.active', "'TRUE'"));
return $qb;
},
'choice_label' => function (UserJob $j) {
return $this->translatableStringHelper->localize($j->getLabel());
},
'multiple' => true,
'label' => 'Métier',
'required' => false,
])
->add('locations', EntityType::class, [
'class' => Location::class,
'query_builder' => static function (EntityRepository $er) {
$qb = $er->createQueryBuilder('l');
$qb
->join('l.locationType', 't')
->where(
$qb->expr()->andX(
$qb->expr()->eq('t.availableForUsers', "'TRUE'"),
$qb->expr()->eq('t.active', "'TRUE'"),
$qb->expr()->eq('l.active', "'TRUE'"),
$qb->expr()->eq('l.availableForUsers', "'TRUE'")
)
);
return $qb;
},
'choice_label' => static function (Location $l) {
return $l->getName();
},
'multiple' => true,
'group_by' => function (Location $l) {
if (null === $type = $l->getLocationType()) {
return null;
}
return $this->translatableStringHelper->localize($type->getTitle());
},
'label' => 'Localisation administrative',
'required' => false,
]);
return $builder->getForm();
}
}

View File

@@ -692,6 +692,10 @@ class AccompanyingPeriod implements
return [[self::STEP_DRAFT, self::STEP_CONFIRMED]];
}
if ($this->getStep() === self::STEP_CLOSED) {
return [[self::STEP_DRAFT, self::STEP_CONFIRMED, self::STEP_CLOSED]];
}
throw new LogicException('no validation group permitted with this step: ' . $this->getStep());
}

View File

@@ -14,7 +14,6 @@ namespace Chill\PersonBundle\Entity\AccompanyingPeriod;
use DateTimeImmutable;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation as Serializer;
use Symfony\Component\Serializer\Annotation\Groups;
/**
* @ORM\Entity
@@ -31,20 +30,20 @@ class Origin
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
* @Groups({"read", "docgen:read"})
* @Serializer\Groups({"read", "docgen:read"})
*/
private ?int $id = null;
/**
* @ORM\Column(type="json")
* @Groups({"read", "docgen:read"})
* @Serializer\Groups({"read", "docgen:read"})
* @Serializer\Context({"is-translatable": true}, groups={"docgen:read"})
*/
private array $label = [];
/**
* @ORM\Column(type="date_immutable", nullable=true)
* @Groups({"read"})
* @Serializer\Groups({"read"})
*/
private ?DateTimeImmutable $noActiveAfter = null;

View File

@@ -11,7 +11,6 @@ declare(strict_types=1);
namespace Chill\PersonBundle\EventListener;
use Chill\MainBundle\Entity\User;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
use Symfony\Component\Security\Core\Security;
@@ -26,8 +25,10 @@ class AccompanyingPeriodWorkEventListener
public function prePersistAccompanyingPeriodWork(AccompanyingPeriodWork $work): void
{
if ($this->security->getUser() instanceof User) {
$work->addReferrer($this->security->getUser());
$referrer = $work->getAccompanyingPeriod()->getUser();
if (null !== $referrer) {
$work->addReferrer($referrer);
}
}
}

View File

@@ -11,12 +11,13 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Menu;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Knp\Menu\MenuItem;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Symfony\Component\Security\Core\Security;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
@@ -24,20 +25,20 @@ use Symfony\Contracts\Translation\TranslatorInterface;
*/
class SectionMenuBuilder implements LocalMenuBuilderInterface
{
protected AuthorizationCheckerInterface $authorizationChecker;
protected ParameterBagInterface $parameterBag;
protected TranslatorInterface $translator;
private Security $security;
/**
* SectionMenuBuilder constructor.
*/
public function __construct(AuthorizationCheckerInterface $authorizationChecker, TranslatorInterface $translator, ParameterBagInterface $parameterBag)
public function __construct(ParameterBagInterface $parameterBag, Security $security, TranslatorInterface $translator)
{
$this->authorizationChecker = $authorizationChecker;
$this->translator = $translator;
$this->parameterBag = $parameterBag;
$this->security = $security;
$this->translator = $translator;
}
/**
@@ -45,7 +46,7 @@ class SectionMenuBuilder implements LocalMenuBuilderInterface
*/
public function buildMenu($menuId, MenuItem $menu, array $parameters)
{
if ($this->authorizationChecker->isGranted(PersonVoter::CREATE) && $this->parameterBag->get('chill_person.create_person_allowed')) {
if ($this->security->isGranted(PersonVoter::CREATE) && $this->parameterBag->get('chill_person.create_person_allowed')) {
$menu->addChild($this->translator->trans('Add a person'), [
'route' => 'chill_person_new',
])
@@ -65,7 +66,7 @@ class SectionMenuBuilder implements LocalMenuBuilderInterface
]);
}
if ($this->authorizationChecker->isGranted(AccompanyingPeriodVoter::REASSIGN_BULK, null)) {
if ($this->security->isGranted(AccompanyingPeriodVoter::REASSIGN_BULK, null)) {
$menu->addChild($this->translator->trans('reassign.Bulk reassign'), [
'route' => 'chill_course_list_reassign',
])
@@ -74,6 +75,16 @@ class SectionMenuBuilder implements LocalMenuBuilderInterface
'icons' => [],
]);
}
if ($this->security->getUser() instanceof User && $this->security->isGranted('ROLE_USER')) {
$menu
->addChild('Régulation', [
'route' => 'chill_person_course_list_regulation',
])
->setExtras([
'order' => 150,
]);
}
}
public static function getMenuIds(): array

View File

@@ -11,13 +11,19 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Repository;
use Chill\MainBundle\Entity\Location;
use Chill\MainBundle\Entity\Scope;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Entity\UserJob;
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
use Chill\MainBundle\Security\Resolver\CenterResolverDispatcherInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
use DateTime;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Security\Core\Security;
use function count;
@@ -62,6 +68,15 @@ final class AccompanyingPeriodACLAwareRepository implements AccompanyingPeriodAC
return $qb;
}
public function countByUnDispatched(array $jobs, array $services, array $administrativeLocations): int
{
$qb = $this->addACLByUnDispatched($this->buildQueryUnDispatched($jobs, $services, $administrativeLocations));
$qb->select('COUNT(ap)');
return $qb->getQuery()->getSingleScalarResult();
}
public function countByUserOpenedAccompanyingPeriod(?User $user): int
{
if (null === $user) {
@@ -126,6 +141,23 @@ final class AccompanyingPeriodACLAwareRepository implements AccompanyingPeriodAC
return $qb->getQuery()->getResult();
}
public function findByUnDispatched(array $jobs, array $services, array $administrativeLocations, ?int $limit = null, ?int $offset = null): array
{
$qb = $this->addACLByUnDispatched($this->buildQueryUnDispatched($jobs, $services, $administrativeLocations));
$qb->select('ap');
if (null !== $limit) {
$qb->setMaxResults($limit);
}
if (null !== $offset) {
$qb->setFirstResult($offset);
}
return $qb->getQuery()->getResult();
}
/**
* @return array|AccompanyingPeriod[]
*/
@@ -146,4 +178,88 @@ final class AccompanyingPeriodACLAwareRepository implements AccompanyingPeriodAC
return $qb->getQuery()->getResult();
}
private function addACLByUnDispatched(QueryBuilder $qb): QueryBuilder
{
$centers = $this->authorizationHelper->getReachableCenters(
$this->security->getUser(),
AccompanyingPeriodVoter::SEE
);
$orX = $qb->expr()->orX();
if (0 === count($centers)) {
return $qb->andWhere("'FALSE' = 'TRUE'");
}
foreach ($centers as $key => $center) {
$scopes = $this->authorizationHelper
->getReachableCircles(
$this->security->getUser(),
AccompanyingPeriodVoter::SEE,
$center
);
$and = $qb->expr()->andX(
$qb->expr()->exists('SELECT part FROM ' . AccompanyingPeriodParticipation::class . ' part ' .
"JOIN part.person p WHERE part.accompanyingPeriod = ap.id AND p.center = :center_{$key}")
);
$qb->setParameter('center_' . $key, $center);
$orScope = $qb->expr()->orX();
foreach ($scopes as $skey => $scope) {
$orScope->add(
$qb->expr()->isMemberOf(':scope_' . $key . '_' . $skey, 'ap.scopes')
);
$qb->setParameter('scope_' . $key . '_' . $skey, $scope);
}
$and->add($orScope);
$orX->add($and);
}
return $qb->andWhere($orX);
}
/**
* @param array|UserJob[] $jobs
* @param array|Scope[] $services
* @param array|Location[] $locations
*/
private function buildQueryUnDispatched(array $jobs, array $services, array $locations): QueryBuilder
{
$qb = $this->accompanyingPeriodRepository->createQueryBuilder('ap');
$qb->where(
$qb->expr()->andX(
$qb->expr()->isNull('ap.user'),
$qb->expr()->neq('ap.step', ':draft'),
$qb->expr()->neq('ap.step', ':closed')
)
)
->setParameter('draft', AccompanyingPeriod::STEP_DRAFT)
->setParameter('closed', AccompanyingPeriod::STEP_CLOSED);
if (0 < count($jobs)) {
$qb->andWhere($qb->expr()->in('ap.job', ':jobs'))
->setParameter('jobs', $jobs);
}
if (0 < count($locations)) {
$qb->andWhere($qb->expr()->in('ap.administrativeLocation', ':locations'))
->setParameter('locations', $locations);
}
if (0 < count($services)) {
$or = $qb->expr()->orX();
foreach ($services as $key => $service) {
$or->add($qb->expr()->isMemberOf('ap.scopes', ':scope_' . $key));
$qb->setParameter('scope_' . $key, $service);
}
$qb->andWhere($or);
}
return $qb;
}
}

View File

@@ -11,11 +11,20 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Repository;
use Chill\MainBundle\Entity\Scope;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Entity\UserJob;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person;
interface AccompanyingPeriodACLAwareRepositoryInterface
{
/**
* @param array|UserJob[] $jobs
* @param array|Scope[] $services
*/
public function countByUnDispatched(array $jobs, array $services, array $administrativeLocations): int;
public function countByUserOpenedAccompanyingPeriod(?User $user): int;
public function findByPerson(
@@ -26,5 +35,13 @@ interface AccompanyingPeriodACLAwareRepositoryInterface
?int $offset = null
): array;
/**
* @param array|UserJob[] $jobs if empty, does not take this argument into account
* @param array|Scope[] $services if empty, does not take this argument into account
*
* @return array|AccompanyingPeriod[]
*/
public function findByUnDispatched(array $jobs, array $services, array $administrativeLocations, ?int $limit = null, ?int $offset = null): array;
public function findByUserOpenedAccompanyingPeriod(?User $user, array $orderBy = [], int $limit = 0, int $offset = 50): array;
}

View File

@@ -232,6 +232,14 @@ const store = createStore({
return;
}
let doc = evaluation.documents.find(d => d.key === payload.oldDocument.key);
if (typeof doc === 'undefined') {
console.error('doc not found');
}
doc.storedObject = payload.document.storedObject;
return;
let newDocument = Object.assign(
payload.document, {
key: evaluation.documents.length + 1,

View File

@@ -333,10 +333,10 @@ export default {
addQueryItem(field, queryItem) {
switch (field) {
case 'lastName':
this.person.lastName = queryItem;
this.person.lastName = this.person.lastName ? this.person.lastName += ` ${queryItem}` : queryItem;
break;
case 'firstName':
this.person.firstName = queryItem;
this.person.firstName = this.person.firstName ? this.person.firstName += ` ${queryItem}` : queryItem;
break;
}
},

View File

@@ -45,7 +45,7 @@ const personMessages = {
},
address: {
create_address: "Ajouter une adresse",
show_address_form: "Créer un ménage et ajouter une adresse",
show_address_form: "Ajouter une adresse pour un usager non suivi et seul dans un ménage",
warning: "Un nouveau ménage va être créé. L'usager sera membre de ce ménage."
}
},

View File

@@ -0,0 +1,88 @@
{% extends 'ChillMainBundle::layout.html.twig' %}
{% block title "Liste de parcours à répartir" %}
{% block js %}
{{ encore_entry_script_tags('mod_set_referrer') }}
{% endblock %}
{% block css %}
{{ encore_entry_link_tags('mod_set_referrer') }}
{% endblock %}
{% macro period_meta(period) %}
{% if is_granted('CHILL_PERSON_ACCOMPANYING_PERIOD_UPDATE', period) %}
<div class="item-col item-meta">
{% set job_id = null %}
{% if period.job is defined %}
{% set job_id = period.job.id %}
{% endif %}
<span
data-set-referrer-app="data-set-referrer-app"
data-set-referrer-accompanying-period-id="{{ period.id }}"
data-set-referrer-job-id="{{ job_id }}"
></span>
</div>
{% endif %}
{% endmacro %}
{% macro period_actions(period) %}
{% if is_granted('CHILL_PERSON_ACCOMPANYING_PERIOD_SEE', period) %}
<li>
<a href="{{ chill_path_add_return_path('chill_person_accompanying_course_index', {'accompanying_period_id': period.id}) }}" class="btn btn-show"></a>
</li>
{% endif %}
{% endmacro %}
{% import _self as m %}
{% block content %}
<div class="col-10">
<h1>{{ block('title') }}</h1>
{{ form_start(form) }}
<div class="row">
<div class="col-md-4">
{{ form_label(form.locations ) }}
{{ form_widget(form.locations, {'attr': {'class': 'select2'}}) }}
</div>
<div class="col-md-4">
{{ form_label(form.jobs) }}
{{ form_widget(form.jobs, {'attr': {'class': 'select2'}}) }}
</div>
<div class="col-md-4">
{{ form_label(form.services) }}
{{ form_widget(form.services, {'attr': {'class': 'select2'}}) }}
</div>
</div>
<ul class="record_actions">
<li>
<button type="submit" class="btn btn-save change-icon">
<i class="fa fa-filter"></i> Filtrer
</button>
</li>
</ul>
{{ form_end(form) }}
{% if periods|length == 0 %}
<p class="chill-no-data-statement">Aucun parcours à désigner, ou droits insuffisants pour les afficher</p>
{% else %}
<p><span class="badge rounded-pill bg-primary">{{ paginator.totalItems }}</span> parcours à attribuer (calculé ce jour à {{ null|format_time('medium') }})</p>
<div class="flex-table">
{% for period in periods %}
{% include '@ChillPerson/AccompanyingPeriod/_list_item.html.twig' with {'period': period,
'recordAction': m.period_actions(period), 'itemMeta': m.period_meta(period) } %}
{% endfor %}
</div>
{% endif %}
{{ chill_pagination(paginator) }}
</div>
{% endblock %}

View File

@@ -82,14 +82,14 @@ class PersonSearch extends AbstractSearch implements HasAdvancedSearchFormInterf
'label' => 'Last name',
'required' => false,
])
->add('birthdate-after', ChillDateType::class, [
'label' => 'Birthdate after',
'required' => false,
])
->add('birthdate', ChillDateType::class, [
'label' => 'Birthdate',
'required' => false,
])
->add('birthdate-after', ChillDateType::class, [
'label' => 'Birthdate after',
'required' => false,
])
->add('birthdate-before', ChillDateType::class, [
'label' => 'Birthdate before',
'required' => false,

View File

@@ -44,6 +44,7 @@ class AccompanyingPeriodDocGenNormalizer implements ContextAwareNormalizerInterf
private const PERIOD_NULL = [
'id',
'closingDate' => DateTime::class,
'closingMotive' => AccompanyingPeriod\ClosingMotive::class,
'confidential',
'confidentialText',
'createdAt' => DateTime::class,
@@ -121,6 +122,7 @@ class AccompanyingPeriodDocGenNormalizer implements ContextAwareNormalizerInterf
'type' => 'accompanying_period',
'isNull' => false,
'closingDate' => $this->normalizer->normalize($period->getClosingDate(), $format, $dateContext),
'closingMotive' => $this->normalizer->normalize($period->getClosingMotive(), $format, array_merge($context, ['docgen:expects' => AccompanyingPeriod\ClosingMotive::class])),
'confidential' => $period->isConfidential(),
'createdAt' => $this->normalizer->normalize($period->getCreatedAt(), $format, $dateContext),
'createdBy' => $this->normalizer->normalize($period->getCreatedBy(), $format, $userContext),

View File

@@ -35,6 +35,6 @@ final class AccompanyingPeriodOriginNormalizer implements NormalizerInterface
public function supportsNormalization($data, $format = null): bool
{
return $data instanceof Origin;
return $data instanceof Origin && 'json' === $format;
}
}

View File

@@ -13,8 +13,6 @@ namespace Chill\PersonBundle\Serializer\Normalizer;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument;
use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Serializer\Normalizer\ContextAwareDenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface;
@@ -35,18 +33,6 @@ class AccompanyingPeriodWorkEvaluationDenormalizer implements ContextAwareDenorm
use ObjectToPopulateTrait;
private EntityManagerInterface $em;
private AccompanyingPeriodWorkRepository $workRepository;
public function __construct(
AccompanyingPeriodWorkRepository $workRepository,
EntityManagerInterface $em
) {
$this->workRepository = $workRepository;
$this->em = $em;
}
public function denormalize($data, $type, $format = null, array $context = [])
{
$evaluation = $this->denormalizer->denormalize($data, $type, $format, array_merge(

View File

@@ -0,0 +1,39 @@
<?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 Controller;
use Chill\MainBundle\Test\PrepareClientTrait;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
/**
* @internal
* @coversNothing
*/
final class AccompanyingPeriodRegulationListControllerTest extends WebTestCase
{
use PrepareClientTrait;
protected function setUp(): void
{
parent::setUp();
self::bootKernel();
}
public function testRegulationList(): void
{
$client = $this->getClientAuthenticated();
$client->request('GET', '/fr/person/periods/undispatched');
$this->assertResponseIsSuccessful();
}
}

View File

@@ -11,9 +11,11 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Tests\Controller;
use Chill\MainBundle\Entity\Center;
use Chill\MainBundle\Test\PrepareClientTrait;
use Chill\PersonBundle\Entity\Person;
use Closure;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
/**
@@ -55,10 +57,10 @@ final class PersonControllerUpdateTest extends WebTestCase
{
self::bootKernel();
$this->em = self::$kernel->getContainer()
->get('doctrine.orm.entity_manager');
$this->em = self::$container
->get(EntityManagerInterface::class);
$center = $this->em->getRepository(\Chill\MainBundle\Entity\Center::class)
$center = $this->em->getRepository(Center::class)
->findOneBy(['name' => 'Center A']);
$this->person = (new Person())

View File

@@ -65,6 +65,7 @@ final class AccompanyingPeriodDocGenNormalizerTest extends KernelTestCase
'type' => 'accompanying_period',
'isNull' => false,
'closingDate' => '@ignored',
'closingMotive' => '@ignored',
'confidential' => true,
'confidentialText' => 'confidentiel',
'createdAt' => '@ignored',
@@ -121,59 +122,7 @@ final class AccompanyingPeriodDocGenNormalizerTest extends KernelTestCase
{
$data = $this->normalizer->normalize(null, 'docgen', ['docgen:expects' => AccompanyingPeriod::class]);
$expected = [
'id' => '',
'type' => 'accompanying_period',
'closingDate' => '@ignored',
'confidential' => false,
'confidentialText' => '',
'createdAt' => '@ignored',
'createdBy' => '@ignored',
'emergency' => false,
'emergencyText' => '',
'openingDate' => '@ignored',
'originText' => '',
'origin' => '@ignored',
'requestorAnonymous' => false,
'resources' => [],
'socialIssues' => '@ignored',
'intensity' => '',
'step' => '',
'closingMotiveText' => '',
'socialIssuesText' => '',
'scopes' => '@ignored',
'scopesText' => '',
'ref' => '@ignored',
'participations' => '@ignored',
'currentParticipations' => '@ignored',
'isClosed' => false,
'hasRef' => false,
'hasRequestor' => false,
'requestorKind' => 'none',
'hasRequestorPerson' => false,
'hasRequestorThirdParty' => false,
'requestorPerson' => '@ignored',
'requestorThirdParty' => '@ignored',
'isNull' => true,
'administrativeLocation' => '@ignored',
'hasAdministrativeLocation' => false,
'hasLocation' => false,
'hasLocationPerson' => false,
'location' => '@ignored',
'locationPerson' => '@ignored',
'works' => [],
];
$this->assertIsArray($data);
$this->assertEqualsCanonicalizing(array_keys($expected), array_keys($data));
foreach ($expected as $key => $item) {
if ('@ignored' === $item) {
continue;
}
$this->assertEquals($item, $data[$key], "test the key {$key}");
}
}
public function testNormalizeParticipations()

View File

@@ -0,0 +1,140 @@
<?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 Serializer\Normalizer;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument;
use Chill\PersonBundle\Serializer\Normalizer\AccompanyingPeriodWorkEvaluationDenormalizer;
use PHPUnit\Framework\TestCase;
use ReflectionProperty;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
/**
* @internal
* @coversNothing
*/
final class AccompanyingPeriodWorkEvaluationDenormalizerTest extends TestCase
{
private const ENCODED_DATA = ' {
"type": "accompanying_period_work_evaluation",
"key": 0,
"evaluation": {
"id": 100,
"type": "social_work_evaluation"
},
"startDate": {
"datetime": "2022-04-29T00:00:00+02:00"
},
"endDate": null,
"maxDate": null,
"warningInterval": "P0D",
"comment": "",
"documents": [
{
"type": "accompanying_period_work_evaluation_document",
"id": 1,
"storedObject": {
"creationDate": {
"datetime": "2022-05-30T21:22:47+0200"
},
"datas": [],
"filename": "ZeoWSjqVc2qN1XUHptTV6S",
"id": 11,
"iv": [
89,
164,
162,
48,
155,
159,
69,
135,
191,
241,
241,
8,
17,
56,
183,
224
],
"keyInfos": {
"alg": "A256CBC",
"ext": true,
"k": "uW4d7si_hi--VQHxi76ZllKQiYzaEJYGN8KBrWXxi7s",
"key_ops": [
"encrypt",
"decrypt"
],
"kty": "oct"
},
"title": "",
"type": "application/vnd.oasis.opendocument.text",
"uuid": "b9dd9eff-a7cf-4a29-89e3-57efc9ed215b"
},
"title": "test",
"key": 2,
"workflows_availables": [
{
"name": "vendee_internal",
"text": "Suivi CD85"
}
],
"workflows": []
}
],
"id": 382
}';
public function testAssociatedDocumentIsTheSame()
{
$this->markTestIncomplete('not yet finished');
$evaluation = new AccompanyingPeriodWorkEvaluation();
$doc = new AccompanyingPeriodWorkEvaluationDocument();
$doc->setStoredObject($storedObject = new StoredObject());
$reflectionProperty = new ReflectionProperty(AccompanyingPeriodWorkEvaluationDocument::class, 'id');
$reflectionProperty->setAccessible(true);
$reflectionProperty->setValue($doc, 1);
$evaluation->addDocument($doc);
$data = json_decode(self::ENCODED_DATA);
$context =
[AbstractNormalizer::OBJECT_TO_POPULATE => $evaluation, 'groups' => ['write']];
$denormalizer = new AccompanyingPeriodWorkEvaluationDenormalizer();
/*
$this->assertTrue(
$denormalizer->supportsDenormalization(
$data,
AccompanyingPeriodWorkEvaluation::class,
'json',
$context
)
);
*/
$denormalizedEvaluation = $denormalizer->denormalize(
$data,
AccompanyingPeriodWorkEvaluation::class,
'json',
$context
);
$this->assertSame($evaluation, $denormalizedEvaluation);
$this->assertCount(1, $evaluation->getDocuments());
$this->assertSame($doc, $evaluation->getDocuments()->first());
$this->assertNotSame($storedObject, $evaluation->getDocuments()->first()->getStoredObject());
}
}

View File

@@ -86,8 +86,9 @@ Civility: Civilité
choose civility: --
All genders: tous les genres
Any person selected: Aucune personne sélectionnée
Create a household and add an address: Créer un ménage et ajouter une adresse
Create a household and add an address: Ajouter une adresse pour un usager non suivi et seul dans un ménage
A new household will be created. The person will be member of this household.: Un nouveau ménage va être créé. L'usager sera membre de ce ménage.
Comment on the gender: Commentaire sur le genre
# dédoublonnage
Old person: Doublon