From 840ef6eed81e9676a594ca76cdfb9c2e6d55326a Mon Sep 17 00:00:00 2001 From: LenaertsJ Date: Wed, 2 Jul 2025 10:53:16 +0000 Subject: [PATCH] Resolve "Fusion actions d'accompagnement" --- .../unreleased/Feature-20250211-142243.yaml | 6 + package.json | 1 + .../Resources/public/vuejs/Activity/store.js | 8 +- .../Embeddable/PrivateCommentEmbeddable.php | 24 ++ .../Resources/public/lib/api/apiMethods.ts | 3 + .../ChillMainBundle/Resources/public/types.ts | 4 + .../Resources/public/vuejs/PickEntity/i18n.js | 2 + .../Resources/views/Form/fields.html.twig | 21 ++ .../Workflow/PrivateCommentEmbeddableTest.php | 56 +++++ .../AccompanyingCourseApiController.php | 20 +- ...ompanyingPeriodWorkDuplicateController.php | 131 ++++++++++ .../AccompanyingPeriodWorkEvaluation.php | 8 - .../AccompanyingPeriodWorkGoal.php | 8 - .../Form/FindAccompanyingPeriodWorkType.php | 42 ++++ .../PickLinkedAccompanyingPeriodWorkType.php | 51 ++++ .../AccompanyingPeriodWorkSelector.ts | 49 ++++ .../Resources/public/types.ts | 224 +++++++++++++++++- .../AccompanyingPeriodWorkItem.vue | 60 +++++ .../AccompanyingPeriodWorkList.vue | 47 ++++ .../AccompanyingPeriodWorkSelectorModal.vue | 101 ++++++++ .../AccompanyingCourseWork/show.html.twig | 8 + .../_details.html.twig | 9 + .../assign_acpw_duplicate.html.twig | 53 +++++ .../confirm.html.twig | 72 ++++++ .../AccompanyingPeriodWorkMergeService.php | 112 +++++++++ ...AccompanyingPeriodWorkMergeServiceTest.php | 205 ++++++++++++++++ .../ChillPersonBundle/chill.webpack.config.js | 6 +- .../translations/messages.fr.yml | 12 + .../Resources/public/types.ts | 47 ++++ symfony.lock | 9 + 30 files changed, 1367 insertions(+), 32 deletions(-) create mode 100644 .changes/unreleased/Feature-20250211-142243.yaml create mode 100644 src/Bundle/ChillMainBundle/Tests/Entity/Workflow/PrivateCommentEmbeddableTest.php create mode 100644 src/Bundle/ChillPersonBundle/Controller/AccompanyingPeriodWorkDuplicateController.php create mode 100644 src/Bundle/ChillPersonBundle/Form/FindAccompanyingPeriodWorkType.php create mode 100644 src/Bundle/ChillPersonBundle/Form/Type/PickLinkedAccompanyingPeriodWorkType.php create mode 100644 src/Bundle/ChillPersonBundle/Resources/public/mod/DuplicateSelector/AccompanyingPeriodWorkSelector.ts create mode 100644 src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AccompanyingPeriodWorkSelector/AccompanyingPeriodWorkItem.vue create mode 100644 src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AccompanyingPeriodWorkSelector/AccompanyingPeriodWorkList.vue create mode 100644 src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AccompanyingPeriodWorkSelector/AccompanyingPeriodWorkSelectorModal.vue create mode 100644 src/Bundle/ChillPersonBundle/Resources/views/AccompanyingPeriodWorkDuplicate/_details.html.twig create mode 100644 src/Bundle/ChillPersonBundle/Resources/views/AccompanyingPeriodWorkDuplicate/assign_acpw_duplicate.html.twig create mode 100644 src/Bundle/ChillPersonBundle/Resources/views/AccompanyingPeriodWorkDuplicate/confirm.html.twig create mode 100644 src/Bundle/ChillPersonBundle/Service/AccompanyingPeriodWork/AccompanyingPeriodWorkMergeService.php create mode 100644 src/Bundle/ChillPersonBundle/Tests/Service/AccompanyingPeriodWork/AccompanyingPeriodWorkMergeServiceTest.php create mode 100644 src/Bundle/ChillThirdPartyBundle/Resources/public/types.ts diff --git a/.changes/unreleased/Feature-20250211-142243.yaml b/.changes/unreleased/Feature-20250211-142243.yaml new file mode 100644 index 000000000..4f0b25e88 --- /dev/null +++ b/.changes/unreleased/Feature-20250211-142243.yaml @@ -0,0 +1,6 @@ +kind: Feature +body: Allow the merge of two accompanying period works +time: 2025-02-11T14:22:43.134106669+01:00 +custom: + Issue: "359" + SchemaChange: No schema change diff --git a/package.json b/package.json index fc50ba29c..73fa14d86 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "@hotwired/stimulus": "^3.0.0", "@luminateone/eslint-baseline": "^1.0.9", "@symfony/stimulus-bridge": "^3.2.0", + "@symfony/ux-translator": "file:vendor/symfony/ux-translator/assets", "@symfony/webpack-encore": "^4.1.0", "@tsconfig/node20": "^20.1.4", "@types/dompurify": "^3.0.5", diff --git a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/store.js b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/store.js index f29e2e6d4..8d09d2dd3 100644 --- a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/store.js +++ b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/store.js @@ -2,7 +2,7 @@ import "es6-promise/auto"; import { createStore } from "vuex"; import { postLocation } from "./api"; import prepareLocations from "./store.locations.js"; -import { makeFetch } from "ChillMainAssets/lib/api/apiMethods"; +import {fetchResults, makeFetch} from "ChillMainAssets/lib/api/apiMethods"; const debug = process.env.NODE_ENV !== "production"; //console.log('window.activity', window.activity); @@ -365,11 +365,11 @@ const store = createStore({ const accompanyingPeriodId = state.activity.accompanyingPeriod.id; const url = `/api/1.0/person/accompanying-course/${accompanyingPeriodId}/works.json`; try { - const works = await makeFetch("GET", url); - // console.log("works", works); + const works = await fetchResults(url); + // console.log('works', works); commit("setAccompanyingPeriodWorks", works); } catch (error) { - console.error("Failed to fetch accompanying period works:", error); + console.error('Failed to fetch works:', error); } }, getWhoAmI({ commit }) { diff --git a/src/Bundle/ChillMainBundle/Entity/Embeddable/PrivateCommentEmbeddable.php b/src/Bundle/ChillMainBundle/Entity/Embeddable/PrivateCommentEmbeddable.php index 0c2af22f7..5ada822c4 100644 --- a/src/Bundle/ChillMainBundle/Entity/Embeddable/PrivateCommentEmbeddable.php +++ b/src/Bundle/ChillMainBundle/Entity/Embeddable/PrivateCommentEmbeddable.php @@ -63,4 +63,28 @@ class PrivateCommentEmbeddable return $this; } + + /** + * Merges comments from the provided object into the current object. + * + * Identifies common user IDs between the current object's comments and the + * newComment's comments. If a user ID exists in both, their comments are + * concatenated with the provided separator. If a user ID exists only in the + * newComment, their comment is added to the current object directly. + * + * @param self $commentsToAppend the object containing the new comments to be merged + * @param string $separator the string used to separate concatenated comments + */ + public function concatenateComments(self $commentsToAppend, string $separator = "\n\n-----------\n\n"): void + { + $commonUserIds = array_intersect(array_keys($this->comments), array_keys($commentsToAppend->getComments())); + + foreach ($commentsToAppend->getComments() as $userId => $comment) { + if (in_array($userId, $commonUserIds, true)) { + $this->comments[$userId] = $this->comments[$userId].$separator.$commentsToAppend->getComments()[$userId]; + } else { + $this->comments[$userId] = $commentsToAppend->getComments()[$userId]; + } + } + } } diff --git a/src/Bundle/ChillMainBundle/Resources/public/lib/api/apiMethods.ts b/src/Bundle/ChillMainBundle/Resources/public/lib/api/apiMethods.ts index b50bb5534..e8256b348 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/lib/api/apiMethods.ts +++ b/src/Bundle/ChillMainBundle/Resources/public/lib/api/apiMethods.ts @@ -61,6 +61,9 @@ export interface ConflictHttpExceptionInterface /** * Generic api method that can be adapted to any fetch request + * + * This method is suitable make a single fetch. When performing a GET to fetch a list of elements, always consider pagination + * and use of the @link{fetchResults} method. */ export const makeFetch = ( method: "POST" | "GET" | "PUT" | "PATCH" | "DELETE", diff --git a/src/Bundle/ChillMainBundle/Resources/public/types.ts b/src/Bundle/ChillMainBundle/Resources/public/types.ts index 90ddacf22..b2ba6d948 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/types.ts +++ b/src/Bundle/ChillMainBundle/Resources/public/types.ts @@ -200,3 +200,7 @@ export interface WorkflowAttachment { updatedBy: User | null; genericDoc: null | GenericDoc; } + +export interface PrivateCommentEmbeddable { + comments: Record; +} diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/PickEntity/i18n.js b/src/Bundle/ChillMainBundle/Resources/public/vuejs/PickEntity/i18n.js index 8502bf53e..31e13bc88 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/PickEntity/i18n.js +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/PickEntity/i18n.js @@ -11,10 +11,12 @@ const appMessages = { user: "Utilisateurs", person: "Usagers", thirdparty: "Tiers", + acpw: "Action d'accompagnements", modal_title_one: "Indiquer un ", user_one: "Utilisateur", thirdparty_one: "Tiers", person_one: "Usager", + acpw_one: "Action d'accompagnement", }, }, }; diff --git a/src/Bundle/ChillMainBundle/Resources/views/Form/fields.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Form/fields.html.twig index 15794b9f7..033fdb342 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/Form/fields.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/Form/fields.html.twig @@ -266,6 +266,27 @@ data-label="{{ form.vars['label']|trans|escape('html_attr') }}"> {% endblock %} +{% block pick_linked_entities_row %} +
+
+ {{ form_label(form) }} + {{ form_help(form) }} +
+
+
+
+ {{ form_widget(form) }} +
+
+{% endblock %} + +{% block pick_linked_entities_widget %} + +
+ +{% endblock %} + {% block pick_postal_code_widget %} {{ form_help(form)}} diff --git a/src/Bundle/ChillMainBundle/Tests/Entity/Workflow/PrivateCommentEmbeddableTest.php b/src/Bundle/ChillMainBundle/Tests/Entity/Workflow/PrivateCommentEmbeddableTest.php new file mode 100644 index 000000000..359b194a8 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Tests/Entity/Workflow/PrivateCommentEmbeddableTest.php @@ -0,0 +1,56 @@ +prophesize(User::class); + $userA->getId()->willReturn(1); + $userB = $this->prophesize(User::class); + $userB->getId()->willReturn(2); + $userC = $this->prophesize(User::class); + $userC->getId()->willReturn(3); + + $toKeep = new PrivateCommentEmbeddable(); + $toKeep->setCommentForUser($userA->reveal(), 'My comment for A'); + $toKeep->setCommentForUser($userB->reveal(), 'My comment for B'); + + $toDelete = new PrivateCommentEmbeddable(); + $toDelete->setCommentForUser($userC->reveal(), 'My comment for C'); + $toDelete->setCommentForUser($userB->reveal(), 'Another comment for B'); + + $toKeep->concatenateComments($toDelete, '----'); + + self::assertTrue($toKeep->hasCommentForUser($userA->reveal())); + self::assertEquals('My comment for A', $toKeep->getCommentForUser($userA->reveal())); + + self::assertTrue($toKeep->hasCommentForUser($userB->reveal())); + self::assertEquals('My comment for B----Another comment for B', $toKeep->getCommentForUser($userB->reveal())); + + self::assertTrue($toKeep->hasCommentForUser($userC->reveal())); + self::assertEquals('My comment for C', $toKeep->getCommentForUser($userC->reveal())); + } +} diff --git a/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseApiController.php b/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseApiController.php index ac7c7dec4..0da1677ac 100644 --- a/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseApiController.php +++ b/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseApiController.php @@ -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 diff --git a/src/Bundle/ChillPersonBundle/Controller/AccompanyingPeriodWorkDuplicateController.php b/src/Bundle/ChillPersonBundle/Controller/AccompanyingPeriodWorkDuplicateController.php new file mode 100644 index 000000000..84d5fea0b --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Controller/AccompanyingPeriodWorkDuplicateController.php @@ -0,0 +1,131 @@ +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); + } + } + } +} diff --git a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWorkEvaluation.php b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWorkEvaluation.php index 748174e19..44713c476 100644 --- a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWorkEvaluation.php +++ b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWorkEvaluation.php @@ -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; diff --git a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWorkGoal.php b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWorkGoal.php index 8e78f40fb..f308c219b 100644 --- a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWorkGoal.php +++ b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWorkGoal.php @@ -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; diff --git a/src/Bundle/ChillPersonBundle/Form/FindAccompanyingPeriodWorkType.php b/src/Bundle/ChillPersonBundle/Form/FindAccompanyingPeriodWorkType.php new file mode 100644 index 000000000..ab78cb541 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Form/FindAccompanyingPeriodWorkType.php @@ -0,0 +1,42 @@ +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); + } +} diff --git a/src/Bundle/ChillPersonBundle/Form/Type/PickLinkedAccompanyingPeriodWorkType.php b/src/Bundle/ChillPersonBundle/Form/Type/PickLinkedAccompanyingPeriodWorkType.php new file mode 100644 index 000000000..55eda49e6 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Form/Type/PickLinkedAccompanyingPeriodWorkType.php @@ -0,0 +1,51 @@ +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'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Resources/public/mod/DuplicateSelector/AccompanyingPeriodWorkSelector.ts b/src/Bundle/ChillPersonBundle/Resources/public/mod/DuplicateSelector/AccompanyingPeriodWorkSelector.ts new file mode 100644 index 000000000..6ceb64b98 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Resources/public/mod/DuplicateSelector/AccompanyingPeriodWorkSelector.ts @@ -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( + '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( + `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: + '', + components: { AccompanyingPeriodWorkSelectorModal }, + data() { + return { accompanyingPeriodId }; + }, + methods: { + pickWork: function (payload: { work: AccompanyingPeriodWork }) { + console.log("payload", payload); + input.value = payload.work.id.toString(); + }, + }, + }); + + app.mount(el); + }); +}); diff --git a/src/Bundle/ChillPersonBundle/Resources/public/types.ts b/src/Bundle/ChillPersonBundle/Resources/public/types.ts index 909f00bb9..78e82e48e 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/types.ts +++ b/src/Bundle/ChillPersonBundle/Resources/public/types.ts @@ -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; +} diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AccompanyingPeriodWorkSelector/AccompanyingPeriodWorkItem.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AccompanyingPeriodWorkSelector/AccompanyingPeriodWorkItem.vue new file mode 100644 index 000000000..df5d2ffdd --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AccompanyingPeriodWorkSelector/AccompanyingPeriodWorkItem.vue @@ -0,0 +1,60 @@ + + + diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AccompanyingPeriodWorkSelector/AccompanyingPeriodWorkList.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AccompanyingPeriodWorkSelector/AccompanyingPeriodWorkList.vue new file mode 100644 index 000000000..c3615c959 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AccompanyingPeriodWorkSelector/AccompanyingPeriodWorkList.vue @@ -0,0 +1,47 @@ + + + + + diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AccompanyingPeriodWorkSelector/AccompanyingPeriodWorkSelectorModal.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AccompanyingPeriodWorkSelector/AccompanyingPeriodWorkSelectorModal.vue new file mode 100644 index 000000000..ad6c4c82f --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AccompanyingPeriodWorkSelector/AccompanyingPeriodWorkSelectorModal.vue @@ -0,0 +1,101 @@ + + + diff --git a/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourseWork/show.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourseWork/show.html.twig index 438a6fb7f..176861d75 100644 --- a/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourseWork/show.html.twig +++ b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourseWork/show.html.twig @@ -68,6 +68,14 @@ {% endif %} + {% if work.accompanyingPeriod.getWorks|length > 1 %} +
  • + + + {{ 'Merge'|trans }} + +
  • + {% endif %} {% if is_granted('CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_UPDATE', work) %}
  • + +
    +

    {{ 'acpw_duplicate.to keep'|trans ~ ':' }}

    +
    +
    + {{ details.details(acpw, accompanyingCourse) }} +
    +
    +
    + +

    {{ 'acpw_duplicate.Assign duplicate'|trans }}

    + {{ form_start(form) }} + {%- if form.acpw is defined -%} + {{ form_row(form.acpw) }} +
    + {% endif %} + {{ form_rest(form) }} + +
    + + {{ form_end(form) }} + + +{% endblock %} + +{% block js %} + {{ parent() }} + {{ encore_entry_script_tags('mod_duplicate_selector') }} +{% endblock %} + +{% block css %} + {{ parent() }} + {{ encore_entry_link_tags('mod_duplicate_selector') }} +{% endblock %} diff --git a/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingPeriodWorkDuplicate/confirm.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingPeriodWorkDuplicate/confirm.html.twig new file mode 100644 index 000000000..92d65b7c3 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingPeriodWorkDuplicate/confirm.html.twig @@ -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 %} +
    + +

    {{ 'acpw_duplicate.title'|trans }}

    +
    +

    {{ 'acpw_duplicate.description'|trans }}

    +
    + +
    +
    +

    {{ 'acpw_duplicate.to delete'|trans ~ ':' }}

    +
    +
    + {{ details.details(acpw2, accompanyingCourse) }} +
    +
    +
    +
    + +
    +
    +

    {{ 'acpw_duplicate.to keep'|trans ~ ':' }}

    +
    +
    + {{ details.details(acpw, accompanyingCourse) }} +
    +
    +
    +
    + + {{ form_start(form) }} + +
    + +
    +
    + {{ form_widget(form.confirm) }} +
    +
    + {{ form_label(form.confirm) }} +
    +
    +
    + + + + {{ form_end(form) }} + +
    +{% endblock %} diff --git a/src/Bundle/ChillPersonBundle/Service/AccompanyingPeriodWork/AccompanyingPeriodWorkMergeService.php b/src/Bundle/ChillPersonBundle/Service/AccompanyingPeriodWork/AccompanyingPeriodWorkMergeService.php new file mode 100644 index 000000000..ba0f93ce3 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Service/AccompanyingPeriodWork/AccompanyingPeriodWorkMergeService.php @@ -0,0 +1,112 @@ +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); + } + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Service/AccompanyingPeriodWork/AccompanyingPeriodWorkMergeServiceTest.php b/src/Bundle/ChillPersonBundle/Tests/Service/AccompanyingPeriodWork/AccompanyingPeriodWorkMergeServiceTest.php new file mode 100644 index 000000000..e69c8beee --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Service/AccompanyingPeriodWork/AccompanyingPeriodWorkMergeServiceTest.php @@ -0,0 +1,205 @@ +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)); + } +} diff --git a/src/Bundle/ChillPersonBundle/chill.webpack.config.js b/src/Bundle/ChillPersonBundle/chill.webpack.config.js index e4c6ed3af..effbec70f 100644 --- a/src/Bundle/ChillPersonBundle/chill.webpack.config.js +++ b/src/Bundle/ChillPersonBundle/chill.webpack.config.js @@ -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", + ); }; diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index 94e6083ca..17a00d525 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -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 diff --git a/src/Bundle/ChillThirdPartyBundle/Resources/public/types.ts b/src/Bundle/ChillThirdPartyBundle/Resources/public/types.ts new file mode 100644 index 000000000..20ae3309b --- /dev/null +++ b/src/Bundle/ChillThirdPartyBundle/Resources/public/types.ts @@ -0,0 +1,47 @@ +import { + Address, + Center, + Civility, + DateTime, + User, +} from "ChillMainAssets/types"; + +export interface Thirdparty { + acronym: string | null; + active: boolean; + address: Address | null; + canonicalized: string | null; + categories: ThirdpartyCategory[]; + centers: Center[]; + children: Thirdparty[]; + civility: Civility | null; + comment: string | null; + contactDataAnonymous: boolean; + createdAt: DateTime; + createdBy: User | null; + email: string | null; + firstname: string | null; + id: number | null; + kind: string; + name: string; + nameCompany: string | null; + parent: Thirdparty | null; + profession: string; + telephone: string | null; + thirdPartyTypes: ThirdpartyType[] | null; + updatedAt: DateTime | null; + updatedBy: User | null; +} + +interface ThirdpartyType { + key: string; + value: string; +} + +export interface ThirdpartyCategory { + id: number; + active: boolean; + name: { + fr: string; + }; +} diff --git a/symfony.lock b/symfony.lock index ee44b19d8..6409f0c1d 100644 --- a/symfony.lock +++ b/symfony.lock @@ -14,6 +14,15 @@ "config/routes/annotations.yaml" ] }, + "doctrine/deprecations": { + "version": "1.1", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "1.0", + "ref": "87424683adc81d7dc305eefec1fced883084aab9" + } + }, "doctrine/doctrine-bundle": { "version": "2.13", "recipe": {