Merge remote-tracking branch 'origin/master' into issue509_parcours_referent

This commit is contained in:
Julien Fastré 2022-03-21 12:59:24 +01:00
commit f084078cf2
14 changed files with 266 additions and 55 deletions

View File

@ -11,6 +11,7 @@ and this project adheres to
## Unreleased
<!-- write down unreleased development here -->
* [person] AccompanyingPeriodWork: add referrers to work, add doctrine event listener to add logged user to referrers collection and display a referrers list in work list (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/502)
* [person] AccompanyingPeriodWorkEvaluation: fix circular reference when serialising (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/495)
* [person] order accompanying period by opening date in search persons, person and household period lists (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/493)
* [parcours] autosave of the pinned comment for draft accompanying course (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/477)

View File

@ -27,7 +27,7 @@
</a>
{% for menu in menus %}
<a class="list-group-item list-group-item-action" href="{{ menu.uri }}">
{{ menu.label|upper }}
{{ menu.label|trans|upper }}
</a>
{% endfor %}
</div>

View File

@ -1,44 +1,43 @@
{% extends '@ChillMain/Admin/Permission/layout_crud_permission_index.html.twig' %}
{% block admin_content -%}
{% embed '@ChillMain/CRUD/_index.html.twig' %}
{% block table_entities_thead_tr %}
<th>{{ 'crud.admin_user.index.is_active'|trans }}</th>
<th>{{ 'crud.admin_user.index.usernames'|trans }}</th>
<th>{{ 'crud.admin_user.index.mains'|trans }}</th>
<th>&nbsp;</th>
{% endblock %}
{% block table_entities_tbody %}
<h1>{{"Users"|trans}}</h1>
{% for entity in entities %}
<tr data-username="{{ entity.username|e('html_attr') }}">
<td>
<div class="flex-table">
<div class="item-bloc">
<div class="item-row">
<div class="item-col">
{{ entity.label }}
{% if entity.isEnabled %}
<i class="fa fa-check chill-green"></i>
{% else %}
<i class="fa fa-times chill-red"></i>
{% endif %}
</td>
<td>
{{ entity.username }}
<br/>
{{ entity.label }}
<br/>
{{ entity.email }}
</td>
<td>
</div>
<div class="item-col"><i>{{ entity.email }}</i></div>
</div>
<div class="item-row">
<div class="item-col">
login: {{ entity.username|e('html_attr') }}
</div>
<div class="item-col">
{% if entity.userJob %}
{{ entity.userJob.label|localize_translatable_string }}
<br/>
{% endif %}
</div>
</div>
<div class="item-row">
<div class="item-col">
{% if entity.mainScope %}
{{ entity.mainScope.name|localize_translatable_string }}
<br/>
{% endif %}
{% if entity.mainCenter %}
{{ entity.mainCenter.name }}
, {{ entity.mainCenter.name }}
{% endif %}
</td>
<td>
</div>
</div>
<div class="item-row">
<ul class="record_actions">
<li>
<a class="btn btn-edit" href="{{ path('chill_crud_admin_user_edit', { 'id': entity.id }) }}"></a>
@ -52,9 +51,12 @@
</li>
{% endif %}
</ul>
</td>
</tr>
</div>
</div>
</div>
{% endfor %}
{% endblock %}
{% endembed %}
{{ chill_pagination(paginator) }}
{% endblock %}

View File

@ -142,6 +142,15 @@ class AccompanyingPeriodWork implements AccompanyingPeriodLinkedWithSocialIssues
*/
private Collection $persons;
/**
* @ORM\ManyToMany(targetEntity=User::class)
* @ORM\JoinTable(name="chill_person_accompanying_period_work_referrer")
* @Serializer\Groups({"read", "docgen:read", "read:accompanyingPeriodWork:light"})
* @Serializer\Groups({"accompanying_period_work:edit"})
* @Serializer\Groups({"accompanying_period_work:create"})
*/
private Collection $referrers;
/**
* @ORM\ManyToMany(targetEntity=Result::class, inversedBy="accompanyingPeriodWorks")
* @ORM\JoinTable(name="chill_person_accompanying_period_work_result")
@ -196,6 +205,7 @@ class AccompanyingPeriodWork implements AccompanyingPeriodLinkedWithSocialIssues
$this->thirdParties = new ArrayCollection();
$this->persons = new ArrayCollection();
$this->accompanyingPeriodWorkEvaluations = new ArrayCollection();
$this->referrers = new ArrayCollection();
}
public function addAccompanyingPeriodWorkEvaluation(AccompanyingPeriodWorkEvaluation $evaluation): self
@ -227,6 +237,15 @@ class AccompanyingPeriodWork implements AccompanyingPeriodLinkedWithSocialIssues
return $this;
}
public function addReferrer(User $referrer): self
{
if (!$this->referrers->contains($referrer)) {
$this->referrers[] = $referrer;
}
return $this;
}
public function addResult(Result $result): self
{
if (!$this->results->contains($result)) {
@ -308,6 +327,14 @@ class AccompanyingPeriodWork implements AccompanyingPeriodLinkedWithSocialIssues
return $this->persons;
}
/**
* @return Collection|User[]
*/
public function getReferrers(): Collection
{
return $this->referrers;
}
/**
* @return Collection|Result[]
*/
@ -382,6 +409,13 @@ class AccompanyingPeriodWork implements AccompanyingPeriodLinkedWithSocialIssues
return $this;
}
public function removeReferrer(User $referrer): self
{
$this->referrers->removeElement($referrer);
return $this;
}
public function removeResult(Result $result): self
{
$this->results->removeElement($result);

View File

@ -0,0 +1,30 @@
<?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\EventListener;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
use Symfony\Component\Security\Core\Security;
class AccompanyingPeriodWorkEventListener
{
private Security $security;
public function __construct(Security $security)
{
$this->security = $security;
}
public function prePersistAccompanyingPeriodWork(AccompanyingPeriodWork $work): void
{
$work->addReferrer($this->security->getUser());
}
}

View File

@ -87,9 +87,12 @@ class AccompanyingPeriodWorkEvaluationRepository implements ObjectRepository
->join('work.accompanyingPeriod', 'period')
->where(
$qb->expr()->andX(
$qb->expr()->eq('period.user', ':user'),
$qb->expr()->isNull('e.endDate'),
$qb->expr()->gte(':now', $qb->expr()->diff('e.maxDate', 'e.warningInterval'))
$qb->expr()->gte(':now', $qb->expr()->diff('e.maxDate', 'e.warningInterval')),
$qb->expr()->orX(
$qb->expr()->eq('period.user', ':user'),
$qb->expr()->isMemberOf(':user', 'work.referrers')
)
)
)
->setParameters([

View File

@ -159,9 +159,12 @@ final class AccompanyingPeriodWorkRepository implements ObjectRepository
->join('w.accompanyingPeriod', 'period')
->where(
$qb->expr()->andX(
$qb->expr()->eq('period.user', ':user'),
$qb->expr()->gte('w.endDate', ':since'),
$qb->expr()->lte('w.startDate', ':until')
$qb->expr()->lte('w.startDate', ':until'),
$qb->expr()->orX(
$qb->expr()->eq('period.user', ':user'),
$qb->expr()->isMemberOf(':user', 'w.referrers')
)
)
)
->setParameters([

View File

@ -151,6 +151,37 @@
</ul>
</div>
<div id="referrers" class="action-row">
<h3>{{ $t('referrers') }}</h3>
<div v-if="!hasReferrers">
<p class="chill-no-data-statement">{{ $t('no_referrers') }}</p>
</div>
<div v-else>
<ul class="list-suggest remove-items inline">
<li v-for="u in referrers" :key="u.id" :title="$t('remove_referrer')" @click="removeReferrer(u)">
<span>
{{ u.text }}
</span>
</li>
</ul>
</div>
<ul class="record_actions">
<li class="add-persons">
<add-persons
ref="referrerPicker"
:key="referrerPicker.key"
:buttonTitle="$t('add_referrers')"
:modalTitle="$t('choose_referrers')"
:options="referrerPicker.options"
@addNewPersons="addReferrers">
</add-persons>
</li>
</ul>
</div>
<div id="handlingThirdParty" class="action-row">
<h3>{{ $t('handling_thirdparty') }}</h3>
@ -289,7 +320,6 @@ import PersonText from 'ChillPersonAssets/vuejs/_components/Entity/PersonText.vu
import {buildLinkCreate} from 'ChillMainAssets/lib/entity-workflow/api.js';
import { makeFetch } from 'ChillMainAssets/lib/api/apiMethods';
const i18n = {
messages: {
fr: {
@ -322,6 +352,10 @@ const i18n = {
available_evaluations_text: "Documents disponibles pour ajout :",
no_evaluations_available: "Aucune évaluation disponible",
no_goals_available: "Aucun objectif disponible",
referrers: "Agents traitants",
no_referrers: "Aucun agent traitant",
choose_referrers: "Choisir des agents traitants",
remove_referrer: "Enlever l'agent"
}
}
};
@ -370,6 +404,17 @@ export default {
}
},
},
referrerPicker: {
key: 'referrer',
options: {
type: ['user'],
priority: null,
uniq: false,
button: {
display: false
}
},
},
};
},
computed: {
@ -381,6 +426,7 @@ export default {
'personsReachables',
'handlingThirdParty',
'thirdParties',
'referrers',
'isPosting',
'errors',
'templatesAvailablesForAction',
@ -389,6 +435,7 @@ export default {
'hasResultsForAction',
'hasHandlingThirdParty',
'hasThirdParties',
'hasReferrers'
]),
startDate: {
get() {
@ -465,6 +512,14 @@ export default {
removeThirdParty(t) {
this.$store.commit('removeThirdParty', t);
},
addReferrers({selected, modal}) {
this.$store.commit('addReferrers', selected.map(r => r.result));
this.$refs.referrerPicker.resetSearch();
modal.showModal = false;
},
removeReferrer(u) {
this.$store.commit('removeReferrer', u);
},
goToGenerateWorkflow({link}) {
console.log('save before leave to generate workflow')
const callback = (data) => {
@ -521,6 +576,7 @@ div#workEditor {
"objectives objectives"
"evaluations evaluations"
"persons persons"
"referrers referrers"
"handling handling"
"tparties tparties"
"errors errors";
@ -543,6 +599,8 @@ div#workEditor {
grid-area: handling; }
#thirdParties {
grid-area: tparties; }
#referrers {
grid-area: referrers; }
#errors {
grid-area: errors; }
@ -657,5 +715,4 @@ div#workEditor {
}
}
</style>

View File

@ -31,6 +31,7 @@ const store = createStore({
.map(p => p.person),
handlingThirdParty: window.accompanyingCourseWork.handlingThierParty,
thirdParties: window.accompanyingCourseWork.thirdParties,
referrers: window.accompanyingCourseWork.referrers,
isPosting: false,
errors: [],
},
@ -54,6 +55,9 @@ const store = createStore({
hasHandlingThirdParty(state) {
return state.handlingThirdParty !== null;
},
hasReferrers(state) {
return state.referrers.length > 0;
},
hasThirdParties(state) {
return state.thirdParties.length > 0;
},
@ -82,6 +86,7 @@ const store = createStore({
},
results: state.resultsPicked.map(r => ({id: r.id, type: r.type})),
thirdParties: state.thirdParties.map(t => ({id: t.id, type: t.type})),
referrers: state.referrers.map(t => ({id: t.id, type: t.type})),
goals: state.goalsPicked.map(g => {
let o = {
type: g.type,
@ -302,6 +307,18 @@ const store = createStore({
state.thirdParties = state.thirdParties
.filter(t => t.id !== thirdParty.id);
},
addReferrers(state, referrers) {
let ids = state.referrers.map(t => t.id);
let unexistings = referrers.filter(t => !ids.includes(t.id));
for (let i in unexistings) {
state.referrers.push(unexistings[i]);
}
},
removeReferrer(state, user) {
state.referrers = state.referrers
.filter(u => u.id !== user.id);
},
setErrors(state, errors) {
state.errors = errors;
},
@ -315,7 +332,6 @@ const store = createStore({
},
actions: {
updateThirdParty({ commit }, payload) {
console.log(payload);
commit('updateThirdParty', payload);
},
getReachablesGoalsForAction({ getters, commit, dispatch }) {

View File

@ -28,11 +28,14 @@
{% if w.createdBy %}
<div class="wl-row">
<div class="wl-col title">
<h3>{{ 'Referrer'|trans }}</h3>
<h3>{{ 'Referrers'|trans }}</h3>
</div>
<div class="wl-col list">
<p class="wl-item">
{{ w.createdBy|chill_entity_render_box }}
{% for u in w.referrers %}
{{ u|chill_entity_render_box }}
{% if not loop.last %}, {% endif %}
{% endfor %}
</p>
</div>
</div>

View File

@ -16,6 +16,7 @@ use Chill\PersonBundle\Entity\Household\HouseholdMember;
use Chill\PersonBundle\Entity\Household\Position;
use Chill\PersonBundle\Entity\Person;
use DateTimeImmutable;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
@ -25,17 +26,24 @@ use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
*/
final class HouseholdNormalizerTest extends KernelTestCase
{
private EntityManagerInterface $entityManager;
private ?NormalizerInterface $normalizer;
private array $toDelete;
protected function setUp(): void
{
self::bootKernel();
$this->normalizer = self::$container->get(NormalizerInterface::class);
$this->entityManager = self::$container->get(EntityManagerInterface::class);
}
public function testNormalizationRecursive()
{
$person = new Person();
$person->setFirstName('ok')->setLastName('ok');
$this->entityManager->persist($person);
$member = new HouseholdMember();
$household = new Household();
$position = (new Position())
@ -44,7 +52,8 @@ final class HouseholdNormalizerTest extends KernelTestCase
$member->setPerson($person)
->setStartDate(new DateTimeImmutable('1 year ago'))
->setEndDate(new DateTimeImmutable('1 month ago'));
->setEndDate(new DateTimeImmutable('1 month ago'))
->setPosition($position);
$household->addMember($member);

View File

@ -12,3 +12,13 @@ services:
event: 'prePersist'
entity: 'Chill\PersonBundle\Entity\PersonAltName'
method: 'prePersistAltName'
Chill\PersonBundle\EventListener\AccompanyingPeriodWorkEventListener:
autoconfigure: true
autowire: true
tags:
-
name: 'doctrine.orm.entity_listener'
event: 'prePersist'
entity: 'Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork'
method: 'prePersistAccompanyingPeriodWork'

View File

@ -0,0 +1,42 @@
<?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\Migrations\Person;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Add referrers to AccompanyingPeriodWork.
*/
final class Version20220310063629 extends AbstractMigration
{
public function down(Schema $schema): void
{
$this->addSql('DROP TABLE chill_person_accompanying_period_work_referrer');
}
public function getDescription(): string
{
return 'Add referrers to AccompanyingPeriodWork';
}
public function up(Schema $schema): void
{
$this->addSql('CREATE TABLE chill_person_accompanying_period_work_referrer (accompanyingperiodwork_id INT NOT NULL, user_id INT NOT NULL, PRIMARY KEY(accompanyingperiodwork_id, user_id))');
$this->addSql('CREATE INDEX IDX_3619F5EBB99F6060 ON chill_person_accompanying_period_work_referrer (accompanyingperiodwork_id)');
$this->addSql('CREATE INDEX IDX_3619F5EBA76ED395 ON chill_person_accompanying_period_work_referrer (user_id)');
$this->addSql('ALTER TABLE chill_person_accompanying_period_work_referrer ADD CONSTRAINT FK_3619F5EBB99F6060 FOREIGN KEY (accompanyingperiodwork_id) REFERENCES chill_person_accompanying_period_work (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
$this->addSql('ALTER TABLE chill_person_accompanying_period_work_referrer ADD CONSTRAINT FK_3619F5EBA76ED395 FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
$this->addSql('INSERT INTO chill_person_accompanying_period_work_referrer (accompanyingperiodwork_id, user_id)
SELECT id, createdby_id FROM postgres.public.chill_person_accompanying_period_work');
}
}

View File

@ -214,6 +214,7 @@ No requestor: Pas de demandeur
No resources: "Pas d'interlocuteurs privilégiés"
Persons associated: Usagers concernés
Referrer: Référent
Referrers: Référents
Some peoples does not belong to any household currently. Add them to an household soon: Certaines personnes n'appartiennent à aucun ménage actuellement. Renseignez leur ménage dès que possible.
Add to household now: Ajouter à un ménage
Any resource for this accompanying course: Aucun interlocuteur privilégié pour ce parcours