From ee9b299d66e5f1a9822abc12b69ea681b7b0d095 Mon Sep 17 00:00:00 2001
From: Guillaume Heurtier <guillaume.heurtier@maarch.org>
Date: Fri, 28 Aug 2020 14:24:04 +0200
Subject: [PATCH] FIX #14003 TIME 7:30 finish print deposit list action

---
 core/xml/actions_pages.xml                    |  10 ++
 migration/20.10/2010.sql                      |   2 +
 rest/index.php                                |   2 +-
 sql/data_fr.sql                               |   1 +
 .../controllers/ActionMethodController.php    |   1 +
 .../PreProcessActionController.php            |  75 ++++++++++
 .../controllers/RegisteredMailController.php  |  55 +-------
 .../controllers/RegisteredMailTrait.php       | 128 +++++++++++++++---
 .../models/RegisteredMailModel.php            |   3 +-
 src/core/lang/lang-en.php                     |   3 +
 src/core/lang/lang-fr.php                     |   3 +
 src/frontend/app/actions/actions.module.ts    |   4 +
 src/frontend/app/actions/actions.service.ts   |  25 ++++
 .../print-deposit-list-action.component.html  |  40 ++++++
 .../print-deposit-list-action.component.scss  |  51 +++++++
 .../print-deposit-list-action.component.ts    | 118 ++++++++++++++++
 src/lang/lang-en.json                         |   4 +-
 src/lang/lang-fr.json                         |   4 +-
 18 files changed, 453 insertions(+), 76 deletions(-)
 create mode 100644 src/frontend/app/actions/print-deposit-list-action/print-deposit-list-action.component.html
 create mode 100644 src/frontend/app/actions/print-deposit-list-action/print-deposit-list-action.component.scss
 create mode 100644 src/frontend/app/actions/print-deposit-list-action/print-deposit-list-action.component.ts

diff --git a/core/xml/actions_pages.xml b/core/xml/actions_pages.xml
index 00c3e391015..c7b1fc0ffeb 100755
--- a/core/xml/actions_pages.xml
+++ b/core/xml/actions_pages.xml
@@ -456,4 +456,14 @@ An action page is described in a ACTIONPAGE tag :
         <MODULE></MODULE>
         <FLAG_CREATE>false</FLAG_CREATE>
     </ACTIONPAGE>
+    <ACTIONPAGE>
+        <ID>printDepositList</ID>
+        <LABEL>_PRINT_DEPOSIT_LIST</LABEL>
+        <NAME>printDepositList</NAME>
+        <DESC>_PRINT_DEPOSIT_LIST</DESC>
+        <component>printDepositListAction</component>
+        <ORIGIN>apps</ORIGIN>
+        <MODULE></MODULE>
+        <FLAG_CREATE>false</FLAG_CREATE>
+    </ACTIONPAGE>
 </ROOT>
diff --git a/migration/20.10/2010.sql b/migration/20.10/2010.sql
index 48f72fe356b..313b4e4dffe 100755
--- a/migration/20.10/2010.sql
+++ b/migration/20.10/2010.sql
@@ -246,6 +246,8 @@ CREATE TABLE IF NOT EXISTS registered_mail_resources (
     CONSTRAINT registered_mail_resources_unique_key UNIQUE (res_id)
 );
 
+INSERT INTO parameters (id, param_value_int) VALUES ('last_deposit_id', 0);
+
 /* RE CREATE VIEWS */
 CREATE OR REPLACE VIEW res_view_letterbox AS
 SELECT r.res_id,
diff --git a/rest/index.php b/rest/index.php
index 70a3f4b3bfd..170abe56f8e 100755
--- a/rest/index.php
+++ b/rest/index.php
@@ -450,6 +450,7 @@ $app->post('/resourcesList/users/{userId}/groups/{groupId}/baskets/{basketId}/ac
 $app->post('/resourcesList/users/{userId}/groups/{groupId}/baskets/{basketId}/actions/{actionId}/checkCloseWithFieldsAction', \Action\controllers\PreProcessActionController::class . ':checkCloseWithFieldsAction');
 $app->post('/resourcesList/users/{userId}/groups/{groupId}/baskets/{basketId}/actions/{actionId}/checkReconcile', \Action\controllers\PreProcessActionController::class . ':checkReconcile');
 $app->post('/resourcesList/users/{userId}/groups/{groupId}/baskets/{basketId}/actions/{actionId}/checkSendAlfresco', \Action\controllers\PreProcessActionController::class . ':checkSendAlfresco');
+$app->post('/resourcesList/users/{userId}/groups/{groupId}/baskets/{basketId}/actions/{actionId}/checkPrintDepositList', \Action\controllers\PreProcessActionController::class . ':checkPrintDepositList');
 
 //Search
 $app->get('/search', \Search\controllers\SearchController::class . ':get');
@@ -634,7 +635,6 @@ $app->get('/registeredMail/countries', \RegisteredMail\controllers\RegisteredMai
 $app->put('/registeredMails/{resId}', \RegisteredMail\controllers\RegisteredMailController::class . ':update');
 //TODO remove
 $app->get('/registeredMail/print', \RegisteredMail\controllers\RegisteredMailController::class . ':printTest');
-$app->get('/registeredMail/printDeposit', \RegisteredMail\controllers\RegisteredMailController::class . ':printDepositSlipTest');
 
 
 $app->run();
diff --git a/sql/data_fr.sql b/sql/data_fr.sql
index d772812bfba..9fe59bc22fa 100755
--- a/sql/data_fr.sql
+++ b/sql/data_fr.sql
@@ -1010,6 +1010,7 @@ INSERT INTO parameters (id, description, param_value_string) VALUES ('thumbnails
 INSERT INTO parameters (id, description, param_value_int) VALUES ('keepDestForRedirection', 'Si activé (1), met le destinataire en copie de la liste de diffusion lors d''une action de redirection', 0);
 INSERT INTO parameters (id, description, param_value_int) VALUES ('QrCodePrefix', 'Si activé (1), ajoute "Maarch_" dans le contenu des QrCode générés. (Utilisable avec MaarchCapture >= 1.4)', 0);
 INSERT INTO parameters (id, description, param_value_int) VALUES ('workingDays', 'Si activé (1), les délais de traitement sont calculés en jours ouvrés (Lundi à Vendredi). Sinon, en jours calendaire', 1);
+INSERT INTO parameters (id, param_value_int) VALUES ('last_deposit_id', 0);
 
 ------------
 --DIFFLIST_TYPES
diff --git a/src/app/action/controllers/ActionMethodController.php b/src/app/action/controllers/ActionMethodController.php
index 4777a37281a..012be071458 100644
--- a/src/app/action/controllers/ActionMethodController.php
+++ b/src/app/action/controllers/ActionMethodController.php
@@ -85,6 +85,7 @@ class ActionMethodController
         'saveAndPrintRegisteredMailAction'          => 'saveAndPrintRegisteredMail',
         'saveAndIndexRegisteredMailAction'          => 'saveRegisteredMail',
         'printRegisteredMailAction'                 => 'printRegisteredMail',
+        'printDepositListAction'                    => 'printDepositList',
         'noConfirmAction'                           => null
     ];
 
diff --git a/src/app/action/controllers/PreProcessActionController.php b/src/app/action/controllers/PreProcessActionController.php
index 482e7815e41..d51b24ae741 100755
--- a/src/app/action/controllers/PreProcessActionController.php
+++ b/src/app/action/controllers/PreProcessActionController.php
@@ -33,6 +33,7 @@ use Group\models\GroupModel;
 use IndexingModel\models\IndexingModelFieldModel;
 use Note\models\NoteModel;
 use Parameter\models\ParameterModel;
+use RegisteredMail\models\RegisteredMailModel;
 use Resource\controllers\ResController;
 use Resource\controllers\ResourceListController;
 use Resource\controllers\StoreController;
@@ -1573,6 +1574,80 @@ class PreProcessActionController
         return $response->withJson(['resourcesInformations' => $resourcesInformations]);
     }
 
+    public function checkPrintDepositList(Request $request, Response $response, array $args)
+    {
+        $body = $request->getParsedBody();
+
+        if (!Validator::arrayType()->notEmpty()->validate($body['resources'])) {
+            return $response->withStatus(400)->withJson(['errors' => 'Body resources is empty or not an array']);
+        }
+
+        $errors = ResourceListController::listControl(['groupId' => $args['groupId'], 'userId' => $args['userId'], 'basketId' => $args['basketId'], 'currentUserId' => $GLOBALS['id']]);
+        if (!empty($errors['errors'])) {
+            return $response->withStatus($errors['code'])->withJson(['errors' => $errors['errors']]);
+        }
+
+        $body['resources'] = array_slice($body['resources'], 0, 500);
+        if (!ResController::hasRightByResId(['resId' => $body['resources'], 'userId' => $GLOBALS['id']])) {
+            return $response->withStatus(403)->withJson(['errors' => 'Document out of perimeter']);
+        }
+        $body['resources'] = PreProcessActionController::getNonLockedResources(['resources' => $body['resources'], 'userId' => $GLOBALS['id']]);
+
+        $processedResources = [];
+        $data = [
+            '2D' => 0,
+            '2C' => 0,
+            'RW' => 0
+        ];
+        $cannotGenerate = [];
+        $canGenerate = [];
+
+        foreach ($body['resources'] as $resource) {
+            if (in_array($resource, $processedResources)) {
+                continue;
+            }
+
+            $registeredMail = RegisteredMailModel::getWithResources([
+                'select' => ['issuing_site', 'type', 'number', 'warranty', 'recipient', 'generated', 'departure_date', 'deposit_id'],
+                'where'  => ['res_letterbox.res_id = ?'],
+                'data'   => [$resource]
+            ]);
+            if (empty($registeredMail[0])) {
+                $cannotGenerate[] = $resource . ' - ' . _NOT_REGISTERED_MAIL;
+                continue;
+            }
+            $registeredMail = $registeredMail[0];
+
+            if (!$registeredMail['generated']) {
+                $cannotGenerate[] = $resource . ' - ' . _NOT_GENERATED;
+                continue;
+            }
+
+        if (empty($registeredMail['deposit_id'])) {
+            $registeredMails = RegisteredMailModel::getWithResources([
+                'select' => ['number', 'warranty', 'reference', 'recipient', 'res_letterbox.res_id'],
+                'where'  => ['type = ?', 'issuing_site = ?', 'departure_date = ?', 'generated = ?'],
+                'data'   => [$registeredMail['type'], $registeredMail['issuing_site'], $registeredMail['departure_date'], true]
+            ]);
+        } else {
+            $registeredMails = RegisteredMailModel::getWithResources([
+                'select' => ['number', 'warranty', 'reference', 'recipient', 'res_letterbox.res_id'],
+                'where'  => ['deposit_id = ?'],
+                'data'   => [$registeredMail['deposit_id']]
+            ]);
+        }
+
+            $resIds = array_column($registeredMails, 'res_id');
+
+            $processedResources = array_merge($processedResources, $resIds);
+
+            $data[$registeredMail['type']] = count($registeredMails);
+            $canGenerate[] = $resource;
+        }
+
+        return $response->withJson(['types' => $data, 'cannotGenerate' => $cannotGenerate, 'canGenerate' => $canGenerate]);
+    }
+
     private static function getNonLockedResources(array $args)
     {
         ValidatorModel::notEmpty($args, ['resources', 'userId']);
diff --git a/src/app/registeredMail/controllers/RegisteredMailController.php b/src/app/registeredMail/controllers/RegisteredMailController.php
index 2495d3ca8c7..e27dc6a8a9d 100644
--- a/src/app/registeredMail/controllers/RegisteredMailController.php
+++ b/src/app/registeredMail/controllers/RegisteredMailController.php
@@ -167,47 +167,6 @@ class RegisteredMailController
         return $response->withJson(['test' => 2]);
     }
 
-    public function printDepositSlipTest(Request $request, Response $response)
-    {
-        $args = [
-            'site' => [
-                'label'           => 'Dunder Mifflin Scranton',
-                'accountNumber'   => 42,
-                'addressNumber'   => '1725',
-                'addressStreet'   => 'Slough Avenue',
-                'addressPostcode' => '18505',
-                'addressTown'     => 'Scranton',
-                'postOfficeLabel' => 'Scranton Post Office'
-            ],
-            'type' => '2D',
-            'trackingNumber' => '1234567890',
-            'departureDate' => '26/08/2010',
-            'registeredMails' => [
-                [
-                    'type'      => '2D',
-                    'number'    => '551',
-                    'warranty'  => 'R2',
-                    'letter'    => true,
-                    'reference' => '15/08/2020 - ma ref',
-                    'recipient' => [
-                        'AFNOR',
-                        'PSG',
-                        'Eric Choupo',
-                        'Porte 160',
-                        '5 Rue de Paris',
-                        'Batiment C',
-                        '75001 Paris',
-                        'FRANCE'
-                    ]
-                ]
-            ]
-        ];
-
-        $result = RegisteredMailController::getDepositSlipPdf($args);
-
-        return $response->withJson($result);
-    }
-
     public static function getRegisteredMailPDF(array $args)
     {
         $registeredMailNumber = RegisteredMailController::getRegisteredMailNumber(['type' => $args['type'], 'rawNumber' => $args['number']]);
@@ -582,7 +541,7 @@ class RegisteredMailController
         return ['fileContent' => $fileContent];
     }
 
-    public static function getDepositSlipPdf(array $args)
+    public static function getDepositListPdf(array $args)
     {
         $pdf = new Fpdi();
         $pdf->setPrintHeader(false);
@@ -677,12 +636,7 @@ class RegisteredMailController
             $pdf->Cell(30, 10, $registeredMailNumber, 1);
             $pdf->Cell(10, 10, $registeredMail['warranty'], 1);
             $pdf->Cell(15, 10, "", 1);
-            if (strlen($registeredMail['reference']) > 19) {
-                $pdf->Cell(30, 10, "", 1);
-            } else {
-//                    $pdf->Cell(30, 10, mb_strimwidth($registeredMail['reference'], 0, 10, ""), 1); // TODO strim width ???
-                $pdf->Cell(30, 10, $registeredMail['reference'], 1);
-            }
+            $pdf->Cell(30, 10, mb_strimwidth($registeredMail['reference'], 0, 25, ""), 1);
 
             $pdf->setFont('times', '', 6);
             if (strlen($registeredMail['recipient'][1] . " " . $registeredMail['recipient'][4] . " " . $registeredMail['recipient'][6]) > 60) {
@@ -697,7 +651,7 @@ class RegisteredMailController
 
             $pdf->Ln();
             //contrôle du nb de reco présent sur la page. Si 16 lignes, changement de page et affichage du footer
-            if ($position % 16 >= 15) {
+            if ($position % 12 >= 11) {
                 $pdf->SetXY(10, 276);
                 $pdf->setFont('times', 'I', 8);
                 $pdf->Cell(0, 0, "*Niveau de garantie (R1 pour tous ou R2, R3");
@@ -709,7 +663,6 @@ class RegisteredMailController
             }
         }
 
-        $position = 0;
         //contrôle du nb de reco présent sur la page. Si trop, saut de page pour la partie réservé à la poste
         if ($position % 10 >= 9) {
             $pdf->SetXY(10, 276);
@@ -749,7 +702,7 @@ class RegisteredMailController
         $pdf->Cell(0, 0, $page . '/' . $nb);
 
         $fileContent = $pdf->Output('', 'S');
-        return ['encodedFileContent' => base64_encode($fileContent)];
+        return ['fileContent' => $fileContent];
     }
 
     public static function getFormattedRegisteredMail(array $args)
diff --git a/src/app/registeredMail/controllers/RegisteredMailTrait.php b/src/app/registeredMail/controllers/RegisteredMailTrait.php
index 43073d8ceca..9aa1f187106 100644
--- a/src/app/registeredMail/controllers/RegisteredMailTrait.php
+++ b/src/app/registeredMail/controllers/RegisteredMailTrait.php
@@ -13,6 +13,7 @@
 namespace RegisteredMail\controllers;
 
 use Contact\controllers\ContactController;
+use Parameter\models\ParameterModel;
 use RegisteredMail\models\IssuingSiteModel;
 use RegisteredMail\models\RegisteredMailModel;
 use RegisteredMail\models\RegisteredNumberRangeModel;
@@ -251,37 +252,51 @@ trait RegisteredMailTrait
         return ['data' => $data];
     }
 
-    public static function printDepositSlip(array $args)
+    public static function printDepositList(array $args)
     {
         ValidatorModel::notEmpty($args, ['resId']);
         ValidatorModel::intVal($args, ['resId']);
 
         static $processedResources;
-        static $data;
+        static $filesByType;
+        static $currentDepositId;
+        static $registeredMailsIdsByType;
+        static $processedTypes;
 
-        if ($data === null) {
-            $data = [
+        if ($filesByType === null) {
+            $filesByType = [
                 '2D' => null,
                 '2C' => null,
                 'RW' => null
             ];
         }
+        if ($registeredMailsIdsByType === null) {
+            $registeredMailsIdsByType = [
+                '2D' => [],
+                '2C' => [],
+                'RW' => []
+            ];
+        }
         if ($processedResources === null) {
             $processedResources = [];
         }
+        if ($processedTypes === null) {
+            $processedTypes = [];
+        }
 
         if (in_array($args['resId'], $processedResources)) {
             return [];
         }
 
         $registeredMail = RegisteredMailModel::getWithResources([
-            'select' => ['issuing_site', 'type', 'number', 'warranty', 'recipient', 'generated', 'departure_date'],
+            'select' => ['issuing_site', 'type', 'number', 'warranty', 'recipient', 'generated', 'departure_date', 'deposit_id'],
             'where'  => ['res_letterbox.res_id = ?'],
             'data'   => [$args['resId']]
         ]);
-        if (empty($registeredMail)) {
+        if (empty($registeredMail[0])) {
             return ['errors' => ['No registered mail for this resource']];
         }
+        $registeredMail = $registeredMail[0];
 
         if (!$registeredMail['generated']) {
             return ['errors' => ['Registered mail not generated for this resource']];
@@ -290,17 +305,37 @@ trait RegisteredMailTrait
         $site = IssuingSiteModel::getById(['id' => $registeredMail['issuing_site']]);
 
         $range = RegisteredNumberRangeModel::get([
-            'where' => ['site_id = ?', 'type = ?'],
-            'data'  => [$registeredMail['issuing_site'], $registeredMail['type']]
-        ]);
-
-        $registeredMails = RegisteredMailModel::getWithResources([
-            'select' => ['number', 'warranty', 'reference', 'recipient', 'res_letterbox.res_id'],
-            'where'  => ['type = ?', 'issuing_site = ?', 'departure_date = ?', 'generated = ?'],
-            'data'   => [$registeredMail['type'], $registeredMail['issuing_site'], $registeredMail['departure_date'], true]
+            'where' => ['site_id = ?', 'type = ?', 'range_start <= ?', 'range_end >= ?'],
+            'data'  => [$registeredMail['issuing_site'], $registeredMail['type'], $registeredMail['number'], $registeredMail['number']]
         ]);
+        if (empty($range[0])) {
+            return ['errors' => ['No range found']];
+        }
+        $range = $range[0];
+
+        if (empty($registeredMail['deposit_id'])) {
+            $registeredMails = RegisteredMailModel::getWithResources([
+                'select'  => ['number', 'warranty', 'reference', 'recipient', 'res_letterbox.res_id'],
+                'where'   => ['type = ?', 'issuing_site = ?', 'departure_date = ?', 'generated = ?'],
+                'data'    => [$registeredMail['type'], $registeredMail['issuing_site'], $registeredMail['departure_date'], true],
+                'orderBy' => ['number']
+            ]);
+
+            if (empty($currentDepositId) || !in_array($registeredMail['type'], $processedTypes)) {
+                $lastDepositId = ParameterModel::getById(['id' => 'last_deposit_id', 'select' => ['param_value_int']]);
+                $currentDepositId = $lastDepositId['param_value_int'] + 1;
+                ParameterModel::update(['id' => 'last_deposit_id', 'param_value_int' => $currentDepositId]);
+            }
+        } else {
+            $registeredMails = RegisteredMailModel::getWithResources([
+                'select'  => ['number', 'warranty', 'reference', 'recipient', 'res_letterbox.res_id'],
+                'where'   => ['deposit_id = ?'],
+                'data'    => [$registeredMail['deposit_id']],
+                'orderBy' => ['number']
+            ]);
+        }
 
-        $args = [
+        $resultPDF = RegisteredMailController::getDepositListPdf([
             'site'            => [
                 'label'              => $site['label'],
                 'postOfficeLabel'    => $site['post_office_label'],
@@ -317,16 +352,67 @@ trait RegisteredMailTrait
             'trackingNumber'  => $range['tracking_account_number'],
             'departureDate'   => $registeredMail['departure_date'],
             'registeredMails' => $registeredMails
-        ];
-
-        $resultPDF = RegisteredMailController::getDepositSlipPdf($args);
+        ]);
 
         $resIds = array_column($registeredMails, 'res_id');
-
         $processedResources = array_merge($processedResources, $resIds);
+        $registeredMailsIdsByType[$registeredMail['type']] = $resIds;
+
+        $filesByType[$registeredMail['type']] = base64_encode($resultPDF['fileContent']);
+
+        if (!empty($currentDepositId)) {
+            foreach ($registeredMailsIdsByType as $type => $ids) {
+                if (!empty($ids) && !in_array($type, $processedTypes)) {
+                    RegisteredMailModel::update([
+                        'set'   => ['deposit_id' => $currentDepositId],
+                        'where' => ['res_id in (?)'],
+                        'data'  => [$ids]
+                    ]);
+                }
+            }
+        }
+        $processedTypes[] = $registeredMail['type'];
 
-        $data[$registeredMail['type']] = $resultPDF['encodedFileContent'];
+        $finalFile = null;
+        foreach ($filesByType as $type => $file) {
+            if (empty($file)) {
+                continue;
+            }
+            if (empty($finalFile)) {
+                $finalFile = $file;
+                continue;
+            }
 
-        return ['data' => $data];
+            $concatPdf = new Fpdi('P', 'pt');
+            $concatPdf->setPrintHeader(false);
+            $concatPdf->setPrintFooter(false);
+            $tmpPath = CoreConfigModel::getTmpPath();
+
+            $firstFile = $tmpPath . 'depositList_first_file' . rand() . '.pdf';
+            file_put_contents($firstFile, base64_decode($finalFile));
+            $pageCount = $concatPdf->setSourceFile($firstFile);
+            for ($pageNo = 1; $pageNo <= $pageCount; $pageNo++) {
+                $pageId = $concatPdf->ImportPage($pageNo);
+                $s = $concatPdf->getTemplatesize($pageId);
+                $concatPdf->AddPage($s['orientation'], $s);
+                $concatPdf->useImportedPage($pageId);
+            }
+
+            $secondFile = $tmpPath . 'depositList_second_file' . rand() . '.pdf';
+            file_put_contents($secondFile, base64_decode($file));
+            $concatPdf->setSourceFile($secondFile);
+            $pageId = $concatPdf->ImportPage(1);
+            $s = $concatPdf->getTemplatesize($pageId);
+            $concatPdf->AddPage($s['orientation'], $s);
+            $concatPdf->useImportedPage($pageId);
+
+            $fileContent = $concatPdf->Output('', 'S');
+
+            $finalFile = base64_encode($fileContent);
+            unlink($firstFile);
+            unlink($secondFile);
+        }
+
+        return ['data' => ['encodedFile' => $finalFile]];
     }
 }
diff --git a/src/app/registeredMail/models/RegisteredMailModel.php b/src/app/registeredMail/models/RegisteredMailModel.php
index a023be78c4e..e40306e3dc2 100644
--- a/src/app/registeredMail/models/RegisteredMailModel.php
+++ b/src/app/registeredMail/models/RegisteredMailModel.php
@@ -119,7 +119,8 @@ class RegisteredMailModel
             'left_join' => ['registered_mail_resources.res_id = res_letterbox.res_id'],
             'where'     => empty($args['where']) ? [] : $args['where'],
             'data'      => empty($args['data']) ? [] : $args['data'],
-            'groupBy'   => empty($args['groupBy']) ? [] : $args['groupBy']
+            'groupBy'   => empty($args['groupBy']) ? [] : $args['groupBy'],
+            'order_by'  => empty($args['orderBy']) ? [] : $args['orderBy'],
         ]);
     }
 }
diff --git a/src/core/lang/lang-en.php b/src/core/lang/lang-en.php
index 4b807833d23..621a9afc10c 100755
--- a/src/core/lang/lang-en.php
+++ b/src/core/lang/lang-en.php
@@ -476,3 +476,6 @@ define('_SAVE_REGISTERED_MAIL', 'Save registered mail');
 define('_SAVE_PRINT_REGISTERED_MAIL', 'Save and print registered mail');
 define('_SAVE_INDEX_REGISTERED_MAIL', 'Save registered mail and start indexing');
 define('_PRINT_REGISTERED_MAIL', 'Print registered mail');
+define('_PRINT_DEPOSIT_LIST', "Print deposit list");
+define('_NOT_REGISTERED_MAIL', 'Not a registered mail');
+define('_NOT_GENERATED', 'Not generated');
diff --git a/src/core/lang/lang-fr.php b/src/core/lang/lang-fr.php
index bb3efab9f7e..8e2b43831aa 100755
--- a/src/core/lang/lang-fr.php
+++ b/src/core/lang/lang-fr.php
@@ -476,3 +476,6 @@ define('_SAVE_REGISTERED_MAIL', 'Enregistrer le recommandé');
 define('_SAVE_PRINT_REGISTERED_MAIL', 'Enregistrer et imprimer le recommandé');
 define('_SAVE_INDEX_REGISTERED_MAIL', 'Enregistrer le recommandé et lancer l\'indexation');
 define('_PRINT_REGISTERED_MAIL', 'Imprimer le recommandé');
+define('_PRINT_DEPOSIT_SLIP', "Imprimer le bordereau d'envoi");
+define('_NOT_REGISTERED_MAIL', "N'est pas un recommandé");
+define('_NOT_GENERATED', 'Non généré');
diff --git a/src/frontend/app/actions/actions.module.ts b/src/frontend/app/actions/actions.module.ts
index 72537a09b3d..ed46df26a35 100644
--- a/src/frontend/app/actions/actions.module.ts
+++ b/src/frontend/app/actions/actions.module.ts
@@ -46,6 +46,7 @@ import { PrintRegisteredMailActionComponent } from './print-registered-mail-acti
 
 import { InternationalizationModule } from '../../service/translate/internationalization.module';
 import { TranslateService } from '@ngx-translate/core';
+import { PrintDepositListActionComponent } from './print-deposit-list-action/print-deposit-list-action.component';
 
 @NgModule({
     imports: [
@@ -92,6 +93,7 @@ import { TranslateService } from '@ngx-translate/core';
         SaveAndPrintRegisteredMailActionComponent,
         SaveAndIndexRegisteredMailActionComponent,
         PrintRegisteredMailActionComponent,
+        PrintDepositListActionComponent,
         ReconcileActionComponent,
     ],
     exports: [
@@ -133,6 +135,7 @@ import { TranslateService } from '@ngx-translate/core';
         SaveAndPrintRegisteredMailActionComponent,
         SaveAndIndexRegisteredMailActionComponent,
         PrintRegisteredMailActionComponent,
+        PrintDepositListActionComponent,
         ReconcileActionComponent,
         DocumentViewerModule
     ],
@@ -167,6 +170,7 @@ import { TranslateService } from '@ngx-translate/core';
         SaveAndPrintRegisteredMailActionComponent,
         SaveAndIndexRegisteredMailActionComponent,
         PrintRegisteredMailActionComponent,
+        PrintDepositListActionComponent,
         ViewDocActionComponent,
         ReconcileActionComponent,
     ]
diff --git a/src/frontend/app/actions/actions.service.ts b/src/frontend/app/actions/actions.service.ts
index 5614dda308f..81ad04d86c7 100644
--- a/src/frontend/app/actions/actions.service.ts
+++ b/src/frontend/app/actions/actions.service.ts
@@ -40,6 +40,7 @@ import { SaveRegisteredMailActionComponent } from './save-registered-mail-action
 import { SaveAndPrintRegisteredMailActionComponent } from './save-and-print-registered-mail-action/save-and-print-registered-mail-action.component';
 import { SaveAndIndexRegisteredMailActionComponent } from './save-and-index-registered-mail-action/save-and-index-registered-mail-action.component';
 import { PrintRegisteredMailActionComponent } from './print-registered-mail-action/print-registered-mail-action.component';
+import {PrintDepositListActionComponent} from './print-deposit-list-action/print-deposit-list-action.component';
 
 @Injectable()
 export class ActionsService implements OnDestroy {
@@ -1067,4 +1068,28 @@ export class ActionsService implements OnDestroy {
             })
         ).subscribe();
     }
+
+    printDepositListAction(options: any = null) {
+        const dialogRef = this.dialog.open(PrintDepositListActionComponent, {
+            panelClass: 'maarch-modal',
+            disableClose: true,
+            width: '500px',
+            data: this.setDatasActionToSend()
+        });
+
+        dialogRef.afterClosed().pipe(
+            tap((resIds: any) => {
+                this.unlockResourceAfterActionModal(resIds);
+            }),
+            filter((resIds: any) => !this.functions.empty(resIds)),
+            tap((resIds: any) => {
+                this.endAction(resIds);
+            }),
+            finalize(() => this.loading = false),
+            catchError((err: any) => {
+                this.notify.handleErrors(err);
+                return of(false);
+            })
+        ).subscribe();
+    }
 }
diff --git a/src/frontend/app/actions/print-deposit-list-action/print-deposit-list-action.component.html b/src/frontend/app/actions/print-deposit-list-action/print-deposit-list-action.component.html
new file mode 100644
index 00000000000..9957f5f7623
--- /dev/null
+++ b/src/frontend/app/actions/print-deposit-list-action/print-deposit-list-action.component.html
@@ -0,0 +1,40 @@
+<div class="mat-dialog-content-container">
+    <h1 mat-dialog-title>{{data.action.label}}</h1>
+    <div mat-dialog-content *ngIf="!loadingInit">
+        <div *ngIf="loading" class="loading" style="display:flex;height:100%;">
+            <mat-spinner style="margin:auto;"></mat-spinner>
+        </div>
+        <div class="row">
+            <div>
+                <div class="alert-message alert-message-info" role="alert">
+                    <p>
+                        {{'lang.registeredMailsIncluded' | translate}}
+                    </p>
+                    <p>
+                        <b>{{'lang.registeredMail_2C' | translate}}</b> : {{types['2C']}}<br>
+                        <b>{{'lang.registeredMail_2D' | translate}}</b> : {{types['2D']}}<br>
+                        <b>{{'lang.registeredMail_RW' | translate}}</b> : {{types['RW']}}<br>
+                    </p>
+                </div>
+                <div *ngIf="cannotGenerate.length !== 0" class="alert-message alert-message-danger" role="alert">
+                    {{'lang.cannotGenerateDepositListForMails' | translate}}
+                    <ul>
+                        <li *ngFor="let identifier of cannotGenerate">
+                            {{identifier}}
+                        </li>
+                    </ul>
+                </div>
+            </div>
+            <div class="col-md-12" *ngIf="canGenerate.length !== 0">
+                <app-note-editor #noteEditor [resIds]="data.resIds"></app-note-editor>
+            </div>
+        </div>
+    </div>
+    <span class="divider-modal"></span>
+    <div mat-dialog-actions class="actions">
+        <button mat-raised-button mat-button color="primary"
+            [disabled]="loading || (canGenerate.length === 0)"
+            (click)="onSubmit()">{{'lang.validate' | translate}}</button>
+        <button mat-raised-button mat-button [disabled]="loading" [mat-dialog-close]="">{{'lang.cancel' | translate}}</button>
+    </div>
+</div>
diff --git a/src/frontend/app/actions/print-deposit-list-action/print-deposit-list-action.component.scss b/src/frontend/app/actions/print-deposit-list-action/print-deposit-list-action.component.scss
new file mode 100644
index 00000000000..93a7f67d614
--- /dev/null
+++ b/src/frontend/app/actions/print-deposit-list-action/print-deposit-list-action.component.scss
@@ -0,0 +1,51 @@
+@import '../../../css/vars.scss';
+
+.highlight {
+    font-size: 110%;
+}
+
+.loading {
+    display: flex;
+    position: absolute;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    background: #ffffffb3;
+    z-index: 1;
+    overflow: hidden;
+}
+
+.acknowledgementList {
+    ul {
+        font-size: 12px;
+        max-height: 100px;
+        overflow: auto;
+        padding-left: 25px;
+        padding-right: 5px;
+        padding-bottom: 10px;
+        margin-top: 10px;
+    }
+
+    p {
+        //font-size: 18px;
+        margin: 0;
+        text-decoration: underline;
+    }
+
+    b {
+        font-size: 120%;
+    }
+}
+
+.models {
+    display: grid;
+    grid-template-columns: 1fr 1fr;
+    grid-gap: 20px;
+    padding-top: 10px;
+    margin-bottom: 10px;
+}
+
+.attachLabel {
+    color: $primary;
+}
diff --git a/src/frontend/app/actions/print-deposit-list-action/print-deposit-list-action.component.ts b/src/frontend/app/actions/print-deposit-list-action/print-deposit-list-action.component.ts
new file mode 100644
index 00000000000..7253ec75e26
--- /dev/null
+++ b/src/frontend/app/actions/print-deposit-list-action/print-deposit-list-action.component.ts
@@ -0,0 +1,118 @@
+import { Component, OnInit, Inject, ViewChild } from '@angular/core';
+import { TranslateService } from '@ngx-translate/core';
+import { NotificationService } from '../../../service/notification/notification.service';
+import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
+import { HttpClient } from '@angular/common/http';
+import { NoteEditorComponent } from '../../notes/note-editor.component';
+import { tap, finalize, catchError } from 'rxjs/operators';
+import { of } from 'rxjs/internal/observable/of';
+import {FunctionsService} from '../../../service/functions.service';
+
+@Component({
+    templateUrl: 'print-deposit-list-action.component.html',
+    styleUrls: ['print-deposit-list-action.component.scss'],
+})
+export class PrintDepositListActionComponent implements OnInit {
+
+    loading: boolean = false;
+    loadingInit: boolean = false;
+
+    types: any = [];
+    canGenerate: any = [];
+    cannotGenerate: any = [];
+
+    @ViewChild('noteEditor', { static: false }) noteEditor: NoteEditorComponent;
+    loadingExport: boolean;
+
+    constructor(
+        public translate: TranslateService,
+        public http: HttpClient,
+        private notify: NotificationService,
+        public dialogRef: MatDialogRef<PrintDepositListActionComponent>,
+        private functions: FunctionsService,
+        @Inject(MAT_DIALOG_DATA) public data: any) { }
+
+    ngOnInit(): void {
+        this.loadingInit = true;
+
+        this.checkPrintDepositList();
+    }
+
+    checkPrintDepositList() {
+        this.http.post('../rest/resourcesList/users/' + this.data.userId + '/groups/' + this.data.groupId + '/baskets/' + this.data.basketId + '/actions/' + this.data.action.id + '/checkPrintDepositList', { resources: this.data.resIds })
+            .subscribe((data: any) => {
+                this.types = data.types;
+                this.canGenerate = data.canGenerate;
+                this.cannotGenerate = data.cannotGenerate;
+                this.loadingInit = false;
+            }, (err) => {
+                this.notify.error(err.error.errors);
+                this.dialogRef.close();
+                this.loadingInit = false;
+            });
+    }
+
+    onSubmit() {
+        this.loading = true;
+        if (this.data.resIds.length > 0) {
+            this.executeAction();
+        }
+    }
+
+    executeAction() {
+        const downloadLink = document.createElement('a');
+        this.http.put(this.data.processActionRoute, { resources: this.canGenerate, note: this.noteEditor.getNote() }).pipe(
+            tap((data: any) => {
+                if (data && data.errors != null) {
+                    this.notify.error(data.errors);
+                    console.log(data.errors);
+                }
+
+                if (!this.functions.empty(data.data.encodedFile)) {
+                    downloadLink.href = `data:application/pdf;base64,${data.data.encodedFile}`;
+                    downloadLink.setAttribute('download', 'descriptif.pdf');
+                    document.body.appendChild(downloadLink);
+                    downloadLink.click();
+                    this.dialogRef.close(this.canGenerate);
+                }
+            }),
+            finalize(() => this.loading = false),
+            catchError((err: any) => {
+                this.notify.handleSoftErrors(err);
+                return of(false);
+            })
+        ).subscribe();
+    }
+
+    // downloadAcknowledgementReceipt(data: any) {
+    //     this.loadingExport = true;
+    //     this.http.post('../rest/acknowledgementReceipts', { 'resources': data }, { responseType: "blob" })
+    //         .subscribe((data) => {
+    //             let downloadLink = document.createElement('a');
+    //             downloadLink.href = window.URL.createObjectURL(data);
+    //             let today: any;
+    //             let dd: any;
+    //             let mm: any;
+    //             let yyyy: any;
+    //
+    //             today = new Date();
+    //             dd = today.getDate();
+    //             mm = today.getMonth() + 1;
+    //             yyyy = today.getFullYear();
+    //
+    //             if (dd < 10) {
+    //                 dd = '0' + dd;
+    //             }
+    //             if (mm < 10) {
+    //                 mm = '0' + mm;
+    //             }
+    //             today = dd + '-' + mm + '-' + yyyy;
+    //             downloadLink.setAttribute('download', "acknowledgement_receipt_maarch_" + today + ".pdf");
+    //             document.body.appendChild(downloadLink);
+    //             downloadLink.click();
+    //             this.loadingExport = false;
+    //         }, (err: any) => {
+    //             this.notify.handleErrors(err);
+    //         });
+    // }
+}
diff --git a/src/lang/lang-en.json b/src/lang/lang-en.json
index 6b37cf3811d..3ff06723dcc 100644
--- a/src/lang/lang-en.json
+++ b/src/lang/lang-en.json
@@ -1911,5 +1911,7 @@
     "argumentMailNotCorrect": "Argument mail is not correct for a user.",
     "argumentPhoneNotCorrect": "Argument phone is not correct for a user.",
     "authorizedRoutesInformations": "One route per row. Routes documentation",
-    "canNotDisabledField": "This field can not be disabled because it is empty and mandatory"
+    "canNotDisabledField": "This field can not be disabled because it is empty and mandatory",
+    "registeredMailsIncluded": "Number of registered mails included",
+    "cannotGenerateDepositListForMails": "Imposible to generate for the following registered mails :"
 }
diff --git a/src/lang/lang-fr.json b/src/lang/lang-fr.json
index 8ce37a2ae0a..57d012b5687 100644
--- a/src/lang/lang-fr.json
+++ b/src/lang/lang-fr.json
@@ -1943,5 +1943,7 @@
     "argumentMailNotCorrect": "La donnée mail n'est pas correcte pour un utilisateur.",
     "argumentPhoneNotCorrect": "La donnée phone n'est pas correcte pour un utilisateur.",
     "authorizedRoutesInformations": "Une route par ligne. Documentation des routes",
-    "canNotDisabledField": "Le champ ne peut pas être désactivé car il est vide et obligatoire"
+    "canNotDisabledField": "Le champ ne peut pas être désactivé car il est vide et obligatoire",
+    "registeredMailsIncluded": "Nombre de recommandés inclus :",
+    "cannotGenerateDepositListForMails": "Génération impossible pour les recommandés suivants :"
 }
-- 
GitLab