Merge branch 'signature-app-master' into 'master'

Signature app master

Closes #307

See merge request Chill-Projet/chill-bundles!743
This commit is contained in:
2024-11-12 20:30:00 +00:00
426 changed files with 22236 additions and 2253 deletions

View File

@@ -11,7 +11,6 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Controller;
use Chill\DocStoreBundle\Serializer\Normalizer\StoredObjectNormalizer;
use Chill\MainBundle\Pagination\PaginatorFactory;
use Chill\MainBundle\Templating\Listing\FilterOrderHelper;
use Chill\MainBundle\Templating\Listing\FilterOrderHelperFactoryInterface;
@@ -116,7 +115,7 @@ final class AccompanyingCourseWorkController extends AbstractController
{
$this->denyAccessUnlessGranted(AccompanyingPeriodWorkVoter::UPDATE, $work);
$json = $this->serializer->normalize($work, 'json', ['groups' => ['read', StoredObjectNormalizer::ADD_DAV_EDIT_LINK_CONTEXT]]);
$json = $this->serializer->normalize($work, 'json', ['groups' => ['read']]);
return $this->render('@ChillPerson/AccompanyingCourseWork/edit.html.twig', [
'accompanyingCourse' => $work->getAccompanyingPeriod(),

View File

@@ -0,0 +1,81 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Controller;
use Chill\MainBundle\Routing\ChillUrlGeneratorInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodWorkVoter;
use Chill\PersonBundle\Service\AccompanyingPeriodWorkEvaluationDocument\AccompanyingPeriodWorkEvaluationDocumentDuplicator;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Serializer\SerializerInterface;
class AccompanyingPeriodWorkEvaluationDocumentDuplicateController
{
public function __construct(
private readonly AccompanyingPeriodWorkEvaluationDocumentDuplicator $duplicator,
private readonly Security $security,
private readonly SerializerInterface $serializer,
private readonly EntityManagerInterface $entityManager,
private readonly ChillUrlGeneratorInterface $urlGenerator,
) {}
#[Route('/api/1.0/person/accompanying-course-work-evaluation-document/{id}/duplicate', methods: ['POST'])]
public function duplicateApi(AccompanyingPeriodWorkEvaluationDocument $document): Response
{
$work = $document->getAccompanyingPeriodWorkEvaluation()->getAccompanyingPeriodWork();
if (!$this->security->isGranted(AccompanyingPeriodWorkVoter::UPDATE, $work)) {
throw new AccessDeniedHttpException('not allowed to edit this accompanying period work');
}
$duplicatedDocument = $this->duplicator->duplicate($document);
$this->entityManager->persist($duplicatedDocument);
$this->entityManager->persist($duplicatedDocument->getStoredObject());
$this->entityManager->flush();
return new JsonResponse(
$this->serializer->serialize($duplicatedDocument, 'json', [AbstractNormalizer::GROUPS => ['read']]),
json: true
);
}
#[Route('/{_locale}/person/accompanying-course-work-evaluation-document/{id}/duplicate', name: 'chill_person_accompanying_period_work_evaluation_document_duplicate', methods: ['POST'])]
public function duplicate(AccompanyingPeriodWorkEvaluationDocument $document): Response
{
$work = $document->getAccompanyingPeriodWorkEvaluation()->getAccompanyingPeriodWork();
if (!$this->security->isGranted(AccompanyingPeriodWorkVoter::UPDATE, $work)) {
throw new AccessDeniedHttpException('not allowed to edit this accompanying period work');
}
$duplicatedDocument = $this->duplicator->duplicate($document);
$this->entityManager->persist($duplicatedDocument);
$this->entityManager->persist($duplicatedDocument->getStoredObject());
$this->entityManager->flush();
return new RedirectResponse(
$this->urlGenerator->forwardReturnPath(
'chill_person_accompanying_period_work_edit',
['id' => $work->getId(), 'doc_id' => $duplicatedDocument->getId()]
)
);
}
}

View File

@@ -0,0 +1,60 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Controller;
use Chill\MainBundle\Repository\Workflow\EntityWorkflowStepSignatureACLAwareRepository;
use Chill\MainBundle\Workflow\EntityWorkflowManager;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class PersonSignatureController extends AbstractController
{
public function __construct(
private readonly EntityWorkflowStepSignatureACLAwareRepository $signatureRepository,
private readonly EntityWorkflowManager $entityWorkflowManager,
) {}
#[Route(path: '/{_locale}/signatures/by-person/{id}', name: 'chill_person_signature_list')]
public function listSignatures(Person $person): Response
{
$this->denyAccessUnlessGranted(PersonVoter::SEE, $person);
$signatures = $this->signatureRepository->findByPersonAndPendingState($person);
$signatureData = [];
foreach ($signatures as $signature) {
$entityWorkflow = $signature->getStep()->getEntityWorkflow();
$handler = $this->entityWorkflowManager->getHandler($entityWorkflow);
$workflow = [
'handler_template' => $handler->getTemplate($entityWorkflow),
'handler_template_data' => $handler->getTemplateData($entityWorkflow),
'entity_workflow' => $entityWorkflow,
];
$storedObject = $this->entityWorkflowManager->getAssociatedStoredObject($entityWorkflow);
$signatureData[] = [
'signature' => $signature,
'document' => $storedObject,
'workflow' => $workflow,
];
}
return $this->render('@ChillPerson/Person/signature_list.html.twig', [
'signatures' => $signatureData,
'person' => $person,
]);
}
}

View File

@@ -25,6 +25,8 @@ use Chill\MainBundle\Entity\Gender;
use Chill\MainBundle\Entity\HasCenterInterface;
use Chill\MainBundle\Entity\Language;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Entity\Workflow\EntityWorkflowSignatureStateEnum;
use Chill\MainBundle\Entity\Workflow\EntityWorkflowStepSignature;
use Chill\MainBundle\Validation\Constraint\PhonenumberConstraint;
use Chill\PersonBundle\Entity\Household\Household;
use Chill\PersonBundle\Entity\Household\HouseholdMember;
@@ -40,6 +42,7 @@ use DateTime;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\Criteria;
use Doctrine\Common\Collections\ReadableCollection;
use Doctrine\Common\Collections\Selectable;
use Doctrine\ORM\Mapping as ORM;
use libphonenumber\PhoneNumber;
@@ -375,6 +378,12 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
#[ORM\ManyToOne(targetEntity: User::class)]
private ?User $updatedBy = null;
/**
* @var Collection<int, EntityWorkflowStepSignature>
*/
#[ORM\OneToMany(targetEntity: EntityWorkflowStepSignature::class, mappedBy: 'personSigner', orphanRemoval: true)]
private Collection $signatures;
/**
* Person constructor.
*/
@@ -396,6 +405,7 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
$this->budgetCharges = new ArrayCollection();
$this->resources = new ArrayCollection();
$this->centerHistory = new ArrayCollection();
$this->signatures = new ArrayCollection();
}
public function __toString(): string
@@ -467,6 +477,35 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
return $this;
}
public function addSignature(EntityWorkflowStepSignature $signature): self
{
if (!$this->signatures->contains($signature)) {
$this->signatures->add($signature);
}
return $this;
}
public function removeSignature(EntityWorkflowStepSignature $signature): self
{
$this->signatures->removeElement($signature);
return $this;
}
/**
* @return ReadableCollection<int, EntityWorkflowStepSignature>
*/
public function getSignatures(): ReadableCollection
{
return $this->signatures;
}
public function getSignaturesPending(): ReadableCollection
{
return $this->signatures->filter(fn (EntityWorkflowStepSignature $signature) => EntityWorkflowSignatureStateEnum::PENDING === $signature->getState());
}
/**
* Function used for validation that check if the accompanying periods of
* the person are not collapsing (i.e. have not shared days) or having

View File

@@ -11,6 +11,7 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Menu;
use Chill\MainBundle\Repository\Workflow\EntityWorkflowStepSignatureACLAwareRepository;
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Repository\ResidentialAddressRepository;
@@ -43,6 +44,7 @@ class PersonMenuBuilder implements LocalMenuBuilderInterface
private readonly Security $security,
protected TranslatorInterface $translator,
private readonly ResidentialAddressRepository $residentialAddressRepo,
private readonly EntityWorkflowStepSignatureACLAwareRepository $entityWorkflowStepSignatureRepository,
) {
$this->showAccompanyingPeriod = $parameterBag->get('chill_person.accompanying_period');
}
@@ -125,6 +127,21 @@ class PersonMenuBuilder implements LocalMenuBuilderInterface
? $nbAccompanyingPeriod : null,
]);
}
$nbSignatures = count($this->entityWorkflowStepSignatureRepository->findByPersonAndPendingState($parameters['person']));
if ($nbSignatures > 0) {
$menu->addChild($this->translator->trans('workflow.signature_list.menu'), [
'route' => 'chill_person_signature_list',
'routeParameters' => [
'id' => $parameters['person']->getId(),
],
])
->setExtras([
'order' => 110,
'counter' => $nbSignatures,
]);
}
}
public static function getMenuIds(): array

View File

@@ -11,12 +11,15 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Repository\AccompanyingPeriod;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\DocStoreBundle\Repository\AssociatedEntityToStoredObjectInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\NonUniqueResultException;
use Doctrine\Persistence\ObjectRepository;
class AccompanyingPeriodWorkEvaluationDocumentRepository implements ObjectRepository
class AccompanyingPeriodWorkEvaluationDocumentRepository implements ObjectRepository, AssociatedEntityToStoredObjectInterface
{
private readonly EntityRepository $repository;
@@ -58,4 +61,18 @@ class AccompanyingPeriodWorkEvaluationDocumentRepository implements ObjectReposi
{
return AccompanyingPeriodWorkEvaluationDocument::class;
}
/**
* @throws NonUniqueResultException
*/
public function findAssociatedEntityToStoredObject(StoredObject $storedObject): ?AccompanyingPeriodWorkEvaluationDocument
{
$qb = $this->repository->createQueryBuilder('acpwed');
$query = $qb
->where('acpwed.storedObject = :storedObject')
->setParameter('storedObject', $storedObject)
->getQuery();
return $query->getOneOrNullResult();
}
}

View File

@@ -22,11 +22,11 @@ use Doctrine\ORM\Query\ResultSetMappingBuilder;
use Doctrine\ORM\QueryBuilder;
use Doctrine\Persistence\ObjectRepository;
final readonly class AccompanyingPeriodWorkRepository implements ObjectRepository
class AccompanyingPeriodWorkRepository implements ObjectRepository
{
private EntityRepository $repository;
private readonly EntityRepository $repository;
public function __construct(private EntityManagerInterface $em)
public function __construct(private readonly EntityManagerInterface $em)
{
$this->repository = $em->getRepository(AccompanyingPeriodWork::class);
}

View File

@@ -3,8 +3,10 @@
*/
span.badge-user,
span.badge-user-group,
span.badge-person,
span.badge-thirdparty {
margin: 0.2rem 0.1rem;
display: inline-block;
padding: 0 0.5em !important;
background-color: $white;
@@ -18,6 +20,11 @@ span.badge-thirdparty {
}
}
span.badge-user-group {
font-weight: 600;
border-width: 0px;
}
span.badge-user {
border-bottom-width: 1px;
&.system {
@@ -231,6 +238,10 @@ div[class*='budget-'] {
background-color: $chill-ll-gray;
color: $chill-blue;
}
&.bg-user-group {
background-color: $chill-l-gray;
color: $chill-blue;
}
&.bg-confidential {
background-color: $chill-ll-gray;
color: $chill-red;

View File

@@ -1,4 +1,12 @@
import {Address, Center, Civility, DateTime} from "../../../ChillMainBundle/Resources/public/types";
import {
Address,
Center,
Civility,
DateTime,
User,
WorkflowAvailable
} from "../../../ChillMainBundle/Resources/public/types";
import {StoredObject} from "../../../ChillDocStoreBundle/Resources/public/types";
export interface Person {
id: number;
@@ -20,3 +28,16 @@ export interface Person {
current_household_id: number;
current_residential_addresses: Address[];
}
export interface AccompanyingPeriodWorkEvaluationDocument {
id: number;
type: "accompanying_period_work_evaluation_document"
storedObject: StoredObject;
title: string;
createdAt: DateTime | null;
createdBy: User | null;
updatedAt: DateTime | null;
updatedBy: User | null;
workflows_availables: WorkflowAvailable[];
workflows: object[];
}

View File

@@ -340,9 +340,8 @@ import ThirdPartyRenderBox from 'ChillThirdPartyAssets/vuejs/_components/Entity/
import PickTemplate from 'ChillDocGeneratorAssets/vuejs/_components/PickTemplate.vue';
import OnTheFly from 'ChillMainAssets/vuejs/OnTheFly/components/OnTheFly.vue';
import ListWorkflowModal from 'ChillMainAssets/vuejs/_components/EntityWorkflow/ListWorkflowModal.vue';
import PickWorkflow from 'ChillMainAssets/vuejs/_components/EntityWorkflow/PickWorkflow.vue';
import PersonText from 'ChillPersonAssets/vuejs/_components/Entity/PersonText.vue';
import {buildLinkCreate} from 'ChillMainAssets/lib/entity-workflow/api.js';
import {buildLinkCreate} from 'ChillMainAssets/lib/entity-workflow/api';
import { makeFetch } from 'ChillMainAssets/lib/api/apiMethods';
const i18n = {
@@ -406,7 +405,6 @@ export default {
PickTemplate,
ListWorkflowModal,
OnTheFly,
PickWorkflow,
PersonText,
},
i18n,

View File

@@ -54,7 +54,7 @@
import FormEvaluation from './FormEvaluation.vue';
import Modal from 'ChillMainAssets/vuejs/_components/Modal';
import ListWorkflowModal from 'ChillMainAssets/vuejs/_components/EntityWorkflow/ListWorkflowModal.vue';
import {buildLinkCreate} from 'ChillMainAssets/lib/entity-workflow/api.js';
import {buildLinkCreate} from 'ChillMainAssets/lib/entity-workflow/api';
const i18n = {
messages: {

View File

@@ -103,7 +103,7 @@
<div class="item-row">
<div class="item-col">
<ul class="record_actions">
<li v-if="d.workflows_availables.length > 0">
<li v-if="d.workflows_availables.length > 0 || d.workflows.length > 0">
<list-workflow-modal
:workflows="d.workflows"
:allowCreate="true"
@@ -140,19 +140,16 @@
@on-stored-object-status-change="onStatusDocumentChanged"
></document-action-buttons-group>
</li>
<li>
<add-async-upload
:buttonTitle="$t('replace')"
:options="asyncUploadOptions"
:btnClasses="{'btn': true, 'btn-edit': true}"
@addDocument="(arg) => replaceDocument(d, arg)"
>
</add-async-upload>
<li v-if="d.storedObject._permissions.canEdit">
<drop-file-modal :existing-doc="d.storedObject" :allow-remove="false" @add-document="(arg) => replaceDocument(d, arg.stored_object, arg.stored_object_version)"></drop-file-modal>
</li>
<li v-if="d.workflows.length === 0">
<a class="btn btn-delete" @click="removeDocument(d)">
</a>
</li>
<li v-if="Number.isInteger(d.id)">
<button type="button" @click="duplicateDocument(d)" class="btn btn-duplicate" title="Dupliquer"></button>
</li>
</ul>
</div>
</div>
@@ -177,12 +174,7 @@
<label class="col-form-label">{{ $t('document_upload') }}</label>
<ul class="record_actions document-upload">
<li>
<add-async-upload
:buttonTitle="$t('browse')"
:options="asyncUploadOptions"
@addDocument="addDocument"
>
</add-async-upload>
<drop-file-modal :allow-remove="false" @add-document="addDocument"></drop-file-modal>
</li>
</ul>
</div>
@@ -199,12 +191,11 @@ import ClassicEditor from 'ChillMainAssets/module/ckeditor5/editor_config';
import { mapGetters, mapState } from 'vuex';
import PickTemplate from 'ChillDocGeneratorAssets/vuejs/_components/PickTemplate.vue';
import {buildLink} from 'ChillDocGeneratorAssets/lib/document-generator';
import AddAsyncUpload from 'ChillDocStoreAssets/vuejs/_components/AddAsyncUpload.vue';
import AddAsyncUploadDownloader from 'ChillDocStoreAssets/vuejs/_components/AddAsyncUploadDownloader.vue';
import ListWorkflowModal from 'ChillMainAssets/vuejs/_components/EntityWorkflow/ListWorkflowModal.vue';
import {buildLinkCreate} from 'ChillMainAssets/lib/entity-workflow/api.js';
import {buildLinkCreate} from 'ChillMainAssets/lib/entity-workflow/api';
import {buildLinkCreate as buildLinkCreateNotification} from 'ChillMainAssets/lib/entity-notification/api';
import DocumentActionButtonsGroup from "ChillDocStoreAssets/vuejs/DocumentActionButtonsGroup.vue";
import DropFileModal from "ChillDocStoreAssets/vuejs/DropFileWidget/DropFileModal.vue";
const i18n = {
messages: {
@@ -243,10 +234,9 @@ export default {
name: "FormEvaluation",
props: ['evaluation', 'docAnchorId'],
components: {
DropFileModal,
ckeditor: CKEditor.component,
PickTemplate,
AddAsyncUpload,
AddAsyncUploadDownloader,
ListWorkflowModal,
DocumentActionButtonsGroup,
},
@@ -380,27 +370,38 @@ export default {
const title = event.target.value;
this.$store.commit('updateDocumentTitle', {id: id, key: key, evaluationKey: this.evaluation.key, title: title});
},
addDocument(storedObject) {
addDocument({stored_object, stored_object_version}) {
let document = {
type: 'accompanying_period_work_evaluation_document',
storedObject: storedObject,
storedObject: stored_object,
title: 'Nouveau document',
};
this.$store.commit('addDocument', {key: this.evaluation.key, document: document});
this.$store.commit('addDocument', {key: this.evaluation.key, document, stored_object_version});
},
replaceDocument(oldDocument, storedObject) {
/**
* Replaces a document in the store with a new document.
*
* @param {Object} oldDocument - The document to be replaced.
* @param {StoredObject} storedObject - The stored object of the new document.
* @param {StoredObjectVersion} storedObjectVersion - The new version of the document
* @return {void}
*/
replaceDocument(oldDocument, storedObject, storedObjectVersion) {
let document = {
type: 'accompanying_period_work_evaluation_document',
storedObject: storedObject,
title: oldDocument.title
};
this.$store.commit('replaceDocument', {key: this.evaluation.key, document: document, oldDocument: oldDocument});
this.$store.commit('replaceDocument', {key: this.evaluation.key, document, oldDocument: oldDocument, stored_object_version: storedObjectVersion});
},
removeDocument(document) {
if (window.confirm("Êtes-vous sûr·e de vouloir supprimer le document qui a pour titre \"" + document.title +"\" ?")) {
this.$store.commit('removeDocument', {key: this.evaluation.key, document: document});
}
},
duplicateDocument(document) {
this.$store.dispatch('duplicateDocument', {evaluation_key: this.evaluation.key, document: document});
},
onStatusDocumentChanged(newStatus) {
console.log('onStatusDocumentChanged', newStatus);
this.$store.commit('statusDocumentChanged', {key: this.evaluation.key, newStatus: newStatus});

View File

@@ -4,6 +4,7 @@ import { findSocialActionsBySocialIssue } from 'ChillPersonAssets/vuejs/_api/Soc
import { create } from 'ChillPersonAssets/vuejs/_api/AccompanyingCourseWork.js';
import { fetchResults, makeFetch } from 'ChillMainAssets/lib/api/apiMethods.ts';
import { fetchTemplates } from 'ChillDocGeneratorAssets/api/pickTemplate.js';
import { duplicate } from '../_api/accompanyingCourseWorkEvaluationDocument';
const debug = process.env.NODE_ENV !== 'production';
const evalFQDN = encodeURIComponent("Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWorkEvaluation");
@@ -219,7 +220,11 @@ const store = createStore({
found.results = found.results.filter(r => r.id !== result.id);
},
addDocument(state, payload) {
// associate version to stored object
payload.document.storedObject.currentVersion = payload.stored_object_version;
let evaluation = state.evaluationsPicked.find(e => e.key === payload.key);
evaluation.documents.push(Object.assign(
payload.document, {
key: evaluation.documents.length + 1,
@@ -234,6 +239,21 @@ const store = createStore({
}
evaluation.documents = evaluation.documents.filter(d => d.key !== document.key);
},
addDuplicatedDocument(state, {document, evaluation_key}) {
console.log('add duplicated document', document);
console.log('add duplicated dcuemnt - evaluation key', evaluation_key);
let evaluation = state.evaluationsPicked.find(e => e.key === evaluation_key);
document.key = evaluation.documents.length + 1;
evaluation.documents.splice(0, 0, document);
},
/**
* Replaces a document in the state with a new document.
*
* @param {object} state - The current state of the application.
* @param {{key: number, oldDocument: {key: number}, stored_object_version: StoredObjectVersion}} payload - The object containing the information about the document to be replaced.
* @return {void} - returns nothing.
*/
replaceDocument(state, payload) {
let evaluation = state.evaluationsPicked.find(e => e.key === payload.key);
if (evaluation === undefined) {
@@ -244,9 +264,10 @@ const store = createStore({
if (typeof doc === 'undefined') {
console.error('doc not found');
return;
}
doc.storedObject = payload.document.storedObject;
doc.storedObject.currentVersion = payload.stored_object_version;
return;
let newDocument = Object.assign(
payload.document, {
@@ -494,6 +515,10 @@ const store = createStore({
addDocument({commit}, payload) {
commit('addDocument', payload);
},
async duplicateDocument({commit}, {document, evaluation_key}) {
const newDoc = await duplicate(document.id);
commit('addDuplicatedDocument', {document: newDoc, evaluation_key});
},
removeDocument({commit}, payload) {
commit('removeDocument', payload);
},

View File

@@ -0,0 +1,6 @@
import {AccompanyingPeriodWorkEvaluationDocument} from "../../types";
import {makeFetch} from "../../../../../ChillMainBundle/Resources/public/lib/api/apiMethods";
export const duplicate = async (id: number): Promise<AccompanyingPeriodWorkEvaluationDocument> => {
return makeFetch<null, AccompanyingPeriodWorkEvaluationDocument>("POST", `/api/1.0/person/accompanying-course-work-evaluation-document/${id}/duplicate`);
}

View File

@@ -27,6 +27,11 @@
v-bind:item="item">
</suggestion-user>
<suggestion-user-group
v-if="item.result.type === 'user_group'"
v-bind:item="item">
></suggestion-user-group>
<suggestion-household
v-if="item.result.type === 'household'"
v-bind:item="item">
@@ -41,6 +46,7 @@ import SuggestionPerson from './TypePerson';
import SuggestionThirdParty from './TypeThirdParty';
import SuggestionUser from './TypeUser';
import SuggestionHousehold from './TypeHousehold';
import SuggestionUserGroup from './TypeUserGroup';
export default {
name: 'PersonSuggestion',
@@ -49,6 +55,7 @@ export default {
SuggestionThirdParty,
SuggestionUser,
SuggestionHousehold,
SuggestionUserGroup,
},
props: [
'item',

View File

@@ -0,0 +1,30 @@
<script setup lang="ts">
import {ResultItem, UserGroup} from "../../../../../../ChillMainBundle/Resources/public/types";
import BadgeEntity from "ChillMainAssets/vuejs/_components/BadgeEntity.vue";
import UserRenderBoxBadge from "ChillMainAssets/vuejs/_components/Entity/UserRenderBoxBadge.vue";
import UserGroupRenderBox from "ChillMainAssets/vuejs/_components/Entity/UserGroupRenderBox.vue";
interface TypeUserGroupProps {
item: ResultItem<UserGroup>;
}
const props = defineProps<TypeUserGroupProps>();
</script>
<template>
<div class="container user-group-container">
<div class="user-group-identification">
<user-group-render-box :user-group="props.item.result"></user-group-render-box>
</div>
</div>
<div class="right_actions">
<span class="badge rounded-pill bg-user-group">
Groupe d'utilisateur
</span>
</div>
</template>
<style scoped lang="scss">
</style>

View File

@@ -211,7 +211,7 @@
<h3 class="chill-beige">Public</h3>
{% if w.note is not empty %}
<blockquote class="chill-user-quote">
{{ w.note|chill_entity_render_box({'metadata': true }) }}
{{ w.note }}
</blockquote>
{% else %}
<span class="chill-no-data-statement">{{ 'No comment associated'|trans }}</span>

View File

@@ -237,8 +237,7 @@
{% if displayContent is defined and displayContent == 'long' %}
{% if e.comment is not empty %}
<blockquote
class="chill-user-quote">{{ e.comment|chill_entity_render_box }}</blockquote>
<blockquote class="chill-user-quote">{{ e.comment }}</blockquote>
{% endif %}
{% import "@ChillDocStore/Macro/macro.html.twig" as m %}

View File

@@ -60,9 +60,11 @@
{{ document.storedObject|chill_document_button_group(document.title, is_granted('CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_UPDATE', document.accompanyingPeriodWorkEvaluation.accompanyingPeriodWork)) }}
</li>
{% endif %}
{% if is_granted('CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_SEE', w)%}
{% if is_granted('CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_UPDATE', w) %}
<li>
<a href="{{ chill_path_add_return_path('chill_person_accompanying_period_work_show', {'id': w.id, 'docId': document.id}) }}" class="btn btn-show"></a>
<form method="post" action="{{ chill_path_add_return_path('chill_person_accompanying_period_work_evaluation_document_duplicate', {id: document.id}) }}">
<button type="submit" class="btn btn-duplicate" title="{{ 'crud.view.link_duplicate'|trans }}"></button>
</form>
</li>
{% endif %}
{% if is_granted('CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_UPDATE', w) %}
@@ -70,6 +72,11 @@
<a href="{{ chill_path_add_return_path('chill_person_accompanying_period_work_edit', {'id': w.id, 'docId': document.id}) }}" class="btn btn-edit"></a>
</li>
{% endif %}
{% if is_granted('CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_SEE', w)%}
<li>
<a href="{{ chill_path_add_return_path('chill_person_accompanying_period_work_show', {'id': w.id, 'docId': document.id}) }}" class="btn btn-show"></a>
</li>
{% endif %}
</ul>
</div>

View File

@@ -328,8 +328,33 @@
</div>
</div>
{% endif %}
{% if person.signaturesPending|length > 0 %}
<div class="item-row separator">
<div class="wrap-list periods-list">
<div class="wl-row">
<div class="wl-col title">
<h3>{{ 'workflow.pending_signatures'|trans({nb_signatures: person.signaturesPending|length}) }}</h3>
</div>
<div class="wl-col list">
{% for signature in person.signaturesPending %}
{% set entityWorkflow = signature.step.entityWorkflow %}
{{ entityWorkflow|chill_entity_render_string }}
<ul class="record_actions small slim">
<li>
<a href="{{ chill_path_force_return_path(path('chill_main_workflow_show', {id: entityWorkflow.id}), 'chill_main_workflow_signature_metadata', {signature_id: signature.id}) }}" class="btn btn-misc">
<i class="fa fa-pencil"></i>
</a>
</li>
<li>
<a href="{{ chill_path_add_return_path('chill_main_workflow_show', {id: entityWorkflow.id}) }}" class="btn btn-show"></a>
</li>
</ul>
{% endfor %}
</div>
</div>
</div>
</div>
{% endif %}
</div>
{% endfor %}
</div>

View File

@@ -0,0 +1,44 @@
{% extends "@ChillPerson/Person/layout.html.twig" %}
{% set activeRouteKey = 'chill_person_signature_list' %}
{% block title %}{{ 'Person signatures'|trans ~ ' ' ~ person|chill_entity_render_string }}{% endblock %}
{% block dam %}
<a class="btn btn-misc" href="{{ chill_path_add_return_path('chill_main_workflow_signature_metadata', { 'signature_id': s.signature.id}) }}"><i class="fa fa-pencil-square-o"></i> {{ 'workflow.signature_zone.button_sign'|trans }}</a>
{% endblock %}
{% block content %}
<h1>{{ 'workflow.signature_list.title'|trans }}</h1>
<div class="container">
<div class="row align-items-center">
{% for s in signatures %}
{% set signature = s.signature %}
{% set workflow = s.workflow %}
{% set document = s.document %}
<div class="item-bloc">
<div class="item-row">
<div class="item-col flex-grow-1">
{% include workflow.handler_template with workflow.handler_template_data|merge({'display_action': true, 'display_action_more': [block('dam')|raw] }) %}
</div>
</div>
</div>
{% endfor %}
</div>
</div>
{% endblock %}
{% block css %}
{{ parent() }}
{{ encore_entry_link_tags('mod_async_upload') }}
{{ encore_entry_script_tags('mod_document_action_buttons_group') }}
{% endblock css %}
{% block js %}
{{ parent() }}
{{ encore_entry_script_tags('mod_async_upload') }}
{{ encore_entry_link_tags('mod_document_action_buttons_group') }}
{% endblock js %}

View File

@@ -13,6 +13,11 @@
</div>
{% if display_action is defined and display_action == true %}
<ul class="record_actions">
{% for dam in display_action_more|default([]) %}
<li>
{{ dam|raw }}
</li>
{% endfor %}
<li>
<a class="btn btn-update"
href="{{ chill_path_add_return_path('chill_person_accompanying_period_work_edit', { 'id': work.id }) }}">

View File

@@ -94,6 +94,11 @@
{% if display_action is defined and display_action == true %}
{# TODO add acl #}
<ul class="record_actions">
{% for dam in display_action_more|default([]) %}
<li>
{{ dam|raw }}
</li>
{% endfor %}
<li>
<a class="btn btn-show" href="{{ path('chill_person_accompanying_period_work_edit', {'id': evaluation.accompanyingPeriodWork.id}) }}">
{{ 'Show'|trans }}

View File

@@ -121,6 +121,11 @@
{% if display_action is defined and display_action == true %}
<ul class="record_actions">
{% for dam in display_action_more|default([]) %}
<li>
{{ dam|raw }}
</li>
{% endfor %}
<li>{{ doc.storedObject|chill_document_button_group(doc.title, is_granted('CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_UPDATE', evaluation.accompanyingPeriodWork)) }}</li>
<li>
<a class="btn btn-show" href="{{ path('chill_person_accompanying_period_work_edit', {'id': evaluation.accompanyingPeriodWork.id, 'doc_id': doc.id}) }}">

View File

@@ -24,13 +24,14 @@ use Symfony\Component\Security\Core\Authorization\Voter\Voter;
class AccompanyingPeriodWorkEvaluationDocumentVoter extends Voter
{
final public const SEE = 'CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_EVALUATION_DOCUMENT_SHOW';
final public const SEE_AND_EDIT = 'CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_EVALUATION_DOCUMENT_EDIT';
public function __construct(private readonly AccessDecisionManagerInterface $accessDecisionManager) {}
protected function supports($attribute, $subject)
public function supports($attribute, $subject): bool
{
return $subject instanceof AccompanyingPeriodWorkEvaluationDocument
&& self::SEE === $attribute;
&& (self::SEE === $attribute || self::SEE_AND_EDIT === $attribute);
}
/**
@@ -39,7 +40,7 @@ class AccompanyingPeriodWorkEvaluationDocumentVoter extends Voter
*
* @return bool|void
*/
protected function voteOnAttribute($attribute, $subject, TokenInterface $token): bool
public function voteOnAttribute($attribute, $subject, TokenInterface $token): bool
{
return match ($attribute) {
self::SEE => $this->accessDecisionManager->decide(
@@ -47,6 +48,11 @@ class AccompanyingPeriodWorkEvaluationDocumentVoter extends Voter
[AccompanyingPeriodWorkEvaluationVoter::SEE],
$subject->getAccompanyingPeriodWorkEvaluation()
),
self::SEE_AND_EDIT => $this->accessDecisionManager->decide(
$token,
[AccompanyingPeriodWorkEvaluationVoter::SEE_AND_EDIT],
$subject->getAccompanyingPeriodWorkEvaluation()
),
default => throw new \UnexpectedValueException("The attribute {$attribute} is not supported"),
};
}

View File

@@ -21,11 +21,14 @@ class AccompanyingPeriodWorkEvaluationVoter extends Voter implements ChillVoterI
{
final public const ALL = [
self::SEE,
self::SEE_AND_EDIT,
self::STATS,
];
final public const SEE = 'CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_EVALUATION_SHOW';
final public const SEE_AND_EDIT = 'CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_EVALUATION_EDIT';
final public const STATS = 'CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_EVALUATION_STATS';
public function __construct(private readonly Security $security) {}
@@ -45,6 +48,7 @@ class AccompanyingPeriodWorkEvaluationVoter extends Voter implements ChillVoterI
return match ($attribute) {
self::STATS => $this->security->isGranted(AccompanyingPeriodVoter::STATS, $subject),
self::SEE => $this->security->isGranted(AccompanyingPeriodWorkVoter::SEE, $subject->getAccompanyingPeriodWork()),
self::SEE_AND_EDIT => $this->security->isGranted(AccompanyingPeriodWorkVoter::UPDATE, $subject->getAccompanyingPeriodWork()),
default => throw new \UnexpectedValueException("attribute {$attribute} is not supported"),
};
}

View File

@@ -0,0 +1,55 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Security\Authorization\StoredObjectVoter;
use Chill\DocStoreBundle\Repository\AssociatedEntityToStoredObjectInterface;
use Chill\DocStoreBundle\Security\Authorization\StoredObjectRoleEnum;
use Chill\DocStoreBundle\Security\Authorization\StoredObjectVoter\AbstractStoredObjectVoter;
use Chill\DocStoreBundle\Service\WorkflowStoredObjectPermissionHelper;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument;
use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocumentRepository;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodWorkEvaluationDocumentVoter;
use Symfony\Component\Security\Core\Security;
class AccompanyingPeriodWorkEvaluationDocumentStoredObjectVoter extends AbstractStoredObjectVoter
{
public function __construct(
private readonly AccompanyingPeriodWorkEvaluationDocumentRepository $repository,
Security $security,
WorkflowStoredObjectPermissionHelper $workflowDocumentService,
) {
parent::__construct($security, $workflowDocumentService);
}
protected function getRepository(): AssociatedEntityToStoredObjectInterface
{
return $this->repository;
}
protected function getClass(): string
{
return AccompanyingPeriodWorkEvaluationDocument::class;
}
protected function attributeToRole(StoredObjectRoleEnum $attribute): string
{
return match ($attribute) {
StoredObjectRoleEnum::SEE => AccompanyingPeriodWorkEvaluationDocumentVoter::SEE,
StoredObjectRoleEnum::EDIT => AccompanyingPeriodWorkEvaluationDocumentVoter::SEE_AND_EDIT,
};
}
protected function canBeAssociatedWithWorkflow(): bool
{
return true;
}
}

View File

@@ -0,0 +1,40 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Security\CenterResolver;
use Chill\MainBundle\Entity\Center;
use Chill\MainBundle\Security\Resolver\CenterResolverInterface;
use Chill\MainBundle\Security\Resolver\ManagerAwareCenterResolverInterface;
use Chill\MainBundle\Security\Resolver\ManagerAwareCenterResolverTrait;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument;
final class AccompanyingPeriodWorkEvaluationDocumentCenterResolver implements CenterResolverInterface, ManagerAwareCenterResolverInterface
{
use ManagerAwareCenterResolverTrait;
public static function getDefaultPriority(): int
{
return 0;
}
public function resolveCenter($entity, ?array $options = []): Center|array
{
/* @var $entity AccompanyingPeriodWorkEvaluationDocument */
return $this->centerResolverManager->resolveCenters($entity->getAccompanyingPeriodWorkEvaluation()
->getAccompanyingPeriodWork()->getAccompanyingPeriod(), $options);
}
public function supports($entity, ?array $options = []): bool
{
return $entity instanceof AccompanyingPeriodWorkEvaluationDocument;
}
}

View File

@@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Service\AccompanyingPeriod;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation;
use Chill\PersonBundle\Entity\Person;
/**
* Provide persons associated with an accompanying period.
*/
class ProvidePersonsAssociated
{
/**
* @return list<Person>
*/
public function getPersonsAssociated(AccompanyingPeriod $period): array
{
return array_values(
$period->getCurrentParticipations()
->map(fn (AccompanyingPeriodParticipation $participation) => $participation->getPerson())
->toArray()
);
}
}

View File

@@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Service\AccompanyingPeriod;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\AccompanyingPeriod\Resource;
use Chill\ThirdPartyBundle\Entity\ThirdParty;
/**
* Provide third parties participating to an AccompanyingPeriod.
*/
class ProvideThirdPartiesAssociated
{
/**
* @return list<ThirdParty>
*/
public function getThirdPartiesAssociated(AccompanyingPeriod $period): array
{
$thirdParties = [];
foreach (
$period
->getResources()->filter(fn (Resource $resource) => null !== $resource->getThirdParty())
->map(fn (Resource $resource) => $resource->getThirdParty()) as $thirdParty) {
$thirdParties[] = $thirdParty;
}
if (null !== $requestor = $period->getRequestorThirdParty()) {
$thirdParties[] = $requestor;
}
return array_values(
// filter objects to remove duplicates
array_filter(
$thirdParties,
fn ($o, $k) => array_search($o, $thirdParties, true) === $k,
ARRAY_FILTER_USE_BOTH
)
);
}
}

View File

@@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Service\AccompanyingPeriodWork;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Service\AccompanyingPeriod\ProvidePersonsAssociated as ProvidePersonsAssociatedAccompanyingPeriod;
/**
* Provide persons associated with an accompanying period work.
*/
class ProvidePersonsAssociated
{
public function __construct(private readonly ProvidePersonsAssociatedAccompanyingPeriod $providePersonsAssociated) {}
/**
* @return list<Person>
*/
public function getPersonsAssociated(AccompanyingPeriod\AccompanyingPeriodWork $work): array
{
return $this->providePersonsAssociated->getPersonsAssociated($work->getAccompanyingPeriod());
}
}

View File

@@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Service\AccompanyingPeriodWork;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
use Chill\PersonBundle\Service\AccompanyingPeriod\ProvideThirdPartiesAssociated as ProvideThirdPartiesAssociatedAccompanyingPeriod;
use Chill\ThirdPartyBundle\Entity\ThirdParty;
/**
* Provide third parties associated with an accompanying period work.
*/
class ProvideThirdPartiesAssociated
{
public function __construct(private readonly ProvideThirdPartiesAssociatedAccompanyingPeriod $thirdPartiesAssociated) {}
/**
* @return list<ThirdParty>
*/
public function getThirdPartiesAssociated(AccompanyingPeriodWork $accompanyingPeriodWork): array
{
$thirdParties = $this->thirdPartiesAssociated->getThirdPartiesAssociated($accompanyingPeriodWork->getAccompanyingPeriod());
if (null !== $tp = $accompanyingPeriodWork->getHandlingThierParty()) {
$thirdParties[] = $tp;
}
foreach ($accompanyingPeriodWork->getThirdParties() as $thirdParty) {
$thirdParties[] = $thirdParty;
}
return array_values(
// filter objects to remove duplicates
array_filter(
$thirdParties,
fn ($o, $k) => array_search($o, $thirdParties, true) === $k,
ARRAY_FILTER_USE_BOTH
)
);
}
}

View File

@@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Service\AccompanyingPeriodWorkEvaluationDocument;
use Chill\DocStoreBundle\Service\StoredObjectDuplicate;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument;
use Symfony\Component\Clock\ClockInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
class AccompanyingPeriodWorkEvaluationDocumentDuplicator
{
public function __construct(
private readonly StoredObjectDuplicate $storedObjectDuplicate,
private readonly TranslatorInterface $translator,
private readonly ClockInterface $clock,
) {}
public function duplicate(AccompanyingPeriodWorkEvaluationDocument $document): AccompanyingPeriodWorkEvaluationDocument
{
$newDocument = new AccompanyingPeriodWorkEvaluationDocument();
$newDocument
->setTitle($document->getTitle().' ('.$this->translator->trans('accompanying_course_evaluation_document.duplicated_at', ['at' => $this->clock->now()]).')')
->setStoredObject($this->storedObjectDuplicate->duplicate($document->getStoredObject()))
;
$document->getAccompanyingPeriodWorkEvaluation()->addDocument($newDocument);
return $newDocument;
}
}

View File

@@ -0,0 +1,86 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Tests\Controller;
use Chill\MainBundle\Controller\WorkflowSignatureCancelController;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
use Chill\MainBundle\Routing\ChillUrlGeneratorInterface;
use Chill\MainBundle\Security\Authorization\EntityWorkflowStepSignatureVoter;
use Chill\MainBundle\Workflow\SignatureStepStateChanger;
use Chill\MainBundle\Workflow\WorkflowTransitionContextDTO;
use Doctrine\ORM\EntityManager;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Routing\RequestContext;
use Symfony\Component\Security\Core\Security;
use Twig\Environment;
/**
* @internal
*
* @coversNothing
*/
class WorkflowSignatureCancelControllerStepTest extends WebTestCase
{
private FormFactoryInterface $formFactory;
private SignatureStepStateChanger $signatureStepStateChanger;
private ChillUrlGeneratorInterface $chillUrlGenerator;
private RequestStack $requestStack;
protected function setUp(): void
{
self::bootKernel();
$this->formFactory = self::getContainer()->get('form.factory');
$this->signatureStepStateChanger = self::getContainer()->get(SignatureStepStateChanger::class);
$this->chillUrlGenerator = self::getContainer()->get(ChillUrlGeneratorInterface::class);
$requestContext = self::getContainer()->get(RequestContext::class);
$requestContext->setParameter('_locale', 'fr');
$this->requestStack = self::getContainer()->get(RequestStack::class);
}
public function testCancelSignatureGet(): void
{
$entityWorkflow = new EntityWorkflow();
$dto = new WorkflowTransitionContextDTO($entityWorkflow);
$dto->futureUserSignature = new User();
$entityWorkflow->setStep('signature', $dto, 'to_signature', new \DateTimeImmutable(), new User());
$signature = $entityWorkflow->getCurrentStep()->getSignatures()->first();
$security = $this->createMock(Security::class);
$security->expects($this->once())->method('isGranted')
->with(EntityWorkflowStepSignatureVoter::CANCEL, $signature)->willReturn(true);
$entityManager = $this->createMock(EntityManager::class);
$twig = $this->createMock(Environment::class);
$twig->expects($this->once())->method('render')->withAnyParameters()
->willReturn('template');
$controller = new WorkflowSignatureCancelController($entityManager, $security, $this->formFactory, $twig, $this->signatureStepStateChanger, $this->chillUrlGenerator);
$request = new Request();
$request->setMethod('GET');
$this->requestStack->push($request);
$response = $controller->cancelSignature($signature, $request);
self::assertEquals(200, $response->getStatusCode());
}
}

View File

@@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Tests\Service\AccompanyingPeriod;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Service\AccompanyingPeriod\ProvideThirdPartiesAssociated;
use Chill\ThirdPartyBundle\Entity\ThirdParty;
use PHPUnit\Framework\TestCase;
/**
* @internal
*
* @coversNothing
*/
class ProvideThirdPartiesAssociatedTest extends TestCase
{
public function testGetThirdPartiesAssociated()
{
$period = new AccompanyingPeriod();
$period->setRequestor($tp1 = new ThirdParty());
$period->addResource((new AccompanyingPeriod\Resource())->setResource($tp1));
$period->addResource((new AccompanyingPeriod\Resource())->setResource($tp2 = new ThirdParty()));
$period->addResource((new AccompanyingPeriod\Resource())->setResource($p1 = new Person()));
$provider = new ProvideThirdPartiesAssociated();
$thirdParties = $provider->getThirdPartiesAssociated($period);
self::assertCount(2, $thirdParties);
self::assertContains($tp1, $thirdParties);
self::assertContains($tp2, $thirdParties);
self::assertNotContains($p1, $thirdParties);
self::assertNotContains(null, $thirdParties);
}
}

View File

@@ -0,0 +1,52 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Tests\Service\AccompanyingPeriodWork;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Service\AccompanyingPeriodWork\ProvideThirdPartiesAssociated;
use Chill\ThirdPartyBundle\Entity\ThirdParty;
use PHPUnit\Framework\TestCase;
/**
* @internal
*
* @coversNothing
*/
class ProvideThirdPartiesAssociatedTest extends TestCase
{
public function testGetThirdPartiesAssociated()
{
$period = new AccompanyingPeriod();
$period->setRequestor($tp1 = new ThirdParty());
$period->addResource((new AccompanyingPeriod\Resource())->setResource($tp1));
$period->addResource((new AccompanyingPeriod\Resource())->setResource($tp2 = new ThirdParty()));
$period->addResource((new AccompanyingPeriod\Resource())->setResource($p1 = new Person()));
$period->addWork($work = new AccompanyingPeriod\AccompanyingPeriodWork());
$work->addThirdParty($tp3 = new ThirdParty());
$work->addThirdParty($tp1);
$work->setHandlingThierParty($tp4 = new ThirdParty());
$providerAccPeriod = new \Chill\PersonBundle\Service\AccompanyingPeriod\ProvideThirdPartiesAssociated();
$provider = new ProvideThirdPartiesAssociated($providerAccPeriod);
$thirdParties = $provider->getThirdPartiesAssociated($work);
self::assertContains($tp1, $thirdParties);
self::assertContains($tp2, $thirdParties);
self::assertContains($tp3, $thirdParties);
self::assertContains($tp4, $thirdParties);
self::assertNotContains($p1, $thirdParties);
self::assertCount(4, $thirdParties);
}
}

View File

@@ -0,0 +1,57 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Tests\Service\AccompanyingPeriodWorkEvaluationDocument;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\DocStoreBundle\Service\StoredObjectDuplicate;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument;
use Chill\PersonBundle\Service\AccompanyingPeriodWorkEvaluationDocument\AccompanyingPeriodWorkEvaluationDocumentDuplicator;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Clock\MockClock;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
* @internal
*
* @coversNothing
*/
class AccompanyingPeriodWorkEvaluationDocumentDuplicatorTest extends TestCase
{
public function testDuplicate()
{
$storedObject = new StoredObject();
$evaluation = new AccompanyingPeriodWorkEvaluation();
$document = new AccompanyingPeriodWorkEvaluationDocument();
$document
->setStoredObject($storedObject)
->setTitle('test title')
->setAccompanyingPeriodWorkEvaluation($evaluation);
$storedObjectDuplicator = $this->createMock(StoredObjectDuplicate::class);
$storedObjectDuplicator->expects($this->once())->method('duplicate')->with($storedObject)->willReturn($newStoredObject = new StoredObject());
$translator = $this->createMock(TranslatorInterface::class);
$translator->method('trans')->willReturn('test translated');
$clock = new MockClock();
$duplicator = new AccompanyingPeriodWorkEvaluationDocumentDuplicator($storedObjectDuplicator, $translator, $clock);
$actual = $duplicator->duplicate($document);
self::assertNotSame($document, $actual);
self::assertStringStartsWith('test title', $actual->getTitle());
self::assertSame($newStoredObject, $actual->getStoredObject());
self::assertSame($evaluation, $actual->getAccompanyingPeriodWorkEvaluation());
}
}

View File

@@ -0,0 +1,86 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Tests\Workflow;
use Chill\DocStoreBundle\Workflow\WorkflowWithPublicViewDocumentHelper;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
use Chill\MainBundle\Repository\Workflow\EntityWorkflowRepository;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument;
use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocumentRepository;
use Chill\PersonBundle\Service\AccompanyingPeriodWork\ProvidePersonsAssociated;
use Chill\PersonBundle\Service\AccompanyingPeriodWork\ProvideThirdPartiesAssociated;
use Chill\PersonBundle\Workflow\AccompanyingPeriodWorkEvaluationDocumentWorkflowHandler;
use PHPUnit\Framework\TestCase;
use Prophecy\PhpUnit\ProphecyTrait;
use Symfony\Contracts\Translation\TranslatorInterface;
use Twig\Environment;
/**
* @internal
*
* @coversNothing
*/
class AccompanyingPeriodWorkEvaluationDocumentWorkflowHandlerTest extends TestCase
{
use ProphecyTrait;
public function testGetSuggestedUsers()
{
$accompanyingCourse = new AccompanyingPeriod();
$accompanyingCourse->setUser($referrer = new User());
$accompanyingCourse->addWork($work = new AccompanyingPeriod\AccompanyingPeriodWork());
$work->addReferrer($workReferrer1 = new User());
$work->addReferrer($workReferrer2 = new User());
$work->addReferrer($referrer);
$work->addAccompanyingPeriodWorkEvaluation($eval = new AccompanyingPeriod\AccompanyingPeriodWorkEvaluation());
$eval->addDocument($doc = new AccompanyingPeriodWorkEvaluationDocument());
$entityWorkflow = new EntityWorkflow();
// Prophesize each dependency
$workflowRepositoryProphecy = $this->prophesize(EntityWorkflowRepository::class);
$translatableStringHelperProphecy = $this->prophesize(TranslatableStringHelperInterface::class);
$translatorProphecy = $this->prophesize(TranslatorInterface::class);
$twig = $this->prophesize(Environment::class);
// Create an instance of the class under test using revealed prophecies directly
$handler = new AccompanyingPeriodWorkEvaluationDocumentWorkflowHandler(
$this->buildRepository($doc, 1),
$workflowRepositoryProphecy->reveal(),
$translatableStringHelperProphecy->reveal(),
$translatorProphecy->reveal(),
new WorkflowWithPublicViewDocumentHelper($twig->reveal()),
$this->prophesize(ProvideThirdPartiesAssociated::class)->reveal(),
$this->prophesize(ProvidePersonsAssociated::class)->reveal(),
);
$entityWorkflow->setRelatedEntityId(1);
$entityWorkflow->setRelatedEntityClass(AccompanyingPeriodWorkEvaluationDocument::class);
$users = $handler->getSuggestedUsers($entityWorkflow);
self::assertContains($referrer, $users);
self::assertContains($workReferrer1, $users);
self::assertContains($workReferrer2, $users);
}
private function buildRepository(AccompanyingPeriodWorkEvaluationDocument $document, int $id): AccompanyingPeriodWorkEvaluationDocumentRepository
{
$repository = $this->prophesize(AccompanyingPeriodWorkEvaluationDocumentRepository::class);
$repository->find($id)->willReturn($document);
return $repository->reveal();
}
}

View File

@@ -0,0 +1,77 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Tests\Workflow;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
use Chill\MainBundle\Repository\Workflow\EntityWorkflowRepository;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationRepository;
use Chill\PersonBundle\Service\AccompanyingPeriodWork\ProvidePersonsAssociated;
use Chill\PersonBundle\Service\AccompanyingPeriodWork\ProvideThirdPartiesAssociated;
use Chill\PersonBundle\Workflow\AccompanyingPeriodWorkEvaluationWorkflowHandler;
use PHPUnit\Framework\TestCase;
use Prophecy\PhpUnit\ProphecyTrait;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
* @internal
*
* @coversNothing
*/
class AccompanyingPeriodWorkEvaluationWorkflowHandlerTest extends TestCase
{
use ProphecyTrait;
public function testGetSuggestedUsers()
{
$accompanyingCourse = new AccompanyingPeriod();
$accompanyingCourse->setUser($referrer = new User());
$accompanyingCourse->addWork($work = new AccompanyingPeriod\AccompanyingPeriodWork());
$work->addReferrer($workReferrer1 = new User());
$work->addReferrer($workReferrer2 = new User());
$work->addReferrer($referrer);
$work->addAccompanyingPeriodWorkEvaluation($eval = new AccompanyingPeriod\AccompanyingPeriodWorkEvaluation());
$entityWorkflow = new EntityWorkflow();
$entityWorkflow->setRelatedEntityId(1);
// Prophesize each dependency
$workflowRepositoryProphecy = $this->prophesize(EntityWorkflowRepository::class);
$translatableStringHelperProphecy = $this->prophesize(TranslatableStringHelperInterface::class);
$translatorProphecy = $this->prophesize(TranslatorInterface::class);
// Create an instance of the class under test using revealed prophecies directly
$handler = new AccompanyingPeriodWorkEvaluationWorkflowHandler(
$this->buildRepository($eval, 1),
$workflowRepositoryProphecy->reveal(),
$translatableStringHelperProphecy->reveal(),
$translatorProphecy->reveal(),
$this->prophesize(ProvideThirdPartiesAssociated::class)->reveal(),
$this->prophesize(ProvidePersonsAssociated::class)->reveal(),
);
$users = $handler->getSuggestedUsers($entityWorkflow);
self::assertContains($referrer, $users);
self::assertContains($workReferrer1, $users);
self::assertContains($workReferrer2, $users);
}
private function buildRepository(AccompanyingPeriod\AccompanyingPeriodWorkEvaluation $evaluation, int $id): AccompanyingPeriodWorkEvaluationRepository
{
$evaluationRepositoryProphecy = $this->prophesize(AccompanyingPeriodWorkEvaluationRepository::class);
$evaluationRepositoryProphecy->find($id)->willReturn($evaluation);
return $evaluationRepositoryProphecy->reveal();
}
}

View File

@@ -0,0 +1,78 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Tests\Workflow;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
use Chill\MainBundle\Repository\Workflow\EntityWorkflowRepository;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkRepository;
use Chill\PersonBundle\Service\AccompanyingPeriodWork\ProvidePersonsAssociated;
use Chill\PersonBundle\Service\AccompanyingPeriodWork\ProvideThirdPartiesAssociated;
use Chill\PersonBundle\Workflow\AccompanyingPeriodWorkWorkflowHandler;
use PHPUnit\Framework\TestCase;
use Prophecy\PhpUnit\ProphecyTrait;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
* @internal
*
* @coversNothing
*/
class AccompanyingPeriodWorkWorkflowHandlerTest extends TestCase
{
use ProphecyTrait;
public function testGetSuggestedUsers()
{
$accompanyingCourse = new AccompanyingPeriod();
$accompanyingCourse->setUser($referrer = new User());
$accompanyingCourse->addWork($work = new AccompanyingPeriod\AccompanyingPeriodWork());
$work->addReferrer($workReferrer1 = new User());
$work->addReferrer($workReferrer2 = new User());
$work->addReferrer($referrer);
$entityWorkflow = new EntityWorkflow();
$entityWorkflow->setRelatedEntityId(1);
// Prophesize each dependency
$workflowRepositoryProphecy = $this->prophesize(EntityWorkflowRepository::class);
$translatableStringHelperProphecy = $this->prophesize(TranslatableStringHelperInterface::class);
$translatorProphecy = $this->prophesize(TranslatorInterface::class);
// Create an instance of the class under test using revealed prophecies directly
$handler = new AccompanyingPeriodWorkWorkflowHandler(
$this->buildRepository($work, 1),
$workflowRepositoryProphecy->reveal(),
$translatableStringHelperProphecy->reveal(),
$translatorProphecy->reveal(),
$this->prophesize(ProvideThirdPartiesAssociated::class)->reveal(),
$this->prophesize(ProvidePersonsAssociated::class)->reveal(),
);
$users = $handler->getSuggestedUsers($entityWorkflow);
self::assertContains($referrer, $users);
self::assertContains($workReferrer1, $users);
self::assertContains($workReferrer2, $users);
}
private function buildRepository(AccompanyingPeriod\AccompanyingPeriodWork $work, int $int): AccompanyingPeriodWorkRepository
{
$accompanyingPeriodWorkRepositoryProphecy = $this->prophesize(AccompanyingPeriodWorkRepository::class);
$accompanyingPeriodWorkRepositoryProphecy
->find($int)
->willReturn($work);
return $accompanyingPeriodWorkRepositoryProphecy->reveal();
}
}

View File

@@ -11,17 +11,36 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Workflow;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\DocStoreBundle\Workflow\WorkflowWithPublicViewDocumentHelper;
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
use Chill\MainBundle\Entity\Workflow\EntityWorkflowSend;
use Chill\MainBundle\Repository\Workflow\EntityWorkflowRepository;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\MainBundle\Workflow\EntityWorkflowHandlerInterface;
use Chill\MainBundle\Workflow\EntityWorkflowWithPublicViewInterface;
use Chill\MainBundle\Workflow\EntityWorkflowWithStoredObjectHandlerInterface;
use Chill\MainBundle\Workflow\Templating\EntityWorkflowViewMetadataDTO;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument;
use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocumentRepository;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodWorkEvaluationDocumentVoter;
use Chill\PersonBundle\Service\AccompanyingPeriodWork\ProvidePersonsAssociated;
use Chill\PersonBundle\Service\AccompanyingPeriodWork\ProvideThirdPartiesAssociated;
use Symfony\Contracts\Translation\TranslatorInterface;
class AccompanyingPeriodWorkEvaluationDocumentWorkflowHandler implements EntityWorkflowHandlerInterface
/**
* @implements EntityWorkflowWithStoredObjectHandlerInterface<AccompanyingPeriodWorkEvaluationDocument>
*/
class AccompanyingPeriodWorkEvaluationDocumentWorkflowHandler implements EntityWorkflowWithStoredObjectHandlerInterface, EntityWorkflowWithPublicViewInterface
{
public function __construct(private readonly AccompanyingPeriodWorkEvaluationDocumentRepository $repository, private readonly TranslatableStringHelperInterface $translatableStringHelper, private readonly TranslatorInterface $translator) {}
public function __construct(
private readonly AccompanyingPeriodWorkEvaluationDocumentRepository $repository,
private readonly EntityWorkflowRepository $workflowRepository,
private readonly TranslatableStringHelperInterface $translatableStringHelper,
private readonly TranslatorInterface $translator,
private readonly WorkflowWithPublicViewDocumentHelper $publicViewDocumentHelper,
private readonly ProvideThirdPartiesAssociated $provideThirdPartiesAssociated,
private readonly ProvidePersonsAssociated $providePersonsAssociated,
) {}
public function getDeletionRoles(): array
{
@@ -67,8 +86,6 @@ class AccompanyingPeriodWorkEvaluationDocumentWorkflowHandler implements EntityW
}
/**
* @param AccompanyingPeriodWorkEvaluationDocument $object
*
* @return array[]
*/
public function getRelatedObjects(object $object): array
@@ -85,17 +102,36 @@ class AccompanyingPeriodWorkEvaluationDocumentWorkflowHandler implements EntityW
public function getSuggestedUsers(EntityWorkflow $entityWorkflow): array
{
$suggestedUsers = $entityWorkflow->getUsersInvolved();
$related = $this->getRelatedEntity($entityWorkflow);
$referrer = $this->getRelatedEntity($entityWorkflow)
->getAccompanyingPeriodWorkEvaluation()
if (null === $related) {
return [];
}
$users = [];
if (null !== $referrer = $related->getAccompanyingPeriodWorkEvaluation()
->getAccompanyingPeriodWork()
->getAccompanyingPeriod()
->getUser();
->getUser()
) {
$users[] = $referrer;
}
$suggestedUsers[spl_object_hash($referrer)] = $referrer;
foreach ($related->getAccompanyingPeriodWorkEvaluation()
->getAccompanyingPeriodWork()
->getReferrersHistoryCurrent() as $referrerHistory
) {
$users[] = $referrerHistory->getUser();
}
return $suggestedUsers;
return array_values(
// filter objects to remove duplicates
array_filter(
$users,
fn ($o, $k) => array_search($o, $users, true) === $k,
ARRAY_FILTER_USE_BOTH
)
);
}
public function getTemplate(EntityWorkflow $entityWorkflow, array $options = []): string
@@ -124,8 +160,49 @@ class AccompanyingPeriodWorkEvaluationDocumentWorkflowHandler implements EntityW
return AccompanyingPeriodWorkEvaluationDocument::class === $entityWorkflow->getRelatedEntityClass();
}
public function getAssociatedStoredObject(EntityWorkflow $entityWorkflow): ?StoredObject
{
return $this->getRelatedEntity($entityWorkflow)?->getStoredObject();
}
public function supportsFreeze(EntityWorkflow $entityWorkflow, array $options = []): bool
{
return false;
}
public function findByRelatedEntity(object $object): array
{
if (!$object instanceof AccompanyingPeriodWorkEvaluationDocument) {
return [];
}
return $this->workflowRepository->findByRelatedEntity(AccompanyingPeriodWorkEvaluationDocument::class, $object->getId());
}
public function renderPublicView(EntityWorkflowSend $entityWorkflowSend, EntityWorkflowViewMetadataDTO $metadata): string
{
return $this->publicViewDocumentHelper->render($entityWorkflowSend, $metadata, $this);
}
public function getSuggestedPersons(EntityWorkflow $entityWorkflow): array
{
$related = $this->getRelatedEntity($entityWorkflow);
if (null === $related) {
return [];
}
return $this->providePersonsAssociated->getPersonsAssociated($related->getAccompanyingPeriodWorkEvaluation()->getAccompanyingPeriodWork());
}
public function getSuggestedThirdParties(EntityWorkflow $entityWorkflow): array
{
$related = $this->getRelatedEntity($entityWorkflow);
if (null === $related) {
return [];
}
return $this->provideThirdPartiesAssociated->getThirdPartiesAssociated($related->getAccompanyingPeriodWorkEvaluation()->getAccompanyingPeriodWork());
}
}

View File

@@ -12,17 +12,30 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Workflow;
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
use Chill\MainBundle\Repository\Workflow\EntityWorkflowRepository;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\MainBundle\Workflow\EntityWorkflowHandlerInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument;
use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationRepository;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodWorkEvaluationVoter;
use Chill\PersonBundle\Service\AccompanyingPeriodWork\ProvidePersonsAssociated;
use Chill\PersonBundle\Service\AccompanyingPeriodWork\ProvideThirdPartiesAssociated;
use Symfony\Contracts\Translation\TranslatorInterface;
class AccompanyingPeriodWorkEvaluationWorkflowHandler implements EntityWorkflowHandlerInterface
/**
* @implements EntityWorkflowHandlerInterface<AccompanyingPeriodWorkEvaluation>
*/
readonly class AccompanyingPeriodWorkEvaluationWorkflowHandler implements EntityWorkflowHandlerInterface
{
public function __construct(private readonly AccompanyingPeriodWorkEvaluationRepository $repository, private readonly TranslatableStringHelperInterface $translatableStringHelper, private readonly TranslatorInterface $translator) {}
public function __construct(
private AccompanyingPeriodWorkEvaluationRepository $repository,
private EntityWorkflowRepository $workflowRepository,
private TranslatableStringHelperInterface $translatableStringHelper,
private TranslatorInterface $translator,
private ProvideThirdPartiesAssociated $provideThirdPartiesAssociated,
private ProvidePersonsAssociated $providePersonsAssociated,
) {}
public function getDeletionRoles(): array
{
@@ -53,9 +66,6 @@ class AccompanyingPeriodWorkEvaluationWorkflowHandler implements EntityWorkflowH
return $this->repository->find($entityWorkflow->getRelatedEntityId());
}
/**
* @param AccompanyingPeriodWorkEvaluation $object
*/
public function getRelatedObjects(object $object): array
{
$relateds = [];
@@ -75,16 +85,34 @@ class AccompanyingPeriodWorkEvaluationWorkflowHandler implements EntityWorkflowH
public function getSuggestedUsers(EntityWorkflow $entityWorkflow): array
{
$suggestedUsers = $entityWorkflow->getUsersInvolved();
$related = $this->getRelatedEntity($entityWorkflow);
$referrer = $this->getRelatedEntity($entityWorkflow)
->getAccompanyingPeriodWork()
if (null === $related) {
return [];
}
$users = [];
if (null !== $referrer = $related->getAccompanyingPeriodWork()
->getAccompanyingPeriod()
->getUser();
->getUser()
) {
$users[] = $referrer;
}
$suggestedUsers[spl_object_hash($referrer)] = $referrer;
foreach ($related->getAccompanyingPeriodWork()
->getReferrersHistoryCurrent() as $referrerHistory
) {
$users[] = $referrerHistory->getUser();
}
return $suggestedUsers;
return array_values(
// filter objects to remove duplicates
array_filter(
$users,
fn ($o, $k) => array_search($o, $users, true) === $k,
ARRAY_FILTER_USE_BOTH
)
);
}
public function getTemplate(EntityWorkflow $entityWorkflow, array $options = []): string
@@ -114,4 +142,35 @@ class AccompanyingPeriodWorkEvaluationWorkflowHandler implements EntityWorkflowH
{
return false;
}
public function findByRelatedEntity(object $object): array
{
if (!$object instanceof AccompanyingPeriodWorkEvaluation) {
return [];
}
return $this->workflowRepository->findByRelatedEntity(AccompanyingPeriodWorkEvaluation::class, $object->getId());
}
public function getSuggestedPersons(EntityWorkflow $entityWorkflow): array
{
$related = $this->getRelatedEntity($entityWorkflow);
if (null === $related) {
return [];
}
return $this->providePersonsAssociated->getPersonsAssociated($related->getAccompanyingPeriodWork());
}
public function getSuggestedThirdParties(EntityWorkflow $entityWorkflow): array
{
$related = $this->getRelatedEntity($entityWorkflow);
if (null === $related) {
return [];
}
return $this->provideThirdPartiesAssociated->getThirdPartiesAssociated($related->getAccompanyingPeriodWork());
}
}

View File

@@ -12,6 +12,7 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Workflow;
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
use Chill\MainBundle\Repository\Workflow\EntityWorkflowRepository;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\MainBundle\Workflow\EntityWorkflowHandlerInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
@@ -19,11 +20,23 @@ use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluatio
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument;
use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkRepository;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodWorkVoter;
use Chill\PersonBundle\Service\AccompanyingPeriodWork\ProvidePersonsAssociated;
use Chill\PersonBundle\Service\AccompanyingPeriodWork\ProvideThirdPartiesAssociated;
use Symfony\Contracts\Translation\TranslatorInterface;
class AccompanyingPeriodWorkWorkflowHandler implements EntityWorkflowHandlerInterface
/**
* @implements EntityWorkflowHandlerInterface<AccompanyingPeriodWork>
*/
readonly class AccompanyingPeriodWorkWorkflowHandler implements EntityWorkflowHandlerInterface
{
public function __construct(private readonly AccompanyingPeriodWorkRepository $repository, private readonly TranslatableStringHelperInterface $translatableStringHelper, private readonly TranslatorInterface $translator) {}
public function __construct(
private AccompanyingPeriodWorkRepository $repository,
private EntityWorkflowRepository $workflowRepository,
private TranslatableStringHelperInterface $translatableStringHelper,
private TranslatorInterface $translator,
private ProvideThirdPartiesAssociated $thirdPartiesAssociated,
private ProvidePersonsAssociated $providePersonsAssociated,
) {}
public function getDeletionRoles(): array
{
@@ -55,9 +68,6 @@ class AccompanyingPeriodWorkWorkflowHandler implements EntityWorkflowHandlerInte
return $this->repository->find($entityWorkflow->getRelatedEntityId());
}
/**
* @param AccompanyingPeriodWork $object
*/
public function getRelatedObjects(object $object): array
{
$relateds = [];
@@ -81,17 +91,32 @@ class AccompanyingPeriodWorkWorkflowHandler implements EntityWorkflowHandlerInte
public function getSuggestedUsers(EntityWorkflow $entityWorkflow): array
{
$suggestedUsers = $entityWorkflow->getUsersInvolved();
$related = $this->getRelatedEntity($entityWorkflow);
$referrer = $this->getRelatedEntity($entityWorkflow)
->getAccompanyingPeriod()
->getUser();
if (null !== $referrer) {
$suggestedUsers[spl_object_hash($referrer)] = $referrer;
if (null === $related) {
return [];
}
return $suggestedUsers;
$users = [];
if (null !== $referrer = $related->getAccompanyingPeriod()
->getUser()
) {
$users[] = $referrer;
}
foreach ($related->getReferrersHistoryCurrent() as $referrerHistory
) {
$users[] = $referrerHistory->getUser();
}
return array_values(
// filter objects to remove duplicates
array_filter(
$users,
fn ($o, $k) => array_search($o, $users, true) === $k,
ARRAY_FILTER_USE_BOTH
)
);
}
public function getTemplate(EntityWorkflow $entityWorkflow, array $options = []): string
@@ -121,4 +146,35 @@ class AccompanyingPeriodWorkWorkflowHandler implements EntityWorkflowHandlerInte
{
return false;
}
public function findByRelatedEntity(object $object): array
{
if (!$object instanceof AccompanyingPeriodWork) {
return [];
}
return $this->workflowRepository->findByRelatedEntity(AccompanyingPeriodWork::class, $object->getId());
}
public function getSuggestedPersons(EntityWorkflow $entityWorkflow): array
{
$related = $this->getRelatedEntity($entityWorkflow);
if (null === $related) {
return [];
}
return $this->providePersonsAssociated->getPersonsAssociated($related);
}
public function getSuggestedThirdParties(EntityWorkflow $entityWorkflow): array
{
$related = $this->getRelatedEntity($entityWorkflow);
if (null === $related) {
return [];
}
return $this->thirdPartiesAssociated->getThirdPartiesAssociated($related);
}
}

View File

@@ -1948,3 +1948,25 @@ paths:
responses:
200:
description: "OK"
/1.0/person/accompanying-course-work-evaluation-document/{id}/duplicate:
post:
tags:
- accompanying-course-work-evaluation-document
summary: Dupliate an an accompanying period work evaluation document
parameters:
- in: path
name: id
required: true
description: The document's id
schema:
type: integer
format: integer
minimum: 1
responses:
200:
description: "OK"
content:
application/json:
schema:
type: object

View File

@@ -68,3 +68,6 @@ services:
autowire: true
tags: ['controller.service_arguments']
Chill\PersonBundle\Controller\PersonSignatureController:
tags: [ 'controller.service_arguments' ]

View File

@@ -165,8 +165,8 @@ exports:
{ total, plural,
=0 {Aucun usager ne correspond aux termes de recherche}
one {Un usager correspond aux termes de recherche}
many {# usagers correspondent aux terms de recherche}
other {# usagers correspondent aux terms de recherche}
many {# usagers correspondent aux termes de recherche}
other {# usagers correspondent aux termes de recherche}
}
'nb person with similar name. Please verify that this is a new person': >-
@@ -175,3 +175,7 @@ exports:
one {Un usager a un nom similaire. Vérifiez qu'il ne s'agit pas de lui.}
other {# usagers ont un nom similaire. Vérifiez qu'il ne s'agit pas de l'un d'eux}
}
accompanying_course_evaluation_document:
duplicated_at: >-
Dupliqué le {at, date, long} à {at, time, short}

View File

@@ -958,6 +958,10 @@ workflow:
Doc for evaluation (n°%eval%): Document de l'évaluation n°%eval%
doc for evaluation deleted: Document supprimé dans une évaluation
SocialAction deleted: Action sociale supprimée
signature_list:
title: Signature en attente
menu: Signature en attente
no_signatures: Aucune signature demandée
period_by_user_list:
Period by user: Parcours d'accompagnement par utilisateur