Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
5.91% covered (danger)
5.91%
13 / 220
14.29% covered (danger)
14.29%
3 / 21
CRAP
0.00% covered (danger)
0.00%
0 / 1
ConformiteOrganisationController
5.91% covered (danger)
5.91%
13 / 220
14.29% covered (danger)
14.29%
3 / 21
3694.53
0.00% covered (danger)
0.00%
0 / 1
 __construct
90.91% covered (success)
90.91%
10 / 11
0.00% covered (danger)
0.00%
0 / 1
3.01
 createAction
0.00% covered (danger)
0.00%
0 / 33
0.00% covered (danger)
0.00%
0 / 1
90
 showAction
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 deleteAction
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 deleteConfirmationAction
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 listConformitesAction
0.00% covered (danger)
0.00%
0 / 28
0.00% covered (danger)
0.00%
0 / 1
42
 editAction
0.00% covered (danger)
0.00%
0 / 24
0.00% covered (danger)
0.00%
0 / 1
90
 reportAction
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
30
 addMissingNewQuestionsAndProcessus
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
20
 getDomain
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getModel
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getModelClass
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getFormType
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 listDataTables
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
30
 getLabelAndKeysArray
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
6
 generateActionCell
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
6
 getRequestCriteria
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
12
 getBaseDataTablesResponse
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
2
 createQueryBuilder
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 count
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
20
 getManager
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2
3namespace App\Domain\Registry\Controller;
4
5use App\Application\Controller\CRUDController;
6use App\Application\Symfony\Security\UserProvider;
7use App\Application\Traits\RepositoryUtils;
8use App\Application\Traits\ServersideDatatablesTrait;
9use App\Domain\Documentation\Model\Category;
10use App\Domain\Registry\Form\Type\ConformiteOrganisation\EvaluationPiloteType;
11use App\Domain\Registry\Form\Type\ConformiteOrganisation\EvaluationType;
12use App\Domain\Registry\Model\ConformiteOrganisation\Conformite;
13use App\Domain\Registry\Model\ConformiteOrganisation\Evaluation;
14use App\Domain\Registry\Model\ConformiteOrganisation\Reponse;
15use App\Domain\Registry\Repository\ConformiteOrganisation as Repository;
16use App\Domain\Registry\Symfony\EventSubscriber\Event\ConformiteOrganisationEvent;
17use App\Domain\Reporting\Handler\WordHandler;
18use App\Domain\User\Dictionary\UserRoleDictionary;
19use App\Domain\User\Model\User;
20use Doctrine\Common\Collections\Collection;
21use Doctrine\ORM\EntityManagerInterface;
22use Doctrine\ORM\QueryBuilder;
23use Doctrine\Persistence\ManagerRegistry;
24use Knp\Snappy\Pdf;
25use Symfony\Component\EventDispatcher\EventDispatcherInterface;
26use Symfony\Component\Form\SubmitButton;
27use Symfony\Component\HttpFoundation\JsonResponse;
28use Symfony\Component\HttpFoundation\Request;
29use Symfony\Component\HttpFoundation\Response;
30use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
31use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
32use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
33use Symfony\Component\Routing\RouterInterface;
34use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
35use Symfony\Contracts\Translation\TranslatorInterface;
36
37/**
38 * @property Repository\Evaluation $repository
39 */
40class ConformiteOrganisationController extends CRUDController
41{
42    use ServersideDatatablesTrait;
43    use RepositoryUtils;
44
45    /**
46     * @var Repository\Question
47     */
48    private $questionRepository;
49
50    /**
51     * @var Repository\Processus
52     */
53    private $processusRepository;
54
55    /**
56     * @var Repository\Conformite
57     */
58    private $conformiteRepository;
59
60    /**
61     * @var EventDispatcherInterface
62     */
63    private $dispatcher;
64
65    /**
66     * @var WordHandler
67     */
68    private $wordHandler;
69    private ManagerRegistry $registry;
70    private RouterInterface $router;
71
72    public function __construct(
73        EntityManagerInterface $entityManager,
74        TranslatorInterface $translator,
75        Repository\Evaluation $repository,
76        Repository\Question $questionRepository,
77        Repository\Processus $processusRepository,
78        Repository\Conformite $conformiteRepository,
79        UserProvider $userProvider,
80        AuthorizationCheckerInterface $authorizationChecker,
81        EventDispatcherInterface $dispatcher,
82        WordHandler $wordHandler,
83        Pdf $pdf,
84        RouterInterface $router,
85        ManagerRegistry $registry,
86    ) {
87        parent::__construct($entityManager, $translator, $repository, $pdf, $userProvider, $authorizationChecker);
88        $this->questionRepository   = $questionRepository;
89        $this->processusRepository  = $processusRepository;
90        $this->conformiteRepository = $conformiteRepository;
91        $this->dispatcher           = $dispatcher;
92        $this->wordHandler          = $wordHandler;
93        $this->router               = $router;
94        $this->registry             = $registry;
95        // Deny access to single collectivity users if conformite organisation module is disabled
96        // Fixes https://gitlab.adullact.net/soluris/madis/-/issues/949
97        $user = $userProvider->getAuthenticatedUser();
98        if ($user && !$user->hasModuleConformiteOrganisation()) {
99            throw new AccessDeniedHttpException('Ce module est désactivé sur votre structure');
100        }
101    }
102
103    public function createAction(Request $request): Response
104    {
105        $organisation = $this->userProvider->getAuthenticatedUser()->getCollectivity();
106
107        if ($organisation && !$organisation->isHasModuleConformiteOrganisation()) {
108            throw new AccessDeniedHttpException('Ce module est désactivé sur la structure');
109        }
110
111        $evaluation = $this->repository->findLastByOrganisation($organisation);
112        if (null !== $evaluation) {
113            $evaluation = clone $evaluation;
114            $this->addMissingNewQuestionsAndProcessus($evaluation);
115        } else {
116            $evaluation = new Evaluation();
117
118            foreach ($this->processusRepository->findAll(['position' => 'asc']) as $processus) {
119                $conformite = new Conformite();
120                $conformite->setProcessus($processus);
121                foreach ($this->questionRepository->findAllByProcessus($processus) as $question) {
122                    $reponse = new Reponse();
123                    $reponse->setConformite($conformite);
124                    $reponse->setQuestion($question);
125                    $conformite->addReponse($reponse);
126                }
127                $evaluation->addConformite($conformite);
128            }
129            $evaluation->setCollectivity($organisation);
130        }
131        $evaluation->setDate(new \DateTime());
132
133        $form = $this->createForm($this->getFormType(), $evaluation);
134
135        $form->handleRequest($request);
136        if ($form->isSubmitted() && $form->isValid()) {
137            $em = $this->getDoctrine()->getManager();
138            /** @var SubmitButton $button */
139            $button = $form->get('save');
140            if ($button->isClicked()) {
141                $evaluation->setIsDraft(false);
142            }
143            $em->persist($evaluation);
144            $em->flush();
145
146            $this->addFlash('success', $this->getFlashbagMessage('success', 'create', $evaluation));
147
148            return $this->redirectToRoute($this->getRouteName('list'));
149        }
150
151        return $this->render($this->getTemplatingBasePath('create'), [
152            'form' => $form->createView(),
153        ]);
154    }
155
156    public function showAction(string $id): Response
157    {
158        /** @var Conformite $object */
159        $object = $this->repository->findOneById($id);
160
161        if ($object->getCollectivity()->isHasModuleConformiteOrganisation()) {
162            return parent::showAction($id);
163        }
164
165        return $this->redirectToRoute('registry_conformite_organisation_list');
166    }
167
168    public function deleteAction(string $id): Response
169    {
170        /** @var Conformite $object */
171        $object = $this->repository->findOneById($id);
172
173        if ($object->getCollectivity()->isHasModuleConformiteOrganisation()) {
174            return parent::deleteAction($id);
175        }
176
177        return $this->redirectToRoute('registry_conformite_organisation_list');
178    }
179
180    public function deleteConfirmationAction(string $id): Response
181    {
182        /** @var Conformite $object */
183        $object = $this->repository->findOneById($id);
184
185        if ($object->getCollectivity()->isHasModuleConformiteOrganisation()) {
186            return parent::deleteConfirmationAction($id);
187        }
188
189        return $this->redirectToRoute('registry_conformite_organisation_list');
190    }
191
192    public function listConformitesAction(Request $request): Response
193    {
194        $criteria     = [];
195        $collectivity = $this->userProvider->getAuthenticatedUser()->getCollectivity();
196        if (!$this->authorizationChecker->isGranted('ROLE_ADMIN')) {
197            $criteria['collectivity'] = $collectivity;
198        }
199
200        $category = $this->entityManager->getRepository(Category::class)->findOneBy([
201            'name' => 'Conformité de la structure',
202        ]);
203
204        $form           = null;
205        $evaluations    = null;
206        $isAdminView    = $this->authorizationChecker->isGranted('ROLE_REFERENT');
207        $lastEvaluation = $this->repository->findLastByOrganisation($collectivity);
208
209        if (!$isAdminView && null !== $lastEvaluation) {
210            $evaluations = $this->repository->findAllByActiveOrganisationWithHasModuleConformiteOrganisationAndOrderedByDate($collectivity);
211
212            $form = $this->createForm(EvaluationPiloteType::class, $lastEvaluation);
213            $form->handleRequest($request);
214            if ($form->isSubmitted() && $form->isValid()) {
215                $em = $this->getDoctrine()->getManager();
216                $em->flush();
217
218                $this->addFlash('success', $this->getFlashbagMessage('success', 'pilote', $evaluations[0]));
219
220                return $this->redirectToRoute($this->getRouteName('list'));
221            }
222            $form = $form->createView();
223        }
224
225        return $this->render('Registry/Conformite_organisation/list.html.twig', [
226            'totalItem'   => $this->repository->count($criteria),
227            'evaluations' => $evaluations,
228            'category'    => $category,
229            'route'       => $this->router->generate('registry_conformite_organisation_list_datatables'),
230            'form'        => $form,
231        ]);
232    }
233
234    public function editAction(Request $request, string $id): Response
235    {
236        /** @var Evaluation $evaluation */
237        $evaluation = $this->repository->findOneById($id);
238        if (!$evaluation) {
239            throw new NotFoundHttpException("No object found with ID '{$id}'");
240        }
241
242        if ($evaluation->getCollectivity() && !$evaluation->getCollectivity()->isHasModuleConformiteOrganisation()) {
243            throw new AccessDeniedHttpException('Ce module est désactivé sur la structure');
244        }
245
246        if (!$evaluation->isDraft()) {
247            throw new BadRequestHttpException("Submitted evaluation can't be modified");
248        }
249
250        /** @var User $user */
251        $user = $this->getUser();
252        if (!$user->hasAccessTo($evaluation)) {
253            return $this->redirectToRoute($this->getRouteName('list'));
254        }
255
256        $this->addMissingNewQuestionsAndProcessus($evaluation);
257
258        $form = $this->createForm($this->getFormType(), $evaluation);
259
260        $form->handleRequest($request);
261        if ($form->isSubmitted() && $form->isValid()) {
262            /** @var SubmitButton $button */
263            $button = $form->get('save');
264            if ($button->isClicked()) {
265                $evaluation->setIsDraft(false);
266            }
267            $this->entityManager->flush();
268
269            $this->addFlash('success', $this->getFlashbagMessage('success', 'edit', $evaluation));
270
271            $this->dispatcher->dispatch(new ConformiteOrganisationEvent($evaluation));
272
273            return $this->redirectToRoute($this->getRouteName('list'));
274        }
275
276        return $this->render($this->getTemplatingBasePath('edit'), [
277            'form' => $form->createView(),
278        ]);
279    }
280
281    public function reportAction(Request $request, string $id)
282    {
283        $evaluation = $this->repository->findOneById($id);
284        if (!$evaluation) {
285            throw new NotFoundHttpException("No object found with ID '{$id}'");
286        }
287        if ($evaluation->getCollectivity() && !$evaluation->getCollectivity()->isHasModuleConformiteOrganisation()) {
288            throw new AccessDeniedHttpException('Ce module est désactivé sur la structure');
289        }
290        $withAllActions = $request->query->getBoolean('all_actions', true);
291
292        /** @var User $user */
293        $user = $this->getUser();
294        if (!$user->hasAccessTo($evaluation)) {
295            return $this->redirectToRoute($this->getRouteName('list'));
296        }
297
298        return $this->wordHandler->generateRegistryConformiteOrganisationReport($evaluation, $withAllActions);
299    }
300
301    private function addMissingNewQuestionsAndProcessus(Evaluation $evaluation)
302    {
303        foreach ($this->processusRepository->findNewNotUsedInGivenConformite($evaluation) as $processus) {
304            $conformite = new Conformite();
305            $conformite->setProcessus($processus);
306            $evaluation->addConformite($conformite);
307        }
308
309        foreach ($evaluation->getConformites() as $conformite) {
310            foreach ($this->questionRepository->findNewNotUsedByGivenConformite($conformite) as $question) {
311                $reponse = new Reponse();
312                $reponse->setQuestion($question);
313                $conformite->addReponse($reponse);
314            }
315        }
316    }
317
318    protected function getDomain(): string
319    {
320        return 'registry';
321    }
322
323    protected function getModel(): string
324    {
325        return 'conformite_organisation';
326    }
327
328    protected function getModelClass(): string
329    {
330        return Evaluation::class;
331    }
332
333    protected function getFormType(): string
334    {
335        return EvaluationType::class;
336    }
337
338    public function listDataTables(Request $request): JsonResponse
339    {
340        $criteria = $this->getRequestCriteria();
341
342        $conformites = $this->getResults($request, $criteria);
343        $reponse     = $this->getBaseDataTablesResponse($request, $conformites, $criteria);
344
345        $yes = '<span class="badge bg-dark-gray">' . $this->translator->trans('global.label.yes') . '</span>';
346        $no  = '<span class="badge bg-gray">' . $this->translator->trans('global.label.no') . '</span>';
347
348        /* @var Evaluation $conformite */
349        foreach ($conformites as $evaluation) {
350            if (is_array($evaluation)) {
351                $evaluation = $evaluation[0];
352            }
353            $reponse['data'][] = [
354                'id'           => $evaluation->getId(),
355                'created_at'   => $evaluation ? date_format($evaluation->getCreatedAt(), 'd/m/Y') : null,
356                'collectivite' => $evaluation->getCollectivity()->getName(),
357                'participant'  => $evaluation->getParticipants()->count(),
358                'draft'        => $evaluation->isDraft() ? $yes : $no,
359                'actions'      => $this->generateActionCell($evaluation),
360            ];
361        }
362
363        $jsonResponse = new JsonResponse();
364        $jsonResponse->setJson(\json_encode($reponse));
365
366        return $jsonResponse;
367    }
368
369    protected function getLabelAndKeysArray(): array
370    {
371        if ($this->authorizationChecker->isGranted('ROLE_REFERENT')) {
372            return [
373                0 => 'created_at',
374                1 => 'collectivite',
375                2 => 'participant',
376                3 => 'draft',
377                4 => 'actions',
378            ];
379        }
380
381        return [
382            0 => 'created_at',
383            1 => 'participant',
384            2 => 'draft',
385            3 => 'actions',
386        ];
387    }
388
389    private function generateActionCell(Evaluation $conformity)
390    {
391        $html = '<a href="' .
392            $this->router->generate('registry_conformite_organisation_report', ['id' => $conformity->getId()]) . '">
393            <i aria-hidden="true" class="fa fa-print"></i> ' .
394            $this->translator->trans('global.action.print')
395            . '</a> ';
396
397        if ($conformity->isDraft()) {
398            $html .= '<a href="' .
399                $this->router->generate('registry_conformite_organisation_edit', ['id' => $conformity->getId()]) . '">
400            <i aria-hidden="true" class="fa fa-pencil"></i> ' .
401                $this->translator->trans('global.action.edit')
402                . '</a> ';
403        }
404
405        $html .= '<a href="' .
406        $this->router->generate('registry_conformite_organisation_delete', ['id' => $conformity->getId()]) .
407        '"><i aria-hidden="true" class="fa fa-trash"></i> ' .
408        $this->translator->trans('global.action.delete')
409        . '</a> '
410        ;
411
412        return $html;
413    }
414
415    private function getRequestCriteria()
416    {
417        $criteria = [];
418        $user     = $this->userProvider->getAuthenticatedUser();
419
420        if (!$this->authorizationChecker->isGranted('ROLE_ADMIN')) {
421            $criteria['collectivity'] = $user->getCollectivity();
422        }
423
424        if (\in_array(UserRoleDictionary::ROLE_REFERENT, $user->getRoles())) {
425            $criteria['collectivity'] = $user->getCollectivitesReferees();
426        }
427
428        return $criteria;
429    }
430
431    protected function getBaseDataTablesResponse(Request $request, $results, array $criteria = [])
432    {
433        $draw = $request->request->get('draw');
434
435        $reponse = [
436            'draw'            => $draw,
437            'recordsTotal'    => $this->repository->count(),
438            'recordsFiltered' => $this->repository->count($criteria),
439            'data'            => [],
440        ];
441
442        return $reponse;
443    }
444
445    protected function createQueryBuilder(): QueryBuilder
446    {
447        return $this->getManager()
448            ->createQueryBuilder()
449            ->select('o')
450            ->from($this->getModelClass(), 'o')
451        ;
452    }
453
454    public function count(array $criteria = [])
455    {
456        $qb = $this
457            ->createQueryBuilder()
458            ->select('count(o.id)')
459        ;
460
461        if (isset($criteria['collectivity']) && $criteria['collectivity'] instanceof Collection) {
462            $qb->leftJoin('o.collectivity', 'collectivite');
463            $this->addInClauseCollectivities($qb, $criteria['collectivity']->toArray());
464            unset($criteria['collectivity']);
465        }
466
467        foreach ($criteria as $key => $value) {
468            $this->addWhereClause($qb, $key, $value);
469        }
470
471        return $qb
472            ->getQuery()
473            ->getSingleScalarResult()
474        ;
475    }
476
477    protected function getManager(): EntityManagerInterface
478    {
479        $manager = $this->registry->getManager();
480
481        if (!$manager instanceof EntityManagerInterface) {
482            throw new \Exception('Registry Manager must be an instance of EntityManagerInterface');
483        }
484
485        return $manager;
486    }
487}