Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 363
0.00% covered (danger)
0.00%
0 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
ConformiteTraitementGenerator
0.00% covered (danger)
0.00%
0 / 363
0.00% covered (danger)
0.00%
0 / 9
5402
0.00% covered (danger)
0.00%
0 / 1
 addGlobalOverview
0.00% covered (danger)
0.00%
0 / 183
0.00% covered (danger)
0.00%
0 / 1
1806
 colorCell
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
2
 valueCell
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
2
 addSyntheticView
0.00% covered (danger)
0.00%
0 / 24
0.00% covered (danger)
0.00%
0 / 1
12
 addDetailedView
0.00% covered (danger)
0.00%
0 / 50
0.00% covered (danger)
0.00%
0 / 1
56
 sortTreatmentByConformiteTraitementByLevelAndTreatmentName
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
 sortReponseByQuestionPosition
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 SyntheticAnnexeList
0.00% covered (danger)
0.00%
0 / 78
0.00% covered (danger)
0.00%
0 / 1
182
 BgColorSyntheticTreatment
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3/**
4 * This file is part of the MADIS - RGPD Management application.
5 *
6 * @copyright Copyright (c) 2018-2019 Soluris - Solutions Numériques Territoriales Innovantes
7 *
8 * This program is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU Affero General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Affero General Public License for more details.
17 *
18 * You should have received a copy of the GNU Affero General Public License
19 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 */
21
22declare(strict_types=1);
23
24namespace App\Domain\Reporting\Generator\Word;
25
26use App\Domain\Registry\Calculator\Completion\ConformiteTraitementCompletion;
27use App\Domain\Registry\Dictionary\ConformiteTraitementLevelDictionary;
28use App\Domain\Registry\Model\ConformiteTraitement\Reponse;
29use App\Domain\Registry\Model\Mesurement;
30use App\Domain\Registry\Model\Treatment;
31use PhpOffice\PhpWord\Element\Section;
32use PhpOffice\PhpWord\Element\Table;
33use PhpOffice\PhpWord\Shared\Converter;
34use PhpOffice\PhpWord\SimpleType\TblWidth;
35use PhpOffice\PhpWord\Style\ListItem;
36
37class ConformiteTraitementGenerator extends AbstractGenerator implements ImpressionGeneratorInterface
38{
39    /**
40     * Global overview : Information to display for conformiteTraitement in overview report.
41     */
42    public function addGlobalOverview(Section $section, array $data, $withAIPD = true, $withConform = true): void
43    {
44        if (empty($data)) {
45            return;
46        }
47        $tableStyleConformite = [
48            'borderColor' => '006699',
49            'borderSize'  => 6,
50            'cellMargin'  => 100,
51            'unit'        => TblWidth::PERCENT,
52            'width'       => 100 * 50,
53            'layout'      => \PhpOffice\PhpWord\Style\Table::LAYOUT_FIXED,
54        ];
55
56        if ($withConform) {
57            $section->addTitle('Analyse de la conformité des traitements', 2);
58
59            uasort($data, [$this, 'sortTreatmentByConformiteTraitementByLevelAndTreatmentName']);
60
61            // Table data
62            // Add header
63            $tableData = [
64                [
65                    'Traitement',
66                    'Gestionnaire',
67                    'Date de révision de la conformité',
68                    'Conformité',
69                ],
70            ];
71
72            $chartCategories = [];
73            $chartData       = [];
74            $conformites     = ConformiteTraitementLevelDictionary::getConformites();
75            foreach ($conformites as $key => $label) {
76                $chartCategories[] = $label;
77                $chartData[$key]   = 0;
78            }
79
80            /** @var Treatment $treatment */
81            foreach ($data as $treatment) {
82                $conformiteTraitement = $treatment->getConformiteTraitement();
83                $level                = ConformiteTraitementCompletion::getConformiteTraitementLevel($conformiteTraitement);
84
85                $date = null;
86                if (!\is_null($conformiteTraitement)) {
87                    $date = $conformiteTraitement->getCreatedAt();
88                }
89
90                $tableData[] = [
91                    $treatment->getName(),
92                    $treatment->getManager(),
93                    ConformiteTraitementLevelDictionary::getConformites()[$level],
94                    $this->getDate($date, 'd/m/Y'),
95                ];
96
97                ++$chartData[$level];
98            }
99
100            $section->addText('Les 10 critères suivants correspondent aux principes fondamentaux du RGPD et ont fait l’objet d’une évaluation :');
101            $listStyle = ['listType' => ListItem::TYPE_NUMBER];
102            $section->addListItem('Finalités', 0, null, $listStyle);
103            $section->addListItem('Licéité', 0, null, $listStyle);
104            $section->addListItem('Minimisation des données', 0, null, $listStyle);
105            $section->addListItem('Qualité des données', 0, null, $listStyle);
106            $section->addListItem('Durée de conservation', 0, null, $listStyle);
107            $section->addListItem('Information des personnes concernées', 0, null, $listStyle);
108            $section->addListItem('Recueil de consentement', 0, null, $listStyle);
109            $section->addListItem('Exercice des différents droits', 0, null, $listStyle);
110            $section->addListItem('Sous-traitance', 0, null, $listStyle);
111            $section->addListItem('Transferts en dehors de l’union européenne', 0, null, $listStyle);
112
113            $textrun = $section->addTextRun();
114            $textrun->addText('Une synthèse de l’analyse de la conformité des traitements et à valeur de preuve ');
115            $textrun->addLink('listConformityTreatments', 'figure en annexe', ['underline' => 'single'], [], true);
116            $textrun->addText('.');
117
118            $section->addText('Ci-dessous l’évaluation de la conformité des traitements au ' . date('d/m/Y') . ' :');
119
120            $chart = $section->addChart(
121                'pie',
122                $chartCategories,
123                $chartData,
124                [
125                    'height' => Converter::cmToEmu(11),
126                    'width'  => Converter::cmToEmu(15),
127                ]
128            );
129
130            $chart->getStyle()->setColors(\array_values(ConformiteTraitementLevelDictionary::getHexaConformitesColors()));
131
132            $countTypes          = array_count_values(array_column($tableData, '2'));
133            $conformes           = array_key_exists('Conforme', $countTypes) ? $countTypes['Conforme'] : 0;
134            $nonConformesMineurs = array_key_exists('Non-conformité mineure', $countTypes) ? $countTypes['Non-conformité mineure'] : 0;
135            $nonConformesMajeurs = array_key_exists('Non-conformité majeure', $countTypes) ? $countTypes['Non-conformité majeure'] : 0;
136            $NonEvalues          = array_key_exists('Non évalué', $countTypes) ? $countTypes['Non évalué'] : 0;
137
138            $section->addText('Sur les ' . (count($tableData) - 1) . ' traitements :');
139            $section->addListItem('Conforme(s) : ' . $conformes);
140            $section->addListItem('Non-conformité(s) mineure(s) : ' . $nonConformesMineurs);
141            $section->addListItem('Non-conformité(s) majeure(s) : ' . $nonConformesMajeurs);
142            $section->addListItem('Non évalué(s) : ' . $NonEvalues);
143
144            $tableConformite = $section->addTable($tableStyleConformite);
145            $headersTable    = $tableData[0];
146            $tableConformite->addRow(null, ['tblHeader' => true, 'cantsplit' => true]);
147            foreach ($headersTable as $element) {
148                $cell = $tableConformite->addCell(2500, $this->cellHeadStyle);
149                $cell->addText($element, $this->textHeadStyle);
150            }
151            unset($tableData[0]);
152            foreach ($tableData as $line) {
153                $tableConformite->addRow(null, ['cantsplit' => true]);
154                $cell1 = $tableConformite->addCell(2500);
155                $cell1->addText($line[0]);
156                $cell2 = $tableConformite->addCell(2500);
157                $cell2->addText($line[1]);
158                $cell3 = $tableConformite->addCell(2500);
159                $cell3->addText($line[3]);
160                $styleCellConformite = match ($line[2]) {
161                    'Conforme'               => ['bgColor' => 'bce292'],
162                    'Non-conformité mineure' => ['bgColor' => 'fac9ad'],
163                    'Non-conformité majeure' => ['bgColor' => 'ffa7a7'],
164                    'Non évalué'             => ['bgColor' => 'ffffff'],
165                };
166                $cell4 = $tableConformite->addCell(2500, $styleCellConformite);
167                $cell4->addText($line[2], ['bold' => true]);
168            }
169        }
170
171        if (false === $withAIPD) {
172            return;
173        }
174        // Analyse impact
175        $section->addTitle('Analyse d’impact', 2);
176
177        $cntAipdToDo     = 0;
178        $cntAipdRealised = 0;
179        foreach ($data as $treatment) {
180            $conformite = $treatment->getConformiteTraitement();
181            $aipd       = null;
182            if ($conformite) {
183                $aipd = $conformite->getLastAnalyseImpact();
184            }
185
186            if ($conformite && $conformite->getNeedsAipd()) {
187                ++$cntAipdToDo;
188            }
189
190            if ($aipd) {
191                ++$cntAipdRealised;
192            }
193        }
194
195        $section->addText("Une analyse d’impact sur la protection des données est une étude, qui doit être réalisée si possible en amont du projet, sur des traitements contenant des critères susceptibles d'engendrer un risque élevé pour les droits et libertés des personnes concernées. ");
196        if (0 === $cntAipdRealised) {
197            $section->addText('À ce jour, il n’y a pas eu d’Analyse d’impact réalisées.');
198        }
199
200        $textrun = $section->addTextRun();
201        $textrun->addText('Le tableau des traitements à risque et à valeur de preuve ');
202        $textrun->addLink('AipdRisks', 'figure en annexe.', ['underline' => 'single'], [], true);
203
204        $section->addText('Ci-dessous, la liste des traitements pour lequel une analyse d’impact sur la protection des données est requise. Au vu des critères, il semble que ' . $cntAipdToDo . ' traitements requière(nt) une analyse d’impact.');
205
206        $tableNeedAipd = $section->addTable($tableStyleConformite);
207        $tableNeedAipd->addRow(null, ['tblHeader' => true, 'cantsplit' => true]);
208        foreach (['Nom du traitement', 'Données sensibles', 'Critères de risques'] as $element) {
209            $cell = $tableNeedAipd->addCell(2500, $this->cellHeadStyle);
210            $cell->addText($element, $this->textHeadStyle);
211        }
212
213        $aipdFinished = [];
214        foreach ($data as $treatment) {
215            $cnt_sensible                                                             = 0;
216            $sensibleDatas                                                            = [];
217            $specificTreatments                                                       = [];
218            $treatment->isLargeScaleCollection() ? $specificTreatments[]              = 'Collecte à large échelle' : null;
219            $treatment->isDataCrossing() ? $specificTreatments[]                      = 'Croisement d\'ensemble de données' : null;
220            $treatment->isAutomatedDecisionsWithLegalEffect() ? $specificTreatments[] = 'Décision automatique avec effet juridique' : null;
221            $treatment->isEvaluationOrRating() ? $specificTreatments[]                = 'Évaluation ou notation' : null;
222            $treatment->isAutomaticExclusionService() ? $specificTreatments[]         = 'Exclusion du bénéfice d\'un droit, d’un service ou contrat' : null;
223            $treatment->isVulnerablePeople() ? $specificTreatments[]                  = 'Personnes vulnérables' : null;
224            $treatment->isSystematicMonitoring() ? $specificTreatments[]              = 'Surveillance systématique' : null;
225            $treatment->isInnovativeUse() ? $specificTreatments[]                     = 'Usage innovant' : null;
226
227            foreach ($treatment->getDataCategories() as $category) {
228                if ($category->isSensible()) {
229                    $sensibleDatas[] = $category;
230                }
231            }
232            if ($treatment->getConformiteTraitement() && $treatment->getConformiteTraitement()->getNeedsAipd()) {
233                $tableNeedAipd->addRow(null, ['cantsplit' => true]);
234                $cell = $tableNeedAipd->addCell(2500);
235                $cell->addText($treatment->getName());
236                $cell = $tableNeedAipd->addCell(2500);
237                foreach ($sensibleDatas as $sensibleData) {
238                    $cell->addListItem(htmlspecialchars((string) $sensibleData, ENT_COMPAT, 'UTF-8'), (int) null, [], [], ['spaceAfter' => 0]);
239                }
240                $cell = $tableNeedAipd->addCell(2500);
241                foreach ($specificTreatments as $specificTreatment) {
242                    $cell->addListItem($specificTreatment, (int) null, [], [], ['spaceAfter' => 0]);
243                }
244            }
245
246            if ($treatment->getConformiteTraitement() && $aipd = $treatment->getConformiteTraitement()->getLastAnalyseImpact()) {
247                if ('non_realisee' !== $aipd->getStatut() && 'en_cours' !== $aipd->getStatut()) {
248                    $aipdFinished[] = [
249                        $treatment->getName(),
250                        $treatment->getConformiteTraitement()->getLastAnalyseImpact()->getUpdatedAt(),
251                        $treatment->getConformiteTraitement()->getLastAnalyseImpact()->getAvisReferent(),
252                        $treatment->getConformiteTraitement()->getLastAnalyseImpact()->getAvisDpd(),
253                        $treatment->getConformiteTraitement()->getLastAnalyseImpact()->getAvisRepresentant(),
254                        $treatment->getConformiteTraitement()->getLastAnalyseImpact()->getAvisResponsable(),
255                    ];
256                }
257            }
258        }
259
260        $section->addTextBreak();
261        $text = match (count($aipdFinished)) {
262            0       => "Aucun traitement n'a fait l'objet d'une analyse d'impact.",
263            1       => "1 traitement a fait l'objet d'une analyse d'impact.",
264            default => count($aipdFinished) . ' traitements ont fait l’objet d’une analyse d’impact.',
265        };
266        $section->addText($text);
267
268        $tableAipdExist = $section->addTable($tableStyleConformite);
269        $tableAipdExist->addRow(null, ['tblHeader' => true, 'cantSplit' => true]);
270        foreach (['Traitements', 'Date de réalisation de l’AIPD', 'Avis du référent RGPD', 'Avis du DPD', 'Avis des représentants des personnes concernées', 'Validation du responsable du traitement'] as $element) {
271            $cell = $tableAipdExist->addCell(1000, $this->cellHeadStyle);
272            $cell->addText($element, $this->textHeadStyle);
273        }
274        foreach ($aipdFinished as $line) {
275            $tableAipdExist->addRow(null, ['cantSplit' => true]);
276            $cell = $tableAipdExist->addCell(1000);
277            $cell->addText($line[0]);
278            $cell = $tableAipdExist->addCell(1000);
279            $cell->addText($line[1]->format('d/m/Y'));
280            for ($i = 2; $i <= 5; ++$i) {
281                $cell = $tableAipdExist->addCell(1000, ['bgColor' => $this->colorCell($line[$i]->getReponse())]);
282                $cell->addText($this->valueCell($line[$i]->getReponse()));
283            }
284        }
285    }
286
287    private function colorCell($value)
288    {
289        $return_value = match ($value) {
290            'favorable'         => 'bce292',
291            'defavorable'       => 'ffa7a7',
292            'pas_de_reponse'    => 'ffffff',
293            'favorable_reserve' => 'fac9ad',
294        };
295
296        return $return_value;
297    }
298
299    private function valueCell($value)
300    {
301        $return_value = match ($value) {
302            'favorable'         => 'Favorable',
303            'defavorable'       => 'Défavorable',
304            'pas_de_reponse'    => 'Pas de réponse',
305            'favorable_reserve' => 'Favorable avec réserve',
306        };
307
308        return $return_value;
309    }
310
311    public function addSyntheticView(Section $section, array $data): void
312    {
313        $section->addTitle('Liste des traitements', 1);
314        $section->addBookmark('Traitements en annexe');
315
316        uasort($data, [$this, 'sortTreatmentByConformiteTraitementByLevelAndTreatmentName']);
317
318        // Table data
319        // Add header
320        $tableData = [
321            [
322                'Traitement',
323                'Gestionnaire',
324                'Date de révision de la conformité',
325                'Conformité',
326            ],
327        ];
328
329        /** @var Treatment $treatment */
330        foreach ($data as $treatment) {
331            $conformiteTraitement = $treatment->getConformiteTraitement();
332            $level                = ConformiteTraitementCompletion::getConformiteTraitementLevel($conformiteTraitement);
333
334            $date = null;
335            if (!\is_null($conformiteTraitement)) {
336                $date = $conformiteTraitement->getCreatedAt();
337            }
338
339            $tableData[] = [
340                $treatment->getName(),
341                $treatment->getManager(),
342                $this->getDate($date, 'd/m/Y'),
343                ConformiteTraitementLevelDictionary::getConformites()[$level],
344            ];
345        }
346
347        $this->addTable($section, $tableData, true, self::TABLE_ORIENTATION_HORIZONTAL);
348    }
349
350    public function addDetailedView(Section $section, array $data): void
351    {
352        $section->addTitle('Détail des traitements', 1);
353
354        /** @var Treatment $treatment */
355        foreach ($data as $key => $treatment) {
356            $conformiteTraitement = $treatment->getConformiteTraitement();
357            if (\is_null($conformiteTraitement)) {
358                continue;
359            }
360
361            if (0 != $key) {
362                $section->addPageBreak();
363            }
364
365            $questionsData = [
366                [
367                    'data' => [
368                        'Principes fondamentaux',
369                        ['text' => 'Conformité', 'style' => $this->textHeadStyle],
370                        ['text' => 'Actions de protections', 'style' => $this->textHeadStyle],
371                    ],
372                    'style' => [
373                        'bgColor' => '3c8dbc',
374                        'bold'    => true,
375                        'color'   => 'ffffff',
376                    ],
377                ],
378            ];
379
380            $reponses = \iterable_to_array($conformiteTraitement->getReponses());
381
382            uasort($reponses, [$this, 'sortReponseByQuestionPosition']);
383
384            foreach ($reponses as $reponse) {
385                $actionsProtections = \array_map(function (Mesurement $mesurement) {
386                    return $mesurement->getName();
387                }, \iterable_to_array($reponse->getActionProtections()));
388
389                $questionsData[] = [
390                    $reponse->getQuestion()->getQuestion(),
391                    $reponse->isConforme() ? 'Conforme' : 'Non-conforme',
392                    !empty($actionsProtections) ? join(', ', $actionsProtections) : 'Pas d\'action',
393                ];
394            }
395
396            $section->addTitle($conformiteTraitement->getTraitement()->getName(), 3);
397            $this->addTable($section, $questionsData, true, self::TABLE_ORIENTATION_VERTICAL);
398
399            $historyData = [
400                [
401                    'Date de création',
402                    $this->getDate($conformiteTraitement->getCreatedAt()),
403                ],
404                [
405                    'Date de modification',
406                    $this->getDate($conformiteTraitement->getUpdatedAt()),
407                ],
408                [
409                    'Modifié par',
410                    $conformiteTraitement->getUpdatedBy(),
411                ],
412            ];
413
414            $section->addTitle('Historique', 3);
415            $this->addTable($section, $historyData, false, self::TABLE_ORIENTATION_VERTICAL);
416        }
417    }
418
419    private function sortTreatmentByConformiteTraitementByLevelAndTreatmentName(Treatment $a, Treatment $b)
420    {
421        $weightA = ConformiteTraitementLevelDictionary::getConformitesWeight()[ConformiteTraitementCompletion::getConformiteTraitementLevel($a->getConformiteTraitement())];
422        $weightB = ConformiteTraitementLevelDictionary::getConformitesWeight()[ConformiteTraitementCompletion::getConformiteTraitementLevel($b->getConformiteTraitement())];
423
424        if ($weightA === $weightB) {
425            return strcmp($a->getName(), $b->getName());
426        }
427
428        return ($weightA < $weightB) ? -1 : 1;
429    }
430
431    private function sortReponseByQuestionPosition(Reponse $a, Reponse $b)
432    {
433        $orderA = $a->getQuestion()->getPosition();
434        $orderB = $b->getQuestion()->getPosition();
435
436        return ($orderA < $orderB) ? -1 : 1;
437    }
438
439    public function SyntheticAnnexeList($section, $treatments)
440    {
441        $section->addBookmark('listConformityTreatments');
442        $section->addTitle('Synthèse de la conformité des traitements évalués', 2);
443        $section->addText('Légende :');
444        $section->AddListItem('C = Conforme');
445        $section->AddListItem('NCM = Non conforme mineure');
446        $section->AddListItem('NC = Non conforme majeure');
447
448        $styleCellHeader = ['textDirection' => \PhpOffice\PhpWord\Style\Cell::TEXT_DIR_BTLR, 'bgColor' => '3c8dbc', 'vAlign' => 'center'];
449
450        // Affichage du header du tableau
451        $tableSyntheticAnnexeList = $section->addTable($this->tableStyle);
452        $tableSyntheticAnnexeList->addRow(2000, ['tblHeader' => true, 'cantsplit' => true]);
453        $cell = $tableSyntheticAnnexeList->addCell(1000, ['bgColor' => '3c8dbc', 'vAlign' => 'bottom']);
454        $cell->addText('Traitements', $this->textHeadStyle);
455
456        $ConformiteNames = ['Finalités', 'Licéité du traitement', 'Minimisation des données', 'Qualité des données', 'Durées de conservation', 'Information des personnes', 'Recueil du consentement', "Droit d'Accès", 'Droit de rectification', 'Droit de limitation', 'Droit de portabilité', "Droit d'effacement", "Droit d'opposition", 'Sous-traitance', 'Transferts hors UE'];
457        foreach ($ConformiteNames as $item) {
458            $cell = $tableSyntheticAnnexeList->addCell(300, $styleCellHeader);
459            $cell->addText($item, $this->textHeadStyle);
460        }
461
462        $cell = $tableSyntheticAnnexeList->addCell(100, ['borderTopColor' => 'ffffff', 'borderTopSize' => 2, 'borderBottomColor' => 'ffffff', 'borderBottomSize' => 2]);
463        $cell->addText('');
464        $cell = $tableSyntheticAnnexeList->addCell(300, ['bgColor' => '3c8dbc', 'vAlign' => 'bottom']);
465        $cell->addText('C', $this->textHeadStyle);
466        $cell = $tableSyntheticAnnexeList->addCell(300, ['bgColor' => '3c8dbc', 'vAlign' => 'bottom']);
467        $cell->addText('NCM', $this->textHeadStyle);
468        $cell = $tableSyntheticAnnexeList->addCell(300, ['bgColor' => '3c8dbc', 'vAlign' => 'bottom']);
469        $cell->addText('NC', $this->textHeadStyle);
470
471        // End header
472
473        $listConformityName = [
474            'C'   => [1 => 0, 2 => 0, 3 => 0, 4 => 0, 5 => 0, 6 => 0, 7 => 0, 8 => 0, 9 => 0, 10 => 0, 11 => 0, 12 => 0, 13 => 0, 14 => 0, 15 => 0],
475            'NC'  => [1 => 0, 2 => 0, 3 => 0, 4 => 0, 5 => 0, 6 => 0, 7 => 0, 8 => 0, 9 => 0, 10 => 0, 11 => 0, 12 => 0, 13 => 0, 14 => 0, 15 => 0],
476            'NCM' => [1 => 0, 2 => 0, 3 => 0, 4 => 0, 5 => 0, 6 => 0, 7 => 0, 8 => 0, 9 => 0, 10 => 0, 11 => 0, 12 => 0, 13 => 0, 14 => 0, 15 => 0],
477        ];
478
479        // Affichage des données de chaque conformité de traitement
480        foreach ($treatments as $treatment) {
481            if ($treatment->getConformiteTraitement()) {
482                $ConformityTreatmentValues = [];
483                $sorted                    = $treatment->getConformiteTraitement()->getReponses()->toArray();
484                usort($sorted, function (Reponse $rep1, Reponse $rep2) {
485                    return $rep1->getQuestion()->getPosition() - $rep2->getQuestion()->getPosition();
486                });
487                /** @var Reponse $response */
488                foreach ($sorted as $response) {
489                    // get number of planned actions for this response
490                    $plannedActions = 0;
491                    /** @var Mesurement $actionProtection */
492                    foreach ($response->getActionProtections() as $actionProtection) {
493                        if (!\is_null($actionProtection->getPlanificationDate())) {
494                            ++$plannedActions;
495                        }
496                    }
497                    $NonConformityValue                                                 = $plannedActions > 0 ? 'NCM' : 'NC';
498                    $ConformityTreatmentValues[$response->getQuestion()->getPosition()] = $response->isConforme() ? 'C' : $NonConformityValue;
499                }
500
501                $tableSyntheticAnnexeList->addRow();
502                $cell = $tableSyntheticAnnexeList->addCell(1000);
503                $cell->addText($treatment->getName(), ['size' => 8]);
504
505                $C   = 0;
506                $NC  = 0;
507                $NCM = 0;
508
509                foreach ($ConformityTreatmentValues as $key => $value) {
510                    // For each question add a cell saying if conforme or not
511                    $cell = $tableSyntheticAnnexeList->addCell(300, ['bgColor' => $this->BgColorSyntheticTreatment($value), 'vAlign' => 'center']);
512                    $cell->addText($value, ['size' => 8], ['alignment' => 'center']);
513                    ++$listConformityName[$value][$key];
514
515                    match ($value) {
516                        'C'   => $C++,
517                        'NC'  => $NC++,
518                        'NCM' => $NCM++,
519                    };
520                }
521
522                $cell = $tableSyntheticAnnexeList->addCell(100, ['borderTopColor' => 'ffffff', 'borderTopSize' => 2, 'borderBottomColor' => 'ffffff', 'borderBottomSize' => 2]);
523                $cell->addText('');
524                $cell = $tableSyntheticAnnexeList->addCell(300, ['bgColor' => 'bce292', 'vAlign' => 'center']);
525                $cell->addText($C, ['bold' => true], ['alignment' => 'center']);
526                $cell = $tableSyntheticAnnexeList->addCell(300, ['bgColor' => 'ffff80', 'vAlign' => 'center']);
527                $cell->addText($NCM, ['bold' => true], ['alignment' => 'center']);
528                $cell = $tableSyntheticAnnexeList->addCell(300, ['bgColor' => 'ffa7a7', 'vAlign' => 'center']);
529                $cell->addText($NC, ['bold' => true], ['alignment' => 'center']);
530            }
531        }
532
533        $tableSyntheticAnnexeList->addRow(60, ['exactHeight' => true]);
534        $cell = $tableSyntheticAnnexeList->addCell(1000, ['borderTopColor' => 'ffffff', 'borderTopSize' => 2, 'borderBottomColor' => 'ffffff', 'borderBottomSize' => 2, 'borderLeftColor' => 'ffffff', 'borderLeftSize' => 2, 'borderRightColor' => 'ffffff', 'borderRightSize' => 2]);
535        $cell->addText('');
536
537        foreach ($listConformityName as $key => $datas) {
538            $tableSyntheticAnnexeList->addRow(400, ['exactHeight' => true]);
539            $cell = $tableSyntheticAnnexeList->addCell(1000, ['bgColor' => '3c8dbc']);
540            $cell->addText($key, $this->textHeadStyle, ['alignment' => 'right']);
541            foreach ($datas as $key1 => $item) {
542                $cell = $tableSyntheticAnnexeList->addCell(300, ['bgColor' => $this->BgColorSyntheticTreatment($key)]);
543                $cell->addText($item, ['bold' => true], ['alignment' => 'center']);
544
545                if (15 === $key1) {
546                    $cell = $tableSyntheticAnnexeList->addCell(100, ['borderTopColor' => 'ffffff', 'borderTopSize' => 2, 'borderRightColor' => 'ffffff', 'BorderRightSize' => 2, 'borderBottomColor' => 'ffffff', 'borderBottomSize' => 2]);
547                    $cell->addText('');
548                }
549            }
550        }
551    }
552
553    private function BgColorSyntheticTreatment($value)
554    {
555        $return_value = match ($value) {
556            'C'   => 'bce292',
557            'NC'  => 'ffa7a7',
558            'NCM' => 'ffff80',
559        };
560
561        return $return_value;
562    }
563}