mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-12 21:34:25 +00:00
Merge branch 'master' into issue433_email_addPerson
This commit is contained in:
commit
344981cf99
10
CHANGELOG.md
10
CHANGELOG.md
@ -14,8 +14,15 @@ and this project adheres to
|
|||||||
* [person] accompanying course work: fix on-the-fly update of thirdParty
|
* [person] accompanying course work: fix on-the-fly update of thirdParty
|
||||||
* [on-the-fly] close modal only after validation
|
* [on-the-fly] close modal only after validation
|
||||||
* [person] correct thirdparty PATCH url + add email and altnames in AddPerson and serializer (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/433)
|
* [person] correct thirdparty PATCH url + add email and altnames in AddPerson and serializer (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/433)
|
||||||
|
* change order for accompanying course work list
|
||||||
|
* [person]: style fix in parcours listing per person. (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/432)
|
||||||
|
* ajoute un ordre dans les localisation (api)
|
||||||
|
|
||||||
|
|
||||||
|
## Test releases
|
||||||
|
|
||||||
|
### test release 2021-02-01
|
||||||
|
>>>>>>> master
|
||||||
|
|
||||||
* renommer "dossier numéro" en "parcours numéro" dans les résultats de recherche
|
* renommer "dossier numéro" en "parcours numéro" dans les résultats de recherche
|
||||||
* renomme date de début en date d'ouverture dans le formulaire parcours
|
* renomme date de début en date d'ouverture dans le formulaire parcours
|
||||||
@ -35,9 +42,6 @@ and this project adheres to
|
|||||||
* [fast_actions] improve fast-actions buttons override mechanism, fix https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/413
|
* [fast_actions] improve fast-actions buttons override mechanism, fix https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/413
|
||||||
* [homepage widget] add vue homepage_widget with asynchone loading, give a global view resume of the user concerned actions, notifications, etc.
|
* [homepage widget] add vue homepage_widget with asynchone loading, give a global view resume of the user concerned actions, notifications, etc.
|
||||||
|
|
||||||
|
|
||||||
## Test releases
|
|
||||||
|
|
||||||
### test release 2021-01-31
|
### test release 2021-01-31
|
||||||
|
|
||||||
* [person] accompanying course: optimisation: do not fetch some resources for the banner (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/409)
|
* [person] accompanying course: optimisation: do not fetch some resources for the banner (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/409)
|
||||||
|
@ -220,7 +220,7 @@ final class ActivityController extends AbstractController
|
|||||||
$this->entityManager->persist($entity);
|
$this->entityManager->persist($entity);
|
||||||
$this->entityManager->flush();
|
$this->entityManager->flush();
|
||||||
|
|
||||||
if ($form->has('gendocTemplateId') && '' !== $form['gendocTemplateId']) {
|
if ($form->has('gendocTemplateId') && null !== $form['gendocTemplateId']->getData()) {
|
||||||
return $this->redirectToRoute(
|
return $this->redirectToRoute(
|
||||||
'chill_docgenerator_generate_from_template',
|
'chill_docgenerator_generate_from_template',
|
||||||
[
|
[
|
||||||
@ -437,7 +437,7 @@ final class ActivityController extends AbstractController
|
|||||||
$this->entityManager->persist($entity);
|
$this->entityManager->persist($entity);
|
||||||
$this->entityManager->flush();
|
$this->entityManager->flush();
|
||||||
|
|
||||||
if ($form->has('gendocTemplateId') && '' !== $form['gendocTemplateId']) {
|
if ($form->has('gendocTemplateId') && null !== $form['gendocTemplateId']->getData()) {
|
||||||
return $this->redirectToRoute(
|
return $this->redirectToRoute(
|
||||||
'chill_docgenerator_generate_from_template',
|
'chill_docgenerator_generate_from_template',
|
||||||
[
|
[
|
||||||
|
@ -97,7 +97,6 @@ class CalendarType extends AbstractType
|
|||||||
return $res;
|
return $res;
|
||||||
},
|
},
|
||||||
static function (?string $dateAsString): DateTimeImmutable {
|
static function (?string $dateAsString): DateTimeImmutable {
|
||||||
|
|
||||||
return new DateTimeImmutable($dateAsString);
|
return new DateTimeImmutable($dateAsString);
|
||||||
}
|
}
|
||||||
));
|
));
|
||||||
|
@ -8,14 +8,13 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-8">
|
<div class="col-8">
|
||||||
<h3>{{ document.title }}</h3>
|
<h3>{{ document.title }}</h3>
|
||||||
<small>{{ document.object.type }}</small>
|
|
||||||
|
|
||||||
{% if document.description is not empty %}
|
{% if document.description is not empty %}
|
||||||
<blockquote class="chill-user-quote mt-2">
|
<blockquote class="chill-user-quote mt-2">
|
||||||
{{ document.description }}
|
{{ document.description }}
|
||||||
</blockquote>
|
</blockquote>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -32,6 +31,13 @@
|
|||||||
|
|
||||||
{% if display_action is defined and display_action == true %}
|
{% if display_action is defined and display_action == true %}
|
||||||
<ul class="record_actions">
|
<ul class="record_actions">
|
||||||
|
{% if document.course != null and is_granted('CHILL_PERSON_ACCOMPANYING_PERIOD_SEE', document.course) %}
|
||||||
|
<li>
|
||||||
|
<a href="{{ path('chill_person_accompanying_course_index', {'accompanying_period_id': document.course.id}) }}" class="btn btn-show change-icon">
|
||||||
|
<i class="fa fa-random"></i> {{ 'Course number'|trans }} {{ document.course.id }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
<li>
|
<li>
|
||||||
{{ m.download_button(document.object, document.title) }}
|
{{ m.download_button(document.object, document.title) }}
|
||||||
</li>
|
</li>
|
||||||
@ -43,19 +49,25 @@
|
|||||||
'changeClass' string
|
'changeClass' string
|
||||||
'noText' boolean
|
'noText' boolean
|
||||||
#}
|
#}
|
||||||
{# vue component #}
|
{# vue component
|
||||||
<span
|
<span
|
||||||
data-module="wopi-link"
|
data-module="wopi-link"
|
||||||
data-wopi-url="{{ path('chill_wopi_file_edit', {'fileId': document.object.uuid}) }}"
|
data-wopi-url="{{ path('chill_wopi_file_edit', {'fileId': document.object.uuid}) }}"
|
||||||
data-doc-title="{{ document.title|e('html_attr') }}"
|
data-doc-title="{{ document.title|e('html_attr') }}"
|
||||||
data-doc-type="{{ document.object.type|e('html_attr') }}"
|
data-doc-type="{{ document.object.type|e('html_attr') }}"
|
||||||
data-button="{{ button|json_encode }}"
|
data-button="{{ button|json_encode }}"
|
||||||
></span>
|
></span> #}
|
||||||
|
<a class="btn btn-update" href="{{ chill_path_add_return_path('chill_wopi_file_edit', {'fileId': document.object.uuid}) }}">{{ 'Edit'|trans }}</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
<a class="btn btn-update change-icon disabled" href="#" title="{{ 'workflow.freezed document'|trans }}">
|
<a class="btn btn-update change-icon disabled" href="#" title="{{ 'workflow.freezed document'|trans }}">
|
||||||
<i class="fa fa-lock me-2"></i>{{ 'Update document'|trans }}
|
<i class="fa fa-lock me-2"></i>{{ 'Update document'|trans }}
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if is_granted('CHILL_ACCOMPANYING_COURSE_DOCUMENT_SEE', document) and document.course != null %}
|
||||||
|
<li>
|
||||||
|
<a href="{{ chill_path_add_return_path('accompanying_course_document_show', {'course': document.course.id, 'id': document.id}) }}" class="btn btn-show"></a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -12,6 +12,8 @@ declare(strict_types=1);
|
|||||||
namespace Chill\MainBundle\Controller;
|
namespace Chill\MainBundle\Controller;
|
||||||
|
|
||||||
use Chill\MainBundle\CRUD\Controller\ApiController;
|
use Chill\MainBundle\CRUD\Controller\ApiController;
|
||||||
|
use Chill\MainBundle\Pagination\PaginatorInterface;
|
||||||
|
use Doctrine\ORM\QueryBuilder;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -19,7 +21,7 @@ use Symfony\Component\HttpFoundation\Request;
|
|||||||
*/
|
*/
|
||||||
class LocationApiController extends ApiController
|
class LocationApiController extends ApiController
|
||||||
{
|
{
|
||||||
public function customizeQuery(string $action, Request $request, $query): void
|
protected function customizeQuery(string $action, Request $request, $query): void
|
||||||
{
|
{
|
||||||
$query
|
$query
|
||||||
->leftJoin('e.locationType', 'lt')
|
->leftJoin('e.locationType', 'lt')
|
||||||
@ -31,4 +33,14 @@ class LocationApiController extends ApiController
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param QueryBuilder $query
|
||||||
|
* @param mixed $_format
|
||||||
|
*/
|
||||||
|
protected function orderQuery(string $action, $query, Request $request, PaginatorInterface $paginator, $_format)
|
||||||
|
{
|
||||||
|
return $query
|
||||||
|
->addOrderBy('e.name', 'ASC');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -236,6 +236,10 @@ class NotificationController extends AbstractController
|
|||||||
'_fragment' => 'comment-' . $commentId,
|
'_fragment' => 'comment-' . $commentId,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($editedCommentForm->isSubmitted() && !$editedCommentForm->isValid()) {
|
||||||
|
$this->addFlash('error', $this->translator->trans('This form contains errors'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,6 +261,10 @@ class NotificationController extends AbstractController
|
|||||||
'id' => $notification->getId(),
|
'id' => $notification->getId(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($appendCommentForm->isSubmitted() && !$appendCommentForm->isValid()) {
|
||||||
|
$this->addFlash('error', $this->translator->trans('This form contains errors'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ use DateTimeInterface;
|
|||||||
use Doctrine\ORM\Event\LifecycleEventArgs;
|
use Doctrine\ORM\Event\LifecycleEventArgs;
|
||||||
use Doctrine\ORM\Event\PreFlushEventArgs;
|
use Doctrine\ORM\Event\PreFlushEventArgs;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
use Symfony\Component\Validator\Constraints as Assert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ORM\Entity
|
* @ORM\Entity
|
||||||
@ -28,6 +29,7 @@ class NotificationComment implements TrackCreationInterface, TrackUpdateInterfac
|
|||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @ORM\Column(type="text")
|
* @ORM\Column(type="text")
|
||||||
|
* @Assert\NotBlank(message="notification.Comment content might not be blank")
|
||||||
*/
|
*/
|
||||||
private string $content = '';
|
private string $content = '';
|
||||||
|
|
||||||
@ -136,9 +138,9 @@ class NotificationComment implements TrackCreationInterface, TrackUpdateInterfac
|
|||||||
$this->recentlyPersisted = true;
|
$this->recentlyPersisted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setContent(string $content): self
|
public function setContent(?string $content): self
|
||||||
{
|
{
|
||||||
$this->content = $content;
|
$this->content = (string) $content;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
@ -79,11 +79,8 @@ class NotificationMailer
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$email = new Email();
|
|
||||||
$email
|
|
||||||
->subject($notification->getTitle());
|
|
||||||
|
|
||||||
if ($notification->isSystem()) {
|
if ($notification->isSystem()) {
|
||||||
|
$email = new Email();
|
||||||
$email
|
$email
|
||||||
->text($notification->getMessage());
|
->text($notification->getMessage());
|
||||||
} else {
|
} else {
|
||||||
@ -96,7 +93,9 @@ class NotificationMailer
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$email->to($addressee->getEmail());
|
$email
|
||||||
|
->subject($notification->getTitle())
|
||||||
|
->to($addressee->getEmail());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$this->mailer->send($email);
|
$this->mailer->send($email);
|
||||||
|
@ -47,7 +47,8 @@ export default {
|
|||||||
console.log('goToGenerateWorkflow', event, workflowName);
|
console.log('goToGenerateWorkflow', event, workflowName);
|
||||||
|
|
||||||
if (!this.$props.preventDefaultMoveToGenerate) {
|
if (!this.$props.preventDefaultMoveToGenerate) {
|
||||||
event.target.click();
|
console.log('to go generate');
|
||||||
|
window.location.assign(this.makeLink(workflowName));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$emit('goToGenerateWorkflow', {event, workflowName, link: this.makeLink(workflowName)});
|
this.$emit('goToGenerateWorkflow', {event, workflowName, link: this.makeLink(workflowName)});
|
||||||
|
@ -91,11 +91,11 @@ export const multiSelectMessages = {
|
|||||||
multiselect: {
|
multiselect: {
|
||||||
placeholder: 'Choisir',
|
placeholder: 'Choisir',
|
||||||
tag_placeholder: 'Créer un nouvel élément',
|
tag_placeholder: 'Créer un nouvel élément',
|
||||||
select_label: 'Appuyer sur "Entrée" pour sélectionner',
|
select_label: '"Entrée" ou cliquez pour sélectionner',
|
||||||
deselect_label: 'Appuyer sur "Entrée" pour désélectionner',
|
deselect_label: '"Entrée" ou cliquez pour désélectionner',
|
||||||
select_group_label: 'Appuyer sur "Entrée" pour sélectionner ce groupe',
|
select_group_label: 'Appuyer sur "Entrée" pour sélectionner ce groupe',
|
||||||
deselect_group_label: 'Appuyer sur "Entrée" pour désélectionner ce groupe',
|
deselect_group_label: 'Appuyer sur "Entrée" pour désélectionner ce groupe',
|
||||||
selected_label: 'Sélectionné'
|
selected_label: 'Sélectionné'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -15,11 +15,11 @@
|
|||||||
|
|
||||||
<div class="notification-comment-list my-5">
|
<div class="notification-comment-list my-5">
|
||||||
<h2 class="chill-blue">{{ 'notification.comments_list'|trans }}</h2>
|
<h2 class="chill-blue">{{ 'notification.comments_list'|trans }}</h2>
|
||||||
|
|
||||||
{% if notification.comments|length > 0 %}
|
{% if notification.comments|length > 0 %}
|
||||||
<div class="flex-table">
|
<div class="flex-table">
|
||||||
{% for comment in notification.comments %}
|
{% for comment in notification.comments %}
|
||||||
|
|
||||||
{% if editedCommentForm is null or editedCommentId != comment.id %}
|
{% if editedCommentForm is null or editedCommentId != comment.id %}
|
||||||
{{ m.show_comment(comment, {
|
{{ m.show_comment(comment, {
|
||||||
'recordAction': _self.recordAction(comment)
|
'recordAction': _self.recordAction(comment)
|
||||||
@ -28,10 +28,11 @@
|
|||||||
<div class="item-bloc">
|
<div class="item-bloc">
|
||||||
<div class="item-row row">
|
<div class="item-row row">
|
||||||
<a id="comment-{{ comment.id }}"></a>
|
<a id="comment-{{ comment.id }}"></a>
|
||||||
|
|
||||||
{{ form_start(editedCommentForm) }}
|
{{ form_start(editedCommentForm) }}
|
||||||
{{ form_errors(editedCommentForm) }}
|
{{ form_errors(editedCommentForm) }}
|
||||||
{{ form_widget(editedCommentForm.content) }}
|
{{ form_widget(editedCommentForm.content) }}
|
||||||
|
{{ form_errors(editedCommentForm.content) }}
|
||||||
<input type="hidden" name="form" value="edit" />
|
<input type="hidden" name="form" value="edit" />
|
||||||
<ul class="record_actions">
|
<ul class="record_actions">
|
||||||
<li class="cancel">
|
<li class="cancel">
|
||||||
@ -46,24 +47,25 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
{{ form_end(editedCommentForm) }}
|
{{ form_end(editedCommentForm) }}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<span class="chill-no-data-statement">{{ 'No comments'|trans }}</span>
|
<span class="chill-no-data-statement">{{ 'No comments'|trans }}</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if appendCommentForm is not null %}
|
{% if appendCommentForm is not null %}
|
||||||
<div class="new-comment my-5">
|
<div class="new-comment my-5">
|
||||||
<h2 class="chill-blue mb-4">{{ 'Write a new comment'|trans }}</h2>
|
<h2 class="chill-blue mb-4">{{ 'Write a new comment'|trans }}</h2>
|
||||||
|
|
||||||
{{ form_start(appendCommentForm) }}
|
{{ form_start(appendCommentForm) }}
|
||||||
{{ form_errors(appendCommentForm) }}
|
{{ form_errors(appendCommentForm) }}
|
||||||
{{ form_widget(appendCommentForm.content) }}
|
{{ form_widget(appendCommentForm.content) }}
|
||||||
|
{{ form_errors(appendCommentForm.content) }}
|
||||||
<input type="hidden" name="form" value="append" />
|
<input type="hidden" name="form" value="append" />
|
||||||
<ul class="record_actions">
|
<ul class="record_actions">
|
||||||
<li>
|
<li>
|
||||||
@ -71,7 +73,7 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
{{ form_end(appendCommentForm) }}
|
{{ form_end(appendCommentForm) }}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
@ -16,16 +16,16 @@ use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
|
|||||||
use Chill\MainBundle\Repository\Workflow\EntityWorkflowStepRepository;
|
use Chill\MainBundle\Repository\Workflow\EntityWorkflowStepRepository;
|
||||||
use Chill\MainBundle\Templating\UI\NotificationCounterInterface;
|
use Chill\MainBundle\Templating\UI\NotificationCounterInterface;
|
||||||
use Psr\Cache\CacheItemPoolInterface;
|
use Psr\Cache\CacheItemPoolInterface;
|
||||||
|
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||||
use Symfony\Component\Security\Core\User\UserInterface;
|
use Symfony\Component\Security\Core\User\UserInterface;
|
||||||
use Symfony\Component\Workflow\Event\Event;
|
use Symfony\Component\Workflow\Event\Event;
|
||||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
|
||||||
|
|
||||||
final class WorkflowByUserCounter implements NotificationCounterInterface, EventSubscriberInterface
|
final class WorkflowByUserCounter implements NotificationCounterInterface, EventSubscriberInterface
|
||||||
{
|
{
|
||||||
private EntityWorkflowStepRepository $workflowStepRepository;
|
|
||||||
|
|
||||||
private CacheItemPoolInterface $cacheItemPool;
|
private CacheItemPoolInterface $cacheItemPool;
|
||||||
|
|
||||||
|
private EntityWorkflowStepRepository $workflowStepRepository;
|
||||||
|
|
||||||
public function __construct(EntityWorkflowStepRepository $workflowStepRepository, CacheItemPoolInterface $cacheItemPool)
|
public function __construct(EntityWorkflowStepRepository $workflowStepRepository, CacheItemPoolInterface $cacheItemPool)
|
||||||
{
|
{
|
||||||
$this->workflowStepRepository = $workflowStepRepository;
|
$this->workflowStepRepository = $workflowStepRepository;
|
||||||
@ -54,14 +54,21 @@ final class WorkflowByUserCounter implements NotificationCounterInterface, Event
|
|||||||
return $nb;
|
return $nb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function generateCacheKeyWorkflowByUser(User $user): string
|
||||||
|
{
|
||||||
|
return 'chill_main_workflow_by_u_' . $user->getId();
|
||||||
|
}
|
||||||
|
|
||||||
public function getCountUnreadByUser(User $user): int
|
public function getCountUnreadByUser(User $user): int
|
||||||
{
|
{
|
||||||
return $this->workflowStepRepository->countUnreadByUser($user);
|
return $this->workflowStepRepository->countUnreadByUser($user);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function generateCacheKeyWorkflowByUser(User $user): string
|
public static function getSubscribedEvents()
|
||||||
{
|
{
|
||||||
return 'chill_main_workflow_by_u_'.$user->getId();
|
return [
|
||||||
|
'workflow.leave' => 'resetWorkflowCache',
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function resetWorkflowCache(Event $event): void
|
public function resetWorkflowCache(Event $event): void
|
||||||
@ -74,11 +81,12 @@ final class WorkflowByUserCounter implements NotificationCounterInterface, Event
|
|||||||
$entityWorkflow = $event->getSubject();
|
$entityWorkflow = $event->getSubject();
|
||||||
$step = $entityWorkflow->getCurrentStep();
|
$step = $entityWorkflow->getCurrentStep();
|
||||||
|
|
||||||
if ($step === null) {
|
if (null === $step) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$keys = [];
|
$keys = [];
|
||||||
|
|
||||||
foreach ($step->getDestUser() as $user) {
|
foreach ($step->getDestUser() as $user) {
|
||||||
$keys[] = self::generateCacheKeyWorkflowByUser($user);
|
$keys[] = self::generateCacheKeyWorkflowByUser($user);
|
||||||
}
|
}
|
||||||
@ -86,15 +94,5 @@ final class WorkflowByUserCounter implements NotificationCounterInterface, Event
|
|||||||
if ([] !== $keys) {
|
if ([] !== $keys) {
|
||||||
$this->cacheItemPool->deleteItems($keys);
|
$this->cacheItemPool->deleteItems($keys);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getSubscribedEvents()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'workflow.leave' => 'resetWorkflowCache',
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -27,4 +27,4 @@ address:
|
|||||||
notification:
|
notification:
|
||||||
At least one addressee: Indiquez au moins un destinataire
|
At least one addressee: Indiquez au moins un destinataire
|
||||||
Title must be defined: Un titre doit être indiqué
|
Title must be defined: Un titre doit être indiqué
|
||||||
|
Comment content might not be blank: Le commentaire ne peut pas être vide
|
||||||
|
@ -15,6 +15,7 @@ use Chill\MainBundle\Pagination\PaginatorFactory;
|
|||||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
|
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
|
||||||
use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkRepository;
|
use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkRepository;
|
||||||
|
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodWorkVoter;
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
||||||
@ -23,7 +24,7 @@ use Symfony\Component\HttpFoundation\Request;
|
|||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\Routing\Annotation\Route;
|
use Symfony\Component\Routing\Annotation\Route;
|
||||||
use Symfony\Component\Serializer\SerializerInterface;
|
use Symfony\Component\Serializer\SerializerInterface;
|
||||||
use Symfony\Component\Translation\TranslatorInterface;
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
|
|
||||||
class AccompanyingCourseWorkController extends AbstractController
|
class AccompanyingCourseWorkController extends AbstractController
|
||||||
{
|
{
|
||||||
@ -60,7 +61,7 @@ class AccompanyingCourseWorkController extends AbstractController
|
|||||||
*/
|
*/
|
||||||
public function createWork(AccompanyingPeriod $period): Response
|
public function createWork(AccompanyingPeriod $period): Response
|
||||||
{
|
{
|
||||||
// TODO ACL
|
$this->denyAccessUnlessGranted(AccompanyingPeriodWorkVoter::CREATE, $period);
|
||||||
|
|
||||||
if ($period->getSocialIssues()->count() === 0) {
|
if ($period->getSocialIssues()->count() === 0) {
|
||||||
$this->addFlash(
|
$this->addFlash(
|
||||||
@ -93,7 +94,8 @@ class AccompanyingCourseWorkController extends AbstractController
|
|||||||
*/
|
*/
|
||||||
public function deleteWork(AccompanyingPeriodWork $work, Request $request): Response
|
public function deleteWork(AccompanyingPeriodWork $work, Request $request): Response
|
||||||
{
|
{
|
||||||
// TODO ACL
|
$this->denyAccessUnlessGranted(AccompanyingPeriodWorkVoter::UPDATE, $work);
|
||||||
|
|
||||||
$em = $this->getDoctrine()->getManager();
|
$em = $this->getDoctrine()->getManager();
|
||||||
|
|
||||||
$form = $this->createDeleteForm($work->getId());
|
$form = $this->createDeleteForm($work->getId());
|
||||||
@ -138,7 +140,8 @@ class AccompanyingCourseWorkController extends AbstractController
|
|||||||
*/
|
*/
|
||||||
public function editWork(AccompanyingPeriodWork $work): Response
|
public function editWork(AccompanyingPeriodWork $work): Response
|
||||||
{
|
{
|
||||||
// TODO ACL
|
$this->denyAccessUnlessGranted(AccompanyingPeriodWorkVoter::UPDATE, $work);
|
||||||
|
|
||||||
$json = $this->serializer->normalize($work, 'json', ['groups' => ['read']]);
|
$json = $this->serializer->normalize($work, 'json', ['groups' => ['read']]);
|
||||||
|
|
||||||
return $this->render('@ChillPerson/AccompanyingCourseWork/edit.html.twig', [
|
return $this->render('@ChillPerson/AccompanyingCourseWork/edit.html.twig', [
|
||||||
@ -157,13 +160,13 @@ class AccompanyingCourseWorkController extends AbstractController
|
|||||||
*/
|
*/
|
||||||
public function listWorkByAccompanyingPeriod(AccompanyingPeriod $period): Response
|
public function listWorkByAccompanyingPeriod(AccompanyingPeriod $period): Response
|
||||||
{
|
{
|
||||||
// TODO ACL
|
$this->denyAccessUnlessGranted(AccompanyingPeriodWorkVoter::SEE, $period);
|
||||||
|
|
||||||
$totalItems = $this->workRepository->countByAccompanyingPeriod($period);
|
$totalItems = $this->workRepository->countByAccompanyingPeriod($period);
|
||||||
$paginator = $this->paginator->create($totalItems);
|
$paginator = $this->paginator->create($totalItems);
|
||||||
|
|
||||||
$works = $this->workRepository->findByAccompanyingPeriod(
|
$works = $this->workRepository->findByAccompanyingPeriodOpenFirst(
|
||||||
$period,
|
$period,
|
||||||
['startDate' => 'DESC', 'endDate' => 'DESC'],
|
|
||||||
$paginator->getItemsPerPage(),
|
$paginator->getItemsPerPage(),
|
||||||
$paginator->getCurrentPageFirstItemNumber()
|
$paginator->getCurrentPageFirstItemNumber()
|
||||||
);
|
);
|
||||||
|
@ -44,8 +44,8 @@ class AccompanyingPeriodWork implements AccompanyingPeriodLinkedWithSocialIssues
|
|||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @ORM\ManyToOne(targetEntity=AccompanyingPeriod::class)
|
* @ORM\ManyToOne(targetEntity=AccompanyingPeriod::class)
|
||||||
* @Serializer\Groups({"read","read:accompanyingPeriodWork:light"})
|
* @Serializer\Groups({"read", "read:accompanyingPeriodWork:light"})
|
||||||
* @Serializer\Context(normalizationContext={"groups"={"read"}}, groups={"read:accompanyingPeriodWork:light"})
|
* @Serializer\Context(normalizationContext={"groups": {"read"}}, groups={"read:accompanyingPeriodWork:light"})
|
||||||
*/
|
*/
|
||||||
private ?AccompanyingPeriod $accompanyingPeriod = null;
|
private ?AccompanyingPeriod $accompanyingPeriod = null;
|
||||||
|
|
||||||
|
@ -13,7 +13,10 @@ namespace Chill\PersonBundle\Menu;
|
|||||||
|
|
||||||
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
|
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
|
||||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||||
|
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
|
||||||
|
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodWorkVoter;
|
||||||
use Knp\Menu\MenuItem;
|
use Knp\Menu\MenuItem;
|
||||||
|
use Symfony\Component\Security\Core\Security;
|
||||||
use Symfony\Component\Workflow\Registry;
|
use Symfony\Component\Workflow\Registry;
|
||||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
|
|
||||||
@ -24,15 +27,18 @@ class AccompanyingCourseMenuBuilder implements LocalMenuBuilderInterface
|
|||||||
{
|
{
|
||||||
protected Registry $registry;
|
protected Registry $registry;
|
||||||
|
|
||||||
|
protected Security $security;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var TranslatorInterface
|
* @var TranslatorInterface
|
||||||
*/
|
*/
|
||||||
protected $translator;
|
protected $translator;
|
||||||
|
|
||||||
public function __construct(TranslatorInterface $translator, Registry $registry)
|
public function __construct(TranslatorInterface $translator, Registry $registry, Security $security)
|
||||||
{
|
{
|
||||||
$this->translator = $translator;
|
$this->translator = $translator;
|
||||||
$this->registry = $registry;
|
$this->registry = $registry;
|
||||||
|
$this->security = $security;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function buildMenu($menuId, MenuItem $menu, array $parameters): void
|
public function buildMenu($menuId, MenuItem $menu, array $parameters): void
|
||||||
@ -47,38 +53,44 @@ class AccompanyingCourseMenuBuilder implements LocalMenuBuilderInterface
|
|||||||
], ])
|
], ])
|
||||||
->setExtras(['order' => 10]);
|
->setExtras(['order' => 10]);
|
||||||
|
|
||||||
$menu->addChild($this->translator->trans('Edit Accompanying Course'), [
|
if ($this->security->isGranted(AccompanyingPeriodVoter::EDIT, $period)) {
|
||||||
'route' => 'chill_person_accompanying_course_edit',
|
$menu->addChild($this->translator->trans('Edit Accompanying Course'), [
|
||||||
'routeParameters' => [
|
'route' => 'chill_person_accompanying_course_edit',
|
||||||
'accompanying_period_id' => $period->getId(),
|
'routeParameters' => [
|
||||||
], ])
|
'accompanying_period_id' => $period->getId(),
|
||||||
->setExtras(['order' => 20]);
|
], ])
|
||||||
|
->setExtras(['order' => 20]);
|
||||||
|
}
|
||||||
|
|
||||||
if (AccompanyingPeriod::STEP_DRAFT === $period->getStep()) {
|
if (AccompanyingPeriod::STEP_DRAFT === $period->getStep()) {
|
||||||
// no more menu items if the period is draft
|
// no more menu items if the period is draft
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$menu->addChild($this->translator->trans('Accompanying Course History'), [
|
if ($this->security->isGranted(AccompanyingPeriodVoter::SEE_DETAILS, $period)) {
|
||||||
'route' => 'chill_person_accompanying_course_history',
|
$menu->addChild($this->translator->trans('Accompanying Course History'), [
|
||||||
'routeParameters' => [
|
'route' => 'chill_person_accompanying_course_history',
|
||||||
'accompanying_period_id' => $period->getId(),
|
'routeParameters' => [
|
||||||
], ])
|
'accompanying_period_id' => $period->getId(),
|
||||||
->setExtras(['order' => 30]);
|
], ])
|
||||||
|
->setExtras(['order' => 30]);
|
||||||
|
|
||||||
$menu->addChild($this->translator->trans('Accompanying Course Action'), [
|
$menu->addChild($this->translator->trans('Accompanying Course Comment'), [
|
||||||
'route' => 'chill_person_accompanying_period_work_list',
|
'route' => 'chill_person_accompanying_period_comment_list',
|
||||||
'routeParameters' => [
|
'routeParameters' => [
|
||||||
'id' => $period->getId(),
|
'accompanying_period_id' => $period->getId(),
|
||||||
], ])
|
], ])
|
||||||
->setExtras(['order' => 40]);
|
->setExtras(['order' => 50]);
|
||||||
|
}
|
||||||
|
|
||||||
$menu->addChild($this->translator->trans('Accompanying Course Comment'), [
|
if ($this->security->isGranted(AccompanyingPeriodWorkVoter::SEE, $period)) {
|
||||||
'route' => 'chill_person_accompanying_period_comment_list',
|
$menu->addChild($this->translator->trans('Accompanying Course Action'), [
|
||||||
'routeParameters' => [
|
'route' => 'chill_person_accompanying_period_work_list',
|
||||||
'accompanying_period_id' => $period->getId(),
|
'routeParameters' => [
|
||||||
], ])
|
'id' => $period->getId(),
|
||||||
->setExtras(['order' => 50]);
|
], ])
|
||||||
|
->setExtras(['order' => 40]);
|
||||||
|
}
|
||||||
|
|
||||||
$workflow = $this->registry->get($period, 'accompanying_period_lifecycle');
|
$workflow = $this->registry->get($period, 'accompanying_period_lifecycle');
|
||||||
|
|
||||||
|
@ -16,17 +16,22 @@ use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
|||||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
|
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
|
||||||
use Chill\PersonBundle\Entity\SocialWork\SocialAction;
|
use Chill\PersonBundle\Entity\SocialWork\SocialAction;
|
||||||
use DateTimeImmutable;
|
use DateTimeImmutable;
|
||||||
|
use Doctrine\DBAL\Types\Types;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Doctrine\ORM\EntityRepository;
|
use Doctrine\ORM\EntityRepository;
|
||||||
|
use Doctrine\ORM\Query\ResultSetMappingBuilder;
|
||||||
use Doctrine\ORM\QueryBuilder;
|
use Doctrine\ORM\QueryBuilder;
|
||||||
use Doctrine\Persistence\ObjectRepository;
|
use Doctrine\Persistence\ObjectRepository;
|
||||||
|
|
||||||
final class AccompanyingPeriodWorkRepository implements ObjectRepository
|
final class AccompanyingPeriodWorkRepository implements ObjectRepository
|
||||||
{
|
{
|
||||||
|
private EntityManagerInterface $em;
|
||||||
|
|
||||||
private EntityRepository $repository;
|
private EntityRepository $repository;
|
||||||
|
|
||||||
public function __construct(EntityManagerInterface $entityManager)
|
public function __construct(EntityManagerInterface $entityManager)
|
||||||
{
|
{
|
||||||
|
$this->em = $entityManager;
|
||||||
$this->repository = $entityManager->getRepository(AccompanyingPeriodWork::class);
|
$this->repository = $entityManager->getRepository(AccompanyingPeriodWork::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,6 +83,36 @@ final class AccompanyingPeriodWorkRepository implements ObjectRepository
|
|||||||
return $this->repository->findByAccompanyingPeriod($period, $orderBy, $limit, $offset);
|
return $this->repository->findByAccompanyingPeriod($period, $orderBy, $limit, $offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a list of accompanying period with a defined order:.
|
||||||
|
*
|
||||||
|
* * first, opened works
|
||||||
|
* * then, closed works
|
||||||
|
*
|
||||||
|
* @return AccompanyingPeriodWork[]
|
||||||
|
*/
|
||||||
|
public function findByAccompanyingPeriodOpenFirst(AccompanyingPeriod $period, int $limit = 10, int $offset = 0): array
|
||||||
|
{
|
||||||
|
$rsm = new ResultSetMappingBuilder($this->em);
|
||||||
|
$rsm->addRootEntityFromClassMetadata(AccompanyingPeriodWork::class, 'w');
|
||||||
|
|
||||||
|
$sql = "SELECT {$rsm} FROM chill_person_accompanying_period_work w
|
||||||
|
WHERE accompanyingPeriod_id = :periodId
|
||||||
|
ORDER BY
|
||||||
|
CASE WHEN enddate IS NULL THEN '-infinity'::timestamp ELSE 'infinity'::timestamp END ASC,
|
||||||
|
startdate DESC,
|
||||||
|
enddate DESC,
|
||||||
|
id DESC
|
||||||
|
LIMIT :limit OFFSET :offset";
|
||||||
|
|
||||||
|
$nq = $this->em->createNativeQuery($sql, $rsm)
|
||||||
|
->setParameter('periodId', $period->getId(), Types::INTEGER)
|
||||||
|
->setParameter('limit', $limit, Types::INTEGER)
|
||||||
|
->setParameter('offset', $offset, Types::INTEGER);
|
||||||
|
|
||||||
|
return $nq->getResult();
|
||||||
|
}
|
||||||
|
|
||||||
public function findNearEndDateByUser(User $user, DateTimeImmutable $since, DateTimeImmutable $until, int $limit = 20, int $offset = 0): array
|
public function findNearEndDateByUser(User $user, DateTimeImmutable $since, DateTimeImmutable $until, int $limit = 20, int $offset = 0): array
|
||||||
{
|
{
|
||||||
return $this->buildQueryNearEndDateByUser($user, $since, $until)
|
return $this->buildQueryNearEndDateByUser($user, $since, $until)
|
||||||
|
@ -60,14 +60,14 @@
|
|||||||
<template v-slot:body>
|
<template v-slot:body>
|
||||||
<p>{{ $t('confirm.sure_description') }}</p>
|
<p>{{ $t('confirm.sure_description') }}</p>
|
||||||
<div v-if="accompanyingCourse.user === null">
|
<div v-if="accompanyingCourse.user === null">
|
||||||
<div v-if="filteredReferrersSuggested.length === 0">
|
<div v-if="usersSuggestedFilteredByJob.length === 0">
|
||||||
<p class="alert alert-warning">{{ $t('confirm.no_suggested_referrer') }}</p>
|
<p class="alert alert-warning">{{ $t('confirm.no_suggested_referrer') }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="filteredReferrersSuggested.length === 1" class="alert alert-info">
|
<div v-if="usersSuggestedFilteredByJob.length === 1" class="alert alert-info">
|
||||||
<p>{{ $t('confirm.one_suggested_referrer') }}:</p>
|
<p>{{ $t('confirm.one_suggested_referrer') }}:</p>
|
||||||
<ul class="list-suggest add-items inline">
|
<ul class="list-suggest add-items inline">
|
||||||
<li>
|
<li>
|
||||||
<user-render-box-badge :user="filteredReferrersSuggested[0]"></user-render-box-badge>
|
<user-render-box-badge :user="usersSuggestedFilteredByJob[0]"></user-render-box-badge>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>{{ $t('confirm.choose_suggested_referrer') }}</p>
|
<p>{{ $t('confirm.choose_suggested_referrer') }}</p>
|
||||||
@ -150,7 +150,6 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
...mapState({
|
...mapState({
|
||||||
accompanyingCourse: state => state.accompanyingCourse,
|
accompanyingCourse: state => state.accompanyingCourse,
|
||||||
filteredReferrersSuggested: state => state.filteredReferrersSuggested
|
|
||||||
}),
|
}),
|
||||||
...mapGetters([
|
...mapGetters([
|
||||||
'isParticipationValid',
|
'isParticipationValid',
|
||||||
@ -160,15 +159,16 @@ export default {
|
|||||||
'isLocationValid',
|
'isLocationValid',
|
||||||
'isJobValid',
|
'isJobValid',
|
||||||
'validationKeys',
|
'validationKeys',
|
||||||
'isValidToBeConfirmed'
|
'isValidToBeConfirmed',
|
||||||
|
'usersSuggestedFilteredByJob',
|
||||||
]),
|
]),
|
||||||
deleteLink() {
|
deleteLink() {
|
||||||
return `/fr/parcours/${this.accompanyingCourse.id}/delete`; //TODO locale
|
return `/fr/parcours/${this.accompanyingCourse.id}/delete`; //TODO locale
|
||||||
},
|
},
|
||||||
disableConfirm() {
|
disableConfirm() {
|
||||||
return this.clickedDoNotChooseReferrer
|
return (this.accompanyingCourse.user === null
|
||||||
? (this.accompanyingCourse.user === null && this.filteredReferrersSuggested.length === 0)
|
&& this.usersSuggestedFilteredByJob.length === 1
|
||||||
: (this.accompanyingCourse.user === null && this.filteredReferrersSuggested.length === 0) || (this.filteredReferrersSuggested.length === 1);
|
&& this.clickedDoNotChooseReferrer === false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@ -183,7 +183,7 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
chooseSuggestedReferrer() {
|
chooseSuggestedReferrer() {
|
||||||
this.$store.dispatch('updateReferrer', this.filteredReferrersSuggested[0])
|
this.$store.dispatch('updateReferrer', this.usersSuggestedFilteredByJob[0])
|
||||||
.catch(({name, violations}) => {
|
.catch(({name, violations}) => {
|
||||||
if (name === 'ValidationException' || name === 'AccessException') {
|
if (name === 'ValidationException' || name === 'AccessException') {
|
||||||
violations.forEach((violation) => this.$toast.open({message: violation}));
|
violations.forEach((violation) => this.$toast.open({message: violation}));
|
||||||
|
@ -1,164 +1,178 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="vue-component">
|
<div class="vue-component">
|
||||||
<h2><a id="section-80"></a>{{ $t('referrer.title') }}</h2>
|
<h2><a id="section-80"></a>{{ $t('referrer.title') }}</h2>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
|
||||||
<label class="col-form-label" for="selectJob">
|
<label class="col-form-label" for="selectJob">
|
||||||
{{ $t('job.label') }}
|
{{ $t('job.label') }}
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<VueMultiselect
|
<VueMultiselect
|
||||||
name="selectJob"
|
name="selectJob"
|
||||||
label="text"
|
label="text"
|
||||||
:custom-label="customJobLabel"
|
:custom-label="customJobLabel"
|
||||||
track-by="id"
|
track-by="id"
|
||||||
:multiple="false"
|
:multiple="false"
|
||||||
:searchable="true"
|
:searchable="true"
|
||||||
:placeholder="$t('job.placeholder')"
|
:placeholder="$t('job.placeholder')"
|
||||||
v-model="valueJob"
|
v-model="valueJob"
|
||||||
:options="jobs"
|
:options="jobs"
|
||||||
:select-label="$t('multiselect.select_label')"
|
:select-label="$t('multiselect.select_label')"
|
||||||
:deselect-label="$t('multiselect.deselect_label')"
|
:deselect-label="$t('multiselect.deselect_label')"
|
||||||
:selected-label="$t('multiselect.selected_label')"
|
:selected-label="$t('multiselect.selected_label')"
|
||||||
@select="updateJob">
|
></VueMultiselect>
|
||||||
</VueMultiselect>
|
|
||||||
|
|
||||||
<label class="col-form-label" for="selectReferrer">
|
<label class="col-form-label" for="selectReferrer">
|
||||||
{{ $t('referrer.label') }}
|
{{ $t('referrer.label') }}
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<VueMultiselect
|
<VueMultiselect
|
||||||
name="selectReferrer"
|
name="selectReferrer"
|
||||||
label="text"
|
label="text"
|
||||||
track-by="id"
|
track-by="id"
|
||||||
:multiple="false"
|
:multiple="false"
|
||||||
:searchable="true"
|
:searchable="true"
|
||||||
:placeholder="$t('referrer.placeholder')"
|
:placeholder="$t('referrer.placeholder')"
|
||||||
v-model="value"
|
v-model="value"
|
||||||
:options="users"
|
:options="users"
|
||||||
:select-label="$t('multiselect.select_label')"
|
:select-label="$t('multiselect.select_label')"
|
||||||
:deselect-label="$t('multiselect.deselect_label')"
|
:deselect-label="$t('multiselect.deselect_label')"
|
||||||
:selected-label="$t('multiselect.selected_label')"
|
:selected-label="$t('multiselect.selected_label')"
|
||||||
@select="updateReferrer">
|
></VueMultiselect>
|
||||||
</VueMultiselect>
|
|
||||||
|
|
||||||
<template v-if="filteredReferrersSuggested.length > 0">
|
<template v-if="usersSuggestedFilteredByJob.length > 0">
|
||||||
<ul class="list-suggest add-items inline">
|
<ul class="list-suggest add-items inline">
|
||||||
<li v-for="(u, i) in filteredReferrersSuggested" @click="updateReferrer(u)" :key="`referrer-${i}`">
|
<li v-for="(u, i) in usersSuggestedFilteredByJob" @click="updateReferrer(u)" :key="`referrer-${i}`">
|
||||||
<span>
|
<span>
|
||||||
<user-render-box-badge :user="u"></user-render-box-badge>
|
<user-render-box-badge :user="u"></user-render-box-badge>
|
||||||
</span>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<ul class="record_actions">
|
<ul class="record_actions">
|
||||||
<li>
|
<li>
|
||||||
<button
|
<button
|
||||||
class="btn btn-create"
|
class="btn btn-create"
|
||||||
type="button"
|
type="button"
|
||||||
name="button"
|
name="button"
|
||||||
@click="assignMe">
|
@click="assignMe">
|
||||||
{{ $t('referrer.assign_me') }}
|
{{ $t('referrer.assign_me') }}
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="!isJobValid" class="alert alert-warning to-confirm">
|
<div v-if="!isJobValid" class="alert alert-warning to-confirm">
|
||||||
{{ $t('job.not_valid') }}
|
{{ $t('job.not_valid') }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import VueMultiselect from 'vue-multiselect';
|
import VueMultiselect from 'vue-multiselect';
|
||||||
import { makeFetch } from 'ChillMainAssets/lib/api/apiMethods';
|
import {makeFetch} from 'ChillMainAssets/lib/api/apiMethods';
|
||||||
import { mapState, mapGetters } from 'vuex';
|
import {mapState, mapGetters} from 'vuex';
|
||||||
import UserRenderBoxBadge from "ChillMainAssets/vuejs/_components/Entity/UserRenderBoxBadge";
|
import UserRenderBoxBadge from "ChillMainAssets/vuejs/_components/Entity/UserRenderBoxBadge";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "Referrer",
|
name: "Referrer",
|
||||||
components: {
|
components: {
|
||||||
UserRenderBoxBadge,
|
UserRenderBoxBadge,
|
||||||
VueMultiselect,
|
VueMultiselect,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
jobs: []
|
jobs: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState({
|
||||||
|
valueJob: state => state.accompanyingCourse.job,
|
||||||
|
}),
|
||||||
|
...mapGetters(['isJobValid', 'usersSuggestedFilteredByJob']),
|
||||||
|
users: function () {
|
||||||
|
let users = this.$store.getters.usersFilteredByJob;
|
||||||
|
|
||||||
|
console.log('users filtered by job', users);
|
||||||
|
|
||||||
|
// ensure that the selected user is in the list. add it if necessary
|
||||||
|
if (this.$store.state.accompanyingCourse.user !== null && users.find(u => this.$store.state.accompanyingCourse.user.id === u.id) === undefined) {
|
||||||
|
console.log('add user to users');
|
||||||
|
users.push(this.$store.state.accompanyingCourse.user);
|
||||||
}
|
}
|
||||||
},
|
|
||||||
computed: {
|
console.log('users to return', users);
|
||||||
...mapState({
|
return users;
|
||||||
value: state => state.accompanyingCourse.user,
|
},
|
||||||
valueJob: state => state.accompanyingCourse.job,
|
valueJob: {
|
||||||
users: state => state.users.filter(u => {
|
get() {
|
||||||
if (u.user_job && state.accompanyingCourse.job) {
|
return this.$store.state.accompanyingCourse.job;
|
||||||
return u.user_job.id === state.accompanyingCourse.job.id;
|
},
|
||||||
} else {
|
set(value) {
|
||||||
return false;
|
this.$store.dispatch('updateJob', value)
|
||||||
}
|
|
||||||
}),
|
|
||||||
filteredReferrersSuggested: state => state.filteredReferrersSuggested,
|
|
||||||
}),
|
|
||||||
...mapGetters([
|
|
||||||
'isJobValid'
|
|
||||||
])
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.getJobs();
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
updateReferrer(value) {
|
|
||||||
this.$store.dispatch('updateReferrer', value)
|
|
||||||
.catch(({name, violations}) => {
|
.catch(({name, violations}) => {
|
||||||
if (name === 'ValidationException' || name === 'AccessException') {
|
if (name === 'ValidationException' || name === 'AccessException') {
|
||||||
violations.forEach((violation) => this.$toast.open({message: violation}));
|
violations.forEach((violation) => this.$toast.open({message: violation}));
|
||||||
} else {
|
} else {
|
||||||
this.$toast.open({message: 'An error occurred'})
|
this.$toast.open({message: 'An error occurred'})
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
|
||||||
getJobs() {
|
|
||||||
const url = '/api/1.0/main/user-job.json';
|
|
||||||
makeFetch('GET', url)
|
|
||||||
.then(response => {
|
|
||||||
this.jobs = response.results;
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
this.$toast.open({message: error.txt})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
updateJob(value) {
|
|
||||||
this.$store.dispatch('updateJob', value)
|
|
||||||
.catch(({name, violations}) => {
|
|
||||||
if (name === 'ValidationException' || name === 'AccessException') {
|
|
||||||
violations.forEach((violation) => this.$toast.open({message: violation}));
|
|
||||||
} else {
|
|
||||||
this.$toast.open({message: 'An error occurred'})
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
customJobLabel(value) {
|
|
||||||
return value.label.fr;
|
|
||||||
},
|
|
||||||
assignMe() {
|
|
||||||
const url = `/api/1.0/main/whoami.json`;
|
|
||||||
makeFetch('GET', url)
|
|
||||||
.then(response => {
|
|
||||||
this.$store.dispatch('updateReferrer', response);
|
|
||||||
return response;
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
commit('catchError', error);
|
|
||||||
this.$toast.open({message: error.body})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
value: {
|
||||||
|
get() {
|
||||||
|
return this.$store.state.accompanyingCourse.user;
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
console.log('set referrer', value);
|
||||||
|
this.$store.dispatch('updateReferrer', value)
|
||||||
|
.catch(({name, violations}) => {
|
||||||
|
if (name === 'ValidationException' || name === 'AccessException') {
|
||||||
|
violations.forEach((violation) => this.$toast.open({message: violation}));
|
||||||
|
} else {
|
||||||
|
this.$toast.open({message: 'An error occurred'})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.getJobs();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
updateReferrer(value) {
|
||||||
|
this.value = value;
|
||||||
|
},
|
||||||
|
getJobs() {
|
||||||
|
const url = '/api/1.0/main/user-job.json';
|
||||||
|
makeFetch('GET', url)
|
||||||
|
.then(response => {
|
||||||
|
this.jobs = response.results;
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
this.$toast.open({message: error.txt})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
customJobLabel(value) {
|
||||||
|
return value.label.fr;
|
||||||
|
},
|
||||||
|
assignMe() {
|
||||||
|
const url = `/api/1.0/main/whoami.json`;
|
||||||
|
makeFetch('GET', url)
|
||||||
|
.then(user => {
|
||||||
|
this.value = user
|
||||||
|
})
|
||||||
|
/*.catch((error) => {
|
||||||
|
commit('catchError', error);
|
||||||
|
this.$toast.open({message: error.body})
|
||||||
|
})*/
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -140,7 +140,7 @@ const appMessages = {
|
|||||||
sure_description: "Une fois le changement confirmé, il ne sera plus possible de le remettre à l'état de brouillon !",
|
sure_description: "Une fois le changement confirmé, il ne sera plus possible de le remettre à l'état de brouillon !",
|
||||||
ok: "Confirmer le parcours",
|
ok: "Confirmer le parcours",
|
||||||
delete: "Supprimer le parcours",
|
delete: "Supprimer le parcours",
|
||||||
no_suggested_referrer: "Il n'y a aucun référent qui puisse être désigné pour ce parcours. Vérifiez la localisation du parcours, les métiers et service indiqués. Si le problème persiste, contactez l'administrateur du logiciel.",
|
no_suggested_referrer: "Il n'y a aucun référent qui puisse être suggéré pour ce parcours. Vérifiez la localisation du parcours, les métiers et service indiqués. Si les données sont correctes, vous pouvez confirmer ce parcours.",
|
||||||
one_suggested_referrer: "Un unique référent peut être suggéré pour ce parcours",
|
one_suggested_referrer: "Un unique référent peut être suggéré pour ce parcours",
|
||||||
choose_suggested_referrer: "Voulez-vous le désigner directement ?",
|
choose_suggested_referrer: "Voulez-vous le désigner directement ?",
|
||||||
choose_button: "Désigner",
|
choose_button: "Désigner",
|
||||||
|
@ -40,7 +40,6 @@ let initPromise = (root) => Promise.all([getScopesPromise(root), accompanyingCou
|
|||||||
scopesAtBackend: accompanyingCourse.scopes.map(scope => scope),
|
scopesAtBackend: accompanyingCourse.scopes.map(scope => scope),
|
||||||
// the users which are available for referrer
|
// the users which are available for referrer
|
||||||
referrersSuggested: [],
|
referrersSuggested: [],
|
||||||
filteredReferrersSuggested: [],
|
|
||||||
// all the users available
|
// all the users available
|
||||||
users: [],
|
users: [],
|
||||||
permissions: {}
|
permissions: {}
|
||||||
@ -93,6 +92,30 @@ let initPromise = (root) => Promise.all([getScopesPromise(root), accompanyingCou
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
usersFilteredByJob(state) {
|
||||||
|
return state.users.filter(u => {
|
||||||
|
if (null !== state.accompanyingCourse.job) {
|
||||||
|
return (u.user_job === null ? null : u.user_job.id) === state.accompanyingCourse.job.id;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
usersSuggestedFilteredByJob(state) {
|
||||||
|
return state.referrersSuggested.filter(u => {
|
||||||
|
if (null !== state.accompanyingCourse.job) {
|
||||||
|
return (u.user_job === null ? null : u.user_job.id) === state.accompanyingCourse.job.id;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}).filter(u => {
|
||||||
|
if (null !== state.accompanyingCourse.user) {
|
||||||
|
return u.id !== state.accompanyingCourse.user.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
},
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
catchError(state, error) {
|
catchError(state, error) {
|
||||||
@ -216,22 +239,6 @@ let initPromise = (root) => Promise.all([getScopesPromise(root), accompanyingCou
|
|||||||
return u;
|
return u;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
setFilteredReferrersSuggested(state) {
|
|
||||||
state.filteredReferrersSuggested = state.referrersSuggested.filter(u => {
|
|
||||||
if (u.user_job && state.accompanyingCourse.job && state.accompanyingCourse.user) {
|
|
||||||
return u.user_job.id === state.accompanyingCourse.job.id && state.accompanyingCourse.user.id !== u.id
|
|
||||||
} else {
|
|
||||||
if (null === state.accompanyingCourse.user) {
|
|
||||||
if (u.user_job && state.accompanyingCourse.job) {
|
|
||||||
return u.user_job.id === state.accompanyingCourse.job.id
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return state.accompanyingCourse.user.id !== u.id;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
confirmAccompanyingCourse(state, response) {
|
confirmAccompanyingCourse(state, response) {
|
||||||
//console.log('### mutation: confirmAccompanyingCourse: response', response);
|
//console.log('### mutation: confirmAccompanyingCourse: response', response);
|
||||||
state.accompanyingCourse.step = response.step;
|
state.accompanyingCourse.step = response.step;
|
||||||
@ -689,8 +696,14 @@ let initPromise = (root) => Promise.all([getScopesPromise(root), accompanyingCou
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
updateReferrer({ commit }, payload) {
|
updateReferrer({ commit }, payload) {
|
||||||
|
console.log('update referrer', payload);
|
||||||
|
console.log('payload !== null', payload !== null);
|
||||||
const url = `/api/1.0/person/accompanying-course/${id}.json`;
|
const url = `/api/1.0/person/accompanying-course/${id}.json`;
|
||||||
const body = { type: "accompanying_period", user: { id: payload.id, type: payload.type }};
|
let body = { type: "accompanying_period", user: null };
|
||||||
|
|
||||||
|
if (payload !== null) {
|
||||||
|
body = { type: "accompanying_period", user: { id: payload.id, type: payload.type } };
|
||||||
|
}
|
||||||
|
|
||||||
return makeFetch('PATCH', url, body)
|
return makeFetch('PATCH', url, body)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
@ -704,12 +717,15 @@ let initPromise = (root) => Promise.all([getScopesPromise(root), accompanyingCou
|
|||||||
},
|
},
|
||||||
updateJob({ commit }, payload) {
|
updateJob({ commit }, payload) {
|
||||||
const url = `/api/1.0/person/accompanying-course/${id}.json`;
|
const url = `/api/1.0/person/accompanying-course/${id}.json`;
|
||||||
const body = { type: "accompanying_period", job: { id: payload.id, type: payload.type }};
|
let body = { type: "accompanying_period", job: null };
|
||||||
|
|
||||||
|
if (payload !== null) {
|
||||||
|
body = { type: "accompanying_period", job: { id: payload.id, type: payload.type } };
|
||||||
|
}
|
||||||
|
|
||||||
return makeFetch('PATCH', url, body)
|
return makeFetch('PATCH', url, body)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
commit('updateJob', response.job);
|
commit('updateJob', response.job);
|
||||||
commit('setFilteredReferrersSuggested');
|
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
commit('catchError', error);
|
commit('catchError', error);
|
||||||
@ -734,7 +750,6 @@ let initPromise = (root) => Promise.all([getScopesPromise(root), accompanyingCou
|
|||||||
async fetchReferrersSuggested({ state, commit}) {
|
async fetchReferrersSuggested({ state, commit}) {
|
||||||
let users = await getReferrersSuggested(state.accompanyingCourse);
|
let users = await getReferrersSuggested(state.accompanyingCourse);
|
||||||
commit('setReferrersSuggested', users);
|
commit('setReferrersSuggested', users);
|
||||||
commit('setFilteredReferrersSuggested');
|
|
||||||
if (
|
if (
|
||||||
null === state.accompanyingCourse.user
|
null === state.accompanyingCourse.user
|
||||||
&& !state.accompanyingCourse.confidential
|
&& !state.accompanyingCourse.confidential
|
||||||
|
@ -23,6 +23,8 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{{ chill_pagination(paginator) }}
|
||||||
|
|
||||||
<ul class="record_actions sticky-form-buttons">
|
<ul class="record_actions sticky-form-buttons">
|
||||||
<li>
|
<li>
|
||||||
<a href="{{ chill_path_add_return_path('chill_person_accompanying_period_work_new', { 'id': accompanyingCourse.id }) }}"
|
<a href="{{ chill_path_add_return_path('chill_person_accompanying_period_work_new', { 'id': accompanyingCourse.id }) }}"
|
||||||
|
@ -41,7 +41,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="wh-col">
|
<div class="wh-col" style="align-items: center;">
|
||||||
{% if chill_accompanying_periods.fields.user == 'visible' %}
|
{% if chill_accompanying_periods.fields.user == 'visible' %}
|
||||||
{# the tags `data-referrer-text` is used by module `@ChillPerson/mod/AccompanyingPeriod/setReferrer.js` #}
|
{# the tags `data-referrer-text` is used by module `@ChillPerson/mod/AccompanyingPeriod/setReferrer.js` #}
|
||||||
{% if period.user %}
|
{% if period.user %}
|
||||||
|
@ -11,17 +11,23 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Chill\PersonBundle\Security\Authorization;
|
namespace Chill\PersonBundle\Security\Authorization;
|
||||||
|
|
||||||
|
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
|
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
|
||||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||||
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
||||||
use Symfony\Component\Security\Core\Security;
|
use Symfony\Component\Security\Core\Security;
|
||||||
use UnexpectedValueException;
|
use UnexpectedValueException;
|
||||||
|
use function get_class;
|
||||||
use function in_array;
|
use function in_array;
|
||||||
|
|
||||||
class AccompanyingPeriodWorkVoter extends Voter
|
class AccompanyingPeriodWorkVoter extends Voter
|
||||||
{
|
{
|
||||||
|
public const CREATE = 'CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_CREATE';
|
||||||
|
|
||||||
public const SEE = 'CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_SEE';
|
public const SEE = 'CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_SEE';
|
||||||
|
|
||||||
|
public const UPDATE = 'CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_UPDATE';
|
||||||
|
|
||||||
private Security $security;
|
private Security $security;
|
||||||
|
|
||||||
public function __construct(Security $security)
|
public function __construct(Security $security)
|
||||||
@ -31,8 +37,14 @@ class AccompanyingPeriodWorkVoter extends Voter
|
|||||||
|
|
||||||
protected function supports($attribute, $subject): bool
|
protected function supports($attribute, $subject): bool
|
||||||
{
|
{
|
||||||
return $subject instanceof AccompanyingPeriodWork
|
return
|
||||||
&& in_array($attribute, $this->getRoles(), true);
|
(
|
||||||
|
$subject instanceof AccompanyingPeriodWork
|
||||||
|
&& in_array($attribute, $this->getRoles(), true)
|
||||||
|
) || (
|
||||||
|
$subject instanceof AccompanyingPeriod
|
||||||
|
&& in_array($attribute, [self::SEE, self::CREATE], true)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -41,13 +53,28 @@ class AccompanyingPeriodWorkVoter extends Voter
|
|||||||
*/
|
*/
|
||||||
protected function voteOnAttribute($attribute, $subject, TokenInterface $token): bool
|
protected function voteOnAttribute($attribute, $subject, TokenInterface $token): bool
|
||||||
{
|
{
|
||||||
switch ($attribute) {
|
if ($subject instanceof AccompanyingPeriodWork) {
|
||||||
case self::SEE:
|
switch ($attribute) {
|
||||||
return $this->security->isGranted(AccompanyingPeriodVoter::SEE, $subject->getAccompanyingPeriod());
|
case self::SEE:
|
||||||
|
return $this->security->isGranted(AccompanyingPeriodVoter::SEE_DETAILS, $subject->getAccompanyingPeriod());
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new UnexpectedValueException("attribute {$attribute} is not supported");
|
throw new UnexpectedValueException("attribute {$attribute} is not supported");
|
||||||
|
}
|
||||||
|
} elseif ($subject instanceof AccompanyingPeriod) {
|
||||||
|
switch ($attribute) {
|
||||||
|
case self::SEE:
|
||||||
|
return $this->security->isGranted(AccompanyingPeriodVoter::SEE_DETAILS, $subject);
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new UnexpectedValueException(sprintf(
|
||||||
|
"attribute {$attribute} is not supported on instance %s",
|
||||||
|
AccompanyingPeriod::class
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
throw new UnexpectedValueException(sprintf("attribute {$attribute} on instance %s is not supported", get_class($subject)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getRoles(): array
|
private function getRoles(): array
|
||||||
|
@ -36,7 +36,7 @@ class HouseholdMembershipSequentialValidator extends ConstraintValidator
|
|||||||
public function validate($person, Constraint $constraint)
|
public function validate($person, Constraint $constraint)
|
||||||
{
|
{
|
||||||
if (!$person instanceof Person) {
|
if (!$person instanceof Person) {
|
||||||
throw new UnexpectedTypeException($constraint, Person::class);
|
throw new UnexpectedTypeException($person, Person::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
$participations = $person->getHouseholdParticipationsShareHousehold();
|
$participations = $person->getHouseholdParticipationsShareHousehold();
|
||||||
|
@ -211,7 +211,7 @@ No requestor: Pas de demandeur
|
|||||||
No resources: "Pas d'interlocuteurs privilégiés"
|
No resources: "Pas d'interlocuteurs privilégiés"
|
||||||
Persons associated: Usagers concernés
|
Persons associated: Usagers concernés
|
||||||
Referrer: Référent
|
Referrer: Référent
|
||||||
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 appartenance à un ménage dès que possible.
|
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
|
Add to household now: Ajouter à un ménage
|
||||||
Any resource for this accompanying course: Aucun interlocuteur privilégié pour ce parcours
|
Any resource for this accompanying course: Aucun interlocuteur privilégié pour ce parcours
|
||||||
course.draft: Brouillon
|
course.draft: Brouillon
|
||||||
@ -557,7 +557,7 @@ household_composition:
|
|||||||
numberOfChildren: Nombre d'enfants mineurs au sein du ménage
|
numberOfChildren: Nombre d'enfants mineurs au sein du ménage
|
||||||
Household composition: Composition du ménage
|
Household composition: Composition du ménage
|
||||||
Composition added: Information sur la composition familiale ajoutée
|
Composition added: Information sur la composition familiale ajoutée
|
||||||
Currently no composition: Aucune composition famiale renseignée.
|
Currently no composition: Aucune composition familiale renseignée.
|
||||||
Add a composition: Ajouter une composition familiale
|
Add a composition: Ajouter une composition familiale
|
||||||
Update composition: Modifier la composition familiale
|
Update composition: Modifier la composition familiale
|
||||||
|
|
||||||
|
@ -17,8 +17,8 @@ use DateTimeImmutable;
|
|||||||
use Doctrine\Common\Collections\ArrayCollection;
|
use Doctrine\Common\Collections\ArrayCollection;
|
||||||
use Doctrine\Common\Collections\Collection;
|
use Doctrine\Common\Collections\Collection;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
use Symfony\Component\Validator\Constraints as Assert;
|
|
||||||
use Symfony\Component\Serializer\Annotation as Serializer;
|
use Symfony\Component\Serializer\Annotation as Serializer;
|
||||||
|
use Symfony\Component\Validator\Constraints as Assert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SingleTask.
|
* SingleTask.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user