mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 18:44:08 +00:00
Merge remote-tracking branch 'origin/master' into workflow/fix-sending-notifications
This commit is contained in:
commit
1fda56b5f7
16
CHANGELOG.md
16
CHANGELOG.md
@ -12,6 +12,18 @@ and this project adheres to
|
||||
|
||||
<!-- write down unreleased development here -->
|
||||
* workflow: fix sending notifications
|
||||
* [thirdparty] Extend the thirdparty search to thirdparty children (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/448)
|
||||
* [person]: AddPersons: allow creation of person or thirdparty only (no users) (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/422)
|
||||
* [person]: AddPersons: allow creation of person or thirdparty depending on allowed types (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/422)
|
||||
* [person]: AddPersons: add suggestion of name when creating new person or thirdparty (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/422)
|
||||
* [main] Address: fix small bug: when modifying an address without street (isNoAddress), also check errors if street is an empty string as back-end change null value to empty string for street (and streetNumber)
|
||||
* [main] Address: stronger client-side validation of addresses (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/449)
|
||||
* [person] accompanying course: filter suggested entities by open participations (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/415)
|
||||
[activity] can click through the cross icon for removing person in concerned group (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/476)
|
||||
[activity] correct associated persons by considering only open participations (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/476)
|
||||
* [person_resources]: Renderboxes used to display person/thirdparty info (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/465)
|
||||
* [Household]: Add end date in HouseholdMember form for 'enfant hors menage' (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/434)
|
||||
* [homepage_widget]: If no sender then display as 'notification automatique' (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/435)
|
||||
|
||||
## Test releases
|
||||
|
||||
@ -23,7 +35,7 @@ and this project adheres to
|
||||
* [person] accompanying course work: fix on-the-fly update of thirdParty
|
||||
* fix normalisation of accompanying course requestor api (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/378)
|
||||
* [person] add a returnPath when clicking on some Person or ThirdParty badge (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/427)
|
||||
* [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
|
||||
* [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
|
||||
@ -39,7 +51,7 @@ and this project adheres to
|
||||
* [address]: Correction residential address 'depuis le' (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/459)
|
||||
* [Documents]: List view adapted to display more information (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/414)
|
||||
* [Thirdparty_contact]: address blurred if confidential in view page (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/450)
|
||||
* [homepage_widget]: If no sender then display as 'notification automatique' (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/435)
|
||||
|
||||
|
||||
### test release 2021-02-01
|
||||
|
||||
|
@ -357,7 +357,7 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
|
||||
if (null !== $this->accompanyingPeriod) {
|
||||
$personsAssociated = [];
|
||||
|
||||
foreach ($this->accompanyingPeriod->getParticipations() as $participation) {
|
||||
foreach ($this->accompanyingPeriod->getOpenParticipations() as $participation) {
|
||||
if ($this->persons->contains($participation->getPerson())) {
|
||||
$personsAssociated[] = $participation->getPerson();
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<li>
|
||||
<span :title="person.text">
|
||||
<span class="chill_denomination" @click.prevent="$emit('remove', person)">
|
||||
<span :title="person.text" @click.prevent="$emit('remove', person)">
|
||||
<span class="chill_denomination">
|
||||
<person-text :person="person" :isCut="true"></person-text>
|
||||
</span>
|
||||
</span>
|
||||
|
@ -14,19 +14,46 @@ namespace Chill\DocStoreBundle\Workflow;
|
||||
use Chill\DocStoreBundle\Entity\AccompanyingCourseDocument;
|
||||
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
|
||||
use Chill\MainBundle\Workflow\EntityWorkflowHandlerInterface;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
class AccompanyingCourseDocumentWorkflowHandler implements EntityWorkflowHandlerInterface
|
||||
{
|
||||
private EntityRepository $repository;
|
||||
|
||||
private TranslatorInterface $translator;
|
||||
|
||||
/**
|
||||
* TODO: injecter le repository directement.
|
||||
*/
|
||||
public function __construct(EntityManagerInterface $em)
|
||||
public function __construct(EntityManagerInterface $em, TranslatorInterface $translator)
|
||||
{
|
||||
$this->repository = $em->getRepository(AccompanyingCourseDocument::class);
|
||||
$this->translator = $translator;
|
||||
}
|
||||
|
||||
public function getEntityData(EntityWorkflow $entityWorkflow, array $options = []): array
|
||||
{
|
||||
$course = $this->getRelatedEntity($entityWorkflow)
|
||||
->getCourse();
|
||||
$persons = [];
|
||||
|
||||
if (null !== $course) {
|
||||
$persons = $course->getCurrentParticipations()->map(static function (AccompanyingPeriodParticipation $participation) {
|
||||
return $participation->getPerson();
|
||||
})->toArray();
|
||||
}
|
||||
|
||||
return [
|
||||
'persons' => array_values($persons),
|
||||
];
|
||||
}
|
||||
|
||||
public function getEntityTitle(EntityWorkflow $entityWorkflow, array $options = []): string
|
||||
{
|
||||
return $this->translator->trans('workflow.Document (n°%doc%)', ['%doc%' => $entityWorkflow->getRelatedEntityId()]);
|
||||
}
|
||||
|
||||
public function getRelatedEntity(EntityWorkflow $entityWorkflow): ?AccompanyingCourseDocument
|
||||
|
@ -11,7 +11,12 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\MainBundle\Controller;
|
||||
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
|
||||
use Chill\MainBundle\Pagination\PaginatorFactory;
|
||||
use Chill\MainBundle\Repository\Workflow\EntityWorkflowRepository;
|
||||
use Chill\MainBundle\Serializer\Model\Collection;
|
||||
use Chill\MainBundle\Serializer\Model\Counter;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use LogicException;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
@ -21,17 +26,71 @@ use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
use Symfony\Component\Serializer\SerializerInterface;
|
||||
|
||||
class WorkflowApiController
|
||||
{
|
||||
private EntityManagerInterface $entityManager;
|
||||
|
||||
private EntityWorkflowRepository $entityWorkflowRepository;
|
||||
|
||||
private PaginatorFactory $paginatorFactory;
|
||||
|
||||
private Security $security;
|
||||
|
||||
public function __construct(Security $security, EntityManagerInterface $entityManager)
|
||||
{
|
||||
private SerializerInterface $serializer;
|
||||
|
||||
public function __construct(
|
||||
EntityManagerInterface $entityManager,
|
||||
EntityWorkflowRepository $entityWorkflowRepository,
|
||||
PaginatorFactory $paginatorFactory,
|
||||
Security $security,
|
||||
SerializerInterface $serializer
|
||||
) {
|
||||
$this->entityManager = $entityManager;
|
||||
$this->entityWorkflowRepository = $entityWorkflowRepository;
|
||||
$this->paginatorFactory = $paginatorFactory;
|
||||
$this->security = $security;
|
||||
$this->serializer = $serializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of workflow which are waiting an action for the user.
|
||||
*
|
||||
* @Route("/api/1.0/main/workflow/my", methods={"GET"})
|
||||
*/
|
||||
public function myWorkflow(Request $request): JsonResponse
|
||||
{
|
||||
if (!$this->security->isGranted('ROLE_USER') || !$this->security->getUser() instanceof User) {
|
||||
throw new AccessDeniedException();
|
||||
}
|
||||
|
||||
$total = $this->entityWorkflowRepository->countByDest($this->security->getUser());
|
||||
|
||||
if ($request->query->getBoolean('countOnly', false)) {
|
||||
return new JsonResponse(
|
||||
$this->serializer->serialize(new Counter($total), 'json'),
|
||||
JsonResponse::HTTP_OK,
|
||||
[],
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
$paginator = $this->paginatorFactory->create($total);
|
||||
|
||||
$workflows = $this->entityWorkflowRepository->findByDest(
|
||||
$this->security->getUser(),
|
||||
['id' => 'DESC'],
|
||||
$paginator->getItemsPerPage(),
|
||||
$paginator->getCurrentPageFirstItemNumber()
|
||||
);
|
||||
|
||||
return new JsonResponse(
|
||||
$this->serializer->serialize(new Collection($workflows, $paginator), 'json', ['groups' => ['read']]),
|
||||
JsonResponse::HTTP_OK,
|
||||
[],
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -139,12 +139,10 @@ class Address
|
||||
private $point;
|
||||
|
||||
/**
|
||||
* @var PostalCode
|
||||
*
|
||||
* @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\PostalCode")
|
||||
* @Groups({"write"})
|
||||
*/
|
||||
private $postcode;
|
||||
private ?PostalCode $postcode;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
|
@ -260,8 +260,7 @@ export default {
|
||||
editPane: false,
|
||||
datePane: false,
|
||||
loading: false,
|
||||
success: false,
|
||||
dirty: false
|
||||
success: false
|
||||
},
|
||||
errors: [],
|
||||
defaultz: {
|
||||
@ -537,17 +536,19 @@ export default {
|
||||
|
||||
checkErrors() {
|
||||
this.errors = [];
|
||||
if (this.flag.dirty) {
|
||||
if (this.entity.selected.country === null) {
|
||||
this.errors.push("Un pays doit être sélectionné.");
|
||||
}
|
||||
if (this.entity.selected.country === null) {
|
||||
this.errors.push("Un pays doit être sélectionné.");
|
||||
}
|
||||
if (this.entity.selected.city === null) {
|
||||
this.errors.push("Une ville doit être sélectionnée.");
|
||||
} else {
|
||||
if (Object.keys(this.entity.selected.city).length === 0) {
|
||||
this.errors.push("Une ville doit être sélectionnée.");
|
||||
}
|
||||
if (!this.entity.selected.isNoAddress) {
|
||||
if (this.entity.selected.address.street === null || this.entity.selected.address.streetNumber === null) {
|
||||
this.errors.push("Une adresse doit être sélectionnée.");
|
||||
}
|
||||
}
|
||||
if (!this.entity.selected.isNoAddress) {
|
||||
if (this.entity.selected.address.street === null || this.entity.selected.address.street === '' || this.entity.selected.address.streetNumber === null || this.entity.selected.address.streetNumber === '') {
|
||||
this.errors.push("Une adresse doit être sélectionnée.");
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -567,7 +568,7 @@ export default {
|
||||
|
||||
this.entity.selected.country = this.context.edit ? this.entity.address.country : {};
|
||||
this.entity.selected.postcode = this.context.edit ? this.entity.address.postcode : {};
|
||||
this.entity.selected.city = {};
|
||||
this.entity.selected.city = this.context.edit ? this.entity.address.postcode : {};
|
||||
|
||||
this.entity.selected.address = {};
|
||||
this.entity.selected.address.street = this.context.edit ? this.entity.address.street: null;
|
||||
@ -583,6 +584,8 @@ export default {
|
||||
this.entity.selected.writeNew.address = this.context.edit && this.entity.address.addressReference === null && this.entity.address.street.length > 0
|
||||
this.entity.selected.writeNew.postcode = false // NB: this used to be this.context.edit, but think it was erroneous;
|
||||
console.log('!! just set writeNew.postcode to', this.entity.selected.writeNew.postcode);
|
||||
|
||||
this.checkErrors();
|
||||
},
|
||||
|
||||
/*
|
||||
|
@ -111,11 +111,9 @@ export default {
|
||||
this.entity.selected.address.streetNumber = value.streetNumber;
|
||||
this.entity.selected.writeNew.address = false;
|
||||
this.updateMapCenter(value.point);
|
||||
this.flag.dirty = true;
|
||||
this.checkErrors();
|
||||
},
|
||||
remove() {
|
||||
this.flag.dirty = true;
|
||||
this.entity.selected.address = {};
|
||||
this.checkErrors();
|
||||
},
|
||||
@ -158,7 +156,6 @@ export default {
|
||||
this.entity.selected.address.street = addr.street;
|
||||
this.entity.selected.address.streetNumber = addr.number;
|
||||
this.entity.selected.writeNew.address = true;
|
||||
this.flag.dirty = true;
|
||||
this.checkErrors();
|
||||
}
|
||||
},
|
||||
|
@ -125,11 +125,9 @@ export default {
|
||||
if (value.center) {
|
||||
this.updateMapCenter(value.center);
|
||||
}
|
||||
this.flag.dirty = true;
|
||||
this.checkErrors();
|
||||
},
|
||||
remove() {
|
||||
this.flag.dirty = true;
|
||||
this.entity.selected.city = {};
|
||||
this.checkErrors();
|
||||
},
|
||||
|
@ -51,7 +51,6 @@ export default {
|
||||
init() {
|
||||
if (this.value !== undefined) {
|
||||
this.selectCountry(this.value);
|
||||
this.flag.dirty = false;
|
||||
}
|
||||
},
|
||||
selectCountryByCode(countryCode) {
|
||||
@ -67,7 +66,6 @@ export default {
|
||||
this.checkErrors();
|
||||
},
|
||||
remove() {
|
||||
this.flag.dirty = true;
|
||||
this.entity.selected.country = null;
|
||||
this.checkErrors();
|
||||
},
|
||||
|
@ -157,6 +157,7 @@ export default {
|
||||
set(value) {
|
||||
console.log('isNoAddress value', value);
|
||||
this.entity.selected.isNoAddress = value;
|
||||
this.checkErrors();
|
||||
},
|
||||
get() {
|
||||
return this.entity.selected.isNoAddress;
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
|
||||
|
||||
<h2>{{ $t('main_title') }}</h2>
|
||||
|
||||
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link"
|
||||
@ -23,7 +23,6 @@
|
||||
:class="{'active': activeTab === 'MyAccompanyingCourses'}"
|
||||
@click="selectTab('MyAccompanyingCourses')">
|
||||
{{ $t('my_accompanying_courses.tab') }}
|
||||
<tab-counter :count="state.accompanyingCourses.count"></tab-counter>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
@ -51,11 +50,19 @@
|
||||
<tab-counter :count="state.tasks.alert.count"></tab-counter>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link"
|
||||
:class="{'active': activeTab === 'MyWorkflows'}"
|
||||
@click="selectTab('MyWorkflows')">
|
||||
{{ $t('my_workflows.tab') }}
|
||||
<tab-counter :count="state.workflows.count"></tab-counter>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item loading ms-auto py-2" v-if="loading">
|
||||
<i class="fa fa-circle-o-notch fa-spin fa-lg text-chill-gray" :title="$t('loading')"></i>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<div class="my-4">
|
||||
<my-customs
|
||||
v-if="activeTab === 'MyCustoms'">
|
||||
@ -75,6 +82,9 @@
|
||||
<my-notifications
|
||||
v-else-if="activeTab === 'MyNotifications'">
|
||||
</my-notifications>
|
||||
<my-workflows
|
||||
v-else-if="activeTab === 'MyWorkflows'">
|
||||
</my-workflows>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -85,6 +95,7 @@ import MyEvaluations from './MyEvaluations';
|
||||
import MyTasks from './MyTasks';
|
||||
import MyAccompanyingCourses from './MyAccompanyingCourses';
|
||||
import MyNotifications from './MyNotifications';
|
||||
import MyWorkflows from './MyWorkflows.vue';
|
||||
import TabCounter from './TabCounter';
|
||||
import { mapState } from "vuex";
|
||||
|
||||
@ -95,6 +106,7 @@ export default {
|
||||
MyWorks,
|
||||
MyEvaluations,
|
||||
MyTasks,
|
||||
MyWorkflows,
|
||||
MyAccompanyingCourses,
|
||||
MyNotifications,
|
||||
TabCounter,
|
||||
@ -126,6 +138,7 @@ export default {
|
||||
'MyWorks',
|
||||
'MyEvaluations',
|
||||
'MyTasks',
|
||||
'MyWorkflows',
|
||||
]) {
|
||||
this.$store.dispatch('getByTab', { tab: m, param: "countOnly=1" });
|
||||
}
|
||||
|
@ -66,6 +66,8 @@ export default {
|
||||
return appMessages.fr.the_activity;
|
||||
case 'Chill\\PersonBundle\\Entity\\AccompanyingPeriod':
|
||||
return appMessages.fr.the_course;
|
||||
case 'Chill\\MainBundle\\Entity\\Workflow\\EntityWorkflow':
|
||||
return appMessages.fr.the_workflow;
|
||||
default:
|
||||
throw 'notification type unknown';
|
||||
}
|
||||
@ -76,6 +78,8 @@ export default {
|
||||
return `/fr/activity/${n.relatedEntityId}/show`
|
||||
case 'Chill\\PersonBundle\\Entity\\AccompanyingPeriod':
|
||||
return `/fr/parcours/${n.relatedEntityId}`
|
||||
case 'Chill\\MainBundle\\Entity\\Workflow\\EntityWorkflow':
|
||||
return `/fr/main/workflow/${n.relatedEntityId}/show`
|
||||
default:
|
||||
throw 'notification type unknown';
|
||||
}
|
||||
|
@ -0,0 +1,88 @@
|
||||
<template>
|
||||
|
||||
<div class="alert alert-light">{{ $t('my_workflows.description') }}</div>
|
||||
<span v-if="noResults" class="chill-no-data-statement">{{ $t('no_data') }}</span>
|
||||
<tab-table v-else>
|
||||
<template v-slot:thead>
|
||||
<th scope="col">{{ $t('Object_workflow') }}</th>
|
||||
<th scope="col">{{ $t('Step') }}</th>
|
||||
<th scope="col">{{ $t('concerned_users') }}</th>
|
||||
<th scope="col"></th>
|
||||
</template>
|
||||
<template v-slot:tbody>
|
||||
<tr v-for="(w, i) in workflows.results" :key="`workflow-${i}`">
|
||||
<td>{{ w.title }}</td>
|
||||
<td>
|
||||
<div class="workflow">
|
||||
<div class="breadcrumb">
|
||||
<i class="fa fa-circle me-1 text-chill-yellow mx-2"></i>
|
||||
<span class="mx-2">{{ getStep(w) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td v-if="w.datas.persons !== null">
|
||||
<span v-for="p in w.datas.persons" class="me-1" :key="p.id">
|
||||
<on-the-fly
|
||||
:type="p.type"
|
||||
:id="p.id"
|
||||
:buttonText="p.textAge"
|
||||
:displayBadge="'true' === 'true'"
|
||||
action="show">
|
||||
</on-the-fly>
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<a class="btn btn-sm btn-show" :href="getUrl(w)">
|
||||
{{ $t('show_entity', { entity: $t('the_workflow') }) }}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
</tab-table>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapGetters } from "vuex";
|
||||
import TabTable from "./TabTable";
|
||||
import OnTheFly from 'ChillMainAssets/vuejs/OnTheFly/components/OnTheFly';
|
||||
|
||||
export default {
|
||||
name: "MyWorkflows",
|
||||
components: {
|
||||
TabTable,
|
||||
OnTheFly
|
||||
},
|
||||
computed: {
|
||||
...mapState([
|
||||
'workflows',
|
||||
]),
|
||||
...mapGetters([
|
||||
'isWorkflowsLoaded',
|
||||
]),
|
||||
noResults() {
|
||||
if (!this.isWorkflowsLoaded) {
|
||||
return false;
|
||||
} else {
|
||||
return this.workflows.count === 0;
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
getUrl(w) {
|
||||
return `/fr/main/workflow/${w.id}/show`;
|
||||
},
|
||||
getStep(w) {
|
||||
const lastStep = w.steps.length - 1
|
||||
return w.steps[lastStep].currentStep.text;
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
span.outdated {
|
||||
font-weight: bold;
|
||||
color: var(--bs-warning);
|
||||
}
|
||||
</style>
|
@ -22,6 +22,10 @@ const appMessages = {
|
||||
tab: "Mes notifications",
|
||||
description: "Liste des notifications reçues et non lues.",
|
||||
},
|
||||
my_workflows: {
|
||||
tab: "Mes workflows",
|
||||
description: "Liste des workflows en attente d'une action."
|
||||
},
|
||||
opening_date: "Date d'ouverture",
|
||||
social_issues: "Problématiques sociales",
|
||||
concerned_persons: "Usagers concernés",
|
||||
@ -33,12 +37,16 @@ const appMessages = {
|
||||
From: "Expéditeur",
|
||||
Subject: "Objet",
|
||||
Entity: "Associé à",
|
||||
Step: "Étape",
|
||||
concerned_users: "Usagers concernés",
|
||||
Object_workflow: "Objet du workflow",
|
||||
show_entity: "Voir {entity}",
|
||||
the_activity: "l'échange",
|
||||
the_course: "le parcours",
|
||||
the_action: "l'action",
|
||||
the_evaluation: "l'évaluation",
|
||||
the_task: "la tâche",
|
||||
the_workflow: "le workflow",
|
||||
StartDate: "Date d'ouverture",
|
||||
SocialAction: "Action d'accompagnement",
|
||||
no_data: "Aucun résultats",
|
||||
@ -61,4 +69,4 @@ Object.assign(appMessages.fr);
|
||||
|
||||
export {
|
||||
appMessages
|
||||
};
|
||||
};
|
||||
|
@ -1,12 +1,6 @@
|
||||
import 'es6-promise/auto';
|
||||
import { createStore } from 'vuex';
|
||||
import { makeFetch } from "ChillMainAssets/lib/api/apiMethods";
|
||||
import MyCustoms from "../MyCustoms";
|
||||
import MyWorks from "../MyWorks";
|
||||
import MyEvaluations from "../MyEvaluations";
|
||||
import MyTasks from "../MyTasks";
|
||||
import MyAccompanyingCourses from "../MyAccompanyingCourses";
|
||||
import MyNotifications from "../MyNotifications";
|
||||
|
||||
const debug = process.env.NODE_ENV !== 'production';
|
||||
|
||||
@ -27,6 +21,7 @@ const store = createStore({
|
||||
},
|
||||
accompanyingCourses: {},
|
||||
notifications: {},
|
||||
workflows: {},
|
||||
errorMsg: [],
|
||||
loading: false
|
||||
},
|
||||
@ -49,6 +44,9 @@ const store = createStore({
|
||||
isNotificationsLoaded(state) {
|
||||
return !isEmpty(state.notifications);
|
||||
},
|
||||
isWorkflowsLoaded(state) {
|
||||
return !isEmpty(state.workflows);
|
||||
},
|
||||
counter(state) {
|
||||
return {
|
||||
works: state.works.count,
|
||||
@ -57,6 +55,7 @@ const store = createStore({
|
||||
tasksAlert: state.tasks.alert.count,
|
||||
accompanyingCourses: state.accompanyingCourses.count,
|
||||
notifications: state.notifications.count,
|
||||
workflows: state.workflows.count
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -85,6 +84,9 @@ const store = createStore({
|
||||
//console.log('addNotifications', notifications);
|
||||
state.notifications = notifications;
|
||||
},
|
||||
addWorkflows(state, workflows) {
|
||||
state.workflows = workflows;
|
||||
},
|
||||
setLoading(state, bool) {
|
||||
state.loading = bool;
|
||||
},
|
||||
@ -180,14 +182,30 @@ const store = createStore({
|
||||
const url = `/api/1.0/main/notification/my/unread${'?'+ param}`;
|
||||
makeFetch('GET', url)
|
||||
.then((response) => {
|
||||
console.log('notifications', response)
|
||||
commit('addNotifications', response);
|
||||
commit('setLoading', false);
|
||||
})
|
||||
.catch((error) => {
|
||||
commit('catchError', error);
|
||||
throw error;
|
||||
})
|
||||
;
|
||||
});
|
||||
}
|
||||
break;
|
||||
case 'MyWorkflows':
|
||||
if (!getters.isWorflowsLoaded) {
|
||||
commit('setLoading', true);
|
||||
const url = '/api/1.0/main/workflow/my';
|
||||
makeFetch('GET', url)
|
||||
.then((response) => {
|
||||
console.log('workflows', response)
|
||||
commit('addWorkflows', response);
|
||||
commit('setLoading', false);
|
||||
})
|
||||
.catch((error) => {
|
||||
commit('catchError', error);
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="nav-item">
|
||||
<li v-if="allowedTypes.includes('person')" class="nav-item">
|
||||
<a class="nav-link" :class="{ active: isActive('person') }">
|
||||
<label for="person">
|
||||
<input type="radio" name="person" id="person" v-model="radioType" value="person">
|
||||
@ -8,7 +8,7 @@
|
||||
</label>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<li v-if="allowedTypes.includes('thirdparty')" class="nav-item">
|
||||
<a class="nav-link" :class="{ active: isActive('thirdparty') }">
|
||||
<label for="thirdparty">
|
||||
<input type="radio" name="thirdparty" id="thirdparty" v-model="radioType" value="thirdparty">
|
||||
@ -21,14 +21,16 @@
|
||||
<div class="my-4">
|
||||
<on-the-fly-person
|
||||
v-if="type === 'person'"
|
||||
v-bind:action="action"
|
||||
:action="action"
|
||||
:query="query"
|
||||
ref="castPerson"
|
||||
>
|
||||
</on-the-fly-person>
|
||||
|
||||
<on-the-fly-thirdparty
|
||||
v-if="type === 'thirdparty'"
|
||||
v-bind:action="action"
|
||||
:action="action"
|
||||
:query="query"
|
||||
ref="castThirdparty"
|
||||
>
|
||||
</on-the-fly-thirdparty>
|
||||
@ -38,18 +40,17 @@
|
||||
<script>
|
||||
import OnTheFlyPerson from 'ChillPersonAssets/vuejs/_components/OnTheFly/Person.vue';
|
||||
import OnTheFlyThirdparty from 'ChillThirdPartyAssets/vuejs/_components/OnTheFly/ThirdParty.vue';
|
||||
import { postPerson } from "ChillPersonAssets/vuejs/_api/OnTheFly";
|
||||
|
||||
export default {
|
||||
name: "OnTheFlyCreate",
|
||||
props: ['action'],
|
||||
props: ['action', 'allowedTypes', 'query'],
|
||||
components: {
|
||||
OnTheFlyPerson,
|
||||
OnTheFlyThirdparty
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
type: 'person' //by default
|
||||
type: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@ -61,7 +62,10 @@ export default {
|
||||
get() {
|
||||
return this.type;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.type = (this.allowedTypes.length === 1 && this.allowedTypes.includes('thirdparty')) ? 'thirdparty' : 'person'
|
||||
},
|
||||
methods: {
|
||||
isActive(tab) {
|
||||
@ -74,7 +78,7 @@ export default {
|
||||
case 'thirdparty':
|
||||
let data = this.$refs.castThirdparty.$data.thirdparty;
|
||||
data.name = data.text;
|
||||
if (data.address !== undefined) {
|
||||
if (data.address !== null) {
|
||||
data.address = { id: data.address.address_id }
|
||||
} else {
|
||||
data.address = null;
|
||||
|
@ -54,6 +54,8 @@
|
||||
<template v-slot:body v-else>
|
||||
<on-the-fly-create
|
||||
:action="action"
|
||||
:allowedTypes="allowedTypes"
|
||||
:query="query"
|
||||
ref="castNew">
|
||||
</on-the-fly-create>
|
||||
</template>
|
||||
@ -90,7 +92,7 @@ export default {
|
||||
OnTheFlyThirdparty,
|
||||
OnTheFlyCreate
|
||||
},
|
||||
props: ['type', 'id', 'action', 'buttonText', 'displayBadge', 'isDead', 'parent'],
|
||||
props: ['type', 'id', 'action', 'buttonText', 'displayBadge', 'isDead', 'parent', 'allowedTypes', 'query'],
|
||||
emits: ['saveFormOnTheFly'],
|
||||
data() {
|
||||
return {
|
||||
@ -129,6 +131,13 @@ export default {
|
||||
return 'action.create';
|
||||
}
|
||||
},
|
||||
titleCreate() {
|
||||
return this.allowedTypes.every(t => t === 'person')
|
||||
? 'onthefly.create.title.person'
|
||||
: this.allowedTypes.every(t => t === 'thirdparty')
|
||||
? 'onthefly.create.title.thirdparty'
|
||||
: 'onthefly.create.title.default'
|
||||
},
|
||||
titleModal() {
|
||||
switch (this.action) {
|
||||
case 'show':
|
||||
@ -136,7 +145,7 @@ export default {
|
||||
case 'edit':
|
||||
return 'onthefly.edit.' + this.type;
|
||||
case 'create':
|
||||
return 'onthefly.create.title';
|
||||
return this.titleCreate;
|
||||
}
|
||||
},
|
||||
titleMessage() {
|
||||
|
@ -9,11 +9,15 @@ const ontheflyMessages = {
|
||||
},
|
||||
edit: {
|
||||
person: "Modifier un usager",
|
||||
thirdparty: "Modifier un tiers"
|
||||
thirdparty: "Modifier un tiers",
|
||||
},
|
||||
create: {
|
||||
button: "Créer \"{q}\"",
|
||||
title: "Création d'un nouvel usager ou d'un tiers professionnel",
|
||||
title: {
|
||||
default: "Création d'un nouvel usager ou d'un tiers professionnel",
|
||||
person: "Création d'un nouvel usager",
|
||||
thirdparty: "Création d'un nouveau tiers professionnel",
|
||||
},
|
||||
person: "un nouvel usager",
|
||||
thirdparty: "un nouveau tiers professionnel"
|
||||
},
|
||||
|
@ -12,6 +12,7 @@ declare(strict_types=1);
|
||||
namespace Chill\MainBundle\Serializer\Normalizer;
|
||||
|
||||
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
|
||||
use Chill\MainBundle\Workflow\EntityWorkflowManager;
|
||||
use Chill\MainBundle\Workflow\Helper\MetadataExtractor;
|
||||
use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
|
||||
use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait;
|
||||
@ -22,12 +23,18 @@ class EntityWorkflowNormalizer implements NormalizerInterface, NormalizerAwareIn
|
||||
{
|
||||
use NormalizerAwareTrait;
|
||||
|
||||
private EntityWorkflowManager $entityWorkflowManager;
|
||||
|
||||
private MetadataExtractor $metadataExtractor;
|
||||
|
||||
private Registry $registry;
|
||||
|
||||
public function __construct(MetadataExtractor $metadataExtractor, Registry $registry)
|
||||
{
|
||||
public function __construct(
|
||||
EntityWorkflowManager $entityWorkflowManager,
|
||||
MetadataExtractor $metadataExtractor,
|
||||
Registry $registry
|
||||
) {
|
||||
$this->entityWorkflowManager = $entityWorkflowManager;
|
||||
$this->metadataExtractor = $metadataExtractor;
|
||||
$this->registry = $registry;
|
||||
}
|
||||
@ -40,6 +47,7 @@ class EntityWorkflowNormalizer implements NormalizerInterface, NormalizerAwareIn
|
||||
public function normalize($object, ?string $format = null, array $context = [])
|
||||
{
|
||||
$workflow = $this->registry->get($object, $object->getWorkflowName());
|
||||
$handler = $this->entityWorkflowManager->getHandler($object);
|
||||
|
||||
return [
|
||||
'type' => 'entity_workflow',
|
||||
@ -49,6 +57,8 @@ class EntityWorkflowNormalizer implements NormalizerInterface, NormalizerAwareIn
|
||||
'workflow' => $this->metadataExtractor->buildArrayPresentationForWorkflow($workflow),
|
||||
'currentStep' => $this->normalizer->normalize($object->getCurrentStep(), $format, $context),
|
||||
'steps' => $this->normalizer->normalize($object->getStepsChained(), $format, $context),
|
||||
'datas' => $this->normalizer->normalize($handler->getEntityData($object), $format, $context),
|
||||
'title' => $handler->getEntityTitle($object),
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,10 @@ use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
|
||||
|
||||
interface EntityWorkflowHandlerInterface
|
||||
{
|
||||
public function getEntityData(EntityWorkflow $entityWorkflow, array $options = []): array;
|
||||
|
||||
public function getEntityTitle(EntityWorkflow $entityWorkflow, array $options = []): string;
|
||||
|
||||
public function getRelatedEntity(EntityWorkflow $entityWorkflow): ?object;
|
||||
|
||||
/**
|
||||
|
@ -125,6 +125,11 @@ components:
|
||||
type: object
|
||||
type:
|
||||
type: string
|
||||
Workflow:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
|
||||
paths:
|
||||
/1.0/search.json:
|
||||
@ -165,13 +170,6 @@ paths:
|
||||
200:
|
||||
description: "OK"
|
||||
/1.0/main/address.json:
|
||||
get:
|
||||
tags:
|
||||
- address
|
||||
summary: Return a list of all Chill addresses
|
||||
responses:
|
||||
200:
|
||||
description: "ok"
|
||||
post:
|
||||
tags:
|
||||
- address
|
||||
@ -222,10 +220,44 @@ paths:
|
||||
description: "Unprocessable entity (validation errors)"
|
||||
400:
|
||||
description: "transition cannot be applyed"
|
||||
/1.0/main/address/{id}.json:
|
||||
get:
|
||||
tags:
|
||||
- address
|
||||
summary: Return an address by id
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
description: The address id
|
||||
schema:
|
||||
type: integer
|
||||
format: integer
|
||||
minimum: 1
|
||||
responses:
|
||||
200:
|
||||
description: "ok"
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Address'
|
||||
404:
|
||||
description: "not found"
|
||||
401:
|
||||
description: "Unauthorized"
|
||||
patch:
|
||||
tags:
|
||||
- address
|
||||
summary: patch an address
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
description: The address id
|
||||
schema:
|
||||
type: integer
|
||||
format: integer
|
||||
minimum: 1
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
@ -277,31 +309,6 @@ paths:
|
||||
400:
|
||||
description: "transition cannot be applyed"
|
||||
|
||||
/1.0/main/address/{id}.json:
|
||||
get:
|
||||
tags:
|
||||
- address
|
||||
summary: Return an address by id
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
description: The address id
|
||||
schema:
|
||||
type: integer
|
||||
format: integer
|
||||
minimum: 1
|
||||
responses:
|
||||
200:
|
||||
description: "ok"
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Address'
|
||||
404:
|
||||
description: "not found"
|
||||
401:
|
||||
description: "Unauthorized"
|
||||
|
||||
/1.0/main/address/{id}/duplicate.json:
|
||||
post:
|
||||
@ -793,4 +800,20 @@ paths:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/UserJob'
|
||||
/1.0/main/workflow/my:
|
||||
get:
|
||||
tags:
|
||||
- workflow
|
||||
summary: Return a list of workflows awaiting for user's action
|
||||
responses:
|
||||
200:
|
||||
description: "ok"
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Workflow'
|
||||
403:
|
||||
description: "Unauthorized"
|
||||
|
||||
|
@ -60,6 +60,7 @@ class UserRefEventSubscriber implements EventSubscriberInterface
|
||||
{
|
||||
if ($period->hasPreviousUser()
|
||||
&& $period->getUser() !== $this->security->getUser()
|
||||
&& null !== $period->getUser()
|
||||
&& $period->getStep() !== AccompanyingPeriod::STEP_DRAFT
|
||||
) {
|
||||
$this->generateNotificationToUser($period);
|
||||
|
@ -25,6 +25,13 @@ class HouseholdMemberType extends AbstractType
|
||||
'label' => 'household.Start date',
|
||||
'input' => 'datetime_immutable',
|
||||
]);
|
||||
|
||||
if (!$options['data']->getPosition()->getShareHousehold()) {
|
||||
$builder->add('endDate', ChillDateType::class, [
|
||||
'label' => 'household.End date',
|
||||
'input' => 'datetime_immutable',
|
||||
]);
|
||||
}
|
||||
$builder
|
||||
->add('comment', ChillTextareaType::class, [
|
||||
'label' => 'household.Comment',
|
||||
|
@ -20,7 +20,6 @@
|
||||
<div class="form-check" v-for="p in participationWithoutHousehold" :key="p.id">
|
||||
<input type="checkbox"
|
||||
class="form-check-input"
|
||||
v-model="hasNoHousehold"
|
||||
name="persons[]"
|
||||
checked="checked"
|
||||
:id="p.person.id"
|
||||
|
@ -97,16 +97,10 @@ export default {
|
||||
...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);
|
||||
}
|
||||
|
||||
console.log('users to return', users);
|
||||
return users;
|
||||
},
|
||||
valueJob: {
|
||||
|
@ -6,7 +6,7 @@
|
||||
<div v-if="accompanyingCourse.requestor && isAnonymous" class="flex-table">
|
||||
|
||||
<label>
|
||||
<input type="checkbox" v-model="isAnonymous" class="me-2" />
|
||||
<input type="checkbox" v-model="requestorIsAnonymous" class="me-2" />
|
||||
{{ $t('requestor.is_anonymous') }}
|
||||
</label>
|
||||
<confidential v-if="accompanyingCourse.requestor.type === 'thirdparty'">
|
||||
@ -72,7 +72,7 @@
|
||||
|
||||
<div v-else-if="accompanyingCourse.requestor && !isAnonymous" class="flex-table">
|
||||
<label>
|
||||
<input type="checkbox" v-model="isAnonymous" class="me-2" />
|
||||
<input type="checkbox" v-model="requestorIsAnonymous" class="me-2" />
|
||||
{{ $t('requestor.is_anonymous') }}
|
||||
</label>
|
||||
|
||||
@ -199,7 +199,7 @@ export default {
|
||||
...mapState({
|
||||
suggestedEntities: state => {
|
||||
return [
|
||||
...state.accompanyingCourse.participations.map(p => p.person),
|
||||
...state.accompanyingCourse.participations.filter((p) => p.endDate === null).map((p) => p.person),
|
||||
...state.accompanyingCourse.resources.map(r => r.resource)
|
||||
]
|
||||
.filter((e) => e !== null)
|
||||
@ -218,7 +218,7 @@ export default {
|
||||
accompanyingCourse() {
|
||||
return this.$store.state.accompanyingCourse
|
||||
},
|
||||
isAnonymous: {
|
||||
requestorIsAnonymous: {
|
||||
set(value) {
|
||||
this.$store.dispatch('requestorIsAnonymous', value);
|
||||
},
|
||||
@ -287,7 +287,9 @@ export default {
|
||||
body.name = payload.data.text;
|
||||
body.email = payload.data.email;
|
||||
body.telephone = payload.data.phonenumber;
|
||||
body.address = { id: payload.data.address.address_id };
|
||||
if (payload.data.address) {
|
||||
body.address = { id: payload.data.address.address_id };
|
||||
}
|
||||
|
||||
makeFetch('PATCH', `/api/1.0/thirdparty/thirdparty/${payload.data.id}.json`, body)
|
||||
.then(response => {
|
||||
|
@ -77,7 +77,7 @@ export default {
|
||||
counter: state => state.accompanyingCourse.resources.length,
|
||||
suggestedEntities: state => [
|
||||
state.accompanyingCourse.requestor,
|
||||
...state.accompanyingCourse.participations.map(p => p.person),
|
||||
...state.accompanyingCourse.participations.filter((p) => p.endDate === null).map((p) => p.person),
|
||||
]
|
||||
.filter((e) => e !== null)
|
||||
.filter(
|
||||
|
@ -428,42 +428,6 @@ let initPromise = (root) => Promise.all([getScopesPromise(root), accompanyingCou
|
||||
throw error;
|
||||
})
|
||||
},
|
||||
/**
|
||||
* On The Fly
|
||||
*/
|
||||
patchOnTheFly({ commit }, payload) {
|
||||
// TODO should be into the dedicated component, no ? JF
|
||||
console.log('## action: patch OnTheFly', payload);
|
||||
let body = { type: payload.type };
|
||||
if (payload.type === 'person') {
|
||||
body.firstName = payload.data.firstName;
|
||||
body.lastName = payload.data.lastName;
|
||||
if (payload.data.birthdate !== null) { body.birthdate = payload.data.birthdate; }
|
||||
body.phonenumber = payload.data.phonenumber;
|
||||
body.mobilenumber = payload.data.mobilenumber;
|
||||
body.gender = payload.data.gender;
|
||||
console.log('id', payload.data.id, 'and body', body);
|
||||
patchPerson(payload.data.id, body)
|
||||
.then(person => new Promise((resolve, reject) => {
|
||||
console.log('patch person', person);
|
||||
commit('updatePerson', { target: payload.target, person: person });
|
||||
resolve();
|
||||
}));
|
||||
}
|
||||
else if (payload.type === 'thirdparty') {
|
||||
body.name = payload.data.text;
|
||||
body.email = payload.data.email;
|
||||
body.telephone = payload.data.phonenumber;
|
||||
body.address = { id: payload.data.address.address_id };
|
||||
console.log('id', payload.data.id, 'and body', body);
|
||||
patchThirdparty(payload.data.id, body)
|
||||
.then(thirdparty => new Promise((resolve, reject) => {
|
||||
console.log('patch thirdparty', thirdparty);
|
||||
commit('updateThirdparty', { target: payload.target, thirdparty: thirdparty });
|
||||
resolve();
|
||||
}));
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Update accompanying course intensity/emergency/confidentiality
|
||||
*/
|
||||
|
@ -397,36 +397,6 @@ const store = createStore({
|
||||
commit('setErrors', error.violations);
|
||||
});
|
||||
},
|
||||
patchOnTheFly({ commit }, payload) {
|
||||
let body = { type: payload.type };
|
||||
const id = payload.data.id;
|
||||
let url = `/api/1.0/person/person/${id}.json`;
|
||||
let mutation = "updatePerson";
|
||||
|
||||
if (payload.type === 'person') {
|
||||
body.firstName = payload.data.firstName;
|
||||
body.lastName = payload.data.lastName;
|
||||
if (payload.data.birthdate !== null) { body.birthdate = payload.data.birthdate; }
|
||||
body.phonenumber = payload.data.phonenumber;
|
||||
body.mobilenumber = payload.data.mobilenumber;
|
||||
body.gender = payload.data.gender;
|
||||
} else if (payload.type === 'thirdparty') {
|
||||
body.name = payload.data.text;
|
||||
body.email = payload.data.email;
|
||||
body.telephone = payload.data.phonenumber;
|
||||
body.address = { id: payload.data.address.address_id };
|
||||
|
||||
url = `/api/1.0/thirdparty/thirdparty/${id}.json`;
|
||||
mutation = 'updateThirdparty'
|
||||
}
|
||||
makeFetch('PATCH', url, body)
|
||||
.then((response) => {
|
||||
commit(mutation, {target: payload.target, thirdparty: response});
|
||||
})
|
||||
.catch((error) => {
|
||||
throw error;
|
||||
})
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -62,8 +62,10 @@
|
||||
|
||||
<div class="create-button">
|
||||
<on-the-fly
|
||||
v-if="query.length >= 3"
|
||||
v-if="queryLength >= 3 && (options.type.includes('person') || options.type.includes('thirdparty'))"
|
||||
:buttonText="$t('onthefly.create.button', {q: query})"
|
||||
:allowedTypes="options.type"
|
||||
:query="query"
|
||||
action="create"
|
||||
@saveFormOnTheFly="saveFormOnTheFly"
|
||||
ref="onTheFly">
|
||||
|
@ -32,6 +32,14 @@
|
||||
<label for="lastname">{{ $t('person.lastname') }}</label>
|
||||
</div>
|
||||
|
||||
<div v-if="queryItems">
|
||||
<ul class="list-suggest add-items inline">
|
||||
<li v-for="(qi, i) in queryItems" :key="i" @click="addQueryItem('lastName', qi)">
|
||||
<span class="person-text">{{ qi }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="form-floating mb-3">
|
||||
<input
|
||||
class="form-control form-control-lg"
|
||||
@ -43,6 +51,14 @@
|
||||
<label for="firstname">{{ $t('person.firstname') }}</label>
|
||||
</div>
|
||||
|
||||
<div v-if="queryItems">
|
||||
<ul class="list-suggest add-items inline">
|
||||
<li v-for="(qi, i) in queryItems" :key="i" @click="addQueryItem('firstName', qi)">
|
||||
<span class="person-text">{{ qi }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div v-for="(a, i) in config.altNames" :key="a.key" class="form-floating mb-3">
|
||||
<input
|
||||
class="form-control form-control-lg"
|
||||
@ -122,7 +138,7 @@ import PersonRenderBox from '../Entity/PersonRenderBox.vue';
|
||||
|
||||
export default {
|
||||
name: "OnTheFlyPerson",
|
||||
props: ['id', 'type', 'action'],
|
||||
props: ['id', 'type', 'action', 'query'],
|
||||
//emits: ['createAction'],
|
||||
components: {
|
||||
PersonRenderBox
|
||||
@ -203,6 +219,9 @@ export default {
|
||||
},
|
||||
personAltNamesLabels() {
|
||||
return this.person.altNames.map(a => a ? a.label : '');
|
||||
},
|
||||
queryItems() {
|
||||
return this.query ? this.query.split(' ') : null;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
@ -244,6 +263,16 @@ export default {
|
||||
)
|
||||
this.person.altNames = updateAltNames;
|
||||
},
|
||||
addQueryItem(field, queryItem) {
|
||||
switch (field) {
|
||||
case 'lastName':
|
||||
this.person.lastName = queryItem;
|
||||
break;
|
||||
case 'firstName':
|
||||
this.person.firstName = queryItem;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -172,7 +172,7 @@
|
||||
|
||||
{%- if options['customButtons']['replace'] is defined -%}
|
||||
{{ options['customButtons']['replace'] }}
|
||||
{%- elseif is_granted('CHILL_PERSON_SEE', person) -%}
|
||||
{%- elseif is_granted('CHILL_PERSON_SEE', person) and options['addLink'] -%}
|
||||
<li>
|
||||
<a class="btn btn-sm btn-show" title="{{ 'Show person'|trans }}"
|
||||
href="{{ path('chill_person_view', { person_id: person.id }) }}"></a>
|
||||
|
@ -56,6 +56,11 @@
|
||||
|
||||
{% if action is defined %}
|
||||
<ul class="record_actions sticky-form-buttons">
|
||||
<li class="cancel">
|
||||
<a href="{{ path('chill_person_resource_list', { 'person_id': resource.personOwner.id } ) }}" class="btn btn-cancel">
|
||||
{{ 'Cancel'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
<li class="edit">
|
||||
<button class="btn btn-edit"
|
||||
type="submit" id="newPersonResource">
|
||||
|
@ -22,41 +22,33 @@
|
||||
<div class="flex-table">
|
||||
{% for resource in personResources %}
|
||||
<div class="item-bloc">
|
||||
<div class="item-row">
|
||||
<div class="item-col" style="width: 50%">
|
||||
{% if resource.kind is not null %}
|
||||
<div class="item-row">
|
||||
<section>
|
||||
<p>{{ resource.kind.title.fr|capitalize }}</p>
|
||||
</section>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="{% if resource.kind is not null %} separator {% endif %}">
|
||||
{% if resource.person is not null %}
|
||||
<div class="denomination h3">
|
||||
<span>
|
||||
{% include '@ChillMain/OnTheFly/_insert_vue_onthefly.html.twig' with {
|
||||
action: 'show', displayBadge: true,
|
||||
targetEntity: { name: 'person', id: resource.person.id },
|
||||
buttonText: resource.person|chill_entity_render_string,
|
||||
isDead: resource.person.deathdate is not null
|
||||
} %}
|
||||
</span>
|
||||
</div>
|
||||
{{ resource.person|chill_entity_render_box({
|
||||
'render': 'bloc',
|
||||
'addLink': true,
|
||||
'addInfo': true,
|
||||
'addAge': true
|
||||
}) }}
|
||||
{% elseif resource.thirdparty is not null %}
|
||||
<div class="denomination h3">
|
||||
<span>
|
||||
{% include '@ChillMain/OnTheFly/_insert_vue_onthefly.html.twig' with {
|
||||
action: 'show', displayBadge: true,
|
||||
targetEntity: { name: 'thirdparty', id: resource.thirdparty.id },
|
||||
buttonText: resource.thirdParty|chill_entity_render_string,
|
||||
parent: resource.thirdparty.parent
|
||||
} %}
|
||||
</span>
|
||||
</div>
|
||||
{{ resource.thirdparty|chill_entity_render_box({
|
||||
'render': 'bloc',
|
||||
'showContacts': false,
|
||||
'addLink': true,
|
||||
'isConfidential': (resource.thirdparty.contactDataAnonymous ? true : false)
|
||||
}) }}
|
||||
{% else %}
|
||||
<div class="denomination h3">
|
||||
<span>{{ resource.freetext }}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="item-col" style="justify-content: flex-end; ">
|
||||
{% if resource.kind %}
|
||||
<span>{{ resource.kind.title.fr|capitalize }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% if resource.comment.comment is not empty %}
|
||||
<div class="item-row separator">
|
||||
|
@ -16,14 +16,32 @@ use Chill\MainBundle\Workflow\EntityWorkflowHandlerInterface;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation;
|
||||
use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationRepository;
|
||||
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodWorkEvaluationVoter;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
class AccompanyingPeriodWorkEvaluationWorkflowHandler implements EntityWorkflowHandlerInterface
|
||||
{
|
||||
private AccompanyingPeriodWorkEvaluationRepository $repository;
|
||||
|
||||
public function __construct(AccompanyingPeriodWorkEvaluationRepository $repository)
|
||||
private TranslatorInterface $translator;
|
||||
|
||||
public function __construct(AccompanyingPeriodWorkEvaluationRepository $repository, TranslatorInterface $translator)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
$this->translator = $translator;
|
||||
}
|
||||
|
||||
public function getEntityData(EntityWorkflow $entityWorkflow, array $options = []): array
|
||||
{
|
||||
$evaluation = $this->getRelatedEntity($entityWorkflow);
|
||||
|
||||
return [
|
||||
'persons' => $evaluation->getAccompanyingPeriodWork()->getPersons(),
|
||||
];
|
||||
}
|
||||
|
||||
public function getEntityTitle(EntityWorkflow $entityWorkflow, array $options = []): string
|
||||
{
|
||||
return $this->translator->trans('workflow.Evaluation (n°%eval%)', ['%eval%' => $entityWorkflow->getRelatedEntityId()]);
|
||||
}
|
||||
|
||||
public function getRelatedEntity(EntityWorkflow $entityWorkflow): ?AccompanyingPeriodWorkEvaluation
|
||||
|
@ -15,17 +15,34 @@ use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
|
||||
use Chill\MainBundle\Workflow\EntityWorkflowHandlerInterface;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
|
||||
use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkRepository;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
class AccompanyingPeriodWorkWorkflowHandler implements EntityWorkflowHandlerInterface
|
||||
{
|
||||
private AccompanyingPeriodWorkRepository $repository;
|
||||
|
||||
public function __construct(AccompanyingPeriodWorkRepository $repository)
|
||||
private TranslatorInterface $translator;
|
||||
|
||||
public function __construct(AccompanyingPeriodWorkRepository $repository, TranslatorInterface $translator)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
$this->translator = $translator;
|
||||
}
|
||||
|
||||
public function getRelatedEntity(EntityWorkflow $entityWorkflow): ?object
|
||||
public function getEntityData(EntityWorkflow $entityWorkflow, array $options = []): array
|
||||
{
|
||||
return [
|
||||
'persons' => $this->getRelatedEntity($entityWorkflow)
|
||||
->getPersons(),
|
||||
];
|
||||
}
|
||||
|
||||
public function getEntityTitle(EntityWorkflow $entityWorkflow, array $options = []): string
|
||||
{
|
||||
return $this->translator->trans('workflow.Work (n°%w%)', ['%w%' => $entityWorkflow->getRelatedEntityId()]);
|
||||
}
|
||||
|
||||
public function getRelatedEntity(EntityWorkflow $entityWorkflow): ?AccompanyingPeriodWork
|
||||
{
|
||||
return $this->repository->find($entityWorkflow->getRelatedEntityId());
|
||||
}
|
||||
|
@ -240,6 +240,7 @@ Select a type: "Choisissez un type"
|
||||
Select a person: "Choisissez un usager"
|
||||
Select a thirdparty: "Choisissez un tiers"
|
||||
Contact person: "Personne de contact"
|
||||
Kind: "Type"
|
||||
|
||||
|
||||
# pickAPersonType
|
||||
|
@ -179,6 +179,8 @@ class ThirdParty implements TrackCreationInterface, TrackUpdateInterface
|
||||
* @var string
|
||||
* @ORM\Column(name="name", type="string", length=255)
|
||||
* @Assert\Length(min="2")
|
||||
* @Assert\NotNull
|
||||
* @Assert\NotBlank
|
||||
* @Groups({"read", "write", "docgen:read", "docgen:read:3party:parent"})
|
||||
*/
|
||||
private ?string $name = '';
|
||||
|
@ -61,6 +61,13 @@
|
||||
<input class="form-control form-control-lg" id="name" v-model="thirdparty.text" v-bind:placeholder="$t('thirdparty.name')" />
|
||||
<label for="name">{{ $t('thirdparty.name') }}</label>
|
||||
</div>
|
||||
<div v-if="query">
|
||||
<ul class="list-suggest add-items inline">
|
||||
<li @click="addQuery(query)">
|
||||
<span class="person-text">{{ query }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<template
|
||||
v-if="thirdparty.kind !== 'child'">
|
||||
@ -85,7 +92,7 @@
|
||||
<div class="input-group mb-3">
|
||||
<span class="input-group-text" id="phonenumber"><i class="fa fa-fw fa-phone"></i></span>
|
||||
<input class="form-control form-control-lg"
|
||||
v-model="thirdparty.phonenumber"
|
||||
v-model="thirdparty.telephone"
|
||||
v-bind:placeholder="$t('thirdparty.phonenumber')"
|
||||
v-bind:aria-label="$t('thirdparty.phonenumber')"
|
||||
aria-describedby="phonenumber" />
|
||||
@ -102,7 +109,7 @@ import BadgeEntity from 'ChillMainAssets/vuejs/_components/BadgeEntity.vue';
|
||||
|
||||
export default {
|
||||
name: "OnTheFlyThirdParty",
|
||||
props: ['id', 'type', 'action'],
|
||||
props: ['id', 'type', 'action', 'query'],
|
||||
components: {
|
||||
ThirdPartyRenderBox,
|
||||
AddAddress,
|
||||
@ -113,6 +120,11 @@ export default {
|
||||
//context: {}, <--
|
||||
thirdparty: {
|
||||
type: 'thirdparty',
|
||||
address: null,
|
||||
kind: 'company',
|
||||
name: '',
|
||||
telephone: '',
|
||||
|
||||
},
|
||||
addAddress: {
|
||||
options: {
|
||||
@ -186,6 +198,9 @@ export default {
|
||||
this.thirdparty.address = payload.address; // <--
|
||||
console.log('switch address to edit mode', this.context);
|
||||
}
|
||||
},
|
||||
addQuery(query) {
|
||||
this.thirdparty.text = query;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
@ -87,14 +87,24 @@ class ThirdPartyApiSearch implements SearchApiInterface
|
||||
foreach ($strs as $str) {
|
||||
if (!empty($str)) {
|
||||
$wheres[] = "(LOWER(UNACCENT(?)) <<% tparty.canonicalized OR
|
||||
tparty.canonicalized LIKE '%' || LOWER(UNACCENT(?)) || '%')";
|
||||
$whereArgs[] = [$str, $str];
|
||||
$pertinence[] = 'STRICT_WORD_SIMILARITY(LOWER(UNACCENT(?)), tparty.canonicalized) + ' .
|
||||
"(tparty.canonicalized LIKE '%s' || LOWER(UNACCENT(?)) || '%')::int + " .
|
||||
tparty.canonicalized LIKE '%' || LOWER(UNACCENT(?)) || '%')
|
||||
OR
|
||||
(LOWER(UNACCENT(?)) <<% parent.canonicalized OR
|
||||
parent.canonicalized LIKE '%' || LOWER(UNACCENT(?)) || '%')
|
||||
";
|
||||
$whereArgs[] = [$str, $str, $str, $str];
|
||||
$pertinence[] = 'GREATEST(
|
||||
STRICT_WORD_SIMILARITY(LOWER(UNACCENT(?)), tparty.canonicalized),
|
||||
STRICT_WORD_SIMILARITY(LOWER(UNACCENT(?)), parent.canonicalized)
|
||||
) + ' .
|
||||
"GREATEST(
|
||||
(tparty.canonicalized LIKE '%s' || LOWER(UNACCENT(?)) || '%')::int,
|
||||
(parent.canonicalized LIKE '%s' || LOWER(UNACCENT(?)) || '%')::int
|
||||
) + " .
|
||||
// take postcode label into account, but lower than the canonicalized field
|
||||
"COALESCE((LOWER(UNACCENT(cmpc.label)) LIKE '%' || LOWER(UNACCENT(?)) || '%')::int * 0.3, 0) + " .
|
||||
"COALESCE((LOWER(UNACCENT(cmpc_p.label)) LIKE '%' || LOWER(UNACCENT(?)) || '%')::int * 0.3, 0)";
|
||||
$pertinenceArgs[] = [$str, $str, $str, $str];
|
||||
$pertinenceArgs[] = [$str, $str, $str, $str, $str, $str];
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user