mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-08-27 18:13:48 +00:00
Resolve "Fusion actions d'accompagnement"
This commit is contained in:
@@ -313,10 +313,24 @@ final class AccompanyingCourseApiController extends ApiController
|
||||
{
|
||||
$this->denyAccessUnlessGranted(AccompanyingPeriodVoter::SEE, $accompanyingPeriod);
|
||||
|
||||
$works = $this->accompanyingPeriodWorkRepository->findBy(['accompanyingPeriod' => $accompanyingPeriod]);
|
||||
dump($works);
|
||||
$total = $this->accompanyingPeriodWorkRepository->countByAccompanyingPeriod($accompanyingPeriod);
|
||||
$paginator = $this->getPaginatorFactory()->create($total);
|
||||
|
||||
return $this->json($works, Response::HTTP_OK, [], ['groups' => ['read']]);
|
||||
$works = $this->accompanyingPeriodWorkRepository->findByAccompanyingPeriodOpenFirst(
|
||||
$accompanyingPeriod,
|
||||
[
|
||||
'types' => [],
|
||||
'before' => null,
|
||||
'after' => null,
|
||||
'user' => [],
|
||||
],
|
||||
$paginator->getItemsPerPage(),
|
||||
$paginator->getCurrentPageFirstItemNumber()
|
||||
);
|
||||
|
||||
$collection = new Collection($works, $paginator);
|
||||
|
||||
return $this->json($collection, Response::HTTP_OK, [], ['groups' => ['read']]);
|
||||
}
|
||||
|
||||
public function workApi($id, Request $request, string $_format): Response
|
||||
|
@@ -0,0 +1,131 @@
|
||||
<?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\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
|
||||
use Chill\PersonBundle\Form\FindAccompanyingPeriodWorkType;
|
||||
use Chill\PersonBundle\Form\PersonConfimDuplicateType;
|
||||
use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkRepository;
|
||||
use Chill\PersonBundle\Service\AccompanyingPeriodWork\AccompanyingPeriodWorkMergeService;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Session\Session;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Translation\TranslatableMessage;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
|
||||
|
||||
class AccompanyingPeriodWorkDuplicateController extends AbstractController
|
||||
{
|
||||
public function __construct(private readonly AccompanyingPeriodWorkRepository $accompanyingPeriodWorkRepository, private readonly TranslatorInterface $translator, private readonly AccompanyingPeriodWorkMergeService $accompanyingPeriodWorkMergeService) {}
|
||||
|
||||
/**
|
||||
* @ParamConverter("accompanyingPeriodWork", options={"id": "acpw_id"})
|
||||
*/
|
||||
#[Route(path: '{_locale}/person/accompanying-period/work/{id}/assign-duplicate', name: 'chill_person_accompanying_period_work_assign_duplicate')]
|
||||
public function assignDuplicate(AccompanyingPeriodWork $acpw, Request $request)
|
||||
{
|
||||
$accompanyingPeriod = $acpw->getAccompanyingPeriod();
|
||||
|
||||
$this->denyAccessUnlessGranted(
|
||||
'CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_UPDATE',
|
||||
$acpw,
|
||||
'You are not allowed to merge this accompanying period work'
|
||||
);
|
||||
|
||||
$form = $this->createForm(FindAccompanyingPeriodWorkType::class, null, ['accompanyingPeriod' => $accompanyingPeriod]);
|
||||
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
$acpw2 = $this->accompanyingPeriodWorkRepository->find($form->get('acpw')->getData());
|
||||
|
||||
$direction = $form->get('direction')->getData();
|
||||
|
||||
if ('starting' === $direction) {
|
||||
$params = [
|
||||
'acpw1_id' => $acpw->getId(),
|
||||
'acpw2_id' => $acpw2->getId(),
|
||||
];
|
||||
} else {
|
||||
$params = [
|
||||
'acpw1_id' => $acpw2->getId(),
|
||||
'acpw2_id' => $acpw->getId(),
|
||||
];
|
||||
}
|
||||
|
||||
return $this->redirectToRoute('chill_person_acpw_duplicate_confirm', $params);
|
||||
}
|
||||
|
||||
return $this->render('@ChillPerson/AccompanyingPeriodWorkDuplicate/assign_acpw_duplicate.html.twig', [
|
||||
'accompanyingCourse' => $accompanyingPeriod,
|
||||
// 'acpwArray' => $acpwArray,
|
||||
'acpw' => $acpw,
|
||||
'form' => $form->createView(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ParamConverter("acpw1", options={"id": "acpw1_id"})
|
||||
* @ParamConverter("acpw2", options={"id": "acpw2_id"})
|
||||
*/
|
||||
#[Route(path: '/{_locale}/person/{acpw1_id}/duplicate/{acpw2_id}/confirm', name: 'chill_person_acpw_duplicate_confirm')]
|
||||
public function confirmAction(AccompanyingPeriodWork $acpw1, AccompanyingPeriodWork $acpw2, Request $request)
|
||||
{
|
||||
$accompanyingPeriod = $acpw1->getAccompanyingPeriod();
|
||||
|
||||
try {
|
||||
$this->validateMerge($acpw1, $acpw2);
|
||||
$form = $this->createForm(PersonConfimDuplicateType::class);
|
||||
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
|
||||
$this->accompanyingPeriodWorkMergeService->merge($acpw1, $acpw2);
|
||||
|
||||
$session = $request->getSession();
|
||||
if ($session instanceof Session) {
|
||||
$session->getFlashBag()->add('success', new TranslatableMessage('acpw_duplicate.Successfully merged'));
|
||||
}
|
||||
|
||||
return $this->redirectToRoute('chill_person_accompanying_period_work_show', ['id' => $acpw1->getId()]);
|
||||
}
|
||||
|
||||
return $this->render('@ChillPerson/AccompanyingPeriodWorkDuplicate/confirm.html.twig', [
|
||||
'acpw' => $acpw1,
|
||||
'acpw2' => $acpw2,
|
||||
'accompanyingCourse' => $accompanyingPeriod,
|
||||
'form' => $form->createView(),
|
||||
]);
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
$this->addFlash('error', $this->translator->trans($e->getMessage()));
|
||||
|
||||
return $this->redirectToRoute('chill_person_accompanying_period_work_assign_duplicate', [
|
||||
'id' => $acpw1->getId(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
private function validateMerge(AccompanyingPeriodWork $acpw1, AccompanyingPeriodWork $acpw2): void
|
||||
{
|
||||
$constraints = [
|
||||
[$acpw1 === $acpw2, 'acpw_duplicate.You cannot merge a accompanying period work with itself. Please choose a different one'],
|
||||
];
|
||||
|
||||
foreach ($constraints as [$condition, $message]) {
|
||||
if ($condition) {
|
||||
throw new \InvalidArgumentException($message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -218,14 +218,6 @@ class AccompanyingPeriodWorkEvaluation implements TrackCreationInterface, TrackU
|
||||
|
||||
public function setAccompanyingPeriodWork(?AccompanyingPeriodWork $accompanyingPeriodWork): AccompanyingPeriodWorkEvaluation
|
||||
{
|
||||
if (
|
||||
$accompanyingPeriodWork instanceof AccompanyingPeriodWork
|
||||
&& $this->accompanyingPeriodWork instanceof AccompanyingPeriodWork
|
||||
&& $this->accompanyingPeriodWork->getId() !== $accompanyingPeriodWork->getId()
|
||||
) {
|
||||
throw new \RuntimeException('Changing the accompanyingPeriodWork is not allowed');
|
||||
}
|
||||
|
||||
$this->accompanyingPeriodWork = $accompanyingPeriodWork;
|
||||
|
||||
return $this;
|
||||
|
@@ -99,14 +99,6 @@ class AccompanyingPeriodWorkGoal
|
||||
|
||||
public function setAccompanyingPeriodWork(?AccompanyingPeriodWork $accompanyingPeriodWork): self
|
||||
{
|
||||
if (
|
||||
$this->accompanyingPeriodWork instanceof AccompanyingPeriodWork
|
||||
&& $accompanyingPeriodWork !== $this->accompanyingPeriodWork
|
||||
&& null !== $accompanyingPeriodWork
|
||||
) {
|
||||
throw new \LogicException('Change accompanying period work is not allowed');
|
||||
}
|
||||
|
||||
$this->accompanyingPeriodWork = $accompanyingPeriodWork;
|
||||
|
||||
return $this;
|
||||
|
@@ -0,0 +1,42 @@
|
||||
<?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\Form;
|
||||
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||
use Chill\PersonBundle\Form\Type\PickLinkedAccompanyingPeriodWorkType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class FindAccompanyingPeriodWorkType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$builder
|
||||
->add('acpw', PickLinkedAccompanyingPeriodWorkType::class, [
|
||||
'label' => 'Social action',
|
||||
'multiple' => false,
|
||||
'accompanyingPeriod' => $options['accompanyingPeriod'],
|
||||
])
|
||||
->add('direction', HiddenType::class, [
|
||||
'data' => 'starting',
|
||||
]);
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver
|
||||
->setRequired('accompanyingPeriod')
|
||||
->setAllowedTypes('accompanyingPeriod', AccompanyingPeriod::class);
|
||||
}
|
||||
}
|
@@ -0,0 +1,51 @@
|
||||
<?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\Form\Type;
|
||||
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class PickLinkedAccompanyingPeriodWorkType extends AbstractType
|
||||
{
|
||||
public function buildView(FormView $view, FormInterface $form, array $options)
|
||||
{
|
||||
$view->vars['multiple'] = $options['multiple'];
|
||||
$view->vars['types'] = ['acpw'];
|
||||
$view->vars['uniqid'] = uniqid('pick_acpw_dyn');
|
||||
$view->vars['as_id'] = true === $options['as_id'] ? '1' : '0';
|
||||
$view->vars['submit_on_adding_new_entity'] = false;
|
||||
$view->vars['pick-entities-type'] = 'acpw';
|
||||
$view->vars['attr']['data-accompanying-period-id'] = $options['accompanyingPeriod']->getId();
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver
|
||||
->setRequired('accompanyingPeriod')
|
||||
->setAllowedTypes('accompanyingPeriod', [AccompanyingPeriod::class])
|
||||
->setDefault('multiple', false)
|
||||
->setAllowedTypes('multiple', ['bool'])
|
||||
->setDefault('compound', false)
|
||||
->setDefault('as_id', false)
|
||||
->setAllowedTypes('as_id', ['bool'])
|
||||
->setDefault('submit_on_adding_new_entity', false)
|
||||
->setAllowedTypes('submit_on_adding_new_entity', ['bool']);
|
||||
}
|
||||
|
||||
public function getBlockPrefix()
|
||||
{
|
||||
return 'pick_linked_entities';
|
||||
}
|
||||
}
|
@@ -0,0 +1,49 @@
|
||||
import { createApp } from "vue";
|
||||
import AccompanyingPeriodWorkSelectorModal from "../../vuejs/_components/AccompanyingPeriodWorkSelector/AccompanyingPeriodWorkSelectorModal.vue";
|
||||
import { AccompanyingPeriodWork } from "../../types";
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const elements = document.querySelectorAll<HTMLDivElement>(
|
||||
'div[data-pick-entities-type="acpw"]',
|
||||
);
|
||||
elements.forEach((el) => {
|
||||
const uniqid = el.dataset.inputUniqid;
|
||||
|
||||
if (undefined === uniqid) {
|
||||
throw "Uniqid not found on this element";
|
||||
}
|
||||
|
||||
const input = document.querySelector<HTMLInputElement>(
|
||||
`input[data-input-uniqid="${uniqid}"]`,
|
||||
);
|
||||
|
||||
if (null === input) {
|
||||
throw "Element with uniqid not found: " + uniqid;
|
||||
}
|
||||
|
||||
const accompanyingPeriodIdAsString = input.dataset.accompanyingPeriodId;
|
||||
|
||||
if (undefined === accompanyingPeriodIdAsString) {
|
||||
throw "accompanying period id not found";
|
||||
}
|
||||
|
||||
const accompanyingPeriodId = Number.parseInt(accompanyingPeriodIdAsString);
|
||||
|
||||
const app = createApp({
|
||||
template:
|
||||
'<accompanying-period-work-selector-modal :accompanying-period-id="accompanyingPeriodId" @pickWork="pickWork"></accompanying-period-work-selector-modal>',
|
||||
components: { AccompanyingPeriodWorkSelectorModal },
|
||||
data() {
|
||||
return { accompanyingPeriodId };
|
||||
},
|
||||
methods: {
|
||||
pickWork: function (payload: { work: AccompanyingPeriodWork }) {
|
||||
console.log("payload", payload);
|
||||
input.value = payload.work.id.toString();
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
app.mount(el);
|
||||
});
|
||||
});
|
@@ -1,12 +1,17 @@
|
||||
import {
|
||||
Address,
|
||||
Center,
|
||||
Civility,
|
||||
DateTime,
|
||||
User,
|
||||
WorkflowAvailable,
|
||||
} from "../../../ChillMainBundle/Resources/public/types";
|
||||
import { StoredObject } from "../../../ChillDocStoreBundle/Resources/public/types";
|
||||
Scope,
|
||||
Center,
|
||||
Civility,
|
||||
DateTime,
|
||||
User,
|
||||
WorkflowAvailable,
|
||||
Job,
|
||||
PrivateCommentEmbeddable,
|
||||
} from "ChillMainAssets/types";
|
||||
import { StoredObject } from "ChillDocStoreAssets/types";
|
||||
import { Thirdparty } from "../../../ChillThirdPartyBundle/Resources/public/types";
|
||||
import { Calendar } from "../../../ChillCalendarBundle/Resources/public/types";
|
||||
|
||||
export interface Person {
|
||||
id: number;
|
||||
@@ -29,6 +34,42 @@ export interface Person {
|
||||
current_residential_addresses: Address[];
|
||||
}
|
||||
|
||||
export interface AccompanyingPeriod {
|
||||
id: number;
|
||||
addressLocation?: Address | null;
|
||||
administrativeLocation?: Location | null;
|
||||
calendars: Calendar[];
|
||||
closingDate?: Date | null;
|
||||
closingMotive?: ClosingMotive | null;
|
||||
comments: Comment[];
|
||||
confidential: boolean;
|
||||
createdAt?: Date | null;
|
||||
createdBy?: User | null;
|
||||
emergency: boolean;
|
||||
intensity?: "occasional" | "regular";
|
||||
job?: Job | null;
|
||||
locationHistories: AccompanyingPeriodLocationHistory[];
|
||||
openingDate?: Date | null;
|
||||
origin?: Origin | null;
|
||||
participations: AccompanyingPeriodParticipation[];
|
||||
personLocation?: Person | null;
|
||||
pinnedComment?: Comment | null;
|
||||
preventUserIsChangedNotification: boolean;
|
||||
remark: string;
|
||||
requestorAnonymous: boolean;
|
||||
requestorPerson?: Person | null;
|
||||
requestorThirdParty?: Thirdparty | null;
|
||||
resources: AccompanyingPeriodResource[];
|
||||
scopes: Scope[];
|
||||
socialIssues: SocialIssue[];
|
||||
step?:
|
||||
| "CLOSED"
|
||||
| "CONFIRMED"
|
||||
| "CONFIRMED_INACTIVE_SHORT"
|
||||
| "CONFIRMED_INACTIVE_LONG"
|
||||
| "DRAFT";
|
||||
}
|
||||
|
||||
export interface AccompanyingPeriodWorkEvaluationDocument {
|
||||
id: number;
|
||||
type: "accompanying_period_work_evaluation_document";
|
||||
@@ -41,3 +82,172 @@ export interface AccompanyingPeriodWorkEvaluationDocument {
|
||||
workflows_availables: WorkflowAvailable[];
|
||||
workflows: object[];
|
||||
}
|
||||
|
||||
export interface AccompanyingPeriodWork {
|
||||
id: number;
|
||||
accompanyingPeriod?: AccompanyingPeriod;
|
||||
accompanyingPeriodWorkEvaluations: AccompanyingPeriodWorkEvaluation[];
|
||||
createdAt?: string;
|
||||
createdAutomatically: boolean;
|
||||
createdAutomaticallyReason: string;
|
||||
createdBy: User;
|
||||
endDate?: string;
|
||||
goals: AccompanyingPeriodWorkGoal[];
|
||||
handlingThierParty?: Thirdparty;
|
||||
note: string;
|
||||
persons: Person[];
|
||||
privateComment: PrivateCommentEmbeddable;
|
||||
referrersHistory: AccompanyingPeriodWorkReferrerHistory[];
|
||||
results: Result[];
|
||||
socialAction?: SocialAction;
|
||||
startDate?: string;
|
||||
thirdParties: Thirdparty[];
|
||||
updatedAt?: string;
|
||||
updatedBy: User;
|
||||
version: number;
|
||||
}
|
||||
|
||||
interface SocialAction {
|
||||
id: number;
|
||||
parent?: SocialAction | null;
|
||||
children: SocialAction[];
|
||||
issue?: SocialIssue | null;
|
||||
ordering: number;
|
||||
title: {
|
||||
fr: string;
|
||||
};
|
||||
defaultNotificationDelay?: string | null;
|
||||
desactivationDate?: string | null;
|
||||
evaluations: Evaluation[];
|
||||
goals: Goal[];
|
||||
results: Result[];
|
||||
}
|
||||
|
||||
export interface AccompanyingPeriodResource {
|
||||
id: number;
|
||||
accompanyingPeriod: AccompanyingPeriod;
|
||||
comment?: string | null;
|
||||
person?: Person | null;
|
||||
thirdParty?: Thirdparty | null;
|
||||
}
|
||||
|
||||
export interface Origin {
|
||||
id: number;
|
||||
label: {
|
||||
fr: string;
|
||||
};
|
||||
noActiveAfter: DateTime;
|
||||
}
|
||||
|
||||
export interface ClosingMotive {
|
||||
id: number;
|
||||
active: boolean;
|
||||
name: {
|
||||
fr: string;
|
||||
};
|
||||
ordering: number;
|
||||
isCanceledAccompanyingPeriod: boolean;
|
||||
parent?: ClosingMotive | null;
|
||||
children: ClosingMotive[];
|
||||
}
|
||||
|
||||
export interface AccompanyingPeriodParticipation {
|
||||
id: number;
|
||||
startDate: DateTime;
|
||||
endDate?: DateTime | null;
|
||||
accompanyingPeriod: AccompanyingPeriod;
|
||||
person: Person;
|
||||
}
|
||||
|
||||
export interface AccompanyingPeriodLocationHistory {
|
||||
id: number;
|
||||
startDate: DateTime;
|
||||
endDate?: DateTime | null;
|
||||
addressLocation?: Address | null;
|
||||
period: AccompanyingPeriod;
|
||||
personLocation?: Person | null;
|
||||
}
|
||||
|
||||
export interface SocialIssue {
|
||||
id: number;
|
||||
parent?: SocialIssue | null;
|
||||
children: SocialIssue[];
|
||||
socialActions?: SocialAction[] | null;
|
||||
ordering: number;
|
||||
title: {
|
||||
fr: string;
|
||||
};
|
||||
desactivationDate?: string | null;
|
||||
}
|
||||
|
||||
export interface Goal {
|
||||
id: number;
|
||||
results: Result[];
|
||||
socialActions?: SocialAction[] | null;
|
||||
title: {
|
||||
fr: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface Result {
|
||||
id: number;
|
||||
accompanyingPeriodWorks: AccompanyingPeriodWork[];
|
||||
accompanyingPeriodWorkGoals: AccompanyingPeriodWorkGoal[];
|
||||
goals: Goal[];
|
||||
socialActions: SocialAction[];
|
||||
title: {
|
||||
fr: string;
|
||||
};
|
||||
desactivationDate?: string | null;
|
||||
}
|
||||
|
||||
export interface AccompanyingPeriodWorkGoal {
|
||||
id: number;
|
||||
accompanyingPeriodWork: AccompanyingPeriodWork;
|
||||
goal: Goal;
|
||||
note: string;
|
||||
results: Result[];
|
||||
}
|
||||
|
||||
export interface AccompanyingPeriodWorkEvaluation {
|
||||
accompanyingPeriodWork: AccompanyingPeriodWork | null;
|
||||
comment: string;
|
||||
createdAt: DateTime | null;
|
||||
createdBy: User | null;
|
||||
documents: AccompanyingPeriodWorkEvaluationDocument[];
|
||||
endDate: DateTime | null;
|
||||
evaluation: Evaluation | null;
|
||||
id: number | null;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
key: any;
|
||||
maxDate: DateTime | null;
|
||||
startDate: DateTime | null;
|
||||
updatedAt: DateTime | null;
|
||||
updatedBy: User | null;
|
||||
warningInterval: string | null;
|
||||
timeSpent: number | null;
|
||||
}
|
||||
|
||||
export interface Evaluation {
|
||||
id: number;
|
||||
url: string;
|
||||
socialActions: SocialAction[];
|
||||
title: {
|
||||
fr: string;
|
||||
};
|
||||
active: boolean;
|
||||
delay: string;
|
||||
notificationDelay: string;
|
||||
}
|
||||
|
||||
export interface AccompanyingPeriodWorkReferrerHistory {
|
||||
id: number;
|
||||
accompanyingPeriodWork: AccompanyingPeriodWork;
|
||||
user: User;
|
||||
startDate: DateTime;
|
||||
endDate: DateTime | null;
|
||||
createdAt: DateTime;
|
||||
updatedAt: DateTime | null;
|
||||
createdBy: User;
|
||||
updatedBy: User | null;
|
||||
}
|
||||
|
@@ -0,0 +1,60 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<div class="item-bloc">
|
||||
<div class="item-row">
|
||||
<h2 class="badge-title">
|
||||
<span class="title_label"></span>
|
||||
<span class="title_action">
|
||||
<span class="chill-entity entity-social-action">
|
||||
<span class="badge bg-light text-dark">
|
||||
{{ acpw?.socialAction?.title.fr }}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<ul class="small_in_title columns mt-1">
|
||||
<li>
|
||||
<span class="item-key">
|
||||
{{ trans(ACCOMPANYING_COURSE_WORK_START_DATE) }} :
|
||||
</span>
|
||||
<b>{{ formatDate(acpw.startDate) }}</b>
|
||||
</li>
|
||||
|
||||
<li v-if="acpw.endDate">
|
||||
<span class="item-key">
|
||||
{{ trans(ACCOMPANYING_COURSE_WORK_END_DATE) }} :
|
||||
</span>
|
||||
<b>{{ formatDate(acpw.endDate) }}</b>
|
||||
</li>
|
||||
</ul>
|
||||
</span>
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
ACCOMPANYING_COURSE_WORK_END_DATE,
|
||||
ACCOMPANYING_COURSE_WORK_START_DATE,
|
||||
trans,
|
||||
} from "translator";
|
||||
import { ISOToDate } from "ChillMainAssets/chill/js/date";
|
||||
import { DateTime } from "ChillMainAssets/types";
|
||||
import { AccompanyingPeriodWork } from "../../../types";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const props = defineProps<{ acpw: AccompanyingPeriodWork }>();
|
||||
const formatDate = (dateObject: DateTime) => {
|
||||
if (dateObject) {
|
||||
const parsedDate = ISOToDate(dateObject.datetime);
|
||||
if (parsedDate) {
|
||||
return new Intl.DateTimeFormat("default", { dateStyle: "short" }).format(
|
||||
parsedDate,
|
||||
);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
@@ -0,0 +1,47 @@
|
||||
<template>
|
||||
<div class="results">
|
||||
<div
|
||||
v-for="acpw in accompanyingPeriodWorks"
|
||||
:key="acpw.id"
|
||||
class="list-item"
|
||||
>
|
||||
<label class="acpw-item">
|
||||
<div>
|
||||
<input
|
||||
type="radio"
|
||||
:value="acpw"
|
||||
v-model="selectedAcpw"
|
||||
name="item"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<accompanying-period-work-item :acpw="acpw" />
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import AccompanyingPeriodWorkItem from "./AccompanyingPeriodWorkItem.vue";
|
||||
import { AccompanyingPeriodWork } from "../../../types";
|
||||
import { defineProps, ref, watch } from "vue";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const props = defineProps<{
|
||||
accompanyingPeriodWorks: AccompanyingPeriodWork[];
|
||||
}>();
|
||||
const selectedAcpw = ref<AccompanyingPeriodWork | null>(null);
|
||||
|
||||
// eslint-disable-next-line vue/valid-define-emits
|
||||
const emit = defineEmits();
|
||||
|
||||
watch(selectedAcpw, (newValue) => {
|
||||
emit("update:selectedAcpw", newValue);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.acpw-item {
|
||||
display: flex;
|
||||
}
|
||||
</style>
|
@@ -0,0 +1,101 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="row justify-content-end">
|
||||
<div class="col-md-6 col-sm-10" v-if="selectedAcpw">
|
||||
<ul class="list-suggest remove-items">
|
||||
<li>
|
||||
<span @click="selectedAcpw = null" class="chill-denomination">{{
|
||||
selectedAcpw?.socialAction?.title.fr
|
||||
}}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="record_actions">
|
||||
<li>
|
||||
<a class="btn btn-sm btn-create mt-3" @click="openModal">
|
||||
{{ trans(ACPW_DUPLICATE_SELECT_ACCOMPANYING_PERIOD_WORK) }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<teleport to="body">
|
||||
<modal
|
||||
v-if="showModal"
|
||||
@close="closeModal"
|
||||
modal-dialog-class="modal-dialog-scrollable modal-xl"
|
||||
>
|
||||
<template #header>
|
||||
<h3>{{ trans(ACPW_DUPLICATE_SELECT_ACCOMPANYING_PERIOD_WORK) }}</h3>
|
||||
</template>
|
||||
|
||||
<template #body>
|
||||
<accompanying-period-work-list
|
||||
:accompanying-period-works="accompanyingPeriodWorks"
|
||||
v-model:selectedAcpw="selectedAcpw"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #footer>
|
||||
<button type="button" class="btn btn-save" @click="confirmSelection">
|
||||
{{ trans(CONFIRM) }}
|
||||
</button>
|
||||
</template>
|
||||
</modal>
|
||||
</teleport>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {onMounted, ref} from "vue";
|
||||
import Modal from "ChillMainAssets/vuejs/_components/Modal.vue";
|
||||
import AccompanyingPeriodWorkList from "./AccompanyingPeriodWorkList.vue";
|
||||
import {AccompanyingPeriodWork} from "../../../types";
|
||||
import {ACPW_DUPLICATE_SELECT_ACCOMPANYING_PERIOD_WORK, CONFIRM, trans,} from "translator";
|
||||
import {fetchResults} from "ChillMainAssets/lib/api/apiMethods";
|
||||
|
||||
interface AccompanyingPeriodWorkSelectorModalProps {
|
||||
accompanyingPeriodId: number;
|
||||
}
|
||||
|
||||
const selectedAcpw = ref<AccompanyingPeriodWork | null>(null);
|
||||
const showModal = ref(false);
|
||||
const accompanyingPeriodWorks = ref<AccompanyingPeriodWork[]>([]);
|
||||
const props = defineProps<AccompanyingPeriodWorkSelectorModalProps>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
pickWork: [payload: { work: AccompanyingPeriodWork | null }];
|
||||
}>();
|
||||
|
||||
onMounted(() => {
|
||||
if (props.accompanyingPeriodId) {
|
||||
getAccompanyingPeriodWorks(props.accompanyingPeriodId);
|
||||
} else {
|
||||
console.error("No accompanyingperiod id was given");
|
||||
}
|
||||
});
|
||||
const getAccompanyingPeriodWorks = async (periodId: number) => {
|
||||
const url = `/api/1.0/person/accompanying-course/${periodId}/works.json`;
|
||||
|
||||
try {
|
||||
accompanyingPeriodWorks.value = await fetchResults(url);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
/* makeFetch<number, AccompanyingPeriodWork[]>("GET", url)
|
||||
.then((response) => {
|
||||
accompanyingPeriodWorks.value = response;
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
});*/
|
||||
};
|
||||
|
||||
const openModal = () => (showModal.value = true);
|
||||
const closeModal = () => (showModal.value = false);
|
||||
const confirmSelection = () => {
|
||||
emit("pickWork", { work: selectedAcpw.value });
|
||||
closeModal();
|
||||
};
|
||||
</script>
|
@@ -68,6 +68,14 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
</li>
|
||||
{% if work.accompanyingPeriod.getWorks|length > 1 %}
|
||||
<li>
|
||||
<a class="btn btn-misc" href="{{ chill_path_add_return_path('chill_person_accompanying_period_work_assign_duplicate', { 'id': work.id }) }}">
|
||||
<i class="bi bi-chevron-contract"></i>
|
||||
{{ 'Merge'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if is_granted('CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_UPDATE', work) %}
|
||||
<li>
|
||||
<a class="btn btn-edit"
|
||||
|
@@ -0,0 +1,9 @@
|
||||
{%- macro details(w, accompanyingCourse, options) -%}
|
||||
{% include '@ChillPerson/AccompanyingCourseWork/_item.html.twig' with {
|
||||
'displayAction': false,
|
||||
'displayContent': 'short',
|
||||
'displayFontSmall': true,
|
||||
'itemBlocClass': '',
|
||||
'displayNotification': false
|
||||
} %}
|
||||
{% endmacro %}
|
@@ -0,0 +1,53 @@
|
||||
{% extends '@ChillPerson/AccompanyingCourse/layout.html.twig' %}
|
||||
|
||||
{% set activeRouteKey = 'chill_person_accompanying_period_work_assign_duplicate' %}
|
||||
|
||||
{% block title %}{{ 'Assign an accompanying period work duplicate' }}{% endblock %}
|
||||
|
||||
{% import '@ChillPerson/AccompanyingPeriodWorkDuplicate/_details.html.twig' as details %}
|
||||
|
||||
{% block content %}
|
||||
<div class="person-duplicate">
|
||||
|
||||
<div class="col">
|
||||
<h4>{{ 'acpw_duplicate.to keep'|trans ~ ':' }}</h4>
|
||||
<div class="accompanying-course-work">
|
||||
<div class="flex-table">
|
||||
{{ details.details(acpw, accompanyingCourse) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3>{{ 'acpw_duplicate.Assign duplicate'|trans }}</h3>
|
||||
{{ form_start(form) }}
|
||||
{%- if form.acpw is defined -%}
|
||||
{{ form_row(form.acpw) }}
|
||||
<div id="linked-acpw-selector" data-accompanying-period='{{ accompanyingCourse.id }}'></div>
|
||||
{% endif %}
|
||||
{{ form_rest(form) }}
|
||||
|
||||
<ul class="record_actions sticky-form-buttons">
|
||||
<li class="cancel">
|
||||
<a href="{{ path('chill_person_accompanying_period_work_show', {'id' : acpw.id}) }}" class="btn btn-cancel">
|
||||
{{ 'Return'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<button class="btn btn-action" type="submit">{{ 'Next'|trans }}</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
{{ form_end(form) }}
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
{{ parent() }}
|
||||
{{ encore_entry_script_tags('mod_duplicate_selector') }}
|
||||
{% endblock %}
|
||||
|
||||
{% block css %}
|
||||
{{ parent() }}
|
||||
{{ encore_entry_link_tags('mod_duplicate_selector') }}
|
||||
{% endblock %}
|
@@ -0,0 +1,72 @@
|
||||
{% extends "@ChillPerson/AccompanyingCourse/layout.html.twig" %}
|
||||
|
||||
{% import '@ChillPerson/AccompanyingPeriodWorkDuplicate/_details.html.twig' as details %}
|
||||
|
||||
{% block title %}{{ 'acpw_duplicate.title'|trans }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-fluid content"><div class="duplicate-content">
|
||||
|
||||
<h1>{{ 'acpw_duplicate.title'|trans }}</h1>
|
||||
<div class="col-md-11 border mb-3 mt-3 p-2">
|
||||
<p>{{ 'acpw_duplicate.description'|trans }}</p>
|
||||
</div>
|
||||
|
||||
<div class="col-md-11">
|
||||
<div class="col">
|
||||
<h4>{{ 'acpw_duplicate.to delete'|trans ~ ':' }}</h4>
|
||||
<div class="accompanying-course-work">
|
||||
<div class="flex-table">
|
||||
{{ details.details(acpw2, accompanyingCourse) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-11 mt-3">
|
||||
<div class="col">
|
||||
<h4>{{ 'acpw_duplicate.to keep'|trans ~ ':' }}</h4>
|
||||
<div class="accompanying-course-work">
|
||||
<div class="flex-table">
|
||||
{{ details.details(acpw, accompanyingCourse) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{ form_start(form) }}
|
||||
|
||||
<div class="col-md-12 centered">
|
||||
|
||||
<div class="container-fluid" style="padding-top: 1em;">
|
||||
<div class="clear" style="padding-top: 10px;">
|
||||
{{ form_widget(form.confirm) }}
|
||||
</div>
|
||||
<div class="col-11">
|
||||
{{ form_label(form.confirm) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="col-12 record_actions">
|
||||
<li class="cancel">
|
||||
<a href="{{ path('chill_person_accompanying_period_work_assign_duplicate', {id : acpw.id}) }}" class="btn btn-cancel">
|
||||
{{ 'Return'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
<li class="cancel">
|
||||
<a href="{{ path('chill_person_acpw_duplicate_confirm', { acpw1_id : acpw2.id, acpw2_id : acpw.id }) }}"
|
||||
class="btn btn-action">
|
||||
<i class="fa fa-exchange"></i>
|
||||
{{ 'Invert'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<button class="btn btn-submit" type="submit"><i class="bi bi-chevron-contract"></i>{{ 'Merge'|trans }}</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
{{ form_end(form) }}
|
||||
|
||||
</div></div>
|
||||
{% endblock %}
|
@@ -0,0 +1,112 @@
|
||||
<?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 Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
/**
|
||||
* Service for merging two AccompanyingPeriodWork entities into a single entity.
|
||||
*/
|
||||
class AccompanyingPeriodWorkMergeService
|
||||
{
|
||||
public function __construct(private readonly EntityManagerInterface $em) {}
|
||||
|
||||
/**
|
||||
* Merges two AccompanyingPeriodWork entities into one by transferring relevant data and removing the obsolete entity.
|
||||
*
|
||||
* @param AccompanyingPeriodWork $toKeep the entity to retain after the merge
|
||||
* @param AccompanyingPeriodWork $toDelete the entity to be removed after transferring data
|
||||
*
|
||||
* @return AccompanyingPeriodWork the kept accompanying period work
|
||||
*/
|
||||
public function merge(AccompanyingPeriodWork $toKeep, AccompanyingPeriodWork $toDelete): AccompanyingPeriodWork
|
||||
{
|
||||
$this->em->wrapInTransaction(function (EntityManagerInterface $entityManager) use ($toKeep, $toDelete) {
|
||||
$this->alterStartDate($toKeep, $toDelete);
|
||||
$this->alterEndDate($toKeep, $toDelete);
|
||||
$this->concatenateComments($toKeep, $toDelete);
|
||||
$this->transferWorkflowsSQL($toKeep, $toDelete);
|
||||
$this->updateReferencesSQL($toKeep, $toDelete);
|
||||
$entityManager->remove($toDelete);
|
||||
});
|
||||
|
||||
return $toKeep;
|
||||
}
|
||||
|
||||
private function transferWorkflowsSQL(AccompanyingPeriodWork $toKeep, AccompanyingPeriodWork $toDelete): void
|
||||
{
|
||||
$this->em->getConnection()->executeQuery(
|
||||
"UPDATE chill_main_workflow_entity w
|
||||
SET relatedentityid = :toKeepId
|
||||
WHERE w.relatedentityid = :toDeleteId
|
||||
AND w.relatedentityclass = 'Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWork'",
|
||||
['toKeepId' => $toKeep->getId(), 'toDeleteId' => $toDelete->getId()]
|
||||
);
|
||||
}
|
||||
|
||||
private function alterStartDate(AccompanyingPeriodWork $toKeep, AccompanyingPeriodWork $toDelete): void
|
||||
{
|
||||
$startDate = min($toKeep->getStartDate(), $toDelete->getStartDate());
|
||||
$toKeep->setStartDate($startDate);
|
||||
}
|
||||
|
||||
private function alterEndDate(AccompanyingPeriodWork $toKeep, AccompanyingPeriodWork $toDelete): void
|
||||
{
|
||||
if (null === $toKeep->getEndDate() || null === $toDelete->getEndDate()) {
|
||||
$toKeep->setEndDate(null);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$endDate = max($toKeep->getEndDate(), $toDelete->getEndDate());
|
||||
$toKeep->setEndDate($endDate);
|
||||
}
|
||||
|
||||
private function concatenateComments(AccompanyingPeriodWork $toKeep, AccompanyingPeriodWork $toDelete): void
|
||||
{
|
||||
$toKeep->setNote($toKeep->getNote()."\n\n-----------------\n\n".$toDelete->getNote());
|
||||
$toKeep->getPrivateComment()->concatenateComments($toDelete->getPrivateComment());
|
||||
}
|
||||
|
||||
private function updateReferencesSQL(AccompanyingPeriodWork $toKeep, AccompanyingPeriodWork $toDelete): void
|
||||
{
|
||||
foreach ($toDelete->getAccompanyingPeriodWorkEvaluations() as $evaluation) {
|
||||
$toKeep->addAccompanyingPeriodWorkEvaluation($evaluation);
|
||||
}
|
||||
|
||||
foreach ($toDelete->getReferrers() as $referrer) {
|
||||
// we only keep the current referrer
|
||||
$toKeep->addReferrer($referrer);
|
||||
}
|
||||
|
||||
foreach ($toDelete->getPersons() as $person) {
|
||||
$toKeep->addPerson($person);
|
||||
}
|
||||
|
||||
if (null === $toKeep->getHandlingThierParty()) {
|
||||
$toKeep->setHandlingThierParty($toDelete->getHandlingThierParty());
|
||||
}
|
||||
|
||||
foreach ($toDelete->getThirdParties() as $thirdParty) {
|
||||
$toKeep->addThirdParty($thirdParty);
|
||||
}
|
||||
|
||||
foreach ($toDelete->getGoals() as $goal) {
|
||||
$toKeep->addGoal($goal);
|
||||
}
|
||||
|
||||
foreach ($toDelete->getResults() as $result) {
|
||||
$toKeep->addResult($result);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,205 @@
|
||||
<?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\MainBundle\Entity\User;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkGoal;
|
||||
use Chill\PersonBundle\Entity\SocialWork\Result;
|
||||
use Chill\PersonBundle\Service\AccompanyingPeriodWork\AccompanyingPeriodWorkMergeService;
|
||||
use Chill\ThirdPartyBundle\Entity\ThirdParty;
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Monolog\Test\TestCase;
|
||||
use Prophecy\Argument;
|
||||
use Prophecy\PhpUnit\ProphecyTrait;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @coversNothing
|
||||
*/
|
||||
class AccompanyingPeriodWorkMergeServiceTest extends TestCase
|
||||
{
|
||||
use ProphecyTrait;
|
||||
|
||||
private function buildMergeService(AccompanyingPeriodWork $toRemove): AccompanyingPeriodWorkMergeService
|
||||
{
|
||||
$entityManager = $this->prophesize(EntityManagerInterface::class);
|
||||
$entityManager->wrapInTransaction(Argument::type('callable'))->will(function ($args) use ($entityManager) {
|
||||
call_user_func_array($args[0], [$entityManager->reveal()]);
|
||||
})->shouldBeCalled();
|
||||
$entityManager->remove($toRemove)->shouldBeCalled();
|
||||
|
||||
$connection = $this->prophesize(Connection::class);
|
||||
$connection->executeQuery(Argument::type('string'), Argument::type('array'))->shouldBeCalled();
|
||||
|
||||
$entityManager->getConnection()->willReturn($connection->reveal());
|
||||
|
||||
return new AccompanyingPeriodWorkMergeService($entityManager->reveal());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideStartDateMoveData
|
||||
*/
|
||||
public function testStartDateMove(AccompanyingPeriodWork $toKeep, AccompanyingPeriodWork $toDelete, ?\DateTime $expected): void
|
||||
{
|
||||
$service = $this->buildMergeService($toDelete);
|
||||
$return = $service->merge($toKeep, $toDelete);
|
||||
|
||||
self::assertEquals($expected, $return->getStartDate());
|
||||
}
|
||||
|
||||
public static function provideStartDateMoveData(): array
|
||||
{
|
||||
return [
|
||||
'Earliest date kept when toKeep is earlier' => [
|
||||
(new AccompanyingPeriodWork())->setStartDate(new \DateTime('2023-01-01')),
|
||||
(new AccompanyingPeriodWork())->setStartDate(new \DateTime('2023-06-01')),
|
||||
new \DateTime('2023-01-01'),
|
||||
],
|
||||
'Earliest date kept when toDelete is earlier' => [
|
||||
(new AccompanyingPeriodWork())->setStartDate(new \DateTime('2023-06-01')),
|
||||
(new AccompanyingPeriodWork())->setStartDate(new \DateTime('2023-01-01')),
|
||||
new \DateTime('2023-01-01'),
|
||||
],
|
||||
'Same start dates remain unchanged' => [
|
||||
(new AccompanyingPeriodWork())->setStartDate(new \DateTime('2023-01-01')),
|
||||
(new AccompanyingPeriodWork())->setStartDate(new \DateTime('2023-01-01')),
|
||||
new \DateTime('2023-01-01'),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideEndDateMoveData
|
||||
*/
|
||||
public function testEndDateMove(AccompanyingPeriodWork $toKeep, AccompanyingPeriodWork $toDelete, ?\DateTimeImmutable $expected): void
|
||||
{
|
||||
$service = $this->buildMergeService($toDelete);
|
||||
$return = $service->merge($toKeep, $toDelete);
|
||||
|
||||
self::assertEquals($expected, $return->getEndDate());
|
||||
}
|
||||
|
||||
public static function provideEndDateMoveData(): array
|
||||
{
|
||||
return [
|
||||
'Oldest date kept when toKeep is older' => [
|
||||
(new AccompanyingPeriodWork())->setEndDate(new \DateTimeImmutable('2022-01-01'))->setStartDate(new \DateTime('2021-01-01')),
|
||||
(new AccompanyingPeriodWork())->setEndDate(new \DateTimeImmutable('2023-06-01'))->setStartDate(new \DateTime('2021-01-01')),
|
||||
new \DateTimeImmutable('2023-06-01'),
|
||||
],
|
||||
'Oldest date kept when toDelete is older' => [
|
||||
(new AccompanyingPeriodWork())->setEndDate(new \DateTimeImmutable('2023-06-01'))->setStartDate(new \DateTime('2021-01-01')),
|
||||
(new AccompanyingPeriodWork())->setEndDate(new \DateTimeImmutable('2022-01-01'))->setStartDate(new \DateTime('2021-01-01')),
|
||||
new \DateTimeImmutable('2023-06-01'),
|
||||
],
|
||||
'Same end dates remain unchanged' => [
|
||||
(new AccompanyingPeriodWork())->setEndDate(new \DateTimeImmutable('2023-01-01'))->setStartDate(new \DateTime('2021-01-01')),
|
||||
(new AccompanyingPeriodWork())->setEndDate(new \DateTimeImmutable('2023-01-01'))->setStartDate(new \DateTime('2021-01-01')),
|
||||
new \DateTimeImmutable('2023-01-01'),
|
||||
],
|
||||
'End date is null if toKeep is null' => [
|
||||
(new AccompanyingPeriodWork())->setEndDate(null)->setStartDate(new \DateTime('2021-01-01')),
|
||||
(new AccompanyingPeriodWork())->setEndDate(new \DateTimeImmutable('2023-01-01'))->setStartDate(new \DateTime('2021-01-01')),
|
||||
null,
|
||||
],
|
||||
'End date is null if toDelete is null' => [
|
||||
(new AccompanyingPeriodWork())->setEndDate(new \DateTimeImmutable('2023-01-01'))->setStartDate(new \DateTime('2021-01-01')),
|
||||
(new AccompanyingPeriodWork())->setEndDate(null)->setStartDate(new \DateTime('2021-01-01')),
|
||||
null,
|
||||
],
|
||||
'End date is null if both are null' => [
|
||||
(new AccompanyingPeriodWork())->setEndDate(null)->setStartDate(new \DateTime('2021-01-01')),
|
||||
(new AccompanyingPeriodWork())->setEndDate(null)->setStartDate(new \DateTime('2021-01-01')),
|
||||
null,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideMoveHandlingThirdPartyData
|
||||
*/
|
||||
public function testMoveHandlingThirdParty(AccompanyingPeriodWork $toKeep, AccompanyingPeriodWork $toDelete, ?ThirdParty $expected): void
|
||||
{
|
||||
$service = $this->buildMergeService($toDelete);
|
||||
$return = $service->merge($toKeep, $toDelete);
|
||||
|
||||
self::assertSame($expected, $return->getHandlingThierParty());
|
||||
}
|
||||
|
||||
public static function provideMoveHandlingThirdPartyData(): iterable
|
||||
{
|
||||
yield 'Third party not change when existing in kept' => [
|
||||
(new AccompanyingPeriodWork())->setStartDate(new \DateTimeImmutable('2022-01-01'))->setHandlingThierParty($tpA = new ThirdParty()),
|
||||
(new AccompanyingPeriodWork())->setStartDate(new \DateTimeImmutable('2022-01-01'))->setHandlingThierParty(new ThirdParty()),
|
||||
$tpA,
|
||||
];
|
||||
|
||||
yield 'Third party will change when not existing in kept' => [
|
||||
(new AccompanyingPeriodWork())->setStartDate(new \DateTimeImmutable('2022-01-01')),
|
||||
(new AccompanyingPeriodWork())->setStartDate(new \DateTimeImmutable('2022-01-01'))->setHandlingThierParty($tpB = new ThirdParty()),
|
||||
$tpB,
|
||||
];
|
||||
|
||||
yield 'Third party do not change when not existing in removed' => [
|
||||
(new AccompanyingPeriodWork())->setStartDate(new \DateTimeImmutable('2022-01-01'))->setHandlingThierParty($tpC = new ThirdParty()),
|
||||
(new AccompanyingPeriodWork())->setStartDate(new \DateTimeImmutable('2022-01-01')),
|
||||
$tpC,
|
||||
];
|
||||
}
|
||||
|
||||
public function testMerge(): void
|
||||
{
|
||||
$accompanyingPeriodWork = new AccompanyingPeriodWork();
|
||||
$accompanyingPeriodWork->setStartDate(new \DateTime('2022-01-01'));
|
||||
$accompanyingPeriodWork->addReferrer($userA = new User());
|
||||
$accompanyingPeriodWork->addReferrer($userC = new User());
|
||||
$accompanyingPeriodWork->addAccompanyingPeriodWorkEvaluation($evaluationA = new AccompanyingPeriodWorkEvaluation());
|
||||
$accompanyingPeriodWork->setNote('blabla');
|
||||
$accompanyingPeriodWork->addThirdParty($thirdPartyA = new ThirdParty());
|
||||
|
||||
$toDelete = new AccompanyingPeriodWork();
|
||||
$toDelete->setStartDate(new \DateTime('2022-01-01'));
|
||||
$toDelete->addReferrer($userB = new User());
|
||||
$toDelete->addReferrer($userC);
|
||||
$toDelete->addAccompanyingPeriodWorkEvaluation($evaluationB = new AccompanyingPeriodWorkEvaluation());
|
||||
$toDelete->setNote('boum');
|
||||
$toDelete->addThirdParty($thirdPartyB = new ThirdParty());
|
||||
$toDelete->addGoal($goalA = new AccompanyingPeriodWorkGoal());
|
||||
$toDelete->addResult($resultA = new Result());
|
||||
|
||||
$service = $this->buildMergeService($toDelete);
|
||||
$service->merge($accompanyingPeriodWork, $toDelete);
|
||||
|
||||
self::assertTrue($accompanyingPeriodWork->getReferrers()->contains($userA));
|
||||
self::assertTrue($accompanyingPeriodWork->getReferrers()->contains($userB));
|
||||
self::assertTrue($accompanyingPeriodWork->getReferrers()->contains($userC));
|
||||
|
||||
self::assertTrue($accompanyingPeriodWork->getAccompanyingPeriodWorkEvaluations()->contains($evaluationA));
|
||||
self::assertTrue($accompanyingPeriodWork->getAccompanyingPeriodWorkEvaluations()->contains($evaluationB));
|
||||
foreach ($accompanyingPeriodWork->getAccompanyingPeriodWorkEvaluations() as $evaluation) {
|
||||
self::assertSame($accompanyingPeriodWork, $evaluation->getAccompanyingPeriodWork());
|
||||
}
|
||||
|
||||
self::assertStringContainsString('blabla', $accompanyingPeriodWork->getNote());
|
||||
self::assertStringContainsString('boum', $toDelete->getNote());
|
||||
|
||||
self::assertTrue($accompanyingPeriodWork->getThirdParties()->contains($thirdPartyA));
|
||||
self::assertTrue($accompanyingPeriodWork->getThirdParties()->contains($thirdPartyB));
|
||||
|
||||
self::assertTrue($accompanyingPeriodWork->getGoals()->contains($goalA));
|
||||
self::assertTrue($accompanyingPeriodWork->getResults()->contains($resultA));
|
||||
}
|
||||
}
|
@@ -6,7 +6,6 @@ module.exports = function (encore, entries) {
|
||||
encore.addAliases({
|
||||
ChillPersonAssets: __dirname + "/Resources/public",
|
||||
});
|
||||
|
||||
encore.addEntry(
|
||||
"vue_household_members_editor",
|
||||
__dirname + "/Resources/public/vuejs/HouseholdMembersEditor/index.js",
|
||||
@@ -31,7 +30,6 @@ module.exports = function (encore, entries) {
|
||||
"vue_export_action_goal_result",
|
||||
__dirname + "/Resources/public/vuejs/ExportFormActionGoalResult/index.js",
|
||||
);
|
||||
|
||||
encore.addEntry(
|
||||
"mod_set_referrer",
|
||||
__dirname + "/Resources/public/mod/AccompanyingPeriod/setReferrer.js",
|
||||
@@ -66,4 +64,8 @@ module.exports = function (encore, entries) {
|
||||
"page_create_person",
|
||||
__dirname + "/Resources/public/page/person/create-person.js",
|
||||
);
|
||||
encore.addEntry(
|
||||
"mod_duplicate_selector",
|
||||
__dirname + "/Resources/public/mod/DuplicateSelector/AccompanyingPeriodWorkSelector.ts",
|
||||
);
|
||||
};
|
||||
|
@@ -1502,6 +1502,18 @@ entity_display_title:
|
||||
Work (n°%w%): "Action d'accompagnement (n°%w%)"
|
||||
Accompanying Course (n°%w%): "Parcours d'accompagnement (n°%w%)"
|
||||
|
||||
acpw_duplicate:
|
||||
title: Fusionner les actions d'accompagnement
|
||||
description: Cette fusion conservera la date de début la plus ancienne, la date de fin la plus récente, toutes les évaluations, documents et workflows. Les agents traitants seront additionnés ainsi que les tiers intervenants. Les commentaires seront mis l'un à la suite de l'autre.
|
||||
Select accompanying period work: Selectionner un action d'accompagnement
|
||||
Assign duplicate: Désigner un action d'accompagnement doublon
|
||||
Accompanying period work to delete: Action d'accompagnement à supprimer
|
||||
Accompanying period work to delete explanation: Cet action d'accompagnement sera supprimé.
|
||||
Accompanying period work to keep: Action d'accompagnement à conserver
|
||||
to keep: Action d'accompagnement à conserver
|
||||
to delete: Action d'accompagnement à supprimer
|
||||
Successfully merged: Action d'accompagnement fusionnée avec succès.
|
||||
|
||||
my_parcours_filters:
|
||||
referrer_parcours_and_acpw: Agent traitant ou réferent
|
||||
referrer_acpw: Agent traitant d'une action
|
||||
|
Reference in New Issue
Block a user